1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//! Module for environments abstractions.

use std::{
    convert::From,
    env::{self, VarError},
    error::Error as StdError,
    fmt::{self, Display},
    io::Error as IoError,
    mem::MaybeUninit,
    os::raw::c_char,
    path::PathBuf,
};

use libc::stat;

type Result<T> = std::result::Result<T, Error>;

/// Possible errors when calling this module functions.
#[derive(Debug)]
pub enum Error {
    Var(VarError),
    Io(IoError),
}

impl Display for Error {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Var(err) => write!(f, "Failed to get variable: {}", err),
            Self::Io(err) => write!(f, "IO error: {}", err),
        }
    }
}

impl From<VarError> for Error {
    #[inline]
    fn from(err: VarError) -> Self { Self::Var(err) }
}

impl From<IoError> for Error {
    #[inline]
    fn from(err: IoError) -> Self { Self::Io(err) }
}

impl StdError for Error {
    #[inline]
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        match self {
            Self::Var(err) => Some(err),
            Self::Io(err) => Some(err),
        }
    }
}

/// Get the logical path of the current directory.
///
/// # Errors
/// If the functions encounters any form of [`VarError`] when getting environment variable
/// or if a call inside set a errno (I/O Error), an error variant will be returned.
pub fn current_dir_logical() -> Result<PathBuf> {
    let pwd = env::var("PWD")?;

    // Same as pwd, but null terminated
    let pwd_null = {
        let mut s = String::new();
        s.push_str(&pwd);
        s.push('\0');
        s
    };

    let (mut logical, mut physical) = (MaybeUninit::uninit(), MaybeUninit::uninit());

    // Validity check
    // if we can get both fisical and logical paths stat, check they are the same inode
    if pwd.starts_with('/') {
        let stat1 = unsafe { stat(pwd_null.as_ptr() as *const c_char, logical.as_mut_ptr()) == 0 };
        let stat2 = unsafe { stat(".\0".as_ptr() as *const c_char, physical.as_mut_ptr()) == 0 };

        let (logical, physical) = unsafe { (logical.assume_init(), physical.assume_init()) };

        if stat1 && stat2 && logical.st_ino == physical.st_ino {
            return Ok(PathBuf::from(pwd));
        }
    }
    Err(Error::Io(IoError::last_os_error()))
}