]> git.cworth.org Git - tar/blobdiff - lib/utimens.c
Imported Upstream version 1.21
[tar] / lib / utimens.c
index e12821962c6927bca06849810718a629b8c2b98c..708de10989d1e703250a841d397cb2b736c5c2f4 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <sys/stat.h>
 #include <sys/time.h>
 #include <unistd.h>
 
@@ -94,89 +95,127 @@ gl_futimens (int fd ATTRIBUTE_UNUSED,
     fsync (fd);
 #endif
 
-  /* There's currently no interface to set file timestamps with
-     nanosecond resolution, so do the best we can, discarding any
-     fractional part of the timestamp.  */
-#if HAVE_FUTIMESAT || HAVE_WORKING_UTIMES
-  struct timeval timeval[2];
-  struct timeval const *t;
-  if (timespec)
+  /* POSIX 200x added two interfaces to set file timestamps with
+     nanosecond resolution.  We provide a fallback for ENOSYS (for
+     example, compiling against Linux 2.6.25 kernel headers and glibc
+     2.7, but running on Linux 2.6.18 kernel).  */
+#if HAVE_UTIMENSAT
+  if (fd < 0)
     {
-      timeval[0].tv_sec = timespec[0].tv_sec;
-      timeval[0].tv_usec = timespec[0].tv_nsec / 1000;
-      timeval[1].tv_sec = timespec[1].tv_sec;
-      timeval[1].tv_usec = timespec[1].tv_nsec / 1000;
-      t = timeval;
+      int result = utimensat (AT_FDCWD, file, timespec, 0);
+# ifdef __linux__
+      /* Work around what might be a kernel bug:
+         http://bugzilla.redhat.com/442352
+         http://bugzilla.redhat.com/449910
+         It appears that utimensat can mistakenly return 280 rather
+         than -1 upon failure.
+         FIXME: remove in 2010 or whenever the offending kernels
+         are no longer in common use.  */
+      if (0 < result)
+        errno = ENOSYS;
+# endif
+
+      if (result == 0 || errno != ENOSYS)
+        return result;
     }
-  else
-    t = NULL;
+#endif
+#if HAVE_FUTIMENS
+  {
+    int result = futimens (fd, timespec);
+# ifdef __linux__
+    /* Work around the same bug as above.  */
+    if (0 < result)
+      errno = ENOSYS;
+# endif
+    if (result == 0 || errno != ENOSYS)
+      return result;
+  }
+#endif
 
+  /* The platform lacks an interface to set file timestamps with
+     nanosecond resolution, so do the best we can, discarding any
+     fractional part of the timestamp.  */
+  {
+#if HAVE_FUTIMESAT || HAVE_WORKING_UTIMES
+    struct timeval timeval[2];
+    struct timeval const *t;
+    if (timespec)
+      {
+       timeval[0].tv_sec = timespec[0].tv_sec;
+       timeval[0].tv_usec = timespec[0].tv_nsec / 1000;
+       timeval[1].tv_sec = timespec[1].tv_sec;
+       timeval[1].tv_usec = timespec[1].tv_nsec / 1000;
+       t = timeval;
+      }
+    else
+      t = NULL;
 
-  if (fd < 0)
-    {
+    if (fd < 0)
+      {
 # if HAVE_FUTIMESAT
-      return futimesat (AT_FDCWD, file, t);
+       return futimesat (AT_FDCWD, file, t);
 # endif
-    }
-  else
-    {
-      /* If futimesat or futimes fails here, don't try to speed things
-        up by returning right away.  glibc can incorrectly fail with
-        errno == ENOENT if /proc isn't mounted.  Also, Mandrake 10.0
-        in high security mode doesn't allow ordinary users to read
-        /proc/self, so glibc incorrectly fails with errno == EACCES.
-        If errno == EIO, EPERM, or EROFS, it's probably safe to fail
-        right away, but these cases are rare enough that they're not
-        worth optimizing, and who knows what other messed-up systems
-        are out there?  So play it safe and fall back on the code
-        below.  */
+      }
+    else
+      {
+       /* If futimesat or futimes fails here, don't try to speed things
+          up by returning right away.  glibc can incorrectly fail with
+          errno == ENOENT if /proc isn't mounted.  Also, Mandrake 10.0
+          in high security mode doesn't allow ordinary users to read
+          /proc/self, so glibc incorrectly fails with errno == EACCES.
+          If errno == EIO, EPERM, or EROFS, it's probably safe to fail
+          right away, but these cases are rare enough that they're not
+          worth optimizing, and who knows what other messed-up systems
+          are out there?  So play it safe and fall back on the code
+          below.  */
 # if HAVE_FUTIMESAT
-      if (futimesat (fd, NULL, t) == 0)
-       return 0;
+       if (futimesat (fd, NULL, t) == 0)
+         return 0;
 # elif HAVE_FUTIMES
-      if (futimes (fd, t) == 0)
-       return 0;
+       if (futimes (fd, t) == 0)
+         return 0;
 # endif
-    }
-#endif
+      }
+#endif /* HAVE_FUTIMESAT || HAVE_WORKING_UTIMES */
 
-  if (!file)
-    {
+    if (!file)
+      {
 #if ! (HAVE_FUTIMESAT || (HAVE_WORKING_UTIMES && HAVE_FUTIMES))
-      errno = ENOSYS;
+       errno = ENOSYS;
 #endif
 
-      /* Prefer EBADF to ENOSYS if both error numbers apply.  */
-      if (errno == ENOSYS)
-       {
-         int fd2 = dup (fd);
-         int dup_errno = errno;
-         if (0 <= fd2)
-           close (fd2);
-         errno = (fd2 < 0 && dup_errno == EBADF ? EBADF : ENOSYS);
-       }
-
-      return -1;
-    }
+       /* Prefer EBADF to ENOSYS if both error numbers apply.  */
+       if (errno == ENOSYS)
+         {
+           int fd2 = dup (fd);
+           int dup_errno = errno;
+           if (0 <= fd2)
+             close (fd2);
+           errno = (fd2 < 0 && dup_errno == EBADF ? EBADF : ENOSYS);
+         }
+
+       return -1;
+      }
 
 #if HAVE_WORKING_UTIMES
-  return utimes (file, t);
+    return utimes (file, t);
 #else
-  {
-    struct utimbuf utimbuf;
-    struct utimbuf const *ut;
-    if (timespec)
-      {
-       utimbuf.actime = timespec[0].tv_sec;
-       utimbuf.modtime = timespec[1].tv_sec;
-       ut = &utimbuf;
-      }
-    else
-      ut = NULL;
+    {
+      struct utimbuf utimbuf;
+      struct utimbuf const *ut;
+      if (timespec)
+       {
+         utimbuf.actime = timespec[0].tv_sec;
+         utimbuf.modtime = timespec[1].tv_sec;
+         ut = &utimbuf;
+       }
+      else
+       ut = NULL;
 
-    return utime (file, ut);
+      return utime (file, ut);
+    }
+#endif /* !HAVE_WORKING_UTIMES */
   }
-#endif
 }
 
 /* Set the access and modification time stamps of FILE to be