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