From 3be453d0bb854e190e1da914d7660b1e24501494 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Sat, 14 Oct 2023 22:52:05 +0300 Subject: [PATCH 1/2] optimize file read in Config::verify `Config::verify` refactored to improve the efficiency and memory usage of file hashing. Signed-off-by: onur-ozkan --- src/bootstrap/download.rs | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/bootstrap/download.rs b/src/bootstrap/download.rs index 8e9614ec89a..2a0dec75599 100644 --- a/src/bootstrap/download.rs +++ b/src/bootstrap/download.rs @@ -320,25 +320,43 @@ impl Config { } /// Returns whether the SHA256 checksum of `path` matches `expected`. - fn verify(&self, path: &Path, expected: &str) -> bool { + pub(crate) fn verify(&self, path: &Path, expected: &str) -> bool { use sha2::Digest; self.verbose(&format!("verifying {}", path.display())); + + if self.dry_run() { + return false; + } + let mut hasher = sha2::Sha256::new(); - // FIXME: this is ok for rustfmt (4.1 MB large at time of writing), but it seems memory-intensive for rustc and larger components. - // Consider using streaming IO instead? - let contents = if self.dry_run() { vec![] } else { t!(fs::read(path)) }; - hasher.update(&contents); - let found = hex::encode(hasher.finalize().as_slice()); - let verified = found == expected; - if !verified && !self.dry_run() { + + let file = t!(File::open(path)); + let mut reader = BufReader::new(file); + + loop { + let buffer = t!(reader.fill_buf()); + let l = buffer.len(); + // break if EOF + if l == 0 { + break; + } + hasher.update(buffer); + reader.consume(l); + } + + let checksum = hex::encode(hasher.finalize().as_slice()); + let verified = checksum == expected; + + if !verified { println!( "invalid checksum: \n\ - found: {found}\n\ + found: {checksum}\n\ expected: {expected}", ); } - return verified; + + verified } } From d16e89dc9d8ebfcf4a475585223fd23a5b36cee0 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Sat, 14 Oct 2023 22:52:49 +0300 Subject: [PATCH 2/2] add unit test for Config::verify Signed-off-by: onur-ozkan --- src/bootstrap/config/tests.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/config/tests.rs b/src/bootstrap/config/tests.rs index d091f33eee4..ae8363b6de9 100644 --- a/src/bootstrap/config/tests.rs +++ b/src/bootstrap/config/tests.rs @@ -3,7 +3,12 @@ use crate::config::TomlConfig; use super::{Config, Flags}; use clap::CommandFactory; use serde::Deserialize; -use std::{env, path::Path}; +use std::{ + env, + fs::{remove_file, File}, + io::Write, + path::Path, +}; fn parse(config: &str) -> Config { Config::parse_inner(&["check".to_owned(), "--config=/does/not/exist".to_owned()], |&_| { @@ -196,3 +201,19 @@ fn rust_optimize() { fn invalid_rust_optimize() { parse("rust.optimize = \"a\""); } + +#[test] +fn verify_file_integrity() { + let config = parse(""); + + let tempfile = config.tempdir().join(".tmp-test-file"); + File::create(&tempfile).unwrap().write_all(b"dummy value").unwrap(); + assert!(tempfile.exists()); + + assert!( + config + .verify(&tempfile, "7e255dd9542648a8779268a0f268b891a198e9828e860ed23f826440e786eae5") + ); + + remove_file(tempfile).unwrap(); +}