Skip to content

Commit

Permalink
dd: add support for skipping in input FIFO
Browse files Browse the repository at this point in the history
For example, `dd skip=1 if=fifo` will now work.
  • Loading branch information
jfinkels committed Dec 4, 2022
1 parent 03db6be commit 52b6ac0
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 0 deletions.
24 changes: 24 additions & 0 deletions src/uu/dd/src/dd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ enum Source {

/// Input from a file.
File(File),

/// Input from a named pipe, also known as a FIFO.
#[cfg(unix)]
Fifo(File),
}

impl Source {
Expand All @@ -113,6 +117,8 @@ impl Source {
Err(e) => Err(e),
},
Self::File(f) => f.seek(io::SeekFrom::Start(n)),
#[cfg(unix)]
Self::Fifo(f) => io::copy(&mut f.take(n), &mut io::sink()),
}
}
}
Expand All @@ -122,6 +128,8 @@ impl Read for Source {
match self {
Self::Stdin(stdin) => stdin.read(buf),
Self::File(f) => f.read(buf),
#[cfg(unix)]
Self::Fifo(f) => f.read(buf),
}
}
}
Expand Down Expand Up @@ -171,6 +179,20 @@ impl<'a> Input<'a> {
}
Ok(Self { src, settings })
}

/// Instantiate this struct with the named pipe as a source.
#[cfg(unix)]
fn new_fifo(filename: &Path, settings: &'a Settings) -> UResult<Self> {
let mut opts = OpenOptions::new();
opts.read(true);
#[cfg(any(target_os = "linux", target_os = "android"))]
opts.custom_flags(make_linux_iflags(&settings.iflags).unwrap_or(0));
let mut src = Source::Fifo(opts.open(filename)?);
if settings.skip > 0 {
src.skip(settings.skip)?;
}
Ok(Self { src, settings })
}
}

#[cfg(any(target_os = "linux", target_os = "android"))]
Expand Down Expand Up @@ -891,6 +913,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
)?;

let i = match settings.infile {
#[cfg(unix)]
Some(ref infile) if is_fifo(infile) => Input::new_fifo(Path::new(&infile), &settings)?,
Some(ref infile) => Input::new_file(Path::new(&infile), &settings)?,
None => Input::new_stdin(&settings)?,
};
Expand Down
33 changes: 33 additions & 0 deletions tests/by-util/test_dd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1481,3 +1481,36 @@ fn test_seek_output_fifo() {
assert!(output.stdout.is_empty());
assert_eq!(&output.stderr, b"1+0 records in\n1+0 records out\n");
}

/// Test that a skip on an input FIFO results in a read.
#[test]
#[cfg(all(unix, not(macos)))]
fn test_skip_input_fifo() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.mkfifo("fifo");

// TODO When `dd` is a bit more advanced, we could use the uutils
// version of dd here as well.
let child = Command::new("dd")
.current_dir(&at.subdir)
.args([
"count=1",
"if=/dev/zero",
&format!("of={}", at.plus_as_string("fifo")),
"status=noxfer",
])
.stderr(Stdio::piped())
.spawn()
.expect("failed to execute child process");

ts.ucmd()
.args(&["count=0", "skip=1", "if=fifo", "status=noxfer"])
.succeeds()
.stderr_only("0+0 records in\n0+0 records out\n");

let output = child.wait_with_output().unwrap();
assert!(output.status.success());
assert!(output.stdout.is_empty());
assert_eq!(&output.stderr, b"1+0 records in\n1+0 records out\n");
}

0 comments on commit 52b6ac0

Please sign in to comment.