Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  test_unistd.rs   Sprache: unbekannt

 
use libc::{_exit, mode_t, off_t};
use nix::errno::Errno;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::fcntl::readlink;
use nix::fcntl::OFlag;
#[cfg(not(target_os = "redox"))]
use nix::fcntl::{self, open};
#[cfg(not(any(
    target_os = "redox",
    target_os = "fuchsia",
    target_os = "haiku"
)))]
use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt};
#[cfg(not(target_os = "redox"))]
use nix::sys::signal::{
    sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal,
};
use nix::sys::stat::{self, Mode, SFlag};
use nix::sys::wait::*;
use nix::unistd::ForkResult::*;
use nix::unistd::*;
use std::env;
#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
use std::ffi::CString;
#[cfg(not(target_os = "redox"))]
use std::fs::DirBuilder;
use std::fs::{self, File};
use std::io::Write;
use std::os::unix::prelude::*;
#[cfg(not(any(
    target_os = "fuchsia",
    target_os = "redox",
    target_os = "haiku"
)))]
use std::path::Path;
use tempfile::{tempdir, tempfile};

use crate::*;

#[test]
#[cfg(not(any(target_os = "netbsd")))]
fn test_fork_and_waitpid() {
    let _m = crate::FORK_MTX.lock();

    // Safe: Child only calls `_exit`, which is signal-safe
    match unsafe { 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:?}"),
            }
        }
    }
}

#[test]
#[cfg(target_os = "freebsd")]
fn test_rfork_and_waitpid() {
    let _m = crate::FORK_MTX.lock();

    // Safe: Child only calls `_exit`, which is signal-safe
    match unsafe { 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
    match unsafe { 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)));
        }
    }
}

#[test]
fn test_mkstemp() {
    let mut path = env::temp_dir();
    path.push("nix_tempfile.XXXXXX");

    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");
}

#[test]
#[cfg(not(any(
    apple_targets,
    target_os = "android",
    target_os = "redox",
    target_os = "haiku"
)))]
fn test_mkfifoat_none() {
    let _m = crate::CWD_LOCK.read();

    let tempdir = tempdir().unwrap();
    let mkfifoat_fifo = tempdir.path().join("mkfifoat_fifo");

    mkfifoat(None, &mkfifoat_fifo, Mode::S_IRUSR).unwrap();

    let stats = stat::stat(&mkfifoat_fifo).unwrap();
    let typ = stat::SFlag::from_bits_truncate(stats.st_mode);
    assert_eq!(typ, SFlag::S_IFIFO);
}

#[test]
#[cfg(not(any(
    apple_targets,
    target_os = "android",
    target_os = "redox",
    target_os = "haiku"
)))]
fn test_mkfifoat() {
    use nix::fcntl;

    let tempdir = tempdir().unwrap();
    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let mkfifoat_name = "mkfifoat_name";

    mkfifoat(Some(dirfd), mkfifoat_name, Mode::S_IRUSR).unwrap();

    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]
#[cfg(not(any(
    apple_targets,
    target_os = "android",
    target_os = "redox",
    target_os = "haiku"
)))]
fn test_mkfifoat_directory_none() {
    let _m = crate::CWD_LOCK.read();

    // mkfifoat should fail if a directory is given
    mkfifoat(None, &env::temp_dir(), Mode::S_IRUSR)
        .expect_err("assertion failed");
}

#[test]
#[cfg(not(any(
    apple_targets,
    target_os = "android",
    target_os = "redox",
    target_os = "haiku"
)))]
fn test_mkfifoat_directory() {
    // mkfifoat should fail if a directory is given
    let tempdir = tempdir().unwrap();
    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let mkfifoat_dir = "mkfifoat_dir";
    stat::mkdirat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR).unwrap();

    mkfifoat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR)
        .expect_err("assertion failed");
}

#[test]
fn test_getpid() {
    let pid: ::libc::pid_t = getpid().into();
    let ppid: ::libc::pid_t = getppid().into();
    assert!(pid > 0);
    assert!(ppid > 0);
}

