utimens: cache whether utimensat syscall works
[gnulib.git] / lib / utimens.c
1 /* Set file access and modification times.
2
3    Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free
4    Software Foundation, Inc.
5
6    This program is free software: you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 3 of the License, or any
9    later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19 /* Written by Paul Eggert.  */
20
21 /* derived from a function in touch.c */
22
23 #include <config.h>
24
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.  */
53 #undef futimens
54
55 #if HAVE_UTIMENSAT || HAVE_FUTIMENS
56 /* Cache variable for whether syscall works; used to avoid calling the
57    syscall if we know it will just fail with ENOSYS.  0 = unknown, 1 =
58    yes, -1 = no.  */
59 static int utimensat_works_really;
60 #endif /* HAVE_UTIMENSAT || HAVE_UTIMENSAT */
61
62 /* Validate the requested timestamps.  Return 0 if the resulting
63    timespec can be used for utimensat (after possibly modifying it to
64    work around bugs in utimensat).  Return 1 if the timespec needs
65    further adjustment based on stat results for utimes or other less
66    powerful interfaces.  Return -1, with errno set to EINVAL, if
67    timespec is out of range.  */
68 static int
69 validate_timespec (struct timespec timespec[2])
70 {
71   int result = 0;
72   assert (timespec);
73   if ((timespec[0].tv_nsec != UTIME_NOW
74        && timespec[0].tv_nsec != UTIME_OMIT
75        && (timespec[0].tv_nsec < 0 || 1000000000 <= timespec[0].tv_nsec))
76       || (timespec[1].tv_nsec != UTIME_NOW
77           && timespec[1].tv_nsec != UTIME_OMIT
78           && (timespec[1].tv_nsec < 0 || 1000000000 <= timespec[1].tv_nsec)))
79     {
80       errno = EINVAL;
81       return -1;
82     }
83   /* Work around Linux kernel 2.6.25 bug, where utimensat fails with
84      EINVAL if tv_sec is not 0 when using the flag values of
85      tv_nsec.  */
86   if (timespec[0].tv_nsec == UTIME_NOW
87       || timespec[0].tv_nsec == UTIME_OMIT)
88     {
89       timespec[0].tv_sec = 0;
90       result = 1;
91     }
92   if (timespec[1].tv_nsec == UTIME_NOW
93       || timespec[1].tv_nsec == UTIME_OMIT)
94     {
95       timespec[1].tv_sec = 0;
96       result = 1;
97     }
98   return result;
99 }
100
101 /* Normalize any UTIME_NOW or UTIME_OMIT values in *TS, using stat
102    buffer STATBUF to obtain the current timestamps of the file.  If
103    both times are UTIME_NOW, set *TS to NULL (as this can avoid some
104    permissions issues).  If both times are UTIME_OMIT, return true
105    (nothing further beyond the prior collection of STATBUF is
106    necessary); otherwise return false.  */
107 static bool
108 update_timespec (struct stat const *statbuf, struct timespec *ts[2])
109 {
110   struct timespec *timespec = *ts;
111   if (timespec[0].tv_nsec == UTIME_OMIT
112       && timespec[1].tv_nsec == UTIME_OMIT)
113     return true;
114   if (timespec[0].tv_nsec == UTIME_NOW
115       && timespec[1].tv_nsec == UTIME_NOW)
116     {
117       *ts = NULL;
118       return false;
119     }
120
121   if (timespec[0].tv_nsec == UTIME_OMIT)
122     timespec[0] = get_stat_atime (statbuf);
123   else if (timespec[0].tv_nsec == UTIME_NOW)
124     gettime (&timespec[0]);
125
126   if (timespec[1].tv_nsec == UTIME_OMIT)
127     timespec[1] = get_stat_mtime (statbuf);
128   else if (timespec[1].tv_nsec == UTIME_NOW)
129     gettime (&timespec[1]);
130
131   return false;
132 }
133
134 /* Set the access and modification time stamps of FD (a.k.a. FILE) to be
135    TIMESPEC[0] and TIMESPEC[1], respectively.
136    FD must be either negative -- in which case it is ignored --
137    or a file descriptor that is open on FILE.
138    If FD is nonnegative, then FILE can be NULL, which means
139    use just futimes (or equivalent) instead of utimes (or equivalent),
140    and fail if on an old system without futimes (or equivalent).
141    If TIMESPEC is null, set the time stamps to the current time.
142    Return 0 on success, -1 (setting errno) on failure.  */
143
144 int
145 fdutimens (char const *file, int fd, struct timespec const timespec[2])
146 {
147   struct timespec adjusted_timespec[2];
148   struct timespec *ts = timespec ? adjusted_timespec : NULL;
149   int adjustment_needed = 0;
150
151   if (ts)
152     {
153       adjusted_timespec[0] = timespec[0];
154       adjusted_timespec[1] = timespec[1];
155       adjustment_needed = validate_timespec (ts);
156     }
157   if (adjustment_needed < 0)
158     return -1;
159
160   /* Require that at least one of FD or FILE are valid.  Works around
161      a Linux bug where futimens (AT_FDCWD, NULL) changes "." rather
162      than failing.  */
163   if (!file)
164     {
165       if (fd < 0)
166         {
167           errno = EBADF;
168           return -1;
169         }
170       if (dup2 (fd, fd) != fd)
171         return -1;
172     }
173
174   /* Some Linux-based NFS clients are buggy, and mishandle time stamps
175      of files in NFS file systems in some cases.  We have no
176      configure-time test for this, but please see
177      <http://bugs.gentoo.org/show_bug.cgi?id=132673> for references to
178      some of the problems with Linux 2.6.16.  If this affects you,
179      compile with -DHAVE_BUGGY_NFS_TIME_STAMPS; this is reported to
180      help in some cases, albeit at a cost in performance.  But you
181      really should upgrade your kernel to a fixed version, since the
182      problem affects many applications.  */
183
184 #if HAVE_BUGGY_NFS_TIME_STAMPS
185   if (fd < 0)
186     sync ();
187   else
188     fsync (fd);
189 #endif
190
191   /* POSIX 2008 added two interfaces to set file timestamps with
192      nanosecond resolution; newer Linux implements both functions via
193      a single syscall.  We provide a fallback for ENOSYS (for example,
194      compiling against Linux 2.6.25 kernel headers and glibc 2.7, but
195      running on Linux 2.6.18 kernel).  */
196 #if HAVE_UTIMENSAT || HAVE_FUTIMENS
197   if (0 <= utimensat_works_really)
198     {
199 # if HAVE_UTIMENSAT
200       if (fd < 0)
201         {
202           int result = utimensat (AT_FDCWD, file, ts, 0);
203 #  ifdef __linux__
204           /* Work around a kernel bug:
205              http://bugzilla.redhat.com/442352
206              http://bugzilla.redhat.com/449910
207              It appears that utimensat can mistakenly return 280 rather
208              than -1 upon ENOSYS failure.
209              FIXME: remove in 2010 or whenever the offending kernels
210              are no longer in common use.  */
211           if (0 < result)
212             errno = ENOSYS;
213 #  endif /* __linux__ */
214           if (result == 0 || errno != ENOSYS)
215             {
216               utimensat_works_really = 1;
217               return result;
218             }
219         }
220 # endif /* HAVE_UTIMENSAT */
221 # if HAVE_FUTIMENS
222       {
223         int result = futimens (fd, timespec);
224 #  ifdef __linux__
225         /* Work around the same bug as above.  */
226         if (0 < result)
227           errno = ENOSYS;
228 #  endif /* __linux__ */
229         if (result == 0 || errno != ENOSYS)
230           {
231             utimensat_works_really = 1;
232             return result;
233           }
234       }
235 # endif /* HAVE_FUTIMENS */
236     }
237   utimensat_works_really = -1;
238 #endif /* HAVE_UTIMENSAT || HAVE_FUTIMENS */
239
240   /* The platform lacks an interface to set file timestamps with
241      nanosecond resolution, so do the best we can, discarding any
242      fractional part of the timestamp.  */
243
244   if (adjustment_needed)
245     {
246       struct stat st;
247       if (fd < 0 ? stat (file, &st) : fstat (fd, &st))
248         return -1;
249       if (update_timespec (&st, &ts))
250         return 0;
251     }
252
253   {
254 #if HAVE_FUTIMESAT || HAVE_WORKING_UTIMES
255     struct timeval timeval[2];
256     struct timeval const *t;
257     if (ts)
258       {
259         timeval[0].tv_sec = ts[0].tv_sec;
260         timeval[0].tv_usec = ts[0].tv_nsec / 1000;
261         timeval[1].tv_sec = ts[1].tv_sec;
262         timeval[1].tv_usec = ts[1].tv_nsec / 1000;
263         t = timeval;
264       }
265     else
266       t = NULL;
267
268     if (fd < 0)
269       {
270 # if HAVE_FUTIMESAT
271         return futimesat (AT_FDCWD, file, t);
272 # endif
273       }
274     else
275       {
276         /* If futimesat or futimes fails here, don't try to speed things
277            up by returning right away.  glibc can incorrectly fail with
278            errno == ENOENT if /proc isn't mounted.  Also, Mandrake 10.0
279            in high security mode doesn't allow ordinary users to read
280            /proc/self, so glibc incorrectly fails with errno == EACCES.
281            If errno == EIO, EPERM, or EROFS, it's probably safe to fail
282            right away, but these cases are rare enough that they're not
283            worth optimizing, and who knows what other messed-up systems
284            are out there?  So play it safe and fall back on the code
285            below.  */
286 # if HAVE_FUTIMESAT
287         if (futimesat (fd, NULL, t) == 0)
288           return 0;
289 # elif HAVE_FUTIMES
290         if (futimes (fd, t) == 0)
291           return 0;
292 # endif
293       }
294 #endif /* HAVE_FUTIMESAT || HAVE_WORKING_UTIMES */
295
296     if (!file)
297       {
298 #if ! (HAVE_FUTIMESAT || (HAVE_WORKING_UTIMES && HAVE_FUTIMES))
299         errno = ENOSYS;
300 #endif
301         return -1;
302       }
303
304 #if HAVE_WORKING_UTIMES
305     return utimes (file, t);
306 #else
307     {
308       struct utimbuf utimbuf;
309       struct utimbuf *ut;
310       if (ts)
311         {
312           utimbuf.actime = ts[0].tv_sec;
313           utimbuf.modtime = ts[1].tv_sec;
314           ut = &utimbuf;
315         }
316       else
317         ut = NULL;
318
319       return utime (file, ut);
320     }
321 #endif /* !HAVE_WORKING_UTIMES */
322   }
323 }
324
325 /* Set the access and modification time stamps of FD (a.k.a. FILE) to be
326    TIMESPEC[0] and TIMESPEC[1], respectively.
327    FD must be either negative -- in which case it is ignored --
328    or a file descriptor that is open on FILE.
329    If FD is nonnegative, then FILE can be NULL, which means
330    use just futimes (or equivalent) instead of utimes (or equivalent),
331    and fail if on an old system without futimes (or equivalent).
332    If TIMESPEC is null, set the time stamps to the current time.
333    Return 0 on success, -1 (setting errno) on failure.  */
334
335 int
336 gl_futimens (int fd, char const *file, struct timespec const timespec[2])
337 {
338   return fdutimens (file, fd, timespec);
339 }
340
341 /* Set the access and modification time stamps of FILE to be
342    TIMESPEC[0] and TIMESPEC[1], respectively.  */
343 int
344 utimens (char const *file, struct timespec const timespec[2])
345 {
346   return gl_futimens (-1, file, timespec);
347 }
348
349 /* Set the access and modification time stamps of the symlink FILE to
350    be TIMESPEC[0] and TIMESPEC[1], respectively.  Fail with ENOSYS if
351    the platform does not support changing symlink timestamps.  */
352 int
353 lutimens (char const *file, struct timespec const timespec[2])
354 {
355   struct timespec adjusted_timespec[2];
356   struct timespec *ts = timespec ? adjusted_timespec : NULL;
357   int adjustment_needed = 0;
358
359   if (ts)
360     {
361       adjusted_timespec[0] = timespec[0];
362       adjusted_timespec[1] = timespec[1];
363       adjustment_needed = validate_timespec (ts);
364     }
365   if (adjustment_needed < 0)
366     return -1;
367
368   /* The Linux kernel did not support symlink timestamps until
369      utimensat, in version 2.6.22, so we don't need to mimic
370      gl_futimens' worry about buggy NFS clients.  But we do have to
371      worry about bogus return values.  */
372
373 #if HAVE_UTIMENSAT
374   if (0 <= utimensat_works_really)
375     {
376       int result = utimensat (AT_FDCWD, file, ts, AT_SYMLINK_NOFOLLOW);
377 # ifdef __linux__
378       /* Work around a kernel bug:
379          http://bugzilla.redhat.com/442352
380          http://bugzilla.redhat.com/449910
381          It appears that utimensat can mistakenly return 280 rather
382          than -1 upon ENOSYS failure.
383          FIXME: remove in 2010 or whenever the offending kernels
384          are no longer in common use.  */
385       if (0 < result)
386         errno = ENOSYS;
387 # endif
388       if (result == 0 || errno != ENOSYS)
389         {
390           utimensat_works_really = 1;
391           return result;
392         }
393     }
394   utimensat_works_really = -1;
395 #endif /* HAVE_UTIMENSAT */
396
397   /* The platform lacks an interface to set file timestamps with
398      nanosecond resolution, so do the best we can, discarding any
399      fractional part of the timestamp.  */
400
401   if (adjustment_needed)
402     {
403       struct stat st;
404       if (lstat (file, &st))
405         return -1;
406       if (update_timespec (&st, &ts))
407         return 0;
408     }
409
410 #if HAVE_LUTIMES
411   {
412     struct timeval timeval[2];
413     struct timeval const *t;
414     if (ts)
415       {
416         timeval[0].tv_sec = ts[0].tv_sec;
417         timeval[0].tv_usec = ts[0].tv_nsec / 1000;
418         timeval[1].tv_sec = ts[1].tv_sec;
419         timeval[1].tv_usec = ts[1].tv_nsec / 1000;
420         t = timeval;
421       }
422     else
423       t = NULL;
424
425     return lutimes (file, t);
426   }
427 #endif /* HAVE_LUTIMES */
428
429   /* Out of luck.  Symlink timestamps can't be changed.  We won't
430      bother changing the timestamps if FILE was not a symlink.  */
431   errno = ENOSYS;
432   return -1;
433 }