diff options
Diffstat (limited to 'vendor/rustix/src/fs')
25 files changed, 3357 insertions, 0 deletions
diff --git a/vendor/rustix/src/fs/abs.rs b/vendor/rustix/src/fs/abs.rs new file mode 100644 index 00000000..236a833a --- /dev/null +++ b/vendor/rustix/src/fs/abs.rs @@ -0,0 +1,304 @@ +//! POSIX-style filesystem functions which operate on bare paths. + +use crate::fd::OwnedFd; +#[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] +use crate::fs::Access; +#[cfg(not(any( + solarish, + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "netbsd", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +use crate::fs::StatFs; +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +use crate::fs::StatVfs; +use crate::fs::{Mode, OFlags, Stat}; +#[cfg(not(target_os = "wasi"))] +use crate::ugid::{Gid, Uid}; +use crate::{backend, io, path}; +#[cfg(feature = "alloc")] +use { + crate::ffi::{CStr, CString}, + crate::path::SMALL_PATH_BUFFER_SIZE, + alloc::vec::Vec, +}; + +/// `open(path, oflags, mode)`—Opens a file. +/// +/// POSIX guarantees that `open` will use the lowest unused file descriptor, +/// however it is not safe in general to rely on this, as file descriptors may +/// be unexpectedly allocated on other threads or in libraries. +/// +/// The `Mode` argument is only significant when creating a file. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/open.html +/// [Linux]: https://man7.org/linux/man-pages/man2/open.2.html +#[inline] +pub fn open<P: path::Arg>(path: P, flags: OFlags, mode: Mode) -> io::Result<OwnedFd> { + path.into_with_c_str(|path| backend::fs::syscalls::open(path, flags, mode)) +} + +/// `chmod(path, mode)`—Sets file or directory permissions. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/chmod.html +/// [Linux]: https://man7.org/linux/man-pages/man2/chmod.2.html +#[cfg(not(target_os = "wasi"))] +#[inline] +pub fn chmod<P: path::Arg>(path: P, mode: Mode) -> io::Result<()> { + path.into_with_c_str(|path| backend::fs::syscalls::chmod(path, mode)) +} + +/// `stat(path)`—Queries metadata for a file or directory. +/// +/// [`Mode::from_raw_mode`] and [`FileType::from_raw_mode`] may be used to +/// interpret the `st_mode` field. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/stat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/stat.2.html +/// [`Mode::from_raw_mode`]: crate::fs::Mode::from_raw_mode +/// [`FileType::from_raw_mode`]: crate::fs::FileType::from_raw_mode +#[inline] +pub fn stat<P: path::Arg>(path: P) -> io::Result<Stat> { + path.into_with_c_str(backend::fs::syscalls::stat) +} + +/// `lstat(path)`—Queries metadata for a file or directory, without following +/// symlinks. +/// +/// [`Mode::from_raw_mode`] and [`FileType::from_raw_mode`] may be used to +/// interpret the `st_mode` field. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/lstat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/lstat.2.html +/// [`Mode::from_raw_mode`]: crate::fs::Mode::from_raw_mode +/// [`FileType::from_raw_mode`]: crate::fs::FileType::from_raw_mode +#[inline] +pub fn lstat<P: path::Arg>(path: P) -> io::Result<Stat> { + path.into_with_c_str(backend::fs::syscalls::lstat) +} + +/// `readlink(path)`—Reads the contents of a symlink. +/// +/// If `reuse` is non-empty, reuse its buffer to store the result if possible. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/readlink.html +/// [Linux]: https://man7.org/linux/man-pages/man2/readlink.2.html +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +#[inline] +pub fn readlink<P: path::Arg, B: Into<Vec<u8>>>(path: P, reuse: B) -> io::Result<CString> { + path.into_with_c_str(|path| _readlink(path, reuse.into())) +} + +#[cfg(feature = "alloc")] +fn _readlink(path: &CStr, mut buffer: Vec<u8>) -> io::Result<CString> { + // This code would benefit from having a better way to read into + // uninitialized memory, but that requires `unsafe`. + buffer.clear(); + buffer.reserve(SMALL_PATH_BUFFER_SIZE); + buffer.resize(buffer.capacity(), 0_u8); + + loop { + let nread = backend::fs::syscalls::readlink(path, &mut buffer)?; + + let nread = nread as usize; + assert!(nread <= buffer.len()); + if nread < buffer.len() { + buffer.resize(nread, 0_u8); + return Ok(CString::new(buffer).unwrap()); + } + // Use `Vec` reallocation strategy to grow capacity exponentially. + buffer.reserve(1); + buffer.resize(buffer.capacity(), 0_u8); + } +} + +/// `rename(old_path, new_path)`—Renames a file or directory. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/rename.html +/// [Linux]: https://man7.org/linux/man-pages/man2/rename.2.html +#[inline] +pub fn rename<P: path::Arg, Q: path::Arg>(old_path: P, new_path: Q) -> io::Result<()> { + old_path.into_with_c_str(|old_path| { + new_path.into_with_c_str(|new_path| backend::fs::syscalls::rename(old_path, new_path)) + }) +} + +/// `unlink(path)`—Unlinks a file. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/unlink.html +/// [Linux]: https://man7.org/linux/man-pages/man2/unlink.2.html +#[inline] +pub fn unlink<P: path::Arg>(path: P) -> io::Result<()> { + path.into_with_c_str(backend::fs::syscalls::unlink) +} + +/// `rmdir(path)`—Removes a directory. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/rmdir.html +/// [Linux]: https://man7.org/linux/man-pages/man2/rmdir.2.html +#[inline] +pub fn rmdir<P: path::Arg>(path: P) -> io::Result<()> { + path.into_with_c_str(backend::fs::syscalls::rmdir) +} + +/// `link(old_path, new_path)`—Creates a hard link. +/// +/// POSIX leaves it implementation-defined whether `link` follows a symlink in +/// `old_path`, or creates a new link to the symbolic link itself. On platforms +/// which have it, [`linkat`] avoids this problem since it has an [`AtFlags`] +/// parameter and the [`AtFlags::SYMLINK_FOLLOW`] flag determines whether +/// symlinks should be followed. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/link.html +/// [Linux]: https://man7.org/linux/man-pages/man2/link.2.html +/// [`linkat`]: crate::fs::linkat +/// [`AtFlags`]: crate::fs::AtFlags +/// [`AtFlags::SYMLINK_FOLLOW`]: crate::fs::AtFlags::SYMLINK_FOLLOW +#[inline] +pub fn link<P: path::Arg, Q: path::Arg>(old_path: P, new_path: Q) -> io::Result<()> { + old_path.into_with_c_str(|old_path| { + new_path.into_with_c_str(|new_path| backend::fs::syscalls::link(old_path, new_path)) + }) +} + +/// `symlink(old_path, new_path)`—Creates a symlink. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/symlink.html +/// [Linux]: https://man7.org/linux/man-pages/man2/symlink.2.html +#[inline] +pub fn symlink<P: path::Arg, Q: path::Arg>(old_path: P, new_path: Q) -> io::Result<()> { + old_path.into_with_c_str(|old_path| { + new_path.into_with_c_str(|new_path| backend::fs::syscalls::symlink(old_path, new_path)) + }) +} + +/// `mkdir(path, mode)`—Creates a directory. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdir.html +/// [Linux]: https://man7.org/linux/man-pages/man2/mkdir.2.html +#[inline] +pub fn mkdir<P: path::Arg>(path: P, mode: Mode) -> io::Result<()> { + path.into_with_c_str(|path| backend::fs::syscalls::mkdir(path, mode)) +} + +/// `access(path, access)`—Tests permissions for a file or directory. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/access.html +/// [Linux]: https://man7.org/linux/man-pages/man2/access.2.html +#[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] +#[inline] +pub fn access<P: path::Arg>(path: P, access: Access) -> io::Result<()> { + path.into_with_c_str(|path| backend::fs::syscalls::access(path, access)) +} + +/// `statfs`—Queries filesystem metadata. +/// +/// Compared to [`statvfs`], this function often provides more information, +/// though it's less portable. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/statfs.2.html +#[cfg(not(any( + solarish, + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "netbsd", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +#[inline] +pub fn statfs<P: path::Arg>(path: P) -> io::Result<StatFs> { + path.into_with_c_str(backend::fs::syscalls::statfs) +} + +/// `statvfs`—Queries filesystem metadata, POSIX version. +/// +/// Compared to [`statfs`], this function often provides less information, but +/// it is more portable. But even so, filesystems are very diverse and not all +/// the fields are meaningful for every filesystem. And `f_fsid` doesn't seem +/// to have a clear meaning anywhere. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/statvfs.html +/// [Linux]: https://man7.org/linux/man-pages/man2/statvfs.2.html +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[inline] +pub fn statvfs<P: path::Arg>(path: P) -> io::Result<StatVfs> { + path.into_with_c_str(backend::fs::syscalls::statvfs) +} + +/// `chown(path, owner, group)`—Sets open file or directory ownership. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/chown.html +/// [Linux]: https://man7.org/linux/man-pages/man2/chown.2.html +#[cfg(not(target_os = "wasi"))] +#[inline] +pub fn chown<P: path::Arg>(path: P, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> { + path.into_with_c_str(|path| backend::fs::syscalls::chown(path, owner, group)) +} diff --git a/vendor/rustix/src/fs/at.rs b/vendor/rustix/src/fs/at.rs new file mode 100644 index 00000000..4ad2df89 --- /dev/null +++ b/vendor/rustix/src/fs/at.rs @@ -0,0 +1,494 @@ +//! POSIX-style `*at` functions. +//! +//! The `dirfd` argument to these functions may be a file descriptor for a +//! directory, the special value [`CWD`], or the special value [`ABS`]. +//! +//! [`CWD`]: crate::fs::CWD +//! [`ABS`]: crate::fs::ABS + +#![allow(unsafe_code)] + +use crate::buffer::Buffer; +use crate::fd::OwnedFd; +#[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] +use crate::fs::Access; +#[cfg(not(target_os = "espidf"))] +use crate::fs::AtFlags; +#[cfg(apple)] +use crate::fs::CloneFlags; +#[cfg(any(linux_kernel, apple))] +use crate::fs::RenameFlags; +#[cfg(not(target_os = "espidf"))] +use crate::fs::Stat; +#[cfg(not(any(apple, target_os = "espidf", target_os = "vita", target_os = "wasi")))] +use crate::fs::{Dev, FileType}; +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +use crate::fs::{Gid, Uid}; +use crate::fs::{Mode, OFlags}; +use crate::{backend, io, path}; +use backend::fd::AsFd; +#[cfg(feature = "alloc")] +use { + crate::ffi::{CStr, CString}, + crate::path::SMALL_PATH_BUFFER_SIZE, + alloc::vec::Vec, + backend::fd::BorrowedFd, +}; +#[cfg(not(any(target_os = "espidf", target_os = "vita")))] +use {crate::fs::Timestamps, crate::timespec::Nsecs}; + +/// `UTIME_NOW` for use with [`utimensat`]. +/// +/// [`utimensat`]: crate::fs::utimensat +#[cfg(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "redox", + target_os = "vita" +)))] +pub const UTIME_NOW: Nsecs = backend::c::UTIME_NOW as Nsecs; + +/// `UTIME_OMIT` for use with [`utimensat`]. +/// +/// [`utimensat`]: crate::fs::utimensat +#[cfg(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "redox", + target_os = "vita" +)))] +pub const UTIME_OMIT: Nsecs = backend::c::UTIME_OMIT as Nsecs; + +/// `openat(dirfd, path, oflags, mode)`—Opens a file. +/// +/// POSIX guarantees that `openat` will use the lowest unused file descriptor, +/// however it is not safe in general to rely on this, as file descriptors may +/// be unexpectedly allocated on other threads or in libraries. +/// +/// The `Mode` argument is only significant when creating a file. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/openat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/openat.2.html +#[inline] +pub fn openat<P: path::Arg, Fd: AsFd>( + dirfd: Fd, + path: P, + oflags: OFlags, + create_mode: Mode, +) -> io::Result<OwnedFd> { + path.into_with_c_str(|path| { + backend::fs::syscalls::openat(dirfd.as_fd(), path, oflags, create_mode) + }) +} + +/// `readlinkat(fd, path)`—Reads the contents of a symlink. +/// +/// If `reuse` already has available capacity, reuse it if possible. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/readlinkat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/readlinkat.2.html +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +#[inline] +pub fn readlinkat<P: path::Arg, Fd: AsFd, B: Into<Vec<u8>>>( + dirfd: Fd, + path: P, + reuse: B, +) -> io::Result<CString> { + path.into_with_c_str(|path| _readlinkat(dirfd.as_fd(), path, reuse.into())) +} + +#[cfg(feature = "alloc")] +#[allow(unsafe_code)] +fn _readlinkat(dirfd: BorrowedFd<'_>, path: &CStr, mut buffer: Vec<u8>) -> io::Result<CString> { + buffer.clear(); + buffer.reserve(SMALL_PATH_BUFFER_SIZE); + + loop { + let buf = buffer.spare_capacity_mut(); + + // SAFETY: `readlinkat` behaves. + let nread = unsafe { + backend::fs::syscalls::readlinkat( + dirfd.as_fd(), + path, + (buf.as_mut_ptr().cast(), buf.len()), + )? + }; + + debug_assert!(nread <= buffer.capacity()); + if nread < buffer.capacity() { + // SAFETY: From the [documentation]: “On success, these calls + // return the number of bytes placed in buf.” + // + // [documentation]: https://man7.org/linux/man-pages/man2/readlinkat.2.html + unsafe { + buffer.set_len(nread); + } + + // SAFETY: + // - “readlink places the contents of the symbolic link pathname + // in the buffer buf” + // - [POSIX definition 3.271: Pathname]: “A string that is used + // to identify a file.” + // - [POSIX definition 3.375: String]: “A contiguous sequence of + // bytes terminated by and including the first null byte.” + // - “readlink does not append a terminating null byte to buf.” + // + // Thus, there will be no NUL bytes in the string. + // + // [POSIX definition 3.271: Pathname]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap03.html#tag_03_271 + // [POSIX definition 3.375: String]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap03.html#tag_03_375 + unsafe { + return Ok(CString::from_vec_unchecked(buffer)); + } + } + + // Use `Vec` reallocation strategy to grow capacity exponentially. + buffer.reserve(buffer.capacity() + 1); + } +} + +/// `readlinkat(fd, path)`—Reads the contents of a symlink, without +/// allocating. +/// +/// This is the "raw" version which avoids allocating, but which truncates the +/// string if it doesn't fit in the provided buffer, and doesn't NUL-terminate +/// the string. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/readlinkat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/readlinkat.2.html +#[inline] +pub fn readlinkat_raw<P: path::Arg, Fd: AsFd, Buf: Buffer<u8>>( + dirfd: Fd, + path: P, + mut buf: Buf, +) -> io::Result<Buf::Output> { + // SAFETY: `readlinkat` behaves. + let len = path.into_with_c_str(|path| unsafe { + backend::fs::syscalls::readlinkat(dirfd.as_fd(), path, buf.parts_mut()) + })?; + // SAFETY: `readlinkat` behaves. + unsafe { Ok(buf.assume_init(len)) } +} + +/// `mkdirat(fd, path, mode)`—Creates a directory. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdirat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/mkdirat.2.html +#[inline] +pub fn mkdirat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, mode: Mode) -> io::Result<()> { + path.into_with_c_str(|path| backend::fs::syscalls::mkdirat(dirfd.as_fd(), path, mode)) +} + +/// `linkat(old_dirfd, old_path, new_dirfd, new_path, flags)`—Creates a hard +/// link. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/linkat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/linkat.2.html +#[cfg(not(target_os = "espidf"))] +#[inline] +pub fn linkat<P: path::Arg, Q: path::Arg, PFd: AsFd, QFd: AsFd>( + old_dirfd: PFd, + old_path: P, + new_dirfd: QFd, + new_path: Q, + flags: AtFlags, +) -> io::Result<()> { + old_path.into_with_c_str(|old_path| { + new_path.into_with_c_str(|new_path| { + backend::fs::syscalls::linkat( + old_dirfd.as_fd(), + old_path, + new_dirfd.as_fd(), + new_path, + flags, + ) + }) + }) +} + +/// `unlinkat(fd, path, flags)`—Unlinks a file or remove a directory. +/// +/// With the [`REMOVEDIR`] flag, this removes a directory. This is in place of +/// a `rmdirat` function. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [`REMOVEDIR`]: AtFlags::REMOVEDIR +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/unlinkat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/unlinkat.2.html +#[cfg(not(target_os = "espidf"))] +#[inline] +pub fn unlinkat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, flags: AtFlags) -> io::Result<()> { + path.into_with_c_str(|path| backend::fs::syscalls::unlinkat(dirfd.as_fd(), path, flags)) +} + +/// `renameat(old_dirfd, old_path, new_dirfd, new_path)`—Renames a file or +/// directory. +/// +/// See [`renameat_with`] to pass additional flags. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/renameat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/renameat.2.html +#[inline] +pub fn renameat<P: path::Arg, Q: path::Arg, PFd: AsFd, QFd: AsFd>( + old_dirfd: PFd, + old_path: P, + new_dirfd: QFd, + new_path: Q, +) -> io::Result<()> { + old_path.into_with_c_str(|old_path| { + new_path.into_with_c_str(|new_path| { + backend::fs::syscalls::renameat( + old_dirfd.as_fd(), + old_path, + new_dirfd.as_fd(), + new_path, + ) + }) + }) +} + +/// `renameat2(old_dirfd, old_path, new_dirfd, new_path, flags)`—Renames a +/// file or directory. +/// +/// `renameat_with` is the same as [`renameat`] but adds an additional +/// flags operand. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/renameat2.2.html +#[cfg(any(apple, linux_kernel))] +#[inline] +#[doc(alias = "renameat2")] +#[doc(alias = "renameatx_np")] +pub fn renameat_with<P: path::Arg, Q: path::Arg, PFd: AsFd, QFd: AsFd>( + old_dirfd: PFd, + old_path: P, + new_dirfd: QFd, + new_path: Q, + flags: RenameFlags, +) -> io::Result<()> { + old_path.into_with_c_str(|old_path| { + new_path.into_with_c_str(|new_path| { + backend::fs::syscalls::renameat2( + old_dirfd.as_fd(), + old_path, + new_dirfd.as_fd(), + new_path, + flags, + ) + }) + }) +} + +/// `symlinkat(old_path, new_dirfd, new_path)`—Creates a symlink. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/symlinkat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/symlinkat.2.html +#[inline] +pub fn symlinkat<P: path::Arg, Q: path::Arg, Fd: AsFd>( + old_path: P, + new_dirfd: Fd, + new_path: Q, +) -> io::Result<()> { + old_path.into_with_c_str(|old_path| { + new_path.into_with_c_str(|new_path| { + backend::fs::syscalls::symlinkat(old_path, new_dirfd.as_fd(), new_path) + }) + }) +} + +/// `fstatat(dirfd, path, flags)`—Queries metadata for a file or directory. +/// +/// [`Mode::from_raw_mode`] and [`FileType::from_raw_mode`] may be used to +/// interpret the `st_mode` field. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fstatat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/fstatat.2.html +/// [`Mode::from_raw_mode`]: crate::fs::Mode::from_raw_mode +/// [`FileType::from_raw_mode`]: crate::fs::FileType::from_raw_mode +#[cfg(not(target_os = "espidf"))] +#[inline] +#[doc(alias = "fstatat")] +pub fn statat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, flags: AtFlags) -> io::Result<Stat> { + path.into_with_c_str(|path| backend::fs::syscalls::statat(dirfd.as_fd(), path, flags)) +} + +/// `faccessat(dirfd, path, access, flags)`—Tests permissions for a file or +/// directory. +/// +/// On Linux before 5.8, this function uses the `faccessat` system call which +/// doesn't support any flags. This function emulates support for the +/// [`AtFlags::EACCESS`] flag by checking whether the uid and gid of the +/// process match the effective uid and gid, in which case the `EACCESS` flag +/// can be ignored. In Linux 5.8 and beyond `faccessat2` is used, which +/// supports flags. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/faccessat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/faccessat.2.html +#[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] +#[inline] +#[doc(alias = "faccessat")] +pub fn accessat<P: path::Arg, Fd: AsFd>( + dirfd: Fd, + path: P, + access: Access, + flags: AtFlags, +) -> io::Result<()> { + path.into_with_c_str(|path| backend::fs::syscalls::accessat(dirfd.as_fd(), path, access, flags)) +} + +/// `utimensat(dirfd, path, times, flags)`—Sets file or directory timestamps. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/utimensat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/utimensat.2.html +#[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] +#[inline] +pub fn utimensat<P: path::Arg, Fd: AsFd>( + dirfd: Fd, + path: P, + times: &Timestamps, + flags: AtFlags, +) -> io::Result<()> { + path.into_with_c_str(|path| backend::fs::syscalls::utimensat(dirfd.as_fd(), path, times, flags)) +} + +/// `fchmodat(dirfd, path, mode, flags)`—Sets file or directory permissions. +/// +/// Platform support for flags varies widely, for example on Linux +/// [`AtFlags::SYMLINK_NOFOLLOW`] is not implemented and therefore +/// [`io::Errno::OPNOTSUPP`] will be returned. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fchmodat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/fchmodat.2.html +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +#[inline] +#[doc(alias = "fchmodat")] +pub fn chmodat<P: path::Arg, Fd: AsFd>( + dirfd: Fd, + path: P, + mode: Mode, + flags: AtFlags, +) -> io::Result<()> { + path.into_with_c_str(|path| backend::fs::syscalls::chmodat(dirfd.as_fd(), path, mode, flags)) +} + +/// `fclonefileat(src, dst_dir, dst, flags)`—Efficiently copies between files. +/// +/// # References +/// - [Apple] +/// +/// [Apple]: https://github.com/apple-oss-distributions/xnu/blob/main/bsd/man/man2/clonefile.2 +#[cfg(apple)] +#[inline] +pub fn fclonefileat<Fd: AsFd, DstFd: AsFd, P: path::Arg>( + src: Fd, + dst_dir: DstFd, + dst: P, + flags: CloneFlags, +) -> io::Result<()> { + dst.into_with_c_str(|dst| { + backend::fs::syscalls::fclonefileat(src.as_fd(), dst_dir.as_fd(), dst, flags) + }) +} + +/// `mknodat(dirfd, path, mode, dev)`—Creates special or normal files. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/mknodat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/mknodat.2.html +#[cfg(not(any( + apple, + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +pub fn mknodat<P: path::Arg, Fd: AsFd>( + dirfd: Fd, + path: P, + file_type: FileType, + mode: Mode, + dev: Dev, +) -> io::Result<()> { + path.into_with_c_str(|path| { + backend::fs::syscalls::mknodat(dirfd.as_fd(), path, file_type, mode, dev) + }) +} + +/// `fchownat(dirfd, path, owner, group, flags)`—Sets file or directory +/// ownership. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fchownat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/fchownat.2.html +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +#[inline] +#[doc(alias = "fchownat")] +pub fn chownat<P: path::Arg, Fd: AsFd>( + dirfd: Fd, + path: P, + owner: Option<Uid>, + group: Option<Gid>, + flags: AtFlags, +) -> io::Result<()> { + path.into_with_c_str(|path| { + backend::fs::syscalls::chownat(dirfd.as_fd(), path, owner, group, flags) + }) +} diff --git a/vendor/rustix/src/fs/constants.rs b/vendor/rustix/src/fs/constants.rs new file mode 100644 index 00000000..5f106342 --- /dev/null +++ b/vendor/rustix/src/fs/constants.rs @@ -0,0 +1,355 @@ +//! Filesystem API constants, translated into `bitflags` constants. + +use crate::backend; + +pub use crate::timespec::{Nsecs, Secs, Timespec}; +pub use backend::fs::types::*; + +impl FileType { + /// Returns `true` if this `FileType` is a regular file. + pub fn is_file(self) -> bool { + self == Self::RegularFile + } + + /// Returns `true` if this `FileType` is a directory. + pub fn is_dir(self) -> bool { + self == Self::Directory + } + + /// Returns `true` if this `FileType` is a symlink. + pub fn is_symlink(self) -> bool { + self == Self::Symlink + } + + /// Returns `true` if this `FileType` is a fifo. + #[cfg(not(target_os = "wasi"))] + pub fn is_fifo(self) -> bool { + self == Self::Fifo + } + + /// Returns `true` if this `FileType` is a socket. + #[cfg(not(target_os = "wasi"))] + pub fn is_socket(self) -> bool { + self == Self::Socket + } + + /// Returns `true` if this `FileType` is a character device. + pub fn is_char_device(self) -> bool { + self == Self::CharacterDevice + } + + /// Returns `true` if this `FileType` is a block device. + pub fn is_block_device(self) -> bool { + self == Self::BlockDevice + } +} + +#[cfg(test)] +#[allow(unused_imports)] +#[allow(unsafe_code)] +mod tests { + use super::*; + use crate::backend::c; + // Rust's libc crate lacks statx for Non-glibc targets. + #[cfg(all(target_os = "linux", target_env = "gnu"))] + use crate::fs::{Statx, StatxTimestamp}; + + #[test] + fn test_layouts() { + #[cfg(linux_kernel)] + assert_eq_size!(FsWord, linux_raw_sys::general::__fsword_t); + + // Don't test against `__kernel_mode_t` on platforms where it's a + // `u16`. + #[cfg(linux_kernel)] + #[cfg(not(any( + target_arch = "x86", + target_arch = "sparc", + target_arch = "avr", + target_arch = "arm", + )))] + assert_eq_size!(RawMode, linux_raw_sys::general::__kernel_mode_t); + + #[cfg(linux_kernel)] + #[cfg(any( + target_arch = "x86", + target_arch = "sparc", + target_arch = "avr", + target_arch = "arm", + ))] + assert_eq_size!(u16, linux_raw_sys::general::__kernel_mode_t); + + let some_stat: Stat = unsafe { core::mem::zeroed() }; + + // Ensure that seconds fields are 64-bit on non-y2038-bug platforms, and + // on Linux where we use statx. + #[cfg(any(linux_kernel, not(fix_y2038)))] + { + assert_eq!(some_stat.st_atime, 0_i64); + assert_eq!(some_stat.st_mtime, 0_i64); + assert_eq!(some_stat.st_ctime, 0_i64); + } + + // Ensure that file offsets are 64-bit. + assert_eq!(some_stat.st_size, 0_i64); + + // Check that various fields match expected types. + assert_eq!(some_stat.st_mode, 0 as RawMode); + assert_eq!(some_stat.st_dev, 0 as Dev); + assert_eq!(some_stat.st_rdev, 0 as Dev); + assert_eq!(some_stat.st_uid, 0 as crate::ugid::RawUid); + assert_eq!(some_stat.st_gid, 0 as crate::ugid::RawGid); + + // `Stat` should match `c::stat` or `c::stat64` unless we need y2038 + // fixes and are using a different layout. + #[cfg(not(any( + all(libc, linux_kernel, target_pointer_width = "32"), + all( + linux_raw, + any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ) + ) + )))] + { + // Check that `Stat` matches `c::stat`. + #[cfg(not(all( + libc, + any( + all(linux_kernel, target_pointer_width = "64"), + target_os = "hurd", + target_os = "emscripten", + target_os = "l4re", + ) + )))] + { + check_renamed_type!(Stat, stat); + check_renamed_struct_field!(Stat, stat, st_dev); + check_renamed_struct_field!(Stat, stat, st_ino); + check_renamed_struct_field!(Stat, stat, st_nlink); + check_renamed_struct_field!(Stat, stat, st_mode); + check_renamed_struct_field!(Stat, stat, st_uid); + check_renamed_struct_field!(Stat, stat, st_gid); + #[cfg(all( + linux_raw, + not(any( + target_arch = "aarch64", + target_arch = "powerpc64", + target_arch = "riscv64", + target_arch = "s390x" + )) + ))] + check_renamed_struct_field!(Stat, stat, __pad0); + check_renamed_struct_field!(Stat, stat, st_rdev); + #[cfg(all(linux_raw, not(any(target_arch = "powerpc64", target_arch = "x86_64"))))] + check_renamed_struct_field!(Stat, stat, __pad1); + check_renamed_struct_field!(Stat, stat, st_size); + check_renamed_struct_field!(Stat, stat, st_blksize); + #[cfg(all( + linux_raw, + not(any( + target_arch = "powerpc64", + target_arch = "s390x", + target_arch = "x86_64" + )) + ))] + check_renamed_struct_field!(Stat, stat, __pad2); + check_renamed_struct_field!(Stat, stat, st_blocks); + check_renamed_struct_field!(Stat, stat, st_atime); + #[cfg(not(target_os = "netbsd"))] + check_renamed_struct_field!(Stat, stat, st_atime_nsec); + #[cfg(target_os = "netbsd")] + check_renamed_struct_renamed_field!(Stat, stat, st_atime_nsec, st_atimensec); + check_renamed_struct_field!(Stat, stat, st_mtime); + #[cfg(not(target_os = "netbsd"))] + check_renamed_struct_field!(Stat, stat, st_mtime_nsec); + #[cfg(target_os = "netbsd")] + check_renamed_struct_renamed_field!(Stat, stat, st_mtime_nsec, st_mtimensec); + check_renamed_struct_field!(Stat, stat, st_ctime); + #[cfg(not(target_os = "netbsd"))] + check_renamed_struct_field!(Stat, stat, st_ctime_nsec); + #[cfg(target_os = "netbsd")] + check_renamed_struct_renamed_field!(Stat, stat, st_ctime_nsec, st_ctimensec); + #[cfg(all( + linux_raw, + not(any( + target_arch = "aarch64", + target_arch = "powerpc64", + target_arch = "riscv64" + )) + ))] + check_renamed_struct_field!(Stat, stat, __unused); + #[cfg(all(linux_raw, not(any(target_arch = "s390x", target_arch = "x86_64"))))] + check_renamed_struct_field!(Stat, stat, __unused4); + #[cfg(all(linux_raw, not(any(target_arch = "s390x", target_arch = "x86_64"))))] + check_renamed_struct_field!(Stat, stat, __unused5); + #[cfg(all( + linux_raw, + not(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "x86_64" + )) + ))] + check_renamed_struct_field!(Stat, stat, __unused6); + } + + // Check that `Stat` matches `c::stat64`. + #[cfg(all( + libc, + any( + all(linux_kernel, target_pointer_width = "64"), + target_os = "hurd", + target_os = "emscripten", + target_os = "l4re", + ) + ))] + { + check_renamed_type!(Stat, stat64); + check_renamed_struct_field!(Stat, stat64, st_dev); + check_renamed_struct_field!(Stat, stat64, st_ino); + check_renamed_struct_field!(Stat, stat64, st_nlink); + check_renamed_struct_field!(Stat, stat64, st_mode); + check_renamed_struct_field!(Stat, stat64, st_uid); + check_renamed_struct_field!(Stat, stat64, st_gid); + #[cfg(all( + linux_raw, + not(any( + target_arch = "aarch64", + target_arch = "powerpc64", + target_arch = "riscv64", + target_arch = "s390x" + )) + ))] + check_renamed_struct_field!(Stat, stat64, __pad0); + check_renamed_struct_field!(Stat, stat64, st_rdev); + #[cfg(all(linux_raw, not(any(target_arch = "powerpc64", target_arch = "x86_64"))))] + check_renamed_struct_field!(Stat, stat64, __pad1); + check_renamed_struct_field!(Stat, stat64, st_size); + check_renamed_struct_field!(Stat, stat64, st_blksize); + #[cfg(all( + linux_raw, + not(any( + target_arch = "powerpc64", + target_arch = "s390x", + target_arch = "x86_64" + )) + ))] + check_renamed_struct_field!(Stat, stat64, __pad2); + check_renamed_struct_field!(Stat, stat64, st_blocks); + check_renamed_struct_field!(Stat, stat64, st_atime); + check_renamed_struct_field!(Stat, stat64, st_atime_nsec); + check_renamed_struct_field!(Stat, stat64, st_mtime); + check_renamed_struct_field!(Stat, stat64, st_mtime_nsec); + check_renamed_struct_field!(Stat, stat64, st_ctime); + check_renamed_struct_field!(Stat, stat64, st_ctime_nsec); + #[cfg(all( + linux_raw, + not(any( + target_arch = "aarch64", + target_arch = "powerpc64", + target_arch = "riscv64" + )) + ))] + check_renamed_struct_field!(Stat, stat64, __unused); + #[cfg(all(linux_raw, not(any(target_arch = "s390x", target_arch = "x86_64"))))] + check_renamed_struct_field!(Stat, stat64, __unused4); + #[cfg(all(linux_raw, not(any(target_arch = "s390x", target_arch = "x86_64"))))] + check_renamed_struct_field!(Stat, stat64, __unused5); + #[cfg(all( + linux_raw, + not(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "x86_64" + )) + ))] + check_renamed_struct_field!(Stat, stat64, __unused6); + } + } + + #[cfg(not(any( + solarish, + target_os = "cygwin", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + target_os = "wasi", + )))] + { + check_renamed_type!(Fsid, fsid_t); + #[cfg(not(libc))] // libc hides the `val` field + check_renamed_struct_field!(Fsid, fsid_t, val); + } + + #[cfg(linux_like)] + { + check_renamed_type!(StatFs, statfs64); + check_renamed_struct_field!(StatFs, statfs64, f_type); + check_renamed_struct_field!(StatFs, statfs64, f_bsize); + check_renamed_struct_field!(StatFs, statfs64, f_blocks); + check_renamed_struct_field!(StatFs, statfs64, f_bfree); + check_renamed_struct_field!(StatFs, statfs64, f_bavail); + check_renamed_struct_field!(StatFs, statfs64, f_files); + check_renamed_struct_field!(StatFs, statfs64, f_ffree); + check_renamed_struct_field!(StatFs, statfs64, f_fsid); + check_renamed_struct_field!(StatFs, statfs64, f_namelen); + check_renamed_struct_field!(StatFs, statfs64, f_frsize); + check_renamed_struct_field!(StatFs, statfs64, f_flags); + #[cfg(linux_raw)] + check_renamed_struct_field!(StatFs, statfs64, f_spare); + } + + // Rust's libc crate lacks statx for Non-glibc targets. + #[cfg(all(target_os = "linux", target_env = "gnu"))] + { + check_renamed_type!(StatxTimestamp, statx_timestamp); + check_renamed_struct_field!(StatxTimestamp, statx_timestamp, tv_sec); + check_renamed_struct_field!(StatxTimestamp, statx_timestamp, tv_nsec); + #[cfg(linux_raw)] + check_renamed_struct_field!(StatxTimestamp, statx_timestamp, __reserved); + + check_renamed_type!(Statx, statx); + check_renamed_struct_field!(Statx, statx, stx_mask); + check_renamed_struct_field!(Statx, statx, stx_blksize); + check_renamed_struct_field!(Statx, statx, stx_attributes); + check_renamed_struct_field!(Statx, statx, stx_nlink); + check_renamed_struct_field!(Statx, statx, stx_uid); + check_renamed_struct_field!(Statx, statx, stx_gid); + check_renamed_struct_field!(Statx, statx, stx_mode); + #[cfg(linux_raw)] + check_renamed_struct_field!(Statx, statx, __spare0); + check_renamed_struct_field!(Statx, statx, stx_ino); + check_renamed_struct_field!(Statx, statx, stx_size); + check_renamed_struct_field!(Statx, statx, stx_blocks); + check_renamed_struct_field!(Statx, statx, stx_attributes_mask); + check_renamed_struct_field!(Statx, statx, stx_atime); + check_renamed_struct_field!(Statx, statx, stx_btime); + check_renamed_struct_field!(Statx, statx, stx_ctime); + check_renamed_struct_field!(Statx, statx, stx_mtime); + check_renamed_struct_field!(Statx, statx, stx_rdev_major); + check_renamed_struct_field!(Statx, statx, stx_rdev_minor); + check_renamed_struct_field!(Statx, statx, stx_dev_major); + check_renamed_struct_field!(Statx, statx, stx_dev_minor); + check_renamed_struct_field!(Statx, statx, stx_mnt_id); + check_renamed_struct_field!(Statx, statx, stx_dio_mem_align); + check_renamed_struct_field!(Statx, statx, stx_dio_offset_align); + #[cfg(not(libc))] // not in libc yet + check_renamed_struct_field!(Statx, statx, stx_subvol); + #[cfg(not(libc))] // not in libc yet + check_renamed_struct_field!(Statx, statx, stx_atomic_write_unit_min); + #[cfg(not(libc))] // not in libc yet + check_renamed_struct_field!(Statx, statx, stx_atomic_write_unit_max); + #[cfg(not(libc))] // not in libc yet + check_renamed_struct_field!(Statx, statx, stx_atomic_write_segments_max); + #[cfg(linux_raw)] + check_renamed_struct_field!(Statx, statx, __spare1); + #[cfg(linux_raw)] + check_renamed_struct_field!(Statx, statx, __spare3); + } + } +} diff --git a/vendor/rustix/src/fs/copy_file_range.rs b/vendor/rustix/src/fs/copy_file_range.rs new file mode 100644 index 00000000..b927d572 --- /dev/null +++ b/vendor/rustix/src/fs/copy_file_range.rs @@ -0,0 +1,20 @@ +use crate::{backend, io}; +use backend::fd::AsFd; + +/// `copy_file_range(fd_in, off_in, fd_out, off_out, len, 0)`—Copies data +/// from one file to another. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/copy_file_range.2.html +#[inline] +pub fn copy_file_range<InFd: AsFd, OutFd: AsFd>( + fd_in: InFd, + off_in: Option<&mut u64>, + fd_out: OutFd, + off_out: Option<&mut u64>, + len: usize, +) -> io::Result<usize> { + backend::fs::syscalls::copy_file_range(fd_in.as_fd(), off_in, fd_out.as_fd(), off_out, len) +} diff --git a/vendor/rustix/src/fs/dir.rs b/vendor/rustix/src/fs/dir.rs new file mode 100644 index 00000000..b3e1e3b9 --- /dev/null +++ b/vendor/rustix/src/fs/dir.rs @@ -0,0 +1,5 @@ +//! `Dir` and `DirEntry`. + +use crate::backend; + +pub use backend::fs::dir::{Dir, DirEntry}; diff --git a/vendor/rustix/src/fs/fadvise.rs b/vendor/rustix/src/fs/fadvise.rs new file mode 100644 index 00000000..93d8e081 --- /dev/null +++ b/vendor/rustix/src/fs/fadvise.rs @@ -0,0 +1,28 @@ +use crate::{backend, io}; +use backend::fd::AsFd; +use backend::fs::types::Advice; +use core::num::NonZeroU64; + +/// `posix_fadvise(fd, offset, len, advice)`—Declares an expected access +/// pattern for a file. +/// +/// If `len` is `None`, the advice extends to the end of the file. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [FreeBSD] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_fadvise.html +/// [Linux]: https://man7.org/linux/man-pages/man2/posix_fadvise.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=posix_fadvise&sektion=2 +#[inline] +#[doc(alias = "posix_fadvise")] +pub fn fadvise<Fd: AsFd>( + fd: Fd, + offset: u64, + len: Option<NonZeroU64>, + advice: Advice, +) -> io::Result<()> { + backend::fs::syscalls::fadvise(fd.as_fd(), offset, len, advice) +} diff --git a/vendor/rustix/src/fs/fcntl.rs b/vendor/rustix/src/fs/fcntl.rs new file mode 100644 index 00000000..ba5368c8 --- /dev/null +++ b/vendor/rustix/src/fs/fcntl.rs @@ -0,0 +1,107 @@ +//! The Unix `fcntl` function is effectively lots of different functions hidden +//! behind a single dynamic dispatch interface. In order to provide a type-safe +//! API, rustix makes them all separate functions so that they can have +//! dedicated static type signatures. + +#[cfg(not(any( + target_os = "emscripten", + target_os = "espidf", + target_os = "fuchsia", + target_os = "horizon", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +use crate::fs::FlockOperation; +use crate::{backend, io}; +use backend::fd::AsFd; +use backend::fs::types::OFlags; + +/// `fcntl(fd, F_GETFL)`—Returns a file descriptor's access mode and status. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fcntl.html +/// [Linux]: https://man7.org/linux/man-pages/man2/fcntl.2.html +#[inline] +#[doc(alias = "F_GETFL")] +pub fn fcntl_getfl<Fd: AsFd>(fd: Fd) -> io::Result<OFlags> { + backend::fs::syscalls::fcntl_getfl(fd.as_fd()) +} + +/// `fcntl(fd, F_SETFL, flags)`—Sets a file descriptor's status. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fcntl.html +/// [Linux]: https://man7.org/linux/man-pages/man2/fcntl.2.html +#[inline] +#[doc(alias = "F_SETFL")] +pub fn fcntl_setfl<Fd: AsFd>(fd: Fd, flags: OFlags) -> io::Result<()> { + backend::fs::syscalls::fcntl_setfl(fd.as_fd(), flags) +} + +/// `fcntl(fd, F_GET_SEALS)`—Return the seals for `fd`'s inode. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/fcntl.2.html +#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))] +#[inline] +#[doc(alias = "F_GET_SEALS")] +pub fn fcntl_get_seals<Fd: AsFd>(fd: Fd) -> io::Result<SealFlags> { + backend::fs::syscalls::fcntl_get_seals(fd.as_fd()) +} + +#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))] +use backend::fs::types::SealFlags; + +/// `fcntl(fd, F_ADD_SEALS)`—Add seals to `fd`'s inode. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/fcntl.2.html +#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))] +#[inline] +#[doc(alias = "F_ADD_SEALS")] +pub fn fcntl_add_seals<Fd: AsFd>(fd: Fd, seals: SealFlags) -> io::Result<()> { + backend::fs::syscalls::fcntl_add_seals(fd.as_fd(), seals) +} + +/// `fcntl(fd, F_SETLK)`—Acquire or release an `fcntl`-style lock. +/// +/// This function doesn't currently have an offset or len; it currently always +/// sets the `l_len` field to 0, which is a special case that means the entire +/// file should be locked. +/// +/// Unlike `flock`-style locks, `fcntl`-style locks are process-associated, +/// meaning that they don't guard against being acquired by two threads in the +/// same process. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fcntl.html +/// [Linux]: https://man7.org/linux/man-pages/man2/fcntl.2.html +#[cfg(not(any( + target_os = "emscripten", + target_os = "espidf", + target_os = "fuchsia", + target_os = "horizon", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +#[doc(alias = "F_SETLK")] +#[doc(alias = "F_SETLKW")] +pub fn fcntl_lock<Fd: AsFd>(fd: Fd, operation: FlockOperation) -> io::Result<()> { + backend::fs::syscalls::fcntl_lock(fd.as_fd(), operation) +} diff --git a/vendor/rustix/src/fs/fcntl_apple.rs b/vendor/rustix/src/fs/fcntl_apple.rs new file mode 100644 index 00000000..a32e46d7 --- /dev/null +++ b/vendor/rustix/src/fs/fcntl_apple.rs @@ -0,0 +1,66 @@ +use crate::{backend, io}; +use backend::fd::AsFd; + +/// `fcntl(fd, F_RDADVISE, radvisory { offset, len })` +/// +/// # References +/// - [Apple] +/// +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html +#[doc(alias = "F_RDADVISE")] +#[inline] +pub fn fcntl_rdadvise<Fd: AsFd>(fd: Fd, offset: u64, len: u64) -> io::Result<()> { + backend::fs::syscalls::fcntl_rdadvise(fd.as_fd(), offset, len) +} + +/// `fcntl(fd, F_FULLFSYNC)` +/// +/// # References +/// - [Apple] +/// +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html +#[doc(alias = "F_FULLSYNC")] +#[inline] +pub fn fcntl_fullfsync<Fd: AsFd>(fd: Fd) -> io::Result<()> { + backend::fs::syscalls::fcntl_fullfsync(fd.as_fd()) +} + +/// `fcntl(fd, F_NOCACHE, value)`—Turn data caching off or on for a file +/// descriptor. +/// +/// See [this mailing list post] for additional information about the meanings +/// of `F_NOCACHE` and `F_GLOBAL_NOCACHE`. +/// +/// [this mailing list post]: https://lists.apple.com/archives/filesystem-dev/2007/Sep/msg00010.html +/// +/// See also [`fcntl_global_nocache`]. +/// +/// # References +/// - [Apple] +/// +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html +#[doc(alias = "F_NOCACHE")] +#[inline] +pub fn fcntl_nocache<Fd: AsFd>(fd: Fd, value: bool) -> io::Result<()> { + backend::fs::syscalls::fcntl_nocache(fd.as_fd(), value) +} + +/// `fcntl(fd, F_GLOBAL_NOCACHE, value)`—Turn data caching off or on for all +/// file descriptors. +/// +/// See [this mailing list post] for additional information about the meanings +/// of `F_NOCACHE` and `F_GLOBAL_NOCACHE`. +/// +/// [this mailing list post]: https://lists.apple.com/archives/filesystem-dev/2007/Sep/msg00010.html +/// +/// See also [`fcntl_nocache`]. +/// +/// # References +/// - [Apple] +/// +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html +#[doc(alias = "F_GLOBAL_NOCACHE")] +#[inline] +pub fn fcntl_global_nocache<Fd: AsFd>(fd: Fd, value: bool) -> io::Result<()> { + backend::fs::syscalls::fcntl_global_nocache(fd.as_fd(), value) +} diff --git a/vendor/rustix/src/fs/fcopyfile.rs b/vendor/rustix/src/fs/fcopyfile.rs new file mode 100644 index 00000000..e8f26ffd --- /dev/null +++ b/vendor/rustix/src/fs/fcopyfile.rs @@ -0,0 +1,88 @@ +use crate::fs::CopyfileFlags; +use crate::{backend, io}; +use backend::fd::AsFd; +use backend::fs::types::copyfile_state_t; + +/// `fcopyfile(from, to, state, flags)` +/// +/// # Safety +/// +/// The `state` operand must be allocated with `copyfile_state_alloc` and not +/// yet freed with `copyfile_state_free`. +/// +/// # References +/// - [Apple] +/// +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/fcopyfile.3.html +#[inline] +pub unsafe fn fcopyfile<FromFd: AsFd, ToFd: AsFd>( + from: FromFd, + to: ToFd, + state: copyfile_state_t, + flags: CopyfileFlags, +) -> io::Result<()> { + backend::fs::syscalls::fcopyfile(from.as_fd(), to.as_fd(), state, flags) +} + +/// `copyfile_state_alloc()` +/// +/// # References +/// - [Apple] +/// +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/fcopyfile.3.html +#[inline] +pub fn copyfile_state_alloc() -> io::Result<copyfile_state_t> { + backend::fs::syscalls::copyfile_state_alloc() +} + +/// `copyfile_state_free(state)` +/// +/// # Safety +/// +/// The `state` operand must be allocated with `copyfile_state_alloc` and not +/// yet freed with `copyfile_state_free`. +/// +/// # References +/// - [Apple] +/// +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/fcopyfile.3.html +#[inline] +pub unsafe fn copyfile_state_free(state: copyfile_state_t) -> io::Result<()> { + backend::fs::syscalls::copyfile_state_free(state) +} + +/// `copyfile_state_get(state, COPYFILE_STATE_COPIED)` +/// +/// # Safety +/// +/// The `state` operand must be allocated with `copyfile_state_alloc` and not +/// yet freed with `copyfile_state_free`. +/// +/// # References +/// - [Apple] +/// +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/fcopyfile.3.html +#[inline] +pub unsafe fn copyfile_state_get_copied(state: copyfile_state_t) -> io::Result<u64> { + backend::fs::syscalls::copyfile_state_get_copied(state) +} + +/// `copyfile_state_get(state, flags, dst)` +/// +/// # Safety +/// +/// The `state` operand must be allocated with `copyfile_state_alloc` and not +/// yet freed with `copyfile_state_free`. +/// +/// # References +/// - [Apple] +/// +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/fcopyfile.3.html +#[inline] +pub unsafe fn copyfile_state_get( + state: copyfile_state_t, + flag: u32, + dst: *mut core::ffi::c_void, +) -> io::Result<()> { + backend::fs::syscalls::copyfile_state_get(state, flag, dst) +} diff --git a/vendor/rustix/src/fs/fd.rs b/vendor/rustix/src/fs/fd.rs new file mode 100644 index 00000000..7ce1b3a4 --- /dev/null +++ b/vendor/rustix/src/fs/fd.rs @@ -0,0 +1,331 @@ +//! Functions which operate on file descriptors. + +#[cfg(not(target_os = "wasi"))] +use crate::fs::Mode; +#[cfg(not(target_os = "wasi"))] +use crate::fs::{Gid, Uid}; +use crate::fs::{SeekFrom, Timespec}; +use crate::{backend, io}; +use backend::fd::AsFd; +#[cfg(not(any( + netbsdlike, + target_os = "dragonfly", + target_os = "espidf", + target_os = "horizon", + target_os = "nto", + target_os = "redox", + target_os = "vita", +)))] +use backend::fs::types::FallocateFlags; +#[cfg(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "solaris", + target_os = "vita", + target_os = "wasi" +)))] +use backend::fs::types::FlockOperation; +#[cfg(linux_kernel)] +use backend::fs::types::FsWord; +use backend::fs::types::Stat; +#[cfg(not(any( + solarish, + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "netbsd", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +use backend::fs::types::StatFs; +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +use backend::fs::types::StatVfs; + +/// Timestamps used by [`utimensat`] and [`futimens`]. +/// +/// [`utimensat`]: crate::fs::utimensat +/// [`futimens`]: crate::fs::futimens +// This is `repr(C)` and specifically laid out to match the representation used +// by `utimensat` and `futimens`, which expect 2-element arrays of timestamps. +#[repr(C)] +#[derive(Debug, Clone)] +pub struct Timestamps { + /// The timestamp of the last access to a filesystem object. + pub last_access: Timespec, + + /// The timestamp of the last modification of a filesystem object. + pub last_modification: Timespec, +} + +/// The filesystem magic number for procfs. +/// +/// See [the `fstatfs` manual page] for more information. +/// +/// [the `fstatfs` manual page]: https://man7.org/linux/man-pages/man2/fstatfs.2.html#DESCRIPTION +#[cfg(linux_kernel)] +pub const PROC_SUPER_MAGIC: FsWord = backend::c::PROC_SUPER_MAGIC as FsWord; + +/// The filesystem magic number for NFS. +/// +/// See [the `fstatfs` manual page] for more information. +/// +/// [the `fstatfs` manual page]: https://man7.org/linux/man-pages/man2/fstatfs.2.html#DESCRIPTION +#[cfg(linux_kernel)] +pub const NFS_SUPER_MAGIC: FsWord = backend::c::NFS_SUPER_MAGIC as FsWord; + +/// `lseek(fd, offset, whence)`—Repositions a file descriptor within a file. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/lseek.html +/// [Linux]: https://man7.org/linux/man-pages/man2/lseek.2.html +#[inline] +#[doc(alias = "lseek")] +pub fn seek<Fd: AsFd>(fd: Fd, pos: SeekFrom) -> io::Result<u64> { + backend::fs::syscalls::seek(fd.as_fd(), pos) +} + +/// `lseek(fd, 0, SEEK_CUR)`—Returns the current position within a file. +/// +/// Return the current position of the file descriptor. This is a subset of +/// the functionality of `seek`, but this interface makes it easier for users +/// to declare their intent not to mutate any state. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/lseek.html +/// [Linux]: https://man7.org/linux/man-pages/man2/lseek.2.html +#[inline] +#[doc(alias = "lseek")] +pub fn tell<Fd: AsFd>(fd: Fd) -> io::Result<u64> { + backend::fs::syscalls::tell(fd.as_fd()) +} + +/// `fchmod(fd, mode)`—Sets open file or directory permissions. +/// +/// This implementation does not support [`OFlags::PATH`] file descriptors, +/// even on platforms where the host libc emulates it. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fchmod.html +/// [Linux]: https://man7.org/linux/man-pages/man2/fchmod.2.html +/// [`OFlags::PATH`]: crate::fs::OFlags::PATH +#[cfg(not(target_os = "wasi"))] +#[inline] +pub fn fchmod<Fd: AsFd>(fd: Fd, mode: Mode) -> io::Result<()> { + backend::fs::syscalls::fchmod(fd.as_fd(), mode) +} + +/// `fchown(fd, owner, group)`—Sets open file or directory ownership. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fchown.html +/// [Linux]: https://man7.org/linux/man-pages/man2/fchown.2.html +#[cfg(not(target_os = "wasi"))] +#[inline] +pub fn fchown<Fd: AsFd>(fd: Fd, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> { + backend::fs::syscalls::fchown(fd.as_fd(), owner, group) +} + +/// `fstat(fd)`—Queries metadata for an open file or directory. +/// +/// [`Mode::from_raw_mode`] and [`FileType::from_raw_mode`] may be used to +/// interpret the `st_mode` field. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fstat.html +/// [Linux]: https://man7.org/linux/man-pages/man2/fstat.2.html +/// [`Mode::from_raw_mode`]: Mode::from_raw_mode +/// [`FileType::from_raw_mode`]: crate::fs::FileType::from_raw_mode +#[inline] +pub fn fstat<Fd: AsFd>(fd: Fd) -> io::Result<Stat> { + backend::fs::syscalls::fstat(fd.as_fd()) +} + +/// `fstatfs(fd)`—Queries filesystem statistics for an open file or directory. +/// +/// Compared to [`fstatvfs`], this function often provides more information, +/// though it's less portable. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/fstatfs.2.html +#[cfg(not(any( + solarish, + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "netbsd", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +#[inline] +pub fn fstatfs<Fd: AsFd>(fd: Fd) -> io::Result<StatFs> { + backend::fs::syscalls::fstatfs(fd.as_fd()) +} + +/// `fstatvfs(fd)`—Queries filesystem statistics for an open file or +/// directory, POSIX version. +/// +/// Compared to [`fstatfs`], this function often provides less information, +/// but it is more portable. But even so, filesystems are very diverse and not +/// all the fields are meaningful for every filesystem. And `f_fsid` doesn't +/// seem to have a clear meaning anywhere. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fstatvfs.html +/// [Linux]: https://man7.org/linux/man-pages/man2/fstatvfs.2.html +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[inline] +pub fn fstatvfs<Fd: AsFd>(fd: Fd) -> io::Result<StatVfs> { + backend::fs::syscalls::fstatvfs(fd.as_fd()) +} + +/// `futimens(fd, times)`—Sets timestamps for an open file or directory. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/futimens.html +/// [Linux]: https://man7.org/linux/man-pages/man2/utimensat.2.html +#[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] +#[inline] +pub fn futimens<Fd: AsFd>(fd: Fd, times: &Timestamps) -> io::Result<()> { + backend::fs::syscalls::futimens(fd.as_fd(), times) +} + +/// `fallocate(fd, mode, offset, len)`—Adjusts file allocation. +/// +/// This is a more general form of `posix_fallocate`, adding a `mode` argument +/// which modifies the behavior. On platforms which only support +/// `posix_fallocate` and not the more general form, no `FallocateFlags` values +/// are defined so it will always be empty. +/// +/// # References +/// - [POSIX] +/// - [Linux `fallocate`] +/// - [Linux `posix_fallocate`] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_fallocate.html +/// [Linux `fallocate`]: https://man7.org/linux/man-pages/man2/fallocate.2.html +/// [Linux `posix_fallocate`]: https://man7.org/linux/man-pages/man3/posix_fallocate.3.html +#[cfg(not(any( + netbsdlike, + target_os = "dragonfly", + target_os = "espidf", + target_os = "horizon", + target_os = "nto", + target_os = "redox", + target_os = "vita", +)))] // not implemented in libc for NetBSD yet +#[inline] +#[doc(alias = "posix_fallocate")] +pub fn fallocate<Fd: AsFd>(fd: Fd, mode: FallocateFlags, offset: u64, len: u64) -> io::Result<()> { + backend::fs::syscalls::fallocate(fd.as_fd(), mode, offset, len) +} + +/// `fsync(fd)`—Ensures that file data and metadata is written to the +/// underlying storage device. +/// +/// On iOS and macOS this isn't sufficient to ensure that data has reached +/// persistent storage; use [`fcntl_fullfsync`] to ensure that. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fsync.html +/// [Linux]: https://man7.org/linux/man-pages/man2/fsync.2.html +/// [`fcntl_fullfsync`]: https://docs.rs/rustix/*/x86_64-apple-darwin/rustix/fs/fn.fcntl_fullfsync.html +#[inline] +pub fn fsync<Fd: AsFd>(fd: Fd) -> io::Result<()> { + backend::fs::syscalls::fsync(fd.as_fd()) +} + +/// `fdatasync(fd)`—Ensures that file data is written to the underlying +/// storage device. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fdatasync.html +/// [Linux]: https://man7.org/linux/man-pages/man2/fdatasync.2.html +#[cfg(not(any( + apple, + target_os = "dragonfly", + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "redox", + target_os = "vita", +)))] +#[inline] +pub fn fdatasync<Fd: AsFd>(fd: Fd) -> io::Result<()> { + backend::fs::syscalls::fdatasync(fd.as_fd()) +} + +/// `ftruncate(fd, length)`—Sets the length of a file. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/ftruncate.html +/// [Linux]: https://man7.org/linux/man-pages/man2/ftruncate.2.html +#[inline] +pub fn ftruncate<Fd: AsFd>(fd: Fd, length: u64) -> io::Result<()> { + backend::fs::syscalls::ftruncate(fd.as_fd(), length) +} + +/// `flock(fd, operation)`—Acquire or release an advisory lock on an open file. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/flock.2.html +#[cfg(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "solaris", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +pub fn flock<Fd: AsFd>(fd: Fd, operation: FlockOperation) -> io::Result<()> { + backend::fs::syscalls::flock(fd.as_fd(), operation) +} + +/// `syncfs(fd)`—Flush cached filesystem data. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/syncfs.2.html +#[cfg(linux_kernel)] +#[inline] +pub fn syncfs<Fd: AsFd>(fd: Fd) -> io::Result<()> { + backend::fs::syscalls::syncfs(fd.as_fd()) +} diff --git a/vendor/rustix/src/fs/getpath.rs b/vendor/rustix/src/fs/getpath.rs new file mode 100644 index 00000000..8e14ff2f --- /dev/null +++ b/vendor/rustix/src/fs/getpath.rs @@ -0,0 +1,14 @@ +use crate::ffi::CString; +use crate::{backend, io}; +use backend::fd::AsFd; + +/// `fcntl(fd, F_GETPATH)` +/// +/// # References +/// - [Apple] +/// +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html +#[inline] +pub fn getpath<Fd: AsFd>(fd: Fd) -> io::Result<CString> { + backend::fs::syscalls::getpath(fd.as_fd()) +} diff --git a/vendor/rustix/src/fs/id.rs b/vendor/rustix/src/fs/id.rs new file mode 100644 index 00000000..1fc2ef81 --- /dev/null +++ b/vendor/rustix/src/fs/id.rs @@ -0,0 +1 @@ +pub use crate::ugid::{Gid, Uid}; diff --git a/vendor/rustix/src/fs/inotify.rs b/vendor/rustix/src/fs/inotify.rs new file mode 100644 index 00000000..ad03dfa6 --- /dev/null +++ b/vendor/rustix/src/fs/inotify.rs @@ -0,0 +1,219 @@ +//! inotify support for working with inotify objects. +//! +//! # Examples +//! +//! ``` +//! use rustix::fs::inotify; +//! use rustix::io; +//! use std::mem::MaybeUninit; +//! +//! # fn test() -> io::Result<()> { +//! // Create an inotify object. In this example, we use `NONBLOCK` so that the +//! // reader fails with `WOULDBLOCK` when no events are ready. Otherwise it +//! // will block until at least one event is ready. +//! let inotify = inotify::init(inotify::CreateFlags::NONBLOCK)?; +//! +//! // Add a directory to watch. +//! inotify::add_watch( +//! &inotify, +//! "/path/to/some/directory/to/watch", +//! inotify::WatchFlags::ALL_EVENTS, +//! )?; +//! +//! // Generate some events in the watched directory… +//! +//! // Loop over pending events. +//! let mut buf = [MaybeUninit::uninit(); 512]; +//! let mut iter = inotify::Reader::new(inotify, &mut buf); +//! loop { +//! let entry = match iter.next() { +//! // Stop iterating if there are no more events for now. +//! Err(io::Errno::WOULDBLOCK) => break, +//! Err(e) => return Err(e), +//! Ok(entry) => entry, +//! }; +//! +//! // Use `entry`… +//! } +//! +//! # Ok(()) +//! # } + +#![allow(unused_qualifications)] + +use super::inotify; +pub use crate::backend::fs::inotify::{CreateFlags, ReadFlags, WatchFlags}; +use crate::backend::fs::syscalls; +use crate::fd::{AsFd, OwnedFd}; +use crate::ffi::CStr; +use crate::io; +use crate::io::{read, Errno}; +use core::mem::{align_of, size_of, MaybeUninit}; +use linux_raw_sys::general::inotify_event; + +/// `inotify_init1(flags)`—Creates a new inotify object. +/// +/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file +/// descriptor from being implicitly passed across `exec` boundaries. +#[doc(alias = "inotify_init1")] +#[inline] +pub fn init(flags: inotify::CreateFlags) -> io::Result<OwnedFd> { + syscalls::inotify_init1(flags) +} + +/// `inotify_add_watch(self, path, flags)`—Adds a watch to inotify. +/// +/// This registers or updates a watch for the filesystem path `path` and +/// returns a watch descriptor corresponding to this watch. +/// +/// Note: Due to the existence of hardlinks, providing two different paths to +/// this method may result in it returning the same watch descriptor. An +/// application should keep track of this externally to avoid logic errors. +#[doc(alias = "inotify_add_watch")] +#[inline] +pub fn add_watch<P: crate::path::Arg, Fd: AsFd>( + inot: Fd, + path: P, + flags: inotify::WatchFlags, +) -> io::Result<i32> { + path.into_with_c_str(|path| syscalls::inotify_add_watch(inot.as_fd(), path, flags)) +} + +/// `inotify_rm_watch(self, wd)`—Removes a watch from this inotify. +/// +/// The watch descriptor provided should have previously been returned by +/// [`inotify::add_watch`] and not previously have been removed. +#[doc(alias = "inotify_rm_watch")] +#[inline] +pub fn remove_watch<Fd: AsFd>(inot: Fd, wd: i32) -> io::Result<()> { + syscalls::inotify_rm_watch(inot.as_fd(), wd) +} + +/// An inotify event iterator implemented with the read syscall. +/// +/// See the [`RawDir`] API for more details and usage examples as this API is +/// based on it. +/// +/// [`RawDir`]: crate::fs::raw_dir::RawDir +pub struct Reader<'buf, Fd: AsFd> { + fd: Fd, + buf: &'buf mut [MaybeUninit<u8>], + initialized: usize, + offset: usize, +} + +impl<'buf, Fd: AsFd> Reader<'buf, Fd> { + /// Create a new iterator from the given file descriptor and buffer. + pub fn new(fd: Fd, buf: &'buf mut [MaybeUninit<u8>]) -> Self { + Self { + fd, + buf: { + let offset = buf.as_ptr().align_offset(align_of::<inotify_event>()); + if offset < buf.len() { + &mut buf[offset..] + } else { + &mut [] + } + }, + initialized: 0, + offset: 0, + } + } +} + +/// An inotify event. +#[doc(alias = "inotify_event")] +#[derive(Debug)] +pub struct Event<'a> { + wd: i32, + events: ReadFlags, + cookie: u32, + file_name: Option<&'a CStr>, +} + +impl<'a> Event<'a> { + /// Returns the watch for which this event occurs. + #[inline] + pub fn wd(&self) -> i32 { + self.wd + } + + /// Returns a description of the events. + #[inline] + #[doc(alias = "mask")] + pub fn events(&self) -> ReadFlags { + self.events + } + + /// Returns the unique cookie associating related events. + #[inline] + pub fn cookie(&self) -> u32 { + self.cookie + } + + /// Returns the file name of this event, if any. + #[inline] + pub fn file_name(&self) -> Option<&CStr> { + self.file_name + } +} + +impl<'buf, Fd: AsFd> Reader<'buf, Fd> { + /// Read the next inotify event. + /// + /// This is similar to [`Iterator::next`] except that it doesn't return an + /// `Option`, because the stream doesn't have an ending. It always returns + /// events or errors. + /// + /// If there are no events in the buffer and none ready to be read: + /// - If the file descriptor was opened with + /// [`inotify::CreateFlags::NONBLOCK`], this will fail with + /// [`Errno::AGAIN`]. + /// - Otherwise this will block until at least one event is ready or an + /// error occurs. + #[allow(unsafe_code)] + #[allow(clippy::should_implement_trait)] + pub fn next(&mut self) -> io::Result<Event<'_>> { + if self.is_buffer_empty() { + match read(self.fd.as_fd(), &mut *self.buf).map(|(init, _)| init.len()) { + Ok(0) => return Err(Errno::INVAL), + Ok(bytes_read) => { + self.initialized = bytes_read; + self.offset = 0; + } + Err(e) => return Err(e), + } + } + + let ptr = self.buf[self.offset..].as_ptr(); + + // SAFETY: + // - This data is initialized by the check above. + // - Assumption: the kernel will not give us partial structs. + // - Assumption: the kernel uses proper alignment between structs. + // - The starting pointer is aligned (performed in `Reader::new`). + let event = unsafe { &*ptr.cast::<inotify_event>() }; + + self.offset += size_of::<inotify_event>() + usize::try_from(event.len).unwrap(); + + Ok(Event { + wd: event.wd, + events: ReadFlags::from_bits_retain(event.mask), + cookie: event.cookie, + file_name: if event.len > 0 { + // SAFETY: The kernel guarantees a NUL-terminated string. + Some(unsafe { CStr::from_ptr(event.name.as_ptr().cast()) }) + } else { + None + }, + }) + } + + /// Returns true if the internal buffer is empty and will be refilled when + /// calling [`next`]. This is useful to avoid further blocking reads. + /// + /// [`next`]: Self::next + pub fn is_buffer_empty(&self) -> bool { + self.offset >= self.initialized + } +} diff --git a/vendor/rustix/src/fs/ioctl.rs b/vendor/rustix/src/fs/ioctl.rs new file mode 100644 index 00000000..c126fdd1 --- /dev/null +++ b/vendor/rustix/src/fs/ioctl.rs @@ -0,0 +1,169 @@ +//! Filesystem-oriented `ioctl` functions. + +#![allow(unsafe_code)] + +#[cfg(linux_kernel)] +use { + crate::backend::c, + crate::fd::AsFd, + crate::{backend, ffi, io, ioctl}, +}; + +use bitflags::bitflags; + +#[cfg(all(linux_kernel, not(any(target_arch = "sparc", target_arch = "sparc64"))))] +use crate::fd::{AsRawFd as _, BorrowedFd}; + +/// `ioctl(fd, BLKSSZGET)`—Returns the logical block size of a block device. +/// +/// This is mentioned in the [Linux `openat` manual page]. +/// +/// [Linux `openat` manual page]: https://man7.org/linux/man-pages/man2/openat.2.html +#[cfg(linux_kernel)] +#[inline] +#[doc(alias = "BLKSSZGET")] +pub fn ioctl_blksszget<Fd: AsFd>(fd: Fd) -> io::Result<u32> { + // SAFETY: `BLZSSZGET` is a getter opcode that gets a `u32`. + unsafe { + let ctl = ioctl::Getter::<{ c::BLKSSZGET }, c::c_uint>::new(); + ioctl::ioctl(fd, ctl) + } +} + +/// `ioctl(fd, BLKPBSZGET)`—Returns the physical block size of a block device. +#[cfg(linux_kernel)] +#[inline] +#[doc(alias = "BLKPBSZGET")] +pub fn ioctl_blkpbszget<Fd: AsFd>(fd: Fd) -> io::Result<u32> { + // SAFETY: `BLKPBSZGET` is a getter opcode that gets a `u32`. + unsafe { + let ctl = ioctl::Getter::<{ c::BLKPBSZGET }, c::c_uint>::new(); + ioctl::ioctl(fd, ctl) + } +} + +/// `ioctl(fd, FICLONE, src_fd)`—Share data between open files. +/// +/// This ioctl is not available on SPARC platforms. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/ioctl_ficlone.2.html +#[cfg(all(linux_kernel, not(any(target_arch = "sparc", target_arch = "sparc64"))))] +#[inline] +#[doc(alias = "FICLONE")] +pub fn ioctl_ficlone<Fd: AsFd, SrcFd: AsFd>(fd: Fd, src_fd: SrcFd) -> io::Result<()> { + unsafe { ioctl::ioctl(fd, Ficlone(src_fd.as_fd())) } +} + +/// `ioctl(fd, EXT4_IOC_RESIZE_FS, blocks)`—Resize ext4 filesystem on fd. +#[cfg(linux_kernel)] +#[inline] +#[doc(alias = "EXT4_IOC_RESIZE_FS")] +pub fn ext4_ioc_resize_fs<Fd: AsFd>(fd: Fd, blocks: u64) -> io::Result<()> { + // SAFETY: `EXT4_IOC_RESIZE_FS` is a pointer setter opcode. + unsafe { + let ctl = ioctl::Setter::<{ backend::fs::EXT4_IOC_RESIZE_FS }, u64>::new(blocks); + ioctl::ioctl(fd, ctl) + } +} + +#[cfg(all(linux_kernel, not(any(target_arch = "sparc", target_arch = "sparc64"))))] +struct Ficlone<'a>(BorrowedFd<'a>); + +#[cfg(all(linux_kernel, not(any(target_arch = "sparc", target_arch = "sparc64"))))] +unsafe impl ioctl::Ioctl for Ficlone<'_> { + type Output = (); + + const IS_MUTATING: bool = false; + + fn opcode(&self) -> ioctl::Opcode { + c::FICLONE as ioctl::Opcode + } + + fn as_ptr(&mut self) -> *mut c::c_void { + self.0.as_raw_fd() as *mut c::c_void + } + + unsafe fn output_from_ptr( + _: ioctl::IoctlOutput, + _: *mut c::c_void, + ) -> io::Result<Self::Output> { + Ok(()) + } +} + +#[cfg(linux_kernel)] +bitflags! { + /// `FS_*` constants for use with [`ioctl_getflags`]. + /// + /// [`ioctl_getflags`]: crate::fs::ioctl::ioctl_getflags + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct IFlags: ffi::c_uint { + /// `FS_APPEND_FL` + const APPEND = linux_raw_sys::general::FS_APPEND_FL; + /// `FS_COMPR_FL` + const COMPRESSED = linux_raw_sys::general::FS_COMPR_FL; + /// `FS_DIRSYNC_FL` + const DIRSYNC = linux_raw_sys::general::FS_DIRSYNC_FL; + /// `FS_IMMUTABLE_FL` + const IMMUTABLE = linux_raw_sys::general::FS_IMMUTABLE_FL; + /// `FS_JOURNAL_DATA_FL` + const JOURNALING = linux_raw_sys::general::FS_JOURNAL_DATA_FL; + /// `FS_NOATIME_FL` + const NOATIME = linux_raw_sys::general::FS_NOATIME_FL; + /// `FS_NOCOW_FL` + const NOCOW = linux_raw_sys::general::FS_NOCOW_FL; + /// `FS_NODUMP_FL` + const NODUMP = linux_raw_sys::general::FS_NODUMP_FL; + /// `FS_NOTAIL_FL` + const NOTAIL = linux_raw_sys::general::FS_NOTAIL_FL; + /// `FS_PROJINHERIT_FL` + const PROJECT_INHERIT = linux_raw_sys::general::FS_PROJINHERIT_FL; + /// `FS_SECRM_FL` + const SECURE_REMOVAL = linux_raw_sys::general::FS_SECRM_FL; + /// `FS_SYNC_FL` + const SYNC = linux_raw_sys::general::FS_SYNC_FL; + /// `FS_TOPDIR_FL` + const TOPDIR = linux_raw_sys::general::FS_TOPDIR_FL; + /// `FS_UNRM_FL` + const UNRM = linux_raw_sys::general::FS_UNRM_FL; + } +} + +/// `ioctl(fd, FS_IOC_GETFLAGS)`—Returns the [inode flags] attributes +/// +/// [inode flags]: https://man7.org/linux/man-pages/man2/ioctl_iflags.2.html +#[cfg(linux_kernel)] +#[inline] +#[doc(alias = "FS_IOC_GETFLAGS")] +pub fn ioctl_getflags<Fd: AsFd>(fd: Fd) -> io::Result<IFlags> { + unsafe { + #[cfg(target_pointer_width = "32")] + let ctl = ioctl::Getter::<{ c::FS_IOC32_GETFLAGS }, u32>::new(); + #[cfg(target_pointer_width = "64")] + let ctl = ioctl::Getter::<{ c::FS_IOC_GETFLAGS }, u32>::new(); + + ioctl::ioctl(fd, ctl).map(IFlags::from_bits_retain) + } +} + +/// `ioctl(fd, FS_IOC_SETFLAGS)`—Modify the [inode flags] attributes +/// +/// [inode flags]: https://man7.org/linux/man-pages/man2/ioctl_iflags.2.html +#[cfg(linux_kernel)] +#[inline] +#[doc(alias = "FS_IOC_SETFLAGS")] +pub fn ioctl_setflags<Fd: AsFd>(fd: Fd, flags: IFlags) -> io::Result<()> { + unsafe { + #[cfg(target_pointer_width = "32")] + let ctl = ioctl::Setter::<{ c::FS_IOC32_SETFLAGS }, u32>::new(flags.bits()); + + #[cfg(target_pointer_width = "64")] + let ctl = ioctl::Setter::<{ c::FS_IOC_SETFLAGS }, u32>::new(flags.bits()); + + ioctl::ioctl(fd, ctl) + } +} diff --git a/vendor/rustix/src/fs/makedev.rs b/vendor/rustix/src/fs/makedev.rs new file mode 100644 index 00000000..e6cf5c0e --- /dev/null +++ b/vendor/rustix/src/fs/makedev.rs @@ -0,0 +1,35 @@ +use crate::backend; +use crate::fs::Dev; + +/// `makedev(maj, min)`—Compute a device ID from a given major and minor ID. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man3/makedev.3.html +#[inline] +pub fn makedev(maj: u32, min: u32) -> Dev { + backend::fs::makedev::makedev(maj, min) +} + +/// `minor(dev)`—Compute the minor ID of a given device ID. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man3/minor.3.html +#[inline] +pub fn minor(dev: Dev) -> u32 { + backend::fs::makedev::minor(dev) +} + +/// `major(dev)`—Compute the major ID of a given device ID. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man3/major.3.html +#[inline] +pub fn major(dev: Dev) -> u32 { + backend::fs::makedev::major(dev) +} diff --git a/vendor/rustix/src/fs/memfd_create.rs b/vendor/rustix/src/fs/memfd_create.rs new file mode 100644 index 00000000..6e49771f --- /dev/null +++ b/vendor/rustix/src/fs/memfd_create.rs @@ -0,0 +1,22 @@ +use crate::fd::OwnedFd; +use crate::{backend, io, path}; +use backend::fs::types::MemfdFlags; + +/// `memfd_create(name, flags)`—Create an anonymous file. +/// +/// For a higher-level API to this functionality, see the [memfd] crate. +/// +/// [memfd]: https://crates.io/crates/memfd +/// +/// # References +/// - [Linux] +/// - [glibc] +/// - [FreeBSD] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/memfd_create.2.html +/// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/Memory_002dmapped-I_002fO.html#index-memfd_005fcreate +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?memfd_create +#[inline] +pub fn memfd_create<P: path::Arg>(name: P, flags: MemfdFlags) -> io::Result<OwnedFd> { + name.into_with_c_str(|name| backend::fs::syscalls::memfd_create(name, flags)) +} diff --git a/vendor/rustix/src/fs/mod.rs b/vendor/rustix/src/fs/mod.rs new file mode 100644 index 00000000..505925f7 --- /dev/null +++ b/vendor/rustix/src/fs/mod.rs @@ -0,0 +1,141 @@ +//! Filesystem operations. + +mod abs; +#[cfg(not(target_os = "redox"))] +mod at; +mod constants; +#[cfg(linux_kernel)] +mod copy_file_range; +#[cfg(all(feature = "alloc", not(any(target_os = "espidf", target_os = "redox"))))] +mod dir; +#[cfg(not(any( + apple, + netbsdlike, + target_os = "dragonfly", + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "redox", + target_os = "solaris", + target_os = "vita", +)))] +mod fadvise; +pub(crate) mod fcntl; +#[cfg(apple)] +mod fcntl_apple; +#[cfg(apple)] +mod fcopyfile; +pub(crate) mod fd; +#[cfg(all(apple, feature = "alloc"))] +mod getpath; +#[cfg(not(target_os = "wasi"))] // WASI doesn't have get[gpu]id. +mod id; +#[cfg(linux_kernel)] +pub mod inotify; +#[cfg(linux_kernel)] +mod ioctl; +#[cfg(not(any( + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +mod makedev; +#[cfg(any(linux_kernel, target_os = "freebsd"))] +mod memfd_create; +#[cfg(linux_kernel)] +mod openat2; +#[cfg(linux_kernel)] +mod raw_dir; +mod seek_from; +#[cfg(target_os = "linux")] +mod sendfile; +#[cfg(not(any(target_os = "espidf", target_os = "redox")))] +mod special; +#[cfg(linux_kernel)] +mod statx; +#[cfg(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +mod sync; +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] +mod xattr; + +pub use abs::*; +#[cfg(not(target_os = "redox"))] +pub use at::*; +pub use constants::*; +#[cfg(linux_kernel)] +pub use copy_file_range::copy_file_range; +#[cfg(all(feature = "alloc", not(any(target_os = "espidf", target_os = "redox"))))] +pub use dir::{Dir, DirEntry}; +#[cfg(not(any( + apple, + netbsdlike, + target_os = "dragonfly", + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "redox", + target_os = "solaris", + target_os = "vita", +)))] +pub use fadvise::fadvise; +pub use fcntl::*; +#[cfg(apple)] +pub use fcntl_apple::*; +#[cfg(apple)] +pub use fcopyfile::*; +pub use fd::*; +#[cfg(all(apple, feature = "alloc"))] +pub use getpath::getpath; +#[cfg(not(target_os = "wasi"))] +pub use id::*; +#[cfg(linux_kernel)] +pub use ioctl::*; +#[cfg(not(any( + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +pub use makedev::*; +#[cfg(any(linux_kernel, target_os = "freebsd"))] +pub use memfd_create::memfd_create; +#[cfg(linux_kernel)] +pub use openat2::openat2; +#[cfg(linux_kernel)] +pub use raw_dir::{RawDir, RawDirEntry}; +pub use seek_from::SeekFrom; +#[cfg(target_os = "linux")] +pub use sendfile::sendfile; +#[cfg(not(any(target_os = "espidf", target_os = "redox")))] +pub use special::*; +#[cfg(linux_kernel)] +pub use statx::*; +#[cfg(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +pub use sync::sync; +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] +pub use xattr::*; + +/// Re-export types common to POSIX-ish platforms. +#[cfg(feature = "std")] +#[cfg(unix)] +pub use std::os::unix::fs::{DirEntryExt, FileExt, FileTypeExt, MetadataExt, OpenOptionsExt}; +#[cfg(feature = "std")] +#[cfg(all(wasi_ext, target_os = "wasi"))] +pub use std::os::wasi::fs::{DirEntryExt, FileExt, FileTypeExt, MetadataExt, OpenOptionsExt}; diff --git a/vendor/rustix/src/fs/openat2.rs b/vendor/rustix/src/fs/openat2.rs new file mode 100644 index 00000000..0a0e1d16 --- /dev/null +++ b/vendor/rustix/src/fs/openat2.rs @@ -0,0 +1,24 @@ +use crate::fd::OwnedFd; +use crate::{backend, io, path}; +use backend::fd::AsFd; +use backend::fs::types::{Mode, OFlags, ResolveFlags}; + +/// `openat2(dirfd, path, OpenHow { oflags, mode, resolve }, sizeof(OpenHow))`— +/// Opens a file with more options. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/openat2.2.html +#[inline] +pub fn openat2<Fd: AsFd, P: path::Arg>( + dirfd: Fd, + path: P, + oflags: OFlags, + mode: Mode, + resolve: ResolveFlags, +) -> io::Result<OwnedFd> { + path.into_with_c_str(|path| { + backend::fs::syscalls::openat2(dirfd.as_fd(), path, oflags, mode, resolve) + }) +} diff --git a/vendor/rustix/src/fs/raw_dir.rs b/vendor/rustix/src/fs/raw_dir.rs new file mode 100644 index 00000000..7fec6fd7 --- /dev/null +++ b/vendor/rustix/src/fs/raw_dir.rs @@ -0,0 +1,236 @@ +//! `RawDir` and `RawDirEntry`. + +use crate::backend::fs::syscalls::getdents_uninit; +use crate::fd::AsFd; +use crate::ffi::CStr; +use crate::fs::FileType; +use crate::io; +use core::fmt; +use core::mem::{align_of, MaybeUninit}; +use linux_raw_sys::general::linux_dirent64; + +/// A directory iterator implemented with getdents. +/// +/// Note: This implementation does not handle growing the buffer. If this +/// functionality is necessary, you'll need to drop the current iterator, +/// resize the buffer, and then re-create the iterator. The iterator is +/// guaranteed to continue where it left off provided the file descriptor isn't +/// changed. See the example in [`RawDir::new`]. +pub struct RawDir<'buf, Fd: AsFd> { + fd: Fd, + buf: &'buf mut [MaybeUninit<u8>], + initialized: usize, + offset: usize, +} + +impl<'buf, Fd: AsFd> RawDir<'buf, Fd> { + /// Create a new iterator from the given file descriptor and buffer. + /// + /// Note: the buffer size may be trimmed to accommodate alignment + /// requirements. + /// + /// # Examples + /// + /// ## Simple but non-portable + /// + /// These examples are non-portable, because file systems may not have a + /// maximum file name length. If you can make assumptions that bound + /// this length, then these examples may suffice. + /// + /// Using the heap: + /// + /// ``` + /// # use std::mem::MaybeUninit; + /// # use rustix::fs::{CWD, Mode, OFlags, openat, RawDir}; + /// # use rustix::cstr; + /// + /// let fd = openat( + /// CWD, + /// cstr!("."), + /// OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC, + /// Mode::empty(), + /// ) + /// .unwrap(); + /// + /// let mut buf = Vec::with_capacity(8192); + /// let mut iter = RawDir::new(fd, buf.spare_capacity_mut()); + /// while let Some(entry) = iter.next() { + /// let entry = entry.unwrap(); + /// dbg!(&entry); + /// } + /// ``` + /// + /// Using the stack: + /// + /// ``` + /// # use std::mem::MaybeUninit; + /// # use rustix::fs::{CWD, Mode, OFlags, openat, RawDir}; + /// # use rustix::cstr; + /// + /// let fd = openat( + /// CWD, + /// cstr!("."), + /// OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC, + /// Mode::empty(), + /// ) + /// .unwrap(); + /// + /// let mut buf = [MaybeUninit::uninit(); 2048]; + /// let mut iter = RawDir::new(fd, &mut buf); + /// while let Some(entry) = iter.next() { + /// let entry = entry.unwrap(); + /// dbg!(&entry); + /// } + /// ``` + /// + /// ## Portable + /// + /// Heap allocated growing buffer for supporting directory entries with + /// arbitrarily large file names: + /// + /// ```ignore + /// # // The `ignore` above can be removed when we can depend on Rust 1.65. + /// # use std::mem::MaybeUninit; + /// # use rustix::fs::{CWD, Mode, OFlags, openat, RawDir}; + /// # use rustix::io::Errno; + /// # use rustix::cstr; + /// + /// let fd = openat( + /// CWD, + /// cstr!("."), + /// OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC, + /// Mode::empty(), + /// ) + /// .unwrap(); + /// + /// let mut buf = Vec::with_capacity(8192); + /// 'read: loop { + /// 'resize: { + /// let mut iter = RawDir::new(&fd, buf.spare_capacity_mut()); + /// while let Some(entry) = iter.next() { + /// let entry = match entry { + /// Err(Errno::INVAL) => break 'resize, + /// r => r.unwrap(), + /// }; + /// dbg!(&entry); + /// } + /// break 'read; + /// } + /// + /// let new_capacity = buf.capacity() * 2; + /// buf.reserve(new_capacity); + /// } + /// ``` + pub fn new(fd: Fd, buf: &'buf mut [MaybeUninit<u8>]) -> Self { + Self { + fd, + buf: { + let offset = buf.as_ptr().align_offset(align_of::<linux_dirent64>()); + if offset < buf.len() { + &mut buf[offset..] + } else { + &mut [] + } + }, + initialized: 0, + offset: 0, + } + } +} + +/// A raw directory entry, similar to [`std::fs::DirEntry`]. +/// +/// Unlike the std version, this may represent the `.` or `..` entries. +pub struct RawDirEntry<'a> { + file_name: &'a CStr, + file_type: u8, + inode_number: u64, + next_entry_cookie: i64, +} + +impl<'a> fmt::Debug for RawDirEntry<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut f = f.debug_struct("RawDirEntry"); + f.field("file_name", &self.file_name()); + f.field("file_type", &self.file_type()); + f.field("ino", &self.ino()); + f.field("next_entry_cookie", &self.next_entry_cookie()); + f.finish() + } +} + +impl<'a> RawDirEntry<'a> { + /// Returns the file name of this directory entry. + #[inline] + pub fn file_name(&self) -> &CStr { + self.file_name + } + + /// Returns the type of this directory entry. + #[inline] + pub fn file_type(&self) -> FileType { + FileType::from_dirent_d_type(self.file_type) + } + + /// Returns the inode number of this directory entry. + #[inline] + #[doc(alias = "inode_number")] + pub fn ino(&self) -> u64 { + self.inode_number + } + + /// Returns the seek cookie to the next directory entry. + #[inline] + #[doc(alias = "off")] + pub fn next_entry_cookie(&self) -> u64 { + self.next_entry_cookie as u64 + } +} + +impl<'buf, Fd: AsFd> RawDir<'buf, Fd> { + /// Identical to [`Iterator::next`] except that [`Iterator::Item`] borrows + /// from self. + /// + /// Note: this interface will be broken to implement a stdlib iterator API + /// with GAT support once one becomes available. + #[allow(unsafe_code)] + #[allow(clippy::should_implement_trait)] + pub fn next(&mut self) -> Option<io::Result<RawDirEntry<'_>>> { + if self.is_buffer_empty() { + match getdents_uninit(self.fd.as_fd(), self.buf) { + Ok(0) => return None, + Ok(bytes_read) => { + self.initialized = bytes_read; + self.offset = 0; + } + Err(e) => return Some(Err(e)), + } + } + + let dirent_ptr = self.buf[self.offset..].as_ptr(); + // SAFETY: + // - This data is initialized by the check above. + // - Assumption: the kernel will not give us partial structs. + // - Assumption: the kernel uses proper alignment between structs. + // - The starting pointer is aligned (performed in `RawDir::new`). + let dirent = unsafe { &*dirent_ptr.cast::<linux_dirent64>() }; + + self.offset += usize::from(dirent.d_reclen); + + Some(Ok(RawDirEntry { + file_type: dirent.d_type, + inode_number: dirent.d_ino.into(), + next_entry_cookie: dirent.d_off.into(), + // SAFETY: The kernel guarantees a NUL-terminated string. + file_name: unsafe { CStr::from_ptr(dirent.d_name.as_ptr().cast()) }, + })) + } + + /// Returns true if the internal buffer is empty and will be refilled when + /// calling [`next`]. + /// + /// [`next`]: Self::next + pub fn is_buffer_empty(&self) -> bool { + self.offset >= self.initialized + } +} diff --git a/vendor/rustix/src/fs/seek_from.rs b/vendor/rustix/src/fs/seek_from.rs new file mode 100644 index 00000000..bc0907ca --- /dev/null +++ b/vendor/rustix/src/fs/seek_from.rs @@ -0,0 +1,53 @@ +//! The following is derived from Rust's +//! library/std/src/io/mod.rs at revision +//! dca3f1b786efd27be3b325ed1e01e247aa589c3b. + +/// Enumeration of possible methods to seek within an I/O object. +/// +/// It is used by the [`seek`] function. +/// +/// This is similar to [`std::io::SeekFrom`], however it adds platform-specific +/// seek options. +/// +/// [`seek`]: crate::fs::seek +#[derive(Copy, PartialEq, Eq, Clone, Debug)] +#[cfg_attr(staged_api, stable(feature = "rust1", since = "1.0.0"))] +pub enum SeekFrom { + /// Sets the offset to the provided number of bytes. + #[cfg_attr(staged_api, stable(feature = "rust1", since = "1.0.0"))] + Start(#[cfg_attr(staged_api, stable(feature = "rust1", since = "1.0.0"))] u64), + + /// Sets the offset to the size of this object plus the specified number of + /// bytes. + /// + /// It is possible to seek beyond the end of an object, but it's an error + /// to seek before byte 0. + #[cfg_attr(staged_api, stable(feature = "rust1", since = "1.0.0"))] + End(#[cfg_attr(staged_api, stable(feature = "rust1", since = "1.0.0"))] i64), + + /// Sets the offset to the current position plus the specified number of + /// bytes. + /// + /// It is possible to seek beyond the end of an object, but it's an error + /// to seek before byte 0. + #[cfg_attr(staged_api, stable(feature = "rust1", since = "1.0.0"))] + Current(#[cfg_attr(staged_api, stable(feature = "rust1", since = "1.0.0"))] i64), + + /// Sets the offset to the current position plus the specified number of + /// bytes, plus the distance to the next byte which is not in a hole. + /// + /// If the offset is in a hole at the end of the file, the seek will fail + /// with [`Errno::NXIO`]. + /// + /// [`Errno::NXIO`]: crate::io::Errno::NXIO + #[cfg(any(apple, freebsdlike, linux_kernel, solarish))] + Data(u64), + + /// Sets the offset to the current position plus the specified number of + /// bytes, plus the distance to the next byte which is in a hole. + /// + /// If there is no hole past the offset, it will be set to the end of the + /// file i.e. there is an implicit hole at the end of any file. + #[cfg(any(apple, freebsdlike, linux_kernel, solarish))] + Hole(u64), +} diff --git a/vendor/rustix/src/fs/sendfile.rs b/vendor/rustix/src/fs/sendfile.rs new file mode 100644 index 00000000..db3d6022 --- /dev/null +++ b/vendor/rustix/src/fs/sendfile.rs @@ -0,0 +1,20 @@ +use crate::{backend, io}; +use backend::fd::AsFd; + +/// `sendfile(out_fd, in_fd, offset, count)`—Transfer data between file +/// descriptors. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/sendfile.2.html +#[cfg(linux_kernel)] +#[inline] +pub fn sendfile<OutFd: AsFd, InFd: AsFd>( + out_fd: OutFd, + in_fd: InFd, + offset: Option<&mut u64>, + count: usize, +) -> io::Result<usize> { + backend::fs::syscalls::sendfile(out_fd.as_fd(), in_fd.as_fd(), offset, count) +} diff --git a/vendor/rustix/src/fs/special.rs b/vendor/rustix/src/fs/special.rs new file mode 100644 index 00000000..276a775f --- /dev/null +++ b/vendor/rustix/src/fs/special.rs @@ -0,0 +1,80 @@ +//! The `CWD` and `ABS` constants, representing the current working directory +//! and absolute-only paths, respectively. +//! +//! # Safety +//! +//! This file uses `AT_FDCWD`, which is a raw file descriptor, but which is +//! always valid, and `-EBADF`, which is an undocumented by commonly used +//! convention of passing a value which will always fail if the accompanying +//! path isn't absolute. + +#![allow(unsafe_code)] + +use crate::backend; +use backend::c; +use backend::fd::{BorrowedFd, RawFd}; + +/// `AT_FDCWD`—A handle representing the current working directory. +/// +/// This is a file descriptor which refers to the process current directory +/// which can be used as the directory argument in `*at` functions such as +/// [`openat`]. +/// +/// # References +/// - [POSIX] +/// +/// [`openat`]: crate::fs::openat +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fcntl.h.html +// SAFETY: `AT_FDCWD` is a reserved value that is never dynamically +// allocated, so it'll remain valid for the duration of `'static`. +#[cfg(not(target_os = "horizon"))] +#[doc(alias = "AT_FDCWD")] +pub const CWD: BorrowedFd<'static> = + unsafe { BorrowedFd::<'static>::borrow_raw(c::AT_FDCWD as RawFd) }; + +/// `-EBADF`—A handle that requires paths to be absolute. +/// +/// This is a file descriptor which refers to no directory, which can be used +/// as the directory argument in `*at` functions such as [`openat`], which +/// causes them to fail with [`BADF`] if the accompanying path is not absolute. +/// +/// This corresponds to the undocumented by commonly used convention of +/// passing `-EBADF` as the `dirfd` argument, which is ignored if the path is +/// absolute, and evokes an `EBADF` error otherwise. +/// +/// [`openat`]: crate::fs::openat +/// [`BADF`]: crate::io::Errno::BADF +// SAFETY: This `-EBADF` convention is commonly used, such as in lxc, so OS's +// aren't going to break it. +pub const ABS: BorrowedFd<'static> = + unsafe { BorrowedFd::<'static>::borrow_raw(c::EBADF.wrapping_neg() as RawFd) }; + +#[cfg(test)] +mod tests { + use super::*; + use crate::fd::AsRawFd as _; + + #[test] + fn test_cwd() { + assert!(CWD.as_raw_fd() != -1); + assert!(CWD.as_raw_fd() != c::STDIN_FILENO); + assert!(CWD.as_raw_fd() != c::STDOUT_FILENO); + assert!(CWD.as_raw_fd() != c::STDERR_FILENO); + #[cfg(linux_kernel)] + #[cfg(feature = "io_uring")] + assert!(CWD.as_raw_fd() != crate::io_uring::IORING_REGISTER_FILES_SKIP.as_raw_fd()); + } + + #[test] + fn test_abs() { + assert!(ABS.as_raw_fd() < 0); + assert!(ABS.as_raw_fd() != -1); + assert!(ABS.as_raw_fd() != c::AT_FDCWD); + assert!(ABS.as_raw_fd() != c::STDIN_FILENO); + assert!(ABS.as_raw_fd() != c::STDOUT_FILENO); + assert!(ABS.as_raw_fd() != c::STDERR_FILENO); + #[cfg(linux_kernel)] + #[cfg(feature = "io_uring")] + assert!(ABS.as_raw_fd() != crate::io_uring::IORING_REGISTER_FILES_SKIP.as_raw_fd()); + } +} diff --git a/vendor/rustix/src/fs/statx.rs b/vendor/rustix/src/fs/statx.rs new file mode 100644 index 00000000..f1d995ec --- /dev/null +++ b/vendor/rustix/src/fs/statx.rs @@ -0,0 +1,275 @@ +//! Linux `statx`. + +use crate::fd::AsFd; +use crate::fs::AtFlags; +use crate::{backend, io, path}; +use backend::c; +use bitflags::bitflags; + +#[cfg(feature = "linux_4_11")] +use backend::fs::syscalls::statx as _statx; +#[cfg(not(feature = "linux_4_11"))] +use compat::statx as _statx; + +/// `struct statx` for use with [`statx`]. +#[repr(C)] +#[derive(Debug, Copy, Clone)] +#[allow(missing_docs)] +#[non_exhaustive] +pub struct Statx { + pub stx_mask: u32, + pub stx_blksize: u32, + pub stx_attributes: StatxAttributes, + pub stx_nlink: u32, + pub stx_uid: u32, + pub stx_gid: u32, + pub stx_mode: u16, + pub(crate) __spare0: [u16; 1], + pub stx_ino: u64, + pub stx_size: u64, + pub stx_blocks: u64, + pub stx_attributes_mask: StatxAttributes, + pub stx_atime: StatxTimestamp, + pub stx_btime: StatxTimestamp, + pub stx_ctime: StatxTimestamp, + pub stx_mtime: StatxTimestamp, + pub stx_rdev_major: u32, + pub stx_rdev_minor: u32, + pub stx_dev_major: u32, + pub stx_dev_minor: u32, + pub stx_mnt_id: u64, + pub stx_dio_mem_align: u32, + pub stx_dio_offset_align: u32, + pub stx_subvol: u64, + pub stx_atomic_write_unit_min: u32, + pub stx_atomic_write_unit_max: u32, + pub stx_atomic_write_segments_max: u32, + pub(crate) __spare1: [u32; 1], + pub(crate) __spare3: [u64; 9], +} + +/// `struct statx_timestamp` for use with [`Statx`]. +#[repr(C)] +#[derive(Debug, Copy, Clone)] +#[non_exhaustive] +pub struct StatxTimestamp { + /// Seconds. + pub tv_sec: i64, + + /// Nanoseconds. Must be less than 1_000_000_000. + pub tv_nsec: u32, + + pub(crate) __reserved: i32, +} + +bitflags! { + /// `STATX_*` constants for use with [`statx`]. + /// + /// [`statx`]: crate::fs::statx + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct StatxFlags: u32 { + /// `STATX_TYPE` + const TYPE = c::STATX_TYPE; + + /// `STATX_MODE` + const MODE = c::STATX_MODE; + + /// `STATX_NLINK` + const NLINK = c::STATX_NLINK; + + /// `STATX_UID` + const UID = c::STATX_UID; + + /// `STATX_GID` + const GID = c::STATX_GID; + + /// `STATX_ATIME` + const ATIME = c::STATX_ATIME; + + /// `STATX_MTIME` + const MTIME = c::STATX_MTIME; + + /// `STATX_CTIME` + const CTIME = c::STATX_CTIME; + + /// `STATX_INO` + const INO = c::STATX_INO; + + /// `STATX_SIZE` + const SIZE = c::STATX_SIZE; + + /// `STATX_BLOCKS` + const BLOCKS = c::STATX_BLOCKS; + + /// `STATX_BASIC_STATS` + const BASIC_STATS = c::STATX_BASIC_STATS; + + /// `STATX_BTIME` + const BTIME = c::STATX_BTIME; + + /// `STATX_MNT_ID` (since Linux 5.8) + const MNT_ID = c::STATX_MNT_ID; + + /// `STATX_DIOALIGN` (since Linux 6.1) + const DIOALIGN = c::STATX_DIOALIGN; + + /// `STATX_ALL` + const ALL = c::STATX_ALL; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +bitflags! { + /// `STATX_ATTR_*` flags for use with [`Statx`]. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct StatxAttributes: u64 { + /// `STATX_ATTR_COMPRESSED` + const COMPRESSED = c::STATX_ATTR_COMPRESSED as u64; + + /// `STATX_ATTR_IMMUTABLE` + const IMMUTABLE = c::STATX_ATTR_IMMUTABLE as u64; + + /// `STATX_ATTR_APPEND` + const APPEND = c::STATX_ATTR_APPEND as u64; + + /// `STATX_ATTR_NODUMP` + const NODUMP = c::STATX_ATTR_NODUMP as u64; + + /// `STATX_ATTR_ENCRYPTED` + const ENCRYPTED = c::STATX_ATTR_ENCRYPTED as u64; + + /// `STATX_ATTR_AUTOMOUNT` + const AUTOMOUNT = c::STATX_ATTR_AUTOMOUNT as u64; + + /// `STATX_ATTR_MOUNT_ROOT` + const MOUNT_ROOT = c::STATX_ATTR_MOUNT_ROOT as u64; + + /// `STATX_ATTR_VERITY` + const VERITY = c::STATX_ATTR_VERITY as u64; + + /// `STATX_ATTR_DAX` + const DAX = c::STATX_ATTR_DAX as u64; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +/// `statx(dirfd, path, flags, mask, statxbuf)`—Extended `stat`. +/// +/// This function returns [`io::Errno::NOSYS`] if `statx` is not available on +/// the platform, such as Linux before 4.11. This also includes older Docker +/// versions where the actual syscall fails with different error codes; rustix +/// handles this and translates them into `NOSYS`. +/// +/// # References +/// - [Linux] +/// +/// # Examples +/// +/// ``` +/// # use std::path::Path; +/// # use std::io; +/// # use rustix::fs::{AtFlags, StatxFlags}; +/// # use rustix::fd::BorrowedFd; +/// /// Try to determine if the provided path is a mount root. Will return +/// /// `Ok(None)` if the kernel is not new enough to support `statx` or +/// /// [`StatxAttributes::MOUNT_ROOT`]. +/// fn is_mountpoint(root: BorrowedFd<'_>, path: &Path) -> io::Result<Option<bool>> { +/// use rustix::fs::{AtFlags, StatxAttributes, StatxFlags}; +/// +/// match rustix::fs::statx( +/// root, +/// path, +/// AtFlags::NO_AUTOMOUNT | AtFlags::SYMLINK_NOFOLLOW, +/// StatxFlags::empty(), +/// ) { +/// Ok(r) => { +/// let present = r.stx_attributes_mask.contains(StatxAttributes::MOUNT_ROOT); +/// Ok(present.then(|| r.stx_attributes.contains(StatxAttributes::MOUNT_ROOT))) +/// } +/// Err(rustix::io::Errno::NOSYS) => Ok(None), +/// Err(e) => Err(e.into()), +/// } +/// } +/// ``` +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/statx.2.html +#[inline] +pub fn statx<P: path::Arg, Fd: AsFd>( + dirfd: Fd, + path: P, + flags: AtFlags, + mask: StatxFlags, +) -> io::Result<Statx> { + path.into_with_c_str(|path| _statx(dirfd.as_fd(), path, flags, mask)) +} + +#[cfg(not(feature = "linux_4_11"))] +mod compat { + use crate::fd::BorrowedFd; + use crate::ffi::CStr; + use crate::fs::{AtFlags, Statx, StatxFlags}; + use crate::{backend, io}; + use core::sync::atomic::{AtomicU8, Ordering}; + + // Linux kernel prior to 4.11 and old versions of Docker don't support + // `statx`. We store the availability in a global to avoid unnecessary + // syscalls. + // + // 0: Unknown + // 1: Not available + // 2: Available + static STATX_STATE: AtomicU8 = AtomicU8::new(0); + + #[inline] + pub fn statx( + dirfd: BorrowedFd<'_>, + path: &CStr, + flags: AtFlags, + mask: StatxFlags, + ) -> io::Result<Statx> { + match STATX_STATE.load(Ordering::Relaxed) { + 0 => statx_init(dirfd, path, flags, mask), + 1 => Err(io::Errno::NOSYS), + _ => backend::fs::syscalls::statx(dirfd, path, flags, mask), + } + } + + /// The first `statx` call. We don't know if `statx` is available yet. + fn statx_init( + dirfd: BorrowedFd<'_>, + path: &CStr, + flags: AtFlags, + mask: StatxFlags, + ) -> io::Result<Statx> { + match backend::fs::syscalls::statx(dirfd, path, flags, mask) { + Err(err) => statx_error(err), + result => { + STATX_STATE.store(2, Ordering::Relaxed); + result + } + } + } + + /// The first `statx` call failed. We can get a variety of error codes + /// from seccomp configs or faulty FUSE drivers, so we don't trust + /// `ENOSYS` or `EPERM` to tell us whether statx is available. + #[cold] + fn statx_error(err: io::Errno) -> io::Result<Statx> { + if backend::fs::syscalls::is_statx_available() { + // Statx is available. Record this, and fail with the error + // code of the initial `statx` call. + STATX_STATE.store(2, Ordering::Relaxed); + Err(err) + } else { + // Statx is not available. Record this, and fail with `NOSYS`. + STATX_STATE.store(1, Ordering::Relaxed); + Err(io::Errno::NOSYS) + } + } +} diff --git a/vendor/rustix/src/fs/sync.rs b/vendor/rustix/src/fs/sync.rs new file mode 100644 index 00000000..cb5f0704 --- /dev/null +++ b/vendor/rustix/src/fs/sync.rs @@ -0,0 +1,14 @@ +use crate::backend; + +/// `sync`—Flush cached filesystem data for all filesystems. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/sync.html +/// [Linux]: https://man7.org/linux/man-pages/man2/sync.2.html +#[inline] +pub fn sync() { + backend::fs::syscalls::sync(); +} diff --git a/vendor/rustix/src/fs/xattr.rs b/vendor/rustix/src/fs/xattr.rs new file mode 100644 index 00000000..ea0eaea1 --- /dev/null +++ b/vendor/rustix/src/fs/xattr.rs @@ -0,0 +1,256 @@ +//! Extended attribute functions. + +#![allow(unsafe_code)] + +use crate::buffer::Buffer; +use crate::{backend, ffi, io, path}; +use backend::c; +use backend::fd::AsFd; +use bitflags::bitflags; + +bitflags! { + /// `XATTR_*` constants for use with [`setxattr`], and other `*setxattr` + /// functions. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct XattrFlags: ffi::c_uint { + /// `XATTR_CREATE` + const CREATE = c::XATTR_CREATE as c::c_uint; + + /// `XATTR_REPLACE` + const REPLACE = c::XATTR_REPLACE as c::c_uint; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +/// `getxattr(path, name, value)`—Get extended filesystem attributes. +/// +/// For a higher-level API to xattr functionality, see the [xattr] crate. +/// +/// [xattr]: https://crates.io/crates/xattr +/// +/// # References +/// - [Linux] +/// - [Apple] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/getxattr.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getxattr.2.html +#[inline] +pub fn getxattr<P: path::Arg, Name: path::Arg, Buf: Buffer<u8>>( + path: P, + name: Name, + mut value: Buf, +) -> io::Result<Buf::Output> { + path.into_with_c_str(|path| { + name.into_with_c_str(|name| { + // SAFETY: `getxattr` behaves. + let len = unsafe { backend::fs::syscalls::getxattr(path, name, value.parts_mut())? }; + // SAFETY: `getxattr` behaves. + unsafe { Ok(value.assume_init(len)) } + }) + }) +} + +/// `lgetxattr(path, name, value.as_ptr(), value.len())`—Get extended +/// filesystem attributes, without following symlinks in the last path +/// component. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/lgetxattr.2.html +#[inline] +pub fn lgetxattr<P: path::Arg, Name: path::Arg, Buf: Buffer<u8>>( + path: P, + name: Name, + mut value: Buf, +) -> io::Result<Buf::Output> { + path.into_with_c_str(|path| { + name.into_with_c_str(|name| { + // SAFETY: `lgetxattr` behaves. + let len = unsafe { backend::fs::syscalls::lgetxattr(path, name, value.parts_mut())? }; + // SAFETY: `lgetxattr` behaves. + unsafe { Ok(value.assume_init(len)) } + }) + }) +} + +/// `fgetxattr(fd, name, value.as_ptr(), value.len())`—Get extended +/// filesystem attributes on an open file descriptor. +/// +/// # References +/// - [Linux] +/// - [Apple] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/fgetxattr.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fgetxattr.2.html +#[inline] +pub fn fgetxattr<Fd: AsFd, Name: path::Arg, Buf: Buffer<u8>>( + fd: Fd, + name: Name, + mut value: Buf, +) -> io::Result<Buf::Output> { + name.into_with_c_str(|name| { + // SAFETY: `fgetxattr` behaves. + let len = unsafe { backend::fs::syscalls::fgetxattr(fd.as_fd(), name, value.parts_mut())? }; + // SAFETY: `fgetxattr` behaves. + unsafe { Ok(value.assume_init(len)) } + }) +} + +/// `setxattr(path, name, value.as_ptr(), value.len(), flags)`—Set extended +/// filesystem attributes. +/// +/// # References +/// - [Linux] +/// - [Apple] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/setxattr.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setxattr.2.html +#[inline] +pub fn setxattr<P: path::Arg, Name: path::Arg>( + path: P, + name: Name, + value: &[u8], + flags: XattrFlags, +) -> io::Result<()> { + path.into_with_c_str(|path| { + name.into_with_c_str(|name| backend::fs::syscalls::setxattr(path, name, value, flags)) + }) +} + +/// `setxattr(path, name, value.as_ptr(), value.len(), flags)`—Set extended +/// filesystem attributes, without following symlinks in the last path +/// component. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/lsetxattr.2.html +#[inline] +pub fn lsetxattr<P: path::Arg, Name: path::Arg>( + path: P, + name: Name, + value: &[u8], + flags: XattrFlags, +) -> io::Result<()> { + path.into_with_c_str(|path| { + name.into_with_c_str(|name| backend::fs::syscalls::lsetxattr(path, name, value, flags)) + }) +} + +/// `fsetxattr(fd, name, value.as_ptr(), value.len(), flags)`—Set extended +/// filesystem attributes on an open file descriptor. +/// +/// # References +/// - [Linux] +/// - [Apple] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/fsetxattr.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fsetxattr.2.html +#[inline] +pub fn fsetxattr<Fd: AsFd, Name: path::Arg>( + fd: Fd, + name: Name, + value: &[u8], + flags: XattrFlags, +) -> io::Result<()> { + name.into_with_c_str(|name| backend::fs::syscalls::fsetxattr(fd.as_fd(), name, value, flags)) +} + +/// `listxattr(path, list.as_ptr(), list.len())`—List extended filesystem +/// attributes. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/listxattr.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/listxattr.2.html +#[inline] +pub fn listxattr<P: path::Arg, Buf: Buffer<u8>>(path: P, mut list: Buf) -> io::Result<Buf::Output> { + path.into_with_c_str(|path| { + // SAFETY: `listxattr` behaves. + let len = unsafe { backend::fs::syscalls::listxattr(path, list.parts_mut())? }; + // SAFETY: `listxattr` behaves. + unsafe { Ok(list.assume_init(len)) } + }) +} + +/// `llistxattr(path, list.as_ptr(), list.len())`—List extended filesystem +/// attributes, without following symlinks in the last path component. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/llistxattr.2.html +#[inline] +pub fn llistxattr<P: path::Arg, Buf: Buffer<u8>>( + path: P, + mut list: Buf, +) -> io::Result<Buf::Output> { + path.into_with_c_str(|path| { + // SAFETY: `flistxattr` behaves. + let len = unsafe { backend::fs::syscalls::llistxattr(path, list.parts_mut())? }; + // SAFETY: `flistxattr` behaves. + unsafe { Ok(list.assume_init(len)) } + }) +} + +/// `flistxattr(fd, list.as_ptr(), list.len())`—List extended filesystem +/// attributes on an open file descriptor. +/// +/// # References +/// - [Linux] +/// - [Apple] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/flistxattr.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/flistxattr.2.html +#[inline] +pub fn flistxattr<Fd: AsFd, Buf: Buffer<u8>>(fd: Fd, mut list: Buf) -> io::Result<Buf::Output> { + // SAFETY: `flistxattr` behaves. + let len = unsafe { backend::fs::syscalls::flistxattr(fd.as_fd(), list.parts_mut())? }; + // SAFETY: `flistxattr` behaves. + unsafe { Ok(list.assume_init(len)) } +} + +/// `removexattr(path, name)`—Remove an extended filesystem attribute. +/// +/// # References +/// - [Linux] +/// - [Apple] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/removexattr.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/removexattr.2.html +pub fn removexattr<P: path::Arg, Name: path::Arg>(path: P, name: Name) -> io::Result<()> { + path.into_with_c_str(|path| { + name.into_with_c_str(|name| backend::fs::syscalls::removexattr(path, name)) + }) +} + +/// `lremovexattr(path, name)`—Remove an extended filesystem attribute, +/// without following symlinks in the last path component. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/lremovexattr.2.html +pub fn lremovexattr<P: path::Arg, Name: path::Arg>(path: P, name: Name) -> io::Result<()> { + path.into_with_c_str(|path| { + name.into_with_c_str(|name| backend::fs::syscalls::lremovexattr(path, name)) + }) +} + +/// `fremovexattr(fd, name)`—Remove an extended filesystem attribute on an +/// open file descriptor. +/// +/// # References +/// - [Linux] +/// - [Apple] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/fremovexattr.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fremovexattr.2.html +pub fn fremovexattr<Fd: AsFd, Name: path::Arg>(fd: Fd, name: Name) -> io::Result<()> { + name.into_with_c_str(|name| backend::fs::syscalls::fremovexattr(fd.as_fd(), name)) +} |
