md5, sha1, sha256, sha512: fix compile error in c++ mode
[gnulib.git] / lib / utimensat.c
index 03e65bf..f3a6651 100644 (file)
@@ -1,5 +1,5 @@
 /* Set the access and modification time of a file relative to directory fd.
-   Copyright (C) 2009 Free Software Foundation, Inc.
+   Copyright (C) 2009-2013 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -48,29 +48,62 @@ int
 rpl_utimensat (int fd, char const *file, struct timespec const times[2],
                int flag)
 {
+# if defined __linux__ || defined __sun
+  struct timespec ts[2];
+# endif
+
   /* See comments in utimens.c for details.  */
   static int utimensat_works_really; /* 0 = unknown, 1 = yes, -1 = no.  */
-  static int utimensat_ctime_really; /* 0 = unknown, 1 = yes, -1 = no.  */
   if (0 <= utimensat_works_really)
     {
       int result;
-      struct stat st1;
-      struct stat st2;
-      struct timespec ts[2];
-      /* Linux kernel 2.6.32 has a bug where mtime of UTIME_OMIT fails
-         to change ctime.  */
-      if (utimensat_ctime_really <= 0 && times
-          && times[0].tv_nsec != UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT)
+# if defined __linux__ || defined __sun
+      struct stat st;
+      /* As recently as Linux kernel 2.6.32 (Dec 2009), several file
+         systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT,
+         but work if both times are either explicitly specified or
+         UTIME_NOW.  Work around it with a preparatory [l]stat prior
+         to calling utimensat; fortunately, there is not much timing
+         impact due to the extra syscall even on file systems where
+         UTIME_OMIT would have worked.
+
+         The same bug occurs in Solaris 11.1 (Apr 2013).
+
+         FIXME: Simplify this for Linux in 2016 and for Solaris in
+         2024, when file system bugs are no longer common.  */
+      if (times && (times[0].tv_nsec == UTIME_OMIT
+                    || times[1].tv_nsec == UTIME_OMIT))
         {
-          if (fstatat (fd, file, &st1, flag))
+          if (fstatat (fd, file, &st, flag))
             return -1;
-          if (utimensat_ctime_really < 0)
-            {
-              ts[0] = times[0];
-              ts[1] = get_stat_mtime (&st1);
-              times = ts;
-            }
+          if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT)
+            return 0;
+          if (times[0].tv_nsec == UTIME_OMIT)
+            ts[0] = get_stat_atime (&st);
+          else
+            ts[0] = times[0];
+          if (times[1].tv_nsec == UTIME_OMIT)
+            ts[1] = get_stat_mtime (&st);
+          else
+            ts[1] = times[1];
+          times = ts;
+        }
+#  ifdef __hppa__
+      /* Linux kernel 2.6.22.19 on hppa does not reject invalid tv_nsec
+         values.  */
+      else if (times
+               && ((times[0].tv_nsec != UTIME_NOW
+                    && ! (0 <= times[0].tv_nsec
+                          && times[0].tv_nsec < TIMESPEC_RESOLUTION))
+                   || (times[1].tv_nsec != UTIME_NOW
+                       && ! (0 <= times[1].tv_nsec
+                             && times[1].tv_nsec < TIMESPEC_RESOLUTION))))
+        {
+          errno = EINVAL;
+          return -1;
         }
+#  endif
+# endif
       result = utimensat (fd, file, times, flag);
       /* Linux kernel 2.6.25 has a bug where it returns EINVAL for
          UTIME_NOW or UTIME_OMIT with non-zero tv_sec, which
@@ -82,37 +115,6 @@ rpl_utimensat (int fd, char const *file, struct timespec const times[2],
       if (result == 0 || (errno != ENOSYS && errno != EINVAL))
         {
           utimensat_works_really = 1;
-          if (result == 0 && utimensat_ctime_really == 0 && times
-              && times[0].tv_nsec != UTIME_OMIT
-              && times[1].tv_nsec == UTIME_OMIT)
-            {
-              /* Perform a followup [l]stat.  See detect_ctime_bug in
-                 utimens.c for more details.  */
-              struct timespec now;
-              if (fstatat (fd, file, &st2, flag))
-                return -1;
-              if (st1.st_ctime != st2.st_ctime
-                  || get_stat_ctime_ns (&st1) != get_stat_ctime_ns (&st2))
-                {
-                  utimensat_ctime_really = 1;
-                  return result;
-                }
-              if (times[0].tv_nsec == UTIME_NOW)
-                now = get_stat_atime (&st2);
-              else
-                gettime (&now);
-              if (now.tv_sec < st2.st_ctime
-                  || 2 < now.tv_sec - st2.st_ctime
-                  || (get_stat_ctime_ns (&st2)
-                      && now.tv_sec - st2.st_ctime < 2
-                      && (20000000 < (1000000000 * (now.tv_sec - st2.st_ctime)
-                                      + now.tv_nsec
-                                      - get_stat_ctime_ns (&st2)))))
-                utimensat_ctime_really = -1;
-              ts[0] = times[0];
-              ts[1] = get_stat_mtime (&st2);
-              result = utimensat (fd, file, ts, flag);
-            }
           return result;
         }
     }