Skip to content

Store downloaded files in a persistent directory until installation #958

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions src/download/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,6 @@ pub fn download(url: &Url,
Err("no working backends".into())
}

pub fn download_to_path(url: &Url,
path: &Path,
callback: Option<&Fn(Event) -> Result<()>>)
-> Result<()> {
for &backend in BACKENDS {
match download_to_path_with_backend(backend, url, path, callback) {
Err(Error(ErrorKind::BackendUnavailable(_), _)) => (),
Err(e) => return Err(e),
Ok(()) => return Ok(()),
}
}

Err("no working backends".into())
}

pub fn download_with_backend(backend: Backend,
url: &Url,
callback: &Fn(Event) -> Result<()>)
Expand Down
85 changes: 83 additions & 2 deletions src/rustup-dist/src/dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ use manifest::Component;
use manifest::Manifest as ManifestV2;
use manifestation::{Manifestation, UpdateStatus, Changes};

use std::path::Path;
use std::path::{Path, PathBuf};
use std::ops;
use url::Url;
use std::fmt;
use std::env;
use std::fs;

use regex::Regex;
use sha2::{Sha256, Digest};
Expand Down Expand Up @@ -482,9 +485,87 @@ pub fn download_and_check<'a>(url_str: &str,
pub struct DownloadCfg<'a> {
pub dist_root: &'a str,
pub temp_cfg: &'a temp::Cfg,
pub download_dir: &'a PathBuf,
pub notify_handler: &'a Fn(Notification),
}


pub struct File {
path: PathBuf,
}

impl ops::Deref for File {
type Target = Path;

fn deref(&self) -> &Path {
ops::Deref::deref(&self.path)
}
}

impl<'a> DownloadCfg<'a> {

pub fn download(&self, url: &Url, hash: &str, notify_handler: &'a Fn(Notification)) -> Result<File> {

try!(utils::ensure_dir_exists("Download Directory", &self.download_dir, &|n| notify_handler(n.into())));
let target_file = self.download_dir.join(Path::new(hash));

if target_file.exists() {
let mut hasher = Sha256::new();
use std::io::Read;
let mut downloaded = try!(fs::File::open(&target_file).chain_err(|| "opening already downloaded file"));
let mut buf = [0; 1024];
loop {
if let Ok(n) = downloaded.read(&mut buf) {
if n == 0 { break; }
hasher.input(&buf[..n]);
} else {
break;
}
}
let cached_result = hasher.result_str();
if hash == cached_result {
notify_handler(Notification::FileAlreadyDownloaded);
notify_handler(Notification::ChecksumValid(&url.to_string()));
return Ok(File { path: target_file, });
} else {
notify_handler(Notification::CachedFileChecksumFailed);
try!(fs::remove_file(&target_file).chain_err(|| "cleaning up previous download"));
}
}

let mut hasher = Sha256::new();

try!(utils::download_file(&url,
&target_file,
Some(&mut hasher),
&|n| notify_handler(n.into())));

let actual_hash = hasher.result_str();

if hash != actual_hash {
// Incorrect hash
return Err(ErrorKind::ChecksumFailed {
url: url.to_string(),
expected: hash.to_string(),
calculated: actual_hash,
}.into());
} else {
notify_handler(Notification::ChecksumValid(&url.to_string()));
return Ok(File { path: target_file, })
}
}

pub fn clean(&self, hashes: &Vec<String>) -> Result<()> {
for hash in hashes.iter() {
let used_file = self.download_dir.join(hash);
if self.download_dir.join(&used_file).exists() {
try!(fs::remove_file(used_file).chain_err(|| "cleaning up cached downloads"));
}
}
Ok(())
}
}

