Browse Source

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.

Paul Eggert 19 years ago
parent
commit
3209329337
11 changed files with 207 additions and 115 deletions
  1. 39 0
      ChangeLog
  2. 9 1
      src/common.h
  3. 1 1
      src/compare.c
  4. 11 12
      src/create.c
  5. 74 47
      src/extract.c
  6. 13 20
      src/list.c
  7. 21 0
      src/misc.c
  8. 21 7
      src/system.c
  9. 6 0
      src/tar.h
  10. 4 2
      src/update.c
  11. 8 25
      src/xheader.c

+ 39 - 0
ChangeLog

@@ -1,3 +1,42 @@
+2005-09-16  Paul Eggert  <[email protected]>
+
+	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  <[email protected]>
 2005-09-16  Paul Eggert  <[email protected]>
 
 
 	Merge changes from gnulib for file system sub-second time stamps.
 	Merge changes from gnulib for file system sub-second time stamps.

+ 9 - 1
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
 /* Return true if the struct stat ST's M time is less than
    newer_mtime_option.  */
    newer_mtime_option.  */
 #define OLDER_STAT_TIME(st, m) \
 #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.  */
 /* Zero if there is no recursion, otherwise FNM_LEADING_DIR.  */
 GLOBAL int recursion_option;
 GLOBAL int recursion_option;
@@ -490,6 +494,10 @@ char *quote_copy_string (const char *);
 int unquote_string (char *);
 int unquote_string (char *);
 
 
 void code_ns_fraction (int, 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 *);
 size_t dot_dot_prefix_len (char const *);
 
 

+ 1 - 1
src/compare.c

@@ -224,7 +224,7 @@ diff_file (void)
       if (!sys_compare_gid (&stat_data, &current_stat_info.stat))
       if (!sys_compare_gid (&stat_data, &current_stat_info.stat))
 	report_difference (&current_stat_info, _("Gid differs"));
 	report_difference (&current_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 (&current_stat_info, _("Mod time differs"));
 	report_difference (&current_stat_info, _("Mod time differs"));
       if (current_header->header.typeflag != GNUTYPE_SPARSE &&
       if (current_header->header.typeflag != GNUTYPE_SPARSE &&
 	  stat_data.st_size != current_stat_info.stat.st_size)
 	  stat_data.st_size != current_stat_info.stat.st_size)

+ 11 - 12
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
 /* A file is considered dumpable if it is sparse and both --sparse and --totals
    are specified.
    are specified.
    Otherwise, it is dumpable unless any of the following conditions occur:
    Otherwise, it is dumpable unless any of the following conditions occur:
-   
+
    a) it is empty *and* world-readable, or
    a) it is empty *and* world-readable, or
    b) current archive is /dev/null */
    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 (archive_format == POSIX_FORMAT)
       {
       {
 	if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec
 	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)
   else if (incremental_option)
     if (archive_format == OLDGNU_FORMAT || archive_format == GNU_FORMAT)
     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;
   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);
 	  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"),
 	  WARN ((0, 0, _("%s: file changed as we read it"),
 		 quotearg_colon (st->orig_file_name)));
 		 quotearg_colon (st->orig_file_name)));
