//! POSIX-style `*at` functions. //! //! The `dirfd` argument to these functions may be a file descriptor for a //! directory, or the special value returned by [`cwd`]. //! //! [`cwd`]: crate::fs::cwd use crate::ffi::{CStr, CString}; #[cfg(not(target_os = "illumos"))] use crate::fs::Access; #[cfg(any(target_os = "ios", target_os = "macos"))] use crate::fs::CloneFlags; #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "wasi")))] use crate::fs::FileType; #[cfg(any(target_os = "android", target_os = "linux"))] use crate::fs::RenameFlags; use crate::fs::{AtFlags, Mode, OFlags, Stat, Timestamps}; use crate::io::{self, OwnedFd}; use crate::path::SMALL_PATH_BUFFER_SIZE; #[cfg(not(target_os = "wasi"))] use crate::process::{Gid, Uid}; use crate::{imp, path}; use alloc::vec::Vec; use imp::fd::{AsFd, BorrowedFd}; use imp::time::types::Nsecs; pub use imp::fs::types::{Dev, RawMode}; /// `UTIME_NOW` for use with [`utimensat`]. /// /// [`utimensat`]: crate::fs::utimensat #[cfg(not(target_os = "redox"))] pub const UTIME_NOW: Nsecs = imp::fs::types::UTIME_NOW as Nsecs; /// `UTIME_OMIT` for use with [`utimensat`]. /// /// [`utimensat`]: crate::fs::utimensat #[cfg(not(target_os = "redox"))] pub const UTIME_OMIT: Nsecs = imp::fs::types::UTIME_OMIT as Nsecs; /// `openat(dirfd, path, oflags, mode)`—Opens a file. /// /// POSIX guarantees that `openat` will use the lowest unused file descriptor, /// however it is not safe in general to rely on this, as file descriptors may /// be unexpectedly allocated on other threads or in libraries. /// /// The `Mode` argument is only significant when creating a file. /// /// # References /// - [POSIX] /// - [Linux] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/openat.html /// [Linux]: https://man7.org/linux/man-pages/man2/open.2.html #[inline] pub fn openat( dirfd: Fd, path: P, oflags: OFlags, create_mode: Mode, ) -> io::Result { path.into_with_c_str(|path| imp::fs::syscalls::openat(dirfd.as_fd(), path, oflags, create_mode)) } /// `readlinkat(fd, path)`—Reads the contents of a symlink. /// /// If `reuse` is non-empty, reuse its buffer to store the result if possible. /// /// # References /// - [POSIX] /// - [Linux] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlinkat.html /// [Linux]: https://man7.org/linux/man-pages/man2/readlinkat.2.html #[inline] pub fn readlinkat>>( dirfd: Fd, path: P, reuse: B, ) -> io::Result { path.into_with_c_str(|path| _readlinkat(dirfd.as_fd(), path, reuse.into())) } fn _readlinkat(dirfd: BorrowedFd<'_>, path: &CStr, mut buffer: Vec) -> io::Result { // This code would benefit from having a better way to read into // uninitialized memory, but that requires `unsafe`. buffer.clear(); buffer.reserve(SMALL_PATH_BUFFER_SIZE); buffer.resize(buffer.capacity(), 0_u8); loop { let nread = imp::fs::syscalls::readlinkat(dirfd.as_fd(), path, &mut buffer)?; let nread = nread as usize; assert!(nread <= buffer.len()); if nread < buffer.len() { buffer.resize(nread, 0_u8); return Ok(CString::new(buffer).unwrap()); } buffer.reserve(1); // use `Vec` reallocation strategy to grow capacity exponentially buffer.resize(buffer.capacity(), 0_u8); } } /// `mkdirat(fd, path, mode)`—Creates a directory. /// /// # References /// - [POSIX] /// - [Linux] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdirat.html /// [Linux]: https://man7.org/linux/man-pages/man2/mkdirat.2.html #[inline] pub fn mkdirat(dirfd: Fd, path: P, mode: Mode) -> io::Result<()> { path.into_with_c_str(|path| imp::fs::syscalls::mkdirat(dirfd.as_fd(), path, mode)) } /// `linkat(old_dirfd, old_path, new_dirfd, new_path, flags)`—Creates a hard /// link. /// /// # References /// - [POSIX] /// - [Linux] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/linkat.html /// [Linux]: https://man7.org/linux/man-pages/man2/linkat.2.html #[inline] pub fn linkat( old_dirfd: PFd, old_path: P, new_dirfd: QFd, new_path: Q, flags: AtFlags, ) -> io::Result<()> { old_path.into_with_c_str(|old_path| { new_path.into_with_c_str(|new_path| { imp::fs::syscalls::linkat( old_dirfd.as_fd(), old_path, new_dirfd.as_fd(), new_path, flags, ) }) }) } /// `unlinkat(fd, path, flags)`—Unlinks a file or remove a directory. /// /// With the [`REMOVEDIR`] flag, this removes a directory. This is in place /// of a `rmdirat` function. /// /// # References /// - [POSIX] /// - [Linux] /// /// [`REMOVEDIR`]: AtFlags::REMOVEDIR /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlinkat.html /// [Linux]: https://man7.org/linux/man-pages/man2/unlinkat.2.html #[inline] pub fn unlinkat(dirfd: Fd, path: P, flags: AtFlags) -> io::Result<()> { path.into_with_c_str(|path| imp::fs::syscalls::unlinkat(dirfd.as_fd(), path, flags)) } /// `renameat(old_dirfd, old_path, new_dirfd, new_path)`—Renames a file or /// directory. /// /// # References /// - [POSIX] /// - [Linux] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/renameat.html /// [Linux]: https://man7.org/linux/man-pages/man2/renameat.2.html #[inline] pub fn renameat( old_dirfd: PFd, old_path: P, new_dirfd: QFd, new_path: Q, ) -> io::Result<()> { old_path.into_with_c_str(|old_path| { new_path.into_with_c_str(|new_path| { imp::fs::syscalls::renameat(old_dirfd.as_fd(), old_path, new_dirfd.as_fd(), new_path) }) }) } /// `renameat2(old_dirfd, old_path, new_dirfd, new_path, flags)`—Renames a /// file or directory. /// /// # References /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man2/renameat2.2.html #[cfg(any(target_os = "android", target_os = "linux"))] #[inline] #[doc(alias = "renameat2")] pub fn renameat_with( old_dirfd: PFd, old_path: P, new_dirfd: QFd, new_path: Q, flags: RenameFlags, ) -> io::Result<()> { old_path.into_with_c_str(|old_path| { new_path.into_with_c_str(|new_path| { imp::fs::syscalls::renameat2( old_dirfd.as_fd(), old_path, new_dirfd.as_fd(), new_path, flags, ) }) }) } /// `symlinkat(old_dirfd, old_path, new_dirfd, new_path)`—Creates a symlink. /// /// # References /// - [POSIX] /// - [Linux] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlinkat.html /// [Linux]: https://man7.org/linux/man-pages/man2/symlinkat.2.html #[inline] pub fn symlinkat( old_path: P, new_dirfd: Fd, new_path: Q, ) -> io::Result<()> { old_path.into_with_c_str(|old_path| { new_path.into_with_c_str(|new_path| { imp::fs::syscalls::symlinkat(old_path, new_dirfd.as_fd(), new_path) }) }) } /// `fstatat(dirfd, path, flags)`—Queries metadata for a file or directory. /// /// [`Mode::from_raw_mode`] and [`FileType::from_raw_mode`] may be used to /// interpret the `st_mode` field. /// /// # References /// - [POSIX] /// - [Linux] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatat.html /// [Linux]: https://man7.org/linux/man-pages/man2/fstatat.2.html /// [`Mode::from_raw_mode`]: crate::fs::Mode::from_raw_mode /// [`FileType::from_raw_mode`]: crate::fs::FileType::from_raw_mode #[inline] #[doc(alias = "fstatat")] pub fn statat(dirfd: Fd, path: P, flags: AtFlags) -> io::Result { path.into_with_c_str(|path| imp::fs::syscalls::statat(dirfd.as_fd(), path, flags)) } /// `faccessat(dirfd, path, access, flags)`—Tests permissions for a file or /// directory. /// /// # References /// - [POSIX] /// - [Linux] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/faccessat.html /// [Linux]: https://man7.org/linux/man-pages/man2/faccessat.2.html #[cfg(not(target_os = "illumos"))] #[inline] #[doc(alias = "faccessat")] pub fn accessat( dirfd: Fd, path: P, access: Access, flags: AtFlags, ) -> io::Result<()> { path.into_with_c_str(|path| imp::fs::syscalls::accessat(dirfd.as_fd(), path, access, flags)) } /// `utimensat(dirfd, path, times, flags)`—Sets file or directory timestamps. /// /// # References /// - [POSIX] /// - [Linux] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimensat.html /// [Linux]: https://man7.org/linux/man-pages/man2/utimensat.2.html #[inline] pub fn utimensat( dirfd: Fd, path: P, times: &Timestamps, flags: AtFlags, ) -> io::Result<()> { path.into_with_c_str(|path| imp::fs::syscalls::utimensat(dirfd.as_fd(), path, times, flags)) } /// `fchmodat(dirfd, path, mode, 0)`—Sets file or directory permissions. /// /// The flags argument is fixed to 0, so `AT_SYMLINK_NOFOLLOW` is not /// supported.
Platform support for this flag varies widely.
/// /// This implementation does not support `O_PATH` file descriptors, even on /// platforms where the host libc emulates it. /// /// # References /// - [POSIX] /// - [Linux] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html /// [Linux]: https://man7.org/linux/man-pages/man2/fchmodat.2.html #[cfg(not(target_os = "wasi"))] #[inline] #[doc(alias = "fchmodat")] pub fn chmodat(dirfd: Fd, path: P, mode: Mode) -> io::Result<()> { path.into_with_c_str(|path| imp::fs::syscalls::chmodat(dirfd.as_fd(), path, mode)) } /// `fclonefileat(src, dst_dir, dst, flags)`—Efficiently copies between files. /// /// # References /// - [Apple] /// /// [Apple]: https://opensource.apple.com/source/xnu/xnu-3789.21.4/bsd/man/man2/clonefile.2.auto.html #[cfg(any(target_os = "ios", target_os = "macos"))] #[inline] pub fn fclonefileat( src: Fd, dst_dir: DstFd, dst: P, flags: CloneFlags, ) -> io::Result<()> { dst.into_with_c_str(|dst| { imp::fs::syscalls::fclonefileat(src.as_fd(), dst_dir.as_fd(), &dst, flags) }) } /// `mknodat(dirfd, path, mode, dev)`—Creates special or normal files. /// /// # References /// - [POSIX] /// - [Linux] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/mknodat.html /// [Linux]: https://man7.org/linux/man-pages/man2/mknodat.2.html #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "wasi")))] #[inline] pub fn mknodat( dirfd: Fd, path: P, file_type: FileType, mode: Mode, dev: Dev, ) -> io::Result<()> { path.into_with_c_str(|path| { imp::fs::syscalls::mknodat(dirfd.as_fd(), path, file_type, mode, dev) }) } /// `fchownat(dirfd, path, owner, group, flags)`—Sets file or directory /// ownership. /// /// # References /// - [POSIX] /// - [Linux] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchownat.html /// [Linux]: https://man7.org/linux/man-pages/man2/fchownat.2.html #[cfg(not(any(target_os = "wasi")))] #[inline] pub fn chownat( dirfd: Fd, path: P, owner: Option, group: Option, flags: AtFlags, ) -> io::Result<()> { path.into_with_c_str(|path| { imp::fs::syscalls::chownat(dirfd.as_fd(), path, owner, group, flags) }) }