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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
use std::{borrow::Cow, env, path::PathBuf};
use bstr::{ByteSlice, ByteVec};
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error("Could not obtain the current working directory")]
CurrentDir(#[from] std::io::Error),
#[error("Relative path \"{}\"tries to reach beyond root filesystem", directory.display())]
InvalidInput { directory: PathBuf },
#[error("Failed to access a directory, or path is not a directory: '{}'", .path.display())]
InaccessibleDirectory { path: PathBuf },
#[error("Could find a git repository in '{}' or in any of its parents", .path.display())]
NoGitRepository { path: PathBuf },
#[error("Could find a git repository in '{}' or in any of its parents within ceiling height of {}", .path.display(), .ceiling_height)]
NoGitRepositoryWithinCeiling { path: PathBuf, ceiling_height: usize },
#[error("Could find a git repository in '{}' or in any of its parents within device limits below '{}'", .path.display(), .limit.display())]
NoGitRepositoryWithinFs { path: PathBuf, limit: PathBuf },
#[error("None of the passed ceiling directories prefixed the git-dir candidate, making them ineffective.")]
NoMatchingCeilingDir,
#[error("Could find a trusted git repository in '{}' or in any of its parents, candidate at '{}' discarded", .path.display(), .candidate.display())]
NoTrustedGitRepository {
path: PathBuf,
candidate: PathBuf,
required: git_sec::Trust,
},
#[error("Could not determine trust level for path '{}'.", .path.display())]
CheckTrust {
path: PathBuf,
#[source]
err: std::io::Error,
},
}
pub struct Options<'a> {
pub required_trust: git_sec::Trust,
pub ceiling_dirs: Vec<PathBuf>,
pub match_ceiling_dir_or_error: bool,
pub cross_fs: bool,
pub current_dir: Option<&'a std::path::Path>,
}
impl Default for Options<'_> {
fn default() -> Self {
Options {
required_trust: git_sec::Trust::Reduced,
ceiling_dirs: vec![],
match_ceiling_dir_or_error: true,
cross_fs: false,
current_dir: None,
}
}
}
impl Options<'_> {
pub fn apply_environment(mut self) -> Self {
let name = "GIT_CEILING_DIRECTORIES";
if let Some(ceiling_dirs) = env::var_os(name).and_then(|c| Vec::from_os_string(c).ok()) {
self.ceiling_dirs = parse_ceiling_dirs(&ceiling_dirs);
}
self
}
}
pub(crate) fn parse_ceiling_dirs(ceiling_dirs: &[u8]) -> Vec<PathBuf> {
let mut should_normalize = true;
let mut result = Vec::new();
for ceiling_dir in ceiling_dirs.split_str(":") {
if ceiling_dir.is_empty() {
should_normalize = false;
continue;
}
let mut dir = match ceiling_dir.to_path() {
Ok(dir) => Cow::Borrowed(dir),
Err(_) => continue,
};
if dir.is_relative() {
continue;
}
if should_normalize {
if let Ok(normalized) = git_path::realpath(&dir) {
dir = Cow::Owned(normalized);
}
}
result.push(dir.into_owned());
}
result
}
#[cfg(test)]
mod tests {
#[test]
#[cfg(unix)]
fn parse_ceiling_dirs_from_environment_format() -> std::io::Result<()> {
use std::{fs, os::unix::fs::symlink};
use super::*;
let dir = tempfile::tempdir().expect("success creating temp dir");
let direct_path = dir.path().join("direct");
let symlink_path = dir.path().join("symlink");
fs::create_dir(&direct_path)?;
symlink(&direct_path, &symlink_path)?;
let symlink_str = symlink_path.to_str().expect("symlink path is valid utf8");
let ceiling_dir_string = format!("{}:relative::{}", symlink_str, symlink_str);
let ceiling_dirs = parse_ceiling_dirs(ceiling_dir_string.as_bytes());
assert_eq!(ceiling_dirs.len(), 2, "Relative path is discarded");
assert_eq!(
ceiling_dirs[0],
symlink_path.canonicalize().expect("symlink path exists"),
"Symlinks are resolved"
);
assert_eq!(
ceiling_dirs[1], symlink_path,
"Symlink are not resolved after empty item"
);
dir.close()
}
}