mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 03:27:44 +00:00
dd: support the [io]flag=nocache option
Add support for the `iflag=nocache` and `oflag=nocache` to make `dd` discard the filesystem cache for the processed portion of the input or output file.
This commit is contained in:
parent
cc2f97ba0d
commit
bd18a2a344
4 changed files with 75 additions and 3 deletions
|
@ -785,6 +785,25 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
|||
// Optimization: if no blocks are to be written, then don't
|
||||
// bother allocating any buffers.
|
||||
if let Some(Num::Blocks(0) | Num::Bytes(0)) = i.settings.count {
|
||||
// Even though we are not reading anything from the input
|
||||
// file, we still need to honor the `nocache` flag, which
|
||||
// requests that we inform the system that we no longer
|
||||
// need the contents of the input file in a system cache.
|
||||
//
|
||||
// TODO Better error handling for overflowing `len`.
|
||||
if i.settings.iflags.nocache {
|
||||
let offset = 0;
|
||||
let len = i.src.len()?.try_into().unwrap();
|
||||
i.discard_cache(offset, len);
|
||||
}
|
||||
// Similarly, discard the system cache for the output file.
|
||||
//
|
||||
// TODO Better error handling for overflowing `len`.
|
||||
if i.settings.oflags.nocache {
|
||||
let offset = 0;
|
||||
let len = o.dst.len()?.try_into().unwrap();
|
||||
o.discard_cache(offset, len);
|
||||
}
|
||||
return finalize(&mut o, rstat, wstat, start, &prog_tx, output_thread);
|
||||
};
|
||||
|
||||
|
@ -792,6 +811,13 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
|||
// This is the max size needed.
|
||||
let mut buf = vec![BUF_INIT_BYTE; bsize];
|
||||
|
||||
// Index in the input file where we are reading bytes and in
|
||||
// the output file where we are writing bytes.
|
||||
//
|
||||
// These are updated on each iteration of the main loop.
|
||||
let mut read_offset = 0;
|
||||
let mut write_offset = 0;
|
||||
|
||||
// The main read/write loop.
|
||||
//
|
||||
// Each iteration reads blocks from the input and writes
|
||||
|
@ -811,6 +837,30 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
|||
}
|
||||
let wstat_update = o.write_blocks(&buf)?;
|
||||
|
||||
// Discard the system file cache for the read portion of
|
||||
// the input file.
|
||||
//
|
||||
// TODO Better error handling for overflowing `offset` and `len`.
|
||||
let read_len = rstat_update.bytes_total;
|
||||
if i.settings.iflags.nocache {
|
||||
let offset = read_offset.try_into().unwrap();
|
||||
let len = read_len.try_into().unwrap();
|
||||
i.discard_cache(offset, len);
|
||||
}
|
||||
read_offset += read_len;
|
||||
|
||||
// Discard the system file cache for the written portion
|
||||
// of the output file.
|
||||
//
|
||||
// TODO Better error handling for overflowing `offset` and `len`.
|
||||
let write_len = wstat_update.bytes_total;
|
||||
if o.settings.oflags.nocache {
|
||||
let offset = write_offset.try_into().unwrap();
|
||||
let len = write_len.try_into().unwrap();
|
||||
o.discard_cache(offset, len);
|
||||
}
|
||||
write_offset += write_len;
|
||||
|
||||
// Update the read/write stats and inform the progress thread once per second.
|
||||
//
|
||||
// If the receiver is disconnected, `send()` returns an
|
||||
|
|
|
@ -304,7 +304,7 @@ impl Parser {
|
|||
"directory" => linux_only!(f, i.directory = true),
|
||||
"dsync" => linux_only!(f, i.dsync = true),
|
||||
"sync" => linux_only!(f, i.sync = true),
|
||||
"nocache" => return Err(ParseError::Unimplemented(f.to_string())),
|
||||
"nocache" => linux_only!(f, i.nocache = true),
|
||||
"nonblock" => linux_only!(f, i.nonblock = true),
|
||||
"noatime" => linux_only!(f, i.noatime = true),
|
||||
"noctty" => linux_only!(f, i.noctty = true),
|
||||
|
@ -336,7 +336,7 @@ impl Parser {
|
|||
"directory" => linux_only!(f, o.directory = true),
|
||||
"dsync" => linux_only!(f, o.dsync = true),
|
||||
"sync" => linux_only!(f, o.sync = true),
|
||||
"nocache" => return Err(ParseError::Unimplemented(f.to_string())),
|
||||
"nocache" => linux_only!(f, o.nocache = true),
|
||||
"nonblock" => linux_only!(f, o.nonblock = true),
|
||||
"noatime" => linux_only!(f, o.noatime = true),
|
||||
"noctty" => linux_only!(f, o.noctty = true),
|
||||
|
|
|
@ -55,7 +55,7 @@ fn unimplemented_flags_should_error() {
|
|||
let mut succeeded = Vec::new();
|
||||
|
||||
// The following flags are not implemented
|
||||
for flag in ["cio", "nocache", "nolinks", "text", "binary"] {
|
||||
for flag in ["cio", "nolinks", "text", "binary"] {
|
||||
let args = vec![format!("iflag={flag}")];
|
||||
|
||||
if Parser::new()
|
||||
|
|
|
@ -1536,3 +1536,25 @@ fn test_multiple_processes_reading_stdin() {
|
|||
.succeeds()
|
||||
.stdout_only("def\n");
|
||||
}
|
||||
|
||||
/// Test that discarding system file cache fails for stdin.
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_nocache_stdin_error() {
|
||||
new_ucmd!()
|
||||
.args(&["iflag=nocache", "count=0", "status=noxfer"])
|
||||
.fails()
|
||||
.code_is(1)
|
||||
.stderr_only("dd: failed to discard cache for: 'standard input': Illegal seek\n0+0 records in\n0+0 records out\n");
|
||||
}
|
||||
|
||||
/// Test for discarding system file cache.
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_nocache_file() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.write_bytes("f", b"a".repeat(1 << 20).as_slice());
|
||||
ucmd.args(&["if=f", "of=/dev/null", "iflag=nocache", "status=noxfer"])
|
||||
.succeeds()
|
||||
.stderr_only("2048+0 records in\n2048+0 records out\n");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue