root/maint/gnulib/lib/utimens.c

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

/* [previous][next][first][last][top][bottom][index][help] */