]> git.cworth.org Git - tar/commitdiff
Preserve timestamp of symlinks when extracting (if utimensat available) 313237
authorCarl Worth <cworth@cworth.org>
Wed, 5 Aug 2009 00:00:35 +0000 (17:00 -0700)
committerCarl Worth <cworth@cworth.org>
Wed, 5 Aug 2009 00:12:36 +0000 (17:12 -0700)
If the utimensat function is not available, then do nothing with
symlink time stamps, (which is the same as the current code).

This closes Debian bug #313237, (thanks to D Goel for reporting it).

debian/changelog
lib/utimens.c
lib/utimens.h
src/extract.c
src/misc.c

index 393a9e1a6567306073bf74a8077f9b9eeaecccec..99ce9de89089e7a03b013d72ee6967528412540a 100644 (file)
@@ -1,3 +1,9 @@
+tar (1.22-3) UNRELEASED; urgency=low
+
+  * Preserve timestamps of extracted symlinks, closes: #313237
+
+ -- Carl Worth <cworth@cworth.org>  Tue, 04 Aug 2009 17:11:18 -0700
+
 tar (1.22-2) unstable; urgency=low
 
   * Add Carl Worth as an uploader.
index 708de10989d1e703250a841d397cb2b736c5c2f4..ae8b0a6ff0ad6f7de46a54beab5f6fd0bad6f9b0 100644 (file)
@@ -72,11 +72,16 @@ struct utimbuf
    use just futimes (or equivalent) instead of utimes (or equivalent),
    and fail if on an old system without futimes (or equivalent).
    If TIMESPEC is null, set the time stamps to the current time.
+   If the file is a symlink and IS_SYMLINK is set, then the
+   time stamps of the symlink itself will be updated if
+   possible, (but if not supported by the operating system
+   then no change will occur).
    Return 0 on success, -1 (setting errno) on failure.  */
 
 int
 gl_futimens (int fd ATTRIBUTE_UNUSED,
-            char const *file, struct timespec const timespec[2])
+            char const *file, struct timespec const timespec[2],
+            int is_symlink)
 {
   /* Some Linux-based NFS clients are buggy, and mishandle time stamps
      of files in NFS file systems in some cases.  We have no
@@ -102,7 +107,8 @@ gl_futimens (int fd ATTRIBUTE_UNUSED,
 #if HAVE_UTIMENSAT
   if (fd < 0)
     {
-      int result = utimensat (AT_FDCWD, file, timespec, 0);
+      int flags = is_symlink ? AT_SYMLINK_NOFOLLOW : 0;
+      int result = utimensat (AT_FDCWD, file, timespec, flags);
 # ifdef __linux__
       /* Work around what might be a kernel bug:
          http://bugzilla.redhat.com/442352
@@ -119,6 +125,12 @@ gl_futimens (int fd ATTRIBUTE_UNUSED,
         return result;
     }
 #endif
+
+  /* Without utimensat we have no way to update a symlink rather than
+   * the target, so just return immediately. */
+  if (is_symlink)
+      return 0;
+
 #if HAVE_FUTIMENS
   {
     int result = futimens (fd, timespec);
@@ -219,9 +231,13 @@ gl_futimens (int fd ATTRIBUTE_UNUSED,
 }
 
 /* Set the access and modification time stamps of FILE to be
-   TIMESPEC[0] and TIMESPEC[1], respectively.  */
+   TIMESPEC[0] and TIMESPEC[1], respectively.
+   If the file is a symlink and is_symlink is set, then the
+   time stamps of the symlink itself will be updated if
+   possible, (but if not supported by the operating system
+   then no change will occur). */
 int
-utimens (char const *file, struct timespec const timespec[2])
+utimens (char const *file, struct timespec const timespec[2], int is_symlink)
 {
-  return gl_futimens (-1, file, timespec);
+  return gl_futimens (-1, file, timespec, is_symlink);
 }
index 169521da73ac357719dc00032aed6e38d2883450..625785c9ddaa55ee21581a5f0933f60036d47203 100644 (file)
@@ -1,3 +1,3 @@
 #include <time.h>
-int gl_futimens (int, char const *, struct timespec const [2]);
-int utimens (char const *, struct timespec const [2]);
+int gl_futimens (int, char const *, struct timespec const [2], int flags);
+int utimens (char const *, struct timespec const [2], int flags);
index 6d703980c3235b9b4244ecb825bad70460b97486..5ca192de3a90000c73bf0db898418a5955fcb1c4 100644 (file)
@@ -239,43 +239,40 @@ set_stat (char const *file_name,
          mode_t invert_permissions, enum permstatus permstatus,
          char typeflag)
 {
-  if (typeflag != SYMTYPE)
+  /* We do the utime before the chmod because some versions of utime are
+     broken and trash the modes of the file.  */
+
+  if (! touch_option && permstatus != INTERDIR_PERMSTATUS)
     {
-      /* We do the utime before the chmod because some versions of utime are
-        broken and trash the modes of the file.  */
+      /* We set the accessed time to `now', which is really the time we
+        started extracting files, unless incremental_option is used, in
+        which case .st_atime is used.  */
 
-      if (! touch_option && permstatus != INTERDIR_PERMSTATUS)
-       {
-         /* We set the accessed time to `now', which is really the time we
-            started extracting files, unless incremental_option is used, in
-            which case .st_atime is used.  */
-
-         /* FIXME: incremental_option should set ctime too, but how?  */
-
-         struct timespec ts[2];
-         if (incremental_option)
-           ts[0] = st->atime;
-         else
-           ts[0] = start_time;
-         ts[1] = st->mtime;
-
-         if (utimens (file_name, ts) != 0)
-           utime_error (file_name);
-         else
-           {
-             check_time (file_name, ts[0]);
-             check_time (file_name, ts[1]);
-           }
-       }
+      /* FIXME: incremental_option should set ctime too, but how?  */
 
-      /* Some systems allow non-root users to give files away.  Once this
-        done, it is not possible anymore to change file permissions.
-        However, setting file permissions now would be incorrect, since
-        they would apply to the wrong user, and there would be a race
-        condition.  So, don't use systems that allow non-root users to
-        give files away.  */
+      struct timespec ts[2];
+      if (incremental_option)
+       ts[0] = st->atime;
+      else
+       ts[0] = start_time;
+      ts[1] = st->mtime;
+
+      if (utimens (file_name, ts, typeflag == SYMTYPE) != 0)
+       utime_error (file_name);
+      else
+      {
+       check_time (file_name, ts[0]);
+       check_time (file_name, ts[1]);
+      }
     }
 
+  /* Some systems allow non-root users to give files away.  Once this
+     done, it is not possible anymore to change file permissions.
+     However, setting file permissions now would be incorrect, since
+     they would apply to the wrong user, and there would be a race
+     condition.  So, don't use systems that allow non-root users to
+     give files away.  */
+
   if (0 < same_owner_option && permstatus != INTERDIR_PERMSTATUS)
     {
       /* When lchown exists, it should be used to change the attributes of
index 951449eb0c0b035246b02fc50f27d7a7c7e1a49f..d8255f7f3ff4433cc88f8b28ccc87de36ebb9516 100644 (file)
@@ -516,7 +516,7 @@ set_file_atime (int fd, char const *file, struct timespec const timespec[2])
     }
 #endif
 
-  return gl_futimens (fd, file, timespec);
+  return gl_futimens (fd, file, timespec, 0);
 }
 
 /* A description of a working directory.  */