diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 54e3190ce..448eaf937 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -469,7 +469,6 @@ impl OutputTrait for Output { let mut opts = OpenOptions::new(); opts.write(true) .create(!cflags.nocreat) - .truncate(!cflags.notrunc) .create_new(cflags.excl) .append(oflags.append); @@ -489,13 +488,13 @@ impl OutputTrait for Output { let mut dst = open_dst(Path::new(&fname), &cflags, &oflags) .map_err_context(|| format!("failed to open {}", fname.quote()))?; - if let Some(amt) = seek { - let amt: u64 = amt - .try_into() - .map_err(|_| USimpleError::new(1, "failed to parse seek amount"))?; - dst.seek(io::SeekFrom::Start(amt)) - .map_err_context(|| "failed to seek in output file".to_string())?; + let i = seek.unwrap_or(0).try_into().unwrap(); + if !cflags.notrunc { + dst.set_len(i) + .map_err_context(|| "failed to truncate output file".to_string())?; } + dst.seek(io::SeekFrom::Start(i)) + .map_err_context(|| "failed to seek in output file".to_string())?; Ok(Self { dst, obs, cflags }) } else { diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index e73fe0673..688c629ef 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -604,5 +604,27 @@ fn test_seek_bytes() { .stdout_is("\0\0\0\0\0\0\0\0abcdefghijklm\n"); } +#[test] +fn test_seek_do_not_overwrite() { + let (at, mut ucmd) = at_and_ucmd!(); + let mut outfile = at.make_file("outfile"); + outfile.write_all(b"abc").unwrap(); + // Skip the first byte of the input, seek past the first byte of + // the output, and write only one byte to the output. + ucmd.args(&[ + "bs=1", + "skip=1", + "seek=1", + "count=1", + "status=noxfer", + "of=outfile", + ]) + .pipe_in("123") + .succeeds() + .stderr_is("1+0 records in\n1+0 records out\n") + .no_stdout(); + assert_eq!(at.read("outfile"), "a2"); +} + // conv=[ascii,ebcdic,ibm], conv=[ucase,lcase], conv=[block,unblock], conv=sync // TODO: Move conv tests from unit test module