standard_lib/fs/path/
rel.rs

1use std::ffi::CString;
2use std::marker::PhantomData;
3use std::mem::MaybeUninit;
4
5use libc::{EACCES, EBADF, EFAULT, EINVAL, ELOOP, ENAMETOOLONG, ENOENT, ENOMEM, ENOTDIR, EOVERFLOW, c_int, stat as Stat};
6
7use super::{Abs, OwnedPath, Path, PathState};
8use crate::fs::error::{ExcessiveLinksError, MetadataOverflowError, MissingComponentError, NoSearchError, NonDirComponentError, OOMError, PathLengthError};
9use crate::fs::file::MetadataError;
10use crate::fs::panic::{BadFdPanic, BadStackAddrPanic, InvalidOpPanic, Panic, UnexpectedErrorPanic};
11use crate::fs::{Directory, Metadata};
12use crate::fs::path::{PathError, PathOrMetadataError};
13use crate::util::{self, sealed::Sealed};
14
15#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Rel {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match *self {}
    }
}Debug)]
16pub enum Rel {}
17
18impl Sealed for Rel {}
19
20impl PathState for Rel {}
21
22impl OwnedPath<Rel> {
23    // TODO: This is necessary for some operations, but doesn't uphold OwnedPath's invariants.
24    pub(crate) unsafe fn dot_slash_dot() -> OwnedPath<Rel> {
25        OwnedPath::<Rel> {
26            _state: PhantomData,
27            bytes: b"/.".into(),
28        }
29    }
30
31    pub fn dot() -> OwnedPath<Rel> {
32        OwnedPath::<Rel> {
33            _state: PhantomData,
34            bytes: b"/".into(),
35        }
36    }
37
38    pub fn resolve_root(self) -> OwnedPath<Abs> {
39        let OwnedPath { bytes, .. } = self;
40        OwnedPath {
41            _state: PhantomData,
42            bytes
43        }
44    }
45}
46
47impl Path<Rel> {
48    // pub fn dot_slash() -> &'static Path<Rel> {
49    //     unsafe { Path::from_unchecked("/") }
50    // }
51
52    pub fn resolve(&self, mut target: OwnedPath<Abs>) -> OwnedPath<Abs> {
53        target.push(self);
54        target
55    }
56
57    pub fn resolve_root(&self) -> OwnedPath<Abs> {
58        self.resolve(OwnedPath::root())
59    }
60
61    pub fn resolve_home(&self) -> Option<OwnedPath<Abs>> {
62        Some(self.resolve(OwnedPath::home()?))
63    }
64
65    pub fn resolve_cwd(&self) -> Option<OwnedPath<Abs>> {
66        Some(self.resolve(OwnedPath::cwd()?))
67    }
68
69    // TODO: add relative methods which use the ..at syscalls and take a directory.
70
71    pub(crate) fn metadata_raw(&self, relative_to: Directory, flags: c_int) -> Result<Metadata, PathOrMetadataError> {
72        // FIXME: Copy here feels bad.
73        let pathname = CString::from(self.to_owned());
74
75        let mut raw_meta: MaybeUninit<Stat> = MaybeUninit::uninit();
76        // SAFETY:
77        // - *relative_to.fd is a valid, open directory file descriptor (guaranteed by Directory's
78        //   ownership of Fd).
79        // - pathname.as_ptr().add(1) is safe because Path<Rel> guarantees at least 1 byte (the
80        //   leading '/'), and CString adds a null terminator, so .add(1) points to either the next
81        //   path component or the null terminator. This skips the leading '/' so fstatat treats the
82        //   path as relative to the directory fd.
83        // - The resulting pointer is still valid, null-terminated, and has no interior null bytes.
84        // - raw_meta.as_mut_ptr() points to valid, properly aligned stack memory allocated for a
85        //   Stat structure. MaybeUninit allows passing uninitialized memory to fstatat, which will
86        //   initialize it.
87        // - flags is validated by the caller to be a valid combination of fstatat flags.
88        if unsafe { libc::fstatat(
89            *relative_to.fd,
90            // Skip the leading '/' so that the path is considered relative.
91            pathname.as_ptr().add(1).cast(),
92            raw_meta.as_mut_ptr(),
93            flags
94        ) } == -1 {
95            match util::fs::err_no() {
96                EACCES       => Err(PathError::from(NoSearchError))?,
97                EBADF        => BadFdPanic.panic(),
98                EFAULT       => BadStackAddrPanic.panic(),
99                EINVAL       => InvalidOpPanic.panic(),
100                ELOOP        => Err(PathError::from(ExcessiveLinksError))?,
101                ENAMETOOLONG => Err(PathError::from(PathLengthError))?,
102                ENOENT       => Err(PathError::from(MissingComponentError))?,
103                ENOMEM       => Err(MetadataError::from(OOMError))?,
104                ENOTDIR      => Err(PathError::from(NonDirComponentError))?,
105                EOVERFLOW    => Err(MetadataError::from(MetadataOverflowError))?,
106                e            => UnexpectedErrorPanic(e).panic(),
107            }
108        }
109        // SAFETY: fstatat either initializes raw_meta or returns an error and diverges.
110        let raw = unsafe { raw_meta.assume_init() };
111
112        Ok(Metadata::from_stat(raw))
113    }
114
115    pub fn metadata(&self, relative_to: Directory) -> Result<Metadata, PathOrMetadataError> {
116        self.metadata_raw(relative_to, 0)
117    }
118
119    pub fn metadata_no_follow(&self, relative_to: Directory) -> Result<Metadata, PathOrMetadataError> {
120        self.metadata_raw(relative_to, libc::AT_SYMLINK_NOFOLLOW)
121    }
122}