Skip to content

Commit ec7c118

Browse files
authored
Merge pull request #1261 from dtolnay/verifyrel
Verify relativized symlink path
2 parents f4453bc + dd5b1fa commit ec7c118

File tree

1 file changed

+66
-31
lines changed

1 file changed

+66
-31
lines changed

Diff for: gen/build/src/out.rs

+66-31
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ pub(crate) fn relative_symlink_file(
3636
let original = original.as_ref();
3737
let link = link.as_ref();
3838

39+
let parent_directory_error = prepare_parent_directory_for_symlink(link).err();
3940
let relativized = best_effort_relativize_symlink(original, link);
4041

41-
symlink_file(&relativized, original, link)
42+
symlink_file(&relativized, original, link, parent_directory_error)
4243
}
4344

4445
pub(crate) fn absolute_symlink_file(
@@ -48,7 +49,9 @@ pub(crate) fn absolute_symlink_file(
4849
let original = original.as_ref();
4950
let link = link.as_ref();
5051

51-
symlink_file(original, original, link)
52+
let parent_directory_error = prepare_parent_directory_for_symlink(link).err();
53+
54+
symlink_file(original, original, link, parent_directory_error)
5255
}
5356

5457
pub(crate) fn relative_symlink_dir(
@@ -58,20 +61,28 @@ pub(crate) fn relative_symlink_dir(
5861
let original = original.as_ref();
5962
let link = link.as_ref();
6063

64+
let parent_directory_error = prepare_parent_directory_for_symlink(link).err();
6165
let relativized = best_effort_relativize_symlink(original, link);
6266

63-
symlink_dir(&relativized, link)
67+
symlink_dir(&relativized, link, parent_directory_error)
6468
}
6569

66-
fn symlink_file(path_for_symlink: &Path, path_for_copy: &Path, link: &Path) -> Result<()> {
67-
let mut create_dir_error = None;
70+
fn prepare_parent_directory_for_symlink(link: &Path) -> fs::Result<()> {
6871
if fs::exists(link) {
6972
best_effort_remove(link);
73+
Ok(())
7074
} else {
7175
let parent = link.parent().unwrap();
72-
create_dir_error = fs::create_dir_all(parent).err();
76+
fs::create_dir_all(parent)
7377
}
78+
}
7479

80+
fn symlink_file(
81+
path_for_symlink: &Path,
82+
path_for_copy: &Path,
83+
link: &Path,
84+
parent_directory_error: Option<fs::Error>,
85+
) -> Result<()> {
7586
match paths::symlink_or_copy(path_for_symlink, path_for_copy, link) {
7687
// As long as symlink_or_copy succeeded, ignore any create_dir_all error.
7788
Ok(()) => Ok(()),
@@ -88,26 +99,22 @@ fn symlink_file(path_for_symlink: &Path, path_for_copy: &Path, link: &Path) -> R
8899
} else {
89100
// If create_dir_all and symlink_or_copy both failed, prefer the
90101
// first error.
91-
Err(Error::Fs(create_dir_error.unwrap_or(err)))
102+
Err(Error::Fs(parent_directory_error.unwrap_or(err)))
92103
}
93104
}
94105
}
95106
}
96107

97-
fn symlink_dir(path_for_symlink: &Path, link: &Path) -> Result<()> {
98-
let mut create_dir_error = None;
99-
if fs::exists(link) {
100-
best_effort_remove(link);
101-
} else {
102-
let parent = link.parent().unwrap();
103-
create_dir_error = fs::create_dir_all(parent).err();
104-
}
105-
108+
fn symlink_dir(
109+
path_for_symlink: &Path,
110+
link: &Path,
111+
parent_directory_error: Option<fs::Error>,
112+
) -> Result<()> {
106113
match fs::symlink_dir(path_for_symlink, link) {
107114
// As long as symlink_dir succeeded, ignore any create_dir_all error.
108115
Ok(()) => Ok(()),
109116
// If create_dir_all and symlink_dir both failed, prefer the first error.
110-
Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))),
117+
Err(err) => Err(Error::Fs(parent_directory_error.unwrap_or(err))),
111118
}
112119
}
113120

