changeset 12194:16c7c4fa9754

utimensat: work around Solaris 9 bug utimes("file/",times) mistakenly succeeds. This commit doesn't fix utimes, but does make utimensat be careful before calling utimes. The test is now enhanced to test trailing slashes and directories. Meanwhile, cygwin 1.5 stat() on a directory changes atime (it does a readdir under the hood to populate st_nlink), so only mtime of a directory is reliable enough for testing. Cygwin 1.7 no longer has this problem, because it no longer wastes time on st_nlink. * lib/utimens.c (fdutimens, lutimens): Force a stat if platform has trailing slash bugs. * tests/test-lutimens.h (test_lutimens): Enhance test. * tests/test-utimens.h (test_utimens): Likewise. * doc/posix-functions/utime.texi (utime): Document the bug. * doc/posix-functions/utimes.texi (utimes): Likewise. * doc/posix-functions/utimensat.texi (utimensat): Likewise. * doc/glibc-functions/futimesat.texi (futimesat): Likewise. * doc/glibc-functions/lutimes.texi (lutimes): Mention utimens. * doc/posix-functions/futimens.texi (futimens): Mention limitation. Signed-off-by: Eric Blake <ebb9@byu.net>
author Eric Blake <ebb9@byu.net>
date Tue, 20 Oct 2009 16:47:36 -0600
parents d1ea4269ac6f
children 531c35ae980d
files ChangeLog doc/glibc-functions/futimesat.texi doc/glibc-functions/lutimes.texi doc/posix-functions/futimens.texi doc/posix-functions/utime.texi doc/posix-functions/utimensat.texi doc/posix-functions/utimes.texi lib/utimens.c tests/test-lutimens.h tests/test-utimens.h
diffstat 10 files changed, 105 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Oct 20 06:09:29 2009 -0600
+++ b/ChangeLog	Tue Oct 20 16:47:36 2009 -0600
@@ -1,5 +1,17 @@
 2009-10-20  Eric Blake  <ebb9@byu.net>
 
+	utimensat: work around Solaris 9 bug
+	* lib/utimens.c (fdutimens, lutimens): Force a stat if platform
+	has trailing slash bugs.
+	* tests/test-lutimens.h (test_lutimens): Enhance test.
+	* tests/test-utimens.h (test_utimens): Likewise.
+	* doc/posix-functions/utime.texi (utime): Enhance documentation.
+	* doc/posix-functions/utimes.texi (utimes): Likewise.
+	* doc/posix-functions/utimensat.texi (utimensat): Likewise.
+	* doc/glibc-functions/futimesat.texi (futimesat): Likewise.
+	* doc/glibc-functions/lutimes.texi (lutimes): Likewise.
+	* doc/posix-functions/futimens.texi (futimens): Likewise.
+
 	fdutimensat: new module
 	* modules/fdutimensat: New file.
 	* lib/fdutimensat.c (fdutimensat): Likewise.
--- a/doc/glibc-functions/futimesat.texi	Tue Oct 20 06:09:29 2009 -0600
+++ b/doc/glibc-functions/futimesat.texi	Tue Oct 20 16:47:36 2009 -0600
@@ -16,6 +16,10 @@
 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 8, Cygwin 1.5.x, mingw,
 Interix 3.5, BeOS.
 @item
+On some platforms, this function mis-handles trailing slash:
+Solaris 9.
+@item
 This function cannot set full timestamp resolution.  Use
-@code{file ? utimensat(fd,file,times,0) : futimens(fd,times)} instead.
+@code{file ? utimensat(fd,file,times,0) : futimens(fd,times)}, or the
+gnulib module fdutimensat, instead.
 @end itemize
--- a/doc/glibc-functions/lutimes.texi	Tue Oct 20 06:09:29 2009 -0600
+++ b/doc/glibc-functions/lutimes.texi	Tue Oct 20 16:47:36 2009 -0600
@@ -16,7 +16,8 @@
 Solaris 10, mingw, Interix 3.5, BeOS.
 @item
 This function cannot set full timestamp resolution.  Use
-@code{utimensat(AT_FDCWD,file,times,AT_SYMLINK_NOFOLLOW)} instead.
+@code{utimensat(AT_FDCWD,file,times,AT_SYMLINK_NOFOLLOW)}, or the
+gnulib module utimens, instead.
 @item
 The mere act of using @code{lstat} modifies the access time of
 symlinks on some platforms, so @code{lutimes} can only effectively
--- a/doc/posix-functions/futimens.texi	Tue Oct 20 06:09:29 2009 -0600
+++ b/doc/posix-functions/futimens.texi	Tue Oct 20 16:47:36 2009 -0600
@@ -13,6 +13,8 @@
 glibc 2.3.6, MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX
 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 10, Cygwin 1.5.x, mingw,
 Interix 3.5, BeOS.
+However, the replacement function may end up truncating timestamps to
+less resolution than supported by the file system.
 @item
 This function returns a bogus value instead of failing with
 @code{ENOSYS} on some platforms:
