Skip to content

Commit d6a734e

Browse files
committed
Auto merge of #11665 - ehuss:cargo-ok-truncated, r=epage
Handle .cargo-ok being truncated This fixes an issue where if the `.cargo-ok` file is truncated then Cargo will get stuck with being unable to extract the package. Creating the `.cargo-ok` file uses `create_new` which will fail if the file exists. If the file gets created, but there is a failure to flush it, or if the filesystem otherwise gets corrupted, then the `create_new` step will fail with `File exists`. The solution here is to delete the cache directory if the `.cargo-ok` file is truncated, as there may be some uncertainty of the validity of the cache. This also adds better error handling if there is some problem opening the `.cargo-ok` file instead of ignoring errors. Closes #11638
2 parents e84a792 + 5c160dd commit d6a734e

File tree

2 files changed

+58
-6
lines changed

2 files changed

+58
-6
lines changed

src/cargo/sources/registry/mod.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,12 @@ use std::borrow::Cow;
162162
use std::collections::BTreeMap;
163163
use std::collections::HashSet;
164164
use std::fs::{File, OpenOptions};
165-
use std::io::Write;
165+
use std::io::{self, Write};
166166
use std::path::{Path, PathBuf};
167167
use std::task::Poll;
168168

169169
use anyhow::Context as _;
170-
use cargo_util::paths::exclude_from_backups_and_indexing;
170+
use cargo_util::paths::{self, exclude_from_backups_and_indexing};
171171
use flate2::read::GzDecoder;
172172
use log::debug;
173173
use semver::Version;
@@ -619,15 +619,22 @@ impl<'cfg> RegistrySource<'cfg> {
619619
// unpacked.
620620
let package_dir = format!("{}-{}", pkg.name(), pkg.version());
621621
let dst = self.src_path.join(&package_dir);
622-
dst.create_dir()?;
623622
let path = dst.join(PACKAGE_SOURCE_LOCK);
624623
let path = self.config.assert_package_cache_locked(&path);
625624
let unpack_dir = path.parent().unwrap();
626-
if let Ok(meta) = path.metadata() {
627-
if meta.len() > 0 {
628-
return Ok(unpack_dir.to_path_buf());
625+
match path.metadata() {
626+
Ok(meta) if meta.len() > 0 => return Ok(unpack_dir.to_path_buf()),
627+
Ok(_meta) => {
628+
// The file appears to be corrupted. Perhaps it failed to flush,
629+
// or the filesystem was corrupted in some way. To be safe, let's
630+
// assume something is wrong and clear it and start over.
631+
log::warn!("unexpected length of {path:?}, clearing cache");
632+
paths::remove_dir_all(dst.as_path_unlocked())?;
629633
}
634+
Err(e) if e.kind() == io::ErrorKind::NotFound => {}
635+
Err(e) => anyhow::bail!("failed to access package completion {path:?}: {e}"),
630636
}
637+
dst.create_dir()?;
631638
let mut tar = {
632639
let size_limit = max_unpack_size(tarball.metadata()?.len());
633640
let gz = GzDecoder::new(tarball);

tests/testsuite/registry.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2866,3 +2866,48 @@ required by package `foo v0.1.0 ([ROOT]/foo)`
28662866
.with_status(101)
28672867
.run();
28682868
}
2869+
2870+
#[cargo_test]
2871+
fn corrupted_ok_overwritten() {
2872+
// Checks what happens if .cargo-ok gets truncated, such as if the file is
2873+
// created, but the flush/close is interrupted.
2874+
Package::new("bar", "1.0.0").publish();
2875+
let p = project()
2876+
.file(
2877+
"Cargo.toml",
2878+
r#"
2879+
[package]
2880+
name = "foo"
2881+
version = "0.1.0"
2882+
2883+
[dependencies]
2884+
bar = "1"
2885+
"#,
2886+
)
2887+
.file("src/lib.rs", "")
2888+
.build();
2889+
p.cargo("fetch")
2890+
.with_stderr(
2891+
"\
2892+
[UPDATING] `dummy-registry` index
2893+
[DOWNLOADING] crates ...
2894+
[DOWNLOADED] bar v1.0.0 (registry `dummy-registry`)
2895+
",
2896+
)
2897+
.run();
2898+
let ok = glob::glob(
2899+
paths::home()
2900+
.join(".cargo/registry/src/*/bar-1.0.0/.cargo-ok")
2901+
.to_str()
2902+
.unwrap(),
2903+
)
2904+
.unwrap()
2905+
.next()
2906+
.unwrap()
2907+
.unwrap();
2908+
// Simulate cargo being interrupted, or filesystem corruption.
2909+
fs::write(&ok, "").unwrap();
2910+
assert_eq!(fs::read_to_string(&ok).unwrap(), "");
2911+
p.cargo("fetch").with_stderr("").run();
2912+
assert_eq!(fs::read_to_string(&ok).unwrap(), "ok");
2913+
}

0 commit comments

Comments
 (0)