diff options
Diffstat (limited to 'vendor/rustix/src/backend/libc/fs')
| -rw-r--r-- | vendor/rustix/src/backend/libc/fs/dir.rs | 423 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/fs/inotify.rs | 131 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/fs/makedev.rs | 138 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/fs/mod.rs | 23 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/fs/syscalls.rs | 2514 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/fs/types.rs | 1164 | 
6 files changed, 4393 insertions, 0 deletions
diff --git a/vendor/rustix/src/backend/libc/fs/dir.rs b/vendor/rustix/src/backend/libc/fs/dir.rs new file mode 100644 index 0000000..82a0a90 --- /dev/null +++ b/vendor/rustix/src/backend/libc/fs/dir.rs @@ -0,0 +1,423 @@ +#[cfg(not(any(solarish, target_os = "haiku", target_os = "nto", target_os = "vita")))] +use super::types::FileType; +use crate::backend::c; +use crate::backend::conv::owned_fd; +use crate::fd::{AsFd, BorrowedFd, OwnedFd}; +use crate::ffi::{CStr, CString}; +use crate::fs::{fcntl_getfl, openat, Mode, OFlags}; +#[cfg(not(target_os = "vita"))] +use crate::fs::{fstat, Stat}; +#[cfg(not(any( +    solarish, +    target_os = "haiku", +    target_os = "netbsd", +    target_os = "nto", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi", +)))] +use crate::fs::{fstatfs, StatFs}; +#[cfg(not(any( +    solarish, +    target_os = "haiku", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi" +)))] +use crate::fs::{fstatvfs, StatVfs}; +use crate::io; +#[cfg(not(any(target_os = "fuchsia", target_os = "vita", target_os = "wasi")))] +#[cfg(feature = "process")] +use crate::process::fchdir; +use alloc::borrow::ToOwned; +#[cfg(not(any(linux_like, target_os = "hurd")))] +use c::readdir as libc_readdir; +#[cfg(any(linux_like, target_os = "hurd"))] +use c::readdir64 as libc_readdir; +use core::fmt; +use core::ptr::NonNull; +use libc_errno::{errno, set_errno, Errno}; + +/// `DIR*` +pub struct Dir { +    /// The `libc` `DIR` pointer. +    libc_dir: NonNull<c::DIR>, + +    /// Have we seen any errors in this iteration? +    any_errors: bool, +} + +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> { +        let raw = owned_fd(fd); +        unsafe { +            let libc_dir = c::fdopendir(raw); + +            if let Some(libc_dir) = NonNull::new(libc_dir) { +                Ok(Self { +                    libc_dir, +                    any_errors: false, +                }) +            } else { +                let err = io::Errno::last_os_error(); +                let _ = c::close(raw); +                Err(err) +            } +        } +    } + +    /// 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] +    #[allow(unused_mut)] +    fn _read_from(fd: BorrowedFd<'_>) -> io::Result<Self> { +        let mut any_errors = false; + +        // Given an arbitrary `OwnedFd`, it's impossible to know whether the +        // user holds a `dup`'d copy which could continue to modify the +        // file description state, which would cause Undefined Behavior after +        // our call to `fdopendir`. To prevent this, we obtain an independent +        // `OwnedFd`. +        let flags = fcntl_getfl(fd)?; +        let fd_for_dir = match openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty()) { +            Ok(fd) => fd, +            #[cfg(not(target_os = "wasi"))] +            Err(io::Errno::NOENT) => { +                // If "." doesn't exist, it means the directory was removed. +                // We treat that as iterating through a directory with no +                // entries. +                any_errors = true; +                crate::io::dup(fd)? +            } +            Err(err) => return Err(err), +        }; + +        let raw = owned_fd(fd_for_dir); +        unsafe { +            let libc_dir = c::fdopendir(raw); + +            if let Some(libc_dir) = NonNull::new(libc_dir) { +                Ok(Self { +                    libc_dir, +                    any_errors, +                }) +            } else { +                let err = io::Errno::last_os_error(); +                let _ = c::close(raw); +                Err(err) +            } +        } +    } + +    /// `rewinddir(self)` +    #[inline] +    pub fn rewind(&mut self) { +        self.any_errors = false; +        unsafe { c::rewinddir(self.libc_dir.as_ptr()) } +    } + +    /// `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; +        } + +        set_errno(Errno(0)); +        let dirent_ptr = unsafe { libc_readdir(self.libc_dir.as_ptr()) }; +        if dirent_ptr.is_null() { +            let curr_errno = errno().0; +            if curr_errno == 0 { +                // We successfully reached the end of the stream. +                None +            } else { +                // `errno` is unknown or non-zero, so an error occurred. +                self.any_errors = true; +                Some(Err(io::Errno(curr_errno))) +            } +        } else { +            // We successfully read an entry. +            unsafe { +                let dirent = &*dirent_ptr; + +                // We have our own copy of OpenBSD's dirent; check that the +                // layout minimally matches libc's. +                #[cfg(target_os = "openbsd")] +                check_dirent_layout(dirent); + +                let result = DirEntry { +                    #[cfg(not(any( +                        solarish, +                        target_os = "aix", +                        target_os = "haiku", +                        target_os = "nto", +                        target_os = "vita" +                    )))] +                    d_type: dirent.d_type, + +                    #[cfg(not(any(freebsdlike, netbsdlike, target_os = "vita")))] +                    d_ino: dirent.d_ino, + +                    #[cfg(any(freebsdlike, netbsdlike))] +                    d_fileno: dirent.d_fileno, + +                    name: CStr::from_ptr(dirent.d_name.as_ptr()).to_owned(), +                }; + +                Some(Ok(result)) +            } +        } +    } + +    /// `fstat(self)` +    #[cfg(not(target_os = "vita"))] +    #[inline] +    pub fn stat(&self) -> io::Result<Stat> { +        fstat(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) }) +    } + +    /// `fstatfs(self)` +    #[cfg(not(any( +        solarish, +        target_os = "haiku", +        target_os = "netbsd", +        target_os = "nto", +        target_os = "redox", +        target_os = "vita", +        target_os = "wasi", +    )))] +    #[inline] +    pub fn statfs(&self) -> io::Result<StatFs> { +        fstatfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) }) +    } + +    /// `fstatvfs(self)` +    #[cfg(not(any( +        solarish, +        target_os = "haiku", +        target_os = "redox", +        target_os = "vita", +        target_os = "wasi" +    )))] +    #[inline] +    pub fn statvfs(&self) -> io::Result<StatVfs> { +        fstatvfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) }) +    } + +    /// `fchdir(self)` +    #[cfg(feature = "process")] +    #[cfg(not(any(target_os = "fuchsia", target_os = "vita", target_os = "wasi")))] +    #[cfg_attr(doc_cfg, doc(cfg(feature = "process")))] +    #[inline] +    pub fn chdir(&self) -> io::Result<()> { +        fchdir(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) }) +    } +} + +/// `Dir` implements `Send` but not `Sync`, because we use `readdir` which is +/// not guaranteed to be thread-safe. Users can wrap this in a `Mutex` if they +/// need `Sync`, which is effectively what'd need to do to implement `Sync` +/// ourselves. +unsafe impl Send for Dir {} + +impl Drop for Dir { +    #[inline] +    fn drop(&mut self) { +        unsafe { c::closedir(self.libc_dir.as_ptr()) }; +    } +} + +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 { +        let mut s = f.debug_struct("Dir"); +        #[cfg(not(target_os = "vita"))] +        s.field("fd", unsafe { &c::dirfd(self.libc_dir.as_ptr()) }); +        s.finish() +    } +} + +/// `struct dirent` +#[derive(Debug)] +pub struct DirEntry { +    #[cfg(not(any( +        solarish, +        target_os = "aix", +        target_os = "haiku", +        target_os = "nto", +        target_os = "vita" +    )))] +    d_type: u8, + +    #[cfg(not(any(freebsdlike, netbsdlike, target_os = "vita")))] +    d_ino: c::ino_t, + +    #[cfg(any(freebsdlike, netbsdlike))] +    d_fileno: c::ino_t, + +    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. +    #[cfg(not(any( +        solarish, +        target_os = "aix", +        target_os = "haiku", +        target_os = "nto", +        target_os = "vita" +    )))] +    #[inline] +    pub fn file_type(&self) -> FileType { +        FileType::from_dirent_d_type(self.d_type) +    } + +    /// Return the inode number of this directory entry. +    #[cfg(not(any(freebsdlike, netbsdlike, target_os = "vita")))] +    #[inline] +    pub fn ino(&self) -> u64 { +        self.d_ino as u64 +    } + +    /// Return the inode number of this directory entry. +    #[cfg(any(freebsdlike, netbsdlike))] +    #[inline] +    pub fn ino(&self) -> u64 { +        #[allow(clippy::useless_conversion)] +        self.d_fileno.into() +    } +} + +/// libc's OpenBSD `dirent` has a private field so we can't construct it +/// directly, so we declare it ourselves to make all fields accessible. +#[cfg(target_os = "openbsd")] +#[repr(C)] +#[derive(Debug)] +struct libc_dirent { +    d_fileno: c::ino_t, +    d_off: c::off_t, +    d_reclen: u16, +    d_type: u8, +    d_namlen: u8, +    __d_padding: [u8; 4], +    d_name: [c::c_char; 256], +} + +/// We have our own copy of OpenBSD's dirent; check that the layout +/// minimally matches libc's. +#[cfg(target_os = "openbsd")] +fn check_dirent_layout(dirent: &c::dirent) { +    use crate::utils::as_ptr; + +    // Check that the basic layouts match. +    #[cfg(test)] +    { +        assert_eq_size!(libc_dirent, c::dirent); +        assert_eq_size!(libc_dirent, c::dirent); +    } + +    // Check that the field offsets match. +    assert_eq!( +        { +            let z = libc_dirent { +                d_fileno: 0_u64, +                d_off: 0_i64, +                d_reclen: 0_u16, +                d_type: 0_u8, +                d_namlen: 0_u8, +                __d_padding: [0_u8; 4], +                d_name: [0 as c::c_char; 256], +            }; +            let base = as_ptr(&z) as usize; +            ( +                (as_ptr(&z.d_fileno) as usize) - base, +                (as_ptr(&z.d_off) as usize) - base, +                (as_ptr(&z.d_reclen) as usize) - base, +                (as_ptr(&z.d_type) as usize) - base, +                (as_ptr(&z.d_namlen) as usize) - base, +                (as_ptr(&z.d_name) as usize) - base, +            ) +        }, +        { +            let z = dirent; +            let base = as_ptr(z) as usize; +            ( +                (as_ptr(&z.d_fileno) as usize) - base, +                (as_ptr(&z.d_off) as usize) - base, +                (as_ptr(&z.d_reclen) as usize) - base, +                (as_ptr(&z.d_type) as usize) - base, +                (as_ptr(&z.d_namlen) as usize) - base, +                (as_ptr(&z.d_name) as usize) - base, +            ) +        } +    ); +} + +#[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 `readdir` to fail. +    unsafe { +        let raw_fd = c::dirfd(dir.libc_dir.as_ptr()); +        let mut owned_fd: crate::fd::OwnedFd = crate::fd::FromRawFd::from_raw_fd(raw_fd); +        crate::io::dup2(&file_fd, &mut owned_fd).unwrap(); +        core::mem::forget(owned_fd); +    } + +    // FreeBSD and macOS seem to read some directory entries before we call +    // `.next()`. +    #[cfg(any(apple, freebsdlike))] +    { +        dir.rewind(); +    } + +    assert!(matches!(dir.next(), Some(Err(_)))); +    assert!(dir.next().is_none()); +} diff --git a/vendor/rustix/src/backend/libc/fs/inotify.rs b/vendor/rustix/src/backend/libc/fs/inotify.rs new file mode 100644 index 0000000..2044bd9 --- /dev/null +++ b/vendor/rustix/src/backend/libc/fs/inotify.rs @@ -0,0 +1,131 @@ +//! inotify support for working with inotifies + +use crate::backend::c; +use crate::backend::conv::{borrowed_fd, c_str, ret, ret_c_int, ret_owned_fd}; +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: u32 { +        /// `IN_CLOEXEC` +        const CLOEXEC = bitcast!(c::IN_CLOEXEC); +        /// `IN_NONBLOCK` +        const NONBLOCK = bitcast!(c::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: u32 { +        /// `IN_ACCESS` +        const ACCESS = c::IN_ACCESS; +        /// `IN_ATTRIB` +        const ATTRIB = c::IN_ATTRIB; +        /// `IN_CLOSE_NOWRITE` +        const CLOSE_NOWRITE = c::IN_CLOSE_NOWRITE; +        /// `IN_CLOSE_WRITE` +        const CLOSE_WRITE = c::IN_CLOSE_WRITE; +        /// `IN_CREATE` +        const CREATE = c::IN_CREATE; +        /// `IN_DELETE` +        const DELETE = c::IN_DELETE; +        /// `IN_DELETE_SELF` +        const DELETE_SELF = c::IN_DELETE_SELF; +        /// `IN_MODIFY` +        const MODIFY = c::IN_MODIFY; +        /// `IN_MOVE_SELF` +        const MOVE_SELF = c::IN_MOVE_SELF; +        /// `IN_MOVED_FROM` +        const MOVED_FROM = c::IN_MOVED_FROM; +        /// `IN_MOVED_TO` +        const MOVED_TO = c::IN_MOVED_TO; +        /// `IN_OPEN` +        const OPEN = c::IN_OPEN; + +        /// `IN_CLOSE` +        const CLOSE = c::IN_CLOSE; +        /// `IN_MOVE` +        const MOVE = c::IN_MOVE; +        /// `IN_ALL_EVENTS` +        const ALL_EVENTS = c::IN_ALL_EVENTS; + +        /// `IN_DONT_FOLLOW` +        const DONT_FOLLOW = c::IN_DONT_FOLLOW; +        /// `IN_EXCL_UNLINK` +        const EXCL_UNLINK = 1; +        /// `IN_MASK_ADD` +        const MASK_ADD = 1; +        /// `IN_MASK_CREATE` +        const MASK_CREATE = 1; +        /// `IN_ONESHOT` +        const ONESHOT = c::IN_ONESHOT; +        /// `IN_ONLYDIR` +        const ONLYDIR = c::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")] +pub fn inotify_init(flags: CreateFlags) -> io::Result<OwnedFd> { +    // SAFETY: `inotify_init1` has no safety preconditions. +    unsafe { ret_owned_fd(c::inotify_init1(bitflags_bits!(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. +pub fn inotify_add_watch<P: crate::path::Arg>( +    inot: BorrowedFd<'_>, +    path: P, +    flags: WatchFlags, +) -> io::Result<i32> { +    path.into_with_c_str(|path| { +        // SAFETY: The fd and path we are passing is guaranteed valid by the +        // type system. +        unsafe { +            ret_c_int(c::inotify_add_watch( +                borrowed_fd(inot), +                c_str(path), +                flags.bits(), +            )) +        } +    }) +} + +/// `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")] +pub fn inotify_remove_watch(inot: BorrowedFd<'_>, wd: i32) -> io::Result<()> { +    // Android's `inotify_rm_watch` takes `u32` despite that +    // `inotify_add_watch` expects a `i32`. +    #[cfg(target_os = "android")] +    let wd = wd as u32; +    // SAFETY: The fd is valid and closing an arbitrary wd is valid. +    unsafe { ret(c::inotify_rm_watch(borrowed_fd(inot), wd)) } +} diff --git a/vendor/rustix/src/backend/libc/fs/makedev.rs b/vendor/rustix/src/backend/libc/fs/makedev.rs new file mode 100644 index 0000000..aa12102 --- /dev/null +++ b/vendor/rustix/src/backend/libc/fs/makedev.rs @@ -0,0 +1,138 @@ +#[cfg(not(all(target_os = "android", target_pointer_width = "32")))] +use crate::backend::c; +use crate::fs::Dev; + +#[cfg(not(any( +    apple, +    solarish, +    target_os = "aix", +    target_os = "android", +    target_os = "emscripten", +)))] +#[inline] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { +    c::makedev(maj, min) +} + +#[cfg(solarish)] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { +    // SAFETY: Solarish's `makedev` is marked unsafe but it isn't doing +    // anything unsafe. +    unsafe { c::makedev(maj, min) } +} + +#[cfg(all(target_os = "android", not(target_pointer_width = "32")))] +#[inline] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { +    c::makedev(maj, min) +} + +#[cfg(all(target_os = "android", target_pointer_width = "32"))] +#[inline] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { +    // 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit, so we do +    // it ourselves. +    ((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) +} + +#[cfg(target_os = "emscripten")] +#[inline] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { +    // Emscripten's `makedev` has a 32-bit return value. +    Dev::from(c::makedev(maj, min)) +} + +#[cfg(apple)] +#[inline] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { +    // Apple's `makedev` oddly has signed argument types and is `unsafe`. +    unsafe { c::makedev(maj as i32, min as i32) } +} + +#[cfg(target_os = "aix")] +#[inline] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { +    // AIX's `makedev` oddly is `unsafe`. +    unsafe { c::makedev(maj, min) } +} + +#[cfg(not(any( +    apple, +    freebsdlike, +    target_os = "android", +    target_os = "emscripten", +    target_os = "netbsd" +)))] +#[inline] +pub(crate) fn major(dev: Dev) -> u32 { +    unsafe { c::major(dev) } +} + +#[cfg(any( +    apple, +    freebsdlike, +    target_os = "netbsd", +    all(target_os = "android", not(target_pointer_width = "32")), +))] +#[inline] +pub(crate) fn major(dev: Dev) -> u32 { +    // On some platforms `major` oddly has signed return types. +    (unsafe { c::major(dev) }) as u32 +} + +#[cfg(all(target_os = "android", target_pointer_width = "32"))] +#[inline] +pub(crate) fn major(dev: Dev) -> u32 { +    // 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit, so we do +    // it ourselves. +    (((dev >> 31 >> 1) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)) as u32 +} + +#[cfg(target_os = "emscripten")] +#[inline] +pub(crate) fn major(dev: Dev) -> u32 { +    // Emscripten's `major` has a 32-bit argument value. +    unsafe { c::major(dev as u32) } +} + +#[cfg(not(any( +    apple, +    freebsdlike, +    target_os = "android", +    target_os = "emscripten", +    target_os = "netbsd" +)))] +#[inline] +pub(crate) fn minor(dev: Dev) -> u32 { +    unsafe { c::minor(dev) } +} + +#[cfg(any( +    apple, +    freebsdlike, +    target_os = "netbsd", +    all(target_os = "android", not(target_pointer_width = "32")) +))] +#[inline] +pub(crate) fn minor(dev: Dev) -> u32 { +    // On some platforms, `minor` oddly has signed return types. +    (unsafe { c::minor(dev) }) as u32 +} + +#[cfg(all(target_os = "android", target_pointer_width = "32"))] +#[inline] +pub(crate) fn minor(dev: Dev) -> u32 { +    // 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit, so we do +    // it ourselves. +    (((dev >> 12) & 0xffff_ff00) | (dev & 0x0000_00ff)) as u32 +} + +#[cfg(target_os = "emscripten")] +#[inline] +pub(crate) fn minor(dev: Dev) -> u32 { +    // Emscripten's `minor` has a 32-bit argument value. +    unsafe { c::minor(dev as u32) } +} diff --git a/vendor/rustix/src/backend/libc/fs/mod.rs b/vendor/rustix/src/backend/libc/fs/mod.rs new file mode 100644 index 0000000..c17e863 --- /dev/null +++ b/vendor/rustix/src/backend/libc/fs/mod.rs @@ -0,0 +1,23 @@ +#[cfg(all(feature = "alloc", not(any(target_os = "espidf", target_os = "redox"))))] +pub(crate) mod dir; +#[cfg(linux_kernel)] +pub mod inotify; +#[cfg(not(any( +    target_os = "espidf", +    target_os = "haiku", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi" +)))] +pub(crate) mod makedev; +#[cfg(not(windows))] +pub(crate) mod syscalls; +pub(crate) mod types; + +// TODO: Fix linux-raw-sys to define ioctl codes for sparc. +#[cfg(all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")))] +pub(crate) const EXT4_IOC_RESIZE_FS: crate::ioctl::RawOpcode = 0x8008_6610; + +#[cfg(all(linux_kernel, not(any(target_arch = "sparc", target_arch = "sparc64"))))] +pub(crate) const EXT4_IOC_RESIZE_FS: crate::ioctl::RawOpcode = +    linux_raw_sys::ioctl::EXT4_IOC_RESIZE_FS as crate::ioctl::RawOpcode; diff --git a/vendor/rustix/src/backend/libc/fs/syscalls.rs b/vendor/rustix/src/backend/libc/fs/syscalls.rs new file mode 100644 index 0000000..70e86cf --- /dev/null +++ b/vendor/rustix/src/backend/libc/fs/syscalls.rs @@ -0,0 +1,2514 @@ +//! libc syscalls supporting `rustix::fs`. + +use crate::backend::c; +#[cfg(any( +    not(target_os = "redox"), +    feature = "alloc", +    all(linux_kernel, feature = "procfs") +))] +use crate::backend::conv::ret_usize; +use crate::backend::conv::{borrowed_fd, c_str, ret, ret_c_int, ret_off_t, ret_owned_fd}; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::ffi::CStr; +#[cfg(all(apple, feature = "alloc"))] +use crate::ffi::CString; +#[cfg(not(any(target_os = "espidf", target_os = "vita")))] +use crate::fs::Access; +#[cfg(not(any( +    apple, +    netbsdlike, +    solarish, +    target_os = "dragonfly", +    target_os = "espidf", +    target_os = "haiku", +    target_os = "redox", +    target_os = "vita", +)))] +use crate::fs::Advice; +#[cfg(not(any(target_os = "espidf", target_os = "redox")))] +use crate::fs::AtFlags; +#[cfg(not(any( +    netbsdlike, +    solarish, +    target_os = "aix", +    target_os = "dragonfly", +    target_os = "espidf", +    target_os = "nto", +    target_os = "redox", +    target_os = "vita", +)))] +use crate::fs::FallocateFlags; +#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))] +use crate::fs::FlockOperation; +#[cfg(any(linux_kernel, target_os = "freebsd"))] +use crate::fs::MemfdFlags; +#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))] +use crate::fs::SealFlags; +#[cfg(not(any( +    solarish, +    target_os = "espidf", +    target_os = "haiku", +    target_os = "netbsd", +    target_os = "nto", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi", +)))] +use crate::fs::StatFs; +#[cfg(not(any(target_os = "espidf", target_os = "vita")))] +use crate::fs::Timestamps; +#[cfg(not(any( +    apple, +    target_os = "espidf", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi" +)))] +use crate::fs::{Dev, FileType}; +use crate::fs::{Mode, OFlags, SeekFrom, Stat}; +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +use crate::fs::{StatVfs, StatVfsMountFlags}; +use crate::io; +#[cfg(all(target_env = "gnu", fix_y2038))] +use crate::timespec::LibcTimespec; +#[cfg(not(target_os = "wasi"))] +use crate::ugid::{Gid, Uid}; +#[cfg(all(apple, feature = "alloc"))] +use alloc::vec; +use core::mem::MaybeUninit; +#[cfg(apple)] +use { +    crate::backend::conv::nonnegative_ret, +    crate::fs::{copyfile_state_t, CloneFlags, CopyfileFlags}, +}; +#[cfg(any(apple, linux_kernel))] +use {crate::fs::XattrFlags, core::mem::size_of, core::ptr::null_mut}; +#[cfg(linux_kernel)] +use { +    crate::fs::{RenameFlags, ResolveFlags, Statx, StatxFlags, CWD}, +    core::ptr::null, +}; + +#[cfg(all(target_env = "gnu", fix_y2038))] +weak!(fn __utimensat64(c::c_int, *const c::c_char, *const LibcTimespec, c::c_int) -> c::c_int); +#[cfg(all(target_env = "gnu", fix_y2038))] +weak!(fn __futimens64(c::c_int, *const LibcTimespec) -> c::c_int); + +/// Use a direct syscall (via libc) for `open`. +/// +/// This is only currently necessary as a workaround for old glibc; see below. +#[cfg(all(unix, target_env = "gnu"))] +fn open_via_syscall(path: &CStr, oflags: OFlags, mode: Mode) -> io::Result<OwnedFd> { +    // Linux on aarch64, loongarch64 and riscv64 has no `open` syscall so use +    // `openat`. +    #[cfg(any( +        target_arch = "aarch64", +        target_arch = "riscv32", +        target_arch = "riscv64", +        target_arch = "csky", +        target_arch = "loongarch64" +    ))] +    { +        openat_via_syscall(CWD, path, oflags, mode) +    } + +    // Use the `open` syscall. +    #[cfg(not(any( +        target_arch = "aarch64", +        target_arch = "riscv32", +        target_arch = "riscv64", +        target_arch = "csky", +        target_arch = "loongarch64" +    )))] +    unsafe { +        syscall! { +            fn open( +                pathname: *const c::c_char, +                oflags: c::c_int, +                mode: c::mode_t +            ) via SYS_open -> c::c_int +        } + +        ret_owned_fd(open( +            c_str(path), +            bitflags_bits!(oflags), +            bitflags_bits!(mode), +        )) +    } +} + +pub(crate) fn open(path: &CStr, oflags: OFlags, mode: Mode) -> io::Result<OwnedFd> { +    // Work around <https://sourceware.org/bugzilla/show_bug.cgi?id=17523>. +    // glibc versions before 2.25 don't handle `O_TMPFILE` correctly. +    #[cfg(all(unix, target_env = "gnu", not(target_os = "hurd")))] +    if oflags.contains(OFlags::TMPFILE) && crate::backend::if_glibc_is_less_than_2_25() { +        return open_via_syscall(path, oflags, mode); +    } + +    // On these platforms, `mode_t` is `u16` and can't be passed directly to a +    // variadic function. +    #[cfg(any( +        apple, +        freebsdlike, +        all(target_os = "android", target_pointer_width = "32") +    ))] +    let mode: c::c_uint = mode.bits().into(); + +    // Otherwise, cast to `mode_t` as that's what `open` is documented to take. +    #[cfg(not(any( +        apple, +        freebsdlike, +        all(target_os = "android", target_pointer_width = "32") +    )))] +    let mode: c::mode_t = mode.bits() as _; + +    unsafe { ret_owned_fd(c::open(c_str(path), bitflags_bits!(oflags), mode)) } +} + +/// Use a direct syscall (via libc) for `openat`. +/// +/// This is only currently necessary as a workaround for old glibc; see below. +#[cfg(all(unix, target_env = "gnu", not(target_os = "hurd")))] +fn openat_via_syscall( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    oflags: OFlags, +    mode: Mode, +) -> io::Result<OwnedFd> { +    syscall! { +        fn openat( +            base_dirfd: c::c_int, +            pathname: *const c::c_char, +            oflags: c::c_int, +            mode: c::mode_t +        ) via SYS_openat -> c::c_int +    } + +    unsafe { +        ret_owned_fd(openat( +            borrowed_fd(dirfd), +            c_str(path), +            bitflags_bits!(oflags), +            bitflags_bits!(mode), +        )) +    } +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn openat( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    oflags: OFlags, +    mode: Mode, +) -> io::Result<OwnedFd> { +    // Work around <https://sourceware.org/bugzilla/show_bug.cgi?id=17523>. +    // glibc versions before 2.25 don't handle `O_TMPFILE` correctly. +    #[cfg(all(unix, target_env = "gnu", not(target_os = "hurd")))] +    if oflags.contains(OFlags::TMPFILE) && crate::backend::if_glibc_is_less_than_2_25() { +        return openat_via_syscall(dirfd, path, oflags, mode); +    } + +    // On these platforms, `mode_t` is `u16` and can't be passed directly to a +    // variadic function. +    #[cfg(any( +        apple, +        freebsdlike, +        all(target_os = "android", target_pointer_width = "32") +    ))] +    let mode: c::c_uint = mode.bits().into(); + +    // Otherwise, cast to `mode_t` as that's what `open` is documented to take. +    #[cfg(not(any( +        apple, +        freebsdlike, +        all(target_os = "android", target_pointer_width = "32") +    )))] +    let mode: c::mode_t = mode.bits() as _; + +    unsafe { +        ret_owned_fd(c::openat( +            borrowed_fd(dirfd), +            c_str(path), +            bitflags_bits!(oflags), +            mode, +        )) +    } +} + +#[cfg(not(any( +    solarish, +    target_os = "espidf", +    target_os = "haiku", +    target_os = "netbsd", +    target_os = "nto", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi", +)))] +#[inline] +pub(crate) fn statfs(filename: &CStr) -> io::Result<StatFs> { +    unsafe { +        let mut result = MaybeUninit::<StatFs>::uninit(); +        ret(c::statfs(c_str(filename), result.as_mut_ptr()))?; +        Ok(result.assume_init()) +    } +} + +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[inline] +pub(crate) fn statvfs(filename: &CStr) -> io::Result<StatVfs> { +    unsafe { +        let mut result = MaybeUninit::<c::statvfs>::uninit(); +        ret(c::statvfs(c_str(filename), result.as_mut_ptr()))?; +        Ok(libc_statvfs_to_statvfs(result.assume_init())) +    } +} + +#[cfg(feature = "alloc")] +#[inline] +pub(crate) fn readlink(path: &CStr, buf: &mut [u8]) -> io::Result<usize> { +    unsafe { +        ret_usize( +            c::readlink(c_str(path), buf.as_mut_ptr().cast::<c::c_char>(), buf.len()) as isize, +        ) +    } +} + +#[cfg(not(target_os = "redox"))] +#[inline] +pub(crate) fn readlinkat( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    buf: &mut [MaybeUninit<u8>], +) -> io::Result<usize> { +    unsafe { +        ret_usize(c::readlinkat( +            borrowed_fd(dirfd), +            c_str(path), +            buf.as_mut_ptr().cast::<c::c_char>(), +            buf.len(), +        ) as isize) +    } +} + +pub(crate) fn mkdir(path: &CStr, mode: Mode) -> io::Result<()> { +    unsafe { ret(c::mkdir(c_str(path), mode.bits() as c::mode_t)) } +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn mkdirat(dirfd: BorrowedFd<'_>, path: &CStr, mode: Mode) -> io::Result<()> { +    unsafe { +        ret(c::mkdirat( +            borrowed_fd(dirfd), +            c_str(path), +            mode.bits() as c::mode_t, +        )) +    } +} + +#[cfg(linux_kernel)] +pub(crate) fn getdents_uninit( +    fd: BorrowedFd<'_>, +    buf: &mut [MaybeUninit<u8>], +) -> io::Result<usize> { +    syscall! { +        fn getdents64( +            fd: c::c_int, +            dirp: *mut c::c_void, +            count: usize +        ) via SYS_getdents64 -> c::ssize_t +    } +    unsafe { +        ret_usize(getdents64( +            borrowed_fd(fd), +            buf.as_mut_ptr().cast::<c::c_void>(), +            buf.len(), +        )) +    } +} + +pub(crate) fn link(old_path: &CStr, new_path: &CStr) -> io::Result<()> { +    unsafe { ret(c::link(c_str(old_path), c_str(new_path))) } +} + +#[cfg(not(any(target_os = "espidf", target_os = "redox")))] +pub(crate) fn linkat( +    old_dirfd: BorrowedFd<'_>, +    old_path: &CStr, +    new_dirfd: BorrowedFd<'_>, +    new_path: &CStr, +    flags: AtFlags, +) -> io::Result<()> { +    // macOS <= 10.9 lacks `linkat`. +    #[cfg(target_os = "macos")] +    unsafe { +        weak! { +            fn linkat( +                c::c_int, +                *const c::c_char, +                c::c_int, +                *const c::c_char, +                c::c_int +            ) -> c::c_int +        } +        // If we have `linkat`, use it. +        if let Some(libc_linkat) = linkat.get() { +            return ret(libc_linkat( +                borrowed_fd(old_dirfd), +                c_str(old_path), +                borrowed_fd(new_dirfd), +                c_str(new_path), +                bitflags_bits!(flags), +            )); +        } +        // Otherwise, see if we can emulate the `AT_FDCWD` case. +        if borrowed_fd(old_dirfd) != c::AT_FDCWD || borrowed_fd(new_dirfd) != c::AT_FDCWD { +            return Err(io::Errno::NOSYS); +        } +        if flags.intersects(!AtFlags::SYMLINK_FOLLOW) { +            return Err(io::Errno::INVAL); +        } +        if !flags.is_empty() { +            return Err(io::Errno::OPNOTSUPP); +        } +        ret(c::link(c_str(old_path), c_str(new_path))) +    } + +    #[cfg(not(target_os = "macos"))] +    unsafe { +        ret(c::linkat( +            borrowed_fd(old_dirfd), +            c_str(old_path), +            borrowed_fd(new_dirfd), +            c_str(new_path), +            bitflags_bits!(flags), +        )) +    } +} + +pub(crate) fn rmdir(path: &CStr) -> io::Result<()> { +    unsafe { ret(c::rmdir(c_str(path))) } +} + +pub(crate) fn unlink(path: &CStr) -> io::Result<()> { +    unsafe { ret(c::unlink(c_str(path))) } +} + +#[cfg(not(any(target_os = "espidf", target_os = "redox")))] +pub(crate) fn unlinkat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<()> { +    // macOS <= 10.9 lacks `unlinkat`. +    #[cfg(target_os = "macos")] +    unsafe { +        weak! { +            fn unlinkat( +                c::c_int, +                *const c::c_char, +                c::c_int +            ) -> c::c_int +        } +        // If we have `unlinkat`, use it. +        if let Some(libc_unlinkat) = unlinkat.get() { +            return ret(libc_unlinkat( +                borrowed_fd(dirfd), +                c_str(path), +                bitflags_bits!(flags), +            )); +        } +        // Otherwise, see if we can emulate the `AT_FDCWD` case. +        if borrowed_fd(dirfd) != c::AT_FDCWD { +            return Err(io::Errno::NOSYS); +        } +        if flags.intersects(!AtFlags::REMOVEDIR) { +            return Err(io::Errno::INVAL); +        } +        if flags.contains(AtFlags::REMOVEDIR) { +            ret(c::rmdir(c_str(path))) +        } else { +            ret(c::unlink(c_str(path))) +        } +    } + +    #[cfg(not(target_os = "macos"))] +    unsafe { +        ret(c::unlinkat( +            borrowed_fd(dirfd), +            c_str(path), +            bitflags_bits!(flags), +        )) +    } +} + +pub(crate) fn rename(old_path: &CStr, new_path: &CStr) -> io::Result<()> { +    unsafe { ret(c::rename(c_str(old_path), c_str(new_path))) } +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn renameat( +    old_dirfd: BorrowedFd<'_>, +    old_path: &CStr, +    new_dirfd: BorrowedFd<'_>, +    new_path: &CStr, +) -> io::Result<()> { +    // macOS <= 10.9 lacks `renameat`. +    #[cfg(target_os = "macos")] +    unsafe { +        weak! { +            fn renameat( +                c::c_int, +                *const c::c_char, +                c::c_int, +                *const c::c_char +            ) -> c::c_int +        } +        // If we have `renameat`, use it. +        if let Some(libc_renameat) = renameat.get() { +            return ret(libc_renameat( +                borrowed_fd(old_dirfd), +                c_str(old_path), +                borrowed_fd(new_dirfd), +                c_str(new_path), +            )); +        } +        // Otherwise, see if we can emulate the `AT_FDCWD` case. +        if borrowed_fd(old_dirfd) != c::AT_FDCWD || borrowed_fd(new_dirfd) != c::AT_FDCWD { +            return Err(io::Errno::NOSYS); +        } +        ret(c::rename(c_str(old_path), c_str(new_path))) +    } + +    #[cfg(not(target_os = "macos"))] +    unsafe { +        ret(c::renameat( +            borrowed_fd(old_dirfd), +            c_str(old_path), +            borrowed_fd(new_dirfd), +            c_str(new_path), +        )) +    } +} + +#[cfg(all(target_os = "linux", target_env = "gnu"))] +pub(crate) fn renameat2( +    old_dirfd: BorrowedFd<'_>, +    old_path: &CStr, +    new_dirfd: BorrowedFd<'_>, +    new_path: &CStr, +    flags: RenameFlags, +) -> io::Result<()> { +    // `renameat2` wasn't supported in glibc until 2.28. +    weak_or_syscall! { +        fn renameat2( +            olddirfd: c::c_int, +            oldpath: *const c::c_char, +            newdirfd: c::c_int, +            newpath: *const c::c_char, +            flags: c::c_uint +        ) via SYS_renameat2 -> c::c_int +    } + +    unsafe { +        ret(renameat2( +            borrowed_fd(old_dirfd), +            c_str(old_path), +            borrowed_fd(new_dirfd), +            c_str(new_path), +            flags.bits(), +        )) +    } +} + +#[cfg(any( +    target_os = "android", +    all(target_os = "linux", not(target_env = "gnu")), +))] +#[inline] +pub(crate) fn renameat2( +    old_dirfd: BorrowedFd<'_>, +    old_path: &CStr, +    new_dirfd: BorrowedFd<'_>, +    new_path: &CStr, +    flags: RenameFlags, +) -> io::Result<()> { +    // At present, `libc` only has `renameat2` defined for glibc. If we have +    // no flags, we can use plain `renameat`, but otherwise we use `syscall!`. +    // to call `renameat2` ourselves. +    if flags.is_empty() { +        renameat(old_dirfd, old_path, new_dirfd, new_path) +    } else { +        syscall! { +            fn renameat2( +                olddirfd: c::c_int, +                oldpath: *const c::c_char, +                newdirfd: c::c_int, +                newpath: *const c::c_char, +                flags: c::c_uint +            ) via SYS_renameat2 -> c::c_int +        } + +        unsafe { +            ret(renameat2( +                borrowed_fd(old_dirfd), +                c_str(old_path), +                borrowed_fd(new_dirfd), +                c_str(new_path), +                flags.bits(), +            )) +        } +    } +} + +pub(crate) fn symlink(old_path: &CStr, new_path: &CStr) -> io::Result<()> { +    unsafe { ret(c::symlink(c_str(old_path), c_str(new_path))) } +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn symlinkat( +    old_path: &CStr, +    new_dirfd: BorrowedFd<'_>, +    new_path: &CStr, +) -> io::Result<()> { +    unsafe { +        ret(c::symlinkat( +            c_str(old_path), +            borrowed_fd(new_dirfd), +            c_str(new_path), +        )) +    } +} + +pub(crate) fn stat(path: &CStr) -> io::Result<Stat> { +    // See the comments in `fstat` about using `crate::fs::statx` here. +    #[cfg(all( +        linux_kernel, +        any( +            target_pointer_width = "32", +            target_arch = "mips64", +            target_arch = "mips64r6" +        ) +    ))] +    { +        match crate::fs::statx( +            crate::fs::CWD, +            path, +            AtFlags::empty(), +            StatxFlags::BASIC_STATS, +        ) { +            Ok(x) => statx_to_stat(x), +            Err(io::Errno::NOSYS) => statat_old(crate::fs::CWD, path, AtFlags::empty()), +            Err(err) => Err(err), +        } +    } + +    // Main version: libc is y2038 safe. Or, the platform is not y2038 safe and +    // there's nothing practical we can do. +    #[cfg(not(all( +        linux_kernel, +        any( +            target_pointer_width = "32", +            target_arch = "mips64", +            target_arch = "mips64r6" +        ) +    )))] +    unsafe { +        let mut stat = MaybeUninit::<Stat>::uninit(); +        ret(c::stat(c_str(path), stat.as_mut_ptr()))?; +        Ok(stat.assume_init()) +    } +} + +pub(crate) fn lstat(path: &CStr) -> io::Result<Stat> { +    // See the comments in `fstat` about using `crate::fs::statx` here. +    #[cfg(all( +        linux_kernel, +        any( +            target_pointer_width = "32", +            target_arch = "mips64", +            target_arch = "mips64r6" +        ) +    ))] +    { +        match crate::fs::statx( +            crate::fs::CWD, +            path, +            AtFlags::SYMLINK_NOFOLLOW, +            StatxFlags::BASIC_STATS, +        ) { +            Ok(x) => statx_to_stat(x), +            Err(io::Errno::NOSYS) => statat_old(crate::fs::CWD, path, AtFlags::SYMLINK_NOFOLLOW), +            Err(err) => Err(err), +        } +    } + +    // Main version: libc is y2038 safe. Or, the platform is not y2038 safe and +    // there's nothing practical we can do. +    #[cfg(not(all( +        linux_kernel, +        any( +            target_pointer_width = "32", +            target_arch = "mips64", +            target_arch = "mips64r6" +        ) +    )))] +    unsafe { +        let mut stat = MaybeUninit::<Stat>::uninit(); +        ret(c::lstat(c_str(path), stat.as_mut_ptr()))?; +        Ok(stat.assume_init()) +    } +} + +#[cfg(not(any(target_os = "espidf", target_os = "redox")))] +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(all( +        linux_kernel, +        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), +        } +    } + +    // Main version: libc is y2038 safe. Or, the platform is not y2038 safe and +    // there's nothing practical we can do. +    #[cfg(not(all( +        linux_kernel, +        any( +            target_pointer_width = "32", +            target_arch = "mips64", +            target_arch = "mips64r6" +        ) +    )))] +    unsafe { +        let mut stat = MaybeUninit::<Stat>::uninit(); +        ret(c::fstatat( +            borrowed_fd(dirfd), +            c_str(path), +            stat.as_mut_ptr(), +            bitflags_bits!(flags), +        ))?; +        Ok(stat.assume_init()) +    } +} + +#[cfg(all( +    linux_kernel, +    any( +        target_pointer_width = "32", +        target_arch = "mips64", +        target_arch = "mips64r6" +    ) +))] +fn statat_old(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat> { +    unsafe { +        let mut result = MaybeUninit::<c::stat64>::uninit(); +        ret(c::fstatat( +            borrowed_fd(dirfd), +            c_str(path), +            result.as_mut_ptr(), +            bitflags_bits!(flags), +        ))?; +        stat64_to_stat(result.assume_init()) +    } +} + +#[cfg(not(any(target_os = "espidf", target_os = "emscripten", target_os = "vita")))] +pub(crate) fn access(path: &CStr, access: Access) -> io::Result<()> { +    unsafe { ret(c::access(c_str(path), access.bits())) } +} + +#[cfg(not(any( +    target_os = "emscripten", +    target_os = "espidf", +    target_os = "redox", +    target_os = "vita" +)))] +pub(crate) fn accessat( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    access: Access, +    flags: AtFlags, +) -> io::Result<()> { +    // macOS <= 10.9 lacks `faccessat`. +    #[cfg(target_os = "macos")] +    unsafe { +        weak! { +            fn faccessat( +                c::c_int, +                *const c::c_char, +                c::c_int, +                c::c_int +            ) -> c::c_int +        } +        // If we have `faccessat`, use it. +        if let Some(libc_faccessat) = faccessat.get() { +            return ret(libc_faccessat( +                borrowed_fd(dirfd), +                c_str(path), +                bitflags_bits!(access), +                bitflags_bits!(flags), +            )); +        } +        // Otherwise, see if we can emulate the `AT_FDCWD` case. +        if borrowed_fd(dirfd) != c::AT_FDCWD { +            return Err(io::Errno::NOSYS); +        } +        if flags.intersects(!(AtFlags::EACCESS | AtFlags::SYMLINK_NOFOLLOW)) { +            return Err(io::Errno::INVAL); +        } +        if !flags.is_empty() { +            return Err(io::Errno::OPNOTSUPP); +        } +        ret(c::access(c_str(path), bitflags_bits!(access))) +    } + +    #[cfg(not(target_os = "macos"))] +    unsafe { +        ret(c::faccessat( +            borrowed_fd(dirfd), +            c_str(path), +            bitflags_bits!(access), +            bitflags_bits!(flags), +        )) +    } +} + +#[cfg(target_os = "emscripten")] +pub(crate) fn access(_path: &CStr, _access: Access) -> io::Result<()> { +    Ok(()) +} + +#[cfg(target_os = "emscripten")] +pub(crate) fn accessat( +    _dirfd: BorrowedFd<'_>, +    _path: &CStr, +    _access: Access, +    _flags: AtFlags, +) -> io::Result<()> { +    Ok(()) +} + +#[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "vita")))] +pub(crate) fn utimensat( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    times: &Timestamps, +    flags: AtFlags, +) -> io::Result<()> { +    // Old 32-bit version: libc has `utimensat` but it is not y2038 safe by +    // default. But there may be a `__utimensat16` we can use. +    #[cfg(fix_y2038)] +    { +        #[cfg(target_env = "gnu")] +        if let Some(libc_utimensat) = __utimensat64.get() { +            let libc_times: [LibcTimespec; 2] = [ +                times.last_access.clone().into(), +                times.last_modification.clone().into(), +            ]; + +            unsafe { +                return ret(libc_utimensat( +                    borrowed_fd(dirfd), +                    c_str(path), +                    libc_times.as_ptr(), +                    bitflags_bits!(flags), +                )); +            } +        } + +        utimensat_old(dirfd, path, times, flags) +    } + +    // Main version: libc is y2038 safe and has `utimensat`. Or, the platform +    // is not y2038 safe and there's nothing practical we can do. +    #[cfg(not(any(apple, fix_y2038)))] +    unsafe { +        use crate::utils::as_ptr; + +        ret(c::utimensat( +            borrowed_fd(dirfd), +            c_str(path), +            as_ptr(times).cast(), +            bitflags_bits!(flags), +        )) +    } + +    // Apple version: `utimensat` was introduced in macOS 10.13. +    #[cfg(apple)] +    unsafe { +        use crate::utils::as_ptr; + +        // ABI details +        weak! { +            fn utimensat( +                c::c_int, +                *const c::c_char, +                *const c::timespec, +                c::c_int +            ) -> c::c_int +        } +        extern "C" { +            fn setattrlist( +                path: *const c::c_char, +                attr_list: *const Attrlist, +                attr_buf: *const c::c_void, +                attr_buf_size: c::size_t, +                options: c::c_ulong, +            ) -> c::c_int; +        } +        const FSOPT_NOFOLLOW: c::c_ulong = 0x0000_0001; + +        // If we have `utimensat`, use it. +        if let Some(have_utimensat) = utimensat.get() { +            return ret(have_utimensat( +                borrowed_fd(dirfd), +                c_str(path), +                as_ptr(times).cast(), +                bitflags_bits!(flags), +            )); +        } + +        // `setattrlistat` was introduced in 10.13 along with `utimensat`, so +        // if we don't have `utimensat`, we don't have `setattrlistat` either. +        // Emulate it using `fork`, and `fchdir` and [`setattrlist`]. +        // +        // [`setattrlist`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setattrlist.2.html +        match c::fork() { +            -1 => Err(io::Errno::IO), +            0 => { +                if c::fchdir(borrowed_fd(dirfd)) != 0 { +                    let code = match libc_errno::errno().0 { +                        c::EACCES => 2, +                        c::ENOTDIR => 3, +                        _ => 1, +                    }; +                    c::_exit(code); +                } + +                let mut flags_arg = 0; +                if flags.contains(AtFlags::SYMLINK_NOFOLLOW) { +                    flags_arg |= FSOPT_NOFOLLOW; +                } + +                let (attrbuf_size, times, attrs) = times_to_attrlist(times); + +                if setattrlist( +                    c_str(path), +                    &attrs, +                    as_ptr(×).cast(), +                    attrbuf_size, +                    flags_arg, +                ) != 0 +                { +                    // Translate expected `errno` codes into ad-hoc integer +                    // values suitable for exit statuses. +                    let code = match libc_errno::errno().0 { +                        c::EACCES => 2, +                        c::ENOTDIR => 3, +                        c::EPERM => 4, +                        c::EROFS => 5, +                        c::ELOOP => 6, +                        c::ENOENT => 7, +                        c::ENAMETOOLONG => 8, +                        c::EINVAL => 9, +                        c::ESRCH => 10, +                        c::ENOTSUP => 11, +                        _ => 1, +                    }; +                    c::_exit(code); +                } + +                c::_exit(0); +            } +            child_pid => { +                let mut wstatus = 0; +                let _ = ret_c_int(c::waitpid(child_pid, &mut wstatus, 0))?; +                if c::WIFEXITED(wstatus) { +                    // Translate our ad-hoc exit statuses back to `errno` +                    // codes. +                    match c::WEXITSTATUS(wstatus) { +                        0 => Ok(()), +                        2 => Err(io::Errno::ACCESS), +                        3 => Err(io::Errno::NOTDIR), +                        4 => Err(io::Errno::PERM), +                        5 => Err(io::Errno::ROFS), +                        6 => Err(io::Errno::LOOP), +                        7 => Err(io::Errno::NOENT), +                        8 => Err(io::Errno::NAMETOOLONG), +                        9 => Err(io::Errno::INVAL), +                        10 => Err(io::Errno::SRCH), +                        11 => Err(io::Errno::NOTSUP), +                        _ => Err(io::Errno::IO), +                    } +                } else { +                    Err(io::Errno::IO) +                } +            } +        } +    } +} + +#[cfg(fix_y2038)] +fn utimensat_old( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    times: &Timestamps, +    flags: AtFlags, +) -> io::Result<()> { +    let old_times = [ +        c::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::OVERFLOW)?, +        }, +        c::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::OVERFLOW)?, +        }, +    ]; +    unsafe { +        ret(c::utimensat( +            borrowed_fd(dirfd), +            c_str(path), +            old_times.as_ptr(), +            bitflags_bits!(flags), +        )) +    } +} + +#[cfg(not(target_os = "wasi"))] +pub(crate) fn chmod(path: &CStr, mode: Mode) -> io::Result<()> { +    unsafe { ret(c::chmod(c_str(path), mode.bits() as c::mode_t)) } +} + +#[cfg(not(any( +    linux_kernel, +    target_os = "espidf", +    target_os = "redox", +    target_os = "wasi" +)))] +pub(crate) fn chmodat( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    mode: Mode, +    flags: AtFlags, +) -> io::Result<()> { +    unsafe { +        ret(c::fchmodat( +            borrowed_fd(dirfd), +            c_str(path), +            mode.bits() as c::mode_t, +            bitflags_bits!(flags), +        )) +    } +} + +#[cfg(linux_kernel)] +pub(crate) fn chmodat( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    mode: Mode, +    flags: AtFlags, +) -> io::Result<()> { +    // Linux's `fchmodat` does not have a flags argument. +    // +    // Use `c::syscall` rather than `c::fchmodat` because some libc +    // implementations, such as musl, add extra logic to `fchmod` to emulate +    // support for `AT_SYMLINK_NOFOLLOW`, which uses `/proc` outside our +    // control. +    syscall! { +        fn fchmodat( +            base_dirfd: c::c_int, +            pathname: *const c::c_char, +            mode: c::mode_t +        ) via SYS_fchmodat -> c::c_int +    } +    if flags == AtFlags::SYMLINK_NOFOLLOW { +        return Err(io::Errno::OPNOTSUPP); +    } +    if !flags.is_empty() { +        return Err(io::Errno::INVAL); +    } +    unsafe { +        ret(fchmodat( +            borrowed_fd(dirfd), +            c_str(path), +            mode.bits() as c::mode_t, +        )) +    } +} + +#[cfg(apple)] +pub(crate) fn fclonefileat( +    srcfd: BorrowedFd<'_>, +    dst_dirfd: BorrowedFd<'_>, +    dst: &CStr, +    flags: CloneFlags, +) -> io::Result<()> { +    syscall! { +        fn fclonefileat( +            srcfd: BorrowedFd<'_>, +            dst_dirfd: BorrowedFd<'_>, +            dst: *const c::c_char, +            flags: c::c_int +        ) via SYS_fclonefileat -> c::c_int +    } + +    unsafe { +        ret(fclonefileat( +            srcfd, +            dst_dirfd, +            c_str(dst), +            bitflags_bits!(flags), +        )) +    } +} + +#[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] +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(c::fchownat( +            borrowed_fd(dirfd), +            c_str(path), +            ow, +            gr, +            bitflags_bits!(flags), +        )) +    } +} + +#[cfg(not(any( +    apple, +    target_os = "espidf", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi" +)))] +pub(crate) fn mknodat( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    file_type: FileType, +    mode: Mode, +    dev: Dev, +) -> io::Result<()> { +    unsafe { +        ret(c::mknodat( +            borrowed_fd(dirfd), +            c_str(path), +            (mode.bits() | file_type.as_raw_mode()) as c::mode_t, +            dev.try_into().map_err(|_e| io::Errno::PERM)?, +        )) +    } +} + +#[cfg(linux_kernel)] +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> { +    syscall! { +        fn copy_file_range( +            fd_in: c::c_int, +            off_in: *mut c::loff_t, +            fd_out: c::c_int, +            off_out: *mut c::loff_t, +            len: usize, +            flags: c::c_uint +        ) via SYS_copy_file_range -> c::ssize_t +    } + +    let mut off_in_val: c::loff_t = 0; +    let mut off_out_val: c::loff_t = 0; +    // Silently cast; we'll get `EINVAL` if the value is negative. +    let off_in_ptr = if let Some(off_in) = &off_in { +        off_in_val = **off_in as i64; +        &mut off_in_val +    } else { +        null_mut() +    }; +    let off_out_ptr = if let Some(off_out) = &off_out { +        off_out_val = **off_out as i64; +        &mut off_out_val +    } else { +        null_mut() +    }; +    let copied = unsafe { +        ret_usize(copy_file_range( +            borrowed_fd(fd_in), +            off_in_ptr, +            borrowed_fd(fd_out), +            off_out_ptr, +            len, +            0, // no flags are defined yet +        ))? +    }; +    if let Some(off_in) = off_in { +        *off_in = off_in_val as u64; +    } +    if let Some(off_out) = off_out { +        *off_out = off_out_val as u64; +    } +    Ok(copied) +} + +#[cfg(not(any( +    apple, +    netbsdlike, +    solarish, +    target_os = "dragonfly", +    target_os = "espidf", +    target_os = "haiku", +    target_os = "redox", +    target_os = "vita", +)))] +pub(crate) fn fadvise(fd: BorrowedFd<'_>, offset: u64, len: u64, advice: Advice) -> io::Result<()> { +    let offset = offset as i64; +    let len = len as i64; + +    // FreeBSD returns `EINVAL` on invalid offsets; emulate the POSIX behavior. +    #[cfg(target_os = "freebsd")] +    let offset = if (offset as i64) < 0 { +        i64::MAX +    } else { +        offset +    }; + +    // FreeBSD returns `EINVAL` on overflow; emulate the POSIX behavior. +    #[cfg(target_os = "freebsd")] +    let len = if len > 0 && offset.checked_add(len).is_none() { +        i64::MAX - offset +    } else { +        len +    }; + +    let err = unsafe { c::posix_fadvise(borrowed_fd(fd), offset, len, advice as c::c_int) }; + +    // `posix_fadvise` returns its error status rather than using `errno`. +    if err == 0 { +        Ok(()) +    } else { +        Err(io::Errno(err)) +    } +} + +pub(crate) fn fcntl_getfl(fd: BorrowedFd<'_>) -> io::Result<OFlags> { +    let flags = unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GETFL))? }; +    Ok(OFlags::from_bits_retain(bitcast!(flags))) +} + +pub(crate) fn fcntl_setfl(fd: BorrowedFd<'_>, flags: OFlags) -> io::Result<()> { +    unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_SETFL, flags.bits())) } +} + +#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))] +pub(crate) fn fcntl_get_seals(fd: BorrowedFd<'_>) -> io::Result<SealFlags> { +    let flags = unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GET_SEALS))? }; +    Ok(SealFlags::from_bits_retain(bitcast!(flags))) +} + +#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))] +pub(crate) fn fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Result<()> { +    unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_ADD_SEALS, seals.bits())) } +} + +#[cfg(not(any( +    target_os = "emscripten", +    target_os = "espidf", +    target_os = "fuchsia", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi" +)))] +#[inline] +pub(crate) fn fcntl_lock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> { +    use c::{flock, F_RDLCK, F_SETLK, F_SETLKW, F_UNLCK, F_WRLCK, SEEK_SET}; + +    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), +    }; + +    unsafe { +        let mut lock: flock = core::mem::zeroed(); +        lock.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. +        lock.l_whence = SEEK_SET as _; +        lock.l_start = 0; +        lock.l_len = 0; + +        ret(c::fcntl(borrowed_fd(fd), cmd, &lock)) +    } +} + +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. +            (c::SEEK_SET, pos as i64) +        } +        SeekFrom::End(offset) => (c::SEEK_END, offset), +        SeekFrom::Current(offset) => (c::SEEK_CUR, offset), +        #[cfg(any(apple, freebsdlike, linux_kernel, solarish))] +        SeekFrom::Data(offset) => (c::SEEK_DATA, offset), +        #[cfg(any(apple, freebsdlike, linux_kernel, solarish))] +        SeekFrom::Hole(offset) => (c::SEEK_HOLE, offset), +    }; + +    // ESP-IDF and Vita don't support 64-bit offsets. +    #[cfg(any(target_os = "espidf", target_os = "vita"))] +    let offset: i32 = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?; + +    let offset = unsafe { ret_off_t(c::lseek(borrowed_fd(fd), offset, whence))? }; +    Ok(offset as u64) +} + +pub(crate) fn tell(fd: BorrowedFd<'_>) -> io::Result<u64> { +    let offset = unsafe { ret_off_t(c::lseek(borrowed_fd(fd), 0, c::SEEK_CUR))? }; +    Ok(offset as u64) +} + +#[cfg(not(any(linux_kernel, target_os = "wasi")))] +pub(crate) fn fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()> { +    unsafe { ret(c::fchmod(borrowed_fd(fd), bitflags_bits!(mode))) } +} + +#[cfg(linux_kernel)] +pub(crate) fn fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()> { +    // Use `c::syscall` rather than `c::fchmod` because some libc +    // implementations, such as musl, add extra logic to `fchmod` to emulate +    // support for `O_PATH`, which uses `/proc` outside our control and +    // interferes with our own use of `O_PATH`. +    syscall! { +        fn fchmod( +            fd: c::c_int, +            mode: c::mode_t +        ) via SYS_fchmod -> c::c_int +    } +    unsafe { ret(fchmod(borrowed_fd(fd), mode.bits() as c::mode_t)) } +} + +#[cfg(not(target_os = "wasi"))] +pub(crate) fn chown(path: &CStr, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> { +    unsafe { +        let (ow, gr) = crate::ugid::translate_fchown_args(owner, group); +        ret(c::chown(c_str(path), ow, gr)) +    } +} + +#[cfg(linux_kernel)] +pub(crate) fn fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> { +    // Use `c::syscall` rather than `c::fchown` because some libc +    // implementations, such as musl, add extra logic to `fchown` to emulate +    // support for `O_PATH`, which uses `/proc` outside our control and +    // interferes with our own use of `O_PATH`. +    syscall! { +        fn fchown( +            fd: c::c_int, +            owner: c::uid_t, +            group: c::gid_t +        ) via SYS_fchown -> c::c_int +    } +    unsafe { +        let (ow, gr) = crate::ugid::translate_fchown_args(owner, group); +        ret(fchown(borrowed_fd(fd), ow, gr)) +    } +} + +#[cfg(not(any(linux_kernel, target_os = "wasi")))] +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(c::fchown(borrowed_fd(fd), ow, gr)) +    } +} + +#[cfg(not(any( +    target_os = "espidf", +    target_os = "solaris", +    target_os = "vita", +    target_os = "wasi" +)))] +pub(crate) fn flock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> { +    unsafe { ret(c::flock(borrowed_fd(fd), operation as c::c_int)) } +} + +#[cfg(linux_kernel)] +pub(crate) fn syncfs(fd: BorrowedFd<'_>) -> io::Result<()> { +    // Some versions of Android libc lack a `syncfs` function. +    #[cfg(target_os = "android")] +    syscall! { +        fn syncfs(fd: c::c_int) via SYS_syncfs -> c::c_int +    } + +    // `syncfs` was added to glibc in 2.20. +    #[cfg(not(target_os = "android"))] +    weak_or_syscall! { +        fn syncfs(fd: c::c_int) via SYS_syncfs -> c::c_int +    } + +    unsafe { ret(syncfs(borrowed_fd(fd))) } +} + +#[cfg(not(any( +    target_os = "espidf", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi" +)))] +pub(crate) fn sync() { +    unsafe { c::sync() } +} + +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(all( +        linux_kernel, +        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), +        } +    } + +    // Main version: libc is y2038 safe. Or, the platform is not y2038 safe and +    // there's nothing practical we can do. +    #[cfg(not(all( +        linux_kernel, +        any( +            target_pointer_width = "32", +            target_arch = "mips64", +            target_arch = "mips64r6" +        ) +    )))] +    unsafe { +        let mut stat = MaybeUninit::<Stat>::uninit(); +        ret(c::fstat(borrowed_fd(fd), stat.as_mut_ptr()))?; +        Ok(stat.assume_init()) +    } +} + +#[cfg(all( +    linux_kernel, +    any( +        target_pointer_width = "32", +        target_arch = "mips64", +        target_arch = "mips64r6" +    ) +))] +fn fstat_old(fd: BorrowedFd<'_>) -> io::Result<Stat> { +    unsafe { +        let mut result = MaybeUninit::<c::stat64>::uninit(); +        ret(c::fstat(borrowed_fd(fd), result.as_mut_ptr()))?; +        stat64_to_stat(result.assume_init()) +    } +} + +#[cfg(not(any( +    solarish, +    target_os = "espidf", +    target_os = "haiku", +    target_os = "netbsd", +    target_os = "nto", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi", +)))] +pub(crate) fn fstatfs(fd: BorrowedFd<'_>) -> io::Result<StatFs> { +    let mut statfs = MaybeUninit::<StatFs>::uninit(); +    unsafe { +        ret(c::fstatfs(borrowed_fd(fd), statfs.as_mut_ptr()))?; +        Ok(statfs.assume_init()) +    } +} + +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +pub(crate) fn fstatvfs(fd: BorrowedFd<'_>) -> io::Result<StatVfs> { +    let mut statvfs = MaybeUninit::<c::statvfs>::uninit(); +    unsafe { +        ret(c::fstatvfs(borrowed_fd(fd), statvfs.as_mut_ptr()))?; +        Ok(libc_statvfs_to_statvfs(statvfs.assume_init())) +    } +} + +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +fn libc_statvfs_to_statvfs(from: c::statvfs) -> StatVfs { +    StatVfs { +        f_bsize: from.f_bsize as u64, +        f_frsize: from.f_frsize as u64, +        f_blocks: from.f_blocks as u64, +        f_bfree: from.f_bfree as u64, +        f_bavail: from.f_bavail as u64, +        f_files: from.f_files as u64, +        f_ffree: from.f_ffree as u64, +        f_favail: from.f_ffree as u64, +        #[cfg(not(target_os = "aix"))] +        f_fsid: from.f_fsid as u64, +        #[cfg(target_os = "aix")] +        f_fsid: ((from.f_fsid.val[0] as u64) << 32) | from.f_fsid.val[1], +        f_flag: StatVfsMountFlags::from_bits_retain(from.f_flag as u64), +        f_namemax: from.f_namemax as u64, +    } +} + +#[cfg(not(any(target_os = "espidf", target_os = "vita")))] +pub(crate) fn futimens(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()> { +    // Old 32-bit version: libc has `futimens` but it is not y2038 safe by +    // default. But there may be a `__futimens64` we can use. +    #[cfg(fix_y2038)] +    { +        #[cfg(target_env = "gnu")] +        if let Some(libc_futimens) = __futimens64.get() { +            let libc_times: [LibcTimespec; 2] = [ +                times.last_access.clone().into(), +                times.last_modification.clone().into(), +            ]; + +            unsafe { +                return ret(libc_futimens(borrowed_fd(fd), libc_times.as_ptr())); +            } +        } + +        futimens_old(fd, times) +    } + +    // Main version: libc is y2038 safe and has `futimens`. Or, the platform +    // is not y2038 safe and there's nothing practical we can do. +    #[cfg(not(any(apple, fix_y2038)))] +    unsafe { +        use crate::utils::as_ptr; + +        ret(c::futimens(borrowed_fd(fd), as_ptr(times).cast())) +    } + +    // Apple version: `futimens` was introduced in macOS 10.13. +    #[cfg(apple)] +    unsafe { +        use crate::utils::as_ptr; + +        // ABI details. +        weak! { +            fn futimens(c::c_int, *const c::timespec) -> c::c_int +        } +        extern "C" { +            fn fsetattrlist( +                fd: c::c_int, +                attr_list: *const Attrlist, +                attr_buf: *const c::c_void, +                attr_buf_size: c::size_t, +                options: c::c_ulong, +            ) -> c::c_int; +        } + +        // If we have `futimens`, use it. +        if let Some(have_futimens) = futimens.get() { +            return ret(have_futimens(borrowed_fd(fd), as_ptr(times).cast())); +        } + +        // Otherwise use `fsetattrlist`. +        let (attrbuf_size, times, attrs) = times_to_attrlist(times); + +        ret(fsetattrlist( +            borrowed_fd(fd), +            &attrs, +            as_ptr(×).cast(), +            attrbuf_size, +            0, +        )) +    } +} + +#[cfg(fix_y2038)] +fn futimens_old(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()> { +    let old_times = [ +        c::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::OVERFLOW)?, +        }, +        c::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::OVERFLOW)?, +        }, +    ]; + +    unsafe { ret(c::futimens(borrowed_fd(fd), old_times.as_ptr())) } +} + +#[cfg(not(any( +    apple, +    netbsdlike, +    solarish, +    target_os = "aix", +    target_os = "dragonfly", +    target_os = "espidf", +    target_os = "nto", +    target_os = "redox", +    target_os = "vita", +)))] +pub(crate) fn fallocate( +    fd: BorrowedFd<'_>, +    mode: FallocateFlags, +    offset: u64, +    len: u64, +) -> io::Result<()> { +    // Silently cast; we'll get `EINVAL` if the value is negative. +    let offset = offset as i64; +    let len = len as i64; + +    #[cfg(any(linux_kernel, target_os = "fuchsia"))] +    unsafe { +        ret(c::fallocate( +            borrowed_fd(fd), +            bitflags_bits!(mode), +            offset, +            len, +        )) +    } + +    #[cfg(not(any(linux_kernel, target_os = "fuchsia")))] +    { +        assert!(mode.is_empty()); +        let err = unsafe { c::posix_fallocate(borrowed_fd(fd), offset, len) }; + +        // `posix_fallocate` returns its error status rather than using +        // `errno`. +        if err == 0 { +            Ok(()) +        } else { +            Err(io::Errno(err)) +        } +    } +} + +#[cfg(apple)] +pub(crate) fn fallocate( +    fd: BorrowedFd<'_>, +    mode: FallocateFlags, +    offset: u64, +    len: u64, +) -> io::Result<()> { +    let offset: i64 = offset.try_into().map_err(|_e| io::Errno::INVAL)?; +    let len = len as i64; + +    assert!(mode.is_empty()); + +    let new_len = offset.checked_add(len).ok_or(io::Errno::FBIG)?; +    let mut store = c::fstore_t { +        fst_flags: c::F_ALLOCATECONTIG, +        fst_posmode: c::F_PEOFPOSMODE, +        fst_offset: 0, +        fst_length: new_len, +        fst_bytesalloc: 0, +    }; +    unsafe { +        if c::fcntl(borrowed_fd(fd), c::F_PREALLOCATE, &store) == -1 { +            // Unable to allocate contiguous disk space; attempt to allocate +            // non-contiguously. +            store.fst_flags = c::F_ALLOCATEALL; +            let _ = ret_c_int(c::fcntl(borrowed_fd(fd), c::F_PREALLOCATE, &store))?; +        } +        ret(c::ftruncate(borrowed_fd(fd), new_len)) +    } +} + +pub(crate) fn fsync(fd: BorrowedFd<'_>) -> io::Result<()> { +    unsafe { ret(c::fsync(borrowed_fd(fd))) } +} + +#[cfg(not(any( +    apple, +    target_os = "dragonfly", +    target_os = "espidf", +    target_os = "haiku", +    target_os = "redox", +    target_os = "vita", +)))] +pub(crate) fn fdatasync(fd: BorrowedFd<'_>) -> io::Result<()> { +    unsafe { ret(c::fdatasync(borrowed_fd(fd))) } +} + +pub(crate) fn ftruncate(fd: BorrowedFd<'_>, length: u64) -> io::Result<()> { +    let length = length.try_into().map_err(|_overflow_err| io::Errno::FBIG)?; +    unsafe { ret(c::ftruncate(borrowed_fd(fd), length)) } +} + +#[cfg(any(linux_kernel, target_os = "freebsd"))] +pub(crate) fn memfd_create(name: &CStr, flags: MemfdFlags) -> io::Result<OwnedFd> { +    #[cfg(target_os = "freebsd")] +    weakcall! { +        fn memfd_create( +            name: *const c::c_char, +            flags: c::c_uint +        ) -> c::c_int +    } + +    #[cfg(linux_kernel)] +    weak_or_syscall! { +        fn memfd_create( +            name: *const c::c_char, +            flags: c::c_uint +        ) via SYS_memfd_create -> c::c_int +    } + +    unsafe { ret_owned_fd(memfd_create(c_str(name), bitflags_bits!(flags))) } +} + +#[cfg(linux_kernel)] +pub(crate) fn openat2( +    dirfd: BorrowedFd<'_>, +    path: &CStr, +    oflags: OFlags, +    mode: Mode, +    resolve: ResolveFlags, +) -> io::Result<OwnedFd> { +    use linux_raw_sys::general::open_how; + +    syscall! { +        fn openat2( +            base_dirfd: c::c_int, +            pathname: *const c::c_char, +            how: *mut open_how, +            size: usize +        ) via SYS_OPENAT2 -> c::c_int +    } + +    let oflags = oflags.bits(); +    let mut open_how = open_how { +        flags: u64::from(oflags), +        mode: u64::from(mode.bits()), +        resolve: resolve.bits(), +    }; + +    unsafe { +        ret_owned_fd(openat2( +            borrowed_fd(dirfd), +            c_str(path), +            &mut open_how, +            size_of::<open_how>(), +        )) +    } +} +#[cfg(all(linux_kernel, target_pointer_width = "32"))] +const SYS_OPENAT2: i32 = 437; +#[cfg(all(linux_kernel, target_pointer_width = "64"))] +const SYS_OPENAT2: i64 = 437; + +#[cfg(target_os = "linux")] +pub(crate) fn sendfile( +    out_fd: BorrowedFd<'_>, +    in_fd: BorrowedFd<'_>, +    offset: Option<&mut u64>, +    count: usize, +) -> io::Result<usize> { +    unsafe { +        ret_usize(c::sendfile64( +            borrowed_fd(out_fd), +            borrowed_fd(in_fd), +            offset.map_or(null_mut(), crate::utils::as_mut_ptr).cast(), +            count, +        )) +    } +} + +/// Convert from a Linux `statx` value to rustix's `Stat`. +#[cfg(all(linux_kernel, target_pointer_width = "32"))] +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).into(), +        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).into(), +        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 as _, +        st_mtime: x +            .stx_mtime +            .tv_sec +            .try_into() +            .map_err(|_| io::Errno::OVERFLOW)?, +        st_mtime_nsec: x.stx_mtime.tv_nsec as _, +        st_ctime: x +            .stx_ctime +            .tv_sec +            .try_into() +            .map_err(|_| io::Errno::OVERFLOW)?, +        st_ctime_nsec: x.stx_ctime.tv_nsec as _, +        st_ino: x.stx_ino.into(), +    }) +} + +/// Convert from a Linux `statx` value to rustix's `Stat`. +/// +/// mips64' `struct stat64` in libc has private fields, and `stx_blocks` +#[cfg(all(linux_kernel, any(target_arch = "mips64", target_arch = "mips64r6")))] +fn statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat> { +    let mut result: Stat = unsafe { core::mem::zeroed() }; + +    result.st_dev = crate::fs::makedev(x.stx_dev_major, x.stx_dev_minor); +    result.st_mode = x.stx_mode.into(); +    result.st_nlink = x.stx_nlink.into(); +    result.st_uid = x.stx_uid.into(); +    result.st_gid = x.stx_gid.into(); +    result.st_rdev = crate::fs::makedev(x.stx_rdev_major, x.stx_rdev_minor); +    result.st_size = x.stx_size.try_into().map_err(|_| io::Errno::OVERFLOW)?; +    result.st_blksize = x.stx_blksize.into(); +    result.st_blocks = x.stx_blocks.try_into().map_err(|_e| io::Errno::OVERFLOW)?; +    result.st_atime = x +        .stx_atime +        .tv_sec +        .try_into() +        .map_err(|_| io::Errno::OVERFLOW)?; +    result.st_atime_nsec = x.stx_atime.tv_nsec as _; +    result.st_mtime = x +        .stx_mtime +        .tv_sec +        .try_into() +        .map_err(|_| io::Errno::OVERFLOW)?; +    result.st_mtime_nsec = x.stx_mtime.tv_nsec as _; +    result.st_ctime = x +        .stx_ctime +        .tv_sec +        .try_into() +        .map_err(|_| io::Errno::OVERFLOW)?; +    result.st_ctime_nsec = x.stx_ctime.tv_nsec as _; +    result.st_ino = x.stx_ino.into(); + +    Ok(result) +} + +/// Convert from a Linux `stat64` value to rustix's `Stat`. +#[cfg(all(linux_kernel, target_pointer_width = "32"))] +fn stat64_to_stat(s64: c::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 `stat64` value to rustix's `Stat`. +/// +/// mips64' `struct stat64` in libc has private fields, and `st_blocks` has +/// type `i64`. +#[cfg(all(linux_kernel, any(target_arch = "mips64", target_arch = "mips64r6")))] +fn stat64_to_stat(s64: c::stat64) -> io::Result<Stat> { +    let mut result: Stat = unsafe { core::mem::zeroed() }; + +    result.st_dev = s64.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?; +    result.st_mode = s64.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?; +    result.st_nlink = s64.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?; +    result.st_uid = s64.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?; +    result.st_gid = s64.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?; +    result.st_rdev = s64.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?; +    result.st_size = s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?; +    result.st_blksize = s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?; +    result.st_blocks = s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?; +    result.st_atime = s64.st_atime.try_into().map_err(|_| io::Errno::OVERFLOW)?; +    result.st_atime_nsec = s64 +        .st_atime_nsec +        .try_into() +        .map_err(|_| io::Errno::OVERFLOW)?; +    result.st_mtime = s64.st_mtime.try_into().map_err(|_| io::Errno::OVERFLOW)?; +    result.st_mtime_nsec = s64 +        .st_mtime_nsec +        .try_into() +        .map_err(|_| io::Errno::OVERFLOW)?; +    result.st_ctime = s64.st_ctime.try_into().map_err(|_| io::Errno::OVERFLOW)?; +    result.st_ctime_nsec = s64 +        .st_ctime_nsec +        .try_into() +        .map_err(|_| io::Errno::OVERFLOW)?; +    result.st_ino = s64.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?; + +    Ok(result) +} + +#[cfg(linux_kernel)] +#[allow(non_upper_case_globals)] +mod sys { +    use super::{c, BorrowedFd, Statx}; + +    weak_or_syscall! { +        pub(super) fn statx( +            dirfd_: BorrowedFd<'_>, +            path: *const c::c_char, +            flags: c::c_int, +            mask: c::c_uint, +            buf: *mut Statx +        ) via SYS_statx -> c::c_int +    } +} + +#[cfg(linux_kernel)] +#[allow(non_upper_case_globals)] +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/ +    #[cfg(not(any(target_os = "android", target_env = "musl")))] +    const STATX__RESERVED: u32 = c::STATX__RESERVED as u32; +    #[cfg(any(target_os = "android", target_env = "musl"))] +    const STATX__RESERVED: u32 = linux_raw_sys::general::STATX__RESERVED; +    if (mask.bits() & STATX__RESERVED) == STATX__RESERVED { +        return Err(io::Errno::INVAL); +    } +    let mask = mask & StatxFlags::all(); + +    let mut statx_buf = MaybeUninit::<Statx>::uninit(); +    unsafe { +        ret(sys::statx( +            dirfd, +            c_str(path), +            bitflags_bits!(flags), +            mask.bits(), +            statx_buf.as_mut_ptr(), +        ))?; +        Ok(statx_buf.assume_init()) +    } +} + +#[cfg(linux_kernel)] +#[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. +        matches!( +            ret(sys::statx(CWD, null(), 0, 0, null_mut())), +            Err(io::Errno::FAULT) +        ) +    } +} + +#[cfg(apple)] +pub(crate) unsafe fn fcopyfile( +    from: BorrowedFd<'_>, +    to: BorrowedFd<'_>, +    state: copyfile_state_t, +    flags: CopyfileFlags, +) -> io::Result<()> { +    extern "C" { +        fn fcopyfile( +            from: c::c_int, +            to: c::c_int, +            state: copyfile_state_t, +            flags: c::c_uint, +        ) -> c::c_int; +    } + +    nonnegative_ret(fcopyfile( +        borrowed_fd(from), +        borrowed_fd(to), +        state, +        bitflags_bits!(flags), +    )) +} + +#[cfg(apple)] +pub(crate) fn copyfile_state_alloc() -> io::Result<copyfile_state_t> { +    extern "C" { +        fn copyfile_state_alloc() -> copyfile_state_t; +    } + +    let result = unsafe { copyfile_state_alloc() }; +    if result.0.is_null() { +        Err(io::Errno::last_os_error()) +    } else { +        Ok(result) +    } +} + +#[cfg(apple)] +pub(crate) unsafe fn copyfile_state_free(state: copyfile_state_t) -> io::Result<()> { +    extern "C" { +        fn copyfile_state_free(state: copyfile_state_t) -> c::c_int; +    } + +    nonnegative_ret(copyfile_state_free(state)) +} + +#[cfg(apple)] +const COPYFILE_STATE_COPIED: u32 = 8; + +#[cfg(apple)] +pub(crate) unsafe fn copyfile_state_get_copied(state: copyfile_state_t) -> io::Result<u64> { +    let mut copied = MaybeUninit::<u64>::uninit(); +    copyfile_state_get(state, COPYFILE_STATE_COPIED, copied.as_mut_ptr().cast())?; +    Ok(copied.assume_init()) +} + +#[cfg(apple)] +pub(crate) unsafe fn copyfile_state_get( +    state: copyfile_state_t, +    flag: u32, +    dst: *mut c::c_void, +) -> io::Result<()> { +    extern "C" { +        fn copyfile_state_get(state: copyfile_state_t, flag: u32, dst: *mut c::c_void) -> c::c_int; +    } + +    nonnegative_ret(copyfile_state_get(state, flag, dst)) +} + +#[cfg(all(apple, feature = "alloc"))] +pub(crate) fn getpath(fd: BorrowedFd<'_>) -> io::Result<CString> { +    // The use of `PATH_MAX` is generally not encouraged, but it +    // is inevitable in this case because macOS defines `fcntl` with +    // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no +    // alternatives. If a better method is invented, it should be used +    // instead. +    let mut buf = vec![0; c::PATH_MAX as usize]; + +    // From the [macOS `fcntl` manual page]: +    // `F_GETPATH` - Get the path of the file descriptor `Fildes`. The argument +    //               must be a buffer of size `MAXPATHLEN` or greater. +    // +    // [macOS `fcntl` manual page]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html +    unsafe { +        ret(c::fcntl(borrowed_fd(fd), c::F_GETPATH, buf.as_mut_ptr()))?; +    } + +    let l = buf.iter().position(|&c| c == 0).unwrap(); +    buf.truncate(l); +    buf.shrink_to_fit(); + +    Ok(CString::new(buf).unwrap()) +} + +#[cfg(apple)] +pub(crate) fn fcntl_rdadvise(fd: BorrowedFd<'_>, offset: u64, len: u64) -> io::Result<()> { +    // From the [macOS `fcntl` manual page]: +    // `F_RDADVISE` - Issue an advisory read async with no copy to user. +    // +    // The `F_RDADVISE` command operates on the following structure which holds +    // information passed from the user to the system: +    // +    // ```c +    // struct radvisory { +    //      off_t   ra_offset;  /* offset into the file */ +    //      int     ra_count;   /* size of the read     */ +    // }; +    // ``` +    // +    // [macOS `fcntl` manual page]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html +    let ra_offset = match offset.try_into() { +        Ok(len) => len, +        // If this conversion fails, the user is providing an offset outside +        // any possible file extent, so just ignore it. +        Err(_) => return Ok(()), +    }; +    let ra_count = match len.try_into() { +        Ok(len) => len, +        // If this conversion fails, the user is providing a dubiously large +        // hint which is unlikely to improve performance. +        Err(_) => return Ok(()), +    }; +    unsafe { +        let radvisory = c::radvisory { +            ra_offset, +            ra_count, +        }; +        ret(c::fcntl(borrowed_fd(fd), c::F_RDADVISE, &radvisory)) +    } +} + +#[cfg(apple)] +pub(crate) fn fcntl_fullfsync(fd: BorrowedFd<'_>) -> io::Result<()> { +    unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_FULLFSYNC)) } +} + +#[cfg(apple)] +pub(crate) fn fcntl_nocache(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { +    unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_NOCACHE, value as c::c_int)) } +} + +#[cfg(apple)] +pub(crate) fn fcntl_global_nocache(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { +    unsafe { +        ret(c::fcntl( +            borrowed_fd(fd), +            c::F_GLOBAL_NOCACHE, +            value as c::c_int, +        )) +    } +} + +/// Convert `times` from a `futimens`/`utimensat` argument into `setattrlist` +/// arguments. +#[cfg(apple)] +fn times_to_attrlist(times: &Timestamps) -> (c::size_t, [c::timespec; 2], Attrlist) { +    // ABI details. +    const ATTR_CMN_MODTIME: u32 = 0x0000_0400; +    const ATTR_CMN_ACCTIME: u32 = 0x0000_1000; +    const ATTR_BIT_MAP_COUNT: u16 = 5; + +    let mut times = times.clone(); + +    // If we have any `UTIME_NOW` elements, replace them with the current time. +    if times.last_access.tv_nsec == c::UTIME_NOW || times.last_modification.tv_nsec == c::UTIME_NOW +    { +        let now = { +            let mut tv = c::timeval { +                tv_sec: 0, +                tv_usec: 0, +            }; +            unsafe { +                let r = c::gettimeofday(&mut tv, null_mut()); +                assert_eq!(r, 0); +            } +            c::timespec { +                tv_sec: tv.tv_sec, +                tv_nsec: (tv.tv_usec * 1000) as _, +            } +        }; +        if times.last_access.tv_nsec == c::UTIME_NOW { +            times.last_access = now; +        } +        if times.last_modification.tv_nsec == c::UTIME_NOW { +            times.last_modification = now; +        } +    } + +    // Pack the return values following the rules for [`getattrlist`]. +    // +    // [`getattrlist`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getattrlist.2.html +    let mut times_size = 0; +    let mut attrs = Attrlist { +        bitmapcount: ATTR_BIT_MAP_COUNT, +        reserved: 0, +        commonattr: 0, +        volattr: 0, +        dirattr: 0, +        fileattr: 0, +        forkattr: 0, +    }; +    let mut return_times = [c::timespec { +        tv_sec: 0, +        tv_nsec: 0, +    }; 2]; +    let mut times_index = 0; +    if times.last_modification.tv_nsec != c::UTIME_OMIT { +        attrs.commonattr |= ATTR_CMN_MODTIME; +        return_times[times_index] = times.last_modification; +        times_index += 1; +        times_size += size_of::<c::timespec>(); +    } +    if times.last_access.tv_nsec != c::UTIME_OMIT { +        attrs.commonattr |= ATTR_CMN_ACCTIME; +        return_times[times_index] = times.last_access; +        times_size += size_of::<c::timespec>(); +    } + +    (times_size, return_times, attrs) +} + +/// Support type for `Attrlist`. +#[cfg(apple)] +type Attrgroup = u32; + +/// Attribute list for use with `setattrlist`. +#[cfg(apple)] +#[repr(C)] +struct Attrlist { +    bitmapcount: u16, +    reserved: u16, +    commonattr: Attrgroup, +    volattr: Attrgroup, +    dirattr: Attrgroup, +    fileattr: Attrgroup, +    forkattr: Attrgroup, +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result<usize> { +    let value_ptr = value.as_mut_ptr(); + +    #[cfg(not(apple))] +    unsafe { +        ret_usize(c::getxattr( +            path.as_ptr(), +            name.as_ptr(), +            value_ptr.cast::<c::c_void>(), +            value.len(), +        )) +    } + +    #[cfg(apple)] +    unsafe { +        ret_usize(c::getxattr( +            path.as_ptr(), +            name.as_ptr(), +            value_ptr.cast::<c::c_void>(), +            value.len(), +            0, +            0, +        )) +    } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn lgetxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result<usize> { +    let value_ptr = value.as_mut_ptr(); + +    #[cfg(not(apple))] +    unsafe { +        ret_usize(c::lgetxattr( +            path.as_ptr(), +            name.as_ptr(), +            value_ptr.cast::<c::c_void>(), +            value.len(), +        )) +    } + +    #[cfg(apple)] +    unsafe { +        ret_usize(c::getxattr( +            path.as_ptr(), +            name.as_ptr(), +            value_ptr.cast::<c::c_void>(), +            value.len(), +            0, +            c::XATTR_NOFOLLOW, +        )) +    } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn fgetxattr(fd: BorrowedFd<'_>, name: &CStr, value: &mut [u8]) -> io::Result<usize> { +    let value_ptr = value.as_mut_ptr(); + +    #[cfg(not(apple))] +    unsafe { +        ret_usize(c::fgetxattr( +            borrowed_fd(fd), +            name.as_ptr(), +            value_ptr.cast::<c::c_void>(), +            value.len(), +        )) +    } + +    #[cfg(apple)] +    unsafe { +        ret_usize(c::fgetxattr( +            borrowed_fd(fd), +            name.as_ptr(), +            value_ptr.cast::<c::c_void>(), +            value.len(), +            0, +            0, +        )) +    } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn setxattr( +    path: &CStr, +    name: &CStr, +    value: &[u8], +    flags: XattrFlags, +) -> io::Result<()> { +    #[cfg(not(apple))] +    unsafe { +        ret(c::setxattr( +            path.as_ptr(), +            name.as_ptr(), +            value.as_ptr().cast::<c::c_void>(), +            value.len(), +            flags.bits() as i32, +        )) +    } + +    #[cfg(apple)] +    unsafe { +        ret(c::setxattr( +            path.as_ptr(), +            name.as_ptr(), +            value.as_ptr().cast::<c::c_void>(), +            value.len(), +            0, +            flags.bits() as i32, +        )) +    } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn lsetxattr( +    path: &CStr, +    name: &CStr, +    value: &[u8], +    flags: XattrFlags, +) -> io::Result<()> { +    #[cfg(not(apple))] +    unsafe { +        ret(c::lsetxattr( +            path.as_ptr(), +            name.as_ptr(), +            value.as_ptr().cast::<c::c_void>(), +            value.len(), +            flags.bits() as i32, +        )) +    } + +    #[cfg(apple)] +    unsafe { +        ret(c::setxattr( +            path.as_ptr(), +            name.as_ptr(), +            value.as_ptr().cast::<c::c_void>(), +            value.len(), +            0, +            flags.bits() as i32 | c::XATTR_NOFOLLOW, +        )) +    } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn fsetxattr( +    fd: BorrowedFd<'_>, +    name: &CStr, +    value: &[u8], +    flags: XattrFlags, +) -> io::Result<()> { +    #[cfg(not(apple))] +    unsafe { +        ret(c::fsetxattr( +            borrowed_fd(fd), +            name.as_ptr(), +            value.as_ptr().cast::<c::c_void>(), +            value.len(), +            flags.bits() as i32, +        )) +    } + +    #[cfg(apple)] +    unsafe { +        ret(c::fsetxattr( +            borrowed_fd(fd), +            name.as_ptr(), +            value.as_ptr().cast::<c::c_void>(), +            value.len(), +            0, +            flags.bits() as i32, +        )) +    } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn listxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result<usize> { +    #[cfg(not(apple))] +    unsafe { +        ret_usize(c::listxattr(path.as_ptr(), list.as_mut_ptr(), list.len())) +    } + +    #[cfg(apple)] +    unsafe { +        ret_usize(c::listxattr( +            path.as_ptr(), +            list.as_mut_ptr(), +            list.len(), +            0, +        )) +    } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn llistxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result<usize> { +    #[cfg(not(apple))] +    unsafe { +        ret_usize(c::llistxattr(path.as_ptr(), list.as_mut_ptr(), list.len())) +    } + +    #[cfg(apple)] +    unsafe { +        ret_usize(c::listxattr( +            path.as_ptr(), +            list.as_mut_ptr(), +            list.len(), +            c::XATTR_NOFOLLOW, +        )) +    } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn flistxattr(fd: BorrowedFd<'_>, list: &mut [c::c_char]) -> io::Result<usize> { +    let fd = borrowed_fd(fd); + +    #[cfg(not(apple))] +    unsafe { +        ret_usize(c::flistxattr(fd, list.as_mut_ptr(), list.len())) +    } + +    #[cfg(apple)] +    unsafe { +        ret_usize(c::flistxattr(fd, list.as_mut_ptr(), list.len(), 0)) +    } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn removexattr(path: &CStr, name: &CStr) -> io::Result<()> { +    #[cfg(not(apple))] +    unsafe { +        ret(c::removexattr(path.as_ptr(), name.as_ptr())) +    } + +    #[cfg(apple)] +    unsafe { +        ret(c::removexattr(path.as_ptr(), name.as_ptr(), 0)) +    } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn lremovexattr(path: &CStr, name: &CStr) -> io::Result<()> { +    #[cfg(not(apple))] +    unsafe { +        ret(c::lremovexattr(path.as_ptr(), name.as_ptr())) +    } + +    #[cfg(apple)] +    unsafe { +        ret(c::removexattr( +            path.as_ptr(), +            name.as_ptr(), +            c::XATTR_NOFOLLOW, +        )) +    } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn fremovexattr(fd: BorrowedFd<'_>, name: &CStr) -> io::Result<()> { +    let fd = borrowed_fd(fd); + +    #[cfg(not(apple))] +    unsafe { +        ret(c::fremovexattr(fd, name.as_ptr())) +    } + +    #[cfg(apple)] +    unsafe { +        ret(c::fremovexattr(fd, name.as_ptr(), 0)) +    } +} + +#[test] +fn test_sizes() { +    #[cfg(linux_kernel)] +    assert_eq_size!(c::loff_t, u64); + +    // Assert that `Timestamps` has the expected layout. If we're not fixing +    // y2038, libc's type should match ours. If we are, it's smaller. +    #[cfg(not(fix_y2038))] +    assert_eq_size!([c::timespec; 2], Timestamps); +    #[cfg(fix_y2038)] +    assert!(core::mem::size_of::<[c::timespec; 2]>() < core::mem::size_of::<Timestamps>()); +} diff --git a/vendor/rustix/src/backend/libc/fs/types.rs b/vendor/rustix/src/backend/libc/fs/types.rs new file mode 100644 index 0000000..19508c2 --- /dev/null +++ b/vendor/rustix/src/backend/libc/fs/types.rs @@ -0,0 +1,1164 @@ +use crate::backend::c; +use bitflags::bitflags; + +#[cfg(not(any(target_os = "espidf", target_os = "vita")))] +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_int { +        /// `R_OK` +        const READ_OK = c::R_OK; + +        /// `W_OK` +        const WRITE_OK = c::W_OK; + +        /// `X_OK` +        const EXEC_OK = c::X_OK; + +        /// `F_OK` +        const EXISTS = c::F_OK; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +#[cfg(not(any(target_os = "espidf", target_os = "redox")))] +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: u32 { +        /// `AT_SYMLINK_NOFOLLOW` +        const SYMLINK_NOFOLLOW = bitcast!(c::AT_SYMLINK_NOFOLLOW); + +        /// `AT_EACCESS` +        #[cfg(not(any(target_os = "emscripten", target_os = "android")))] +        const EACCESS = bitcast!(c::AT_EACCESS); + +        /// `AT_REMOVEDIR` +        const REMOVEDIR = bitcast!(c::AT_REMOVEDIR); + +        /// `AT_SYMLINK_FOLLOW` +        const SYMLINK_FOLLOW = bitcast!(c::AT_SYMLINK_FOLLOW); + +        /// `AT_NO_AUTOMOUNT` +        #[cfg(any(linux_like, target_os = "fuchsia"))] +        const NO_AUTOMOUNT = bitcast!(c::AT_NO_AUTOMOUNT); + +        /// `AT_EMPTY_PATH` +        #[cfg(any( +            linux_kernel, +            target_os = "freebsd", +            target_os = "fuchsia", +        ))] +        const EMPTY_PATH = bitcast!(c::AT_EMPTY_PATH); + +        /// `AT_RESOLVE_BENEATH` +        #[cfg(target_os = "freebsd")] +        const RESOLVE_BENEATH = bitcast!(c::AT_RESOLVE_BENEATH); + +        /// `AT_STATX_SYNC_AS_STAT` +        #[cfg(all(target_os = "linux", target_env = "gnu"))] +        const STATX_SYNC_AS_STAT = bitcast!(c::AT_STATX_SYNC_AS_STAT); + +        /// `AT_STATX_FORCE_SYNC` +        #[cfg(all(target_os = "linux", target_env = "gnu"))] +        const STATX_FORCE_SYNC = bitcast!(c::AT_STATX_FORCE_SYNC); + +        /// `AT_STATX_DONT_SYNC` +        #[cfg(all(target_os = "linux", target_env = "gnu"))] +        const STATX_DONT_SYNC = bitcast!(c::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` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const RWXU = c::S_IRWXU as RawMode; + +        /// `S_IRUSR` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const RUSR = c::S_IRUSR as RawMode; + +        /// `S_IWUSR` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const WUSR = c::S_IWUSR as RawMode; + +        /// `S_IXUSR` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const XUSR = c::S_IXUSR as RawMode; + +        /// `S_IRWXG` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const RWXG = c::S_IRWXG as RawMode; + +        /// `S_IRGRP` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const RGRP = c::S_IRGRP as RawMode; + +        /// `S_IWGRP` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const WGRP = c::S_IWGRP as RawMode; + +        /// `S_IXGRP` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const XGRP = c::S_IXGRP as RawMode; + +        /// `S_IRWXO` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const RWXO = c::S_IRWXO as RawMode; + +        /// `S_IROTH` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const ROTH = c::S_IROTH as RawMode; + +        /// `S_IWOTH` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const WOTH = c::S_IWOTH as RawMode; + +        /// `S_IXOTH` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const XOTH = c::S_IXOTH as RawMode; + +        /// `S_ISUID` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const SUID = c::S_ISUID as RawMode; + +        /// `S_ISGID` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const SGID = c::S_ISGID as RawMode; + +        /// `S_ISVTX` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const SVTX = c::S_ISVTX as RawMode; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +#[cfg(not(target_os = "espidf"))] +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() +    } +} + +#[cfg(not(target_os = "espidf"))] +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) +    } +} + +#[cfg(not(target_os = "espidf"))] +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: u32 { +        /// `O_ACCMODE` +        const ACCMODE = bitcast!(c::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 = bitcast!(c::O_RDONLY | c::O_WRONLY | c::O_RDWR); + +        /// `O_APPEND` +        const APPEND = bitcast!(c::O_APPEND); + +        /// `O_CREAT` +        #[doc(alias = "CREAT")] +        const CREATE = bitcast!(c::O_CREAT); + +        /// `O_DIRECTORY` +        #[cfg(not(target_os = "espidf"))] +        const DIRECTORY = bitcast!(c::O_DIRECTORY); + +        /// `O_DSYNC` +        #[cfg(not(any(target_os = "dragonfly", target_os = "espidf", target_os = "l4re", target_os = "redox", target_os = "vita")))] +        const DSYNC = bitcast!(c::O_DSYNC); + +        /// `O_EXCL` +        const EXCL = bitcast!(c::O_EXCL); + +        /// `O_FSYNC` +        #[cfg(any( +            bsd, +            all(target_os = "linux", not(target_env = "musl")), +        ))] +        const FSYNC = bitcast!(c::O_FSYNC); + +        /// `O_NOFOLLOW` +        #[cfg(not(target_os = "espidf"))] +        const NOFOLLOW = bitcast!(c::O_NOFOLLOW); + +        /// `O_NONBLOCK` +        const NONBLOCK = bitcast!(c::O_NONBLOCK); + +        /// `O_RDONLY` +        const RDONLY = bitcast!(c::O_RDONLY); + +        /// `O_WRONLY` +        const WRONLY = bitcast!(c::O_WRONLY); + +        /// `O_RDWR` +        /// +        /// This is not equal to `RDONLY | WRONLY`. It's a distinct flag. +        const RDWR = bitcast!(c::O_RDWR); + +        /// `O_NOCTTY` +        #[cfg(not(any(target_os = "espidf", target_os = "l4re", target_os = "redox", target_os = "vita")))] +        const NOCTTY = bitcast!(c::O_NOCTTY); + +        /// `O_RSYNC` +        #[cfg(any( +            linux_kernel, +            netbsdlike, +            target_os = "emscripten", +            target_os = "wasi", +        ))] +        const RSYNC = bitcast!(c::O_RSYNC); + +        /// `O_SYNC` +        #[cfg(not(any(target_os = "l4re", target_os = "redox")))] +        const SYNC = bitcast!(c::O_SYNC); + +        /// `O_TRUNC` +        const TRUNC = bitcast!(c::O_TRUNC); + +        /// `O_PATH` +        #[cfg(any( +            linux_kernel, +            target_os = "emscripten", +            target_os = "freebsd", +            target_os = "fuchsia", +            target_os = "redox", +        ))] +        const PATH = bitcast!(c::O_PATH); + +        /// `O_CLOEXEC` +        const CLOEXEC = bitcast!(c::O_CLOEXEC); + +        /// `O_TMPFILE` +        #[cfg(any( +            linux_kernel, +            target_os = "emscripten", +            target_os = "fuchsia", +        ))] +        const TMPFILE = bitcast!(c::O_TMPFILE); + +        /// `O_NOATIME` +        #[cfg(any( +            linux_kernel, +            target_os = "fuchsia", +        ))] +        const NOATIME = bitcast!(c::O_NOATIME); + +        /// `O_DIRECT` +        #[cfg(any( +            linux_kernel, +            target_os = "emscripten", +            target_os = "freebsd", +            target_os = "fuchsia", +            target_os = "netbsd", +        ))] +        const DIRECT = bitcast!(c::O_DIRECT); + +        /// `O_RESOLVE_BENEATH` +        #[cfg(target_os = "freebsd")] +        const RESOLVE_BENEATH = bitcast!(c::O_RESOLVE_BENEATH); + +        /// `O_EMPTY_PATH` +        #[cfg(target_os = "freebsd")] +        const EMPTY_PATH = bitcast!(c::O_EMPTY_PATH); + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +#[cfg(apple)] +bitflags! { +    /// `CLONE_*` constants for use with [`fclonefileat`]. +    /// +    /// [`fclonefileat`]: crate::fs::fclonefileat +    #[repr(transparent)] +    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +    pub struct CloneFlags: u32 { +        /// `CLONE_NOFOLLOW` +        const NOFOLLOW = 1; + +        /// `CLONE_NOOWNERCOPY` +        const NOOWNERCOPY = 2; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +#[cfg(apple)] +mod copyfile { +    pub(super) const ACL: u32 = 1 << 0; +    pub(super) const STAT: u32 = 1 << 1; +    pub(super) const XATTR: u32 = 1 << 2; +    pub(super) const DATA: u32 = 1 << 3; +    pub(super) const SECURITY: u32 = STAT | ACL; +    pub(super) const METADATA: u32 = SECURITY | XATTR; +    pub(super) const ALL: u32 = METADATA | DATA; +} + +#[cfg(apple)] +bitflags! { +    /// `COPYFILE_*` constants for use with [`fcopyfile`]. +    /// +    /// [`fcopyfile`]: crate::fs::fcopyfile +    #[repr(transparent)] +    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +    pub struct CopyfileFlags: c::c_uint { +        /// `COPYFILE_ACL` +        const ACL = copyfile::ACL; + +        /// `COPYFILE_STAT` +        const STAT = copyfile::STAT; + +        /// `COPYFILE_XATTR` +        const XATTR = copyfile::XATTR; + +        /// `COPYFILE_DATA` +        const DATA = copyfile::DATA; + +        /// `COPYFILE_SECURITY` +        const SECURITY = copyfile::SECURITY; + +        /// `COPYFILE_METADATA` +        const METADATA = copyfile::METADATA; + +        /// `COPYFILE_ALL` +        const ALL = copyfile::ALL; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +#[cfg(linux_kernel)] +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 = 0x01; + +        /// `RESOLVE_NO_MAGICLINKS` +        const NO_MAGICLINKS = 0x02; + +        /// `RESOLVE_NO_SYMLINKS` +        const NO_SYMLINKS = 0x04; + +        /// `RESOLVE_BENEATH` +        const BENEATH = 0x08; + +        /// `RESOLVE_IN_ROOT` +        const IN_ROOT = 0x10; + +        /// `RESOLVE_CACHED` (since Linux 5.12) +        const CACHED = 0x20; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +#[cfg(linux_kernel)] +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 = bitcast!(c::RENAME_EXCHANGE); + +        /// `RENAME_NOREPLACE` +        const NOREPLACE = bitcast!(c::RENAME_NOREPLACE); + +        /// `RENAME_WHITEOUT` +        const WHITEOUT = bitcast!(c::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 = c::S_IFREG as isize, + +    /// `S_IFDIR` +    Directory = c::S_IFDIR as isize, + +    /// `S_IFLNK` +    Symlink = c::S_IFLNK as isize, + +    /// `S_IFIFO` +    #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `S_IFIFO`. +    #[doc(alias = "IFO")] +    Fifo = c::S_IFIFO as isize, + +    /// `S_IFSOCK` +    #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `S_IFSOCK`. +    Socket = c::S_IFSOCK as isize, + +    /// `S_IFCHR` +    CharacterDevice = c::S_IFCHR as isize, + +    /// `S_IFBLK` +    BlockDevice = c::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 as c::mode_t) & c::S_IFMT { +            c::S_IFREG => Self::RegularFile, +            c::S_IFDIR => Self::Directory, +            c::S_IFLNK => Self::Symlink, +            #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `S_IFIFO`. +            c::S_IFIFO => Self::Fifo, +            #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `S_IFSOCK`. +            c::S_IFSOCK => Self::Socket, +            c::S_IFCHR => Self::CharacterDevice, +            c::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 => c::S_IFREG as RawMode, +            Self::Directory => c::S_IFDIR as RawMode, +            Self::Symlink => c::S_IFLNK as RawMode, +            #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `S_IFIFO`. +            Self::Fifo => c::S_IFIFO as RawMode, +            #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `S_IFSOCK`. +            Self::Socket => c::S_IFSOCK as RawMode, +            Self::CharacterDevice => c::S_IFCHR as RawMode, +            Self::BlockDevice => c::S_IFBLK as RawMode, +            Self::Unknown => c::S_IFMT as RawMode, +        } +    } + +    /// Construct a `FileType` from the `d_type` field of a `c::dirent`. +    #[cfg(not(any( +        solarish, +        target_os = "aix", +        target_os = "espidf", +        target_os = "haiku", +        target_os = "nto", +        target_os = "redox", +        target_os = "vita" +    )))] +    #[inline] +    pub(crate) const fn from_dirent_d_type(d_type: u8) -> Self { +        match d_type { +            c::DT_REG => Self::RegularFile, +            c::DT_DIR => Self::Directory, +            c::DT_LNK => Self::Symlink, +            #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `DT_SOCK`. +            c::DT_SOCK => Self::Socket, +            #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `DT_FIFO`. +            c::DT_FIFO => Self::Fifo, +            c::DT_CHR => Self::CharacterDevice, +            c::DT_BLK => Self::BlockDevice, +            // c::DT_UNKNOWN | +            _ => Self::Unknown, +        } +    } +} + +/// `POSIX_FADV_*` constants for use with [`fadvise`]. +/// +/// [`fadvise`]: crate::fs::fadvise +#[cfg(not(any( +    apple, +    netbsdlike, +    solarish, +    target_os = "dragonfly", +    target_os = "espidf", +    target_os = "haiku", +    target_os = "redox", +    target_os = "vita", +)))] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub enum Advice { +    /// `POSIX_FADV_NORMAL` +    Normal = c::POSIX_FADV_NORMAL as c::c_uint, + +    /// `POSIX_FADV_SEQUENTIAL` +    Sequential = c::POSIX_FADV_SEQUENTIAL as c::c_uint, + +    /// `POSIX_FADV_RANDOM` +    Random = c::POSIX_FADV_RANDOM as c::c_uint, + +    /// `POSIX_FADV_NOREUSE` +    NoReuse = c::POSIX_FADV_NOREUSE as c::c_uint, + +    /// `POSIX_FADV_WILLNEED` +    WillNeed = c::POSIX_FADV_WILLNEED as c::c_uint, + +    /// `POSIX_FADV_DONTNEED` +    DontNeed = c::POSIX_FADV_DONTNEED as c::c_uint, +} + +#[cfg(any(linux_kernel, target_os = "freebsd"))] +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 = c::MFD_CLOEXEC; + +        /// `MFD_ALLOW_SEALING` +        const ALLOW_SEALING = c::MFD_ALLOW_SEALING; + +        /// `MFD_HUGETLB` (since Linux 4.14) +        const HUGETLB = c::MFD_HUGETLB; + +        /// `MFD_HUGE_64KB` +        const HUGE_64KB = c::MFD_HUGE_64KB; +        /// `MFD_HUGE_512JB` +        const HUGE_512KB = c::MFD_HUGE_512KB; +        /// `MFD_HUGE_1MB` +        const HUGE_1MB = c::MFD_HUGE_1MB; +        /// `MFD_HUGE_2MB` +        const HUGE_2MB = c::MFD_HUGE_2MB; +        /// `MFD_HUGE_8MB` +        const HUGE_8MB = c::MFD_HUGE_8MB; +        /// `MFD_HUGE_16MB` +        const HUGE_16MB = c::MFD_HUGE_16MB; +        /// `MFD_HUGE_32MB` +        const HUGE_32MB = c::MFD_HUGE_32MB; +        /// `MFD_HUGE_256MB` +        const HUGE_256MB = c::MFD_HUGE_256MB; +        /// `MFD_HUGE_512MB` +        const HUGE_512MB = c::MFD_HUGE_512MB; +        /// `MFD_HUGE_1GB` +        const HUGE_1GB = c::MFD_HUGE_1GB; +        /// `MFD_HUGE_2GB` +        const HUGE_2GB = c::MFD_HUGE_2GB; +        /// `MFD_HUGE_16GB` +        const HUGE_16GB = c::MFD_HUGE_16GB; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))] +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 = bitcast!(c::F_SEAL_SEAL); +        /// `F_SEAL_SHRINK` +        const SHRINK = bitcast!(c::F_SEAL_SHRINK); +        /// `F_SEAL_GROW` +        const GROW = bitcast!(c::F_SEAL_GROW); +        /// `F_SEAL_WRITE` +        const WRITE = bitcast!(c::F_SEAL_WRITE); +        /// `F_SEAL_FUTURE_WRITE` (since Linux 5.1) +        #[cfg(linux_kernel)] +        const FUTURE_WRITE = bitcast!(c::F_SEAL_FUTURE_WRITE); + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +#[cfg(all(target_os = "linux", target_env = "gnu"))] +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; +    } +} + +#[cfg(any( +    target_os = "android", +    all(target_os = "linux", not(target_env = "gnu")), +))] +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 = 0x0001; + +        /// `STATX_MODE` +        const MODE = 0x0002; + +        /// `STATX_NLINK` +        const NLINK = 0x0004; + +        /// `STATX_UID` +        const UID = 0x0008; + +        /// `STATX_GID` +        const GID = 0x0010; + +        /// `STATX_ATIME` +        const ATIME = 0x0020; + +        /// `STATX_MTIME` +        const MTIME = 0x0040; + +        /// `STATX_CTIME` +        const CTIME = 0x0080; + +        /// `STATX_INO` +        const INO = 0x0100; + +        /// `STATX_SIZE` +        const SIZE = 0x0200; + +        /// `STATX_BLOCKS` +        const BLOCKS = 0x0400; + +        /// `STATX_BASIC_STATS` +        const BASIC_STATS = 0x07ff; + +        /// `STATX_BTIME` +        const BTIME = 0x800; + +        /// `STATX_MNT_ID` (since Linux 5.8) +        const MNT_ID = 0x1000; + +        /// `STATX_ALL` +        const ALL = 0xfff; + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +#[cfg(not(any( +    netbsdlike, +    solarish, +    target_os = "aix", +    target_os = "espidf", +    target_os = "nto", +    target_os = "redox", +    target_os = "vita" +)))] +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` +        #[cfg(not(any( +            bsd, +            target_os = "aix", +            target_os = "haiku", +            target_os = "hurd", +            target_os = "wasi", +        )))] +        const KEEP_SIZE = bitcast!(c::FALLOC_FL_KEEP_SIZE); +        /// `FALLOC_FL_PUNCH_HOLE` +        #[cfg(not(any( +            bsd, +            target_os = "aix", +            target_os = "haiku", +            target_os = "hurd", +            target_os = "wasi", +        )))] +        const PUNCH_HOLE = bitcast!(c::FALLOC_FL_PUNCH_HOLE); +        /// `FALLOC_FL_NO_HIDE_STALE` +        #[cfg(not(any( +            bsd, +            target_os = "aix", +            target_os = "emscripten", +            target_os = "fuchsia", +            target_os = "haiku", +            target_os = "hurd", +            target_os = "l4re", +            target_os = "linux", +            target_os = "wasi", +        )))] +        const NO_HIDE_STALE = bitcast!(c::FALLOC_FL_NO_HIDE_STALE); +        /// `FALLOC_FL_COLLAPSE_RANGE` +        #[cfg(not(any( +            bsd, +            target_os = "aix", +            target_os = "haiku", +            target_os = "hurd", +            target_os = "emscripten", +            target_os = "wasi", +        )))] +        const COLLAPSE_RANGE = bitcast!(c::FALLOC_FL_COLLAPSE_RANGE); +        /// `FALLOC_FL_ZERO_RANGE` +        #[cfg(not(any( +            bsd, +            target_os = "aix", +            target_os = "haiku", +            target_os = "hurd", +            target_os = "emscripten", +            target_os = "wasi", +        )))] +        const ZERO_RANGE = bitcast!(c::FALLOC_FL_ZERO_RANGE); +        /// `FALLOC_FL_INSERT_RANGE` +        #[cfg(not(any( +            bsd, +            target_os = "aix", +            target_os = "haiku", +            target_os = "hurd", +            target_os = "emscripten", +            target_os = "wasi", +        )))] +        const INSERT_RANGE = bitcast!(c::FALLOC_FL_INSERT_RANGE); +        /// `FALLOC_FL_UNSHARE_RANGE` +        #[cfg(not(any( +            bsd, +            target_os = "aix", +            target_os = "haiku", +            target_os = "hurd", +            target_os = "emscripten", +            target_os = "wasi", +        )))] +        const UNSHARE_RANGE = bitcast!(c::FALLOC_FL_UNSHARE_RANGE); + +        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> +        const _ = !0; +    } +} + +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +bitflags! { +    /// `ST_*` constants for use with [`StatVfs`]. +    #[repr(transparent)] +    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +    pub struct StatVfsMountFlags: u64 { +        /// `ST_MANDLOCK` +        #[cfg(any(linux_kernel, target_os = "emscripten", target_os = "fuchsia"))] +        const MANDLOCK = c::ST_MANDLOCK as u64; + +        /// `ST_NOATIME` +        #[cfg(any(linux_kernel, target_os = "emscripten", target_os = "fuchsia"))] +        const NOATIME = c::ST_NOATIME as u64; + +        /// `ST_NODEV` +        #[cfg(any( +            linux_kernel, +            target_os = "aix", +            target_os = "emscripten", +            target_os = "fuchsia" +        ))] +        const NODEV = c::ST_NODEV as u64; + +        /// `ST_NODIRATIME` +        #[cfg(any(linux_kernel, target_os = "emscripten", target_os = "fuchsia"))] +        const NODIRATIME = c::ST_NODIRATIME as u64; + +        /// `ST_NOEXEC` +        #[cfg(any(linux_kernel, target_os = "emscripten", target_os = "fuchsia"))] +        const NOEXEC = c::ST_NOEXEC as u64; + +        /// `ST_NOSUID` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const NOSUID = c::ST_NOSUID as u64; + +        /// `ST_RDONLY` +        #[cfg(not(any(target_os = "espidf", target_os = "vita")))] +        const RDONLY = c::ST_RDONLY as u64; + +        /// `ST_RELATIME` +        #[cfg(any(target_os = "android", all(target_os = "linux", target_env = "gnu")))] +        const RELATIME = c::ST_RELATIME as u64; + +        /// `ST_SYNCHRONOUS` +        #[cfg(any(linux_kernel, target_os = "emscripten", target_os = "fuchsia"))] +        const SYNCHRONOUS = c::ST_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 +#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum FlockOperation { +    /// `LOCK_SH` +    LockShared = bitcast!(c::LOCK_SH), +    /// `LOCK_EX` +    LockExclusive = bitcast!(c::LOCK_EX), +    /// `LOCK_UN` +    Unlock = bitcast!(c::LOCK_UN), +    /// `LOCK_SH | LOCK_NB` +    NonBlockingLockShared = bitcast!(c::LOCK_SH | c::LOCK_NB), +    /// `LOCK_EX | LOCK_NB` +    NonBlockingLockExclusive = bitcast!(c::LOCK_EX | c::LOCK_NB), +    /// `LOCK_UN | LOCK_NB` +    NonBlockingUnlock = bitcast!(c::LOCK_UN | c::LOCK_NB), +} + +/// `struct stat` for use with [`statat`] and [`fstat`]. +/// +/// [`statat`]: crate::fs::statat +/// [`fstat`]: crate::fs::fstat +#[cfg(not(any(linux_like, target_os = "hurd")))] +pub type Stat = c::stat; + +/// `struct stat` for use with [`statat`] and [`fstat`]. +/// +/// [`statat`]: crate::fs::statat +/// [`fstat`]: crate::fs::fstat +#[cfg(any( +    all(linux_kernel, target_pointer_width = "64"), +    target_os = "hurd", +    target_os = "emscripten", +    target_os = "l4re", +))] +pub type Stat = c::stat64; + +/// `struct stat` for use with [`statat`] and [`fstat`]. +/// +/// [`statat`]: crate::fs::statat +/// [`fstat`]: crate::fs::fstat +// On 32-bit, 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(all(linux_kernel, target_pointer_width = "32"))] +#[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 statfs` for use with [`statfs`] and [`fstatfs`]. +/// +/// [`statfs`]: crate::fs::statfs +/// [`fstatfs`]: crate::fs::fstatfs +#[cfg(not(any( +    linux_like, +    solarish, +    target_os = "espidf", +    target_os = "haiku", +    target_os = "netbsd", +    target_os = "nto", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi", +)))] +#[allow(clippy::module_name_repetitions)] +pub type StatFs = c::statfs; + +/// `struct statfs` for use with [`statfs`] and [`fstatfs`]. +/// +/// [`statfs`]: crate::fs::statfs +/// [`fstatfs`]: crate::fs::fstatfs +#[cfg(linux_like)] +pub type StatFs = c::statfs64; + +/// `struct statvfs` for use with [`statvfs`] and [`fstatvfs`]. +/// +/// [`statvfs`]: crate::fs::statvfs +/// [`fstatvfs`]: crate::fs::fstatvfs +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[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 +#[cfg(all(target_os = "linux", target_env = "gnu"))] +// Use the glibc `struct statx`. +pub type Statx = c::statx; + +/// `struct statx_timestamp` for use with [`Statx`]. +#[cfg(all(target_os = "linux", target_env = "gnu"))] +// Use the glibc `struct statx_timestamp`. +pub type StatxTimestamp = c::statx; + +/// `struct statx` for use with [`statx`]. +/// +/// [`statx`]: crate::fs::statx +// Non-glibc ABIs don't currently declare a `struct statx`, so we declare it +// ourselves. +#[cfg(any( +    target_os = "android", +    all(target_os = "linux", not(target_env = "gnu")), +))] +#[repr(C)] +#[allow(missing_docs)] +pub struct Statx { +    pub stx_mask: u32, +    pub stx_blksize: u32, +    pub stx_attributes: u64, +    pub stx_nlink: u32, +    pub stx_uid: u32, +    pub stx_gid: u32, +    pub stx_mode: u16, +    __statx_pad1: [u16; 1], +    pub stx_ino: u64, +    pub stx_size: u64, +    pub stx_blocks: u64, +    pub stx_attributes_mask: u64, +    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, +    __statx_pad2: u64, +    __statx_pad3: [u64; 12], +} + +/// `struct statx_timestamp` for use with [`Statx`]. +// Non-glibc ABIs don't currently declare a `struct statx_timestamp`, so we +// declare it ourselves. +#[cfg(any( +    target_os = "android", +    all(target_os = "linux", not(target_env = "gnu")), +))] +#[repr(C)] +#[allow(missing_docs)] +pub struct StatxTimestamp { +    pub tv_sec: i64, +    pub tv_nsec: u32, +    pub __statx_timestamp_pad1: [i32; 1], +} + +/// `mode_t` +#[cfg(not(all(target_os = "android", target_pointer_width = "32")))] +pub type RawMode = c::mode_t; + +/// `mode_t` +#[cfg(all(target_os = "android", target_pointer_width = "32"))] +pub type RawMode = c::c_uint; + +/// `dev_t` +#[cfg(not(all(target_os = "android", target_pointer_width = "32")))] +pub type Dev = c::dev_t; + +/// `dev_t` +#[cfg(all(target_os = "android", target_pointer_width = "32"))] +pub type Dev = c::c_ulonglong; + +/// `__fsword_t` +#[cfg(all( +    target_os = "linux", +    not(target_env = "musl"), +    not(target_arch = "s390x"), +))] +pub type FsWord = c::__fsword_t; + +/// `__fsword_t` +#[cfg(all( +    any(target_os = "android", all(target_os = "linux", target_env = "musl")), +    target_pointer_width = "32", +))] +pub type FsWord = u32; + +/// `__fsword_t` +#[cfg(all( +    any(target_os = "android", all(target_os = "linux", target_env = "musl")), +    not(target_arch = "s390x"), +    target_pointer_width = "64", +))] +pub type FsWord = u64; + +/// `__fsword_t` +// s390x uses `u32` for `statfs` entries on glibc, even though `__fsword_t` is +// `u64`. +#[cfg(all(target_os = "linux", target_arch = "s390x", target_env = "gnu"))] +pub type FsWord = u32; + +/// `__fsword_t` +// s390x uses `u64` for `statfs` entries on musl. +#[cfg(all(target_os = "linux", target_arch = "s390x", target_env = "musl"))] +pub type FsWord = u64; + +/// `copyfile_state_t`—State for use with [`fcopyfile`]. +/// +/// [`fcopyfile`]: crate::fs::fcopyfile +#[cfg(apple)] +#[allow(non_camel_case_types)] +#[repr(transparent)] +#[derive(Copy, Clone)] +pub struct copyfile_state_t(pub(crate) *mut c::c_void);  | 
