bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: utimensat round 4


From: Eric Blake
Subject: Re: utimensat round 4
Date: Tue, 20 Oct 2009 21:47:45 -0600
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.23) Gecko/20090812 Thunderbird/2.0.0.23 Mnenhy/0.7.6.666

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

According to Eric Blake on 10/15/2009 12:22 PM:
> Eric Blake <ebb9 <at> byu.net> writes:
> 
>> The current state of the series is now pushed.  Subsequent improvements,
>> like utimensat, will come later.
> 
> Here's the current state; I'll probably commit up through patch 4 within the 
> next 24 hours, but spend more time hammering on cygwin with patch 5.

And here's what I hope is the final round for gnulib for the *at family
(utimensat was the last on my list).  I'd push now, except right now
savannah is experiencing some problems and complaining that the file
system is read-only.  Coreutils could probably use a followup to add a
test for using touch with trailing slash.

Eric Blake (2):
      [1/2] fdutimensat: new module
coreutils copy.c currently uses gl_utimens (earlier in the series, I
swapped argument order and added the alternate name fdutimens, to make it
clear that it is different than *at interfaces) and lutimens.  In order to
convert copy.c to use fts, we need a directory-relative versions.  Hence:
fdutimensat and lutimensat.

      [2/2] utimensat: work around Solaris 9 bug
utimes() (and hence 'touch -c file/') mistakenly succeeds on Solaris 9.
And in the process of testing this, I discovered that cygwin 1.5.x stat()
modifies directory atime (since fixed in cygwin 1.7).

- --
Don't work too hard, make some time for fun as well!

Eric Blake             address@hidden
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAkrehGEACgkQ84KuGfSFAYAQ0QCgx18GyMxFLklb4mJccqdXlQLF
6hoAoLdP6m5mpJY+r31Dfgihfc5eXU3P
=Y5RT
-----END PGP SIGNATURE-----
>From 06b8c126a964a60ad353cc4f7b296060ae5c9df4 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Tue, 20 Oct 2009 06:09:29 -0600
Subject: [PATCH 1/2] fdutimensat: new module

Needed for coreutils copy.c to be rewritten to use fts.

* modules/fdutimensat: New file.
* lib/fdutimensat.c (fdutimensat): Likewise.
* lib/utimens.h (fdutimensat, lutimensat): Declare new functions.
* MODULES.html.sh (File system functions): Mention module.
* modules/fdutimensat-tests: New test.
* tests/test-fdutimensat.c: Likewise.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog                 |    8 +++
 MODULES.html.sh           |    1 +
 lib/fdutimensat.c         |   54 +++++++++++++++++
 lib/utimens.h             |   14 +++++
 modules/fdutimensat       |   30 ++++++++++
 modules/fdutimensat-tests |   19 ++++++
 tests/test-fdutimensat.c  |  142 +++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 268 insertions(+), 0 deletions(-)
 create mode 100644 lib/fdutimensat.c
 create mode 100644 modules/fdutimensat
 create mode 100644 modules/fdutimensat-tests
 create mode 100644 tests/test-fdutimensat.c

diff --git a/ChangeLog b/ChangeLog
index 20dd1a1..fe0baae 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2009-10-20  Eric Blake  <address@hidden>

+       fdutimensat: new module
+       * modules/fdutimensat: New file.
+       * lib/fdutimensat.c (fdutimensat): Likewise.
+       * lib/utimens.h (fdutimensat, lutimensat): Declare new functions.
+       * MODULES.html.sh (File system functions): Mention module.
+       * modules/fdutimensat-tests: New test.
+       * tests/test-fdutimensat.c: Likewise.
+
        doc: regenerate INSTALL
        * doc/INSTALL: Reflect recent autoconf update.
        * doc/INSTALL.ISO: Likewise.
diff --git a/MODULES.html.sh b/MODULES.html.sh
index ca27ad9..62abf0f 100755
--- a/MODULES.html.sh
+++ b/MODULES.html.sh
@@ -2464,6 +2464,7 @@ func_all_modules ()
   func_module euidaccess
   func_module faccessat
   func_module fdopendir
+  func_module fdutimensat
   func_module file-type
   func_module fileblocks
   func_module filemode
