Skip to content

Commit 10a9b0b

Browse files
committed
checksum: split treatment of algo-based and non-algo based into separate functions
1 parent df16c1c commit 10a9b0b

File tree

1 file changed

+102
-70
lines changed

1 file changed

+102
-70
lines changed

src/uucore/src/lib/features/checksum.rs

Lines changed: 102 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,9 @@ impl LineInfo {
464464

465465
let mut r = *regex;
466466
if !algo_based {
467+
// The cached regex ensures that when processing non-algo based regexes,
468+
// its cannot be changed (can't have single and double space regexes
469+
// used in the same file).
467470
if cached_regex.is_some() {
468471
r = cached_regex.unwrap();
469472
} else {
@@ -636,13 +639,101 @@ fn identify_algo_name_and_length(
636639
Some((algorithm, bits))
637640
}
638641

642+
/// Given a filename and an algorithm, compute the digest and compare it with
643+
/// the expected one.
644+
fn compute_and_check_digest_from_file(
645+
filename: &[u8],
646+
expected_checksum: &str,
647+
mut algo: HashAlgorithm,
648+
opts: ChecksumOptions,
649+
) -> Result<(), LineCheckError> {
650+
let (filename_to_check_unescaped, prefix) = unescape_filename(filename);
651+
let real_filename_to_check = os_str_from_bytes(&filename_to_check_unescaped)?;
652+
653+
// Open the input file
654+
let file_to_check = get_file_to_check(&real_filename_to_check, opts)?;
655+
let mut file_reader = BufReader::new(file_to_check);
656+
657+
// Read the file and calculate the checksum
658+
let create_fn = &mut algo.create_fn;
659+
let mut digest = create_fn();
660+
let (calculated_checksum, _) =
661+
digest_reader(&mut digest, &mut file_reader, opts.binary, algo.bits).unwrap();
662+
663+
// Do the checksum validation
664+
let checksum_correct = expected_checksum == calculated_checksum;
665+
print_file_report(
666+
std::io::stdout(),
667+
filename,
668+
FileChecksumResult::from_bool(checksum_correct),
669+
prefix,
670+
opts,
671+
);
672+
673+
if checksum_correct {
674+
Ok(())
675+
} else {
676+
Err(LineCheckError::DigestMismatch)
677+
}
678+
}
679+
680+
/// Check a digest checksum with non-algo based pre-treatment.
681+
fn process_algo_based_line(
682+
line_info: &LineInfo,
683+
cli_algo_name: Option<&str>,
684+
opts: ChecksumOptions,
685+
) -> Result<(), LineCheckError> {
686+
let filename_to_check = line_info.filename.as_slice();
687+
let expected_checksum =
688+
get_expected_digest_as_hex_string(line_info).ok_or(LineCheckError::ImproperlyFormatted)?;
689+
690+
let (algo_name, algo_bitlen) = identify_algo_name_and_length(line_info, cli_algo_name)
691+
.ok_or(LineCheckError::ImproperlyFormatted)?;
692+
693+
let algo = detect_algo(&algo_name, algo_bitlen)?;
694+
695+
compute_and_check_digest_from_file(filename_to_check, &expected_checksum, algo, opts)
696+
}
697+
698+
/// Check a digest checksum with non-algo based pre-treatment.
699+
fn process_non_algo_based_line(
700+
i: usize,
701+
line_info: &LineInfo,
702+
cli_algo_name: &str,
703+
cli_algo_length: Option<usize>,
704+
opts: ChecksumOptions,
705+
) -> Result<(), LineCheckError> {
706+
let mut filename_to_check = line_info.filename.as_slice();
707+
if filename_to_check.starts_with(b"*") && i == 0 && line_info.regex_str() == SINGLE_SPACE_REGEX
708+
{
709+
// Remove the leading asterisk if present - only for the first line
710+
filename_to_check = &filename_to_check[1..];
711+
}
712+
let expected_checksum =
713+
get_expected_digest_as_hex_string(line_info).ok_or(LineCheckError::ImproperlyFormatted)?;
714+
715+
// When a specific algorithm name is input, use it and use the provided bits
716+
// except when dealing with blake2b, where we will detect the length
717+
let (algo_name, algo_bitlen) = if cli_algo_name == ALGORITHM_OPTIONS_BLAKE2B {
718+
// division by 2 converts the length of the Blake2b checksum from hexadecimal
719+
// characters to bytes, as each byte is represented by two hexadecimal characters.
720+
let length = Some(expected_checksum.len() / 2);
721+
(ALGORITHM_OPTIONS_BLAKE2B.to_string(), length)
722+
} else {
723+
(cli_algo_name.to_lowercase(), cli_algo_length)
724+
};
725+
726+
let algo = detect_algo(&algo_name, algo_bitlen)?;
727+
728+
compute_and_check_digest_from_file(filename_to_check, &expected_checksum, algo, opts)
729+
}
730+
639731
/// Parses a checksum line, detect the algorithm to use, read the file and produce
640732
/// its digest, and compare it to the expected value.
641733
///
642734
/// Returns `Ok(bool)` if the comparison happened, bool indicates if the digest
643735
/// matched the expected.
644736
/// If the comparison didn't happen, return a `LineChecksumError`.
645-
#[allow(clippy::too_many_arguments)]
646737
fn process_checksum_line(
647738
filename_input: &OsStr,
648739
line: &OsStr,
@@ -654,82 +745,23 @@ fn process_checksum_line(
654745
) -> Result<(), LineCheckError> {
655746
let line_bytes = os_str_as_bytes(line)?;
656747

657-
// early return on empty or commented lines.
748+
// Early return on empty or commented lines.
658749
if line.is_empty() || line_bytes.starts_with(b"#") {
659750
return Err(LineCheckError::Skipped);
660751
}
661752

753+
// Use `LineInfo` to extract the data of a line.
754+
// Then, depending on its format, apply a different pre-treatment.
662755
if let Some(line_info) = LineInfo::parse(line, cached_regex) {
663-
// The cached regex ensures that when processing non-algo based regexes,
664-
// its cannot be changed (can't have single and double space regexes
665-
// used in the same file).
666-
if cached_regex.is_none() && !line_info.is_algo_based() {
667-
let _ = cached_regex.insert(line_info.regex);
668-
}
669-
670-
let mut filename_to_check = line_info.filename.as_slice();
671-
672-
if filename_to_check.starts_with(b"*")
673-
&& i == 0
674-
&& line_info.regex_str() == SINGLE_SPACE_REGEX
675-
{
676-
// Remove the leading asterisk if present - only for the first line
677-
filename_to_check = &filename_to_check[1..];
678-
}
679-
680-
let expected_checksum = get_expected_digest_as_hex_string(&line_info)
681-
.ok_or(LineCheckError::ImproperlyFormatted)?;
682-
683-
// If the algo_name is provided, we use it, otherwise we try to detect it
684-
let (algo_name, length) = if line_info.is_algo_based() {
685-
identify_algo_name_and_length(&line_info, cli_algo_name)
686-
.ok_or(LineCheckError::ImproperlyFormatted)?
687-
} else if let Some(a) = cli_algo_name {
688-
// When a specific algorithm name is input, use it and use the provided bits
689-
// except when dealing with blake2b, where we will detect the length
690-
if cli_algo_name == Some(ALGORITHM_OPTIONS_BLAKE2B) {
691-
// division by 2 converts the length of the Blake2b checksum from hexadecimal
692-
// characters to bytes, as each byte is represented by two hexadecimal characters.
693-
let length = Some(expected_checksum.len() / 2);
694-
(ALGORITHM_OPTIONS_BLAKE2B.to_string(), length)
695-
} else {
696-
(a.to_lowercase(), cli_algo_length)
697-
}
756+
if line_info.is_algo_based() {
757+
process_algo_based_line(&line_info, cli_algo_name, opts)
758+
} else if let Some(cli_algo) = cli_algo_name {
759+
// If we match a non-algo based regex, we expect a cli argument
760+
// to give us the algorithm to use
761+
process_non_algo_based_line(i, &line_info, cli_algo, cli_algo_length, opts)
698762
} else {
699-
// Default case if no algorithm is specified and non-algo based format is matched
763+
// We have no clue of what algorithm to use
700764
return Err(LineCheckError::ImproperlyFormatted);
701-
};
702-
703-
let mut algo = detect_algo(&algo_name, length)?;
704-
705-
let (filename_to_check_unescaped, prefix) = unescape_filename(filename_to_check);
706-
707-
let real_filename_to_check = os_str_from_bytes(&filename_to_check_unescaped)?;
708-
709-
// manage the input file
710-
let file_to_check = get_file_to_check(&real_filename_to_check, opts)?;
711-
let mut file_reader = BufReader::new(file_to_check);
712-
713-
// Read the file and calculate the checksum
714-
let create_fn = &mut algo.create_fn;
715-
let mut digest = create_fn();
716-
let (calculated_checksum, _) =
717-
digest_reader(&mut digest, &mut file_reader, opts.binary, algo.bits).unwrap();
718-
719-
// Do the checksum validation
720-
let checksum_correct = expected_checksum == calculated_checksum;
721-
print_file_report(
722-
std::io::stdout(),
723-
filename_to_check,
724-
FileChecksumResult::from_bool(checksum_correct),
725-
prefix,
726-
opts,
727-
);
728-
729-
if checksum_correct {
730-
Ok(())
731-
} else {
732-
Err(LineCheckError::DigestMismatch)
733765
}
734766
} else {
735767
if opts.warn {

0 commit comments

Comments
 (0)