Adds plumbing for conv=sparse. Not impl.

- conv=sparse option requires knowledge of File/Stdout to change
  behaviour.
- Unclear how best to impl this.
- Possible option: develop 4 versions of dd<X,Y> for each valid pair of { File, Stdin,
  Stdout }.
This commit is contained in:
Tyler 2021-04-22 10:56:33 -07:00
parent b461f102cc
commit 5aabdf854d
3 changed files with 105 additions and 21 deletions

View file

@ -18,14 +18,18 @@ mod parseargs;
mod conversion_tables;
use conversion_tables::*;
use std::convert::TryInto;
use std::error::Error;
use std::fs::File;
use std::fs::{
File, OpenOptions,
};
use getopts;
use std::io::{
self, Read, Write,
Seek,
};
use std::sync::mpsc;
use std::thread;
use getopts;
const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION";
const SUMMARY: &str = "convert, and optionally copy, a file";
@ -218,10 +222,16 @@ impl Output<File> {
let obs = parseargs::parse_obs(matches)?;
let cf = parseargs::parse_conv_flag_output(matches)?;
if let Some(fname) = matches.opt_str("if")
if let Some(fname) = matches.opt_str("of")
{
let dst = OpenOptions::new()
.write(true)
.create(!cf.nocreat)
.truncate(!cf.notrunc)
.open(fname)?;
Ok(Output {
dst: File::open(fname)?,
dst,
obs,
cf,
})
@ -233,6 +243,32 @@ impl Output<File> {
}
}
impl<W: Write> Seek for Output<W>
{
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>
{
unimplemented!()
}
}
// impl Seek for Output<io::Stdout>
// {
// fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>
// {
// // Default method. Called when output dst not backed by a traditional file and
// // should not be seeked (eg. stdout)
// Ok(0)
// }
// }
//
// impl Seek for Output<File>
// {
// fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>
// {
// self.dst.seek(pos)
// }
// }
impl<W: Write> Write for Output<W>
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize>
@ -246,6 +282,11 @@ impl<W: Write> Write for Output<W>
}
}
fn is_sparse(buf: &[u8]) -> bool
{
buf.iter().all(| &e | e == 0)
}
fn gen_prog_updater(rx: mpsc::Receiver<usize>) -> impl Fn() -> ()
{
move || {
@ -290,8 +331,9 @@ fn dd<R: Read, W: Write>(mut i: Input<R>, mut o: Output<W>) -> Result<(usize, us
loop
{
let mut buf = vec![DEFAULT_FILL_BYTE; o.obs];
let r_len =
match i.fill_n(&mut buf, o.obs)? {
// Read
let r_len = match i.fill_n(&mut buf, o.obs)? {
SrcStat::Read(len) =>
{
bytes_in += len;
@ -301,11 +343,19 @@ fn dd<R: Read, W: Write>(mut i: Input<R>, mut o: Output<W>) -> Result<(usize, us
break,
};
let w_len = o.write(&buf[..r_len])?;
// TODO: Some flag (sync?) controls this behaviour
// o.flush()?;
// Write
let w_len = if o.cf.sparse && is_sparse(&buf)
{
let seek_amt: i64 = r_len.try_into()?;
o.seek(io::SeekFrom::Current(seek_amt))?;
r_len
}
else
{
o.write(&buf[..r_len])?
};
// Prog
bytes_out += w_len;
if let Some(prog_tx) = &prog_tx
@ -314,6 +364,13 @@ fn dd<R: Read, W: Write>(mut i: Input<R>, mut o: Output<W>) -> Result<(usize, us
}
}
// TODO: Also ensure file metadata is written when fsync option is specified. When _wouldn't_ this happen?
// See fs::File::sync_all && fs::File::sync_data methods!
if o.cf.fsync || o.cf.fdatasync
{
o.flush()?;
}
Ok((bytes_in, bytes_out))
}

View file

@ -17,6 +17,7 @@ pub enum ParseError
MultipleFmtTable,
MultipleUCaseLCase,
MultipleBlockUnblock,
MultipleExclNoCreat,
ConvFlagNoMatch(String),
NoMatchingMultiplier(String),
MultiplierStringContainsNoValue(String),
@ -274,11 +275,9 @@ fn parse_conv_opts(matches: &getopts::Matches) -> Result<Vec<ConvFlag>, ParseErr
if let Some(comma_str) = matches.opt_str("conv")
{
println!("Parsing conv: {}", comma_str);
for s in comma_str.split(",")
{
let flag = s.parse()?;
println!("found flag: {:?}", &flag);
flags.push(flag);
}
}
@ -409,9 +408,23 @@ pub fn parse_conv_flag_output(matches: &getopts::Matches) -> Result<ConvFlagOutp
ConvFlag::Sparse =>
sparse = true,
ConvFlag::Excl =>
excl = true,
if !nocreat
{
excl = true;
}
else
{
return Err(ParseError::MultipleExclNoCreat);
},
ConvFlag::NoCreat =>
nocreat = true,
if !excl
{
nocreat = true;
}
else
{
return Err(ParseError::MultipleExclNoCreat);
},
ConvFlag::NoTrunc =>
notrunc = true,
ConvFlag::FDataSync =>

View file

@ -76,6 +76,20 @@ fn cfi_block_error()
let cfi_parsed = parse_conv_flag_input(&matches).unwrap();
}
#[test]
#[should_panic]
fn cfi_creat_error()
{
let args = vec![
String::from("dd"),
String::from("--conv=excl,nocreat"),
];
let matches = build_app!().parse(args);
let cfi_parsed = parse_conv_flag_output(&matches).unwrap();
}
#[test]
fn parse_cfi_token_ibm()
{