1 | //! Timestamps for files in Rust |
2 | //! |
3 | //! This library provides platform-agnostic inspection of the various timestamps |
4 | //! present in the standard `fs::Metadata` structure. |
5 | //! |
6 | //! # Installation |
7 | //! |
8 | //! Add this to your `Cargo.toml`: |
9 | //! |
10 | //! ```toml |
11 | //! [dependencies] |
12 | //! filetime = "0.2" |
13 | //! ``` |
14 | //! |
15 | //! # Usage |
16 | //! |
17 | //! ```no_run |
18 | //! use std::fs; |
19 | //! use filetime::FileTime; |
20 | //! |
21 | //! let metadata = fs::metadata("foo.txt" ).unwrap(); |
22 | //! |
23 | //! let mtime = FileTime::from_last_modification_time(&metadata); |
24 | //! println!("{}" , mtime); |
25 | //! |
26 | //! let atime = FileTime::from_last_access_time(&metadata); |
27 | //! assert!(mtime < atime); |
28 | //! |
29 | //! // Inspect values that can be interpreted across platforms |
30 | //! println!("{}" , mtime.unix_seconds()); |
31 | //! println!("{}" , mtime.nanoseconds()); |
32 | //! |
33 | //! // Print the platform-specific value of seconds |
34 | //! println!("{}" , mtime.seconds()); |
35 | //! ``` |
36 | |
37 | use std::fmt; |
38 | use std::fs; |
39 | use std::io; |
40 | use std::path::Path; |
41 | use std::time::{Duration, SystemTime, UNIX_EPOCH}; |
42 | |
43 | cfg_if::cfg_if! { |
44 | if #[cfg(target_os = "redox" )] { |
45 | #[path = "redox.rs" ] |
46 | mod imp; |
47 | } else if #[cfg(windows)] { |
48 | #[path = "windows.rs" ] |
49 | mod imp; |
50 | } else if #[cfg(all(target_family = "wasm" , not(target_os = "emscripten" )))] { |
51 | #[path = "wasm.rs" ] |
52 | mod imp; |
53 | } else { |
54 | #[path = "unix/mod.rs" ] |
55 | mod imp; |
56 | } |
57 | } |
58 | |
59 | /// A helper structure to represent a timestamp for a file. |
60 | /// |
61 | /// The actual value contined within is platform-specific and does not have the |
62 | /// same meaning across platforms, but comparisons and stringification can be |
63 | /// significant among the same platform. |
64 | #[derive (Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone, Hash)] |
65 | pub struct FileTime { |
66 | seconds: i64, |
67 | nanos: u32, |
68 | } |
69 | |
70 | impl FileTime { |
71 | /// Creates a new timestamp representing a 0 time. |
72 | /// |
73 | /// Useful for creating the base of a cmp::max chain of times. |
74 | pub const fn zero() -> FileTime { |
75 | FileTime { |
76 | seconds: 0, |
77 | nanos: 0, |
78 | } |
79 | } |
80 | |
81 | const fn emulate_second_only_system(self) -> FileTime { |
82 | if cfg!(emulate_second_only_system) { |
83 | FileTime { |
84 | seconds: self.seconds, |
85 | nanos: 0, |
86 | } |
87 | } else { |
88 | self |
89 | } |
90 | } |
91 | |
92 | /// Creates a new timestamp representing the current system time. |
93 | /// |
94 | /// ``` |
95 | /// # use filetime::FileTime; |
96 | /// # |
97 | /// # fn example() -> std::io::Result<()> { |
98 | /// # let path = "" ; |
99 | /// # |
100 | /// filetime::set_file_mtime(path, FileTime::now())?; |
101 | /// # |
102 | /// # Ok(()) |
103 | /// # } |
104 | /// ``` |
105 | /// |
106 | /// Equivalent to `FileTime::from_system_time(SystemTime::now())`. |
107 | pub fn now() -> FileTime { |
108 | FileTime::from_system_time(SystemTime::now()) |
109 | } |
110 | |
111 | /// Creates a new instance of `FileTime` with a number of seconds and |
112 | /// nanoseconds relative to the Unix epoch, 1970-01-01T00:00:00Z. |
113 | /// |
114 | /// Negative seconds represent times before the Unix epoch, and positive |
115 | /// values represent times after it. Nanos always count forwards in time. |
116 | /// |
117 | /// Note that this is typically the relative point that Unix time stamps are |
118 | /// from, but on Windows the native time stamp is relative to January 1, |
119 | /// 1601 so the return value of `seconds` from the returned `FileTime` |
120 | /// instance may not be the same as that passed in. |
121 | pub const fn from_unix_time(seconds: i64, nanos: u32) -> FileTime { |
122 | FileTime { |
123 | seconds: seconds + if cfg!(windows) { 11644473600 } else { 0 }, |
124 | nanos, |
125 | } |
126 | .emulate_second_only_system() |
127 | } |
128 | |
129 | /// Creates a new timestamp from the last modification time listed in the |
130 | /// specified metadata. |
131 | /// |
132 | /// The returned value corresponds to the `mtime` field of `stat` on Unix |
133 | /// platforms and the `ftLastWriteTime` field on Windows platforms. |
134 | pub fn from_last_modification_time(meta: &fs::Metadata) -> FileTime { |
135 | imp::from_last_modification_time(meta).emulate_second_only_system() |
136 | } |
137 | |
138 | /// Creates a new timestamp from the last access time listed in the |
139 | /// specified metadata. |
140 | /// |
141 | /// The returned value corresponds to the `atime` field of `stat` on Unix |
142 | /// platforms and the `ftLastAccessTime` field on Windows platforms. |
143 | pub fn from_last_access_time(meta: &fs::Metadata) -> FileTime { |
144 | imp::from_last_access_time(meta).emulate_second_only_system() |
145 | } |
146 | |
147 | /// Creates a new timestamp from the creation time listed in the specified |
148 | /// metadata. |
149 | /// |
150 | /// The returned value corresponds to the `birthtime` field of `stat` on |
151 | /// Unix platforms and the `ftCreationTime` field on Windows platforms. Note |
152 | /// that not all Unix platforms have this field available and may return |
153 | /// `None` in some circumstances. |
154 | pub fn from_creation_time(meta: &fs::Metadata) -> Option<FileTime> { |
155 | imp::from_creation_time(meta).map(|x| x.emulate_second_only_system()) |
156 | } |
157 | |
158 | /// Creates a new timestamp from the given SystemTime. |
159 | /// |
160 | /// Windows counts file times since 1601-01-01T00:00:00Z, and cannot |
161 | /// represent times before this, but it's possible to create a SystemTime |
162 | /// that does. This function will error if passed such a SystemTime. |
163 | pub fn from_system_time(time: SystemTime) -> FileTime { |
164 | let epoch = if cfg!(windows) { |
165 | UNIX_EPOCH - Duration::from_secs(11644473600) |
166 | } else { |
167 | UNIX_EPOCH |
168 | }; |
169 | |
170 | time.duration_since(epoch) |
171 | .map(|d| FileTime { |
172 | seconds: d.as_secs() as i64, |
173 | nanos: d.subsec_nanos(), |
174 | }) |
175 | .unwrap_or_else(|e| { |
176 | let until_epoch = e.duration(); |
177 | let (sec_offset, nanos) = if until_epoch.subsec_nanos() == 0 { |
178 | (0, 0) |
179 | } else { |
180 | (-1, 1_000_000_000 - until_epoch.subsec_nanos()) |
181 | }; |
182 | |
183 | FileTime { |
184 | seconds: -1 * until_epoch.as_secs() as i64 + sec_offset, |
185 | nanos, |
186 | } |
187 | }) |
188 | .emulate_second_only_system() |
189 | } |
190 | |
191 | /// Returns the whole number of seconds represented by this timestamp. |
192 | /// |
193 | /// Note that this value's meaning is **platform specific**. On Unix |
194 | /// platform time stamps are typically relative to January 1, 1970, but on |
195 | /// Windows platforms time stamps are relative to January 1, 1601. |
196 | pub const fn seconds(&self) -> i64 { |
197 | self.seconds |
198 | } |
199 | |
200 | /// Returns the whole number of seconds represented by this timestamp, |
201 | /// relative to the Unix epoch start of January 1, 1970. |
202 | /// |
203 | /// Note that this does not return the same value as `seconds` for Windows |
204 | /// platforms as seconds are relative to a different date there. |
205 | pub const fn unix_seconds(&self) -> i64 { |
206 | self.seconds - if cfg!(windows) { 11644473600 } else { 0 } |
207 | } |
208 | |
209 | /// Returns the nanosecond precision of this timestamp. |
210 | /// |
211 | /// The returned value is always less than one billion and represents a |
212 | /// portion of a second forward from the seconds returned by the `seconds` |
213 | /// method. |
214 | pub const fn nanoseconds(&self) -> u32 { |
215 | self.nanos |
216 | } |
217 | } |
218 | |
219 | impl fmt::Display for FileTime { |
220 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
221 | write!(f, " {}. {:09}s" , self.seconds, self.nanos) |
222 | } |
223 | } |
224 | |
225 | impl From<SystemTime> for FileTime { |
226 | fn from(time: SystemTime) -> FileTime { |
227 | FileTime::from_system_time(time) |
228 | } |
229 | } |
230 | |
231 | /// Set the last access and modification times for a file on the filesystem. |
232 | /// |
233 | /// This function will set the `atime` and `mtime` metadata fields for a file |
234 | /// on the local filesystem, returning any error encountered. |
235 | pub fn set_file_times<P>(p: P, atime: FileTime, mtime: FileTime) -> io::Result<()> |
236 | where |
237 | P: AsRef<Path>, |
238 | { |
239 | imp::set_file_times(p.as_ref(), atime, mtime) |
240 | } |
241 | |
242 | /// Set the last access and modification times for a file handle. |
243 | /// |
244 | /// This function will either or both of the `atime` and `mtime` metadata |
245 | /// fields for a file handle , returning any error encountered. If `None` is |
246 | /// specified then the time won't be updated. If `None` is specified for both |
247 | /// options then no action is taken. |
248 | pub fn set_file_handle_times( |
249 | f: &fs::File, |
250 | atime: Option<FileTime>, |
251 | mtime: Option<FileTime>, |
252 | ) -> io::Result<()> { |
253 | imp::set_file_handle_times(f, atime, mtime) |
254 | } |
255 | |
256 | /// Set the last access and modification times for a file on the filesystem. |
257 | /// This function does not follow symlink. |
258 | /// |
259 | /// This function will set the `atime` and `mtime` metadata fields for a file |
260 | /// on the local filesystem, returning any error encountered. |
261 | pub fn set_symlink_file_times<P>(p: P, atime: FileTime, mtime: FileTime) -> io::Result<()> |
262 | where |
263 | P: AsRef<Path>, |
264 | { |
265 | imp::set_symlink_file_times(p.as_ref(), atime, mtime) |
266 | } |
267 | |
268 | /// Set the last modification time for a file on the filesystem. |
269 | /// |
270 | /// This function will set the `mtime` metadata field for a file on the local |
271 | /// filesystem, returning any error encountered. |
272 | /// |
273 | /// # Platform support |
274 | /// |
275 | /// Where supported this will attempt to issue just one syscall to update only |
276 | /// the `mtime`, but where not supported this may issue one syscall to learn the |
277 | /// existing `atime` so only the `mtime` can be configured. |
278 | pub fn set_file_mtime<P>(p: P, mtime: FileTime) -> io::Result<()> |
279 | where |
280 | P: AsRef<Path>, |
281 | { |
282 | imp::set_file_mtime(p.as_ref(), mtime) |
283 | } |
284 | |
285 | /// Set the last access time for a file on the filesystem. |
286 | /// |
287 | /// This function will set the `atime` metadata field for a file on the local |
288 | /// filesystem, returning any error encountered. |
289 | /// |
290 | /// # Platform support |
291 | /// |
292 | /// Where supported this will attempt to issue just one syscall to update only |
293 | /// the `atime`, but where not supported this may issue one syscall to learn the |
294 | /// existing `mtime` so only the `atime` can be configured. |
295 | pub fn set_file_atime<P>(p: P, atime: FileTime) -> io::Result<()> |
296 | where |
297 | P: AsRef<Path>, |
298 | { |
299 | imp::set_file_atime(p.as_ref(), atime) |
300 | } |
301 | |
302 | #[cfg (test)] |
303 | mod tests { |
304 | use super::{ |
305 | set_file_atime, set_file_handle_times, set_file_mtime, set_file_times, |
306 | set_symlink_file_times, FileTime, |
307 | }; |
308 | use std::fs::{self, File}; |
309 | use std::io; |
310 | use std::path::Path; |
311 | use std::time::{Duration, UNIX_EPOCH}; |
312 | use tempfile::Builder; |
313 | |
314 | #[cfg (unix)] |
315 | fn make_symlink_file<P, Q>(src: P, dst: Q) -> io::Result<()> |
316 | where |
317 | P: AsRef<Path>, |
318 | Q: AsRef<Path>, |
319 | { |
320 | use std::os::unix::fs::symlink; |
321 | symlink(src, dst) |
322 | } |
323 | |
324 | #[cfg (windows)] |
325 | fn make_symlink_file<P, Q>(src: P, dst: Q) -> io::Result<()> |
326 | where |
327 | P: AsRef<Path>, |
328 | Q: AsRef<Path>, |
329 | { |
330 | use std::os::windows::fs::symlink_file; |
331 | symlink_file(src, dst) |
332 | } |
333 | |
334 | #[cfg (unix)] |
335 | fn make_symlink_dir<P, Q>(src: P, dst: Q) -> io::Result<()> |
336 | where |
337 | P: AsRef<Path>, |
338 | Q: AsRef<Path>, |
339 | { |
340 | use std::os::unix::fs::symlink; |
341 | symlink(src, dst) |
342 | } |
343 | |
344 | #[cfg (windows)] |
345 | fn make_symlink_dir<P, Q>(src: P, dst: Q) -> io::Result<()> |
346 | where |
347 | P: AsRef<Path>, |
348 | Q: AsRef<Path>, |
349 | { |
350 | use std::os::windows::fs::symlink_dir; |
351 | symlink_dir(src, dst) |
352 | } |
353 | |
354 | #[test ] |
355 | #[cfg (windows)] |
356 | fn from_unix_time_test() { |
357 | let time = FileTime::from_unix_time(10, 100_000_000); |
358 | assert_eq!(11644473610, time.seconds); |
359 | assert_eq!(100_000_000, time.nanos); |
360 | |
361 | let time = FileTime::from_unix_time(-10, 100_000_000); |
362 | assert_eq!(11644473590, time.seconds); |
363 | assert_eq!(100_000_000, time.nanos); |
364 | |
365 | let time = FileTime::from_unix_time(-12_000_000_000, 0); |
366 | assert_eq!(-355526400, time.seconds); |
367 | assert_eq!(0, time.nanos); |
368 | } |
369 | |
370 | #[test ] |
371 | #[cfg (not(windows))] |
372 | fn from_unix_time_test() { |
373 | let time = FileTime::from_unix_time(10, 100_000_000); |
374 | assert_eq!(10, time.seconds); |
375 | assert_eq!(100_000_000, time.nanos); |
376 | |
377 | let time = FileTime::from_unix_time(-10, 100_000_000); |
378 | assert_eq!(-10, time.seconds); |
379 | assert_eq!(100_000_000, time.nanos); |
380 | |
381 | let time = FileTime::from_unix_time(-12_000_000_000, 0); |
382 | assert_eq!(-12_000_000_000, time.seconds); |
383 | assert_eq!(0, time.nanos); |
384 | } |
385 | |
386 | #[test ] |
387 | #[cfg (windows)] |
388 | fn from_system_time_test() { |
389 | let time = FileTime::from_system_time(UNIX_EPOCH + Duration::from_secs(10)); |
390 | assert_eq!(11644473610, time.seconds); |
391 | assert_eq!(0, time.nanos); |
392 | |
393 | let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_secs(10)); |
394 | assert_eq!(11644473590, time.seconds); |
395 | assert_eq!(0, time.nanos); |
396 | |
397 | let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_millis(1100)); |
398 | assert_eq!(11644473598, time.seconds); |
399 | assert_eq!(900_000_000, time.nanos); |
400 | |
401 | let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_secs(12_000_000_000)); |
402 | assert_eq!(-355526400, time.seconds); |
403 | assert_eq!(0, time.nanos); |
404 | } |
405 | |
406 | #[test ] |
407 | #[cfg (not(windows))] |
408 | fn from_system_time_test() { |
409 | let time = FileTime::from_system_time(UNIX_EPOCH + Duration::from_secs(10)); |
410 | assert_eq!(10, time.seconds); |
411 | assert_eq!(0, time.nanos); |
412 | |
413 | let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_secs(10)); |
414 | assert_eq!(-10, time.seconds); |
415 | assert_eq!(0, time.nanos); |
416 | |
417 | let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_millis(1100)); |
418 | assert_eq!(-2, time.seconds); |
419 | assert_eq!(900_000_000, time.nanos); |
420 | |
421 | let time = FileTime::from_system_time(UNIX_EPOCH - Duration::from_secs(12_000_000)); |
422 | assert_eq!(-12_000_000, time.seconds); |
423 | assert_eq!(0, time.nanos); |
424 | } |
425 | |
426 | #[test ] |
427 | fn set_file_times_test() -> io::Result<()> { |
428 | let td = Builder::new().prefix("filetime" ).tempdir()?; |
429 | let path = td.path().join("foo.txt" ); |
430 | let mut f = File::create(&path)?; |
431 | |
432 | let metadata = fs::metadata(&path)?; |
433 | let mtime = FileTime::from_last_modification_time(&metadata); |
434 | let atime = FileTime::from_last_access_time(&metadata); |
435 | set_file_times(&path, atime, mtime)?; |
436 | |
437 | let new_mtime = FileTime::from_unix_time(10_000, 0); |
438 | set_file_times(&path, atime, new_mtime)?; |
439 | |
440 | let metadata = fs::metadata(&path)?; |
441 | let mtime = FileTime::from_last_modification_time(&metadata); |
442 | assert_eq!(mtime, new_mtime, "modification should be updated" ); |
443 | |
444 | // Update just mtime |
445 | let new_mtime = FileTime::from_unix_time(20_000, 0); |
446 | set_file_handle_times(&mut f, None, Some(new_mtime))?; |
447 | let metadata = f.metadata()?; |
448 | let mtime = FileTime::from_last_modification_time(&metadata); |
449 | assert_eq!(mtime, new_mtime, "modification time should be updated" ); |
450 | let new_atime = FileTime::from_last_access_time(&metadata); |
451 | assert_eq!(atime, new_atime, "accessed time should not be updated" ); |
452 | |
453 | // Update just atime |
454 | let new_atime = FileTime::from_unix_time(30_000, 0); |
455 | set_file_handle_times(&mut f, Some(new_atime), None)?; |
456 | let metadata = f.metadata()?; |
457 | let mtime = FileTime::from_last_modification_time(&metadata); |
458 | assert_eq!(mtime, new_mtime, "modification time should not be updated" ); |
459 | let atime = FileTime::from_last_access_time(&metadata); |
460 | assert_eq!(atime, new_atime, "accessed time should be updated" ); |
461 | |
462 | let spath = td.path().join("bar.txt" ); |
463 | make_symlink_file(&path, &spath)?; |
464 | let metadata = fs::symlink_metadata(&spath)?; |
465 | let smtime = FileTime::from_last_modification_time(&metadata); |
466 | |
467 | set_file_times(&spath, atime, mtime)?; |
468 | |
469 | let metadata = fs::metadata(&path)?; |
470 | let cur_mtime = FileTime::from_last_modification_time(&metadata); |
471 | assert_eq!(mtime, cur_mtime); |
472 | |
473 | let metadata = fs::symlink_metadata(&spath)?; |
474 | let cur_mtime = FileTime::from_last_modification_time(&metadata); |
475 | assert_eq!(smtime, cur_mtime); |
476 | |
477 | set_file_times(&spath, atime, new_mtime)?; |
478 | |
479 | let metadata = fs::metadata(&path)?; |
480 | let mtime = FileTime::from_last_modification_time(&metadata); |
481 | assert_eq!(mtime, new_mtime); |
482 | |
483 | let metadata = fs::symlink_metadata(&spath)?; |
484 | let mtime = FileTime::from_last_modification_time(&metadata); |
485 | assert_eq!(mtime, smtime); |
486 | Ok(()) |
487 | } |
488 | |
489 | #[test ] |
490 | fn set_dir_times_test() -> io::Result<()> { |
491 | let td = Builder::new().prefix("filetime" ).tempdir()?; |
492 | let path = td.path().join("foo" ); |
493 | fs::create_dir(&path)?; |
494 | |
495 | let metadata = fs::metadata(&path)?; |
496 | let mtime = FileTime::from_last_modification_time(&metadata); |
497 | let atime = FileTime::from_last_access_time(&metadata); |
498 | set_file_times(&path, atime, mtime)?; |
499 | |
500 | let new_mtime = FileTime::from_unix_time(10_000, 0); |
501 | set_file_times(&path, atime, new_mtime)?; |
502 | |
503 | let metadata = fs::metadata(&path)?; |
504 | let mtime = FileTime::from_last_modification_time(&metadata); |
505 | assert_eq!(mtime, new_mtime, "modification should be updated" ); |
506 | |
507 | // Update just mtime |
508 | let new_mtime = FileTime::from_unix_time(20_000, 0); |
509 | set_file_mtime(&path, new_mtime)?; |
510 | let metadata = fs::metadata(&path)?; |
511 | let mtime = FileTime::from_last_modification_time(&metadata); |
512 | assert_eq!(mtime, new_mtime, "modification time should be updated" ); |
513 | let new_atime = FileTime::from_last_access_time(&metadata); |
514 | assert_eq!(atime, new_atime, "accessed time should not be updated" ); |
515 | |
516 | // Update just atime |
517 | let new_atime = FileTime::from_unix_time(30_000, 0); |
518 | set_file_atime(&path, new_atime)?; |
519 | let metadata = fs::metadata(&path)?; |
520 | let mtime = FileTime::from_last_modification_time(&metadata); |
521 | assert_eq!(mtime, new_mtime, "modification time should not be updated" ); |
522 | let atime = FileTime::from_last_access_time(&metadata); |
523 | assert_eq!(atime, new_atime, "accessed time should be updated" ); |
524 | |
525 | let spath = td.path().join("bar" ); |
526 | make_symlink_dir(&path, &spath)?; |
527 | let metadata = fs::symlink_metadata(&spath)?; |
528 | let smtime = FileTime::from_last_modification_time(&metadata); |
529 | |
530 | set_file_times(&spath, atime, mtime)?; |
531 | |
532 | let metadata = fs::metadata(&path)?; |
533 | let cur_mtime = FileTime::from_last_modification_time(&metadata); |
534 | assert_eq!(mtime, cur_mtime); |
535 | |
536 | let metadata = fs::symlink_metadata(&spath)?; |
537 | let cur_mtime = FileTime::from_last_modification_time(&metadata); |
538 | assert_eq!(smtime, cur_mtime); |
539 | |
540 | set_file_times(&spath, atime, new_mtime)?; |
541 | |
542 | let metadata = fs::metadata(&path)?; |
543 | let mtime = FileTime::from_last_modification_time(&metadata); |
544 | assert_eq!(mtime, new_mtime); |
545 | |
546 | let metadata = fs::symlink_metadata(&spath)?; |
547 | let mtime = FileTime::from_last_modification_time(&metadata); |
548 | assert_eq!(mtime, smtime); |
549 | Ok(()) |
550 | } |
551 | |
552 | #[test ] |
553 | fn set_file_times_pre_unix_epoch_test() { |
554 | let td = Builder::new().prefix("filetime" ).tempdir().unwrap(); |
555 | let path = td.path().join("foo.txt" ); |
556 | File::create(&path).unwrap(); |
557 | |
558 | let metadata = fs::metadata(&path).unwrap(); |
559 | let mtime = FileTime::from_last_modification_time(&metadata); |
560 | let atime = FileTime::from_last_access_time(&metadata); |
561 | set_file_times(&path, atime, mtime).unwrap(); |
562 | |
563 | let new_mtime = FileTime::from_unix_time(-10_000, 0); |
564 | if cfg!(target_os = "aix" ) { |
565 | // On AIX, os checks if the unix timestamp is valid. |
566 | let result = set_file_times(&path, atime, new_mtime); |
567 | assert!(result.is_err()); |
568 | assert!(result.err().unwrap().kind() == std::io::ErrorKind::InvalidInput); |
569 | } else { |
570 | set_file_times(&path, atime, new_mtime).unwrap(); |
571 | |
572 | let metadata = fs::metadata(&path).unwrap(); |
573 | let mtime = FileTime::from_last_modification_time(&metadata); |
574 | assert_eq!(mtime, new_mtime); |
575 | } |
576 | } |
577 | |
578 | #[test ] |
579 | #[cfg (windows)] |
580 | fn set_file_times_pre_windows_epoch_test() { |
581 | let td = Builder::new().prefix("filetime" ).tempdir().unwrap(); |
582 | let path = td.path().join("foo.txt" ); |
583 | File::create(&path).unwrap(); |
584 | |
585 | let metadata = fs::metadata(&path).unwrap(); |
586 | let mtime = FileTime::from_last_modification_time(&metadata); |
587 | let atime = FileTime::from_last_access_time(&metadata); |
588 | set_file_times(&path, atime, mtime).unwrap(); |
589 | |
590 | let new_mtime = FileTime::from_unix_time(-12_000_000_000, 0); |
591 | assert!(set_file_times(&path, atime, new_mtime).is_err()); |
592 | } |
593 | |
594 | #[test ] |
595 | fn set_symlink_file_times_test() { |
596 | let td = Builder::new().prefix("filetime" ).tempdir().unwrap(); |
597 | let path = td.path().join("foo.txt" ); |
598 | File::create(&path).unwrap(); |
599 | |
600 | let metadata = fs::metadata(&path).unwrap(); |
601 | let mtime = FileTime::from_last_modification_time(&metadata); |
602 | let atime = FileTime::from_last_access_time(&metadata); |
603 | set_symlink_file_times(&path, atime, mtime).unwrap(); |
604 | |
605 | let new_mtime = FileTime::from_unix_time(10_000, 0); |
606 | set_symlink_file_times(&path, atime, new_mtime).unwrap(); |
607 | |
608 | let metadata = fs::metadata(&path).unwrap(); |
609 | let mtime = FileTime::from_last_modification_time(&metadata); |
610 | assert_eq!(mtime, new_mtime); |
611 | |
612 | let spath = td.path().join("bar.txt" ); |
613 | make_symlink_file(&path, &spath).unwrap(); |
614 | |
615 | let metadata = fs::symlink_metadata(&spath).unwrap(); |
616 | let smtime = FileTime::from_last_modification_time(&metadata); |
617 | let satime = FileTime::from_last_access_time(&metadata); |
618 | set_symlink_file_times(&spath, smtime, satime).unwrap(); |
619 | |
620 | let metadata = fs::metadata(&path).unwrap(); |
621 | let mtime = FileTime::from_last_modification_time(&metadata); |
622 | assert_eq!(mtime, new_mtime); |
623 | |
624 | let new_smtime = FileTime::from_unix_time(20_000, 0); |
625 | set_symlink_file_times(&spath, atime, new_smtime).unwrap(); |
626 | |
627 | let metadata = fs::metadata(&spath).unwrap(); |
628 | let mtime = FileTime::from_last_modification_time(&metadata); |
629 | assert_eq!(mtime, new_mtime); |
630 | |
631 | let metadata = fs::symlink_metadata(&spath).unwrap(); |
632 | let mtime = FileTime::from_last_modification_time(&metadata); |
633 | assert_eq!(mtime, new_smtime); |
634 | } |
635 | |
636 | #[test ] |
637 | fn set_symlink_dir_times_test() { |
638 | let td = Builder::new().prefix("filetime" ).tempdir().unwrap(); |
639 | let path = td.path().join("foo" ); |
640 | fs::create_dir(&path).unwrap(); |
641 | |
642 | let metadata = fs::metadata(&path).unwrap(); |
643 | let mtime = FileTime::from_last_modification_time(&metadata); |
644 | let atime = FileTime::from_last_access_time(&metadata); |
645 | set_symlink_file_times(&path, atime, mtime).unwrap(); |
646 | |
647 | let new_mtime = FileTime::from_unix_time(10_000, 0); |
648 | set_symlink_file_times(&path, atime, new_mtime).unwrap(); |
649 | |
650 | let metadata = fs::metadata(&path).unwrap(); |
651 | let mtime = FileTime::from_last_modification_time(&metadata); |
652 | assert_eq!(mtime, new_mtime); |
653 | |
654 | let spath = td.path().join("bar" ); |
655 | make_symlink_dir(&path, &spath).unwrap(); |
656 | |
657 | let metadata = fs::symlink_metadata(&spath).unwrap(); |
658 | let smtime = FileTime::from_last_modification_time(&metadata); |
659 | let satime = FileTime::from_last_access_time(&metadata); |
660 | set_symlink_file_times(&spath, smtime, satime).unwrap(); |
661 | |
662 | let metadata = fs::metadata(&path).unwrap(); |
663 | let mtime = FileTime::from_last_modification_time(&metadata); |
664 | assert_eq!(mtime, new_mtime); |
665 | |
666 | let new_smtime = FileTime::from_unix_time(20_000, 0); |
667 | set_symlink_file_times(&spath, atime, new_smtime).unwrap(); |
668 | |
669 | let metadata = fs::metadata(&spath).unwrap(); |
670 | let mtime = FileTime::from_last_modification_time(&metadata); |
671 | assert_eq!(mtime, new_mtime); |
672 | |
673 | let metadata = fs::symlink_metadata(&spath).unwrap(); |
674 | let mtime = FileTime::from_last_modification_time(&metadata); |
675 | assert_eq!(mtime, new_smtime); |
676 | } |
677 | |
678 | #[test ] |
679 | fn set_single_time_test() { |
680 | use super::{set_file_atime, set_file_mtime}; |
681 | |
682 | let td = Builder::new().prefix("filetime" ).tempdir().unwrap(); |
683 | let path = td.path().join("foo.txt" ); |
684 | File::create(&path).unwrap(); |
685 | |
686 | let metadata = fs::metadata(&path).unwrap(); |
687 | let mtime = FileTime::from_last_modification_time(&metadata); |
688 | let atime = FileTime::from_last_access_time(&metadata); |
689 | set_file_times(&path, atime, mtime).unwrap(); |
690 | |
691 | let new_mtime = FileTime::from_unix_time(10_000, 0); |
692 | set_file_mtime(&path, new_mtime).unwrap(); |
693 | |
694 | let metadata = fs::metadata(&path).unwrap(); |
695 | let mtime = FileTime::from_last_modification_time(&metadata); |
696 | assert_eq!(mtime, new_mtime, "modification time should be updated" ); |
697 | assert_eq!( |
698 | atime, |
699 | FileTime::from_last_access_time(&metadata), |
700 | "access time should not be updated" , |
701 | ); |
702 | |
703 | let new_atime = FileTime::from_unix_time(20_000, 0); |
704 | set_file_atime(&path, new_atime).unwrap(); |
705 | |
706 | let metadata = fs::metadata(&path).unwrap(); |
707 | let atime = FileTime::from_last_access_time(&metadata); |
708 | assert_eq!(atime, new_atime, "access time should be updated" ); |
709 | assert_eq!( |
710 | mtime, |
711 | FileTime::from_last_modification_time(&metadata), |
712 | "modification time should not be updated" |
713 | ); |
714 | } |
715 | } |
716 | |