From 1c189d99bceb3662d83ffcb557ab71cd13892284 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sun, 10 Dec 2023 08:27:36 +0800 Subject: [PATCH] feat: Add associated constants `UTIME_OMIT` `UTIME_NOW` to `TimeSpec` (#1879) * feat: support UTIME_NOW & UTIME_OMIT * fmt * fmt * linux x32 type cast * fmt --- changelog/1879.added.md | 1 + src/sys/stat.rs | 8 ++++- src/sys/time.rs | 11 +++++++ test/test_stat.rs | 72 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 changelog/1879.added.md diff --git a/changelog/1879.added.md b/changelog/1879.added.md new file mode 100644 index 0000000000..6f2b9a04d6 --- /dev/null +++ b/changelog/1879.added.md @@ -0,0 +1 @@ +Add associated constants `UTIME_OMIT` `UTIME_NOW` for `TimeSpec` diff --git a/src/sys/stat.rs b/src/sys/stat.rs index 20b5294b31..369008a800 100644 --- a/src/sys/stat.rs +++ b/src/sys/stat.rs @@ -376,6 +376,9 @@ pub fn lutimes( /// Change the access and modification times of the file specified by a file descriptor. /// +/// If you want to set the timestamp to now, use `TimeSpec::UTIME_NOW`. Use +/// `TimeSpec::UTIME_OMIT` if you don't want to change it. +/// /// # References /// /// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html). @@ -408,6 +411,9 @@ pub enum UtimensatFlags { /// `utimes(path, times)`. The latter is a deprecated API so prefer using the /// former if the platforms you care about support it. /// +/// If you want to set the timestamp to now, use `TimeSpec::UTIME_NOW`. Use +/// `TimeSpec::UTIME_OMIT` if you don't want to change it. +/// /// # References /// /// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html). @@ -448,4 +454,4 @@ pub fn mkdirat( })?; Errno::result(res).map(drop) -} +} \ No newline at end of file diff --git a/src/sys/time.rs b/src/sys/time.rs index ac13de419c..a63a03dc88 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -329,6 +329,17 @@ impl TimeValLike for TimeSpec { } impl TimeSpec { + /// Leave the timestamp unchanged. + #[cfg(not(target_os = "redox"))] + // At the time of writing this PR, redox does not support this feature + pub const UTIME_OMIT: TimeSpec = + TimeSpec::new(0, libc::UTIME_OMIT as timespec_tv_nsec_t); + /// Update the timestamp to `Now` + // At the time of writing this PR, redox does not support this feature + #[cfg(not(target_os = "redox"))] + pub const UTIME_NOW: TimeSpec = + TimeSpec::new(0, libc::UTIME_NOW as timespec_tv_nsec_t); + /// Construct a new `TimeSpec` from its components #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self { diff --git a/test/test_stat.rs b/test/test_stat.rs index 0dd6fe89c5..386f1084cc 100644 --- a/test/test_stat.rs +++ b/test/test_stat.rs @@ -413,3 +413,75 @@ fn test_mknodat() { assert_eq!(mode & libc::S_IFREG, libc::S_IFREG); assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU); } + +#[test] +#[cfg(not(any(target_os = "redox", target_os = "haiku")))] +fn test_futimens_unchanged() { + let tempdir = tempfile::tempdir().unwrap(); + let fullpath = tempdir.path().join("file"); + drop(File::create(&fullpath).unwrap()); + let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty()) + .unwrap(); + + let old_atime = fs::metadata(fullpath.as_path()) + .unwrap() + .accessed() + .unwrap(); + let old_mtime = fs::metadata(fullpath.as_path()) + .unwrap() + .modified() + .unwrap(); + + futimens(fd, &TimeSpec::UTIME_OMIT, &TimeSpec::UTIME_OMIT).unwrap(); + + let new_atime = fs::metadata(fullpath.as_path()) + .unwrap() + .accessed() + .unwrap(); + let new_mtime = fs::metadata(fullpath.as_path()) + .unwrap() + .modified() + .unwrap(); + assert_eq!(old_atime, new_atime); + assert_eq!(old_mtime, new_mtime); +} + +#[test] +#[cfg(not(any(target_os = "redox", target_os = "haiku")))] +fn test_utimensat_unchanged() { + let _dr = crate::DirRestore::new(); + let tempdir = tempfile::tempdir().unwrap(); + let filename = "foo.txt"; + let fullpath = tempdir.path().join(filename); + drop(File::create(&fullpath).unwrap()); + let dirfd = + fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()) + .unwrap(); + + let old_atime = fs::metadata(fullpath.as_path()) + .unwrap() + .accessed() + .unwrap(); + let old_mtime = fs::metadata(fullpath.as_path()) + .unwrap() + .modified() + .unwrap(); + utimensat( + Some(dirfd), + filename, + &TimeSpec::UTIME_OMIT, + &TimeSpec::UTIME_OMIT, + UtimensatFlags::NoFollowSymlink, + ) + .unwrap(); + let new_atime = fs::metadata(fullpath.as_path()) + .unwrap() + .accessed() + .unwrap(); + let new_mtime = fs::metadata(fullpath.as_path()) + .unwrap() + .modified() + .unwrap(); + assert_eq!(old_atime, new_atime); + assert_eq!(old_mtime, new_mtime); +}