mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37: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
|
// Optimization: if no blocks are to be written, then don't
|
||||||
// bother allocating any buffers.
|
// bother allocating any buffers.
|
||||||
if let Some(Num::Blocks(0) | Num::Bytes(0)) = i.settings.count {
|
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);
|
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.
|
// This is the max size needed.
|
||||||
let mut buf = vec![BUF_INIT_BYTE; bsize];
|
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.
|
// The main read/write loop.
|
||||||
//
|
//
|
||||||
// Each iteration reads blocks from the input and writes
|
// 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)?;
|
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.
|
// Update the read/write stats and inform the progress thread once per second.
|
||||||
//
|
//
|
||||||
// If the receiver is disconnected, `send()` returns an
|
// If the receiver is disconnected, `send()` returns an
|
||||||
|
|
|
@ -304,7 +304,7 @@ impl Parser {
|
||||||
"directory" => linux_only!(f, i.directory = true),
|
"directory" => linux_only!(f, i.directory = true),
|
||||||
"dsync" => linux_only!(f, i.dsync = true),
|
"dsync" => linux_only!(f, i.dsync = true),
|
||||||
"sync" => linux_only!(f, i.sync = 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),
|
"nonblock" => linux_only!(f, i.nonblock = true),
|
||||||
"noatime" => linux_only!(f, i.noatime = true),
|
"noatime" => linux_only!(f, i.noatime = true),
|
||||||
"noctty" => linux_only!(f, i.noctty = true),
|
"noctty" => linux_only!(f, i.noctty = true),
|
||||||
|
@ -336,7 +336,7 @@ impl Parser {
|
||||||
"directory" => linux_only!(f, o.directory = true),
|
"directory" => linux_only!(f, o.directory = true),
|
||||||
"dsync" => linux_only!(f, o.dsync = true),
|
"dsync" => linux_only!(f, o.dsync = true),
|
||||||
"sync" => linux_only!(f, o.sync = 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),
|
"nonblock" => linux_only!(f, o.nonblock = true),
|
||||||
"noatime" => linux_only!(f, o.noatime = true),
|
"noatime" => linux_only!(f, o.noatime = true),
|
||||||
"noctty" => linux_only!(f, o.noctty = true),
|
"noctty" => linux_only!(f, o.noctty = true),
|
||||||
|
|
|
@ -55,7 +55,7 @@ fn unimplemented_flags_should_error() {
|
||||||
let mut succeeded = Vec::new();
|
let mut succeeded = Vec::new();
|
||||||
|
|
||||||
// The following flags are not implemented
|
// 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}")];
|
let args = vec![format!("iflag={flag}")];
|
||||||
|
|
||||||
if Parser::new()
|
if Parser::new()
|
||||||
|
|
|
@ -1536,3 +1536,25 @@ fn test_multiple_processes_reading_stdin() {
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_only("def\n");
|
.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