Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 17 additions & 26 deletions src/uu/dd/src/dd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use clap::{crate_version, Arg, ArgMatches, Command};
use gcd::Gcd;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult};
use uucore::{show_error, InvalidEncodingHandling};
use uucore::InvalidEncodingHandling;

const ABOUT: &str = "copy, and optionally convert, a file system resource";
const BUF_INIT_BYTE: u8 = 0xDD;
Expand All @@ -61,8 +61,7 @@ impl Input<io::Stdin> {
let iseek =
parseargs::parse_seek_skip_amt(&ibs, iflags.skip_bytes, matches, options::ISEEK)?;
let count = parseargs::parse_count(&iflags, matches)?;

let mut i = Self {
let i = Self {
src: io::stdin(),
ibs,
print_level,
Expand All @@ -71,17 +70,23 @@ impl Input<io::Stdin> {
iflags,
};

// The --skip and --iseek flags are additive. On a stream, they discard bytes.
// there is way to seek stdin, so we interpret stdin as a File
// to make it seekable
#[cfg(any(target_family = "unix", target_family = "wasi"))]
let mut seekable_src = unsafe {
use std::os::unix::io::{AsRawFd, FromRawFd};
std::fs::File::from_raw_fd(std::io::stdin().as_raw_fd())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(driveby comment, I don't have much context) Unless seekable_src is fed to mem::forget at some point this likely is a bad idea because File::drop will then close the stdin file descriptor. You should either try_clone (dup) it or wrap this in a ManuallyDrop.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. Will fix it once I figure out why the current solution works if I use dd in command line and does not work if I run tests from tests/by-utils.

};
#[cfg(target_family = "windows")]
let mut seekable_src = unsafe {
use std::os::windows::io::{AsRawHandle, FromRawHandle};
std::fs::File::from_raw_handle(std::io::stdin().as_raw_handle())
};
let amt = skip.unwrap_or(0) + iseek.unwrap_or(0);
if amt > 0 {
if let Err(e) = i.read_skip(amt) {
if let io::ErrorKind::UnexpectedEof = e.kind() {
show_error!("'standard input': cannot skip to specified offset");
} else {
return io::Result::Err(e)
.map_err_context(|| "I/O error while skipping".to_string());
}
}
seekable_src
.seek(io::SeekFrom::Start(amt))
.map_err_context(|| "failed to seek in input file".to_string())?;
}

Ok(i)
Expand Down Expand Up @@ -261,20 +266,6 @@ impl<R: Read> Input<R> {
records_truncated: 0,
})
}

/// Skips amount_to_read bytes from the Input by copying into a sink
fn read_skip(&mut self, amount_to_read: u64) -> std::io::Result<()> {
let copy_result = io::copy(&mut self.src.by_ref().take(amount_to_read), &mut io::sink());
if let Ok(n) = copy_result {
if n != amount_to_read {
io::Result::Err(io::Error::new(io::ErrorKind::UnexpectedEof, ""))
} else {
Ok(())
}
} else {
io::Result::Err(copy_result.unwrap_err())
}
}
}

trait OutputTrait: Sized + Write {
Expand Down
13 changes: 13 additions & 0 deletions tests/by-util/test_dd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1186,3 +1186,16 @@ fn test_final_stats_si_iec() {
let s = result.stderr_str();
assert!(s.starts_with("2+0 records in\n2+0 records out\n1024 bytes (1 KB, 1024 B) copied,"));
}

#[test]
fn test_partial_skip_stdin() {
Copy link
Author

@ArtemSkrebkov ArtemSkrebkov Jun 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sylvestre

Well... I added the test, but it does not work as expected and fails with Illegal seek. As any other test which tries to seek stdin.

However, it can work outside the test system (for example, if I run tests/misc/head-c.sh)

I am confused why it does not work in the test system. Let me know if there are any insights on this.

let input = "abc\ndef\n";
// build_test_file!("infile.txt", input.as_bytes());
new_ucmd!()
.args(&["status=none", "bs=1", "skip=1", "count=0"])
.pipe_in(input)
.succeeds()
.no_stderr()
.stdout_is("bc")
.success();
}