@@ -29,5 +31,10 @@
 @item
 Some platforms lack the ability to change the timestamps of a file
 descriptor, so the replacement can fail with @code{ENOSYS}; the gnulib
-module @samp{utimens} provides a more reliable interface @code{gl_futimens}.
+module @samp{utimens} provides a more reliable interface @code{fdutimens}.
+@item
+The mere act of using @code{stat} modifies the access time of
+directories on some platforms, so @code{utimensat} can only
+effectively change directory modification time:
+Cygwin 1.5.x.
 @end itemize
--- a/doc/posix-functions/utime.texi	Tue Oct 20 06:09:29 2009 -0600
+++ b/doc/posix-functions/utime.texi	Tue Oct 20 16:47:36 2009 -0600
@@ -16,8 +16,12 @@
 Portability problems not fixed by Gnulib:
 @itemize
 @item
+On some platforms, this function mis-handles trailing slash:
+Solaris 9.
+@item
 This function cannot set full timestamp resolution.  Use
-@code{utimensat(AT_FDCWD,file,times,0)} instead.
+@code{utimensat(AT_FDCWD,file,times,0)}, or the gnulib module utimens,
+instead.
 @item
 On some platforms, the prototype for @code{utime} omits @code{const}
 for the second argument.  Fortunately, the argument is not modified,
--- a/doc/posix-functions/utimensat.texi	Tue Oct 20 06:09:29 2009 -0600
+++ b/doc/posix-functions/utimensat.texi	Tue Oct 20 16:47:36 2009 -0600
@@ -13,6 +13,10 @@
 glibc 2.3.6, MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX
 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 10, Cygwin 1.5.x, mingw,
 Interix 3.5, BeOS.
+However, the replacement function may end up truncating timestamps to
+less resolution than supported by the file system.  Furthermore, the
+replacement function is not safe to be used in libraries and is not
+multithread-safe.
 @item
 This function returns a bogus value instead of failing with
 @code{ENOSYS} on some platforms:
@@ -35,6 +39,11 @@
 symlinks on some platforms, so @code{utimensat} with
 @code{AT_SYMLINK_NOFOLLOW} can only effectively change modification time:
 Cygwin.
+@item
+The mere act of using @code{stat} modifies the access time of
+directories on some platforms, so @code{utimensat} can only
+effectively change directory modification time:
+Cygwin 1.5.x.
 @end itemize
 
-The gnulib module utimens provides a similar interface.
+The gnulib module fdutimensat provides a similar interface.
--- a/doc/posix-functions/utimes.texi	Tue Oct 20 06:09:29 2009 -0600
+++ b/doc/posix-functions/utimes.texi	Tue Oct 20 16:47:36 2009 -0600
@@ -16,9 +16,13 @@
 This function is missing on some platforms:
 mingw, Interix 3.5, BeOS.
 @item
+On some platforms, this function mis-handles trailing slash:
+Solaris 9.
+@item
 This function cannot set full timestamp resolution.  In particular,
 some platforms incorrectly round rather than truncate.  Use
-@code{utimensat(AT_FDCWD,file,times,0)} instead.
+@code{utimensat(AT_FDCWD,file,times,0)}, or the gnulib module utimens,
+instead.
 @item
 On some platforms, @code{utimes (file, NULL)} fails to set the
 file's timestamp to the current time:
--- a/lib/utimens.c	Tue Oct 20 06:09:29 2009 -0600
+++ b/lib/utimens.c	Tue Oct 20 16:47:36 2009 -0600
@@ -60,6 +60,12 @@
 static int utimensat_works_really;
 #endif /* HAVE_UTIMENSAT || HAVE_UTIMENSAT */
 
+/* Solaris 9 mistakenly succeeds when given a non-directory with a
+   trailing slash.  Force the use of rpl_stat for a fix.  */
+#ifndef REPLACE_FUNC_STAT_FILE
+# define REPLACE_FUNC_STAT_FILE 0
+#endif
+
 /* Validate the requested timestamps.  Return 0 if the resulting
    timespec can be used for utimensat (after possibly modifying it to
    work around bugs in utimensat).  Return 1 if the timespec needs
@@ -242,12 +248,12 @@
      nanosecond resolution, so do the best we can, discarding any
      fractional part of the timestamp.  */
 
-  if (adjustment_needed)
+  if (adjustment_needed || (REPLACE_FUNC_STAT_FILE && fd < 0))
     {
       struct stat st;
       if (fd < 0 ? stat (file, &st) : fstat (fd, &st))
         return -1;
-      if (update_timespec (&st, &ts))
+      if (ts && update_timespec (&st, &ts))
         return 0;
     }
 
@@ -401,11 +407,11 @@
      nanosecond resolution, so do the best we can, discarding any
      fractional part of the timestamp.  */
 
