@@ -12,8 +12,10 @@ mod datastructures;
1212mod numbers;
1313mod parseargs;
1414mod progress;
15+ mod stdio;
1516
1617use crate :: bufferedoutput:: BufferedOutput ;
18+ use crate :: stdio:: { StdinRaw , StdoutRaw } ;
1719use blocks:: conv_block_unblock_helper;
1820use datastructures:: { ConversionMode , IConvFlags , IFlags , OConvFlags , OFlags , options} ;
1921#[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
@@ -25,7 +27,6 @@ use progress::ProgUpdateType;
2527use progress:: { ProgUpdate , ReadStat , StatusLevel , WriteStat , gen_prog_updater} ;
2628#[ cfg( target_os = "linux" ) ]
2729use progress:: { check_and_reset_sigusr1, install_sigusr1_handler} ;
28- use uucore:: io:: OwnedFileDescriptorOrHandle ;
2930use uucore:: translate;
3031
3132use std:: cmp;
@@ -37,15 +38,12 @@ use std::fs::{File, OpenOptions};
3738use std:: io:: { self , Read , Seek , SeekFrom , Write } ;
3839#[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
3940use std:: os:: fd:: AsFd ;
41+ #[ cfg( unix) ]
42+ use std:: os:: unix:: fs:: FileTypeExt ;
4043#[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
4144use std:: os:: unix:: fs:: OpenOptionsExt ;
42- #[ cfg( unix) ]
43- use std:: os:: unix:: {
44- fs:: FileTypeExt ,
45- io:: { AsRawFd , FromRawFd } ,
46- } ;
4745#[ cfg( windows) ]
48- use std:: os:: windows:: { fs:: MetadataExt , io :: AsHandle } ;
46+ use std:: os:: windows:: fs:: MetadataExt ;
4947use std:: path:: Path ;
5048use std:: sync:: atomic:: AtomicU8 ;
5149use std:: sync:: { Arc , atomic:: Ordering :: Relaxed , mpsc} ;
@@ -178,20 +176,20 @@ impl Num {
178176 }
179177}
180178
181- /// Read and discard `n` bytes from `reader ` using a buffer of size `buf_size`.
179+ /// Read and discard `n` bytes from `file ` using a buffer of size `buf_size`.
182180///
183181/// This is more efficient than `io::copy` with `BufReader` because it reads
184182/// directly in `buf_size`-sized chunks, matching GNU dd's behavior.
185183/// Returns the total number of bytes actually read.
186- fn read_and_discard < R : Read > ( reader : & mut R , n : u64 , buf_size : usize ) -> io:: Result < u64 > {
184+ fn read_and_discard ( file : & mut File , n : u64 , buf_size : usize ) -> io:: Result < u64 > {
187185 // todo: consider splice()ing to /dev/null on Linux
188186 let mut buf = Vec :: with_capacity ( buf_size) ;
189187 let mut total = 0u64 ;
190188 let mut remaining = n;
191189 while remaining > 0 {
192190 let to_read = cmp:: min ( remaining, buf_size as u64 ) ;
193191 buf. clear ( ) ;
194- match reader . by_ref ( ) . take ( to_read) . read_to_end ( & mut buf) {
192+ match file . take ( to_read) . read_to_end ( & mut buf) {
195193 Ok ( 0 ) => break , // EOF
196194 Ok ( bytes_read) => {
197195 total += bytes_read as u64 ;
@@ -207,44 +205,25 @@ fn read_and_discard<R: Read>(reader: &mut R, n: u64, buf_size: usize) -> io::Res
207205
208206/// Data sources.
209207///
210- /// Use [`Source::stdin_as_file `] if available to enable more
208+ /// Use [`Source::StdinRaw `] if available to enable more
211209/// fine-grained access to reading from stdin.
212210enum Source {
213- /// Input from stdin.
214- #[ cfg( not( unix) ) ]
215- Stdin ( io:: Stdin ) ,
216-
217211 /// Input from a file.
218212 File ( File ) ,
219213
220214 /// Input from stdin, opened from its file descriptor.
221- #[ cfg( unix) ]
222- StdinFile ( File ) ,
215+ StdinRaw ( StdinRaw ) ,
223216
224217 /// Input from a named pipe, also known as a FIFO.
225218 #[ cfg( unix) ]
226219 Fifo ( File ) ,
227220}
228221
229222impl Source {
230- /// Create a source from stdin using its raw file descriptor.
231- ///
232- /// This returns an instance of the `Source::StdinFile` variant,
233- /// using the raw file descriptor of [`io::Stdin`] to create
234- /// the [`File`] parameter. You can use this instead of
235- /// `Source::Stdin` to allow reading from stdin without consuming
236- /// the entire contents of stdin when this process terminates.
237- #[ 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)
242- }
243-
244223 fn skip ( & mut self , n : u64 , ibs : usize ) -> io:: Result < u64 > {
245224 match self {
246225 #[ cfg( not( unix) ) ]
247- Self :: Stdin ( stdin) => {
226+ Self :: StdinRaw ( stdin) => {
248227 let m = read_and_discard ( stdin, n, ibs) ?;
249228 if m < n {
250229 show_error ! (
@@ -255,8 +234,8 @@ impl Source {
255234 Ok ( m)
256235 }
257236 #[ cfg( unix) ]
258- Self :: StdinFile ( f ) => {
259- if let Ok ( Some ( len) ) = try_get_len_of_block_device ( f )
237+ Self :: StdinRaw ( stdin ) => {
238+ if let Ok ( Some ( len) ) = try_get_len_of_block_device ( stdin )
260239 && len < n
261240 {
262241 // GNU compatibility:
@@ -269,9 +248,9 @@ impl Source {
269248 return Ok ( len) ;
270249 }
271250 // Get file length before seeking to avoid race condition
272- let file_len = f . metadata ( ) . as_ref ( ) . map_or ( u64:: MAX , Metadata :: len) ;
251+ let file_len = stdin . metadata ( ) . as_ref ( ) . map_or ( u64:: MAX , Metadata :: len) ;
273252 // Try seek first; fall back to read if not seekable
274- match n. try_into ( ) . ok ( ) . map ( |n| f . seek ( SeekFrom :: Current ( n) ) ) {
253+ match n. try_into ( ) . ok ( ) . map ( |n| stdin . seek ( SeekFrom :: Current ( n) ) ) {
275254 Some ( Ok ( pos) ) => {
276255 if pos > file_len {
277256 show_error ! (
@@ -284,7 +263,7 @@ impl Source {
284263 // ESPIPE means the file descriptor is not seekable (e.g., a pipe),
285264 // so fall back to reading and discarding bytes using ibs-sized buffer
286265 Some ( Err ( e) ) if e. raw_os_error ( ) == Some ( libc:: ESPIPE ) => {
287- let m = read_and_discard ( f , n, ibs) ?;
266+ let m = read_and_discard ( stdin , n, ibs) ?;
288267 if m < n {
289268 show_error ! (
290269 "{}" ,
@@ -331,11 +310,8 @@ impl Source {
331310impl Read for Source {
332311 fn read ( & mut self , buf : & mut [ u8 ] ) -> io:: Result < usize > {
333312 match self {
334- #[ cfg( not( unix) ) ]
335- Self :: Stdin ( stdin) => stdin. read ( buf) ,
336313 Self :: File ( f) => f. read ( buf) ,
337- #[ cfg( unix) ]
338- Self :: StdinFile ( f) => f. read ( buf) ,
314+ Self :: StdinRaw ( f) => f. read ( buf) ,
339315 #[ cfg( unix) ]
340316 Self :: Fifo ( f) => f. read ( buf) ,
341317 }
@@ -359,10 +335,11 @@ struct Input<'a> {
359335impl < ' a > Input < ' a > {
360336 /// Instantiate this struct with stdin as a source.
361337 fn new_stdin ( settings : & ' a Settings ) -> UResult < Self > {
338+ let stdin = StdinRaw :: new ( ) ;
339+
362340 #[ cfg( not( unix) ) ]
363341 let mut src = {
364- let f = File :: from ( io:: stdin ( ) . as_handle ( ) . try_clone_to_owned ( ) ?) ;
365- let is_file = if let Ok ( metadata) = f. metadata ( ) {
342+ let is_file = if let Ok ( metadata) = stdin. metadata ( ) {
366343 // this hack is needed as there is no other way on windows
367344 // to differentiate between the case where `seek` works
368345 // on a file handle or not. i.e. when the handle is no real
@@ -373,15 +350,15 @@ impl<'a> Input<'a> {
373350 false
374351 } ;
375352 if is_file {
376- Source :: File ( f )
353+ Source :: File ( stdin . try_clone ( ) ? )
377354 } else {
378- Source :: Stdin ( io :: stdin ( ) )
355+ Source :: StdinRaw ( stdin)
379356 }
380357 } ;
381358 #[ cfg( unix) ]
382- let mut src = Source :: stdin_as_file ( ) ;
359+ let mut src = Source :: StdinRaw ( stdin ) ;
383360 #[ cfg( unix) ]
384- if let Source :: StdinFile ( f) = & src
361+ if let Source :: StdinRaw ( f) = & src
385362 && settings. iflags . directory
386363 && !f. metadata ( ) ?. is_dir ( )
387364 {
@@ -599,7 +576,7 @@ enum Density {
599576/// Data destinations.
600577enum Dest {
601578 /// Output to stdout.
602- Stdout ( File ) ,
579+ StdoutRaw ( StdoutRaw ) ,
603580
604581 /// Output to a file.
605582 ///
@@ -619,7 +596,7 @@ enum Dest {
619596impl Dest {
620597 fn fsync ( & mut self ) -> io:: Result < ( ) > {
621598 match self {
622- Self :: Stdout ( stdout) => stdout. flush ( ) ,
599+ Self :: StdoutRaw ( stdout) => stdout. flush ( ) ,
623600 Self :: File ( f, _) => {
624601 f. flush ( ) ?;
625602 f. sync_all ( )
@@ -636,7 +613,7 @@ impl Dest {
636613
637614 fn fdatasync ( & mut self ) -> io:: Result < ( ) > {
638615 match self {
639- Self :: Stdout ( stdout) => stdout. flush ( ) ,
616+ Self :: StdoutRaw ( stdout) => stdout. flush ( ) ,
640617 Self :: File ( f, _) => {
641618 f. flush ( ) ?;
642619 f. sync_data ( )
@@ -654,7 +631,7 @@ impl Dest {
654631 #[ cfg_attr( not( unix) , allow( unused_variables) ) ]
655632 fn seek ( & mut self , n : u64 , obs : usize ) -> io:: Result < u64 > {
656633 match self {
657- Self :: Stdout ( stdout) => io:: copy ( & mut io:: repeat ( 0 ) . take ( n) , stdout) ,
634+ Self :: StdoutRaw ( stdout) => io:: copy ( & mut io:: repeat ( 0 ) . take ( n) , & mut * * stdout) ,
658635 Self :: File ( f, _) => {
659636 #[ cfg( unix) ]
660637 if let Ok ( Some ( len) ) = try_get_len_of_block_device ( f)
@@ -790,7 +767,7 @@ impl Write for Dest {
790767 Err ( e) => Err ( e) ,
791768 }
792769 }
793- Self :: Stdout ( stdout) => stdout. write ( buf) ,
770+ Self :: StdoutRaw ( stdout) => stdout. write ( buf) ,
794771 #[ cfg( unix) ]
795772 Self :: Fifo ( f) => f. write ( buf) ,
796773 #[ cfg( unix) ]
@@ -800,7 +777,7 @@ impl Write for Dest {
800777
801778 fn flush ( & mut self ) -> io:: Result < ( ) > {
802779 match self {
803- Self :: Stdout ( stdout) => stdout. flush ( ) ,
780+ Self :: StdoutRaw ( stdout) => stdout. flush ( ) ,
804781 Self :: File ( f, _) => f. flush ( ) ,
805782 #[ cfg( unix) ]
806783 Self :: Fifo ( f) => f. flush ( ) ,
@@ -827,8 +804,8 @@ struct Output<'a> {
827804impl < ' a > Output < ' a > {
828805 /// Instantiate this struct with stdout as a destination.
829806 fn new_stdout ( settings : & ' a Settings ) -> UResult < Self > {
830- let fx = OwnedFileDescriptorOrHandle :: from ( io :: stdout ( ) ) ? ;
831- let mut dst = Dest :: Stdout ( fx . into_file ( ) ) ;
807+ let stdout = StdoutRaw :: new ( ) ;
808+ let mut dst = Dest :: StdoutRaw ( stdout ) ;
832809 dst. seek ( settings. seek , settings. obs )
833810 . map_err_context ( || translate ! ( "dd-error-write-error" ) ) ?;
834811 Ok ( Self { dst, settings } )
@@ -888,16 +865,17 @@ impl<'a> Output<'a> {
888865 /// already opened by the system (stdout) and has a state
889866 /// (current position) that shall be used.
890867 fn new_file_from_stdout ( settings : & ' a Settings ) -> UResult < Self > {
891- let fx = OwnedFileDescriptorOrHandle :: from ( io :: stdout ( ) ) ? ;
868+ let stdout = StdoutRaw :: new ( ) ;
892869 #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
893870 if let Some ( libc_flags) = make_linux_oflags ( & settings. oflags ) {
894871 nix:: fcntl:: fcntl (
895- fx . as_raw ( ) . as_fd ( ) ,
872+ stdout . as_fd ( ) ,
896873 FcntlArg :: F_SETFL ( OFlag :: from_bits_retain ( libc_flags) ) ,
897874 ) ?;
898875 }
899876
900- Self :: prepare_file ( fx. into_file ( ) , settings)
877+ // TODO: avoid cloning the underlying file descriptor here
878+ Self :: prepare_file ( stdout. try_clone ( ) ?, settings)
901879 }
902880
903881 /// Instantiate this struct with the given named pipe as a destination.
0 commit comments