Skip to content

Commit 2da2c90

Browse files
dd: fix nocache flag handling at EOF (#9818)
* dd: fix nocache flag handling at EOF * Add tests for nocache EOF handling --------- Co-authored-by: Sylvestre Ledru <[email protected]>
1 parent 996e5e8 commit 2da2c90

File tree

2 files changed

+60
-35
lines changed

2 files changed

+60
-35
lines changed

src/uu/dd/src/dd.rs

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -219,17 +219,6 @@ impl Source {
219219
Self::StdinFile(f)
220220
}
221221

222-
/// The length of the data source in number of bytes.
223-
///
224-
/// If it cannot be determined, then this function returns 0.
225-
fn len(&self) -> io::Result<i64> {
226-
#[allow(clippy::match_wildcard_for_single_variants)]
227-
match self {
228-
Self::File(f) => Ok(f.metadata()?.len().try_into().unwrap_or(i64::MAX)),
229-
_ => Ok(0),
230-
}
231-
}
232-
233222
fn skip(&mut self, n: u64) -> io::Result<u64> {
234223
match self {
235224
#[cfg(not(unix))]
@@ -673,17 +662,6 @@ impl Dest {
673662
_ => Err(Errno::ESPIPE), // "Illegal seek"
674663
}
675664
}
676-
677-
/// The length of the data destination in number of bytes.
678-
///
679-
/// If it cannot be determined, then this function returns 0.
680-
fn len(&self) -> io::Result<i64> {
681-
#[allow(clippy::match_wildcard_for_single_variants)]
682-
match self {
683-
Self::File(f, _) => Ok(f.metadata()?.len().try_into().unwrap_or(i64::MAX)),
684-
_ => Ok(0),
685-
}
686-
}
687665
}
688666

689667
/// Decide whether the given buffer is all zeros.
@@ -1063,21 +1041,12 @@ impl BlockWriter<'_> {
10631041
/// depending on the command line arguments, this function
10641042
/// informs the OS to flush/discard the caches for input and/or output file.
10651043
fn flush_caches_full_length(i: &Input, o: &Output) -> io::Result<()> {
1066-
// TODO Better error handling for overflowing `len`.
1044+
// Using len=0 in posix_fadvise means "to end of file"
10671045
if i.settings.iflags.nocache {
1068-
let offset = 0;
1069-
#[allow(clippy::useless_conversion)]
1070-
let len = i.src.len()?.try_into().unwrap();
1071-
i.discard_cache(offset, len);
1046+
i.discard_cache(0, 0);
10721047
}
1073-
// Similarly, discard the system cache for the output file.
1074-
//
1075-
// TODO Better error handling for overflowing `len`.
10761048
if i.settings.oflags.nocache {
1077-
let offset = 0;
1078-
#[allow(clippy::useless_conversion)]
1079-
let len = o.dst.len()?.try_into().unwrap();
1080-
o.discard_cache(offset, len);
1049+
o.discard_cache(0, 0);
10811050
}
10821051

10831052
Ok(())
@@ -1185,6 +1154,7 @@ fn dd_copy(mut i: Input, o: Output) -> io::Result<()> {
11851154

11861155
let input_nocache = i.settings.iflags.nocache;
11871156
let output_nocache = o.settings.oflags.nocache;
1157+
let output_direct = o.settings.oflags.direct;
11881158

11891159
// Add partial block buffering, if needed.
11901160
let mut o = if o.settings.buffered {
@@ -1208,6 +1178,12 @@ fn dd_copy(mut i: Input, o: Output) -> io::Result<()> {
12081178
let loop_bsize = calc_loop_bsize(i.settings.count, &rstat, &wstat, i.settings.ibs, bsize);
12091179
let rstat_update = read_helper(&mut i, &mut buf, loop_bsize)?;
12101180
if rstat_update.is_empty() {
1181+
if input_nocache {
1182+
i.discard_cache(read_offset.try_into().unwrap(), 0);
1183+
}
1184+
if output_nocache || output_direct {
1185+
o.discard_cache(write_offset.try_into().unwrap(), 0);
1186+
}
12111187
break;
12121188
}
12131189
let wstat_update = o.write_blocks(&buf)?;

tests/by-util/test_dd.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
5-
// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, availible, behaviour, bmax, bremain, btotal, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rposition, rremain, rsofar, rstat, sigusr, sigval, wlen, wstat abcdefghijklm abcdefghi nabcde nabcdefg abcdefg fifoname seekable
5+
// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, availible, behaviour, bmax, bremain, btotal, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rposition, rremain, rsofar, rstat, sigusr, sigval, wlen, wstat abcdefghijklm abcdefghi nabcde nabcdefg abcdefg fifoname seekable fadvise FADV DONTNEED
66

77
use uutests::at_and_ucmd;
88
use uutests::new_ucmd;
@@ -1840,3 +1840,52 @@ fn test_skip_overflow() {
18401840
"dd: invalid number: ‘9223372036854775808’: Value too large for defined data type",
18411841
);
18421842
}
1843+
1844+
#[test]
1845+
#[cfg(target_os = "linux")]
1846+
fn test_nocache_eof() {
1847+
let (at, mut ucmd) = at_and_ucmd!();
1848+
at.write_bytes("in.f", &vec![0u8; 1234567]);
1849+
ucmd.args(&[
1850+
"if=in.f",
1851+
"of=out.f",
1852+
"bs=1M",
1853+
"oflag=nocache,sync",
1854+
"status=noxfer",
1855+
])
1856+
.succeeds();
1857+
assert_eq!(at.read_bytes("out.f").len(), 1234567);
1858+
}
1859+
1860+
#[test]
1861+
#[cfg(all(target_os = "linux", feature = "printf"))]
1862+
fn test_nocache_eof_fadvise_zero_length() {
1863+
use std::process::Command;
1864+
let (at, _ucmd) = at_and_ucmd!();
1865+
at.write_bytes("in.f", &vec![0u8; 1234567]);
1866+
1867+
let strace_file = at.plus_as_string("strace.out");
1868+
let result = Command::new("strace")
1869+
.args(["-o", &strace_file, "-e", "fadvise64,fadvise64_64"])
1870+
.arg(get_tests_binary())
1871+
.args([
1872+
"dd",
1873+
"if=in.f",
1874+
"of=out.f",
1875+
"bs=1M",
1876+
"oflag=nocache,sync",
1877+
"status=none",
1878+
])
1879+
.current_dir(at.as_string())
1880+
.output();
1881+
1882+
if result.is_err() {
1883+
return; // strace not available
1884+
}
1885+
1886+
let strace = at.read("strace.out");
1887+
assert!(
1888+
strace.contains(", 0, POSIX_FADV_DONTNEED"),
1889+
"Expected len=0 at EOF: {strace}"
1890+
);
1891+
}

0 commit comments

Comments
 (0)