root/lib/utimens.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. validate_timespec
  2. update_timespec
  3. fdutimens
  4. utimens
  5. lutimens

     1 /* Set file access and modification times.
     2 
     3    Copyright (C) 2003-2023 Free Software Foundation, Inc.
     4 
     5    This file is free software: you can redistribute it and/or modify
     6    it under the terms of the GNU Lesser General Public License as
     7    published by the Free Software Foundation, either version 3 of the
     8    License, or (at your option) any later version.
     9 
    10    This file is distributed in the hope that it will be useful,
    11    but WITHOUT ANY WARRANTY; without even the implied warranty of
    12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13    GNU Lesser General Public License for more details.
    14 
    15    You should have received a copy of the GNU Lesser General Public License
    16    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
    17 
    18 /* Written by Paul Eggert.  */
    19 
    20 /* derived from a function in touch.c */
    21 
    22 #include <config.h>
    23 
    24 #define _GL_UTIMENS_INLINE _GL_EXTERN_INLINE
    25 #include "utimens.h"
    26 
    27 #include <errno.h>
    28 #include <fcntl.h>
    29 #include <string.h>
    30 #include <sys/stat.h>
    31 #include <sys/time.h>
    32 #include <unistd.h>
    33 #include <utime.h>
    34 
    35 #include "stat-time.h"
    36 #include "timespec.h"
    37 
    38 /* On native Windows, use SetFileTime; but avoid this when compiling
    39    GNU Emacs, which arranges for this in some other way and which
    40    defines WIN32_LEAN_AND_MEAN itself.  */
    41 
    42 #if defined _WIN32 && ! defined __CYGWIN__ && ! defined EMACS_CONFIGURATION
    43 # define USE_SETFILETIME
    44 # define WIN32_LEAN_AND_MEAN
    45 # include <windows.h>
    46 # if GNULIB_MSVC_NOTHROW
    47 #  include "msvc-nothrow.h"
    48 # else
    49 #  include <io.h>
    50 # endif
    51 #endif
    52 
    53 /* Avoid recursion with rpl_futimens or rpl_utimensat.  */
    54 #undef futimens
    55 #if !HAVE_NEARLY_WORKING_UTIMENSAT
    56 # undef utimensat
    57 #endif
    58 
    59 /* Solaris 9 mistakenly succeeds when given a non-directory with a
    60    trailing slash.  Force the use of rpl_stat for a fix.  */
    61 #ifndef REPLACE_FUNC_STAT_FILE
    62 # define REPLACE_FUNC_STAT_FILE 0
    63 #endif
    64 
    65 #if HAVE_UTIMENSAT || HAVE_FUTIMENS
    66 /* Cache variables for whether the utimensat syscall works; used to
    67    avoid calling the syscall if we know it will just fail with ENOSYS,
    68    and to avoid unnecessary work in massaging timestamps if the
    69    syscall will work.  Multiple variables are needed, to distinguish
    70    between the following scenarios on Linux:
    71    utimensat doesn't exist, or is in glibc but kernel 2.6.18 fails with ENOSYS
    72    kernel 2.6.22 and earlier rejects AT_SYMLINK_NOFOLLOW
    73    kernel 2.6.25 and earlier reject UTIME_NOW/UTIME_OMIT with non-zero tv_sec
    74    kernel 2.6.32 used with xfs or ntfs-3g fail to honor UTIME_OMIT
    75    utimensat completely works
    76    For each cache variable: 0 = unknown, 1 = yes, -1 = no.  */
    77 static int utimensat_works_really;
    78 static int lutimensat_works_really;
    79 #endif /* HAVE_UTIMENSAT || HAVE_FUTIMENS */
    80 
    81 /* Validate the requested timestamps.  Return 0 if the resulting
    82    timespec can be used for utimensat (after possibly modifying it to
    83    work around bugs in utimensat).  Return a positive value if the
    84    timespec needs further adjustment based on stat results: 1 if any
    85    adjustment is needed for utimes, and 2 if any adjustment is needed
    86    for Linux utimensat.  Return -1, with errno set to EINVAL, if
    87    timespec is out of range.  */
    88 static int
    89 validate_timespec (struct timespec timespec[2])
    90 {
    91   int result = 0;
    92   int utime_omit_count = 0;
    93   if ((timespec[0].tv_nsec != UTIME_NOW
    94        && timespec[0].tv_nsec != UTIME_OMIT
    95        && ! (0 <= timespec[0].tv_nsec
    96              && timespec[0].tv_nsec < TIMESPEC_HZ))
    97       || (timespec[1].tv_nsec != UTIME_NOW
    98           && timespec[1].tv_nsec != UTIME_OMIT
    99           && ! (0 <= timespec[1].tv_nsec
   100                 && timespec[1].tv_nsec < TIMESPEC_HZ)))
   101     {
   102       errno = EINVAL;
   103       return -1;
   104     }
   105   /* Work around Linux kernel 2.6.25 bug, where utimensat fails with
   106      EINVAL if tv_sec is not 0 when using the flag values of tv_nsec.
   107      Flag a Linux kernel 2.6.32 bug, where an mtime of UTIME_OMIT
   108      fails to bump ctime.  */
   109   if (timespec[0].tv_nsec == UTIME_NOW
   110       || timespec[0].tv_nsec == UTIME_OMIT)
   111     {
   112       timespec[0].tv_sec = 0;
   113       result = 1;
   114       if (timespec[0].tv_nsec == UTIME_OMIT)
   115         utime_omit_count++;
   116     }
   117   if (timespec[1].tv_nsec == UTIME_NOW
   118       || timespec[1].tv_nsec == UTIME_OMIT)
   119     {
   120       timespec[1].tv_sec = 0;
   121       result = 1;
   122       if (timespec[1].tv_nsec == UTIME_OMIT)
   123         utime_omit_count++;
   124     }
   125   return result + (utime_omit_count == 1);
   126 }
   127 
   128 /* Normalize any UTIME_NOW or UTIME_OMIT values in (*TS)[0] and (*TS)[1],
   129    using STATBUF to obtain the current timestamps of the file.  If
   130    both times are UTIME_NOW, set *TS to NULL (as this can avoid some
   131    permissions issues).  If both times are UTIME_OMIT, return true
   132    (nothing further beyond the prior collection of STATBUF is
   133    necessary); otherwise return false.  */
   134 static bool
   135 update_timespec (struct stat const *statbuf, struct timespec **ts)
   136 {
   137   struct timespec *timespec = *ts;
   138   if (timespec[0].tv_nsec == UTIME_OMIT
   139       && timespec[1].tv_nsec == UTIME_OMIT)
   140     return true;
   141   if (timespec[0].tv_nsec == UTIME_NOW
   142       && timespec[1].tv_nsec == UTIME_NOW)
   143     {
   144       *ts = NULL;
   145       return false;
   146     }
   147 
   148   if (timespec[0].tv_nsec == UTIME_OMIT)
   149     timespec[0] = get_stat_atime (statbuf);
   150   else if (timespec[0].tv_nsec == UTIME_NOW)
   151     gettime (&timespec[0]);
   152 
   153   if (timespec[1].tv_nsec == UTIME_OMIT)
   154     timespec[1] = get_stat_mtime (statbuf);
   155   else if (timespec[1].tv_nsec == UTIME_NOW)
   156     gettime (&timespec[1]);
   157 
   158   return false;
   159 }
   160 
   161 /* Set the access and modification timestamps of FD (a.k.a. FILE) to be
   162    TIMESPEC[0] and TIMESPEC[1], respectively.
   163    FD must be either negative -- in which case it is ignored --
   164    or a file descriptor that is open on FILE.
   165    If FD is nonnegative, then FILE can be NULL, which means
   166    use just futimes (or equivalent) instead of utimes (or equivalent),
   167    and fail if on an old system without futimes (or equivalent).
   168    If TIMESPEC is null, set the timestamps to the current time.
   169    Return 0 on success, -1 (setting errno) on failure.  */
   170 
   171 int
   172 fdutimens (int fd, char const *file, struct timespec const timespec[2])
   173 {
   174   struct timespec adjusted_timespec[2];
   175   struct timespec *ts = timespec ? adjusted_timespec : NULL;
   176   int adjustment_needed = 0;
   177   struct stat st;
   178 
   179   if (ts)
   180     {
   181       adjusted_timespec[0] = timespec[0];
   182       adjusted_timespec[1] = timespec[1];
   183       adjustment_needed = validate_timespec (ts);
   184     }
   185   if (adjustment_needed < 0)
   186     return -1;
   187 
   188   /* Require that at least one of FD or FILE are potentially valid, to avoid
   189      a Linux bug where futimens (AT_FDCWD, NULL) changes "." rather
   190      than failing.  */
   191   if (fd < 0 && !file)
   192     {
   193       errno = EBADF;
   194       return -1;
   195     }
   196 
   197   /* Some Linux-based NFS clients are buggy, and mishandle timestamps
   198      of files in NFS file systems in some cases.  We have no
   199      configure-time test for this, but please see
   200      <https://bugs.gentoo.org/show_bug.cgi?id=132673> for references to
   201      some of the problems with Linux 2.6.16.  If this affects you,
   202      compile with -DHAVE_BUGGY_NFS_TIME_STAMPS; this is reported to
   203      help in some cases, albeit at a cost in performance.  But you
   204      really should upgrade your kernel to a fixed version, since the
   205      problem affects many applications.  */
   206 
   207 #if HAVE_BUGGY_NFS_TIME_STAMPS
   208   if (fd < 0)
   209     sync ();
   210   else
   211     fsync (fd);
   212 #endif
   213 
   214   /* POSIX 2008 added two interfaces to set file timestamps with
   215      nanosecond resolution; newer Linux implements both functions via
   216      a single syscall.  We provide a fallback for ENOSYS (for example,
   217      compiling against Linux 2.6.25 kernel headers and glibc 2.7, but
   218      running on Linux 2.6.18 kernel).  */
   219 #if HAVE_UTIMENSAT || HAVE_FUTIMENS
   220   if (0 <= utimensat_works_really)
   221     {
   222       int result;
   223 # if __linux__ || __sun
   224       /* As recently as Linux kernel 2.6.32 (Dec 2009), several file
   225          systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT,
   226          but work if both times are either explicitly specified or
   227          UTIME_NOW.  Work around it with a preparatory [f]stat prior
   228          to calling futimens/utimensat; fortunately, there is not much
   229          timing impact due to the extra syscall even on file systems
   230          where UTIME_OMIT would have worked.
   231 
   232          The same bug occurs in Solaris 11.1 (Apr 2013).
   233 
   234          FIXME: Simplify this for Linux in 2016 and for Solaris in
   235          2024, when file system bugs are no longer common.  */
   236       if (adjustment_needed == 2)
   237         {
   238           if (fd < 0 ? stat (file, &st) : fstat (fd, &st))
   239             return -1;
   240           if (ts[0].tv_nsec == UTIME_OMIT)
   241             ts[0] = get_stat_atime (&st);
   242           else if (ts[1].tv_nsec == UTIME_OMIT)
   243             ts[1] = get_stat_mtime (&st);
   244           /* Note that st is good, in case utimensat gives ENOSYS.  */
   245           adjustment_needed++;
   246         }
   247 # endif
   248 # if HAVE_UTIMENSAT
   249       if (fd < 0)
   250         {
   251 #  if defined __APPLE__ && defined __MACH__
   252           size_t len = strlen (file);
   253           if (len > 0 && file[len - 1] == '/')
   254             {
   255               struct stat statbuf;
   256               if (stat (file, &statbuf) < 0)
   257                 return -1;
   258               if (!S_ISDIR (statbuf.st_mode))
   259                 {
   260                   errno = ENOTDIR;
   261                   return -1;
   262                 }
   263             }
   264 #  endif
   265           result = utimensat (AT_FDCWD, file, ts, 0);
   266 #  ifdef __linux__
   267           /* Work around a kernel bug:
   268              https://bugzilla.redhat.com/show_bug.cgi?id=442352
   269              https://bugzilla.redhat.com/show_bug.cgi?id=449910
   270              It appears that utimensat can mistakenly return 280 rather
   271              than -1 upon ENOSYS failure.
   272              FIXME: remove in 2010 or whenever the offending kernels
   273              are no longer in common use.  */
   274           if (0 < result)
   275             errno = ENOSYS;
   276 #  endif /* __linux__ */
   277           if (result == 0 || errno != ENOSYS)
   278             {
   279               utimensat_works_really = 1;
   280               return result;
   281             }
   282         }
   283 # endif /* HAVE_UTIMENSAT */
   284 # if HAVE_FUTIMENS
   285       if (0 <= fd)
   286         {
   287           result = futimens (fd, ts);
   288 #  ifdef __linux__
   289           /* Work around the same bug as above.  */
   290           if (0 < result)
   291             errno = ENOSYS;
   292 #  endif /* __linux__ */
   293           if (result == 0 || errno != ENOSYS)
   294             {
   295               utimensat_works_really = 1;
   296               return result;
   297             }
   298         }
   299 # endif /* HAVE_FUTIMENS */
   300     }
   301   utimensat_works_really = -1;
   302   lutimensat_works_really = -1;
   303 #endif /* HAVE_UTIMENSAT || HAVE_FUTIMENS */
   304 
   305 #ifdef USE_SETFILETIME
   306   /* On native Windows, use SetFileTime(). See
   307      <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfiletime>
   308      <https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime>  */
   309   if (0 <= fd)
   310     {
   311       HANDLE handle;
   312       FILETIME current_time;
   313       FILETIME last_access_time;
   314       FILETIME last_write_time;
   315 
   316       handle = (HANDLE) _get_osfhandle (fd);
   317       if (handle == INVALID_HANDLE_VALUE)
   318         {
   319           errno = EBADF;
   320           return -1;
   321         }
   322 
   323       if (ts == NULL || ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW)
   324         {
   325           /* GetSystemTimeAsFileTime
   326              <https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime>.
   327              It would be overkill to use
   328              GetSystemTimePreciseAsFileTime
   329              <https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime>.  */
   330           GetSystemTimeAsFileTime (&current_time);
   331         }
   332 
   333       if (ts == NULL || ts[0].tv_nsec == UTIME_NOW)
   334         {
   335           last_access_time = current_time;
   336         }
   337       else if (ts[0].tv_nsec == UTIME_OMIT)
   338         {
   339           last_access_time.dwLowDateTime = 0;
   340           last_access_time.dwHighDateTime = 0;
   341         }
   342       else
   343         {
   344           ULONGLONG time_since_16010101 =
   345             (ULONGLONG) ts[0].tv_sec * 10000000 + ts[0].tv_nsec / 100 + 116444736000000000LL;
   346           last_access_time.dwLowDateTime = (DWORD) time_since_16010101;
   347           last_access_time.dwHighDateTime = time_since_16010101 >> 32;
   348         }
   349 
   350       if (ts == NULL || ts[1].tv_nsec == UTIME_NOW)
   351         {
   352           last_write_time = current_time;
   353         }
   354       else if (ts[1].tv_nsec == UTIME_OMIT)
   355         {
   356           last_write_time.dwLowDateTime = 0;
   357           last_write_time.dwHighDateTime = 0;
   358         }
   359       else
   360         {
   361           ULONGLONG time_since_16010101 =
   362             (ULONGLONG) ts[1].tv_sec * 10000000 + ts[1].tv_nsec / 100 + 116444736000000000LL;
   363           last_write_time.dwLowDateTime = (DWORD) time_since_16010101;
   364           last_write_time.dwHighDateTime = time_since_16010101 >> 32;
   365         }
   366 
   367       if (SetFileTime (handle, NULL, &last_access_time, &last_write_time))
   368         return 0;
   369       else
   370         {
   371           DWORD sft_error = GetLastError ();
   372           #if 0
   373           fprintf (stderr, "fdutimens SetFileTime error 0x%x\n", (unsigned int) sft_error);
   374           #endif
   375           switch (sft_error)
   376             {
   377             case ERROR_ACCESS_DENIED: /* fd was opened without O_RDWR */
   378               errno = EACCES; /* not specified by POSIX */
   379               break;
   380             default:
   381               errno = EINVAL;
   382               break;
   383             }
   384           return -1;
   385         }
   386     }
   387 #endif
   388 
   389   /* The platform lacks an interface to set file timestamps with
   390      nanosecond resolution, so do the best we can, discarding any
   391      fractional part of the timestamp.  */
   392 
   393   if (adjustment_needed || (REPLACE_FUNC_STAT_FILE && fd < 0))
   394     {
   395       if (adjustment_needed != 3
   396           && (fd < 0 ? stat (file, &st) : fstat (fd, &st)))
   397         return -1;
   398       if (ts && update_timespec (&st, &ts))
   399         return 0;
   400     }
   401 
   402   {
   403 #if HAVE_FUTIMESAT || HAVE_WORKING_UTIMES
   404     struct timeval timeval[2];
   405     struct timeval *t;
   406     if (ts)
   407       {
   408         timeval[0] = (struct timeval) { .tv_sec  = ts[0].tv_sec,
   409                                         .tv_usec = ts[0].tv_nsec / 1000 };
   410         timeval[1] = (struct timeval) { .tv_sec  = ts[1].tv_sec,
   411                                         .tv_usec = ts[1].tv_nsec / 1000 };
   412         t = timeval;
   413       }
   414     else
   415       t = NULL;
   416 
   417     if (fd < 0)
   418       {
   419 # if HAVE_FUTIMESAT
   420         return futimesat (AT_FDCWD, file, t);
   421 # endif
   422       }
   423     else
   424       {
   425         /* If futimesat or futimes fails here, don't try to speed things
   426            up by returning right away.  glibc can incorrectly fail with
   427            errno == ENOENT if /proc isn't mounted.  Also, Mandrake 10.0
   428            in high security mode doesn't allow ordinary users to read
   429            /proc/self, so glibc incorrectly fails with errno == EACCES.
   430            If errno == EIO, EPERM, or EROFS, it's probably safe to fail
   431            right away, but these cases are rare enough that they're not
   432            worth optimizing, and who knows what other messed-up systems
   433            are out there?  So play it safe and fall back on the code
   434            below.  */
   435 
   436 # if (HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG) || HAVE_FUTIMES
   437 #  if HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG
   438 #   undef futimes
   439 #   define futimes(fd, t) futimesat (fd, NULL, t)
   440 #  endif
   441         if (futimes (fd, t) == 0)
   442           {
   443 #  if __linux__ && __GLIBC__
   444             /* Work around a longstanding glibc bug, still present as
   445                of 2010-12-27.  On older Linux kernels that lack both
   446                utimensat and utimes, glibc's futimes rounds instead of
   447                truncating when falling back on utime.  The same bug
   448                occurs in futimesat with a null 2nd arg.  */
   449             if (t)
   450               {
   451                 bool abig = 500000 <= t[0].tv_usec;
   452                 bool mbig = 500000 <= t[1].tv_usec;
   453                 if ((abig | mbig) && fstat (fd, &st) == 0)
   454                   {
   455                     /* If these two subtractions overflow, they'll
   456                        track the overflows inside the buggy glibc.  */
   457                     time_t adiff = st.st_atime - t[0].tv_sec;
   458                     time_t mdiff = st.st_mtime - t[1].tv_sec;
   459 
   460                     struct timeval *tt = NULL;
   461                     struct timeval truncated_timeval[2];
   462                     truncated_timeval[0] = t[0];
   463                     truncated_timeval[1] = t[1];
   464                     if (abig && adiff == 1 && get_stat_atime_ns (&st) == 0)
   465                       {
   466                         tt = truncated_timeval;
   467                         tt[0].tv_usec = 0;
   468                       }
   469                     if (mbig && mdiff == 1 && get_stat_mtime_ns (&st) == 0)
   470                       {
   471                         tt = truncated_timeval;
   472                         tt[1].tv_usec = 0;
   473                       }
   474                     if (tt)
   475                       futimes (fd, tt);
   476                   }
   477               }
   478 #  endif
   479 
   480             return 0;
   481           }
   482 # endif
   483       }
   484 #endif /* HAVE_FUTIMESAT || HAVE_WORKING_UTIMES */
   485 
   486     if (!file)
   487       {
   488 #if ! ((HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG)          \
   489         || (HAVE_WORKING_UTIMES && HAVE_FUTIMES))
   490         errno = ENOSYS;
   491 #endif
   492         return -1;
   493       }
   494 
   495 #ifdef USE_SETFILETIME
   496     return _gl_utimens_windows (file, ts);
   497 #elif HAVE_WORKING_UTIMES
   498     return utimes (file, t);
   499 #else
   500     {
   501       struct utimbuf utimbuf;
   502       struct utimbuf *ut;
   503       if (ts)
   504         {
   505           utimbuf = (struct utimbuf) { .actime  = ts[0].tv_sec,
   506                                        .modtime = ts[1].tv_sec };
   507           ut = &utimbuf;
   508         }
   509       else
   510         ut = NULL;
   511 
   512       return utime (file, ut);
   513     }
   514 #endif /* !HAVE_WORKING_UTIMES */
   515   }
   516 }
   517 
   518 /* Set the access and modification timestamps of FILE to be
   519    TIMESPEC[0] and TIMESPEC[1], respectively.  */
   520 int
   521 utimens (char const *file, struct timespec const timespec[2])
   522 {
   523   return fdutimens (-1, file, timespec);
   524 }
   525 
   526 /* Set the access and modification timestamps of FILE to be
   527    TIMESPEC[0] and TIMESPEC[1], respectively, without dereferencing
   528    symlinks.  Fail with ENOSYS if the platform does not support
   529    changing symlink timestamps, but FILE was a symlink.  */
   530 int
   531 lutimens (char const *file, struct timespec const timespec[2])
   532 {
   533   struct timespec adjusted_timespec[2];
   534   struct timespec *ts = timespec ? adjusted_timespec : NULL;
   535   int adjustment_needed = 0;
   536   struct stat st;
   537 
   538   if (ts)
   539     {
   540       adjusted_timespec[0] = timespec[0];
   541       adjusted_timespec[1] = timespec[1];
   542       adjustment_needed = validate_timespec (ts);
   543     }
   544   if (adjustment_needed < 0)
   545     return -1;
   546 
   547   /* The Linux kernel did not support symlink timestamps until
   548      utimensat, in version 2.6.22, so we don't need to mimic
   549      fdutimens' worry about buggy NFS clients.  But we do have to
   550      worry about bogus return values.  */
   551 
   552 #if HAVE_UTIMENSAT
   553   if (0 <= lutimensat_works_really)
   554     {
   555       int result;
   556 # if __linux__ || __sun
   557       /* As recently as Linux kernel 2.6.32 (Dec 2009), several file
   558          systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT,
   559          but work if both times are either explicitly specified or
   560          UTIME_NOW.  Work around it with a preparatory lstat prior to
   561          calling utimensat; fortunately, there is not much timing
   562          impact due to the extra syscall even on file systems where
   563          UTIME_OMIT would have worked.
   564 
   565          The same bug occurs in Solaris 11.1 (Apr 2013).
   566 
   567          FIXME: Simplify this for Linux in 2016 and for Solaris in
   568          2024, when file system bugs are no longer common.  */
   569       if (adjustment_needed == 2)
   570         {
   571           if (lstat (file, &st))
   572             return -1;
   573           if (ts[0].tv_nsec == UTIME_OMIT)
   574             ts[0] = get_stat_atime (&st);
   575           else if (ts[1].tv_nsec == UTIME_OMIT)
   576             ts[1] = get_stat_mtime (&st);
   577           /* Note that st is good, in case utimensat gives ENOSYS.  */
   578           adjustment_needed++;
   579         }
   580 # endif
   581       result = utimensat (AT_FDCWD, file, ts, AT_SYMLINK_NOFOLLOW);
   582 # ifdef __linux__
   583       /* Work around a kernel bug:
   584          https://bugzilla.redhat.com/show_bug.cgi?id=442352
   585          https://bugzilla.redhat.com/show_bug.cgi?id=449910
   586          It appears that utimensat can mistakenly return 280 rather
   587          than -1 upon ENOSYS failure.
   588          FIXME: remove in 2010 or whenever the offending kernels
   589          are no longer in common use.  */
   590       if (0 < result)
   591         errno = ENOSYS;
   592 # endif
   593       if (result == 0 || errno != ENOSYS)
   594         {
   595           utimensat_works_really = 1;
   596           lutimensat_works_really = 1;
   597           return result;
   598         }
   599     }
   600   lutimensat_works_really = -1;
   601 #endif /* HAVE_UTIMENSAT */
   602 
   603   /* The platform lacks an interface to set file timestamps with
   604      nanosecond resolution, so do the best we can, discarding any
   605      fractional part of the timestamp.  */
   606 
   607   if (adjustment_needed || REPLACE_FUNC_STAT_FILE)
   608     {
   609       if (adjustment_needed != 3 && lstat (file, &st))
   610         return -1;
   611       if (ts && update_timespec (&st, &ts))
   612         return 0;
   613     }
   614 
   615   /* On Linux, lutimes is a thin wrapper around utimensat, so there is
   616      no point trying lutimes if utimensat failed with ENOSYS.  */
   617 #if HAVE_LUTIMES && !HAVE_UTIMENSAT
   618   {
   619     struct timeval timeval[2];
   620     struct timeval *t;
   621     int result;
   622     if (ts)
   623       {
   624         timeval[0] = (struct timeval) { .tv_sec = ts[0].tv_sec,
   625                                         .tv_usec = ts[0].tv_nsec / 1000 };
   626         timeval[1] = (struct timeval) { .tv_sec = ts[1].tv_sec,
   627                                         .tv_usec = ts[1].tv_nsec / 1000 };
   628         t = timeval;
   629       }
   630     else
   631       t = NULL;
   632 
   633     result = lutimes (file, t);
   634     if (result == 0 || errno != ENOSYS)
   635       return result;
   636   }
   637 #endif /* HAVE_LUTIMES && !HAVE_UTIMENSAT */
   638 
   639   /* Out of luck for symlinks, but we still handle regular files.  */
   640   if (!(adjustment_needed || REPLACE_FUNC_STAT_FILE) && lstat (file, &st))
   641     return -1;
   642   if (!S_ISLNK (st.st_mode))
   643     return fdutimens (-1, file, ts);
   644   errno = ENOSYS;
   645   return -1;
   646 }

/* [<][>][^][v][top][bottom][index][help] */