Make it possible to compile strerror.c separately, unconditionally.
[gnulib.git] / lib / utimens.c
index b60ec87..71bc510 100644 (file)
@@ -1,4 +1,7 @@
-/* Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+/* Set file access and modification times.
+
+   Copyright (C) 2003, 2004, 2005, 2006, 2007 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 the
 
 /* derived from a function in touch.c */
 
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
+#include <config.h>
 
 #include "utimens.h"
 
 #include <errno.h>
 #include <fcntl.h>
+#include <sys/time.h>
+#include <unistd.h>
 
 #if HAVE_UTIME_H
 # include <utime.h>
@@ -51,8 +54,10 @@ struct utimbuf
 # endif
 #endif
 
-#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
-# define __attribute__(x)
+#ifndef __attribute__
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
+#  define __attribute__(x)
+# endif
 #endif
 
 #ifndef ATTRIBUTE_UNUSED
@@ -73,6 +78,23 @@ int
 futimens (int fd ATTRIBUTE_UNUSED,
          char const *file, struct timespec const timespec[2])
 {
+  /* Some Linux-based NFS clients are buggy, and mishandle time stamps
+     of files in NFS file systems in some cases.  We have no
+     configure-time test for this, but please see
+     <http://bugs.gentoo.org/show_bug.cgi?id=132673> for references to
+     some of the problems with Linux 2.6.16.  If this affects you,
+     compile with -DHAVE_BUGGY_NFS_TIME_STAMPS; this is reported to
+     help in some cases, albeit at a cost in performance.  But you
+     really should upgrade your kernel to a fixed version, since the
+     problem affects many applications.  */
+
+#if HAVE_BUGGY_NFS_TIME_STAMPS
+  if (fd < 0)
+    sync ();
+  else
+    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.  */
@@ -90,40 +112,57 @@ futimens (int fd ATTRIBUTE_UNUSED,
   else
     t = NULL;
 
+
+  if (fd < 0)
+    {
 # if HAVE_FUTIMESAT
-  return fd < 0 ? futimesat (AT_FDCWD, file, t) : futimesat (fd, NULL, t);
-# elif HAVE_FUTIMES
-  if (0 <= fd)
+      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.  */
+# if HAVE_FUTIMESAT
+      if (futimesat (fd, NULL, t) == 0)
+       return 0;
+# elif HAVE_FUTIMES
       if (futimes (fd, t) == 0)
        return 0;
-
-      /* On GNU/Linux without the futimes syscall and without /proc
-        mounted, glibc futimes fails with errno == ENOENT.  Fall back
-        on utimes if we get a weird error number like that.  */
-      switch (errno)
-       {
-       case EACCES:
-       case EIO:
-       case EPERM:
-       case EROFS:
-         return -1;
-       }
-    }
 # endif
+    }
 #endif
 
-#if ! HAVE_FUTIMES_AT
-
   if (!file)
     {
+#if ! (HAVE_FUTIMESAT || (HAVE_WORKING_UTIMES && HAVE_FUTIMES))
       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;
     }
 
-# if HAVE_WORKING_UTIMES
+#if HAVE_WORKING_UTIMES
   return utimes (file, t);
-# else
+#else
   {
     struct utimbuf utimbuf;
     struct utimbuf const *ut;
@@ -138,8 +177,6 @@ futimens (int fd ATTRIBUTE_UNUSED,
 
     return utime (file, ut);
   }
-# endif
-
 #endif
 }