+ int result;
+# if defined __linux__ || defined __sun
+ struct stat st;
+ /* 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 [l]stat 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.
+
+ The same bug occurs in Solaris 11.1 (Apr 2013).
+
+ FIXME: Simplify this for Linux in 2016 and for Solaris in
+ 2024, when file system bugs are no longer common. */
+ if (times && (times[0].tv_nsec == UTIME_OMIT
+ || times[1].tv_nsec == UTIME_OMIT))
+ {
+ if (fstatat (fd, file, &st, flag))
+ return -1;
+ if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT)
+ return 0;
+ if (times[0].tv_nsec == UTIME_OMIT)
+ ts[0] = get_stat_atime (&st);
+ else
+ ts[0] = times[0];
+ if (times[1].tv_nsec == UTIME_OMIT)
+ ts[1] = get_stat_mtime (&st);
+ else
+ ts[1] = times[1];
+ times = ts;
+ }
+# ifdef __hppa__
+ /* Linux kernel 2.6.22.19 on hppa does not reject invalid tv_nsec
+ values. */
+ else if (times
+ && ((times[0].tv_nsec != UTIME_NOW
+ && ! (0 <= times[0].tv_nsec
+ && times[0].tv_nsec < TIMESPEC_RESOLUTION))
+ || (times[1].tv_nsec != UTIME_NOW
+ && ! (0 <= times[1].tv_nsec
+ && times[1].tv_nsec < TIMESPEC_RESOLUTION))))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+# endif
+# endif
+ result = utimensat (fd, file, times, flag);