Skip to content

Commit c03ffdf

Browse files
committed
dd: fix GNU test 'dd/skip-seek-past-dev'
1 parent 0562c50 commit c03ffdf

File tree

1 file changed

+48
-9
lines changed

1 file changed

+48
-9
lines changed

src/uu/dd/src/dd.rs

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ use std::time;
4343
use clap::{crate_version, Arg, Command};
4444
use gcd::Gcd;
4545
use uucore::display::Quotable;
46-
use uucore::error::{FromIo, UResult};
46+
use uucore::error::{set_exit_code, FromIo, UResult};
4747
use uucore::{format_usage, help_about, help_section, help_usage, show_error};
4848

4949
const ABOUT: &str = help_about!("dd.md");
@@ -143,14 +143,25 @@ impl Source {
143143
Err(e) => Err(e),
144144
},
145145
#[cfg(unix)]
146-
Self::StdinFile(f) => match io::copy(&mut f.take(n), &mut io::sink()) {
147-
Ok(m) if m < n => {
148-
show_error!("'standard input': cannot skip to specified offset");
149-
Ok(m)
146+
Self::StdinFile(f) => {
147+
if let Ok(Some(len)) = try_get_len_of_block_device(f) {
148+
if len < n {
149+
// GNU compatibility:
150+
// this case prints the stats but sets the exit code to 1
151+
show_error!("'standard input': cannot skip: Invalid argument");
152+
set_exit_code(1);
153+
return Ok(len);
154+
}
150155
}
151-
Ok(m) => Ok(m),
152-
Err(e) => Err(e),
153-
},
156+
match io::copy(&mut f.take(n), &mut io::sink()) {
157+
Ok(m) if m < n => {
158+
show_error!("'standard input': cannot skip to specified offset");
159+
Ok(m)
160+
}
161+
Ok(m) => Ok(m),
162+
Err(e) => Err(e),
163+
}
164+
}
154165
Self::File(f) => f.seek(io::SeekFrom::Start(n)),
155166
#[cfg(unix)]
156167
Self::Fifo(f) => io::copy(&mut f.take(n), &mut io::sink()),
@@ -426,7 +437,18 @@ impl Dest {
426437
fn seek(&mut self, n: u64) -> io::Result<u64> {
427438
match self {
428439
Self::Stdout(stdout) => io::copy(&mut io::repeat(0).take(n), stdout),
429-
Self::File(f, _) => f.seek(io::SeekFrom::Start(n)),
440+
Self::File(f, _) => {
441+
if let Ok(Some(len)) = try_get_len_of_block_device(f) {
442+
if len < n {
443+
// GNU compatibility:
444+
// this case prints the stats but sets the exit code to 1
445+
show_error!("'standard output': cannot seek: Invalid argument");
446+
set_exit_code(1);
447+
return Ok(len);
448+
}
449+
}
450+
f.seek(io::SeekFrom::Start(n))
451+
}
430452
#[cfg(unix)]
431453
Self::Fifo(f) => {
432454
// Seeking in a named pipe means *reading* from the pipe.
@@ -925,6 +947,23 @@ fn is_stdout_redirected_to_seekable_file() -> bool {
925947
}
926948
}
927949

950+
/// Try to get the len if it is a block device
951+
fn try_get_len_of_block_device(file: &mut File) -> io::Result<Option<u64>> {
952+
#[cfg(not(unix))]
953+
return Ok(None);
954+
955+
let ftype = file.metadata()?.file_type();
956+
#[cfg(unix)]
957+
if !ftype.is_block_device() {
958+
return Ok(None);
959+
}
960+
961+
// FIXME: this can be replaced by file.stream_len() when stable.
962+
let len = file.seek(SeekFrom::End(0))?;
963+
file.rewind()?;
964+
Ok(Some(len))
965+
}
966+
928967
/// Decide whether the named file is a named pipe, also known as a FIFO.
929968
#[cfg(unix)]
930969
fn is_fifo(filename: &str) -> bool {

0 commit comments

Comments
 (0)