pub fn download_hash(url: &str, cfg: DownloadCfg) -> Result<String> {
let hash_url = try!(utils::parse_url(&(url.to_owned() + ".sha256")));
let hash_file = try!(cfg.temp_cfg.new_file());
Expand Down Expand Up @@ -551,7 +632,7 @@ pub fn update_from_dist_<'a>(download: DownloadCfg<'a>,
Ok(Some((m, hash))) => {
return match try!(manifestation.update(&m,
changes,
&download.temp_cfg,
&download,
download.notify_handler.clone())) {
UpdateStatus::Unchanged => Ok(None),
UpdateStatus::Changed => Ok(Some(hash)),
Expand Down
1 change: 1 addition & 0 deletions src/rustup-dist/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ extern crate walkdir;
extern crate toml;
extern crate flate2;
extern crate tar;
extern crate url;
#[macro_use]
extern crate rustup_utils;
#[macro_use]
Expand Down
38 changes: 13 additions & 25 deletions src/rustup-dist/src/manifestation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@

use config::Config;
use manifest::{Component, Manifest, TargetedPackage};
use dist::{download_and_check, DownloadCfg, TargetTriple, DEFAULT_DIST_SERVER};
use dist::{download_and_check, DownloadCfg, TargetTriple, DEFAULT_DIST_SERVER, File};
use component::{Components, Transaction, TarGzPackage, Package};
use temp;
use errors::*;
use notifications::*;
use rustup_utils::utils;
use prefix::InstallPrefix;
use sha2::{Sha256, Digest};
use std::path::Path;

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

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

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

notify_handler(Notification::DownloadingComponent(&component.pkg,
Expand All @@ -137,32 +138,14 @@ impl Manifestation {
url
};

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

let mut hasher = Sha256::new();
try!(utils::download_file(&url_url,
&temp_file,
Some(&mut hasher),
&|n| notify_handler(n.into())).chain_err(|| {
let dowloaded_file = try!(download_cfg.download(&url_url, &hash, &notify_handler).chain_err(|| {
ErrorKind::ComponentDownloadFailed(component.clone())
}));
things_downloaded.push(hash);

let actual_hash = hasher.result_str();

if hash != actual_hash {
// Incorrect hash
return Err(ErrorKind::ChecksumFailed {
url: url,
expected: hash,
calculated: actual_hash,
}.into());
} else {
notify_handler(Notification::ChecksumValid(&url));
}

things_to_install.push((component, temp_file));
things_to_install.push((component, dowloaded_file));
}

// Begin transaction
Expand Down Expand Up @@ -226,6 +209,8 @@ impl Manifestation {
// End transaction
tx.commit();

try!(download_cfg.clean(&things_downloaded));

Ok(UpdateStatus::Changed)
}

Expand Down Expand Up @@ -315,8 +300,11 @@ impl Manifestation {
&self.target_triple,
Some(&self.target_triple)));

use std::path::PathBuf;
let dld_dir = PathBuf::from("bogus");
let dlcfg = DownloadCfg {
dist_root: "bogus",
download_dir: &dld_dir,
temp_cfg: temp_cfg,
notify_handler: notify_handler
};
Expand Down
8 changes: 6 additions & 2 deletions src/rustup-dist/src/notifications.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub enum Notification<'a> {
NoUpdateHash(&'a Path),
ChecksumValid(&'a str),
SignatureValid(&'a str),
FileAlreadyDownloaded,
CachedFileChecksumFailed,
RollingBack,
ExtensionNotInstalled(&'a Component),
NonFatalError(&'a Error),
Expand Down Expand Up @@ -48,6 +50,7 @@ impl<'a> Notification<'a> {
Temp(ref n) => n.level(),
Utils(ref n) => n.level(),
ChecksumValid(_) | NoUpdateHash(_) |
FileAlreadyDownloaded |
DownloadingLegacyManifest => NotificationLevel::Verbose,
Extracting(_, _) | SignatureValid(_) |
DownloadingComponent(_, _, _) |
Expand All @@ -56,7 +59,7 @@ impl<'a> Notification<'a> {
ManifestChecksumFailedHack |
RollingBack | DownloadingManifest(_) => NotificationLevel::Info,
CantReadUpdateHash(_) | ExtensionNotInstalled(_) |
MissingInstalledComponent(_) => NotificationLevel::Warn,
MissingInstalledComponent(_) | CachedFileChecksumFailed => NotificationLevel::Warn,
NonFatalError(_) => NotificationLevel::Error,
}
}
Expand All @@ -80,6 +83,8 @@ impl<'a> Display for Notification<'a> {
NoUpdateHash(path) => write!(f, "no update hash at: '{}'", path.display()),
ChecksumValid(_) => write!(f, "checksum passed"),
SignatureValid(_) => write!(f, "signature valid"),
FileAlreadyDownloaded => write!(f, "reusing previously downloaded file"),
CachedFileChecksumFailed => write!(f, "bad checksum for cached download"),
RollingBack => write!(f, "rolling back changes"),
ExtensionNotInstalled(c) => {
write!(f, "extension '{}' was not installed", c.name())
Expand All @@ -106,4 +111,3 @@ impl<'a> Display for Notification<'a> {
}
}
}

Loading