Skip to content

Commit 2478331

Browse files
committed
Auto merge of rust-lang#10214 - weihanglo:revert-10188-issue-9528, r=alexcrichton
Be resilient to most IO error and filesystem loop while walking dirs Let `PathSource::walk` be resilient to most IO errors and filesystem loop. This PR also - Add a test validating the resilience against filesystem loop to prevent regression. - Emit warning when filesystem loop found while walking the filesystem. This is the only way I can think of now to solve rust-lang#9528 Fixes rust-lang#10213.
2 parents dc6d847 + d92dcea commit 2478331

File tree

4 files changed

+32
-80
lines changed

4 files changed

+32
-80
lines changed

src/cargo/sources/path.rs

+19-5
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ impl<'cfg> PathSource<'cfg> {
340340
ret.extend(files.into_iter());
341341
}
342342
Err(..) => {
343-
PathSource::walk(&file_path, &mut ret, false, filter)?;
343+
self.walk(&file_path, &mut ret, false, filter)?;
344344
}
345345
}
346346
} else if filter(&file_path, is_dir) {
@@ -378,11 +378,12 @@ impl<'cfg> PathSource<'cfg> {
378378
filter: &mut dyn FnMut(&Path, bool) -> bool,
379379
) -> CargoResult<Vec<PathBuf>> {
380380
let mut ret = Vec::new();
381-
PathSource::walk(pkg.root(), &mut ret, true, filter)?;
381+
self.walk(pkg.root(), &mut ret, true, filter)?;
382382
Ok(ret)
383383
}
384384

385385
fn walk(
386+
&self,
386387
path: &Path,
387388
ret: &mut Vec<PathBuf>,
388389
is_root: bool,
@@ -420,9 +421,22 @@ impl<'cfg> PathSource<'cfg> {
420421
true
421422
});
422423
for entry in walkdir {
423-
let entry = entry?;
424-
if !entry.file_type().is_dir() {
425-
ret.push(entry.path().to_path_buf());
424+
match entry {
425+
Ok(entry) => {
426+
if !entry.file_type().is_dir() {
427+
ret.push(entry.into_path());
428+
}
429+
}
430+
Err(err) if err.loop_ancestor().is_some() => {
431+
self.config.shell().warn(err)?;
432+
}
433+
Err(err) => match err.path() {
434+
// If the error occurs with a path, simply recover from it.
435+
// Don't worry about error skipping here, the callers would
436+
// still hit the IO error if they do access it thereafter.
437+
Some(path) => ret.push(path.to_path_buf()),
438+
None => return Err(err.into()),
439+
},
426440
}
427441
}
428442

tests/testsuite/build.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -1668,7 +1668,7 @@ package `test v0.0.0 ([CWD])`
16681668
}
16691669

16701670
#[cargo_test]
1671-
/// Make sure broken symlinks don't break the build
1671+
/// Make sure broken and loop symlinks don't break the build
16721672
///
16731673
/// This test requires you to be able to make symlinks.
16741674
/// For windows, this may require you to enable developer mode.
@@ -1681,9 +1681,17 @@ fn ignore_broken_symlinks() {
16811681
.file("Cargo.toml", &basic_bin_manifest("foo"))
16821682
.file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
16831683
.symlink("Notafile", "bar")
1684+
// To hit the symlink directory, we need a build script
1685+
// to trigger a full scan of package files.
1686+
.file("build.rs", &main_file(r#""build script""#, &[]))
1687+
.symlink_dir("a/b", "a/b/c/d/foo")
16841688
.build();
16851689

1686-
p.cargo("build").run();
1690+
p.cargo("build")
1691+
.with_stderr_contains(
1692+
"[WARNING] File system loop found: [..]/a/b/c/d/foo points to an ancestor [..]/a/b",
1693+
)
1694+
.run();
16871695
assert!(p.bin("foo").is_file());
16881696

16891697
p.process(&p.bin("foo")).with_stdout("i am foo\n").run();

tests/testsuite/build_script.rs

-69
Original file line numberDiff line numberDiff line change
@@ -4618,75 +4618,6 @@ fn links_interrupted_can_restart() {
46184618
.run();
46194619
}
46204620

4621-
#[cargo_test]
4622-
#[cfg(unix)]
4623-
fn build_script_scan_eacces() {
4624-
// build.rs causes a scan of the whole project, which can be a problem if
4625-
// a directory is not accessible.
4626-
use cargo_test_support::git;
4627-
use std::os::unix::fs::PermissionsExt;
4628-
4629-
let p = project()
4630-
.file("src/lib.rs", "")
4631-
.file("build.rs", "fn main() {}")
4632-
.file("secrets/stuff", "")
4633-
.build();
4634-
let path = p.root().join("secrets");
4635-
fs::set_permissions(&path, fs::Permissions::from_mode(0o0)).unwrap();
4636-
// The last "Caused by" is a string from libc such as the following:
4637-
// Permission denied (os error 13)
4638-
p.cargo("build")
4639-
.with_stderr(
4640-
"\
4641-
[ERROR] failed to determine package fingerprint for build script for foo v0.0.1 ([..]/foo)
4642-
An I/O error happened[..]
4643-
4644-
By default[..]
4645-
it to[..]
4646-
file[..]
4647-
See[..]
4648-
4649-
Caused by:
4650-
failed to determine the most recently modified file in [..]/foo
4651-
4652-
Caused by:
4653-
failed to determine list of files in [..]/foo
4654-
4655-
Caused by:
4656-
IO error for operation on [..]/foo/secrets: [..]
4657-
4658-
Caused by:
4659-
[..]
4660-
",
4661-
)
4662-
.with_status(101)
4663-
.run();
4664-
4665-
// Try `package.exclude` to skip a directory.
4666-
p.change_file(
4667-
"Cargo.toml",
4668-
r#"
4669-
[package]
4670-
name = "foo"
4671-
version = "0.0.1"
4672-
exclude = ["secrets"]
4673-
"#,
4674-
);
4675-
p.cargo("build").run();
4676-
4677-
// Try with git. This succeeds because the git status walker ignores
4678-
// directories it can't access.
4679-
p.change_file("Cargo.toml", &basic_manifest("foo", "0.0.1"));
4680-
p.build_dir().rm_rf();
4681-
let repo = git::init(&p.root());
4682-
git::add(&repo);
4683-
git::commit(&repo);
4684-
p.cargo("build").run();
4685-
4686-
// Restore permissions so that the directory can be deleted.
4687-
fs::set_permissions(&path, fs::Permissions::from_mode(0o755)).unwrap();
4688-
}
4689-
46904621
#[cargo_test]
46914622
fn dev_dep_with_links() {
46924623
let p = project()

tests/testsuite/package.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -794,10 +794,10 @@ fn broken_symlink() {
794794
.with_status(101)
795795
.with_stderr_contains(
796796
"\
797-
error: failed to determine list of files [..]/foo
797+
[ERROR] failed to prepare local package for uploading
798798
799799
Caused by:
800-
IO error for operation on [..]/foo/src/foo.rs: [..]
800+
failed to open for archiving: `[..]foo.rs`
801801
802802
Caused by:
803803
[..]
@@ -841,9 +841,8 @@ fn filesystem_loop() {
841841
.symlink_dir("a/b", "a/b/c/d/foo")
842842
.build()
843843
.cargo("package -v")
844-
.with_status(101)
845844
.with_stderr_contains(
846-
" File system loop found: [..]/a/b/c/d/foo points to an ancestor [..]/a/b",
845+
"[WARNING] File system loop found: [..]/a/b/c/d/foo points to an ancestor [..]/a/b",
847846
)
848847
.run();
849848
}

0 commit comments

Comments
 (0)