#[test]
#[cfg(not(target_os = "redox"))]
fn test_getsid() {
    let none_sid: ::libc::pid_t = getsid(None).unwrap().into();
    let pid_sid: ::libc::pid_t = getsid(Some(getpid())).unwrap().into();
    assert!(none_sid > 0);
    assert_eq!(none_sid, pid_sid);
}

#[cfg(linux_android)]
mod linux_android {
    use nix::unistd::gettid;

    #[test]
    fn test_gettid() {
        let tid: ::libc::pid_t = gettid().into();
        assert!(tid > 0);
    }
}

#[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);
    let mut group_list = getgrouplist(&user, group).unwrap();
    assert!(group_list.contains(&group));

    initgroups(&user, group).unwrap();

    let mut new_groups = getgroups().unwrap();

    new_groups.sort_by_key(|gid| gid.as_raw());
    group_list.sort_by_key(|gid| gid.as_raw());
    assert_eq!(new_groups, group_list);

    // Revert back to the old groups
    setgroups(&old_groups).unwrap();
}

#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
macro_rules! execve_test_factory (
    ($test_name:ident, $syscall:ident, $exe: expr $(, $pathname:expr, $flags:expr)*) => (

    #[cfg(test)]
    mod $test_name {
    use std::ffi::CStr;
    use super::*;

    const EMPTY: &'static [u8] = b"\0";
    const DASH_C: &'static [u8] = b"-c\0";
    const BIGARG: &'static [u8] = b"echo nix!!! && echo foo=$foo && echo baz=$baz\0";
    const FOO: &'static [u8] = b"foo=bar\0";
    const BAZ: &'static [u8] = b"baz=quux\0";

    fn syscall_cstr_ref() -> Result<std::convert::Infallible, nix::Error> {
        $syscall(
            $exe,
            $(CString::new($pathname).unwrap().as_c_str(), )*
            &[CStr::from_bytes_with_nul(EMPTY).unwrap(),
              CStr::from_bytes_with_nul(DASH_C).unwrap(),
              CStr::from_bytes_with_nul(BIGARG).unwrap()],
            &[CStr::from_bytes_with_nul(FOO).unwrap(),
              CStr::from_bytes_with_nul(BAZ).unwrap()]
            $(, $flags)*)
    }

    fn syscall_cstring() -> Result<std::convert::Infallible, nix::Error> {
        $syscall(
            $exe,
            $(CString::new($pathname).unwrap().as_c_str(), )*
            &[CString::from(CStr::from_bytes_with_nul(EMPTY).unwrap()),
              CString::from(CStr::from_bytes_with_nul(DASH_C).unwrap()),
              CString::from(CStr::from_bytes_with_nul(BIGARG).unwrap())],
            &[CString::from(CStr::from_bytes_with_nul(FOO).unwrap()),
              CString::from(CStr::from_bytes_with_nul(BAZ).unwrap())]
            $(, $flags)*)
    }

    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.
        match unsafe{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.
                let mut 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());
    } else if #[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());
    } else if #[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.
    }
}

#[cfg(any(
    target_os = "haiku",
    target_os = "hurd",
    target_os = "linux",
    target_os = "openbsd"
))]
execve_test_factory!(test_execvpe, execvpe, &CString::new("sh").unwrap());

