From: Paul Eggert Date: Sat, 17 Sep 2005 06:41:55 +0000 (+0000) Subject: Don't filter time stamps through the resolution supported X-Git-Url: https://git.brokenzipper.com/gitweb?a=commitdiff_plain;h=3209329337c87a102fa12d7eb60a3df323ba56c1;p=chaz%2Ftar Don't filter time stamps through the resolution supported by struct stat; keep them to full nanosecond resolution. This affects behavior only on older hosts or file systems that have lower-resolution time stamps. * src/common.h (OLDER_STAT_TIME): Parenthesize arg. (OLDER_TAR_STAT_TIME): New macro. (code_timespec): New function. (BILLION, LOG10_BILLION, TIMESPEC_STRSIZE_BOUND): New constants. * src/compare.c (diff_file): Use full time stamp resolution. * src/create.c (start_header, dump_file0): Likewise. (start_header, dump_file0): Adjust to new structure layout. (dump_regular_finish): Simplify by using timespec_cmp. * src/extract.c (struct delayed_set_stat): Don't store stat info that we don't need, to save space. All uses changed. (struct delayed_set_stat, struct delayed_link, file_newer_p): (create_placeholder_file, extract_link, apply_delayed_links): Use full time stamp resolution. (check_time): Use code_timespec rather than rolling our own code. (set_stat, delay_set_stat): Arg now points to tar_stat_info to avoid losing time information. All callers changed. * src/list.c (read_and, decode_header, print_heaeder): Use full time stamp resolution. * src/misc.c (code_timespec): New function. * src/tar.h (struct tar_stat_info): Record atime, mtime, ctime separately, for benefit of hosts with lower resolution. * src/update.c (update_archive): Use full time stamp resolution. * src/xheader.c (code_time): Use new code_timespec function to simplify code. (atime_coder, atime_decoder, ctime_coder, ctime_decoder): (mtime_coder, mtime_decoder): Use full time stamp resolution. Report time stamps to full resolution in environment. Report memory allocation failures rather than ignoring them. * src/system.c (time_to_env): New function. (oct_to_env, str_to_env, chr_to_env): Report memory allocation failures. (stat_to_env): Report full resolution in time stamps. --- diff --git a/ChangeLog b/ChangeLog index dcc627a..b35e183 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,42 @@ +2005-09-16 Paul Eggert + + Don't filter time stamps through the resolution supported + by struct stat; keep them to full nanosecond resolution. + This affects behavior only on older hosts or file systems + that have lower-resolution time stamps. + * src/common.h (OLDER_STAT_TIME): Parenthesize arg. + (OLDER_TAR_STAT_TIME): New macro. + (code_timespec): New function. + (BILLION, LOG10_BILLION, TIMESPEC_STRSIZE_BOUND): New constants. + * src/compare.c (diff_file): Use full time stamp resolution. + * src/create.c (start_header, dump_file0): Likewise. + (start_header, dump_file0): Adjust to new structure layout. + (dump_regular_finish): Simplify by using timespec_cmp. + * src/extract.c (struct delayed_set_stat): Don't store stat info + that we don't need, to save space. All uses changed. + (struct delayed_set_stat, struct delayed_link, file_newer_p): + (create_placeholder_file, extract_link, apply_delayed_links): + Use full time stamp resolution. + (check_time): Use code_timespec rather than rolling our own code. + (set_stat, delay_set_stat): Arg now points to tar_stat_info to + avoid losing time information. All callers changed. + * src/list.c (read_and, decode_header, print_heaeder): + Use full time stamp resolution. + * src/misc.c (code_timespec): New function. + * src/tar.h (struct tar_stat_info): Record atime, mtime, ctime + separately, for benefit of hosts with lower resolution. + * src/update.c (update_archive): Use full time stamp resolution. + * src/xheader.c (code_time): Use new code_timespec function + to simplify code. + (atime_coder, atime_decoder, ctime_coder, ctime_decoder): + (mtime_coder, mtime_decoder): Use full time stamp resolution. + + Report time stamps to full resolution in environment. + Report memory allocation failures rather than ignoring them. + * src/system.c (time_to_env): New function. + (oct_to_env, str_to_env, chr_to_env): Report memory allocation failures. + (stat_to_env): Report full resolution in time stamps. + 2005-09-16 Paul Eggert Merge changes from gnulib for file system sub-second time stamps. diff --git a/src/common.h b/src/common.h index be7f35d..a4352c3 100644 --- a/src/common.h +++ b/src/common.h @@ -189,7 +189,11 @@ 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) \ - (timespec_cmp (get_stat_##m##time (&st), newer_mtime_option) < 0) + (timespec_cmp (get_stat_##m##time (&(st)), newer_mtime_option) < 0) + +/* Likewise, for struct tar_stat_info ST. */ +#define OLDER_TAR_STAT_TIME(st, m) \ + (timespec_cmp ((st).m##time, newer_mtime_option) < 0) /* Zero if there is no recursion, otherwise FNM_LEADING_DIR. */ GLOBAL int recursion_option; @@ -490,6 +494,10 @@ char *quote_copy_string (const char *); int unquote_string (char *); void code_ns_fraction (int, char *); +char const *code_timespec (struct timespec, char *); +enum { BILLION = 1000000000, LOG10_BILLION = 9 }; +enum { TIMESPEC_STRSIZE_BOUND = + UINTMAX_STRSIZE_BOUND + LOG10_BILLION + sizeof "-." - 1 }; size_t dot_dot_prefix_len (char const *); diff --git a/src/compare.c b/src/compare.c index 98a953b..02240aa 100644 --- a/src/compare.c +++ b/src/compare.c @@ -224,7 +224,7 @@ diff_file (void) if (!sys_compare_gid (&stat_data, ¤t_stat_info.stat)) report_difference (¤t_stat_info, _("Gid differs")); - if (stat_data.st_mtime != current_stat_info.stat.st_mtime) + if (timespec_cmp (get_stat_mtime (&stat_data), current_stat_info.mtime)) report_difference (¤t_stat_info, _("Mod time differs")); if (current_header->header.typeflag != GNUTYPE_SPARSE && stat_data.st_size != current_stat_info.stat.st_size) diff --git a/src/create.c b/src/create.c index 9e3922d..c5a5d71 100644 --- a/src/create.c +++ b/src/create.c @@ -353,7 +353,7 @@ string_to_chars (char const *str, char *p, size_t s) /* A file is considered dumpable if it is sparse and both --sparse and --totals are specified. Otherwise, it is dumpable unless any of the following conditions occur: - + a) it is empty *and* world-readable, or b) current archive is /dev/null */ @@ -698,7 +698,7 @@ start_header (struct tar_stat_info *st) } { - struct timespec mtime = get_stat_mtime (&st->stat); + struct timespec mtime = st->mtime; if (archive_format == POSIX_FORMAT) { if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec @@ -747,8 +747,8 @@ start_header (struct tar_stat_info *st) else if (incremental_option) if (archive_format == OLDGNU_FORMAT || archive_format == GNU_FORMAT) { - TIME_TO_CHARS (st->stat.st_atime, header->oldgnu_header.atime); - TIME_TO_CHARS (st->stat.st_ctime, header->oldgnu_header.ctime); + TIME_TO_CHARS (st->atime.tv_sec, header->oldgnu_header.atime); + TIME_TO_CHARS (st->ctime.tv_sec, header->oldgnu_header.ctime); } header->header.typeflag = archive_format == V7_FORMAT ? AREGTYPE : REGTYPE; @@ -954,9 +954,8 @@ dump_regular_finish (int fd, struct tar_stat_info *st, { stat_diag (st->orig_file_name); } - else if (final_stat.st_ctime != original_ctime.tv_sec - || (get_stat_ctime (&final_stat).tv_nsec - != original_ctime.tv_nsec)) + else if (timespec_cmp (get_stat_ctime (&final_stat), original_ctime) + != 0) { WARN ((0, 0, _("%s: file changed as we read it"), quotearg_colon (st->orig_file_name))); @@ -1408,9 +1407,9 @@ dump_file0 (struct tar_stat_info *st, char *p, return; } st->archive_file_size = st->stat.st_size; - original_ctime = get_stat_ctime (&st->stat); - restore_times[0] = get_stat_atime (&st->stat); - restore_times[1] = get_stat_mtime (&st->stat); + st->atime = restore_times[0] = get_stat_atime (&st->stat); + st->mtime = restore_times[1] = get_stat_mtime (&st->stat); + st->ctime = original_ctime = get_stat_ctime (&st->stat); #ifdef S_ISHIDDEN if (S_ISHIDDEN (st->stat.st_mode)) @@ -1433,8 +1432,8 @@ dump_file0 (struct tar_stat_info *st, char *p, if (!(incremental_option && !is_individual_file (p)) && !S_ISDIR (st->stat.st_mode) - && OLDER_STAT_TIME (st->stat, m) - && (!after_date_option || OLDER_STAT_TIME (st->stat, c))) + && OLDER_TAR_STAT_TIME (*st, m) + && (!after_date_option || OLDER_TAR_STAT_TIME (*st, c))) { if (!incremental_option && verbose_option) WARN ((0, 0, _("%s: file is unchanged; not dumped"), diff --git a/src/extract.c b/src/extract.c index 80f2f9c..d826371 100644 --- a/src/extract.c +++ b/src/extract.c @@ -57,7 +57,13 @@ enum permstatus struct delayed_set_stat { struct delayed_set_stat *next; - struct stat stat_info; + dev_t dev; + ino_t ino; + mode_t mode; + uid_t uid; + gid_t gid; + struct timespec atime; + struct timespec mtime; size_t file_name_len; mode_t invert_permissions; enum permstatus permstatus; @@ -76,7 +82,7 @@ struct delayed_link /* The device, inode number and last-modified time of the placeholder. */ dev_t dev; ino_t ino; - time_t mtime; + struct timespec mtime; /* True if the link is symbolic. */ bool is_symlink; @@ -206,23 +212,23 @@ check_time (char const *file_name, struct timespec t) gettime (&now); if (timespec_cmp (now, t) < 0) { - 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) + char buf[TIMESPEC_STRSIZE_BOUND]; + struct timespec diff; + diff.tv_sec = t.tv_sec - now.tv_sec; + diff.tv_nsec = t.tv_nsec - now.tv_nsec; + if (diff.tv_nsec < 0) { - dns += 1000000000; - ds--; + diff.tv_nsec += BILLION; + diff.tv_sec--; } - 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)); + WARN ((0, 0, _("%s: time stamp %s is %s s in the future"), + file_name, tartime (t, true), code_timespec (diff, buf))); } } } /* Restore stat attributes (owner, group, mode and times) for - FILE_NAME, using information given in *STAT_INFO. + FILE_NAME, using information given in *ST. If CUR_INFO is nonzero, *CUR_INFO is the file's currernt status. If not restoring permissions, invert the @@ -237,7 +243,7 @@ check_time (char const *file_name, struct timespec t) static void set_stat (char const *file_name, - struct stat const *stat_info, + struct tar_stat_info const *st, struct stat const *cur_info, mode_t invert_permissions, enum permstatus permstatus, char typeflag) @@ -256,8 +262,8 @@ set_stat (char const *file_name, /* FIXME: incremental_option should set ctime too, but how? */ struct timespec ts[2]; - ts[0] = incremental_option ? get_stat_atime (stat_info) : start_time; - ts[1] = get_stat_mtime (stat_info); + ts[0] = incremental_option ? st->atime : start_time; + ts[1] = st->mtime; if (utimens (file_name, ts) != 0) utime_error (file_name); @@ -272,7 +278,7 @@ set_stat (char const *file_name, done, it is not possible anymore to change file permissions, so we have to set permissions prior to possibly giving files away. */ - set_mode (file_name, stat_info, cur_info, + set_mode (file_name, &st->stat, cur_info, invert_permissions, permstatus, typeflag); } @@ -286,48 +292,54 @@ set_stat (char const *file_name, if (typeflag == SYMTYPE) { #if HAVE_LCHOWN - if (lchown (file_name, stat_info->st_uid, stat_info->st_gid) < 0) + if (lchown (file_name, st->stat.st_uid, st->stat.st_gid) < 0) chown_error_details (file_name, - stat_info->st_uid, stat_info->st_gid); + st->stat.st_uid, st->stat.st_gid); #endif } else { - if (chown (file_name, stat_info->st_uid, stat_info->st_gid) < 0) + if (chown (file_name, st->stat.st_uid, st->stat.st_gid) < 0) chown_error_details (file_name, - stat_info->st_uid, stat_info->st_gid); + st->stat.st_uid, st->stat.st_gid); /* On a few systems, and in particular, those allowing to give files away, changing the owner or group destroys the suid or sgid bits. So let's attempt setting these bits once more. */ - if (stat_info->st_mode & (S_ISUID | S_ISGID | S_ISVTX)) - set_mode (file_name, stat_info, 0, + if (st->stat.st_mode & (S_ISUID | S_ISGID | S_ISVTX)) + set_mode (file_name, &st->stat, 0, invert_permissions, permstatus, typeflag); } } } /* Remember to restore stat attributes (owner, group, mode and times) - for the directory FILE_NAME, using information given in *STAT_INFO, + for the directory FILE_NAME, using information given in *ST, once we stop extracting files into that directory. If not restoring permissions, remember to invert the INVERT_PERMISSIONS bits from the file's current permissions. PERMSTATUS specifies the status of the file's permissions. */ static void -delay_set_stat (char const *file_name, struct stat const *stat_info, +delay_set_stat (char const *file_name, struct tar_stat_info const *st, mode_t invert_permissions, enum permstatus permstatus) { size_t file_name_len = strlen (file_name); struct delayed_set_stat *data = xmalloc (offsetof (struct delayed_set_stat, file_name) + file_name_len + 1); + data->next = delayed_set_stat_head; + data->dev = st->stat.st_dev; + data->ino = st->stat.st_ino; + data->mode = st->stat.st_mode; + data->uid = st->stat.st_uid; + data->gid = st->stat.st_gid; + data->atime = st->atime; + data->mtime = st->mtime; data->file_name_len = file_name_len; - strcpy (data->file_name, file_name); data->invert_permissions = invert_permissions; data->permstatus = permstatus; data->after_links = 0; - data->stat_info = *stat_info; - data->next = delayed_set_stat_head; + strcpy (data->file_name, file_name); delayed_set_stat_head = data; } @@ -352,7 +364,13 @@ repair_delayed_set_stat (char const *dir, if (st.st_dev == dir_stat_info->st_dev && st.st_ino == dir_stat_info->st_ino) { - data->stat_info = current_stat_info.stat; + data->dev = current_stat_info.stat.st_dev; + data->ino = current_stat_info.stat.st_ino; + data->mode = current_stat_info.stat.st_mode; + data->uid = current_stat_info.stat.st_uid; + data->gid = current_stat_info.stat.st_gid; + data->atime = current_stat_info.atime; + data->mtime = current_stat_info.mtime; data->invert_permissions = (MODE_RWX & (current_stat_info.stat.st_mode ^ st.st_mode)); data->permstatus = ARCHIVED_PERMSTATUS; @@ -408,7 +426,7 @@ make_directories (char *file_name) invert_permissions is zero, because repair_delayed_set_stat may need to update the struct. */ delay_set_stat (file_name, - ¤t_stat_info.stat /* ignored */, + ¤t_stat_info /* ignored */, invert_permissions, INTERDIR_PERMSTATUS); print_for_mkdir (file_name, cursor - file_name, mode); @@ -448,7 +466,7 @@ file_newer_p (const char *file_name, struct tar_stat_info *tar_stat) return errno != ENOENT; } if (!S_ISDIR (st.st_mode) - && st.st_mtime >= tar_stat->stat.st_mtime) + && timespec_cmp (tar_stat->mtime, get_stat_mtime (&st)) <= 0) { return true; } @@ -551,8 +569,7 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links) stat_error (data->file_name); skip_this_one = 1; } - else if (! (st.st_dev == data->stat_info.st_dev - && (st.st_ino == data->stat_info.st_ino))) + else if (! (st.st_dev == data->dev && st.st_ino == data->ino)) { ERROR ((0, 0, _("%s: Directory renamed before its status could be extracted"), @@ -562,8 +579,16 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links) } if (! skip_this_one) - set_stat (data->file_name, &data->stat_info, cur_info, - data->invert_permissions, data->permstatus, DIRTYPE); + { + struct tar_stat_info st; + st.stat.st_mode = data->mode; + st.stat.st_uid = data->uid; + st.stat.st_gid = data->gid; + st.atime = data->atime; + st.mtime = data->mtime; + set_stat (data->file_name, &st, cur_info, + data->invert_permissions, data->permstatus, DIRTYPE); + } delayed_set_stat_head = data->next; free (data); @@ -627,7 +652,7 @@ extract_dir (char *file_name, int typeflag) if (status == 0 || old_files_option == DEFAULT_OLD_FILES || old_files_option == OVERWRITE_OLD_FILES) - delay_set_stat (file_name, ¤t_stat_info.stat, + delay_set_stat (file_name, ¤t_stat_info, MODE_RWX & (mode ^ current_stat_info.stat.st_mode), (status == 0 ? ARCHIVED_PERMSTATUS @@ -771,7 +796,7 @@ extract_file (char *file_name, int typeflag) if (to_command_option) sys_wait_command (); else - set_stat (file_name, ¤t_stat_info.stat, 0, 0, + set_stat (file_name, ¤t_stat_info, NULL, 0, (old_files_option == OVERWRITE_OLD_FILES ? UNKNOWN_PERMSTATUS : ARCHIVED_PERMSTATUS), typeflag); @@ -815,7 +840,7 @@ create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made) delayed_link_head = p; p->dev = st.st_dev; p->ino = st.st_ino; - p->mtime = st.st_mtime; + p->mtime = get_stat_mtime (&st); p->is_symlink = is_symlink; if (is_symlink) { @@ -842,8 +867,8 @@ create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made) stat_error (h->file_name); else { - h->stat_info.st_dev = st.st_dev; - h->stat_info.st_ino = st.st_ino; + h->dev = st.st_dev; + h->ino = st.st_ino; } } while ((h = h->next) && ! h->after_links); @@ -879,7 +904,7 @@ extract_link (char *file_name, int typeflag) for (; ds; ds = ds->next) if (ds->dev == st1.st_dev && ds->ino == st1.st_ino - && ds->mtime == st1.st_mtime) + && timespec_cmp (ds->mtime, get_stat_mtime (&st1)) == 0) { struct string_list *p = xmalloc (offsetof (struct string_list, string) + strlen (file_name) + 1); @@ -926,7 +951,7 @@ extract_symlink (char *file_name, int typeflag) break; if (status == 0) - set_stat (file_name, ¤t_stat_info.stat, 0, 0, 0, SYMTYPE); + set_stat (file_name, ¤t_stat_info, NULL, 0, 0, SYMTYPE); else symlink_error (current_stat_info.link_name, file_name); return status; @@ -958,7 +983,8 @@ extract_node (char *file_name, int typeflag) if (status != 0) mknod_error (file_name); else - set_stat (file_name, ¤t_stat_info.stat, 0, 0, ARCHIVED_PERMSTATUS, typeflag); + set_stat (file_name, ¤t_stat_info, NULL, 0, + ARCHIVED_PERMSTATUS, typeflag); return status; } #endif @@ -975,7 +1001,7 @@ extract_fifo (char *file_name, int typeflag) break; if (status == 0) - set_stat (file_name, ¤t_stat_info.stat, NULL, 0, + set_stat (file_name, ¤t_stat_info, NULL, 0, ARCHIVED_PERMSTATUS, typeflag); else mkfifo_error (file_name); @@ -1217,7 +1243,7 @@ apply_delayed_links (void) if (lstat (source, &st) == 0 && st.st_dev == ds->dev && st.st_ino == ds->ino - && st.st_mtime == ds->mtime) + && timespec_cmp (get_stat_mtime (&st), ds->mtime) == 0) { /* Unlink the placeholder, then create a hard link if possible, a symbolic link otherwise. */ @@ -1234,10 +1260,11 @@ apply_delayed_links (void) symlink_error (ds->target, source); else { + struct tar_stat_info st1; + st1.stat.st_uid = ds->uid; + st1.stat.st_gid = ds->gid; + set_stat (source, &st1, NULL, 0, 0, SYMTYPE); valid_source = source; - st.st_uid = ds->uid; - st.st_gid = ds->gid; - set_stat (source, &st, 0, 0, 0, SYMTYPE); } } } diff --git a/src/list.c b/src/list.c index 633713a..d859163 100644 --- a/src/list.c +++ b/src/list.c @@ -99,8 +99,8 @@ read_and (void (*do_something) (void)) /* FIXME: Grab fractional time stamps from extended header. */ mtime.tv_nsec = 0, - set_stat_mtime (¤t_stat_info.stat, mtime), - OLDER_STAT_TIME (current_stat_info.stat, m))) + current_stat_info.mtime = mtime, + OLDER_TAR_STAT_TIME (current_stat_info, m))) || excluded_name (current_stat_info.file_name)) { switch (current_header->header.typeflag) @@ -243,7 +243,7 @@ list_archive (void) } if (multi_volume_option) assign_string (&save_name, 0); - + return; } @@ -512,9 +512,6 @@ 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) { @@ -536,9 +533,8 @@ 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); - mtime.tv_sec = TIME_FROM_HEADER (header->header.mtime); - mtime.tv_nsec = 0; - set_stat_mtime (&stat_info->stat, mtime); + stat_info->mtime.tv_sec = TIME_FROM_HEADER (header->header.mtime); + stat_info->mtime.tv_nsec = 0; assign_string (&stat_info->uname, header->header.uname[0] ? header->header.uname : NULL); assign_string (&stat_info->gname, @@ -546,21 +542,18 @@ decode_header (union block *header, struct tar_stat_info *stat_info, if (format == OLDGNU_FORMAT && incremental_option) { - 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; + stat_info->atime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.atime); + stat_info->ctime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.ctime); + stat_info->atime.tv_nsec = stat_info->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; + stat_info->atime.tv_sec = TIME_FROM_HEADER (header->star_header.atime); + stat_info->ctime.tv_sec = TIME_FROM_HEADER (header->star_header.ctime); + stat_info->atime.tv_nsec = stat_info->ctime.tv_nsec = 0; } else - atime = ctime = start_time; - - set_stat_atime (&stat_info->stat, atime); - set_stat_ctime (&stat_info->stat, ctime); + stat_info->atime = stat_info->ctime = start_time; if (format == V7_FORMAT) { @@ -1094,7 +1087,7 @@ print_header (struct tar_stat_info *st, off_t block_ordinal) /* Time stamp. */ - time_stamp = tartime (get_stat_mtime (&st->stat), false); + time_stamp = tartime (st->mtime, false); time_stamp_len = strlen (time_stamp); if (datewidth < time_stamp_len) datewidth = time_stamp_len; diff --git a/src/misc.c b/src/misc.c index 55bedd2..7322dd2 100644 --- a/src/misc.c +++ b/src/misc.c @@ -239,6 +239,27 @@ code_ns_fraction (int ns, char *p) } } } + +char const * +code_timespec (struct timespec t, char sbuf[TIMESPEC_STRSIZE_BOUND]) +{ + time_t s = t.tv_sec; + int ns = t.tv_nsec; + 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); + return np; +} /* File handling. */ diff --git a/src/system.c b/src/system.c index b1e4eef..05313db 100644 --- a/src/system.c +++ b/src/system.c @@ -611,7 +611,16 @@ dec_to_env (char *envar, uintmax_t num) char *numstr; numstr = STRINGIFY_BIGINT (num, buf); - setenv (envar, numstr, 1); + if (setenv (envar, numstr, 1) != 0) + xalloc_die (); +} + +static void +time_to_env (char *envar, struct timespec t) +{ + char buf[TIMESPEC_STRSIZE_BOUND]; + if (setenv (envar, code_timespec (t, buf), 1) != 0) + xalloc_die (); } static void @@ -620,14 +629,18 @@ 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); + if (setenv (envar, buf, 1) != 0) + xalloc_die (); } static void str_to_env (char *envar, char const *str) { if (str) - setenv (envar, str, 1); + { + if (setenv (envar, str, 1) != 0) + xalloc_die (); + } else unsetenv (envar); } @@ -638,7 +651,8 @@ chr_to_env (char *envar, char c) char buf[2]; buf[0] = c; buf[1] = 0; - setenv (envar, buf, 1); + if (setenv (envar, buf, 1) != 0) + xalloc_die (); } static void @@ -650,9 +664,9 @@ stat_to_env (char *name, char type, struct tar_stat_info *st) str_to_env ("TAR_REALNAME", st->file_name); str_to_env ("TAR_UNAME", st->uname); str_to_env ("TAR_GNAME", st->gname); - dec_to_env ("TAR_MTIME", st->stat.st_mtime); - dec_to_env ("TAR_ATIME", st->stat.st_atime); - dec_to_env ("TAR_CTIME", st->stat.st_ctime); + time_to_env ("TAR_ATIME", st->atime); + time_to_env ("TAR_MTIME", st->mtime); + time_to_env ("TAR_CTIME", 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); diff --git a/src/tar.h b/src/tar.h index 225e721..78837be 100644 --- a/src/tar.h +++ b/src/tar.h @@ -281,6 +281,12 @@ struct tar_stat_info char *gname; /* group name of owner */ struct stat stat; /* regular filesystem stat */ + /* STAT doesn't always have access, data modification, and status + change times in a convenient form, so store them separately. */ + struct timespec atime; + struct timespec mtime; + struct timespec ctime; + 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/update.c b/src/update.c index 0db091d..f28c6dc 100644 --- a/src/update.c +++ b/src/update.c @@ -129,7 +129,7 @@ update_archive (void) decode_header (current_header, ¤t_stat_info, ¤t_format, 0); archive_format = current_format; - + if (subcommand_option == UPDATE_SUBCOMMAND && (name = name_scan (current_stat_info.file_name)) != NULL) { @@ -138,7 +138,9 @@ update_archive (void) chdir_do (name->change_dir); if (deref_stat (dereference_option, current_stat_info.file_name, &s) == 0 - && s.st_mtime <= current_stat_info.stat.st_mtime) + && (timespec_cmp (get_stat_mtime (&s), + current_stat_info.mtime) + <= 0)) add_avoided_name (current_stat_info.file_name); } diff --git a/src/xheader.c b/src/xheader.c index 48c857f..34b3c61 100644 --- a/src/xheader.c +++ b/src/xheader.c @@ -57,8 +57,6 @@ static size_t global_header_count; However it should wait until buffer.c is finally rewritten */ -enum { BILLION = 1000000000, LOG10_BILLION = 9 }; - /* Keyword options */ @@ -747,23 +745,8 @@ decode_string (char **string, char const *arg) static void code_time (struct timespec t, char const *keyword, struct xheader *xhdr) { - 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); + char buf[TIMESPEC_STRSIZE_BOUND]; + xheader_print (xhdr, keyword, code_timespec (t, buf)); } static bool @@ -900,7 +883,7 @@ static void atime_coder (struct tar_stat_info const *st, char const *keyword, struct xheader *xhdr, void *data __attribute__ ((unused))) { - code_time (get_stat_atime (&st->stat), keyword, xhdr); + code_time (st->atime, keyword, xhdr); } static void @@ -908,7 +891,7 @@ atime_decoder (struct tar_stat_info *st, char const *arg) { struct timespec ts; if (decode_time (&ts, arg, "atime")) - set_stat_atime (&st->stat, ts); + st->atime = ts; } static void @@ -956,7 +939,7 @@ static void ctime_coder (struct tar_stat_info const *st, char const *keyword, struct xheader *xhdr, void *data __attribute__ ((unused))) { - code_time (get_stat_ctime (&st->stat), keyword, xhdr); + code_time (st->ctime, keyword, xhdr); } static void @@ -964,14 +947,14 @@ ctime_decoder (struct tar_stat_info *st, char const *arg) { struct timespec ts; if (decode_time (&ts, arg, "ctime")) - set_stat_ctime (&st->stat, ts); + st->ctime = ts; } static void mtime_coder (struct tar_stat_info const *st, char const *keyword, struct xheader *xhdr, void *data __attribute__ ((unused))) { - code_time (get_stat_mtime (&st->stat), keyword, xhdr); + code_time (st->mtime, keyword, xhdr); } static void @@ -979,7 +962,7 @@ mtime_decoder (struct tar_stat_info *st, char const *arg) { struct timespec ts; if (decode_time (&ts, arg, "mtime")) - set_stat_mtime (&st->stat, ts); + st->mtime = ts; } static void