Mercurial > hg > octave-kai > gnulib-hg
changeset 12160:d0732ed2dd55
utimens: add UTIME_NOW and UTIME_OMIT support
These flags make it possible to implement futimens and utimensat;
they also make touch(1) more efficient, by avoiding stat or
gettime if native utimensat works.
* lib/utimens.c (validate_timespec, update_timespec): New helper
functions.
(gl_futimens, lutimens): Use them.
* modules/utimens (Depends-on): Add gettime, lstat, stat-time,
stdbool, sys_stat.
(Link): Mention resulting library dependency.
* modules/utimecmp (Link): Likewise.
* modules/utimens-tests (Depends-on): Drop stat-time, stdbool.
(Makefile.am): Pick up library dependency.
* lib/sys_stat.in.h (UTIME_NOW, UTIME_OMIT): Guarantee a
definition.
* tests/test-sys_stat.c: Test the definitions.
* doc/posix-headers/sys_stat.texi (sys/stat.h): Document this.
* NEWS: Document library dependency.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Wed, 07 Oct 2009 16:05:34 -0600 |
parents | 0ae159ba1adf |
children | c4a167621772 |
files | ChangeLog NEWS doc/posix-headers/sys_stat.texi lib/sys_stat.in.h lib/utimens.c modules/utimecmp modules/utimens modules/utimens-tests tests/test-sys_stat.c |
diffstat | 9 files changed, 189 insertions(+), 24 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,21 @@ 2009-10-10 Eric Blake <ebb9@byu.net> + utimens: add UTIME_NOW and UTIME_OMIT support + * lib/utimens.c (validate_timespec, update_timespec): New helper + functions. + (gl_futimens, lutimens): Use them. + * modules/utimens (Depends-on): Add gettime, lstat, stat-time, + stdbool, sys_stat. + (Link): Mention resulting library dependency. + * modules/utimecmp (Link): Likewise. + * modules/utimens-tests (Depends-on): Drop stat-time, stdbool. + (Makefile.am): Pick up library dependency. + * lib/sys_stat.in.h (UTIME_NOW, UTIME_OMIT): Guarantee a + definition. + * tests/test-sys_stat.c: Test the definitions. + * doc/posix-headers/sys_stat.texi (sys/stat.h): Document this. + * NEWS: Document library dependency. + utimecmp: support symlink timestamps * lib/utimecmp.c (utimecmp): Use new interface. Skip effort of hashing when possible. Use pathconf when available.
--- a/NEWS +++ b/NEWS @@ -6,6 +6,9 @@ Date Modules Changes +2009-10-10 utimens The use of this module now requires linking with + $(LIB_CLOCK_GETTIME). + 2009-09-16 canonicalize-lgpl The include file is changed from "canonicalize.h" to <stdlib.h>.
--- a/doc/posix-headers/sys_stat.texi +++ b/doc/posix-headers/sys_stat.texi @@ -17,6 +17,9 @@ on other platforms. @item The functions @code{lstat} and @code{mkdir} are not declared on mingw. +@item +The macros @code{UTIME_NOW} and @code{UTIME_OMIT} are missing on some +platforms. @end itemize Portability problems not fixed by Gnulib:
--- a/lib/sys_stat.in.h +++ b/lib/sys_stat.in.h @@ -278,6 +278,12 @@ # define S_IRWXUGO (S_IRWXU | S_IRWXG | S_IRWXO) #endif +/* Macros for futimens and utimensat. */ +#ifndef UTIME_NOW +# define UTIME_NOW (-1) +# define UTIME_OMIT (-2) +#endif + #ifdef __cplusplus extern "C" {
--- a/lib/utimens.c +++ b/lib/utimens.c @@ -24,12 +24,17 @@ #include "utimens.h" +#include <assert.h> #include <errno.h> #include <fcntl.h> +#include <stdbool.h> #include <sys/stat.h> #include <sys/time.h> #include <unistd.h> +#include "stat-time.h" +#include "timespec.h" + #if HAVE_UTIME_H # include <utime.h> #endif @@ -44,6 +49,78 @@ }; #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 + further adjustment based on stat results for utimes or other less + powerful interfaces. Return -1, with errno set to EINVAL, if + timespec is out of range. */ +static int +validate_timespec (struct timespec timespec[2]) +{ + int result = 0; + assert (timespec); + if ((timespec[0].tv_nsec != UTIME_NOW + && timespec[0].tv_nsec != UTIME_OMIT + && (timespec[0].tv_nsec < 0 || 1000000000 <= timespec[0].tv_nsec)) + || (timespec[1].tv_nsec != UTIME_NOW + && timespec[1].tv_nsec != UTIME_OMIT + && (timespec[1].tv_nsec < 0 || 1000000000 <= timespec[1].tv_nsec))) + { + errno = EINVAL; + return -1; + } + /* Work around Linux kernel 2.6.25 bug, where utimensat fails with + EINVAL if tv_sec is not 0 when using the flag values of + tv_nsec. */ + if (timespec[0].tv_nsec == UTIME_NOW + || timespec[0].tv_nsec == UTIME_OMIT) + { + timespec[0].tv_sec = 0; + result = 1; + } + if (timespec[1].tv_nsec == UTIME_NOW + || timespec[1].tv_nsec == UTIME_OMIT) + { + timespec[1].tv_sec = 0; + result = 1; + } + return result; +} + +/* Normalize any UTIME_NOW or UTIME_OMIT values in *TS, using stat + buffer STATBUF to obtain the current timestamps of the file. If + both times are UTIME_NOW, set *TS to NULL (as this can avoid some + permissions issues). If both times are UTIME_OMIT, return true + (nothing further beyond the prior collection of STATBUF is + necessary); otherwise return false. */ +static bool +update_timespec (struct stat const *statbuf, struct timespec *ts[2]) +{ + struct timespec *timespec = *ts; + if (timespec[0].tv_nsec == UTIME_OMIT + && timespec[1].tv_nsec == UTIME_OMIT) + return true; + if (timespec[0].tv_nsec == UTIME_NOW + && timespec[1].tv_nsec == UTIME_NOW) + { + *ts = NULL; + return false; + } + + if (timespec[0].tv_nsec == UTIME_OMIT) + timespec[0] = get_stat_atime (statbuf); + else if (timespec[0].tv_nsec == UTIME_NOW) + gettime (×pec[0]); + + if (timespec[1].tv_nsec == UTIME_OMIT) + timespec[1] = get_stat_mtime (statbuf); + else if (timespec[1].tv_nsec == UTIME_NOW) + gettime (×pec[1]); + + return false; +} + /* Set the access and modification time stamps of FD (a.k.a. FILE) to be TIMESPEC[0] and TIMESPEC[1], respectively. FD must be either negative -- in which case it is ignored -- @@ -57,6 +134,19 @@ int gl_futimens (int fd, char const *file, struct timespec const timespec[2]) { + struct timespec adjusted_timespec[2]; + struct timespec *ts = timespec ? adjusted_timespec : NULL; + int adjustment_needed = 0; + + if (ts) + { + adjusted_timespec[0] = timespec[0]; + adjusted_timespec[1] = timespec[1]; + adjustment_needed = validate_timespec (ts); + } + if (adjustment_needed < 0) + return -1; + /* Require that at least one of FD or FILE are valid. Works around a Linux bug where futimens (AT_FDCWD, NULL) changes "." rather than failing. */ @@ -88,16 +178,16 @@ fsync (fd); #endif - /* POSIX 200x added two interfaces to set file timestamps with + /* POSIX 2008 added two interfaces to set file timestamps with nanosecond resolution. We provide a fallback for ENOSYS (for example, compiling against Linux 2.6.25 kernel headers and glibc 2.7, but running on Linux 2.6.18 kernel). */ #if HAVE_UTIMENSAT if (fd < 0) { - int result = utimensat (AT_FDCWD, file, timespec, 0); + int result = utimensat (AT_FDCWD, file, ts, 0); # ifdef __linux__ - /* Work around what might be a kernel bug: + /* Work around a kernel bug: http://bugzilla.redhat.com/442352 http://bugzilla.redhat.com/449910 It appears that utimensat can mistakenly return 280 rather @@ -128,16 +218,26 @@ /* The platform lacks an interface to set file timestamps with nanosecond resolution, so do the best we can, discarding any fractional part of the timestamp. */ + + if (adjustment_needed) + { + struct stat st; + if (fd < 0 ? stat (file, &st) : fstat (fd, &st)) + return -1; + if (update_timespec (&st, &ts)) + return 0; + } + { #if HAVE_FUTIMESAT || HAVE_WORKING_UTIMES struct timeval timeval[2]; struct timeval const *t; - if (timespec) + if (ts) { - timeval[0].tv_sec = timespec[0].tv_sec; - timeval[0].tv_usec = timespec[0].tv_nsec / 1000; - timeval[1].tv_sec = timespec[1].tv_sec; - timeval[1].tv_usec = timespec[1].tv_nsec / 1000; + timeval[0].tv_sec = ts[0].tv_sec; + timeval[0].tv_usec = ts[0].tv_nsec / 1000; + timeval[1].tv_sec = ts[1].tv_sec; + timeval[1].tv_usec = ts[1].tv_nsec / 1000; t = timeval; } else @@ -185,10 +285,10 @@ { struct utimbuf utimbuf; struct utimbuf *ut; - if (timespec) + if (ts) { - utimbuf.actime = timespec[0].tv_sec; - utimbuf.modtime = timespec[1].tv_sec; + utimbuf.actime = ts[0].tv_sec; + utimbuf.modtime = ts[1].tv_sec; ut = &utimbuf; } else @@ -212,9 +312,21 @@ be TIMESPEC[0] and TIMESPEC[1], respectively. Fail with ENOSYS if the platform does not support changing symlink timestamps. */ int -lutimens (char const *file _UNUSED_PARAMETER_, - struct timespec const timespec[2] _UNUSED_PARAMETER_) +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; + + if (ts) + { + adjusted_timespec[0] = timespec[0]; + adjusted_timespec[1] = timespec[1]; + adjustment_needed = validate_timespec (ts); + } + if (adjustment_needed < 0) + return -1; + /* The Linux kernel did not support symlink timestamps until utimensat, in version 2.6.22, so we don't need to mimic gl_futimens' worry about buggy NFS clients. But we do have to @@ -222,7 +334,7 @@ #if HAVE_UTIMENSAT { - int result = utimensat (AT_FDCWD, file, timespec, AT_SYMLINK_NOFOLLOW); + int result = utimensat (AT_FDCWD, file, ts, AT_SYMLINK_NOFOLLOW); # ifdef __linux__ /* Work around a kernel bug: http://bugzilla.redhat.com/442352 @@ -243,16 +355,26 @@ /* The platform lacks an interface to set file timestamps with nanosecond resolution, so do the best we can, discarding any fractional part of the timestamp. */ + + if (adjustment_needed) + { + struct stat st; + if (lstat (file, &st)) + return -1; + if (update_timespec (&st, &ts)) + return 0; + } + #if HAVE_LUTIMES { struct timeval timeval[2]; struct timeval const *t; - if (timespec) + if (ts) { - timeval[0].tv_sec = timespec[0].tv_sec; - timeval[0].tv_usec = timespec[0].tv_nsec / 1000; - timeval[1].tv_sec = timespec[1].tv_sec; - timeval[1].tv_usec = timespec[1].tv_nsec / 1000; + timeval[0].tv_sec = ts[0].tv_sec; + timeval[0].tv_usec = ts[0].tv_nsec / 1000; + timeval[1].tv_sec = ts[1].tv_sec; + timeval[1].tv_usec = ts[1].tv_nsec / 1000; t = timeval; } else
--- a/modules/utimecmp +++ b/modules/utimecmp @@ -26,6 +26,9 @@ Include: "utimecmp.h" +Link: +$(LIB_CLOCK_GETTIME) + License: GPL
--- a/modules/utimens +++ b/modules/utimens @@ -11,6 +11,11 @@ Depends-on: dup2 errno +lstat +gettime +stat-time +stdbool +sys_stat sys_time time @@ -22,8 +27,11 @@ Include: "utimens.h" +Link: +$(LIB_CLOCK_GETTIME) + License: GPL Maintainer: -Paul Eggert, Jim Meyering +Paul Eggert, Jim Meyering, Eric Blake
--- a/modules/utimens-tests +++ b/modules/utimens-tests @@ -5,8 +5,6 @@ tests/test-utimens.c Depends-on: -stat-time -stdbool symlink timespec utimecmp @@ -17,4 +15,4 @@ Makefile.am: TESTS += test-utimens check_PROGRAMS += test-utimens -test_utimens_LDADD = $(LDADD) @LIBINTL@ +test_utimens_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) @LIBINTL@
--- a/tests/test-sys_stat.c +++ b/tests/test-sys_stat.c @@ -1,5 +1,5 @@ /* Test of <sys/stat.h> substitute. - Copyright (C) 2007-2008 Free Software Foundation, Inc. + Copyright (C) 2007-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 @@ -253,6 +253,12 @@ verify (!S_ISWHT (S_IFSOCK)); #endif +#if ((0 <= UTIME_NOW && UTIME_NOW < 1000000000) \ + || (0 <= UTIME_OMIT && UTIME_OMIT < 1000000000) \ + || UTIME_NOW == UTIME_OMIT) +invalid UTIME macros +#endif + /* Check the existence of some types. */ nlink_t t1;