cfg_if! {
    if #[cfg(target_os = "android")] {
        use nix::fcntl::AtFlags;
        execve_test_factory!(test_execveat_empty, execveat,
                             Some(File::open("/system/bin/sh").unwrap().into_raw_fd()),
                             "", AtFlags::AT_EMPTY_PATH);
        execve_test_factory!(test_execveat_relative, execveat,
                             Some(File::open("/system/bin/").unwrap().into_raw_fd()),
                             "./sh", AtFlags::empty());
        execve_test_factory!(test_execveat_absolute, execveat,
                             Some(File::open("/").unwrap().into_raw_fd()),
                             "/system/bin/sh", AtFlags::empty());
    } else if #[cfg(all(target_os = "linux", any(target_arch ="x86_64", target_arch ="x86")))] {
        use nix::fcntl::AtFlags;
        execve_test_factory!(test_execveat_empty, execveat, Some(File::open("/bin/sh").unwrap().into_raw_fd()),
                             "", AtFlags::AT_EMPTY_PATH);
        execve_test_factory!(test_execveat_relative, execveat, Some(File::open("/bin/").unwrap().into_raw_fd()),
                             "./sh", AtFlags::empty());
        execve_test_factory!(test_execveat_absolute, execveat, Some(File::open("/").unwrap().into_raw_fd()),
                             "/bin/sh", AtFlags::empty());
    }
}

#[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();

    fchdir(tmpdir_fd).expect("assertion failed");
    assert_eq!(getcwd().unwrap(), tmpdir_path);

    close(tmpdir_fd).expect("assertion failed");
}

#[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)
    let mut inner_tmp_dir = tmpdir_path;
    for _ in 0..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();
    }

    chown(&path, uid, gid).unwrap();
    chown(&path, uid, None).unwrap();
    chown(&path, None, gid).unwrap();

    fs::remove_file(&path).unwrap();
    chown(&path, uid, gid).unwrap_err();
}

#[test]
fn test_fchown() {
    // Testing for anything other than our own UID/GID is hard.
    let uid = Some(getuid());
    let gid = Some(getgid());

    let path = tempfile().unwrap();
    let fd = path.as_raw_fd();

    fchown(fd, uid, gid).unwrap();
    fchown(fd, uid, None).unwrap();
    fchown(fd, None, gid).unwrap();
    fchown(999999999, uid, gid).unwrap_err();
}

#[test]
#[cfg(not(target_os = "redox"))]
fn test_fchownat() {
    use nix::fcntl::AtFlags;

    let _dr = crate::DirRestore::new();
    // 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();
    }

    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();

    fchownat(Some(dirfd), "file", uid, gid, AtFlags::empty()).unwrap();

    chdir(tempdir.path()).unwrap();
    fchownat(None, "file", uid, gid, AtFlags::empty()).unwrap();

    fs::remove_file(&path).unwrap();
    fchownat(None, "file", uid, gid, AtFlags::empty()).unwrap_err();
}

#[test]
fn test_lseek() {
    const CONTENTS: &[u8] = b"abcdef123456";
    let mut tmp = tempfile().unwrap();
    tmp.write_all(CONTENTS).unwrap();

    let offset: off_t = 5;
    lseek(tmp.as_raw_fd(), offset, Whence::SeekSet).unwrap();

    let mut buf = [0u8; 7];
    crate::read_exact(&tmp, &mut buf);
    assert_eq!(b"f123456", &buf);
}

#[cfg(linux_android)]
#[test]
fn test_lseek64() {
    const CONTENTS: &[u8] = b"abcdef123456";
    let mut tmp = tempfile().unwrap();
    tmp.write_all(CONTENTS).unwrap();

    lseek64(tmp.as_raw_fd(), 5, Whence::SeekSet).unwrap();

    let mut buf = [0u8; 7];
    crate::read_exact(&tmp, &mut buf);
    assert_eq!(b"f123456", &buf);
}

cfg_if! {
    if #[cfg(linux_android)] {
        macro_rules! require_acct{
            () => {
                require_capability!("test_acct", CAP_SYS_PACCT);
            }
        }
    } else if #[cfg(target_os = "freebsd")] {
        macro_rules! require_acct{
            () => {
                skip_if_not_root!("test_acct");
                skip_if_jailed!("test_acct");
            }
        }
    } else if #[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "haiku")))] {
        macro_rules! require_acct{
            () => {
                skip_if_not_root!("test_acct");
            }
        }
    }
}

#[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())
}

