Merge pull request #4191 from jfinkels/dd-sparse

dd: correctly set file size when conv=sparse
This commit is contained in:
Sylvestre Ledru 2022-11-30 08:38:09 +01:00 committed by GitHub
commit 5efdc51609
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 1 deletions

View file

@ -324,6 +324,17 @@ impl Dest {
Self::File(f, _) => f.seek(io::SeekFrom::Start(n)),
}
}
/// Truncate the underlying file to the current stream position, if possible.
fn truncate(&mut self) -> io::Result<()> {
match self {
Self::Stdout(_) => Ok(()),
Self::File(f, _) => {
let pos = f.stream_position()?;
f.set_len(pos)
}
}
}
}
/// Decide whether the given buffer is all zeros.
@ -406,7 +417,6 @@ impl<'a> Output<'a> {
// suppress the error by calling `Result::ok()`. This matches
// the behavior of GNU `dd` when given the command-line
// argument `of=/dev/null`.
if !settings.oconv.notrunc {
dst.set_len(settings.seek).ok();
}
@ -570,6 +580,18 @@ impl<'a> Output<'a> {
// Flush the output, if configured to do so.
self.sync()?;
// Truncate the file to the final cursor location.
//
// Calling `set_len()` may result in an error (for example,
// when calling it on `/dev/null`), but we don't want to
// terminate the process when that happens. 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`.
if !self.settings.oconv.notrunc {
self.dst.truncate().ok();
}
// Print the final read/write statistics.
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), true);
prog_tx.send(prog_update).unwrap_or(());

View file

@ -1394,3 +1394,27 @@ fn test_sync_delayed_reader() {
assert_eq!(&output.stdout, &expected);
assert_eq!(&output.stderr, b"0+8 records in\n4+0 records out\n");
}
/// Test for making a sparse copy of the input file.
#[test]
fn test_sparse() {
let (at, mut ucmd) = at_and_ucmd!();
// Create a file and make it a large sparse file.
//
// On common Linux filesystems, setting the length to one megabyte
// should cause the file to become a sparse file, but it depends
// on the system.
std::fs::File::create(at.plus("infile"))
.unwrap()
.set_len(1024 * 1024)
.unwrap();
// Perform a sparse copy.
ucmd.args(&["bs=32K", "if=infile", "of=outfile", "conv=sparse"])
.succeeds();
// The number of bytes in the file should be accurate though the
// number of blocks stored on disk may be zero.
assert_eq!(at.metadata("infile").len(), at.metadata("outfile").len());
}