]> git.cworth.org Git - tar/blobdiff - lib/utimens.c
Preserve timestamp of symlinks when extracting (if utimensat available)
[tar] / lib / utimens.c
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);
 }