#[cfg(any(linux_android, freebsdlike, target_os = "openbsd"))]
#[test]
fn test_getresuid() {
    let resuids = getresuid().unwrap();
    assert_ne!(resuids.real.as_raw(), libc::uid_t::MAX);
    assert_ne!(resuids.effective.as_raw(), libc::uid_t::MAX);
    assert_ne!(resuids.saved.as_raw(), libc::uid_t::MAX);
}

#[cfg(any(linux_android, freebsdlike, target_os = "openbsd"))]
#[test]
fn test_getresgid() {
    let resgids = getresgid().unwrap();
    assert_ne!(resgids.real.as_raw(), libc::gid_t::MAX);
    assert_ne!(resgids.effective.as_raw(), libc::gid_t::MAX);
    assert_ne!(resgids.saved.as_raw(), libc::gid_t::MAX);
}

// 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");

    {
        let mut tmp = File::create(&path).unwrap();
        const CONTENTS: &[u8] = b"12345678";
        tmp.write_all(CONTENTS).unwrap();
    }

    truncate(&path, 4).unwrap();

    let metadata = fs::metadata(&path).unwrap();
    assert_eq!(4, metadata.len());
}

#[test]
fn test_ftruncate() {
    let tempdir = tempdir().unwrap();
    let path = tempdir.path().join("file");

    let mut file = File::create(&path).unwrap();
    const CONTENTS: &[u8] = b"12345678";
    file.write_all(CONTENTS).unwrap();

    ftruncate(&file, 2).unwrap();
    drop(file);

    let metadata = fs::metadata(&path).unwrap();
    assert_eq!(2, metadata.len());
}

// Used in `test_alarm`.
#[cfg(not(target_os = "redox"))]
static mut ALARM_CALLED: bool = false;

// Used in `test_alarm`.
#[cfg(not(target_os = "redox"))]
pub extern "C" fn alarm_signal_handler(raw_signal: libc::c_int) {
    assert_eq!(raw_signal, libc::SIGALRM, "unexpected signal: {raw_signal}");
    unsafe { ALARM_CALLED = true };
}

#[test]
#[cfg(not(target_os = "redox"))]
fn test_alarm() {
    use std::{
        thread,
        time::{Duration, Instant},
    };

    // 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));
        if unsafe { 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");
    }
}

#[test]
#[cfg(not(target_os = "redox"))]
fn test_canceling_alarm() {
    let _m = crate::SIGNAL_MTX.lock();

    assert_eq!(alarm::cancel(), None);

    assert_eq!(alarm::set(60), None);
    assert_eq!(alarm::cancel(), Some(60));
}

#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_symlinkat() {
    let _m = crate::CWD_LOCK.read();

    let tempdir = tempdir().unwrap();

    let target = tempdir.path().join("a");
    let linkpath = tempdir.path().join("b");
    symlinkat(&target, None, &linkpath).unwrap();
    assert_eq!(
        readlink(&linkpath).unwrap().to_str().unwrap(),
        target.to_str().unwrap()
    );

    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let target = "c";
    let linkpath = "d";
    symlinkat(target, Some(dirfd), linkpath).unwrap();
    assert_eq!(
        readlink(&tempdir.path().join(linkpath))
            .unwrap()
            .to_str()
            .unwrap(),
        target
    );
}

#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_linkat_file() {
    use nix::fcntl::AtFlags;

    let tempdir = tempdir().unwrap();
    let oldfilename = "foo.txt";
    let oldfilepath = tempdir.path().join(oldfilename);

    let newfilename = "bar.txt";
    let newfilepath = tempdir.path().join(newfilename);

    // Create file
    File::create(oldfilepath).unwrap();

    // Get file descriptor for base directory
    let dirfd =
        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
            .unwrap();

    // Attempt hard link file at relative path
    linkat(
        Some(dirfd),
        oldfilename,
        Some(dirfd),
        newfilename,
        AtFlags::AT_SYMLINK_FOLLOW,
    )
    .unwrap();
    assert!(newfilepath.exists());
}

