Skip to content

Commit 5ea4a0a

Browse files
committed
dd: refactor stdin handling to use StdinRaw struct
1 parent f65350a commit 5ea4a0a

1 file changed

Lines changed: 40 additions & 11 deletions

File tree

src/uu/dd/src/dd.rs

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ use std::ffi::OsString;
3535
use std::fs::Metadata;
3636
use std::fs::{File, OpenOptions};
3737
use std::io::{self, Read, Seek, SeekFrom, Write};
38+
use std::mem::ManuallyDrop;
39+
use std::ops::Deref;
40+
use std::ops::DerefMut;
3841
#[cfg(any(target_os = "linux", target_os = "android"))]
3942
use std::os::fd::AsFd;
4043
#[cfg(any(target_os = "linux", target_os = "android"))]
@@ -205,6 +208,34 @@ fn read_and_discard<R: Read>(reader: &mut R, n: u64, buf_size: usize) -> io::Res
205208
Ok(total)
206209
}
207210

211+
#[cfg(unix)]
212+
struct StdinRaw(ManuallyDrop<File>);
213+
214+
#[cfg(unix)]
215+
impl StdinRaw {
216+
fn new() -> Self {
217+
let fd = io::stdin().as_raw_fd();
218+
let f = unsafe { File::from_raw_fd(fd) };
219+
Self(ManuallyDrop::new(f))
220+
}
221+
}
222+
223+
#[cfg(unix)]
224+
impl Deref for StdinRaw {
225+
type Target = File;
226+
227+
fn deref(&self) -> &Self::Target {
228+
&self.0
229+
}
230+
}
231+
232+
#[cfg(unix)]
233+
impl DerefMut for StdinRaw {
234+
fn deref_mut(&mut self) -> &mut Self::Target {
235+
&mut self.0
236+
}
237+
}
238+
208239
/// Data sources.
209240
///
210241
/// Use [`Source::stdin_as_file`] if available to enable more
@@ -219,7 +250,7 @@ enum Source {
219250

220251
/// Input from stdin, opened from its file descriptor.
221252
#[cfg(unix)]
222-
StdinFile(File),
253+
StdinRaw(StdinRaw),
223254

224255
/// Input from a named pipe, also known as a FIFO.
225256
#[cfg(unix)]
@@ -229,16 +260,14 @@ enum Source {
229260
impl Source {
230261
/// Create a source from stdin using its raw file descriptor.
231262
///
232-
/// This returns an instance of the `Source::StdinFile` variant,
263+
/// This returns an instance of the `Source::StdinRaw` variant,
233264
/// using the raw file descriptor of [`io::Stdin`] to create
234265
/// the [`File`] parameter. You can use this instead of
235266
/// `Source::Stdin` to allow reading from stdin without consuming
236267
/// the entire contents of stdin when this process terminates.
237268
#[cfg(unix)]
238-
fn stdin_as_file() -> Self {
239-
let fd = io::stdin().as_raw_fd();
240-
let f = unsafe { File::from_raw_fd(fd) };
241-
Self::StdinFile(f)
269+
fn stdin_raw() -> Self {
270+
Self::StdinRaw(StdinRaw::new())
242271
}
243272

244273
fn skip(&mut self, n: u64, ibs: usize) -> io::Result<u64> {
@@ -255,7 +284,7 @@ impl Source {
255284
Ok(m)
256285
}
257286
#[cfg(unix)]
258-
Self::StdinFile(f) => {
287+
Self::StdinRaw(f) => {
259288
if let Ok(Some(len)) = try_get_len_of_block_device(f)
260289
&& len < n
261290
{
@@ -284,7 +313,7 @@ impl Source {
284313
// ESPIPE means the file descriptor is not seekable (e.g., a pipe),
285314
// so fall back to reading and discarding bytes using ibs-sized buffer
286315
Some(Err(e)) if e.raw_os_error() == Some(libc::ESPIPE) => {
287-
let m = read_and_discard(f, n, ibs)?;
316+
let m = read_and_discard::<File>(f, n, ibs)?;
288317
if m < n {
289318
show_error!(
290319
"{}",
@@ -335,7 +364,7 @@ impl Read for Source {
335364
Self::Stdin(stdin) => stdin.read(buf),
336365
Self::File(f) => f.read(buf),
337366
#[cfg(unix)]
338-
Self::StdinFile(f) => f.read(buf),
367+
Self::StdinRaw(f) => f.read(buf),
339368
#[cfg(unix)]
340369
Self::Fifo(f) => f.read(buf),
341370
}
@@ -379,9 +408,9 @@ impl<'a> Input<'a> {
379408
}
380409
};
381410
#[cfg(unix)]
382-
let mut src = Source::stdin_as_file();
411+
let mut src = Source::stdin_raw();
383412
#[cfg(unix)]
384-
if let Source::StdinFile(f) = &src
413+
if let Source::StdinRaw(f) = &src
385414
&& settings.iflags.directory
386415
&& !f.metadata()?.is_dir()
387416
{

0 commit comments

Comments
 (0)