This source file includes following definitions.
- validate_timespec
- update_timespec
- fdutimens
- utimens
- lutimens
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 #include <config.h>
23
24 #define _GL_UTIMENS_INLINE _GL_EXTERN_INLINE
25 #include "utimens.h"
26
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <string.h>
30 #include <sys/stat.h>
31 #include <sys/time.h>
32 #include <unistd.h>
33 #include <utime.h>
34
35 #include "stat-time.h"
36 #include "timespec.h"
37
38
39
40
41
42 #if defined _WIN32 && ! defined __CYGWIN__ && ! defined EMACS_CONFIGURATION
43 # define USE_SETFILETIME
44 # define WIN32_LEAN_AND_MEAN
45 # include <windows.h>
46 # if GNULIB_MSVC_NOTHROW
47 # include "msvc-nothrow.h"
48 # else
49 # include <io.h>
50 # endif
51 #endif
52
53
54 #undef futimens
55 #if !HAVE_NEARLY_WORKING_UTIMENSAT
56 # undef utimensat
57 #endif
58
59
60
61 #ifndef REPLACE_FUNC_STAT_FILE
62 # define REPLACE_FUNC_STAT_FILE 0
63 #endif
64
65 #if HAVE_UTIMENSAT || HAVE_FUTIMENS
66
67
68
69
70
71
72
73
74
75
76
77 static int utimensat_works_really;
78 static int lutimensat_works_really;
79 #endif
80
81
82
83
84
85
86
87
88 static int
89 validate_timespec (struct timespec timespec[2])
90 {
91 int result = 0;
92 int utime_omit_count = 0;
93 if ((timespec[0].tv_nsec != UTIME_NOW
94 && timespec[0].tv_nsec != UTIME_OMIT
95 && ! (0 <= timespec[0].tv_nsec
96 && timespec[0].tv_nsec < TIMESPEC_HZ))
97 || (timespec[1].tv_nsec != UTIME_NOW
98 && timespec[1].tv_nsec != UTIME_OMIT
99 && ! (0 <= timespec[1].tv_nsec
100 && timespec[1].tv_nsec < TIMESPEC_HZ)))
101 {
102 errno = EINVAL;
103 return -1;
104 }
105
106
107
108
109 if (timespec[0].tv_nsec == UTIME_NOW
110 || timespec[0].tv_nsec == UTIME_OMIT)
111 {
112 timespec[0].tv_sec = 0;
113 result = 1;
114 if (timespec[0].tv_nsec == UTIME_OMIT)
115 utime_omit_count++;
116 }
117 if (timespec[1].tv_nsec == UTIME_NOW
118 || timespec[1].tv_nsec == UTIME_OMIT)
119 {
120 timespec[1].tv_sec = 0;
121 result = 1;
122 if (timespec[1].tv_nsec == UTIME_OMIT)
123 utime_omit_count++;
124 }
125 return result + (utime_omit_count == 1);
126 }
127
128
129
130
131
132
133
134 static bool
135 update_timespec (struct stat const *statbuf, struct timespec **ts)
136 {
137 struct timespec *timespec = *ts;
138 if (timespec[0].tv_nsec == UTIME_OMIT
139 && timespec[1].tv_nsec == UTIME_OMIT)
140 return true;
141 if (timespec[0].tv_nsec == UTIME_NOW
142 && timespec[1].tv_nsec == UTIME_NOW)
143 {
144 *ts = NULL;
145 return false;
146 }
147
148 if (timespec[0].tv_nsec == UTIME_OMIT)
149 timespec[0] = get_stat_atime (statbuf);
150 else if (timespec[0].tv_nsec == UTIME_NOW)
151 gettime (×pec[0]);
152
153 if (timespec[1].tv_nsec == UTIME_OMIT)
154 timespec[1] = get_stat_mtime (statbuf);
155 else if (timespec[1].tv_nsec == UTIME_NOW)
156 gettime (×pec[1]);
157
158 return false;
159 }
160
161
162
163
164
165
166
167
168
169
170
171 int
172 fdutimens (int fd, char const *file, struct timespec const timespec[2])
173 {
174 struct timespec adjusted_timespec[2];
175 struct timespec *ts = timespec ? adjusted_timespec : NULL;
176 int adjustment_needed = 0;
177 struct stat st;
178
179 if (ts)
180 {
181 adjusted_timespec[0] = timespec[0];
182 adjusted_timespec[1] = timespec[1];
183 adjustment_needed = validate_timespec (ts);
184 }
185 if (adjustment_needed < 0)
186 return -1;
187
188
189
190
191 if (fd < 0 && !file)
192 {
193 errno = EBADF;
194 return -1;
195 }
196
197
198
199
200
201
202
203
204
205
206
207 #if HAVE_BUGGY_NFS_TIME_STAMPS
208 if (fd < 0)
209 sync ();
210 else
211 fsync (fd);
212 #endif
213
214
215
216
217
218
219 #if HAVE_UTIMENSAT || HAVE_FUTIMENS
220 if (0 <= utimensat_works_really)
221 {
222 int result;
223 # if __linux__ || __sun
224
225
226
227
228
229
230
231
232
233
234
235
236 if (adjustment_needed == 2)
237 {
238 if (fd < 0 ? stat (file, &st) : fstat (fd, &st))
239 return -1;
240 if (ts[0].tv_nsec == UTIME_OMIT)
241 ts[0] = get_stat_atime (&st);
242 else if (ts[1].tv_nsec == UTIME_OMIT)
243 ts[1] = get_stat_mtime (&st);
244
245 adjustment_needed++;
246 }
247 # endif
248 # if HAVE_UTIMENSAT
249 if (fd < 0)
250 {
251 # if defined __APPLE__ && defined __MACH__
252 size_t len = strlen (file);
253 if (len > 0 && file[len - 1] == '/')
254 {
255 struct stat statbuf;
256 if (stat (file, &statbuf) < 0)
257 return -1;
258 if (!S_ISDIR (statbuf.st_mode))
259 {
260 errno = ENOTDIR;
261 return -1;
262 }
263 }
264 # endif
265 result = utimensat (AT_FDCWD, file, ts, 0);
266 # ifdef __linux__
267
268
269
270
271
272
273
274 if (0 < result)
275 errno = ENOSYS;
276 # endif
277 if (result == 0 || errno != ENOSYS)
278 {
279 utimensat_works_really = 1;
280 return result;
281 }
282 }
283 # endif
284 # if HAVE_FUTIMENS
285 if (0 <= fd)
286 {
287 result = futimens (fd, ts);
288 # ifdef __linux__
289
290 if (0 < result)
291 errno = ENOSYS;
292 # endif
293 if (result == 0 || errno != ENOSYS)
294 {
295 utimensat_works_really = 1;
296 return result;
297 }
298 }
299 # endif
300 }
301 utimensat_works_really = -1;
302 lutimensat_works_really = -1;
303 #endif
304
305 #ifdef USE_SETFILETIME
306
307
308
309 if (0 <= fd)
310 {
311 HANDLE handle;
312 FILETIME current_time;
313 FILETIME last_access_time;
314 FILETIME last_write_time;
315
316 handle = (HANDLE) _get_osfhandle (fd);
317 if (handle == INVALID_HANDLE_VALUE)
318 {
319 errno = EBADF;
320 return -1;
321 }
322
323 if (ts == NULL || ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW)
324 {
325
326
327
328
329
330 GetSystemTimeAsFileTime (¤t_time);
331 }
332
333 if (ts == NULL || ts[0].tv_nsec == UTIME_NOW)
334 {
335 last_access_time = current_time;
336 }
337 else if (ts[0].tv_nsec == UTIME_OMIT)
338 {
339 last_access_time.dwLowDateTime = 0;
340 last_access_time.dwHighDateTime = 0;
341 }
342 else
343 {
344 ULONGLONG time_since_16010101 =
345 (ULONGLONG) ts[0].tv_sec * 10000000 + ts[0].tv_nsec / 100 + 116444736000000000LL;
346 last_access_time.dwLowDateTime = (DWORD) time_since_16010101;
347 last_access_time.dwHighDateTime = time_since_16010101 >> 32;
348 }
349
350 if (ts == NULL || ts[1].tv_nsec == UTIME_NOW)
351 {
352 last_write_time = current_time;
353 }
354 else if (ts[1].tv_nsec == UTIME_OMIT)
355 {
356 last_write_time.dwLowDateTime = 0;
357 last_write_time.dwHighDateTime = 0;
358 }
359 else
360 {
361 ULONGLONG time_since_16010101 =
362 (ULONGLONG) ts[1].tv_sec * 10000000 + ts[1].tv_nsec / 100 + 116444736000000000LL;
363 last_write_time.dwLowDateTime = (DWORD) time_since_16010101;
364 last_write_time.dwHighDateTime = time_since_16010101 >> 32;
365 }
366
367 if (SetFileTime (handle, NULL, &last_access_time, &last_write_time))
368 return 0;
369 else
370 {
371 DWORD sft_error = GetLastError ();
372 #if 0
373 fprintf (stderr, "fdutimens SetFileTime error 0x%x\n", (unsigned int) sft_error);
374 #endif
375 switch (sft_error)
376 {
377 case ERROR_ACCESS_DENIED:
378 errno = EACCES;
379 break;
380 default:
381 errno = EINVAL;
382 break;
383 }
384 return -1;
385 }
386 }
387 #endif
388
389
390
391
392
393 if (adjustment_needed || (REPLACE_FUNC_STAT_FILE && fd < 0))
394 {
395 if (adjustment_needed != 3
396 && (fd < 0 ? stat (file, &st) : fstat (fd, &st)))
397 return -1;
398 if (ts && update_timespec (&st, &ts))
399 return 0;
400 }
401
402 {
403 #if HAVE_FUTIMESAT || HAVE_WORKING_UTIMES
404 struct timeval timeval[2];
405 struct timeval *t;
406 if (ts)
407 {
408 timeval[0] = (struct timeval) { .tv_sec = ts[0].tv_sec,
409 .tv_usec = ts[0].tv_nsec / 1000 };
410 timeval[1] = (struct timeval) { .tv_sec = ts[1].tv_sec,
411 .tv_usec = ts[1].tv_nsec / 1000 };
412 t = timeval;
413 }
414 else
415 t = NULL;
416
417 if (fd < 0)
418 {
419 # if HAVE_FUTIMESAT
420 return futimesat (AT_FDCWD, file, t);
421 # endif
422 }
423 else
424 {
425
426
427
428
429
430
431
432
433
434
435
436 # if (HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG) || HAVE_FUTIMES
437 # if HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG
438 # undef futimes
439 # define futimes(fd, t) futimesat (fd, NULL, t)
440 # endif
441 if (futimes (fd, t) == 0)
442 {
443 # if __linux__ && __GLIBC__
444
445
446
447
448
449 if (t)
450 {
451 bool abig = 500000 <= t[0].tv_usec;
452 bool mbig = 500000 <= t[1].tv_usec;
453 if ((abig | mbig) && fstat (fd, &st) == 0)
454 {
455
456
457 time_t adiff = st.st_atime - t[0].tv_sec;
458 time_t mdiff = st.st_mtime - t[1].tv_sec;
459
460 struct timeval *tt = NULL;
461 struct timeval truncated_timeval[2];
462 truncated_timeval[0] = t[0];
463 truncated_timeval[1] = t[1];
464 if (abig && adiff == 1 && get_stat_atime_ns (&st) == 0)
465 {
466 tt = truncated_timeval;
467 tt[0].tv_usec = 0;
468 }
469 if (mbig && mdiff == 1 && get_stat_mtime_ns (&st) == 0)
470 {
471 tt = truncated_timeval;
472 tt[1].tv_usec = 0;
473 }
474 if (tt)
475 futimes (fd, tt);
476 }
477 }
478 # endif
479
480 return 0;
481 }
482 # endif
483 }
484 #endif
485
486 if (!file)
487 {
488 #if ! ((HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG) \
489 || (HAVE_WORKING_UTIMES && HAVE_FUTIMES))
490 errno = ENOSYS;
491 #endif
492 return -1;
493 }
494
495 #ifdef USE_SETFILETIME
496 return _gl_utimens_windows (file, ts);
497 #elif HAVE_WORKING_UTIMES
498 return utimes (file, t);
499 #else
500 {
501 struct utimbuf utimbuf;
502 struct utimbuf *ut;
503 if (ts)
504 {
505 utimbuf = (struct utimbuf) { .actime = ts[0].tv_sec,
506 .modtime = ts[1].tv_sec };
507 ut = &utimbuf;
508 }
509 else
510 ut = NULL;
511
512 return utime (file, ut);
513 }
514 #endif
515 }
516 }
517
518
519
520 int
521 utimens (char const *file, struct timespec const timespec[2])
522 {
523 return fdutimens (-1, file, timespec);
524 }
525
526
527
528
529
530 int
531 lutimens (char const *file, struct timespec const timespec[2])
532 {
533 struct timespec adjusted_timespec[2];
534 struct timespec *ts = timespec ? adjusted_timespec : NULL;
535 int adjustment_needed = 0;
536 struct stat st;
537
538 if (ts)
539 {
540 adjusted_timespec[0] = timespec[0];
541 adjusted_timespec[1] = timespec[1];
542 adjustment_needed = validate_timespec (ts);
543 }
544 if (adjustment_needed < 0)
545 return -1;
546
547
548
549
550
551
552 #if HAVE_UTIMENSAT
553 if (0 <= lutimensat_works_really)
554 {
555 int result;
556 # if __linux__ || __sun
557
558
559
560
561
562
563
564
565
566
567
568
569 if (adjustment_needed == 2)
570 {
571 if (lstat (file, &st))
572 return -1;
573 if (ts[0].tv_nsec == UTIME_OMIT)
574 ts[0] = get_stat_atime (&st);
575 else if (ts[1].tv_nsec == UTIME_OMIT)
576 ts[1] = get_stat_mtime (&st);
577
578 adjustment_needed++;
579 }
580 # endif
581 result = utimensat (AT_FDCWD, file, ts, AT_SYMLINK_NOFOLLOW);
582 # ifdef __linux__
583
584
585
586
587
588
589
590 if (0 < result)
591 errno = ENOSYS;
592 # endif
593 if (result == 0 || errno != ENOSYS)
594 {
595 utimensat_works_really = 1;
596 lutimensat_works_really = 1;
597 return result;
598 }
599 }
600 lutimensat_works_really = -1;
601 #endif
602
603
604
605
606
607 if (adjustment_needed || REPLACE_FUNC_STAT_FILE)
608 {
609 if (adjustment_needed != 3 && lstat (file, &st))
610 return -1;
611 if (ts && update_timespec (&st, &ts))
612 return 0;
613 }
614
615
616
617 #if HAVE_LUTIMES && !HAVE_UTIMENSAT
618 {
619 struct timeval timeval[2];
620 struct timeval *t;
621 int result;
622 if (ts)
623 {
624 timeval[0] = (struct timeval) { .tv_sec = ts[0].tv_sec,
625 .tv_usec = ts[0].tv_nsec / 1000 };
626 timeval[1] = (struct timeval) { .tv_sec = ts[1].tv_sec,
627 .tv_usec = ts[1].tv_nsec / 1000 };
628 t = timeval;
629 }
630 else
631 t = NULL;
632
633 result = lutimes (file, t);
634 if (result == 0 || errno != ENOSYS)
635 return result;
636 }
637 #endif
638
639
640 if (!(adjustment_needed || REPLACE_FUNC_STAT_FILE) && lstat (file, &st))
641 return -1;
642 if (!S_ISLNK (st.st_mode))
643 return fdutimens (-1, file, ts);
644 errno = ENOSYS;
645 return -1;
646 }