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