diff --git a/CHANGELOG.md b/CHANGELOG.md index 6688c8f8ee..d2a97bc4dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#916](https://github.com/nix-rust/nix/pull/916)) - Added `kmod` module that allows loading and unloading kernel modules on Linux. ([#930](https://github.com/nix-rust/nix/pull/930)) -- Added `futimens` and `utimesat` wrappers ([#944](https://github.com/nix-rust/nix/pull/944)) +- Added `futimens` and `utimesat` wrappers ([#944](https://github.com/nix-rust/nix/pull/944)), + an `lutimes` wrapper ([#967](https://github.com/nix-rust/nix/pull/967)), and a `utimes` wrapper ([#946](https://github.com/nix-rust/nix/pull/946)). - Added `AF_UNSPEC` wrapper to `AddressFamily` ([#948](https://github.com/nix-rust/nix/pull/948)) - Added the `mode_t` public alias within `sys::stat`. diff --git a/src/sys/stat.rs b/src/sys/stat.rs index a0b3aafcb9..925c0edef9 100644 --- a/src/sys/stat.rs +++ b/src/sys/stat.rs @@ -199,6 +199,26 @@ pub fn utimes(path: &P, atime: &TimeVal, mtime: &TimeVal) - Errno::result(res).map(|_| ()) } +/// Change the access and modification times of a file without following symlinks. +/// +/// `lutimes(path, times)` is identical to +/// `utimensat(None, path, times, UtimensatFlags::NoFollowSymlink)`. The former +/// is a deprecated API so prefer using the latter if the platforms you care +/// about support it. +/// +/// # References +/// +/// [lutimes(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html). +#[cfg(not(target_os = "android"))] +pub fn lutimes(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()> { + let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()]; + let res = path.with_nix_path(|cstr| unsafe { + libc::lutimes(cstr.as_ptr(), ×[0]) + })?; + + Errno::result(res).map(|_| ()) +} + /// Change the access and modification times of the file specified by a file descriptor. /// /// # References diff --git a/test/test_stat.rs b/test/test_stat.rs index 01d86a79b2..bd16e635dd 100644 --- a/test/test_stat.rs +++ b/test/test_stat.rs @@ -6,7 +6,7 @@ use std::time::{Duration, UNIX_EPOCH}; use libc::{S_IFMT, S_IFLNK}; use nix::fcntl; -use nix::sys::stat::{self, fchmod, fchmodat, fstat, futimens, lstat, stat, utimes, utimensat}; +use nix::sys::stat::{self, fchmod, fchmodat, fstat, futimens, lstat, lutimes, stat, utimes, utimensat}; use nix::sys::stat::{FileStat, Mode, FchmodatFlags, UtimensatFlags}; use nix::sys::time::{TimeSpec, TimeVal, TimeValLike}; use nix::unistd::chdir; @@ -178,6 +178,25 @@ fn test_utimes() { assert_times_eq(9990, 5550, &fs::metadata(&fullpath).unwrap()); } +#[test] +fn test_lutimes() { + let tempdir = tempfile::tempdir().unwrap(); + let target = tempdir.path().join("target"); + let fullpath = tempdir.path().join("symlink"); + drop(File::create(&target).unwrap()); + symlink(&target, &fullpath).unwrap(); + + let exp_target_metadata = fs::symlink_metadata(&target).unwrap(); + lutimes(&fullpath, &TimeVal::seconds(4560), &TimeVal::seconds(1230)).unwrap(); + assert_times_eq(4560, 1230, &fs::symlink_metadata(&fullpath).unwrap()); + + let target_metadata = fs::symlink_metadata(&target).unwrap(); + assert_eq!(exp_target_metadata.accessed().unwrap(), target_metadata.accessed().unwrap(), + "atime of symlink target was unexpectedly modified"); + assert_eq!(exp_target_metadata.modified().unwrap(), target_metadata.modified().unwrap(), + "mtime of symlink target was unexpectedly modified"); +} + #[test] fn test_futimens() { let tempdir = tempfile::tempdir().unwrap();