X-Git-Url: https://erislabs.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=lib%2Futimens.c;h=5603600bf911bd20b5e73b6620db58172a5304bc;hb=ec60d8959e16a743f09704c397931f2621f511ae;hp=9119bc430ec39cdd69b121087fb27a62a8b2ccd1;hpb=aba351689b1b5ce068b76d112c1f4929ac26ab9c;p=gnulib.git diff --git a/lib/utimens.c b/lib/utimens.c index 9119bc430..5603600bf 100644 --- a/lib/utimens.c +++ b/lib/utimens.c @@ -49,6 +49,25 @@ struct utimbuf }; #endif +/* Avoid recursion with rpl_futimens or rpl_utimensat. */ +#undef futimens +#undef utimensat + +#if HAVE_UTIMENSAT || HAVE_FUTIMENS +/* Cache variables for whether the utimensat syscall works; used to + avoid calling the syscall if we know it will just fail with ENOSYS. + There are some Linux kernel versions where a flag of 0 passes, but + not AT_SYMLINK_NOFOLLOW. 0 = unknown, 1 = yes, -1 = no. */ +static int utimensat_works_really; +static int lutimensat_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 @@ -179,52 +198,65 @@ fdutimens (char const *file, int fd, 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; + lutimensat_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; } @@ -261,7 +293,7 @@ fdutimens (char const *file, int fd, struct timespec const timespec[2]) worth optimizing, and who knows what other messed-up systems are out there? So play it safe and fall back on the code below. */ -# if HAVE_FUTIMESAT +# if HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG if (futimesat (fd, NULL, t) == 0) return 0; # elif HAVE_FUTIMES @@ -273,7 +305,8 @@ fdutimens (char const *file, int fd, struct timespec const timespec[2]) if (!file) { -#if ! (HAVE_FUTIMESAT || (HAVE_WORKING_UTIMES && HAVE_FUTIMES)) +#if ! ((HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG) \ + || (HAVE_WORKING_UTIMES && HAVE_FUTIMES)) errno = ENOSYS; #endif return -1; @@ -321,18 +354,21 @@ gl_futimens (int fd, char const *file, struct timespec const timespec[2]) 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; + int result; if (ts) { @@ -349,39 +385,45 @@ 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 <= lutimensat_works_really) + { + 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; + lutimensat_works_really = 1; + return result; + } + } + lutimensat_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; } -#if HAVE_LUTIMES + /* On Linux, lutimes is a thin wrapper around utimensat, so there is + no point trying lutimes if utimensat failed with ENOSYS. */ +#if HAVE_LUTIMES && !HAVE_UTIMENSAT { struct timeval timeval[2]; struct timeval const *t; @@ -396,12 +438,17 @@ lutimens (char const *file, struct timespec const timespec[2]) else t = NULL; - return lutimes (file, t); + result = lutimes (file, t); + if (result == 0 || errno != ENOSYS) + return result; } -#endif /* HAVE_LUTIMES */ +#endif /* HAVE_LUTIMES && !HAVE_UTIMENSAT */ - /* 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; }