cksum: stop checksum computation from panicking over write error

This commit is contained in:
Dorian Péron
2026-04-30 15:50:52 +02:00
committed by Sylvestre Ledru
parent 2b6ba09c4a
commit dac86df588
5 changed files with 70 additions and 45 deletions
+2 -1
View File
@@ -7,6 +7,7 @@
use std::borrow::Borrow;
use std::ffi::OsString;
use std::io;
use clap::builder::ValueParser;
use clap::{Arg, ArgAction, ArgMatches, Command, ValueHint};
@@ -213,7 +214,7 @@ pub fn checksum_main(
line_ending,
};
perform_checksum_computation(opts, files)?;
perform_checksum_computation(io::stdout(), opts, files)?;
Ok(())
}
+1
View File
@@ -7,6 +7,7 @@ common-tip = tip
common-usage = Usage
common-help = help
common-version = version
common-write-error = write error
# Common clap error messages
clap-error-unexpected-argument = { $error_word }: unexpected argument '{ $arg }' found
+1
View File
@@ -7,6 +7,7 @@ common-tip = conseil
common-usage = Utilisation
common-help = aide
common-version = version
common-write-error = erreur d'écriture
# Messages d'erreur clap communs
clap-error-unexpected-argument = { $error_word } : argument inattendu '{ $arg }' trouvé
+62 -40
View File
@@ -7,7 +7,7 @@
use std::ffi::OsStr;
use std::fs::File;
use std::io::{self, BufReader, Read, Write};
use std::io;
use std::path::Path;
use crate::checksum::{
@@ -131,12 +131,13 @@ impl OutputFormat {
}
}
fn print_legacy_checksum(
fn write_legacy_checksum(
mut w: impl io::Write,
options: &ChecksumComputeOptions,
filename: &OsStr,
sum: &DigestOutput,
size: usize,
) {
) -> io::Result<()> {
debug_assert!(options.algo_kind.is_legacy());
debug_assert!(matches!(sum, DigestOutput::U16(_) | DigestOutput::Crc(_)));
@@ -148,32 +149,40 @@ fn print_legacy_checksum(
// Print the sum
match (options.algo_kind, sum) {
(SizedAlgoKind::Sysv, DigestOutput::U16(sum)) => print!(
(SizedAlgoKind::Sysv, DigestOutput::U16(sum)) => write!(
w,
"{prefix}{sum} {}",
size.div_ceil(options.algo_kind.bitlen()),
),
)?,
(SizedAlgoKind::Bsd, DigestOutput::U16(sum)) => {
// The BSD checksum output is 5 digit integer
let bsd_width = 5;
print!(
write!(
w,
"{prefix}{sum:0bsd_width$} {:bsd_width$}",
size.div_ceil(options.algo_kind.bitlen()),
);
)?;
}
(SizedAlgoKind::Crc | SizedAlgoKind::Crc32b, DigestOutput::Crc(sum)) => {
print!("{prefix}{sum} {size}");
write!(w, "{prefix}{sum} {size}")?;
}
(algo, output) => unreachable!("Bug: Invalid legacy checksum ({algo:?}, {output:?})"),
}
// Print the filename after a space if not stdin
if escaped_filename != "-" {
print!(" ");
let _dropped_result = io::stdout().write_all(escaped_filename.as_bytes());
write!(w, " ")?;
w.write_all(escaped_filename.as_bytes())?;
}
Ok(())
}
fn print_tagged_checksum(options: &ChecksumComputeOptions, filename: &OsStr, sum: &String) {
fn write_tagged_checksum(
mut w: impl io::Write,
options: &ChecksumComputeOptions,
filename: &OsStr,
sum: &String,
) -> io::Result<()> {
let (escaped_filename, prefix) = if options.line_ending == LineEnding::Nul {
(filename.to_string_lossy().to_string(), "")
} else {
@@ -181,21 +190,22 @@ fn print_tagged_checksum(options: &ChecksumComputeOptions, filename: &OsStr, sum
};
// Print algo name and opening parenthesis.
print!("{prefix}{} (", options.algo_kind.to_tag());
write!(w, "{prefix}{} (", options.algo_kind.to_tag())?;
// Print filename
let _dropped_result = io::stdout().write_all(escaped_filename.as_bytes());
w.write_all(escaped_filename.as_bytes())?;
// Print closing parenthesis and sum
print!(") = {sum}");
write!(w, ") = {sum}")
}
fn print_untagged_checksum(
fn write_untagged_checksum(
mut w: impl io::Write,
options: &ChecksumComputeOptions,
filename: &OsStr,
sum: &String,
reading_mode: ReadingMode,
) {
) -> io::Result<()> {
let (escaped_filename, prefix) = if options.line_ending == LineEnding::Nul {
(filename.to_string_lossy().to_string(), "")
} else {
@@ -203,10 +213,10 @@ fn print_untagged_checksum(
};
// Print checksum and reading mode flag
print!("{prefix}{sum} {}", reading_mode.as_char());
write!(w, "{prefix}{sum} {}", reading_mode.as_char())?;
// Print filename
let _dropped_result = io::stdout().write_all(escaped_filename.as_bytes());
w.write_all(escaped_filename.as_bytes())
}
/// Calculate checksum
@@ -215,8 +225,13 @@ fn print_untagged_checksum(
///
/// * `options` - CLI options for the assigning checksum algorithm
/// * `files` - A iterator of [`OsStr`] which is a bunch of files that are using for calculating checksum
pub fn perform_checksum_computation<'a, I>(options: ChecksumComputeOptions, files: I) -> UResult<()>
pub fn perform_checksum_computation<'a, W, I>(
mut w: W,
options: ChecksumComputeOptions,
files: I,
) -> UResult<()>
where
W: io::Write,
I: Iterator<Item = &'a OsStr>,
{
let mut files = files.peekable();
@@ -239,11 +254,11 @@ where
}
// Handle the file input
let mut file = BufReader::with_capacity(
let mut file = io::BufReader::with_capacity(
READ_BUFFER_SIZE,
if filename == "-" {
stdin_buf = io::stdin();
Box::new(stdin_buf) as Box<dyn Read>
Box::new(stdin_buf) as Box<dyn io::Read>
} else {
file_buf = match File::open(filepath) {
Ok(file) => file,
@@ -252,7 +267,7 @@ where
continue;
}
};
Box::new(file_buf) as Box<dyn Read>
Box::new(file_buf) as Box<dyn io::Read>
},
);
@@ -275,30 +290,37 @@ where
match options.output_format {
OutputFormat::Raw => {
// Cannot handle multiple files anyway, output immediately.
digest_output.write_raw(io::stdout())?;
digest_output
.write_raw(&mut w)
.map_err(ChecksumError::Write)?;
return Ok(());
}
OutputFormat::Legacy => {
print_legacy_checksum(&options, filename, &digest_output, sz);
}
OutputFormat::Tagged(digest_format) => {
print_tagged_checksum(
&options,
filename,
&encode_sum(digest_output, digest_format)?,
);
}
OutputFormat::Untagged(digest_format, reading_mode) => {
print_untagged_checksum(
&options,
filename,
&encode_sum(digest_output, digest_format)?,
reading_mode,
);
write_legacy_checksum(&mut w, &options, filename, &digest_output, sz)
.map_err(ChecksumError::Write)?;
}
OutputFormat::Tagged(digest_format) => write_tagged_checksum(
&mut w,
&options,
filename,
&encode_sum(digest_output, digest_format)?,
)
.map_err(ChecksumError::Write)?,
OutputFormat::Untagged(digest_format, reading_mode) => write_untagged_checksum(
&mut w,
&options,
filename,
&encode_sum(digest_output, digest_format)?,
reading_mode,
)
.map_err(ChecksumError::Write)?,
}
print!("{}", options.line_ending);
write!(w, "{}", options.line_ending).map_err(ChecksumError::Write)?;
}
if options.line_ending != LineEnding::Newline {
w.flush().map_err(ChecksumError::Write)?;
}
Ok(())
}
+4 -4
View File
@@ -12,12 +12,12 @@ use std::num::IntErrorKind;
use os_display::Quotable;
use thiserror::Error;
use crate::error::{UError, UResult};
use crate::show_error;
use crate::error::{UError, UResult, strip_errno};
use crate::sum::{
Blake2b, Blake3, Bsd, CRC32B, Crc, Digest, DigestOutput, DigestWriter, Md5, Sha1, Sha3_224,
Sha3_256, Sha3_384, Sha3_512, Sha224, Sha256, Sha384, Sha512, Shake128, Shake256, Sm3, SysV,
};
use crate::{show_error, translate};
pub mod compute;
pub mod validate;
@@ -432,8 +432,8 @@ pub enum ChecksumError {
NeedAlgorithmToHash,
#[error("unknown algorithm: {0}: clap should have prevented this case")]
UnknownAlgorithm(String),
#[error("")]
Io(#[from] io::Error),
#[error("{}: {}", translate!("common-write-error"), strip_errno(.0))]
Write(io::Error),
}
impl UError for ChecksumError {