mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-30 20:47:46 +00:00
checksum: split treatment of algo-based and non-algo based into separate functions
This commit is contained in:
parent
df16c1c655
commit
10a9b0bfbf
1 changed files with 102 additions and 70 deletions
|
@ -464,6 +464,9 @@ impl LineInfo {
|
||||||
|
|
||||||
let mut r = *regex;
|
let mut r = *regex;
|
||||||
if !algo_based {
|
if !algo_based {
|
||||||
|
// The cached regex ensures that when processing non-algo based regexes,
|
||||||
|
// its cannot be changed (can't have single and double space regexes
|
||||||
|
// used in the same file).
|
||||||
if cached_regex.is_some() {
|
if cached_regex.is_some() {
|
||||||
r = cached_regex.unwrap();
|
r = cached_regex.unwrap();
|
||||||
} else {
|
} else {
|
||||||
|
@ -636,13 +639,101 @@ fn identify_algo_name_and_length(
|
||||||
Some((algorithm, bits))
|
Some((algorithm, bits))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given a filename and an algorithm, compute the digest and compare it with
|
||||||
|
/// the expected one.
|
||||||
|
fn compute_and_check_digest_from_file(
|
||||||
|
filename: &[u8],
|
||||||
|
expected_checksum: &str,
|
||||||
|
mut algo: HashAlgorithm,
|
||||||
|
opts: ChecksumOptions,
|
||||||
|
) -> Result<(), LineCheckError> {
|
||||||
|
let (filename_to_check_unescaped, prefix) = unescape_filename(filename);
|
||||||
|
let real_filename_to_check = os_str_from_bytes(&filename_to_check_unescaped)?;
|
||||||
|
|
||||||
|
// Open the input file
|
||||||
|
let file_to_check = get_file_to_check(&real_filename_to_check, opts)?;
|
||||||
|
let mut file_reader = BufReader::new(file_to_check);
|
||||||
|
|
||||||
|
// Read the file and calculate the checksum
|
||||||
|
let create_fn = &mut algo.create_fn;
|
||||||
|
let mut digest = create_fn();
|
||||||
|
let (calculated_checksum, _) =
|
||||||
|
digest_reader(&mut digest, &mut file_reader, opts.binary, algo.bits).unwrap();
|
||||||
|
|
||||||
|
// Do the checksum validation
|
||||||
|
let checksum_correct = expected_checksum == calculated_checksum;
|
||||||
|
print_file_report(
|
||||||
|
std::io::stdout(),
|
||||||
|
filename,
|
||||||
|
FileChecksumResult::from_bool(checksum_correct),
|
||||||
|
prefix,
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
|
||||||
|
if checksum_correct {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(LineCheckError::DigestMismatch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check a digest checksum with non-algo based pre-treatment.
|
||||||
|
fn process_algo_based_line(
|
||||||
|
line_info: &LineInfo,
|
||||||
|
cli_algo_name: Option<&str>,
|
||||||
|
opts: ChecksumOptions,
|
||||||
|
) -> Result<(), LineCheckError> {
|
||||||
|
let filename_to_check = line_info.filename.as_slice();
|
||||||
|
let expected_checksum =
|
||||||
|
get_expected_digest_as_hex_string(line_info).ok_or(LineCheckError::ImproperlyFormatted)?;
|
||||||
|
|
||||||
|
let (algo_name, algo_bitlen) = identify_algo_name_and_length(line_info, cli_algo_name)
|
||||||
|
.ok_or(LineCheckError::ImproperlyFormatted)?;
|
||||||
|
|
||||||
|
let algo = detect_algo(&algo_name, algo_bitlen)?;
|
||||||
|
|
||||||
|
compute_and_check_digest_from_file(filename_to_check, &expected_checksum, algo, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check a digest checksum with non-algo based pre-treatment.
|
||||||
|
fn process_non_algo_based_line(
|
||||||
|
i: usize,
|
||||||
|
line_info: &LineInfo,
|
||||||
|
cli_algo_name: &str,
|
||||||
|
cli_algo_length: Option<usize>,
|
||||||
|
opts: ChecksumOptions,
|
||||||
|
) -> Result<(), LineCheckError> {
|
||||||
|
let mut filename_to_check = line_info.filename.as_slice();
|
||||||
|
if filename_to_check.starts_with(b"*") && i == 0 && line_info.regex_str() == SINGLE_SPACE_REGEX
|
||||||
|
{
|
||||||
|
// Remove the leading asterisk if present - only for the first line
|
||||||
|
filename_to_check = &filename_to_check[1..];
|
||||||
|
}
|
||||||
|
let expected_checksum =
|
||||||
|
get_expected_digest_as_hex_string(line_info).ok_or(LineCheckError::ImproperlyFormatted)?;
|
||||||
|
|
||||||
|
// When a specific algorithm name is input, use it and use the provided bits
|
||||||
|
// except when dealing with blake2b, where we will detect the length
|
||||||
|
let (algo_name, algo_bitlen) = if cli_algo_name == ALGORITHM_OPTIONS_BLAKE2B {
|
||||||
|
// division by 2 converts the length of the Blake2b checksum from hexadecimal
|
||||||
|
// characters to bytes, as each byte is represented by two hexadecimal characters.
|
||||||
|
let length = Some(expected_checksum.len() / 2);
|
||||||
|
(ALGORITHM_OPTIONS_BLAKE2B.to_string(), length)
|
||||||
|
} else {
|
||||||
|
(cli_algo_name.to_lowercase(), cli_algo_length)
|
||||||
|
};
|
||||||
|
|
||||||
|
let algo = detect_algo(&algo_name, algo_bitlen)?;
|
||||||
|
|
||||||
|
compute_and_check_digest_from_file(filename_to_check, &expected_checksum, algo, opts)
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses a checksum line, detect the algorithm to use, read the file and produce
|
/// Parses a checksum line, detect the algorithm to use, read the file and produce
|
||||||
/// its digest, and compare it to the expected value.
|
/// its digest, and compare it to the expected value.
|
||||||
///
|
///
|
||||||
/// Returns `Ok(bool)` if the comparison happened, bool indicates if the digest
|
/// Returns `Ok(bool)` if the comparison happened, bool indicates if the digest
|
||||||
/// matched the expected.
|
/// matched the expected.
|
||||||
/// If the comparison didn't happen, return a `LineChecksumError`.
|
/// If the comparison didn't happen, return a `LineChecksumError`.
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn process_checksum_line(
|
fn process_checksum_line(
|
||||||
filename_input: &OsStr,
|
filename_input: &OsStr,
|
||||||
line: &OsStr,
|
line: &OsStr,
|
||||||
|
@ -654,82 +745,23 @@ fn process_checksum_line(
|
||||||
) -> Result<(), LineCheckError> {
|
) -> Result<(), LineCheckError> {
|
||||||
let line_bytes = os_str_as_bytes(line)?;
|
let line_bytes = os_str_as_bytes(line)?;
|
||||||
|
|
||||||
// early return on empty or commented lines.
|
// Early return on empty or commented lines.
|
||||||
if line.is_empty() || line_bytes.starts_with(b"#") {
|
if line.is_empty() || line_bytes.starts_with(b"#") {
|
||||||
return Err(LineCheckError::Skipped);
|
return Err(LineCheckError::Skipped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use `LineInfo` to extract the data of a line.
|
||||||
|
// Then, depending on its format, apply a different pre-treatment.
|
||||||
if let Some(line_info) = LineInfo::parse(line, cached_regex) {
|
if let Some(line_info) = LineInfo::parse(line, cached_regex) {
|
||||||
// The cached regex ensures that when processing non-algo based regexes,
|
if line_info.is_algo_based() {
|
||||||
// its cannot be changed (can't have single and double space regexes
|
process_algo_based_line(&line_info, cli_algo_name, opts)
|
||||||
// used in the same file).
|
} else if let Some(cli_algo) = cli_algo_name {
|
||||||
if cached_regex.is_none() && !line_info.is_algo_based() {
|
// If we match a non-algo based regex, we expect a cli argument
|
||||||
let _ = cached_regex.insert(line_info.regex);
|
// to give us the algorithm to use
|
||||||
}
|
process_non_algo_based_line(i, &line_info, cli_algo, cli_algo_length, opts)
|
||||||
|
|
||||||
let mut filename_to_check = line_info.filename.as_slice();
|
|
||||||
|
|
||||||
if filename_to_check.starts_with(b"*")
|
|
||||||
&& i == 0
|
|
||||||
&& line_info.regex_str() == SINGLE_SPACE_REGEX
|
|
||||||
{
|
|
||||||
// Remove the leading asterisk if present - only for the first line
|
|
||||||
filename_to_check = &filename_to_check[1..];
|
|
||||||
}
|
|
||||||
|
|
||||||
let expected_checksum = get_expected_digest_as_hex_string(&line_info)
|
|
||||||
.ok_or(LineCheckError::ImproperlyFormatted)?;
|
|
||||||
|
|
||||||
// If the algo_name is provided, we use it, otherwise we try to detect it
|
|
||||||
let (algo_name, length) = if line_info.is_algo_based() {
|
|
||||||
identify_algo_name_and_length(&line_info, cli_algo_name)
|
|
||||||
.ok_or(LineCheckError::ImproperlyFormatted)?
|
|
||||||
} else if let Some(a) = cli_algo_name {
|
|
||||||
// When a specific algorithm name is input, use it and use the provided bits
|
|
||||||
// except when dealing with blake2b, where we will detect the length
|
|
||||||
if cli_algo_name == Some(ALGORITHM_OPTIONS_BLAKE2B) {
|
|
||||||
// division by 2 converts the length of the Blake2b checksum from hexadecimal
|
|
||||||
// characters to bytes, as each byte is represented by two hexadecimal characters.
|
|
||||||
let length = Some(expected_checksum.len() / 2);
|
|
||||||
(ALGORITHM_OPTIONS_BLAKE2B.to_string(), length)
|
|
||||||
} else {
|
|
||||||
(a.to_lowercase(), cli_algo_length)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Default case if no algorithm is specified and non-algo based format is matched
|
// We have no clue of what algorithm to use
|
||||||
return Err(LineCheckError::ImproperlyFormatted);
|
return Err(LineCheckError::ImproperlyFormatted);
|
||||||
};
|
|
||||||
|
|
||||||
let mut algo = detect_algo(&algo_name, length)?;
|
|
||||||
|
|
||||||
let (filename_to_check_unescaped, prefix) = unescape_filename(filename_to_check);
|
|
||||||
|
|
||||||
let real_filename_to_check = os_str_from_bytes(&filename_to_check_unescaped)?;
|
|
||||||
|
|
||||||
// manage the input file
|
|
||||||
let file_to_check = get_file_to_check(&real_filename_to_check, opts)?;
|
|
||||||
let mut file_reader = BufReader::new(file_to_check);
|
|
||||||
|
|
||||||
// Read the file and calculate the checksum
|
|
||||||
let create_fn = &mut algo.create_fn;
|
|
||||||
let mut digest = create_fn();
|
|
||||||
let (calculated_checksum, _) =
|
|
||||||
digest_reader(&mut digest, &mut file_reader, opts.binary, algo.bits).unwrap();
|
|
||||||
|
|
||||||
// Do the checksum validation
|
|
||||||
let checksum_correct = expected_checksum == calculated_checksum;
|
|
||||||
print_file_report(
|
|
||||||
std::io::stdout(),
|
|
||||||
filename_to_check,
|
|
||||||
FileChecksumResult::from_bool(checksum_correct),
|
|
||||||
prefix,
|
|
||||||
opts,
|
|
||||||
);
|
|
||||||
|
|
||||||
if checksum_correct {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(LineCheckError::DigestMismatch)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if opts.warn {
|
if opts.warn {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue