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