standard_lib/fs/path/
rel.rs

1use std::ffi::{CString, OsString};
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(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            inner: OsString::from("/."),
28        }
29    }
30
31    pub fn dot() -> OwnedPath<Rel> {
32        OwnedPath::<Rel> {
33            _state: PhantomData,
34            inner: OsString::from("/"),
35        }
36    }
37
38    pub fn resolve_root(self) -> OwnedPath<Abs> {
39        let OwnedPath { _state, inner } = self;
40        OwnedPath {
41            _state: PhantomData,
42            inner
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        if unsafe { libc::fstatat(
77            *relative_to.fd,
78            // Skip the leading '/' so that the path is considered relative.
79            pathname.as_ptr().add(1).cast(),
80            raw_meta.as_mut_ptr(),
81            flags
82        ) } == -1 {
83            match util::fs::err_no() {
84                EACCES       => Err(PathError::from(NoSearchError))?,
85                EBADF        => BadFdPanic.panic(),
86                EFAULT       => BadStackAddrPanic.panic(),
87                EINVAL       => InvalidOpPanic.panic(),
88                ELOOP        => Err(PathError::from(ExcessiveLinksError))?,
89                ENAMETOOLONG => Err(PathError::from(PathLengthError))?,
90                ENOENT       => Err(PathError::from(MissingComponentError))?,
91                ENOMEM       => Err(MetadataError::from(OOMError))?,
92                ENOTDIR      => Err(PathError::from(NonDirComponentError))?,
93                EOVERFLOW    => Err(MetadataError::from(MetadataOverflowError))?,
94                e            => UnexpectedErrorPanic(e).panic(),
95            }
96        }
97        // SAFETY: stat either initializes raw_meta or returns an error and diverges.
98        let raw = unsafe { raw_meta.assume_init() };
99
100        Ok(Metadata::from_stat(raw))
101    }
102
103    pub fn metadata(&self, relative_to: Directory) -> Result<Metadata, PathOrMetadataError> {
104        self.metadata_raw(relative_to, 0)
105    }
106
107    pub fn metadata_no_follow(&self, relative_to: Directory) -> Result<Metadata, PathOrMetadataError> {
108        self.metadata_raw(relative_to, libc::AT_SYMLINK_NOFOLLOW)
109    }
110}