@@ -150,6 +157,34 @@ fn best_effort_relativize_symlink(original: impl AsRef<Path>, link: impl AsRef<P
150157
let original = original.as_ref();
151158
let link = link.as_ref();
152159

160+
let relative_path = match abstractly_relativize_symlink(original, link) {
161+
Some(relative_path) => relative_path,
162+
None => return original.to_path_buf(),
163+
};
164+
165+
// Sometimes "a/b/../c" refers to a different canonical location than "a/c".
166+
// This can happen if 'b' is a symlink. The '..' canonicalizes to the parent
167+
// directory of the symlink's target, not back to 'a'. In cxx-build's case
168+
// someone could be using `--target-dir` with a location containing such
169+
// symlinks.
170+
if let Ok(original_canonical) = original.canonicalize() {
171+
if let Ok(relative_canonical) = link.parent().unwrap().join(&relative_path).canonicalize() {
172+
if original_canonical == relative_canonical {
173+
return relative_path;
174+
}
175+
}
176+
}
177+
178+
original.to_path_buf()
179+
}
180+
181+
fn abstractly_relativize_symlink(
182+
original: impl AsRef<Path>,
183+
link: impl AsRef<Path>,
184+
) -> Option<PathBuf> {
185+
let original = original.as_ref();
186+
let link = link.as_ref();
187+
153188
// Relativization only makes sense if there is a semantically meaningful
154189
// base directory shared between the two paths.
155190
//
@@ -171,13 +206,13 @@ fn best_effort_relativize_symlink(original: impl AsRef<Path>, link: impl AsRef<P
171206
|| path_contains_intermediate_components(original)
172207
|| path_contains_intermediate_components(link)
173208
{
174-
return original.to_path_buf();
209+
return None;
175210
}
176211

177212
let (common_prefix, rest_of_original, rest_of_link) = split_after_common_prefix(original, link);
178213

179214
if common_prefix == Path::new("") {
180-
return original.to_path_buf();
215+
return None;
181216
}
182217

183218
let mut rest_of_link = rest_of_link.components();
@@ -190,7 +225,7 @@ fn best_effort_relativize_symlink(original: impl AsRef<Path>, link: impl AsRef<P
190225
path_to_common_prefix.push(Component::ParentDir);
191226
}
192227

193-
path_to_common_prefix.join(rest_of_original)
228+
Some(path_to_common_prefix.join(rest_of_original))
194229
}
195230

196231
fn path_contains_intermediate_components(path: impl AsRef<Path>) -> bool {
@@ -230,23 +265,23 @@ fn split_after_common_prefix<'first, 'second>(
230265

231266
#[cfg(test)]
232267
mod tests {
233-
use crate::out::best_effort_relativize_symlink;
268+
use crate::out::abstractly_relativize_symlink;
234269
use std::path::Path;
235270

236271
#[cfg(not(windows))]
237272
#[test]
238273
fn test_relativize_symlink_unix() {
239274
assert_eq!(
240-
best_effort_relativize_symlink("/foo/bar/baz", "/foo/spam/eggs"),
241-
Path::new("../bar/baz"),
275+
abstractly_relativize_symlink("/foo/bar/baz", "/foo/spam/eggs").as_deref(),
276+
Some(Path::new("../bar/baz")),
242277
);
243278
assert_eq!(
244-
best_effort_relativize_symlink("/foo/bar/../baz", "/foo/spam/eggs"),
245-
Path::new("/foo/bar/../baz"),
279+
abstractly_relativize_symlink("/foo/bar/../baz", "/foo/spam/eggs"),
280+
None,
246281
);
247282
assert_eq!(
248-
best_effort_relativize_symlink("/foo/bar/baz", "/foo/spam/./eggs"),
249-
Path::new("../bar/baz"),
283+
abstractly_relativize_symlink("/foo/bar/baz", "/foo/spam/./eggs").as_deref(),
284+
Some(Path::new("../bar/baz")),
250285
);
251286
}
252287

@@ -260,12 +295,12 @@ mod tests {
260295
let windows_different_volume_link = PathBuf::from_iter(["d:\\", "users", "link"]);
261296

262297
assert_eq!(
263-
best_effort_relativize_symlink(&windows_target, windows_link),
264-
Path::new("..\\windows\\foo"),
298+
abstractly_relativize_symlink(&windows_target, windows_link).as_deref(),
299+
Some(Path::new("..\\windows\\foo")),
265300
);
266301
assert_eq!(
267-
best_effort_relativize_symlink(&windows_target, windows_different_volume_link),
268-
windows_target,
302+
abstractly_relativize_symlink(&windows_target, windows_different_volume_link),
303+
None,
269304
);
270305
}
271306
}

0 commit comments

Comments
 (0)