mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
dd: correctly set file size when conv=sparse
Before this commit, if `sparsefile` were a regular file of non-zero size whose contents are all null bytes, then dd if=sparsefile of=outfile conv=notrunc would have resulted in `outfile` having zero size as reported by `stat`. After this commit, `outfile` will have the same size as `sparsefile` (even if the contents are represented sparsely by the filesystem).
This commit is contained in:
parent
e307f624e8
commit
9632db4f86
2 changed files with 47 additions and 1 deletions
|
@ -324,6 +324,17 @@ impl Dest {
|
||||||
Self::File(f, _) => f.seek(io::SeekFrom::Start(n)),
|
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.
|
/// 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
|
// suppress the error by calling `Result::ok()`. This matches
|
||||||
// the behavior of GNU `dd` when given the command-line
|
// the behavior of GNU `dd` when given the command-line
|
||||||
// argument `of=/dev/null`.
|
// argument `of=/dev/null`.
|
||||||
|
|
||||||
if !settings.oconv.notrunc {
|
if !settings.oconv.notrunc {
|
||||||
dst.set_len(settings.seek).ok();
|
dst.set_len(settings.seek).ok();
|
||||||
}
|
}
|
||||||
|
@ -570,6 +580,18 @@ impl<'a> Output<'a> {
|
||||||
// Flush the output, if configured to do so.
|
// Flush the output, if configured to do so.
|
||||||
self.sync()?;
|
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.
|
// Print the final read/write statistics.
|
||||||
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), true);
|
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), true);
|
||||||
prog_tx.send(prog_update).unwrap_or(());
|
prog_tx.send(prog_update).unwrap_or(());
|
||||||
|
|
|
@ -1394,3 +1394,27 @@ fn test_sync_delayed_reader() {
|
||||||
assert_eq!(&output.stdout, &expected);
|
assert_eq!(&output.stdout, &expected);
|
||||||
assert_eq!(&output.stderr, b"0+8 records in\n4+0 records out\n");
|
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());
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue