diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /vendor/rustix/tests/fs | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/rustix/tests/fs')
-rw-r--r-- | vendor/rustix/tests/fs/cwd.rs | 3 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/dir.rs | 37 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/fcntl.rs | 17 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/file.rs | 83 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/flock.rs | 34 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/futimens.rs | 42 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/invalid_offset.rs | 182 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/long_paths.rs | 28 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/main.rs | 47 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/makedev.rs | 10 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/mkdirat.rs | 33 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/mknodat.rs | 27 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/openat.rs | 33 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/openat2.rs | 184 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/readdir.rs | 68 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/renameat.rs | 104 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/statfs.rs | 49 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/utimensat.rs | 127 | ||||
-rw-r--r-- | vendor/rustix/tests/fs/y2038.rs | 146 |
19 files changed, 1254 insertions, 0 deletions
diff --git a/vendor/rustix/tests/fs/cwd.rs b/vendor/rustix/tests/fs/cwd.rs new file mode 100644 index 000000000..749c84f24 --- /dev/null +++ b/vendor/rustix/tests/fs/cwd.rs @@ -0,0 +1,3 @@ +/// Make sure we can use `cwd` in const contexts. +#[allow(dead_code)] +const CWD: rustix::fd::BorrowedFd<'static> = rustix::fs::cwd(); diff --git a/vendor/rustix/tests/fs/dir.rs b/vendor/rustix/tests/fs/dir.rs new file mode 100644 index 000000000..f5120be96 --- /dev/null +++ b/vendor/rustix/tests/fs/dir.rs @@ -0,0 +1,37 @@ +#[test] +fn test_dir() { + let t = rustix::fs::openat( + rustix::fs::cwd(), + rustix::cstr!("."), + rustix::fs::OFlags::RDONLY | rustix::fs::OFlags::CLOEXEC, + rustix::fs::Mode::empty(), + ) + .unwrap(); + + let dir = rustix::fs::Dir::read_from(&t).unwrap(); + + let _file = rustix::fs::openat( + &t, + rustix::cstr!("Cargo.toml"), + rustix::fs::OFlags::RDONLY | rustix::fs::OFlags::CLOEXEC, + rustix::fs::Mode::empty(), + ) + .unwrap(); + + let mut saw_dot = false; + let mut saw_dotdot = false; + let mut saw_cargo_toml = false; + for entry in dir { + let entry = entry.unwrap(); + if entry.file_name() == rustix::cstr!(".") { + saw_dot = true; + } else if entry.file_name() == rustix::cstr!("..") { + saw_dotdot = true; + } else if entry.file_name() == rustix::cstr!("Cargo.toml") { + saw_cargo_toml = true; + } + } + assert!(saw_dot); + assert!(saw_dotdot); + assert!(saw_cargo_toml); +} diff --git a/vendor/rustix/tests/fs/fcntl.rs b/vendor/rustix/tests/fs/fcntl.rs new file mode 100644 index 000000000..1f8b78387 --- /dev/null +++ b/vendor/rustix/tests/fs/fcntl.rs @@ -0,0 +1,17 @@ +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +#[test] +fn test_fcntl_dupfd_cloexec() { + use rustix::fd::AsFd; + use std::os::unix::io::AsRawFd; + + let file = rustix::fs::openat( + rustix::fs::cwd(), + "Cargo.toml", + rustix::fs::OFlags::RDONLY, + rustix::fs::Mode::empty(), + ) + .unwrap(); + + let new = rustix::fs::fcntl_dupfd_cloexec(&file, 700).unwrap(); + assert_eq!(new.as_fd().as_raw_fd(), 700); +} diff --git a/vendor/rustix/tests/fs/file.rs b/vendor/rustix/tests/fs/file.rs new file mode 100644 index 000000000..5c09f640d --- /dev/null +++ b/vendor/rustix/tests/fs/file.rs @@ -0,0 +1,83 @@ +#[cfg(not(target_os = "redox"))] +#[test] +fn test_file() { + #[cfg(not(target_os = "illumos"))] + rustix::fs::accessat( + rustix::fs::cwd(), + "Cargo.toml", + rustix::fs::Access::READ_OK, + rustix::fs::AtFlags::empty(), + ) + .unwrap(); + + assert_eq!( + rustix::fs::openat( + rustix::fs::cwd(), + "Cagro.motl", + rustix::fs::OFlags::RDONLY, + rustix::fs::Mode::empty(), + ) + .unwrap_err(), + rustix::io::Errno::NOENT + ); + + let file = rustix::fs::openat( + rustix::fs::cwd(), + "Cargo.toml", + rustix::fs::OFlags::RDONLY, + rustix::fs::Mode::empty(), + ) + .unwrap(); + + assert_eq!( + rustix::fs::openat( + &file, + "Cargo.toml", + rustix::fs::OFlags::RDONLY, + rustix::fs::Mode::empty(), + ) + .unwrap_err(), + rustix::io::Errno::NOTDIR + ); + + #[cfg(not(any( + target_os = "dragonfly", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox", + )))] + rustix::fs::fadvise(&file, 0, 10, rustix::fs::Advice::Normal).unwrap(); + + assert_eq!( + rustix::fs::fcntl_getfd(&file).unwrap(), + rustix::fs::FdFlags::empty() + ); + assert_eq!( + rustix::fs::fcntl_getfl(&file).unwrap(), + rustix::fs::OFlags::empty() + ); + + let stat = rustix::fs::fstat(&file).unwrap(); + assert!(stat.st_size > 0); + assert!(stat.st_blocks > 0); + + #[cfg(not(any( + target_os = "illumos", + target_os = "netbsd", + target_os = "redox", + target_os = "wasi", + )))] + // not implemented in libc for netbsd yet + { + let statfs = rustix::fs::fstatfs(&file).unwrap(); + assert!(statfs.f_blocks > 0); + } + + #[cfg(feature = "net")] + assert_eq!(rustix::io::is_read_write(&file).unwrap(), (true, false)); + + assert_ne!(rustix::io::ioctl_fionread(&file).unwrap(), 0); +} diff --git a/vendor/rustix/tests/fs/flock.rs b/vendor/rustix/tests/fs/flock.rs new file mode 100644 index 000000000..1b7df6ffd --- /dev/null +++ b/vendor/rustix/tests/fs/flock.rs @@ -0,0 +1,34 @@ +#[cfg(not(target_os = "redox"))] +#[test] +fn test_flock() { + use rustix::fs::{cwd, flock, openat, FlockOperation, Mode, OFlags}; + + let f = openat(cwd(), "Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); + flock(&f, FlockOperation::LockExclusive).unwrap(); + flock(&f, FlockOperation::Unlock).unwrap(); + let g = openat(cwd(), "Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); + flock(&g, FlockOperation::LockExclusive).unwrap(); + flock(&g, FlockOperation::Unlock).unwrap(); + drop(f); + drop(g); + + let f = openat(cwd(), "Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); + flock(&f, FlockOperation::LockShared).unwrap(); + let g = openat(cwd(), "Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); + flock(&g, FlockOperation::LockShared).unwrap(); + flock(&f, FlockOperation::Unlock).unwrap(); + flock(&g, FlockOperation::Unlock).unwrap(); + drop(f); + drop(g); + + let f = openat(cwd(), "Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); + flock(&f, FlockOperation::LockShared).unwrap(); + flock(&f, FlockOperation::LockExclusive).unwrap(); + flock(&f, FlockOperation::Unlock).unwrap(); + let g = openat(cwd(), "Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); + flock(&g, FlockOperation::LockShared).unwrap(); + flock(&g, FlockOperation::LockExclusive).unwrap(); + flock(&g, FlockOperation::Unlock).unwrap(); + drop(f); + drop(g); +} diff --git a/vendor/rustix/tests/fs/futimens.rs b/vendor/rustix/tests/fs/futimens.rs new file mode 100644 index 000000000..81f8b8a27 --- /dev/null +++ b/vendor/rustix/tests/fs/futimens.rs @@ -0,0 +1,42 @@ +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +#[test] +fn test_futimens() { + use rustix::fs::{cwd, fstat, futimens, openat, Mode, OFlags, Timespec, Timestamps}; + + let tmp = tempfile::tempdir().unwrap(); + let dir = openat(cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); + + let foo = openat( + &dir, + "foo", + OFlags::CREATE | OFlags::WRONLY | OFlags::CLOEXEC, + Mode::empty(), + ) + .unwrap(); + + let times = Timestamps { + last_access: Timespec { + tv_sec: 44000, + tv_nsec: 45000, + }, + last_modification: Timespec { + tv_sec: 46000, + tv_nsec: 47000, + }, + }; + futimens(&foo, ×).unwrap(); + + let after = fstat(&foo).unwrap(); + + assert_eq!(times.last_modification.tv_sec as u64, after.st_mtime as u64); + #[cfg(not(target_os = "netbsd"))] + assert_eq!( + times.last_modification.tv_nsec as u64, + after.st_mtime_nsec as u64 + ); + #[cfg(target_os = "netbsd")] + assert_eq!( + times.last_modification.tv_nsec as u64, + after.st_mtimensec as u64 + ); +} diff --git a/vendor/rustix/tests/fs/invalid_offset.rs b/vendor/rustix/tests/fs/invalid_offset.rs new file mode 100644 index 000000000..995e302a1 --- /dev/null +++ b/vendor/rustix/tests/fs/invalid_offset.rs @@ -0,0 +1,182 @@ +//! Tests for extreme `u64` file offsets. +//! +//! POSIX-ish interfaces tend to use signed integers for file offsets, while +//! Rust APIs tend to use `u64`. Test that extreme `u64` values in APIs that +//! take file offsets are properly diagnosed. +//! +//! These tests are disabled on ios/macos since those platforms kill the +//! process with `SIGXFSZ` instead of returning an error. + +#![cfg(not(any(target_os = "redox", target_os = "wasi")))] + +use rustix::io::SeekFrom; + +#[test] +fn invalid_offset_seek() { + use rustix::fs::{cwd, openat, seek, Mode, OFlags}; + let tmp = tempfile::tempdir().unwrap(); + let dir = openat(cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); + let file = openat( + &dir, + "foo", + OFlags::WRONLY | OFlags::TRUNC | OFlags::CREATE, + Mode::RUSR | Mode::WUSR, + ) + .unwrap(); + + seek(&file, SeekFrom::Start(u64::MAX)).unwrap_err(); + seek(&file, SeekFrom::Start(i64::MAX as u64 + 1)).unwrap_err(); + seek(&file, SeekFrom::End(-1)).unwrap_err(); + seek(&file, SeekFrom::End(i64::MIN)).unwrap_err(); + seek(&file, SeekFrom::Current(-1)).unwrap_err(); + seek(&file, SeekFrom::Current(i64::MIN)).unwrap_err(); +} + +#[cfg(not(any( + target_os = "dragonfly", + target_os = "illumos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox", +)))] +#[test] +fn invalid_offset_fallocate() { + use rustix::fs::{cwd, fallocate, openat, FallocateFlags, Mode, OFlags}; + let tmp = tempfile::tempdir().unwrap(); + let dir = openat(cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); + let file = openat( + &dir, + "foo", + OFlags::WRONLY | OFlags::TRUNC | OFlags::CREATE, + Mode::RUSR | Mode::WUSR, + ) + .unwrap(); + + fallocate(&file, FallocateFlags::empty(), u64::MAX, 1).unwrap_err(); + fallocate(&file, FallocateFlags::empty(), i64::MAX as u64 + 1, 1).unwrap_err(); + fallocate(&file, FallocateFlags::empty(), 0, u64::MAX).unwrap_err(); + fallocate(&file, FallocateFlags::empty(), 0, i64::MAX as u64 + 1).unwrap_err(); +} + +#[cfg(not(any( + target_os = "dragonfly", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox", +)))] +#[test] +fn invalid_offset_fadvise() { + use rustix::fs::{cwd, fadvise, openat, Advice, Mode, OFlags}; + let tmp = tempfile::tempdir().unwrap(); + let dir = openat(cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); + let file = openat( + &dir, + "foo", + OFlags::WRONLY | OFlags::TRUNC | OFlags::CREATE, + Mode::RUSR | Mode::WUSR, + ) + .unwrap(); + + // `fadvise` never fails on invalid offsets. + fadvise(&file, i64::MAX as u64, i64::MAX as u64, Advice::Normal).unwrap(); + fadvise(&file, u64::MAX, 0, Advice::Normal).unwrap(); + fadvise(&file, i64::MAX as u64, 1, Advice::Normal).unwrap(); + fadvise(&file, 1, i64::MAX as u64, Advice::Normal).unwrap(); + fadvise(&file, i64::MAX as u64 + 1, 0, Advice::Normal).unwrap(); + fadvise(&file, u64::MAX, i64::MAX as u64, Advice::Normal).unwrap(); + + // `fadvise` fails on invalid lengths. + fadvise(&file, u64::MAX, u64::MAX, Advice::Normal).unwrap_err(); + fadvise(&file, i64::MAX as u64, u64::MAX, Advice::Normal).unwrap_err(); + fadvise(&file, 0, u64::MAX, Advice::Normal).unwrap_err(); + fadvise(&file, u64::MAX, i64::MAX as u64 + 1, Advice::Normal).unwrap_err(); + fadvise(&file, i64::MAX as u64 + 1, u64::MAX, Advice::Normal).unwrap_err(); + fadvise(&file, i64::MAX as u64, i64::MAX as u64 + 1, Advice::Normal).unwrap_err(); + fadvise(&file, 0, i64::MAX as u64 + 1, Advice::Normal).unwrap_err(); +} + +#[test] +fn invalid_offset_pread() { + use rustix::fs::{cwd, openat, Mode, OFlags}; + use rustix::io::pread; + let tmp = tempfile::tempdir().unwrap(); + let dir = openat(cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); + let file = openat( + &dir, + "foo", + OFlags::RDWR | OFlags::TRUNC | OFlags::CREATE, + Mode::RUSR | Mode::WUSR, + ) + .unwrap(); + + let mut buf = [0_u8; 1]; + pread(&file, &mut buf, u64::MAX).unwrap_err(); + pread(&file, &mut buf, i64::MAX as u64 + 1).unwrap_err(); +} + +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +#[test] +fn invalid_offset_pwrite() { + use rustix::fs::{cwd, openat, Mode, OFlags}; + use rustix::io::pwrite; + let tmp = tempfile::tempdir().unwrap(); + let dir = openat(cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); + let file = openat( + &dir, + "foo", + OFlags::WRONLY | OFlags::TRUNC | OFlags::CREATE, + Mode::RUSR | Mode::WUSR, + ) + .unwrap(); + + let buf = [0_u8; 1]; + pwrite(&file, &buf, u64::MAX).unwrap_err(); + pwrite(&file, &buf, i64::MAX as u64 + 1).unwrap_err(); +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn invalid_offset_copy_file_range() { + use rustix::fs::{copy_file_range, cwd, openat, Mode, OFlags}; + use rustix::io::write; + let tmp = tempfile::tempdir().unwrap(); + let dir = openat(cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); + let foo = openat( + &dir, + "foo", + OFlags::RDWR | OFlags::TRUNC | OFlags::CREATE, + Mode::RUSR | Mode::WUSR, + ) + .unwrap(); + let bar = openat( + &dir, + "bar", + OFlags::WRONLY | OFlags::TRUNC | OFlags::CREATE, + Mode::RUSR | Mode::WUSR, + ) + .unwrap(); + write(&foo, b"a").unwrap(); + + let mut off_in = u64::MAX; + let mut off_out = 0; + copy_file_range(&foo, Some(&mut off_in), &bar, Some(&mut off_out), 1).unwrap_err(); + + let mut off_in = i64::MAX as u64 + 1; + let mut off_out = 0; + copy_file_range(&foo, Some(&mut off_in), &bar, Some(&mut off_out), 1).unwrap_err(); + + let mut off_in = 0; + let mut off_out = u64::MAX; + copy_file_range(&foo, Some(&mut off_in), &bar, Some(&mut off_out), 1).unwrap_err(); + + let mut off_in = 0; + let mut off_out = i64::MAX as u64; + copy_file_range(&foo, Some(&mut off_in), &bar, Some(&mut off_out), 1).unwrap_err(); + + let mut off_in = 0; + let mut off_out = i64::MAX as u64 + 1; + copy_file_range(&foo, Some(&mut off_in), &bar, Some(&mut off_out), 1).unwrap_err(); +} diff --git a/vendor/rustix/tests/fs/long_paths.rs b/vendor/rustix/tests/fs/long_paths.rs new file mode 100644 index 000000000..dbc2fa6ea --- /dev/null +++ b/vendor/rustix/tests/fs/long_paths.rs @@ -0,0 +1,28 @@ +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +#[test] +fn test_long_paths() { + use rustix::fs::{cwd, mkdirat, openat, Mode, OFlags}; + + let tmp = tempfile::tempdir().unwrap(); + let dir = openat(cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); + + #[cfg(libc)] + const PATH_MAX: usize = libc::PATH_MAX as usize; + #[cfg(linux_raw)] + const PATH_MAX: usize = linux_raw_sys::general::PATH_MAX as usize; + + mkdirat(&dir, "a", Mode::RUSR | Mode::XUSR | Mode::WUSR).unwrap(); + + let mut long_path = String::new(); + for _ in 0..PATH_MAX / 5 { + long_path.push_str("a/../"); + } + + let mut too_long_path = String::new(); + for _ in 0..PATH_MAX / 4 { + too_long_path.push_str("a/../"); + } + + let _ = openat(&dir, &long_path, OFlags::RDONLY, Mode::empty()).unwrap(); + let _ = openat(&dir, &too_long_path, OFlags::RDONLY, Mode::empty()).unwrap_err(); +} diff --git a/vendor/rustix/tests/fs/main.rs b/vendor/rustix/tests/fs/main.rs new file mode 100644 index 000000000..23928a6ba --- /dev/null +++ b/vendor/rustix/tests/fs/main.rs @@ -0,0 +1,47 @@ +//! Tests for [`rustix::fs`]. + +#![cfg(feature = "fs")] +#![cfg(not(windows))] +#![cfg_attr(target_os = "wasi", feature(wasi_ext))] +#![cfg_attr(io_lifetimes_use_std, feature(io_safety))] +#![cfg_attr(core_c_str, feature(core_c_str))] + +mod cwd; +mod dir; +mod fcntl; +mod file; +#[cfg(not(target_os = "wasi"))] +mod flock; +mod futimens; +mod invalid_offset; +mod long_paths; +#[cfg(not(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox", + target_os = "wasi", +)))] +mod makedev; +mod mkdirat; +mod mknodat; +#[cfg(any(target_os = "android", target_os = "linux"))] +mod openat; +#[cfg(any(target_os = "android", target_os = "linux"))] +mod openat2; +mod readdir; +mod renameat; +#[cfg(not(any( + target_os = "illumos", + target_os = "netbsd", + target_os = "redox", + target_os = "wasi", +)))] +// not implemented in libc for netbsd yet +mod statfs; +mod utimensat; +mod y2038; diff --git a/vendor/rustix/tests/fs/makedev.rs b/vendor/rustix/tests/fs/makedev.rs new file mode 100644 index 000000000..79b5199e8 --- /dev/null +++ b/vendor/rustix/tests/fs/makedev.rs @@ -0,0 +1,10 @@ +use rustix::fs::{major, makedev, minor}; + +#[test] +fn makedev_roundtrip() { + let maj = 0x2324_2526; + let min = 0x6564_6361; + let dev = makedev(maj, min); + assert_eq!(maj, major(dev)); + assert_eq!(min, minor(dev)); +} diff --git a/vendor/rustix/tests/fs/mkdirat.rs b/vendor/rustix/tests/fs/mkdirat.rs new file mode 100644 index 000000000..884588037 --- /dev/null +++ b/vendor/rustix/tests/fs/mkdirat.rs @@ -0,0 +1,33 @@ +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +#[test] +fn test_mkdirat() { + use rustix::fs::{cwd, mkdirat, openat, statat, unlinkat, AtFlags, FileType, Mode, OFlags}; + + let tmp = tempfile::tempdir().unwrap(); + let dir = openat(cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); + + mkdirat(&dir, "foo", Mode::RWXU).unwrap(); + let stat = statat(&dir, "foo", AtFlags::empty()).unwrap(); + assert_eq!(FileType::from_raw_mode(stat.st_mode), FileType::Directory); + unlinkat(&dir, "foo", AtFlags::REMOVEDIR).unwrap(); +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_mkdirat_with_o_path() { + use rustix::fs::{cwd, mkdirat, openat, statat, unlinkat, AtFlags, FileType, Mode, OFlags}; + + let tmp = tempfile::tempdir().unwrap(); + let dir = openat( + cwd(), + tmp.path(), + OFlags::RDONLY | OFlags::PATH, + Mode::empty(), + ) + .unwrap(); + + mkdirat(&dir, "foo", Mode::RWXU).unwrap(); + let stat = statat(&dir, "foo", AtFlags::empty()).unwrap(); + assert_eq!(FileType::from_raw_mode(stat.st_mode), FileType::Directory); + unlinkat(&dir, "foo", AtFlags::REMOVEDIR).unwrap(); +} diff --git a/vendor/rustix/tests/fs/mknodat.rs b/vendor/rustix/tests/fs/mknodat.rs new file mode 100644 index 000000000..fa1c84f69 --- /dev/null +++ b/vendor/rustix/tests/fs/mknodat.rs @@ -0,0 +1,27 @@ +#[cfg(not(any( + target_os = "ios", + target_os = "macos", + target_os = "redox", + target_os = "wasi", +)))] +#[test] +fn test_mknodat() { + use rustix::fs::{cwd, mknodat, openat, statat, unlinkat, AtFlags, FileType, Mode, OFlags}; + + let tmp = tempfile::tempdir().unwrap(); + let dir = openat(cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); + + // Create a regular file. Not supported on FreeBSD or OpenBSD. + #[cfg(not(any(target_os = "freebsd", target_os = "openbsd")))] + { + mknodat(&dir, "foo", FileType::RegularFile, Mode::empty(), 0).unwrap(); + let stat = statat(&dir, "foo", AtFlags::empty()).unwrap(); + assert_eq!(FileType::from_raw_mode(stat.st_mode), FileType::RegularFile); + unlinkat(&dir, "foo", AtFlags::empty()).unwrap(); + } + + mknodat(&dir, "foo", FileType::Fifo, Mode::empty(), 0).unwrap(); + let stat = statat(&dir, "foo", AtFlags::empty()).unwrap(); + assert_eq!(FileType::from_raw_mode(stat.st_mode), FileType::Fifo); + unlinkat(&dir, "foo", AtFlags::empty()).unwrap(); +} diff --git a/vendor/rustix/tests/fs/openat.rs b/vendor/rustix/tests/fs/openat.rs new file mode 100644 index 000000000..564574dc1 --- /dev/null +++ b/vendor/rustix/tests/fs/openat.rs @@ -0,0 +1,33 @@ +use std::fs::File; + +use io_lifetimes::{FromFd, IntoFd}; +use rustix::fs::{cwd, openat, Mode, OFlags}; +use std::io::Write; + +#[test] +fn test_openat_tmpfile() { + let tmp = tempfile::tempdir().unwrap(); + let dir = openat( + cwd(), + tmp.path(), + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ) + .unwrap(); + let f = match openat( + &dir, + ".", + OFlags::WRONLY | OFlags::CLOEXEC | OFlags::TMPFILE, + Mode::from_bits_truncate(0o644), + ) { + Ok(f) => Ok(Some(File::from_fd(f.into_fd()))), + // TODO: Factor out the `Err`, once we no longer support Rust 1.48. + Err(rustix::io::Errno::OPNOTSUPP) + | Err(rustix::io::Errno::ISDIR) + | Err(rustix::io::Errno::NOENT) => Ok(None), + Err(e) => Err(e), + }; + if let Some(mut f) = f.unwrap() { + write!(f, "hello world").unwrap(); + } +} diff --git a/vendor/rustix/tests/fs/openat2.rs b/vendor/rustix/tests/fs/openat2.rs new file mode 100644 index 000000000..0b1d86fe2 --- /dev/null +++ b/vendor/rustix/tests/fs/openat2.rs @@ -0,0 +1,184 @@ +use rustix::fd::AsFd; +use rustix::fs::{cwd, mkdirat, openat, openat2, symlinkat, Mode, OFlags, ResolveFlags}; +use rustix::io::OwnedFd; +use rustix::{io, path}; +use std::os::unix::io::AsRawFd; + +/// Like `openat2`, but keep retrying until it fails or succeeds. +fn openat2_more<Fd: AsFd, P: path::Arg>( + dirfd: Fd, + path: P, + oflags: OFlags, + mode: Mode, + resolve: ResolveFlags, +) -> io::Result<OwnedFd> { + let path = path.as_cow_c_str().unwrap().into_owned(); + loop { + match openat2(dirfd.as_fd(), &path, oflags, mode, resolve) { + Ok(file) => return Ok(file), + Err(io::Errno::AGAIN) => continue, + Err(err) => return Err(err), + } + } +} + +#[test] +fn test_openat2() { + let tmp = tempfile::tempdir().unwrap(); + let dir = openat(cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); + + // Detect whether `openat2` is available. + match openat2( + &dir, + ".", + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::empty(), + ) { + Ok(_file) => (), + Err(io::Errno::NOSYS) => return, + Err(_err) => return, + } + + // Create a file. + let _ = openat2_more( + &dir, + "test.txt", + OFlags::WRONLY | OFlags::CREATE | OFlags::TRUNC | OFlags::CLOEXEC, + Mode::RUSR, + ResolveFlags::empty(), + ) + .unwrap(); + + // Test `NO_SYMLINKS`. + symlinkat("test.txt", &dir, "symlink.txt").unwrap(); + let _ = openat2_more( + &dir, + "symlink.txt", + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::empty(), + ) + .unwrap(); + let _ = openat2_more( + &dir, + "symlink.txt", + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::NO_MAGICLINKS, + ) + .unwrap(); + let _ = openat2_more( + &dir, + "symlink.txt", + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::NO_SYMLINKS, + ) + .unwrap_err(); + + // Test `NO_MAGICLINKS`. + let test = openat2_more( + &dir, + "test.txt", + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::empty(), + ) + .unwrap(); + let _ = openat2_more( + &dir, + &format!("/proc/self/fd/{}", test.as_fd().as_raw_fd()), + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::empty(), + ) + .unwrap(); + let _ = openat2_more( + &dir, + &format!("/proc/self/fd/{}", test.as_fd().as_raw_fd()), + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::NO_SYMLINKS, + ) + .unwrap_err(); + let _ = openat2_more( + &dir, + &format!("/proc/self/fd/{}", test.as_fd().as_raw_fd()), + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::NO_MAGICLINKS, + ) + .unwrap_err(); + + // Test `NO_XDEV`. + let root = openat2_more( + &dir, + "/", + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::empty(), + ) + .unwrap(); + let _ = openat2_more( + &root, + "proc", + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::empty(), + ) + .unwrap(); + let _ = openat2_more( + &root, + "proc", + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::NO_XDEV, + ) + .unwrap_err(); + + // Test `BENEATH`. + let _ = openat2_more( + &dir, + "..", + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::empty(), + ) + .unwrap(); + let _ = openat2_more( + &dir, + "..", + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::BENEATH, + ) + .unwrap_err(); + + // Test `IN_ROOT`. + let _ = openat2_more( + &dir, + "/proc", + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::empty(), + ) + .unwrap(); + let _ = openat2_more( + &dir, + "/proc", + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::IN_ROOT, + ) + .unwrap_err(); + mkdirat(&dir, "proc", Mode::RUSR | Mode::XUSR).unwrap(); + let _ = openat2_more( + &dir, + "/proc", + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ResolveFlags::IN_ROOT, + ) + .unwrap(); +} diff --git a/vendor/rustix/tests/fs/readdir.rs b/vendor/rustix/tests/fs/readdir.rs new file mode 100644 index 000000000..8925660a9 --- /dev/null +++ b/vendor/rustix/tests/fs/readdir.rs @@ -0,0 +1,68 @@ +#![cfg(not(target_os = "redox"))] + +use rustix::fs::{Dir, DirEntry}; +use std::collections::HashMap; + +#[test] +fn dir_entries() { + let tmpdir = tempfile::tempdir().expect("construct tempdir"); + let dirfd = std::fs::File::open(tmpdir.path()).expect("open tempdir as file"); + let mut dir = Dir::read_from(dirfd).expect("construct Dir from dirfd"); + + let entries = read_entries(&mut dir); + assert_eq!(entries.len(), 0, "no files in directory"); + + let _f1 = std::fs::File::create(tmpdir.path().join("file1")).expect("create file1"); + + let entries = read_entries(&mut dir); + assert!( + entries.get("file1").is_some(), + "directory contains `file1`: {:?}", + entries + ); + assert_eq!(entries.len(), 1); + + let _f2 = std::fs::File::create(tmpdir.path().join("file2")).expect("create file1"); + let entries = read_entries(&mut dir); + assert!( + entries.get("file1").is_some(), + "directory contains `file1`: {:?}", + entries + ); + assert!( + entries.get("file2").is_some(), + "directory contains `file2`: {:?}", + entries + ); + assert_eq!(entries.len(), 2); +} + +fn read_entries(dir: &mut Dir) -> HashMap<String, DirEntry> { + dir.rewind(); + let mut out = HashMap::new(); + loop { + match dir.read() { + Some(e) => { + let e = e.expect("non-error entry"); + let name = e.file_name().to_str().expect("utf8 filename").to_owned(); + if name != "." && name != ".." { + out.insert(name, e); + } + } + None => break, + } + } + out +} + +#[test] +fn dir_from_openat() { + let dirfd = rustix::fs::openat( + rustix::fs::cwd(), + ".", + rustix::fs::OFlags::RDONLY, + rustix::fs::Mode::empty(), + ) + .expect("open cwd as file"); + let _dir = Dir::read_from(dirfd).expect("construct Dir from dirfd"); +} diff --git a/vendor/rustix/tests/fs/renameat.rs b/vendor/rustix/tests/fs/renameat.rs new file mode 100644 index 000000000..2eea2e77a --- /dev/null +++ b/vendor/rustix/tests/fs/renameat.rs @@ -0,0 +1,104 @@ +#[cfg(any(target_os = "android", target_os = "linux"))] +use rustix::fs::Stat; + +#[cfg(any(target_os = "android", target_os = "linux"))] +fn same(a: &Stat, b: &Stat) -> bool { + a.st_ino == b.st_ino && a.st_dev == b.st_dev +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_renameat() { + use rustix::fs::{cwd, openat, renameat, statat, AtFlags, Mode, OFlags}; + + let tmp = tempfile::tempdir().unwrap(); + let dir = openat( + cwd(), + tmp.path(), + OFlags::RDONLY | OFlags::PATH, + Mode::empty(), + ) + .unwrap(); + + let _ = openat(&dir, "foo", OFlags::CREATE | OFlags::WRONLY, Mode::empty()).unwrap(); + let before = statat(&dir, "foo", AtFlags::empty()).unwrap(); + renameat(&dir, "foo", &dir, "bar").unwrap(); + let renamed = statat(&dir, "bar", AtFlags::empty()).unwrap(); + assert!(same(&before, &renamed)); +} + +/// Like `test_renameat` but the file already exists, so `renameat` +/// overwrites it. +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_renameat_overwrite() { + use rustix::fs::{cwd, openat, renameat, statat, AtFlags, Mode, OFlags}; + + let tmp = tempfile::tempdir().unwrap(); + let dir = openat( + cwd(), + tmp.path(), + OFlags::RDONLY | OFlags::PATH, + Mode::empty(), + ) + .unwrap(); + + let _ = openat(&dir, "foo", OFlags::CREATE | OFlags::WRONLY, Mode::empty()).unwrap(); + let _ = openat(&dir, "bar", OFlags::CREATE | OFlags::WRONLY, Mode::empty()).unwrap(); + let before = statat(&dir, "foo", AtFlags::empty()).unwrap(); + renameat(&dir, "foo", &dir, "bar").unwrap(); + let renamed = statat(&dir, "bar", AtFlags::empty()).unwrap(); + assert!(same(&before, &renamed)); +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_renameat_with() { + use rustix::fs::{cwd, openat, renameat_with, statat, AtFlags, Mode, OFlags, RenameFlags}; + + let tmp = tempfile::tempdir().unwrap(); + let dir = openat( + cwd(), + tmp.path(), + OFlags::RDONLY | OFlags::PATH, + Mode::empty(), + ) + .unwrap(); + + let _ = openat(&dir, "foo", OFlags::CREATE | OFlags::WRONLY, Mode::empty()).unwrap(); + let before = statat(&dir, "foo", AtFlags::empty()).unwrap(); + + match renameat_with(&dir, "foo", &dir, "red", RenameFlags::empty()) { + Ok(()) => (), + Err(e) if e == rustix::io::Errno::NOSYS => return, + Err(e) => unreachable!("unexpected error from renameat_with: {:?}", e), + } + + let renamed = statat(&dir, "red", AtFlags::empty()).unwrap(); + assert!(same(&before, &renamed)); + + let _ = openat( + &dir, + "green", + OFlags::CREATE | OFlags::WRONLY, + Mode::empty(), + ) + .unwrap(); + + #[cfg(all(target_os = "linux", target_env = "gnu"))] + { + let green = statat(&dir, "green", AtFlags::empty()).unwrap(); + + renameat_with(&dir, "red", &dir, "green", RenameFlags::NOREPLACE).unwrap_err(); + let renamed = statat(&dir, "red", AtFlags::empty()).unwrap(); + assert!(same(&before, &renamed)); + let orig = statat(&dir, "green", AtFlags::empty()).unwrap(); + assert!(same(&green, &orig)); + + renameat_with(&dir, "red", &dir, "green", RenameFlags::EXCHANGE).unwrap(); + let renamed = statat(&dir, "red", AtFlags::empty()).unwrap(); + assert!(same(&green, &renamed)); + let orig = statat(&dir, "green", AtFlags::empty()).unwrap(); + assert!(same(&before, &orig)); + } +} diff --git a/vendor/rustix/tests/fs/statfs.rs b/vendor/rustix/tests/fs/statfs.rs new file mode 100644 index 000000000..f8bf2e350 --- /dev/null +++ b/vendor/rustix/tests/fs/statfs.rs @@ -0,0 +1,49 @@ +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_statfs_abi() { + use rustix::fs::{FsWord, StatFs, NFS_SUPER_MAGIC, PROC_SUPER_MAGIC}; + + // Ensure these all have consistent types. + let t: StatFs = unsafe { std::mem::zeroed() }; + let _s: FsWord = t.f_type; + let _u: FsWord = PROC_SUPER_MAGIC; + let _v: FsWord = NFS_SUPER_MAGIC; + + // Ensure that after all the platform-specific dancing we have to do, this + // constant comes out with the correct value. + #[cfg(all(libc, not(target_env = "musl")))] + { + assert_eq!( + i128::from(PROC_SUPER_MAGIC), + i128::from(libc::PROC_SUPER_MAGIC) + ); + assert_eq!( + i128::from(NFS_SUPER_MAGIC), + i128::from(libc::NFS_SUPER_MAGIC) + ); + } + + #[cfg(linux_raw)] + { + assert_eq!( + i128::from(PROC_SUPER_MAGIC), + i128::from(linux_raw_sys::general::PROC_SUPER_MAGIC) + ); + assert_eq!( + i128::from(NFS_SUPER_MAGIC), + i128::from(linux_raw_sys::general::NFS_SUPER_MAGIC) + ); + } + + assert_eq!(PROC_SUPER_MAGIC, 0x0000_9fa0); + assert_eq!(NFS_SUPER_MAGIC, 0x0000_6969); +} + +#[test] +fn test_statfs() { + let statfs = rustix::fs::statfs("Cargo.toml").unwrap(); + let f_blocks = statfs.f_blocks; + assert_ne!(f_blocks, 0); + // Previously we checked f_files != 0 here, but at least btrfs doesn't set + // that. +} diff --git a/vendor/rustix/tests/fs/utimensat.rs b/vendor/rustix/tests/fs/utimensat.rs new file mode 100644 index 000000000..f0fb7e27b --- /dev/null +++ b/vendor/rustix/tests/fs/utimensat.rs @@ -0,0 +1,127 @@ +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +#[test] +fn test_utimensat() { + use rustix::fs::{cwd, openat, statat, utimensat, AtFlags, Mode, OFlags, Timespec, Timestamps}; + + let tmp = tempfile::tempdir().unwrap(); + let dir = openat( + cwd(), + tmp.path(), + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ) + .unwrap(); + + let _ = openat( + &dir, + "foo", + OFlags::CREATE | OFlags::WRONLY | OFlags::CLOEXEC, + Mode::empty(), + ) + .unwrap(); + + let times = Timestamps { + last_access: Timespec { + tv_sec: 44000, + tv_nsec: 45000, + }, + last_modification: Timespec { + tv_sec: 46000, + tv_nsec: 47000, + }, + }; + utimensat(&dir, "foo", ×, AtFlags::empty()).unwrap(); + + let after = statat(&dir, "foo", AtFlags::empty()).unwrap(); + + assert_eq!(times.last_modification.tv_sec as u64, after.st_mtime as u64); + #[cfg(not(target_os = "netbsd"))] + assert_eq!( + times.last_modification.tv_nsec as u64, + after.st_mtime_nsec as u64 + ); + #[cfg(target_os = "netbsd")] + assert_eq!( + times.last_modification.tv_nsec as u64, + after.st_mtimensec as u64 + ); + assert!(times.last_access.tv_sec as u64 >= after.st_atime as u64); + #[cfg(not(target_os = "netbsd"))] + assert!( + times.last_access.tv_sec as u64 > after.st_atime as u64 + || times.last_access.tv_nsec as u64 >= after.st_atime_nsec as u64 + ); + #[cfg(target_os = "netbsd")] + assert!( + times.last_access.tv_sec as u64 > after.st_atime as u64 + || times.last_access.tv_nsec as u64 >= after.st_atimensec as u64 + ); +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +#[test] +fn test_utimensat_noent() { + use rustix::fs::{cwd, openat, utimensat, AtFlags, Mode, OFlags, Timespec, Timestamps}; + + let tmp = tempfile::tempdir().unwrap(); + let dir = openat( + cwd(), + tmp.path(), + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ) + .unwrap(); + + let times = Timestamps { + last_access: Timespec { + tv_sec: 44000, + tv_nsec: 45000, + }, + last_modification: Timespec { + tv_sec: 46000, + tv_nsec: 47000, + }, + }; + assert_eq!( + utimensat(&dir, "foo", ×, AtFlags::empty()).unwrap_err(), + rustix::io::Errno::NOENT + ); +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +#[test] +fn test_utimensat_notdir() { + use rustix::fs::{cwd, openat, utimensat, AtFlags, Mode, OFlags, Timespec, Timestamps}; + + let tmp = tempfile::tempdir().unwrap(); + let dir = openat( + cwd(), + tmp.path(), + OFlags::RDONLY | OFlags::CLOEXEC, + Mode::empty(), + ) + .unwrap(); + + let foo = openat( + &dir, + "foo", + OFlags::CREATE | OFlags::WRONLY | OFlags::CLOEXEC, + Mode::empty(), + ) + .unwrap(); + + let times = Timestamps { + last_access: Timespec { + tv_sec: 44000, + tv_nsec: 45000, + }, + last_modification: Timespec { + tv_sec: 46000, + tv_nsec: 47000, + }, + }; + assert_eq!( + utimensat(&foo, "bar", ×, AtFlags::empty()).unwrap_err(), + rustix::io::Errno::NOTDIR + ); +} diff --git a/vendor/rustix/tests/fs/y2038.rs b/vendor/rustix/tests/fs/y2038.rs new file mode 100644 index 000000000..4299d709b --- /dev/null +++ b/vendor/rustix/tests/fs/y2038.rs @@ -0,0 +1,146 @@ +/// Test that we can set a file timestamp to a date past the year 2038 with +/// `utimensat` and read it back again. +/// +/// See tests/time/y2038.rs for more information about y2038 testing. +#[cfg(not(all(target_env = "musl", target_pointer_width = "32")))] +#[cfg(not(all(target_os = "android", target_pointer_width = "32")))] +#[cfg(not(all(target_os = "emscripten", target_pointer_width = "32")))] +#[test] +fn test_y2038_with_utimensat() { + use rustix::fs::{ + cwd, fstat, openat, statat, utimensat, AtFlags, Mode, OFlags, Timespec, Timestamps, + }; + use std::convert::TryInto; + + let tmp = tempfile::tempdir().unwrap(); + let dir = openat(&cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); + + let m_sec = 1_u64 << 32; + let m_nsec = 17_u32; + let a_sec = m_sec + 1; + let a_nsec = m_nsec + 1; + + let timestamps = Timestamps { + last_modification: Timespec { + tv_sec: m_sec as _, + tv_nsec: m_nsec as _, + }, + last_access: Timespec { + tv_sec: a_sec as _, + tv_nsec: a_nsec as _, + }, + }; + let _ = openat(&dir, "foo", OFlags::CREATE | OFlags::WRONLY, Mode::RUSR).unwrap(); + + match utimensat(&dir, "foo", ×tamps, AtFlags::empty()) { + Ok(()) => (), + + // On 32-bit platforms, accept `EOVERFLOW`, meaning that y2038 support + // is not available in this version of the OS. + #[cfg(target_pointer_width = "32")] + Err(rustix::io::Errno::OVERFLOW) => return, + + Err(e) => panic!("unexpected error: {:?}", e), + } + + // Use `statat` to read back the timestamp. + let stat = statat(&dir, "foo", AtFlags::empty()).unwrap(); + + assert_eq!( + TryInto::<u64>::try_into(stat.st_mtime).unwrap() as u64, + m_sec + ); + assert_eq!(stat.st_mtime_nsec as u32, m_nsec); + assert!(TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 >= a_sec); + assert!( + TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 > a_sec + || stat.st_atime_nsec as u32 >= a_nsec + ); + + // Now test the same thing, but with `fstat`. + let file = openat(&dir, "foo", OFlags::RDONLY, Mode::empty()).unwrap(); + let stat = fstat(&file).unwrap(); + + assert_eq!( + TryInto::<u64>::try_into(stat.st_mtime).unwrap() as u64, + m_sec + ); + assert_eq!(stat.st_mtime_nsec as u32, m_nsec); + assert!(TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 >= a_sec); + assert!( + TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 > a_sec + || stat.st_atime_nsec as u32 >= a_nsec + ); +} + +/// Test that we can set a file timestamp to a date past the year 2038 with +/// `futimens` and read it back again. +/// +/// See tests/time/y2038.rs for more information about y2038 testing. +#[cfg(not(all(target_env = "musl", target_pointer_width = "32")))] +#[cfg(not(all(target_os = "android", target_pointer_width = "32")))] +#[cfg(not(all(target_os = "emscripten", target_pointer_width = "32")))] +#[test] +fn test_y2038_with_futimens() { + use rustix::fs::{ + cwd, fstat, futimens, openat, statat, AtFlags, Mode, OFlags, Timespec, Timestamps, + }; + use std::convert::TryInto; + + let tmp = tempfile::tempdir().unwrap(); + let dir = openat(&cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); + + let m_sec = 1_u64 << 32; + let m_nsec = 17_u32; + let a_sec = m_sec + 1; + let a_nsec = m_nsec + 1; + + let timestamps = Timestamps { + last_modification: Timespec { + tv_sec: m_sec as _, + tv_nsec: m_nsec as _, + }, + last_access: Timespec { + tv_sec: a_sec as _, + tv_nsec: a_nsec as _, + }, + }; + let file = openat(&dir, "foo", OFlags::CREATE | OFlags::WRONLY, Mode::RUSR).unwrap(); + + match futimens(&file, ×tamps) { + Ok(()) => (), + + // On 32-bit platforms, accept `EOVERFLOW`, meaning that y2038 support + // is not available in this version of the OS. + #[cfg(target_pointer_width = "32")] + Err(rustix::io::Errno::OVERFLOW) => return, + + Err(e) => panic!("unexpected error: {:?}", e), + } + + // Use `statat` to read back the timestamp. + let stat = statat(&dir, "foo", AtFlags::empty()).unwrap(); + + assert_eq!(TryInto::<u64>::try_into(stat.st_mtime).unwrap(), m_sec); + assert_eq!(stat.st_mtime_nsec as u32, m_nsec); + assert!(TryInto::<u64>::try_into(stat.st_atime).unwrap() >= a_sec); + assert!( + TryInto::<u64>::try_into(stat.st_atime).unwrap() > a_sec + || stat.st_atime_nsec as u32 >= a_nsec + ); + + // Now test the same thing, but with `fstat`. + let file = openat(&dir, "foo", OFlags::RDONLY, Mode::empty()).unwrap(); + let stat = fstat(&file).unwrap(); + + assert_eq!( + TryInto::<u64>::try_into(stat.st_mtime).unwrap() as u64, + m_sec + ); + assert_eq!(stat.st_mtime_nsec as u32, m_nsec); + assert!(TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 >= a_sec); + assert!( + TryInto::<u64>::try_into(stat.st_atime).unwrap() as u64 > a_sec + || stat.st_atime_nsec as u32 >= a_nsec + ); +} |