Skip to content
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

Fix checksum failures #759

Merged
merged 2 commits into from
Oct 21, 2016
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
26 changes: 23 additions & 3 deletions ci/sync-dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@
# * Sync local bins to dev archives
# python sync-dist.py local-to-dev-archives 0.2.0
#
# * Update dev release number
# python sync-dist.py update-dev-release 0.2.0
#
# * Sync local bins to prod archives
# python sync-dist.py local-to-prod-archives 0.2.0
#
# * Sync local bins to prod
# python sync-dist.py local-to-prod
#
# * Update prod release number
# python sync-dist.py update-prod-release 0.2.0
#
# Don't forget to tag the release, dummy!

import sys
Expand All @@ -26,8 +32,10 @@
def usage():
print ("usage: sync-dist dev-to-local [--live-run]\n"
" sync-dist local-to-dev-archives $version [--live-run]\n"
" sync-dist update-dev-release $version [--live-run]\n"
" sync-dist local-to-prod-archives $version [--live-run]\n"
" sync-dist local-to-prod [--live-run]\n")
" sync-dist local-to-prod [--live-run]\n"
" sync-dist update-prod-release $version [--live-run]\n")
sys.exit(1)

command = None
Expand All @@ -41,11 +49,13 @@ def usage():

if not command in ["dev-to-local",
"local-to-dev-archives",
"update-dev-release",
"local-to-prod-archives",
"local-to-prod"]:
"local-to-prod",
"update-prod-release"]:
usage()

if "archives" in command:
if "archives" in command or "release" in command:
if len(sys.argv) < 3:
usage()
archive_version = sys.argv[2]
Expand Down Expand Up @@ -77,12 +87,22 @@ def usage():
s3cmd = "s3cmd sync ./local-rustup/dist/ s3://{}/rustup/archive/{}/".format(s3_bucket, archive_version)
elif command == "local-to-prod":
s3cmd = "s3cmd sync ./local-rustup/dist/ s3://{}/rustup/dist/".format(s3_bucket)
elif command == "update-dev-release" \
or command == "update-prod-release":
s3cmd = "s3cmd put ./local-rustup/release-stable.toml s3://{}/rustup/release-stable.toml".format(s3_bucket)
else:
sys.exit(1)

print "s3 command: {}".format(s3cmd)
print

# Create the release information
if command == "update-dev-release" \
or command == "update-prod-release":
with open("./local-rustup/release-stable.toml", "w") as f:
f.write("schema-version = '1'\n")
f.write("version = '{}'\n".format(archive_version))

def run_s3cmd(command):
s3cmd = command.split(" ")

Expand Down
1 change: 1 addition & 0 deletions src/rustup-cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ extern crate scopeguard;
extern crate tempdir;
extern crate sha2;
extern crate markdown;
extern crate toml;

#[cfg(windows)]
extern crate winapi;
Expand Down
66 changes: 31 additions & 35 deletions src/rustup-cli/self_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,11 @@ use common::{self, Confirm};
use errors::*;
use rustup_dist::dist;
use rustup_utils::utils;
use sha2::{Sha256, Digest};
use std::env;
use std::env::consts::EXE_SUFFIX;
use std::path::{Path, PathBuf};
use std::process::{self, Command};
use std::fs::{self, File};
use std::io::Read;
use std::fs;
use tempdir::TempDir;
use term2;
use regex::Regex;
Expand Down Expand Up @@ -1210,6 +1208,8 @@ fn parse_new_rustup_version(version: String) -> String {
}

