Skip to content

Commit a1287b6

Browse files
committed
Auto merge of #958 - jelford:cache_downloaded_files, r=brson
Store downloaded files in a persistent directory until installation This PR should go some way to addressing #889 by caching downloaded components in a persistent directory until they have been installed. This way, if the download/install process is interrupted, the file that made it can be re-used. This should help ease the pain of intermittent connections.
2 parents d15ee14 + 2ce8d72 commit a1287b6

File tree

8 files changed

+256
-134
lines changed

8 files changed

+256
-134
lines changed

src/download/src/lib.rs

-15
Original file line numberDiff line numberDiff line change
@@ -47,21 +47,6 @@ pub fn download(url: &Url,
4747
Err("no working backends".into())
4848
}
4949

50-
pub fn download_to_path(url: &Url,
51-
path: &Path,
52-
callback: Option<&Fn(Event) -> Result<()>>)
53-
-> Result<()> {
54-
for &backend in BACKENDS {
55-
match download_to_path_with_backend(backend, url, path, callback) {
56-
Err(Error(ErrorKind::BackendUnavailable(_), _)) => (),
57-
Err(e) => return Err(e),
58-
Ok(()) => return Ok(()),
59-
}
60-
}
61-
62-
Err("no working backends".into())
63-
}
64-
6550
pub fn download_with_backend(backend: Backend,
6651
url: &Url,
6752
callback: &Fn(Event) -> Result<()>)

src/rustup-dist/src/dist.rs

+83-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ use manifest::Component;
88
use manifest::Manifest as ManifestV2;
99
use manifestation::{Manifestation, UpdateStatus, Changes};
1010

11-
use std::path::Path;
11+
use std::path::{Path, PathBuf};
12+
use std::ops;
13+
use url::Url;
1214
use std::fmt;
1315
use std::env;
16+
use std::fs;
1417

1518
use regex::Regex;
1619
use sha2::{Sha256, Digest};
@@ -482,9 +485,87 @@ pub fn download_and_check<'a>(url_str: &str,
482485
pub struct DownloadCfg<'a> {
483486
pub dist_root: &'a str,
484487
pub temp_cfg: &'a temp::Cfg,
488+
pub download_dir: &'a PathBuf,
485489
pub notify_handler: &'a Fn(Notification),
486490
}
487491

