X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Futimens.c;h=ce759a1d09cd3db87c108eb421d6484801a5a311;hb=d4e9c2ea4e9f119a09d37870065f05912bbe9b3c;hp=b2b25d13d4719109de20db0af26c574c1437111a;hpb=080cbfcab5a4ddf2405c018aa7c7f95ac336a465;p=gnulib.git diff --git a/lib/utimens.c b/lib/utimens.c index b2b25d13d..ce759a1d0 100644 --- a/lib/utimens.c +++ b/lib/utimens.c @@ -49,6 +49,23 @@ struct utimbuf }; #endif +/* Avoid recursion with rpl_futimens or rpl_utimensat. */ +#undef futimens +#undef utimensat + +#if HAVE_UTIMENSAT || HAVE_FUTIMENS +/* Cache variable for whether syscall works; used to avoid calling the + syscall if we know it will just fail with ENOSYS. 0 = unknown, 1 = + yes, -1 = no. */ +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 @@ -132,7 +149,7 @@ update_timespec (struct stat const *statbuf, struct timespec *ts[2]) Return 0 on success, -1 (setting errno) on failure. */ int -gl_futimens (int fd, char const *file, struct timespec const timespec[2]) +fdutimens (char const *file, int fd, struct timespec const timespec[2]) { struct timespec adjusted_timespec[2]; struct timespec *ts = timespec ? adjusted_timespec : NULL; @@ -179,52 +196,64 @@ gl_futimens (int fd, char const *file, struct timespec const timespec[2]) #endif /* 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) + nanosecond resolution; newer Linux implements both functions via + a single syscall. 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 || HAVE_FUTIMENS + if (0 <= utimensat_works_really) { - int result = utimensat (AT_FDCWD, file, ts, 0); -# ifdef __linux__ - /* Work around a kernel bug: - http://bugzilla.redhat.com/442352 - http://bugzilla.redhat.com/449910 - It appears that utimensat can mistakenly return 280 rather - than -1 upon failure. - FIXME: remove in 2010 or whenever the offending kernels - are no longer in common use. */ - if (0 < result) - errno = ENOSYS; -# endif - - if (result == 0 || errno != ENOSYS) - return result; +# if HAVE_UTIMENSAT + if (fd < 0) + { + int result = utimensat (AT_FDCWD, file, ts, 0); +# ifdef __linux__ + /* Work around a kernel bug: + http://bugzilla.redhat.com/442352 + http://bugzilla.redhat.com/449910 + It appears that utimensat can mistakenly return 280 rather + than -1 upon ENOSYS failure. + FIXME: remove in 2010 or whenever the offending kernels + are no longer in common use. */ + if (0 < result) + errno = ENOSYS; +# endif /* __linux__ */ + if (result == 0 || errno != ENOSYS) + { + utimensat_works_really = 1; + return result; + } + } +# endif /* HAVE_UTIMENSAT */ +# if HAVE_FUTIMENS + { + int result = futimens (fd, ts); +# ifdef __linux__ + /* Work around the same bug as above. */ + if (0 < result) + errno = ENOSYS; +# endif /* __linux__ */ + if (result == 0 || errno != ENOSYS) + { + utimensat_works_really = 1; + return result; + } + } +# endif /* HAVE_FUTIMENS */ } -#endif -#if HAVE_FUTIMENS - { - int result = futimens (fd, timespec); -# ifdef __linux__ - /* Work around the same bug as above. */ - if (0 < result) - errno = ENOSYS; -# endif - if (result == 0 || errno != ENOSYS) - return result; - } -#endif + utimensat_works_really = -1; +#endif /* HAVE_UTIMENSAT || HAVE_FUTIMENS */ /* 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) + 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; } @@ -300,23 +329,41 @@ gl_futimens (int fd, char const *file, struct timespec const timespec[2]) } } +/* 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 -- + 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 +gl_futimens (int fd, char const *file, struct timespec const timespec[2]) +{ + return fdutimens (file, fd, timespec); +} + /* Set the access and modification time stamps of FILE to be TIMESPEC[0] and TIMESPEC[1], respectively. */ 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) { @@ -333,35 +380,38 @@ lutimens (char const *file, struct timespec const timespec[2]) worry about bogus return values. */ #if HAVE_UTIMENSAT - { - int result = utimensat (AT_FDCWD, file, ts, AT_SYMLINK_NOFOLLOW); + if (0 <= utimensat_works_really) + { + int result = utimensat (AT_FDCWD, file, ts, AT_SYMLINK_NOFOLLOW); # ifdef __linux__ - /* Work around a kernel bug: - http://bugzilla.redhat.com/442352 - http://bugzilla.redhat.com/449910 - It appears that utimensat can mistakenly return 280 rather - than -1 upon ENOSYS failure. - FIXME: remove in 2010 or whenever the offending kernels - are no longer in common use. */ - if (0 < result) - errno = ENOSYS; + /* Work around a kernel bug: + http://bugzilla.redhat.com/442352 + http://bugzilla.redhat.com/449910 + It appears that utimensat can mistakenly return 280 rather + than -1 upon ENOSYS failure. + FIXME: remove in 2010 or whenever the offending kernels + are no longer in common use. */ + if (0 < result) + errno = ENOSYS; # endif - - if (result == 0 || errno != ENOSYS) - return result; - } + if (result == 0 || errno != ENOSYS) + { + utimensat_works_really = 1; + return result; + } + } + utimensat_works_really = -1; #endif /* HAVE_UTIMENSAT */ /* 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) + if (adjustment_needed || REPLACE_FUNC_STAT_FILE) { - struct stat st; if (lstat (file, &st)) return -1; - if (update_timespec (&st, &ts)) + if (ts && update_timespec (&st, &ts)) return 0; } @@ -384,8 +434,11 @@ lutimens (char const *file, struct timespec const timespec[2]) } #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 || REPLACE_FUNC_STAT_FILE) && lstat (file, &st)) + return -1; + if (!S_ISLNK (st.st_mode)) + return fdutimens (file, -1, ts); errno = ENOSYS; return -1; }