// Safe: Child only calls `_exit`, which is signal-safe matchunsafe { fork() }.expect("Error: Fork Failed") {
Child => unsafe { _exit(0) },
Parent { child } => { // assert that child was created and pid > 0 let child_raw: ::libc::pid_t = child.into();
assert!(child_raw > 0); let wait_status = waitpid(child, None); match wait_status { // assert that waitpid returned correct status and the pid is the one of the child
Ok(WaitStatus::Exited(pid_t, _)) => assert_eq!(pid_t, child),
// panic, must never happen
s @ Ok(_) => {
panic!("Child exited {s:?}, should never happen")
}
// panic, waitpid should never fail
Err(s) => panic!("Error: waitpid returned Err({s:?}"),
}
}
}
}
// Safe: Child only calls `_exit`, which is signal-safe matchunsafe { rfork(RforkFlags::RFPROC | RforkFlags::RFTHREAD) }
.expect("Error: Rfork Failed")
{
Child => unsafe { _exit(0) },
Parent { child } => { // assert that child was created and pid > 0 let child_raw: ::libc::pid_t = child.into();
assert!(child_raw > 0); let wait_status = waitpid(child, None); match wait_status { // assert that waitpid returned correct status and the pid is the one of the child
Ok(WaitStatus::Exited(pid_t, _)) => assert_eq!(pid_t, child),
// panic, must never happen
s @ Ok(_) => {
panic!("Child exited {s:?}, should never happen")
}
// panic, waitpid should never fail
Err(s) => panic!("Error: waitpid returned Err({s:?}"),
}
}
}
}
#[test] fn test_wait() { // Grab FORK_MTX so wait doesn't reap a different test's child process let _m = crate::FORK_MTX.lock();
// Safe: Child only calls `_exit`, which is signal-safe matchunsafe { fork() }.expect("Error: Fork Failed") {
Child => unsafe { _exit(0) },
Parent { child } => { let wait_status = wait();
// just assert that (any) one child returns with WaitStatus::Exited
assert_eq!(wait_status, Ok(WaitStatus::Exited(child, 0)));
}
}
}
let result = mkstemp(&path); match result {
Ok((fd, path)) => {
close(fd).unwrap();
unlink(path.as_path()).unwrap();
}
Err(e) => panic!("mkstemp failed: {e}"),
}
}
#[test] fn test_mkstemp_directory() { // mkstemp should fail if a directory is given
mkstemp(&env::temp_dir()).expect_err("assertion failed");
}
#[test] #[cfg(not(target_os = "redox"))] fn test_mkfifo() { let tempdir = tempdir().unwrap(); let mkfifo_fifo = tempdir.path().join("mkfifo_fifo");
mkfifo(&mkfifo_fifo, Mode::S_IRUSR).unwrap();
let stats = stat::stat(&mkfifo_fifo).unwrap(); let typ = stat::SFlag::from_bits_truncate(stats.st_mode as mode_t);
assert_eq!(typ, SFlag::S_IFIFO);
}
#[test] #[cfg(not(target_os = "redox"))] fn test_mkfifo_directory() { // mkfifo should fail if a directory is given
mkfifo(&env::temp_dir(), Mode::S_IRUSR).expect_err("assertion failed");
}
let stats =
stat::fstatat(Some(dirfd), mkfifoat_name, fcntl::AtFlags::empty())
.unwrap(); let typ = stat::SFlag::from_bits_truncate(stats.st_mode);
assert_eq!(typ, SFlag::S_IFIFO);
}
#[test] // `getgroups()` and `setgroups()` do not behave as expected on Apple platforms #[cfg(not(any(
apple_targets,
target_os = "redox",
target_os = "fuchsia",
target_os = "haiku"
)))] fn test_setgroups() { // Skip this test when not run as root as `setgroups()` requires root.
skip_if_not_root!("test_setgroups");
let _m = crate::GROUPS_MTX.lock();
// Save the existing groups let old_groups = getgroups().unwrap();
// Set some new made up groups let groups = [Gid::from_raw(123), Gid::from_raw(456)];
setgroups(&groups).unwrap();
let new_groups = getgroups().unwrap();
assert_eq!(new_groups, groups);
// Revert back to the old groups
setgroups(&old_groups).unwrap();
}
#[test] // `getgroups()` and `setgroups()` do not behave as expected on Apple platforms #[cfg(not(any(
apple_targets,
target_os = "redox",
target_os = "fuchsia",
target_os = "haiku",
solarish
)))] fn test_initgroups() { // Skip this test when not run as root as `initgroups()` and `setgroups()` // require root.
skip_if_not_root!("test_initgroups");
let _m = crate::GROUPS_MTX.lock();
// Save the existing groups let old_groups = getgroups().unwrap();
// It doesn't matter if the root user is not called "root" or if a user // called "root" doesn't exist. We are just checking that the extra, // made-up group, `123`, is set. // FIXME: Test the other half of initgroups' functionality: whether the // groups that the user belongs to are also set. let user = CString::new("root").unwrap(); let group = Gid::from_raw(123); letmut group_list = getgrouplist(&user, group).unwrap();
assert!(group_list.contains(&group));
fn common_test(syscall: fn() -> Result<std::convert::Infallible, nix::Error>) { if"execveat" == stringify!($syscall) { // Though undocumented, Docker's default seccomp profile seems to // block this syscall. https://github.com/nix-rust/nix/issues/1122
skip_if_seccomp!($test_name);
}
let m = crate::FORK_MTX.lock(); // The `exec`d process will write to `writer`, and we'll read that // data from `reader`. let (reader, writer) = pipe().unwrap();
// Safe: Child calls `exit`, `dup`, `close` and the provided `exec*` family function. // NOTE: Technically, this makes the macro unsafe to use because you could pass anything. // The tests make sure not to do that, though. matchunsafe{fork()}.unwrap() {
Child => { // Make `writer` be the stdout of the new process.
dup2(writer.as_raw_fd(), 1).unwrap(); let r = syscall(); let _ = std::io::stderr()
.write_all(format!("{:?}", r).as_bytes()); // Should only get here in event of error unsafe{ _exit(1) };
},
Parent { child } => { // Wait for the child to exit. let ws = waitpid(child, None);
drop(m);
assert_eq!(ws, Ok(WaitStatus::Exited(child, 0))); // Read 1024 bytes. letmut buf = [0u8; 1024];
read(reader.as_raw_fd(), &mut buf).unwrap(); // It should contain the things we printed using `/bin/sh`. let string = String::from_utf8_lossy(&buf);
assert!(string.contains("nix!!!"));
assert!(string.contains("foo=bar"));
assert!(string.contains("baz=quux"));
}
}
}
// These tests frequently fail on musl, probably due to // https://github.com/nix-rust/nix/issues/555 #[cfg_attr(target_env = "musl", ignore)] #[test] fn test_cstr_ref() {
common_test(syscall_cstr_ref);
}
// These tests frequently fail on musl, probably due to // https://github.com/nix-rust/nix/issues/555 #[cfg_attr(target_env = "musl", ignore)] #[test] fn test_cstring() {
common_test(syscall_cstring);
}
}
)
);
cfg_if! { if#[cfg(target_os = "android")] {
execve_test_factory!(test_execve, execve, CString::new("/system/bin/sh").unwrap().as_c_str());
execve_test_factory!(test_fexecve, fexecve, File::open("/system/bin/sh").unwrap().into_raw_fd());
} elseif#[cfg(any(freebsdlike, target_os = "linux", target_os = "hurd"))] { // These tests frequently fail on musl, probably due to // https://github.com/nix-rust/nix/issues/555
execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str());
execve_test_factory!(test_fexecve, fexecve, File::open("/bin/sh").unwrap().into_raw_fd());
} elseif#[cfg(any(solarish, apple_targets, netbsdlike))] {
execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str()); // No fexecve() on ios, macos, NetBSD, OpenBSD.
}
}
#[test] #[cfg(not(target_os = "fuchsia"))] fn test_fchdir() { // fchdir changes the process's cwd let _dr = crate::DirRestore::new();
let tmpdir = tempdir().unwrap(); let tmpdir_path = tmpdir.path().canonicalize().unwrap(); let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd();
#[test] fn test_getcwd() { // chdir changes the process's cwd let _dr = crate::DirRestore::new();
let tmpdir = tempdir().unwrap(); let tmpdir_path = tmpdir.path().canonicalize().unwrap();
chdir(&tmpdir_path).expect("assertion failed");
assert_eq!(getcwd().unwrap(), tmpdir_path);
// make path 500 chars longer so that buffer doubling in getcwd // kicks in. Note: One path cannot be longer than 255 bytes // (NAME_MAX) whole path cannot be longer than PATH_MAX (usually // 4096 on linux, 1024 on macos) letmut inner_tmp_dir = tmpdir_path; for _ in0..5 { let newdir = "a".repeat(100);
inner_tmp_dir.push(newdir);
mkdir(inner_tmp_dir.as_path(), Mode::S_IRWXU)
.expect("assertion failed");
}
chdir(inner_tmp_dir.as_path()).expect("assertion failed");
assert_eq!(getcwd().unwrap(), inner_tmp_dir.as_path());
}
#[test] fn test_chown() { // Testing for anything other than our own UID/GID is hard. let uid = Some(getuid()); let gid = Some(getgid());
let tempdir = tempdir().unwrap(); let path = tempdir.path().join("file");
{
File::create(&path).unwrap();
}
#[test] #[cfg(not(any(
target_os = "redox",
target_os = "fuchsia",
target_os = "haiku"
)))] fn test_acct() { use std::process::Command; use std::{thread, time}; use tempfile::NamedTempFile;
let _m = crate::FORK_MTX.lock();
require_acct!();
let file = NamedTempFile::new().unwrap(); let path = file.path().to_str().unwrap();
acct::enable(path).unwrap();
loop {
Command::new("echo").arg("Hello world").output().unwrap(); let len = fs::metadata(path).unwrap().len(); if len > 0 { break;
}
thread::sleep(time::Duration::from_millis(10));
}
acct::disable().unwrap();
}
#[cfg_attr(target_os = "hurd", ignore)] #[test] fn test_fpathconf_limited() { let f = tempfile().unwrap(); // PATH_MAX is limited on most platforms, so it makes a good test let path_max = fpathconf(f, PathconfVar::PATH_MAX);
assert!(
path_max
.expect("fpathconf failed")
.expect("PATH_MAX is unlimited")
> 0
);
}
#[cfg_attr(target_os = "hurd", ignore)] #[test] fn test_pathconf_limited() { // PATH_MAX is limited on most platforms, so it makes a good test let path_max = pathconf("/", PathconfVar::PATH_MAX);
assert!(
path_max
.expect("pathconf failed")
.expect("PATH_MAX is unlimited")
> 0
);
}
#[cfg_attr(target_os = "hurd", ignore)] #[test] fn test_sysconf_limited() { // OPEN_MAX is limited on most platforms, so it makes a good test let open_max = sysconf(SysconfVar::OPEN_MAX);
assert!(
open_max
.expect("sysconf failed")
.expect("OPEN_MAX is unlimited")
> 0
);
}
#[cfg(target_os = "freebsd")] #[test] fn test_sysconf_unsupported() { // I know of no sysconf variables that are unsupported everywhere, but // _XOPEN_CRYPT is unsupported on FreeBSD 11.0, which is one of the platforms // we test. let open_max = sysconf(SysconfVar::_XOPEN_CRYPT);
assert!(open_max.expect("sysconf failed").is_none())
}
// Test that we can create a pair of pipes. No need to verify that they pass // data; that's the domain of the OS, not nix. #[test] fn test_pipe() { let (fd0, fd1) = pipe().unwrap(); let m0 = stat::SFlag::from_bits_truncate(
stat::fstat(fd0.as_raw_fd()).unwrap().st_mode as mode_t,
); // S_IFIFO means it's a pipe
assert_eq!(m0, SFlag::S_IFIFO); let m1 = stat::SFlag::from_bits_truncate(
stat::fstat(fd1.as_raw_fd()).unwrap().st_mode as mode_t,
);
assert_eq!(m1, SFlag::S_IFIFO);
}
// pipe2(2) is the same as pipe(2), except it allows setting some flags. Check // that we can set a flag. #[cfg(any(
linux_android,
freebsdlike,
solarish,
netbsdlike,
target_os = "emscripten",
target_os = "redox",
))] #[test] fn test_pipe2() { use nix::fcntl::{fcntl, FcntlArg, FdFlag};
let (fd0, fd1) = pipe2(OFlag::O_CLOEXEC).unwrap(); let f0 = FdFlag::from_bits_truncate(
fcntl(fd0.as_raw_fd(), FcntlArg::F_GETFD).unwrap(),
);
assert!(f0.contains(FdFlag::FD_CLOEXEC)); let f1 = FdFlag::from_bits_truncate(
fcntl(fd1.as_raw_fd(), FcntlArg::F_GETFD).unwrap(),
);
assert!(f1.contains(FdFlag::FD_CLOEXEC));
}
#[test] #[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] fn test_truncate() { let tempdir = tempdir().unwrap(); let path = tempdir.path().join("file");
// Maybe other tests that fork interfere with this one? let _m = crate::SIGNAL_MTX.lock();
let handler = SigHandler::Handler(alarm_signal_handler); let signal_action =
SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty()); let old_handler = unsafe {
sigaction(Signal::SIGALRM, &signal_action)
.expect("unable to set signal handler for alarm")
};
// Set an alarm.
assert_eq!(alarm::set(60), None);
// Overwriting an alarm should return the old alarm.
assert_eq!(alarm::set(1), Some(60));
// We should be woken up after 1 second by the alarm, so we'll sleep for 3 // seconds to be sure. let starttime = Instant::now(); loop {
thread::sleep(Duration::from_millis(100)); ifunsafe { ALARM_CALLED } { break;
} if starttime.elapsed() > Duration::from_secs(3) {
panic!("Timeout waiting for SIGALRM");
}
}
// Reset the signal. unsafe {
sigaction(Signal::SIGALRM, &old_handler)
.expect("unable to set signal handler for alarm");
}
}
// Get file descriptor for base directory of new file let dirfd = fcntl::open(
tempdir_newfile.path(),
fcntl::OFlag::empty(),
stat::Mode::empty(),
)
.unwrap();
// Attempt hard link file using curent working directory as relative path for old file path
chdir(tempdir_oldfile.path()).unwrap();
linkat(
None,
oldfilename,
Some(dirfd),
newfilename,
AtFlags::AT_SYMLINK_FOLLOW,
)
.unwrap();
assert!(newfilepath.exists());
}
// Get file descriptor for base directory of old file let dirfd = fcntl::open(
tempdir_oldfile.path(),
fcntl::OFlag::empty(),
stat::Mode::empty(),
)
.unwrap();
// Attempt hard link file using current working directory as relative path for new file path
chdir(tempdir_newfile.path()).unwrap();
linkat(
Some(dirfd),
oldfilename,
None,
newfilename,
AtFlags::AT_SYMLINK_FOLLOW,
)
.unwrap();
assert!(newfilepath.exists());
}
// Create symlink to file
symlinkat(&oldfilepath, None, &symoldfilepath).unwrap();
// Get file descriptor for base directory let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
// Attempt link target of symlink of file at relative path
linkat(
Some(dirfd),
symoldfilename,
Some(dirfd),
newfilename,
AtFlags::AT_SYMLINK_FOLLOW,
)
.unwrap();
let newfilestat = stat::stat(&newfilepath).unwrap();
// Check the file type of the new link
assert_eq!(
stat::SFlag::from_bits_truncate(newfilestat.st_mode as mode_t)
& SFlag::S_IFMT,
SFlag::S_IFREG
);
// Check the number of hard links to the original file
assert_eq!(newfilestat.st_nlink, 2);
}
#[test] #[cfg(not(target_os = "redox"))] fn test_unlinkat_dir_noremovedir() { let tempdir = tempdir().unwrap(); let dirname = "foo_dir"; let dirpath = tempdir.path().join(dirname);
// Create dir
DirBuilder::new().recursive(true).create(dirpath).unwrap();
// Get file descriptor for base directory let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
// Attempt unlink dir at relative path without proper flag let err_result =
unlinkat(Some(dirfd), dirname, UnlinkatFlags::NoRemoveDir).unwrap_err();
assert!(err_result == Errno::EISDIR || err_result == Errno::EPERM);
}
#[test] #[cfg(not(target_os = "redox"))] fn test_unlinkat_dir_removedir() { let tempdir = tempdir().unwrap(); let dirname = "foo_dir"; let dirpath = tempdir.path().join(dirname);
// Create dir
DirBuilder::new().recursive(true).create(&dirpath).unwrap();
// Get file descriptor for base directory let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
// Attempt unlink dir at relative path with proper flag
unlinkat(Some(dirfd), dirname, UnlinkatFlags::RemoveDir).unwrap();
assert!(!dirpath.exists());
}
#[test] #[cfg(not(target_os = "redox"))] fn test_unlinkat_file() { let tempdir = tempdir().unwrap(); let filename = "foo.txt"; let filepath = tempdir.path().join(filename);
// Create file
File::create(&filepath).unwrap();
// Get file descriptor for base directory let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
#[test] fn test_access_not_existing() { let tempdir = tempdir().unwrap(); let dir = tempdir.path().join("does_not_exist.txt");
assert_eq!(
access(&dir, AccessFlags::F_OK).err().unwrap(),
Errno::ENOENT
);
}
#[test] fn test_access_file_exists() { let tempdir = tempdir().unwrap(); let path = tempdir.path().join("does_exist.txt"); let _file = File::create(path.clone()).unwrap();
access(&path, AccessFlags::R_OK | AccessFlags::W_OK)
.expect("assertion failed");
}
#[cfg(not(target_os = "redox"))] #[test] fn test_user_into_passwd() { // get the UID of the "nobody" user #[cfg(not(target_os = "haiku"))] let test_username = "nobody"; // "nobody" unavailable on haiku #[cfg(target_os = "haiku")] let test_username = "user";
let nobody = User::from_name(test_username).unwrap().unwrap(); let pwd: libc::passwd = nobody.into(); let _: User = (&pwd).into();
}
/// Tests setting the filesystem UID with `setfsuid`. #[cfg(linux_android)] #[test] fn test_setfsuid() { use std::os::unix::fs::PermissionsExt; use std::{fs, io, thread};
require_capability!("test_setfsuid", CAP_SETUID);
// get the UID of the "nobody" user let nobody = User::from_name("nobody").unwrap().unwrap();
// create a temporary file with permissions '-rw-r-----' let file = tempfile::NamedTempFile::new_in("/var/tmp").unwrap(); let temp_path = file.into_temp_path(); let temp_path_2 = temp_path.to_path_buf(); letmut permissions = fs::metadata(&temp_path).unwrap().permissions();
permissions.set_mode(0o640);
// spawn a new thread where to test setfsuid
thread::spawn(move || { // set filesystem UID let fuid = setfsuid(nobody.uid); // trying to open the temporary file should fail with EACCES let res = fs::File::open(&temp_path); let err = res.expect_err("assertion failed");
assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
// assert fuid actually changes let prev_fuid = setfsuid(Uid::from_raw(-1i32 as u32));
assert_ne!(prev_fuid, fuid);
})
.join()
.unwrap();
// open the temporary file with the current thread filesystem UID
fs::File::open(temp_path_2).unwrap();
}
// on linux, we can just call ttyname on the pty master directly, but // apparently osx requires that ttyname is called on a slave pty (can't // find this documented anywhere, but it seems to empirically be the case)
grantpt(&fd).expect("grantpt failed");
unlockpt(&fd).expect("unlockpt failed"); let sname = unsafe { ptsname(&fd) }.expect("ptsname failed"); let fds = fs::OpenOptions::new()
.read(true)
.write(true)
.open(Path::new(&sname))
.expect("open failed");
let name = ttyname(fds).expect("ttyname failed");
assert!(name.starts_with("/dev"));
}
#[test] #[cfg(not(target_os = "redox"))] fn test_faccessat_none_not_existing() { use nix::fcntl::AtFlags; let tempdir = tempfile::tempdir().unwrap(); let dir = tempdir.path().join("does_not_exist.txt");
assert_eq!(
faccessat(None, &dir, AccessFlags::F_OK, AtFlags::empty())
.err()
.unwrap(),
Errno::ENOENT
);
}
#[test] #[cfg(not(target_os = "redox"))] fn test_faccessat_not_existing() { use nix::fcntl::AtFlags; let tempdir = tempfile::tempdir().unwrap(); let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); let not_exist_file = "does_not_exist.txt";
assert_eq!(
faccessat(
Some(dirfd),
not_exist_file,
AccessFlags::F_OK,
AtFlags::empty(),
)
.err()
.unwrap(),
Errno::ENOENT
);
}
#[test] #[cfg(not(target_os = "redox"))] fn test_faccessat_none_file_exists() { use nix::fcntl::AtFlags; let tempdir = tempfile::tempdir().unwrap(); let path = tempdir.path().join("does_exist.txt"); let _file = File::create(path.clone()).unwrap();
assert!(faccessat(
None,
&path,
AccessFlags::R_OK | AccessFlags::W_OK,
AtFlags::empty(),
)
.is_ok());
}
#[test] #[cfg(not(target_os = "redox"))] fn test_faccessat_file_exists() { use nix::fcntl::AtFlags; let tempdir = tempfile::tempdir().unwrap(); let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); let exist_file = "does_exist.txt"; let path = tempdir.path().join(exist_file); let _file = File::create(path.clone()).unwrap();
assert!(faccessat(
Some(dirfd),
&path,
AccessFlags::R_OK | AccessFlags::W_OK,
AtFlags::empty(),
)
.is_ok());
}
#[test] #[cfg(any(all(target_os = "linux", not(target_env = "uclibc")), freebsdlike))] fn test_eaccess_not_existing() { let tempdir = tempdir().unwrap(); let dir = tempdir.path().join("does_not_exist.txt");
assert_eq!(
eaccess(&dir, AccessFlags::F_OK).err().unwrap(),
Errno::ENOENT
);
}
#[test] #[cfg(any(all(target_os = "linux", not(target_env = "uclibc")), freebsdlike))] fn test_eaccess_file_exists() { let tempdir = tempdir().unwrap(); let path = tempdir.path().join("does_exist.txt"); let _file = File::create(path.clone()).unwrap();
eaccess(&path, AccessFlags::R_OK | AccessFlags::W_OK)
.expect("assertion failed");
}
#[test] #[cfg(bsd)] fn test_group_from() { let group = Group::from_name("wheel").unwrap().unwrap();
assert!(group.name == "wheel"); let group_id = group.gid; let group = Group::from_gid(group_id).unwrap().unwrap();
assert_eq!(group.gid, group_id);
assert_eq!(group.name, "wheel");
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.31 Sekunden
(vorverarbeitet am 2026-06-18)
¤
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.