@@ -1408,9 +1407,9 @@ dump_file0 (struct tar_stat_info *st, char *p,
       return;
       return;
     }
     }
   st->archive_file_size = st->stat.st_size;
   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
 #ifdef S_ISHIDDEN
   if (S_ISHIDDEN (st->stat.st_mode))
   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))
   if (!(incremental_option && !is_individual_file (p))
       && !S_ISDIR (st->stat.st_mode)
       && !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)
       if (!incremental_option && verbose_option)
 	WARN ((0, 0, _("%s: file is unchanged; not dumped"),
 	WARN ((0, 0, _("%s: file is unchanged; not dumped"),

+ 74 - 47
src/extract.c

@@ -57,7 +57,13 @@ enum permstatus
 struct delayed_set_stat
 struct delayed_set_stat
   {
   {
     struct delayed_set_stat *next;
     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;
     size_t file_name_len;
     mode_t invert_permissions;
     mode_t invert_permissions;
     enum permstatus permstatus;
     enum permstatus permstatus;
@@ -76,7 +82,7 @@ struct delayed_link
     /* The device, inode number and last-modified time of the placeholder.  */
     /* The device, inode number and last-modified time of the placeholder.  */
     dev_t dev;
     dev_t dev;
     ino_t ino;
     ino_t ino;
-    time_t mtime;
+    struct timespec mtime;
 
 
     /* True if the link is symbolic.  */
     /* True if the link is symbolic.  */
     bool is_symlink;
     bool is_symlink;
@@ -206,23 +212,23 @@ check_time (char const *file_name, struct timespec t)
       gettime (&now);
       gettime (&now);
       if (timespec_cmp (now, t) < 0)
       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
 /* 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
    If CUR_INFO is nonzero, *CUR_INFO is the
    file's currernt status.
    file's currernt status.
    If not restoring permissions, invert the
    If not restoring permissions, invert the
@@ -237,7 +243,7 @@ check_time (char const *file_name, struct timespec t)
 
 
 static void
 static void
 set_stat (char const *file_name,
 set_stat (char const *file_name,
-	  struct stat const *stat_info,
+	  struct tar_stat_info const *st,
 	  struct stat const *cur_info,
 	  struct stat const *cur_info,
 	  mode_t invert_permissions, enum permstatus permstatus,
 	  mode_t invert_permissions, enum permstatus permstatus,
 	  char typeflag)
 	  char typeflag)
@@ -256,8 +262,8 @@ set_stat (char const *file_name,
 	  /* FIXME: incremental_option should set ctime too, but how?  */
 	  /* FIXME: incremental_option should set ctime too, but how?  */
 
 
 	  struct timespec ts[2];
 	  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)
 	  if (utimens (file_name, ts) != 0)
 	    utime_error (file_name);
 	    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
 	 done, it is not possible anymore to change file permissions, so we
 	 have to set permissions prior to possibly giving files away.  */
 	 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);
 		invert_permissions, permstatus, typeflag);
     }
     }
 
 
@@ -286,48 +292,54 @@ set_stat (char const *file_name,
       if (typeflag == SYMTYPE)
       if (typeflag == SYMTYPE)
 	{
 	{
 #if HAVE_LCHOWN
 #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,
 	    chown_error_details (file_name,
-				 stat_info->st_uid, stat_info->st_gid);
+				 st->stat.st_uid, st->stat.st_gid);
 #endif
 #endif
 	}
 	}
       else
       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,
 	    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
 	  /* On a few systems, and in particular, those allowing to give files
 	     away, changing the owner or group destroys the suid or sgid bits.
 	     away, changing the owner or group destroys the suid or sgid bits.
 	     So let's attempt setting these bits once more.  */
 	     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);
 		      invert_permissions, permstatus, typeflag);
 	}
 	}
     }
     }
 }
 }
 
 
 /* Remember to restore stat attributes (owner, group, mode and times)
 /* 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.
    once we stop extracting files into that directory.
    If not restoring permissions, remember to invert the
    If not restoring permissions, remember to invert the
    INVERT_PERMISSIONS bits from the file's current permissions.
    INVERT_PERMISSIONS bits from the file's current permissions.
    PERMSTATUS specifies the status of the file's permissions.  */
    PERMSTATUS specifies the status of the file's permissions.  */
 static void
 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)
 		mode_t invert_permissions, enum permstatus permstatus)
 {
 {
   size_t file_name_len = strlen (file_name);
   size_t file_name_len = strlen (file_name);
   struct delayed_set_stat *data =
   struct delayed_set_stat *data =
     xmalloc (offsetof (struct delayed_set_stat, file_name)
     xmalloc (offsetof (struct delayed_set_stat, file_name)
 	     + file_name_len + 1);
 	     + 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;
   data->file_name_len = file_name_len;
-  strcpy (data->file_name, file_name);
   data->invert_permissions = invert_permissions;
   data->invert_permissions = invert_permissions;
   data->permstatus = permstatus;
   data->permstatus = permstatus;
   data->after_links = 0;
   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;
   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
       if (st.st_dev == dir_stat_info->st_dev
 	  && st.st_ino == dir_stat_info->st_ino)
 	  && 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 =
 	  data->invert_permissions =
 	    (MODE_RWX & (current_stat_info.stat.st_mode ^ st.st_mode));
 	    (MODE_RWX & (current_stat_info.stat.st_mode ^ st.st_mode));
 	  data->permstatus = ARCHIVED_PERMSTATUS;
 	  data->permstatus = ARCHIVED_PERMSTATUS;
@@ -408,7 +426,7 @@ make_directories (char *file_name)
 	     invert_permissions is zero, because
 	     invert_permissions is zero, because
 	     repair_delayed_set_stat may need to update the struct.  */
 	     repair_delayed_set_stat may need to update the struct.  */
 	  delay_set_stat (file_name,
 	  delay_set_stat (file_name,
-			  &current_stat_info.stat /* ignored */,
+			  &current_stat_info /* ignored */,
 			  invert_permissions, INTERDIR_PERMSTATUS);
 			  invert_permissions, INTERDIR_PERMSTATUS);
 
 
 	  print_for_mkdir (file_name, cursor - file_name, mode);
 	  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;
       return errno != ENOENT;
     }
     }
   if (!S_ISDIR (st.st_mode)
   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;
       return true;
     }
     }
