From 1f44e358506b5fd6e03652005438cfddbdb670e3 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 17 Oct 2024 07:49:06 -0700 Subject: [PATCH] Add a `rustix::fs::ABS` constant. Add a `rustix::fs::ABS` constant, which corresponds to the undocumented but commonly used convention of using `-EBADF` as the file descriptor in `openat` and similar calls. This makes them fail if the path is not absolute. Fixes #1187. --- src/backend/libc/fs/syscalls.rs | 13 ++----- src/backend/linux_raw/c.rs | 2 +- src/backend/linux_raw/conv.rs | 2 +- src/fs/at.rs | 5 ++- src/fs/cwd.rs | 29 -------------- src/fs/mod.rs | 12 ++---- src/fs/special.rs | 68 +++++++++++++++++++++++++++++++++ src/process/chdir.rs | 2 +- tests/fs/cwd.rs | 4 -- tests/fs/main.rs | 2 +- 10 files changed, 83 insertions(+), 56 deletions(-) delete mode 100644 src/fs/cwd.rs create mode 100644 src/fs/special.rs delete mode 100644 tests/fs/cwd.rs diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs index 205dc0094..2d2c26ea8 100644 --- a/src/backend/libc/fs/syscalls.rs +++ b/src/backend/libc/fs/syscalls.rs @@ -596,14 +596,9 @@ pub(crate) fn stat(path: &CStr) -> io::Result { ) ))] { - match crate::fs::statx( - crate::fs::CWD, - path, - AtFlags::empty(), - StatxFlags::BASIC_STATS, - ) { + match crate::fs::statx(CWD, path, AtFlags::empty(), StatxFlags::BASIC_STATS) { Ok(x) => statx_to_stat(x), - Err(io::Errno::NOSYS) => statat_old(crate::fs::CWD, path, AtFlags::empty()), + Err(io::Errno::NOSYS) => statat_old(CWD, path, AtFlags::empty()), Err(err) => Err(err), } } @@ -637,13 +632,13 @@ pub(crate) fn lstat(path: &CStr) -> io::Result { ))] { match crate::fs::statx( - crate::fs::CWD, + CWD, path, AtFlags::SYMLINK_NOFOLLOW, StatxFlags::BASIC_STATS, ) { Ok(x) => statx_to_stat(x), - Err(io::Errno::NOSYS) => statat_old(crate::fs::CWD, path, AtFlags::SYMLINK_NOFOLLOW), + Err(io::Errno::NOSYS) => statat_old(CWD, path, AtFlags::SYMLINK_NOFOLLOW), Err(err) => Err(err), } } diff --git a/src/backend/linux_raw/c.rs b/src/backend/linux_raw/c.rs index c4e1001b7..08ded6b35 100644 --- a/src/backend/linux_raw/c.rs +++ b/src/backend/linux_raw/c.rs @@ -8,7 +8,7 @@ pub(crate) type size_t = usize; pub(crate) use linux_raw_sys::ctypes::*; -pub(crate) use linux_raw_sys::errno::EINVAL; +pub(crate) use linux_raw_sys::errno::{EBADF, EINVAL}; pub(crate) use linux_raw_sys::general::{__kernel_fd_set as fd_set, __FD_SETSIZE as FD_SETSIZE}; pub(crate) use linux_raw_sys::ioctl::{FIONBIO, FIONREAD}; // Import the kernel's `uid_t` and `gid_t` if they're 32-bit. diff --git a/src/backend/linux_raw/conv.rs b/src/backend/linux_raw/conv.rs index 269b7ade4..c8e8a4280 100644 --- a/src/backend/linux_raw/conv.rs +++ b/src/backend/linux_raw/conv.rs @@ -162,7 +162,7 @@ impl<'a, Num: ArgNumber> From> for ArgReg<'a, Num> { pub(super) unsafe fn raw_fd<'a, Num: ArgNumber>(fd: RawFd) -> ArgReg<'a, Num> { // Use `no_fd` when passing `-1` is intended. #[cfg(feature = "fs")] - debug_assert!(fd == crate::fs::CWD.as_raw_fd() || fd >= 0); + debug_assert!(fd == crate::fs::CWD.as_raw_fd() || fd == crate::fs::ABS.as_raw_fd() || fd >= 0); // Don't pass the `io_uring_register_files_skip` sentry value this way. #[cfg(feature = "io_uring")] diff --git a/src/fs/at.rs b/src/fs/at.rs index bf9628070..94db1baee 100644 --- a/src/fs/at.rs +++ b/src/fs/at.rs @@ -1,9 +1,10 @@ //! POSIX-style `*at` functions. //! //! The `dirfd` argument to these functions may be a file descriptor for a -//! directory, or the special value [`CWD`]. +//! directory, the special value [`CWD`], or the special value [`ABS`]. //! -//! [`cwd`]: crate::fs::CWD +//! [`CWD`]: crate::fs::CWD +//! [`ABS`]: crate::fs::ABS use crate::fd::OwnedFd; use crate::ffi::CStr; diff --git a/src/fs/cwd.rs b/src/fs/cwd.rs deleted file mode 100644 index 3b6092eec..000000000 --- a/src/fs/cwd.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! The `cwd` function, representing the current working directory. -//! -//! # Safety -//! -//! This file uses `AT_FDCWD`, which is a raw file descriptor, but which is -//! always valid. - -#![allow(unsafe_code)] - -use crate::backend; -use backend::c; -use backend::fd::{BorrowedFd, RawFd}; - -/// `AT_FDCWD`—A handle representing the current working directory. -/// -/// This is a file descriptor which refers to the process current directory -/// which can be used as the directory argument in `*at` functions such as -/// [`openat`]. -/// -/// # References -/// - [POSIX] -/// -/// [`openat`]: crate::fs::openat -/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fcntl.h.html -// SAFETY: `AT_FDCWD` is a reserved value that is never dynamically -// allocated, so it'll remain valid for the duration of `'static`. -#[doc(alias = "AT_FDCWD")] -pub const CWD: BorrowedFd<'static> = - unsafe { BorrowedFd::<'static>::borrow_raw(c::AT_FDCWD as RawFd) }; diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 3b76b58e2..de64f54f4 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -6,10 +6,6 @@ mod at; mod constants; #[cfg(linux_kernel)] mod copy_file_range; -#[cfg(not(any(target_os = "espidf", target_os = "redox")))] -#[cfg(not(target_os = "haiku"))] -// Haiku needs -mod cwd; #[cfg(all(feature = "alloc", not(any(target_os = "espidf", target_os = "redox"))))] mod dir; #[cfg(not(any( @@ -54,6 +50,8 @@ mod raw_dir; mod seek_from; #[cfg(target_os = "linux")] mod sendfile; +#[cfg(not(any(target_os = "espidf", target_os = "redox")))] +mod special; #[cfg(linux_kernel)] mod statx; #[cfg(not(any( @@ -72,10 +70,6 @@ pub use at::*; pub use constants::*; #[cfg(linux_kernel)] pub use copy_file_range::copy_file_range; -#[cfg(not(any(target_os = "espidf", target_os = "redox")))] -#[cfg(not(target_os = "haiku"))] -// Haiku needs -pub use cwd::*; #[cfg(all(feature = "alloc", not(any(target_os = "espidf", target_os = "redox"))))] pub use dir::{Dir, DirEntry}; #[cfg(not(any( @@ -118,6 +112,8 @@ pub use raw_dir::{RawDir, RawDirEntry}; pub use seek_from::SeekFrom; #[cfg(target_os = "linux")] pub use sendfile::sendfile; +#[cfg(not(any(target_os = "espidf", target_os = "redox")))] +pub use special::*; #[cfg(linux_kernel)] pub use statx::statx; #[cfg(not(any( diff --git a/src/fs/special.rs b/src/fs/special.rs new file mode 100644 index 000000000..400ac8426 --- /dev/null +++ b/src/fs/special.rs @@ -0,0 +1,68 @@ +//! The `CWD` and `ABS` constants, representing the current working directory +//! and absolute-only paths, respectively. +//! +//! # Safety +//! +//! This file uses `AT_FDCWD`, which is a raw file descriptor, but which is +//! always valid, and `-EBADF`, which is an undocumented by commonly used +//! convention for passing a value which will always fail if the accompanying +//! path isn't absolute. + +#![allow(unsafe_code)] + +use crate::backend; +use backend::c; +use backend::fd::{BorrowedFd, RawFd}; + +/// `AT_FDCWD`—A handle representing the current working directory. +/// +/// This is a file descriptor which refers to the process current directory +/// which can be used as the directory argument in `*at` functions such as +/// [`openat`]. +/// +/// # References +/// - [POSIX] +/// +/// [`openat`]: crate::fs::openat +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fcntl.h.html +// SAFETY: `AT_FDCWD` is a reserved value that is never dynamically +// allocated, so it'll remain valid for the duration of `'static`. +#[doc(alias = "AT_FDCWD")] +pub const CWD: BorrowedFd<'static> = + unsafe { BorrowedFd::<'static>::borrow_raw(c::AT_FDCWD as RawFd) }; + +/// `-EBADF`—A handle that requires paths to be absolute. +/// +/// This is a file descriptor which refers to no directory, which can be used +/// as the directory argument in `*at` functions such as [`openat`], which +/// causes them to fail if the accompanying path is not absolute. +/// +/// [`openat`]: crate::fs::openat +// SAFETY: This `-EBADF` convention is commonly used, such as in lxc, so OS's +// aren't going to break it. +pub const ABS: BorrowedFd<'static> = + unsafe { BorrowedFd::<'static>::borrow_raw(c::EBADF.wrapping_neg() as RawFd) }; + +#[cfg(test)] +mod tests { + use super::*; + use crate::fd::AsRawFd; + + #[test] + fn test_cwd() { + assert!(CWD.as_raw_fd() != -1); + #[cfg(linux_kernel)] + #[cfg(feature = "io_uring")] + assert!(CWD.as_raw_fd() != linux_raw_sys::io_uring::IORING_REGISTER_FILES_SKIP); + } + + #[test] + fn test_abs() { + assert!(ABS.as_raw_fd() < 0); + assert!(ABS.as_raw_fd() != -1); + assert!(ABS.as_raw_fd() != c::AT_FDCWD); + #[cfg(linux_kernel)] + #[cfg(feature = "io_uring")] + assert!(ABS.as_raw_fd() != linux_raw_sys::io_uring::IORING_REGISTER_FILES_SKIP); + } +} diff --git a/src/process/chdir.rs b/src/process/chdir.rs index d38a33fda..04331ee01 100644 --- a/src/process/chdir.rs +++ b/src/process/chdir.rs @@ -40,7 +40,7 @@ pub fn fchdir(fd: Fd) -> io::Result<()> { backend::process::syscalls::fchdir(fd.as_fd()) } -/// `getCWD`—Return the current working directory. +/// `getcwd`—Return the current working directory. /// /// If `reuse` already has available capacity, reuse it if possible. /// diff --git a/tests/fs/cwd.rs b/tests/fs/cwd.rs deleted file mode 100644 index e9f345164..000000000 --- a/tests/fs/cwd.rs +++ /dev/null @@ -1,4 +0,0 @@ -/// Make sure we can use `cwd` in const contexts. -#[allow(dead_code)] -#[cfg(not(target_os = "redox"))] -const CWD: rustix::fd::BorrowedFd<'static> = rustix::fs::CWD; diff --git a/tests/fs/main.rs b/tests/fs/main.rs index db074fedb..8c8c0e5c0 100644 --- a/tests/fs/main.rs +++ b/tests/fs/main.rs @@ -5,7 +5,6 @@ #![cfg_attr(core_c_str, feature(core_c_str))] mod chmodat; -mod cwd; #[cfg(not(target_os = "redox"))] mod dir; mod fcntl; @@ -42,6 +41,7 @@ mod renameat; #[cfg(any(linux_kernel, target_os = "freebsd"))] mod seals; mod seek; +mod special; #[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] mod statfs; #[cfg(linux_kernel)]