Port to Solaris 8.
[bpt/emacs.git] / lib / utimens.c
CommitLineData
c8fff863
PE
1/* Set file access and modification times.
2
3 Copyright (C) 2003-2012 Free Software Foundation, Inc.
4
5 This program is free software: you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 3 of the License, or any
8 later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17
18/* Written by Paul Eggert. */
19
20/* derived from a function in touch.c */
21
22#include <config.h>
23
24#include "utimens.h"
25
26#include <assert.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <stdbool.h>
30#include <sys/stat.h>
31#include <sys/time.h>
32#include <unistd.h>
33
34#include "stat-time.h"
35#include "timespec.h"
36
37#if HAVE_UTIME_H
38# include <utime.h>
39#endif
40
41/* Some systems (even some that do have <utime.h>) don't declare this
42 structure anywhere. */
43#ifndef HAVE_STRUCT_UTIMBUF
44struct utimbuf
45{
46 long actime;
47 long modtime;
48};
49#endif
50
51/* Avoid recursion with rpl_futimens or rpl_utimensat. */
52#undef futimens
53#undef utimensat
54
55/* Solaris 9 mistakenly succeeds when given a non-directory with a
56 trailing slash. Force the use of rpl_stat for a fix. */
57#ifndef REPLACE_FUNC_STAT_FILE
58# define REPLACE_FUNC_STAT_FILE 0
59#endif
60
61#if HAVE_UTIMENSAT || HAVE_FUTIMENS
62/* Cache variables for whether the utimensat syscall works; used to
63 avoid calling the syscall if we know it will just fail with ENOSYS,
64 and to avoid unnecessary work in massaging timestamps if the
65 syscall will work. Multiple variables are needed, to distinguish
66 between the following scenarios on Linux:
67 utimensat doesn't exist, or is in glibc but kernel 2.6.18 fails with ENOSYS
68 kernel 2.6.22 and earlier rejects AT_SYMLINK_NOFOLLOW
69 kernel 2.6.25 and earlier reject UTIME_NOW/UTIME_OMIT with non-zero tv_sec
70 kernel 2.6.32 used with xfs or ntfs-3g fail to honor UTIME_OMIT
71 utimensat completely works
72 For each cache variable: 0 = unknown, 1 = yes, -1 = no. */
73static int utimensat_works_really;
74static int lutimensat_works_really;
75#endif /* HAVE_UTIMENSAT || HAVE_FUTIMENS */
76
77/* Validate the requested timestamps. Return 0 if the resulting
78 timespec can be used for utimensat (after possibly modifying it to
79 work around bugs in utimensat). Return a positive value if the
80 timespec needs further adjustment based on stat results: 1 if any
81 adjustment is needed for utimes, and 2 if any adjustment is needed
82 for Linux utimensat. Return -1, with errno set to EINVAL, if
83 timespec is out of range. */
84static int
85validate_timespec (struct timespec timespec[2])
86{
87 int result = 0;
88 int utime_omit_count = 0;
89 assert (timespec);
90 if ((timespec[0].tv_nsec != UTIME_NOW
91 && timespec[0].tv_nsec != UTIME_OMIT
92 && (timespec[0].tv_nsec < 0 || 1000000000 <= timespec[0].tv_nsec))
93 || (timespec[1].tv_nsec != UTIME_NOW
94 && timespec[1].tv_nsec != UTIME_OMIT
95 && (timespec[1].tv_nsec < 0 || 1000000000 <= timespec[1].tv_nsec)))
96 {
97 errno = EINVAL;
98 return -1;
99 }
100 /* Work around Linux kernel 2.6.25 bug, where utimensat fails with
101 EINVAL if tv_sec is not 0 when using the flag values of tv_nsec.
102 Flag a Linux kernel 2.6.32 bug, where an mtime of UTIME_OMIT
103 fails to bump ctime. */
104 if (timespec[0].tv_nsec == UTIME_NOW
105 || timespec[0].tv_nsec == UTIME_OMIT)
106 {
107 timespec[0].tv_sec = 0;
108 result = 1;
109 if (timespec[0].tv_nsec == UTIME_OMIT)
110 utime_omit_count++;
111 }
112 if (timespec[1].tv_nsec == UTIME_NOW
113 || timespec[1].tv_nsec == UTIME_OMIT)
114 {
115 timespec[1].tv_sec = 0;
116 result = 1;
117 if (timespec[1].tv_nsec == UTIME_OMIT)
118 utime_omit_count++;
119 }
120 return result + (utime_omit_count == 1);
121}
122
123/* Normalize any UTIME_NOW or UTIME_OMIT values in *TS, using stat
124 buffer STATBUF to obtain the current timestamps of the file. If
125 both times are UTIME_NOW, set *TS to NULL (as this can avoid some
126 permissions issues). If both times are UTIME_OMIT, return true
127 (nothing further beyond the prior collection of STATBUF is
128 necessary); otherwise return false. */
129static bool
130update_timespec (struct stat const *statbuf, struct timespec *ts[2])
131{
132 struct timespec *timespec = *ts;
133 if (timespec[0].tv_nsec == UTIME_OMIT
134 && timespec[1].tv_nsec == UTIME_OMIT)
135 return true;
136 if (timespec[0].tv_nsec == UTIME_NOW
137 && timespec[1].tv_nsec == UTIME_NOW)
138 {
139 *ts = NULL;
140 return false;
141 }
142
143 if (timespec[0].tv_nsec == UTIME_OMIT)
144 timespec[0] = get_stat_atime (statbuf);
145 else if (timespec[0].tv_nsec == UTIME_NOW)
146 gettime (&timespec[0]);
147
148 if (timespec[1].tv_nsec == UTIME_OMIT)
149 timespec[1] = get_stat_mtime (statbuf);
150 else if (timespec[1].tv_nsec == UTIME_NOW)
151 gettime (&timespec[1]);
152
153 return false;
154}
155
156/* Set the access and modification time stamps of FD (a.k.a. FILE) to be
157 TIMESPEC[0] and TIMESPEC[1], respectively.
158 FD must be either negative -- in which case it is ignored --
159 or a file descriptor that is open on FILE.
160 If FD is nonnegative, then FILE can be NULL, which means
161 use just futimes (or equivalent) instead of utimes (or equivalent),
162 and fail if on an old system without futimes (or equivalent).
163 If TIMESPEC is null, set the time stamps to the current time.
164 Return 0 on success, -1 (setting errno) on failure. */
165
166int
167fdutimens (int fd, char const *file, struct timespec const timespec[2])
168{
169 struct timespec adjusted_timespec[2];
170 struct timespec *ts = timespec ? adjusted_timespec : NULL;
171 int adjustment_needed = 0;
172 struct stat st;
173
174 if (ts)
175 {
176 adjusted_timespec[0] = timespec[0];
177 adjusted_timespec[1] = timespec[1];
178 adjustment_needed = validate_timespec (ts);
179 }
180 if (adjustment_needed < 0)
181 return -1;
182
183 /* Require that at least one of FD or FILE are potentially valid, to avoid
184 a Linux bug where futimens (AT_FDCWD, NULL) changes "." rather
185 than failing. */
186 if (fd < 0 && !file)
187 {
188 errno = EBADF;
189 return -1;
190 }
191
192 /* Some Linux-based NFS clients are buggy, and mishandle time stamps
193 of files in NFS file systems in some cases. We have no
194 configure-time test for this, but please see
195 <http://bugs.gentoo.org/show_bug.cgi?id=132673> for references to
196 some of the problems with Linux 2.6.16. If this affects you,
197 compile with -DHAVE_BUGGY_NFS_TIME_STAMPS; this is reported to
198 help in some cases, albeit at a cost in performance. But you
199 really should upgrade your kernel to a fixed version, since the
200 problem affects many applications. */
201
202#if HAVE_BUGGY_NFS_TIME_STAMPS
203 if (fd < 0)
204 sync ();
205 else
206 fsync (fd);
207#endif
208
209 /* POSIX 2008 added two interfaces to set file timestamps with
210 nanosecond resolution; newer Linux implements both functions via
211 a single syscall. We provide a fallback for ENOSYS (for example,
212 compiling against Linux 2.6.25 kernel headers and glibc 2.7, but
213 running on Linux 2.6.18 kernel). */
214#if HAVE_UTIMENSAT || HAVE_FUTIMENS
215 if (0 <= utimensat_works_really)
216 {
217 int result;
218# if __linux__
219 /* As recently as Linux kernel 2.6.32 (Dec 2009), several file
220 systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT,
221 but work if both times are either explicitly specified or
222 UTIME_NOW. Work around it with a preparatory [f]stat prior
223 to calling futimens/utimensat; fortunately, there is not much
224 timing impact due to the extra syscall even on file systems
225 where UTIME_OMIT would have worked. FIXME: Simplify this in
226 2012, when file system bugs are no longer common. */
227 if (adjustment_needed == 2)
228 {
229 if (fd < 0 ? stat (file, &st) : fstat (fd, &st))
230 return -1;
231 if (ts[0].tv_nsec == UTIME_OMIT)
232 ts[0] = get_stat_atime (&st);
233 else if (ts[1].tv_nsec == UTIME_OMIT)
234 ts[1] = get_stat_mtime (&st);
235 /* Note that st is good, in case utimensat gives ENOSYS. */
236 adjustment_needed++;
237 }
238# endif /* __linux__ */
239# if HAVE_UTIMENSAT
240 if (fd < 0)
241 {
242 result = utimensat (AT_FDCWD, file, ts, 0);
243# ifdef __linux__
244 /* Work around a kernel bug:
245 http://bugzilla.redhat.com/442352
246 http://bugzilla.redhat.com/449910
247 It appears that utimensat can mistakenly return 280 rather
248 than -1 upon ENOSYS failure.
249 FIXME: remove in 2010 or whenever the offending kernels
250 are no longer in common use. */
251 if (0 < result)
252 errno = ENOSYS;
253# endif /* __linux__ */
254 if (result == 0 || errno != ENOSYS)
255 {
256 utimensat_works_really = 1;
257 return result;
258 }
259 }
260# endif /* HAVE_UTIMENSAT */
261# if HAVE_FUTIMENS
262 if (0 <= fd)
263 {
264 result = futimens (fd, ts);
265# ifdef __linux__
266 /* Work around the same bug as above. */
267 if (0 < result)
268 errno = ENOSYS;
269# endif /* __linux__ */
270 if (result == 0 || errno != ENOSYS)
271 {
272 utimensat_works_really = 1;
273 return result;
274 }
275 }
276# endif /* HAVE_FUTIMENS */
277 }
278 utimensat_works_really = -1;
279 lutimensat_works_really = -1;
280#endif /* HAVE_UTIMENSAT || HAVE_FUTIMENS */
281
282 /* The platform lacks an interface to set file timestamps with
283 nanosecond resolution, so do the best we can, discarding any
284 fractional part of the timestamp. */
285
286 if (adjustment_needed || (REPLACE_FUNC_STAT_FILE && fd < 0))
287 {
288 if (adjustment_needed != 3
289 && (fd < 0 ? stat (file, &st) : fstat (fd, &st)))
290 return -1;
291 if (ts && update_timespec (&st, &ts))
292 return 0;
293 }
294
295 {
296#if HAVE_FUTIMESAT || HAVE_WORKING_UTIMES
297 struct timeval timeval[2];
298 struct timeval *t;
299 if (ts)
300 {
301 timeval[0].tv_sec = ts[0].tv_sec;
302 timeval[0].tv_usec = ts[0].tv_nsec / 1000;
303 timeval[1].tv_sec = ts[1].tv_sec;
304 timeval[1].tv_usec = ts[1].tv_nsec / 1000;
305 t = timeval;
306 }
307 else
308 t = NULL;
309
310 if (fd < 0)
311 {
312# if HAVE_FUTIMESAT
313 return futimesat (AT_FDCWD, file, t);
314# endif
315 }
316 else
317 {
318 /* If futimesat or futimes fails here, don't try to speed things
319 up by returning right away. glibc can incorrectly fail with
320 errno == ENOENT if /proc isn't mounted. Also, Mandrake 10.0
321 in high security mode doesn't allow ordinary users to read
322 /proc/self, so glibc incorrectly fails with errno == EACCES.
323 If errno == EIO, EPERM, or EROFS, it's probably safe to fail
324 right away, but these cases are rare enough that they're not
325 worth optimizing, and who knows what other messed-up systems
326 are out there? So play it safe and fall back on the code
327 below. */
328
329# if (HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG) || HAVE_FUTIMES
330# if HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG
331# undef futimes
332# define futimes(fd, t) futimesat (fd, NULL, t)
333# endif
334 if (futimes (fd, t) == 0)
335 {
336# if __linux__ && __GLIBC__
337 /* Work around a longstanding glibc bug, still present as
338 of 2010-12-27. On older Linux kernels that lack both
339 utimensat and utimes, glibc's futimes rounds instead of
340 truncating when falling back on utime. The same bug
341 occurs in futimesat with a null 2nd arg. */
342 if (t)
343 {
344 bool abig = 500000 <= t[0].tv_usec;
345 bool mbig = 500000 <= t[1].tv_usec;
346 if ((abig | mbig) && fstat (fd, &st) == 0)
347 {
348 /* If these two subtractions overflow, they'll
349 track the overflows inside the buggy glibc. */
350 time_t adiff = st.st_atime - t[0].tv_sec;
351 time_t mdiff = st.st_mtime - t[1].tv_sec;
352
353 struct timeval *tt = NULL;
354 struct timeval truncated_timeval[2];
355 truncated_timeval[0] = t[0];
356 truncated_timeval[1] = t[1];
357 if (abig && adiff == 1 && get_stat_atime_ns (&st) == 0)
358 {
359 tt = truncated_timeval;
360 tt[0].tv_usec = 0;
361 }
362 if (mbig && mdiff == 1 && get_stat_mtime_ns (&st) == 0)
363 {
364 tt = truncated_timeval;
365 tt[1].tv_usec = 0;
366 }
367 if (tt)
368 futimes (fd, tt);
369 }
370 }
371# endif
372
373 return 0;
374 }
375# endif
376 }
377#endif /* HAVE_FUTIMESAT || HAVE_WORKING_UTIMES */
378
379 if (!file)
380 {
381#if ! ((HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG) \
382 || (HAVE_WORKING_UTIMES && HAVE_FUTIMES))
383 errno = ENOSYS;
384#endif
385 return -1;
386 }
387
388#if HAVE_WORKING_UTIMES
389 return utimes (file, t);
390#else
391 {
392 struct utimbuf utimbuf;
393 struct utimbuf *ut;
394 if (ts)
395 {
396 utimbuf.actime = ts[0].tv_sec;
397 utimbuf.modtime = ts[1].tv_sec;
398 ut = &utimbuf;
399 }
400 else
401 ut = NULL;
402
403 return utime (file, ut);
404 }
405#endif /* !HAVE_WORKING_UTIMES */
406 }
407}
408
409/* Set the access and modification time stamps of FILE to be
410 TIMESPEC[0] and TIMESPEC[1], respectively. */
411int
412utimens (char const *file, struct timespec const timespec[2])
413{
414 return fdutimens (-1, file, timespec);
415}
416
417/* Set the access and modification time stamps of FILE to be
418 TIMESPEC[0] and TIMESPEC[1], respectively, without dereferencing
419 symlinks. Fail with ENOSYS if the platform does not support
420 changing symlink timestamps, but FILE was a symlink. */
421int
422lutimens (char const *file, struct timespec const timespec[2])
423{
424 struct timespec adjusted_timespec[2];
425 struct timespec *ts = timespec ? adjusted_timespec : NULL;
426 int adjustment_needed = 0;
427 struct stat st;
428
429 if (ts)
430 {
431 adjusted_timespec[0] = timespec[0];
432 adjusted_timespec[1] = timespec[1];
433 adjustment_needed = validate_timespec (ts);
434 }
435 if (adjustment_needed < 0)
436 return -1;
437
438 /* The Linux kernel did not support symlink timestamps until
439 utimensat, in version 2.6.22, so we don't need to mimic
440 fdutimens' worry about buggy NFS clients. But we do have to
441 worry about bogus return values. */
442
443#if HAVE_UTIMENSAT
444 if (0 <= lutimensat_works_really)
445 {
446 int result;
447# if __linux__
448 /* As recently as Linux kernel 2.6.32 (Dec 2009), several file
449 systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT,
450 but work if both times are either explicitly specified or
451 UTIME_NOW. Work around it with a preparatory lstat prior to
452 calling utimensat; fortunately, there is not much timing
453 impact due to the extra syscall even on file systems where
454 UTIME_OMIT would have worked. FIXME: Simplify this in 2012,
455 when file system bugs are no longer common. */
456 if (adjustment_needed == 2)
457 {
458 if (lstat (file, &st))
459 return -1;
460 if (ts[0].tv_nsec == UTIME_OMIT)
461 ts[0] = get_stat_atime (&st);
462 else if (ts[1].tv_nsec == UTIME_OMIT)
463 ts[1] = get_stat_mtime (&st);
464 /* Note that st is good, in case utimensat gives ENOSYS. */
465 adjustment_needed++;
466 }
467# endif /* __linux__ */
468 result = utimensat (AT_FDCWD, file, ts, AT_SYMLINK_NOFOLLOW);
469# ifdef __linux__
470 /* Work around a kernel bug:
471 http://bugzilla.redhat.com/442352
472 http://bugzilla.redhat.com/449910
473 It appears that utimensat can mistakenly return 280 rather
474 than -1 upon ENOSYS failure.
475 FIXME: remove in 2010 or whenever the offending kernels
476 are no longer in common use. */
477 if (0 < result)
478 errno = ENOSYS;
479# endif
480 if (result == 0 || errno != ENOSYS)
481 {
482 utimensat_works_really = 1;
483 lutimensat_works_really = 1;
484 return result;
485 }
486 }
487 lutimensat_works_really = -1;
488#endif /* HAVE_UTIMENSAT */
489
490 /* The platform lacks an interface to set file timestamps with
491 nanosecond resolution, so do the best we can, discarding any
492 fractional part of the timestamp. */
493
494 if (adjustment_needed || REPLACE_FUNC_STAT_FILE)
495 {
496 if (adjustment_needed != 3 && lstat (file, &st))
497 return -1;
498 if (ts && update_timespec (&st, &ts))
499 return 0;
500 }
501
502 /* On Linux, lutimes is a thin wrapper around utimensat, so there is
503 no point trying lutimes if utimensat failed with ENOSYS. */
504#if HAVE_LUTIMES && !HAVE_UTIMENSAT
505 {
506 struct timeval timeval[2];
507 struct timeval *t;
508 int result;
509 if (ts)
510 {
511 timeval[0].tv_sec = ts[0].tv_sec;
512 timeval[0].tv_usec = ts[0].tv_nsec / 1000;
513 timeval[1].tv_sec = ts[1].tv_sec;
514 timeval[1].tv_usec = ts[1].tv_nsec / 1000;
515 t = timeval;
516 }
517 else
518 t = NULL;
519
520 result = lutimes (file, t);
521 if (result == 0 || errno != ENOSYS)
522 return result;
523 }
524#endif /* HAVE_LUTIMES && !HAVE_UTIMENSAT */
525
526 /* Out of luck for symlinks, but we still handle regular files. */
527 if (!(adjustment_needed || REPLACE_FUNC_STAT_FILE) && lstat (file, &st))
528 return -1;
529 if (!S_ISLNK (st.st_mode))
530 return fdutimens (-1, file, ts);
531 errno = ENOSYS;
532 return -1;
533}