Skip to content

Commit 4d53ac6

Browse files
committed
Fix checksum failures with new self-update process
This changes the update process in the following ways: the current version is read from the server at /rustup/stable-release.toml; if the version is different from the running version then rustup downloads the new release from the archives at /rustup/archive/$version/. Fixes #524
1 parent 83f44e7 commit 4d53ac6

File tree

4 files changed

+94
-119
lines changed

4 files changed

+94
-119
lines changed

ci/sync-dist.py

+23-3
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,18 @@
1010
# * Sync local bins to dev archives
1111
# python sync-dist.py local-to-dev-archives 0.2.0
1212
#
13+
# * Update dev release number
14+
# python sync-dist.py update-dev-release 0.2.0
15+
#
1316
# * Sync local bins to prod archives
1417
# python sync-dist.py local-to-prod-archives 0.2.0
1518
#
1619
# * Sync local bins to prod
1720
# python sync-dist.py local-to-prod
1821
#
22+
# * Update prod release number
23+
# python sync-dist.py update-prod-release 0.2.0
24+
#
1925
# Don't forget to tag the release, dummy!
2026

2127
import sys
@@ -26,8 +32,10 @@
2632
def usage():
2733
print ("usage: sync-dist dev-to-local [--live-run]\n"
2834
" sync-dist local-to-dev-archives $version [--live-run]\n"
35+
" sync-dist update-dev-release $version [--live-run]\n"
2936
" sync-dist local-to-prod-archives $version [--live-run]\n"
30-
" sync-dist local-to-prod [--live-run]\n")
37+
" sync-dist local-to-prod [--live-run]\n"
38+
" sync-dist update-prod-release $version [--live-run]\n")
3139
sys.exit(1)
3240

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

4250
if not command in ["dev-to-local",
4351
"local-to-dev-archives",
52+
"update-dev-release",
4453
"local-to-prod-archives",
45-
"local-to-prod"]:
54+
"local-to-prod",
55+
"update-prod-release"]:
4656
usage()
4757

48-
if "archives" in command:
58+
if "archives" in command or "release" in command:
4959
if len(sys.argv) < 3:
5060
usage()
5161
archive_version = sys.argv[2]
@@ -77,12 +87,22 @@ def usage():
7787
s3cmd = "s3cmd sync ./local-rustup/dist/ s3://{}/rustup/archive/{}/".format(s3_bucket, archive_version)
7888
elif command == "local-to-prod":
7989
s3cmd = "s3cmd sync ./local-rustup/dist/ s3://{}/rustup/dist/".format(s3_bucket)
90+
elif command == "update-dev-release" \
91+
or command == "update-prod-release":
92+
s3cmd = "s3cmd put ./local-rustup/release-stable.toml s3://{}/rustup/release-stable.toml".format(s3_bucket)
8093
else:
8194
sys.exit(1)
8295

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

99+
# Create the release information
100+
if command == "update-dev-release" \
101+
or command == "update-prod-release":
102+
with open("./local-rustup/release-stable.toml", "w") as f:
103+
f.write("schema-version = '1'\n")
104+
f.write("version = '{}'\n".format(archive_version))
105+
86106
def run_s3cmd(command):
87107
s3cmd = command.split(" ")
88108

src/rustup-cli/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ extern crate scopeguard;
1919
extern crate tempdir;
2020
extern crate sha2;
2121
extern crate markdown;
22+
extern crate toml;
2223

2324
#[cfg(windows)]
2425
extern crate winapi;

src/rustup-cli/self_update.rs

+31-35
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,11 @@ use common::{self, Confirm};
3434
use errors::*;
3535
use rustup_dist::dist;
3636
use rustup_utils::utils;
37-
use sha2::{Sha256, Digest};
3837
use std::env;
3938
use std::env::consts::EXE_SUFFIX;
4039
use std::path::{Path, PathBuf};
4140
use std::process::{self, Command};
42-
use std::fs::{self, File};
43-
use std::io::Read;
41+
use std::fs;
4442
use tempdir::TempDir;
4543
use term2;
4644
use regex::Regex;
@@ -1210,6 +1208,8 @@ fn parse_new_rustup_version(version: String) -> String {
12101208
}
12111209

