From 07b0edb8c9fc4d9df3c5f0ba852e7e0b42d60f90 Mon Sep 17 00:00:00 2001 From: Julio Merino Date: Mon, 5 Nov 2018 09:16:58 -0500 Subject: [PATCH] Add a wrapper for lutimes(2) PR #944 added wrappers for the more-modern futimens(2) and utimesat(2), but unfortunately these APIs are not available on old-ish systems. In particular, macOS Sierra and below don't implement them, making the new APIs unusable. Whether we should care about such "old" systems is debatable, but the problem is that, at the moment, this is the only macOS version usable on Travis to test kexts and, thus, to test FUSE file systems. This should have been part of PR #946, which added a wrapper for utimes(2) following this same rationale, but missed lutimes(2) because I simply didn't notice it existed. --- CHANGELOG.md | 3 ++- src/sys/stat.rs | 19 +++++++++++++++++++ test/test_stat.rs | 21 ++++++++++++++++++++- 3 files changed, 41 insertions(+), 2 deletions(-) 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..6301fdd87a 100644 --- a/src/sys/stat.rs +++ b/src/sys/stat.rs @@ -199,6 +199,25 @@ 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). +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();