@@ -551,8 +569,7 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links)
 	      stat_error (data->file_name);
 	      stat_error (data->file_name);
 	      skip_this_one = 1;
 	      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,
 	      ERROR ((0, 0,
 		      _("%s: Directory renamed before its status could be extracted"),
 		      _("%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)
       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;
       delayed_set_stat_head = data->next;
       free (data);
       free (data);
@@ -627,7 +652,7 @@ extract_dir (char *file_name, int typeflag)
   if (status == 0
   if (status == 0
       || old_files_option == DEFAULT_OLD_FILES
       || old_files_option == DEFAULT_OLD_FILES
       || old_files_option == OVERWRITE_OLD_FILES)
       || old_files_option == OVERWRITE_OLD_FILES)
-    delay_set_stat (file_name, &current_stat_info.stat,
+    delay_set_stat (file_name, &current_stat_info,
 		    MODE_RWX & (mode ^ current_stat_info.stat.st_mode),
 		    MODE_RWX & (mode ^ current_stat_info.stat.st_mode),
 		    (status == 0
 		    (status == 0
 		     ? ARCHIVED_PERMSTATUS
 		     ? ARCHIVED_PERMSTATUS
@@ -771,7 +796,7 @@ extract_file (char *file_name, int typeflag)
   if (to_command_option)
   if (to_command_option)
     sys_wait_command ();
     sys_wait_command ();
   else
   else
-    set_stat (file_name, &current_stat_info.stat, 0, 0,
+    set_stat (file_name, &current_stat_info, NULL, 0,
 	      (old_files_option == OVERWRITE_OLD_FILES ?
 	      (old_files_option == OVERWRITE_OLD_FILES ?
 	       UNKNOWN_PERMSTATUS : ARCHIVED_PERMSTATUS),
 	       UNKNOWN_PERMSTATUS : ARCHIVED_PERMSTATUS),
 	      typeflag);
 	      typeflag);
@@ -815,7 +840,7 @@ create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made)
       delayed_link_head = p;
       delayed_link_head = p;
       p->dev = st.st_dev;
       p->dev = st.st_dev;
       p->ino = st.st_ino;
       p->ino = st.st_ino;
-      p->mtime = st.st_mtime;
+      p->mtime = get_stat_mtime (&st);
       p->is_symlink = is_symlink;
       p->is_symlink = is_symlink;
       if (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);
 		stat_error (h->file_name);
 	      else
 	      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);
 	  while ((h = h->next) && ! h->after_links);
@@ -879,7 +904,7 @@ extract_link (char *file_name, int typeflag)
 	    for (; ds; ds = ds->next)
 	    for (; ds; ds = ds->next)
 	      if (ds->dev == st1.st_dev
 	      if (ds->dev == st1.st_dev
 		  && ds->ino == st1.st_ino
 		  && 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)
 		  struct string_list *p =  xmalloc (offsetof (struct string_list, string)
 						    + strlen (file_name) + 1);
 						    + strlen (file_name) + 1);
@@ -926,7 +951,7 @@ extract_symlink (char *file_name, int typeflag)
       break;
       break;
 
 
   if (status == 0)
   if (status == 0)
-    set_stat (file_name, &current_stat_info.stat, 0, 0, 0, SYMTYPE);
+    set_stat (file_name, &current_stat_info, NULL, 0, 0, SYMTYPE);
   else
   else
     symlink_error (current_stat_info.link_name, file_name);
     symlink_error (current_stat_info.link_name, file_name);
   return status;
   return status;
@@ -958,7 +983,8 @@ extract_node (char *file_name, int typeflag)
   if (status != 0)
   if (status != 0)
     mknod_error (file_name);
     mknod_error (file_name);
   else
   else
-    set_stat (file_name, &current_stat_info.stat, 0, 0, ARCHIVED_PERMSTATUS, typeflag);
+    set_stat (file_name, &current_stat_info, NULL, 0,
+	      ARCHIVED_PERMSTATUS, typeflag);
   return status;
   return status;
 }
 }
 #endif
 #endif
@@ -975,7 +1001,7 @@ extract_fifo (char *file_name, int typeflag)
       break;
       break;
 
 
   if (status == 0)
   if (status == 0)
-    set_stat (file_name, &current_stat_info.stat, NULL, 0,
+    set_stat (file_name, &current_stat_info, NULL, 0,
 	      ARCHIVED_PERMSTATUS, typeflag);
 	      ARCHIVED_PERMSTATUS, typeflag);
   else
   else
     mkfifo_error (file_name);
     mkfifo_error (file_name);
@@ -1217,7 +1243,7 @@ apply_delayed_links (void)
 	  if (lstat (source, &st) == 0
 	  if (lstat (source, &st) == 0
 	      && st.st_dev == ds->dev
 	      && st.st_dev == ds->dev
 	      && st.st_ino == ds->ino
 	      && 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,
 	      /* Unlink the placeholder, then create a hard link if possible,
 		 a symbolic link otherwise.  */
 		 a symbolic link otherwise.  */
@@ -1234,10 +1260,11 @@ apply_delayed_links (void)
 		symlink_error (ds->target, source);
 		symlink_error (ds->target, source);
 	      else
 	      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;
 		  valid_source = source;
-		  st.st_uid = ds->uid;
-		  st.st_gid = ds->gid;
-		  set_stat (source, &st, 0, 0, 0, SYMTYPE);
 		}
 		}
 	    }
 	    }
 	}
 	}