diff --git a/lib/fdutimensat.c b/lib/fdutimensat.c
new file mode 100644
index 0000000..e49f823
--- /dev/null
+++ b/lib/fdutimensat.c
@@ -0,0 +1,54 @@
+/* Set file access and modification times.
+
+   Copyright (C) 2009 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
+   Free Software Foundation; either version 3 of the License, or any
+   later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Eric Blake.  */
+
+/* derived from a function in utimens.c */
+
+#include <config.h>
+
+#include "utimens.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+/* Set the access and modification time stamps of FD (a.k.a. FILE) to be
+   TIMESPEC[0] and TIMESPEC[1], respectively; relative to directory DIR.
+   FD must be either negative -- in which case it is ignored --
+   or a file descriptor that is open on FILE.
+   If FD is nonnegative, then FILE can be NULL, which means
+   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.
+   Return 0 on success, -1 (setting errno) on failure.  */
+
+int
+fdutimensat (int dir, char const *file, int fd, struct timespec const ts[2])
+{
+  int result = 1;
+  if (0 <= fd)
+    result = futimens (fd, ts);
+  if (file && (fd < 0 || (result == -1 && errno == ENOSYS)))
+    result = utimensat (dir, file, ts, 0);
+  if (result == 1)
+    {
+      errno = EBADF;
+      result = -1;
+    }
+  return result;
+}
diff --git a/lib/utimens.h b/lib/utimens.h
index a02c6a4..bb0326d 100644
--- a/lib/utimens.h
+++ b/lib/utimens.h
@@ -3,3 +3,17 @@ int fdutimens (char const *, int, struct timespec const [2]);
 int gl_futimens (int, char const *, struct timespec const [2]);
 int utimens (char const *, struct timespec const [2]);
 int lutimens (char const *, struct timespec const [2]);
