summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/src/fcntl.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/nix/src/fcntl.rs')
-rw-r--r--third_party/rust/nix/src/fcntl.rs561
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)),
}
}
}