(root)/
tar-1.35/
gnu/
utimens.c
       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  }