Skip to content

Commit df47f3f

Browse files
authored
Merge pull request #1889 from Glamhoth/partial-fix
Removing .partial that causes repeated failure to download a component
2 parents e44868f + 984ccea commit df47f3f

File tree

4 files changed

+95
-8
lines changed

4 files changed

+95
-8
lines changed

download/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub fn download_to_path_with_backend(
4242
callback: Option<&dyn Fn(Event<'_>) -> Result<()>>,
4343
) -> Result<()> {
4444
use std::cell::RefCell;
45+
use std::fs::remove_file;
4546
use std::fs::OpenOptions;
4647
use std::io::{Read, Seek, SeekFrom, Write};
4748

@@ -114,7 +115,10 @@ pub fn download_to_path_with_backend(
114115
Ok(())
115116
}()
116117
.map_err(|e| {
117-
// TODO is there any point clearing up here? What kind of errors will leave us with an unusable partial?
118+
// TODO: We currently clear up the cached download on any error, should we restrict it to a subset?
119+
remove_file(path)
120+
.chain_err(|| "cleaning up cached downloads")
121+
.unwrap();
118122
e
119123
})
120124
}

src/dist/download.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,26 +68,39 @@ impl<'a> DownloadCfg<'a> {
6868
+ ".partial",
6969
);
7070

71+
let partial_file_existed = partial_file_path.exists();
72+
7173
let mut hasher = Sha256::new();
7274

73-
utils::download_file_with_resume(
75+
if let Err(e) = utils::download_file_with_resume(
7476
&url,
7577
&partial_file_path,
7678
Some(&mut hasher),
7779
true,
7880
&|n| (self.notify_handler)(n.into()),
79-
)?;
81+
) {
82+
if partial_file_existed {
83+
return Err(e).chain_err(|| ErrorKind::BrokenPartialFile);
84+
} else {
85+
return Err(e);
86+
}
87+
};
8088

8189
let actual_hash = format!("{:x}", hasher.result());
8290

8391
if hash != actual_hash {
8492
// Incorrect hash
85-
Err(ErrorKind::ChecksumFailed {
86-
url: url.to_string(),
87-
expected: hash.to_string(),
88-
calculated: actual_hash,
93+
if partial_file_existed {
94+
self.clean(&[hash.to_string() + &".partial".to_string()])?;
95+
Err(ErrorKind::BrokenPartialFile.into())
96+
} else {
97+
Err(ErrorKind::ChecksumFailed {
98+
url: url.to_string(),
99+
expected: hash.to_string(),
100+
calculated: actual_hash,
101+
}
102+
.into())
89103
}
90-
.into())
91104
} else {
92105
(self.notify_handler)(Notification::ChecksumValid(&url.to_string()));
93106

src/errors.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,9 @@ error_chain! {
362362
description("invalid PGP key"),
363363
display("unable to read the PGP key '{}'", v.display())
364364
}
365+
BrokenPartialFile {
366+
description("partially downloaded file may have been damaged and was removed, please try again")
367+
}
365368
}
366369
}
367370

tests/dist.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ use std::str::FromStr;
2525
use std::sync::Arc;
2626
use url::Url;
2727

28+
const SHA256_HASH_LEN: usize = 64;
29+
2830
// Creates a mock dist server populated with some test data
2931
pub fn create_mock_dist_server(
3032
path: &Path,
@@ -1984,3 +1986,68 @@ fn checks_files_hashes_before_reuse() {
19841986
assert!(noticed_bad_checksum.get());
19851987
})
19861988
}
1989+
1990+
#[test]
1991+
fn handle_corrupt_partial_downloads() {
1992+
setup(None, false, &|url,
1993+
toolchain,
1994+
prefix,
1995+
download_cfg,
1996+
temp_cfg| {
1997+
// write a corrupt partial out
1998+
let path = url.to_file_path().unwrap();
1999+
let target_hash = utils::read_file(
2000+
"target hash",
2001+
&path.join("dist/2016-02-02/rustc-nightly-x86_64-apple-darwin.tar.gz.sha256"),
2002+
)
2003+
.unwrap()[..SHA256_HASH_LEN]
2004+
.to_owned();
2005+
2006+
utils::ensure_dir_exists(
2007+
"download dir",
2008+
&download_cfg.download_dir,
2009+
&|_: Notification<'_>| {},
2010+
)
2011+
.unwrap();
2012+
let partial_path = download_cfg
2013+
.download_dir
2014+
.join(format!("{}.partial", target_hash));
2015+
utils_raw::write_file(
2016+
&partial_path,
2017+
"file will be resumed from here and not match hash",
2018+
)
2019+
.unwrap();
2020+
2021+
let err = update_from_dist(
2022+
url,
2023+
toolchain,
2024+
prefix,
2025+
&[],
2026+
&[],
2027+
download_cfg,
2028+
temp_cfg,
2029+
false,
2030+
)
2031+
.unwrap_err();
2032+
2033+
match err.kind() {
2034+
ErrorKind::ComponentDownloadFailed(_) => (),
2035+
e => panic!("Unexpected error: {:?}", e),
2036+
}
2037+
2038+
update_from_dist(
2039+
url,
2040+
toolchain,
2041+
prefix,
2042+
&[],
2043+
&[],
2044+
&download_cfg,
2045+
temp_cfg,
2046+
false,
2047+
)
2048+
.unwrap();
2049+
2050+
assert!(utils::path_exists(&prefix.path().join("bin/rustc")));
2051+
assert!(utils::path_exists(&prefix.path().join("lib/libstd.rlib")));
2052+
});
2053+
}

0 commit comments

Comments
 (0)