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