+
+#if GNULIB_FDUTIMENSAT
+# include <fcntl.h>
+# include <sys/stat.h>
+
+int fdutimensat (int dir, char const *name, int fd, struct timespec const [2]);
+
+/* Using this function makes application code slightly more readable.  */
+static inline int
+lutimensat (int fd, char const *file, struct timespec const times[2])
+{
+  return utimensat (fd, file, times, AT_SYMLINK_NOFOLLOW);
+}
+#endif
diff --git a/modules/fdutimensat b/modules/fdutimensat
new file mode 100644
index 0000000..38f8158
--- /dev/null
+++ b/modules/fdutimensat
@@ -0,0 +1,30 @@
+Description:
+Set file access and modification times, relative to a directory.
+
+Files:
+lib/fdutimensat.c
+lib/utimens.h
+
+Depends-on:
+futimens
+inline
+utimensat
+
+configure.ac:
+gl_MODULE_INDICATOR([fdutimensat])
+
+Makefile.am:
+lib_SOURCES += fdutimensat.c
+
+Include:
+<fcntl.h>
+"utimens.h"
+
+Link:
+$(LIB_CLOCK_GETTIME)
+
+License:
+GPL
+
+Maintainer:
+Paul Eggert, Jim Meyering, Eric Blake
diff --git a/modules/fdutimensat-tests b/modules/fdutimensat-tests
new file mode 100644
index 0000000..abd813d
--- /dev/null
+++ b/modules/fdutimensat-tests
@@ -0,0 +1,19 @@
+Files:
+tests/test-futimens.h
+tests/test-lutimens.h
+tests/test-utimens.h
+tests/test-utimens-common.h
+tests/test-fdutimensat.c
+
+Depends-on:
+progname
+timespec
+utimecmp
+
+configure.ac:
+AC_CHECK_FUNCS_ONCE([usleep])
+
+Makefile.am:
+TESTS += test-fdutimensat
+check_PROGRAMS += test-fdutimensat
+test_fdutimensat_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) @LIBINTL@
diff --git a/tests/test-fdutimensat.c b/tests/test-fdutimensat.c
new file mode 100644
index 0000000..b5c60ae
--- /dev/null
+++ b/tests/test-fdutimensat.c
@@ -0,0 +1,142 @@
+/* Tests of fdutimensat.
+   Copyright (C) 2009 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 Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Eric Blake <address@hidden>, 2009.  */
+
+#include <config.h>
+
+#include "utimens.h"
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define ASSERT(expr) \
+  do                                                                         \
+    {                                                                        \
+      if (!(expr))                                                           \
+        {                                                                    \
+          fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__);  \
+          fflush (stderr);                                                   \
+          abort ();                                                          \
+        }                                                                    \
+    }                                                                        \
+  while (0)
+
+#define BASE "test-fdutimensat.t"
+
+#include "test-futimens.h"
+#include "test-lutimens.h"
+#include "test-utimens.h"
+
+static int dfd = AT_FDCWD;
+
+/* Wrap fdutimensat to behave like futimens.  */
+static int
+do_futimens (int fd, struct timespec const times[2])
+{
+  return fdutimensat (dfd, NULL, fd, times);
+}
+
+/* Test the use of file descriptors alongside a name.  */
+static int
+do_fdutimens (char const *name, struct timespec const times[2])
+{
+  int result;
+  int fd = openat (dfd, name, O_WRONLY);
+  if (fd < 0)
+    fd = openat (dfd, name, O_RDONLY);
+  errno = 0;
+  result = fdutimensat (dfd, name, fd, times);
+  if (0 <= fd)
+    {
+      int saved_errno = errno;
+      close (fd);
+      errno = saved_errno;
+    }
+  return result;
+}
+
+/* Wrap lutimensat to behave like lutimens.  */
+static int
+do_lutimens (const char *name, struct timespec const times[2])
+{
+  return lutimensat (dfd, name, times);
+}
+
+/* Wrap fdutimensat to behave like utimens.  */
+static int
+do_utimens (const char *name, struct timespec const times[2])
+{
+  return fdutimensat (dfd, name, -1, times);
+}
+
+int
+main ()
+{
+  int result1; /* Skip because of no symlink support.  */
+  int result2; /* Skip because of no futimens support.  */
+  int result3; /* Skip because of no lutimens support.  */
+  int fd;
+
+  /* Clean up any trash from prior testsuite runs.  */
+  ASSERT (system ("rm -rf " BASE "*") == 0);
+
+  /* Basic tests.  */
+  result1 = test_utimens (do_utimens, true);
+  ASSERT (test_utimens (do_fdutimens, false) == result1);
+  result2 = test_futimens (do_futimens, result1 == 0);
+  result3 = test_lutimens (do_lutimens, (result1 + result2) == 0);
+  /* We expect 0/0, 0/77, or 77/77, but not 77/0.  */
+  ASSERT (result1 <= result3);
+  dfd = open (".", O_RDONLY);
+  ASSERT (0 <= dfd);
+  ASSERT (test_utimens (do_utimens, false) == result1);
+  ASSERT (test_utimens (do_fdutimens, false) == result1);
+  ASSERT (test_futimens (do_futimens, false) == result2);
+  ASSERT (test_lutimens (do_lutimens, false) == result3);
+
+  /* Directory relative tests.  */
+  ASSERT (mkdir (BASE "dir", 0700) == 0);
+  ASSERT (chdir (BASE "dir") == 0);
+  fd = creat ("file", 0600);
+  ASSERT (0 <= fd);
+  errno = 0;
+  ASSERT (fdutimensat (fd, ".", AT_FDCWD, NULL) == -1);
+  ASSERT (errno == ENOTDIR);
+  {
+    struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } };
+    struct stat st;
+    ASSERT (fdutimensat (dfd, BASE "dir/file", fd, ts) == 0);
+    ASSERT (stat ("file", &st) == 0);
+    ASSERT (st.st_atime == Y2K);
+    ASSERT (get_stat_atime_ns (&st) == 0);
+    ASSERT (st.st_mtime == Y2K);
+    ASSERT (get_stat_mtime_ns (&st) == 0);
+  }
+  ASSERT (close (fd) == 0);
+  ASSERT (close (dfd) == 0);
+  errno = 0;
+  ASSERT (fdutimensat (dfd, ".", -1, NULL) == -1);
+  ASSERT (errno == EBADF);
+
+  /* Cleanup.  */
+  ASSERT (chdir ("..") == 0);
+  ASSERT (unlink (BASE "dir/file") == 0);
+  ASSERT (rmdir (BASE "dir") == 0);
+  return result1 | result2 | result3;
+}
-- 
1.6.5.rc1


>From 8c871c07bc6a6bd7f4112053863833020569b272 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Tue, 20 Oct 2009 16:47:36 -0600
Subject: [PATCH 2/2] 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 <address@hidden>
---
 ChangeLog                          |   12 ++++++++++++
 doc/glibc-functions/futimesat.texi |    6 +++++-
 doc/glibc-functions/lutimes.texi   |    3 ++-
 doc/posix-functions/futimens.texi  |    9 ++++++++-
 doc/posix-functions/utime.texi     |    6 +++++-
 doc/posix-functions/utimensat.texi |   11 ++++++++++-
 doc/posix-functions/utimes.texi    |    6 +++++-
 lib/utimens.c                      |   16 +++++++++++-----
 tests/test-lutimens.h              |   35 +++++++++++++++++++++++++++++++++++
 tests/test-utimens.h               |   12 ++++++++++++
 10 files changed, 105 insertions(+), 11 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index fe0baae..3271366 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2009-10-20  Eric Blake  <address@hidden>

+       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.
diff --git a/doc/glibc-functions/futimesat.texi 
b/doc/glibc-functions/futimesat.texi
index dbd3d1d..109e412 100644
--- a/doc/glibc-functions/futimesat.texi
+++ b/doc/glibc-functions/futimesat.texi
@@ -16,6 +16,10 @@ futimesat
 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.
