Skip to content

Commit 1f87b6d

Browse files
committed
Lock unpacking instead of local registry tarball.
1 parent 5cd68f8 commit 1f87b6d

File tree

2 files changed

+48
-41
lines changed

2 files changed

+48
-41
lines changed

src/cargo/sources/registry/local.rs

+24-25
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ use hex;
1111

1212
pub struct LocalRegistry<'cfg> {
1313
index_path: Filesystem,
14-
cache_path: Filesystem,
1514
root: Filesystem,
15+
src_path: Filesystem,
1616
config: &'cfg Config,
1717
}
1818

1919
impl<'cfg> LocalRegistry<'cfg> {
2020
pub fn new(root: &Path, config: &'cfg Config, name: &str) -> LocalRegistry<'cfg> {
2121
LocalRegistry {
22-
cache_path: config.registry_cache_path().join(name),
22+
src_path: config.registry_source_path().join(name),
2323
index_path: Filesystem::new(root.join("index")),
2424
root: Filesystem::new(root.to_path_buf()),
2525
config,
@@ -70,39 +70,38 @@ impl<'cfg> RegistryData for LocalRegistry<'cfg> {
7070
}
7171

7272
fn download(&mut self, pkg: PackageId, checksum: &str) -> CargoResult<MaybeLock> {
73-
let filename = format!("{}-{}.crate", pkg.name(), pkg.version());
74-
75-
// Attempt to open an read-only lock first to avoid an exclusive write lock.
76-
//
77-
// If this fails then we fall through to the exclusive path where we copy
78-
// the file.
79-
if let Ok(dst) = self.cache_path.open_ro(&filename, self.config, &filename) {
80-
let meta = dst.file().metadata()?;
81-
if meta.len() > 0 {
82-
return Ok(MaybeLock::Ready(dst));
83-
}
73+
let crate_file = format!("{}-{}.crate", pkg.name(), pkg.version());
74+
let mut crate_file = self.root.open_ro(&crate_file, self.config, "crate file")?;
75+
76+
// If we've already got an unpacked version of this crate, then skip the
77+
// checksum below as it is in theory already verified.
78+
let dst = format!("{}-{}", pkg.name(), pkg.version());
79+
if self.src_path.join(dst).into_path_unlocked().exists() {
80+
return Ok(MaybeLock::Ready(crate_file));
8481
}
8582

8683
self.config.shell().status("Unpacking", pkg)?;
8784

88-
// Verify the checksum and copy over the .crate.
89-
let mut buf = Vec::new();
90-
let mut crate_file_source = self.root.open_ro(&filename, self.config, "crate file")?;
91-
let _ = crate_file_source
92-
.read_to_end(&mut buf)
93-
.chain_err(|| format!("failed to read `{}`", crate_file_source.path().display()))?;
94-
85+
// We don't actually need to download anything per-se, we just need to
86+
// verify the checksum matches the .crate file itself.
9587
let mut state = Sha256::new();
96-
state.update(&buf);
88+
let mut buf = [0; 64 * 1024];
89+
loop {
90+
let n = crate_file
91+
.read(&mut buf)
92+
.chain_err(|| format!("failed to read `{}`", crate_file.path().display()))?;
93+
if n == 0 {
94+
break;
95+
}
96+
state.update(&buf[..n]);
97+
}
9798
if hex::encode(state.finish()) != checksum {
9899
failure::bail!("failed to verify the checksum of `{}`", pkg)
99100
}
100101

101-
let mut dst = self.cache_path.open_rw(&filename, self.config, &filename)?;
102-
dst.write_all(&buf)?;
103-
dst.seek(SeekFrom::Start(0))?;
102+
crate_file.seek(SeekFrom::Start(0))?;
104103

105-
Ok(MaybeLock::Ready(dst))
104+
Ok(MaybeLock::Ready(crate_file))
106105
}
107106

108107
fn finish_download(

src/cargo/sources/registry/mod.rs

+24-16
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@
160160
161161
use std::borrow::Cow;
162162
use std::collections::BTreeMap;
163-
use std::fs::File;
163+
use std::io::Write;
164164
use std::path::{Path, PathBuf};
165165

166166
use flate2::read::GzDecoder;
@@ -426,23 +426,28 @@ impl<'cfg> RegistrySource<'cfg> {
426426
///
427427
/// No action is taken if the source looks like it's already unpacked.
428428
fn unpack_package(&self, pkg: PackageId, tarball: &FileLock) -> CargoResult<PathBuf> {
429-
let dst = self
430-
.src_path
431-
.join(&format!("{}-{}", pkg.name(), pkg.version()));
432-
dst.create_dir()?;
433-
// Note that we've already got the `tarball` locked above, and that
434-
// implies a lock on the unpacked destination as well, so this access
435-
// via `into_path_unlocked` should be ok.
436-
let dst = dst.into_path_unlocked();
437-
let ok = dst.join(".cargo-ok");
438-
if ok.exists() {
439-
return Ok(dst);
429+
// The `.cargo-ok` file is used to track if the source is already
430+
// unpacked and to lock the directory for unpacking.
431+
let mut ok = {
432+
let package_dir = format!("{}-{}", pkg.name(), pkg.version());
433+
let dst = self
434+
.src_path
435+
.join(&package_dir);
436+
dst.create_dir()?;
437+
dst.open_rw(".cargo-ok", self.config, &package_dir)?
438+
};
439+
let unpack_dir = ok.parent().to_path_buf();
440+
441+
// If the file has any data, assume the source is already unpacked.
442+
let meta = ok.file().metadata()?;
443+
if meta.len() > 0 {
444+
return Ok(unpack_dir);
440445
}
441446

442447
let gz = GzDecoder::new(tarball.file());
443448
let mut tar = Archive::new(gz);
444-
let prefix = dst.file_name().unwrap();
445-
let parent = dst.parent().unwrap();
449+
let prefix = unpack_dir.file_name().unwrap();
450+
let parent = unpack_dir.parent().unwrap();
446451
for entry in tar.entries()? {
447452
let mut entry = entry.chain_err(|| "failed to iterate over archive")?;
448453
let entry_path = entry
@@ -470,8 +475,11 @@ impl<'cfg> RegistrySource<'cfg> {
470475
.unpack_in(parent)
471476
.chain_err(|| format!("failed to unpack entry at `{}`", entry_path.display()))?;
472477
}
473-
File::create(&ok)?;
474-
Ok(dst)
478+
479+
// Write to the lock file to indicate that unpacking was successful.
480+
write!(ok, "ok")?;
481+
482+
Ok(unpack_dir)
475483
}
476484

477485
fn do_update(&mut self) -> CargoResult<()> {

0 commit comments

Comments
 (0)