diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:18:32 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:18:32 +0000 |
commit | 4547b622d8d29df964fa2914213088b148c498fc (patch) | |
tree | 9fc6b25f3c3add6b745be9a2400a6e96140046e9 /vendor/rustix/src/backend/linux_raw/fs/syscalls.rs | |
parent | Releasing progress-linux version 1.66.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-4547b622d8d29df964fa2914213088b148c498fc.tar.xz rustc-4547b622d8d29df964fa2914213088b148c498fc.zip |
Merging upstream version 1.67.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/rustix/src/backend/linux_raw/fs/syscalls.rs')
-rw-r--r-- | vendor/rustix/src/backend/linux_raw/fs/syscalls.rs | 1384 |
1 files changed, 1384 insertions, 0 deletions
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 000000000..ff58f0a7b --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/fs/syscalls.rs @@ -0,0 +1,1384 @@ +//! linux_raw syscalls supporting `rustix::fs`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code)] +#![allow(dead_code)] +#![allow(clippy::undocumented_unsafe_blocks)] + +use super::super::c; +use super::super::conv::{ + by_ref, c_int, c_uint, dev_t, oflags_for_open_how, opt_mut, pass_usize, raw_fd, ret, ret_c_int, + ret_c_uint, ret_owned_fd, ret_usize, size_of, slice_mut, zero, +}; +#[cfg(target_pointer_width = "64")] +use super::super::conv::{loff_t, loff_t_from_u64, ret_u64}; +#[cfg(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "mips64", + target_pointer_width = "32", +))] +use crate::fd::AsFd; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::ffi::CStr; +use crate::fs::{ + Access, Advice, AtFlags, FallocateFlags, FileType, FlockOperation, MemfdFlags, Mode, OFlags, + RenameFlags, ResolveFlags, SealFlags, Stat, StatFs, StatVfs, StatVfsMountFlags, StatxFlags, + Timestamps, +}; +use crate::io::{self, SeekFrom}; +use crate::process::{Gid, Uid}; +use core::convert::TryInto; +use core::mem::MaybeUninit; +#[cfg(target_arch = "mips64")] +use linux_raw_sys::general::stat as linux_stat64; +use linux_raw_sys::general::{ + __kernel_fsid_t, __kernel_timespec, open_how, statx, AT_EACCESS, AT_FDCWD, AT_REMOVEDIR, + AT_SYMLINK_NOFOLLOW, F_ADD_SEALS, F_GETFL, F_GETLEASE, F_GETOWN, F_GETPIPE_SZ, F_GETSIG, + F_GET_SEALS, F_SETFL, F_SETPIPE_SZ, SEEK_CUR, SEEK_END, SEEK_SET, STATX__RESERVED, +}; +#[cfg(target_pointer_width = "32")] +use { + super::super::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(filename: &CStr, flags: OFlags, mode: Mode) -> io::Result<OwnedFd> { + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + { + openat(crate::fs::cwd().as_fd(), filename, flags, mode) + } + #[cfg(all( + target_pointer_width = "32", + not(any(target_arch = "aarch64", target_arch = "riscv64")), + ))] + unsafe { + ret_owned_fd(syscall_readonly!(__NR_open, filename, flags, mode)) + } + #[cfg(all( + target_pointer_width = "64", + not(any(target_arch = "aarch64", target_arch = "riscv64")), + ))] + unsafe { + ret_owned_fd(syscall_readonly!(__NR_open, filename, flags, mode)) + } +} + +#[inline] +pub(crate) fn openat( + dirfd: BorrowedFd<'_>, + filename: &CStr, + flags: OFlags, + mode: Mode, +) -> io::Result<OwnedFd> { + #[cfg(target_pointer_width = "32")] + unsafe { + ret_owned_fd(syscall_readonly!(__NR_openat, dirfd, filename, flags, mode)) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_owned_fd(syscall_readonly!(__NR_openat, dirfd, filename, flags, mode)) + } +} + +#[inline] +pub(crate) fn openat2( + dirfd: BorrowedFd<'_>, + pathname: &CStr, + flags: OFlags, + mode: Mode, + resolve: ResolveFlags, +) -> io::Result<OwnedFd> { + #[cfg(target_pointer_width = "32")] + unsafe { + ret_owned_fd(syscall_readonly!( + __NR_openat2, + dirfd, + pathname, + by_ref(&open_how { + flags: oflags_for_open_how(flags), + mode: u64::from(mode.bits()), + resolve: resolve.bits(), + }), + size_of::<open_how, _>() + )) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_owned_fd(syscall_readonly!( + __NR_openat2, + dirfd, + pathname, + 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(filename: &CStr, mode: Mode) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_fchmodat, + raw_fd(AT_FDCWD), + filename, + mode + )) + } +} + +#[inline] +pub(crate) fn chmodat(dirfd: BorrowedFd<'_>, filename: &CStr, mode: Mode) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_fchmodat, dirfd, filename, 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<'_>, + filename: &CStr, + owner: Option<Uid>, + group: Option<Gid>, + flags: AtFlags, +) -> io::Result<()> { + unsafe { + let (ow, gr) = crate::process::translate_fchown_args(owner, group); + ret(syscall_readonly!( + __NR_fchownat, + dirfd, + filename, + c_uint(ow), + c_uint(gr), + flags + )) + } +} + +#[inline] +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(syscall_readonly!(__NR_fchown, fd, c_uint(ow), c_uint(gr))) + } +} + +#[inline] +pub(crate) fn mknodat( + dirfd: BorrowedFd<'_>, + filename: &CStr, + file_type: FileType, + mode: Mode, + dev: u64, +) -> io::Result<()> { + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall_readonly!( + __NR_mknodat, + dirfd, + filename, + (mode, file_type), + dev_t(dev)? + )) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall_readonly!( + __NR_mknodat, + dirfd, + filename, + (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), + }; + _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 = "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 = "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(target_arch = "mips")] + unsafe { + ret(syscall_readonly!( + __NR_fadvise64, + fd, + zero(), + hi(pos), + lo(pos), + hi(len), + lo(len), + advice + )) + } + #[cfg(all( + target_pointer_width = "32", + not(any(target_arch = "arm", target_arch = "mips", target_arch = "powerpc")), + ))] + unsafe { + ret(syscall_readonly!( + __NR_fadvise64_64, + fd, + hi(pos), + lo(pos), + hi(len), + lo(len), + advice + )) + } + #[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!(__NR_flock, fd, c_uint(operation as c::c_uint))) } +} + +#[inline] +pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result<Stat> { + #[cfg(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), + } + } + + #[cfg(all(target_pointer_width = "64", not(target_arch = "mips64")))] + 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"))] +fn fstat_old(fd: BorrowedFd<'_>) -> io::Result<Stat> { + let mut result = MaybeUninit::<linux_stat64>::uninit(); + + #[cfg(target_arch = "mips64")] + 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(filename: &CStr) -> io::Result<Stat> { + #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))] + { + match statx( + crate::fs::cwd().as_fd(), + filename, + AtFlags::empty(), + StatxFlags::BASIC_STATS, + ) { + Ok(x) => statx_to_stat(x), + Err(io::Errno::NOSYS) => stat_old(filename), + 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), + filename, + &mut result, + c_uint(0) + ))?; + Ok(result.assume_init()) + } +} + +#[cfg(any(target_pointer_width = "32", target_arch = "mips64"))] +fn stat_old(filename: &CStr) -> io::Result<Stat> { + let mut result = MaybeUninit::<linux_stat64>::uninit(); + + #[cfg(target_arch = "mips64")] + unsafe { + ret(syscall!( + __NR_newfstatat, + raw_fd(AT_FDCWD), + filename, + &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), + filename, + &mut result, + c_uint(0) + ))?; + stat_to_stat(result.assume_init()) + } +} + +#[inline] +pub(crate) fn statat(dirfd: BorrowedFd<'_>, filename: &CStr, flags: AtFlags) -> io::Result<Stat> { + #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))] + { + match statx(dirfd, filename, flags, StatxFlags::BASIC_STATS) { + Ok(x) => statx_to_stat(x), + Err(io::Errno::NOSYS) => statat_old(dirfd, filename, flags), + 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, + dirfd, + filename, + &mut result, + flags + ))?; + Ok(result.assume_init()) + } +} + +#[cfg(any(target_pointer_width = "32", target_arch = "mips64"))] +fn statat_old(dirfd: BorrowedFd<'_>, filename: &CStr, flags: AtFlags) -> io::Result<Stat> { + let mut result = MaybeUninit::<linux_stat64>::uninit(); + + #[cfg(target_arch = "mips64")] + unsafe { + ret(syscall!( + __NR_newfstatat, + dirfd, + filename, + &mut result, + flags + ))?; + stat_to_stat(result.assume_init()) + } + + #[cfg(target_pointer_width = "32")] + unsafe { + ret(syscall!( + __NR_fstatat64, + dirfd, + filename, + &mut result, + flags + ))?; + stat_to_stat(result.assume_init()) + } +} + +#[inline] +pub(crate) fn lstat(filename: &CStr) -> io::Result<Stat> { + #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))] + { + match statx( + crate::fs::cwd().as_fd(), + filename, + AtFlags::SYMLINK_NOFOLLOW, + StatxFlags::BASIC_STATS, + ) { + Ok(x) => statx_to_stat(x), + Err(io::Errno::NOSYS) => lstat_old(filename), + 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), + filename, + &mut result, + c_uint(AT_SYMLINK_NOFOLLOW) + ))?; + Ok(result.assume_init()) + } +} + +#[cfg(any(target_pointer_width = "32", target_arch = "mips64"))] +fn lstat_old(filename: &CStr) -> io::Result<Stat> { + let mut result = MaybeUninit::<linux_stat64>::uninit(); + + #[cfg(target_arch = "mips64")] + unsafe { + ret(syscall!( + __NR_newfstatat, + raw_fd(AT_FDCWD), + filename, + &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), + filename, + &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"))] +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(target_arch = "mips64")] +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<'_>, + pathname: &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, + pathname, + flags, + mask, + &mut statx_buf + ))?; + Ok(statx_buf.assume_init()) + } +} + +#[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(syscall!( + __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(filename: &CStr) -> io::Result<StatFs> { + #[cfg(target_pointer_width = "32")] + unsafe { + let mut result = MaybeUninit::<StatFs>::uninit(); + ret(syscall!( + __NR_statfs64, + filename, + 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, filename, &mut result))?; + Ok(result.assume_init()) + } +} + +#[inline] +pub(crate) fn statvfs(filename: &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(filename)?; + + 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: f_fsid_val0 as u32 as u64 | ((f_fsid_val1 as u32 as u64) << 32), + f_flag: unsafe { StatVfsMountFlags::from_bits_unchecked(statfs.f_flags as u64) }, + f_namemax: statfs.f_namelen as u64, + } +} + +#[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 [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_truncate) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_c_uint(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETFL))) + .map(OFlags::from_bits_truncate) + } +} + +#[inline] +pub(crate) fn fcntl_setfl(fd: BorrowedFd<'_>, flags: OFlags) -> io::Result<()> { + #[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_getlease(fd: BorrowedFd<'_>) -> io::Result<c::c_int> { + #[cfg(target_pointer_width = "32")] + unsafe { + ret_c_int(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETLEASE))) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_c_int(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETLEASE))) + } +} + +#[inline] +pub(crate) fn fcntl_getown(fd: BorrowedFd<'_>) -> io::Result<c::c_int> { + #[cfg(target_pointer_width = "32")] + unsafe { + ret_c_int(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETOWN))) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_c_int(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETOWN))) + } +} + +#[inline] +pub(crate) fn fcntl_getsig(fd: BorrowedFd<'_>) -> io::Result<c::c_int> { + #[cfg(target_pointer_width = "32")] + unsafe { + ret_c_int(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETSIG))) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_c_int(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETSIG))) + } +} + +#[inline] +pub(crate) fn fcntl_getpipe_sz(fd: BorrowedFd<'_>) -> io::Result<usize> { + #[cfg(target_pointer_width = "32")] + unsafe { + ret_usize(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETPIPE_SZ))) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_usize(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETPIPE_SZ))) + } +} + +#[inline] +pub(crate) fn fcntl_setpipe_sz(fd: BorrowedFd<'_>, size: c::c_int) -> io::Result<usize> { + #[cfg(target_pointer_width = "32")] + unsafe { + ret_usize(syscall_readonly!( + __NR_fcntl64, + fd, + c_uint(F_SETPIPE_SZ), + c_int(size) + )) + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret_usize(syscall_readonly!( + __NR_fcntl, + fd, + c_uint(F_SETPIPE_SZ), + c_int(size) + )) + } +} + +#[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_unchecked(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_unchecked(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 rename(oldname: &CStr, newname: &CStr) -> io::Result<()> { + #[cfg(target_arch = "riscv64")] + unsafe { + ret(syscall_readonly!( + __NR_renameat2, + raw_fd(AT_FDCWD), + oldname, + raw_fd(AT_FDCWD), + newname, + c_uint(0) + )) + } + #[cfg(not(target_arch = "riscv64"))] + unsafe { + ret(syscall_readonly!( + __NR_renameat, + raw_fd(AT_FDCWD), + oldname, + raw_fd(AT_FDCWD), + newname + )) + } +} + +#[inline] +pub(crate) fn renameat( + old_dirfd: BorrowedFd<'_>, + oldname: &CStr, + new_dirfd: BorrowedFd<'_>, + newname: &CStr, +) -> io::Result<()> { + #[cfg(target_arch = "riscv64")] + unsafe { + ret(syscall_readonly!( + __NR_renameat2, + old_dirfd, + oldname, + new_dirfd, + newname, + c_uint(0) + )) + } + #[cfg(not(target_arch = "riscv64"))] + unsafe { + ret(syscall_readonly!( + __NR_renameat, + old_dirfd, + oldname, + new_dirfd, + newname + )) + } +} + +#[inline] +pub(crate) fn renameat2( + old_dirfd: BorrowedFd<'_>, + oldname: &CStr, + new_dirfd: BorrowedFd<'_>, + newname: &CStr, + flags: RenameFlags, +) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_renameat2, + old_dirfd, + oldname, + new_dirfd, + newname, + flags + )) + } +} + +#[inline] +pub(crate) fn unlink(pathname: &CStr) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_unlinkat, + raw_fd(AT_FDCWD), + pathname, + c_uint(0) + )) + } +} + +#[inline] +pub(crate) fn unlinkat(dirfd: BorrowedFd<'_>, pathname: &CStr, flags: AtFlags) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_unlinkat, dirfd, pathname, flags)) } +} + +#[inline] +pub(crate) fn rmdir(pathname: &CStr) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_unlinkat, + raw_fd(AT_FDCWD), + pathname, + c_uint(AT_REMOVEDIR) + )) + } +} + +#[inline] +pub(crate) fn link(oldname: &CStr, newname: &CStr) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_linkat, + raw_fd(AT_FDCWD), + oldname, + raw_fd(AT_FDCWD), + newname, + c_uint(0) + )) + } +} + +#[inline] +pub(crate) fn linkat( + old_dirfd: BorrowedFd<'_>, + oldname: &CStr, + new_dirfd: BorrowedFd<'_>, + newname: &CStr, + flags: AtFlags, +) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_linkat, + old_dirfd, + oldname, + new_dirfd, + newname, + flags + )) + } +} + +#[inline] +pub(crate) fn symlink(oldname: &CStr, newname: &CStr) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_symlinkat, + oldname, + raw_fd(AT_FDCWD), + newname + )) + } +} + +#[inline] +pub(crate) fn symlinkat(oldname: &CStr, dirfd: BorrowedFd<'_>, newname: &CStr) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_symlinkat, oldname, dirfd, newname)) } +} + +#[inline] +pub(crate) fn mkdir(pathname: &CStr, mode: Mode) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_mkdirat, + raw_fd(AT_FDCWD), + pathname, + mode + )) + } +} + +#[inline] +pub(crate) fn mkdirat(dirfd: BorrowedFd<'_>, pathname: &CStr, mode: Mode) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_mkdirat, dirfd, pathname, mode)) } +} + +#[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 utimensat( + dirfd: BorrowedFd<'_>, + pathname: &CStr, + times: &Timestamps, + flags: AtFlags, +) -> io::Result<()> { + _utimensat(dirfd, Some(pathname), times, flags) +} + +#[inline] +fn _utimensat( + dirfd: BorrowedFd<'_>, + pathname: Option<&CStr>, + times: &Timestamps, + flags: AtFlags, +) -> io::Result<()> { + // Assert that `Timestamps` has the expected layout. + let _ = unsafe { core::mem::transmute::<Timestamps, [__kernel_timespec; 2]>(times.clone()) }; + + #[cfg(target_pointer_width = "32")] + unsafe { + match ret(syscall_readonly!( + __NR_utimensat_time64, + dirfd, + pathname, + by_ref(times), + flags + )) { + Err(io::Errno::NOSYS) => _utimensat_old(dirfd, pathname, times, flags), + otherwise => otherwise, + } + } + #[cfg(target_pointer_width = "64")] + unsafe { + ret(syscall_readonly!( + __NR_utimensat, + dirfd, + pathname, + by_ref(times), + flags + )) + } +} + +#[cfg(target_pointer_width = "32")] +unsafe fn _utimensat_old( + dirfd: BorrowedFd<'_>, + pathname: 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, + pathname, + old_times_addr, + flags + )) +} + +#[inline] +pub(crate) fn futimens(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()> { + _utimensat(fd, None, times, AtFlags::empty()) +} + +pub(crate) fn accessat( + dirfd: BorrowedFd<'_>, + path: &CStr, + access: Access, + flags: AtFlags, +) -> io::Result<()> { + // 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::process::getuid() == crate::process::geteuid() + && crate::process::getgid() == crate::process::getegid()) + { + return unsafe { ret(syscall_readonly!(__NR_faccessat, dirfd, path, access)) }; + } + + if flags.bits() != AT_EACCESS { + return Err(io::Errno::INVAL); + } + + // TODO: Use faccessat2 in newer Linux versions. + Err(io::Errno::NOSYS) +} + +#[inline] +pub(crate) fn copy_file_range( + fd_in: BorrowedFd<'_>, + off_in: Option<&mut u64>, + fd_out: BorrowedFd<'_>, + off_out: Option<&mut u64>, + len: u64, +) -> io::Result<u64> { + let len: usize = len.try_into().unwrap_or(usize::MAX); + _copy_file_range(fd_in, off_in, fd_out, off_out, len, 0).map(|result| result as u64) +} + +#[inline] +fn _copy_file_range( + fd_in: BorrowedFd<'_>, + off_in: Option<&mut u64>, + fd_out: BorrowedFd<'_>, + off_out: Option<&mut u64>, + len: usize, + flags: c::c_uint, +) -> 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(flags) + )) + } +} + +#[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) + )) + } +} |