#[test] #[cfg(target_os = "linux")] // QEMU does not handle openat well enough to satisfy this test // https://gitlab.com/qemu-project/qemu/-/issues/829 #[cfg_attr(qemu, ignore)] fn test_openat2_forbidden() { letmut tmp = NamedTempFile::new().unwrap();
tmp.write_all(b"let me out").unwrap();
let dirfd =
open(tmp.path().parent().unwrap(), OFlag::empty(), Mode::empty())
.unwrap();
let escape_attempt =
tmp.path().parent().unwrap().join("../../../hello.txt");
let res = openat2(
dirfd,
&escape_attempt,
OpenHow::new()
.flags(OFlag::O_RDONLY)
.resolve(ResolveFlag::RESOLVE_BENEATH),
);
assert_eq!(res.unwrap_err(), Errno::EXDEV);
}
#[test] #[cfg(not(target_os = "redox"))] fn test_renameat() { let old_dir = tempfile::tempdir().unwrap(); let old_dirfd =
open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap(); let old_path = old_dir.path().join("old");
File::create(old_path).unwrap(); let new_dir = tempfile::tempdir().unwrap(); let new_dirfd =
open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
renameat(&old_dirfd, "old", &new_dirfd, "new").unwrap();
assert_eq!(
renameat(&old_dirfd, "old", &new_dirfd, "new").unwrap_err(),
Errno::ENOENT
);
assert!(new_dir.path().join("new").exists());
}
/// This test creates a temporary file containing the contents /// 'foobarbaz' and uses the `copy_file_range` call to transfer /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The /// resulting file is read and should contain the contents `bar`. /// The from_offset should be updated by the call to reflect /// the 3 bytes read (6). #[cfg(any(
linux_android, // Not available until FreeBSD 13.0
all(target_os = "freebsd", fbsd14),
))] #[test] // QEMU does not support copy_file_range. Skip under qemu #[cfg_attr(qemu, ignore)] fn test_copy_file_range() { use nix::fcntl::copy_file_range;
// Check if we read exactly 100 bytes letmut buf = [0u8; 200];
assert_eq!(100, read(&tmp, &mut buf).unwrap());
}
// The tests below are disabled for the listed targets // due to OFD locks not being available in the kernel/libc // versions used in the CI environment, probably because // they run under QEMU.
#[test] #[cfg(all(target_os = "linux", not(target_env = "musl")))] #[cfg_attr(target_env = "uclibc", ignore)] // uclibc doesn't support OFD locks, but the test should still compile fn test_ofd_write_lock() { use nix::sys::stat::fstat; use std::mem;
let tmp = NamedTempFile::new().unwrap();
let statfs = nix::sys::statfs::fstatfs(tmp.as_file()).unwrap(); if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC { // OverlayFS is a union file system. It returns one inode value in // stat(2), but a different one shows up in /proc/locks. So we must // skip the test.
skip!("/proc/locks does not work on overlayfs");
} let inode = fstat(&tmp).expect("fstat failed").st_ino as usize;
#[test] #[cfg(all(target_os = "linux", not(target_env = "musl")))] #[cfg_attr(target_env = "uclibc", ignore)] // uclibc doesn't support OFD locks, but the test should still compile fn test_ofd_read_lock() { use nix::sys::stat::fstat; use std::mem;
let tmp = NamedTempFile::new().unwrap();
let statfs = nix::sys::statfs::fstatfs(tmp.as_file()).unwrap(); if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC { // OverlayFS is a union file system. It returns one inode value in // stat(2), but a different one shows up in /proc/locks. So we must // skip the test.
skip!("/proc/locks does not work on overlayfs");
} let inode = fstat(&tmp).expect("fstat failed").st_ino as usize;
let file = File::open("/proc/locks").expect("open /proc/locks failed"); let buf = BufReader::new(file);
for line in buf.lines() { let line = line.unwrap(); let parts: Vec<_> = line.split_whitespace().collect(); let lock_type = parts[1]; let lock_access = parts[3]; let ino_parts: Vec<_> = parts[5].split(':').collect(); let ino: usize = ino_parts[2].parse().unwrap(); if ino == inode { return Some((lock_type.to_string(), lock_access.to_string()));
}
}
None
}
}
#[cfg(any(
linux_android,
target_os = "emscripten",
target_os = "fuchsia",
target_os = "wasi",
target_env = "uclibc",
target_os = "freebsd"
))] mod test_posix_fadvise { use nix::errno::Errno; use nix::fcntl::*; use nix::unistd::pipe; use tempfile::NamedTempFile;
use nix::errno::Errno; use nix::fcntl::*; use nix::unistd::pipe; use std::io::Read; use tempfile::NamedTempFile;
#[test] fn success() { const LEN: usize = 100; letmut tmp = NamedTempFile::new().unwrap(); let res = posix_fallocate(&tmp, 0, LEN as libc::off_t); match res {
Ok(_) => { letmut data = [1u8; LEN];
assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
assert_eq!(&data[..], &[0u8; LEN][..]);
}
Err(Errno::EINVAL) => { // POSIX requires posix_fallocate to return EINVAL both for // invalid arguments (i.e. len < 0) and if the operation is not // supported by the file system. // There's no way to tell for sure whether the file system // supports posix_fallocate, so we must pass the test if it // returns EINVAL.
}
_ => res.unwrap(),
}
}
#[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] #[test] fn test_f_kinfo() { use nix::fcntl::*; use std::path::PathBuf;
let tmp = NamedTempFile::new().unwrap(); // With TMPDIR set with UFS, the vnode name is not entered // into the name cache thus path is always empty. // Therefore, we reopen the tempfile a second time for the test // to pass. let tmp2 = File::open(tmp.path()).unwrap(); letmut path = PathBuf::new(); let res =
fcntl(&tmp2, FcntlArg::F_KINFO(&mut path)).expect("get path failed");
assert_ne!(res, -1);
assert_eq!(path, tmp.path());
}
/// Test `Flock` and associated functions. /// #[cfg(not(any(target_os = "redox", target_os = "solaris")))] mod test_flock { use nix::fcntl::*; use tempfile::NamedTempFile;
/// Verify that `Flock::lock()` correctly obtains a lock, and subsequently unlocks upon drop. #[test] fn lock_and_drop() { // Get 2 `File` handles to same underlying file. let file1 = NamedTempFile::new().unwrap(); let file2 = file1.reopen().unwrap(); let file1 = file1.into_file();
// Lock first handle let lock1 = Flock::lock(file1, FlockArg::LockExclusive).unwrap();
// Attempt to lock second handle let file2 = match Flock::lock(file2, FlockArg::LockExclusiveNonblock) {
Ok(_) => panic!("Expected second exclusive lock to fail."),
Err((f, _)) => f,
};
// Drop first lock
std::mem::drop(lock1);
// Attempt to lock second handle again (but successfully) if Flock::lock(file2, FlockArg::LockExclusiveNonblock).is_err() {
panic!("Expected locking to be successful.");
}
}
/// An exclusive lock can be downgraded #[test] fn downgrade() { let file1 = NamedTempFile::new().unwrap(); let file2 = file1.reopen().unwrap(); let file1 = file1.into_file();
// Lock first handle let lock1 = Flock::lock(file1, FlockArg::LockExclusive).unwrap();
// Attempt to lock second handle let file2 = Flock::lock(file2, FlockArg::LockSharedNonblock)
.unwrap_err()
.0;
// Downgrade the lock
lock1.relock(FlockArg::LockShared).unwrap();
// Attempt to lock second handle again (but successfully)
Flock::lock(file2, FlockArg::LockSharedNonblock)
.expect("Expected locking to be successful.");
}
/// Verify that `Flock::unlock()` correctly obtains unlocks. #[test] fn unlock() { // Get 2 `File` handles to same underlying file. let file1 = NamedTempFile::new().unwrap(); let file2 = file1.reopen().unwrap(); let file1 = file1.into_file();
// Lock first handle let lock1 = Flock::lock(file1, FlockArg::LockExclusive).unwrap();
// Unlock and retain file so any erroneous flocks also remain present. let _file1 = lock1.unlock().unwrap();
// Attempt to lock second handle. if Flock::lock(file2, FlockArg::LockExclusiveNonblock).is_err() {
panic!("Expected locking to be successful.");
}
}
/// A shared lock can be upgraded #[test] fn upgrade() { let file1 = NamedTempFile::new().unwrap(); let file2 = file1.reopen().unwrap(); let file3 = file1.reopen().unwrap(); let file1 = file1.into_file();
// Lock first handle let lock1 = Flock::lock(file1, FlockArg::LockShared).unwrap();
// Attempt to lock second handle
{
Flock::lock(file2, FlockArg::LockSharedNonblock)
.expect("Locking should've succeeded");
}
// Upgrade the lock
lock1.relock(FlockArg::LockExclusive).unwrap();
// Acquiring an additional shared lock should fail
Flock::lock(file3, FlockArg::LockSharedNonblock)
.expect_err("Should not have been able to lock the file");
}
}
#[cfg(apple_targets)] #[test] fn test_f_rdadvise() { use nix::fcntl::*;
let contents = vec![1; 1024]; letmut buf = [0; 1024]; letmut tmp = NamedTempFile::new().unwrap();
tmp.write_all(&contents).unwrap(); let fd = open(tmp.path(), OFlag::empty(), Mode::empty()).unwrap(); let rad = libc::radvisory {
ra_offset: 0,
ra_count: contents.len() as _,
}; let res = fcntl(&tmp, FcntlArg::F_RDADVISE(rad)).expect("rdadivse failed");
assert_ne!(res, -1);
assert_eq!(contents.len(), read(&fd, &mut buf).unwrap());
assert_eq!(contents, &buf[0..contents.len()]);
}
#[cfg(apple_targets)] #[test] fn test_f_log2phys() { use nix::fcntl::*;
#[cfg(apple_targets)] #[test] fn test_f_transferextents() { use nix::fcntl::*; use std::os::fd::AsRawFd;
let tmp1 = NamedTempFile::new().unwrap(); let tmp2 = NamedTempFile::new().unwrap(); let res = fcntl(&tmp1, FcntlArg::F_TRANSFEREXTENTS(tmp2.as_raw_fd()))
.expect("transferextents failed");
assert_ne!(res, -1);
}
#[cfg(target_os = "freebsd")] #[test] fn test_f_readahead() { use nix::fcntl::*;
let tmp = NamedTempFile::new().unwrap(); letmut res = fcntl(&tmp, FcntlArg::F_READAHEAD(1_000_000))
.expect("read ahead failed");
assert_ne!(res, -1);
res = fcntl(&tmp, FcntlArg::F_READAHEAD(-1024)).expect("read ahead failed");
assert_ne!(res, -1);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.