@@ -13,6 +13,7 @@ mod numbers;
1313mod parseargs;
1414mod progress;
1515
16+ use crate :: bufferedoutput:: BufferedOutput ;
1617use blocks:: conv_block_unblock_helper;
1718use datastructures:: * ;
1819use parseargs:: Parser ;
@@ -801,6 +802,68 @@ impl<'a> Output<'a> {
801802 Ok ( ( ) )
802803 }
803804 }
805+
806+ /// Truncate the underlying file to the current stream position, if possible.
807+ fn truncate ( & mut self ) -> std:: io:: Result < ( ) > {
808+ self . dst . truncate ( )
809+ }
810+ }
811+
812+ /// The block writer either with or without partial block buffering.
813+ enum BlockWriter < ' a > {
814+ /// Block writer with partial block buffering.
815+ ///
816+ /// Partial blocks are buffered until completed.
817+ Buffered ( BufferedOutput < ' a > ) ,
818+
819+ /// Block writer without partial block buffering.
820+ ///
821+ /// Partial blocks are written immediately.
822+ Unbuffered ( Output < ' a > ) ,
823+ }
824+
825+ impl < ' a > BlockWriter < ' a > {
826+ fn discard_cache ( & self , offset : libc:: off_t , len : libc:: off_t ) {
827+ match self {
828+ Self :: Unbuffered ( o) => o. discard_cache ( offset, len) ,
829+ Self :: Buffered ( o) => o. discard_cache ( offset, len) ,
830+ }
831+ }
832+
833+ fn flush ( & mut self ) -> io:: Result < WriteStat > {
834+ match self {
835+ Self :: Unbuffered ( _) => Ok ( WriteStat :: default ( ) ) ,
836+ Self :: Buffered ( o) => o. flush ( ) ,
837+ }
838+ }
839+
840+ fn sync ( & mut self ) -> io:: Result < ( ) > {
841+ match self {
842+ Self :: Unbuffered ( o) => o. sync ( ) ,
843+ Self :: Buffered ( o) => o. sync ( ) ,
844+ }
845+ }
846+
847+ /// Truncate the file to the final cursor location.
848+ fn truncate ( & mut self ) {
849+ // Calling `set_len()` may result in an error (for example,
850+ // when calling it on `/dev/null`), but we don't want to
851+ // terminate the process when that happens. Instead, we
852+ // suppress the error by calling `Result::ok()`. This matches
853+ // the behavior of GNU `dd` when given the command-line
854+ // argument `of=/dev/null`.
855+ match self {
856+ Self :: Unbuffered ( o) => o. truncate ( ) . ok ( ) ,
857+ Self :: Buffered ( o) => o. truncate ( ) . ok ( ) ,
858+ } ;
859+ }
860+
861+ fn write_blocks ( & mut self , buf : & [ u8 ] ) -> std:: io:: Result < WriteStat > {
862+ match self {
863+ Self :: Unbuffered ( o) => o. write_blocks ( buf) ,
864+ Self :: Buffered ( o) => o. write_blocks ( buf) ,
865+ }
866+ }
804867}
805868
806869/// Copy the given input data to this output, consuming both.
@@ -814,7 +877,7 @@ impl<'a> Output<'a> {
814877///
815878/// If there is a problem reading from the input or writing to
816879/// this output.
817- fn dd_copy ( mut i : Input , mut o : Output ) -> std:: io:: Result < ( ) > {
880+ fn dd_copy ( mut i : Input , o : Output ) -> std:: io:: Result < ( ) > {
818881 // The read and write statistics.
819882 //
820883 // These objects are counters, initialized to zero. After each
@@ -851,6 +914,9 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
851914 let ( prog_tx, rx) = mpsc:: channel ( ) ;
852915 let output_thread = thread:: spawn ( gen_prog_updater ( rx, i. settings . status ) ) ;
853916
917+ // Whether to truncate the output file after all blocks have been written.
918+ let truncate = !o. settings . oconv . notrunc ;
919+
854920 // Optimization: if no blocks are to be written, then don't
855921 // bother allocating any buffers.
856922 if let Some ( Num :: Blocks ( 0 ) | Num :: Bytes ( 0 ) ) = i. settings . count {
@@ -875,7 +941,15 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
875941 let len = o. dst . len ( ) ?. try_into ( ) . unwrap ( ) ;
876942 o. discard_cache ( offset, len) ;
877943 }
878- return finalize ( & mut o, rstat, wstat, start, & prog_tx, output_thread) ;
944+ return finalize (
945+ BlockWriter :: Unbuffered ( o) ,
946+ rstat,
947+ wstat,
948+ start,
949+ & prog_tx,
950+ output_thread,
951+ truncate,
952+ ) ;
879953 } ;
880954
881955 // Create a common buffer with a capacity of the block size.
@@ -895,6 +969,16 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
895969 let mut read_offset = 0 ;
896970 let mut write_offset = 0 ;
897971
972+ let input_nocache = i. settings . iflags . nocache ;
973+ let output_nocache = o. settings . oflags . nocache ;
974+
975+ // Add partial block buffering, if needed.
976+ let mut o = if o. settings . buffered {
977+ BlockWriter :: Buffered ( BufferedOutput :: new ( o) )
978+ } else {
979+ BlockWriter :: Unbuffered ( o)
980+ } ;
981+
898982 // The main read/write loop.
899983 //
900984 // Each iteration reads blocks from the input and writes
@@ -919,7 +1003,7 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
9191003 //
9201004 // TODO Better error handling for overflowing `offset` and `len`.
9211005 let read_len = rstat_update. bytes_total ;
922- if i . settings . iflags . nocache {
1006+ if input_nocache {
9231007 let offset = read_offset. try_into ( ) . unwrap ( ) ;
9241008 let len = read_len. try_into ( ) . unwrap ( ) ;
9251009 i. discard_cache ( offset, len) ;
@@ -931,7 +1015,7 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
9311015 //
9321016 // TODO Better error handling for overflowing `offset` and `len`.
9331017 let write_len = wstat_update. bytes_total ;
934- if o . settings . oflags . nocache {
1018+ if output_nocache {
9351019 let offset = write_offset. try_into ( ) . unwrap ( ) ;
9361020 let len = write_len. try_into ( ) . unwrap ( ) ;
9371021 o. discard_cache ( offset, len) ;
@@ -951,34 +1035,33 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
9511035 prog_tx. send ( prog_update) . unwrap_or ( ( ) ) ;
9521036 }
9531037 }
954- finalize ( & mut o, rstat, wstat, start, & prog_tx, output_thread)
1038+ finalize ( o, rstat, wstat, start, & prog_tx, output_thread, truncate )
9551039}
9561040
9571041/// Flush output, print final stats, and join with the progress thread.
9581042fn finalize < T > (
959- output : & mut Output ,
1043+ mut output : BlockWriter ,
9601044 rstat : ReadStat ,
9611045 wstat : WriteStat ,
9621046 start : Instant ,
9631047 prog_tx : & mpsc:: Sender < ProgUpdate > ,
9641048 output_thread : thread:: JoinHandle < T > ,
1049+ truncate : bool ,
9651050) -> std:: io:: Result < ( ) > {
966- // Flush the output, if configured to do so.
1051+ // Flush the output in case a partial write has been buffered but
1052+ // not yet written.
1053+ let wstat_update = output. flush ( ) ?;
1054+
1055+ // Sync the output, if configured to do so.
9671056 output. sync ( ) ?;
9681057
9691058 // Truncate the file to the final cursor location.
970- //
971- // Calling `set_len()` may result in an error (for example,
972- // when calling it on `/dev/null`), but we don't want to
973- // terminate the process when that happens. Instead, we
974- // suppress the error by calling `Result::ok()`. This matches
975- // the behavior of GNU `dd` when given the command-line
976- // argument `of=/dev/null`.
977- if !output. settings . oconv . notrunc {
978- output. dst . truncate ( ) . ok ( ) ;
1059+ if truncate {
1060+ output. truncate ( ) ;
9791061 }
9801062
9811063 // Print the final read/write statistics.
1064+ let wstat = wstat + wstat_update;
9821065 let prog_update = ProgUpdate :: new ( rstat, wstat, start. elapsed ( ) , true ) ;
9831066 prog_tx. send ( prog_update) . unwrap_or ( ( ) ) ;
9841067 // Wait for the output thread to finish
0 commit comments