+ 13 - 20
src/list.c

@@ -99,8 +99,8 @@ read_and (void (*do_something) (void))
 		      /* FIXME: Grab fractional time stamps from
 		      /* FIXME: Grab fractional time stamps from
 			 extended header.  */
 			 extended header.  */
 		      mtime.tv_nsec = 0,
 		      mtime.tv_nsec = 0,
-		      set_stat_mtime (&current_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))
 	      || excluded_name (current_stat_info.file_name))
 	    {
 	    {
 	      switch (current_header->header.typeflag)
 	      switch (current_header->header.typeflag)
@@ -243,7 +243,7 @@ list_archive (void)
 	}
 	}
       if (multi_volume_option)
       if (multi_volume_option)
 	assign_string (&save_name, 0);
 	assign_string (&save_name, 0);
-      
+
       return;
       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_pointer, int do_user_group)
 {
 {
   enum archive_format format;
   enum archive_format format;
-  struct timespec atime;
-  struct timespec ctime;
-  struct timespec mtime;
 
 
   if (strcmp (header->header.magic, TMAGIC) == 0)
   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;
   *format_pointer = format;
 
 
   stat_info->stat.st_mode = MODE_FROM_HEADER (header->header.mode);
   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,
   assign_string (&stat_info->uname,
 		 header->header.uname[0] ? header->header.uname : NULL);
 		 header->header.uname[0] ? header->header.uname : NULL);
   assign_string (&stat_info->gname,
   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)
   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)
   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
   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)
   if (format == V7_FORMAT)
     {
     {
@@ -1094,7 +1087,7 @@ print_header (struct tar_stat_info *st, off_t block_ordinal)
 
 
       /* Time stamp.  */
       /* Time stamp.  */
 
 
-      time_stamp = tartime (get_stat_mtime (&st->stat), false);
+      time_stamp = tartime (st->mtime, false);
       time_stamp_len = strlen (time_stamp);
       time_stamp_len = strlen (time_stamp);
       if (datewidth < time_stamp_len)
       if (datewidth < time_stamp_len)
 	datewidth = time_stamp_len;
 	datewidth = time_stamp_len;

+ 21 - 0
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.  */
 /* File handling.  */
 
 

+ 21 - 7
src/system.c

@@ -611,7 +611,16 @@ dec_to_env (char *envar, uintmax_t num)
   char *numstr;
   char *numstr;
 
 
   numstr = STRINGIFY_BIGINT (num, buf);
   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
 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];
   char buf[1+1+(sizeof(unsigned long)*CHAR_BIT+2)/3];
 
 
   snprintf (buf, sizeof buf, "0%lo", num);
   snprintf (buf, sizeof buf, "0%lo", num);
-  setenv (envar, buf, 1);
+  if (setenv (envar, buf, 1) != 0)
+    xalloc_die ();
 }
 }
 
 
 static void
 static void
 str_to_env (char *envar, char const *str)
 str_to_env (char *envar, char const *str)
 {
 {
   if (str)
   if (str)
-    setenv (envar, str, 1);
+    {
+      if (setenv (envar, str, 1) != 0)
+	xalloc_die ();
+    }
   else
   else
     unsetenv (envar);
     unsetenv (envar);
 }
 }
