Skip to content

Commit 603d374

Browse files
authored
Merge pull request #9806 from ChrisDryden/fix-dd-no-allocate
dd: use ibs/obs-sized buffer for skip/seek on non-seekable files
2 parents 7fe4513 + 3aca9fe commit 603d374

File tree

1 file changed

+49
-26
lines changed

1 file changed

+49
-26
lines changed

src/uu/dd/src/dd.rs

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,32 @@ impl Num {
183183
}
184184
}
185185

186+
/// Read and discard `n` bytes from `reader` using a buffer of size `buf_size`.
187+
///
188+
/// This is more efficient than `io::copy` with `BufReader` because it reads
189+
/// directly in `buf_size`-sized chunks, matching GNU dd's behavior.
190+
/// Returns the total number of bytes actually read.
191+
fn read_and_discard<R: Read>(reader: &mut R, n: u64, buf_size: usize) -> io::Result<u64> {
192+
let mut buf = vec![0u8; buf_size];
193+
let mut total = 0u64;
194+
let mut remaining = n;
195+
196+
while remaining > 0 {
197+
let to_read = cmp::min(remaining, buf_size as u64) as usize;
198+
match reader.read(&mut buf[..to_read]) {
199+
Ok(0) => break, // EOF
200+
Ok(bytes_read) => {
201+
total += bytes_read as u64;
202+
remaining -= bytes_read as u64;
203+
}
204+
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
205+
Err(e) => return Err(e),
206+
}
207+
}
208+
209+
Ok(total)
210+
}
211+
186212
/// Data sources.
187213
///
188214
/// Use [`Source::stdin_as_file`] if available to enable more
@@ -219,20 +245,19 @@ impl Source {
219245
Self::StdinFile(f)
220246
}
221247

222-
fn skip(&mut self, n: u64) -> io::Result<u64> {
248+
fn skip(&mut self, n: u64, ibs: usize) -> io::Result<u64> {
223249
match self {
224250
#[cfg(not(unix))]
225-
Self::Stdin(stdin) => match io::copy(&mut stdin.take(n), &mut io::sink()) {
226-
Ok(m) if m < n => {
251+
Self::Stdin(stdin) => {
252+
let m = read_and_discard(stdin, n, ibs)?;
253+
if m < n {
227254
show_error!(
228255
"{}",
229256
translate!("dd-error-cannot-skip-offset", "file" => "standard input")
230257
);
231-
Ok(m)
232258
}
233-
Ok(m) => Ok(m),
234-
Err(e) => Err(e),
235-
},
259+
Ok(m)
260+
}
236261
#[cfg(unix)]
237262
Self::StdinFile(f) => {
238263
if let Ok(Some(len)) = try_get_len_of_block_device(f) {
@@ -247,21 +272,18 @@ impl Source {
247272
return Ok(len);
248273
}
249274
}
250-
match io::copy(&mut f.take(n), &mut io::sink()) {
251-
Ok(m) if m < n => {
252-
show_error!(
253-
"{}",
254-
translate!("dd-error-cannot-skip-offset", "file" => "standard input")
255-
);
256-
Ok(m)
257-
}
258-
Ok(m) => Ok(m),
259-
Err(e) => Err(e),
275+
let m = read_and_discard(f, n, ibs)?;
276+
if m < n {
277+
show_error!(
278+
"{}",
279+
translate!("dd-error-cannot-skip-offset", "file" => "standard input")
280+
);
260281
}
282+
Ok(m)
261283
}
262284
Self::File(f) => f.seek(SeekFrom::Current(n.try_into().unwrap())),
263285
#[cfg(unix)]
264-
Self::Fifo(f) => io::copy(&mut f.take(n), &mut io::sink()),
286+
Self::Fifo(f) => read_and_discard(f, n, ibs),
265287
}
266288
}
267289

@@ -346,7 +368,7 @@ impl<'a> Input<'a> {
346368
}
347369
}
348370
if settings.skip > 0 {
349-
src.skip(settings.skip)?;
371+
src.skip(settings.skip, settings.ibs)?;
350372
}
351373
Ok(Self { src, settings })
352374
}
@@ -369,7 +391,7 @@ impl<'a> Input<'a> {
369391

370392
let mut src = Source::File(src);
371393
if settings.skip > 0 {
372-
src.skip(settings.skip)?;
394+
src.skip(settings.skip, settings.ibs)?;
373395
}
374396
Ok(Self { src, settings })
375397
}
@@ -383,7 +405,7 @@ impl<'a> Input<'a> {
383405
opts.custom_flags(make_linux_iflags(&settings.iflags).unwrap_or(0));
384406
let mut src = Source::Fifo(opts.open(filename)?);
385407
if settings.skip > 0 {
386-
src.skip(settings.skip)?;
408+
src.skip(settings.skip, settings.ibs)?;
387409
}
388410
Ok(Self { src, settings })
389411
}
@@ -605,7 +627,8 @@ impl Dest {
605627
}
606628
}
607629

608-
fn seek(&mut self, n: u64) -> io::Result<u64> {
630+
#[cfg_attr(not(unix), allow(unused_variables))]
631+
fn seek(&mut self, n: u64, obs: usize) -> io::Result<u64> {
609632
match self {
610633
Self::Stdout(stdout) => io::copy(&mut io::repeat(0).take(n), stdout),
611634
Self::File(f, _) => {
@@ -627,7 +650,7 @@ impl Dest {
627650
#[cfg(unix)]
628651
Self::Fifo(f) => {
629652
// Seeking in a named pipe means *reading* from the pipe.
630-
io::copy(&mut f.take(n), &mut io::sink())
653+
read_and_discard(f, n, obs)
631654
}
632655
#[cfg(unix)]
633656
Self::Sink => Ok(0),
@@ -781,7 +804,7 @@ impl<'a> Output<'a> {
781804
/// Instantiate this struct with stdout as a destination.
782805
fn new_stdout(settings: &'a Settings) -> UResult<Self> {
783806
let mut dst = Dest::Stdout(io::stdout());
784-
dst.seek(settings.seek)
807+
dst.seek(settings.seek, settings.obs)
785808
.map_err_context(|| translate!("dd-error-write-error"))?;
786809
Ok(Self { dst, settings })
787810
}
@@ -829,7 +852,7 @@ impl<'a> Output<'a> {
829852
Density::Dense
830853
};
831854
let mut dst = Dest::File(dst, density);
832-
dst.seek(settings.seek)
855+
dst.seek(settings.seek, settings.obs)
833856
.map_err_context(|| translate!("dd-error-failed-to-seek"))?;
834857
Ok(Self { dst, settings })
835858
}
@@ -859,7 +882,7 @@ impl<'a> Output<'a> {
859882
// file for reading. But then we need to close the file and
860883
// re-open it for writing.
861884
if settings.seek > 0 {
862-
Dest::Fifo(File::open(filename)?).seek(settings.seek)?;
885+
Dest::Fifo(File::open(filename)?).seek(settings.seek, settings.obs)?;
863886
}
864887
// If `count=0`, then we don't bother opening the file for
865888
// writing because that would cause this process to block

0 commit comments

Comments
 (0)