diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 87949a08e..e6698244b 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -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 { 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 { } } +impl Seek for Output +{ + fn seek(&mut self, pos: io::SeekFrom) -> io::Result + { + unimplemented!() + } +} + +// impl Seek for Output +// { +// fn seek(&mut self, pos: io::SeekFrom) -> io::Result +// { +// // 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 +// { +// fn seek(&mut self, pos: io::SeekFrom) -> io::Result +// { +// self.dst.seek(pos) +// } +// } + impl Write for Output { fn write(&mut self, buf: &[u8]) -> io::Result @@ -246,6 +282,11 @@ impl Write for Output } } +fn is_sparse(buf: &[u8]) -> bool +{ + buf.iter().all(| &e | e == 0) +} + fn gen_prog_updater(rx: mpsc::Receiver) -> impl Fn() -> () { move || { @@ -290,22 +331,31 @@ fn dd(mut i: Input, mut o: Output) -> Result<(usize, us loop { let mut buf = vec![DEFAULT_FILL_BYTE; o.obs]; - let r_len = - match i.fill_n(&mut buf, o.obs)? { - SrcStat::Read(len) => - { - bytes_in += len; - len - }, - SrcStat::EOF => - break, + + // Read + let r_len = match i.fill_n(&mut buf, o.obs)? { + SrcStat::Read(len) => + { + bytes_in += len; + len + }, + SrcStat::EOF => + 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(mut i: Input, mut o: Output) -> 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)) } diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index b1f157da6..29f643e90 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -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, 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 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 => diff --git a/src/uu/dd/src/parseargs/test.rs b/src/uu/dd/src/parseargs/test.rs index 0a1db2868..491d3f335 100644 --- a/src/uu/dd/src/parseargs/test.rs +++ b/src/uu/dd/src/parseargs/test.rs @@ -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() {