diff --git a/changelog/2440.changed.md b/changelog/2440.changed.md new file mode 100644 index 0000000000..cd4f7feef7 --- /dev/null +++ b/changelog/2440.changed.md @@ -0,0 +1 @@ +Module unistd now adopts I/O safety. diff --git a/src/fcntl.rs b/src/fcntl.rs index f38b4d5d8a..a8fc3a91c5 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -13,8 +13,6 @@ use std::ffi::CStr; use std::ffi::OsString; #[cfg(not(any(target_os = "redox", target_os = "solaris")))] use std::ops::{Deref, DerefMut}; -#[cfg(not(target_os = "redox"))] -use std::os::raw; use std::os::unix::ffi::OsStringExt; #[cfg(not(any(target_os = "redox", target_os = "solaris")))] use std::os::unix::io::OwnedFd; @@ -234,12 +232,8 @@ libc_bitflags!( ); /// Computes the raw fd consumed by a function of the form `*at`. -#[cfg(any( - all(feature = "fs", not(target_os = "redox")), - all(feature = "process", linux_android), - all(feature = "fanotify", target_os = "linux") -))] -pub(crate) fn at_rawfd(fd: Option) -> raw::c_int { +#[cfg(all(feature = "fanotify", target_os = "linux"))] +pub(crate) fn at_rawfd(fd: Option) -> RawFd { fd.unwrap_or(libc::AT_FDCWD) } diff --git a/src/poll.rs b/src/poll.rs index 0ad9f40d3b..7b34da77c5 100644 --- a/src/poll.rs +++ b/src/poll.rs @@ -36,7 +36,7 @@ impl<'fd> PollFd<'fd> { /// let mut fds = [pfd]; /// poll(&mut fds, PollTimeout::NONE).unwrap(); /// let mut buf = [0u8; 80]; - /// read(r.as_raw_fd(), &mut buf[..]); + /// read(&r, &mut buf[..]); /// ``` // Unlike I/O functions, constructors like this must take `BorrowedFd` // instead of AsFd or &AsFd. Otherwise, an `OwnedFd` argument would be diff --git a/src/pty.rs b/src/pty.rs index 171bbfa138..b702de8d96 100644 --- a/src/pty.rs +++ b/src/pty.rs @@ -73,7 +73,7 @@ impl IntoRawFd for PtyMaster { impl io::Read for PtyMaster { fn read(&mut self, buf: &mut [u8]) -> io::Result { - unistd::read(self.0.as_raw_fd(), buf).map_err(io::Error::from) + unistd::read(&self.0, buf).map_err(io::Error::from) } } @@ -88,7 +88,7 @@ impl io::Write for PtyMaster { impl io::Read for &PtyMaster { fn read(&mut self, buf: &mut [u8]) -> io::Result { - unistd::read(self.0.as_raw_fd(), buf).map_err(io::Error::from) + unistd::read(&self.0, buf).map_err(io::Error::from) } } diff --git a/src/sys/eventfd.rs b/src/sys/eventfd.rs index 50a4f091bd..0199b18125 100644 --- a/src/sys/eventfd.rs +++ b/src/sys/eventfd.rs @@ -1,6 +1,6 @@ use crate::errno::Errno; -use crate::{Result,unistd}; -use std::os::unix::io::{FromRawFd, OwnedFd, AsRawFd, AsFd, RawFd, BorrowedFd}; +use crate::{unistd, Result}; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd}; libc_bitflags! { pub struct EfdFlags: libc::c_int { @@ -10,7 +10,10 @@ libc_bitflags! { } } -#[deprecated(since = "0.28.0", note = "Use EventFd::from_value_and_flags() instead")] +#[deprecated( + since = "0.28.0", + note = "Use EventFd::from_value_and_flags() instead" +)] pub fn eventfd(initval: libc::c_uint, flags: EfdFlags) -> Result { let res = unsafe { libc::eventfd(initval, flags.bits()) }; @@ -26,9 +29,12 @@ impl EventFd { Self::from_value_and_flags(0, EfdFlags::empty()) } /// Constructs [`EventFd`] with the given `init_val` and `flags`. - /// + /// /// Wrapper around [`libc::eventfd`]. - pub fn from_value_and_flags(init_val: u32, flags: EfdFlags) -> Result { + pub fn from_value_and_flags( + init_val: u32, + flags: EfdFlags, + ) -> Result { let res = unsafe { libc::eventfd(init_val, flags.bits()) }; Errno::result(res).map(|r| Self(unsafe { OwnedFd::from_raw_fd(r) })) } @@ -41,29 +47,29 @@ impl EventFd { Self::from_value_and_flags(init_val, EfdFlags::empty()) } /// Arms `self`, a following call to `poll`, `select` or `epoll` will return immediately. - /// + /// /// [`EventFd::write`] with `1`. pub fn arm(&self) -> Result { self.write(1) } /// Defuses `self`, a following call to `poll`, `select` or `epoll` will block. - /// + /// /// [`EventFd::write`] with `0`. pub fn defuse(&self) -> Result { self.write(0) } /// Enqueues `value` triggers. - /// + /// /// The next `value` calls to `poll`, `select` or `epoll` will return immediately. - /// + /// /// [`EventFd::write`] with `value`. - pub fn write(&self, value: u64) -> Result { - unistd::write(&self.0,&value.to_ne_bytes()) + pub fn write(&self, value: u64) -> Result { + unistd::write(&self.0, &value.to_ne_bytes()) } // Reads the value from the file descriptor. pub fn read(&self) -> Result { let mut arr = [0; std::mem::size_of::()]; - unistd::read(self.0.as_raw_fd(),&mut arr)?; + unistd::read(&self.0, &mut arr)?; Ok(u64::from_ne_bytes(arr)) } } diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index e22c52753d..c3b7fbd6d4 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -252,7 +252,11 @@ impl Drop for FanotifyEvent { if self.0.fd == libc::FAN_NOFD { return; } - let e = close(self.0.fd); + // SAFETY: + // + // If this fd is not `FAN_NOFD`, then it should be a valid, owned file + // descriptor, which means we can safely close it. + let e = unsafe { close(self.0.fd) }; if !std::thread::panicking() && e == Err(Errno::EBADF) { panic!("Closing an invalid file descriptor!"); }; @@ -362,7 +366,7 @@ impl Fanotify { let mut events = Vec::new(); let mut offset = 0; - let nread = read(self.fd.as_raw_fd(), &mut buffer)?; + let nread = read(&self.fd, &mut buffer)?; while (nread - offset) >= metadata_size { let metadata = unsafe { diff --git a/src/sys/inotify.rs b/src/sys/inotify.rs index 9cbeb53973..ee87a87d36 100644 --- a/src/sys/inotify.rs +++ b/src/sys/inotify.rs @@ -201,7 +201,7 @@ impl Inotify { let mut events = Vec::new(); let mut offset = 0; - let nread = read(self.fd.as_raw_fd(), &mut buffer)?; + let nread = read(&self.fd, &mut buffer)?; while (nread - offset) >= header_size { let event = unsafe { diff --git a/src/sys/timerfd.rs b/src/sys/timerfd.rs index 6a13c843e8..2fad7d8fd0 100644 --- a/src/sys/timerfd.rs +++ b/src/sys/timerfd.rs @@ -208,7 +208,7 @@ impl TimerFd { /// /// Note: If the alarm is unset, then you will wait forever. pub fn wait(&self) -> Result<()> { - while let Err(e) = read(self.fd.as_fd().as_raw_fd(), &mut [0u8; 8]) { + while let Err(e) = read(&self.fd, &mut [0u8; 8]) { if e == Errno::ECANCELED { break; } diff --git a/src/unistd.rs b/src/unistd.rs index 58ede6eb9b..c846e7c408 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -2,11 +2,6 @@ use crate::errno::Errno; -#[cfg(any( - all(feature = "fs", not(target_os = "redox")), - all(feature = "process", linux_android) -))] -use crate::fcntl::at_rawfd; #[cfg(not(target_os = "redox"))] #[cfg(feature = "fs")] use crate::fcntl::AtFlags; @@ -25,20 +20,15 @@ use crate::fcntl::AtFlags; use crate::fcntl::OFlag; #[cfg(all(feature = "fs", bsd))] use crate::sys::stat::FileFlag; -#[cfg(feature = "fs")] -use crate::sys::stat::Mode; use crate::{Error, NixPath, Result}; #[cfg(not(target_os = "redox"))] use cfg_if::cfg_if; -use libc::{ - c_char, c_int, c_long, c_uint, gid_t, mode_t, off_t, pid_t, size_t, uid_t, -}; +use libc::{c_char, c_int, c_long, c_uint, gid_t, off_t, pid_t, size_t, uid_t}; use std::convert::Infallible; #[cfg(not(target_os = "redox"))] use std::ffi::CString; use std::ffi::{CStr, OsStr, OsString}; use std::os::unix::ffi::{OsStrExt, OsStringExt}; -use std::os::unix::io::{AsFd, AsRawFd, OwnedFd, RawFd}; use std::path::PathBuf; use std::{fmt, mem, ptr}; @@ -365,7 +355,9 @@ feature! { /// Get the group process id (GPID) of the foreground process group on the /// terminal associated to file descriptor (FD). #[inline] -pub fn tcgetpgrp(fd: F) -> Result { +pub fn tcgetpgrp(fd: F) -> Result { + use std::os::fd::AsRawFd; + let res = unsafe { libc::tcgetpgrp(fd.as_fd().as_raw_fd()) }; Errno::result(res).map(Pid) } @@ -375,7 +367,9 @@ pub fn tcgetpgrp(fd: F) -> Result { /// Get the group process id (PGID) to the foreground process group on the /// terminal associated to file descriptor (FD). #[inline] -pub fn tcsetpgrp(fd: F, pgrp: Pid) -> Result<()> { +pub fn tcsetpgrp(fd: F, pgrp: Pid) -> Result<()> { + use std::os::fd::AsRawFd; + let res = unsafe { libc::tcsetpgrp(fd.as_fd().as_raw_fd(), pgrp.into()) }; Errno::result(res).map(drop) } @@ -412,8 +406,7 @@ pub fn gettid() -> Pid { feature! { #![feature = "fs"] -/// Create a copy of the specified file descriptor (see -/// [dup(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)). +/// Create a copy of the specified file descriptor. /// /// The new file descriptor will have a new index but refer to the same /// resource as the old file descriptor and the old and new file descriptors may @@ -422,31 +415,237 @@ feature! { /// for the file descriptor will be the lowest fd index that is available. /// /// The two file descriptors do not share file descriptor flags (e.g. `OFlag::FD_CLOEXEC`). +/// +/// # Reference +/// +/// * [POSIX manual](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html) +/// +/// # See also +/// +/// * [`dup2()`] +/// * [`dup2_raw()`] +/// * `dup3()` +/// * `dup3_raw()` #[inline] -pub fn dup(oldfd: RawFd) -> Result { - let res = unsafe { libc::dup(oldfd) }; +pub fn dup(oldfd: Fd) -> Result { + use std::os::fd::AsRawFd; + use std::os::fd::OwnedFd; + use std::os::fd::FromRawFd; - Errno::result(res) + let res = unsafe { libc::dup(oldfd.as_fd().as_raw_fd()) }; + Errno::result(res)?; + // SAFETY: + // + // `dup(2)` would return a valid owned file descriptor on success + Ok( unsafe { OwnedFd::from_raw_fd(res) }) } -/// Create a copy of the specified file descriptor using the specified fd (see -/// [dup(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)). +/// Duplicate `fd` with Stdin, i.e., Stdin redirection. +#[inline] +pub fn dup2_stdin(fd: Fd) -> Result<()> { + use std::os::fd::AsRawFd; + use libc::STDIN_FILENO; + + let res = unsafe { libc::dup2(fd.as_fd().as_raw_fd(), STDIN_FILENO) }; + Errno::result(res).map(drop) +} + +/// Duplicate `fd` with Stdout, i.e., Stdout redirection. +/// +/// # Examples +/// +/// Redirect the Stdout to file foo and restore it: +/// +/// ```no_run +/// use nix::fcntl::open; +/// use nix::fcntl::OFlag; +/// use nix::sys::stat::Mode; +/// use nix::unistd::dup; +/// use nix::unistd::dup2_stdout; +/// use std::io::{stdout, Write}; +/// +/// let mut stdout = stdout(); +/// +/// // Save the previous Stdout so that we can restore it +/// let saved_stdout = dup(&stdout).unwrap(); +/// let foo = open( +/// "foo", +/// OFlag::O_RDWR | OFlag::O_CLOEXEC | OFlag::O_CREAT | OFlag::O_EXCL, +/// Mode::S_IRWXU, +/// ) +/// .unwrap(); +/// // Now our Stdout has been redirected to file foo +/// dup2_stdout(foo).unwrap(); +/// // Let's say hi to foo +/// // NOTE: add a newline here to flush the buffer +/// stdout.write(b"Hi, foo!\n").unwrap(); +/// +/// // Restore the Stdout +/// dup2_stdout(saved_stdout).unwrap(); +/// +/// // Let's say hi to Stdout +/// // NOTE: add a newline here to flush the buffer +/// stdout.write(b"Hi, Stdout!\n").unwrap(); +/// ``` +#[inline] +pub fn dup2_stdout(fd: Fd) -> Result<()> { + use std::os::fd::AsRawFd; + use libc::STDOUT_FILENO; + + let res = unsafe { libc::dup2(fd.as_fd().as_raw_fd(), STDOUT_FILENO) }; + Errno::result(res).map(drop) +} + +/// Duplicate `fd` with Stderr, i.e., Stderr redirection. +/// +/// # Examples +/// +/// See the example of [`dup2_stdout()`](fn.dup2_stdout.html#examples) +#[inline] +pub fn dup2_stderr(fd: Fd) -> Result<()> { + use std::os::fd::AsRawFd; + use libc::STDERR_FILENO; + + let res = unsafe { libc::dup2(fd.as_fd().as_raw_fd(), STDERR_FILENO) }; + Errno::result(res).map(drop) +} + +/// Create a copy of `oldfd` using `newfd`. /// /// This function behaves similar to `dup()` except that it will try to use the -/// specified fd instead of allocating a new one. See the man pages for more -/// detail on the exact behavior of this function. +/// specified fd `newfd` instead of allocating a new one. See the man pages for +/// more detail on the exact behavior of this function. +/// +/// This function does not allow you to duplicate `oldfd` with any file descriptor +/// you want, to do that, use [`dup2_raw()`]. +/// +/// # Stdin/Stdout/Stderr redirection +/// +/// To duplicate a fd with Stdin/Stdout/Stderr, see: +/// +/// * [`dup2_stdin()`] +/// * [`dup2_stdout()`] +/// * [`dup2_stderr()`] +/// +/// # Reference +/// +/// [dup(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html) #[inline] -pub fn dup2(oldfd: RawFd, newfd: RawFd) -> Result { - let res = unsafe { libc::dup2(oldfd, newfd) }; +pub fn dup2(oldfd: Fd, newfd: &mut std::os::fd::OwnedFd) -> Result<()> { + use std::os::fd::AsRawFd; - Errno::result(res) + let res = unsafe { libc::dup2(oldfd.as_fd().as_raw_fd(), newfd.as_raw_fd()) }; + + Errno::result(res).map(drop) +} + +/// Create a copy of `oldfd` with any fd value you want. +/// +/// # Safety +/// +/// Since this function returns an `OwnedFd`, you have to ensure that the returned +/// `OwnedFd` is the ONLY owner of the file descriptor specified `newfd`. Otherwise, +/// double close could happen. +/// +/// ```no_run +/// # use nix::{ +/// # fcntl::{open, OFlag}, +/// # sys::stat::Mode, +/// # unistd::dup2_raw, +/// # }; +/// # use std::os::fd::OwnedFd; +/// # use std::os::fd::AsRawFd; +/// let oldfd: OwnedFd = open("foo", OFlag::O_RDONLY, Mode::empty()).unwrap(); +/// let newfd: OwnedFd = open("bar", OFlag::O_RDONLY, Mode::empty()).unwrap(); +/// +/// // SAFETY: +/// // it is NOT safe. +/// // NOTE that we are passing a RawFd to `newfd` +/// let duplicated_fd: OwnedFd = unsafe { dup2_raw(&oldfd, newfd.as_raw_fd()) }.unwrap(); +/// +/// // `newfd` and `duplicated_fd` refer to the same file descriptor, and +/// // they are both owned, double close will happen here. +/// ``` +/// +/// # Examples +/// +/// Duplicate a file descriptor with a descriptor that is still not open: +/// +/// ```no_run +/// # use nix::{ +/// # fcntl::{open, OFlag}, +/// # sys::stat::Mode, +/// # unistd::dup2_raw, +/// # }; +/// let oldfd = open("foo", OFlag::O_RDONLY, Mode::empty()).unwrap(); +/// +/// // SAFETY: +/// // It is safe given that we are sure that fd 100 is not open, and the returned +/// // OwnedFd will be its only owner. +/// let duplicated_fd = unsafe { dup2_raw(&oldfd, 100) }.unwrap(); +/// +/// // do something with `duplicated_fd` +/// ``` +/// +/// The code demonstrating double close can be fixed by passing `newfd` by value: +/// +/// ```no_run +/// # use nix::{ +/// # fcntl::{open, OFlag}, +/// # sys::stat::Mode, +/// # unistd::dup2_raw, +/// # }; +/// # use std::os::fd::OwnedFd; +/// let oldfd: OwnedFd = open("foo", OFlag::O_RDONLY, Mode::empty()).unwrap(); +/// let newfd: OwnedFd = open("bar", OFlag::O_RDONLY, Mode::empty()).unwrap(); +/// +/// // SAFETY: +/// // it is safe since `duplicated_fd` is the only owner of the fd it refers to. +/// // NOTE that we are passing `newfd` by value, i.e., transfer the ownership +/// let duplicated_fd: OwnedFd = unsafe { dup2_raw(&oldfd, newfd) }.unwrap(); +/// ``` +/// +/// # Reference +/// +/// * [POSIX manual](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html) +/// +/// # See also +/// +/// * [`dup2()`] +#[inline] +pub unsafe fn dup2_raw(oldfd: Fd1, newfd: Fd2) -> Result { + use std::os::fd::AsRawFd; + use std::os::fd::FromRawFd; + use std::os::fd::OwnedFd; + + let duplicated_fd = unsafe { + libc::dup2(oldfd.as_fd().as_raw_fd(), newfd.into_raw_fd()) + }; + // SAFETY: + // + // This is unsafe if `newfd` is not a file descriptor that can be consumed + Ok(unsafe { + OwnedFd::from_raw_fd(duplicated_fd) + }) } /// Create a new copy of the specified file descriptor using the specified fd -/// and flags (see [`dup(2)`](https://man7.org/linux/man-pages/man2/dup.2.html)). +/// and flags. +/// +/// This function behaves similar to [`dup2()`] but allows flags to be specified +/// for the new file descriptor. Currently, the only flag that is allowed is +/// [`OFlag::O_CLOEXEC`], setting other flags will return `EINVAL`. Also, if +/// `oldfd` and `newfd` have the same fd value, `EINVAL` will also be returned. +/// +/// This function does not allow you to duplicate `oldfd` with any file descriptor +/// you want, to do that, use [`dup3_raw()`]. /// -/// This function behaves similar to `dup2()` but allows for flags to be -/// specified. +/// # References +/// +/// * [FreeBSD](https://man.freebsd.org/cgi/man.cgi?query=dup3&sektion=3) +/// * [Linux](https://man7.org/linux/man-pages/man2/dup.2.html) +/// * [NetBSD](https://man.netbsd.org/dup3.2) +/// * [OpenBSD](https://man.openbsd.org/dup3.2) #[cfg(any( netbsdlike, solarish, @@ -455,10 +654,59 @@ pub fn dup2(oldfd: RawFd, newfd: RawFd) -> Result { target_os = "hurd", target_os = "linux" ))] -pub fn dup3(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result { - let res = unsafe { libc::dup3(oldfd, newfd, flags.bits()) }; +pub fn dup3(oldfd: Fd, newfd: &mut std::os::fd::OwnedFd, flags: OFlag) -> Result<()> { + use std::os::fd::AsRawFd; - Errno::result(res) + let res = unsafe { libc::dup3(oldfd.as_fd().as_raw_fd(), newfd.as_raw_fd(), flags.bits()) }; + Errno::result(res).map(drop) +} + +/// Create a new copy of the specified file descriptor using the specified fd +/// and flags. +/// +/// This function behaves similar to [`dup3()`] except for it allows you to specify +/// arbitrary fd values. +/// +/// # Safety +/// +/// Since this function returns an `OwnedFd`, you have to ensure that the returned +/// `OwnedFd` is the ONLY owner of the file descriptor specified `newfd`. Otherwise, +/// double close could happen. +/// +/// For more information, see the documentation of [`dup2_raw()`]. +/// +/// # References +/// +/// * [FreeBSD](https://man.freebsd.org/cgi/man.cgi?query=dup3&sektion=3) +/// * [Linux](https://man7.org/linux/man-pages/man2/dup.2.html) +/// * [NetBSD](https://man.netbsd.org/dup3.2) +/// * [OpenBSD](https://man.openbsd.org/dup3.2) +/// +/// # See also +/// +/// * [`dup3()`] +#[cfg(any( + netbsdlike, + solarish, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux" +))] +pub unsafe fn dup3_raw(oldfd: Fd1, newfd: Fd2, flags: OFlag) -> Result { + use std::os::fd::AsRawFd; + use std::os::fd::OwnedFd; + use std::os::fd::FromRawFd; + + let res = unsafe { libc::dup3(oldfd.as_fd().as_raw_fd(), newfd.into_raw_fd(), flags.bits()) }; + Errno::result(res)?; + + // SAFETY: + // + // This is unsafe if `newfd` is not a file descriptor that can be consumed + Ok(unsafe { + OwnedFd::from_raw_fd(res) + }) } /// Change the current working directory of the calling process (see @@ -482,8 +730,10 @@ pub fn chdir(path: &P) -> Result<()> { /// pages for additional details on possible failure cases. #[inline] #[cfg(not(target_os = "fuchsia"))] -pub fn fchdir(dirfd: RawFd) -> Result<()> { - let res = unsafe { libc::fchdir(dirfd) }; +pub fn fchdir(dirfd: Fd) -> Result<()> { + use std::os::fd::AsRawFd; + + let res = unsafe { libc::fchdir(dirfd.as_fd().as_raw_fd()) }; Errno::result(res).map(drop) } @@ -515,9 +765,9 @@ pub fn fchdir(dirfd: RawFd) -> Result<()> { /// } /// ``` #[inline] -pub fn mkdir(path: &P, mode: Mode) -> Result<()> { +pub fn mkdir(path: &P, mode: crate::sys::stat::Mode) -> Result<()> { let res = path.with_nix_path(|cstr| unsafe { - libc::mkdir(cstr.as_ptr(), mode.bits() as mode_t) + libc::mkdir(cstr.as_ptr(), mode.bits() as libc::mode_t) })?; Errno::result(res).map(drop) @@ -554,9 +804,9 @@ pub fn mkdir(path: &P, mode: Mode) -> Result<()> { /// ``` #[inline] #[cfg(not(target_os = "redox"))] // RedoxFS does not support fifo yet -pub fn mkfifo(path: &P, mode: Mode) -> Result<()> { +pub fn mkfifo(path: &P, mode: crate::sys::stat::Mode) -> Result<()> { let res = path.with_nix_path(|cstr| unsafe { - libc::mkfifo(cstr.as_ptr(), mode.bits() as mode_t) + libc::mkfifo(cstr.as_ptr(), mode.bits() as libc::mode_t) })?; Errno::result(res).map(drop) @@ -564,9 +814,17 @@ pub fn mkfifo(path: &P, mode: Mode) -> Result<()> { /// Creates new fifo special file (named pipe) with path `path` and access rights `mode`. /// -/// If `dirfd` has a value, then `path` is relative to directory associated with the file descriptor. +/// # Examples +/// +/// Create a FIFO in the current working directory: +/// +/// ```no_run +/// use nix::fcntl::AT_FDCWD; +/// use nix::unistd::mkfifoat; +/// use nix::sys::stat::Mode; /// -/// If `dirfd` is `None`, then `path` is relative to the current working directory. +/// mkfifoat(AT_FDCWD, "fifo", Mode::S_IRWXU).unwrap(); +/// ``` /// /// # References /// @@ -579,13 +837,15 @@ pub fn mkfifo(path: &P, mode: Mode) -> Result<()> { target_os = "android", target_os = "redox" )))] -pub fn mkfifoat( - dirfd: Option, +pub fn mkfifoat( + dirfd: Fd, path: &P, - mode: Mode, + mode: crate::sys::stat::Mode, ) -> Result<()> { + use std::os::fd::AsRawFd; + let res = path.with_nix_path(|cstr| unsafe { - libc::mkfifoat(at_rawfd(dirfd), cstr.as_ptr(), mode.bits() as mode_t) + libc::mkfifoat(dirfd.as_fd().as_raw_fd(), cstr.as_ptr(), mode.bits() as libc::mode_t) })?; Errno::result(res).map(drop) @@ -593,24 +853,34 @@ pub fn mkfifoat( /// Creates a symbolic link at `path2` which points to `path1`. /// -/// If `dirfd` has a value, then `path2` is relative to directory associated -/// with the file descriptor. +/// # Examples /// -/// If `dirfd` is `None`, then `path2` is relative to the current working -/// directory. This is identical to `libc::symlink(path1, path2)`. +/// Assume file foo exists in the current working directory, create a symlink +/// to it: /// -/// See also [symlinkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlinkat.html). +/// ```no_run +/// use nix::fcntl::AT_FDCWD; +/// use nix::unistd::symlinkat; +/// +/// symlinkat("foo", AT_FDCWD, "link_to_foo").unwrap(); +/// ``` +/// +/// # References +/// +/// [POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlinkat.html) #[cfg(not(target_os = "redox"))] -pub fn symlinkat( +pub fn symlinkat( path1: &P1, - dirfd: Option, + dirfd: Fd, path2: &P2, ) -> Result<()> { + use std::os::fd::AsRawFd; + let res = path1.with_nix_path(|path1| { path2.with_nix_path(|path2| unsafe { libc::symlinkat( path1.as_ptr(), - dirfd.unwrap_or(libc::AT_FDCWD), + dirfd.as_fd().as_raw_fd(), path2.as_ptr(), ) }) @@ -738,9 +1008,11 @@ pub fn chown( /// provided for that argument. Ownership change will be attempted for the path /// only if `Some` owner/group is provided. #[inline] -pub fn fchown(fd: RawFd, owner: Option, group: Option) -> Result<()> { +pub fn fchown(fd: Fd, owner: Option, group: Option) -> Result<()> { + use std::os::fd::AsRawFd; + let (uid, gid) = chown_raw_ids(owner, group); - let res = unsafe { libc::fchown(fd, uid, gid) }; + let res = unsafe { libc::fchown(fd.as_fd().as_raw_fd(), uid, gid) }; Errno::result(res).map(drop) } @@ -766,14 +1038,10 @@ impl FchownatFlags { /// provided for that argument. Ownership change will be attempted for the path /// only if `Some` owner/group is provided. /// -/// The file to be changed is determined relative to the directory associated -/// with the file descriptor `dirfd` or the current working directory -/// if `dirfd` is `None`. -/// /// If `flag` is `AtFlags::AT_SYMLINK_NOFOLLOW` and `path` names a symbolic link, /// then the mode of the symbolic link is changed. /// -/// `fchownat(None, path, owner, group, AtFlags::AT_SYMLINK_NOFOLLOW)` is identical to +/// `fchownat(AT_FDCWD, path, owner, group, AtFlags::AT_SYMLINK_NOFOLLOW)` is identical to /// a call `libc::lchown(path, owner, group)`. That's why `lchown` is unimplemented in /// the `nix` crate. /// @@ -781,17 +1049,19 @@ impl FchownatFlags { /// /// [fchownat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchownat.html). #[cfg(not(target_os = "redox"))] -pub fn fchownat( - dirfd: Option, +pub fn fchownat( + dirfd: Fd, path: &P, owner: Option, group: Option, flag: AtFlags, ) -> Result<()> { + use std::os::fd::AsRawFd; + let res = path.with_nix_path(|cstr| unsafe { let (uid, gid) = chown_raw_ids(owner, group); libc::fchownat( - at_rawfd(dirfd), + dirfd.as_fd().as_raw_fd(), cstr.as_ptr(), uid, gid, @@ -910,15 +1180,17 @@ pub fn execvpe, SE: AsRef>( /// is referenced as a file descriptor instead of a path. #[cfg(any(linux_android, freebsdlike, target_os = "hurd"))] #[inline] -pub fn fexecve, SE: AsRef>( - fd: RawFd, +pub fn fexecve, SE: AsRef>( + fd: Fd, args: &[SA], env: &[SE], ) -> Result { + use std::os::fd::AsRawFd; + let args_p = to_exec_array(args); let env_p = to_exec_array(env); - unsafe { libc::fexecve(fd, args_p.as_ptr(), env_p.as_ptr()) }; + unsafe { libc::fexecve(fd.as_fd().as_raw_fd(), args_p.as_ptr(), env_p.as_ptr()) }; Err(Errno::last()) } @@ -935,21 +1207,22 @@ pub fn fexecve, SE: AsRef>( /// is referenced as a file descriptor to the base directory plus a path. #[cfg(linux_android)] #[inline] -pub fn execveat, SE: AsRef>( - dirfd: Option, +pub fn execveat, SE: AsRef>( + dirfd: Fd, pathname: &CStr, args: &[SA], env: &[SE], flags: super::fcntl::AtFlags, ) -> Result { - let dirfd = at_rawfd(dirfd); + use std::os::fd::AsRawFd; + let args_p = to_exec_array(args); let env_p = to_exec_array(env); unsafe { libc::syscall( libc::SYS_execveat, - dirfd, + dirfd.as_fd().as_raw_fd(), pathname.as_ptr(), args_p.as_ptr(), env_p.as_ptr(), @@ -1059,42 +1332,57 @@ pub fn gethostname() -> Result { } } -/// Close a raw file descriptor +/// Close a file descriptor. /// -/// Be aware that many Rust types implicitly close-on-drop, including -/// `std::fs::File`. Explicitly closing them with this method too can result in -/// a double-close condition, which can cause confusing `EBADF` errors in -/// seemingly unrelated code. Caveat programmer. See also -/// [close(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html). +/// # Safety /// -/// # Examples +/// If you pass a `RawFd` to this function, ensure that this `close()` won't +/// trigger a double close. /// /// ```no_run /// use std::os::unix::io::AsRawFd; /// use nix::unistd::close; /// /// let f = tempfile::tempfile().unwrap(); -/// close(f.as_raw_fd()).unwrap(); // Bad! f will also close on drop! +/// // SAFETY: +/// // +/// // NOT safe! f will also close on drop! +/// unsafe { close(f.as_raw_fd()).unwrap() }; /// ``` /// +/// We should pass `f` by value: +/// +/// In the following case, it is generally preferred to call `drop(f)` rather +/// than `close()`. +/// /// ```rust /// use std::os::unix::io::IntoRawFd; /// use nix::unistd::close; /// /// let f = tempfile::tempfile().unwrap(); -/// close(f.into_raw_fd()).unwrap(); // Good. into_raw_fd consumes f +/// // SAFETY: +/// // +/// // We are safe! `into_raw_fd()` consumes f +/// unsafe { close(f).unwrap() }; /// ``` -pub fn close(fd: RawFd) -> Result<()> { - let res = unsafe { libc::close(fd) }; +pub unsafe fn close(fd: Fd) -> Result<()> { + let res = unsafe { libc::close(fd.into_raw_fd()) }; Errno::result(res).map(drop) } /// Read from a raw file descriptor. /// /// See also [read(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html) -pub fn read(fd: RawFd, buf: &mut [u8]) -> Result { - let res = - unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len() as size_t) }; +pub fn read(fd: Fd, buf: &mut [u8]) -> Result { + use std::os::fd::AsRawFd; + + let res = unsafe { + libc::read( + fd.as_fd().as_raw_fd(), + buf.as_mut_ptr().cast(), + buf.len() as size_t, + ) + }; Errno::result(res).map(|r| r as usize) } @@ -1102,7 +1390,9 @@ pub fn read(fd: RawFd, buf: &mut [u8]) -> Result { /// Write to a raw file descriptor. /// /// See also [write(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html) -pub fn write(fd: Fd, buf: &[u8]) -> Result { +pub fn write(fd: Fd, buf: &[u8]) -> Result { + use std::os::fd::AsRawFd; + let res = unsafe { libc::write( fd.as_fd().as_raw_fd(), @@ -1155,8 +1445,10 @@ pub enum Whence { /// Move the read/write file offset. /// /// See also [lseek(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lseek.html) -pub fn lseek(fd: RawFd, offset: off_t, whence: Whence) -> Result { - let res = unsafe { libc::lseek(fd, offset, whence as i32) }; +pub fn lseek(fd: Fd, offset: off_t, whence: Whence) -> Result { + use std::os::fd::AsRawFd; + + let res = unsafe { libc::lseek(fd.as_fd().as_raw_fd(), offset, whence as i32) }; Errno::result(res).map(|r| r as off_t) } @@ -1166,12 +1458,14 @@ pub fn lseek(fd: RawFd, offset: off_t, whence: Whence) -> Result { /// Unlike [`lseek`], it takes a 64-bit argument even on platforms where [`libc::off_t`] is /// 32 bits. #[cfg(linux_android)] -pub fn lseek64( - fd: RawFd, +pub fn lseek64( + fd: Fd, offset: libc::off64_t, whence: Whence, ) -> Result { - let res = unsafe { libc::lseek64(fd, offset, whence as i32) }; + use std::os::fd::AsRawFd; + + let res = unsafe { libc::lseek64(fd.as_fd().as_raw_fd(), offset, whence as i32) }; Errno::result(res).map(|r| r as libc::off64_t) } @@ -1180,8 +1474,9 @@ pub fn lseek64( /// Create an interprocess channel. /// /// See also [pipe(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pipe.html) -pub fn pipe() -> std::result::Result<(OwnedFd, OwnedFd), Error> { - let mut fds = mem::MaybeUninit::<[OwnedFd; 2]>::uninit(); +pub fn pipe( +) -> std::result::Result<(std::os::fd::OwnedFd, std::os::fd::OwnedFd), Error> { + let mut fds = mem::MaybeUninit::<[std::os::fd::OwnedFd; 2]>::uninit(); let res = unsafe { libc::pipe(fds.as_mut_ptr().cast()) }; @@ -1219,8 +1514,8 @@ feature! { target_os = "redox", netbsdlike, ))] -pub fn pipe2(flags: OFlag) -> Result<(OwnedFd, OwnedFd)> { - let mut fds = mem::MaybeUninit::<[OwnedFd; 2]>::uninit(); +pub fn pipe2(flags: OFlag) -> Result<(std::os::fd::OwnedFd, std::os::fd::OwnedFd)> { + let mut fds = mem::MaybeUninit::<[std::os::fd::OwnedFd; 2]>::uninit(); let res = unsafe { libc::pipe2(fds.as_mut_ptr().cast(), flags.bits()) }; @@ -1247,16 +1542,20 @@ pub fn truncate(path: &P, len: off_t) -> Result<()> { /// /// See also /// [ftruncate(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html) -pub fn ftruncate(fd: Fd, len: off_t) -> Result<()> { +pub fn ftruncate(fd: Fd, len: off_t) -> Result<()> { + use std::os::fd::AsRawFd; + Errno::result(unsafe { libc::ftruncate(fd.as_fd().as_raw_fd(), len) }).map(drop) } /// Determines if the file descriptor refers to a valid terminal type device. -pub fn isatty(fd: RawFd) -> Result { +pub fn isatty(fd: Fd) -> Result { + use std::os::fd::AsRawFd; + unsafe { // ENOTTY means `fd` is a valid file descriptor, but not a TTY, so // we return `Ok(false)` - if libc::isatty(fd) == 1 { + if libc::isatty(fd.as_fd().as_raw_fd()) == 1 { Ok(true) } else { match Errno::last() { @@ -1283,31 +1582,34 @@ impl LinkatFlags { /// Link one file to another file /// -/// Creates a new link (directory entry) at `newpath` for the existing file at `oldpath`. In the -/// case of a relative `oldpath`, the path is interpreted relative to the directory associated -/// with file descriptor `olddirfd` instead of the current working directory and similiarly for -/// `newpath` and file descriptor `newdirfd`. In case `flag` is `AtFlags::AT_SYMLINK_FOLLOW` and -/// `oldpath` names a symoblic link, a new link for the target of the symbolic link is created. -/// If either `olddirfd` or `newdirfd` is `None`, `AT_FDCWD` is used respectively where `oldpath` -/// and/or `newpath` is then interpreted relative to the current working directory of the calling -/// process. If either `oldpath` or `newpath` is absolute, then `dirfd` is ignored. +/// Creates a new hard link (directory entry) at `newpath` for the existing file +/// at `oldpath`. In the case of a relative `oldpath`, the path is interpreted +/// relative to the directory associated with file descriptor `olddirfd` instead +/// of the current working directory and similarly for `newpath` and file +/// descriptor `newdirfd`. If either `oldpath` or `newpath` is absolute, then +/// `dirfd` is ignored. +/// +/// In case `flag` is `AtFlags::AT_SYMLINK_FOLLOW` and `oldpath` names a symoblic +/// link, a new link for the target of the symbolic link is created. /// /// # References /// See also [linkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/linkat.html) -#[cfg(not(target_os = "redox"))] // RedoxFS does not support symlinks yet -pub fn linkat( - olddirfd: Option, +#[cfg(not(target_os = "redox"))] // Redox does not have this yet +pub fn linkat( + olddirfd: Fd1, oldpath: &P, - newdirfd: Option, + newdirfd: Fd2, newpath: &P, flag: AtFlags, ) -> Result<()> { + use std::os::fd::AsRawFd; + let res = oldpath.with_nix_path(|oldcstr| { newpath.with_nix_path(|newcstr| unsafe { libc::linkat( - at_rawfd(olddirfd), + olddirfd.as_fd().as_raw_fd(), oldcstr.as_ptr(), - at_rawfd(newdirfd), + newdirfd.as_fd().as_raw_fd(), newcstr.as_ptr(), flag.bits(), ) @@ -1336,27 +1638,29 @@ pub enum UnlinkatFlags { /// Remove a directory entry /// -/// In the case of a relative path, the directory entry to be removed is determined relative to -/// the directory associated with the file descriptor `dirfd` or the current working directory -/// if `dirfd` is `None`. In the case of an absolute `path` `dirfd` is ignored. If `flag` is -/// `UnlinkatFlags::RemoveDir` then removal of the directory entry specified by `dirfd` and `path` -/// is performed. +/// In the case of a relative path, the directory entry to be removed is determined +/// relative to the directory associated with the file descriptor `dirfd`. In the +/// case of an absolute `path` `dirfd` is ignored. If `flag` is +/// `UnlinkatFlags::RemoveDir` then removal of the directory entry specified by +/// `dirfd` and `path` is performed. /// /// # References /// See also [unlinkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlinkat.html) #[cfg(not(target_os = "redox"))] -pub fn unlinkat( - dirfd: Option, +pub fn unlinkat( + dirfd: Fd, path: &P, flag: UnlinkatFlags, ) -> Result<()> { + use std::os::fd::AsRawFd; + let atflag = match flag { UnlinkatFlags::RemoveDir => AtFlags::AT_REMOVEDIR, UnlinkatFlags::NoRemoveDir => AtFlags::empty(), }; let res = path.with_nix_path(|cstr| unsafe { libc::unlinkat( - at_rawfd(dirfd), + dirfd.as_fd().as_raw_fd(), cstr.as_ptr(), atflag.bits() as libc::c_int, ) @@ -1387,8 +1691,10 @@ pub fn sync() { /// /// See also [syncfs(2)](https://man7.org/linux/man-pages/man2/sync.2.html) #[cfg(any(linux_android, target_os = "hurd"))] -pub fn syncfs(fd: RawFd) -> Result<()> { - let res = unsafe { libc::syncfs(fd) }; +pub fn syncfs(fd: Fd) -> Result<()> { + use std::os::fd::AsRawFd; + + let res = unsafe { libc::syncfs(fd.as_fd().as_raw_fd()) }; Errno::result(res).map(drop) } @@ -1397,8 +1703,10 @@ pub fn syncfs(fd: RawFd) -> Result<()> { /// /// See also [fsync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html) #[inline] -pub fn fsync(fd: RawFd) -> Result<()> { - let res = unsafe { libc::fsync(fd) }; +pub fn fsync(fd: Fd) -> Result<()> { + use std::os::fd::AsRawFd; + + let res = unsafe { libc::fsync(fd.as_fd().as_raw_fd()) }; Errno::result(res).map(drop) } @@ -1419,7 +1727,9 @@ pub fn fsync(fd: RawFd) -> Result<()> { target_os = "hurd", ))] #[inline] -pub fn fdatasync(fd: RawFd) -> Result<()> { +pub fn fdatasync(fd: Fd) -> Result<()> { + use std::os::fd::AsRawFd; + cfg_if! { // apple libc supports fdatasync too, albeit not being present in its headers // [fdatasync](https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/vfs/vfs_syscalls.c#L7728) @@ -1431,7 +1741,7 @@ pub fn fdatasync(fd: RawFd) -> Result<()> { use libc::fdatasync as fdatasync; } } - let res = unsafe { fdatasync(fd) }; + let res = unsafe { fdatasync(fd.as_fd().as_raw_fd()) }; Errno::result(res).map(drop) } @@ -1960,7 +2270,10 @@ feature! { /// // do something with fd /// ``` #[inline] -pub fn mkstemp(template: &P) -> Result<(RawFd, PathBuf)> { +pub fn mkstemp(template: &P) -> Result<(std::os::fd::OwnedFd, PathBuf)> { + use std::os::fd::OwnedFd; + use std::os::fd::FromRawFd; + let mut path = template.with_nix_path(|path| path.to_bytes_with_nul().to_owned())?; let p = path.as_mut_ptr().cast(); @@ -1969,6 +2282,10 @@ pub fn mkstemp(template: &P) -> Result<(RawFd, PathBuf)> { debug_assert!(last == Some(b'\0')); let pathname = OsString::from_vec(path); Errno::result(fd)?; + // SAFETY: + // + // `mkstemp(3)` should return a valid owned file descriptor on success. + let fd = unsafe { OwnedFd::from_raw_fd(fd) }; Ok((fd, PathBuf::from(pathname))) } } @@ -2189,7 +2506,9 @@ pub enum PathconfVar { /// - `Ok(None)`: the variable has no limit (for limit variables) or is /// unsupported (for option variables) /// - `Err(x)`: an error occurred -pub fn fpathconf(fd: F, var: PathconfVar) -> Result> { +pub fn fpathconf(fd: F, var: PathconfVar) -> Result> { + use std::os::fd::AsRawFd; + let raw = unsafe { Errno::clear(); libc::fpathconf(fd.as_fd().as_raw_fd(), var as c_int) @@ -3108,26 +3427,25 @@ pub fn access(path: &P, amode: AccessFlags) -> Result<()> { Errno::result(res).map(drop) } -/// Checks the file named by `path` for accessibility according to the flags given by `mode` -/// -/// If `dirfd` has a value, then `path` is relative to directory associated with the file descriptor. -/// -/// If `dirfd` is `None`, then `path` is relative to the current working directory. +/// Checks the file named by `dirfd` and `path` for accessibility according to +/// the flags given by `mode` /// /// # References /// /// [faccessat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/faccessat.html) // redox: does not appear to support the *at family of syscalls. #[cfg(not(target_os = "redox"))] -pub fn faccessat( - dirfd: Option, +pub fn faccessat( + dirfd: Fd, path: &P, mode: AccessFlags, flags: AtFlags, ) -> Result<()> { + use std::os::fd::AsRawFd; + let res = path.with_nix_path(|cstr| unsafe { libc::faccessat( - at_rawfd(dirfd), + dirfd.as_fd().as_raw_fd(), cstr.as_ptr(), mode.bits(), flags.bits(), @@ -3624,7 +3942,9 @@ feature! { /// Get the name of the terminal device that is open on file descriptor fd /// (see [`ttyname(3)`](https://man7.org/linux/man-pages/man3/ttyname.3.html)). #[cfg(not(target_os = "fuchsia"))] -pub fn ttyname(fd: F) -> Result { +pub fn ttyname(fd: F) -> Result { + use std::os::fd::AsRawFd; + #[cfg(not(target_os = "hurd"))] const PATH_MAX: usize = libc::PATH_MAX as usize; #[cfg(target_os = "hurd")] @@ -3650,7 +3970,9 @@ feature! { /// /// See also [getpeereid(3)](https://www.freebsd.org/cgi/man.cgi?query=getpeereid) #[cfg(bsd)] -pub fn getpeereid(fd: F) -> Result<(Uid, Gid)> { +pub fn getpeereid(fd: F) -> Result<(Uid, Gid)> { + use std::os::fd::AsRawFd; + let mut uid = 1; let mut gid = 1; diff --git a/test/sys/test_memfd.rs b/test/sys/test_memfd.rs index 1ca81ef8c3..a062e69a48 100644 --- a/test/sys/test_memfd.rs +++ b/test/sys/test_memfd.rs @@ -5,18 +5,17 @@ fn test_memfd_create() { use nix::unistd::lseek; use nix::unistd::read; use nix::unistd::{write, Whence}; - use std::os::fd::{AsFd, AsRawFd}; let fd = memfd_create("test_memfd_create_name", MemFdCreateFlag::MFD_CLOEXEC) .unwrap(); let contents = b"hello"; - assert_eq!(write(fd.as_fd(), contents).unwrap(), 5); + assert_eq!(write(&fd, contents).unwrap(), 5); - lseek(fd.as_raw_fd(), 0, Whence::SeekSet).unwrap(); + lseek(&fd, 0, Whence::SeekSet).unwrap(); let mut buf = vec![0_u8; contents.len()]; - assert_eq!(read(fd.as_raw_fd(), &mut buf).unwrap(), 5); + assert_eq!(read(&fd, &mut buf).unwrap(), 5); assert_eq!(contents, buf.as_slice()); } diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index 3bb882d10c..44a32aaf5e 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -321,7 +321,7 @@ pub fn test_socketpair() { .unwrap(); write(&fd1, b"hello").unwrap(); let mut buf = [0; 5]; - read(fd2.as_raw_fd(), &mut buf).unwrap(); + read(&fd2, &mut buf).unwrap(); assert_eq!(&buf[..], b"hello"); } @@ -908,9 +908,15 @@ pub fn test_scm_rights() { // Ensure that the received file descriptor works write(&w, b"world").unwrap(); let mut buf = [0u8; 5]; - read(received_r.as_raw_fd(), &mut buf).unwrap(); + // SAFETY: + // should be safe since we don't use it after close + let borrowed_received_r = + unsafe { std::os::fd::BorrowedFd::borrow_raw(received_r) }; + read(borrowed_received_r, &mut buf).unwrap(); assert_eq!(&buf[..], b"world"); - close(received_r).unwrap(); + // SAFETY: + // there shouldn't be double close + unsafe { close(received_r).unwrap() }; } // Disable the test on emulated platforms due to not enabled support of AF_ALG in QEMU from rust cross @@ -975,8 +981,12 @@ pub fn test_af_alg_cipher() { // allocate buffer for encrypted data let mut encrypted = vec![0u8; payload_len]; + // SAFETY: + // should be safe since session_socket won't be closed before the use of this borrowed one + let borrowed_session_socket = + unsafe { std::os::fd::BorrowedFd::borrow_raw(session_socket) }; let num_bytes = - read(session_socket.as_raw_fd(), &mut encrypted).expect("read encrypt"); + read(borrowed_session_socket, &mut encrypted).expect("read encrypt"); assert_eq!(num_bytes, payload_len); let iov = IoSlice::new(&encrypted); @@ -998,8 +1008,12 @@ pub fn test_af_alg_cipher() { // allocate buffer for decrypted data let mut decrypted = vec![0u8; payload_len]; + // SAFETY: + // should be safe since session_socket won't be closed before the use of this borrowed one + let borrowed_session_socket = + unsafe { std::os::fd::BorrowedFd::borrow_raw(session_socket) }; let num_bytes = - read(session_socket.as_raw_fd(), &mut decrypted).expect("read decrypt"); + read(borrowed_session_socket, &mut decrypted).expect("read decrypt"); assert_eq!(num_bytes, payload_len); assert_eq!(decrypted, payload); @@ -1087,8 +1101,12 @@ pub fn test_af_alg_aead() { // allocate buffer for encrypted data let mut encrypted = vec![0u8; (assoc_size as usize) + payload_len + auth_size]; + // SAFETY: + // should be safe since session_socket won't be closed before the use of this borrowed one + let borrowed_session_socket = + unsafe { std::os::fd::BorrowedFd::borrow_raw(session_socket) }; let num_bytes = - read(session_socket.as_raw_fd(), &mut encrypted).expect("read encrypt"); + read(borrowed_session_socket, &mut encrypted).expect("read encrypt"); assert_eq!(num_bytes, payload_len + auth_size + (assoc_size as usize)); for i in 0..assoc_size { @@ -1131,8 +1149,7 @@ pub fn test_af_alg_aead() { unsafe { std::os::fd::BorrowedFd::borrow_raw(session_socket) }; fcntl(borrowed_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)) .expect("fcntl non_blocking"); - let num_bytes = - read(session_socket.as_raw_fd(), &mut decrypted).expect("read decrypt"); + let num_bytes = read(borrowed_fd, &mut decrypted).expect("read decrypt"); assert!(num_bytes >= payload_len + (assoc_size as usize)); assert_eq!( @@ -1622,9 +1639,15 @@ fn test_impl_scm_credentials_and_rights( // Ensure that the received file descriptor works write(&w, b"world").unwrap(); let mut buf = [0u8; 5]; - read(received_r.as_raw_fd(), &mut buf).unwrap(); + // SAFETY: + // It should be safe if we don't use this BorrowedFd after close. + let received_r_borrowed = + unsafe { std::os::fd::BorrowedFd::borrow_raw(received_r) }; + read(received_r_borrowed, &mut buf).unwrap(); assert_eq!(&buf[..], b"world"); - close(received_r).unwrap(); + // SAFETY: + // double-close won't happen + unsafe { close(received_r).unwrap() }; Ok(()) } @@ -1666,8 +1689,11 @@ pub fn test_named_unixdomain() { let s3 = accept(s1.as_raw_fd()).expect("accept failed"); + // SAFETY: + // It should be safe considering that s3 will be open within this test + let s3 = unsafe { std::os::fd::BorrowedFd::borrow_raw(s3) }; let mut buf = [0; 5]; - read(s3.as_raw_fd(), &mut buf).unwrap(); + read(s3, &mut buf).unwrap(); thr.join().unwrap(); assert_eq!(&buf[..], b"hello"); diff --git a/test/sys/test_termios.rs b/test/sys/test_termios.rs index cbcb1b0f72..9f71bd863f 100644 --- a/test/sys/test_termios.rs +++ b/test/sys/test_termios.rs @@ -1,4 +1,4 @@ -use std::os::unix::io::{AsFd, AsRawFd}; +use std::os::unix::io::AsFd; use tempfile::tempfile; use nix::errno::Errno; @@ -100,7 +100,7 @@ fn test_local_flags() { let pty = openpty(None, &termios).unwrap(); // Set the master is in nonblocking mode or reading will never return. - let flags = fcntl::fcntl(pty.master.as_fd(), fcntl::F_GETFL).unwrap(); + let flags = fcntl::fcntl(&pty.master, fcntl::F_GETFL).unwrap(); let new_flags = fcntl::OFlag::from_bits_truncate(flags) | fcntl::OFlag::O_NONBLOCK; fcntl::fcntl(pty.master.as_fd(), fcntl::F_SETFL(new_flags)).unwrap(); @@ -111,6 +111,6 @@ fn test_local_flags() { // Try to read from the master, which should not have anything as echoing was disabled. let mut buf = [0u8; 10]; - let read = read(pty.master.as_raw_fd(), &mut buf).unwrap_err(); + let read = read(&pty.master, &mut buf).unwrap_err(); assert_eq!(read, Errno::EAGAIN); } diff --git a/test/sys/test_uio.rs b/test/sys/test_uio.rs index d035a7bb04..f7d648403e 100644 --- a/test/sys/test_uio.rs +++ b/test/sys/test_uio.rs @@ -4,7 +4,6 @@ use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use std::fs::OpenOptions; use std::io::IoSlice; -use std::os::unix::io::AsRawFd; use std::{cmp, iter}; #[cfg(not(target_os = "redox"))] @@ -49,7 +48,7 @@ fn test_writev() { let written = write_res.expect("couldn't write"); // Check whether we written all data assert_eq!(to_write.len(), written); - let read_res = read(reader.as_raw_fd(), &mut read_buf[..]); + let read_res = read(&reader, &mut read_buf[..]); let read = read_res.expect("couldn't read"); // Check we have read as much as we written assert_eq!(read, written); @@ -228,7 +227,6 @@ fn test_process_vm_readv() { use nix::sys::signal::*; use nix::sys::wait::*; use nix::unistd::ForkResult::*; - use std::os::unix::io::AsRawFd; require_capability!("test_process_vm_readv", CAP_SYS_PTRACE); let _m = crate::FORK_MTX.lock(); @@ -242,7 +240,7 @@ fn test_process_vm_readv() { Parent { child } => { drop(w); // wait for child - read(r.as_raw_fd(), &mut [0u8]).unwrap(); + read(&r, &mut [0u8]).unwrap(); drop(r); let ptr = vector.as_ptr() as usize; diff --git a/test/test.rs b/test/test.rs index fc4c6a67ff..3427b2c3db 100644 --- a/test/test.rs +++ b/test/test.rs @@ -38,7 +38,7 @@ mod test_unistd; use nix::unistd::{chdir, getcwd, read}; use parking_lot::{Mutex, RwLock, RwLockWriteGuard}; -use std::os::unix::io::{AsFd, AsRawFd}; +use std::os::unix::io::AsFd; use std::path::PathBuf; /// Helper function analogous to `std::io::Read::read_exact`, but for `Fd`s @@ -47,7 +47,7 @@ fn read_exact(f: Fd, buf: &mut [u8]) { while len < buf.len() { // get_mut would be better than split_at_mut, but it requires nightly let (_, remaining) = buf.split_at_mut(len); - len += read(f.as_fd().as_raw_fd(), remaining).unwrap(); + len += read(&f, remaining).unwrap(); } } diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs index efc38efbd9..d49f49670f 100644 --- a/test/test_fcntl.rs +++ b/test/test_fcntl.rs @@ -37,8 +37,6 @@ use tempfile::NamedTempFile; // https://gitlab.com/qemu-project/qemu/-/issues/829 #[cfg_attr(qemu, ignore)] fn test_openat() { - use std::os::fd::AsRawFd; - const CONTENTS: &[u8] = b"abcd"; let mut tmp = NamedTempFile::new().unwrap(); tmp.write_all(CONTENTS).unwrap(); @@ -55,7 +53,7 @@ fn test_openat() { .unwrap(); let mut buf = [0u8; 1024]; - assert_eq!(4, read(fd.as_raw_fd(), &mut buf).unwrap()); + assert_eq!(4, read(&fd, &mut buf).unwrap()); assert_eq!(CONTENTS, &buf[0..4]); } @@ -65,8 +63,6 @@ fn test_openat() { // https://gitlab.com/qemu-project/qemu/-/issues/829 #[cfg_attr(qemu, ignore)] fn test_openat2() { - use std::os::fd::AsRawFd; - const CONTENTS: &[u8] = b"abcd"; let mut tmp = NamedTempFile::new().unwrap(); tmp.write_all(CONTENTS).unwrap(); @@ -86,7 +82,7 @@ fn test_openat2() { .unwrap(); let mut buf = [0u8; 1024]; - assert_eq!(4, read(fd.as_raw_fd(), &mut buf).unwrap()); + assert_eq!(4, read(&fd, &mut buf).unwrap()); assert_eq!(CONTENTS, &buf[0..4]); } @@ -316,8 +312,6 @@ mod linux_android { #[test] fn test_splice() { - use std::os::fd::AsRawFd; - const CONTENTS: &[u8] = b"abcdef123456"; let mut tmp = tempfile().unwrap(); tmp.write_all(CONTENTS).unwrap(); @@ -331,15 +325,13 @@ mod linux_android { assert_eq!(2, res); let mut buf = [0u8; 1024]; - assert_eq!(2, read(rd.as_raw_fd(), &mut buf).unwrap()); + assert_eq!(2, read(&rd, &mut buf).unwrap()); assert_eq!(b"f1", &buf[0..2]); assert_eq!(7, offset); } #[test] fn test_tee() { - use std::os::fd::AsRawFd; - let (rd1, wr1) = pipe().unwrap(); let (rd2, wr2) = pipe().unwrap(); @@ -352,18 +344,16 @@ mod linux_android { let mut buf = [0u8; 1024]; // Check the tee'd bytes are at rd2. - assert_eq!(2, read(rd2.as_raw_fd(), &mut buf).unwrap()); + assert_eq!(2, read(&rd2, &mut buf).unwrap()); assert_eq!(b"ab", &buf[0..2]); // Check all the bytes are still at rd1. - assert_eq!(3, read(rd1.as_raw_fd(), &mut buf).unwrap()); + assert_eq!(3, read(&rd1, &mut buf).unwrap()); assert_eq!(b"abc", &buf[0..3]); } #[test] fn test_vmsplice() { - use std::os::fd::AsRawFd; - let (rd, wr) = pipe().unwrap(); let buf1 = b"abcdef"; @@ -376,22 +366,20 @@ mod linux_android { // Check the bytes can be read at rd. let mut buf = [0u8; 32]; - assert_eq!(6, read(rd.as_raw_fd(), &mut buf).unwrap()); + assert_eq!(6, read(&rd, &mut buf).unwrap()); assert_eq!(b"abcdef", &buf[0..6]); } #[cfg(target_os = "linux")] #[test] fn test_fallocate() { - use std::os::fd::AsRawFd; - let tmp = NamedTempFile::new().unwrap(); fallocate(&tmp, FallocateFlags::empty(), 0, 100).unwrap(); // Check if we read exactly 100 bytes let mut buf = [0u8; 200]; - assert_eq!(100, read(tmp.as_raw_fd(), &mut buf).unwrap()); + assert_eq!(100, read(&tmp, &mut buf).unwrap()); } // The tests below are disabled for the listed targets diff --git a/test/test_sendfile.rs b/test/test_sendfile.rs index 6333bf8662..3ca8092fc6 100644 --- a/test/test_sendfile.rs +++ b/test/test_sendfile.rs @@ -7,7 +7,6 @@ use tempfile::tempfile; cfg_if! { if #[cfg(linux_android)] { use nix::unistd::{pipe, read}; - use std::os::unix::io::AsRawFd; } else if #[cfg(any(freebsdlike, apple_targets, solarish))] { use std::net::Shutdown; use std::os::unix::net::UnixStream; @@ -28,7 +27,7 @@ fn test_sendfile_linux() { assert_eq!(2, res); let mut buf = [0u8; 1024]; - assert_eq!(2, read(rd.as_raw_fd(), &mut buf).unwrap()); + assert_eq!(2, read(&rd, &mut buf).unwrap()); assert_eq!(b"f1", &buf[0..2]); assert_eq!(7, offset); } @@ -47,7 +46,7 @@ fn test_sendfile64_linux() { assert_eq!(2, res); let mut buf = [0u8; 1024]; - assert_eq!(2, read(rd.as_raw_fd(), &mut buf).unwrap()); + assert_eq!(2, read(&rd, &mut buf).unwrap()); assert_eq!(b"f1", &buf[0..2]); assert_eq!(7, offset); } diff --git a/test/test_unistd.rs b/test/test_unistd.rs index 53d1bc79a0..5cb019bd95 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -26,7 +26,6 @@ use std::ffi::CString; 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", @@ -121,8 +120,7 @@ fn test_mkstemp() { let result = mkstemp(&path); match result { - Ok((fd, path)) => { - close(fd).unwrap(); + Ok((_, path)) => { unlink(path.as_path()).unwrap(); } Err(e) => panic!("mkstemp failed: {e}"), @@ -163,12 +161,14 @@ fn test_mkfifo_directory() { target_os = "haiku" )))] fn test_mkfifoat_none() { + use nix::fcntl::AT_FDCWD; + 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(); + mkfifoat(AT_FDCWD, &mkfifoat_fifo, Mode::S_IRUSR).unwrap(); let stats = stat::stat(&mkfifoat_fifo).unwrap(); let typ = stat::SFlag::from_bits_truncate(stats.st_mode); @@ -184,13 +184,12 @@ fn test_mkfifoat_none() { )))] fn test_mkfifoat() { use nix::fcntl; - use std::os::fd::AsRawFd; let tempdir = tempdir().unwrap(); let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); let mkfifoat_name = "mkfifoat_name"; - mkfifoat(Some(dirfd.as_raw_fd()), mkfifoat_name, Mode::S_IRUSR).unwrap(); + mkfifoat(&dirfd, mkfifoat_name, Mode::S_IRUSR).unwrap(); let stats = stat::fstatat(&dirfd, mkfifoat_name, fcntl::AtFlags::empty()).unwrap(); @@ -206,10 +205,12 @@ fn test_mkfifoat() { target_os = "haiku" )))] fn test_mkfifoat_directory_none() { + use nix::fcntl::AT_FDCWD; + let _m = crate::CWD_LOCK.read(); // mkfifoat should fail if a directory is given - mkfifoat(None, &env::temp_dir(), Mode::S_IRUSR) + mkfifoat(AT_FDCWD, &env::temp_dir(), Mode::S_IRUSR) .expect_err("assertion failed"); } @@ -221,15 +222,13 @@ fn test_mkfifoat_directory_none() { target_os = "haiku" )))] fn test_mkfifoat_directory() { - use std::os::fd::AsRawFd; - // 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(&dirfd, mkfifoat_dir, Mode::S_IRUSR).unwrap(); - mkfifoat(Some(dirfd.as_raw_fd()), mkfifoat_dir, Mode::S_IRUSR) + mkfifoat(&dirfd, mkfifoat_dir, Mode::S_IRUSR) .expect_err("assertion failed"); } @@ -387,7 +386,7 @@ macro_rules! execve_test_factory ( match unsafe{fork()}.unwrap() { Child => { // Make `writer` be the stdout of the new process. - dup2(writer.as_raw_fd(), 1).unwrap(); + nix::unistd::dup2_stdout(&writer).unwrap(); let r = syscall(); let _ = std::io::stderr() .write_all(format!("{:?}", r).as_bytes()); @@ -401,7 +400,7 @@ macro_rules! execve_test_factory ( assert_eq!(ws, Ok(WaitStatus::Exited(child, 0))); // Read 1024 bytes. let mut buf = [0u8; 1024]; - read(reader.as_raw_fd(), &mut buf).unwrap(); + read(&reader, &mut buf).unwrap(); // It should contain the things we printed using `/bin/sh`. let string = String::from_utf8_lossy(&buf); assert!(string.contains("nix!!!")); @@ -434,12 +433,12 @@ macro_rules! execve_test_factory ( 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()); + execve_test_factory!(test_fexecve, fexecve, &File::open("/system/bin/sh").unwrap()); } 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()); + execve_test_factory!(test_fexecve, fexecve, &File::open("/bin/sh").unwrap()); } 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. @@ -458,21 +457,21 @@ 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()), + &File::open("/system/bin/sh").unwrap(), "", AtFlags::AT_EMPTY_PATH); execve_test_factory!(test_execveat_relative, execveat, - Some(File::open("/system/bin/").unwrap().into_raw_fd()), + &File::open("/system/bin/").unwrap(), "./sh", AtFlags::empty()); execve_test_factory!(test_execveat_absolute, execveat, - Some(File::open("/").unwrap().into_raw_fd()), + &File::open("/").unwrap(), "/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()), + execve_test_factory!(test_execveat_empty, execveat, &File::open("/bin/sh").unwrap(), "", AtFlags::AT_EMPTY_PATH); - execve_test_factory!(test_execveat_relative, execveat, Some(File::open("/bin/").unwrap().into_raw_fd()), + execve_test_factory!(test_execveat_relative, execveat, &File::open("/bin/").unwrap(), "./sh", AtFlags::empty()); - execve_test_factory!(test_execveat_absolute, execveat, Some(File::open("/").unwrap().into_raw_fd()), + execve_test_factory!(test_execveat_absolute, execveat, &File::open("/").unwrap(), "/bin/sh", AtFlags::empty()); } } @@ -485,12 +484,10 @@ fn test_fchdir() { let tmpdir = tempdir().unwrap(); let tmpdir_path = tmpdir.path().canonicalize().unwrap(); - let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd(); + let tmpdir_fd = File::open(&tmpdir_path).unwrap(); - fchdir(tmpdir_fd).expect("assertion failed"); + fchdir(&tmpdir_fd).expect("assertion failed"); assert_eq!(getcwd().unwrap(), tmpdir_path); - - close(tmpdir_fd).expect("assertion failed"); } #[test] @@ -544,20 +541,18 @@ fn test_fchown() { let uid = Some(getuid()); let gid = Some(getgid()); - let path = tempfile().unwrap(); - let fd = path.as_raw_fd(); + let file = tempfile().unwrap(); - fchown(fd, uid, gid).unwrap(); - fchown(fd, uid, None).unwrap(); - fchown(fd, None, gid).unwrap(); - fchown(999999999, uid, gid).unwrap_err(); + fchown(&file, uid, gid).unwrap(); + fchown(&file, uid, None).unwrap(); + fchown(&file, None, gid).unwrap(); } #[test] #[cfg(not(target_os = "redox"))] fn test_fchownat() { use nix::fcntl::AtFlags; - use std::os::fd::AsRawFd; + use nix::fcntl::AT_FDCWD; let _dr = crate::DirRestore::new(); // Testing for anything other than our own UID/GID is hard. @@ -572,14 +567,13 @@ fn test_fchownat() { let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); - fchownat(Some(dirfd.as_raw_fd()), "file", uid, gid, AtFlags::empty()) - .unwrap(); + fchownat(&dirfd, "file", uid, gid, AtFlags::empty()).unwrap(); chdir(tempdir.path()).unwrap(); - fchownat(None, "file", uid, gid, AtFlags::empty()).unwrap(); + fchownat(AT_FDCWD, "file", uid, gid, AtFlags::empty()).unwrap(); fs::remove_file(&path).unwrap(); - fchownat(None, "file", uid, gid, AtFlags::empty()).unwrap_err(); + fchownat(AT_FDCWD, "file", uid, gid, AtFlags::empty()).unwrap_err(); } #[test] @@ -589,7 +583,7 @@ fn test_lseek() { tmp.write_all(CONTENTS).unwrap(); let offset: off_t = 5; - lseek(tmp.as_raw_fd(), offset, Whence::SeekSet).unwrap(); + lseek(&tmp, offset, Whence::SeekSet).unwrap(); let mut buf = [0u8; 7]; crate::read_exact(&tmp, &mut buf); @@ -603,7 +597,7 @@ fn test_lseek64() { let mut tmp = tempfile().unwrap(); tmp.write_all(CONTENTS).unwrap(); - lseek64(tmp.as_raw_fd(), 5, Whence::SeekSet).unwrap(); + lseek64(&tmp, 5, Whence::SeekSet).unwrap(); let mut buf = [0u8; 7]; crate::read_exact(&tmp, &mut buf); @@ -874,7 +868,7 @@ fn test_canceling_alarm() { #[test] #[cfg(not(any(target_os = "redox", target_os = "haiku")))] fn test_symlinkat() { - use std::os::fd::AsRawFd; + use nix::fcntl::AT_FDCWD; let _m = crate::CWD_LOCK.read(); @@ -882,7 +876,7 @@ fn test_symlinkat() { let target = tempdir.path().join("a"); let linkpath = tempdir.path().join("b"); - symlinkat(&target, None, &linkpath).unwrap(); + symlinkat(&target, AT_FDCWD, &linkpath).unwrap(); assert_eq!( readlink(&linkpath).unwrap().to_str().unwrap(), target.to_str().unwrap() @@ -891,7 +885,7 @@ fn test_symlinkat() { let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); let target = "c"; let linkpath = "d"; - symlinkat(target, Some(dirfd.as_raw_fd()), linkpath).unwrap(); + symlinkat(target, &dirfd, linkpath).unwrap(); assert_eq!( readlink(&tempdir.path().join(linkpath)) .unwrap() @@ -905,7 +899,6 @@ fn test_symlinkat() { #[cfg(not(any(target_os = "redox", target_os = "haiku")))] fn test_linkat_file() { use nix::fcntl::AtFlags; - use std::os::fd::AsRawFd; let tempdir = tempdir().unwrap(); let oldfilename = "foo.txt"; @@ -924,9 +917,9 @@ fn test_linkat_file() { // Attempt hard link file at relative path linkat( - Some(dirfd.as_raw_fd()), + &dirfd, oldfilename, - Some(dirfd.as_raw_fd()), + &dirfd, newfilename, AtFlags::AT_SYMLINK_FOLLOW, ) @@ -938,7 +931,7 @@ fn test_linkat_file() { #[cfg(not(any(target_os = "redox", target_os = "haiku")))] fn test_linkat_olddirfd_none() { use nix::fcntl::AtFlags; - use std::os::fd::AsRawFd; + use nix::fcntl::AT_FDCWD; let _dr = crate::DirRestore::new(); @@ -964,9 +957,9 @@ fn test_linkat_olddirfd_none() { // Attempt hard link file using curent working directory as relative path for old file path chdir(tempdir_oldfile.path()).unwrap(); linkat( - None, + AT_FDCWD, oldfilename, - Some(dirfd.as_raw_fd()), + &dirfd, newfilename, AtFlags::AT_SYMLINK_FOLLOW, ) @@ -978,7 +971,7 @@ fn test_linkat_olddirfd_none() { #[cfg(not(any(target_os = "redox", target_os = "haiku")))] fn test_linkat_newdirfd_none() { use nix::fcntl::AtFlags; - use std::os::fd::AsRawFd; + use nix::fcntl::AT_FDCWD; let _dr = crate::DirRestore::new(); @@ -1004,9 +997,9 @@ fn test_linkat_newdirfd_none() { // Attempt hard link file using current working directory as relative path for new file path chdir(tempdir_newfile.path()).unwrap(); linkat( - Some(dirfd.as_raw_fd()), + &dirfd, oldfilename, - None, + AT_FDCWD, newfilename, AtFlags::AT_SYMLINK_FOLLOW, ) @@ -1018,7 +1011,7 @@ fn test_linkat_newdirfd_none() { #[cfg(not(any(apple_targets, target_os = "redox", target_os = "haiku")))] fn test_linkat_no_follow_symlink() { use nix::fcntl::AtFlags; - use std::os::fd::AsRawFd; + use nix::fcntl::AT_FDCWD; let _m = crate::CWD_LOCK.read(); @@ -1036,7 +1029,7 @@ fn test_linkat_no_follow_symlink() { File::create(&oldfilepath).unwrap(); // Create symlink to file - symlinkat(&oldfilepath, None, &symoldfilepath).unwrap(); + symlinkat(&oldfilepath, AT_FDCWD, &symoldfilepath).unwrap(); // Get file descriptor for base directory let dirfd = @@ -1045,9 +1038,9 @@ fn test_linkat_no_follow_symlink() { // Attempt link symlink of file at relative path linkat( - Some(dirfd.as_raw_fd()), + &dirfd, symoldfilename, - Some(dirfd.as_raw_fd()), + &dirfd, newfilename, AtFlags::empty(), ) @@ -1064,6 +1057,7 @@ fn test_linkat_no_follow_symlink() { #[cfg(not(any(target_os = "redox", target_os = "haiku")))] fn test_linkat_follow_symlink() { use nix::fcntl::AtFlags; + use nix::fcntl::AT_FDCWD; let _m = crate::CWD_LOCK.read(); @@ -1081,7 +1075,7 @@ fn test_linkat_follow_symlink() { File::create(&oldfilepath).unwrap(); // Create symlink to file - symlinkat(&oldfilepath, None, &symoldfilepath).unwrap(); + symlinkat(&oldfilepath, AT_FDCWD, &symoldfilepath).unwrap(); // Get file descriptor for base directory let dirfd = @@ -1090,9 +1084,9 @@ fn test_linkat_follow_symlink() { // Attempt link target of symlink of file at relative path linkat( - Some(dirfd.as_raw_fd()), + &dirfd, symoldfilename, - Some(dirfd.as_raw_fd()), + &dirfd, newfilename, AtFlags::AT_SYMLINK_FOLLOW, ) @@ -1114,8 +1108,6 @@ fn test_linkat_follow_symlink() { #[test] #[cfg(not(target_os = "redox"))] fn test_unlinkat_dir_noremovedir() { - use std::os::fd::AsRawFd; - let tempdir = tempdir().unwrap(); let dirname = "foo_dir"; let dirpath = tempdir.path().join(dirname); @@ -1130,16 +1122,13 @@ fn test_unlinkat_dir_noremovedir() { // Attempt unlink dir at relative path without proper flag let err_result = - unlinkat(Some(dirfd.as_raw_fd()), dirname, UnlinkatFlags::NoRemoveDir) - .unwrap_err(); + unlinkat(&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() { - use std::os::fd::AsRawFd; - let tempdir = tempdir().unwrap(); let dirname = "foo_dir"; let dirpath = tempdir.path().join(dirname); @@ -1153,16 +1142,13 @@ fn test_unlinkat_dir_removedir() { .unwrap(); // Attempt unlink dir at relative path with proper flag - unlinkat(Some(dirfd.as_raw_fd()), dirname, UnlinkatFlags::RemoveDir) - .unwrap(); + unlinkat(&dirfd, dirname, UnlinkatFlags::RemoveDir).unwrap(); assert!(!dirpath.exists()); } #[test] #[cfg(not(target_os = "redox"))] fn test_unlinkat_file() { - use std::os::fd::AsRawFd; - let tempdir = tempdir().unwrap(); let filename = "foo.txt"; let filepath = tempdir.path().join(filename); @@ -1176,12 +1162,7 @@ fn test_unlinkat_file() { .unwrap(); // Attempt unlink file at relative path - unlinkat( - Some(dirfd.as_raw_fd()), - filename, - UnlinkatFlags::NoRemoveDir, - ) - .unwrap(); + unlinkat(&dirfd, filename, UnlinkatFlags::NoRemoveDir).unwrap(); assert!(!filepath.exists()); } @@ -1264,6 +1245,8 @@ fn test_setfsuid() { target_os = "haiku" )))] fn test_ttyname() { + use std::os::fd::AsRawFd; + let fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed"); assert!(fd.as_raw_fd() > 0); @@ -1312,10 +1295,12 @@ fn test_getpeereid() { #[cfg(not(target_os = "redox"))] fn test_faccessat_none_not_existing() { use nix::fcntl::AtFlags; + use nix::fcntl::AT_FDCWD; + let tempdir = tempfile::tempdir().unwrap(); let dir = tempdir.path().join("does_not_exist.txt"); assert_eq!( - faccessat(None, &dir, AccessFlags::F_OK, AtFlags::empty()) + faccessat(AT_FDCWD, &dir, AccessFlags::F_OK, AtFlags::empty()) .err() .unwrap(), Errno::ENOENT @@ -1326,20 +1311,14 @@ fn test_faccessat_none_not_existing() { #[cfg(not(target_os = "redox"))] fn test_faccessat_not_existing() { use nix::fcntl::AtFlags; - use std::os::fd::AsRawFd; 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.as_raw_fd()), - not_exist_file, - AccessFlags::F_OK, - AtFlags::empty(), - ) - .err() - .unwrap(), + faccessat(&dirfd, not_exist_file, AccessFlags::F_OK, AtFlags::empty(),) + .err() + .unwrap(), Errno::ENOENT ); } @@ -1348,11 +1327,13 @@ fn test_faccessat_not_existing() { #[cfg(not(target_os = "redox"))] fn test_faccessat_none_file_exists() { use nix::fcntl::AtFlags; + use nix::fcntl::AT_FDCWD; + let tempdir = tempfile::tempdir().unwrap(); let path = tempdir.path().join("does_exist.txt"); let _file = File::create(path.clone()).unwrap(); assert!(faccessat( - None, + AT_FDCWD, &path, AccessFlags::R_OK | AccessFlags::W_OK, AtFlags::empty(), @@ -1364,7 +1345,6 @@ fn test_faccessat_none_file_exists() { #[cfg(not(target_os = "redox"))] fn test_faccessat_file_exists() { use nix::fcntl::AtFlags; - use std::os::fd::AsRawFd; let tempdir = tempfile::tempdir().unwrap(); let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); @@ -1372,7 +1352,7 @@ fn test_faccessat_file_exists() { let path = tempdir.path().join(exist_file); let _file = File::create(path.clone()).unwrap(); assert!(faccessat( - Some(dirfd.as_raw_fd()), + &dirfd, &path, AccessFlags::R_OK | AccessFlags::W_OK, AtFlags::empty(),