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