#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_linkat_olddirfd_none() {
    use nix::fcntl::AtFlags;

    let _dr = crate::DirRestore::new();

    let tempdir_oldfile = tempdir().unwrap();
    let oldfilename = "foo.txt";
    let oldfilepath = tempdir_oldfile.path().join(oldfilename);

    let tempdir_newfile = tempdir().unwrap();
    let newfilename = "bar.txt";
    let newfilepath = tempdir_newfile.path().join(newfilename);

    // Create file
    File::create(oldfilepath).unwrap();

    // 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());
}

#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_linkat_newdirfd_none() {
    use nix::fcntl::AtFlags;

    let _dr = crate::DirRestore::new();

    let tempdir_oldfile = tempdir().unwrap();
    let oldfilename = "foo.txt";
    let oldfilepath = tempdir_oldfile.path().join(oldfilename);

    let tempdir_newfile = tempdir().unwrap();
    let newfilename = "bar.txt";
    let newfilepath = tempdir_newfile.path().join(newfilename);

    // Create file
    File::create(oldfilepath).unwrap();

    // 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());
}

#[test]
#[cfg(not(any(apple_targets, target_os = "redox", target_os = "haiku")))]
fn test_linkat_no_follow_symlink() {
    use nix::fcntl::AtFlags;

    let _m = crate::CWD_LOCK.read();

    let tempdir = tempdir().unwrap();
    let oldfilename = "foo.txt";
    let oldfilepath = tempdir.path().join(oldfilename);

    let symoldfilename = "symfoo.txt";
    let symoldfilepath = tempdir.path().join(symoldfilename);

    let newfilename = "nofollowsymbar.txt";
    let newfilepath = tempdir.path().join(newfilename);

    // Create file
    File::create(&oldfilepath).unwrap();

    // 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 symlink of file at relative path
    linkat(
        Some(dirfd),
        symoldfilename,
        Some(dirfd),
        newfilename,
        AtFlags::empty(),
    )
    .unwrap();

    // Assert newfile is actually a symlink to oldfile.
    assert_eq!(
        readlink(&newfilepath).unwrap().to_str().unwrap(),
        oldfilepath.to_str().unwrap()
    );
}

#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_linkat_follow_symlink() {
    use nix::fcntl::AtFlags;

    let _m = crate::CWD_LOCK.read();

    let tempdir = tempdir().unwrap();
    let oldfilename = "foo.txt";
    let oldfilepath = tempdir.path().join(oldfilename);

    let symoldfilename = "symfoo.txt";
    let symoldfilepath = tempdir.path().join(symoldfilename);

    let newfilename = "nofollowsymbar.txt";
    let newfilepath = tempdir.path().join(newfilename);

    // Create file
    File::create(&oldfilepath).unwrap();

    // 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();

    // Attempt unlink file at relative path
    unlinkat(Some(dirfd), filename, UnlinkatFlags::NoRemoveDir).unwrap();
    assert!(!filepath.exists());
}

#[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();
    let mut 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();
}

#[test]
#[cfg(not(any(
    target_os = "redox",
    target_os = "fuchsia",
    target_os = "haiku"
)))]
fn test_ttyname() {
    let fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
    assert!(fd.as_raw_fd() > 0);

    // 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(any(target_os = "redox", target_os = "fuchsia")))]
fn test_ttyname_not_pty() {
    let fd = File::open("/dev/zero").unwrap();
    assert_eq!(ttyname(fd), Err(Errno::ENOTTY));
}

#[test]
#[cfg(bsd)]
fn test_getpeereid() {
    use std::os::unix::net::UnixStream;
    let (sock_a, sock_b) = UnixStream::pair().unwrap();

    let (uid_a, gid_a) = getpeereid(sock_a).unwrap();
    let (uid_b, gid_b) = getpeereid(sock_b).unwrap();

    let uid = geteuid();
    let gid = getegid();

    assert_eq!(uid, uid_a);
    assert_eq!(gid, gid_a);
    assert_eq!(uid_a, uid_b);
    assert_eq!(gid_a, gid_b);
}

#[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");
}

[ Dauer der Verarbeitung: 0.7 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge