standard_lib/fs/path/
rel.rs

1use std::ffi::{CString, OsStr};
2use std::marker::PhantomData;
3use std::mem::MaybeUninit;
4
5use libc::{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(Debug)]
16pub enum Rel {}
17
18impl Sealed for Rel {}
19
20impl PathState for Rel {}
21
22impl OwnedPath<Rel> {
23    pub fn resolve_root(self) -> OwnedPath<Abs> {
24        let OwnedPath { _state, inner } = self;
25        OwnedPath {
26            _state: PhantomData,
27            inner
28        }
29    }
30}
31
32impl Path<Rel> {
33    pub fn resolve(&self, mut target: OwnedPath<Abs>) -> OwnedPath<Abs> {
34        target.push(self);
35        target
36    }
37
38    pub fn resolve_root(&self) -> OwnedPath<Abs> {
39        self.resolve(OwnedPath::root())
40    }
41
42    pub fn resolve_home(&self) -> Option<OwnedPath<Abs>> {
43        Some(self.resolve(OwnedPath::home()?))
44    }
45
46    pub fn resolve_cwd(&self) -> Option<OwnedPath<Abs>> {
47        Some(self.resolve(OwnedPath::cwd()?))
48    }
49
50    // TODO: add relative methods which use the ..at syscalls and take a directory.
51
52    pub(crate) fn metadata_raw(&self, relative_to: Directory, flags: c_int) -> Result<Metadata, PathOrMetadataError> {
53        // FIXME: Copy here feels bad.
54        let pathname = CString::from(self.to_owned());
55
56        let mut raw_meta: MaybeUninit<Stat> = MaybeUninit::uninit();
57        if unsafe { libc::fstatat(
58            *relative_to.fd,
59            // Skip the leading '/' so that the path is considered relative.
60            pathname.as_ptr().add(1).cast(),
61            raw_meta.as_mut_ptr(),
62            flags
63        ) } == -1 {
64            match util::fs::err_no() {
65                libc::EACCES => Err(PathError::from(NoSearchError))?,
66                libc::EBADF => BadFdPanic.panic(),
67                libc::EFAULT => BadStackAddrPanic.panic(),
68                libc::EINVAL => InvalidOpPanic.panic(),
69                libc::ELOOP => Err(PathError::from(ExcessiveLinksError))?,
70                libc::ENAMETOOLONG => Err(PathError::from(PathLengthError))?,
71                libc::ENOENT => Err(PathError::from(MissingComponentError))?,
72                libc::ENOMEM => Err(MetadataError::from(OOMError))?,
73                libc::ENOTDIR => Err(PathError::from(NonDirComponentError))?,
74                libc::EOVERFLOW => Err(MetadataError::from(MetadataOverflowError))?,
75                e => UnexpectedErrorPanic(e).panic(),
76            }
77        }
78        // SAFETY: stat either initializes raw_meta or returns an error and diverges.
79        let raw = unsafe { raw_meta.assume_init() };
80
81        Ok(Metadata::from_stat(raw))
82    }
83
84    pub fn metadata(&self, relative_to: Directory) -> Result<Metadata, PathOrMetadataError> {
85        self.metadata_raw(relative_to, 0)
86    }
87
88    pub fn metadata_no_follow(&self, relative_to: Directory) -> Result<Metadata, PathOrMetadataError> {
89        self.metadata_raw(relative_to, libc::AT_SYMLINK_NOFOLLOW)
90    }
91}
92
93impl<O: AsRef<OsStr>> From<O> for OwnedPath<Rel> {
94    fn from(value: O) -> Self {
95        Self::from_os_str_sanitized(value.as_ref())
96    }
97}