492+
493+
pub struct File {
494+
path: PathBuf,
495+
}
496+
497+
impl ops::Deref for File {
498+
type Target = Path;
499+
500+
fn deref(&self) -> &Path {
501+
ops::Deref::deref(&self.path)
502+
}
503+
}
504+
505+
impl<'a> DownloadCfg<'a> {
506+
507+
pub fn download(&self, url: &Url, hash: &str, notify_handler: &'a Fn(Notification)) -> Result<File> {
508+
509+
try!(utils::ensure_dir_exists("Download Directory", &self.download_dir, &|n| notify_handler(n.into())));
510+
let target_file = self.download_dir.join(Path::new(hash));
511+
512+
if target_file.exists() {
513+
let mut hasher = Sha256::new();
514+
use std::io::Read;
515+
let mut downloaded = try!(fs::File::open(&target_file).chain_err(|| "opening already downloaded file"));
516+
let mut buf = [0; 1024];
517+
loop {
518+
if let Ok(n) = downloaded.read(&mut buf) {
519+
if n == 0 { break; }
520+
hasher.input(&buf[..n]);
521+
} else {
522+
break;
523+
}
524+
}
525+
let cached_result = hasher.result_str();
526+
if hash == cached_result {
527+
notify_handler(Notification::FileAlreadyDownloaded);
528+
notify_handler(Notification::ChecksumValid(&url.to_string()));
529+
return Ok(File { path: target_file, });
530+
} else {
531+
notify_handler(Notification::CachedFileChecksumFailed);
532+
try!(fs::remove_file(&target_file).chain_err(|| "cleaning up previous download"));
533+
}
534+
}
535+
536+
let mut hasher = Sha256::new();
537+
538+
try!(utils::download_file(&url,
539+
&target_file,
540+
Some(&mut hasher),
541+
&|n| notify_handler(n.into())));
542+
543+
let actual_hash = hasher.result_str();
544+
545+
if hash != actual_hash {
546+
// Incorrect hash
547+
return Err(ErrorKind::ChecksumFailed {
548+
url: url.to_string(),
549+
expected: hash.to_string(),
550+
calculated: actual_hash,
551+
}.into());
552+
} else {
553+
notify_handler(Notification::ChecksumValid(&url.to_string()));
554+
return Ok(File { path: target_file, })
555+
}
556+
}
557+
558+
pub fn clean(&self, hashes: &Vec<String>) -> Result<()> {
559+
for hash in hashes.iter() {
560+
let used_file = self.download_dir.join(hash);
561+
if self.download_dir.join(&used_file).exists() {
562+
try!(fs::remove_file(used_file).chain_err(|| "cleaning up cached downloads"));
563+
}
564+
}
565+
Ok(())
566+
}
567+
}
568+
488569
pub fn download_hash(url: &str, cfg: DownloadCfg) -> Result<String> {
489570
let hash_url = try!(utils::parse_url(&(url.to_owned() + ".sha256")));
490571
let hash_file = try!(cfg.temp_cfg.new_file());
@@ -551,7 +632,7 @@ pub fn update_from_dist_<'a>(download: DownloadCfg<'a>,
551632
Ok(Some((m, hash))) => {
552633
return match try!(manifestation.update(&m,
553634
changes,
554-
&download.temp_cfg,
635+
&download,
555636
download.notify_handler.clone())) {
556637
UpdateStatus::Unchanged => Ok(None),
557638
UpdateStatus::Changed => Ok(Some(hash)),

src/rustup-dist/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ extern crate walkdir;
77
extern crate toml;
88
extern crate flate2;
99
extern crate tar;
10+
extern crate url;
1011
#[macro_use]
1112
extern crate rustup_utils;
1213
#[macro_use]

src/rustup-dist/src/manifestation.rs

+13-25
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
44
use config::Config;
55
use manifest::{Component, Manifest, TargetedPackage};
6-
use dist::{download_and_check, DownloadCfg, TargetTriple, DEFAULT_DIST_SERVER};
6+
use dist::{download_and_check, DownloadCfg, TargetTriple, DEFAULT_DIST_SERVER, File};
77
use component::{Components, Transaction, TarGzPackage, Package};
88
use temp;
99
use errors::*;
1010
use notifications::*;
1111
use rustup_utils::utils;
1212
use prefix::InstallPrefix;
13-
use sha2::{Sha256, Digest};
1413
use std::path::Path;
1514

1615
pub const DIST_MANIFEST: &'static str = "multirust-channel-manifest.toml";
@@ -73,10 +72,11 @@ impl Manifestation {
7372
pub fn update(&self,
7473
new_manifest: &Manifest,
7574
changes: Changes,
76-
temp_cfg: &temp::Cfg,
75+
download_cfg: &DownloadCfg,
7776
notify_handler: &Fn(Notification)) -> Result<UpdateStatus> {
7877

7978
// Some vars we're going to need a few times
79+
let temp_cfg = download_cfg.temp_cfg;
8080
let prefix = self.installation.prefix();
8181
let ref rel_installed_manifest_path = prefix.rel_manifest_file(DIST_MANIFEST);
8282
let ref installed_manifest_path = prefix.path().join(rel_installed_manifest_path);
@@ -125,7 +125,8 @@ impl Manifestation {
125125
let altered = temp_cfg.dist_server != DEFAULT_DIST_SERVER;
126126

127127
// Download component packages and validate hashes
128-
let mut things_to_install: Vec<(Component, temp::File)> = Vec::new();
128+
let mut things_to_install: Vec<(Component, File)> = Vec::new();
129+
let mut things_downloaded: Vec<String> = Vec::new();
129130
for (component, url, hash) in components_urls_and_hashes {
130131

131132
notify_handler(Notification::DownloadingComponent(&component.pkg,
@@ -137,32 +138,14 @@ impl Manifestation {
137138
url
138139
};
139140

140-
// Download each package to temp file
141-
let temp_file = try!(temp_cfg.new_file());
142141
let url_url = try!(utils::parse_url(&url));
143142

144-
let mut hasher = Sha256::new();
145-
try!(utils::download_file(&url_url,
146-
&temp_file,
147-
Some(&mut hasher),
148-
&|n| notify_handler(n.into())).chain_err(|| {
143+
let dowloaded_file = try!(download_cfg.download(&url_url, &hash, &notify_handler).chain_err(|| {
149144
ErrorKind::ComponentDownloadFailed(component.clone())
150145
}));
146+
things_downloaded.push(hash);
151147

152-
let actual_hash = hasher.result_str();
153-
154-
if hash != actual_hash {
155-
// Incorrect hash
156-
return Err(ErrorKind::ChecksumFailed {
157-
url: url,
158-
expected: hash,
159-
calculated: actual_hash,
160-
}.into());
161-
} else {
162-
notify_handler(Notification::ChecksumValid(&url));
163-
}
164-
165-
things_to_install.push((component, temp_file));
148+
things_to_install.push((component, dowloaded_file));
166149
}
167150

168151
// Begin transaction
@@ -226,6 +209,8 @@ impl Manifestation {
226209
// End transaction
227210
tx.commit();
228211

212+
try!(download_cfg.clean(&things_downloaded));
213+
229214
Ok(UpdateStatus::Changed)
230215
}
231216

@@ -315,8 +300,11 @@ impl Manifestation {
315300
&self.target_triple,
316301
Some(&self.target_triple)));
317302

303+
use std::path::PathBuf;
304+
let dld_dir = PathBuf::from("bogus");
318305
let dlcfg = DownloadCfg {
319306
dist_root: "bogus",
307+
download_dir: &dld_dir,
320308
temp_cfg: temp_cfg,
321309
notify_handler: notify_handler
322310
};

src/rustup-dist/src/notifications.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ pub enum Notification<'a> {
1818
NoUpdateHash(&'a Path),
1919
ChecksumValid(&'a str),
2020
SignatureValid(&'a str),
21+
FileAlreadyDownloaded,
22+
CachedFileChecksumFailed,
2123
RollingBack,
2224
ExtensionNotInstalled(&'a Component),
2325
NonFatalError(&'a Error),
@@ -48,6 +50,7 @@ impl<'a> Notification<'a> {
4850
Temp(ref n) => n.level(),
4951
Utils(ref n) => n.level(),
5052
ChecksumValid(_) | NoUpdateHash(_) |
53+
FileAlreadyDownloaded |
5154
DownloadingLegacyManifest => NotificationLevel::Verbose,
5255
Extracting(_, _) | SignatureValid(_) |
5356
DownloadingComponent(_, _, _) |
@@ -56,7 +59,7 @@ impl<'a> Notification<'a> {
5659
ManifestChecksumFailedHack |
5760
RollingBack | DownloadingManifest(_) => NotificationLevel::Info,
5861
CantReadUpdateHash(_) | ExtensionNotInstalled(_) |
59-
MissingInstalledComponent(_) => NotificationLevel::Warn,
62+
MissingInstalledComponent(_) | CachedFileChecksumFailed => NotificationLevel::Warn,
6063
NonFatalError(_) => NotificationLevel::Error,
6164
}
6265
}
@@ -80,6 +83,8 @@ impl<'a> Display for Notification<'a> {
8083
NoUpdateHash(path) => write!(f, "no update hash at: '{}'", path.display()),
8184
ChecksumValid(_) => write!(f, "checksum passed"),
8285
SignatureValid(_) => write!(f, "signature valid"),
86+
FileAlreadyDownloaded => write!(f, "reusing previously downloaded file"),
87+
CachedFileChecksumFailed => write!(f, "bad checksum for cached download"),
8388
RollingBack => write!(f, "rolling back changes"),
8489
ExtensionNotInstalled(c) => {
8590
write!(f, "extension '{}' was not installed", c.name())
@@ -106,4 +111,3 @@ impl<'a> Display for Notification<'a> {
106111
}
107112
}
108113
}
109-

0 commit comments

Comments
 (0)