-  if (adjustment_needed)
+  if (adjustment_needed || REPLACE_FUNC_STAT_FILE)
     {
       if (lstat (file, &st))
         return -1;
-      if (update_timespec (&st, &ts))
+      if (ts && update_timespec (&st, &ts))
         return 0;
     }
 
@@ -429,7 +435,7 @@
 #endif /* HAVE_LUTIMES */
 
   /* Out of luck for symlinks, but we still handle regular files.  */
-  if (!adjustment_needed && lstat (file, &st))
+  if (!(adjustment_needed || REPLACE_FUNC_STAT_FILE) && lstat (file, &st))
     return -1;
   if (!S_ISLNK (st.st_mode))
     return fdutimens (file, -1, ts);
--- a/tests/test-lutimens.h	Tue Oct 20 06:09:29 2009 -0600
+++ b/tests/test-lutimens.h	Tue Oct 20 16:47:36 2009 -0600
@@ -34,6 +34,9 @@
   ASSERT (func ("no_such", NULL) == -1);
   ASSERT (errno == ENOENT);
   errno = 0;
+  ASSERT (func ("no_such/", NULL) == -1);
+  ASSERT (errno == ENOENT || errno == ENOTDIR);
+  errno = 0;
   ASSERT (func ("", NULL) == -1);
   ASSERT (errno == ENOENT);
   ASSERT (close (creat (BASE "file", 0600)) == 0);
@@ -42,6 +45,15 @@
   ASSERT (st1.st_mtime != Y2K);
   {
     struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } };
+    errno = 0;
+    ASSERT (func (BASE "file/", ts) == -1);
+    ASSERT (errno == ENOTDIR);
+    ASSERT (stat (BASE "file", &st2) == 0);
+    ASSERT (st1.st_atime == st2.st_atime);
+    ASSERT (st1.st_mtime == st2.st_mtime);
+  }
+  {
+    struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } };
     ASSERT (func (BASE "file", ts) == 0);
   }
   ASSERT (stat (BASE "file", &st1) == 0);
@@ -138,7 +150,30 @@
     ASSERT (utimecmp (BASE "link", &st1, &st2, 0) <= 0);
   }
 
+  /* Symlink to directory.  */
+  ASSERT (unlink (BASE "link") == 0);
+  ASSERT (symlink (BASE "dir", BASE "link") == 0);
+  ASSERT (mkdir (BASE "dir", 0700) == 0);
+  {
+    struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } };
+    ASSERT (func (BASE "link/", ts) == 0);
+  }
+  /* On cygwin 1.5, stat() changes atime of directories, so only check
+     mtime.  */
+  ASSERT (stat (BASE "dir", &st1) == 0);
+  ASSERT (st1.st_mtime == Y2K);
+  ASSERT (lstat (BASE "link", &st1) == 0);
+  ASSERT (st1.st_atime != Y2K);
+  ASSERT (st1.st_mtime != Y2K);
+  ASSERT (func (BASE "link", NULL) == 0);
+  ASSERT (stat (BASE "dir", &st1) == 0);
+  ASSERT (st1.st_mtime == Y2K);
+  ASSERT (lstat (BASE "link", &st1) == 0);
+  ASSERT (st1.st_atime != Y2K);
+  ASSERT (st1.st_mtime != Y2K);
+
   /* Cleanup.  */
+  ASSERT (rmdir (BASE "dir") == 0);
   ASSERT (unlink (BASE "link") == 0);
   return 0;
 }
--- a/tests/test-utimens.h	Tue Oct 20 06:09:29 2009 -0600
+++ b/tests/test-utimens.h	Tue Oct 20 16:47:36 2009 -0600
@@ -58,6 +58,9 @@
   ASSERT (func ("no_such", NULL) == -1);
   ASSERT (errno == ENOENT);
   errno = 0;
+  ASSERT (func ("no_such/", NULL) == -1);
+  ASSERT (errno == ENOENT || errno == ENOTDIR);
+  errno = 0;
   ASSERT (func ("", NULL) == -1);
   ASSERT (errno == ENOENT);
   {
@@ -72,6 +75,12 @@
     ASSERT (func (BASE "file", ts) == -1);
     ASSERT (errno == EINVAL);
   }
+  {
+    struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } };
+    errno = 0;
+    ASSERT (func (BASE "file/", ts) == -1);
+    ASSERT (errno == ENOTDIR || errno == EINVAL);
+  }
   ASSERT (stat (BASE "file", &st2) == 0);
   ASSERT (st1.st_atime == st2.st_atime);
   ASSERT (get_stat_atime_ns (&st1) == get_stat_atime_ns (&st2));
@@ -113,6 +122,9 @@
     }
   ASSERT (lstat (BASE "link", &st1) == 0);
   ASSERT (st1.st_mtime != Y2K);
+  errno = 0;
+  ASSERT (func (BASE "link/", NULL) == -1);
+  ASSERT (errno == ENOTDIR);
   {
     struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } };
     ASSERT (func (BASE "link", ts) == 0);