@@ -638,7 +651,8 @@ chr_to_env (char *envar, char c)
   char buf[2];
   char buf[2];
   buf[0] = c;
   buf[0] = c;
   buf[1] = 0;
   buf[1] = 0;
-  setenv (envar, buf, 1);
+  if (setenv (envar, buf, 1) != 0)
+    xalloc_die ();
 }
 }
 
 
 static void
 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_REALNAME", st->file_name);
   str_to_env ("TAR_UNAME", st->uname);
   str_to_env ("TAR_UNAME", st->uname);
   str_to_env ("TAR_GNAME", st->gname);
   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_SIZE", st->stat.st_size);
   dec_to_env ("TAR_UID", st->stat.st_uid);
   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);

+ 6 - 0
src/tar.h

@@ -281,6 +281,12 @@ struct tar_stat_info
   char          *gname;     /* group name of owner */
   char          *gname;     /* group name of owner */
   struct stat   stat;       /* regular filesystem stat */
   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.
   off_t archive_file_size;  /* Size of file as stored in the archive.
 			       Equals stat.st_size for non-sparse files */
 			       Equals stat.st_size for non-sparse files */
 
 

+ 4 - 2
src/update.c

@@ -129,7 +129,7 @@ update_archive (void)
 	    decode_header (current_header, &current_stat_info,
 	    decode_header (current_header, &current_stat_info,
 			   &current_format, 0);
 			   &current_format, 0);
 	    archive_format = current_format;
 	    archive_format = current_format;
