diff options
Diffstat (limited to 'gnu/utimens.c')
-rw-r--r-- | gnu/utimens.c | 515 |
1 files changed, 0 insertions, 515 deletions
diff --git a/gnu/utimens.c b/gnu/utimens.c deleted file mode 100644 index 8ce10d1..0000000 --- a/gnu/utimens.c +++ /dev/null @@ -1,515 +0,0 @@ -/* -*- buffer-read-only: t -*- vi: set ro: */ -/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ -/* Set file access and modification times. - - Copyright (C) 2003-2010 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 <http://www.gnu.org/licenses/>. */ - -/* Written by Paul Eggert. */ - -/* derived from a function in touch.c */ - -#include <config.h> - -#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 - -/* Some systems (even some that do have <utime.h>) don't declare this - structure anywhere. */ -#ifndef HAVE_STRUCT_UTIMBUF -struct utimbuf -{ - long actime; - long modtime; -}; -#endif - -/* Avoid recursion with rpl_futimens or rpl_utimensat. */ -#undef futimens -#undef 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 - -#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, - and to avoid unnecessary work in massaging timestamps if the - syscall will work. Multiple variables are needed, to distinguish - between the following scenarios on Linux: - utimensat doesn't exist, or is in glibc but kernel 2.6.18 fails with ENOSYS - kernel 2.6.22 and earlier rejects AT_SYMLINK_NOFOLLOW - kernel 2.6.25 and earlier reject UTIME_NOW/UTIME_OMIT with non-zero tv_sec - kernel 2.6.32 used with xfs or ntfs-3g fail to honor UTIME_OMIT - utimensat completely works - For each cache variable: 0 = unknown, 1 = yes, -1 = no. */ -static int utimensat_works_really; -static int lutimensat_works_really; -#endif /* HAVE_UTIMENSAT || HAVE_FUTIMENS */ - -/* 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 a positive value if the - timespec needs further adjustment based on stat results: 1 if any - adjustment is needed for utimes, and 2 if any adjustment is needed - for Linux utimensat. Return -1, with errno set to EINVAL, if - timespec is out of range. */ -static int -validate_timespec (struct timespec timespec[2]) -{ - int result = 0; - int utime_omit_count = 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. - Flag a Linux kernel 2.6.32 bug, where an mtime of UTIME_OMIT - fails to bump ctime. */ - if (timespec[0].tv_nsec == UTIME_NOW - || timespec[0].tv_nsec == UTIME_OMIT) - { - timespec[0].tv_sec = 0; - result = 1; - if (timespec[0].tv_nsec == UTIME_OMIT) - utime_omit_count++; - } - if (timespec[1].tv_nsec == UTIME_NOW - || timespec[1].tv_nsec == UTIME_OMIT) - { - timespec[1].tv_sec = 0; - result = 1; - if (timespec[1].tv_nsec == UTIME_OMIT) - utime_omit_count++; - } - return result + (utime_omit_count == 1); -} - -/* 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 -- - 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 -fdutimens (char const *file, int fd, 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) - { - 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. */ - if (!file) - { - if (fd < 0) - { - errno = EBADF; - return -1; - } - if (dup2 (fd, fd) != fd) - return -1; - } - - /* Some Linux-based NFS clients are buggy, and mishandle time stamps - of files in NFS file systems in some cases. We have no - configure-time test for this, but please see - <http://bugs.gentoo.org/show_bug.cgi?id=132673> for references to - some of the problems with Linux 2.6.16. If this affects you, - compile with -DHAVE_BUGGY_NFS_TIME_STAMPS; this is reported to - help in some cases, albeit at a cost in performance. But you - really should upgrade your kernel to a fixed version, since the - problem affects many applications. */ - -#if HAVE_BUGGY_NFS_TIME_STAMPS - if (fd < 0) - sync (); - else - fsync (fd); -#endif - - /* POSIX 2008 added two interfaces to set file timestamps with - 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; -# if __linux__ - /* As recently as Linux kernel 2.6.32 (Dec 2009), several file - systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT, - but work if both times are either explicitly specified or - UTIME_NOW. Work around it with a preparatory [f]stat prior - to calling futimens/utimensat; fortunately, there is not much - timing impact due to the extra syscall even on file systems - where UTIME_OMIT would have worked. FIXME: Simplify this in - 2012, when file system bugs are no longer common. */ - if (adjustment_needed == 2) - { - if (fd < 0 ? stat (file, &st) : fstat (fd, &st)) - return -1; - if (ts[0].tv_nsec == UTIME_OMIT) - ts[0] = get_stat_atime (&st); - else if (ts[1].tv_nsec == UTIME_OMIT) - ts[1] = get_stat_mtime (&st); - /* Note that st is good, in case utimensat gives ENOSYS. */ - adjustment_needed++; - } -# endif /* __linux__ */ -# if HAVE_UTIMENSAT - if (fd < 0) - { - 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 - if (0 <= fd) - { - 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 */ - } - 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 || (REPLACE_FUNC_STAT_FILE && fd < 0)) - { - if (adjustment_needed != 3 - && (fd < 0 ? stat (file, &st) : fstat (fd, &st))) - return -1; - if (ts && update_timespec (&st, &ts)) - return 0; - } - - { -#if HAVE_FUTIMESAT || HAVE_WORKING_UTIMES - struct timeval timeval[2]; - struct timeval const *t; - if (ts) - { - 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 - t = NULL; - - if (fd < 0) - { -# if HAVE_FUTIMESAT - return futimesat (AT_FDCWD, file, t); -# endif - } - else - { - /* If futimesat or futimes fails here, don't try to speed things - up by returning right away. glibc can incorrectly fail with - errno == ENOENT if /proc isn't mounted. Also, Mandrake 10.0 - in high security mode doesn't allow ordinary users to read - /proc/self, so glibc incorrectly fails with errno == EACCES. - If errno == EIO, EPERM, or EROFS, it's probably safe to fail - right away, but these cases are rare enough that they're not - 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 && !FUTIMESAT_NULL_BUG - if (futimesat (fd, NULL, t) == 0) - return 0; -# elif HAVE_FUTIMES - if (futimes (fd, t) == 0) - return 0; -# endif - } -#endif /* HAVE_FUTIMESAT || HAVE_WORKING_UTIMES */ - - if (!file) - { -#if ! ((HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG) \ - || (HAVE_WORKING_UTIMES && HAVE_FUTIMES)) - errno = ENOSYS; -#endif - return -1; - } - -#if HAVE_WORKING_UTIMES - return utimes (file, t); -#else - { - struct utimbuf utimbuf; - struct utimbuf *ut; - if (ts) - { - utimbuf.actime = ts[0].tv_sec; - utimbuf.modtime = ts[1].tv_sec; - ut = &utimbuf; - } - else - ut = NULL; - - return utime (file, ut); - } -#endif /* !HAVE_WORKING_UTIMES */ - } -} - -/* 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 fdutimens (file, -1, timespec); -} - -/* 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) - { - 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 - worry about bogus return values. */ - -#if HAVE_UTIMENSAT - if (0 <= lutimensat_works_really) - { - int result; -# if __linux__ - /* As recently as Linux kernel 2.6.32 (Dec 2009), several file - systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT, - but work if both times are either explicitly specified or - UTIME_NOW. Work around it with a preparatory lstat prior to - calling utimensat; fortunately, there is not much timing - impact due to the extra syscall even on file systems where - UTIME_OMIT would have worked. FIXME: Simplify this in 2012, - when file system bugs are no longer common. */ - if (adjustment_needed == 2) - { - if (lstat (file, &st)) - return -1; - if (ts[0].tv_nsec == UTIME_OMIT) - ts[0] = get_stat_atime (&st); - else if (ts[1].tv_nsec == UTIME_OMIT) - ts[1] = get_stat_mtime (&st); - /* Note that st is good, in case utimensat gives ENOSYS. */ - adjustment_needed++; - } -# endif /* __linux__ */ - 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; -# endif - 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 || REPLACE_FUNC_STAT_FILE) - { - if (adjustment_needed != 3 && lstat (file, &st)) - return -1; - if (ts && update_timespec (&st, &ts)) - return 0; - } - - /* 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; - int result; - if (ts) - { - 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 - t = NULL; - - result = lutimes (file, t); - if (result == 0 || errno != ENOSYS) - return result; - } -#endif /* HAVE_LUTIMES && !HAVE_UTIMENSAT */ - - /* 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; -} |