diff options
Diffstat (limited to 'vendor/rustix-0.37.6/src/backend/libc/fs')
-rw-r--r-- | vendor/rustix-0.37.6/src/backend/libc/fs/dir.rs | 405 | ||||
-rw-r--r-- | vendor/rustix-0.37.6/src/backend/libc/fs/inotify.rs | 121 | ||||
-rw-r--r-- | vendor/rustix-0.37.6/src/backend/libc/fs/makedev.rs | 120 | ||||
-rw-r--r-- | vendor/rustix-0.37.6/src/backend/libc/fs/mod.rs | 9 | ||||
-rw-r--r-- | vendor/rustix-0.37.6/src/backend/libc/fs/syscalls.rs | 1810 | ||||
-rw-r--r-- | vendor/rustix-0.37.6/src/backend/libc/fs/types.rs | 1150 |
6 files changed, 3615 insertions, 0 deletions
diff --git a/vendor/rustix-0.37.6/src/backend/libc/fs/dir.rs b/vendor/rustix-0.37.6/src/backend/libc/fs/dir.rs new file mode 100644 index 000000000..d1c901323 --- /dev/null +++ b/vendor/rustix-0.37.6/src/backend/libc/fs/dir.rs @@ -0,0 +1,405 @@ +use super::super::c; +use super::super::conv::owned_fd; +#[cfg(not(any(solarish, target_os = "haiku")))] +use super::types::FileType; +use crate::fd::{AsFd, BorrowedFd}; +use crate::ffi::CStr; +#[cfg(target_os = "wasi")] +use crate::ffi::CString; +use crate::fs::{fcntl_getfl, fstat, openat, Mode, OFlags, Stat}; +#[cfg(not(any( + solarish, + target_os = "haiku", + target_os = "netbsd", + target_os = "redox", + target_os = "wasi", +)))] +use crate::fs::{fstatfs, StatFs}; +#[cfg(not(any(solarish, target_os = "haiku", target_os = "redox", target_os = "wasi")))] +use crate::fs::{fstatvfs, StatVfs}; +use crate::io; +#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] +use crate::process::fchdir; +#[cfg(target_os = "wasi")] +use alloc::borrow::ToOwned; +#[cfg(not(any(linux_like, target_os = "openbsd")))] +use c::dirent as libc_dirent; +#[cfg(not(linux_like))] +use c::readdir as libc_readdir; +#[cfg(linux_like)] +use c::{dirent64 as libc_dirent, readdir64 as libc_readdir}; +use core::fmt; +use core::mem::zeroed; +use core::ptr::NonNull; +use libc_errno::{errno, set_errno, Errno}; + +/// `DIR*` +#[repr(transparent)] +pub struct Dir(NonNull<c::DIR>); + +impl Dir { + /// Construct a `Dir` that reads entries from the given directory + /// file descriptor. + #[inline] + pub fn read_from<Fd: AsFd>(fd: Fd) -> io::Result<Self> { + Self::_read_from(fd.as_fd()) + } + + #[inline] + fn _read_from(fd: BorrowedFd<'_>) -> io::Result<Self> { + // 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 = openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty())?; + + 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)) + } else { + let err = io::Errno::last_os_error(); + let _ = c::close(raw); + Err(err) + } + } + } + + /// `rewinddir(self)` + #[inline] + pub fn rewind(&mut self) { + unsafe { c::rewinddir(self.0.as_ptr()) } + } + + /// `readdir(self)`, where `None` means the end of the directory. + pub fn read(&mut self) -> Option<io::Result<DirEntry>> { + set_errno(Errno(0)); + let dirent_ptr = unsafe { libc_readdir(self.0.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. + Some(Err(io::Errno(curr_errno))) + } + } else { + // We successfully read an entry. + unsafe { + // 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_ptr); + + let result = DirEntry { + dirent: read_dirent(&*dirent_ptr.cast()), + + #[cfg(target_os = "wasi")] + name: CStr::from_ptr((*dirent_ptr).d_name.as_ptr()).to_owned(), + }; + + Some(Ok(result)) + } + } + } + + /// `fstat(self)` + #[inline] + pub fn stat(&self) -> io::Result<Stat> { + fstat(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) }) + } + + /// `fstatfs(self)` + #[cfg(not(any( + solarish, + target_os = "haiku", + target_os = "netbsd", + target_os = "redox", + target_os = "wasi", + )))] + #[inline] + pub fn statfs(&self) -> io::Result<StatFs> { + fstatfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) }) + } + + /// `fstatvfs(self)` + #[cfg(not(any(solarish, target_os = "haiku", target_os = "redox", target_os = "wasi")))] + #[inline] + pub fn statvfs(&self) -> io::Result<StatVfs> { + fstatvfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) }) + } + + /// `fchdir(self)` + #[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] + #[inline] + pub fn chdir(&self) -> io::Result<()> { + fchdir(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) }) + } +} + +// A `dirent` pointer returned from `readdir` may not point to a full `dirent` +// struct, as the name is NUL-terminated and memory may not be allocated for +// the full extent of the struct. Copy the fields one at a time. +unsafe fn read_dirent(input: &libc_dirent) -> libc_dirent { + #[cfg(not(any(solarish, target_os = "aix", target_os = "haiku")))] + let d_type = input.d_type; + + #[cfg(not(any( + apple, + freebsdlike, + target_os = "aix", + target_os = "haiku", + target_os = "netbsd", + target_os = "wasi", + )))] + let d_off = input.d_off; + + #[cfg(target_os = "aix")] + let d_offset = input.d_offset; + + #[cfg(not(any(freebsdlike, netbsdlike)))] + let d_ino = input.d_ino; + + #[cfg(any(freebsdlike, netbsdlike))] + let d_fileno = input.d_fileno; + + #[cfg(not(any(target_os = "dragonfly", target_os = "wasi")))] + let d_reclen = input.d_reclen; + + #[cfg(any(bsd, target_os = "aix"))] + let d_namlen = input.d_namlen; + + #[cfg(apple)] + let d_seekoff = input.d_seekoff; + + #[cfg(target_os = "haiku")] + let d_dev = input.d_dev; + #[cfg(target_os = "haiku")] + let d_pdev = input.d_pdev; + #[cfg(target_os = "haiku")] + let d_pino = input.d_pino; + + // Construct the input. Rust will give us an error if any OS has a input + // with a field that we missed here. And we can avoid blindly copying the + // whole `d_name` field, which may not be entirely allocated. + #[cfg_attr(target_os = "wasi", allow(unused_mut))] + #[cfg(not(freebsdlike))] + let mut dirent = libc_dirent { + #[cfg(not(any(solarish, target_os = "aix", target_os = "haiku")))] + d_type, + #[cfg(not(any( + apple, + target_os = "aix", + target_os = "freebsd", // Until FreeBSD 12 + target_os = "haiku", + target_os = "netbsd", + target_os = "wasi", + )))] + d_off, + #[cfg(target_os = "aix")] + d_offset, + #[cfg(not(any(netbsdlike, target_os = "freebsd")))] + d_ino, + #[cfg(any(netbsdlike, target_os = "freebsd"))] + d_fileno, + #[cfg(not(target_os = "wasi"))] + d_reclen, + #[cfg(any(apple, netbsdlike, target_os = "aix", target_os = "freebsd"))] + d_namlen, + #[cfg(apple)] + d_seekoff, + // The `d_name` field is NUL-terminated, and we need to be careful not + // to read bytes past the NUL, even though they're within the nominal + // extent of the `struct dirent`, because they may not be allocated. So + // don't read it from `dirent_ptr`. + // + // In theory this could use `MaybeUninit::uninit().assume_init()`, but + // that [invokes undefined behavior]. + // + // [invokes undefined behavior]: https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#initialization-invariant + d_name: zeroed(), + #[cfg(target_os = "openbsd")] + __d_padding: zeroed(), + #[cfg(target_os = "haiku")] + d_dev, + #[cfg(target_os = "haiku")] + d_pdev, + #[cfg(target_os = "haiku")] + d_pino, + }; + /* + pub d_ino: ino_t, + pub d_pino: i64, + pub d_reclen: ::c_ushort, + pub d_name: [::c_char; 1024], // Max length is _POSIX_PATH_MAX + */ + + // On dragonfly and FreeBSD 12, `dirent` has some non-public padding fields + // so we can't directly initialize it. + #[cfg(freebsdlike)] + let mut dirent = { + let mut dirent: libc_dirent = zeroed(); + dirent.d_fileno = d_fileno; + dirent.d_namlen = d_namlen; + dirent.d_type = d_type; + #[cfg(target_os = "freebsd")] + { + dirent.d_reclen = d_reclen; + } + dirent + }; + + // Copy from d_name, reading up to and including the first NUL. + #[cfg(not(target_os = "wasi"))] + { + let name_len = CStr::from_ptr(input.d_name.as_ptr()) + .to_bytes_with_nul() + .len(); + dirent.d_name[..name_len].copy_from_slice(&input.d_name[..name_len]); + } + + dirent +} + +/// `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.0.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 { + f.debug_struct("Dir") + .field("fd", unsafe { &c::dirfd(self.0.as_ptr()) }) + .finish() + } +} + +/// `struct dirent` +#[derive(Debug)] +pub struct DirEntry { + dirent: libc_dirent, + + #[cfg(target_os = "wasi")] + name: CString, +} + +impl DirEntry { + /// Returns the file name of this directory entry. + #[inline] + pub fn file_name(&self) -> &CStr { + #[cfg(not(target_os = "wasi"))] + unsafe { + CStr::from_ptr(self.dirent.d_name.as_ptr()) + } + + #[cfg(target_os = "wasi")] + &self.name + } + + /// Returns the type of this directory entry. + #[cfg(not(any(solarish, target_os = "aix", target_os = "haiku")))] + #[inline] + pub fn file_type(&self) -> FileType { + FileType::from_dirent_d_type(self.dirent.d_type) + } + + /// Return the inode number of this directory entry. + #[cfg(not(any(freebsdlike, netbsdlike)))] + #[inline] + pub fn ino(&self) -> u64 { + self.dirent.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.dirent.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; + use core::mem::{align_of, size_of}; + + // Check that the basic layouts match. + assert_eq!(size_of::<libc_dirent>(), size_of::<c::dirent>()); + assert_eq!(align_of::<libc_dirent>(), align_of::<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, + ) + } + ); +} diff --git a/vendor/rustix-0.37.6/src/backend/libc/fs/inotify.rs b/vendor/rustix-0.37.6/src/backend/libc/fs/inotify.rs new file mode 100644 index 000000000..8a42e0583 --- /dev/null +++ b/vendor/rustix-0.37.6/src/backend/libc/fs/inotify.rs @@ -0,0 +1,121 @@ +//! inotify support for working with inotifies + +use super::super::c; +use super::super::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 + pub struct CreateFlags: c::c_int { + /// `IN_CLOEXEC` + const CLOEXEC = c::IN_CLOEXEC; + /// `IN_NONBLOCK` + const NONBLOCK = c::IN_NONBLOCK; + } +} + +bitflags! { + /// `IN*` for use with [`inotify_add_watch`]. + /// + /// [`inotify_add_watch`]: crate::fs::inotify::inotify_add_watch + #[derive(Default)] + 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; + } +} + +/// `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(flags.bits())) } +} + +/// `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> { + let path = path.as_cow_c_str().unwrap(); + // 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 `inotify_add_watch` is 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-0.37.6/src/backend/libc/fs/makedev.rs b/vendor/rustix-0.37.6/src/backend/libc/fs/makedev.rs new file mode 100644 index 000000000..afe942a59 --- /dev/null +++ b/vendor/rustix-0.37.6/src/backend/libc/fs/makedev.rs @@ -0,0 +1,120 @@ +#[cfg(not(all(target_os = "android", target_pointer_width = "32")))] +use super::super::c; +use crate::fs::Dev; + +#[cfg(not(any( + apple, + target_os = "aix", + target_os = "android", + target_os = "emscripten", +)))] +#[inline] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { + 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, + netbsdlike, + target_os = "android", + target_os = "emscripten", +)))] +#[inline] +pub(crate) fn major(dev: Dev) -> u32 { + unsafe { c::major(dev) } +} + +#[cfg(all(target_os = "android", not(target_pointer_width = "32")))] +#[inline] +pub(crate) fn major(dev: Dev) -> u32 { + // Android's `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, + netbsdlike, + target_os = "android", + target_os = "emscripten", +)))] +#[inline] +pub(crate) fn minor(dev: Dev) -> u32 { + unsafe { c::minor(dev) } +} + +#[cfg(all(target_os = "android", not(target_pointer_width = "32")))] +#[inline] +pub(crate) fn minor(dev: Dev) -> u32 { + // Android's `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-0.37.6/src/backend/libc/fs/mod.rs b/vendor/rustix-0.37.6/src/backend/libc/fs/mod.rs new file mode 100644 index 000000000..54a48103c --- /dev/null +++ b/vendor/rustix-0.37.6/src/backend/libc/fs/mod.rs @@ -0,0 +1,9 @@ +#[cfg(not(target_os = "redox"))] +pub(crate) mod dir; +#[cfg(any(target_os = "android", target_os = "linux"))] +pub mod inotify; +#[cfg(not(any(solarish, target_os = "haiku", target_os = "redox", target_os = "wasi")))] +pub(crate) mod makedev; +#[cfg(not(windows))] +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix-0.37.6/src/backend/libc/fs/syscalls.rs b/vendor/rustix-0.37.6/src/backend/libc/fs/syscalls.rs new file mode 100644 index 000000000..77b49ee3e --- /dev/null +++ b/vendor/rustix-0.37.6/src/backend/libc/fs/syscalls.rs @@ -0,0 +1,1810 @@ +//! libc syscalls supporting `rustix::fs`. + +use super::super::c; +use super::super::conv::{ + borrowed_fd, c_str, ret, ret_c_int, ret_off_t, ret_owned_fd, ret_ssize_t, +}; +#[cfg(any(target_os = "android", target_os = "linux"))] +use super::super::conv::{syscall_ret, syscall_ret_owned_fd, syscall_ret_ssize_t}; +#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] +use super::super::offset::libc_fallocate; +#[cfg(not(any( + apple, + netbsdlike, + solarish, + target_os = "dragonfly", + target_os = "haiku", + target_os = "redox", +)))] +use super::super::offset::libc_posix_fadvise; +#[cfg(not(any( + apple, + netbsdlike, + solarish, + target_os = "aix", + target_os = "android", + target_os = "dragonfly", + target_os = "fuchsia", + target_os = "linux", + target_os = "redox", +)))] +use super::super::offset::libc_posix_fallocate; +use super::super::offset::{libc_fstat, libc_fstatat, libc_ftruncate, libc_lseek, libc_off_t}; +#[cfg(not(any( + solarish, + target_os = "haiku", + target_os = "netbsd", + target_os = "redox", + target_os = "wasi", +)))] +use super::super::offset::{libc_fstatfs, libc_statfs}; +#[cfg(not(any(solarish, target_os = "haiku", target_os = "redox", target_os = "wasi")))] +use super::super::offset::{libc_fstatvfs, libc_statvfs}; +#[cfg(all( + any(target_arch = "arm", target_arch = "mips", target_arch = "x86"), + target_env = "gnu", +))] +use super::super::time::types::LibcTimespec; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::ffi::CStr; +#[cfg(apple)] +use crate::ffi::CString; +#[cfg(not(solarish))] +use crate::fs::Access; +#[cfg(not(any( + apple, + netbsdlike, + solarish, + target_os = "dragonfly", + target_os = "haiku", + target_os = "redox", +)))] +use crate::fs::Advice; +#[cfg(not(any( + netbsdlike, + solarish, + target_os = "aix", + target_os = "dragonfly", + target_os = "redox", +)))] +use crate::fs::FallocateFlags; +#[cfg(not(target_os = "wasi"))] +use crate::fs::FlockOperation; +#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] +use crate::fs::MemfdFlags; +#[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", +))] +use crate::fs::SealFlags; +#[cfg(not(any( + solarish, + target_os = "haiku", + target_os = "netbsd", + target_os = "redox", + target_os = "wasi", +)))] +use crate::fs::StatFs; +#[cfg(any(target_os = "android", target_os = "linux"))] +use crate::fs::{cwd, RenameFlags, ResolveFlags, Statx, StatxFlags}; +#[cfg(not(any(apple, target_os = "redox", target_os = "wasi")))] +use crate::fs::{Dev, FileType}; +use crate::fs::{Mode, OFlags, Stat, Timestamps}; +#[cfg(not(any(solarish, target_os = "haiku", target_os = "redox", target_os = "wasi")))] +use crate::fs::{StatVfs, StatVfsMountFlags}; +use crate::io::{self, SeekFrom}; +#[cfg(not(target_os = "wasi"))] +use crate::process::{Gid, Uid}; +#[cfg(not(all( + any(target_arch = "arm", target_arch = "mips", target_arch = "x86"), + target_env = "gnu", +)))] +use crate::utils::as_ptr; +use core::convert::TryInto; +#[cfg(any(apple, target_os = "android", target_os = "linux"))] +use core::mem::size_of; +use core::mem::MaybeUninit; +#[cfg(any(target_os = "android", target_os = "linux"))] +use core::ptr::null; +#[cfg(any(apple, target_os = "android", target_os = "linux"))] +use core::ptr::null_mut; +#[cfg(apple)] +use { + super::super::conv::nonnegative_ret, + crate::fs::{copyfile_state_t, CloneFlags, CopyfileFlags}, +}; +#[cfg(not(target_os = "redox"))] +use {super::super::offset::libc_openat, crate::fs::AtFlags}; + +#[cfg(all( + any(target_arch = "arm", target_arch = "mips", target_arch = "x86"), + target_env = "gnu", +))] +weak!(fn __utimensat64(c::c_int, *const c::c_char, *const LibcTimespec, c::c_int) -> c::c_int); +#[cfg(all( + any(target_arch = "arm", target_arch = "mips", target_arch = "x86"), + target_env = "gnu", +))] +weak!(fn __futimens64(c::c_int, *const LibcTimespec) -> c::c_int); + +/// 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"))] +fn openat_via_syscall( + dirfd: BorrowedFd<'_>, + path: &CStr, + oflags: OFlags, + mode: Mode, +) -> io::Result<OwnedFd> { + unsafe { + let dirfd = borrowed_fd(dirfd); + let path = c_str(path); + let oflags = oflags.bits(); + let mode = c::c_uint::from(mode.bits()); + ret_owned_fd(c::syscall( + c::SYS_openat, + c::c_long::from(dirfd), + path, + c::c_long::from(oflags), + mode as c::c_long, + ) as c::c_int) + } +} + +#[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"))] + if oflags.contains(OFlags::TMPFILE) && crate::backend::if_glibc_is_less_than_2_25() { + return openat_via_syscall(dirfd, path, oflags, mode); + } + unsafe { + // Pass `mode` as a `c_uint` even if `mode_t` is narrower, since + // `libc_openat` is declared as a variadic function and narrower + // arguments are promoted. + ret_owned_fd(libc_openat( + borrowed_fd(dirfd), + c_str(path), + oflags.bits(), + c::c_uint::from(mode.bits()), + )) + } +} + +#[cfg(not(any( + solarish, + target_os = "haiku", + target_os = "netbsd", + target_os = "redox", + target_os = "wasi", +)))] +#[inline] +pub(crate) fn statfs(filename: &CStr) -> io::Result<StatFs> { + unsafe { + let mut result = MaybeUninit::<StatFs>::uninit(); + ret(libc_statfs(c_str(filename), result.as_mut_ptr()))?; + Ok(result.assume_init()) + } +} + +#[cfg(not(any(solarish, target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[inline] +pub(crate) fn statvfs(filename: &CStr) -> io::Result<StatVfs> { + unsafe { + let mut result = MaybeUninit::<libc_statvfs>::uninit(); + ret(libc_statvfs(c_str(filename), result.as_mut_ptr()))?; + Ok(libc_statvfs_to_statvfs(result.assume_init())) + } +} + +#[cfg(not(target_os = "redox"))] +#[inline] +pub(crate) fn readlinkat(dirfd: BorrowedFd<'_>, path: &CStr, buf: &mut [u8]) -> io::Result<usize> { + unsafe { + ret_ssize_t(c::readlinkat( + borrowed_fd(dirfd), + c_str(path), + buf.as_mut_ptr().cast::<c::c_char>(), + buf.len(), + )) + .map(|nread| nread as usize) + } +} + +#[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(any(target_os = "android", target_os = "linux"))] +pub(crate) fn getdents_uninit( + fd: BorrowedFd<'_>, + buf: &mut [MaybeUninit<u8>], +) -> io::Result<usize> { + unsafe { + syscall_ret_ssize_t(c::syscall( + c::SYS_getdents64, + fd, + buf.as_mut_ptr().cast::<c::c_char>(), + buf.len(), + )) + } + .map(|nread| nread as usize) +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn linkat( + old_dirfd: BorrowedFd<'_>, + old_path: &CStr, + new_dirfd: BorrowedFd<'_>, + new_path: &CStr, + flags: AtFlags, +) -> io::Result<()> { + unsafe { + ret(c::linkat( + borrowed_fd(old_dirfd), + c_str(old_path), + borrowed_fd(new_dirfd), + c_str(new_path), + flags.bits(), + )) + } +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn unlinkat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<()> { + unsafe { ret(c::unlinkat(borrowed_fd(dirfd), c_str(path), flags.bits())) } +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn renameat( + old_dirfd: BorrowedFd<'_>, + old_path: &CStr, + new_dirfd: BorrowedFd<'_>, + new_path: &CStr, +) -> io::Result<()> { + 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<()> { + // `getrandom` 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(), + )) + } +} + +/// At present, `libc` only has `renameat2` defined for glibc. On other +/// ABIs, `RenameFlags` has no flags defined, and we use plain `renameat`. +#[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<()> { + assert!(flags.is_empty()); + renameat(old_dirfd, old_path, new_dirfd, 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), + )) + } +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn statat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat> { + // 32-bit and mips64 Linux: `struct stat64` is not y2038 compatible; use + // `statx`. + #[cfg(all( + any(target_os = "android", target_os = "linux"), + any(target_pointer_width = "32", target_arch = "mips64"), + ))] + { + match 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( + any(target_os = "android", target_os = "linux"), + any(target_pointer_width = "32", target_arch = "mips64"), + )))] + unsafe { + let mut stat = MaybeUninit::<Stat>::uninit(); + ret(libc_fstatat( + borrowed_fd(dirfd), + c_str(path), + stat.as_mut_ptr(), + flags.bits(), + ))?; + Ok(stat.assume_init()) + } +} + +#[cfg(all( + any(target_os = "android", target_os = "linux"), + any(target_pointer_width = "32", target_arch = "mips64"), +))] +fn statat_old(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat> { + unsafe { + let mut result = MaybeUninit::<c::stat64>::uninit(); + ret(libc_fstatat( + borrowed_fd(dirfd), + c_str(path), + result.as_mut_ptr(), + flags.bits(), + ))?; + stat64_to_stat(result.assume_init()) + } +} + +#[cfg(not(any(solarish, target_os = "emscripten", target_os = "redox")))] +pub(crate) fn accessat( + dirfd: BorrowedFd<'_>, + path: &CStr, + access: Access, + flags: AtFlags, +) -> io::Result<()> { + unsafe { + ret(c::faccessat( + borrowed_fd(dirfd), + c_str(path), + access.bits(), + flags.bits(), + )) + } +} + +#[cfg(target_os = "emscripten")] +pub(crate) fn accessat( + _dirfd: BorrowedFd<'_>, + _path: &CStr, + _access: Access, + _flags: AtFlags, +) -> io::Result<()> { + Ok(()) +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn utimensat( + dirfd: BorrowedFd<'_>, + path: &CStr, + times: &Timestamps, + flags: AtFlags, +) -> io::Result<()> { + // 32-bit gnu version: libc has `utimensat` but it is not y2038 safe by + // default. + #[cfg(all( + any(target_arch = "arm", target_arch = "mips", target_arch = "x86"), + target_env = "gnu", + ))] + unsafe { + if let Some(libc_utimensat) = __utimensat64.get() { + let libc_times: [LibcTimespec; 2] = [ + times.last_access.clone().into(), + times.last_modification.clone().into(), + ]; + + ret(libc_utimensat( + borrowed_fd(dirfd), + c_str(path), + libc_times.as_ptr(), + flags.bits(), + )) + } else { + 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, + all( + any(target_arch = "arm", target_arch = "mips", target_arch = "x86"), + target_env = "gnu", + ) + )))] + unsafe { + // Assert that `Timestamps` has the expected layout. + let _ = core::mem::transmute::<Timestamps, [c::timespec; 2]>(times.clone()); + + ret(c::utimensat( + borrowed_fd(dirfd), + c_str(path), + as_ptr(times).cast(), + flags.bits(), + )) + } + + // `utimensat` was introduced in macOS 10.13. + #[cfg(apple)] + unsafe { + // 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() { + // Assert that `Timestamps` has the expected layout. + let _ = core::mem::transmute::<Timestamps, [c::timespec; 2]>(times.clone()); + + return ret(have_utimensat( + borrowed_fd(dirfd), + c_str(path), + as_ptr(times).cast(), + flags.bits(), + )); + } + + // `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(all( + any(target_arch = "arm", target_arch = "mips", target_arch = "x86"), + target_env = "gnu", +))] +unsafe 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, + }, + c::timespec { + tv_sec: times + .last_modification + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: times.last_modification.tv_nsec, + }, + ]; + ret(c::utimensat( + borrowed_fd(dirfd), + c_str(path), + old_times.as_ptr(), + flags.bits(), + )) +} + +#[cfg(not(any( + target_os = "android", + target_os = "linux", + 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, + flags.bits(), + )) + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +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. + if flags == AtFlags::SYMLINK_NOFOLLOW { + return Err(io::Errno::OPNOTSUPP); + } + if !flags.is_empty() { + return Err(io::Errno::INVAL); + } + unsafe { + // Pass `mode` as a `c_uint` even if `mode_t` is narrower, since + // `libc_openat` is declared as a variadic function and narrower + // arguments are promoted. + syscall_ret(c::syscall( + c::SYS_fchmodat, + borrowed_fd(dirfd), + c_str(path), + c::c_uint::from(mode.bits()), + )) + } +} + +#[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), flags.bits())) } +} + +#[cfg(not(any(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::process::translate_fchown_args(owner, group); + ret(c::fchownat( + borrowed_fd(dirfd), + c_str(path), + ow, + gr, + flags.bits(), + )) + } +} + +#[cfg(not(any(apple, target_os = "redox", 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(any(target_os = "android", target_os = "linux"))] +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> { + assert_eq!(size_of::<c::loff_t>(), size_of::<u64>()); + + 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 { + syscall_ret_ssize_t(c::syscall( + c::SYS_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 as usize) +} + +#[cfg(not(any( + apple, + netbsdlike, + solarish, + target_os = "dragonfly", + target_os = "haiku", + target_os = "redox", +)))] +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 { libc_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> { + unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GETFL)).map(OFlags::from_bits_truncate) } +} + +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( + target_os = "android", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", +))] +pub(crate) fn fcntl_get_seals(fd: BorrowedFd<'_>) -> io::Result<SealFlags> { + unsafe { + ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GET_SEALS)) + .map(|flags| SealFlags::from_bits_unchecked(flags)) + } +} + +#[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", +))] +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 = "fuchsia", + target_os = "redox", + 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): (c::c_int, libc_off_t) = 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(freebsdlike, target_os = "linux", target_os = "solaris"))] + SeekFrom::Data(offset) => (c::SEEK_DATA, offset), + #[cfg(any(freebsdlike, target_os = "linux", target_os = "solaris"))] + SeekFrom::Hole(offset) => (c::SEEK_HOLE, offset), + }; + let offset = unsafe { ret_off_t(libc_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(libc_lseek(borrowed_fd(fd), 0, c::SEEK_CUR))? }; + Ok(offset as u64) +} + +#[cfg(not(any(target_os = "android", target_os = "linux", target_os = "wasi")))] +pub(crate) fn fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()> { + unsafe { ret(c::fchmod(borrowed_fd(fd), mode.bits())) } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +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`. + unsafe { + syscall_ret(c::syscall( + c::SYS_fchmod, + borrowed_fd(fd), + c::c_uint::from(mode.bits()), + )) + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +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`. + unsafe { + let (ow, gr) = crate::process::translate_fchown_args(owner, group); + syscall_ret(c::syscall(c::SYS_fchown, borrowed_fd(fd), ow, gr)) + } +} + +#[cfg(not(any(target_os = "android", target_os = "linux", target_os = "wasi")))] +pub(crate) fn fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> { + unsafe { + let (ow, gr) = crate::process::translate_fchown_args(owner, group); + ret(c::fchown(borrowed_fd(fd), ow, gr)) + } +} + +#[cfg(not(any(target_os = "solaris", 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(any(target_os = "android", target_os = "linux"))] +pub(crate) fn syncfs(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { ret(c::syncfs(borrowed_fd(fd))) } +} + +#[cfg(not(any(solarish, target_os = "redox", target_os = "wasi")))] +pub(crate) fn sync() { + // TODO: Remove this when upstream libc adds `sync`. + #[cfg(target_os = "android")] + unsafe { + syscall_ret(c::syscall(c::SYS_sync)).ok(); + } + + #[cfg(not(target_os = "android"))] + 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`. + #[cfg(all( + any(target_os = "android", target_os = "linux"), + any(target_pointer_width = "32", target_arch = "mips64"), + ))] + { + match 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( + any(target_os = "android", target_os = "linux"), + any(target_pointer_width = "32", target_arch = "mips64"), + )))] + unsafe { + let mut stat = MaybeUninit::<Stat>::uninit(); + ret(libc_fstat(borrowed_fd(fd), stat.as_mut_ptr()))?; + Ok(stat.assume_init()) + } +} + +#[cfg(all( + any(target_os = "android", target_os = "linux"), + any(target_pointer_width = "32", target_arch = "mips64"), +))] +fn fstat_old(fd: BorrowedFd<'_>) -> io::Result<Stat> { + unsafe { + let mut result = MaybeUninit::<c::stat64>::uninit(); + ret(libc_fstat(borrowed_fd(fd), result.as_mut_ptr()))?; + stat64_to_stat(result.assume_init()) + } +} + +#[cfg(not(any( + solarish, + target_os = "haiku", + target_os = "netbsd", + target_os = "redox", + target_os = "wasi", +)))] +pub(crate) fn fstatfs(fd: BorrowedFd<'_>) -> io::Result<StatFs> { + let mut statfs = MaybeUninit::<StatFs>::uninit(); + unsafe { + ret(libc_fstatfs(borrowed_fd(fd), statfs.as_mut_ptr()))?; + Ok(statfs.assume_init()) + } +} + +#[cfg(not(any(solarish, target_os = "haiku", target_os = "redox", target_os = "wasi")))] +pub(crate) fn fstatvfs(fd: BorrowedFd<'_>) -> io::Result<StatVfs> { + let mut statvfs = MaybeUninit::<libc_statvfs>::uninit(); + unsafe { + ret(libc_fstatvfs(borrowed_fd(fd), statvfs.as_mut_ptr()))?; + Ok(libc_statvfs_to_statvfs(statvfs.assume_init())) + } +} + +#[cfg(not(any(solarish, target_os = "haiku", target_os = "redox", target_os = "wasi")))] +fn libc_statvfs_to_statvfs(from: libc_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, + f_fsid: from.f_fsid as u64, + f_flag: unsafe { StatVfsMountFlags::from_bits_unchecked(from.f_flag as u64) }, + f_namemax: from.f_namemax as u64, + } +} + +pub(crate) fn futimens(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()> { + // 32-bit gnu version: libc has `futimens` but it is not y2038 safe by default. + #[cfg(all( + any(target_arch = "arm", target_arch = "mips", target_arch = "x86"), + target_env = "gnu", + ))] + unsafe { + if let Some(libc_futimens) = __futimens64.get() { + let libc_times: [LibcTimespec; 2] = [ + times.last_access.clone().into(), + times.last_modification.clone().into(), + ]; + + ret(libc_futimens(borrowed_fd(fd), libc_times.as_ptr())) + } else { + 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, + all( + any(target_arch = "arm", target_arch = "mips", target_arch = "x86"), + target_env = "gnu", + ) + )))] + unsafe { + // Assert that `Timestamps` has the expected layout. + let _ = core::mem::transmute::<Timestamps, [c::timespec; 2]>(times.clone()); + + ret(c::futimens(borrowed_fd(fd), as_ptr(times).cast())) + } + + // `futimens` was introduced in macOS 10.13. + #[cfg(apple)] + unsafe { + // 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() { + // Assert that `Timestamps` has the expected layout. + let _ = core::mem::transmute::<Timestamps, [c::timespec; 2]>(times.clone()); + + 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(all( + any(target_arch = "arm", target_arch = "mips", target_arch = "x86"), + target_env = "gnu", +))] +unsafe 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, + }, + c::timespec { + tv_sec: times + .last_modification + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: times.last_modification.tv_nsec, + }, + ]; + + ret(c::futimens(borrowed_fd(fd), old_times.as_ptr())) +} + +#[cfg(not(any( + apple, + netbsdlike, + solarish, + target_os = "aix", + target_os = "dragonfly", + target_os = "redox", +)))] +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(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + unsafe { + ret(libc_fallocate(borrowed_fd(fd), mode.bits(), offset, len)) + } + + #[cfg(not(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))] + { + assert!(mode.is_empty()); + let err = unsafe { libc_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 = "haiku", + target_os = "redox", +)))] +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(libc_ftruncate(borrowed_fd(fd), length)) } +} + +#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] +pub(crate) fn memfd_create(path: &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(any(target_os = "android", target_os = "linux"))] + 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(path), flags.bits())) } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub(crate) fn openat2( + dirfd: BorrowedFd<'_>, + path: &CStr, + oflags: OFlags, + mode: Mode, + resolve: ResolveFlags, +) -> io::Result<OwnedFd> { + let oflags: i32 = oflags.bits(); + let open_how = OpenHow { + oflag: u64::from(oflags as u32), + mode: u64::from(mode.bits()), + resolve: resolve.bits(), + }; + + unsafe { + syscall_ret_owned_fd(c::syscall( + SYS_OPENAT2, + borrowed_fd(dirfd), + c_str(path), + &open_how, + SIZEOF_OPEN_HOW, + )) + } +} +#[cfg(all( + target_pointer_width = "32", + any(target_os = "android", target_os = "linux"), +))] +const SYS_OPENAT2: i32 = 437; +#[cfg(all( + target_pointer_width = "64", + any(target_os = "android", target_os = "linux"), +))] +const SYS_OPENAT2: i64 = 437; + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[repr(C)] +#[derive(Debug)] +struct OpenHow { + oflag: u64, + mode: u64, + resolve: u64, +} +#[cfg(any(target_os = "android", target_os = "linux"))] +const SIZEOF_OPEN_HOW: usize = size_of::<OpenHow>(); + +#[cfg(target_os = "linux")] +pub(crate) fn sendfile( + out_fd: BorrowedFd<'_>, + in_fd: BorrowedFd<'_>, + offset: Option<&mut u64>, + count: usize, +) -> io::Result<usize> { + unsafe { + let nsent = ret_ssize_t(c::sendfile64( + borrowed_fd(out_fd), + borrowed_fd(in_fd), + offset.map_or(null_mut(), crate::utils::as_mut_ptr).cast(), + count, + ))?; + Ok(nsent as usize) + } +} + +/// Convert from a Linux `statx` value to rustix's `Stat`. +#[cfg(all( + any(target_os = "android", target_os = "linux"), + 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( + any(target_os = "android", target_os = "linux"), + target_arch = "mips64", +))] +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( + any(target_os = "android", target_os = "linux"), + 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( + any(target_os = "android", target_os = "linux"), + target_arch = "mips64", +))] +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(any(target_os = "android", target_os = "linux"))] +#[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(any(target_os = "android", target_os = "linux"))] +#[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 = libc::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), + flags.bits(), + mask.bits(), + statx_buf.as_mut_ptr(), + ))?; + Ok(statx_buf.assume_init()) + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[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, + flags.bits(), + )) +} + +#[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(apple)] +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 = alloc::vec![0; c::PATH_MAX as usize]; + + // From the [macOS `fcntl` man page]: + // `F_GETPATH` - Get the path of the file descriptor `Fildes`. The argument + // must be a buffer of size `MAXPATHLEN` or greater. + // + // [macOS `fcntl` man 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); + + // TODO: On Rust 1.56, we can use `shrink_to` here. + //buf.shrink_to(l + 1); + 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` man 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` man 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)) } +} + +/// 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(target_os = "android", target_os = "linux"))] +pub(crate) fn mount( + source: Option<&CStr>, + target: &CStr, + file_system_type: Option<&CStr>, + flags: super::types::MountFlagsArg, + data: Option<&CStr>, +) -> io::Result<()> { + unsafe { + ret(c::mount( + source.map_or_else(null, CStr::as_ptr), + target.as_ptr(), + file_system_type.map_or_else(null, CStr::as_ptr), + flags.0, + data.map_or_else(null, CStr::as_ptr).cast(), + )) + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub(crate) fn unmount(target: &CStr, flags: super::types::UnmountFlags) -> io::Result<()> { + unsafe { ret(c::umount2(target.as_ptr(), flags.bits())) } +} diff --git a/vendor/rustix-0.37.6/src/backend/libc/fs/types.rs b/vendor/rustix-0.37.6/src/backend/libc/fs/types.rs new file mode 100644 index 000000000..f635f2cca --- /dev/null +++ b/vendor/rustix-0.37.6/src/backend/libc/fs/types.rs @@ -0,0 +1,1150 @@ +use super::super::c; +use bitflags::bitflags; + +bitflags! { + /// `*_OK` constants for use with [`accessat`]. + /// + /// [`accessat`]: fn.accessat.html + 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; + } +} + +#[cfg(not(target_os = "redox"))] +bitflags! { + /// `AT_*` constants for use with [`openat`], [`statat`], and other `*at` + /// functions. + /// + /// [`openat`]: crate::fs::openat + /// [`statat`]: crate::fs::statat + pub struct AtFlags: c::c_int { + /// `AT_REMOVEDIR` + const REMOVEDIR = c::AT_REMOVEDIR; + + /// `AT_SYMLINK_FOLLOW` + const SYMLINK_FOLLOW = c::AT_SYMLINK_FOLLOW; + + /// `AT_SYMLINK_NOFOLLOW` + const SYMLINK_NOFOLLOW = c::AT_SYMLINK_NOFOLLOW; + + /// `AT_EMPTY_PATH` + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + ))] + const EMPTY_PATH = c::AT_EMPTY_PATH; + + /// `AT_RESOLVE_BENEATH` + #[cfg(target_os = "freebsd")] + const RESOLVE_BENEATH = c::AT_RESOLVE_BENEATH; + + /// `AT_EACCESS` + #[cfg(not(any(target_os = "emscripten", target_os = "android")))] + const EACCESS = c::AT_EACCESS; + + /// `AT_STATX_SYNC_AS_STAT` + #[cfg(all(target_os = "linux", target_env = "gnu"))] + const STATX_SYNC_AS_STAT = c::AT_STATX_SYNC_AS_STAT; + + /// `AT_STATX_FORCE_SYNC` + #[cfg(all(target_os = "linux", target_env = "gnu"))] + const STATX_FORCE_SYNC = c::AT_STATX_FORCE_SYNC; + + /// `AT_STATX_DONT_SYNC` + #[cfg(all(target_os = "linux", target_env = "gnu"))] + const STATX_DONT_SYNC = c::AT_STATX_DONT_SYNC; + } +} + +bitflags! { + /// `S_I*` constants for use with [`openat`], [`chmodat`], and [`fchmod`]. + /// + /// [`openat`]: crate::fs::openat + /// [`chmodat`]: crate::fs::chmodat + /// [`fchmod`]: crate::fs::fchmod + pub struct Mode: RawMode { + /// `S_IRWXU` + #[cfg(not(target_os = "wasi"))] // WASI doesn't have Unix-style mode flags. + const RWXU = c::S_IRWXU as RawMode; + + /// `S_IRUSR` + #[cfg(not(target_os = "wasi"))] // WASI doesn't have Unix-style mode flags. + const RUSR = c::S_IRUSR as RawMode; + + /// `S_IWUSR` + #[cfg(not(target_os = "wasi"))] // WASI doesn't have Unix-style mode flags. + const WUSR = c::S_IWUSR as RawMode; + + /// `S_IXUSR` + #[cfg(not(target_os = "wasi"))] // WASI doesn't have Unix-style mode flags. + const XUSR = c::S_IXUSR as RawMode; + + /// `S_IRWXG` + #[cfg(not(target_os = "wasi"))] // WASI doesn't have Unix-style mode flags. + const RWXG = c::S_IRWXG as RawMode; + + /// `S_IRGRP` + #[cfg(not(target_os = "wasi"))] // WASI doesn't have Unix-style mode flags. + const RGRP = c::S_IRGRP as RawMode; + + /// `S_IWGRP` + #[cfg(not(target_os = "wasi"))] // WASI doesn't have Unix-style mode flags. + const WGRP = c::S_IWGRP as RawMode; + + /// `S_IXGRP` + #[cfg(not(target_os = "wasi"))] // WASI doesn't have Unix-style mode flags. + const XGRP = c::S_IXGRP as RawMode; + + /// `S_IRWXO` + #[cfg(not(target_os = "wasi"))] // WASI doesn't have Unix-style mode flags. + const RWXO = c::S_IRWXO as RawMode; + + /// `S_IROTH` + #[cfg(not(target_os = "wasi"))] // WASI doesn't have Unix-style mode flags. + const ROTH = c::S_IROTH as RawMode; + + /// `S_IWOTH` + #[cfg(not(target_os = "wasi"))] // WASI doesn't have Unix-style mode flags. + const WOTH = c::S_IWOTH as RawMode; + + /// `S_IXOTH` + #[cfg(not(target_os = "wasi"))] // WASI doesn't have Unix-style mode flags. + const XOTH = c::S_IXOTH as RawMode; + + /// `S_ISUID` + #[cfg(not(target_os = "wasi"))] // WASI doesn't have Unix-style mode flags. + const SUID = c::S_ISUID as RawMode; + + /// `S_ISGID` + #[cfg(not(target_os = "wasi"))] // WASI doesn't have Unix-style mode flags. + const SGID = c::S_ISGID as RawMode; + + /// `S_ISVTX` + #[cfg(not(target_os = "wasi"))] // WASI doesn't have Unix-style mode flags. + const SVTX = c::S_ISVTX as RawMode; + } +} + +impl Mode { + /// Construct a `Mode` from the mode bits of the `st_mode` field of + /// a `Stat`. + #[inline] + pub const fn from_raw_mode(st_mode: RawMode) -> Self { + Self::from_bits_truncate(st_mode) + } + + /// Construct an `st_mode` value from `Stat`. + #[inline] + pub const fn as_raw_mode(self) -> RawMode { + self.bits() + } +} + +impl From<RawMode> for Mode { + /// Support conversions from raw mode values to `Mode`. + /// + /// ``` + /// use rustix::fs::{Mode, RawMode}; + /// assert_eq!(Mode::from(0o700), Mode::RWXU); + /// ``` + #[inline] + fn from(st_mode: RawMode) -> Self { + Self::from_raw_mode(st_mode) + } +} + +impl From<Mode> for RawMode { + /// Support conversions from `Mode to raw mode values. + /// + /// ``` + /// use rustix::fs::{Mode, RawMode}; + /// assert_eq!(RawMode::from(Mode::RWXU), 0o700); + /// ``` + #[inline] + fn from(mode: Mode) -> Self { + mode.as_raw_mode() + } +} + +bitflags! { + /// `O_*` constants for use with [`openat`]. + /// + /// [`openat`]: crate::fs::openat + pub struct OFlags: c::c_int { + /// `O_ACCMODE` + const ACCMODE = c::O_ACCMODE; + + /// Similar to `ACCMODE`, but just includes the read/write flags, and + /// no other flags. + /// + /// Some implementations include `O_PATH` in `O_ACCMODE`, when + /// sometimes we really just want the read/write bits. Caution is + /// indicated, as the presence of `O_PATH` may mean that the read/write + /// bits don't have their usual meaning. + const RWMODE = c::O_RDONLY | c::O_WRONLY | c::O_RDWR; + + /// `O_APPEND` + const APPEND = c::O_APPEND; + + /// `O_CREAT` + #[doc(alias = "CREAT")] + const CREATE = c::O_CREAT; + + /// `O_DIRECTORY` + const DIRECTORY = c::O_DIRECTORY; + + /// `O_DSYNC` + #[cfg(not(any(target_os = "dragonfly", target_os = "redox")))] + const DSYNC = c::O_DSYNC; + + /// `O_EXCL` + const EXCL = c::O_EXCL; + + /// `O_FSYNC` + #[cfg(any( + bsd, + all(target_os = "linux", not(target_env = "musl")), + ))] + const FSYNC = c::O_FSYNC; + + /// `O_NOFOLLOW` + const NOFOLLOW = c::O_NOFOLLOW; + + /// `O_NONBLOCK` + const NONBLOCK = c::O_NONBLOCK; + + /// `O_RDONLY` + const RDONLY = c::O_RDONLY; + + /// `O_WRONLY` + const WRONLY = c::O_WRONLY; + + /// `O_RDWR` + const RDWR = c::O_RDWR; + + /// `O_NOCTTY` + #[cfg(not(target_os = "redox"))] + const NOCTTY = c::O_NOCTTY; + + /// `O_RSYNC` + #[cfg(any( + netbsdlike, + target_os = "android", + target_os = "emscripten", + target_os = "linux", + target_os = "wasi", + ))] + const RSYNC = c::O_RSYNC; + + /// `O_SYNC` + #[cfg(not(target_os = "redox"))] + const SYNC = c::O_SYNC; + + /// `O_TRUNC` + const TRUNC = c::O_TRUNC; + + /// `O_PATH` + #[cfg(any( + target_os = "android", + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + target_os = "redox", + ))] + const PATH = c::O_PATH; + + /// `O_CLOEXEC` + const CLOEXEC = c::O_CLOEXEC; + + /// `O_TMPFILE` + #[cfg(any( + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "linux", + ))] + const TMPFILE = c::O_TMPFILE; + + /// `O_NOATIME` + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + const NOATIME = c::O_NOATIME; + + /// `O_DIRECT` + #[cfg(any( + target_os = "android", + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + target_os = "netbsd", + ))] + const DIRECT = c::O_DIRECT; + + /// `O_RESOLVE_BENEATH` + #[cfg(target_os = "freebsd")] + const RESOLVE_BENEATH = c::O_RESOLVE_BENEATH; + + /// `O_EMPTY_PATH` + #[cfg(target_os = "freebsd")] + const EMPTY_PATH = c::O_EMPTY_PATH; + } +} + +#[cfg(apple)] +bitflags! { + /// `CLONE_*` constants for use with [`fclonefileat`]. + /// + /// [`fclonefileat`]: crate::fs::fclonefileat + pub struct CloneFlags: c::c_int { + /// `CLONE_NOFOLLOW` + const NOFOLLOW = 1; + + /// `CLONE_NOOWNERCOPY` + const NOOWNERCOPY = 2; + } +} + +#[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. + 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; + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +bitflags! { + /// `RESOLVE_*` constants for use with [`openat2`]. + /// + /// [`openat2`]: crate::fs::openat2 + #[derive(Default)] + 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; + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +bitflags! { + /// `RENAME_*` constants for use with [`renameat_with`]. + /// + /// [`renameat_with`]: crate::fs::renameat_with + pub struct RenameFlags: c::c_uint { + /// `RENAME_EXCHANGE` + const EXCHANGE = c::RENAME_EXCHANGE as _; + + /// `RENAME_NOREPLACE` + const NOREPLACE = c::RENAME_NOREPLACE as _; + + /// `RENAME_WHITEOUT` + const WHITEOUT = c::RENAME_WHITEOUT as _; + } +} + +/// `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`. + 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 `Stat`. + 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 = "haiku", target_os = "redox")))] + 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 = "haiku", + target_os = "redox", +)))] +#[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(target_os = "android", target_os = "freebsd", target_os = "linux"))] +bitflags! { + /// `MFD_*` constants for use with [`memfd_create`]. + /// + /// [`memfd_create`]: crate::fs::memfd_create + 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; + } +} + +#[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", +))] +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 + pub struct SealFlags: i32 { + /// `F_SEAL_SEAL`. + const SEAL = c::F_SEAL_SEAL; + /// `F_SEAL_SHRINK`. + const SHRINK = c::F_SEAL_SHRINK; + /// `F_SEAL_GROW`. + const GROW = c::F_SEAL_GROW; + /// `F_SEAL_WRITE`. + const WRITE = c::F_SEAL_WRITE; + /// `F_SEAL_FUTURE_WRITE` (since Linux 5.1) + #[cfg(any(target_os = "android", target_os = "linux"))] + const FUTURE_WRITE = c::F_SEAL_FUTURE_WRITE; + } +} + +#[cfg(all(target_os = "linux", target_env = "gnu"))] +bitflags! { + /// `STATX_*` constants for use with [`statx`]. + /// + /// [`statx`]: crate::fs::statx + 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_ALL` + const ALL = c::STATX_ALL; + } +} + +#[cfg(any( + target_os = "android", + all(target_os = "linux", not(target_env = "gnu")), +))] +bitflags! { + /// `STATX_*` constants for use with [`statx`]. + /// + /// [`statx`]: crate::fs::statx + 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; + } +} + +#[cfg(not(any(netbsdlike, solarish, target_os = "aix", target_os = "redox")))] +bitflags! { + /// `FALLOC_FL_*` constants for use with [`fallocate`]. + /// + /// [`fallocate`]: crate::fs::fallocate + pub struct FallocateFlags: i32 { + /// `FALLOC_FL_KEEP_SIZE` + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "wasi", + )))] + const KEEP_SIZE = c::FALLOC_FL_KEEP_SIZE; + /// `FALLOC_FL_PUNCH_HOLE` + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "wasi", + )))] + const PUNCH_HOLE = c::FALLOC_FL_PUNCH_HOLE; + /// `FALLOC_FL_NO_HIDE_STALE` + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "linux", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "wasi", + )))] + const NO_HIDE_STALE = c::FALLOC_FL_NO_HIDE_STALE; + /// `FALLOC_FL_COLLAPSE_RANGE` + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "emscripten", + target_os = "wasi", + )))] + const COLLAPSE_RANGE = c::FALLOC_FL_COLLAPSE_RANGE; + /// `FALLOC_FL_ZERO_RANGE` + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "emscripten", + target_os = "wasi", + )))] + const ZERO_RANGE = c::FALLOC_FL_ZERO_RANGE; + /// `FALLOC_FL_INSERT_RANGE` + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "emscripten", + target_os = "wasi", + )))] + const INSERT_RANGE = c::FALLOC_FL_INSERT_RANGE; + /// `FALLOC_FL_UNSHARE_RANGE` + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "emscripten", + target_os = "wasi", + )))] + const UNSHARE_RANGE = c::FALLOC_FL_UNSHARE_RANGE; + } +} + +#[cfg(not(any(solarish, target_os = "haiku", target_os = "redox", target_os = "wasi")))] +bitflags! { + /// `ST_*` constants for use with [`StatVfs`]. + pub struct StatVfsMountFlags: u64 { + /// `ST_MANDLOCK` + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] + const MANDLOCK = libc::ST_MANDLOCK as u64; + + /// `ST_NOATIME` + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] + const NOATIME = libc::ST_NOATIME as u64; + + /// `ST_NODEV` + #[cfg(any(target_os = "aix", target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] + const NODEV = libc::ST_NODEV as u64; + + /// `ST_NODIRATIME` + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] + const NODIRATIME = libc::ST_NODIRATIME as u64; + + /// `ST_NOEXEC` + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] + const NOEXEC = libc::ST_NOEXEC as u64; + + /// `ST_NOSUID` + const NOSUID = libc::ST_NOSUID as u64; + + /// `ST_RDONLY` + const RDONLY = libc::ST_RDONLY as u64; + + /// `ST_RELATIME` + #[cfg(any(target_os = "android", all(target_os = "linux", target_env = "gnu")))] + const RELATIME = libc::ST_RELATIME as u64; + + /// `ST_SYNCHRONOUS` + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] + const SYNCHRONOUS = libc::ST_SYNCHRONOUS as u64; + } +} + +/// `LOCK_*` constants for use with [`flock`] and [`fcntl_lock`]. +/// +/// [`flock`]: crate::fs::flock +/// [`fcntl_lock`]: crate::fs::fcntl_lock +#[cfg(not(target_os = "wasi"))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(i32)] +pub enum FlockOperation { + /// `LOCK_SH` + LockShared = c::LOCK_SH, + /// `LOCK_EX` + LockExclusive = c::LOCK_EX, + /// `LOCK_UN` + Unlock = c::LOCK_UN, + /// `LOCK_SH | LOCK_NB` + NonBlockingLockShared = c::LOCK_SH | c::LOCK_NB, + /// `LOCK_EX | LOCK_NB` + NonBlockingLockExclusive = c::LOCK_EX | c::LOCK_NB, + /// `LOCK_UN | LOCK_NB` + NonBlockingUnlock = c::LOCK_UN | c::LOCK_NB, +} + +/// `struct stat` for use with [`statat`] and [`fstat`]. +/// +/// [`statat`]: crate::fs::statat +/// [`fstat`]: crate::fs::fstat +#[cfg(not(linux_like))] +pub type Stat = c::stat; + +/// `struct stat` for use with [`statat`] and [`fstat`]. +/// +/// [`statat`]: crate::fs::statat +/// [`fstat`]: crate::fs::fstat +#[cfg(any( + all( + any(target_os = "android", target_os = "linux"), + target_pointer_width = "64", + ), + 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( + any(target_os = "android", target_os = "linux"), + 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 = "haiku", + target_os = "netbsd", + target_os = "redox", + 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(solarish, 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, even though `__fsword_t` is `u64`. +#[cfg(all(target_os = "linux", target_arch = "s390x"))] +pub type FsWord = u32; + +/// `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); + +#[cfg(any(target_os = "android", target_os = "linux"))] +bitflags! { + /// `MS_*` constants for use with [`mount`]. + /// + /// [`mount`]: crate::fs::mount + pub struct MountFlags: c::c_ulong { + /// `MS_BIND` + const BIND = c::MS_BIND; + + /// `MS_DIRSYNC` + const DIRSYNC = c::MS_DIRSYNC; + + /// `MS_LAZYTIME` + const LAZYTIME = c::MS_LAZYTIME; + + /// `MS_MANDLOCK` + #[doc(alias = "MANDLOCK")] + const PERMIT_MANDATORY_FILE_LOCKING = c::MS_MANDLOCK; + + /// `MS_NOATIME` + const NOATIME = c::MS_NOATIME; + + /// `MS_NODEV` + const NODEV = c::MS_NODEV; + + /// `MS_NODIRATIME` + const NODIRATIME = c::MS_NODIRATIME; + + /// `MS_NOEXEC` + const NOEXEC = c::MS_NOEXEC; + + /// `MS_NOSUID` + const NOSUID = c::MS_NOSUID; + + /// `MS_RDONLY` + const RDONLY = c::MS_RDONLY; + + /// `MS_REC` + const REC = c::MS_REC; + + /// `MS_RELATIME` + const RELATIME = c::MS_RELATIME; + + /// `MS_SILENT` + const SILENT = c::MS_SILENT; + + /// `MS_STRICTATIME` + const STRICTATIME = c::MS_STRICTATIME; + + /// `MS_SYNCHRONOUS` + const SYNCHRONOUS = c::MS_SYNCHRONOUS; + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +bitflags! { + /// `MS_*` constants for use with [`change_mount`]. + /// + /// [`change_mount`]: crate::fs::mount::change_mount. + pub struct MountPropagationFlags: c::c_ulong { + /// `MS_SHARED` + const SHARED = c::MS_SHARED; + /// `MS_PRIVATE` + const PRIVATE = c::MS_PRIVATE; + /// `MS_SLAVE` + const SLAVE = c::MS_SLAVE; + /// `MS_UNBINDABLE` + const UNBINDABLE = c::MS_UNBINDABLE; + /// `MS_REC` + const REC = c::MS_REC; + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +bitflags! { + pub(crate) struct InternalMountFlags: c::c_ulong { + const REMOUNT = c::MS_REMOUNT; + const MOVE = c::MS_MOVE; + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub(crate) struct MountFlagsArg(pub(crate) c::c_ulong); + +#[cfg(any(target_os = "android", target_os = "linux"))] +bitflags! { + /// `MNT_*` constants for use with [`unmount`]. + /// + /// [`unmount`]: crate::fs::mount::unmount + pub struct UnmountFlags: c::c_int { + /// `MNT_FORCE` + const FORCE = c::MNT_FORCE; + /// `MNT_DETACH` + const DETACH = c::MNT_DETACH; + /// `MNT_EXPIRE` + const EXPIRE = c::MNT_EXPIRE; + /// `UMOUNT_NOFOLLOW` + const NOFOLLOW = c::UMOUNT_NOFOLLOW; + } +} |