-	    
+
 	    if (subcommand_option == UPDATE_SUBCOMMAND
 	    if (subcommand_option == UPDATE_SUBCOMMAND
 		&& (name = name_scan (current_stat_info.file_name)) != NULL)
 		&& (name = name_scan (current_stat_info.file_name)) != NULL)
 	      {
 	      {
@@ -138,7 +138,9 @@ update_archive (void)
 		chdir_do (name->change_dir);
 		chdir_do (name->change_dir);
 		if (deref_stat (dereference_option,
 		if (deref_stat (dereference_option,
 				current_stat_info.file_name, &s) == 0
 				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);
 		  add_avoided_name (current_stat_info.file_name);
 	      }
 	      }
 
 

+ 8 - 25
src/xheader.c

@@ -57,8 +57,6 @@ static size_t global_header_count;
 
 
    However it should wait until buffer.c is finally rewritten */
    However it should wait until buffer.c is finally rewritten */
 
 
-enum { BILLION = 1000000000, LOG10_BILLION = 9 };
-
 
 
 /* Keyword options */
 /* Keyword options */
 
 
@@ -747,23 +745,8 @@ decode_string (char **string, char const *arg)
 static void
 static void
 code_time (struct timespec t, char const *keyword, struct xheader *xhdr)
 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
 static bool
@@ -900,7 +883,7 @@ static void
 atime_coder (struct tar_stat_info const *st, char const *keyword,
 atime_coder (struct tar_stat_info const *st, char const *keyword,
 	     struct xheader *xhdr, void *data __attribute__ ((unused)))
 	     struct xheader *xhdr, void *data __attribute__ ((unused)))
 {
 {
-  code_time (get_stat_atime (&st->stat), keyword, xhdr);
+  code_time (st->atime, keyword, xhdr);
 }
 }
 
 
 static void
 static void
@@ -908,7 +891,7 @@ atime_decoder (struct tar_stat_info *st, char const *arg)
 {
 {
   struct timespec ts;
   struct timespec ts;
   if (decode_time (&ts, arg, "atime"))
   if (decode_time (&ts, arg, "atime"))
-    set_stat_atime (&st->stat, ts);
+    st->atime = ts;
 }
 }
 
 
 static void
 static void
@@ -956,7 +939,7 @@ static void
 ctime_coder (struct tar_stat_info const *st, char const *keyword,
 ctime_coder (struct tar_stat_info const *st, char const *keyword,
 	     struct xheader *xhdr, void *data __attribute__ ((unused)))
 	     struct xheader *xhdr, void *data __attribute__ ((unused)))
 {
 {
-  code_time (get_stat_ctime (&st->stat), keyword, xhdr);
+  code_time (st->ctime, keyword, xhdr);
 }
 }
 
 
 static void
 static void
@@ -964,14 +947,14 @@ ctime_decoder (struct tar_stat_info *st, char const *arg)
 {
 {
   struct timespec ts;
   struct timespec ts;
   if (decode_time (&ts, arg, "ctime"))
   if (decode_time (&ts, arg, "ctime"))
-    set_stat_ctime (&st->stat, ts);
+    st->ctime = ts;
 }
 }
 
 
 static void
 static void
 mtime_coder (struct tar_stat_info const *st, char const *keyword,
 mtime_coder (struct tar_stat_info const *st, char const *keyword,
 	     struct xheader *xhdr, void *data __attribute__ ((unused)))
 	     struct xheader *xhdr, void *data __attribute__ ((unused)))
 {
 {
-  code_time (get_stat_mtime (&st->stat), keyword, xhdr);
+  code_time (st->mtime, keyword, xhdr);
 }
 }
 
 
 static void
 static void
@@ -979,7 +962,7 @@ mtime_decoder (struct tar_stat_info *st, char const *arg)
 {
 {
   struct timespec ts;
   struct timespec ts;
   if (decode_time (&ts, arg, "mtime"))
   if (decode_time (&ts, arg, "mtime"))
-    set_stat_mtime (&st->stat, ts);
+    st->mtime = ts;
 }
 }
 
 
 static void
 static void