address@hidden
 This function cannot set full timestamp resolution.  Use
address@hidden ? utimensat(fd,file,times,0) : futimens(fd,times)} instead.
address@hidden ? utimensat(fd,file,times,0) : futimens(fd,times)}, or the
+gnulib module fdutimensat, instead.
 @end itemize
diff --git a/doc/glibc-functions/lutimes.texi b/doc/glibc-functions/lutimes.texi
index 2971c99..2371c51 100644
--- a/doc/glibc-functions/lutimes.texi
+++ b/doc/glibc-functions/lutimes.texi
@@ -16,7 +16,8 @@ lutimes
 Solaris 10, mingw, Interix 3.5, BeOS.
 @item
 This function cannot set full timestamp resolution.  Use
address@hidden(AT_FDCWD,file,times,AT_SYMLINK_NOFOLLOW)} instead.
address@hidden(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
diff --git a/doc/posix-functions/futimens.texi 
b/doc/posix-functions/futimens.texi
index 1434033..fee3d08 100644
--- a/doc/posix-functions/futimens.texi
+++ b/doc/posix-functions/futimens.texi
@@ -13,6 +13,8 @@ futimens
 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 @@ futimens
 @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}.
address@hidden
+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
diff --git a/doc/posix-functions/utime.texi b/doc/posix-functions/utime.texi
index d14685c..f50af81 100644
--- a/doc/posix-functions/utime.texi
+++ b/doc/posix-functions/utime.texi
@@ -16,8 +16,12 @@ utime
 Portability problems not fixed by Gnulib:
 @itemize
 @item
+On some platforms, this function mis-handles trailing slash:
+Solaris 9.
address@hidden
 This function cannot set full timestamp resolution.  Use
address@hidden(AT_FDCWD,file,times,0)} instead.
address@hidden(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,
diff --git a/doc/posix-functions/utimensat.texi 
b/doc/posix-functions/utimensat.texi
index 7164965..67f5078 100644
--- a/doc/posix-functions/utimensat.texi
+++ b/doc/posix-functions/utimensat.texi
@@ -13,6 +13,10 @@ utimensat
 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 @@ utimensat
 symlinks on some platforms, so @code{utimensat} with
 @code{AT_SYMLINK_NOFOLLOW} can only effectively change modification time:
 Cygwin.
address@hidden
+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.
diff --git a/doc/posix-functions/utimes.texi b/doc/posix-functions/utimes.texi
index 5f6cb7b..390aa98 100644
--- a/doc/posix-functions/utimes.texi
+++ b/doc/posix-functions/utimes.texi
@@ -16,9 +16,13 @@ utimes
 This function is missing on some platforms:
 mingw, Interix 3.5, BeOS.
 @item
+On some platforms, this function mis-handles trailing slash:
+Solaris 9.
address@hidden
 This function cannot set full timestamp resolution.  In particular,
 some platforms incorrectly round rather than truncate.  Use
address@hidden(AT_FDCWD,file,times,0)} instead.
address@hidden(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:
diff --git a/lib/utimens.c b/lib/utimens.c
index b33f883..ffc60b6 100644
--- a/lib/utimens.c
+++ b/lib/utimens.c
@@ -60,6 +60,12 @@ struct utimbuf
 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 @@ fdutimens (char const *file, int fd, struct timespec 
const timespec[2])
      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 @@ lutimens (char const *file, struct timespec const 
timespec[2])
      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 @@ lutimens (char const *file, struct timespec const 
timespec[2])
 #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);
diff --git a/tests/test-lutimens.h b/tests/test-lutimens.h
index 6765832..c9302c8 100644
--- a/tests/test-lutimens.h
+++ b/tests/test-lutimens.h
@@ -34,6 +34,9 @@ test_lutimens (int (*func) (char const *, struct timespec 
const *), bool print)
   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 @@ test_lutimens (int (*func) (char const *, struct timespec 
const *), bool print)
   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 @@ test_lutimens (int (*func) (char const *, struct timespec 
const *), bool print)
     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;
 }
diff --git a/tests/test-utimens.h b/tests/test-utimens.h
index abb4d26..710741a 100644
--- a/tests/test-utimens.h
+++ b/tests/test-utimens.h
@@ -58,6 +58,9 @@ test_utimens (int (*func) (char const *, struct timespec 
const *), bool print)
   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 @@ test_utimens (int (*func) (char const *, struct timespec 
const *), bool print)
     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 @@ test_utimens (int (*func) (char const *, struct timespec 
const *), bool print)
     }
   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);
-- 
1.6.5.rc1


reply via email to

[Prev in Thread] Current Thread [Next in Thread]