Mercurial > hg > octave-lyh > gnulib-hg
changeset 12173:efdb2ab5743f
utimens: let lutimens work on non-symlinks
Coreutils new 'touch -h' is easier to write if we guarantee POSIX
semantics of utimensat(fd,"file",NULL,AT_SYMLINK_NOFOLLOW), rather
than blindly failing with ENOSYS even on non-symlinks.
* lib/utimens.c (lutimens): Fall back to utimens rather than
failing with ENOSYS, when file is not a symlink.
(utimens): Reduce redirection.
* tests/test-lutimens.h (test_lutimens): Update test to cover
non-symlinks.
* tests/test-utimens.h (test_utimens): Update test to cover
symlinks.
* tests/test-utimens.c (main): Update caller.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Thu, 15 Oct 2009 12:04:57 -0600 |
parents | 52ebfd595434 |
children | 73f2681e0524 |
files | ChangeLog lib/utimens.c tests/test-lutimens.h tests/test-utimens.c tests/test-utimens.h |
diffstat | 5 files changed, 90 insertions(+), 28 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,15 @@ 2009-10-16 Eric Blake <ebb9@byu.net> + utimens: let lutimens work on non-symlinks + * lib/utimens.c (lutimens): Fall back to utimens rather than + failing with ENOSYS, when file is not a symlink. + (utimens): Reduce redirection. + * tests/test-lutimens.h (test_lutimens): Update test to cover + non-symlinks. + * tests/test-utimens.h (test_utimens): Update test to cover + symlinks. + * tests/test-utimens.c (main): Update caller. + utimens: cache whether utimensat syscall works * lib/utimens.c (utimensat_works_really): New cache variable. (fdutimens, lutimens): Use it to avoid failing syscall.
--- a/lib/utimens.c +++ b/lib/utimens.c @@ -343,18 +343,20 @@ int utimens (char const *file, struct timespec const timespec[2]) { - return gl_futimens (-1, file, timespec); + return fdutimens (file, -1, timespec); } -/* Set the access and modification time stamps of the symlink FILE to - be TIMESPEC[0] and TIMESPEC[1], respectively. Fail with ENOSYS if - the platform does not support changing symlink timestamps. */ +/* Set the access and modification time stamps of FILE to be + TIMESPEC[0] and TIMESPEC[1], respectively, without dereferencing + symlinks. Fail with ENOSYS if the platform does not support + changing symlink timestamps, but FILE was a symlink. */ int lutimens (char const *file, struct timespec const timespec[2]) { struct timespec adjusted_timespec[2]; struct timespec *ts = timespec ? adjusted_timespec : NULL; int adjustment_needed = 0; + struct stat st; if (ts) { @@ -400,7 +402,6 @@ if (adjustment_needed) { - struct stat st; if (lstat (file, &st)) return -1; if (update_timespec (&st, &ts)) @@ -426,8 +427,11 @@ } #endif /* HAVE_LUTIMES */ - /* Out of luck. Symlink timestamps can't be changed. We won't - bother changing the timestamps if FILE was not a symlink. */ + /* Out of luck for symlinks, but we still handle regular files. */ + if (!adjustment_needed && lstat (file, &st)) + return -1; + if (!S_ISLNK (st.st_mode)) + return fdutimens (file, -1, ts); errno = ENOSYS; return -1; }
--- a/tests/test-lutimens.h +++ b/tests/test-lutimens.h @@ -24,12 +24,34 @@ test_lutimens (int (*func) (char const *, struct timespec const *), bool print) { int result; + int saved_errno; struct stat st1; struct stat st2; bool atime_supported = true; - if (symlink ("nowhere", BASE "link")) + /* Non-symlinks should be handled just like utimens. */ + errno = 0; + ASSERT (func ("no_such", NULL) == -1); + ASSERT (errno == ENOENT); + errno = 0; + ASSERT (func ("", NULL) == -1); + ASSERT (errno == ENOENT); + ASSERT (close (creat (BASE "file", 0600)) == 0); + ASSERT (stat (BASE "file", &st1) == 0); + ASSERT (st1.st_atime != Y2K); + ASSERT (st1.st_mtime != Y2K); + { + struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } }; + ASSERT (func (BASE "file", ts) == 0); + } + ASSERT (stat (BASE "file", &st1) == 0); + ASSERT (st1.st_atime == Y2K); + ASSERT (st1.st_mtime == Y2K); + + /* Play with symlink timestamps. */ + if (symlink (BASE "file", BASE "link")) { + ASSERT (unlink (BASE "file") == 0); if (print) fputs ("skipping test: symlinks not supported on this file system\n", stderr); @@ -37,7 +59,16 @@ } errno = 0; result = func (BASE "link", NULL); - if (result == -1 && errno == ENOSYS) + saved_errno = errno; + /* Make sure we did not reference through link by accident. */ + ASSERT (stat (BASE "file", &st1) == 0); + ASSERT (st1.st_atime == Y2K); + ASSERT (st1.st_mtime == Y2K); + ASSERT (lstat (BASE "link", &st1) == 0); + ASSERT (st1.st_atime != Y2K); + ASSERT (st1.st_mtime != Y2K); + ASSERT (unlink (BASE "file") == 0); + if (result == -1 && saved_errno == ENOSYS) { ASSERT (unlink (BASE "link") == 0); if (print) @@ -57,12 +88,6 @@ atime_supported = false; /* Invalid arguments. */ - errno = 0; - ASSERT (func ("no_such", NULL) == -1); - ASSERT (errno == ENOENT); - errno = 0; - ASSERT (func ("", NULL) == -1); - ASSERT (errno == ENOENT); { struct timespec ts[2] = { { Y2K, UTIME_BOGUS_POS }, { Y2K, 0 } }; errno = 0;
--- a/tests/test-utimens.c +++ b/tests/test-utimens.c @@ -71,20 +71,19 @@ int main () { - int result1; - int result2; + int result1; /* Skip because of no symlink support. */ + int result2; /* Skip because of no futimens support. */ + int result3; /* Skip because of no lutimens support. */ /* Clean up any trash from prior testsuite runs. */ ASSERT (system ("rm -rf " BASE "*") == 0); - ASSERT (test_utimens (utimens) == 0); - ASSERT (test_utimens (do_fdutimens) == 0); - result1 = test_futimens (do_futimens, true); - if (result1) - ASSERT (result1 == 77); + result1 = test_utimens (utimens, true); + ASSERT (test_utimens (do_fdutimens, false) == result1); /* Print only one skip message. */ - result2 = test_lutimens (lutimens, result1 == 0); - if (result2) - ASSERT (result2 == 77); - return result1 | result2; + result2 = test_futimens (do_futimens, result1 == 0); + result3 = test_lutimens (lutimens, (result1 + result2) == 0); + /* We expect 0/0, 0/77, or 77/77, but not 77/0. */ + ASSERT (result1 <= result3); + return result1 | result2 | result3; }
--- a/tests/test-utimens.h +++ b/tests/test-utimens.h @@ -18,9 +18,10 @@ /* This file is designed to test both utimens(a,b) and utimensat(AT_FDCWD,a,b,0). FUNC is the function to test. Assumes - that BASE and ASSERT are already defined. */ + that BASE and ASSERT are already defined. If PRINT, warn before + skipping tests with status 77. */ static int -test_utimens (int (*func) (char const *, struct timespec const *)) +test_utimens (int (*func) (char const *, struct timespec const *), bool print) { struct stat st1; struct stat st2; @@ -101,7 +102,30 @@ ASSERT (0 <= utimecmp (BASE "file", &st2, &st1, UTIMECMP_TRUNCATE_SOURCE)); } + /* Make sure this dereferences symlinks. */ + if (symlink (BASE "file", BASE "link")) + { + ASSERT (unlink (BASE "file") == 0); + if (print) + fputs ("skipping test: symlinks not supported on this file system\n", + stderr); + return 77; + } + ASSERT (lstat (BASE "link", &st1) == 0); + ASSERT (st1.st_mtime != Y2K); + { + struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } }; + ASSERT (func (BASE "link", ts) == 0); + ASSERT (lstat (BASE "link", &st2) == 0); + /* Can't compare atimes, since lstat() changes symlink atime on cygwin. */ + ASSERT (st1.st_mtime == st2.st_mtime); + ASSERT (stat (BASE "link", &st2) == 0); + ASSERT (st2.st_mtime == Y2K); + ASSERT (get_stat_mtime_ns (&st2) == 0); + } + /* Cleanup. */ + ASSERT (unlink (BASE "link") == 0); ASSERT (unlink (BASE "file") == 0); return 0; }