diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
commit | 8dd16259287f58f9273002717ec4d27e97127719 (patch) | |
tree | 3863e62a53829a84037444beab3abd4ed9dfc7d0 /third_party/rust/nix/src/fcntl.rs | |
parent | Releasing progress-linux version 126.0.1-1~progress7.99u1. (diff) | |
download | firefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz firefox-8dd16259287f58f9273002717ec4d27e97127719.zip |
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/nix/src/fcntl.rs')
-rw-r--r-- | third_party/rust/nix/src/fcntl.rs | 561 |
1 files changed, 424 insertions, 137 deletions
diff --git a/third_party/rust/nix/src/fcntl.rs b/third_party/rust/nix/src/fcntl.rs index 9bfecda5ac..ccefe955de 100644 --- a/third_party/rust/nix/src/fcntl.rs +++ b/third_party/rust/nix/src/fcntl.rs @@ -1,27 +1,39 @@ +//! file control options use crate::errno::Errno; -use libc::{self, c_char, c_int, c_uint, size_t, ssize_t}; +#[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] +use core::slice; +use libc::{c_int, c_uint, size_t, ssize_t}; +#[cfg(any( + target_os = "netbsd", + apple_targets, + target_os = "dragonfly", + all(target_os = "freebsd", target_arch = "x86_64"), +))] +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; use std::os::unix::io::RawFd; -// For splice and copy_file_range +#[cfg(not(any(target_os = "redox", target_os = "solaris")))] +use std::os::unix::io::{AsRawFd, OwnedFd}; #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "linux" + target_os = "netbsd", + apple_targets, + target_os = "dragonfly", + all(target_os = "freebsd", target_arch = "x86_64"), ))] -use std::{ - os::unix::io::{AsFd, AsRawFd}, - ptr, -}; +use std::path::PathBuf; +#[cfg(any(linux_android, target_os = "freebsd"))] +use std::{os::unix::io::AsFd, ptr}; #[cfg(feature = "fs")] use crate::{sys::stat::Mode, NixPath, Result}; #[cfg(any( - target_os = "linux", - target_os = "android", + linux_android, target_os = "emscripten", target_os = "fuchsia", target_os = "wasi", @@ -32,41 +44,58 @@ use crate::{sys::stat::Mode, NixPath, Result}; pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice}; #[cfg(not(target_os = "redox"))] -#[cfg(any(feature = "fs", feature = "process"))] +#[cfg(any(feature = "fs", feature = "process", feature = "user"))] libc_bitflags! { + /// Flags that control how the various *at syscalls behave. #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "process"))))] pub struct AtFlags: c_int { + #[allow(missing_docs)] + #[doc(hidden)] + // Should not be used by the public API, but only internally. AT_REMOVEDIR; + /// Used with [`linkat`](crate::unistd::linkat`) to create a link to a symbolic link's + /// target, instead of to the symbolic link itself. AT_SYMLINK_FOLLOW; + /// Used with functions like [`fstatat`](crate::sys::stat::fstatat`) to operate on a link + /// itself, instead of the symbolic link's target. AT_SYMLINK_NOFOLLOW; - #[cfg(any(target_os = "android", target_os = "linux"))] + /// Don't automount the terminal ("basename") component of pathname if it is a directory + /// that is an automount point. + #[cfg(linux_android)] AT_NO_AUTOMOUNT; - #[cfg(any(target_os = "android", target_os = "linux"))] + /// If the provided path is an empty string, operate on the provided directory file + /// descriptor instead. + #[cfg(any(linux_android, target_os = "freebsd", target_os = "hurd"))] AT_EMPTY_PATH; + /// Used with [`faccessat`](crate::unistd::faccessat), the checks for accessibility are + /// performed using the effective user and group IDs instead of the real user and group ID #[cfg(not(target_os = "android"))] AT_EACCESS; } } -#[cfg(any(feature = "fs", feature = "term"))] +#[cfg(any( + feature = "fs", + feature = "term", + all(feature = "fanotify", target_os = "linux") +))] libc_bitflags!( /// Configuration options for opened files. - #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "term"))))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "term", all(feature = "fanotify", target_os = "linux")))))] pub struct OFlag: c_int { /// Mask for the access mode of the file. O_ACCMODE; /// Use alternate I/O semantics. #[cfg(target_os = "netbsd")] - #[cfg_attr(docsrs, doc(cfg(all())))] O_ALT_IO; /// Open the file in append-only mode. O_APPEND; /// Generate a signal when input or output becomes possible. - #[cfg(not(any(target_os = "aix", - target_os = "illumos", - target_os = "solaris", - target_os = "haiku")))] - #[cfg_attr(docsrs, doc(cfg(all())))] + #[cfg(not(any( + solarish, + target_os = "aix", + target_os = "haiku" + )))] O_ASYNC; /// Closes the file descriptor once an `execve` call is made. /// @@ -75,68 +104,42 @@ libc_bitflags!( /// Create the file if it does not exist. O_CREAT; /// Try to minimize cache effects of the I/O for this file. - #[cfg(any(target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - target_os = "netbsd"))] - #[cfg_attr(docsrs, doc(cfg(all())))] + #[cfg(any( + freebsdlike, + linux_android, + solarish, + target_os = "netbsd" + ))] O_DIRECT; /// If the specified path isn't a directory, fail. - #[cfg(not(any(target_os = "illumos", target_os = "solaris")))] - #[cfg_attr(docsrs, doc(cfg(all())))] O_DIRECTORY; /// Implicitly follow each `write()` with an `fdatasync()`. - #[cfg(any(target_os = "android", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd"))] - #[cfg_attr(docsrs, doc(cfg(all())))] + #[cfg(any(linux_android, apple_targets, netbsdlike))] O_DSYNC; /// Error out if a file was not created. O_EXCL; /// Open for execute only. #[cfg(target_os = "freebsd")] - #[cfg_attr(docsrs, doc(cfg(all())))] O_EXEC; /// Open with an exclusive file lock. - #[cfg(any(target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(all())))] + #[cfg(any(bsd, target_os = "redox"))] O_EXLOCK; /// Same as `O_SYNC`. - #[cfg(any(target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - all(target_os = "linux", not(target_env = "musl")), - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", + #[cfg(any(bsd, + all(target_os = "linux", not(target_env = "musl"), not(target_env = "ohos")), target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(all())))] O_FSYNC; /// Allow files whose sizes can't be represented in an `off_t` to be opened. - #[cfg(any(target_os = "android", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all())))] + #[cfg(linux_android)] O_LARGEFILE; /// Do not update the file last access time during `read(2)`s. - #[cfg(any(target_os = "android", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all())))] + #[cfg(linux_android)] O_NOATIME; /// Don't attach the device as the process' controlling terminal. #[cfg(not(target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(all())))] O_NOCTTY; /// Same as `O_NONBLOCK`. #[cfg(not(any(target_os = "redox", target_os = "haiku")))] - #[cfg_attr(docsrs, doc(cfg(all())))] O_NDELAY; /// `open()` will fail if the given path is a symbolic link. O_NOFOLLOW; @@ -144,13 +147,11 @@ libc_bitflags!( O_NONBLOCK; /// Don't deliver `SIGPIPE`. #[cfg(target_os = "netbsd")] - #[cfg_attr(docsrs, doc(cfg(all())))] O_NOSIGPIPE; /// Obtain a file descriptor for low-level access. /// /// The file itself is not opened and other file operations will fail. - #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(all())))] + #[cfg(any(linux_android, target_os = "redox"))] O_PATH; /// Only allow reading. /// @@ -161,36 +162,24 @@ libc_bitflags!( /// This should not be combined with `O_WRONLY` or `O_RDONLY`. O_RDWR; /// Similar to `O_DSYNC` but applies to `read`s instead. - #[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))] - #[cfg_attr(docsrs, doc(cfg(all())))] + #[cfg(any(target_os = "linux", netbsdlike))] O_RSYNC; /// Skip search permission checks. #[cfg(target_os = "netbsd")] - #[cfg_attr(docsrs, doc(cfg(all())))] O_SEARCH; /// Open with a shared file lock. - #[cfg(any(target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(all())))] + #[cfg(any(bsd, target_os = "redox"))] O_SHLOCK; /// Implicitly follow each `write()` with an `fsync()`. #[cfg(not(target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(all())))] O_SYNC; /// Create an unnamed temporary file. - #[cfg(any(target_os = "android", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all())))] + #[cfg(linux_android)] O_TMPFILE; /// Truncate an existing regular file to 0 length if it allows writing. O_TRUNC; /// Restore default TTY attributes. #[cfg(target_os = "freebsd")] - #[cfg_attr(docsrs, doc(cfg(all())))] O_TTY_INIT; /// Only allow writing. /// @@ -199,9 +188,23 @@ 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<RawFd>) -> raw::c_int { + fd.unwrap_or(libc::AT_FDCWD) +} + feature! { #![feature = "fs"] +/// open or create a file for reading, writing or executing +/// +/// # See Also +/// [`open`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html) // The conversion is not identical on all operating systems. #[allow(clippy::useless_conversion)] pub fn open<P: ?Sized + NixPath>( @@ -216,21 +219,37 @@ pub fn open<P: ?Sized + NixPath>( Errno::result(fd) } +/// open or create a file for reading, writing or executing +/// +/// The `openat` function is equivalent to the [`open`] function except in the case where the path +/// specifies a relative path. In that case, the file to be opened is determined relative to the +/// directory associated with the file descriptor `fd`. +/// +/// # See Also +/// [`openat`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/openat.html) // The conversion is not identical on all operating systems. #[allow(clippy::useless_conversion)] #[cfg(not(target_os = "redox"))] pub fn openat<P: ?Sized + NixPath>( - dirfd: RawFd, + dirfd: Option<RawFd>, path: &P, oflag: OFlag, mode: Mode, ) -> Result<RawFd> { let fd = path.with_nix_path(|cstr| unsafe { - libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) + libc::openat(at_rawfd(dirfd), cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) })?; Errno::result(fd) } +/// Change the name of a file. +/// +/// The `renameat` function is equivalent to `rename` except in the case where either `old_path` +/// or `new_path` specifies a relative path. In such cases, the file to be renamed (or the its new +/// name, respectively) is located relative to `old_dirfd` or `new_dirfd`, respectively +/// +/// # See Also +/// [`renameat`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html) #[cfg(not(target_os = "redox"))] pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>( old_dirfd: Option<RawFd>, @@ -255,16 +274,30 @@ pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>( #[cfg(all(target_os = "linux", target_env = "gnu"))] #[cfg(feature = "fs")] libc_bitflags! { + /// Flags for use with [`renameat2`]. #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pub struct RenameFlags: u32 { + /// Atomically exchange `old_path` and `new_path`. RENAME_EXCHANGE; + /// Don't overwrite `new_path` of the rename. Return an error if `new_path` already + /// exists. RENAME_NOREPLACE; + /// creates a "whiteout" object at the source of the rename at the same time as performing + /// the rename. + /// + /// This operation makes sense only for overlay/union filesystem implementations. RENAME_WHITEOUT; } } feature! { #![feature = "fs"] +/// Like [`renameat`], but with an additional `flags` argument. +/// +/// A `renameat2` call with an empty flags argument is equivalent to `renameat`. +/// +/// # See Also +/// * [`rename`](https://man7.org/linux/man-pages/man2/rename.2.html) #[cfg(all(target_os = "linux", target_env = "gnu"))] pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>( old_dirfd: Option<RawFd>, @@ -306,12 +339,12 @@ fn readlink_maybe_at<P: ?Sized + NixPath>( Some(dirfd) => libc::readlinkat( dirfd, cstr.as_ptr(), - v.as_mut_ptr() as *mut c_char, + v.as_mut_ptr().cast(), v.capacity() as size_t, ), None => libc::readlink( cstr.as_ptr(), - v.as_mut_ptr() as *mut c_char, + v.as_mut_ptr().cast(), v.capacity() as size_t, ), } @@ -322,7 +355,11 @@ fn inner_readlink<P: ?Sized + NixPath>( dirfd: Option<RawFd>, path: &P, ) -> Result<OsString> { - let mut v = Vec::with_capacity(libc::PATH_MAX as usize); + #[cfg(not(target_os = "hurd"))] + const PATH_MAX: usize = libc::PATH_MAX as usize; + #[cfg(target_os = "hurd")] + const PATH_MAX: usize = 1024; // Hurd does not define a hard limit, so try a guess first + let mut v = Vec::with_capacity(PATH_MAX); { // simple case: result is strictly less than `PATH_MAX` @@ -340,7 +377,7 @@ fn inner_readlink<P: ?Sized + NixPath>( let reported_size = match dirfd { #[cfg(target_os = "redox")] Some(_) => unreachable!(), - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(linux_android, target_os = "freebsd", target_os = "hurd"))] Some(dirfd) => { let flags = if path.is_empty() { AtFlags::AT_EMPTY_PATH @@ -348,18 +385,19 @@ fn inner_readlink<P: ?Sized + NixPath>( AtFlags::empty() }; super::sys::stat::fstatat( - dirfd, + Some(dirfd), path, flags | AtFlags::AT_SYMLINK_NOFOLLOW, ) } #[cfg(not(any( - target_os = "android", - target_os = "linux", - target_os = "redox" + linux_android, + target_os = "redox", + target_os = "freebsd", + target_os = "hurd" )))] Some(dirfd) => super::sys::stat::fstatat( - dirfd, + Some(dirfd), path, AtFlags::AT_SYMLINK_NOFOLLOW, ), @@ -375,7 +413,7 @@ fn inner_readlink<P: ?Sized + NixPath>( } else { // If lstat doesn't cooperate, or reports an error, be a little less // precise. - (libc::PATH_MAX as usize).max(128) << 1 + PATH_MAX.max(128) << 1 } }; @@ -400,29 +438,32 @@ fn inner_readlink<P: ?Sized + NixPath>( } } +/// Read value of a symbolic link +/// +/// # See Also +/// * [`readlink`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html) pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> { inner_readlink(None, path) } +/// Read value of a symbolic link. +/// +/// Equivalent to [`readlink` ] except where `path` specifies a relative path. In that case, +/// interpret `path` relative to open file specified by `dirfd`. +/// +/// # See Also +/// * [`readlink`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html) #[cfg(not(target_os = "redox"))] pub fn readlinkat<P: ?Sized + NixPath>( - dirfd: RawFd, + dirfd: Option<RawFd>, path: &P, ) -> Result<OsString> { + let dirfd = at_rawfd(dirfd); inner_readlink(Some(dirfd), path) } - -/// Computes the raw fd consumed by a function of the form `*at`. -#[cfg(not(target_os = "redox"))] -pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int { - match fd { - None => libc::AT_FDCWD, - Some(fd) => fd, - } -} } -#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))] +#[cfg(any(linux_android, target_os = "freebsd"))] #[cfg(feature = "fs")] libc_bitflags!( /// Additional flags for file sealing, which allows for limiting operations on a file. @@ -436,6 +477,10 @@ libc_bitflags!( F_SEAL_GROW; /// The file contents cannot be modified. F_SEAL_WRITE; + /// The file contents cannot be modified, except via shared writable mappings that were + /// created prior to the seal being set. Since Linux 5.1. + #[cfg(linux_android)] + F_SEAL_FUTURE_WRITE; } ); @@ -452,59 +497,105 @@ libc_bitflags!( feature! { #![feature = "fs"] +/// Commands for use with [`fcntl`]. #[cfg(not(target_os = "redox"))] #[derive(Debug, Eq, Hash, PartialEq)] #[non_exhaustive] pub enum FcntlArg<'a> { + /// Duplicate the provided file descriptor F_DUPFD(RawFd), + /// Duplicate the provided file descriptor and set the `FD_CLOEXEC` flag on it. F_DUPFD_CLOEXEC(RawFd), + /// Get the close-on-exec flag associated with the file descriptor F_GETFD, + /// Set the close-on-exec flag associated with the file descriptor F_SETFD(FdFlag), // FD_FLAGS + /// Get descriptor status flags F_GETFL, + /// Set descriptor status flags F_SETFL(OFlag), // O_NONBLOCK + /// Set or clear a file segment lock F_SETLK(&'a libc::flock), + /// Like [`F_SETLK`](FcntlArg::F_SETLK) except that if a shared or exclusive lock is blocked by + /// other locks, the process waits until the request can be satisfied. F_SETLKW(&'a libc::flock), + /// Get the first lock that blocks the lock description F_GETLK(&'a mut libc::flock), - #[cfg(any(target_os = "linux", target_os = "android"))] + /// Acquire or release an open file description lock + #[cfg(linux_android)] F_OFD_SETLK(&'a libc::flock), - #[cfg(any(target_os = "linux", target_os = "android"))] + /// Like [`F_OFD_SETLK`](FcntlArg::F_OFD_SETLK) except that if a conflicting lock is held on + /// the file, then wait for that lock to be released. + #[cfg(linux_android)] F_OFD_SETLKW(&'a libc::flock), - #[cfg(any(target_os = "linux", target_os = "android"))] + /// Determine whether it would be possible to create the given lock. If not, return details + /// about one existing lock that would prevent it. + #[cfg(linux_android)] F_OFD_GETLK(&'a mut libc::flock), + /// Add seals to the file #[cfg(any( - target_os = "android", - target_os = "linux", + linux_android, target_os = "freebsd" ))] F_ADD_SEALS(SealFlag), + /// Get seals associated with the file #[cfg(any( - target_os = "android", - target_os = "linux", + linux_android, target_os = "freebsd" ))] F_GET_SEALS, - #[cfg(any(target_os = "macos", target_os = "ios"))] + /// Asks the drive to flush all buffered data to permanent storage. + #[cfg(apple_targets)] F_FULLFSYNC, - #[cfg(any(target_os = "linux", target_os = "android"))] + /// fsync + issue barrier to drive + #[cfg(apple_targets)] + F_BARRIERFSYNC, + /// Return the capacity of a pipe + #[cfg(linux_android)] F_GETPIPE_SZ, - #[cfg(any(target_os = "linux", target_os = "android"))] + /// Change the capacity of a pipe + #[cfg(linux_android)] F_SETPIPE_SZ(c_int), + /// Look up the path of an open file descriptor, if possible. + #[cfg(any( + target_os = "netbsd", + target_os = "dragonfly", + apple_targets, + ))] + F_GETPATH(&'a mut PathBuf), + /// Look up the path of an open file descriptor, if possible. + #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] + F_KINFO(&'a mut PathBuf), + /// Return the full path without firmlinks of the fd. + #[cfg(apple_targets)] + F_GETPATH_NOFIRMLINK(&'a mut PathBuf), // TODO: Rest of flags } +/// Commands for use with [`fcntl`]. #[cfg(target_os = "redox")] #[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)] #[non_exhaustive] pub enum FcntlArg { + /// Duplicate the provided file descriptor F_DUPFD(RawFd), + /// Duplicate the provided file descriptor and set the `FD_CLOEXEC` flag on it. F_DUPFD_CLOEXEC(RawFd), + /// Get the close-on-exec flag associated with the file descriptor F_GETFD, + /// Set the close-on-exec flag associated with the file descriptor F_SETFD(FdFlag), // FD_FLAGS + /// Get descriptor status flags F_GETFL, + /// Set descriptor status flags F_SETFL(OFlag), // O_NONBLOCK } pub use self::FcntlArg::*; +/// Perform various operations on open file descriptors. +/// +/// # See Also +/// * [`fcntl`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html) // TODO: Figure out how to handle value fcntl returns pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> { let res = unsafe { @@ -523,51 +614,95 @@ pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> { F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock), #[cfg(not(target_os = "redox"))] F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock), - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(linux_android)] F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock), - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(linux_android)] F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock), - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(linux_android)] F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock), #[cfg(any( - target_os = "android", - target_os = "linux", + linux_android, target_os = "freebsd" ))] F_ADD_SEALS(flag) => { libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()) } #[cfg(any( - target_os = "android", - target_os = "linux", + linux_android, target_os = "freebsd" ))] F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS), - #[cfg(any(target_os = "macos", target_os = "ios"))] + #[cfg(apple_targets)] F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC), - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(apple_targets)] + F_BARRIERFSYNC => libc::fcntl(fd, libc::F_BARRIERFSYNC), + #[cfg(linux_android)] F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ), - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(linux_android)] F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size), + #[cfg(any( + target_os = "dragonfly", + target_os = "netbsd", + apple_targets, + ))] + F_GETPATH(path) => { + let mut buffer = vec![0; libc::PATH_MAX as usize]; + let res = libc::fcntl(fd, libc::F_GETPATH, buffer.as_mut_ptr()); + let ok_res = Errno::result(res)?; + let optr = CStr::from_bytes_until_nul(&buffer).unwrap(); + *path = PathBuf::from(OsString::from(optr.to_str().unwrap())); + return Ok(ok_res) + }, + #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] + F_KINFO(path) => { + let mut info: libc::kinfo_file = std::mem::zeroed(); + info.kf_structsize = std::mem::size_of::<libc::kinfo_file>() as i32; + let res = libc::fcntl(fd, libc::F_KINFO, &mut info); + let ok_res = Errno::result(res)?; + let p = info.kf_path; + let u8_slice = slice::from_raw_parts(p.as_ptr().cast(), p.len()); + let optr = CStr::from_bytes_until_nul(u8_slice).unwrap(); + *path = PathBuf::from(OsString::from(optr.to_str().unwrap())); + return Ok(ok_res) + }, + #[cfg(apple_targets)] + F_GETPATH_NOFIRMLINK(path) => { + let mut buffer = vec![0; libc::PATH_MAX as usize]; + let res = libc::fcntl(fd, libc::F_GETPATH_NOFIRMLINK, buffer.as_mut_ptr()); + let ok_res = Errno::result(res)?; + let optr = CStr::from_bytes_until_nul(&buffer).unwrap(); + *path = PathBuf::from(OsString::from(optr.to_str().unwrap())); + return Ok(ok_res) + }, } }; Errno::result(res) } -// TODO: convert to libc_enum +/// Operations for use with [`Flock::lock`]. +#[cfg(not(any(target_os = "redox", target_os = "solaris")))] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[non_exhaustive] pub enum FlockArg { + /// shared file lock LockShared, + /// exclusive file lock LockExclusive, + /// Unlock file Unlock, + /// Shared lock. Do not block when locking. LockSharedNonblock, + /// Exclusive lock. Do not block when locking. LockExclusiveNonblock, + #[allow(missing_docs)] + #[deprecated(since = "0.28.0", note = "Use FlockArg::Unlock instead")] UnlockNonblock, } +#[allow(missing_docs)] #[cfg(not(any(target_os = "redox", target_os = "solaris")))] +#[deprecated(since = "0.28.0", note = "`fcntl::Flock` should be used instead.")] pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> { use self::FlockArg::*; @@ -582,15 +717,133 @@ pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> { LockExclusiveNonblock => { libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) } + #[allow(deprecated)] UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB), } }; Errno::result(res).map(drop) } + +/// Represents valid types for flock. +/// +/// # Safety +/// Types implementing this must not be `Clone`. +#[cfg(not(any(target_os = "redox", target_os = "solaris")))] +pub unsafe trait Flockable: AsRawFd {} + +/// Represents an owned flock, which unlocks on drop. +/// +/// See [flock(2)](https://linux.die.net/man/2/flock) for details on locking semantics. +#[cfg(not(any(target_os = "redox", target_os = "solaris")))] +#[derive(Debug)] +pub struct Flock<T: Flockable>(T); + +#[cfg(not(any(target_os = "redox", target_os = "solaris")))] +impl<T: Flockable> Drop for Flock<T> { + fn drop(&mut self) { + let res = Errno::result(unsafe { libc::flock(self.0.as_raw_fd(), libc::LOCK_UN) }); + if res.is_err() && !std::thread::panicking() { + panic!("Failed to remove flock: {}", res.unwrap_err()); + } + } +} + +#[cfg(not(any(target_os = "redox", target_os = "solaris")))] +impl<T: Flockable> Deref for Flock<T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} +#[cfg(not(any(target_os = "redox", target_os = "solaris")))] +impl<T: Flockable> DerefMut for Flock<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[cfg(not(any(target_os = "redox", target_os = "solaris")))] +impl<T: Flockable> Flock<T> { + /// Obtain a/an flock. + /// + /// # Example + /// ``` + /// # use std::io::Write; + /// # use std::fs::File; + /// # use nix::fcntl::{Flock, FlockArg}; + /// # fn do_stuff(file: File) { + /// let mut file = match Flock::lock(file, FlockArg::LockExclusive) { + /// Ok(l) => l, + /// Err(_) => return, + /// }; + /// + /// // Do stuff + /// let data = "Foo bar"; + /// _ = file.write(data.as_bytes()); + /// _ = file.sync_data(); + /// # } + pub fn lock(t: T, args: FlockArg) -> std::result::Result<Self, (T, Errno)> { + let flags = match args { + FlockArg::LockShared => libc::LOCK_SH, + FlockArg::LockExclusive => libc::LOCK_EX, + FlockArg::LockSharedNonblock => libc::LOCK_SH | libc::LOCK_NB, + FlockArg::LockExclusiveNonblock => libc::LOCK_EX | libc::LOCK_NB, + #[allow(deprecated)] + FlockArg::Unlock | FlockArg::UnlockNonblock => return Err((t, Errno::EINVAL)), + }; + match Errno::result(unsafe { libc::flock(t.as_raw_fd(), flags) }) { + Ok(_) => Ok(Self(t)), + Err(errno) => Err((t, errno)), + } + } + + /// Remove the lock and return the object wrapped within. + /// + /// # Example + /// ``` + /// # use std::fs::File; + /// # use nix::fcntl::{Flock, FlockArg}; + /// fn do_stuff(file: File) -> nix::Result<()> { + /// let mut lock = match Flock::lock(file, FlockArg::LockExclusive) { + /// Ok(l) => l, + /// Err((_,e)) => return Err(e), + /// }; + /// + /// // Do critical section + /// + /// // Unlock + /// let file = match lock.unlock() { + /// Ok(f) => f, + /// Err((_, e)) => return Err(e), + /// }; + /// + /// // Do anything else + /// + /// Ok(()) + /// } + pub fn unlock(self) -> std::result::Result<T, (Self, Errno)> { + let inner = unsafe { match Errno::result(libc::flock(self.0.as_raw_fd(), libc::LOCK_UN)) { + Ok(_) => std::ptr::read(&self.0), + Err(errno) => return Err((self, errno)), + }}; + + std::mem::forget(self); + Ok(inner) + } +} + +// Safety: `File` is not [std::clone::Clone]. +#[cfg(not(any(target_os = "redox", target_os = "solaris")))] +unsafe impl Flockable for std::fs::File {} + +// Safety: `OwnedFd` is not [std::clone::Clone]. +#[cfg(not(any(target_os = "redox", target_os = "solaris")))] +unsafe impl Flockable for OwnedFd {} } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] #[cfg(feature = "zerocopy")] libc_bitflags! { /// Additional flags to `splice` and friends. @@ -636,7 +889,7 @@ feature! { // Note: FreeBSD defines the offset argument as "off_t". Linux and Android // define it as "loff_t". But on both OSes, on all supported platforms, those // are 64 bits. So Nix uses i64 to make the docs simple and consistent. -#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] +#[cfg(any(linux_android, target_os = "freebsd"))] pub fn copy_file_range<Fd1: AsFd, Fd2: AsFd>( fd_in: Fd1, off_in: Option<&mut i64>, @@ -669,7 +922,7 @@ pub fn copy_file_range<Fd1: AsFd, Fd2: AsFd>( let ret = unsafe { libc::syscall( libc::SYS_copy_file_range, - fd_in, + fd_in.as_fd().as_raw_fd(), off_in, fd_out.as_fd().as_raw_fd(), off_out, @@ -682,7 +935,11 @@ pub fn copy_file_range<Fd1: AsFd, Fd2: AsFd>( Errno::result(ret).map(|r| r as usize) } -#[cfg(any(target_os = "linux", target_os = "android"))] +/// Splice data to/from a pipe +/// +/// # See Also +/// *[`splice`](https://man7.org/linux/man-pages/man2/splice.2.html) +#[cfg(linux_android)] pub fn splice( fd_in: RawFd, off_in: Option<&mut libc::loff_t>, @@ -704,7 +961,11 @@ pub fn splice( Errno::result(ret).map(|r| r as usize) } -#[cfg(any(target_os = "linux", target_os = "android"))] +/// Duplicate pipe content +/// +/// # See Also +/// *[`tee`](https://man7.org/linux/man-pages/man2/tee.2.html) +#[cfg(linux_android)] pub fn tee( fd_in: RawFd, fd_out: RawFd, @@ -715,7 +976,11 @@ pub fn tee( Errno::result(ret).map(|r| r as usize) } -#[cfg(any(target_os = "linux", target_os = "android"))] +/// Splice user pages to/from a pipe +/// +/// # See Also +/// *[`vmsplice`](https://man7.org/linux/man-pages/man2/vmsplice.2.html) +#[cfg(linux_android)] pub fn vmsplice( fd: RawFd, iov: &[std::io::IoSlice<'_>], @@ -724,7 +989,7 @@ pub fn vmsplice( let ret = unsafe { libc::vmsplice( fd, - iov.as_ptr() as *const libc::iovec, + iov.as_ptr().cast(), iov.len(), flags.bits(), ) @@ -793,16 +1058,22 @@ pub struct SpacectlRange(pub libc::off_t, pub libc::off_t); #[cfg(any(target_os = "freebsd"))] impl SpacectlRange { + /// Is the range empty? + /// + /// After a successful call to [`fspacectl`], A value of `true` for `SpacectlRange::is_empty` + /// indicates that the operation is complete. #[inline] pub fn is_empty(&self) -> bool { self.1 == 0 } + /// Remaining length of the range #[inline] pub fn len(&self) -> libc::off_t { self.1 } + /// Next file offset to operate on #[inline] pub fn offset(&self) -> libc::off_t { self.0 @@ -849,6 +1120,7 @@ impl SpacectlRange { /// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef"); /// ``` #[cfg(target_os = "freebsd")] +#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> { let mut rqsr = libc::spacectl_range { r_offset: range.0, @@ -897,6 +1169,7 @@ pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> { /// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef"); /// ``` #[cfg(target_os = "freebsd")] +#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined pub fn fspacectl_all( fd: RawFd, offset: libc::off_t, @@ -922,8 +1195,7 @@ pub fn fspacectl_all( } #[cfg(any( - target_os = "linux", - target_os = "android", + linux_android, target_os = "emscripten", target_os = "fuchsia", target_os = "wasi", @@ -937,21 +1209,34 @@ mod posix_fadvise { #[cfg(feature = "fs")] libc_enum! { + /// The specific advice provided to [`posix_fadvise`]. #[repr(i32)] #[non_exhaustive] #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pub enum PosixFadviseAdvice { + /// Revert to the default data access behavior. POSIX_FADV_NORMAL, + /// The file data will be accessed sequentially. POSIX_FADV_SEQUENTIAL, + /// A hint that file data will be accessed randomly, and prefetching is likely not + /// advantageous. POSIX_FADV_RANDOM, + /// The specified data will only be accessed once and then not reused. POSIX_FADV_NOREUSE, + /// The specified data will be accessed in the near future. POSIX_FADV_WILLNEED, + /// The specified data will not be accessed in the near future. POSIX_FADV_DONTNEED, } } feature! { #![feature = "fs"] + /// Allows a process to describe to the system its data access behavior for an open file + /// descriptor. + /// + /// # See Also + /// * [`posix_fadvise`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fadvise.html) pub fn posix_fadvise( fd: RawFd, offset: libc::off_t, @@ -963,20 +1248,22 @@ mod posix_fadvise { if res == 0 { Ok(()) } else { - Err(Errno::from_i32(res)) + Err(Errno::from_raw(res)) } } } } +/// Pre-allocate storage for a range in a file +/// +/// # See Also +/// * [`posix_fallocate`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fallocate.html) #[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "dragonfly", + linux_android, + freebsdlike, target_os = "emscripten", target_os = "fuchsia", target_os = "wasi", - target_os = "freebsd" ))] pub fn posix_fallocate( fd: RawFd, @@ -987,7 +1274,7 @@ pub fn posix_fallocate( match Errno::result(res) { Err(err) => Err(err), Ok(0) => Ok(()), - Ok(errno) => Err(Errno::from_i32(errno)), + Ok(errno) => Err(Errno::from_raw(errno)), } } } |