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