diff options
Diffstat (limited to 'vendor/rustix/src/backend/linux_raw/fs')
| -rw-r--r-- | vendor/rustix/src/backend/linux_raw/fs/dir.rs | 315 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/linux_raw/fs/inotify.rs | 118 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/linux_raw/fs/makedev.rs | 19 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/linux_raw/fs/mod.rs | 13 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/linux_raw/fs/syscalls.rs | 1670 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/linux_raw/fs/types.rs | 739 | 
6 files changed, 2874 insertions, 0 deletions
diff --git a/vendor/rustix/src/backend/linux_raw/fs/dir.rs b/vendor/rustix/src/backend/linux_raw/fs/dir.rs new file mode 100644 index 0000000..dbddd58 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/fs/dir.rs @@ -0,0 +1,315 @@ +use crate::fd::{AsFd, BorrowedFd, OwnedFd}; +use crate::ffi::{CStr, CString}; +use crate::fs::{ +    fcntl_getfl, fstat, fstatfs, fstatvfs, openat, FileType, Mode, OFlags, Stat, StatFs, StatVfs, +}; +use crate::io; +#[cfg(feature = "process")] +use crate::process::fchdir; +use crate::utils::as_ptr; +use alloc::borrow::ToOwned; +use alloc::vec::Vec; +use core::fmt; +use core::mem::size_of; +use linux_raw_sys::general::{linux_dirent64, SEEK_SET}; + +/// `DIR*` +pub struct Dir { +    /// The `OwnedFd` that we read directory entries from. +    fd: OwnedFd, + +    /// Have we seen any errors in this iteration? +    any_errors: bool, + +    /// Should we rewind the stream on the next iteration? +    rewind: bool, + +    /// The buffer for `linux_dirent64` entries. +    buf: Vec<u8>, + +    /// Where we are in the buffer. +    pos: usize, +} + +impl Dir { +    /// Take ownership of `fd` and construct a `Dir` that reads entries from +    /// the given directory file descriptor. +    #[inline] +    pub fn new<Fd: Into<OwnedFd>>(fd: Fd) -> io::Result<Self> { +        Self::_new(fd.into()) +    } + +    #[inline] +    fn _new(fd: OwnedFd) -> io::Result<Self> { +        Ok(Self { +            fd, +            any_errors: false, +            rewind: false, +            buf: Vec::new(), +            pos: 0, +        }) +    } + +    /// Borrow `fd` and construct a `Dir` that reads entries from the given +    /// directory file descriptor. +    #[inline] +    pub fn read_from<Fd: AsFd>(fd: Fd) -> io::Result<Self> { +        Self::_read_from(fd.as_fd()) +    } + +    #[inline] +    fn _read_from(fd: BorrowedFd<'_>) -> io::Result<Self> { +        let flags = fcntl_getfl(fd)?; +        let fd_for_dir = openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty())?; + +        Ok(Self { +            fd: fd_for_dir, +            any_errors: false, +            rewind: false, +            buf: Vec::new(), +            pos: 0, +        }) +    } + +    /// `rewinddir(self)` +    #[inline] +    pub fn rewind(&mut self) { +        self.any_errors = false; +        self.rewind = true; +        self.pos = self.buf.len(); +    } + +    /// `readdir(self)`, where `None` means the end of the directory. +    pub fn read(&mut self) -> Option<io::Result<DirEntry>> { +        // If we've seen errors, don't continue to try to read anyting further. +        if self.any_errors { +            return None; +        } + +        // If a rewind was requested, seek to the beginning. +        if self.rewind { +            self.rewind = false; +            match io::retry_on_intr(|| { +                crate::backend::fs::syscalls::_seek(self.fd.as_fd(), 0, SEEK_SET) +            }) { +                Ok(_) => (), +                Err(err) => { +                    self.any_errors = true; +                    return Some(Err(err)); +                } +            } +        } + +        // Compute linux_dirent64 field offsets. +        let z = linux_dirent64 { +            d_ino: 0_u64, +            d_off: 0_i64, +            d_type: 0_u8, +            d_reclen: 0_u16, +            d_name: Default::default(), +        }; +        let base = as_ptr(&z) as usize; +        let offsetof_d_reclen = (as_ptr(&z.d_reclen) as usize) - base; +        let offsetof_d_name = (as_ptr(&z.d_name) as usize) - base; +        let offsetof_d_ino = (as_ptr(&z.d_ino) as usize) - base; +        let offsetof_d_type = (as_ptr(&z.d_type) as usize) - base; + +        // Test if we need more entries, and if so, read more. +        if self.buf.len() - self.pos < size_of::<linux_dirent64>() { +            match self.read_more()? { +                Ok(()) => (), +                Err(err) => return Some(Err(err)), +            } +        } + +        // We successfully read an entry. Extract the fields. +        let pos = self.pos; + +        // Do an unaligned u16 load. +        let d_reclen = u16::from_ne_bytes([ +            self.buf[pos + offsetof_d_reclen], +            self.buf[pos + offsetof_d_reclen + 1], +        ]); +        assert!(self.buf.len() - pos >= d_reclen as usize); +        self.pos += d_reclen as usize; + +        // Read the NUL-terminated name from the `d_name` field. Without +        // `unsafe`, we need to scan for the NUL twice: once to obtain a size +        // for the slice, and then once within `CStr::from_bytes_with_nul`. +        let name_start = pos + offsetof_d_name; +        let name_len = self.buf[name_start..] +            .iter() +            .position(|x| *x == b'\0') +            .unwrap(); +        let name = CStr::from_bytes_with_nul(&self.buf[name_start..][..=name_len]).unwrap(); +        let name = name.to_owned(); +        assert!(name.as_bytes().len() <= self.buf.len() - name_start); + +        // Do an unaligned u64 load. +        let d_ino = u64::from_ne_bytes([ +            self.buf[pos + offsetof_d_ino], +            self.buf[pos + offsetof_d_ino + 1], +            self.buf[pos + offsetof_d_ino + 2], +            self.buf[pos + offsetof_d_ino + 3], +            self.buf[pos + offsetof_d_ino + 4], +            self.buf[pos + offsetof_d_ino + 5], +            self.buf[pos + offsetof_d_ino + 6], +            self.buf[pos + offsetof_d_ino + 7], +        ]); + +        let d_type = self.buf[pos + offsetof_d_type]; + +        // Check that our types correspond to the `linux_dirent64` types. +        let _ = linux_dirent64 { +            d_ino, +            d_off: 0, +            d_type, +            d_reclen, +            d_name: Default::default(), +        }; + +        Some(Ok(DirEntry { +            d_ino, +            d_type, +            name, +        })) +    } + +    #[must_use] +    fn read_more(&mut self) -> Option<io::Result<()>> { +        // The first few times we're called, we allocate a relatively small +        // buffer, because many directories are small. If we're called more, +        // use progressively larger allocations, up to a fixed maximum. +        // +        // The specific sizes and policy here have not been tuned in detail yet +        // and may need to be adjusted. In doing so, we should be careful to +        // avoid unbounded buffer growth. This buffer only exists to share the +        // cost of a `getdents` call over many entries, so if it gets too big, +        // cache and heap usage will outweigh the benefit. And ultimately, +        // directories can contain more entries than we can allocate contiguous +        // memory for, so we'll always need to cap the size at some point. +        if self.buf.len() < 1024 * size_of::<linux_dirent64>() { +            self.buf.reserve(32 * size_of::<linux_dirent64>()); +        } +        self.buf.resize(self.buf.capacity(), 0); +        let nread = match io::retry_on_intr(|| { +            crate::backend::fs::syscalls::getdents(self.fd.as_fd(), &mut self.buf) +        }) { +            Ok(nread) => nread, +            Err(io::Errno::NOENT) => { +                self.any_errors = true; +                return None; +            } +            Err(err) => { +                self.any_errors = true; +                return Some(Err(err)); +            } +        }; +        self.buf.resize(nread, 0); +        self.pos = 0; +        if nread == 0 { +            None +        } else { +            Some(Ok(())) +        } +    } + +    /// `fstat(self)` +    #[inline] +    pub fn stat(&self) -> io::Result<Stat> { +        fstat(&self.fd) +    } + +    /// `fstatfs(self)` +    #[inline] +    pub fn statfs(&self) -> io::Result<StatFs> { +        fstatfs(&self.fd) +    } + +    /// `fstatvfs(self)` +    #[inline] +    pub fn statvfs(&self) -> io::Result<StatVfs> { +        fstatvfs(&self.fd) +    } + +    /// `fchdir(self)` +    #[cfg(feature = "process")] +    #[cfg_attr(doc_cfg, doc(cfg(feature = "process")))] +    #[inline] +    pub fn chdir(&self) -> io::Result<()> { +        fchdir(&self.fd) +    } +} + +impl Iterator for Dir { +    type Item = io::Result<DirEntry>; + +    #[inline] +    fn next(&mut self) -> Option<Self::Item> { +        Self::read(self) +    } +} + +impl fmt::Debug for Dir { +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +        f.debug_struct("Dir").field("fd", &self.fd).finish() +    } +} + +/// `struct dirent` +#[derive(Debug)] +pub struct DirEntry { +    d_ino: u64, +    d_type: u8, +    name: CString, +} + +impl DirEntry { +    /// Returns the file name of this directory entry. +    #[inline] +    pub fn file_name(&self) -> &CStr { +        &self.name +    } + +    /// Returns the type of this directory entry. +    #[inline] +    pub fn file_type(&self) -> FileType { +        FileType::from_dirent_d_type(self.d_type) +    } + +    /// Return the inode number of this directory entry. +    #[inline] +    pub fn ino(&self) -> u64 { +        self.d_ino +    } +} + +#[test] +fn dir_iterator_handles_io_errors() { +    // create a dir, keep the FD, then delete the dir +    let tmp = tempfile::tempdir().unwrap(); +    let fd = crate::fs::openat( +        crate::fs::CWD, +        tmp.path(), +        crate::fs::OFlags::RDONLY | crate::fs::OFlags::CLOEXEC, +        crate::fs::Mode::empty(), +    ) +    .unwrap(); + +    let file_fd = crate::fs::openat( +        &fd, +        tmp.path().join("test.txt"), +        crate::fs::OFlags::WRONLY | crate::fs::OFlags::CREATE, +        crate::fs::Mode::RWXU, +    ) +    .unwrap(); + +    let mut dir = Dir::read_from(&fd).unwrap(); + +    // Reach inside the `Dir` and replace its directory with a file, which +    // will cause the subsequent `getdents64` to fail. +    crate::io::dup2(&file_fd, &mut dir.fd).unwrap(); + +    assert!(matches!(dir.next(), Some(Err(_)))); +    assert!(dir.next().is_none()); +} diff --git a/vendor/rustix/src/backend/linux_raw/fs/inotify.rs b/vendor/rustix/src/backend/linux_raw/fs/inotify.rs new file mode 100644 index 0000000..851335b --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/fs/inotify.rs @@ -0,0 +1,118 @@ +//! inotify support for working with inotifies + +use crate::backend::c; +use crate::backend::fs::syscalls; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::io; +use bitflags::bitflags; + +bitflags! { +    /// `IN_*` for use with [`inotify_init`]. +    /// +    /// [`inotify_init`]: crate::fs::inotify::inotify_init +    #[repr(transparent)] +    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +    pub struct CreateFlags: c::c_uint { +        /// `IN_CLOEXEC` +        const CLOEXEC = linux_raw_sys::general::IN_CLOEXEC; +        /// `IN_NONBLOCK` +        const NONBLOCK = linux_raw_sys::general::IN_NONBLOCK; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +bitflags! { +    /// `IN*` for use with [`inotify_add_watch`]. +    /// +    /// [`inotify_add_watch`]: crate::fs::inotify::inotify_add_watch +    #[repr(transparent)] +    #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] +    pub struct WatchFlags: c::c_uint { +        /// `IN_ACCESS` +        const ACCESS = linux_raw_sys::general::IN_ACCESS; +        /// `IN_ATTRIB` +        const ATTRIB = linux_raw_sys::general::IN_ATTRIB; +        /// `IN_CLOSE_NOWRITE` +        const CLOSE_NOWRITE = linux_raw_sys::general::IN_CLOSE_NOWRITE; +        /// `IN_CLOSE_WRITE` +        const CLOSE_WRITE = linux_raw_sys::general::IN_CLOSE_WRITE; +        /// `IN_CREATE` +        const CREATE = linux_raw_sys::general::IN_CREATE; +        /// `IN_DELETE` +        const DELETE = linux_raw_sys::general::IN_DELETE; +        /// `IN_DELETE_SELF` +        const DELETE_SELF = linux_raw_sys::general::IN_DELETE_SELF; +        /// `IN_MODIFY` +        const MODIFY = linux_raw_sys::general::IN_MODIFY; +        /// `IN_MOVE_SELF` +        const MOVE_SELF = linux_raw_sys::general::IN_MOVE_SELF; +        /// `IN_MOVED_FROM` +        const MOVED_FROM = linux_raw_sys::general::IN_MOVED_FROM; +        /// `IN_MOVED_TO` +        const MOVED_TO = linux_raw_sys::general::IN_MOVED_TO; +        /// `IN_OPEN` +        const OPEN = linux_raw_sys::general::IN_OPEN; + +        /// `IN_CLOSE` +        const CLOSE = linux_raw_sys::general::IN_CLOSE; +        /// `IN_MOVE` +        const MOVE = linux_raw_sys::general::IN_MOVE; +        /// `IN_ALL_EVENTS` +        const ALL_EVENTS = linux_raw_sys::general::IN_ALL_EVENTS; + +        /// `IN_DONT_FOLLOW` +        const DONT_FOLLOW = linux_raw_sys::general::IN_DONT_FOLLOW; +        /// `IN_EXCL_UNLINK` +        const EXCL_UNLINK = linux_raw_sys::general::IN_EXCL_UNLINK; +        /// `IN_MASK_ADD` +        const MASK_ADD = linux_raw_sys::general::IN_MASK_ADD; +        /// `IN_MASK_CREATE` +        const MASK_CREATE = linux_raw_sys::general::IN_MASK_CREATE; +        /// `IN_ONESHOT` +        const ONESHOT = linux_raw_sys::general::IN_ONESHOT; +        /// `IN_ONLYDIR` +        const ONLYDIR = linux_raw_sys::general::IN_ONLYDIR; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +/// `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 inotify_init(flags: 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. +#[inline] +pub fn inotify_add_watch<P: crate::path::Arg>( +    inot: BorrowedFd<'_>, +    path: P, +    flags: WatchFlags, +) -> io::Result<i32> { +    path.into_with_c_str(|path| syscalls::inotify_add_watch(inot, 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 inotify_remove_watch(inot: BorrowedFd<'_>, wd: i32) -> io::Result<()> { +    syscalls::inotify_rm_watch(inot, wd) +} diff --git a/vendor/rustix/src/backend/linux_raw/fs/makedev.rs b/vendor/rustix/src/backend/linux_raw/fs/makedev.rs new file mode 100644 index 0000000..284ba2f --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/fs/makedev.rs @@ -0,0 +1,19 @@ +use crate::fs::Dev; + +#[inline] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { +    ((u64::from(maj) & 0xffff_f000_u64) << 32) +        | ((u64::from(maj) & 0x0000_0fff_u64) << 8) +        | ((u64::from(min) & 0xffff_ff00_u64) << 12) +        | (u64::from(min) & 0x0000_00ff_u64) +} + +#[inline] +pub(crate) fn major(dev: Dev) -> u32 { +    (((dev >> 31 >> 1) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)) as u32 +} + +#[inline] +pub(crate) fn minor(dev: Dev) -> u32 { +    (((dev >> 12) & 0xffff_ff00) | (dev & 0x0000_00ff)) as u32 +} diff --git a/vendor/rustix/src/backend/linux_raw/fs/mod.rs b/vendor/rustix/src/backend/linux_raw/fs/mod.rs new file mode 100644 index 0000000..9f53c5d --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/fs/mod.rs @@ -0,0 +1,13 @@ +#[cfg(feature = "alloc")] +pub(crate) mod dir; +pub mod inotify; +pub(crate) mod makedev; +pub(crate) mod syscalls; +pub(crate) mod types; + +// TODO: Fix linux-raw-sys to define ioctl codes for sparc. +#[cfg(any(target_arch = "sparc", target_arch = "sparc64"))] +pub(crate) const EXT4_IOC_RESIZE_FS: u32 = 0x8008_6610; + +#[cfg(not(any(target_arch = "sparc", target_arch = "sparc64")))] +pub(crate) use linux_raw_sys::ioctl::EXT4_IOC_RESIZE_FS; diff --git a/vendor/rustix/src/backend/linux_raw/fs/syscalls.rs b/vendor/rustix/src/backend/linux_raw/fs/syscalls.rs new file mode 100644 index 0000000..47d01df --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/fs/syscalls.rs @@ -0,0 +1,1670 @@ +//! linux_raw syscalls supporting `rustix::fs`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code)] +#![allow(clippy::undocumented_unsafe_blocks)] + +use crate::backend::c; +use crate::backend::conv::fs::oflags_for_open_how; +#[cfg(any( +    not(feature = "linux_4_11"), +    target_arch = "aarch64", +    target_arch = "riscv64", +    target_arch = "mips", +    target_arch = "mips32r6", +))] +use crate::backend::conv::zero; +use crate::backend::conv::{ +    by_ref, c_int, c_uint, dev_t, opt_mut, pass_usize, raw_fd, ret, ret_c_int, ret_c_uint, +    ret_infallible, ret_owned_fd, ret_usize, size_of, slice, slice_mut, +}; +#[cfg(target_pointer_width = "64")] +use crate::backend::conv::{loff_t, loff_t_from_u64, ret_u64}; +#[cfg(any( +    target_arch = "aarch64", +    target_arch = "riscv64", +    target_arch = "mips64", +    target_arch = "mips64r6", +    target_pointer_width = "32", +))] +use crate::fd::AsFd; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::ffi::CStr; +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +use crate::fs::CWD; +use crate::fs::{ +    inotify, Access, Advice, AtFlags, FallocateFlags, FileType, FlockOperation, Gid, MemfdFlags, +    Mode, OFlags, RenameFlags, ResolveFlags, SealFlags, SeekFrom, Stat, StatFs, StatVfs, +    StatVfsMountFlags, StatxFlags, Timestamps, Uid, XattrFlags, +}; +use crate::io; +use core::mem::MaybeUninit; +#[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] +use linux_raw_sys::general::stat as linux_stat64; +use linux_raw_sys::general::{ +    __kernel_fsid_t, open_how, statx, AT_EACCESS, AT_FDCWD, AT_REMOVEDIR, AT_SYMLINK_NOFOLLOW, +    F_ADD_SEALS, F_GETFL, F_GET_SEALS, F_SETFL, SEEK_CUR, SEEK_DATA, SEEK_END, SEEK_HOLE, SEEK_SET, +    STATX__RESERVED, +}; +#[cfg(target_pointer_width = "32")] +use { +    crate::backend::conv::{hi, lo, slice_just_addr}, +    linux_raw_sys::general::stat64 as linux_stat64, +    linux_raw_sys::general::timespec as __kernel_old_timespec, +}; + +#[inline] +pub(crate) fn open(path: &CStr, flags: OFlags, mode: Mode) -> io::Result<OwnedFd> { +    // Always enable support for large files. +    let flags = flags | OFlags::from_bits_retain(c::O_LARGEFILE); + +    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +    { +        openat(CWD.as_fd(), path, flags, mode) +    } +    #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] +    unsafe { +        ret_owned_fd(syscall_readonly!(__NR_open, path, flags, mode)) +    } +} + +#[inline] +pub(crate) fn openat( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    flags: OFlags, +    mode: Mode, +) -> io::Result<OwnedFd> { +    // Always enable support for large files. +    let flags = flags | OFlags::from_bits_retain(c::O_LARGEFILE); + +    unsafe { ret_owned_fd(syscall_readonly!(__NR_openat, dirfd, path, flags, mode)) } +} + +#[inline] +pub(crate) fn openat2( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    mut flags: OFlags, +    mode: Mode, +    resolve: ResolveFlags, +) -> io::Result<OwnedFd> { +    // Enable support for large files, but not with `O_PATH` because +    // `openat2` doesn't like those flags together. +    if !flags.contains(OFlags::PATH) { +        flags |= OFlags::from_bits_retain(c::O_LARGEFILE); +    } + +    unsafe { +        ret_owned_fd(syscall_readonly!( +            __NR_openat2, +            dirfd, +            path, +            by_ref(&open_how { +                flags: oflags_for_open_how(flags), +                mode: u64::from(mode.bits()), +                resolve: resolve.bits(), +            }), +            size_of::<open_how, _>() +        )) +    } +} + +#[inline] +pub(crate) fn chmod(path: &CStr, mode: Mode) -> io::Result<()> { +    unsafe { +        ret(syscall_readonly!( +            __NR_fchmodat, +            raw_fd(AT_FDCWD), +            path, +            mode +        )) +    } +} + +#[inline] +pub(crate) fn chmodat( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    mode: Mode, +    flags: AtFlags, +) -> io::Result<()> { +    if flags == AtFlags::SYMLINK_NOFOLLOW { +        return Err(io::Errno::OPNOTSUPP); +    } +    if !flags.is_empty() { +        return Err(io::Errno::INVAL); +    } +    unsafe { ret(syscall_readonly!(__NR_fchmodat, dirfd, path, mode)) } +} + +#[inline] +pub(crate) fn fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()> { +    unsafe { ret(syscall_readonly!(__NR_fchmod, fd, mode)) } +} + +#[inline] +pub(crate) fn chownat( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    owner: Option<Uid>, +    group: Option<Gid>, +    flags: AtFlags, +) -> io::Result<()> { +    unsafe { +        let (ow, gr) = crate::ugid::translate_fchown_args(owner, group); +        ret(syscall_readonly!( +            __NR_fchownat, +            dirfd, +            path, +            c_uint(ow), +            c_uint(gr), +            flags +        )) +    } +} + +#[inline] +pub(crate) fn chown(path: &CStr, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> { +    // Most architectures have a `chown` syscall. +    #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] +    unsafe { +        let (ow, gr) = crate::ugid::translate_fchown_args(owner, group); +        ret(syscall_readonly!(__NR_chown, path, c_uint(ow), c_uint(gr))) +    } + +    // Aarch64 and RISC-V don't, so use `fchownat`. +    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +    unsafe { +        let (ow, gr) = crate::ugid::translate_fchown_args(owner, group); +        ret(syscall_readonly!( +            __NR_fchownat, +            raw_fd(AT_FDCWD), +            path, +            c_uint(ow), +            c_uint(gr), +            zero() +        )) +    } +} + +#[inline] +pub(crate) fn fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> { +    unsafe { +        let (ow, gr) = crate::ugid::translate_fchown_args(owner, group); +        ret(syscall_readonly!(__NR_fchown, fd, c_uint(ow), c_uint(gr))) +    } +} + +#[inline] +pub(crate) fn mknodat( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    file_type: FileType, +    mode: Mode, +    dev: u64, +) -> io::Result<()> { +    #[cfg(target_pointer_width = "32")] +    unsafe { +        ret(syscall_readonly!( +            __NR_mknodat, +            dirfd, +            path, +            (mode, file_type), +            dev_t(dev)? +        )) +    } +    #[cfg(target_pointer_width = "64")] +    unsafe { +        ret(syscall_readonly!( +            __NR_mknodat, +            dirfd, +            path, +            (mode, file_type), +            dev_t(dev) +        )) +    } +} + +#[inline] +pub(crate) fn seek(fd: BorrowedFd<'_>, pos: SeekFrom) -> io::Result<u64> { +    let (whence, offset) = match pos { +        SeekFrom::Start(pos) => { +            let pos: u64 = pos; +            // Silently cast; we'll get `EINVAL` if the value is negative. +            (SEEK_SET, pos as i64) +        } +        SeekFrom::End(offset) => (SEEK_END, offset), +        SeekFrom::Current(offset) => (SEEK_CUR, offset), +        SeekFrom::Data(offset) => (SEEK_DATA, offset), +        SeekFrom::Hole(offset) => (SEEK_HOLE, offset), +    }; +    _seek(fd, offset, whence) +} + +#[inline] +pub(crate) fn _seek(fd: BorrowedFd<'_>, offset: i64, whence: c::c_uint) -> io::Result<u64> { +    #[cfg(target_pointer_width = "32")] +    unsafe { +        let mut result = MaybeUninit::<u64>::uninit(); +        ret(syscall!( +            __NR__llseek, +            fd, +            // Don't use the hi/lo functions here because Linux's llseek +            // takes its 64-bit argument differently from everything else. +            pass_usize((offset >> 32) as usize), +            pass_usize(offset as usize), +            &mut result, +            c_uint(whence) +        ))?; +        Ok(result.assume_init()) +    } +    #[cfg(target_pointer_width = "64")] +    unsafe { +        ret_u64(syscall_readonly!( +            __NR_lseek, +            fd, +            loff_t(offset), +            c_uint(whence) +        )) +    } +} + +#[inline] +pub(crate) fn tell(fd: BorrowedFd<'_>) -> io::Result<u64> { +    _seek(fd, 0, SEEK_CUR).map(|x| x as u64) +} + +#[inline] +pub(crate) fn ftruncate(fd: BorrowedFd<'_>, length: u64) -> io::Result<()> { +    // <https://github.com/torvalds/linux/blob/fcadab740480e0e0e9fa9bd272acd409884d431a/arch/arm64/kernel/sys32.c#L81-L83> +    #[cfg(all( +        target_pointer_width = "32", +        any( +            target_arch = "arm", +            target_arch = "mips", +            target_arch = "mips32r6", +            target_arch = "powerpc" +        ), +    ))] +    unsafe { +        ret(syscall_readonly!( +            __NR_ftruncate64, +            fd, +            zero(), +            hi(length), +            lo(length) +        )) +    } +    #[cfg(all( +        target_pointer_width = "32", +        not(any( +            target_arch = "arm", +            target_arch = "mips", +            target_arch = "mips32r6", +            target_arch = "powerpc" +        )), +    ))] +    unsafe { +        ret(syscall_readonly!( +            __NR_ftruncate64, +            fd, +            hi(length), +            lo(length) +        )) +    } +    #[cfg(target_pointer_width = "64")] +    unsafe { +        ret(syscall_readonly!( +            __NR_ftruncate, +            fd, +            loff_t_from_u64(length) +        )) +    } +} + +#[inline] +pub(crate) fn fallocate( +    fd: BorrowedFd<'_>, +    mode: FallocateFlags, +    offset: u64, +    len: u64, +) -> io::Result<()> { +    #[cfg(target_pointer_width = "32")] +    unsafe { +        ret(syscall_readonly!( +            __NR_fallocate, +            fd, +            mode, +            hi(offset), +            lo(offset), +            hi(len), +            lo(len) +        )) +    } +    #[cfg(target_pointer_width = "64")] +    unsafe { +        ret(syscall_readonly!( +            __NR_fallocate, +            fd, +            mode, +            loff_t_from_u64(offset), +            loff_t_from_u64(len) +        )) +    } +} + +#[inline] +pub(crate) fn fadvise(fd: BorrowedFd<'_>, pos: u64, len: u64, advice: Advice) -> io::Result<()> { +    // On ARM, the arguments are reordered so that the len and pos argument +    // pairs are aligned. And ARM has a custom syscall code for this. +    #[cfg(target_arch = "arm")] +    unsafe { +        ret(syscall_readonly!( +            __NR_arm_fadvise64_64, +            fd, +            advice, +            hi(pos), +            lo(pos), +            hi(len), +            lo(len) +        )) +    } + +    // On powerpc, the arguments are reordered as on ARM. +    #[cfg(target_arch = "powerpc")] +    unsafe { +        ret(syscall_readonly!( +            __NR_fadvise64_64, +            fd, +            advice, +            hi(pos), +            lo(pos), +            hi(len), +            lo(len) +        )) +    } + +    // On mips, the arguments are not reordered, and padding is inserted +    // instead to ensure alignment. +    #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))] +    unsafe { +        ret(syscall_readonly!( +            __NR_fadvise64, +            fd, +            zero(), +            hi(pos), +            lo(pos), +            hi(len), +            lo(len), +            advice +        )) +    } + +    // For all other 32-bit architectures, use `fadvise64_64` so that we get a +    // 64-bit length. +    #[cfg(all( +        target_pointer_width = "32", +        not(any( +            target_arch = "arm", +            target_arch = "mips", +            target_arch = "mips32r6", +            target_arch = "powerpc" +        )), +    ))] +    unsafe { +        ret(syscall_readonly!( +            __NR_fadvise64_64, +            fd, +            hi(pos), +            lo(pos), +            hi(len), +            lo(len), +            advice +        )) +    } + +    // On 64-bit architectures, use `fadvise64` which is sufficient. +    #[cfg(target_pointer_width = "64")] +    unsafe { +        ret(syscall_readonly!( +            __NR_fadvise64, +            fd, +            loff_t_from_u64(pos), +            loff_t_from_u64(len), +            advice +        )) +    } +} + +#[inline] +pub(crate) fn fsync(fd: BorrowedFd<'_>) -> io::Result<()> { +    unsafe { ret(syscall_readonly!(__NR_fsync, fd)) } +} + +#[inline] +pub(crate) fn fdatasync(fd: BorrowedFd<'_>) -> io::Result<()> { +    unsafe { ret(syscall_readonly!(__NR_fdatasync, fd)) } +} + +#[inline] +pub(crate) fn flock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> { +    unsafe { +        ret(syscall_readonly!( +            __NR_flock, +            fd, +            c_uint(operation as c::c_uint) +        )) +    } +} + +#[inline] +pub(crate) fn syncfs(fd: BorrowedFd<'_>) -> io::Result<()> { +    unsafe { ret(syscall_readonly!(__NR_syncfs, fd)) } +} + +#[inline] +pub(crate) fn sync() { +    unsafe { ret_infallible(syscall_readonly!(__NR_sync)) } +} + +#[inline] +pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result<Stat> { +    // 32-bit and mips64 Linux: `struct stat64` is not y2038 compatible; use +    // `statx`. +    // +    // And, some old platforms don't support `statx`, and some fail with a +    // confusing error code, so we call `crate::fs::statx` to handle that. If +    // `statx` isn't available, fall back to the buggy system call. +    #[cfg(any( +        target_pointer_width = "32", +        target_arch = "mips64", +        target_arch = "mips64r6" +    ))] +    { +        match crate::fs::statx(fd, cstr!(""), AtFlags::EMPTY_PATH, StatxFlags::BASIC_STATS) { +            Ok(x) => statx_to_stat(x), +            Err(io::Errno::NOSYS) => fstat_old(fd), +            Err(err) => Err(err), +        } +    } + +    #[cfg(all( +        target_pointer_width = "64", +        not(target_arch = "mips64"), +        not(target_arch = "mips64r6") +    ))] +    unsafe { +        let mut result = MaybeUninit::<Stat>::uninit(); +        ret(syscall!(__NR_fstat, fd, &mut result))?; +        Ok(result.assume_init()) +    } +} + +#[cfg(any( +    target_pointer_width = "32", +    target_arch = "mips64", +    target_arch = "mips64r6", +))] +fn fstat_old(fd: BorrowedFd<'_>) -> io::Result<Stat> { +    let mut result = MaybeUninit::<linux_stat64>::uninit(); + +    #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] +    unsafe { +        ret(syscall!(__NR_fstat, fd, &mut result))?; +        stat_to_stat(result.assume_init()) +    } + +    #[cfg(target_pointer_width = "32")] +    unsafe { +        ret(syscall!(__NR_fstat64, fd, &mut result))?; +        stat_to_stat(result.assume_init()) +    } +} + +#[inline] +pub(crate) fn stat(path: &CStr) -> io::Result<Stat> { +    // See the comments in `fstat` about using `crate::fs::statx` here. +    #[cfg(any( +        target_pointer_width = "32", +        target_arch = "mips64", +        target_arch = "mips64r6" +    ))] +    { +        match crate::fs::statx( +            crate::fs::CWD.as_fd(), +            path, +            AtFlags::empty(), +            StatxFlags::BASIC_STATS, +        ) { +            Ok(x) => statx_to_stat(x), +            Err(io::Errno::NOSYS) => stat_old(path), +            Err(err) => Err(err), +        } +    } + +    #[cfg(all( +        target_pointer_width = "64", +        not(target_arch = "mips64"), +        not(target_arch = "mips64r6"), +    ))] +    unsafe { +        let mut result = MaybeUninit::<Stat>::uninit(); +        ret(syscall!( +            __NR_newfstatat, +            raw_fd(AT_FDCWD), +            path, +            &mut result, +            c_uint(0) +        ))?; +        Ok(result.assume_init()) +    } +} + +#[cfg(any( +    target_pointer_width = "32", +    target_arch = "mips64", +    target_arch = "mips64r6" +))] +fn stat_old(path: &CStr) -> io::Result<Stat> { +    let mut result = MaybeUninit::<linux_stat64>::uninit(); + +    #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] +    unsafe { +        ret(syscall!( +            __NR_newfstatat, +            raw_fd(AT_FDCWD), +            path, +            &mut result, +            c_uint(0) +        ))?; +        stat_to_stat(result.assume_init()) +    } + +    #[cfg(target_pointer_width = "32")] +    unsafe { +        ret(syscall!( +            __NR_fstatat64, +            raw_fd(AT_FDCWD), +            path, +            &mut result, +            c_uint(0) +        ))?; +        stat_to_stat(result.assume_init()) +    } +} + +#[inline] +pub(crate) fn statat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat> { +    // See the comments in `fstat` about using `crate::fs::statx` here. +    #[cfg(any( +        target_pointer_width = "32", +        target_arch = "mips64", +        target_arch = "mips64r6" +    ))] +    { +        match crate::fs::statx(dirfd, path, flags, StatxFlags::BASIC_STATS) { +            Ok(x) => statx_to_stat(x), +            Err(io::Errno::NOSYS) => statat_old(dirfd, path, flags), +            Err(err) => Err(err), +        } +    } + +    #[cfg(all( +        target_pointer_width = "64", +        not(target_arch = "mips64"), +        not(target_arch = "mips64r6"), +    ))] +    unsafe { +        let mut result = MaybeUninit::<Stat>::uninit(); +        ret(syscall!(__NR_newfstatat, dirfd, path, &mut result, flags))?; +        Ok(result.assume_init()) +    } +} + +#[cfg(any( +    target_pointer_width = "32", +    target_arch = "mips64", +    target_arch = "mips64r6" +))] +fn statat_old(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat> { +    let mut result = MaybeUninit::<linux_stat64>::uninit(); + +    #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] +    unsafe { +        ret(syscall!(__NR_newfstatat, dirfd, path, &mut result, flags))?; +        stat_to_stat(result.assume_init()) +    } + +    #[cfg(target_pointer_width = "32")] +    unsafe { +        ret(syscall!(__NR_fstatat64, dirfd, path, &mut result, flags))?; +        stat_to_stat(result.assume_init()) +    } +} + +#[inline] +pub(crate) fn lstat(path: &CStr) -> io::Result<Stat> { +    // See the comments in `fstat` about using `crate::fs::statx` here. +    #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))] +    { +        match crate::fs::statx( +            crate::fs::CWD.as_fd(), +            path, +            AtFlags::SYMLINK_NOFOLLOW, +            StatxFlags::BASIC_STATS, +        ) { +            Ok(x) => statx_to_stat(x), +            Err(io::Errno::NOSYS) => lstat_old(path), +            Err(err) => Err(err), +        } +    } + +    #[cfg(all(target_pointer_width = "64", not(target_arch = "mips64")))] +    unsafe { +        let mut result = MaybeUninit::<Stat>::uninit(); +        ret(syscall!( +            __NR_newfstatat, +            raw_fd(AT_FDCWD), +            path, +            &mut result, +            c_uint(AT_SYMLINK_NOFOLLOW) +        ))?; +        Ok(result.assume_init()) +    } +} + +#[cfg(any(target_pointer_width = "32", target_arch = "mips64"))] +fn lstat_old(path: &CStr) -> io::Result<Stat> { +    let mut result = MaybeUninit::<linux_stat64>::uninit(); + +    #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] +    unsafe { +        ret(syscall!( +            __NR_newfstatat, +            raw_fd(AT_FDCWD), +            path, +            &mut result, +            c_uint(AT_SYMLINK_NOFOLLOW) +        ))?; +        stat_to_stat(result.assume_init()) +    } + +    #[cfg(target_pointer_width = "32")] +    unsafe { +        ret(syscall!( +            __NR_fstatat64, +            raw_fd(AT_FDCWD), +            path, +            &mut result, +            c_uint(AT_SYMLINK_NOFOLLOW) +        ))?; +        stat_to_stat(result.assume_init()) +    } +} + +/// Convert from a Linux `statx` value to rustix's `Stat`. +#[cfg(any( +    target_pointer_width = "32", +    target_arch = "mips64", +    target_arch = "mips64r6" +))] +fn statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat> { +    Ok(Stat { +        st_dev: crate::fs::makedev(x.stx_dev_major, x.stx_dev_minor), +        st_mode: x.stx_mode.into(), +        st_nlink: x.stx_nlink.into(), +        st_uid: x.stx_uid.into(), +        st_gid: x.stx_gid.into(), +        st_rdev: crate::fs::makedev(x.stx_rdev_major, x.stx_rdev_minor), +        st_size: x.stx_size.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_blksize: x.stx_blksize.into(), +        st_blocks: x.stx_blocks.into(), +        st_atime: x +            .stx_atime +            .tv_sec +            .try_into() +            .map_err(|_| io::Errno::OVERFLOW)?, +        st_atime_nsec: x.stx_atime.tv_nsec.into(), +        st_mtime: x +            .stx_mtime +            .tv_sec +            .try_into() +            .map_err(|_| io::Errno::OVERFLOW)?, +        st_mtime_nsec: x.stx_mtime.tv_nsec.into(), +        st_ctime: x +            .stx_ctime +            .tv_sec +            .try_into() +            .map_err(|_| io::Errno::OVERFLOW)?, +        st_ctime_nsec: x.stx_ctime.tv_nsec.into(), +        st_ino: x.stx_ino.into(), +    }) +} + +/// Convert from a Linux `stat64` value to rustix's `Stat`. +#[cfg(target_pointer_width = "32")] +fn stat_to_stat(s64: linux_raw_sys::general::stat64) -> io::Result<Stat> { +    Ok(Stat { +        st_dev: s64.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_mode: s64.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_nlink: s64.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_uid: s64.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_gid: s64.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_rdev: s64.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_size: s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_blksize: s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_blocks: s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_atime: s64.st_atime.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_atime_nsec: s64 +            .st_atime_nsec +            .try_into() +            .map_err(|_| io::Errno::OVERFLOW)?, +        st_mtime: s64.st_mtime.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_mtime_nsec: s64 +            .st_mtime_nsec +            .try_into() +            .map_err(|_| io::Errno::OVERFLOW)?, +        st_ctime: s64.st_ctime.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_ctime_nsec: s64 +            .st_ctime_nsec +            .try_into() +            .map_err(|_| io::Errno::OVERFLOW)?, +        st_ino: s64.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?, +    }) +} + +/// Convert from a Linux `stat` value to rustix's `Stat`. +#[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] +fn stat_to_stat(s: linux_raw_sys::general::stat) -> io::Result<Stat> { +    Ok(Stat { +        st_dev: s.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_mode: s.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_nlink: s.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_uid: s.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_gid: s.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_rdev: s.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_size: s.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_blksize: s.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_blocks: s.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_atime: s.st_atime.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_atime_nsec: s +            .st_atime_nsec +            .try_into() +            .map_err(|_| io::Errno::OVERFLOW)?, +        st_mtime: s.st_mtime.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_mtime_nsec: s +            .st_mtime_nsec +            .try_into() +            .map_err(|_| io::Errno::OVERFLOW)?, +        st_ctime: s.st_ctime.try_into().map_err(|_| io::Errno::OVERFLOW)?, +        st_ctime_nsec: s +            .st_ctime_nsec +            .try_into() +            .map_err(|_| io::Errno::OVERFLOW)?, +        st_ino: s.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?, +    }) +} + +#[inline] +pub(crate) fn statx( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    flags: AtFlags, +    mask: StatxFlags, +) -> io::Result<statx> { +    // If a future Linux kernel adds more fields to `struct statx` and users +    // passing flags unknown to rustix in `StatxFlags`, we could end up +    // writing outside of the buffer. To prevent this possibility, we mask off +    // any flags that we don't know about. +    // +    // This includes `STATX__RESERVED`, which has a value that we know, but +    // which could take on arbitrary new meaning in the future. Linux currently +    // rejects this flag with `EINVAL`, so we do the same. +    // +    // This doesn't rely on `STATX_ALL` because [it's deprecated] and already +    // doesn't represent all the known flags. +    // +    // [it's deprecated]: https://patchwork.kernel.org/project/linux-fsdevel/patch/20200505095915.11275-7-mszeredi@redhat.com/ +    if (mask.bits() & STATX__RESERVED) == STATX__RESERVED { +        return Err(io::Errno::INVAL); +    } +    let mask = mask & StatxFlags::all(); + +    unsafe { +        let mut statx_buf = MaybeUninit::<statx>::uninit(); +        ret(syscall!( +            __NR_statx, +            dirfd, +            path, +            flags, +            mask, +            &mut statx_buf +        ))?; +        Ok(statx_buf.assume_init()) +    } +} + +#[cfg(not(feature = "linux_4_11"))] +#[inline] +pub(crate) fn is_statx_available() -> bool { +    unsafe { +        // Call `statx` with null pointers so that if it fails for any reason +        // other than `EFAULT`, we know it's not supported. This can use +        // "readonly" because we don't pass it a buffer to mutate. +        matches!( +            ret(syscall_readonly!( +                __NR_statx, +                raw_fd(AT_FDCWD), +                zero(), +                zero(), +                zero(), +                zero() +            )), +            Err(io::Errno::FAULT) +        ) +    } +} + +#[inline] +pub(crate) fn fstatfs(fd: BorrowedFd<'_>) -> io::Result<StatFs> { +    #[cfg(target_pointer_width = "32")] +    unsafe { +        let mut result = MaybeUninit::<StatFs>::uninit(); +        ret(syscall!( +            __NR_fstatfs64, +            fd, +            size_of::<StatFs, _>(), +            &mut result +        ))?; +        Ok(result.assume_init()) +    } + +    #[cfg(target_pointer_width = "64")] +    unsafe { +        let mut result = MaybeUninit::<StatFs>::uninit(); +        ret(syscall!(__NR_fstatfs, fd, &mut result))?; +        Ok(result.assume_init()) +    } +} + +#[inline] +pub(crate) fn fstatvfs(fd: BorrowedFd<'_>) -> io::Result<StatVfs> { +    // Linux doesn't have an `fstatvfs` syscall; we have to do `fstatfs` and +    // translate the fields as best we can. +    let statfs = fstatfs(fd)?; + +    Ok(statfs_to_statvfs(statfs)) +} + +#[inline] +pub(crate) fn statfs(path: &CStr) -> io::Result<StatFs> { +    #[cfg(target_pointer_width = "32")] +    unsafe { +        let mut result = MaybeUninit::<StatFs>::uninit(); +        ret(syscall!( +            __NR_statfs64, +            path, +            size_of::<StatFs, _>(), +            &mut result +        ))?; +        Ok(result.assume_init()) +    } +    #[cfg(target_pointer_width = "64")] +    unsafe { +        let mut result = MaybeUninit::<StatFs>::uninit(); +        ret(syscall!(__NR_statfs, path, &mut result))?; +        Ok(result.assume_init()) +    } +} + +#[inline] +pub(crate) fn statvfs(path: &CStr) -> io::Result<StatVfs> { +    // Linux doesn't have a `statvfs` syscall; we have to do `statfs` and +    // translate the fields as best we can. +    let statfs = statfs(path)?; + +    Ok(statfs_to_statvfs(statfs)) +} + +fn statfs_to_statvfs(statfs: StatFs) -> StatVfs { +    let __kernel_fsid_t { val } = statfs.f_fsid; +    let [f_fsid_val0, f_fsid_val1]: [i32; 2] = val; + +    StatVfs { +        f_bsize: statfs.f_bsize as u64, +        f_frsize: if statfs.f_frsize != 0 { +            statfs.f_frsize +        } else { +            statfs.f_bsize +        } as u64, +        f_blocks: statfs.f_blocks as u64, +        f_bfree: statfs.f_bfree as u64, +        f_bavail: statfs.f_bavail as u64, +        f_files: statfs.f_files as u64, +        f_ffree: statfs.f_ffree as u64, +        f_favail: statfs.f_ffree as u64, +        f_fsid: u64::from(f_fsid_val0 as u32) | u64::from(f_fsid_val1 as u32) << 32, +        f_flag: StatVfsMountFlags::from_bits_retain(statfs.f_flags as u64), +        f_namemax: statfs.f_namelen as u64, +    } +} + +#[cfg(feature = "alloc")] +#[inline] +pub(crate) fn readlink(path: &CStr, buf: &mut [u8]) -> io::Result<usize> { +    let (buf_addr_mut, buf_len) = slice_mut(buf); +    unsafe { +        ret_usize(syscall!( +            __NR_readlinkat, +            raw_fd(AT_FDCWD), +            path, +            buf_addr_mut, +            buf_len +        )) +    } +} + +#[inline] +pub(crate) fn readlinkat( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    buf: &mut [MaybeUninit<u8>], +) -> io::Result<usize> { +    let (buf_addr_mut, buf_len) = slice_mut(buf); +    unsafe { +        ret_usize(syscall!( +            __NR_readlinkat, +            dirfd, +            path, +            buf_addr_mut, +            buf_len +        )) +    } +} + +#[inline] +pub(crate) fn fcntl_getfl(fd: BorrowedFd<'_>) -> io::Result<OFlags> { +    #[cfg(target_pointer_width = "32")] +    unsafe { +        ret_c_uint(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETFL))) +            .map(OFlags::from_bits_retain) +    } +    #[cfg(target_pointer_width = "64")] +    unsafe { +        ret_c_uint(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETFL))).map(OFlags::from_bits_retain) +    } +} + +#[inline] +pub(crate) fn fcntl_setfl(fd: BorrowedFd<'_>, flags: OFlags) -> io::Result<()> { +    // Always enable support for large files. +    let flags = flags | OFlags::from_bits_retain(c::O_LARGEFILE); + +    #[cfg(target_pointer_width = "32")] +    unsafe { +        ret(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_SETFL), flags)) +    } +    #[cfg(target_pointer_width = "64")] +    unsafe { +        ret(syscall_readonly!(__NR_fcntl, fd, c_uint(F_SETFL), flags)) +    } +} + +#[inline] +pub(crate) fn fcntl_get_seals(fd: BorrowedFd<'_>) -> io::Result<SealFlags> { +    #[cfg(target_pointer_width = "32")] +    unsafe { +        ret_c_int(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GET_SEALS))) +            .map(|seals| SealFlags::from_bits_retain(seals as u32)) +    } +    #[cfg(target_pointer_width = "64")] +    unsafe { +        ret_c_int(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GET_SEALS))) +            .map(|seals| SealFlags::from_bits_retain(seals as u32)) +    } +} + +#[inline] +pub(crate) fn fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Result<()> { +    #[cfg(target_pointer_width = "32")] +    unsafe { +        ret(syscall_readonly!( +            __NR_fcntl64, +            fd, +            c_uint(F_ADD_SEALS), +            seals +        )) +    } +    #[cfg(target_pointer_width = "64")] +    unsafe { +        ret(syscall_readonly!( +            __NR_fcntl, +            fd, +            c_uint(F_ADD_SEALS), +            seals +        )) +    } +} + +#[inline] +pub(crate) fn fcntl_lock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> { +    #[cfg(target_pointer_width = "64")] +    use linux_raw_sys::general::{flock, F_SETLK, F_SETLKW}; +    #[cfg(target_pointer_width = "32")] +    use linux_raw_sys::general::{flock64 as flock, F_SETLK64 as F_SETLK, F_SETLKW64 as F_SETLKW}; +    use linux_raw_sys::general::{F_RDLCK, F_UNLCK, F_WRLCK}; + +    let (cmd, l_type) = match operation { +        FlockOperation::LockShared => (F_SETLKW, F_RDLCK), +        FlockOperation::LockExclusive => (F_SETLKW, F_WRLCK), +        FlockOperation::Unlock => (F_SETLKW, F_UNLCK), +        FlockOperation::NonBlockingLockShared => (F_SETLK, F_RDLCK), +        FlockOperation::NonBlockingLockExclusive => (F_SETLK, F_WRLCK), +        FlockOperation::NonBlockingUnlock => (F_SETLK, F_UNLCK), +    }; + +    let lock = flock { +        l_type: l_type as _, + +        // When `l_len` is zero, this locks all the bytes from +        // `l_whence`/`l_start` to the end of the file, even as the +        // file grows dynamically. +        l_whence: SEEK_SET as _, +        l_start: 0, +        l_len: 0, + +        // Unused. +        l_pid: 0, +    }; + +    #[cfg(target_pointer_width = "32")] +    unsafe { +        ret(syscall_readonly!( +            __NR_fcntl64, +            fd, +            c_uint(cmd), +            by_ref(&lock) +        )) +    } +    #[cfg(target_pointer_width = "64")] +    unsafe { +        ret(syscall_readonly!( +            __NR_fcntl, +            fd, +            c_uint(cmd), +            by_ref(&lock) +        )) +    } +} + +#[inline] +pub(crate) fn rename(old_path: &CStr, new_path: &CStr) -> io::Result<()> { +    #[cfg(target_arch = "riscv64")] +    unsafe { +        ret(syscall_readonly!( +            __NR_renameat2, +            raw_fd(AT_FDCWD), +            old_path, +            raw_fd(AT_FDCWD), +            new_path, +            c_uint(0) +        )) +    } +    #[cfg(not(target_arch = "riscv64"))] +    unsafe { +        ret(syscall_readonly!( +            __NR_renameat, +            raw_fd(AT_FDCWD), +            old_path, +            raw_fd(AT_FDCWD), +            new_path +        )) +    } +} + +#[inline] +pub(crate) fn renameat( +    old_dirfd: BorrowedFd<'_>, +    old_path: &CStr, +    new_dirfd: BorrowedFd<'_>, +    new_path: &CStr, +) -> io::Result<()> { +    #[cfg(target_arch = "riscv64")] +    unsafe { +        ret(syscall_readonly!( +            __NR_renameat2, +            old_dirfd, +            old_path, +            new_dirfd, +            new_path, +            c_uint(0) +        )) +    } +    #[cfg(not(target_arch = "riscv64"))] +    unsafe { +        ret(syscall_readonly!( +            __NR_renameat, +            old_dirfd, +            old_path, +            new_dirfd, +            new_path +        )) +    } +} + +#[inline] +pub(crate) fn renameat2( +    old_dirfd: BorrowedFd<'_>, +    old_path: &CStr, +    new_dirfd: BorrowedFd<'_>, +    new_path: &CStr, +    flags: RenameFlags, +) -> io::Result<()> { +    unsafe { +        ret(syscall_readonly!( +            __NR_renameat2, +            old_dirfd, +            old_path, +            new_dirfd, +            new_path, +            flags +        )) +    } +} + +#[inline] +pub(crate) fn unlink(path: &CStr) -> io::Result<()> { +    unsafe { +        ret(syscall_readonly!( +            __NR_unlinkat, +            raw_fd(AT_FDCWD), +            path, +            c_uint(0) +        )) +    } +} + +#[inline] +pub(crate) fn unlinkat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<()> { +    unsafe { ret(syscall_readonly!(__NR_unlinkat, dirfd, path, flags)) } +} + +#[inline] +pub(crate) fn rmdir(path: &CStr) -> io::Result<()> { +    unsafe { +        ret(syscall_readonly!( +            __NR_unlinkat, +            raw_fd(AT_FDCWD), +            path, +            c_uint(AT_REMOVEDIR) +        )) +    } +} + +#[inline] +pub(crate) fn link(old_path: &CStr, new_path: &CStr) -> io::Result<()> { +    unsafe { +        ret(syscall_readonly!( +            __NR_linkat, +            raw_fd(AT_FDCWD), +            old_path, +            raw_fd(AT_FDCWD), +            new_path, +            c_uint(0) +        )) +    } +} + +#[inline] +pub(crate) fn linkat( +    old_dirfd: BorrowedFd<'_>, +    old_path: &CStr, +    new_dirfd: BorrowedFd<'_>, +    new_path: &CStr, +    flags: AtFlags, +) -> io::Result<()> { +    unsafe { +        ret(syscall_readonly!( +            __NR_linkat, +            old_dirfd, +            old_path, +            new_dirfd, +            new_path, +            flags +        )) +    } +} + +#[inline] +pub(crate) fn symlink(old_path: &CStr, new_path: &CStr) -> io::Result<()> { +    unsafe { +        ret(syscall_readonly!( +            __NR_symlinkat, +            old_path, +            raw_fd(AT_FDCWD), +            new_path +        )) +    } +} + +#[inline] +pub(crate) fn symlinkat(old_path: &CStr, dirfd: BorrowedFd<'_>, new_path: &CStr) -> io::Result<()> { +    unsafe { ret(syscall_readonly!(__NR_symlinkat, old_path, dirfd, new_path)) } +} + +#[inline] +pub(crate) fn mkdir(path: &CStr, mode: Mode) -> io::Result<()> { +    unsafe { +        ret(syscall_readonly!( +            __NR_mkdirat, +            raw_fd(AT_FDCWD), +            path, +            mode +        )) +    } +} + +#[inline] +pub(crate) fn mkdirat(dirfd: BorrowedFd<'_>, path: &CStr, mode: Mode) -> io::Result<()> { +    unsafe { ret(syscall_readonly!(__NR_mkdirat, dirfd, path, mode)) } +} + +#[cfg(feature = "alloc")] +#[inline] +pub(crate) fn getdents(fd: BorrowedFd<'_>, dirent: &mut [u8]) -> io::Result<usize> { +    let (dirent_addr_mut, dirent_len) = slice_mut(dirent); + +    unsafe { ret_usize(syscall!(__NR_getdents64, fd, dirent_addr_mut, dirent_len)) } +} + +#[inline] +pub(crate) fn getdents_uninit( +    fd: BorrowedFd<'_>, +    dirent: &mut [MaybeUninit<u8>], +) -> io::Result<usize> { +    let (dirent_addr_mut, dirent_len) = slice_mut(dirent); + +    unsafe { ret_usize(syscall!(__NR_getdents64, fd, dirent_addr_mut, dirent_len)) } +} + +#[inline] +pub(crate) fn utimensat( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    times: &Timestamps, +    flags: AtFlags, +) -> io::Result<()> { +    _utimensat(dirfd, Some(path), times, flags) +} + +#[inline] +fn _utimensat( +    dirfd: BorrowedFd<'_>, +    path: Option<&CStr>, +    times: &Timestamps, +    flags: AtFlags, +) -> io::Result<()> { +    // `utimensat_time64` was introduced in Linux 5.1. The old `utimensat` +    // syscall is not y2038-compatible on 32-bit architectures. +    #[cfg(target_pointer_width = "32")] +    unsafe { +        match ret(syscall_readonly!( +            __NR_utimensat_time64, +            dirfd, +            path, +            by_ref(times), +            flags +        )) { +            Err(io::Errno::NOSYS) => _utimensat_old(dirfd, path, times, flags), +            otherwise => otherwise, +        } +    } +    #[cfg(target_pointer_width = "64")] +    unsafe { +        ret(syscall_readonly!( +            __NR_utimensat, +            dirfd, +            path, +            by_ref(times), +            flags +        )) +    } +} + +#[cfg(target_pointer_width = "32")] +unsafe fn _utimensat_old( +    dirfd: BorrowedFd<'_>, +    path: Option<&CStr>, +    times: &Timestamps, +    flags: AtFlags, +) -> io::Result<()> { +    // See the comments in `rustix_clock_gettime_via_syscall` about +    // emulation. +    let old_times = [ +        __kernel_old_timespec { +            tv_sec: times +                .last_access +                .tv_sec +                .try_into() +                .map_err(|_| io::Errno::OVERFLOW)?, +            tv_nsec: times +                .last_access +                .tv_nsec +                .try_into() +                .map_err(|_| io::Errno::INVAL)?, +        }, +        __kernel_old_timespec { +            tv_sec: times +                .last_modification +                .tv_sec +                .try_into() +                .map_err(|_| io::Errno::OVERFLOW)?, +            tv_nsec: times +                .last_modification +                .tv_nsec +                .try_into() +                .map_err(|_| io::Errno::INVAL)?, +        }, +    ]; +    // The length of the array is fixed and not passed into the syscall. +    let old_times_addr = slice_just_addr(&old_times); +    ret(syscall_readonly!( +        __NR_utimensat, +        dirfd, +        path, +        old_times_addr, +        flags +    )) +} + +#[inline] +pub(crate) fn futimens(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()> { +    _utimensat(fd, None, times, AtFlags::empty()) +} + +#[inline] +pub(crate) fn access(path: &CStr, access: Access) -> io::Result<()> { +    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +    { +        accessat_noflags(CWD.as_fd(), path, access) +    } + +    #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] +    unsafe { +        ret(syscall_readonly!(__NR_access, path, access)) +    } +} + +pub(crate) fn accessat( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    access: Access, +    flags: AtFlags, +) -> io::Result<()> { +    if !flags +        .difference(AtFlags::EACCESS | AtFlags::SYMLINK_NOFOLLOW) +        .is_empty() +    { +        return Err(io::Errno::INVAL); +    } + +    // Linux's `faccessat` syscall doesn't have a flags argument, so if we have +    // any flags, use the newer `faccessat2` introduced in Linux 5.8 which +    // does. Unless we're on Android where using newer system calls can cause +    // seccomp to abort the process. +    #[cfg(not(target_os = "android"))] +    if !flags.is_empty() { +        unsafe { +            match ret(syscall_readonly!( +                __NR_faccessat2, +                dirfd, +                path, +                access, +                flags +            )) { +                Ok(()) => return Ok(()), +                Err(io::Errno::NOSYS) => {} +                Err(other) => return Err(other), +            } +        } +    } + +    // Linux's `faccessat` doesn't have a flags parameter. If we have +    // `AT_EACCESS` and we're not setuid or setgid, we can emulate it. +    if flags.is_empty() +        || (flags.bits() == AT_EACCESS +            && crate::backend::ugid::syscalls::getuid() +                == crate::backend::ugid::syscalls::geteuid() +            && crate::backend::ugid::syscalls::getgid() +                == crate::backend::ugid::syscalls::getegid()) +    { +        return accessat_noflags(dirfd, path, access); +    } + +    Err(io::Errno::NOSYS) +} + +#[inline] +fn accessat_noflags(dirfd: BorrowedFd<'_>, path: &CStr, access: Access) -> io::Result<()> { +    unsafe { ret(syscall_readonly!(__NR_faccessat, dirfd, path, access)) } +} + +#[inline] +pub(crate) fn copy_file_range( +    fd_in: BorrowedFd<'_>, +    off_in: Option<&mut u64>, +    fd_out: BorrowedFd<'_>, +    off_out: Option<&mut u64>, +    len: usize, +) -> io::Result<usize> { +    unsafe { +        ret_usize(syscall!( +            __NR_copy_file_range, +            fd_in, +            opt_mut(off_in), +            fd_out, +            opt_mut(off_out), +            pass_usize(len), +            c_uint(0) +        )) +    } +} + +#[inline] +pub(crate) fn memfd_create(name: &CStr, flags: MemfdFlags) -> io::Result<OwnedFd> { +    unsafe { ret_owned_fd(syscall_readonly!(__NR_memfd_create, name, flags)) } +} + +#[inline] +pub(crate) fn sendfile( +    out_fd: BorrowedFd<'_>, +    in_fd: BorrowedFd<'_>, +    offset: Option<&mut u64>, +    count: usize, +) -> io::Result<usize> { +    #[cfg(target_pointer_width = "32")] +    unsafe { +        ret_usize(syscall!( +            __NR_sendfile64, +            out_fd, +            in_fd, +            opt_mut(offset), +            pass_usize(count) +        )) +    } +    #[cfg(target_pointer_width = "64")] +    unsafe { +        ret_usize(syscall!( +            __NR_sendfile, +            out_fd, +            in_fd, +            opt_mut(offset), +            pass_usize(count) +        )) +    } +} + +#[inline] +pub(crate) fn inotify_init1(flags: inotify::CreateFlags) -> io::Result<OwnedFd> { +    unsafe { ret_owned_fd(syscall_readonly!(__NR_inotify_init1, flags)) } +} + +#[inline] +pub(crate) fn inotify_add_watch( +    infd: BorrowedFd<'_>, +    path: &CStr, +    flags: inotify::WatchFlags, +) -> io::Result<i32> { +    unsafe { ret_c_int(syscall_readonly!(__NR_inotify_add_watch, infd, path, flags)) } +} + +#[inline] +pub(crate) fn inotify_rm_watch(infd: BorrowedFd<'_>, wfd: i32) -> io::Result<()> { +    unsafe { ret(syscall_readonly!(__NR_inotify_rm_watch, infd, c_int(wfd))) } +} + +#[inline] +pub(crate) fn getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result<usize> { +    let (value_addr_mut, value_len) = slice_mut(value); +    unsafe { +        ret_usize(syscall!( +            __NR_getxattr, +            path, +            name, +            value_addr_mut, +            value_len +        )) +    } +} + +#[inline] +pub(crate) fn lgetxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result<usize> { +    let (value_addr_mut, value_len) = slice_mut(value); +    unsafe { +        ret_usize(syscall!( +            __NR_lgetxattr, +            path, +            name, +            value_addr_mut, +            value_len +        )) +    } +} + +#[inline] +pub(crate) fn fgetxattr(fd: BorrowedFd<'_>, name: &CStr, value: &mut [u8]) -> io::Result<usize> { +    let (value_addr_mut, value_len) = slice_mut(value); +    unsafe { +        ret_usize(syscall!( +            __NR_fgetxattr, +            fd, +            name, +            value_addr_mut, +            value_len +        )) +    } +} + +#[inline] +pub(crate) fn setxattr( +    path: &CStr, +    name: &CStr, +    value: &[u8], +    flags: XattrFlags, +) -> io::Result<()> { +    let (value_addr, value_len) = slice(value); +    unsafe { +        ret(syscall_readonly!( +            __NR_setxattr, +            path, +            name, +            value_addr, +            value_len, +            flags +        )) +    } +} + +#[inline] +pub(crate) fn lsetxattr( +    path: &CStr, +    name: &CStr, +    value: &[u8], +    flags: XattrFlags, +) -> io::Result<()> { +    let (value_addr, value_len) = slice(value); +    unsafe { +        ret(syscall_readonly!( +            __NR_lsetxattr, +            path, +            name, +            value_addr, +            value_len, +            flags +        )) +    } +} + +#[inline] +pub(crate) fn fsetxattr( +    fd: BorrowedFd<'_>, +    name: &CStr, +    value: &[u8], +    flags: XattrFlags, +) -> io::Result<()> { +    let (value_addr, value_len) = slice(value); +    unsafe { +        ret(syscall_readonly!( +            __NR_fsetxattr, +            fd, +            name, +            value_addr, +            value_len, +            flags +        )) +    } +} + +#[inline] +pub(crate) fn listxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result<usize> { +    let (list_addr_mut, list_len) = slice_mut(list); +    unsafe { ret_usize(syscall!(__NR_listxattr, path, list_addr_mut, list_len)) } +} + +#[inline] +pub(crate) fn llistxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result<usize> { +    let (list_addr_mut, list_len) = slice_mut(list); +    unsafe { ret_usize(syscall!(__NR_llistxattr, path, list_addr_mut, list_len)) } +} + +#[inline] +pub(crate) fn flistxattr(fd: BorrowedFd<'_>, list: &mut [c::c_char]) -> io::Result<usize> { +    let (list_addr_mut, list_len) = slice_mut(list); +    unsafe { ret_usize(syscall!(__NR_flistxattr, fd, list_addr_mut, list_len)) } +} + +#[inline] +pub(crate) fn removexattr(path: &CStr, name: &CStr) -> io::Result<()> { +    unsafe { ret(syscall_readonly!(__NR_removexattr, path, name)) } +} + +#[inline] +pub(crate) fn lremovexattr(path: &CStr, name: &CStr) -> io::Result<()> { +    unsafe { ret(syscall_readonly!(__NR_lremovexattr, path, name)) } +} + +#[inline] +pub(crate) fn fremovexattr(fd: BorrowedFd<'_>, name: &CStr) -> io::Result<()> { +    unsafe { ret(syscall_readonly!(__NR_fremovexattr, fd, name)) } +} + +#[test] +fn test_sizes() { +    assert_eq_size!(linux_raw_sys::general::__kernel_loff_t, u64); + +    // Assert that `Timestamps` has the expected layout. +    assert_eq_size!([linux_raw_sys::general::__kernel_timespec; 2], Timestamps); +} diff --git a/vendor/rustix/src/backend/linux_raw/fs/types.rs b/vendor/rustix/src/backend/linux_raw/fs/types.rs new file mode 100644 index 0000000..39823c4 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/fs/types.rs @@ -0,0 +1,739 @@ +use crate::backend::c; +use bitflags::bitflags; + +bitflags! { +    /// `*_OK` constants for use with [`accessat`]. +    /// +    /// [`accessat`]: fn.accessat.html +    #[repr(transparent)] +    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +    pub struct Access: c::c_uint { +        /// `R_OK` +        const READ_OK = linux_raw_sys::general::R_OK; + +        /// `W_OK` +        const WRITE_OK = linux_raw_sys::general::W_OK; + +        /// `X_OK` +        const EXEC_OK = linux_raw_sys::general::X_OK; + +        /// `F_OK` +        const EXISTS = linux_raw_sys::general::F_OK; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +bitflags! { +    /// `AT_*` constants for use with [`openat`], [`statat`], and other `*at` +    /// functions. +    /// +    /// [`openat`]: crate::fs::openat +    /// [`statat`]: crate::fs::statat +    #[repr(transparent)] +    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +    pub struct AtFlags: c::c_uint { +        /// `AT_SYMLINK_NOFOLLOW` +        const SYMLINK_NOFOLLOW = linux_raw_sys::general::AT_SYMLINK_NOFOLLOW; + +        /// `AT_EACCESS` +        const EACCESS = linux_raw_sys::general::AT_EACCESS; + +        /// `AT_REMOVEDIR` +        const REMOVEDIR = linux_raw_sys::general::AT_REMOVEDIR; + +        /// `AT_SYMLINK_FOLLOW` +        const SYMLINK_FOLLOW = linux_raw_sys::general::AT_SYMLINK_FOLLOW; + +        /// `AT_NO_AUTOMOUNT` +        const NO_AUTOMOUNT = linux_raw_sys::general::AT_NO_AUTOMOUNT; + +        /// `AT_EMPTY_PATH` +        const EMPTY_PATH = linux_raw_sys::general::AT_EMPTY_PATH; + +        /// `AT_STATX_SYNC_AS_STAT` +        const STATX_SYNC_AS_STAT = linux_raw_sys::general::AT_STATX_SYNC_AS_STAT; + +        /// `AT_STATX_FORCE_SYNC` +        const STATX_FORCE_SYNC = linux_raw_sys::general::AT_STATX_FORCE_SYNC; + +        /// `AT_STATX_DONT_SYNC` +        const STATX_DONT_SYNC = linux_raw_sys::general::AT_STATX_DONT_SYNC; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +bitflags! { +    /// `S_I*` constants for use with [`openat`], [`chmodat`], and [`fchmod`]. +    /// +    /// [`openat`]: crate::fs::openat +    /// [`chmodat`]: crate::fs::chmodat +    /// [`fchmod`]: crate::fs::fchmod +    #[repr(transparent)] +    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +    pub struct Mode: RawMode { +        /// `S_IRWXU` +        const RWXU = linux_raw_sys::general::S_IRWXU; + +        /// `S_IRUSR` +        const RUSR = linux_raw_sys::general::S_IRUSR; + +        /// `S_IWUSR` +        const WUSR = linux_raw_sys::general::S_IWUSR; + +        /// `S_IXUSR` +        const XUSR = linux_raw_sys::general::S_IXUSR; + +        /// `S_IRWXG` +        const RWXG = linux_raw_sys::general::S_IRWXG; + +        /// `S_IRGRP` +        const RGRP = linux_raw_sys::general::S_IRGRP; + +        /// `S_IWGRP` +        const WGRP = linux_raw_sys::general::S_IWGRP; + +        /// `S_IXGRP` +        const XGRP = linux_raw_sys::general::S_IXGRP; + +        /// `S_IRWXO` +        const RWXO = linux_raw_sys::general::S_IRWXO; + +        /// `S_IROTH` +        const ROTH = linux_raw_sys::general::S_IROTH; + +        /// `S_IWOTH` +        const WOTH = linux_raw_sys::general::S_IWOTH; + +        /// `S_IXOTH` +        const XOTH = linux_raw_sys::general::S_IXOTH; + +        /// `S_ISUID` +        const SUID = linux_raw_sys::general::S_ISUID; + +        /// `S_ISGID` +        const SGID = linux_raw_sys::general::S_ISGID; + +        /// `S_ISVTX` +        const SVTX = linux_raw_sys::general::S_ISVTX; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +impl Mode { +    /// Construct a `Mode` from the mode bits of the `st_mode` field of a +    /// `Mode`. +    #[inline] +    pub const fn from_raw_mode(st_mode: RawMode) -> Self { +        Self::from_bits_truncate(st_mode) +    } + +    /// Construct an `st_mode` value from a `Mode`. +    #[inline] +    pub const fn as_raw_mode(self) -> RawMode { +        self.bits() +    } +} + +impl From<RawMode> for Mode { +    /// Support conversions from raw mode values to `Mode`. +    /// +    /// ``` +    /// use rustix::fs::{Mode, RawMode}; +    /// assert_eq!(Mode::from(0o700), Mode::RWXU); +    /// ``` +    #[inline] +    fn from(st_mode: RawMode) -> Self { +        Self::from_raw_mode(st_mode) +    } +} + +impl From<Mode> for RawMode { +    /// Support conversions from `Mode` to raw mode values. +    /// +    /// ``` +    /// use rustix::fs::{Mode, RawMode}; +    /// assert_eq!(RawMode::from(Mode::RWXU), 0o700); +    /// ``` +    #[inline] +    fn from(mode: Mode) -> Self { +        mode.as_raw_mode() +    } +} + +bitflags! { +    /// `O_*` constants for use with [`openat`]. +    /// +    /// [`openat`]: crate::fs::openat +    #[repr(transparent)] +    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +    pub struct OFlags: c::c_uint { +        /// `O_ACCMODE` +        const ACCMODE = linux_raw_sys::general::O_ACCMODE; + +        /// Similar to `ACCMODE`, but just includes the read/write flags, and +        /// no other flags. +        /// +        /// On some platforms, `PATH` may be included in `ACCMODE`, when +        /// sometimes we really just want the read/write bits. Caution is +        /// indicated, as the presence of `PATH` may mean that the read/write +        /// bits don't have their usual meaning. +        const RWMODE = linux_raw_sys::general::O_RDONLY | +                       linux_raw_sys::general::O_WRONLY | +                       linux_raw_sys::general::O_RDWR; + +        /// `O_APPEND` +        const APPEND = linux_raw_sys::general::O_APPEND; + +        /// `O_CREAT` +        #[doc(alias = "CREAT")] +        const CREATE = linux_raw_sys::general::O_CREAT; + +        /// `O_DIRECTORY` +        const DIRECTORY = linux_raw_sys::general::O_DIRECTORY; + +        /// `O_DSYNC`. +        const DSYNC = linux_raw_sys::general::O_SYNC; + +        /// `O_EXCL` +        const EXCL = linux_raw_sys::general::O_EXCL; + +        /// `O_FSYNC`. +        const FSYNC = linux_raw_sys::general::O_SYNC; + +        /// `O_NOFOLLOW` +        const NOFOLLOW = linux_raw_sys::general::O_NOFOLLOW; + +        /// `O_NONBLOCK` +        const NONBLOCK = linux_raw_sys::general::O_NONBLOCK; + +        /// `O_RDONLY` +        const RDONLY = linux_raw_sys::general::O_RDONLY; + +        /// `O_WRONLY` +        const WRONLY = linux_raw_sys::general::O_WRONLY; + +        /// `O_RDWR` +        /// +        /// This is not equal to `RDONLY | WRONLY`. It's a distinct flag. +        const RDWR = linux_raw_sys::general::O_RDWR; + +        /// `O_NOCTTY` +        const NOCTTY = linux_raw_sys::general::O_NOCTTY; + +        /// `O_RSYNC`. +        const RSYNC = linux_raw_sys::general::O_SYNC; + +        /// `O_SYNC` +        const SYNC = linux_raw_sys::general::O_SYNC; + +        /// `O_TRUNC` +        const TRUNC = linux_raw_sys::general::O_TRUNC; + +        /// `O_PATH` +        const PATH = linux_raw_sys::general::O_PATH; + +        /// `O_CLOEXEC` +        const CLOEXEC = linux_raw_sys::general::O_CLOEXEC; + +        /// `O_TMPFILE` +        const TMPFILE = linux_raw_sys::general::O_TMPFILE; + +        /// `O_NOATIME` +        const NOATIME = linux_raw_sys::general::O_NOATIME; + +        /// `O_DIRECT` +        const DIRECT = linux_raw_sys::general::O_DIRECT; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +bitflags! { +    /// `RESOLVE_*` constants for use with [`openat2`]. +    /// +    /// [`openat2`]: crate::fs::openat2 +    #[repr(transparent)] +    #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] +    pub struct ResolveFlags: u64 { +        /// `RESOLVE_NO_XDEV` +        const NO_XDEV = linux_raw_sys::general::RESOLVE_NO_XDEV as u64; + +        /// `RESOLVE_NO_MAGICLINKS` +        const NO_MAGICLINKS = linux_raw_sys::general::RESOLVE_NO_MAGICLINKS as u64; + +        /// `RESOLVE_NO_SYMLINKS` +        const NO_SYMLINKS = linux_raw_sys::general::RESOLVE_NO_SYMLINKS as u64; + +        /// `RESOLVE_BENEATH` +        const BENEATH = linux_raw_sys::general::RESOLVE_BENEATH as u64; + +        /// `RESOLVE_IN_ROOT` +        const IN_ROOT = linux_raw_sys::general::RESOLVE_IN_ROOT as u64; + +        /// `RESOLVE_CACHED` (since Linux 5.12) +        const CACHED = linux_raw_sys::general::RESOLVE_CACHED as u64; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +bitflags! { +    /// `RENAME_*` constants for use with [`renameat_with`]. +    /// +    /// [`renameat_with`]: crate::fs::renameat_with +    #[repr(transparent)] +    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +    pub struct RenameFlags: c::c_uint { +        /// `RENAME_EXCHANGE` +        const EXCHANGE = linux_raw_sys::general::RENAME_EXCHANGE; + +        /// `RENAME_NOREPLACE` +        const NOREPLACE = linux_raw_sys::general::RENAME_NOREPLACE; + +        /// `RENAME_WHITEOUT` +        const WHITEOUT = linux_raw_sys::general::RENAME_WHITEOUT; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +/// `S_IF*` constants for use with [`mknodat`] and [`Stat`]'s `st_mode` field. +/// +/// [`mknodat`]: crate::fs::mknodat +/// [`Stat`]: crate::fs::Stat +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum FileType { +    /// `S_IFREG` +    RegularFile = linux_raw_sys::general::S_IFREG as isize, + +    /// `S_IFDIR` +    Directory = linux_raw_sys::general::S_IFDIR as isize, + +    /// `S_IFLNK` +    Symlink = linux_raw_sys::general::S_IFLNK as isize, + +    /// `S_IFIFO` +    #[doc(alias = "IFO")] +    Fifo = linux_raw_sys::general::S_IFIFO as isize, + +    /// `S_IFSOCK` +    Socket = linux_raw_sys::general::S_IFSOCK as isize, + +    /// `S_IFCHR` +    CharacterDevice = linux_raw_sys::general::S_IFCHR as isize, + +    /// `S_IFBLK` +    BlockDevice = linux_raw_sys::general::S_IFBLK as isize, + +    /// An unknown filesystem object. +    Unknown, +} + +impl FileType { +    /// Construct a `FileType` from the `S_IFMT` bits of the `st_mode` field of +    /// a `Stat`. +    #[inline] +    pub const fn from_raw_mode(st_mode: RawMode) -> Self { +        match st_mode & linux_raw_sys::general::S_IFMT { +            linux_raw_sys::general::S_IFREG => Self::RegularFile, +            linux_raw_sys::general::S_IFDIR => Self::Directory, +            linux_raw_sys::general::S_IFLNK => Self::Symlink, +            linux_raw_sys::general::S_IFIFO => Self::Fifo, +            linux_raw_sys::general::S_IFSOCK => Self::Socket, +            linux_raw_sys::general::S_IFCHR => Self::CharacterDevice, +            linux_raw_sys::general::S_IFBLK => Self::BlockDevice, +            _ => Self::Unknown, +        } +    } + +    /// Construct an `st_mode` value from a `FileType`. +    #[inline] +    pub const fn as_raw_mode(self) -> RawMode { +        match self { +            Self::RegularFile => linux_raw_sys::general::S_IFREG, +            Self::Directory => linux_raw_sys::general::S_IFDIR, +            Self::Symlink => linux_raw_sys::general::S_IFLNK, +            Self::Fifo => linux_raw_sys::general::S_IFIFO, +            Self::Socket => linux_raw_sys::general::S_IFSOCK, +            Self::CharacterDevice => linux_raw_sys::general::S_IFCHR, +            Self::BlockDevice => linux_raw_sys::general::S_IFBLK, +            Self::Unknown => linux_raw_sys::general::S_IFMT, +        } +    } + +    /// Construct a `FileType` from the `d_type` field of a `c::dirent`. +    #[inline] +    pub(crate) const fn from_dirent_d_type(d_type: u8) -> Self { +        match d_type as u32 { +            linux_raw_sys::general::DT_REG => Self::RegularFile, +            linux_raw_sys::general::DT_DIR => Self::Directory, +            linux_raw_sys::general::DT_LNK => Self::Symlink, +            linux_raw_sys::general::DT_SOCK => Self::Socket, +            linux_raw_sys::general::DT_FIFO => Self::Fifo, +            linux_raw_sys::general::DT_CHR => Self::CharacterDevice, +            linux_raw_sys::general::DT_BLK => Self::BlockDevice, +            // linux_raw_sys::general::DT_UNKNOWN | +            _ => Self::Unknown, +        } +    } +} + +/// `POSIX_FADV_*` constants for use with [`fadvise`]. +/// +/// [`fadvise`]: crate::fs::fadvise +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub enum Advice { +    /// `POSIX_FADV_NORMAL` +    Normal = linux_raw_sys::general::POSIX_FADV_NORMAL, + +    /// `POSIX_FADV_SEQUENTIAL` +    Sequential = linux_raw_sys::general::POSIX_FADV_SEQUENTIAL, + +    /// `POSIX_FADV_RANDOM` +    Random = linux_raw_sys::general::POSIX_FADV_RANDOM, + +    /// `POSIX_FADV_NOREUSE` +    NoReuse = linux_raw_sys::general::POSIX_FADV_NOREUSE, + +    /// `POSIX_FADV_WILLNEED` +    WillNeed = linux_raw_sys::general::POSIX_FADV_WILLNEED, + +    /// `POSIX_FADV_DONTNEED` +    DontNeed = linux_raw_sys::general::POSIX_FADV_DONTNEED, +} + +bitflags! { +    /// `MFD_*` constants for use with [`memfd_create`]. +    /// +    /// [`memfd_create`]: crate::fs::memfd_create +    #[repr(transparent)] +    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +    pub struct MemfdFlags: c::c_uint { +        /// `MFD_CLOEXEC` +        const CLOEXEC = linux_raw_sys::general::MFD_CLOEXEC; + +        /// `MFD_ALLOW_SEALING` +        const ALLOW_SEALING = linux_raw_sys::general::MFD_ALLOW_SEALING; + +        /// `MFD_HUGETLB` (since Linux 4.14) +        const HUGETLB = linux_raw_sys::general::MFD_HUGETLB; + +        /// `MFD_HUGE_64KB` +        const HUGE_64KB = linux_raw_sys::general::MFD_HUGE_64KB; +        /// `MFD_HUGE_512JB` +        const HUGE_512KB = linux_raw_sys::general::MFD_HUGE_512KB; +        /// `MFD_HUGE_1MB` +        const HUGE_1MB = linux_raw_sys::general::MFD_HUGE_1MB; +        /// `MFD_HUGE_2MB` +        const HUGE_2MB = linux_raw_sys::general::MFD_HUGE_2MB; +        /// `MFD_HUGE_8MB` +        const HUGE_8MB = linux_raw_sys::general::MFD_HUGE_8MB; +        /// `MFD_HUGE_16MB` +        const HUGE_16MB = linux_raw_sys::general::MFD_HUGE_16MB; +        /// `MFD_HUGE_32MB` +        const HUGE_32MB = linux_raw_sys::general::MFD_HUGE_32MB; +        /// `MFD_HUGE_256MB` +        const HUGE_256MB = linux_raw_sys::general::MFD_HUGE_256MB; +        /// `MFD_HUGE_512MB` +        const HUGE_512MB = linux_raw_sys::general::MFD_HUGE_512MB; +        /// `MFD_HUGE_1GB` +        const HUGE_1GB = linux_raw_sys::general::MFD_HUGE_1GB; +        /// `MFD_HUGE_2GB` +        const HUGE_2GB = linux_raw_sys::general::MFD_HUGE_2GB; +        /// `MFD_HUGE_16GB` +        const HUGE_16GB = linux_raw_sys::general::MFD_HUGE_16GB; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +bitflags! { +    /// `F_SEAL_*` constants for use with [`fcntl_add_seals`] and +    /// [`fcntl_get_seals`]. +    /// +    /// [`fcntl_add_seals`]: crate::fs::fcntl_add_seals +    /// [`fcntl_get_seals`]: crate::fs::fcntl_get_seals +    #[repr(transparent)] +    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +    pub struct SealFlags: u32 { +        /// `F_SEAL_SEAL`. +        const SEAL = linux_raw_sys::general::F_SEAL_SEAL; +        /// `F_SEAL_SHRINK`. +        const SHRINK = linux_raw_sys::general::F_SEAL_SHRINK; +        /// `F_SEAL_GROW`. +        const GROW = linux_raw_sys::general::F_SEAL_GROW; +        /// `F_SEAL_WRITE`. +        const WRITE = linux_raw_sys::general::F_SEAL_WRITE; +        /// `F_SEAL_FUTURE_WRITE` (since Linux 5.1) +        const FUTURE_WRITE = linux_raw_sys::general::F_SEAL_FUTURE_WRITE; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +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 = linux_raw_sys::general::STATX_TYPE; + +        /// `STATX_MODE` +        const MODE = linux_raw_sys::general::STATX_MODE; + +        /// `STATX_NLINK` +        const NLINK = linux_raw_sys::general::STATX_NLINK; + +        /// `STATX_UID` +        const UID = linux_raw_sys::general::STATX_UID; + +        /// `STATX_GID` +        const GID = linux_raw_sys::general::STATX_GID; + +        /// `STATX_ATIME` +        const ATIME = linux_raw_sys::general::STATX_ATIME; + +        /// `STATX_MTIME` +        const MTIME = linux_raw_sys::general::STATX_MTIME; + +        /// `STATX_CTIME` +        const CTIME = linux_raw_sys::general::STATX_CTIME; + +        /// `STATX_INO` +        const INO = linux_raw_sys::general::STATX_INO; + +        /// `STATX_SIZE` +        const SIZE = linux_raw_sys::general::STATX_SIZE; + +        /// `STATX_BLOCKS` +        const BLOCKS = linux_raw_sys::general::STATX_BLOCKS; + +        /// `STATX_BASIC_STATS` +        const BASIC_STATS = linux_raw_sys::general::STATX_BASIC_STATS; + +        /// `STATX_BTIME` +        const BTIME = linux_raw_sys::general::STATX_BTIME; + +        /// `STATX_MNT_ID` (since Linux 5.8) +        const MNT_ID = linux_raw_sys::general::STATX_MNT_ID; + +        /// `STATX_DIOALIGN` (since Linux 6.1) +        const DIOALIGN = linux_raw_sys::general::STATX_DIOALIGN; + +        /// `STATX_ALL` +        const ALL = linux_raw_sys::general::STATX_ALL; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +bitflags! { +    /// `FALLOC_FL_*` constants for use with [`fallocate`]. +    /// +    /// [`fallocate`]: crate::fs::fallocate +    #[repr(transparent)] +    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +    pub struct FallocateFlags: u32 { +        /// `FALLOC_FL_KEEP_SIZE` +        const KEEP_SIZE = linux_raw_sys::general::FALLOC_FL_KEEP_SIZE; +        /// `FALLOC_FL_PUNCH_HOLE` +        const PUNCH_HOLE = linux_raw_sys::general::FALLOC_FL_PUNCH_HOLE; +        /// `FALLOC_FL_NO_HIDE_STALE` +        const NO_HIDE_STALE = linux_raw_sys::general::FALLOC_FL_NO_HIDE_STALE; +        /// `FALLOC_FL_COLLAPSE_RANGE` +        const COLLAPSE_RANGE = linux_raw_sys::general::FALLOC_FL_COLLAPSE_RANGE; +        /// `FALLOC_FL_ZERO_RANGE` +        const ZERO_RANGE = linux_raw_sys::general::FALLOC_FL_ZERO_RANGE; +        /// `FALLOC_FL_INSERT_RANGE` +        const INSERT_RANGE = linux_raw_sys::general::FALLOC_FL_INSERT_RANGE; +        /// `FALLOC_FL_UNSHARE_RANGE` +        const UNSHARE_RANGE = linux_raw_sys::general::FALLOC_FL_UNSHARE_RANGE; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +bitflags! { +    /// `ST_*` constants for use with [`StatVfs`]. +    #[repr(transparent)] +    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +    pub struct StatVfsMountFlags: u64 { +        /// `ST_MANDLOCK` +        const MANDLOCK = linux_raw_sys::general::MS_MANDLOCK as u64; + +        /// `ST_NOATIME` +        const NOATIME = linux_raw_sys::general::MS_NOATIME as u64; + +        /// `ST_NODEV` +        const NODEV = linux_raw_sys::general::MS_NODEV as u64; + +        /// `ST_NODIRATIME` +        const NODIRATIME = linux_raw_sys::general::MS_NODIRATIME as u64; + +        /// `ST_NOEXEC` +        const NOEXEC = linux_raw_sys::general::MS_NOEXEC as u64; + +        /// `ST_NOSUID` +        const NOSUID = linux_raw_sys::general::MS_NOSUID as u64; + +        /// `ST_RDONLY` +        const RDONLY = linux_raw_sys::general::MS_RDONLY as u64; + +        /// `ST_RELATIME` +        const RELATIME = linux_raw_sys::general::MS_RELATIME as u64; + +        /// `ST_SYNCHRONOUS` +        const SYNCHRONOUS = linux_raw_sys::general::MS_SYNCHRONOUS as u64; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +/// `LOCK_*` constants for use with [`flock`] and [`fcntl_lock`]. +/// +/// [`flock`]: crate::fs::flock +/// [`fcntl_lock`]: crate::fs::fcntl_lock +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum FlockOperation { +    /// `LOCK_SH` +    LockShared = linux_raw_sys::general::LOCK_SH, +    /// `LOCK_EX` +    LockExclusive = linux_raw_sys::general::LOCK_EX, +    /// `LOCK_UN` +    Unlock = linux_raw_sys::general::LOCK_UN, +    /// `LOCK_SH | LOCK_NB` +    NonBlockingLockShared = linux_raw_sys::general::LOCK_SH | linux_raw_sys::general::LOCK_NB, +    /// `LOCK_EX | LOCK_NB` +    NonBlockingLockExclusive = linux_raw_sys::general::LOCK_EX | linux_raw_sys::general::LOCK_NB, +    /// `LOCK_UN | LOCK_NB` +    NonBlockingUnlock = linux_raw_sys::general::LOCK_UN | linux_raw_sys::general::LOCK_NB, +} + +/// `struct stat` for use with [`statat`] and [`fstat`]. +/// +/// [`statat`]: crate::fs::statat +/// [`fstat`]: crate::fs::fstat +// On 32-bit, and mips64, Linux's `struct stat64` has a 32-bit `st_mtime` and +// friends, so we use our own struct, populated from `statx` where possible, to +// avoid the y2038 bug. +#[cfg(any( +    target_pointer_width = "32", +    target_arch = "mips64", +    target_arch = "mips64r6" +))] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +#[allow(missing_docs)] +pub struct Stat { +    pub st_dev: u64, +    pub st_mode: u32, +    pub st_nlink: u32, +    pub st_uid: u32, +    pub st_gid: u32, +    pub st_rdev: u64, +    pub st_size: i64, +    pub st_blksize: u32, +    pub st_blocks: u64, +    pub st_atime: u64, +    pub st_atime_nsec: u32, +    pub st_mtime: u64, +    pub st_mtime_nsec: u32, +    pub st_ctime: u64, +    pub st_ctime_nsec: u32, +    pub st_ino: u64, +} + +/// `struct stat` for use with [`statat`] and [`fstat`]. +/// +/// [`statat`]: crate::fs::statat +/// [`fstat`]: crate::fs::fstat +#[cfg(all( +    target_pointer_width = "64", +    not(target_arch = "mips64"), +    not(target_arch = "mips64r6") +))] +pub type Stat = linux_raw_sys::general::stat; + +/// `struct statfs` for use with [`statfs`] and [`fstatfs`]. +/// +/// [`statfs`]: crate::fs::statfs +/// [`fstatfs`]: crate::fs::fstatfs +#[allow(clippy::module_name_repetitions)] +pub type StatFs = linux_raw_sys::general::statfs64; + +/// `struct statvfs` for use with [`statvfs`] and [`fstatvfs`]. +/// +/// [`statvfs`]: crate::fs::statvfs +/// [`fstatvfs`]: crate::fs::fstatvfs +#[allow(missing_docs)] +pub struct StatVfs { +    pub f_bsize: u64, +    pub f_frsize: u64, +    pub f_blocks: u64, +    pub f_bfree: u64, +    pub f_bavail: u64, +    pub f_files: u64, +    pub f_ffree: u64, +    pub f_favail: u64, +    pub f_fsid: u64, +    pub f_flag: StatVfsMountFlags, +    pub f_namemax: u64, +} + +/// `struct statx` for use with [`statx`]. +/// +/// [`statx`]: crate::fs::statx +pub type Statx = linux_raw_sys::general::statx; + +/// `struct statx_timestamp` for use with [`Statx`]. +pub type StatxTimestamp = linux_raw_sys::general::statx_timestamp; + +/// `mode_t` +#[cfg(not(any( +    target_arch = "x86", +    target_arch = "sparc", +    target_arch = "avr", +    target_arch = "arm", +)))] +pub type RawMode = linux_raw_sys::general::__kernel_mode_t; + +/// `mode_t` +#[cfg(any( +    target_arch = "x86", +    target_arch = "sparc", +    target_arch = "avr", +    target_arch = "arm", +))] +// Don't use `__kernel_mode_t` since it's `u16` which differs from `st_size`. +pub type RawMode = c::c_uint; + +/// `dev_t` +// Within the kernel the dev_t is 32-bit, but userspace uses a 64-bit field. +pub type Dev = u64; + +/// `__fsword_t` +#[cfg(not(any(target_arch = "mips64", target_arch = "mips64r6")))] +pub type FsWord = linux_raw_sys::general::__fsword_t; + +/// `__fsword_t` +#[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] +pub type FsWord = i64;  | 
