# HG changeset patch # User Eric Blake # Date 1256040569 21600 # Node ID d1ea4269ac6f6cb8657fe6aafe78893f8f5b842a # Parent b0d3e25d7cdc04c99c852b160beee445b6046c55 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 diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2009-10-20 Eric Blake + 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 --- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -2464,6 +2464,7 @@ 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 --- /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 . */ + +/* Written by Eric Blake. */ + +/* derived from a function in utimens.c */ + +#include + +#include "utimens.h" + +#include +#include +#include + +/* 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 --- a/lib/utimens.h +++ b/lib/utimens.h @@ -3,3 +3,17 @@ 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 +# include + +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 --- /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: + +"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 --- /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 --- /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 . */ + +/* Written by Eric Blake , 2009. */ + +#include + +#include "utimens.h" + +#include +#include +#include +#include + +#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; +}