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