Mercurial > hg > octave-lojdl > gnulib-hg
changeset 12466:e31a9e90d604
utimens: check for ctime update
futimens/utimensat on Linux fails to bump ctime if mtime is
UTIME_OMIT and atime is specified.
* tests/test-utimens-common.h (check_ctime): Define.
* tests/test-utimens.h (test_utimens): Expose the Linux bug.
* tests/test-futimens.h (test_futimens): Likewise.
* tests/test-lutimens.h (test_lutimens): Likewise.
* doc/posix-functions/futimens.texi (futimens): Document the bug.
* doc/posix-functions/utimensat.texi (utimensat): Likewise.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Thu, 17 Dec 2009 12:30:47 -0700 |
parents | 196d4470a4b5 |
children | 912bdb1c31fb |
files | ChangeLog doc/posix-functions/futimens.texi doc/posix-functions/utimensat.texi tests/test-futimens.h tests/test-lutimens.h tests/test-utimens-common.h tests/test-utimens.h |
diffstat | 7 files changed, 144 insertions(+), 25 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2009-12-19 Eric Blake <ebb9@byu.net> + + utimens: check for ctime update + * tests/test-utimens-common.h (check_ctime): Define. + * tests/test-utimens.h (test_utimens): Expose the Linux bug. + * tests/test-futimens.h (test_futimens): Likewise. + * tests/test-lutimens.h (test_lutimens): Likewise. + * doc/posix-functions/futimens.texi (futimens): Document the bug. + * doc/posix-functions/utimensat.texi (utimensat): Likewise. + 2009-12-19 Bruno Haible <bruno@clisp.org> dprintf-posix: Check against memory leak fixed on 2009-12-15.
--- a/doc/posix-functions/futimens.texi +++ b/doc/posix-functions/futimens.texi @@ -24,6 +24,10 @@ the @code{tv_sec} argument to be 0, and don't necessarily handle all file permissions in the manner required by POSIX: Linux kernel 2.6.25. +@item +When using @code{UTIME_OMIT} for the modification time, but specifying +an access time, some systems fail to update the change time: +Linux kernel 2.6.32. @end itemize Portability problems not fixed by Gnulib:
--- a/doc/posix-functions/utimensat.texi +++ b/doc/posix-functions/utimensat.texi @@ -22,10 +22,18 @@ @code{ENOSYS} on some platforms: Linux kernel 2.6.21. @item +This function fails with @code{ENOSYS} if passed the flag +@code{AT_SYMLINK_NOFOLLOW} on a regular file: +Linux kernel 2.6.22. +@item When using @code{UTIME_OMIT} or @code{UTIME_NOW}, some systems require the @code{tv_sec} argument to be 0, and don't necessarily handle all file permissions in the manner required by POSIX: Linux kernel 2.6.25. +@item +When using @code{UTIME_OMIT} for the modification time, but specifying +an access time, some systems fail to update the change time: +Linux kernel 2.6.32. @end itemize Portability problems not fixed by Gnulib:
--- a/tests/test-futimens.h +++ b/tests/test-futimens.h @@ -53,6 +53,10 @@ UTIMECMP_TRUNCATE_SOURCE to compensate, with st1 as the source. */ ASSERT (0 <= utimecmp (BASE "file", &st2, &st1, UTIMECMP_TRUNCATE_SOURCE)); + if (check_ctime) + ASSERT (st1.st_ctime < st2.st_ctime + || (st1.st_ctime == st2.st_ctime + && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2))); { /* On some NFS systems, the 'now' timestamp of creat or a NULL timespec is determined by the server, but the 'now' timestamp @@ -101,17 +105,40 @@ ASSERT (st2.st_mtime == Y2K); ASSERT (0 <= get_stat_mtime_ns (&st2)); ASSERT (get_stat_mtime_ns (&st2) < BILLION); + if (check_ctime) + ASSERT (st1.st_ctime < st2.st_ctime + || (st1.st_ctime == st2.st_ctime + && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2))); } /* Play with UTIME_OMIT, UTIME_NOW. */ { + struct stat st3; struct timespec ts[2] = { { BILLION, UTIME_OMIT }, { 0, UTIME_NOW } }; + nap (); + ASSERT (func (fd, ts) == 0); + ASSERT (fstat (fd, &st3) == 0); + ASSERT (st3.st_atime == Y2K); + ASSERT (0 <= get_stat_atime_ns (&st3)); + ASSERT (get_stat_atime_ns (&st3) <= BILLION / 2); + ASSERT (utimecmp (BASE "file", &st1, &st3, 0) <= 0); + if (check_ctime) + ASSERT (st2.st_ctime < st3.st_ctime + || (st2.st_ctime == st3.st_ctime + && get_stat_ctime_ns (&st2) < get_stat_ctime_ns (&st3))); + nap (); + ts[0].tv_nsec = 0; + ts[1].tv_nsec = UTIME_OMIT; ASSERT (func (fd, ts) == 0); ASSERT (fstat (fd, &st2) == 0); - ASSERT (st2.st_atime == Y2K); - ASSERT (0 <= get_stat_atime_ns (&st2)); - ASSERT (get_stat_atime_ns (&st2) <= BILLION / 2); - ASSERT (utimecmp (BASE "file", &st1, &st2, 0) <= 0); + ASSERT (st2.st_atime == BILLION); + ASSERT (get_stat_atime_ns (&st2) == 0); + ASSERT (st3.st_mtime == st2.st_mtime); + ASSERT (get_stat_mtime_ns (&st3) == get_stat_mtime_ns (&st2)); + if (check_ctime) + ASSERT (st3.st_ctime < st2.st_ctime + || (st3.st_ctime == st2.st_ctime + && get_stat_ctime_ns (&st3) < get_stat_ctime_ns (&st2))); } /* Cleanup. */
--- a/tests/test-lutimens.h +++ b/tests/test-lutimens.h @@ -54,11 +54,16 @@ } { struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } }; + nap (); ASSERT (func (BASE "file", ts) == 0); } - ASSERT (stat (BASE "file", &st1) == 0); - ASSERT (st1.st_atime == Y2K); - ASSERT (st1.st_mtime == Y2K); + ASSERT (stat (BASE "file", &st2) == 0); + ASSERT (st2.st_atime == Y2K); + ASSERT (st2.st_mtime == Y2K); + if (check_ctime) + ASSERT (st1.st_ctime < st2.st_ctime + || (st1.st_ctime == st2.st_ctime + && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2))); /* Play with symlink timestamps. */ if (symlink (BASE "file", BASE "link")) @@ -98,6 +103,8 @@ if (st1.st_atime != st2.st_atime || get_stat_atime_ns (&st1) != get_stat_atime_ns (&st2)) atime_supported = false; + ASSERT (st1.st_ctime == st2.st_ctime); + ASSERT (get_stat_ctime_ns (&st1) == get_stat_ctime_ns (&st2)); /* Invalid arguments. */ { @@ -123,6 +130,7 @@ /* Set both times. */ { struct timespec ts[2] = { { Y2K, BILLION / 2 - 1 }, { Y2K, BILLION - 1 } }; + nap (); ASSERT (func (BASE "link", ts) == 0); ASSERT (lstat (BASE "link", &st2) == 0); if (atime_supported) @@ -134,20 +142,46 @@ ASSERT (st2.st_mtime == Y2K); ASSERT (0 <= get_stat_mtime_ns (&st2)); ASSERT (get_stat_mtime_ns (&st2) < BILLION); + if (check_ctime) + ASSERT (st1.st_ctime < st2.st_ctime + || (st1.st_ctime == st2.st_ctime + && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2))); } /* Play with UTIME_OMIT, UTIME_NOW. */ { + struct stat st3; struct timespec ts[2] = { { BILLION, UTIME_OMIT }, { 0, UTIME_NOW } }; + nap (); + ASSERT (func (BASE "link", ts) == 0); + ASSERT (lstat (BASE "link", &st3) == 0); + if (atime_supported) + { + ASSERT (st3.st_atime == Y2K); + ASSERT (0 <= get_stat_atime_ns (&st3)); + ASSERT (get_stat_atime_ns (&st3) < BILLION / 2); + } + ASSERT (utimecmp (BASE "link", &st1, &st3, 0) <= 0); + if (check_ctime) + ASSERT (st2.st_ctime < st3.st_ctime + || (st2.st_ctime == st3.st_ctime + && get_stat_ctime_ns (&st2) < get_stat_ctime_ns (&st3))); + nap (); + ts[0].tv_nsec = 0; + ts[1].tv_nsec = UTIME_OMIT; ASSERT (func (BASE "link", ts) == 0); ASSERT (lstat (BASE "link", &st2) == 0); if (atime_supported) { - ASSERT (st2.st_atime == Y2K); - ASSERT (0 <= get_stat_atime_ns (&st2)); - ASSERT (get_stat_atime_ns (&st2) < BILLION / 2); + ASSERT (st2.st_atime == BILLION); + ASSERT (get_stat_atime_ns (&st2) == 0); } - ASSERT (utimecmp (BASE "link", &st1, &st2, 0) <= 0); + ASSERT (st3.st_mtime == st2.st_mtime); + ASSERT (get_stat_mtime_ns (&st3) == get_stat_mtime_ns (&st2)); + if (check_ctime) + ASSERT (st3.st_ctime < st2.st_ctime + || (st3.st_ctime == st2.st_ctime + && get_stat_ctime_ns (&st3) < get_stat_ctime_ns (&st2))); } /* Symlink to directory. */
--- a/tests/test-utimens-common.h +++ b/tests/test-utimens-common.h @@ -19,14 +19,14 @@ #ifndef GL_TEST_UTIMENS_COMMON # define GL_TEST_UTIMENS_COMMON -#include <fcntl.h> -#include <errno.h> -#include <string.h> -#include <unistd.h> +# include <fcntl.h> +# include <errno.h> +# include <string.h> +# include <unistd.h> -#include "stat-time.h" -#include "timespec.h" -#include "utimecmp.h" +# include "stat-time.h" +# include "timespec.h" +# include "utimecmp.h" enum { BILLION = 1000 * 1000 * 1000, @@ -62,9 +62,18 @@ a quantization boundary equal to the resolution. Our usage of utimecmp allows equality, so no need to waste 980 milliseconds if the replacement usleep rounds to 1 second. */ -#if HAVE_USLEEP +# if HAVE_USLEEP usleep (20 * 1000); /* 20 milliseconds. */ -#endif +# endif } +# if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__ +/* Skip ctime tests on native Windows, since it is either a copy of + mtime or birth time (depending on the file system), rather than a + properly tracked change time. */ +# define check_ctime 0 +# else +# define check_ctime 1 +# endif + #endif /* GL_TEST_UTIMENS_COMMON */
--- a/tests/test-utimens.h +++ b/tests/test-utimens.h @@ -37,6 +37,10 @@ ASSERT (func (BASE "file", NULL) == 0); ASSERT (stat (BASE "file", &st2) == 0); ASSERT (0 <= utimecmp (BASE "file", &st2, &st1, UTIMECMP_TRUNCATE_SOURCE)); + if (check_ctime) + ASSERT (st1.st_ctime < st2.st_ctime + || (st1.st_ctime == st2.st_ctime + && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2))); { /* On some NFS systems, the 'now' timestamp of creat or a NULL timespec is determined by the server, but the 'now' timestamp @@ -97,18 +101,41 @@ ASSERT (st2.st_mtime == Y2K); ASSERT (0 <= get_stat_mtime_ns (&st2)); ASSERT (get_stat_mtime_ns (&st2) < BILLION); + if (check_ctime) + ASSERT (st1.st_ctime < st2.st_ctime + || (st1.st_ctime == st2.st_ctime + && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2))); } /* Play with UTIME_OMIT, UTIME_NOW. */ { + struct stat st3; struct timespec ts[2] = { { BILLION, UTIME_OMIT }, { 0, UTIME_NOW } }; + nap (); + ASSERT (func (BASE "file", ts) == 0); + ASSERT (stat (BASE "file", &st3) == 0); + ASSERT (st3.st_atime == Y2K); + ASSERT (0 <= get_stat_atime_ns (&st3)); + ASSERT (get_stat_atime_ns (&st3) < BILLION / 2); + /* See comment above about this utimecmp call. */ + ASSERT (0 <= utimecmp (BASE "file", &st3, &st1, UTIMECMP_TRUNCATE_SOURCE)); + if (check_ctime) + ASSERT (st2.st_ctime < st3.st_ctime + || (st2.st_ctime == st3.st_ctime + && get_stat_ctime_ns (&st2) < get_stat_ctime_ns (&st3))); + nap (); + ts[0].tv_nsec = 0; + ts[1].tv_nsec = UTIME_OMIT; ASSERT (func (BASE "file", ts) == 0); ASSERT (stat (BASE "file", &st2) == 0); - ASSERT (st2.st_atime == Y2K); - ASSERT (0 <= get_stat_atime_ns (&st2)); - ASSERT (get_stat_atime_ns (&st2) < BILLION / 2); - /* See comment above about this utimecmp call. */ - ASSERT (0 <= utimecmp (BASE "file", &st2, &st1, UTIMECMP_TRUNCATE_SOURCE)); + ASSERT (st2.st_atime == BILLION); + ASSERT (get_stat_atime_ns (&st2) == 0); + ASSERT (st3.st_mtime == st2.st_mtime); + ASSERT (get_stat_mtime_ns (&st3) == get_stat_mtime_ns (&st2)); + if (check_ctime) + ASSERT (st3.st_ctime < st2.st_ctime + || (st3.st_ctime == st2.st_ctime + && get_stat_ctime_ns (&st3) < get_stat_ctime_ns (&st2))); } /* Make sure this dereferences symlinks. */