Skip to content

Commit af465ec

Browse files
authored
fix(command-vendor): strip_prefix panic in cp_sources method (#16214)
### What does this PR try to resolve? When executing the `cargo vendor` command, there is a probability that mv pkg fails, causing `strip_prefix` to panic. This PR aims to fix this issue. Fix: #15875 ### How to test and review this PR? Modify the code to make the move pkg action inevitably fail. Then rebuild cargo and execute `cargo vendor` to test it.
2 parents 5fe481d + 39300ea commit af465ec

File tree

2 files changed

+63
-7
lines changed

2 files changed

+63
-7
lines changed

src/cargo/ops/vendor.rs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use std::collections::HashSet;
2020
use std::collections::{BTreeMap, BTreeSet, HashMap};
2121
use std::ffi::OsStr;
2222
use std::fs::{self, File, OpenOptions};
23-
use std::io::{Read, Write};
23+
use std::io::{self, Read, Write};
2424
use std::path::{Path, PathBuf};
2525

2626
pub struct VendorOptions<'a> {
@@ -291,14 +291,37 @@ fn sync(
291291
.tempdir_in(vendor_dir)?;
292292
let unpacked_src =
293293
registry.unpack_package_in(id, staging_dir.path(), &vendor_this)?;
294-
if let Err(e) = fs::rename(&unpacked_src, &dst) {
295-
// This fallback is mainly for Windows 10 versions earlier than 1607.
296-
// The destination of `fs::rename` can't be a directory in older versions.
297-
// Can be removed once the minimal supported Windows version gets bumped.
294+
295+
let rename_result = if gctx
296+
.get_env_os("__CARGO_TEST_VENDOR_FALLBACK_CP_SOURCES")
297+
.is_some()
298+
{
299+
Err(io::Error::new(
300+
io::ErrorKind::Other,
301+
"simulated rename error for testing",
302+
))
303+
} else {
304+
fs::rename(&unpacked_src, &dst)
305+
};
306+
307+
if let Err(e) = rename_result {
308+
// This fallback is worked for sometimes `fs::rename` failed in a specific situation, such as:
309+
// - In Windows 10 versions earlier than 1607, the destination of `fs::rename` can't be a directory in older versions.
310+
// - `from` and `to` are on separate filesystems.
311+
// - AntiVirus or our system indexer are doing stuf simutaneously.
312+
// - Any other reasons documented in std::fs::rename.
298313
tracing::warn!("failed to `mv {unpacked_src:?} {dst:?}`: {e}");
299314
let paths: Vec<_> = walkdir(&unpacked_src).map(|e| e.into_path()).collect();
300-
cp_sources(pkg, src, &paths, &dst, &mut file_cksums, &mut tmp_buf, gctx)
301-
.with_context(|| format!("failed to copy vendored sources for {id}"))?;
315+
cp_sources(
316+
pkg,
317+
&unpacked_src,
318+
&paths,
319+
&dst,
320+
&mut file_cksums,
321+
&mut tmp_buf,
322+
gctx,
323+
)
324+
.with_context(|| format!("failed to copy vendored sources for {id}"))?;
302325
} else {
303326
compute_file_cksums(&dst)?;
304327
}

tests/testsuite/vendor.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2086,3 +2086,36 @@ Caused by:
20862086
"#]])
20872087
.run();
20882088
}
2089+
2090+
#[cargo_test]
2091+
fn vendor_rename_fallback() {
2092+
let p = project()
2093+
.file(
2094+
"Cargo.toml",
2095+
r#"
2096+
[package]
2097+
name = "foo"
2098+
version = "0.1.0"
2099+
2100+
[dependencies]
2101+
log = "0.3.5"
2102+
"#,
2103+
)
2104+
.file("src/lib.rs", "")
2105+
.build();
2106+
2107+
Package::new("log", "0.3.5").publish();
2108+
2109+
p.cargo("vendor --respect-source-config --no-delete")
2110+
.env("CARGO_LOG", "cargo::ops::vendor=warn")
2111+
.env("__CARGO_TEST_VENDOR_FALLBACK_CP_SOURCES", "true")
2112+
.with_status(0)
2113+
.with_stderr_data(str![[r#"
2114+
...
2115+
[..]failed to `mv "[..]vendor[..].vendor-staging[..]log-0.3.5" "[..]vendor[..]log"`: simulated rename error for testing
2116+
...
2117+
"#]])
2118+
.run();
2119+
2120+
assert!(p.root().join("vendor/log/Cargo.toml").exists());
2121+
}

0 commit comments

Comments
 (0)