utimens: fix regression on Solaris
[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 /* 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, ts);
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 && !FUTIMESAT_NULL_BUG
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 && !FUTIMESAT_NULL_BUG)          \
306         || (HAVE_WORKING_UTIMES && HAVE_FUTIMES))
307         errno = ENOSYS;
308 #endif
309         return -1;
310       }
311
312 #if HAVE_WORKING_UTIMES
313     return utimes (file, t);
314 #else
315     {
316       struct utimbuf utimbuf;
317       struct utimbuf *ut;
318       if (ts)
319         {
320           utimbuf.actime = ts[0].tv_sec;
321           utimbuf.modtime = ts[1].tv_sec;
322           ut = &utimbuf;
323         }
324       else
325         ut = NULL;
326
327       return utime (file, ut);
328     }
329 #endif /* !HAVE_WORKING_UTIMES */
330   }
331 }
332
333 /* Set the access and modification time stamps of FD (a.k.a. FILE) to be
334    TIMESPEC[0] and TIMESPEC[1], respectively.
335    FD must be either negative -- in which case it is ignored --
336    or a file descriptor that is open on FILE.
337    If FD is nonnegative, then FILE can be NULL, which means
338    use just futimes (or equivalent) instead of utimes (or equivalent),
339    and fail if on an old system without futimes (or equivalent).
340    If TIMESPEC is null, set the time stamps to the current time.
341    Return 0 on success, -1 (setting errno) on failure.  */
342
343 int
344 gl_futimens (int fd, char const *file, struct timespec const timespec[2])
345 {
346   return fdutimens (file, fd, timespec);
347 }
348
349 /* Set the access and modification time stamps of FILE to be
350    TIMESPEC[0] and TIMESPEC[1], respectively.  */
351 int
352 utimens (char const *file, struct timespec const timespec[2])
353 {
354   return fdutimens (file, -1, timespec);
355 }
356
357 /* Set the access and modification time stamps of FILE to be
358    TIMESPEC[0] and TIMESPEC[1], respectively, without dereferencing
359    symlinks.  Fail with ENOSYS if the platform does not support
360    changing symlink timestamps, but FILE was a symlink.  */
361 int
362 lutimens (char const *file, struct timespec const timespec[2])
363 {
364   struct timespec adjusted_timespec[2];
365   struct timespec *ts = timespec ? adjusted_timespec : NULL;
366   int adjustment_needed = 0;
367   struct stat st;
368
369   if (ts)
370     {
371       adjusted_timespec[0] = timespec[0];
372       adjusted_timespec[1] = timespec[1];
373       adjustment_needed = validate_timespec (ts);
374     }
375   if (adjustment_needed < 0)
376     return -1;
377
378   /* The Linux kernel did not support symlink timestamps until
379      utimensat, in version 2.6.22, so we don't need to mimic
380      gl_futimens' worry about buggy NFS clients.  But we do have to
381      worry about bogus return values.  */
382
383 #if HAVE_UTIMENSAT
384   if (0 <= utimensat_works_really)
385     {
386       int result = utimensat (AT_FDCWD, file, ts, AT_SYMLINK_NOFOLLOW);
387 # ifdef __linux__
388       /* Work around a kernel bug:
389          http://bugzilla.redhat.com/442352
390          http://bugzilla.redhat.com/449910
391          It appears that utimensat can mistakenly return 280 rather
392          than -1 upon ENOSYS failure.
393          FIXME: remove in 2010 or whenever the offending kernels
394          are no longer in common use.  */
395       if (0 < result)
396         errno = ENOSYS;
397 # endif
398       if (result == 0 || errno != ENOSYS)
399         {
400           utimensat_works_really = 1;
401           return result;
402         }
403     }
404   utimensat_works_really = -1;
405 #endif /* HAVE_UTIMENSAT */
406
407   /* The platform lacks an interface to set file timestamps with
408      nanosecond resolution, so do the best we can, discarding any
409      fractional part of the timestamp.  */
410
411   if (adjustment_needed || REPLACE_FUNC_STAT_FILE)
412     {
413       if (lstat (file, &st))
414         return -1;
415       if (ts && update_timespec (&st, &ts))
416         return 0;
417     }
418
419 #if HAVE_LUTIMES
420   {
421     struct timeval timeval[2];
422     struct timeval const *t;
423     if (ts)
424       {
425         timeval[0].tv_sec = ts[0].tv_sec;
426         timeval[0].tv_usec = ts[0].tv_nsec / 1000;
427         timeval[1].tv_sec = ts[1].tv_sec;
428         timeval[1].tv_usec = ts[1].tv_nsec / 1000;
429         t = timeval;
430       }
431     else
432       t = NULL;
433
434     return lutimes (file, t);
435   }
436 #endif /* HAVE_LUTIMES */
437
438   /* Out of luck for symlinks, but we still handle regular files.  */
439   if (!(adjustment_needed || REPLACE_FUNC_STAT_FILE) && lstat (file, &st))
440     return -1;
441   if (!S_ISLNK (st.st_mode))
442     return fdutimens (file, -1, ts);
443   errno = ENOSYS;
444   return -1;
445 }