pub fn prepare_update() -> Result<Option<PathBuf>> {
use toml;

let ref cargo_home = try!(utils::cargo_home());
let ref rustup_path = cargo_home.join(&format!("bin/rustup{}", EXE_SUFFIX));
let ref setup_path = cargo_home.join(&format!("bin/rustup-init{}", EXE_SUFFIX));
Expand All @@ -1231,53 +1231,49 @@ pub fn prepare_update() -> Result<Option<PathBuf>> {
let tempdir = try!(TempDir::new("rustup-update")
.chain_err(|| "error creating temp directory"));

// Get download URL
let url = format!("{}/{}/rustup-init{}", update_root, triple, EXE_SUFFIX);

// Calculate own hash
let mut hasher = Sha256::new();
let mut self_exe = try!(File::open(rustup_path)
.chain_err(|| "can't open self exe to calculate hash"));
let ref mut buf = [0; 4096];
loop {
let bytes = try!(self_exe.read(buf)
.chain_err(|| "failed to read from self exe while calculating hash"));
if bytes == 0 { break; }
hasher.input(&buf[0..bytes]);
}
let current_hash = hasher.result_str();
drop(self_exe);
// Get current version
let current_version = env!("CARGO_PKG_VERSION");

// Download latest hash
// Download available version
info!("checking for self-updates");
let hash_url = try!(utils::parse_url(&(url.clone() + ".sha256")));
let hash_file = tempdir.path().join("hash");
try!(utils::download_file(&hash_url, &hash_file, None, &|_| ()));
let mut latest_hash = try!(utils::read_file("hash", &hash_file));
latest_hash.truncate(64);
let release_file_url = format!("{}/release-stable.toml", update_root);
let release_file_url = try!(utils::parse_url(&release_file_url));
let release_file = tempdir.path().join("release-stable.toml");
try!(utils::download_file(&release_file_url, &release_file, None, &|_| ()));
let release_toml_str = try!(utils::read_file("rustup release", &release_file));
let release_toml = try!(toml::Parser::new(&release_toml_str).parse()
.ok_or(Error::from("unable to parse rustup release file")));
let schema = try!(release_toml.get("schema-version")
.ok_or(Error::from("no schema key in rustup release file")));
let schema = try!(schema.as_str()
.ok_or(Error::from("invalid schema key in rustup release file")));
let available_version = try!(release_toml.get("version")
.ok_or(Error::from("no version key in rustup release file")));
let available_version = try!(available_version.as_str()
.ok_or(Error::from("invalid version key in rustup release file")));

if schema != "1" {
return Err(Error::from(&*format!("unknown schema version '{}' in rustup release file", schema)));
}

// If up-to-date
if latest_hash == current_hash {
if available_version == current_version {
return Ok(None);
}

// Get download URL
let url = format!("{}/archive/{}/{}/rustup-init{}", update_root,
available_version, triple, EXE_SUFFIX);

// Get download path
let download_url = try!(utils::parse_url(&url));

// Download new version
info!("downloading self-update");
let mut hasher = Sha256::new();
try!(utils::download_file(&download_url,
&setup_path,
Some(&mut hasher),
None,
&|_| ()));
let download_hash = hasher.result_str();

// Check that hash is correct
if latest_hash != download_hash {
info!("update not yet available. bug #364");
return Ok(None);
}

// Mark as executable
try!(utils::make_executable(setup_path));
Expand Down
4 changes: 2 additions & 2 deletions src/rustup-dist/src/notifications.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ impl<'a> Notification<'a> {
DownloadingComponent(_, _, _) |
InstallingComponent(_, _, _) |
ComponentAlreadyInstalled(_) |
ManifestChecksumFailedHack |
RollingBack | DownloadingManifest(_) => NotificationLevel::Info,
CantReadUpdateHash(_) | ExtensionNotInstalled(_) |
MissingInstalledComponent(_) |
ManifestChecksumFailedHack => NotificationLevel::Warn,
MissingInstalledComponent(_) => NotificationLevel::Warn,
NonFatalError(_) => NotificationLevel::Error,
}
}
Expand Down
120 changes: 39 additions & 81 deletions tests/cli-self-upd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ use rustup_mock::clitools::{self, Config, Scenario,
expect_stderr_ok,
expect_err, expect_err_ex,
this_host_triple};
use rustup_mock::dist::{create_hash, calc_hash};
use rustup_mock::dist::{calc_hash};
use rustup_mock::{get_path, restore_path};
use rustup_utils::raw;
use rustup_utils::{utils, raw};

macro_rules! for_host { ($s: expr) => (&format!($s, this_host_triple())) }

const TEST_VERSION: &'static str = "1.1.1";

pub fn setup(f: &Fn(&Config)) {
clitools::setup(Scenario::SimpleV2, &|config| {
// Lock protects environment variables
Expand All @@ -60,16 +62,15 @@ pub fn update_setup(f: &Fn(&Config, &Path)) {
let ref self_dist = self_dist_tmp.path();

let ref trip = this_host_triple();
let ref dist_dir = self_dist.join(&format!("{}", trip));
let ref dist_dir = self_dist.join(&format!("archive/{}/{}", TEST_VERSION, trip));
let ref dist_exe = dist_dir.join(&format!("rustup-init{}", EXE_SUFFIX));
let ref dist_hash = dist_dir.join(&format!("rustup-init{}.sha256", EXE_SUFFIX));
let ref rustup_bin = config.exedir.join(&format!("rustup-init{}", EXE_SUFFIX));

fs::create_dir_all(dist_dir).unwrap();
output_release_file(self_dist, "1", TEST_VERSION);
fs::copy(rustup_bin, dist_exe).unwrap();
// Modify the exe so it hashes different
raw::append_file(dist_exe, "").unwrap();
create_hash(dist_exe, dist_hash);

let ref root_url = format!("file://{}", self_dist.display());
env::set_var("RUSTUP_UPDATE_ROOT", root_url);
Expand All @@ -78,6 +79,15 @@ pub fn update_setup(f: &Fn(&Config, &Path)) {
});
}

fn output_release_file(dist_dir: &Path, schema: &str, version: &str) {
let contents = format!(r#"
schema-version = "{}"
version = "{}"
"#, schema, version);
let file = dist_dir.join("release-stable.toml");
utils::write_file("release", &file, &contents).unwrap();
}

#[test]
fn install_bins_to_cargo_home() {
setup(&|config| {
Expand Down Expand Up @@ -503,88 +513,13 @@ fn update_but_delete_existing_updater_first() {
});
}

#[test]
fn update_no_change() {
update_setup(&|config, self_dist| {
expect_ok(config, &["rustup-init", "-y"]);

let ref trip = this_host_triple();
let ref dist_dir = self_dist.join(&format!("{}", trip));
let ref dist_exe = dist_dir.join(&format!("rustup-init{}", EXE_SUFFIX));
let ref dist_hash = dist_dir.join(&format!("rustup-init{}.sha256", EXE_SUFFIX));
let ref rustup_bin = config.exedir.join(&format!("rustup{}", EXE_SUFFIX));
fs::copy(rustup_bin, dist_exe).unwrap();
create_hash(dist_exe, dist_hash);

expect_ok_ex(config, &["rustup", "self", "update"],
r"",
r"info: checking for self-updates
");

});
}

#[test]
#[ignore] // Workaround for #346
fn update_bad_hash() {
update_setup(&|config, self_dist| {
expect_ok(config, &["rustup-init", "-y"]);

let ref trip = this_host_triple();
let ref dist_dir = self_dist.join(&format!("{}", trip));
let ref dist_hash = dist_dir.join(&format!("rustup-init{}.sha256", EXE_SUFFIX));

let ref some_other_file = config.distdir.join("dist/channel-rust-nightly.toml");

create_hash(some_other_file, dist_hash);

expect_err(config, &["rustup", "self", "update"],
"checksum failed");
});
}

// Workaround for #346
#[test]
fn update_hash_drift() {
update_setup(&|config, self_dist| {
expect_ok(config, &["rustup-init", "-y"]);

let ref trip = this_host_triple();
let ref dist_dir = self_dist.join(&format!("{}", trip));
let ref dist_hash = dist_dir.join(&format!("rustup-init{}.sha256", EXE_SUFFIX));

let ref some_other_file = config.distdir.join("dist/channel-rust-nightly.toml");

create_hash(some_other_file, dist_hash);

expect_stderr_ok(config, &["rustup", "self", "update"],
"update not yet available");
});
}

#[test]
fn update_hash_file_404() {
update_setup(&|config, self_dist| {
expect_ok(config, &["rustup-init", "-y"]);

let ref trip = this_host_triple();
let ref dist_dir = self_dist.join(&format!("{}", trip));
let ref dist_hash = dist_dir.join(&format!("rustup-init{}.sha256", EXE_SUFFIX));

fs::remove_file(dist_hash).unwrap();

expect_err(config, &["rustup", "self", "update"],
"could not download file");
});
}

#[test]
fn update_download_404() {
update_setup(&|config, self_dist| {
expect_ok(config, &["rustup-init", "-y"]);

let ref trip = this_host_triple();
let ref dist_dir = self_dist.join(&format!("{}", trip));
let ref dist_dir = self_dist.join(&format!("archive/{}/{}", TEST_VERSION, trip));
let ref dist_exe = dist_dir.join(&format!("rustup-init{}", EXE_SUFFIX));

fs::remove_file(dist_exe).unwrap();
Expand Down Expand Up @@ -632,6 +567,29 @@ fn update_updates_rustup_bin() {
});
}

#[test]
fn update_bad_schema() {
update_setup(&|config, self_dist| {
expect_ok(config, &["rustup-init", "-y"]);
output_release_file(self_dist, "17", "1.1.1");
expect_err(config, &["rustup", "self", "update"],
"unknown schema version");
});
}

#[test]
fn update_no_change() {
let version = env!("CARGO_PKG_VERSION");
update_setup(&|config, self_dist| {
expect_ok(config, &["rustup-init", "-y"]);
output_release_file(self_dist, "1", version);
expect_ok_ex(config, &["rustup", "self", "update"],
r"",
r"info: checking for self-updates
");
});
}

#[test]
fn rustup_self_updates() {
update_setup(&|config, _| {
Expand Down