From 6f6f6251e920cb6f1290f62bb2142a313cc82f98 Mon Sep 17 00:00:00 2001 From: Tyler Date: Wed, 18 Aug 2021 17:08:01 -0700 Subject: [PATCH] dd: apply @miDeb patch to address issue #2572 --- src/uu/dd/src/dd.rs | 302 ++++++------------ .../dd/src/dd_unit_tests/conversion_tests.rs | 4 +- src/uu/dd/src/dd_unit_tests/mod.rs | 2 +- 3 files changed, 109 insertions(+), 199 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index b300dff2d..6be986649 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -275,13 +275,19 @@ impl Input { } } +trait OutputTrait: Sized + Write { + fn new(matches: &Matches) -> Result>; + fn fsync(&mut self) -> io::Result<()>; + fn fdatasync(&mut self) -> io::Result<()>; +} + struct Output { dst: W, obs: usize, cflags: OConvFlags, } -impl Output { +impl OutputTrait for Output { fn new(matches: &Matches) -> Result> { let obs = parseargs::parse_obs(matches)?; let cflags = parseargs::parse_conv_flag_output(matches)?; @@ -300,6 +306,100 @@ impl Output { } } +impl Output +where + Self: OutputTrait, +{ + fn write_blocks(&mut self, buf: Vec) -> io::Result { + let mut writes_complete = 0; + let mut writes_partial = 0; + let mut bytes_total = 0; + + for chunk in buf.chunks(self.obs) { + match self.write(chunk)? { + wlen if wlen < chunk.len() => { + writes_partial += 1; + bytes_total += wlen; + } + wlen => { + writes_complete += 1; + bytes_total += wlen; + } + } + } + + Ok(WriteStat { + writes_complete, + writes_partial, + bytes_total: bytes_total.try_into().unwrap_or(0u128), + }) + } + + fn dd_out(mut self, mut i: Input) -> Result<(), Box> { + let mut rstat = ReadStat { + reads_complete: 0, + reads_partial: 0, + records_truncated: 0, + }; + let mut wstat = WriteStat { + writes_complete: 0, + writes_partial: 0, + bytes_total: 0, + }; + let start = time::Instant::now(); + let bsize = calc_bsize(i.ibs, self.obs); + + let prog_tx = { + let (tx, rx) = mpsc::channel(); + thread::spawn(gen_prog_updater(rx, i.print_level)); + tx + }; + + while below_count_limit(&i.count, &rstat, &wstat) { + // Read/Write + let loop_bsize = calc_loop_bsize(&i.count, &rstat, &wstat, i.ibs, bsize); + match read_helper(&mut i, loop_bsize)? { + ( + ReadStat { + reads_complete: 0, + reads_partial: 0, + .. + }, + _, + ) => break, + (rstat_update, buf) => { + let wstat_update = self.write_blocks(buf)?; + + rstat += rstat_update; + wstat += wstat_update; + } + }; + // Update Prog + prog_tx.send(ProgUpdate { + read_stat: rstat, + write_stat: wstat, + duration: start.elapsed(), + })?; + } + + if self.cflags.fsync { + self.fsync()?; + } else if self.cflags.fdatasync { + self.fdatasync()?; + } + + match i.print_level { + Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {} + _ => print_transfer_stats(&ProgUpdate { + read_stat: rstat, + write_stat: wstat, + duration: start.elapsed(), + }), + } + Ok(()) + } +} + #[cfg(target_os = "linux")] fn make_linux_oflags(oflags: &OFlags) -> Option { let mut flag = 0; @@ -340,7 +440,7 @@ fn make_linux_oflags(oflags: &OFlags) -> Option { } } -impl Output { +impl OutputTrait for Output { fn new(matches: &Matches) -> Result> { fn open_dst(path: &Path, cflags: &OConvFlags, oflags: &OFlags) -> Result { let mut opts = OpenOptions::new(); @@ -430,62 +530,6 @@ impl Write for Output { } } -impl Output { - /// Write all data in the given buffer in writes of size obs. - fn write_blocks(&mut self, buf: Vec) -> io::Result { - let mut writes_complete = 0; - let mut writes_partial = 0; - let mut bytes_total = 0; - - for chunk in buf.chunks(self.obs) { - match self.write(chunk)? { - wlen if wlen < chunk.len() => { - writes_partial += 1; - bytes_total += wlen; - } - wlen => { - writes_complete += 1; - bytes_total += wlen; - } - } - } - - Ok(WriteStat { - writes_complete, - writes_partial, - bytes_total: bytes_total.try_into().unwrap_or(0u128), - }) - } -} - -impl Output { - /// Write all data in the given buffer in writes of size obs. - fn write_blocks(&mut self, buf: Vec) -> io::Result { - let mut writes_complete = 0; - let mut writes_partial = 0; - let mut bytes_total = 0; - - for chunk in buf.chunks(self.obs) { - match self.write(chunk)? { - wlen if wlen < chunk.len() => { - writes_partial += 1; - bytes_total += wlen; - } - wlen => { - writes_complete += 1; - bytes_total += wlen; - } - } - } - - Ok(WriteStat { - writes_complete, - writes_partial, - bytes_total: bytes_total.try_into().unwrap_or(0u128), - }) - } -} - /// Splits the content of buf into cbs-length blocks /// Appends padding as specified by conv=block and cbs=N /// Expects ascii encoded data @@ -827,140 +871,6 @@ fn below_count_limit(count: &Option, rstat: &ReadStat, wstat: &WriteS } } -/// Perform the copy/convert operations. Stdout version -/// Note: The body of this function should be kept identical to dd_fileout. This is definitely a problem from a maintenance perspective -/// and should be addressed (TODO). The problem exists because some of dd's functionality depends on whether the output is a file or stdout. -fn dd_stdout(mut i: Input, mut o: Output) -> Result<(), Box> { - let mut rstat = ReadStat { - reads_complete: 0, - reads_partial: 0, - records_truncated: 0, - }; - let mut wstat = WriteStat { - writes_complete: 0, - writes_partial: 0, - bytes_total: 0, - }; - let start = time::Instant::now(); - let bsize = calc_bsize(i.ibs, o.obs); - - let prog_tx = { - let (tx, rx) = mpsc::channel(); - thread::spawn(gen_prog_updater(rx, i.print_level)); - tx - }; - - while below_count_limit(&i.count, &rstat, &wstat) { - // Read/Write - let loop_bsize = calc_loop_bsize(&i.count, &rstat, &wstat, i.ibs, bsize); - match read_helper(&mut i, loop_bsize)? { - ( - ReadStat { - reads_complete: 0, - reads_partial: 0, - .. - }, - _, - ) => break, - (rstat_update, buf) => { - let wstat_update = o.write_blocks(buf)?; - - rstat += rstat_update; - wstat += wstat_update; - } - }; - // Update Prog - prog_tx.send(ProgUpdate { - read_stat: rstat, - write_stat: wstat, - duration: start.elapsed(), - })?; - } - - if o.cflags.fsync { - o.fsync()?; - } else if o.cflags.fdatasync { - o.fdatasync()?; - } - - match i.print_level { - Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {} - _ => print_transfer_stats(&ProgUpdate { - read_stat: rstat, - write_stat: wstat, - duration: start.elapsed(), - }), - } - Ok(()) -} - -/// Perform the copy/convert operations. File backed output version -/// Note: The body of this function should be kept identical to dd_stdout. This is definitely a problem from a maintenance perspective -/// and should be addressed (TODO). The problem exists because some of dd's functionality depends on whether the output is a file or stdout. -fn dd_fileout(mut i: Input, mut o: Output) -> Result<(), Box> { - let mut rstat = ReadStat { - reads_complete: 0, - reads_partial: 0, - records_truncated: 0, - }; - let mut wstat = WriteStat { - writes_complete: 0, - writes_partial: 0, - bytes_total: 0, - }; - let start = time::Instant::now(); - let bsize = calc_bsize(i.ibs, o.obs); - - let prog_tx = { - let (tx, rx) = mpsc::channel(); - thread::spawn(gen_prog_updater(rx, i.print_level)); - tx - }; - - while below_count_limit(&i.count, &rstat, &wstat) { - // Read/Write - let loop_bsize = calc_loop_bsize(&i.count, &rstat, &wstat, i.ibs, bsize); - match read_helper(&mut i, loop_bsize)? { - ( - ReadStat { - reads_complete: 0, - reads_partial: 0, - .. - }, - _, - ) => break, - (rstat_update, buf) => { - let wstat_update = o.write_blocks(buf)?; - - rstat += rstat_update; - wstat += wstat_update; - } - }; - // Update Prog - prog_tx.send(ProgUpdate { - read_stat: rstat, - write_stat: wstat, - duration: start.elapsed(), - })?; - } - - if o.cflags.fsync { - o.fsync()?; - } else if o.cflags.fdatasync { - o.fdatasync()?; - } - - match i.print_level { - Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {} - _ => print_transfer_stats(&ProgUpdate { - read_stat: rstat, - write_stat: wstat, - duration: start.elapsed(), - }), - } - Ok(()) -} - fn append_dashes_if_not_present(mut acc: Vec, mut s: String) -> Vec { if !s.starts_with("--") && !s.starts_with('-') { s.insert_str(0, "--"); @@ -1009,7 +919,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let (i, o) = unpack_or_rtn!(Input::::new(&matches), Output::::new(&matches)); - dd_fileout(i, o) + o.dd_out(i) } (false, true) => { let (i, o) = unpack_or_rtn!( @@ -1017,7 +927,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { Output::::new(&matches) ); - dd_fileout(i, o) + o.dd_out(i) } (true, false) => { let (i, o) = unpack_or_rtn!( @@ -1025,7 +935,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { Output::::new(&matches) ); - dd_stdout(i, o) + o.dd_out(i) } (false, false) => { let (i, o) = unpack_or_rtn!( @@ -1033,7 +943,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { Output::::new(&matches) ); - dd_stdout(i, o) + o.dd_out(i) } }; match result { diff --git a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs index c4515d3a2..9255a1a89 100644 --- a/src/uu/dd/src/dd_unit_tests/conversion_tests.rs +++ b/src/uu/dd/src/dd_unit_tests/conversion_tests.rs @@ -153,7 +153,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() { cflags: OConvFlags::default(), }; - dd_fileout(i, o).unwrap(); + o.dd_out(i).unwrap(); // EBCDIC->ASCII let test_name = "all-valid-ebcdic-to-ascii"; @@ -175,7 +175,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() { cflags: OConvFlags::default(), }; - dd_fileout(i, o).unwrap(); + o.dd_out(i).unwrap(); // Final Comparison let res = File::open(&tmp_fname_ea).unwrap(); diff --git a/src/uu/dd/src/dd_unit_tests/mod.rs b/src/uu/dd/src/dd_unit_tests/mod.rs index 27b5a18ad..9641c9bba 100644 --- a/src/uu/dd/src/dd_unit_tests/mod.rs +++ b/src/uu/dd/src/dd_unit_tests/mod.rs @@ -67,7 +67,7 @@ macro_rules! make_spec_test ( #[test] fn $test_id() { - dd_fileout($i,$o).unwrap(); + $o.dd_out($i).unwrap(); let res = File::open($tmp_fname).unwrap(); // Check test file isn't empty (unless spec file is too)