mirror of
https://github.com/uutils/coreutils
synced 2025-01-19 00:24:13 +00:00
Merge pull request #6390 from sylvestre/cksum-check
cksum: implement check (Closes: #5705)
This commit is contained in:
commit
b718f954e8
12 changed files with 855 additions and 116 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2494,6 +2494,7 @@ version = "0.0.26"
|
|||
dependencies = [
|
||||
"clap",
|
||||
"hex",
|
||||
"regex",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
|
|
|
@ -16,8 +16,9 @@ path = "src/cksum.rs"
|
|||
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
uucore = { workspace = true, features = ["encoding", "sum"] }
|
||||
uucore = { workspace = true, features = ["checksum", "encoding", "sum"] }
|
||||
hex = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
|
||||
[[bin]]
|
||||
name = "cksum"
|
||||
|
|
|
@ -5,15 +5,18 @@
|
|||
|
||||
// spell-checker:ignore (ToDO) fname, algo
|
||||
use clap::{crate_version, value_parser, Arg, ArgAction, Command};
|
||||
use hex::decode;
|
||||
use hex::encode;
|
||||
use regex::Regex;
|
||||
use std::error::Error;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::Display;
|
||||
use std::fs::File;
|
||||
use std::io::BufRead;
|
||||
use std::io::{self, stdin, stdout, BufReader, Read, Write};
|
||||
use std::iter;
|
||||
use std::path::Path;
|
||||
use uucore::checksum::cksum_output;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::set_exit_code;
|
||||
use uucore::{
|
||||
encoding,
|
||||
error::{FromIo, UError, UResult, USimpleError},
|
||||
|
@ -40,6 +43,20 @@ const ALGORITHM_OPTIONS_SHA512: &str = "sha512";
|
|||
const ALGORITHM_OPTIONS_BLAKE2B: &str = "blake2b";
|
||||
const ALGORITHM_OPTIONS_SM3: &str = "sm3";
|
||||
|
||||
const SUPPORTED_ALGO: [&str; 11] = [
|
||||
ALGORITHM_OPTIONS_SYSV,
|
||||
ALGORITHM_OPTIONS_BSD,
|
||||
ALGORITHM_OPTIONS_CRC,
|
||||
ALGORITHM_OPTIONS_MD5,
|
||||
ALGORITHM_OPTIONS_SHA1,
|
||||
ALGORITHM_OPTIONS_SHA224,
|
||||
ALGORITHM_OPTIONS_SHA256,
|
||||
ALGORITHM_OPTIONS_SHA384,
|
||||
ALGORITHM_OPTIONS_SHA512,
|
||||
ALGORITHM_OPTIONS_BLAKE2B,
|
||||
ALGORITHM_OPTIONS_SM3,
|
||||
];
|
||||
|
||||
#[derive(Debug)]
|
||||
enum CkSumError {
|
||||
RawMultipleFiles,
|
||||
|
@ -73,10 +90,10 @@ impl Display for CkSumError {
|
|||
}
|
||||
|
||||
fn detect_algo(
|
||||
program: &str,
|
||||
algo: &str,
|
||||
length: Option<usize>,
|
||||
) -> (&'static str, Box<dyn Digest + 'static>, usize) {
|
||||
match program {
|
||||
match algo {
|
||||
ALGORITHM_OPTIONS_SYSV => (
|
||||
ALGORITHM_OPTIONS_SYSV,
|
||||
Box::new(SYSV::new()) as Box<dyn Digest>,
|
||||
|
@ -205,7 +222,7 @@ where
|
|||
ALGORITHM_OPTIONS_SYSV | ALGORITHM_OPTIONS_BSD => {
|
||||
sum_hex.parse::<u16>().unwrap().to_be_bytes().to_vec()
|
||||
}
|
||||
_ => decode(sum_hex).unwrap(),
|
||||
_ => hex::decode(sum_hex).unwrap(),
|
||||
};
|
||||
// Cannot handle multiple files anyway, output immediately.
|
||||
stdout().write_all(&bytes)?;
|
||||
|
@ -214,7 +231,8 @@ where
|
|||
OutputFormat::Hexadecimal => sum_hex,
|
||||
OutputFormat::Base64 => match options.algo_name {
|
||||
ALGORITHM_OPTIONS_CRC | ALGORITHM_OPTIONS_SYSV | ALGORITHM_OPTIONS_BSD => sum_hex,
|
||||
_ => encoding::encode(encoding::Format::Base64, &decode(sum_hex).unwrap()).unwrap(),
|
||||
_ => encoding::encode(encoding::Format::Base64, &hex::decode(sum_hex).unwrap())
|
||||
.unwrap(),
|
||||
},
|
||||
};
|
||||
// The BSD checksum output is 5 digit integer
|
||||
|
@ -299,7 +317,7 @@ fn digest_read<T: Read>(
|
|||
// Assume it's SHAKE. result_str() doesn't work with shake (as of 8/30/2016)
|
||||
let mut bytes = vec![0; (output_bits + 7) / 8];
|
||||
digest.hash_finalize(&mut bytes);
|
||||
Ok((encode(bytes), output_size))
|
||||
Ok((hex::encode(bytes), output_size))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,6 +330,7 @@ mod options {
|
|||
pub const RAW: &str = "raw";
|
||||
pub const BASE64: &str = "base64";
|
||||
pub const CHECK: &str = "check";
|
||||
pub const STRICT: &str = "strict";
|
||||
pub const TEXT: &str = "text";
|
||||
pub const BINARY: &str = "binary";
|
||||
}
|
||||
|
@ -353,17 +372,45 @@ fn had_reset(args: &[String]) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
/// Calculates the length of the digest for the given algorithm.
|
||||
fn calculate_blake2b_length(length: usize) -> UResult<Option<usize>> {
|
||||
match length {
|
||||
0 => Ok(None),
|
||||
n if n % 8 != 0 => {
|
||||
uucore::show_error!("invalid length: \u{2018}{length}\u{2019}");
|
||||
Err(io::Error::new(io::ErrorKind::InvalidInput, "length is not a multiple of 8").into())
|
||||
}
|
||||
n if n > 512 => {
|
||||
uucore::show_error!("invalid length: \u{2018}{length}\u{2019}");
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"maximum digest length for \u{2018}BLAKE2b\u{2019} is 512 bits",
|
||||
)
|
||||
.into())
|
||||
}
|
||||
n => {
|
||||
// Divide by 8, as our blake2b implementation expects bytes instead of bits.
|
||||
if n == 512 {
|
||||
// When length is 512, it is blake2b's default.
|
||||
// So, don't show it
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(n / 8))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* cksum has a bunch of legacy behavior.
|
||||
* We handle this in this function to make sure they are self contained
|
||||
* and "easier" to understand
|
||||
*/
|
||||
fn handle_tag_text_binary_flags(matches: &clap::ArgMatches, check: bool) -> UResult<(bool, bool)> {
|
||||
fn handle_tag_text_binary_flags(matches: &clap::ArgMatches) -> UResult<(bool, bool)> {
|
||||
let untagged: bool = matches.get_flag(options::UNTAGGED);
|
||||
let tag: bool = matches.get_flag(options::TAG);
|
||||
let tag: bool = tag || !untagged;
|
||||
|
||||
let text_flag: bool = matches.get_flag(options::TEXT);
|
||||
let binary_flag: bool = matches.get_flag(options::BINARY);
|
||||
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
|
@ -371,70 +418,209 @@ fn handle_tag_text_binary_flags(matches: &clap::ArgMatches, check: bool) -> URes
|
|||
|
||||
let asterisk: bool = prompt_asterisk(tag, binary_flag, had_reset);
|
||||
|
||||
if (binary_flag || text_flag) && check {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"the --binary and --text options are meaningless when verifying checksums",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
Ok((tag, asterisk))
|
||||
}
|
||||
|
||||
/***
|
||||
* Do the checksum validation (can be strict or not)
|
||||
*/
|
||||
fn perform_checksum_validation<'a, I>(
|
||||
files: I,
|
||||
strict: bool,
|
||||
algo_name_input: Option<&str>,
|
||||
) -> UResult<()>
|
||||
where
|
||||
I: Iterator<Item = &'a OsStr>,
|
||||
{
|
||||
// Regexp to handle the two input formats:
|
||||
// 1. <algo>[-<bits>] (<filename>) = <checksum>
|
||||
// algo must be uppercase or b (for blake2b)
|
||||
// 2. <checksum> [* ]<filename>
|
||||
let regex_pattern = r"^\s*\\?(?P<algo>(?:[A-Z0-9]+|BLAKE2b))(?:-(?P<bits>\d+))?\s?\((?P<filename1>.*)\) = (?P<checksum1>[a-fA-F0-9]+)$|^(?P<checksum2>[a-fA-F0-9]+)\s[* ](?P<filename2>.*)";
|
||||
let re = Regex::new(regex_pattern).unwrap();
|
||||
|
||||
// if cksum has several input files, it will print the result for each file
|
||||
for filename_input in files {
|
||||
let mut bad_format = 0;
|
||||
let mut failed_cksum = 0;
|
||||
let mut failed_open_file = 0;
|
||||
let mut properly_formatted = false;
|
||||
let input_is_stdin = filename_input == OsStr::new("-");
|
||||
|
||||
let file: Box<dyn Read> = if input_is_stdin {
|
||||
Box::new(stdin()) // Use stdin if "-" is specified
|
||||
} else {
|
||||
match File::open(filename_input) {
|
||||
Ok(f) => Box::new(f),
|
||||
Err(_) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!(
|
||||
"{}: No such file or directory",
|
||||
filename_input.to_string_lossy()
|
||||
),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
};
|
||||
let reader = BufReader::new(file);
|
||||
|
||||
// for each line in the input, check if it is a valid checksum line
|
||||
for line in reader.lines() {
|
||||
let line = line.unwrap_or_else(|_| String::new());
|
||||
if let Some(caps) = re.captures(&line) {
|
||||
properly_formatted = true;
|
||||
|
||||
// Determine what kind of file input we had
|
||||
// we need it for case "--check -a sm3 <file>" when <file> is
|
||||
// <algo>[-<bits>] (<filename>) = <checksum>
|
||||
let algo_based_format =
|
||||
caps.name("filename1").is_some() && caps.name("checksum1").is_some();
|
||||
|
||||
let filename_to_check = caps
|
||||
.name("filename1")
|
||||
.or(caps.name("filename2"))
|
||||
.unwrap()
|
||||
.as_str();
|
||||
let expected_checksum = caps
|
||||
.name("checksum1")
|
||||
.or(caps.name("checksum2"))
|
||||
.unwrap()
|
||||
.as_str();
|
||||
|
||||
// If the algo_name is provided, we use it, otherwise we try to detect it
|
||||
let (algo_name, length) = if algo_based_format {
|
||||
// When the algo-based format is matched, extract details from regex captures
|
||||
let algorithm = caps.name("algo").map_or("", |m| m.as_str()).to_lowercase();
|
||||
if !SUPPORTED_ALGO.contains(&algorithm.as_str()) {
|
||||
// Not supported algo, leave early
|
||||
properly_formatted = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
let bits = caps.name("bits").map_or(Some(None), |m| {
|
||||
let bits_value = m.as_str().parse::<usize>().unwrap();
|
||||
if bits_value % 8 == 0 {
|
||||
Some(Some(bits_value / 8))
|
||||
} else {
|
||||
properly_formatted = false;
|
||||
None // Return None to signal a parsing or divisibility issue
|
||||
}
|
||||
});
|
||||
|
||||
if bits.is_none() {
|
||||
// If bits is None, we have a parsing or divisibility issue
|
||||
// Exit the loop outside of the closure
|
||||
continue;
|
||||
}
|
||||
|
||||
(algorithm, bits.unwrap())
|
||||
} else if let Some(a) = algo_name_input {
|
||||
// When a specific algorithm name is input, use it and default bits to None
|
||||
(a.to_lowercase(), None)
|
||||
} else {
|
||||
// Default case if no algorithm is specified and non-algo based format is matched
|
||||
(String::new(), None)
|
||||
};
|
||||
|
||||
if algo_based_format && algo_name_input.map_or(false, |input| algo_name != input) {
|
||||
bad_format += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if algo_name.is_empty() {
|
||||
// we haven't been able to detect the algo name. No point to continue
|
||||
properly_formatted = false;
|
||||
continue;
|
||||
}
|
||||
let (_, mut algo, bits) = detect_algo(&algo_name, length);
|
||||
|
||||
// manage the input file
|
||||
let file_to_check: Box<dyn Read> = if filename_to_check == "-" {
|
||||
Box::new(stdin()) // Use stdin if "-" is specified in the checksum file
|
||||
} else {
|
||||
match File::open(filename_to_check) {
|
||||
Ok(f) => Box::new(f),
|
||||
Err(err) => {
|
||||
// yes, we have both stderr and stdout here
|
||||
show!(err.map_err_context(|| filename_to_check.to_string()));
|
||||
println!("{}: FAILED open or read", filename_to_check);
|
||||
failed_open_file += 1;
|
||||
// we could not open the file but we want to continue
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut file_reader = BufReader::new(file_to_check);
|
||||
// Read the file and calculate the checksum
|
||||
let (calculated_checksum, _) =
|
||||
digest_read(&mut algo, &mut file_reader, bits).unwrap();
|
||||
|
||||
// Do the checksum validation
|
||||
if expected_checksum == calculated_checksum {
|
||||
println!("{}: OK", filename_to_check);
|
||||
} else {
|
||||
println!("{}: FAILED", filename_to_check);
|
||||
failed_cksum += 1;
|
||||
}
|
||||
} else {
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
bad_format += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// not a single line correctly formatted found
|
||||
// return an error
|
||||
if !properly_formatted {
|
||||
let filename = filename_input.to_string_lossy();
|
||||
uucore::show_error!(
|
||||
"{}: no properly formatted checksum lines found",
|
||||
if input_is_stdin {
|
||||
"standard input"
|
||||
} else {
|
||||
&filename
|
||||
}
|
||||
.maybe_quote()
|
||||
);
|
||||
set_exit_code(1);
|
||||
}
|
||||
// strict means that we should have an exit code.
|
||||
if strict && bad_format > 0 {
|
||||
set_exit_code(1);
|
||||
}
|
||||
|
||||
// if we have any failed checksum verification, we set an exit code
|
||||
if failed_cksum > 0 || failed_open_file > 0 {
|
||||
set_exit_code(1);
|
||||
}
|
||||
|
||||
// if any incorrectly formatted line, show it
|
||||
cksum_output(bad_format, failed_cksum, failed_open_file);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
|
||||
let algo_name: &str = match matches.get_one::<String>(options::ALGORITHM) {
|
||||
Some(v) => v,
|
||||
None => ALGORITHM_OPTIONS_CRC,
|
||||
};
|
||||
|
||||
let input_length = matches.get_one::<usize>(options::LENGTH);
|
||||
let check = matches.get_flag(options::CHECK);
|
||||
|
||||
let length = if let Some(length) = input_length {
|
||||
match length.to_owned() {
|
||||
0 => None,
|
||||
n if n % 8 != 0 => {
|
||||
// GNU's implementation seem to use these quotation marks
|
||||
// in their error messages, so we do the same.
|
||||
uucore::show_error!("invalid length: \u{2018}{length}\u{2019}");
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"length is not a multiple of 8",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
n if n > 512 => {
|
||||
uucore::show_error!("invalid length: \u{2018}{length}\u{2019}");
|
||||
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"maximum digest length for \u{2018}BLAKE2b\u{2019} is 512 bits",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
n => {
|
||||
if algo_name != ALGORITHM_OPTIONS_BLAKE2B {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"--length is only supported with --algorithm=blake2b",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
// Divide by 8, as our blake2b implementation expects bytes
|
||||
// instead of bits.
|
||||
Some(n / 8)
|
||||
let algo_name: &str = match matches.get_one::<String>(options::ALGORITHM) {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
if check {
|
||||
// if we are doing a --check, we should not default to crc
|
||||
""
|
||||
} else {
|
||||
ALGORITHM_OPTIONS_CRC
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (tag, asterisk) = handle_tag_text_binary_flags(&matches, check)?;
|
||||
|
||||
if ["bsd", "crc", "sysv"].contains(&algo_name) && check {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
|
@ -443,6 +629,51 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
.into());
|
||||
}
|
||||
|
||||
let input_length = matches.get_one::<usize>(options::LENGTH);
|
||||
|
||||
let length = match input_length {
|
||||
Some(length) => {
|
||||
if algo_name == ALGORITHM_OPTIONS_BLAKE2B {
|
||||
calculate_blake2b_length(*length)?
|
||||
} else {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"--length is only supported with --algorithm=blake2b",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
if check {
|
||||
let text_flag: bool = matches.get_flag(options::TEXT);
|
||||
let binary_flag: bool = matches.get_flag(options::BINARY);
|
||||
let strict = matches.get_flag(options::STRICT);
|
||||
|
||||
if (binary_flag || text_flag) && check {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"the --binary and --text options are meaningless when verifying checksums",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
// Determine the appropriate algorithm option to pass
|
||||
let algo_option = if algo_name.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(algo_name)
|
||||
};
|
||||
|
||||
// Execute the checksum validation based on the presence of files or the use of stdin
|
||||
return match matches.get_many::<String>(options::FILE) {
|
||||
Some(files) => perform_checksum_validation(files.map(OsStr::new), strict, algo_option),
|
||||
None => perform_checksum_validation(iter::once(OsStr::new("-")), strict, algo_option),
|
||||
};
|
||||
}
|
||||
|
||||
let (tag, asterisk) = handle_tag_text_binary_flags(&matches)?;
|
||||
|
||||
let (name, algo, bits) = detect_algo(algo_name, length);
|
||||
|
||||
let output_format = if matches.get_flag(options::RAW) {
|
||||
|
@ -490,19 +721,7 @@ pub fn uu_app() -> Command {
|
|||
.short('a')
|
||||
.help("select the digest type to use. See DIGEST below")
|
||||
.value_name("ALGORITHM")
|
||||
.value_parser([
|
||||
ALGORITHM_OPTIONS_SYSV,
|
||||
ALGORITHM_OPTIONS_BSD,
|
||||
ALGORITHM_OPTIONS_CRC,
|
||||
ALGORITHM_OPTIONS_MD5,
|
||||
ALGORITHM_OPTIONS_SHA1,
|
||||
ALGORITHM_OPTIONS_SHA224,
|
||||
ALGORITHM_OPTIONS_SHA256,
|
||||
ALGORITHM_OPTIONS_SHA384,
|
||||
ALGORITHM_OPTIONS_SHA512,
|
||||
ALGORITHM_OPTIONS_BLAKE2B,
|
||||
ALGORITHM_OPTIONS_SM3,
|
||||
]),
|
||||
.value_parser(SUPPORTED_ALGO),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::UNTAGGED)
|
||||
|
@ -535,12 +754,12 @@ pub fn uu_app() -> Command {
|
|||
.help("emit a raw binary digest, not hexadecimal")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
/*.arg(
|
||||
.arg(
|
||||
Arg::new(options::STRICT)
|
||||
.long(options::STRICT)
|
||||
.help("exit non-zero for improperly formatted checksum lines")
|
||||
.action(ArgAction::SetTrue),
|
||||
)*/
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::CHECK)
|
||||
.short('c')
|
||||
|
@ -580,6 +799,7 @@ pub fn uu_app() -> Command {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::had_reset;
|
||||
use crate::calculate_blake2b_length;
|
||||
use crate::prompt_asterisk;
|
||||
|
||||
#[test]
|
||||
|
@ -646,4 +866,16 @@ mod tests {
|
|||
assert!(prompt_asterisk(false, true, false));
|
||||
assert!(!prompt_asterisk(false, false, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_calculate_length() {
|
||||
assert_eq!(calculate_blake2b_length(256).unwrap(), Some(32));
|
||||
assert_eq!(calculate_blake2b_length(512).unwrap(), None);
|
||||
assert_eq!(calculate_blake2b_length(256).unwrap(), Some(32));
|
||||
calculate_blake2b_length(255).unwrap_err();
|
||||
|
||||
calculate_blake2b_length(33).unwrap_err();
|
||||
|
||||
calculate_blake2b_length(513).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ path = "src/hashsum.rs"
|
|||
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
uucore = { workspace = true, features = ["sum"] }
|
||||
uucore = { workspace = true, features = ["checksum", "sum"] }
|
||||
memchr = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
|
|
|
@ -12,7 +12,6 @@ use clap::{Arg, ArgMatches, Command};
|
|||
use hex::encode;
|
||||
use regex::Captures;
|
||||
use regex::Regex;
|
||||
use std::cmp::Ordering;
|
||||
use std::error::Error;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fs::File;
|
||||
|
@ -20,6 +19,8 @@ use std::io::{self, stdin, BufRead, BufReader, Read};
|
|||
use std::iter;
|
||||
use std::num::ParseIntError;
|
||||
use std::path::Path;
|
||||
use uucore::checksum::cksum_output;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::USimpleError;
|
||||
use uucore::error::{set_exit_code, FromIo, UError, UResult};
|
||||
use uucore::sum::{
|
||||
|
@ -27,7 +28,6 @@ use uucore::sum::{
|
|||
Sha3_384, Sha3_512, Sha512, Shake128, Shake256,
|
||||
};
|
||||
use uucore::util_name;
|
||||
use uucore::{display::Quotable, show_warning_caps};
|
||||
use uucore::{format_usage, help_about, help_usage};
|
||||
|
||||
const NAME: &str = "hashsum";
|
||||
|
@ -835,35 +835,7 @@ where
|
|||
}
|
||||
|
||||
if !options.status && !skip_summary {
|
||||
match bad_format.cmp(&1) {
|
||||
Ordering::Equal => {
|
||||
show_warning_caps!("{} line is improperly formatted", bad_format);
|
||||
}
|
||||
Ordering::Greater => {
|
||||
show_warning_caps!("{} lines are improperly formatted", bad_format);
|
||||
}
|
||||
Ordering::Less => {}
|
||||
};
|
||||
|
||||
match failed_cksum.cmp(&1) {
|
||||
Ordering::Equal => {
|
||||
show_warning_caps!("{} computed checksum did NOT match", failed_cksum);
|
||||
}
|
||||
Ordering::Greater => {
|
||||
show_warning_caps!("{} computed checksums did NOT match", failed_cksum);
|
||||
}
|
||||
Ordering::Less => {}
|
||||
};
|
||||
|
||||
match failed_open_file.cmp(&1) {
|
||||
Ordering::Equal => {
|
||||
show_warning_caps!("{} listed file could not be read", failed_open_file);
|
||||
}
|
||||
Ordering::Greater => {
|
||||
show_warning_caps!("{} listed files could not be read", failed_open_file);
|
||||
}
|
||||
Ordering::Less => {}
|
||||
}
|
||||
cksum_output(bad_format, failed_cksum, failed_open_file);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -75,6 +75,7 @@ default = []
|
|||
# * non-default features
|
||||
backup-control = []
|
||||
colors = []
|
||||
checksum = []
|
||||
encoding = ["data-encoding", "data-encoding-macro", "z85", "thiserror"]
|
||||
entries = ["libc"]
|
||||
fs = ["dunce", "libc", "winapi-util", "windows-sys"]
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#[cfg(feature = "backup-control")]
|
||||
pub mod backup_control;
|
||||
#[cfg(feature = "checksum")]
|
||||
pub mod checksum;
|
||||
#[cfg(feature = "colors")]
|
||||
pub mod colors;
|
||||
#[cfg(feature = "encoding")]
|
||||
|
|
27
src/uucore/src/lib/features/checksum.rs
Normal file
27
src/uucore/src/lib/features/checksum.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
// This file is part of the uutils coreutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use crate::show_warning_caps;
|
||||
|
||||
#[allow(clippy::comparison_chain)]
|
||||
pub fn cksum_output(bad_format: i32, failed_cksum: i32, failed_open_file: i32) {
|
||||
if bad_format == 1 {
|
||||
show_warning_caps!("{} line is improperly formatted", bad_format);
|
||||
} else if bad_format > 1 {
|
||||
show_warning_caps!("{} lines are improperly formatted", bad_format);
|
||||
}
|
||||
|
||||
if failed_cksum == 1 {
|
||||
show_warning_caps!("{} computed checksum did NOT match", failed_cksum);
|
||||
} else if failed_cksum > 1 {
|
||||
show_warning_caps!("{} computed checksums did NOT match", failed_cksum);
|
||||
}
|
||||
|
||||
if failed_open_file == 1 {
|
||||
show_warning_caps!("{} listed file could not be read", failed_open_file);
|
||||
} else if failed_open_file > 1 {
|
||||
show_warning_caps!("{} listed files could not be read", failed_open_file);
|
||||
}
|
||||
}
|
|
@ -37,6 +37,8 @@ pub use crate::parser::shortcut_value_parser;
|
|||
// * feature-gated modules
|
||||
#[cfg(feature = "backup-control")]
|
||||
pub use crate::features::backup_control;
|
||||
#[cfg(feature = "checksum")]
|
||||
pub use crate::features::checksum;
|
||||
#[cfg(feature = "colors")]
|
||||
pub use crate::features::colors;
|
||||
#[cfg(feature = "encoding")]
|
||||
|
|
|
@ -80,6 +80,21 @@ fn test_nonexisting_file() {
|
|||
.stderr_contains(format!("cksum: {file_name}: No such file or directory"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonexisting_file_out() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.write(
|
||||
"f",
|
||||
"MD5 (nonexistent) = e5773576fc75ff0f8eba14f61587ae28\n",
|
||||
);
|
||||
|
||||
ucmd.arg("-c")
|
||||
.arg("f")
|
||||
.fails()
|
||||
.stdout_contains("nonexistent: FAILED open or read")
|
||||
.stderr_contains("cksum: nonexistent: No such file or directory");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_one_nonexisting_file() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
|
@ -316,6 +331,16 @@ fn test_length_with_wrong_algorithm() {
|
|||
.no_stdout()
|
||||
.stderr_contains("cksum: --length is only supported with --algorithm=blake2b")
|
||||
.code_is(1);
|
||||
|
||||
new_ucmd!()
|
||||
.arg("--length=16")
|
||||
.arg("--algorithm=md5")
|
||||
.arg("-c")
|
||||
.arg("foo.sums")
|
||||
.fails()
|
||||
.no_stdout()
|
||||
.stderr_contains("cksum: --length is only supported with --algorithm=blake2b")
|
||||
.code_is(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -325,7 +350,7 @@ fn test_length_not_supported() {
|
|||
.arg("lorem_ipsum.txt")
|
||||
.fails()
|
||||
.no_stdout()
|
||||
.stderr_is_fixture("unsupported_length.expected")
|
||||
.stderr_contains("--length is only supported with --algorithm=blake2b")
|
||||
.code_is(1);
|
||||
}
|
||||
|
||||
|
@ -337,7 +362,9 @@ fn test_length() {
|
|||
.arg("lorem_ipsum.txt")
|
||||
.arg("alice_in_wonderland.txt")
|
||||
.succeeds()
|
||||
.stdout_is_fixture("supported_length.expected");
|
||||
.stdout_contains(
|
||||
"BLAKE2b-16 (lorem_ipsum.txt) = 7e2f\nBLAKE2b-16 (alice_in_wonderland.txt) = a546",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -474,11 +501,23 @@ fn test_all_algorithms_fail_on_folder() {
|
|||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_dev_null() {
|
||||
fn test_check_error_incorrect_format() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
at.write("checksum", "e5773576fc75ff0f8eba14f61587ae28 README.md");
|
||||
|
||||
at.touch("f");
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-c")
|
||||
.arg("checksum")
|
||||
.fails()
|
||||
.stderr_contains("no properly formatted checksum lines found");
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_dev_null() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
|
@ -490,6 +529,33 @@ fn test_dev_null() {
|
|||
.stdout_contains("d41d8cd98f00b204e9800998ecf8427e ");
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_blake2b_512() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
at.touch("f");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-a")
|
||||
.arg("blake2b")
|
||||
.arg("-l512")
|
||||
.arg("f")
|
||||
.succeeds()
|
||||
.stdout_contains("BLAKE2b (f) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce");
|
||||
|
||||
// test also the read
|
||||
at.write("checksum", "BLAKE2b (f) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce");
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("checksum")
|
||||
.succeeds()
|
||||
.stdout_contains("f: OK");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reset_binary() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
@ -663,3 +729,442 @@ fn test_conflicting_options() {
|
|||
)
|
||||
.code_is(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_algo_err() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
let at = &scene.fixtures;
|
||||
|
||||
at.touch("f");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-a")
|
||||
.arg("sm3")
|
||||
.arg("--check")
|
||||
.arg("f")
|
||||
.fails()
|
||||
.no_stdout()
|
||||
.stderr_contains("cksum: f: no properly formatted checksum lines found")
|
||||
.code_is(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_pipe() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
let at = &scene.fixtures;
|
||||
|
||||
at.touch("f");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("-")
|
||||
.pipe_in("f")
|
||||
.fails()
|
||||
.no_stdout()
|
||||
.stderr_contains("cksum: 'standard input': no properly formatted checksum lines found")
|
||||
.code_is(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cksum_check_empty_line() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
at.touch("f");
|
||||
at.write("CHECKSUM", "\
|
||||
SHA384 (f) = 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b\n\
|
||||
BLAKE2b (f) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce\n\
|
||||
BLAKE2b-384 (f) = b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100\n\
|
||||
SM3 (f) = 1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b\n\n");
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("CHECKSUM")
|
||||
.succeeds()
|
||||
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n")
|
||||
.stderr_does_not_contain("line is improperly formatted");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cksum_check_space() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
at.touch("f");
|
||||
at.write("CHECKSUM", "\
|
||||
SHA384 (f) = 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b\n\
|
||||
BLAKE2b (f) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce\n\
|
||||
BLAKE2b-384 (f) = b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100\n\
|
||||
SM3 (f) = 1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b\n \n");
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("CHECKSUM")
|
||||
.succeeds()
|
||||
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n")
|
||||
.stderr_contains("line is improperly formatted");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cksum_check_leading_info() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
at.touch("f");
|
||||
at.write("CHECKSUM", "\
|
||||
\\SHA384 (f) = 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b\n\
|
||||
\\BLAKE2b (f) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce\n\
|
||||
\\BLAKE2b-384 (f) = b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100\n\
|
||||
\\SM3 (f) = 1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b\n");
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("CHECKSUM")
|
||||
.succeeds()
|
||||
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cksum_check() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
at.touch("f");
|
||||
at.write("CHECKSUM", "\
|
||||
SHA384 (f) = 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b\n\
|
||||
BLAKE2b (f) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce\n\
|
||||
BLAKE2b-384 (f) = b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100\n\
|
||||
SM3 (f) = 1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b\n");
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("CHECKSUM")
|
||||
.succeeds()
|
||||
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n")
|
||||
.stderr_does_not_contain("line is improperly formatted");
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("--strict")
|
||||
.arg("CHECKSUM")
|
||||
.succeeds()
|
||||
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n")
|
||||
.stderr_does_not_contain("line is improperly formatted");
|
||||
// inject invalid content
|
||||
at.append("CHECKSUM", "incorrect data");
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("CHECKSUM")
|
||||
.succeeds()
|
||||
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n")
|
||||
.stderr_contains("line is improperly formatted");
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("--strict")
|
||||
.arg("CHECKSUM")
|
||||
.fails()
|
||||
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n")
|
||||
.stderr_contains("line is improperly formatted");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cksum_check_case() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
at.touch("f");
|
||||
at.write(
|
||||
"CHECKSUM",
|
||||
"Sha1 (f) = da39a3ee5e6b4b0d3255bfef95601890afd80709\n",
|
||||
);
|
||||
scene.ucmd().arg("--check").arg("CHECKSUM").fails();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cksum_check_invalid() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
let commands = [vec!["-a", "sha384"]];
|
||||
at.touch("f");
|
||||
at.touch("CHECKSUM");
|
||||
for command in &commands {
|
||||
let result = scene.ucmd().args(command).arg("f").succeeds();
|
||||
at.append("CHECKSUM", result.stdout_str());
|
||||
}
|
||||
// inject invalid content
|
||||
at.append("CHECKSUM", "again incorrect data\naze\n");
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("--strict")
|
||||
.arg("CHECKSUM")
|
||||
.fails()
|
||||
.stdout_contains("f: OK\n")
|
||||
.stderr_contains("2 lines");
|
||||
|
||||
// without strict, it passes
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("CHECKSUM")
|
||||
.succeeds()
|
||||
.stdout_contains("f: OK\n")
|
||||
.stderr_contains("2 lines");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cksum_check_failed() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
let commands = [vec!["-a", "sha384"]];
|
||||
at.touch("f");
|
||||
at.touch("CHECKSUM");
|
||||
for command in &commands {
|
||||
let result = scene.ucmd().args(command).arg("f").succeeds();
|
||||
at.append("CHECKSUM", result.stdout_str());
|
||||
}
|
||||
// inject invalid content
|
||||
at.append("CHECKSUM", "again incorrect data\naze\nSM3 (input) = 7cfb120d4fabea2a904948538a438fdb57c725157cb40b5aee8d937b8351477e\n");
|
||||
|
||||
let result = scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("--strict")
|
||||
.arg("CHECKSUM")
|
||||
.fails();
|
||||
|
||||
assert!(result
|
||||
.stderr_str()
|
||||
.contains("input: No such file or directory"));
|
||||
assert!(result
|
||||
.stderr_str()
|
||||
.contains("2 lines are improperly formatted\n"));
|
||||
assert!(result
|
||||
.stderr_str()
|
||||
.contains("1 listed file could not be read\n"));
|
||||
assert!(result.stdout_str().contains("f: OK\n"));
|
||||
|
||||
// without strict
|
||||
let result = scene.ucmd().arg("--check").arg("CHECKSUM").fails();
|
||||
|
||||
assert!(result
|
||||
.stderr_str()
|
||||
.contains("input: No such file or directory"));
|
||||
assert!(result
|
||||
.stderr_str()
|
||||
.contains("2 lines are improperly formatted\n"));
|
||||
assert!(result
|
||||
.stderr_str()
|
||||
.contains("1 listed file could not be read\n"));
|
||||
assert!(result.stdout_str().contains("f: OK\n"));
|
||||
|
||||
// tests with two files
|
||||
at.touch("CHECKSUM2");
|
||||
at.write("f2", "42");
|
||||
for command in &commands {
|
||||
let result = scene.ucmd().args(command).arg("f2").succeeds();
|
||||
at.append("CHECKSUM2", result.stdout_str());
|
||||
}
|
||||
// inject invalid content
|
||||
at.append("CHECKSUM2", "again incorrect data\naze\nSM3 (input2) = 7cfb120d4fabea2a904948538a438fdb57c725157cb40b5aee8d937b8351477e\n");
|
||||
at.append("CHECKSUM2", "again incorrect data\naze\nSM3 (input2) = 7cfb120d4fabea2a904948538a438fdb57c725157cb40b5aee8d937b8351477e\n");
|
||||
|
||||
let result = scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("CHECKSUM")
|
||||
.arg("CHECKSUM2")
|
||||
.fails();
|
||||
println!("result.stderr_str() {}", result.stderr_str());
|
||||
println!("result.stdout_str() {}", result.stdout_str());
|
||||
assert!(result
|
||||
.stderr_str()
|
||||
.contains("input2: No such file or directory"));
|
||||
assert!(result
|
||||
.stderr_str()
|
||||
.contains("4 lines are improperly formatted\n"));
|
||||
assert!(result
|
||||
.stderr_str()
|
||||
.contains("2 listed files could not be read\n"));
|
||||
assert!(result.stdout_str().contains("f: OK\n"));
|
||||
assert!(result.stdout_str().contains("2: OK\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_md5_format() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
let at = &scene.fixtures;
|
||||
at.touch("empty");
|
||||
at.write("f", "d41d8cd98f00b204e9800998ecf8427e *empty\n");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-a")
|
||||
.arg("md5")
|
||||
.arg("--check")
|
||||
.arg("f")
|
||||
.succeeds()
|
||||
.stdout_contains("empty: OK");
|
||||
|
||||
// with a second file
|
||||
at.write("not-empty", "42");
|
||||
at.write("f2", "a1d0c6e83f027327d8461063f4ac58a6 *not-empty\n");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-a")
|
||||
.arg("md5")
|
||||
.arg("--check")
|
||||
.arg("f")
|
||||
.arg("f2")
|
||||
.succeeds()
|
||||
.stdout_contains("empty: OK")
|
||||
.stdout_contains("not-empty: OK");
|
||||
}
|
||||
|
||||
// Manage the mixed behavior
|
||||
// cksum --check -a sm3 CHECKSUMS
|
||||
// when CHECKSUM contains among other lines:
|
||||
// SHA384 (input) = f392fd0ae43879ced890c665a1d47179116b5eddf6fb5b49f4982746418afdcbd54ba5eedcd422af3592f57f666da285
|
||||
#[test]
|
||||
fn test_cksum_mixed() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
let commands = [
|
||||
vec!["-a", "sha384"],
|
||||
vec!["-a", "blake2b"],
|
||||
vec!["-a", "blake2b", "-l", "384"],
|
||||
vec!["-a", "sm3"],
|
||||
];
|
||||
at.touch("f");
|
||||
at.touch("CHECKSUM");
|
||||
for command in &commands {
|
||||
let result = scene.ucmd().args(command).arg("f").succeeds();
|
||||
at.append("CHECKSUM", result.stdout_str());
|
||||
}
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("-a")
|
||||
.arg("sm3")
|
||||
.arg("CHECKSUM")
|
||||
.succeeds()
|
||||
.stdout_contains("f: OK")
|
||||
.stderr_contains("3 lines are improperly formatted");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cksum_garbage() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
// Incorrect data at the start
|
||||
at.write(
|
||||
"check-file",
|
||||
"garbage MD5 (README.md) = e5773576fc75ff0f8eba14f61587ae28",
|
||||
);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("check-file")
|
||||
.fails()
|
||||
.stderr_contains("check-file: no properly formatted checksum lines found");
|
||||
|
||||
// Incorrect data at the end
|
||||
at.write(
|
||||
"check-file",
|
||||
"MD5 (README.md) = e5773576fc75ff0f8eba14f61587ae28 garbage",
|
||||
);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("check-file")
|
||||
.fails()
|
||||
.stderr_contains("check-file: no properly formatted checksum lines found");
|
||||
}
|
||||
|
||||
#[ignore = "Should fail on bits"]
|
||||
#[test]
|
||||
fn test_md5_bits() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.write(
|
||||
"f",
|
||||
"MD5-65536 (README.md) = e5773576fc75ff0f8eba14f61587ae28\n",
|
||||
);
|
||||
|
||||
ucmd.arg("-c")
|
||||
.arg("f")
|
||||
.fails()
|
||||
.stderr_contains("f: no properly formatted checksum lines found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blake2b_bits() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.write(
|
||||
"f",
|
||||
"BLAKE2b-257 (README.md) = f9a984b70cf9a7549920864860fd1131c9fb6c0552def0b6dcce1d87b4ec4c5d\n"
|
||||
);
|
||||
|
||||
ucmd.arg("-c")
|
||||
.arg("f")
|
||||
.fails()
|
||||
.stderr_contains("f: no properly formatted checksum lines found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bsd_case() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
at.write("f", "bSD (README.md) = 0000\n");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-c")
|
||||
.arg("f")
|
||||
.fails()
|
||||
.stderr_contains("f: no properly formatted checksum lines found");
|
||||
at.write("f", "BsD (README.md) = 0000\n");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-c")
|
||||
.arg("f")
|
||||
.fails()
|
||||
.stderr_contains("f: no properly formatted checksum lines found");
|
||||
}
|
||||
|
||||
#[ignore = "Different output"]
|
||||
#[test]
|
||||
fn test_blake2d_tested_with_sha1() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.write(
|
||||
"f",
|
||||
"BLAKE2b (f) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce\n"
|
||||
);
|
||||
|
||||
ucmd.arg("-a")
|
||||
.arg("sha1")
|
||||
.arg("-c")
|
||||
.arg("f")
|
||||
.fails()
|
||||
.stderr_contains("f: no properly formatted checksum lines found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unknown_sha() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.write("f", "SHA4 (README.md) = 00000000\n");
|
||||
|
||||
ucmd.arg("-c")
|
||||
.arg("f")
|
||||
.fails()
|
||||
.stderr_contains("f: no properly formatted checksum lines found");
|
||||
}
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
BLAKE2b-16 (lorem_ipsum.txt) = 7e2f
|
||||
BLAKE2b-16 (alice_in_wonderland.txt) = a546
|
|
@ -1,2 +0,0 @@
|
|||
cksum: invalid length: ‘15’
|
||||
cksum: length is not a multiple of 8
|
Loading…
Reference in a new issue