Blob Blame History Raw
diff --git a/gnu/stat-time.h b/gnu/stat-time.h
index 1dc4098..7b8428e 100644
--- a/gnu/stat-time.h
+++ b/gnu/stat-time.h
@@ -144,7 +144,7 @@ get_stat_mtime (struct stat const *st)
 }
 
 /* Return *ST's birth time, if available; otherwise return a value
-   with negative tv_nsec.  */
+   with tv_sec and tv_nsec both equal to -1.  */
 static inline struct timespec
 get_stat_birthtime (struct stat const *st)
 {
@@ -163,7 +163,7 @@ get_stat_birthtime (struct stat const *st)
   t.tv_sec = st->st_ctime;
   t.tv_nsec = 0;
 #else
-  /* Birth time is not supported.  Set tv_sec to avoid undefined behavior.  */
+  /* Birth time is not supported.  */
   t.tv_sec = -1;
   t.tv_nsec = -1;
   /* Avoid a "parameter unused" warning.  */
@@ -177,10 +177,12 @@ get_stat_birthtime (struct stat const *st)
      using zero.  Attempt to work around this problem.  Alas, this can
      report failure even for valid time stamps.  Also, NetBSD
      sometimes returns junk in the birth time fields; work around this
-     bug if it it is detected.  There's no need to detect negative
-     tv_nsec junk as negative tv_nsec already indicates an error.  */
-  if (t.tv_sec == 0 || 1000000000 <= t.tv_nsec)
-    t.tv_nsec = -1;
+     bug if it is detected.  */
+  if (! (t.tv_sec && 0 <= t.tv_nsec && t.tv_nsec < 1000000000))
+    {
+      t.tv_sec = -1;
+      t.tv_nsec = -1;
+    }
 #endif
 
   return t;
diff --git a/src/extract.c b/src/extract.c
index 340beea..3afb95d 100644
--- a/src/extract.c
+++ b/src/extract.c
@@ -119,12 +119,15 @@ struct delayed_link
     /* The next delayed link in the list.  */
     struct delayed_link *next;
 
-    /* The device, inode number and ctime of the placeholder.  Use
-       ctime, not mtime, to make false matches less likely if some
-       other process removes the placeholder.  */
+    /* The device, inode number and birthtime of the placeholder.
+       birthtime.tv_nsec is negative if the birthtime is not available.
+       Don't use mtime as this would allow for false matches if some
+       other process removes the placeholder.  Don't use ctime as
+       this would cause race conditions and other screwups, e.g.,
+       when restoring hard-linked symlinks.  */
     dev_t dev;
     ino_t ino;
-    struct timespec ctime;
+    struct timespec birthtime;
 
     /* True if the link is symbolic.  */
     bool is_symlink;
@@ -1200,7 +1203,7 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made)
       delayed_link_head = p;
       p->dev = st.st_dev;
       p->ino = st.st_ino;
-      p->ctime = get_stat_ctime (&st);
+      p->birthtime = get_stat_birthtime (&st);
       p->is_symlink = is_symlink;
       if (is_symlink)
 	{
@@ -1265,7 +1268,8 @@ extract_link (char *file_name, int typeflag)
 	      if (ds->change_dir == chdir_current
 		  && ds->dev == st1.st_dev
 		  && ds->ino == st1.st_ino
-		  && timespec_cmp (ds->ctime, get_stat_ctime (&st1)) == 0)
+		  && (timespec_cmp (ds->birthtime, get_stat_birthtime (&st1))
+		      == 0))
 		{
 		  struct string_list *p =  xmalloc (offsetof (struct string_list, string)
 						    + strlen (file_name) + 1);
@@ -1638,7 +1642,7 @@ apply_delayed_links (void)
 	  if (fstatat (chdir_fd, source, &st, AT_SYMLINK_NOFOLLOW) == 0
 	      && st.st_dev == ds->dev
 	      && st.st_ino == ds->ino
-	      && timespec_cmp (get_stat_ctime (&st), ds->ctime) == 0)
+	      && timespec_cmp (get_stat_birthtime (&st), ds->birthtime) == 0)
 	    {
 	      /* Unlink the placeholder, then create a hard link if possible,
 		 a symbolic link otherwise.  */