From: Paul Eggert Date: Wed, 22 Jun 2005 06:24:39 +0000 (+0000) Subject: Carefully crafted invalid headers can cause buffer overrun. X-Git-Url: https://git.dogcows.com/gitweb?a=commitdiff_plain;h=d659cbaccdc1f3279c49107cf15f15a639738529;p=chaz%2Ftar Carefully crafted invalid headers can cause buffer overrun. Invalid header fields go undiagnosed. Some valid time strings are ignored. * NEWS: Better support for full-resolution time stamps. The -v option now prints time stamps only to 1-minute resolution. * gnulib.modules: Add utimens. * lib/.cvsignore: Add imaxtostr.c, inttostr.c, inttostr.h, offtostr.c, umaxtostr.c, utimens.c, utimens.h. Remove paxconvert.c. * lib/Makefile.tmpl (libtar_a_SOURCES): Remove paxconvert.c. * lib/paxconvert.c: Remove; superseded by umaxtostr.c. * po/POTFILES.in: Remove lib/paxconvert.c. Add lib/xalloc-die.c, lib/obstack.c. * src/buffer.c (set_start_time, compute_duration, start_time): Use gettime rather than rolling our own code. * src/common.h (OLDGNU_NAME_FIELD_SIZE, MAXOCTAL11, MAXOCTAL7): Remove. (newer_ctime_option): Remove. (timespec_lt): New function. (OLDER_STAT_TIME): Use it. (string_to_chars): First arg is char const *, not char *. (tartime): Time arg is now struct timespec. New bool arg. All callers changed. (code_ns_fraction): New decl. (sys_stat_nanoseconds): Remove decl. (get_stat_atime, get_stat_ctime, get_stat_mtime): New functions. (set_stat_atime, set_stat_ctime, set_stat_mtime): New functions. * src/compare.c: Include utimens.h rather than rolling our own. (diff_dir, diff_file, diff_link, diff_symlink, diff_special): Prototype. (diff_dumpdir, diff_multivol): Prototype. (diff_file): Support higher-resolution time stamps. * src/create.c: Include utimens.h rather than rolling our own. (MAX_OCTAL_VAL): New macro. (tar_copy_str, string_to_chars): Don't bother to zero-fill; the destination is already zeroed. (string_to_chars): First arg is char const *. (start_private_header): Use MINOR_TO_CHARS, not MAJOR_TO_CHARS, for minor device number. (write_header_name, dump_hard_link, dump_file0): Simplify test for old GNU format. (start_header): Put in placeholders for uid, etc., even when using extended headers, for benefit of older "tar" implementations. Don't assume uintmax_t is wider than 32 bits. Output extended header for mtime if needed. (dump_regular_finish, dump_file0): Support extended time stamp resolution. * src/extract.c: Include utimens.h rather than rolling our own. (check_time): Support extended time stamp resolution. * src/list.c: Include . (tartime): Use umaxtostr rather than stringify_uintmax_t_backwards. * src/xheader.c: Include . Do not include . (strtoimax) [!HAVE_DECL_STRTOIMAX && !defined strtoimax]: New decl. (strtoumax) [!HAVE_DECL_STRTOUMAX && !defined strtoumax]: New decl. (BILLION, LOG10_BILLION): New constants. (to_decimal): Remove; superseded by inttostr. All callers changed to use umaxtostr. (xheader_format_name): Don't assume pids and uintmax_t values fit in 63 bytes (!) when printed. (decode_record): Don't bother to check for ERANGE; an out of range value must be treater than len_max anyway. If the length is out of range, output it in the diagnostic. (format_uintmax): Remove; all callers changed to use umaxtostr. (xheader_print): Don't assume sizes can be printed in 99 bytes (!). (out_of_range_header): New function. (decode_time): Use it. (code_time): Accept struct timespec, not time_t and unsigned long. All callers changed. Size sbuf properly, and remove unnecessary check. Don't assume time stamps can fit in 199 bytes. Handle negative time stamps. Handle fractional time stamps more consistently. Don't output unnecessary trailing zeros. (decode_time): Yield struct timespec, not time_t and unsigned long. All callers changed. Handle negative time stamps. Truncate towards minus infinity consistently. Improve overflow checks, and output a better diagnostic on overflow. (code_num): Don't assume uintmax_t can be printed in 99 bytes (!). (decode_num): New function, for better diagnostics. (atime_coder, atime_decoder, gid_decoder, ctime_coder): (ctime_decoder, mtime_coder, mtime_decoder, size_decoder): (uid_decoder, sparse_size_decoder, sparse_numblocks_decoder): (sparse_offset_decoder, sparse_numbytes_decoder): Use decode_num, etc., instead of xstrtoumax, etc. * src/xheader.c (sparse_numblocks_decoder): Remove unchecked use of `calloc'. Use xcalloc instead. (decode_time, gid_decoder, size_decoder, uid_decoder): (sparse_size_decoder, sparse_offset_decoder, sparse_numblocks_decoder): Ensure that the result of calling xstrtoumax is no larger than the maximum value for the target type. Upon any failure, exit with a diagnostic. (sparse_numblocks_decoder): Avoid buffer overrun/heap corruption: use x2nrealloc, rather than `n *= 2' and xrealloc(p, n,.... (decode_time): Rewrite to accept time strings like 1119018481.000000000. Before, such strings were always ignored. --- diff --git a/ChangeLog b/ChangeLog index bd3d4de..14e01a7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,105 @@ +2005-06-21 Paul Eggert + + Further improvements inspired by Jim Meyering's fixes. + + * NEWS: Better support for full-resolution time stamps. + The -v option now prints time stamps only to 1-minute resolution. + * gnulib.modules: Add utimens. + * lib/.cvsignore: Add imaxtostr.c, inttostr.c, inttostr.h, + offtostr.c, umaxtostr.c, utimens.c, utimens.h. Remove paxconvert.c. + * lib/Makefile.tmpl (libtar_a_SOURCES): Remove paxconvert.c. + * lib/paxconvert.c: Remove; superseded by umaxtostr.c. + * po/POTFILES.in: Remove lib/paxconvert.c. Add lib/xalloc-die.c, + lib/obstack.c. + * src/buffer.c (set_start_time, compute_duration, start_time): + Use gettime rather than rolling our own code. + * src/common.h (OLDGNU_NAME_FIELD_SIZE, MAXOCTAL11, MAXOCTAL7): Remove. + (newer_ctime_option): Remove. + (timespec_lt): New function. + (OLDER_STAT_TIME): Use it. + (string_to_chars): First arg is char const *, not char *. + (tartime): Time arg is now struct timespec. New bool arg. + All callers changed. + (code_ns_fraction): New decl. + (sys_stat_nanoseconds): Remove decl. + (get_stat_atime, get_stat_ctime, get_stat_mtime): New functions. + (set_stat_atime, set_stat_ctime, set_stat_mtime): New functions. + * src/compare.c: Include utimens.h rather than rolling our own. + (diff_dir, diff_file, diff_link, diff_symlink, diff_special): + Prototype. + (diff_dumpdir, diff_multivol): Prototype. + (diff_file): Support higher-resolution time stamps. + * src/create.c: Include utimens.h rather than rolling our own. + (MAX_OCTAL_VAL): New macro. + (tar_copy_str, string_to_chars): Don't bother to zero-fill; + the destination is already zeroed. + (string_to_chars): First arg is char const *. + (start_private_header): Use MINOR_TO_CHARS, not MAJOR_TO_CHARS, + for minor device number. + (write_header_name, dump_hard_link, dump_file0): + Simplify test for old GNU format. + (start_header): Put in placeholders for uid, etc., even when + using extended headers, for benefit of older "tar" implementations. + Don't assume uintmax_t is wider than 32 bits. + Output extended header for mtime if needed. + (dump_regular_finish, dump_file0): + Support extended time stamp resolution. + * src/extract.c: Include utimens.h rather than rolling our own. + (check_time): Support extended time stamp resolution. + * src/list.c: Include . + (tartime): Use umaxtostr rather than stringify_uintmax_t_backwards. + * src/xheader.c: Include . + Do not include . + (strtoimax) [!HAVE_DECL_STRTOIMAX && !defined strtoimax]: New decl. + (strtoumax) [!HAVE_DECL_STRTOUMAX && !defined strtoumax]: New decl. + (BILLION, LOG10_BILLION): New constants. + (to_decimal): Remove; superseded by inttostr. All callers changed + to use umaxtostr. + (xheader_format_name): Don't assume pids and uintmax_t values + fit in 63 bytes (!) when printed. + (decode_record): Don't bother to check for ERANGE; an out of range + value must be treater than len_max anyway. + If the length is out of range, output it in the diagnostic. + (format_uintmax): Remove; all callers changed to use umaxtostr. + (xheader_print): Don't assume sizes can be printed in 99 bytes (!). + (out_of_range_header): New function. + (decode_time): Use it. + (code_time): Accept struct timespec, not time_t and unsigned long. + All callers changed. Size sbuf properly, and remove unnecessary check. + Don't assume time stamps can fit in 199 bytes. + Handle negative time stamps. Handle fractional time stamps + more consistently. Don't output unnecessary trailing zeros. + (decode_time): Yield struct timespec, not time_t and unsigned long. + All callers changed. + Handle negative time stamps. Truncate towards minus infinity + consistently. Improve overflow checks, and output a better + diagnostic on overflow. + (code_num): Don't assume uintmax_t can be printed in 99 bytes (!). + (decode_num): New function, for better diagnostics. + (atime_coder, atime_decoder, gid_decoder, ctime_coder): + (ctime_decoder, mtime_coder, mtime_decoder, size_decoder): + (uid_decoder, sparse_size_decoder, sparse_numblocks_decoder): + (sparse_offset_decoder, sparse_numbytes_decoder): + Use decode_num, etc., instead of xstrtoumax, etc. + +2005-06-21 Jim Meyering + + Carefully crafted invalid headers can cause buffer overrun. + Invalid header fields go undiagnosed. + Some valid time strings are ignored. + + * src/xheader.c (sparse_numblocks_decoder): Remove unchecked use + of `calloc'. Use xcalloc instead. + (decode_time, gid_decoder, size_decoder, uid_decoder): + (sparse_size_decoder, sparse_offset_decoder, sparse_numblocks_decoder): + Ensure that the result of calling xstrtoumax is no larger than + the maximum value for the target type. Upon any failure, exit with + a diagnostic. + (sparse_numblocks_decoder): Avoid buffer overrun/heap corruption: + use x2nrealloc, rather than `n *= 2' and xrealloc(p, n,.... + (decode_time): Rewrite to accept time strings like + 1119018481.000000000. Before, such strings were always ignored. + 2005-06-13 Sergey Poznyakoff * src/create.c (dump_file0): Check for is_avoided_name() @@ -6,7 +108,7 @@ * tests/update.at: New file * tests/Makefile.am (TESTSUITE_AT): Add update.at * tests/testsuite.at: Likewise - + 2005-06-13 Sergey Poznyakoff * configure.ac (AC_STRUCT_ST_BLKSIZE) diff --git a/NEWS b/NEWS index e13e44a..ab057ec 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,15 @@ automatically. It is not necessary to give --null option. is useful e.g. for processing output from `find dir -print0'. An orthogonal option --unquote is provided as well. +* Better support for full-resolution time stamps. Tar cannot restore +time stamps to full nanosecond resolution, though, until the kernel +guys get their act together and give us a system call to set file time +stamps to nanosecond resolution. + +* The -v option now prints time stamps only to 1-minute resolution, +not full resolution, to avoid using up too many output columns. +Nanosecond resolution is now supported, but that would be too much. + * Bugfixes ** Allow non-option arguments to be interspersed with options. diff --git a/gnulib.modules b/gnulib.modules index 27db4db..c1caace 100644 --- a/gnulib.modules +++ b/gnulib.modules @@ -40,6 +40,7 @@ timespec unlinkdir unlocked-io utime +utimens xalloc xalloc-die xgetcwd diff --git a/lib/.cvsignore b/lib/.cvsignore index 7cc0022..529dd0b 100644 --- a/lib/.cvsignore +++ b/lib/.cvsignore @@ -73,7 +73,10 @@ hash.c hash.h human.c human.h +imaxtostr.c intprops.h +inttostr.c +inttostr.h lchown.c lchown.h localcharset.c @@ -90,10 +93,10 @@ modechange.c modechange.h obstack.c obstack.h +offtostr.c openat.c openat.h pathmax.h -paxconvert.c paxerror.c paxexit.c paxlib.h @@ -149,12 +152,15 @@ system.h time_r.c time_r.h timespec.h +umaxtostr.c unistd-safer.h unlinkdir.c unlinkdir.h unlocked-io.h unsetenv.c utime.c +utimens.c +utimens.h vasnprintf.c vasnprintf.h vsnprintf.c diff --git a/lib/Makefile.tmpl b/lib/Makefile.tmpl index 5239449..c20e3c4 100644 --- a/lib/Makefile.tmpl +++ b/lib/Makefile.tmpl @@ -20,7 +20,7 @@ noinst_LIBRARIES = libtar.a noinst_HEADERS = system.h localedir.h rmt.h paxlib.h -libtar_a_SOURCES = prepargs.c prepargs.h rtapelib.c paxerror.c paxexit.c paxconvert.c paxnames.c +libtar_a_SOURCES = prepargs.c prepargs.h rtapelib.c paxerror.c paxexit.c paxnames.c localedir = $(datadir)/locale diff --git a/po/POTFILES.in b/po/POTFILES.in index bd2a577..dc7320c 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,6 +1,7 @@ # List of files which contain translatable strings. -# Copyright (C) 1996, 1999, 2000, 2003 Free Software Foundation, Inc. +# Copyright (C) 1996, 1999, 2000, 2003, 2004, 2005 Free Software +# Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,17 +20,19 @@ # Library files lib/argmatch.c +lib/argp-help.c +lib/argp-parse.c lib/error.c lib/getopt.c lib/human.c -lib/quotearg.c -lib/xmalloc.c -lib/rtapelib.c -lib/argp-help.c -lib/paxconvert.c +lib/obstack.c lib/paxerror.c lib/paxexit.c lib/paxnames.c +lib/quotearg.c +lib/rtapelib.c +lib/xalloc-die.c +lib/xmalloc.c rmt/rmt.c @@ -51,4 +54,3 @@ src/xheader.c # Testsuite tests/genfile.c - diff --git a/src/buffer.c b/src/buffer.c index 6fc3f43..5977f5a 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -125,23 +125,16 @@ double duration; void set_start_time () { -#if HAVE_CLOCK_GETTIME - if (clock_gettime (CLOCK_REALTIME, &start_timespec) != 0) -#endif - start_time = time (0); + gettime (&start_time); } void compute_duration () { -#if HAVE_CLOCK_GETTIME struct timespec now; - if (clock_gettime (CLOCK_REALTIME, &now) == 0) - duration += ((now.tv_sec - start_timespec.tv_sec) - + (now.tv_nsec - start_timespec.tv_nsec) / 1e9); - else -#endif - duration += time (NULL) - start_time; + gettime (&now); + duration += ((now.tv_sec - start_time.tv_sec) + + (now.tv_nsec - start_time.tv_nsec) / 1e9); set_start_time (); } @@ -542,7 +535,7 @@ open_archive (enum access_mode wanted_access) strip_trailing_slashes (current_stat_info.file_name); record_start->header.typeflag = GNUTYPE_VOLHDR; - TIME_TO_CHARS (start_time, record_start->header.mtime); + TIME_TO_CHARS (start_time.tv_sec, record_start->header.mtime); finish_header (¤t_stat_info, record_start, -1); } break; @@ -587,8 +580,8 @@ flush_write (void) { if (save_name) { - assign_string (&real_s_name, - safer_name_suffix (save_name, false, + assign_string (&real_s_name, + safer_name_suffix (save_name, false, absolute_names_option)); real_s_totsize = save_totsize; real_s_sizeleft = save_sizeleft; @@ -636,7 +629,7 @@ flush_write (void) memset (record_start, 0, BLOCKSIZE); sprintf (record_start->header.name, "%s Volume %d", volume_label_option, volno); - TIME_TO_CHARS (start_time, record_start->header.mtime); + TIME_TO_CHARS (start_time.tv_sec, record_start->header.mtime); record_start->header.typeflag = GNUTYPE_VOLHDR; finish_header (¤t_stat_info, record_start, -1); } @@ -695,7 +688,7 @@ flush_write (void) assign_string (&real_s_name, 0); else { - assign_string (&real_s_name, + assign_string (&real_s_name, safer_name_suffix (save_name, false, absolute_names_option)); real_s_sizeleft = save_sizeleft; @@ -825,7 +818,7 @@ flush_read (void) { if (save_name) { - assign_string (&real_s_name, + assign_string (&real_s_name, safer_name_suffix (save_name, false, absolute_names_option)); real_s_sizeleft = save_sizeleft; diff --git a/src/common.h b/src/common.h index e6860a1..70a925c 100644 --- a/src/common.h +++ b/src/common.h @@ -23,19 +23,12 @@ /* The checksum field is filled with this while the checksum is computed. */ #define CHKBLANKS " " /* 8 blanks, no null */ -/* Old GNU stores zero-terminated file name */ -#define OLDGNU_NAME_FIELD_SIZE 99 - /* Some constants from POSIX are given names. */ #define NAME_FIELD_SIZE 100 #define PREFIX_FIELD_SIZE 155 #define UNAME_FIELD_SIZE 32 #define GNAME_FIELD_SIZE 32 -/* FIXME */ -#define MAXOCTAL11 017777777777L -#define MAXOCTAL7 07777777 - /* Some various global definitions. */ @@ -185,10 +178,6 @@ GLOBAL mode_t initial_umask; GLOBAL bool multi_volume_option; -/* The same variable holds the time, whether mtime or ctime. Just fake a - non-existing option, for making the code clearer, elsewhere. */ -#define newer_ctime_option newer_mtime_option - /* Specified threshold date and time. Files having an older time stamp do not get archived (also see after_date_option above). */ GLOBAL struct timespec newer_mtime_option; @@ -199,9 +188,14 @@ GLOBAL struct timespec newer_mtime_option; /* Return true if the struct stat ST's M time is less than newer_mtime_option. */ #define OLDER_STAT_TIME(st, m) \ - ((st).st_##m##time < newer_mtime_option.tv_sec \ - || ((st).st_##m##time == newer_mtime_option.tv_sec \ - && TIMESPEC_NS ((st).st_##m##tim) < newer_mtime_option.tv_nsec)) + timespec_lt (get_stat_##m##time (&st), newer_mtime_option) + +/* Return true if A < B. */ +static inline +timespec_lt (struct timespec a, struct timespec b) +{ + return a.tv_sec < b.tv_sec || (a.tv_sec == b.tv_sec && a.tv_nsec < b.tv_nsec); +} /* Zero if there is no recursion, otherwise FNM_LEADING_DIR. */ GLOBAL int recursion_option; @@ -281,12 +275,7 @@ GLOBAL int archive; GLOBAL bool dev_null_output; /* Timestamp for when we started execution. */ -#if HAVE_CLOCK_GETTIME - GLOBAL struct timespec start_timespec; -# define start_time (start_timespec.tv_sec) -#else - GLOBAL time_t start_time; -#endif +GLOBAL struct timespec start_time; GLOBAL struct tar_stat_info current_stat_info; @@ -410,7 +399,7 @@ void size_to_chars (size_t, char *, size_t); void time_to_chars (time_t, char *, size_t); void uid_to_chars (uid_t, char *, size_t); void uintmax_to_chars (uintmax_t, char *, size_t); -void string_to_chars (char *, char *, size_t); +void string_to_chars (char const *, char *, size_t); /* Module diffarch.c. */ @@ -464,7 +453,7 @@ extern size_t recent_long_link_blocks; void decode_header (union block *, struct tar_stat_info *, enum archive_format *, int); -char const *tartime (time_t); +char const *tartime (struct timespec, bool); #define GID_FROM_HEADER(where) gid_from_header (where, sizeof (where)) #define MAJOR_FROM_HEADER(where) major_from_header (where, sizeof (where)) @@ -505,6 +494,8 @@ void assign_string (char **, const char *); char *quote_copy_string (const char *); int unquote_string (char *); +void code_ns_fraction (int, char *); + size_t dot_dot_prefix_len (char const *); enum remove_option @@ -621,7 +612,6 @@ void xheader_set_option (char *string); /* Module system.c */ -void sys_stat_nanoseconds (struct tar_stat_info *); void sys_detect_dev_null_output (void); void sys_save_archive_dev_ino (void); void sys_drain_input_pipe (void); @@ -654,3 +644,120 @@ bool sparse_diff_file (int, struct tar_stat_info *); /* Module utf8.c */ bool string_ascii_p (const char *str); bool utf8_convert (bool to_utf, char const *input, char **output); + + +/* FIXME: The following should get moved into gnulib. */ + +static inline struct timespec +get_stat_atime (struct stat const *st) +{ +#if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC + return st->st_atim; +#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC + return st->st_atimespec; +#else + struct timespec t; + t.tv_sec = st->st_atime; +# if defined HAVE_STRUCT_STAT_ST_ATIMENSEC + t.tv_nsec = st->stat.st_atimensec; +# elif defined HAVE_STRUCT_STAT_ST_SPARE1 + t.tv_nsec = st->stat.st_spare1 * 1000; +# else + t.tv_nsec = 0; +# endif + return t; +#endif +} + +static inline struct timespec +get_stat_ctime (struct stat const *st) +{ +#if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC + return st->st_ctim; +#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC + return st->st_ctimespec; +#else + struct timespec t; + t.tv_sec = st->st_ctime; +# if defined HAVE_STRUCT_STAT_ST_ATIMENSEC + t.tv_nsec = st->stat.st_ctimensec; +# elif defined HAVE_STRUCT_STAT_ST_SPARE1 + t.tv_nsec = st->stat.st_spare1 * 1000; +# else + t.tv_nsec = 0; +# endif + return t; +#endif +} + +static inline struct timespec +get_stat_mtime (struct stat const *st) +{ +#if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC + return st->st_mtim; +#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC + return st->st_mtimespec; +#else + struct timespec t; + t.tv_sec = st->st_mtime; +# if defined HAVE_STRUCT_STAT_ST_ATIMENSEC + t.tv_nsec = st->stat.st_mtimensec; +# elif defined HAVE_STRUCT_STAT_ST_SPARE1 + t.tv_nsec = st->stat.st_spare1 * 1000; +# else + t.tv_nsec = 0; +# endif + return t; +#endif +} + +static inline void +set_stat_atime (struct stat *st, struct timespec t) +{ +#if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC + st->st_atim = t; +#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC + st->st_atimespec = t; +#else + st->st_atime = t.tv_sec; +# if defined HAVE_STRUCT_STAT_ST_ATIMENSEC + st->stat.st_atimensec = t.tv_nsec; +# elif defined HAVE_STRUCT_STAT_ST_SPARE1 + st->stat.st_spare1 = t.tv_nsec / 1000; +# endif +#endif +} + +static inline void +set_stat_ctime (struct stat *st, struct timespec t) +{ +#if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC + st->st_ctim = t; +#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC + st->st_ctimespec = t; +#else + st->st_ctime = t.tv_sec; +# if defined HAVE_STRUCT_STAT_ST_ATIMENSEC + st->stat.st_ctimensec = t.tv_nsec; +# elif defined HAVE_STRUCT_STAT_ST_SPARE1 + st->stat.st_spare1 = t.tv_nsec / 1000; +# endif +#endif +} + +static inline void +set_stat_mtime (struct stat *st, struct timespec t) +{ +#if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC + st->st_mtim = t; +#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC + st->st_mtimespec = t; +#else + st->st_mtime = t.tv_sec; +# if defined HAVE_STRUCT_STAT_ST_ATIMENSEC + st->stat.st_mtimensec = t.tv_nsec; +# elif defined HAVE_STRUCT_STAT_ST_SPARE1 + st->stat.st_spare1 = t.tv_nsec / 1000; +# endif +#endif +} diff --git a/src/compare.c b/src/compare.c index 0ad1223..98a953b 100644 --- a/src/compare.c +++ b/src/compare.c @@ -1,7 +1,7 @@ /* Diff files from a tar archive. Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001, - 2003, 2004 Free Software Foundation, Inc. + 2003, 2004, 2005 Free Software Foundation, Inc. Written by John Gilmore, on 1987-04-30. @@ -21,21 +21,12 @@ #include -#if HAVE_UTIME_H -# include -#else -struct utimbuf - { - long actime; - long modtime; - }; -#endif - #if HAVE_LINUX_FD_H # include #endif #include +#include #include "common.h" #include @@ -196,7 +187,7 @@ get_stat_data (char const *file_name, struct stat *stat_data) static void -diff_dir () +diff_dir (void) { struct stat stat_data; @@ -211,7 +202,7 @@ diff_dir () } static void -diff_file () +diff_file (void) { struct stat stat_data; @@ -254,10 +245,6 @@ diff_file () else { int status; - struct utimbuf restore_times; - - restore_times.actime = stat_data.st_atime; - restore_times.modtime = stat_data.st_mtime; if (current_stat_info.is_sparse) sparse_diff_file (diff_handle, ¤t_stat_info); @@ -265,7 +252,7 @@ diff_file () { if (multi_volume_option) { - assign_string (&save_name, + assign_string (&save_name, current_stat_info.orig_file_name); save_totsize = current_stat_info.stat.st_size; /* save_sizeleft is set in read_and_process. */ @@ -283,14 +270,20 @@ diff_file () close_error (current_stat_info.file_name); if (atime_preserve_option) - utime (current_stat_info.file_name, &restore_times); + { + struct timespec ts[2]; + ts[0] = get_stat_atime (&stat_data); + ts[1] = get_stat_mtime (&stat_data); + if (utimens (current_stat_info.file_name, ts) != 0) + utime_error (current_stat_info.file_name); + } } } } } static void -diff_link () +diff_link (void) { struct stat file_data; struct stat link_data; @@ -305,7 +298,7 @@ diff_link () #ifdef HAVE_READLINK static void -diff_symlink () +diff_symlink (void) { size_t len = strlen (current_stat_info.link_name); char *linkbuf = alloca (len + 1); @@ -327,7 +320,7 @@ diff_symlink () #endif static void -diff_special () +diff_special (void) { struct stat stat_data; @@ -361,7 +354,7 @@ diff_special () } static void -diff_dumpdir () +diff_dumpdir (void) { char *dumpdir_buffer = get_directory_contents (current_stat_info.file_name, 0); @@ -387,7 +380,7 @@ diff_dumpdir () } static void -diff_multivol () +diff_multivol (void) { struct stat stat_data; int fd, status; @@ -398,7 +391,7 @@ diff_multivol () diff_dir (); return; } - + if (!get_stat_data (current_stat_info.file_name, &stat_data)) return; @@ -418,7 +411,7 @@ diff_multivol () } fd = open (current_stat_info.file_name, O_RDONLY | O_BINARY); - + if (fd < 0) { open_error (current_stat_info.file_name); @@ -445,7 +438,7 @@ diff_multivol () if (multi_volume_option) assign_string (&save_name, 0); - + status = close (fd); if (status != 0) close_error (current_stat_info.file_name); @@ -498,7 +491,7 @@ diff_archive (void) diff_symlink (); break; #endif - + case CHRTYPE: case BLKTYPE: case FIFOTYPE: @@ -508,7 +501,7 @@ diff_archive (void) case GNUTYPE_DUMPDIR: diff_dumpdir (); /* Fall through. */ - + case DIRTYPE: diff_dir (); break; diff --git a/src/create.c b/src/create.c index c9be71c..6381f9f 100644 --- a/src/create.c +++ b/src/create.c @@ -21,17 +21,8 @@ #include -#if HAVE_UTIME_H -# include -#else -struct utimbuf - { - long actime; - long modtime; - }; -#endif - #include +#include #include "common.h" #include @@ -51,6 +42,10 @@ struct link ? ((uintmax_t) 1 << ((digits) * (bits_per_digit))) - 1 \ : (uintmax_t) -1) +/* The maximum uintmax_t value that can be represented with octal + digits and a trailing NUL in BUFFER. */ +#define MAX_OCTAL_VAL(buffer) MAX_VAL_WITH_DIGITS (sizeof (buffer) - 1, LG_8) + /* Convert VALUE to an octal representation suitable for tar headers. Output to buffer WHERE with size SIZE. The result is undefined if SIZE is 0 or if VALUE is too large to fit. */ @@ -69,6 +64,29 @@ to_octal (uintmax_t value, char *where, size_t size) while (i); } +/* Copy at most LEN bytes from the string SRC to DST. Terminate with + NUL unless SRC is LEN or more bytes long. */ + +static void +tar_copy_str (char *dst, const char *src, size_t len) +{ + size_t i; + for (i = 0; i < len; i++) + if (! (dst[i] = src[i])) + break; +} + +/* Same as tar_copy_str, but always terminate with NUL if using + is OLDGNU format */ + +static void +tar_name_copy_str (char *dst, const char *src, size_t len) +{ + tar_copy_str (dst, src, len); + if (archive_format == OLDGNU_FORMAT) + dst[len-1] = 0; +} + /* Convert NEGATIVE VALUE to a base-256 representation suitable for tar headers. NEGATIVE is 1 if VALUE was negative before being cast to uintmax_t, 0 otherwise. Output to buffer WHERE with size SIZE. @@ -325,10 +343,10 @@ uintmax_to_chars (uintmax_t v, char *p, size_t s) } void -string_to_chars (char *str, char *p, size_t s) +string_to_chars (char const *str, char *p, size_t s) { - strncpy (p, str, s); - p[s-1] = 0; + tar_copy_str (p, str, s); + p[s - 1] = '\0'; } @@ -361,25 +379,6 @@ write_eot (void) set_next_block_after (pointer); } -/* Copy at most LEN bytes from SRC to DST. Terminate with NUL unless - SRC is LEN characters long */ -static void -tar_copy_str (char *dst, const char *src, size_t len) -{ - dst[len-1] = 0; - strncpy (dst, src, len); -} - -/* Same as tar_copy_str, but always terminate with NUL if using - is OLDGNU format */ -static void -tar_name_copy_str (char *dst, const char *src, size_t len) -{ - tar_copy_str (dst, src, len); - if (archive_format == OLDGNU_FORMAT) - dst[len-1] = 0; -} - /* Write a "private" header */ union block * start_private_header (const char *name, size_t size) @@ -398,7 +397,7 @@ start_private_header (const char *name, size_t size) UID_TO_CHARS (getuid (), header->header.uid); GID_TO_CHARS (getgid (), header->header.gid); MAJOR_TO_CHARS (0, header->header.devmajor); - MAJOR_TO_CHARS (0, header->header.devminor); + MINOR_TO_CHARS (0, header->header.devminor); strncpy (header->header.magic, TMAGIC, TMAGLEN); strncpy (header->header.version, TVERSION, TVERSLEN); return header; @@ -600,9 +599,8 @@ write_header_name (struct tar_stat_info *st) xheader_store ("path", st, NULL); return write_short_name (st); } - else if ((archive_format == OLDGNU_FORMAT - && OLDGNU_NAME_FIELD_SIZE < strlen (st->file_name)) - || NAME_FIELD_SIZE < strlen (st->file_name)) + else if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) + < strlen (st->file_name)) return write_long_name (st); else return write_short_name (st); @@ -662,39 +660,74 @@ start_header (struct tar_stat_info *st) else MODE_TO_CHARS (st->stat.st_mode, header->header.mode); - if (st->stat.st_uid > MAXOCTAL7 && archive_format == POSIX_FORMAT) - xheader_store ("uid", st, NULL); - else - UID_TO_CHARS (st->stat.st_uid, header->header.uid); + { + uid_t uid = st->stat.st_uid; + if (archive_format == POSIX_FORMAT + && MAX_OCTAL_VAL (header->header.uid) < uid) + { + xheader_store ("uid", st, NULL); + uid = 0; + } + UID_TO_CHARS (uid, header->header.uid); + } - if (st->stat.st_gid > MAXOCTAL7 && archive_format == POSIX_FORMAT) - xheader_store ("gid", st, NULL); - else - GID_TO_CHARS (st->stat.st_gid, header->header.gid); + { + gid_t gid = st->stat.st_gid; + if (archive_format == POSIX_FORMAT + && MAX_OCTAL_VAL (header->header.gid) < gid) + { + xheader_store ("gid", st, NULL); + gid = 0; + } + GID_TO_CHARS (gid, header->header.gid); + } - if (st->stat.st_size > MAXOCTAL11 && archive_format == POSIX_FORMAT) - xheader_store ("size", st, NULL); - else - OFF_TO_CHARS (st->stat.st_size, header->header.size); + { + off_t size = st->stat.st_size; + if (archive_format == POSIX_FORMAT + && MAX_OCTAL_VAL (header->header.size) < size) + { + xheader_store ("size", st, NULL); + size = 0; + } + OFF_TO_CHARS (size, header->header.size); + } - TIME_TO_CHARS (st->stat.st_mtime, header->header.mtime); + { + struct timespec mtime = get_stat_mtime (&st->stat); + if (archive_format == POSIX_FORMAT) + { + if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec + || mtime.tv_nsec != 0) + xheader_store ("mtime", st, NULL); + if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec) + mtime.tv_sec = 0; + } + TIME_TO_CHARS (mtime.tv_sec, header->header.mtime); + } /* FIXME */ if (S_ISCHR (st->stat.st_mode) || S_ISBLK (st->stat.st_mode)) { - st->devmajor = major (st->stat.st_rdev); - st->devminor = minor (st->stat.st_rdev); + major_t devmajor = major (st->stat.st_rdev); + minor_t devminor = minor (st->stat.st_rdev); - if (st->devmajor > MAXOCTAL7 && archive_format == POSIX_FORMAT) - xheader_store ("devmajor", st, NULL); - else - MAJOR_TO_CHARS (st->devmajor, header->header.devmajor); + if (archive_format == POSIX_FORMAT + && MAX_OCTAL_VAL (header->header.devmajor) < devmajor) + { + xheader_store ("devmajor", st, NULL); + devmajor = 0; + } + MAJOR_TO_CHARS (devmajor, header->header.devmajor); - if (st->devminor > MAXOCTAL7 && archive_format == POSIX_FORMAT) - xheader_store ("devminor", st, NULL); - else - MAJOR_TO_CHARS (st->devminor, header->header.devminor); + if (archive_format == POSIX_FORMAT + && MAX_OCTAL_VAL (header->header.devminor) < devminor) + { + xheader_store ("devminor", st, NULL); + devminor = 0; + } + MINOR_TO_CHARS (devminor, header->header.devminor); } else if (archive_format != GNU_FORMAT && archive_format != OLDGNU_FORMAT) { @@ -750,15 +783,13 @@ start_header (struct tar_stat_info *st) && (strlen (st->uname) > UNAME_FIELD_SIZE || !string_ascii_p (st->uname))) xheader_store ("uname", st, NULL); - else - UNAME_TO_CHARS (st->uname, header->header.uname); + UNAME_TO_CHARS (st->uname, header->header.uname); if (archive_format == POSIX_FORMAT && (strlen (st->gname) > GNAME_FIELD_SIZE || !string_ascii_p (st->gname))) xheader_store ("gname", st, NULL); - else - GNAME_TO_CHARS (st->gname, header->header.gname); + GNAME_TO_CHARS (st->gname, header->header.gname); } return header; @@ -888,7 +919,7 @@ dump_regular_file (int fd, struct tar_stat_info *st) size_left -= count; if (count) set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE); - + if (count != bufsize) { char buf[UINTMAX_STRSIZE_BOUND]; @@ -909,7 +940,8 @@ dump_regular_file (int fd, struct tar_stat_info *st) } static void -dump_regular_finish (int fd, struct tar_stat_info *st, time_t original_ctime) +dump_regular_finish (int fd, struct tar_stat_info *st, + struct timespec original_ctime) { if (fd >= 0) { @@ -918,7 +950,9 @@ dump_regular_finish (int fd, struct tar_stat_info *st, time_t original_ctime) { stat_diag (st->orig_file_name); } - else if (final_stat.st_ctime != original_ctime) + else if (final_stat.st_ctime != original_ctime.tv_sec + || (get_stat_ctime (&final_stat).tv_nsec + != original_ctime.tv_nsec)) { WARN ((0, 0, _("%s: file changed as we read it"), quotearg_colon (st->orig_file_name))); @@ -1270,9 +1304,8 @@ dump_hard_link (struct tar_stat_info *st) block_ordinal = current_block_ordinal (); assign_string (&st->link_name, link_name); - if ((archive_format == OLDGNU_FORMAT - && OLDGNU_NAME_FIELD_SIZE < strlen (link_name)) - || NAME_FIELD_SIZE < strlen (link_name)) + if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) + < strlen (link_name)) write_long_link (st); st->stat.st_size = 0; @@ -1354,15 +1387,15 @@ dump_file0 (struct tar_stat_info *st, char *p, { union block *header; char type; - time_t original_ctime; - struct utimbuf restore_times; + struct timespec original_ctime; + struct timespec restore_times[2]; off_t block_ordinal = -1; if (interactive_option && !confirm ("add", p)) return; assign_string (&st->orig_file_name, p); - assign_string (&st->file_name, + assign_string (&st->file_name, safer_name_suffix (p, false, absolute_names_option)); if (deref_stat (dereference_option, p, &st->stat) != 0) @@ -1371,10 +1404,9 @@ dump_file0 (struct tar_stat_info *st, char *p, return; } st->archive_file_size = st->stat.st_size; - sys_stat_nanoseconds (st); - original_ctime = st->stat.st_ctime; - restore_times.actime = st->stat.st_atime; - restore_times.modtime = st->stat.st_mtime; + original_ctime = get_stat_ctime (&st->stat); + restore_times[0] = get_stat_atime (&st->stat); + restore_times[1] = get_stat_mtime (&st->stat); #ifdef S_ISHIDDEN if (S_ISHIDDEN (st->stat.st_mode)) @@ -1420,7 +1452,7 @@ dump_file0 (struct tar_stat_info *st, char *p, { dump_dir (st, top_level, parent_device); if (atime_preserve_option) - utime (p, &restore_times); + utimens (p, restore_times); return; } else @@ -1486,7 +1518,7 @@ dump_file0 (struct tar_stat_info *st, char *p, } if (atime_preserve_option) - utime (st->orig_file_name, &restore_times); + utimens (st->orig_file_name, restore_times); file_count_links (st); return; } @@ -1507,8 +1539,7 @@ dump_file0 (struct tar_stat_info *st, char *p, } buffer[size] = '\0'; assign_string (&st->link_name, buffer); - if ((archive_format == OLDGNU_FORMAT && size > OLDGNU_NAME_FIELD_SIZE) - || size > NAME_FIELD_SIZE) + if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size) write_long_link (st); block_ordinal = current_block_ordinal (); diff --git a/src/extract.c b/src/extract.c index 0e758a2..9be0a8b 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1,7 +1,7 @@ /* Extract files from a tar archive. Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000, - 2001, 2003, 2004 Free Software Foundation, Inc. + 2001, 2003, 2004, 2005 Free Software Foundation, Inc. Written by John Gilmore, on 1985-11-19. @@ -21,19 +21,10 @@ #include #include +#include #include #include -#if HAVE_UTIME_H -# include -#else -struct utimbuf - { - long actime; - long modtime; - }; -#endif - #include "common.h" static bool we_are_root; /* true if our effective uid == 0 */ @@ -201,15 +192,30 @@ set_mode (char const *file_name, /* Check time after successfully setting FILE_NAME's time stamp to T. */ static void -check_time (char const *file_name, time_t t) +check_time (char const *file_name, struct timespec t) { - time_t now; - if (t <= 0) + if (t.tv_sec <= 0) WARN ((0, 0, _("%s: implausibly old time stamp %s"), - file_name, tartime (t))); - else if (start_time < t && (now = time (0)) < t) - WARN ((0, 0, _("%s: time stamp %s is %lu s in the future"), - file_name, tartime (t), (unsigned long) (t - now))); + file_name, tartime (t, true))); + else if (timespec_lt (start_time, t)) + { + struct timespec now; + gettime (&now); + if (timespec_lt (now, t)) + { + unsigned long int ds = t.tv_sec - now.tv_sec; + int dns = t.tv_nsec - now.tv_nsec; + char dnsbuf[sizeof ".FFFFFFFFF"]; + if (dns < 0) + { + dns += 1000000000; + ds--; + } + code_ns_fraction (dns, dnsbuf); + WARN ((0, 0, _("%s: time stamp %s is %lu%s s in the future"), + file_name, tartime (t, true), ds, dnsbuf)); + } + } } /* Restore stat attributes (owner, group, mode and times) for @@ -233,8 +239,6 @@ set_stat (char const *file_name, mode_t invert_permissions, enum permstatus permstatus, char typeflag) { - struct utimbuf utimbuf; - if (typeflag != SYMTYPE) { /* We do the utime before the chmod because some versions of utime are @@ -248,19 +252,16 @@ set_stat (char const *file_name, /* FIXME: incremental_option should set ctime too, but how? */ - if (incremental_option) - utimbuf.actime = stat_info->st_atime; - else - utimbuf.actime = start_time; - - utimbuf.modtime = stat_info->st_mtime; + struct timespec ts[2]; + ts[0] = incremental_option ? get_stat_atime (stat_info) : start_time; + ts[1] = get_stat_mtime (stat_info); - if (utime (file_name, &utimbuf) < 0) + if (utimens (file_name, ts) != 0) utime_error (file_name); else { - check_time (file_name, utimbuf.actime); - check_time (file_name, utimbuf.modtime); + check_time (file_name, ts[0]); + check_time (file_name, ts[1]); } } @@ -778,7 +779,7 @@ extract_file (char *file_name, int typeflag) static int extract_link (char *file_name, int typeflag) { - char const *link_name = safer_name_suffix (current_stat_info.link_name, + char const *link_name = safer_name_suffix (current_stat_info.link_name, true, absolute_names_option); int interdir_made = 0; @@ -1134,7 +1135,7 @@ extract_archive (void) if (verbose_option) print_header (¤t_stat_info, -1); - file_name = safer_name_suffix (current_stat_info.file_name, + file_name = safer_name_suffix (current_stat_info.file_name, false, absolute_names_option); if (strip_name_components) { diff --git a/src/incremen.c b/src/incremen.c index 9bd09ff..69805a7 100644 --- a/src/incremen.c +++ b/src/incremen.c @@ -354,6 +354,8 @@ read_directory_file (void) _("Time stamp out of range"))); else { + /* FIXME: This should also input nanoseconds, but that will be a + change in file format. */ newer_mtime_option.tv_sec = t; newer_mtime_option.tv_nsec = 0; } @@ -444,7 +446,9 @@ write_directory_file (void) if (sys_truncate (fileno (fp)) != 0) truncate_error (listed_incremental_option); - fprintf (fp, "%lu\n", (unsigned long) start_time); + /* FIXME: This should also output nanoseconds, but that will be a + change in file format. */ + fprintf (fp, "%lu\n", (unsigned long int) start_time.tv_sec); if (! ferror (fp) && directory_table) hash_do_for_each (directory_table, write_directory_file_entry, fp); if (ferror (fp)) diff --git a/src/list.c b/src/list.c index e56e7dc..ddf044d 100644 --- a/src/list.c +++ b/src/list.c @@ -19,10 +19,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* Define to non-zero for forcing old ctime format instead of ISO format. */ -#undef USE_OLD_CTIME - #include +#include #include #include "common.h" @@ -68,6 +66,7 @@ read_and (void (*do_something) (void)) { enum read_header status = HEADER_STILL_UNREAD; enum read_header prev_status; + struct timespec mtime; base64_init (); name_gather (); @@ -95,13 +94,12 @@ read_and (void (*do_something) (void)) || (NEWER_OPTION_INITIALIZED (newer_mtime_option) /* FIXME: We get mtime now, and again later; this causes duplicate diagnostics if header.mtime is bogus. */ - && ((current_stat_info.stat.st_mtime + && ((mtime.tv_sec = TIME_FROM_HEADER (current_header->header.mtime)), -#ifdef ST_MTIM_NSEC /* FIXME: Grab fractional time stamps from extended header. */ - current_stat_info.stat.st_mtim.ST_MTIM_NSEC = 0, -#endif + mtime.tv_nsec = 0, + set_stat_mtime (¤t_stat_info.stat, mtime), OLDER_STAT_TIME (current_stat_info.stat, m))) || excluded_name (current_stat_info.file_name)) { @@ -522,6 +520,9 @@ decode_header (union block *header, struct tar_stat_info *stat_info, enum archive_format *format_pointer, int do_user_group) { enum archive_format format; + struct timespec atime; + struct timespec ctime; + struct timespec mtime; if (strcmp (header->header.magic, TMAGIC) == 0) { @@ -543,22 +544,31 @@ decode_header (union block *header, struct tar_stat_info *stat_info, *format_pointer = format; stat_info->stat.st_mode = MODE_FROM_HEADER (header->header.mode); - stat_info->stat.st_mtime = TIME_FROM_HEADER (header->header.mtime); + mtime.tv_sec = TIME_FROM_HEADER (header->header.mtime); + mtime.tv_nsec = 0; + set_stat_mtime (&stat_info->stat, mtime); assign_string (&stat_info->uname, header->header.uname[0] ? header->header.uname : NULL); assign_string (&stat_info->gname, header->header.gname[0] ? header->header.gname : NULL); - stat_info->devmajor = MAJOR_FROM_HEADER (header->header.devmajor); - stat_info->devminor = MINOR_FROM_HEADER (header->header.devminor); - - stat_info->stat.st_atime = start_time; - stat_info->stat.st_ctime = start_time; if (format == OLDGNU_FORMAT && incremental_option) { - stat_info->stat.st_atime = TIME_FROM_HEADER (header->oldgnu_header.atime); - stat_info->stat.st_ctime = TIME_FROM_HEADER (header->oldgnu_header.ctime); + atime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.atime); + ctime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.ctime); + atime.tv_nsec = ctime.tv_nsec = 0; + } + else if (format == STAR_FORMAT) + { + atime.tv_sec = TIME_FROM_HEADER (header->star_header.atime); + ctime.tv_sec = TIME_FROM_HEADER (header->star_header.ctime); + atime.tv_nsec = ctime.tv_nsec = 0; } + else + atime = ctime = start_time; + + set_stat_atime (&stat_info->stat, atime); + set_stat_ctime (&stat_info->stat, ctime); if (format == V7_FORMAT) { @@ -568,13 +578,6 @@ decode_header (union block *header, struct tar_stat_info *stat_info, } else { - - if (format == STAR_FORMAT) - { - stat_info->stat.st_atime = TIME_FROM_HEADER (header->star_header.atime); - stat_info->stat.st_ctime = TIME_FROM_HEADER (header->star_header.ctime); - } - if (do_user_group) { /* FIXME: Decide if this should somewhat depend on -p. */ @@ -594,8 +597,9 @@ decode_header (union block *header, struct tar_stat_info *stat_info, { case BLKTYPE: case CHRTYPE: - stat_info->stat.st_rdev = makedev (stat_info->devmajor, - stat_info->devminor); + stat_info->stat.st_rdev = + makedev (MAJOR_FROM_HEADER (header->header.devmajor), + MINOR_FROM_HEADER (header->header.devminor)); break; default: @@ -922,47 +926,59 @@ uintmax_from_header (const char *p, size_t s) /* Return a printable representation of T. The result points to static storage that can be reused in the next call to this - function, to ctime, or to asctime. */ + function, to ctime, or to asctime. If FULL_TIME, then output the + time stamp to its full resolution; otherwise, just output it to + 1-minute resolution. */ char const * -tartime (time_t t) +tartime (struct timespec t, bool full_time) { + enum { fraclen = sizeof ".FFFFFFFFF" - 1 }; static char buffer[max (UINTMAX_STRSIZE_BOUND + 1, - INT_STRLEN_BOUND (int) + 16)]; + INT_STRLEN_BOUND (int) + 16) + + fraclen]; + struct tm *tm; + time_t s = t.tv_sec; + int ns = t.tv_nsec; + bool negative = s < 0; char *p; -#if USE_OLD_CTIME - p = ctime (&t); - if (p) + if (negative && ns != 0) { - char const *time_stamp = p + 4; - for (p += 16; p[3] != '\n'; p++) - p[0] = p[3]; - p[0] = '\0'; - return time_stamp; + s++; + ns = 1000000000 - ns; } -#else - /* Use ISO 8610 format. See: - http://www.cl.cam.ac.uk/~mgk25/iso-time.html */ - struct tm *tm = utc_option ? gmtime (&t) : localtime (&t); + + tm = utc_option ? gmtime (&s) : localtime (&s); if (tm) { - sprintf (buffer, "%04ld-%02d-%02d %02d:%02d:%02d", - tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); + if (full_time) + { + sprintf (buffer, "%04ld-%02d-%02d %02d:%02d:%02d", + tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + code_ns_fraction (ns, buffer + strlen (buffer)); + } + else + sprintf (buffer, "%04ld-%02d-%02d %02d:%02d", + tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min); return buffer; } -#endif /* The time stamp cannot be broken down, most likely because it is out of range. Convert it as an integer, right-adjusted in a field with the same width as the usual - 19-byte 4-year ISO time format. */ - p = stringify_uintmax_t_backwards (t < 0 ? - (uintmax_t) t : (uintmax_t) t, - buffer + sizeof buffer); - if (t < 0) + 4-year ISO time format. */ + p = umaxtostr (negative ? - (uintmax_t) s : s, + buffer + sizeof buffer - UINTMAX_STRSIZE_BOUND - fraclen); + if (negative) *--p = '-'; - while (buffer + sizeof buffer - 19 - 1 < p) + while ((buffer + sizeof buffer - sizeof "YYYY-MM-DD HH:MM" + + (full_time ? sizeof ":SS.FFFFFFFFF" - 1 : 0)) + < p) *--p = ' '; + if (full_time) + code_ns_fraction (ns, buffer + sizeof buffer - 1 - fraclen); return p; } @@ -979,23 +995,24 @@ tartime (time_t t) /* FIXME: Note that print_header uses the globals HEAD, HSTAT, and HEAD_STANDARD, which must be set up in advance. Not very clean.. */ -/* UGSWIDTH starts with 18, so with user and group names <= 8 chars, the - columns never shift during the listing. */ -#define UGSWIDTH 18 -static int ugswidth = UGSWIDTH; /* maximum width encountered so far */ +/* Width of "user/group size", with initial value chosen + heuristically. This grows as needed, though this may cause some + stairstepping in the output. Make it too small and the output will + almost always look ragged. Make it too large and the output will + be spaced out too far. */ +static int ugswidth = 19; -/* DATEWIDTH is the number of columns taken by the date and time fields. */ -#if USE_OLD_CDATE -# define DATEWIDTH 19 -#else -# define DATEWIDTH 18 -#endif +/* Width of printed time stamps. It grows if longer time stamps are + found (typically, those with nanosecond resolution). Like + USGWIDTH, some stairstepping may occur. */ +static int datewidth = sizeof "YYYY-MM-DD HH:MM" - 1; void print_header (struct tar_stat_info *st, off_t block_ordinal) { char modes[11]; char const *time_stamp; + int time_stamp_len; char *temp_name = st->orig_file_name ? st->orig_file_name : st->file_name; /* These hold formatted ints. */ @@ -1005,6 +1022,7 @@ print_header (struct tar_stat_info *st, off_t block_ordinal) /* holds formatted size or major,minor */ char uintbuf[UINTMAX_STRSIZE_BOUND]; int pad; + int sizelen; if (block_number_option) { @@ -1084,7 +1102,10 @@ print_header (struct tar_stat_info *st, off_t block_ordinal) /* Time stamp. */ - time_stamp = tartime (st->stat.st_mtime); + time_stamp = tartime (get_stat_mtime (&st->stat), false); + time_stamp_len = strlen (time_stamp); + if (datewidth < time_stamp_len) + datewidth = time_stamp_len; /* User and group names. */ @@ -1159,12 +1180,14 @@ print_header (struct tar_stat_info *st, off_t block_ordinal) /* Figure out padding and print the whole line. */ - pad = strlen (user) + strlen (group) + strlen (size) + 1; + sizelen = strlen (size); + pad = strlen (user) + 1 + strlen (group) + 1 + sizelen; if (pad > ugswidth) ugswidth = pad; - fprintf (stdlis, "%s %s/%s %*s%s %s", - modes, user, group, ugswidth - pad, "", size, time_stamp); + fprintf (stdlis, "%s %s/%s %*s %-*s", + modes, user, group, ugswidth - pad + sizelen, size, + datewidth, time_stamp); fprintf (stdlis, " %s", quotearg (temp_name)); @@ -1248,7 +1271,7 @@ print_for_mkdir (char *dirname, int length, mode_t mode) STRINGIFY_BIGINT (current_block_ordinal (), buf)); } - fprintf (stdlis, "%s %*s %.*s\n", modes, ugswidth + DATEWIDTH, + fprintf (stdlis, "%s %*s %.*s\n", modes, ugswidth + 1 + datewidth, _("Creating directory:"), length, quotearg (dirname)); } } diff --git a/src/misc.c b/src/misc.c index 2ba52a3..55bedd2 100644 --- a/src/misc.c +++ b/src/misc.c @@ -206,6 +206,40 @@ unquote_string (char *string) return result; } +/* Handling numbers. */ + +/* Output fraction and trailing digits appropriate for a nanoseconds + count equal to NS, but don't output unnecessary '.' or trailing + zeros. */ + +void +code_ns_fraction (int ns, char *p) +{ + if (ns == 0) + *p = '\0'; + else + { + int i = 9; + *p++ = '.'; + + while (ns % 10 == 0) + { + ns /= 10; + i--; + } + + p[i] = '\0'; + + for (;;) + { + p[--i] = '0' + ns % 10; + if (i == 0) + break; + ns /= 10; + } + } +} + /* File handling. */ /* Saved names in case backup needs to be undone. */ diff --git a/src/system.c b/src/system.c index dee82b2..b1e4eef 100644 --- a/src/system.c +++ b/src/system.c @@ -22,30 +22,6 @@ #include #include -void -sys_stat_nanoseconds (struct tar_stat_info *st) -{ -#if defined(HAVE_STRUCT_STAT_ST_SPARE1) - st->atime_nsec = st->stat.st_spare1 * 1000; - st->mtime_nsec = st->stat.st_spare2 * 1000; - st->ctime_nsec = st->stat.st_spare3 * 1000; -#elif defined(HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC) - st->atime_nsec = st->stat.st_atim.tv_nsec; - st->mtime_nsec = st->stat.st_mtim.tv_nsec; - st->ctime_nsec = st->stat.st_ctim.tv_nsec; -#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC) - st->atime_nsec = st->stat.st_atimespec.tv_nsec; - st->mtime_nsec = st->stat.st_mtimespec.tv_nsec; - st->ctime_nsec = st->stat.st_ctimespec.tv_nsec; -#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC) - st->atime_nsec = st->stat.st_atimensec; - st->mtime_nsec = st->stat.st_mtimensec; - st->ctime_nsec = st->stat.st_ctimensec; -#else - st->atime_nsec = st->mtime_nsec = st->ctime_nsec = 0; -#endif -} - #if MSDOS bool @@ -642,7 +618,7 @@ static void oct_to_env (char *envar, unsigned long num) { char buf[1+1+(sizeof(unsigned long)*CHAR_BIT+2)/3]; - + snprintf (buf, sizeof buf, "0%lo", num); setenv (envar, buf, 1); } @@ -679,7 +655,7 @@ stat_to_env (char *name, char type, struct tar_stat_info *st) dec_to_env ("TAR_CTIME", st->stat.st_ctime); dec_to_env ("TAR_SIZE", st->stat.st_size); dec_to_env ("TAR_UID", st->stat.st_uid); - dec_to_env ("TAR_GID", st->stat.st_gid); + dec_to_env ("TAR_GID", st->stat.st_gid); switch (type) { @@ -713,7 +689,7 @@ sys_exec_command (char *file_name, int typechar, struct tar_stat_info *st) { int p[2]; char *argv[4]; - + xpipe (p); pipe_handler = signal (SIGPIPE, SIG_IGN); pid = xfork (); @@ -727,7 +703,7 @@ sys_exec_command (char *file_name, int typechar, struct tar_stat_info *st) /* Child */ xdup2 (p[PREAD], STDIN_FILENO); xclose (p[PWRITE]); - + stat_to_env (file_name, typechar, st); argv[0] = "/bin/sh"; @@ -744,7 +720,7 @@ void sys_wait_command (void) { int status; - + if (pid < 0) return; @@ -776,4 +752,3 @@ sys_wait_command (void) } #endif /* not MSDOS */ - diff --git a/src/tar.c b/src/tar.c index 692120e..b0e039d 100644 --- a/src/tar.c +++ b/src/tar.c @@ -656,7 +656,7 @@ read_name_from_file (FILE *fp, struct obstack *stk) { int c; size_t counter = 0; - + for (c = getc (fp); c != EOF && c != filename_terminator; c = getc (fp)) { if (c == 0) @@ -721,7 +721,7 @@ update_argv (const char *filename, struct argp_state *state) size_t new_argc; bool is_stdin = false; enum read_file_list_state read_state; - + if (!strcmp (filename, "-")) { is_stdin = true; @@ -741,7 +741,7 @@ update_argv (const char *filename, struct argp_state *state) if (read_state == file_list_zero) { size_t size; - + WARN ((0, 0, N_("%s: file name read contains nul character"), quotearg_colon (filename))); @@ -751,7 +751,7 @@ update_argv (const char *filename, struct argp_state *state) for (; size > 0; size--, p++) if (*p) obstack_1grow (&argv_stk, *p); - else + else obstack_1grow (&argv_stk, '\n'); obstack_1grow (&argv_stk, 0); count = 1; @@ -973,15 +973,14 @@ parse_opt (int key, char *arg, struct argp_state *state) stat_error (arg); USAGE_ERROR ((0, 0, _("Date sample file not found"))); } - newer_mtime_option.tv_sec = st.st_mtime; - newer_mtime_option.tv_nsec = TIMESPEC_NS (st.st_mtim); + newer_mtime_option = get_stat_mtime (&st); } else { if (! get_date (&newer_mtime_option, arg, NULL)) { WARN ((0, 0, _("Substituting %s for unknown date format %s"), - tartime (newer_mtime_option.tv_sec), quote (arg))); + tartime (newer_mtime_option, false), quote (arg))); newer_mtime_option.tv_nsec = 0; } else @@ -1819,16 +1818,10 @@ decode_options (int argc, char **argv) if (verbose_option && args.textual_date_option) { - /* FIXME: tartime should support nanoseconds, too, so that this - comparison doesn't complain about lost nanoseconds. */ - char const *treated_as = tartime (newer_mtime_option.tv_sec); + char const *treated_as = tartime (newer_mtime_option, true); if (strcmp (args.textual_date_option, treated_as) != 0) - WARN ((0, 0, - ngettext ("Treating date `%s' as %s + %ld nanosecond", - "Treating date `%s' as %s + %ld nanoseconds", - newer_mtime_option.tv_nsec), - args.textual_date_option, treated_as, - newer_mtime_option.tv_nsec)); + WARN ((0, 0, _("Treating date `%s' as %s"), + args.textual_date_option, treated_as)); } } diff --git a/src/tar.h b/src/tar.h index cc937bb..3e958ec 100644 --- a/src/tar.h +++ b/src/tar.h @@ -277,17 +277,10 @@ struct tar_stat_info trailing slash before it was normalized. */ char *link_name; /* name of link for the current archive entry. */ - unsigned int devminor; /* device minor number */ - unsigned int devmajor; /* device major number */ char *uname; /* user name of owner */ char *gname; /* group name of owner */ struct stat stat; /* regular filesystem stat */ - /* Nanosecond parts of file timestamps (if available) */ - unsigned long atime_nsec; - unsigned long mtime_nsec; - unsigned long ctime_nsec; - off_t archive_file_size; /* Size of file as stored in the archive. Equals stat.st_size for non-sparse files */ diff --git a/src/xheader.c b/src/xheader.c index a45d24a..e88934e 100644 --- a/src/xheader.c +++ b/src/xheader.c @@ -20,14 +20,21 @@ #include #include +#include #include #include -#include #include "common.h" #include +#if !HAVE_DECL_STRTOIMAX && !defined strtoimax +intmax_t strtoimax (); +#endif +#if !HAVE_DECL_STRTOUMAX && !defined strtoumax +uintmax_t strtoumax (); +#endif + static bool xheader_protected_pattern_p (char const *pattern); static bool xheader_protected_keyword_p (char const *keyword); static void xheader_set_single_keyword (char *) __attribute__ ((noreturn)); @@ -50,6 +57,8 @@ static size_t global_header_count; However it should wait until buffer.c is finally rewritten */ +enum { BILLION = 1000000000, LOG10_BILLION = 9 }; + /* Keyword options */ @@ -191,26 +200,6 @@ xheader_set_option (char *string) } } -static void -to_decimal (uintmax_t value, char *where, size_t size) -{ - size_t i = 0, j; - - where[i++] = 0; - do - { - where[i++] = '0' + value % 10; - value /= 10; - } - while (i < size && value); - for (j = 0, i--; j < i; j++, i--) - { - char c = where[j]; - where[j] = where[i]; - where[i] = c; - } -} - /* string Includes: Replaced By: %d The directory name of the file, @@ -232,8 +221,10 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, bool allow_n) const char *p; char *dir = NULL; char *base = NULL; - char pidbuf[64]; - char nbuf[64]; + char pidbuf[UINTMAX_STRSIZE_BOUND]; + char const *pptr; + char nbuf[UINTMAX_STRSIZE_BOUND]; + char const *nptr = NULL; for (p = fmt; *p && (p = strchr (p, '%')); ) { @@ -246,7 +237,7 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, bool allow_n) case 'd': if (st) { - dir = safer_name_suffix (dir_name (st->orig_file_name), + dir = safer_name_suffix (dir_name (st->orig_file_name), false, absolute_names_option); len += strlen (dir) - 1; } @@ -261,15 +252,15 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, bool allow_n) break; case 'p': - to_decimal (getpid (), pidbuf, sizeof pidbuf); - len += strlen (pidbuf) - 1; + pptr = umaxtostr (getpid (), pidbuf); + len += pidbuf + sizeof pidbuf - 1 - pptr - 1; break; case 'n': if (allow_n) { - to_decimal (global_header_count + 1, pidbuf, sizeof pidbuf); - len += strlen (nbuf) - 1; + nptr = umaxtostr (global_header_count + 1, nbuf); + len += nbuf + sizeof nbuf - 1 - nptr - 1; } break; } @@ -301,14 +292,14 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, bool allow_n) break; case 'p': - q = stpcpy (q, pidbuf); + q = stpcpy (q, pptr); p += 2; break; case 'n': - if (allow_n) + if (nptr) { - q = stpcpy (q, nbuf); + q = stpcpy (q, nptr); p += 2; } /* else fall through */ @@ -492,9 +483,11 @@ decode_record (char **ptr, errno = 0; len = strtoul (p, &len_lim, 10); - if (len_max < len || (len == ULONG_MAX && errno == ERANGE)) + if (len_max < len) { - ERROR ((0, 0, _("Malformed extended header: length out of range"))); + int len_len = len_lim - p; + ERROR ((0, 0, _("Extended header length %*s is out of range"), + len_len, p)); return false; } @@ -650,48 +643,24 @@ xheader_read (union block *p, size_t size) while (size > 0); } -static size_t -format_uintmax (uintmax_t val, char *buf, size_t s) -{ - if (!buf) - { - s = 0; - do - s++; - while ((val /= 10) != 0); - } - else - { - char *p = buf + s - 1; - - do - { - *p-- = val % 10 + '0'; - } - while ((val /= 10) != 0); - - while (p >= buf) - *p-- = '0'; - } - return s; -} - static void xheader_print (struct xheader *xhdr, char const *keyword, char const *value) { size_t len = strlen (keyword) + strlen (value) + 3; /* ' ' + '=' + '\n' */ - size_t p, n = 0; - char nbuf[100]; + size_t p; + size_t n = 0; + char nbuf[UINTMAX_STRSIZE_BOUND]; + char const *np; do { p = n; - n = format_uintmax (len + p, NULL, 0); + np = umaxtostr (len + p, nbuf); + n = nbuf + sizeof nbuf - 1 - np; } while (n != p); - format_uintmax (len + n, nbuf, n); - obstack_grow (xhdr->stk, nbuf, n); + obstack_grow (xhdr->stk, np, n); obstack_1grow (xhdr->stk, ' '); obstack_grow (xhdr->stk, keyword, strlen (keyword)); obstack_1grow (xhdr->stk, '='); @@ -729,6 +698,24 @@ xheader_destroy (struct xheader *xhdr) /* Implementations */ + +static void +out_of_range_header (char const *keyword, char const *value, + uintmax_t minus_minval, uintmax_t maxval) +{ + char minval_buf[UINTMAX_STRSIZE_BOUND + 1]; + char maxval_buf[UINTMAX_STRSIZE_BOUND]; + char *minval_string = umaxtostr (minus_minval, minval_buf + 1); + char *maxval_string = umaxtostr (maxval, maxval_buf); + if (minus_minval) + *--minval_string = '-'; + + /* TRANSLATORS: The first %s is the pax extended header keyword + (atime, gid, etc.). */ + ERROR ((0, 0, _("Extended header %s=%s is out of range %s..%s"), + keyword, value, minval_string, maxval_string)); +} + static void code_string (char const *string, char const *keyword, struct xheader *xhdr) { @@ -758,41 +745,141 @@ decode_string (char **string, char const *arg) } static void -code_time (time_t t, unsigned long nano, - char const *keyword, struct xheader *xhdr) +code_time (struct timespec t, char const *keyword, struct xheader *xhdr) { - char sbuf[200]; - size_t s = format_uintmax (t, NULL, 0); - if (s + 11 >= sizeof sbuf) - return; - format_uintmax (t, sbuf, s); - sbuf[s++] = '.'; - s += format_uintmax (nano, sbuf + s, 9); - sbuf[s] = 0; - xheader_print (xhdr, keyword, sbuf); + time_t s = t.tv_sec; + int ns = t.tv_nsec; + char sbuf[1/*"-"*/ + UINTMAX_STRSIZE_BOUND + 1/*"."*/ + LOG10_BILLION]; + char *np; + bool negative = s < 0; + + if (negative && ns != 0) + { + s++; + ns = BILLION - ns; + } + + np = umaxtostr (negative ? - (uintmax_t) s : (uintmax_t) s, sbuf + 1); + if (negative) + *--np = '-'; + code_ns_fraction (ns, sbuf + UINTMAX_STRSIZE_BOUND); + xheader_print (xhdr, keyword, np); } -static void -decode_time (char const *arg, time_t *secs, unsigned long *nsecs) +static bool +decode_time (struct timespec *ts, char const *arg, char const *keyword) { - uintmax_t u; + time_t s; + unsigned long int ns = 0; char *p; - if (xstrtoumax (arg, &p, 10, &u, "") == LONGINT_OK) + char *arg_lim; + bool negative = *arg == '-'; + + errno = 0; + + if (ISDIGIT (arg[negative])) { - *secs = u; - if (*p == '.' && xstrtoumax (p+1, NULL, 10, &u, "") == LONGINT_OK) - *nsecs = u; + if (negative) + { + intmax_t i = strtoimax (arg, &arg_lim, 10); + if (TYPE_SIGNED (time_t) ? i < TYPE_MINIMUM (time_t) : i < 0) + goto out_of_range; + s = i; + } + else + { + uintmax_t i = strtoumax (arg, &arg_lim, 10); + if (TYPE_MAXIMUM (time_t) < i) + goto out_of_range; + s = i; + } + + p = arg_lim; + + if (errno == ERANGE) + goto out_of_range; + + if (*p == '.') + { + int digits = 0; + bool trailing_nonzero = false; + + while (ISDIGIT (*++p)) + if (digits < LOG10_BILLION) + { + ns = 10 * ns + (*p - '0'); + digits++; + } + else + trailing_nonzero |= *p != '0'; + + while (digits++ < LOG10_BILLION) + ns *= 10; + + if (negative) + { + /* Convert "-1.10000000000001" to s == -2, ns == 89999999. + I.e., truncate time stamps towards minus infinity while + converting them to internal form. */ + ns += trailing_nonzero; + if (ns != 0) + { + if (s == TYPE_MINIMUM (time_t)) + goto out_of_range; + s--; + ns = BILLION - ns; + } + } + } + + if (! *p) + { + ts->tv_sec = s; + ts->tv_nsec = ns; + return true; + } } + + ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"), + keyword, arg)); + return false; + + out_of_range: + out_of_range_header (keyword, arg, - (uintmax_t) TYPE_MINIMUM (time_t), + TYPE_MAXIMUM (time_t)); + return false; } static void code_num (uintmax_t value, char const *keyword, struct xheader *xhdr) { - char sbuf[100]; - size_t s = format_uintmax (value, NULL, 0); - format_uintmax (value, sbuf, s); - sbuf[s] = 0; - xheader_print (xhdr, keyword, sbuf); + char sbuf[UINTMAX_STRSIZE_BOUND]; + xheader_print (xhdr, keyword, umaxtostr (value, sbuf)); +} + +static bool +decode_num (uintmax_t *num, char const *arg, uintmax_t maxval, + char const *keyword) +{ + uintmax_t u; + char *arg_lim; + + if (! (ISDIGIT (*arg) + && (errno = 0, u = strtoumax (arg, &arg_lim, 10), !*arg_lim))) + { + ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"), + keyword, arg)); + return false; + } + + if (! (u <= maxval && errno != ERANGE)) + { + out_of_range_header (keyword, arg, 0, maxval); + return false; + } + + *num = u; + return true; } static void @@ -813,13 +900,15 @@ static void atime_coder (struct tar_stat_info const *st, char const *keyword, struct xheader *xhdr, void *data __attribute__ ((unused))) { - code_time (st->stat.st_atime, st->atime_nsec, keyword, xhdr); + code_time (get_stat_atime (&st->stat), keyword, xhdr); } static void atime_decoder (struct tar_stat_info *st, char const *arg) { - decode_time (arg, &st->stat.st_atime, &st->atime_nsec); + struct timespec ts; + if (decode_time (&ts, arg, "atime")) + set_stat_atime (&st->stat, ts); } static void @@ -833,7 +922,7 @@ static void gid_decoder (struct tar_stat_info *st, char const *arg) { uintmax_t u; - if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK) + if (decode_num (&u, arg, TYPE_MAXIMUM (gid_t), "gid")) st->stat.st_gid = u; } @@ -867,26 +956,30 @@ static void ctime_coder (struct tar_stat_info const *st, char const *keyword, struct xheader *xhdr, void *data __attribute__ ((unused))) { - code_time (st->stat.st_ctime, st->ctime_nsec, keyword, xhdr); + code_time (get_stat_ctime (&st->stat), keyword, xhdr); } static void ctime_decoder (struct tar_stat_info *st, char const *arg) { - decode_time (arg, &st->stat.st_ctime, &st->ctime_nsec); + struct timespec ts; + if (decode_time (&ts, arg, "ctime")) + set_stat_ctime (&st->stat, ts); } static void mtime_coder (struct tar_stat_info const *st, char const *keyword, struct xheader *xhdr, void *data __attribute__ ((unused))) { - code_time (st->stat.st_mtime, st->mtime_nsec, keyword, xhdr); + code_time (get_stat_mtime (&st->stat), keyword, xhdr); } static void mtime_decoder (struct tar_stat_info *st, char const *arg) { - decode_time (arg, &st->stat.st_mtime, &st->mtime_nsec); + struct timespec ts; + if (decode_time (&ts, arg, "mtime")) + set_stat_mtime (&st->stat, ts); } static void @@ -915,7 +1008,7 @@ static void size_decoder (struct tar_stat_info *st, char const *arg) { uintmax_t u; - if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK) + if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), "size")) st->archive_file_size = st->stat.st_size = u; } @@ -930,7 +1023,7 @@ static void uid_decoder (struct tar_stat_info *st, char const *arg) { uintmax_t u; - if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK) + if (decode_num (&u, arg, TYPE_MAXIMUM (uid_t), "uid")) st->stat.st_uid = u; } @@ -958,7 +1051,7 @@ static void sparse_size_decoder (struct tar_stat_info *st, char const *arg) { uintmax_t u; - if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK) + if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), "GNU.sparse.size")) st->stat.st_size = u; } @@ -974,10 +1067,10 @@ static void sparse_numblocks_decoder (struct tar_stat_info *st, char const *arg) { uintmax_t u; - if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK) + if (decode_num (&u, arg, SIZE_MAX, "GNU.sparse.numblocks")) { st->sparse_map_size = u; - st->sparse_map = calloc(st->sparse_map_size, sizeof(st->sparse_map[0])); + st->sparse_map = xcalloc (u, sizeof st->sparse_map[0]); st->sparse_map_avail = 0; } } @@ -994,7 +1087,7 @@ static void sparse_offset_decoder (struct tar_stat_info *st, char const *arg) { uintmax_t u; - if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK) + if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), "GNU.sparse.offset")) st->sparse_map[st->sparse_map_avail].offset = u; } @@ -1010,15 +1103,12 @@ static void sparse_numbytes_decoder (struct tar_stat_info *st, char const *arg) { uintmax_t u; - if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK) + if (decode_num (&u, arg, SIZE_MAX, "GNU.sparse.numbytes")) { if (st->sparse_map_avail == st->sparse_map_size) - { - st->sparse_map_size *= 2; - st->sparse_map = xrealloc (st->sparse_map, - st->sparse_map_size - * sizeof st->sparse_map[0]); - } + st->sparse_map = x2nrealloc (st->sparse_map, + &st->sparse_map_size, + sizeof st->sparse_map[0]); st->sparse_map[st->sparse_map_avail++].numbytes = u; } }