12121210
pub fn prepare_update() -> Result<Option<PathBuf>> {
1211+
use toml;
1212+
12131213
let ref cargo_home = try!(utils::cargo_home());
12141214
let ref rustup_path = cargo_home.join(&format!("bin/rustup{}", EXE_SUFFIX));
12151215
let ref setup_path = cargo_home.join(&format!("bin/rustup-init{}", EXE_SUFFIX));
@@ -1231,53 +1231,49 @@ pub fn prepare_update() -> Result<Option<PathBuf>> {
12311231
let tempdir = try!(TempDir::new("rustup-update")
12321232
.chain_err(|| "error creating temp directory"));
12331233

1234-
// Get download URL
1235-
let url = format!("{}/{}/rustup-init{}", update_root, triple, EXE_SUFFIX);
1236-
1237-
// Calculate own hash
1238-
let mut hasher = Sha256::new();
1239-
let mut self_exe = try!(File::open(rustup_path)
1240-
.chain_err(|| "can't open self exe to calculate hash"));
1241-
let ref mut buf = [0; 4096];
1242-
loop {
1243-
let bytes = try!(self_exe.read(buf)
1244-
.chain_err(|| "failed to read from self exe while calculating hash"));
1245-
if bytes == 0 { break; }
1246-
hasher.input(&buf[0..bytes]);
1247-
}
1248-
let current_hash = hasher.result_str();
1249-
drop(self_exe);
1234+
// Get current version
1235+
let current_version = env!("CARGO_PKG_VERSION");
12501236

1251-
// Download latest hash
1237+
// Download available version
12521238
info!("checking for self-updates");
1253-
let hash_url = try!(utils::parse_url(&(url.clone() + ".sha256")));
1254-
let hash_file = tempdir.path().join("hash");
1255-
try!(utils::download_file(&hash_url, &hash_file, None, &|_| ()));
1256-
let mut latest_hash = try!(utils::read_file("hash", &hash_file));
1257-
latest_hash.truncate(64);
1239+
let release_file_url = format!("{}/release-stable.toml", update_root);
1240+
let release_file_url = try!(utils::parse_url(&release_file_url));
1241+
let release_file = tempdir.path().join("release-stable.toml");
1242+
try!(utils::download_file(&release_file_url, &release_file, None, &|_| ()));
1243+
let release_toml_str = try!(utils::read_file("rustup release", &release_file));
1244+
let release_toml = try!(toml::Parser::new(&release_toml_str).parse()
1245+
.ok_or(Error::from("unable to parse rustup release file")));
1246+
let schema = try!(release_toml.get("schema-version")
1247+
.ok_or(Error::from("no schema key in rustup release file")));
1248+
let schema = try!(schema.as_str()
1249+
.ok_or(Error::from("invalid schema key in rustup release file")));
1250+
let available_version = try!(release_toml.get("version")
1251+
.ok_or(Error::from("no version key in rustup release file")));
1252+
let available_version = try!(available_version.as_str()
1253+
.ok_or(Error::from("invalid version key in rustup release file")));
1254+
1255+
if schema != "1" {
1256+
return Err(Error::from(&*format!("unknown schema version '{}' in rustup release file", schema)));
1257+
}
12581258

12591259
// If up-to-date
1260-
if latest_hash == current_hash {
1260+
if available_version == current_version {
12611261
return Ok(None);
12621262
}
12631263

1264+
// Get download URL
1265+
let url = format!("{}/archive/{}/{}/rustup-init{}", update_root,
1266+
available_version, triple, EXE_SUFFIX);
1267+
12641268
// Get download path
12651269
let download_url = try!(utils::parse_url(&url));
12661270

12671271
// Download new version
12681272
info!("downloading self-update");
1269-
let mut hasher = Sha256::new();
12701273
try!(utils::download_file(&download_url,
12711274
&setup_path,
1272-
Some(&mut hasher),
1275+
None,
12731276
&|_| ()));
1274-
let download_hash = hasher.result_str();
1275-
1276-
// Check that hash is correct
1277-
if latest_hash != download_hash {
1278-
info!("update not yet available. bug #364");
1279-
return Ok(None);
1280-
}
12811277

12821278
// Mark as executable
12831279
try!(utils::make_executable(setup_path));

tests/cli-self-upd.rs

+39-81
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@ use rustup_mock::clitools::{self, Config, Scenario,
2929
expect_stderr_ok,
3030
expect_err, expect_err_ex,
3131
this_host_triple};
32-
use rustup_mock::dist::{create_hash, calc_hash};
32+
use rustup_mock::dist::{calc_hash};
3333
use rustup_mock::{get_path, restore_path};
34-
use rustup_utils::raw;
34+
use rustup_utils::{utils, raw};
3535

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

38+
const TEST_VERSION: &'static str = "1.1.1";
39+
3840
pub fn setup(f: &Fn(&Config)) {
3941
clitools::setup(Scenario::SimpleV2, &|config| {
4042
// Lock protects environment variables
@@ -60,16 +62,15 @@ pub fn update_setup(f: &Fn(&Config, &Path)) {
6062
let ref self_dist = self_dist_tmp.path();
6163

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

6869
fs::create_dir_all(dist_dir).unwrap();
70+
output_release_file(self_dist, "1", TEST_VERSION);
6971
fs::copy(rustup_bin, dist_exe).unwrap();
7072
// Modify the exe so it hashes different
7173
raw::append_file(dist_exe, "").unwrap();
72-
create_hash(dist_exe, dist_hash);
7374

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

82+
fn output_release_file(dist_dir: &Path, schema: &str, version: &str) {
83+
let contents = format!(r#"
84+
schema-version = "{}"
85+
version = "{}"
86+
"#, schema, version);
87+
let file = dist_dir.join("release-stable.toml");
88+
utils::write_file("release", &file, &contents).unwrap();
89+
}
90+
8191
#[test]
8292
fn install_bins_to_cargo_home() {
8393
setup(&|config| {
@@ -503,88 +513,13 @@ fn update_but_delete_existing_updater_first() {
503513
});
504514
}
505515

506-
#[test]
507-
fn update_no_change() {
508-
update_setup(&|config, self_dist| {
509-
expect_ok(config, &["rustup-init", "-y"]);
510-
511-
let ref trip = this_host_triple();
512-
let ref dist_dir = self_dist.join(&format!("{}", trip));
513-
let ref dist_exe = dist_dir.join(&format!("rustup-init{}", EXE_SUFFIX));
514-
let ref dist_hash = dist_dir.join(&format!("rustup-init{}.sha256", EXE_SUFFIX));
515-
let ref rustup_bin = config.exedir.join(&format!("rustup{}", EXE_SUFFIX));
516-
fs::copy(rustup_bin, dist_exe).unwrap();
517-
create_hash(dist_exe, dist_hash);
518-
519-
expect_ok_ex(config, &["rustup", "self", "update"],
520-
r"",
521-
r"info: checking for self-updates
522-
");
523-
524-
});
525-
}
526-
527-
#[test]
528-
#[ignore] // Workaround for #346
529-
fn update_bad_hash() {
530-
update_setup(&|config, self_dist| {
531-
expect_ok(config, &["rustup-init", "-y"]);
532-
533-
let ref trip = this_host_triple();
534-
let ref dist_dir = self_dist.join(&format!("{}", trip));
535-
let ref dist_hash = dist_dir.join(&format!("rustup-init{}.sha256", EXE_SUFFIX));
536-
537-
let ref some_other_file = config.distdir.join("dist/channel-rust-nightly.toml");
538-
539-
create_hash(some_other_file, dist_hash);
540-
541-
expect_err(config, &["rustup", "self", "update"],
542-
"checksum failed");
543-
});
544-
}
545-
546-
// Workaround for #346
547-
#[test]
548-
fn update_hash_drift() {
549-
update_setup(&|config, self_dist| {
550-
expect_ok(config, &["rustup-init", "-y"]);
551-
552-
let ref trip = this_host_triple();
553-
let ref dist_dir = self_dist.join(&format!("{}", trip));
554-
let ref dist_hash = dist_dir.join(&format!("rustup-init{}.sha256", EXE_SUFFIX));
555-
556-
let ref some_other_file = config.distdir.join("dist/channel-rust-nightly.toml");
557-
558-
create_hash(some_other_file, dist_hash);
559-
560-
expect_stderr_ok(config, &["rustup", "self", "update"],
561-
"update not yet available");
562-
});
563-
}
564-
565-
#[test]
566-
fn update_hash_file_404() {
567-
update_setup(&|config, self_dist| {
568-
expect_ok(config, &["rustup-init", "-y"]);
569-
570-
let ref trip = this_host_triple();
571-
let ref dist_dir = self_dist.join(&format!("{}", trip));
572-
let ref dist_hash = dist_dir.join(&format!("rustup-init{}.sha256", EXE_SUFFIX));
573-
574-
fs::remove_file(dist_hash).unwrap();
575-
576-
expect_err(config, &["rustup", "self", "update"],
577-
"could not download file");
578-
});
579-
}
580-
581516
#[test]
582517
fn update_download_404() {
583518
update_setup(&|config, self_dist| {
584519
expect_ok(config, &["rustup-init", "-y"]);
585520

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

590525
fs::remove_file(dist_exe).unwrap();
@@ -632,6 +567,29 @@ fn update_updates_rustup_bin() {
632567
});
633568
}
634569

570+
#[test]
571+
fn update_bad_schema() {
572+
update_setup(&|config, self_dist| {
573+
expect_ok(config, &["rustup-init", "-y"]);
574+
output_release_file(dist_dir, "17", "1.1.1");
575+
expect_err(config, &["rustup", "self", "update"],
576+
"unknown schema version");
577+
});
578+
}
579+
580+
#[test]
581+
fn update_no_change() {
582+
let version = env!("CARGO_PKG_VERSION");
583+
update_setup(&|config, self_dist| {
584+
expect_ok(config, &["rustup-init", "-y"]);
585+
output_release_file(dist_dir, "1", version);
586+
expect_ok_ex(config, &["rustup", "self", "update"],
587+
r"",
588+
r"info: checking for self-updates
589+
");
590+
});
591+
}
592+
635593
#[test]
636594
fn rustup_self_updates() {
637595
update_setup(&|config, _| {

0 commit comments

Comments
 (0)