Fix type-error when calling parse_size from dd

This commit is contained in:
Omer Tuchfeld 2022-02-06 21:21:07 +01:00
parent b6c952c46e
commit 88dfb8d374
3 changed files with 80 additions and 55 deletions

View file

@ -83,8 +83,8 @@ pub struct OFlags {
/// then becomes Bytes(N)
#[derive(Debug, PartialEq)]
pub enum CountType {
Reads(usize),
Bytes(usize),
Reads(u64),
Bytes(u64),
}
#[derive(Debug)]

View file

@ -37,9 +37,8 @@ use std::time;
use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
use gcd::Gcd;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError};
use uucore::show_error;
use uucore::InvalidEncodingHandling;
use uucore::error::{FromIo, UResult};
use uucore::{show_error, InvalidEncodingHandling};
const ABOUT: &str = "copy, and optionally convert, a file system resource";
const BUF_INIT_BYTE: u8 = 0xDD;
@ -75,11 +74,13 @@ impl Input<io::Stdin> {
};
if let Some(amt) = skip {
let num_bytes_read = i
.force_fill(amt.try_into().unwrap())
.map_err_context(|| "failed to read input".to_string())?;
if num_bytes_read < amt {
show_error!("'standard input': cannot skip to specified offset");
if let Err(e) = i.read_skip(amt) {
if let io::ErrorKind::UnexpectedEof = e.kind() {
show_error!("'standard input': cannot skip to specified offset");
} else {
return io::Result::Err(e)
.map_err_context(|| "I/O error while skipping".to_string());
}
}
}
@ -148,9 +149,6 @@ impl Input<File> {
};
if let Some(amt) = skip {
let amt: u64 = amt
.try_into()
.map_err(|_| USimpleError::new(1, "failed to parse seek amount"))?;
src.seek(io::SeekFrom::Start(amt))
.map_err_context(|| "failed to seek in input file".to_string())?;
}
@ -262,19 +260,18 @@ impl<R: Read> Input<R> {
})
}
/// Read the specified number of bytes from this reader.
///
/// On success, this method returns the number of bytes read. If
/// this reader has fewer than `n` bytes available, then it reads
/// as many as possible. In that case, this method returns a
/// number less than `n`.
///
/// # Errors
///
/// If there is a problem reading.
fn force_fill(&mut self, n: u64) -> std::io::Result<usize> {
let mut buf = vec![];
self.take(n).read_to_end(&mut buf)
/// Skips amount_to_read bytes from the Input by copying into a sink
fn read_skip(&mut self, amount_to_read: u64) -> std::io::Result<()> {
let copy_result = io::copy(&mut self.src.by_ref().take(amount_to_read), &mut io::sink());
if let Ok(n) = copy_result {
if n != amount_to_read {
io::Result::Err(io::Error::new(io::ErrorKind::UnexpectedEof, ""))
} else {
Ok(())
}
} else {
io::Result::Err(copy_result.unwrap_err())
}
}
}
@ -301,8 +298,7 @@ impl OutputTrait for Output<io::Stdout> {
// stdout is not seekable, so we just write null bytes.
if let Some(amt) = seek {
let bytes = vec![b'\0'; amt];
dst.write_all(&bytes)
io::copy(&mut io::repeat(0u8).take(amt as u64), &mut dst)
.map_err_context(|| String::from("write error"))?;
}
@ -526,7 +522,7 @@ impl OutputTrait for Output<File> {
// Instead, we suppress the error by calling
// `Result::ok()`. This matches the behavior of GNU `dd`
// when given the command-line argument `of=/dev/null`.
let i = seek.unwrap_or(0).try_into().unwrap();
let i = seek.unwrap_or(0);
if !cflags.notrunc {
dst.set_len(i).ok();
}
@ -658,15 +654,14 @@ fn calc_loop_bsize(
) -> usize {
match count {
Some(CountType::Reads(rmax)) => {
let rmax: u64 = (*rmax).try_into().unwrap();
let rsofar = rstat.reads_complete + rstat.reads_partial;
let rremain: usize = (rmax - rsofar).try_into().unwrap();
cmp::min(ideal_bsize, rremain * ibs)
let rremain = rmax - rsofar;
cmp::min(ideal_bsize as u64, rremain * ibs as u64) as usize
}
Some(CountType::Bytes(bmax)) => {
let bmax: u128 = (*bmax).try_into().unwrap();
let bremain: usize = (bmax - wstat.bytes_total).try_into().unwrap();
cmp::min(ideal_bsize, bremain)
let bremain: u128 = bmax - wstat.bytes_total;
cmp::min(ideal_bsize as u128, bremain as u128) as usize
}
None => ideal_bsize,
}
@ -677,7 +672,7 @@ fn calc_loop_bsize(
fn below_count_limit(count: &Option<CountType>, rstat: &ReadStat, wstat: &WriteStat) -> bool {
match count {
Some(CountType::Reads(n)) => {
let n = (*n).try_into().unwrap();
let n = *n;
rstat.reads_complete + rstat.reads_partial <= n
}
Some(CountType::Bytes(n)) => {

View file

@ -31,6 +31,10 @@ pub enum ParseError {
BlockUnblockWithoutCBS,
StatusLevelNotRecognized(String),
Unimplemented(String),
BsOutOfRange,
IbsOutOfRange,
ObsOutOfRange,
CbsOutOfRange,
}
impl ParseError {
@ -48,6 +52,10 @@ impl ParseError {
Self::BlockUnblockWithoutCBS => Self::BlockUnblockWithoutCBS,
Self::StatusLevelNotRecognized(_) => Self::StatusLevelNotRecognized(s),
Self::Unimplemented(_) => Self::Unimplemented(s),
Self::BsOutOfRange => Self::BsOutOfRange,
Self::IbsOutOfRange => Self::IbsOutOfRange,
Self::ObsOutOfRange => Self::ObsOutOfRange,
Self::CbsOutOfRange => Self::CbsOutOfRange,
}
}
}
@ -92,6 +100,18 @@ impl std::fmt::Display for ParseError {
Self::StatusLevelNotRecognized(arg) => {
write!(f, "status=LEVEL not recognized -> {}", arg)
}
ParseError::BsOutOfRange => {
write!(f, "bs=N cannot fit into memory")
}
ParseError::IbsOutOfRange => {
write!(f, "ibs=N cannot fit into memory")
}
ParseError::ObsOutOfRange => {
write!(f, "ibs=N cannot fit into memory")
}
ParseError::CbsOutOfRange => {
write!(f, "cbs=N cannot fit into memory")
}
Self::Unimplemented(arg) => {
write!(f, "feature not implemented on this system -> {}", arg)
}
@ -334,7 +354,7 @@ fn show_zero_multiplier_warning() {
}
/// Parse bytes using str::parse, then map error if needed.
fn parse_bytes_only(s: &str) -> Result<usize, ParseError> {
fn parse_bytes_only(s: &str) -> Result<u64, ParseError> {
s.parse()
.map_err(|_| ParseError::MultiplierStringParseFailure(s.to_string()))
}
@ -364,7 +384,7 @@ fn parse_bytes_only(s: &str) -> Result<usize, ParseError> {
/// assert_eq!(parse_bytes_no_x("2b").unwrap(), 2 * 512);
/// assert_eq!(parse_bytes_no_x("2k").unwrap(), 2 * 1024);
/// ```
fn parse_bytes_no_x(s: &str) -> Result<usize, ParseError> {
fn parse_bytes_no_x(s: &str) -> Result<u64, ParseError> {
let (num, multiplier) = match (s.find('c'), s.rfind('w'), s.rfind('b')) {
(None, None, None) => match uucore::parse_size::parse_size(s) {
Ok(n) => (n, 1),
@ -387,7 +407,7 @@ fn parse_bytes_no_x(s: &str) -> Result<usize, ParseError> {
/// Parse byte and multiplier like 512, 5KiB, or 1G.
/// Uses uucore::parse_size, and adds the 'w' and 'c' suffixes which are mentioned
/// in dd's info page.
fn parse_bytes_with_opt_multiplier(s: &str) -> Result<usize, ParseError> {
fn parse_bytes_with_opt_multiplier(s: &str) -> Result<u64, ParseError> {
// TODO On my Linux system, there seems to be a maximum block size of 4096 bytes:
//
// $ printf "%0.sa" {1..10000} | dd bs=4095 count=1 status=none | wc -c
@ -420,9 +440,27 @@ fn parse_bytes_with_opt_multiplier(s: &str) -> Result<usize, ParseError> {
pub fn parse_ibs(matches: &Matches) -> Result<usize, ParseError> {
if let Some(mixed_str) = matches.value_of(options::BS) {
parse_bytes_with_opt_multiplier(mixed_str)
parse_bytes_with_opt_multiplier(mixed_str)?
.try_into()
.map_err(|_| ParseError::BsOutOfRange)
} else if let Some(mixed_str) = matches.value_of(options::IBS) {
parse_bytes_with_opt_multiplier(mixed_str)
parse_bytes_with_opt_multiplier(mixed_str)?
.try_into()
.map_err(|_| ParseError::IbsOutOfRange)
} else {
Ok(512)
}
}
pub fn parse_obs(matches: &Matches) -> Result<usize, ParseError> {
if let Some(mixed_str) = matches.value_of("bs") {
parse_bytes_with_opt_multiplier(mixed_str)?
.try_into()
.map_err(|_| ParseError::BsOutOfRange)
} else if let Some(mixed_str) = matches.value_of("obs") {
parse_bytes_with_opt_multiplier(mixed_str)?
.try_into()
.map_err(|_| ParseError::ObsOutOfRange)
} else {
Ok(512)
}
@ -430,7 +468,9 @@ pub fn parse_ibs(matches: &Matches) -> Result<usize, ParseError> {
fn parse_cbs(matches: &Matches) -> Result<Option<usize>, ParseError> {
if let Some(s) = matches.value_of(options::CBS) {
let bytes = parse_bytes_with_opt_multiplier(s)?;
let bytes = parse_bytes_with_opt_multiplier(s)?
.try_into()
.map_err(|_| ParseError::CbsOutOfRange)?;
Ok(Some(bytes))
} else {
Ok(None)
@ -447,16 +487,6 @@ pub(crate) fn parse_status_level(matches: &Matches) -> Result<Option<StatusLevel
}
}
pub fn parse_obs(matches: &Matches) -> Result<usize, ParseError> {
if let Some(mixed_str) = matches.value_of("bs") {
parse_bytes_with_opt_multiplier(mixed_str)
} else if let Some(mixed_str) = matches.value_of("obs") {
parse_bytes_with_opt_multiplier(mixed_str)
} else {
Ok(512)
}
}
fn parse_ctable(fmt: Option<ConvFlag>, case: Option<ConvFlag>) -> Option<&'static ConversionTable> {
fn parse_conv_and_case_table(
fmt: &ConvFlag,
@ -715,13 +745,13 @@ pub fn parse_skip_amt(
ibs: &usize,
iflags: &IFlags,
matches: &Matches,
) -> Result<Option<usize>, ParseError> {
) -> Result<Option<u64>, ParseError> {
if let Some(amt) = matches.value_of(options::SKIP) {
let n = parse_bytes_with_opt_multiplier(amt)?;
if iflags.skip_bytes {
Ok(Some(n))
} else {
Ok(Some(ibs * n))
Ok(Some(*ibs as u64 * n))
}
} else {
Ok(None)
@ -733,13 +763,13 @@ pub fn parse_seek_amt(
obs: &usize,
oflags: &OFlags,
matches: &Matches,
) -> Result<Option<usize>, ParseError> {
) -> Result<Option<u64>, ParseError> {
if let Some(amt) = matches.value_of(options::SEEK) {
let n = parse_bytes_with_opt_multiplier(amt)?;
if oflags.seek_bytes {
Ok(Some(n))
} else {
Ok(Some(obs * n))
Ok(Some(*obs as u64 * n))
}
} else {
Ok(None)