diff --git a/src/rustup-dist/src/dist.rs b/src/rustup-dist/src/dist.rs index 8e4165116f..a833ef85ea 100644 --- a/src/rustup-dist/src/dist.rs +++ b/src/rustup-dist/src/dist.rs @@ -7,19 +7,15 @@ use prefix::InstallPrefix; use manifest::Component; use manifest::Manifest as ManifestV2; use manifestation::{Manifestation, UpdateStatus, Changes}; +use download::{DownloadCfg}; -use std::path::{Path, PathBuf}; -use std::ops; -use url::Url; +use std::path::Path; use std::fmt; use std::env; -use std::fs; use regex::Regex; -use sha2::{Sha256, Digest}; pub const DEFAULT_DIST_SERVER: &'static str = "https://static.rust-lang.org"; -pub const UPDATE_HASH_LEN: usize = 20; // Deprecated pub const DEFAULT_DIST_ROOT: &'static str = "https://static.rust-lang.org/dist"; @@ -440,168 +436,6 @@ impl fmt::Display for ToolchainDesc { } } -pub fn download_and_check<'a>(url_str: &str, - update_hash: Option<&Path>, - ext: &str, - cfg: DownloadCfg<'a>) - -> Result, String)>> { - let hash = try!(download_hash(url_str, cfg)); - let partial_hash: String = hash.chars().take(UPDATE_HASH_LEN).collect(); - - if let Some(hash_file) = update_hash { - if utils::is_file(hash_file) { - if let Ok(contents) = utils::read_file("update hash", hash_file) { - if contents == partial_hash { - // Skip download, update hash matches - return Ok(None); - } - } else { - (cfg.notify_handler)(Notification::CantReadUpdateHash(hash_file)); - } - } else { - (cfg.notify_handler)(Notification::NoUpdateHash(hash_file)); - } - } - - let url = try!(utils::parse_url(url_str)); - let file = try!(cfg.temp_cfg.new_file_with_ext("", ext)); - - let mut hasher = Sha256::new(); - try!(utils::download_file(&url, - &file, - Some(&mut hasher), - &|n| (cfg.notify_handler)(n.into()))); - let actual_hash = hasher.result_str(); - - if hash != actual_hash { - // Incorrect hash - return Err(ErrorKind::ChecksumFailed { - url: url_str.to_owned(), - expected: hash, - calculated: actual_hash, - } - .into()); - } else { - (cfg.notify_handler)(Notification::ChecksumValid(url_str)); - } - - // TODO: Check the signature of the file - - Ok(Some((file, partial_hash))) -} - -#[derive(Copy, Clone)] -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) - } -} - -fn file_hash(path: &Path) -> Result { - let mut hasher = Sha256::new(); - use std::io::Read; - let mut downloaded = try!(fs::File::open(&path).chain_err(|| "opening already downloaded file")); - let mut buf = vec![0; 32768]; - loop { - if let Ok(n) = downloaded.read(&mut buf) { - if n == 0 { break; } - hasher.input(&buf[..n]); - } else { - break; - } - } - - Ok(hasher.result_str()) -} - -impl<'a> DownloadCfg<'a> { - - pub fn download(&self, url: &Url, hash: &str, notify_handler: &'a Fn(Notification)) -> Result { - - 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 cached_result = try!(file_hash(&target_file)); - 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 partial_file_path = - target_file.with_file_name( - target_file.file_name().map(|s| { - s.to_str().unwrap_or("_")}) - .unwrap_or("_") - .to_owned() - + ".partial"); - - let mut hasher = Sha256::new(); - - try!(utils::download_file_with_resume(&url, - &partial_file_path, - Some(&mut hasher), - true, - &|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())); - try!(fs::rename(&partial_file_path, &target_file)); - return Ok(File { path: target_file }); - } - } - - pub fn clean(&self, hashes: &Vec) -> 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 { - let hash_url = try!(utils::parse_url(&(url.to_owned() + ".sha256"))); - let hash_file = try!(cfg.temp_cfg.new_file()); - - try!(utils::download_file(&hash_url, - &hash_file, - None, - &|n| (cfg.notify_handler)(n.into()))); - - Ok(try!(utils::read_file("hash", &hash_file).map(|s| s[0..64].to_owned()))) -} // Installs or updates a toolchain from a dist server. If an initial // install then it will be installed with the default components. If @@ -711,7 +545,7 @@ fn dl_v2_manifest<'a>(download: DownloadCfg<'a>, toolchain: &ToolchainDesc) -> Result> { let manifest_url = toolchain.manifest_v2_url(download.dist_root); - let manifest_dl_res = download_and_check(&manifest_url, update_hash, ".toml", download); + let manifest_dl_res = download.download_and_check(&manifest_url, update_hash, ".toml"); if let Ok(manifest_dl) = manifest_dl_res { // Downloaded ok! @@ -751,7 +585,7 @@ fn dl_v1_manifest<'a>(download: DownloadCfg<'a>, toolchain: &ToolchainDesc) -> R } let manifest_url = toolchain.manifest_v1_url(download.dist_root); - let manifest_dl = try!(download_and_check(&manifest_url, None, "", download)); + let manifest_dl = try!(download.download_and_check(&manifest_url, None, "")); let (manifest_file, _) = manifest_dl.unwrap(); let manifest_str = try!(utils::read_file("manifest", &manifest_file)); let urls = manifest_str.lines().map(|s| format!("{}/{}", root_url, s)).collect(); diff --git a/src/rustup-dist/src/download.rs b/src/rustup-dist/src/download.rs index 81f1103cec..66e56f3687 100644 --- a/src/rustup-dist/src/download.rs +++ b/src/rustup-dist/src/download.rs @@ -1,95 +1,186 @@ +use rustup_utils::{utils}; use errors::*; -use notifications::*; -use rustup_utils::utils; use temp; - +use notifications::*; use sha2::{Sha256, Digest}; +use url::Url; -use std::path::Path; -use std::process::Command; +use std::path::{Path, PathBuf}; +use std::fs; +use std::ops; + +const UPDATE_HASH_LEN: usize = 20; + +#[derive(Copy, Clone)] 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 gpg_key: Option<&'a str>, } + +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 get(&self, url: &str) -> Result> { - if let Some(key) = self.gpg_key { - // Download and verify with GPG key - - let sig_url = try!(utils::parse_url(&format!("{}.asc", url))); - let sig_file = try!(self.temp_cfg.new_file()); - try!(utils::download_file(&sig_url, &sig_file, None, - &|n| (self.notify_handler)(n.into()))); - - let target_url = try!(utils::parse_url(url)); - let target_file = try!(self.temp_cfg.new_file()); - - { - let target_filename: &Path = &target_file; - try!(utils::download_file(&target_url, - &target_file, - None, - &|n| (self.notify_handler)(n.into()))); - - let key_file = try!(self.temp_cfg.new_file()); - let key_filename: &Path = &key_file; - try!(utils::write_file("key", &key_file, key)); - - let gpg = try!(utils::find_cmd(&["gpg2", "gpg"]) - .ok_or("could not find 'gpg' on PATH")); - - try!(utils::cmd_status("gpg", - Command::new(gpg) - .arg("--no-permission-warning") - .arg("--dearmor") - .arg(key_filename))); - - try!(utils::cmd_status("gpg", - Command::new(gpg) - .arg("--no-permission-warning") - .arg("--keyring") - .arg(&key_filename.with_extension("gpg")) - .arg("--verify") - .arg(target_filename))); + + /// Downloads a file, validating its hash, and resuming interrupted downloads + /// Partial downloads are stored in `self.download_dir`, keyed by hash. If the + /// target file already exists, then the hash is checked and it is returned + /// immediately without re-downloading. + pub fn download(&self, url: &Url, hash: &str) -> Result { + + try!(utils::ensure_dir_exists("Download Directory", &self.download_dir, &|n| (self.notify_handler)(n.into()))); + let target_file = self.download_dir.join(Path::new(hash)); + + if target_file.exists() { + let cached_result = try!(file_hash(&target_file)); + if hash == cached_result { + (self.notify_handler)(Notification::FileAlreadyDownloaded); + (self.notify_handler)(Notification::ChecksumValid(&url.to_string())); + return Ok(File { path: target_file, }); + } else { + (self.notify_handler)(Notification::CachedFileChecksumFailed); + try!(fs::remove_file(&target_file).chain_err(|| "cleaning up previous download")); } + } + - Ok(target_file) + let partial_file_path = + target_file.with_file_name( + target_file.file_name().map(|s| { + s.to_str().unwrap_or("_")}) + .unwrap_or("_") + .to_owned() + + ".partial"); + + let mut hasher = Sha256::new(); + + try!(utils::download_file_with_resume(&url, + &partial_file_path, + Some(&mut hasher), + true, + &|n| (self.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 { - // Download and verify with checksum + (self.notify_handler)(Notification::ChecksumValid(&url.to_string())); + try!(fs::rename(&partial_file_path, &target_file)); + return Ok(File { path: target_file }); + } + } - let hash_url = try!(utils::parse_url(&format!("{}.sha256", url))); - let hash_file = try!(self.temp_cfg.new_file()); - try!(utils::download_file(&hash_url, &hash_file, None, - &|n| (self.notify_handler)(n.into()))); + pub fn clean(&self, hashes: &Vec) -> 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(()) + } - let hash = try!(utils::read_file("hash", &hash_file).map(|s| s[0..64].to_owned())); - let mut hasher = Sha256::new(); + fn download_hash(&self, url: &str) -> Result { + let hash_url = try!(utils::parse_url(&(url.to_owned() + ".sha256"))); + let hash_file = try!(self.temp_cfg.new_file()); - let target_url = try!(utils::parse_url(url)); - let target_file = try!(self.temp_cfg.new_file()); - try!(utils::download_file(&target_url, - &target_file, - Some(&mut hasher), - &|n| (self.notify_handler)(n.into()))); + try!(utils::download_file(&hash_url, + &hash_file, + None, + &|n| (self.notify_handler)(n.into()))); - let actual_hash = hasher.result_str(); + Ok(try!(utils::read_file("hash", &hash_file).map(|s| s[0..64].to_owned()))) + } - if hash != actual_hash { - // Incorrect hash - return Err(ErrorKind::ChecksumFailed { - url: url.to_owned(), - expected: hash, - calculated: actual_hash, - }.into()); + /// Downloads a file, sourcing its hash from the same url with a `.sha256` suffix. + /// If `update_hash` is present, then that will be compared to the downloaded hash, + /// and if they match, the download is skipped. + pub fn download_and_check(&self, + url_str: &str, + update_hash: Option<&Path>, + ext: &str) + -> Result, String)>> { + let hash = try!(self.download_hash(url_str)); + let partial_hash: String = hash.chars().take(UPDATE_HASH_LEN).collect(); + + if let Some(hash_file) = update_hash { + + if utils::is_file(hash_file) { + if let Ok(contents) = utils::read_file("update hash", hash_file) { + if contents == partial_hash { + // Skip download, update hash matches + return Ok(None); + } + } else { + (self.notify_handler)(Notification::CantReadUpdateHash(hash_file)); + } } else { - (self.notify_handler)(Notification::ChecksumValid(url)); + (self.notify_handler)(Notification::NoUpdateHash(hash_file)); } + } + + let url = try!(utils::parse_url(url_str)); + let file = try!(self.temp_cfg.new_file_with_ext("", ext)); - Ok(target_file) + let mut hasher = Sha256::new(); + try!(utils::download_file(&url, + &file, + Some(&mut hasher), + &|n| (self.notify_handler)(n.into()))); + let actual_hash = hasher.result_str(); + + if hash != actual_hash { + // Incorrect hash + return Err(ErrorKind::ChecksumFailed { + url: url_str.to_owned(), + expected: hash, + calculated: actual_hash, + } + .into()); + } else { + (self.notify_handler)(Notification::ChecksumValid(url_str)); } + // TODO: Check the signature of the file + + Ok(Some((file, partial_hash))) + } +} + + +fn file_hash(path: &Path) -> Result { + let mut hasher = Sha256::new(); + use std::io::Read; + let mut downloaded = try!(fs::File::open(&path).chain_err(|| "opening already downloaded file")); + let mut buf = vec![0; 32768]; + loop { + if let Ok(n) = downloaded.read(&mut buf) { + if n == 0 { break; } + hasher.input(&buf[..n]); + } else { + break; + } } + + Ok(hasher.result_str()) } diff --git a/src/rustup-dist/src/manifestation.rs b/src/rustup-dist/src/manifestation.rs index 59da29146b..208df01181 100644 --- a/src/rustup-dist/src/manifestation.rs +++ b/src/rustup-dist/src/manifestation.rs @@ -3,12 +3,13 @@ use config::Config; use manifest::{Component, Manifest, TargetedPackage}; -use dist::{download_and_check, DownloadCfg, TargetTriple, DEFAULT_DIST_SERVER, File}; +use dist::{TargetTriple, DEFAULT_DIST_SERVER}; use component::{Components, Transaction, TarGzPackage, Package}; use temp; use errors::*; use notifications::*; use rustup_utils::utils; +use download::{DownloadCfg, File}; use prefix::InstallPrefix; use std::path::Path; @@ -140,7 +141,7 @@ impl Manifestation { let url_url = try!(utils::parse_url(&url)); - let dowloaded_file = try!(download_cfg.download(&url_url, &hash, ¬ify_handler).chain_err(|| { + let dowloaded_file = try!(download_cfg.download(&url_url, &hash).chain_err(|| { ErrorKind::ComponentDownloadFailed(component.clone()) })); things_downloaded.push(hash); @@ -314,7 +315,7 @@ impl Manifestation { notify_handler: notify_handler }; - let dl = try!(download_and_check(&url, update_hash, ".tar.gz", dlcfg)); + let dl = try!(dlcfg.download_and_check(&url, update_hash, ".tar.gz")); if dl.is_none() { return Ok(None); }; diff --git a/src/rustup-dist/tests/dist.rs b/src/rustup-dist/tests/dist.rs index 17b62a48fe..39c210f504 100644 --- a/src/rustup-dist/tests/dist.rs +++ b/src/rustup-dist/tests/dist.rs @@ -18,7 +18,7 @@ use rustup_dist::prefix::InstallPrefix; use rustup_dist::ErrorKind; use rustup_dist::errors::Result; use rustup_dist::dist::{ToolchainDesc, TargetTriple, DEFAULT_DIST_SERVER}; -use rustup_dist::dist; +use rustup_dist::download::DownloadCfg; use rustup_dist::Notification; use rustup_utils::utils; use rustup_utils::raw as utils_raw; @@ -269,9 +269,8 @@ fn update_from_dist(dist_server: &Url, prefix: &InstallPrefix, add: &[Component], remove: &[Component], - download_cfg: &dist::DownloadCfg, - temp_cfg: &temp::Cfg, - notify_handler: &Fn(Notification)) -> Result { + download_cfg: &DownloadCfg, + temp_cfg: &temp::Cfg) -> Result { // Download the dist manifest and place it into the installation prefix let ref manifest_url = try!(make_manifest_url(dist_server, toolchain)); @@ -289,7 +288,7 @@ fn update_from_dist(dist_server: &Url, remove_extensions: remove.to_owned(), }; - manifestation.update(&manifest, changes, download_cfg, notify_handler.clone()) + manifestation.update(&manifest, changes, download_cfg, download_cfg.notify_handler.clone()) } fn make_manifest_url(dist_server: &Url, toolchain: &ToolchainDesc) -> Result { @@ -309,7 +308,7 @@ fn uninstall(toolchain: &ToolchainDesc, prefix: &InstallPrefix, temp_cfg: &temp: } fn setup(edit: Option<&Fn(&str, &mut MockPackage)>, - f: &Fn(&Url, &ToolchainDesc, &InstallPrefix, &dist::DownloadCfg, &temp::Cfg)) { + f: &Fn(&Url, &ToolchainDesc, &InstallPrefix, &DownloadCfg, &temp::Cfg)) { let dist_tempdir = TempDir::new("multirust").unwrap(); create_mock_dist_server(dist_tempdir.path(), edit).write(&[ManifestVersion::V2]); @@ -323,7 +322,7 @@ fn setup(edit: Option<&Fn(&str, &mut MockPackage)>, let ref url = Url::parse(&format!("file://{}", dist_tempdir.path().to_string_lossy())).unwrap(); let ref toolchain = ToolchainDesc::from_str("nightly-x86_64-apple-darwin").unwrap(); let ref prefix = InstallPrefix::from(prefix_tempdir.path().to_owned()); - let ref download_cfg = dist::DownloadCfg { + let ref download_cfg = DownloadCfg { dist_root: "phony", temp_cfg: temp_cfg, download_dir: &prefix.path().to_owned().join("downloads"), @@ -336,7 +335,7 @@ fn setup(edit: Option<&Fn(&str, &mut MockPackage)>, #[test] fn initial_install() { setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/rustc"))); assert!(utils::path_exists(&prefix.path().join("lib/libstd.rlib"))); @@ -346,7 +345,7 @@ fn initial_install() { #[test] fn test_uninstall() { setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); uninstall(toolchain, prefix, temp_cfg, &|_| ()).unwrap(); assert!(!utils::path_exists(&prefix.path().join("bin/rustc"))); @@ -357,7 +356,7 @@ fn test_uninstall() { #[test] fn uninstall_removes_config_file() { setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.manifest_file("multirust-config.toml"))); uninstall(toolchain, prefix, temp_cfg, &|_| ()).unwrap(); assert!(!utils::path_exists(&prefix.manifest_file("multirust-config.toml"))); @@ -368,10 +367,10 @@ fn uninstall_removes_config_file() { fn upgrade() { setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { change_channel_date(url, "nightly", "2016-02-01"); - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); assert_eq!("2016-02-01", utils_raw::read_file(&prefix.path().join("bin/rustc")).unwrap()); change_channel_date(url, "nightly", "2016-02-02"); - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); assert_eq!("2016-02-02", utils_raw::read_file(&prefix.path().join("bin/rustc")).unwrap()); }); } @@ -390,10 +389,10 @@ fn update_removes_components_that_dont_exist() { }; setup(Some(edit), &|url, toolchain, prefix, download_cfg, temp_cfg| { change_channel_date(url, "nightly", "2016-02-01"); - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/bonus"))); change_channel_date(url, "nightly", "2016-02-02"); - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); assert!(!utils::path_exists(&prefix.path().join("bin/bonus"))); }); } @@ -411,13 +410,13 @@ fn update_preserves_extensions() { ]; change_channel_date(url, "nightly", "2016-02-01"); - update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); change_channel_date(url, "nightly", "2016-02-02"); - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); @@ -450,12 +449,12 @@ fn update_preserves_extensions_that_became_components() { ]; change_channel_date(url, "nightly", "2016-02-01"); - update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/bonus"))); change_channel_date(url, "nightly", "2016-02-02"); - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/bonus"))); }); } @@ -480,10 +479,10 @@ fn update_preserves_components_that_became_extensions() { }; setup(Some(edit), &|url, toolchain, prefix, download_cfg, temp_cfg| { change_channel_date(url, "nightly", "2016-02-01"); - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/bonus"))); change_channel_date(url, "nightly", "2016-02-02"); - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/bonus"))); }); } @@ -491,9 +490,9 @@ fn update_preserves_components_that_became_extensions() { #[test] fn update_makes_no_changes_for_identical_manifest() { setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { - let status = update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + let status = update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); assert_eq!(status, UpdateStatus::Changed); - let status = update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + let status = update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); assert_eq!(status, UpdateStatus::Unchanged); }); } @@ -510,7 +509,7 @@ fn add_extensions_for_initial_install() { } ]; - update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); }); @@ -519,7 +518,7 @@ fn add_extensions_for_initial_install() { #[test] fn add_extensions_for_same_manifest() { setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); let ref adds = vec![ Component { @@ -530,7 +529,7 @@ fn add_extensions_for_same_manifest() { } ]; - update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); @@ -542,7 +541,7 @@ fn add_extensions_for_upgrade() { setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { change_channel_date(url, "nightly", "2016-02-01"); - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); change_channel_date(url, "nightly", "2016-02-02"); @@ -555,7 +554,7 @@ fn add_extensions_for_upgrade() { } ]; - update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); @@ -572,7 +571,7 @@ fn add_extension_not_in_manifest() { }, ]; - update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg).unwrap(); }); } @@ -586,7 +585,7 @@ fn add_extension_that_is_required_component() { }, ]; - update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg).unwrap(); }); } @@ -603,7 +602,7 @@ fn add_extensions_for_same_manifest_when_extension_already_installed() { #[test] fn add_extensions_does_not_remove_other_components() { setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); let ref adds = vec![ Component { @@ -611,7 +610,7 @@ fn add_extensions_does_not_remove_other_components() { }, ]; - update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/rustc"))); }); @@ -628,7 +627,7 @@ fn remove_extensions_for_initial_install() { }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg).unwrap(); }); } @@ -644,7 +643,7 @@ fn remove_extensions_for_same_manifest() { } ]; - update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg).unwrap(); let ref removes = vec![ Component { @@ -652,7 +651,7 @@ fn remove_extensions_for_same_manifest() { }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg).unwrap(); assert!(!utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); @@ -673,7 +672,7 @@ fn remove_extensions_for_upgrade() { } ]; - update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg).unwrap(); change_channel_date(url, "nightly", "2016-02-02"); @@ -683,7 +682,7 @@ fn remove_extensions_for_upgrade() { }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg).unwrap(); assert!(!utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); @@ -696,7 +695,7 @@ fn remove_extension_not_in_manifest() { setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { change_channel_date(url, "nightly", "2016-02-01"); - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); change_channel_date(url, "nightly", "2016-02-02"); @@ -706,7 +705,7 @@ fn remove_extension_not_in_manifest() { }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg).unwrap(); }); } @@ -733,7 +732,7 @@ fn remove_extension_not_in_manifest_but_is_already_installed() { pkg: "bonus".to_string(), target: Some(TargetTriple::from_str("x86_64-apple-darwin")) }, ]; - update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/bonus"))); change_channel_date(url, "nightly", "2016-02-02"); @@ -743,7 +742,7 @@ fn remove_extension_not_in_manifest_but_is_already_installed() { pkg: "bonus".to_string(), target: Some(TargetTriple::from_str("x86_64-apple-darwin")) }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg).unwrap(); }); } @@ -751,7 +750,7 @@ fn remove_extension_not_in_manifest_but_is_already_installed() { #[should_panic] fn remove_extension_that_is_required_component() { setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); let ref removes = vec![ Component { @@ -759,7 +758,7 @@ fn remove_extension_that_is_required_component() { }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg).unwrap(); }); } @@ -767,7 +766,7 @@ fn remove_extension_that_is_required_component() { #[should_panic] fn remove_extension_not_installed() { setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); let ref removes = vec![ Component { @@ -775,7 +774,7 @@ fn remove_extension_not_installed() { }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg).unwrap(); }); } @@ -793,7 +792,7 @@ fn remove_extensions_does_not_remove_other_components() { }, ]; - update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg).unwrap(); let ref removes = vec![ Component { @@ -801,7 +800,7 @@ fn remove_extensions_does_not_remove_other_components() { }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/rustc"))); }); @@ -818,7 +817,7 @@ fn add_and_remove_for_upgrade() { }, ]; - update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg).unwrap(); change_channel_date(url, "nightly", "2016-02-02"); @@ -834,7 +833,7 @@ fn add_and_remove_for_upgrade() { }, ]; - update_from_dist(url, toolchain, prefix, adds, removes, download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, removes, download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(!utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); @@ -850,7 +849,7 @@ fn add_and_remove() { }, ]; - update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg).unwrap(); let ref adds = vec![ Component { @@ -864,7 +863,7 @@ fn add_and_remove() { }, ]; - update_from_dist(url, toolchain, prefix, adds, removes, download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, removes, download_cfg, temp_cfg).unwrap(); assert!(utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(!utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); @@ -875,7 +874,7 @@ fn add_and_remove() { #[should_panic] fn add_and_remove_same_component() { setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap(); let ref adds = vec![ Component { @@ -889,7 +888,7 @@ fn add_and_remove_same_component() { }, ]; - update_from_dist(url, toolchain, prefix, adds, removes, download_cfg, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, removes, download_cfg, temp_cfg).unwrap(); }); } @@ -900,7 +899,7 @@ fn bad_component_hash() { let path = path.join("dist/2016-02-02/rustc-nightly-x86_64-apple-darwin.tar.gz"); utils_raw::write_file(&path, "bogus").unwrap(); - let err = update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap_err(); + let err = update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap_err(); match *err.kind() { ErrorKind::ComponentDownloadFailed(_) => (), @@ -916,7 +915,7 @@ fn unable_to_download_component() { let path = path.join("dist/2016-02-02/rustc-nightly-x86_64-apple-darwin.tar.gz"); fs::remove_file(&path).unwrap(); - let err = update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap_err(); + let err = update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg).unwrap_err(); match *err.kind() { ErrorKind::ComponentDownloadFailed(..) => (), @@ -942,16 +941,25 @@ fn reuse_downloaded_file() { prevent_installation(prefix); - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| {}).unwrap_err(); + let reuse_notification_fired = Arc::new(Cell::new(false)); + + let download_cfg = DownloadCfg { + dist_root: download_cfg.dist_root, + temp_cfg: download_cfg.temp_cfg, + download_dir: download_cfg.download_dir, + notify_handler: &|n| { + if let Notification::FileAlreadyDownloaded = n { + reuse_notification_fired.set(true); + } + } + }; + + update_from_dist(url, toolchain, prefix, &[], &[], &download_cfg, temp_cfg).unwrap_err(); + assert!(!reuse_notification_fired.get()); allow_installation(&prefix); - let reuse_notification_fired = Arc::new(Cell::new(false)); - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|n| { - if let Notification::FileAlreadyDownloaded = n { - reuse_notification_fired.set(true); - } - }).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], &download_cfg, temp_cfg).unwrap(); assert!(reuse_notification_fired.get()); }) @@ -969,12 +977,18 @@ fn checks_files_hashes_before_reuse() { println!("wrote previous download to {}", prev_download.display()); let noticed_bad_checksum = Arc::new(Cell::new(false)); - update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|n| { - println!("{:?}", n); - if let Notification::CachedFileChecksumFailed = n { - noticed_bad_checksum.set(true); + let download_cfg = DownloadCfg { + dist_root: download_cfg.dist_root, + temp_cfg: download_cfg.temp_cfg, + download_dir: download_cfg.download_dir, + notify_handler: &|n| { + if let Notification::CachedFileChecksumFailed = n { + noticed_bad_checksum.set(true); + } } - }).unwrap(); + }; + + update_from_dist(url, toolchain, prefix, &[], &[], &download_cfg, temp_cfg).unwrap(); assert!(noticed_bad_checksum.get()); }) diff --git a/src/rustup/install.rs b/src/rustup/install.rs index de348d4535..49c5814c41 100644 --- a/src/rustup/install.rs +++ b/src/rustup/install.rs @@ -6,6 +6,7 @@ use rustup_dist::prefix::InstallPrefix; use rustup_utils::utils; use rustup_dist::temp; use rustup_dist::dist; +use rustup_dist::download::DownloadCfg; use rustup_dist::component::{Components, TarGzPackage, Transaction, Package}; use errors::Result; use std::path::Path; @@ -15,7 +16,7 @@ pub enum InstallMethod<'a> { Copy(&'a Path), Link(&'a Path), Installer(&'a Path, &'a temp::Cfg), - Dist(&'a dist::ToolchainDesc, Option<&'a Path>, dist::DownloadCfg<'a>), + Dist(&'a dist::ToolchainDesc, Option<&'a Path>, DownloadCfg<'a>), } impl<'a> InstallMethod<'a> { diff --git a/src/rustup/toolchain.rs b/src/rustup/toolchain.rs index 434f9cfdee..acc7810eb7 100644 --- a/src/rustup/toolchain.rs +++ b/src/rustup/toolchain.rs @@ -1,7 +1,7 @@ use errors::*; use notifications::*; use rustup_dist; -use rustup_dist::dist; +use rustup_dist::download::DownloadCfg; use rustup_utils::utils; use rustup_dist::prefix::InstallPrefix; use rustup_dist::dist::{ToolchainDesc}; @@ -154,8 +154,8 @@ impl<'a> Toolchain<'a> { } } - fn download_cfg(&self) -> dist::DownloadCfg { - dist::DownloadCfg { + fn download_cfg(&self) -> DownloadCfg { + DownloadCfg { dist_root: &self.cfg.dist_root_url, temp_cfg: &self.cfg.temp_cfg, download_dir: &self.cfg.download_dir,