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