Skip to content

Commit fc3822b

Browse files
committed
feat: support UTIME_NOW & UTIME_OMIT
1 parent 2ab5558 commit fc3822b

File tree

4 files changed

+89
-2
lines changed

4 files changed

+89
-2
lines changed

changelog/1879.added.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add associated constants `UTIME_OMIT` `UTIME_NOW` for `TimeSpec`

src/sys/stat.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,9 @@ pub fn lutimes<P: ?Sized + NixPath>(
376376

377377
/// Change the access and modification times of the file specified by a file descriptor.
378378
///
379+
/// If you want to set the timestamp to now, use [`TimeSpec::UTIME_NOW`]. Use
380+
/// [`TimeSpec::UTIME_OMIT`] if you don't want to change it.
381+
///
379382
/// # References
380383
///
381384
/// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html).
@@ -408,6 +411,9 @@ pub enum UtimensatFlags {
408411
/// `utimes(path, times)`. The latter is a deprecated API so prefer using the
409412
/// former if the platforms you care about support it.
410413
///
414+
/// If you want to set the timestamp to now, use [`TimeSpec::UTIME_NOW`]. Use
415+
/// [`TimeSpec::UTIME_OMIT`] if you don't want to change it.
416+
///
411417
/// # References
412418
///
413419
/// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
@@ -448,4 +454,4 @@ pub fn mkdirat<P: ?Sized + NixPath>(
448454
})?;
449455

450456
Errno::result(res).map(drop)
451-
}
457+
}

src/sys/time.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,13 @@ impl TimeValLike for TimeSpec {
329329
}
330330

331331
impl TimeSpec {
332+
/// Leave the timestamp unchanged.
333+
#[cfg(not(target_os = "redox"))]
334+
pub const UTIME_OMIT: TimeSpec = TimeSpec::new(0, libc::UTIME_OMIT);
335+
/// Update the timestamp to `Now`
336+
#[cfg(not(target_os = "redox"))]
337+
pub const UTIME_NOW: TimeSpec = TimeSpec::new(0, libc::UTIME_NOW);
338+
332339
/// Construct a new `TimeSpec` from its components
333340
#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
334341
pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self {
@@ -804,4 +811,4 @@ mod test {
804811
assert_eq!(TimeVal::nanoseconds(1402).to_string(), "0.000001 seconds");
805812
assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds");
806813
}
807-
}
814+
}

test/test_stat.rs

+73
Original file line numberDiff line numberDiff line change
@@ -415,3 +415,76 @@ fn test_mknodat() {
415415
assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
416416
assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
417417
}
418+
419+
#[test]
420+
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
421+
fn test_futimens_unchanged() {
422+
let tempdir = tempfile::tempdir().unwrap();
423+
let fullpath = tempdir.path().join("file");
424+
drop(File::create(&fullpath).unwrap());
425+
let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty())
426+
.unwrap();
427+
428+
let old_atime = fs::metadata(fullpath.as_path())
429+
.unwrap()
430+
.accessed()
431+
.unwrap();
432+
let old_mtime = fs::metadata(fullpath.as_path())
433+
.unwrap()
434+
.modified()
435+
.unwrap();
436+
437+
futimens(fd, &TimeSpec::UTIME_OMIT, &TimeSpec::UTIME_OMIT).unwrap();
438+
439+
let new_atime = fs::metadata(fullpath.as_path())
440+
.unwrap()
441+
.accessed()
442+
.unwrap();
443+
let new_mtime = fs::metadata(fullpath.as_path())
444+
.unwrap()
445+
.modified()
446+
.unwrap();
447+
assert_eq!(old_atime, new_atime);
448+
assert_eq!(old_mtime, new_mtime);
449+
}
450+
451+
452+
#[test]
453+
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
454+
fn test_utimensat_unchanged() {
455+
let _dr = crate::DirRestore::new();
456+
let tempdir = tempfile::tempdir().unwrap();
457+
let filename = "foo.txt";
458+
let fullpath = tempdir.path().join(filename);
459+
drop(File::create(&fullpath).unwrap());
460+
let dirfd =
461+
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
462+
.unwrap();
463+
464+
let old_atime = fs::metadata(fullpath.as_path())
465+
.unwrap()
466+
.accessed()
467+
.unwrap();
468+
let old_mtime = fs::metadata(fullpath.as_path())
469+
.unwrap()
470+
.modified()
471+
.unwrap();
472+
utimensat(
473+
Some(dirfd),
474+
filename,
475+
&TimeSpec::UTIME_OMIT,
476+
&TimeSpec::UTIME_OMIT,
477+
UtimensatFlags::NoFollowSymlink,
478+
)
479+
.unwrap();
480+
let new_atime = fs::metadata(fullpath.as_path())
481+
.unwrap()
482+
.accessed()
483+
.unwrap();
484+
let new_mtime = fs::metadata(fullpath.as_path())
485+
.unwrap()
486+
.modified()
487+
.unwrap();
488+
assert_eq!(old_atime, new_atime);
489+
assert_eq!(old_mtime, new_mtime);
490+
}

0 commit comments

Comments
 (0)