|
1 | | -use std::fs::File; |
| 1 | +use std::fs::{self, File}; |
2 | 2 | use std::os::unix::fs::symlink; |
3 | 3 | use std::os::unix::prelude::AsRawFd; |
| 4 | +use std::time::{Duration, UNIX_EPOCH}; |
4 | 5 |
|
5 | 6 | use libc::{S_IFMT, S_IFLNK}; |
6 | 7 |
|
7 | 8 | use nix::fcntl; |
8 | | -use nix::sys::stat::{self, fchmod, fchmodat, fstat, lstat, stat}; |
9 | | -use nix::sys::stat::{FileStat, Mode, FchmodatFlags}; |
| 9 | +use nix::sys::stat::{self, fchmod, fchmodat, fstat, futimens, lstat, stat, utimensat}; |
| 10 | +use nix::sys::stat::{FileStat, Mode, FchmodatFlags, UtimensatFlags}; |
| 11 | +use nix::sys::time::{TimeSpec, TimeValLike}; |
10 | 12 | use nix::unistd::chdir; |
11 | 13 | use nix::Result; |
12 | 14 | use tempfile; |
@@ -152,3 +154,48 @@ fn test_fchmodat() { |
152 | 154 | let file_stat2 = stat(&fullpath).unwrap(); |
153 | 155 | assert_eq!(file_stat2.st_mode & 0o7777, mode2.bits()); |
154 | 156 | } |
| 157 | + |
| 158 | +/// Asserts that the atime and mtime in a file's metadata match expected values. |
| 159 | +/// |
| 160 | +/// The atime and mtime are expressed with a resolution of seconds because some file systems |
| 161 | +/// (like macOS's HFS+) do not have higher granularity. |
| 162 | +fn assert_times_eq(exp_atime_sec: u64, exp_mtime_sec: u64, attr: &fs::Metadata) { |
| 163 | + assert_eq!( |
| 164 | + Duration::new(exp_atime_sec, 0), |
| 165 | + attr.accessed().unwrap().duration_since(UNIX_EPOCH).unwrap()); |
| 166 | + assert_eq!( |
| 167 | + Duration::new(exp_mtime_sec, 0), |
| 168 | + attr.modified().unwrap().duration_since(UNIX_EPOCH).unwrap()); |
| 169 | +} |
| 170 | + |
| 171 | +#[test] |
| 172 | +fn test_futimens() { |
| 173 | + let tempdir = tempfile::tempdir().unwrap(); |
| 174 | + let fullpath = tempdir.path().join("file"); |
| 175 | + drop(File::create(&fullpath).unwrap()); |
| 176 | + |
| 177 | + let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); |
| 178 | + |
| 179 | + futimens(fd, &TimeSpec::seconds(10), &TimeSpec::seconds(20)).unwrap(); |
| 180 | + assert_times_eq(10, 20, &fs::metadata(&fullpath).unwrap()); |
| 181 | +} |
| 182 | + |
| 183 | +#[test] |
| 184 | +fn test_utimensat() { |
| 185 | + let tempdir = tempfile::tempdir().unwrap(); |
| 186 | + let filename = "foo.txt"; |
| 187 | + let fullpath = tempdir.path().join(filename); |
| 188 | + drop(File::create(&fullpath).unwrap()); |
| 189 | + |
| 190 | + let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); |
| 191 | + |
| 192 | + utimensat(Some(dirfd), filename, &TimeSpec::seconds(12345), &TimeSpec::seconds(678), |
| 193 | + UtimensatFlags::FollowSymlink).unwrap(); |
| 194 | + assert_times_eq(12345, 678, &fs::metadata(&fullpath).unwrap()); |
| 195 | + |
| 196 | + chdir(tempdir.path()).unwrap(); |
| 197 | + |
| 198 | + utimensat(None, filename, &TimeSpec::seconds(500), &TimeSpec::seconds(800), |
| 199 | + UtimensatFlags::FollowSymlink).unwrap(); |
| 200 | + assert_times_eq(500, 800, &fs::metadata(&fullpath).unwrap()); |
| 201 | +} |
0 commit comments