#[cfg(not(target_os = "redox"))] #[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"))))] pubstruct 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; /// 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; /// 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",
all(feature = "fanotify", target_os = "linux")
))]
libc_bitflags!( /// Configuration options for opened files. #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "term", all(feature = "fanotify", target_os = "linux")))))] pubstruct OFlag: c_int { /// Mask for the access mode of the file.
O_ACCMODE; /// Use alternate I/O semantics. #[cfg(target_os = "netbsd")]
O_ALT_IO; /// Open the file in append-only mode.
O_APPEND; /// Generate a signal when input or output becomes possible. #[cfg(not(any(
solarish,
target_os = "aix",
target_os = "haiku"
)))]
O_ASYNC; /// Closes the file descriptor once an `execve` call is made. /// /// Also sets the file offset to the beginning of the file.
O_CLOEXEC; /// Create the file if it does not exist.
O_CREAT; /// Try to minimize cache effects of the I/O for this file. #[cfg(any(
freebsdlike,
linux_android,
solarish,
target_os = "netbsd"
))]
O_DIRECT; /// If the specified path isn't a directory, fail.
O_DIRECTORY; /// Implicitly follow each `write()` with an `fdatasync()`. #[cfg(any(linux_android, apple_targets, target_os = "freebsd", netbsdlike))]
O_DSYNC; /// Error out if a file was not created.
O_EXCL; /// Open for execute only. #[cfg(target_os = "freebsd")]
O_EXEC; /// Open with an exclusive file lock. #[cfg(any(bsd, target_os = "redox"))]
O_EXLOCK; /// Same as `O_SYNC`. #[cfg(any(bsd,
all(target_os = "linux", not(target_env = "musl"), not(target_env = "ohos")),
target_os = "redox"))]
O_FSYNC; /// Allow files whose sizes can't be represented in an `off_t` to be opened. #[cfg(linux_android)]
O_LARGEFILE; /// Do not update the file last access time during `read(2)`s. #[cfg(linux_android)]
O_NOATIME; /// Don't attach the device as the process' controlling terminal. #[cfg(not(target_os = "redox"))]
O_NOCTTY; /// Same as `O_NONBLOCK`. #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
O_NDELAY; /// `open()` will fail if the given path is a symbolic link.
O_NOFOLLOW; /// When possible, open the file in nonblocking mode.
O_NONBLOCK; /// Don't deliver `SIGPIPE`. #[cfg(target_os = "netbsd")]
O_NOSIGPIPE; /// Obtain a file descriptor for low-level access. /// /// The file itself is not opened and other file operations will fail. #[cfg(any(linux_android, target_os = "redox", target_os = "freebsd", target_os = "fuchsia"))]
O_PATH; /// Only allow reading. /// /// This should not be combined with `O_WRONLY` or `O_RDWR`.
O_RDONLY; /// Allow both reading and writing. /// /// 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", netbsdlike))]
O_RSYNC; /// Open directory for search only. Skip search permission checks on /// later `openat()` calls using the obtained file descriptor. #[cfg(any(
apple_targets,
solarish,
target_os = "netbsd",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "emscripten",
target_os = "aix",
target_os = "wasi"
))]
O_SEARCH; /// Open with a shared file lock. #[cfg(any(bsd, target_os = "redox"))]
O_SHLOCK; /// Implicitly follow each `write()` with an `fsync()`. #[cfg(not(target_os = "redox"))]
O_SYNC; /// Create an unnamed temporary file. #[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")]
O_TTY_INIT; /// Only allow writing. /// /// This should not be combined with `O_RDONLY` or `O_RDWR`.
O_WRONLY;
}
);
/// 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)] pubfn open<P: ?Sized + NixPath>(
path: &P,
oflag: OFlag,
mode: Mode,
) -> Result<RawFd> { let fd = path.with_nix_path(|cstr| unsafe {
libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint)
})?;
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"))] pubfn openat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
path: &P,
oflag: OFlag,
mode: Mode,
) -> Result<RawFd> { let fd = path.with_nix_path(|cstr| unsafe {
libc::openat(at_rawfd(dirfd), cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint)
})?;
Errno::result(fd)
}
cfg_if::cfg_if! { if#[cfg(target_os = "linux")] {
libc_bitflags! { /// Path resolution flags. /// /// See [path resolution(7)](https://man7.org/linux/man-pages/man7/path_resolution.7.html) /// for details of the resolution process. pubstruct ResolveFlag: libc::c_ulonglong { /// Do not permit the path resolution to succeed if any component of /// the resolution is not a descendant of the directory indicated by /// dirfd. This causes absolute symbolic links (and absolute values of /// pathname) to be rejected.
RESOLVE_BENEATH;
/// Treat the directory referred to by dirfd as the root directory /// while resolving pathname.
RESOLVE_IN_ROOT;
/// Disallow all magic-link resolution during path resolution. Magic /// links are symbolic link-like objects that are most notably found /// in proc(5); examples include `/proc/[pid]/exe` and `/proc/[pid]/fd/*`. /// /// See symlink(7) for more details.
RESOLVE_NO_MAGICLINKS;
/// Disallow resolution of symbolic links during path resolution. This /// option implies RESOLVE_NO_MAGICLINKS.
RESOLVE_NO_SYMLINKS;
/// Disallow traversal of mount points during path resolution (including /// all bind mounts).
RESOLVE_NO_XDEV;
}
}
impl OpenHow { /// Create a new zero-filled `open_how`. pubfn new() -> Self { // safety: according to the man page, open_how MUST be zero-initialized // on init so that unknown fields are also zeroed. Self(unsafe {
std::mem::MaybeUninit::zeroed().assume_init()
})
}
/// Set the open flags used to open a file, completely overwriting any /// existing flags. pubfn flags(mutself, flags: OFlag) -> Self { let flags = flags.bits() as libc::c_ulonglong; self.0.flags = flags; self
}
/// Set the file mode new files will be created with, overwriting any /// existing flags. pubfn mode(mutself, mode: Mode) -> Self { let mode = mode.bits() as libc::c_ulonglong; self.0.mode = mode; self
}
/// Set resolve flags, completely overwriting any existing flags. /// /// See [ResolveFlag] for more detail. pubfn resolve(mutself, resolve: ResolveFlag) -> Self { let resolve = resolve.bits(); self.0.resolve = resolve; self
}
}
// safety: default isn't derivable because libc::open_how must be zeroed impl Default for OpenHow { fn default() -> Self { Self::new()
}
}
/// Open or create a file for reading, writing or executing. /// /// `openat2` is an extension of the [`openat`] function that allows the caller /// to control how path resolution happens. /// /// # See also /// /// [openat2](https://man7.org/linux/man-pages/man2/openat2.2.html) pubfn openat2<P: ?Sized + NixPath>(
dirfd: RawFd,
path: &P, mut how: OpenHow,
) -> Result<RawFd> { let fd = path.with_nix_path(|cstr| unsafe {
libc::syscall(
libc::SYS_openat2,
dirfd,
cstr.as_ptr(),
&mut how as *mut OpenHow,
std::mem::size_of::<libc::open_how>(),
)
})?;
Errno::result(fd as RawFd)
}
}
}
/// 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"))] pubfn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
old_dirfd: Option<RawFd>,
old_path: &P1,
new_dirfd: Option<RawFd>,
new_path: &P2,
) -> Result<()> { let res = old_path.with_nix_path(|old_cstr| {
new_path.with_nix_path(|new_cstr| unsafe {
libc::renameat(
at_rawfd(old_dirfd),
old_cstr.as_ptr(),
at_rawfd(new_dirfd),
new_cstr.as_ptr(),
)
})
})??;
Errno::result(res).map(drop)
}
}
#[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")))] pubstruct 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"))] pubfn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
old_dirfd: Option<RawFd>,
old_path: &P1,
new_dirfd: Option<RawFd>,
new_path: &P2,
flags: RenameFlags,
) -> Result<()> { let res = old_path.with_nix_path(|old_cstr| {
new_path.with_nix_path(|new_cstr| unsafe {
libc::renameat2(
at_rawfd(old_dirfd),
old_cstr.as_ptr(),
at_rawfd(new_dirfd),
new_cstr.as_ptr(),
flags.bits(),
)
})
})??;
Errno::result(res).map(drop)
}
fn inner_readlink<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
path: &P,
) -> Result<OsString> { #[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 letmut v = Vec::with_capacity(PATH_MAX);
{ // simple case: result is strictly less than `PATH_MAX` let res = readlink_maybe_at(dirfd, path, &mut v)?; let len = Errno::result(res)?;
debug_assert!(len >= 0); if (len as usize) < v.capacity() { return wrap_readlink_result(v, res);
}
}
// Uh oh, the result is too long... // Let's try to ask lstat how many bytes to allocate. letmut try_size = { let reported_size = match dirfd { #[cfg(target_os = "redox")]
Some(_) => unreachable!(), #[cfg(any(linux_android, target_os = "freebsd", target_os = "hurd"))]
Some(dirfd) => { let flags = if path.is_empty() {
AtFlags::AT_EMPTY_PATH
} else {
AtFlags::empty()
}; super::sys::stat::fstatat(
Some(dirfd),
path,
flags | AtFlags::AT_SYMLINK_NOFOLLOW,
)
} #[cfg(not(any(
linux_android,
target_os = "redox",
target_os = "freebsd",
target_os = "hurd"
)))]
Some(dirfd) => super::sys::stat::fstatat(
Some(dirfd),
path,
AtFlags::AT_SYMLINK_NOFOLLOW,
),
None => super::sys::stat::lstat(path),
}
.map(|x| x.st_size)
.unwrap_or(0);
if reported_size > 0 { // Note: even if `lstat`'s apparently valid answer turns out to be // wrong, we will still read the full symlink no matter what.
reported_size as usize + 1
} else { // If lstat doesn't cooperate, or reports an error, be a little less // precise.
PATH_MAX.max(128) << 1
}
};
loop {
{
v.reserve_exact(try_size); let res = readlink_maybe_at(dirfd, path, &mut v)?; let len = Errno::result(res)?;
debug_assert!(len >= 0); if (len as usize) < v.capacity() { return wrap_readlink_result(v, res);
}
}
// Ugh! Still not big enough! match try_size.checked_shl(1) {
Some(next_size) => try_size = next_size, // It's absurd that this would happen, but handle it sanely // anyway.
None => break Err(Errno::ENAMETOOLONG),
}
}
}
/// 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"))] pubfn readlinkat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
path: &P,
) -> Result<OsString> { let dirfd = at_rawfd(dirfd);
inner_readlink(Some(dirfd), path)
}
}
#[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. #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pubstruct SealFlag: c_int { /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`.
F_SEAL_SEAL; /// The file cannot be reduced in size.
F_SEAL_SHRINK; /// The size of the file cannot be increased.
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;
}
);
#[cfg(feature = "fs")]
libc_bitflags!( /// Additional configuration flags for `fcntl`'s `F_SETFD`. #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pubstruct FdFlag: c_int { /// The file descriptor will automatically be closed during a successful `execve(2)`.
FD_CLOEXEC;
}
);
feature! { #![feature = "fs"]
/// Commands for use with [`fcntl`]. #[cfg(not(target_os = "redox"))] #[derive(Debug, Eq, Hash, PartialEq)] #[non_exhaustive] pubenum 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), /// Acquire or release an open file description lock #[cfg(linux_android)]
F_OFD_SETLK(&'a libc::flock), /// 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), /// 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(
linux_android,
target_os = "freebsd"
))]
F_ADD_SEALS(SealFlag), /// Get seals associated with the file #[cfg(any(
linux_android,
target_os = "freebsd"
))]
F_GET_SEALS, /// Asks the drive to flush all buffered data to permanent storage. #[cfg(apple_targets)]
F_FULLFSYNC, /// fsync + issue barrier to drive #[cfg(apple_targets)]
F_BARRIERFSYNC, /// Return the capacity of a pipe #[cfg(linux_android)]
F_GETPIPE_SZ, /// 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] pubenum 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
} pubuseself::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 pubfn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> { let res = unsafe { match arg {
F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
F_DUPFD_CLOEXEC(rawfd) => {
libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd)
}
F_GETFD => libc::fcntl(fd, libc::F_GETFD),
F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
F_GETFL => libc::fcntl(fd, libc::F_GETFL),
F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()), #[cfg(not(target_os = "redox"))]
F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock), #[cfg(not(target_os = "redox"))]
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(linux_android)]
F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock), #[cfg(linux_android)]
F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock), #[cfg(linux_android)]
F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock), #[cfg(any(
linux_android,
target_os = "freebsd"
))]
F_ADD_SEALS(flag) => {
libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits())
} #[cfg(any(
linux_android,
target_os = "freebsd"
))]
F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS), #[cfg(apple_targets)]
F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC), #[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(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) => { letmut 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) => { letmut 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) => { letmut 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)
}
/// Operations for use with [`Flock::lock`]. #[cfg(not(any(target_os = "redox", target_os = "solaris")))] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[non_exhaustive] pubenum 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.")] pubfn flock(fd: RawFd, arg: FlockArg) -> Result<()> { useself::FlockArg::*;
/// Represents valid types for flock. /// /// # Safety /// Types implementing this must not be `Clone`. #[cfg(not(any(target_os = "redox", target_os = "solaris")))] pubunsafetrait 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)] pubstruct Flock<T: Flockable>(T);
#[cfg(not(any(target_os = "redox", target_os = "solaris")))] impl<T: Flockable> Drop for Flock<T> { fn drop(&mutself) { 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;
/// 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(()) /// } pubfn 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)
}
/// Relock the file. This can upgrade or downgrade the lock type. /// /// # Example /// ``` /// # use std::fs::File; /// # use nix::fcntl::{Flock, FlockArg}; /// # use tempfile::tempfile; /// let f: std::fs::File = tempfile().unwrap(); /// let locked_file = Flock::lock(f, FlockArg::LockExclusive).unwrap(); /// // Do stuff, then downgrade the lock /// locked_file.relock(FlockArg::LockShared).unwrap(); /// ``` pubfn relock(&self, arg: FlockArg) -> Result<()> { let flags = match arg {
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(Errno::EINVAL),
};
Errno::result(unsafe { libc::flock(self.as_raw_fd(), flags) }).map(drop)
}
}
// Safety: `File` is not [std::clone::Clone]. #[cfg(not(any(target_os = "redox", target_os = "solaris")))] unsafeimpl Flockable for std::fs::File {}
// Safety: `OwnedFd` is not [std::clone::Clone]. #[cfg(not(any(target_os = "redox", target_os = "solaris")))] unsafeimpl Flockable for OwnedFd {}
}
#[cfg(linux_android)] #[cfg(feature = "zerocopy")]
libc_bitflags! { /// Additional flags to `splice` and friends. #[cfg_attr(docsrs, doc(cfg(feature = "zerocopy")))] pubstruct SpliceFFlags: c_uint { /// Request that pages be moved instead of copied. /// /// Not applicable to `vmsplice`.
SPLICE_F_MOVE; /// Do not block on I/O.
SPLICE_F_NONBLOCK; /// Hint that more data will be coming in a subsequent splice. /// /// Not applicable to `vmsplice`.
SPLICE_F_MORE; /// Gift the user pages to the kernel. /// /// Not applicable to `splice`.
SPLICE_F_GIFT;
}
}
feature! { #![feature = "zerocopy"]
/// Copy a range of data from one file to another /// /// The `copy_file_range` system call performs an in-kernel copy between /// file descriptors `fd_in` and `fd_out` without the additional cost of /// transferring data from the kernel to user space and back again. There may be /// additional optimizations for specific file systems. It copies up to `len` /// bytes of data from file descriptor `fd_in` to file descriptor `fd_out`, /// overwriting any data that exists within the requested range of the target /// file. /// /// If the `off_in` and/or `off_out` arguments are used, the values /// will be mutated to reflect the new position within the file after /// copying. If they are not used, the relevant file descriptors will be seeked /// to the new position. /// /// On successful completion the number of bytes actually copied will be /// returned. // 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(linux_android, target_os = "freebsd"))] pubfn copy_file_range<Fd1: AsFd, Fd2: AsFd>(
fd_in: Fd1,
off_in: Option<&mut i64>,
fd_out: Fd2,
off_out: Option<&mut i64>,
len: usize,
) -> Result<usize> { let off_in = off_in
.map(|offset| offset as *mut i64)
.unwrap_or(ptr::null_mut()); let off_out = off_out
.map(|offset| offset as *mut i64)
.unwrap_or(ptr::null_mut());
cfg_if::cfg_if! { if#[cfg(target_os = "freebsd")] { let ret = unsafe {
libc::copy_file_range(
fd_in.as_fd().as_raw_fd(),
off_in,
fd_out.as_fd().as_raw_fd(),
off_out,
len, 0,
)
};
} else { // May Linux distros still don't include copy_file_range in their // libc implementations, so we need to make a direct syscall. let ret = unsafe {
libc::syscall(
libc::SYS_copy_file_range,
fd_in.as_fd().as_raw_fd(),
off_in,
fd_out.as_fd().as_raw_fd(),
off_out,
len, 0,
)
};
}
}
Errno::result(ret).map(|r| r as usize)
}
/// Splice data to/from a pipe /// /// # See Also /// *[`splice`](https://man7.org/linux/man-pages/man2/splice.2.html) #[cfg(linux_android)] pubfn splice<Fd1: AsFd, Fd2: AsFd>(
fd_in: Fd1,
off_in: Option<&mut libc::loff_t>,
fd_out: Fd2,
off_out: Option<&mut libc::loff_t>,
len: usize,
flags: SpliceFFlags,
) -> Result<usize> { let off_in = off_in
.map(|offset| offset as *mut libc::loff_t)
.unwrap_or(ptr::null_mut()); let off_out = off_out
.map(|offset| offset as *mut libc::loff_t)
.unwrap_or(ptr::null_mut());
let ret = unsafe {
libc::splice(fd_in.as_fd().as_raw_fd(), off_in, fd_out.as_fd().as_raw_fd(), off_out, len, flags.bits())
};
Errno::result(ret).map(|r| r as usize)
}
/// Duplicate pipe content /// /// # See Also /// *[`tee`](https://man7.org/linux/man-pages/man2/tee.2.html) #[cfg(linux_android)] pubfn tee<Fd1: AsFd, Fd2: AsFd>(
fd_in: Fd1,
fd_out: Fd2,
len: usize,
flags: SpliceFFlags,
) -> Result<usize> { let ret = unsafe { libc::tee(fd_in.as_fd().as_raw_fd(), fd_out.as_fd().as_raw_fd(), len, flags.bits()) };
Errno::result(ret).map(|r| r as usize)
}
/// Splice user pages to/from a pipe /// /// # See Also /// *[`vmsplice`](https://man7.org/linux/man-pages/man2/vmsplice.2.html) #[cfg(linux_android)] pubfn vmsplice<F: AsFd>(
fd: F,
iov: &[std::io::IoSlice<'_>],
flags: SpliceFFlags,
) -> Result<usize> { let ret = unsafe {
libc::vmsplice(
fd.as_fd().as_raw_fd(),
iov.as_ptr().cast(),
iov.len(),
flags.bits(),
)
};
Errno::result(ret).map(|r| r as usize)
}
}
#[cfg(target_os = "linux")] #[cfg(feature = "fs")]
libc_bitflags!( /// Mode argument flags for fallocate determining operation performed on a given range. #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pubstruct FallocateFlags: c_int { /// File size is not changed. /// /// offset + len can be greater than file size.
FALLOC_FL_KEEP_SIZE; /// Deallocates space by creating a hole. /// /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes.
FALLOC_FL_PUNCH_HOLE; /// Removes byte range from a file without leaving a hole. /// /// Byte range to collapse starts at offset and continues for len bytes.
FALLOC_FL_COLLAPSE_RANGE; /// Zeroes space in specified byte range. /// /// Byte range starts at offset and continues for len bytes.
FALLOC_FL_ZERO_RANGE; /// Increases file space by inserting a hole within the file size. /// /// Does not overwrite existing data. Hole starts at offset and continues for len bytes.
FALLOC_FL_INSERT_RANGE; /// Shared file data extants are made private to the file. /// /// Gaurantees that a subsequent write will not fail due to lack of space.
FALLOC_FL_UNSHARE_RANGE;
}
);
feature! { #![feature = "fs"]
/// Manipulates file space. /// /// Allows the caller to directly manipulate the allocated disk space for the /// file referred to by fd. #[cfg(target_os = "linux")] #[cfg(feature = "fs")] pubfn fallocate(
fd: RawFd,
mode: FallocateFlags,
offset: libc::off_t,
len: libc::off_t,
) -> Result<()> { let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) };
Errno::result(res).map(drop)
}
/// Argument to [`fspacectl`] describing the range to zero. The first member is /// the file offset, and the second is the length of the region. #[cfg(any(target_os = "freebsd"))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pubstruct 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] pubfn is_empty(&self) -> bool { self.1 == 0
}
/// Remaining length of the range #[inline] pubfn len(&self) -> libc::off_t { self.1
}
/// Next file offset to operate on #[inline] pubfn offset(&self) -> libc::off_t { self.0
}
}
/// Punch holes in a file. /// /// `fspacectl` instructs the file system to deallocate a portion of a file. /// After a successful operation, this region of the file will return all zeroes /// if read. If the file system supports deallocation, then it may free the /// underlying storage, too. /// /// # Arguments /// /// - `fd` - File to operate on /// - `range.0` - File offset at which to begin deallocation /// - `range.1` - Length of the region to deallocate /// /// # Returns /// /// The operation may deallocate less than the entire requested region. On /// success, it returns the region that still remains to be deallocated. The /// caller should loop until the returned region is empty. /// /// # Example /// #[cfg_attr(fbsd14, doc = " ```")] #[cfg_attr(not(fbsd14), doc = " ```no_run")] /// # use std::io::Write; /// # use std::os::unix::fs::FileExt; /// # use std::os::unix::io::AsRawFd; /// # use nix::fcntl::*; /// # use tempfile::tempfile; /// const INITIAL: &[u8] = b"0123456789abcdef"; /// let mut f = tempfile().unwrap(); /// f.write_all(INITIAL).unwrap(); /// let mut range = SpacectlRange(3, 6); /// while (!range.is_empty()) { /// range = fspacectl(f.as_raw_fd(), range).unwrap(); /// } /// let mut buf = vec![0; INITIAL.len()]; /// f.read_exact_at(&mut buf, 0).unwrap(); /// 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 pubfn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> { letmut rqsr = libc::spacectl_range {
r_offset: range.0,
r_len: range.1,
}; let res = unsafe {
libc::fspacectl(
fd,
libc::SPACECTL_DEALLOC, // Only one command is supported ATM
&rqsr, 0, // No flags are currently supported
&mut rqsr,
)
};
Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len))
}
/// Like [`fspacectl`], but will never return incomplete. /// /// # Arguments /// /// - `fd` - File to operate on /// - `offset` - File offset at which to begin deallocation /// - `len` - Length of the region to deallocate /// /// # Returns /// /// Returns `()` on success. On failure, the region may or may not be partially /// deallocated. /// /// # Example /// #[cfg_attr(fbsd14, doc = " ```")] #[cfg_attr(not(fbsd14), doc = " ```no_run")] /// # use std::io::Write; /// # use std::os::unix::fs::FileExt; /// # use std::os::unix::io::AsRawFd; /// # use nix::fcntl::*; /// # use tempfile::tempfile; /// const INITIAL: &[u8] = b"0123456789abcdef"; /// let mut f = tempfile().unwrap(); /// f.write_all(INITIAL).unwrap(); /// fspacectl_all(f.as_raw_fd(), 3, 6).unwrap(); /// let mut buf = vec![0; INITIAL.len()]; /// f.read_exact_at(&mut buf, 0).unwrap(); /// 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 pubfn fspacectl_all(
fd: RawFd,
offset: libc::off_t,
len: libc::off_t,
) -> Result<()> { letmut rqsr = libc::spacectl_range {
r_offset: offset,
r_len: len,
}; while rqsr.r_len > 0 { let res = unsafe {
libc::fspacectl(
fd,
libc::SPACECTL_DEALLOC, // Only one command is supported ATM
&rqsr, 0, // No flags are currently supported
&mut rqsr,
)
};
Errno::result(res)?;
}
Ok(())
}
#[cfg(feature = "fs")]
libc_enum! { /// The specific advice provided to [`posix_fadvise`]. #[repr(i32)] #[non_exhaustive] #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pubenum 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) pubfn posix_fadvise(
fd: RawFd,
offset: libc::off_t,
len: libc::off_t,
advice: PosixFadviseAdvice,
) -> Result<()> { let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
if res == 0 {
Ok(())
} else {
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(
linux_android,
freebsdlike,
target_os = "emscripten",
target_os = "fuchsia",
target_os = "wasi",
))] pubfn posix_fallocate(
fd: RawFd,
offset: libc::off_t,
len: libc::off_t,
) -> Result<()> { let res = unsafe { libc::posix_fallocate(fd, offset, len) }; match Errno::result(res) {
Err(err) => Err(err),
Ok(0) => Ok(()),
Ok(errno) => Err(Errno::from_raw(errno)),
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet am 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.