};
#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
#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;
}
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
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;
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)
{
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;
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;
}