@@ -45,15 +45,17 @@ where
4545 R : Read ,
4646 W : Write ,
4747{
48- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
49- {
50- kernel_copy:: copy_spec ( reader, writer)
48+ cfg_if:: cfg_if! {
49+ if #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ] {
50+ crate :: sys:: kernel_copy:: copy_spec( reader, writer)
51+ } else {
52+ generic_copy( reader, writer)
53+ }
5154 }
52-
53- #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
54- generic_copy ( reader, writer)
5555}
5656
57+ /// The general read-write-loop implementation of
58+ /// `io::copy` that is used when specializations are not available or not applicable.
5759pub ( crate ) fn generic_copy < R : ?Sized , W : ?Sized > ( reader : & mut R , writer : & mut W ) -> io:: Result < u64 >
5860where
5961 R : Read ,
@@ -84,374 +86,3 @@ where
8486 written += len as u64 ;
8587 }
8688}
87-
88- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
89- mod kernel_copy {
90-
91- use crate :: cmp:: min;
92- use crate :: convert:: TryInto ;
93- use crate :: fs:: { File , Metadata } ;
94- use crate :: io:: {
95- BufRead , BufReader , BufWriter , Read , Result , StderrLock , StdinLock , StdoutLock , Take , Write ,
96- } ;
97- use crate :: mem:: ManuallyDrop ;
98- use crate :: net:: TcpStream ;
99- use crate :: os:: unix:: fs:: FileTypeExt ;
100- use crate :: os:: unix:: io:: { AsRawFd , FromRawFd , RawFd } ;
101- use crate :: process:: { ChildStderr , ChildStdin , ChildStdout } ;
102- use crate :: sys:: fs:: { copy_regular_files, sendfile_splice, CopyResult , SpliceMode } ;
103-
104- pub ( super ) fn copy_spec < R : Read + ?Sized , W : Write + ?Sized > (
105- read : & mut R ,
106- write : & mut W ,
107- ) -> Result < u64 > {
108- let copier = Copier { read, write } ;
109- SpecCopy :: copy ( copier)
110- }
111-
112- /// This type represents either the inferred `FileType` of a `RawFd` based on the source
113- /// type from which it was extracted or the actual metadata
114- ///
115- /// The methods on this type only provide hints, due to `AsRawFd` and `FromRawFd` the inferred
116- /// type may be wrong.
117- enum FdMeta {
118- /// We obtained the FD from a type that can contain any type of `FileType` and queried the metadata
119- /// because it is cheaper than probing all possible syscalls (reader side)
120- Metadata ( Metadata ) ,
121- Socket ,
122- Pipe ,
123- /// We don't have any metadata, e.g. because the original type was `File` which can represent
124- /// any `FileType` and we did not query the metadata either since it did not seem beneficial
125- /// (writer side)
126- NoneObtained ,
127- }
128-
129- impl FdMeta {
130- fn maybe_fifo ( & self ) -> bool {
131- match self {
132- FdMeta :: Metadata ( meta) => meta. file_type ( ) . is_fifo ( ) ,
133- FdMeta :: Socket => false ,
134- FdMeta :: Pipe => true ,
135- FdMeta :: NoneObtained => true ,
136- }
137- }
138-
139- fn potential_sendfile_source ( & self ) -> bool {
140- match self {
141- // procfs erronously shows 0 length on non-empty readable files.
142- // and if a file is truly empty then a `read` syscall will determine that and skip the write syscall
143- // thus there would be benefit from attempting sendfile
144- FdMeta :: Metadata ( meta)
145- if meta. file_type ( ) . is_file ( ) && meta. len ( ) > 0
146- || meta. file_type ( ) . is_block_device ( ) =>
147- {
148- true
149- }
150- _ => false ,
151- }
152- }
153-
154- fn copy_file_range_candidate ( & self ) -> bool {
155- match self {
156- // copy_file_range will fail on empty procfs files. `read` can determine whether EOF has been reached
157- // without extra cost and skip the write, thus there is no benefit in attempting copy_file_range
158- FdMeta :: Metadata ( meta) if meta. is_file ( ) && meta. len ( ) > 0 => true ,
159- FdMeta :: NoneObtained => true ,
160- _ => false ,
161- }
162- }
163- }
164-
165- struct CopyParams ( FdMeta , Option < RawFd > ) ;
166-
167- struct Copier < ' a , ' b , R : Read + ?Sized , W : Write + ?Sized > {
168- pub read : & ' a mut R ,
169- pub write : & ' b mut W ,
170- }
171-
172- trait SpecCopy {
173- fn copy ( self ) -> Result < u64 > ;
174- }
175-
176- impl < R : Read + ?Sized , W : Write + ?Sized > SpecCopy for Copier < ' _ , ' _ , R , W > {
177- default fn copy ( self ) -> Result < u64 > {
178- super :: generic_copy ( self . read , self . write )
179- }
180- }
181-
182- impl < R : CopyRead , W : CopyWrite > SpecCopy for Copier < ' _ , ' _ , R , W > {
183- fn copy ( self ) -> Result < u64 > {
184- let ( reader, writer) = ( self . read , self . write ) ;
185- let r_cfg = reader. properties ( ) ;
186- let w_cfg = writer. properties ( ) ;
187-
188- // before direct operations on file descriptors ensure that all source and sink buffers are emtpy
189- let mut flush = || -> crate :: io:: Result < u64 > {
190- let bytes = reader. drain_to ( writer, u64:: MAX ) ?;
191- // BufWriter buffered bytes have already been accounted for in earlier write() calls
192- writer. flush ( ) ?;
193- Ok ( bytes)
194- } ;
195-
196- let mut written = 0u64 ;
197-
198- if let ( CopyParams ( input_meta, Some ( readfd) ) , CopyParams ( output_meta, Some ( writefd) ) ) =
199- ( r_cfg, w_cfg)
200- {
201- written += flush ( ) ?;
202- let max_write = reader. min_limit ( ) ;
203-
204- if input_meta. copy_file_range_candidate ( ) && output_meta. copy_file_range_candidate ( )
205- {
206- let result = copy_regular_files ( readfd, writefd, max_write) ;
207-
208- match result {
209- CopyResult :: Ended ( Ok ( bytes_copied) ) => return Ok ( bytes_copied + written) ,
210- CopyResult :: Ended ( err) => return err,
211- CopyResult :: Fallback ( bytes) => written += bytes,
212- }
213- }
214-
215- // on modern kernels sendfile can copy from any mmapable type (some but not all regular files and block devices)
216- // to any writable file descriptor. On older kernels the writer side can only be a socket.
217- // So we just try and fallback if needed.
218- // If current file offsets + write sizes overflow it may also fail, we do not try to fix that and instead
219- // fall back to the generic copy loop.
220- if input_meta. potential_sendfile_source ( ) {
221- let result = sendfile_splice ( SpliceMode :: Sendfile , readfd, writefd, max_write) ;
222-
223- match result {
224- CopyResult :: Ended ( Ok ( bytes_copied) ) => return Ok ( bytes_copied + written) ,
225- CopyResult :: Ended ( err) => return err,
226- CopyResult :: Fallback ( bytes) => written += bytes,
227- }
228- }
229-
230- if input_meta. maybe_fifo ( ) || output_meta. maybe_fifo ( ) {
231- let result = sendfile_splice ( SpliceMode :: Splice , readfd, writefd, max_write) ;
232-
233- match result {
234- CopyResult :: Ended ( Ok ( bytes_copied) ) => return Ok ( bytes_copied + written) ,
235- CopyResult :: Ended ( err) => return err,
236- CopyResult :: Fallback ( 0 ) => { /* use fallback */ }
237- CopyResult :: Fallback ( _) => {
238- unreachable ! ( "splice should not return > 0 bytes on the fallback path" )
239- }
240- }
241- }
242- }
243-
244- match super :: generic_copy ( reader, writer) {
245- Ok ( bytes) => Ok ( bytes + written) ,
246- err => err,
247- }
248- }
249- }
250-
251- #[ rustc_specialization_trait]
252- trait CopyRead : Read {
253- fn drain_to < W : Write > ( & mut self , _writer : & mut W , _limit : u64 ) -> Result < u64 > {
254- Ok ( 0 )
255- }
256-
257- /// The minimum of the limit of all `Take<_>` wrappers, `u64::MAX` otherwise.
258- /// This method does not account for data `BufReader` buffers and would underreport
259- /// the limit of a `Take<BufReader<Take<_>>>` type. Thus its result is only valid
260- /// after draining the buffers.
261- fn min_limit ( & self ) -> u64 {
262- u64:: MAX
263- }
264-
265- fn properties ( & self ) -> CopyParams ;
266- }
267-
268- #[ rustc_specialization_trait]
269- trait CopyWrite : Write {
270- fn properties ( & self ) -> CopyParams ;
271- }
272-
273- impl < T > CopyRead for & mut T
274- where
275- T : CopyRead ,
276- {
277- fn drain_to < W : Write > ( & mut self , writer : & mut W , limit : u64 ) -> Result < u64 > {
278- ( * * self ) . drain_to ( writer, limit)
279- }
280-
281- fn min_limit ( & self ) -> u64 {
282- ( * * self ) . min_limit ( )
283- }
284-
285- fn properties ( & self ) -> CopyParams {
286- ( * * self ) . properties ( )
287- }
288- }
289-
290- impl < T > CopyWrite for & mut T
291- where
292- T : CopyWrite ,
293- {
294- fn properties ( & self ) -> CopyParams {
295- ( * * self ) . properties ( )
296- }
297- }
298-
299- impl CopyRead for File {
300- fn properties ( & self ) -> CopyParams {
301- CopyParams ( fd_to_meta ( self ) , Some ( self . as_raw_fd ( ) ) )
302- }
303- }
304-
305- impl CopyRead for & File {
306- fn properties ( & self ) -> CopyParams {
307- CopyParams ( fd_to_meta ( * self ) , Some ( self . as_raw_fd ( ) ) )
308- }
309- }
310-
311- impl CopyWrite for File {
312- fn properties ( & self ) -> CopyParams {
313- CopyParams ( FdMeta :: NoneObtained , Some ( self . as_raw_fd ( ) ) )
314- }
315- }
316-
317- impl CopyWrite for & File {
318- fn properties ( & self ) -> CopyParams {
319- CopyParams ( FdMeta :: NoneObtained , Some ( self . as_raw_fd ( ) ) )
320- }
321- }
322-
323- impl CopyRead for TcpStream {
324- fn properties ( & self ) -> CopyParams {
325- // avoid the stat syscall since we can be fairly sure it's a socket
326- CopyParams ( FdMeta :: Socket , Some ( self . as_raw_fd ( ) ) )
327- }
328- }
329-
330- impl CopyRead for & TcpStream {
331- fn properties ( & self ) -> CopyParams {
332- // avoid the stat syscall since we can be fairly sure it's a socket
333- CopyParams ( FdMeta :: Socket , Some ( self . as_raw_fd ( ) ) )
334- }
335- }
336-
337- impl CopyWrite for TcpStream {
338- fn properties ( & self ) -> CopyParams {
339- // avoid the stat syscall since we can be fairly sure it's a socket
340- CopyParams ( FdMeta :: Socket , Some ( self . as_raw_fd ( ) ) )
341- }
342- }
343-
344- impl CopyWrite for & TcpStream {
345- fn properties ( & self ) -> CopyParams {
346- // avoid the stat syscall since we can be fairly sure it's a socket
347- CopyParams ( FdMeta :: Socket , Some ( self . as_raw_fd ( ) ) )
348- }
349- }
350-
351- impl CopyWrite for ChildStdin {
352- fn properties ( & self ) -> CopyParams {
353- CopyParams ( FdMeta :: Pipe , Some ( self . as_raw_fd ( ) ) )
354- }
355- }
356-
357- impl CopyRead for ChildStdout {
358- fn properties ( & self ) -> CopyParams {
359- CopyParams ( FdMeta :: Pipe , Some ( self . as_raw_fd ( ) ) )
360- }
361- }
362-
363- impl CopyRead for ChildStderr {
364- fn properties ( & self ) -> CopyParams {
365- CopyParams ( FdMeta :: Pipe , Some ( self . as_raw_fd ( ) ) )
366- }
367- }
368-
369- impl CopyRead for StdinLock < ' _ > {
370- fn drain_to < W : Write > ( & mut self , writer : & mut W , outer_limit : u64 ) -> Result < u64 > {
371- let buf_reader = self . as_mut_buf ( ) ;
372- let buf = buf_reader. buffer ( ) ;
373- let buf = & buf[ 0 ..min ( buf. len ( ) , outer_limit. try_into ( ) . unwrap_or ( usize:: MAX ) ) ] ;
374- let bytes_drained = buf. len ( ) ;
375- writer. write_all ( buf) ?;
376- buf_reader. consume ( bytes_drained) ;
377-
378- Ok ( bytes_drained as u64 )
379- }
380-
381- fn properties ( & self ) -> CopyParams {
382- CopyParams ( fd_to_meta ( self ) , Some ( self . as_raw_fd ( ) ) )
383- }
384- }
385-
386- impl CopyWrite for StdoutLock < ' _ > {
387- fn properties ( & self ) -> CopyParams {
388- CopyParams ( FdMeta :: NoneObtained , Some ( self . as_raw_fd ( ) ) )
389- }
390- }
391-
392- impl CopyWrite for StderrLock < ' _ > {
393- fn properties ( & self ) -> CopyParams {
394- CopyParams ( FdMeta :: NoneObtained , Some ( self . as_raw_fd ( ) ) )
395- }
396- }
397-
398- impl < T : CopyRead > CopyRead for Take < T > {
399- fn drain_to < W : Write > ( & mut self , writer : & mut W , outer_limit : u64 ) -> Result < u64 > {
400- let local_limit = self . limit ( ) ;
401- let combined_limit = min ( outer_limit, local_limit) ;
402- let bytes_drained = self . get_mut ( ) . drain_to ( writer, combined_limit) ?;
403- // update limit since read() was bypassed
404- self . set_limit ( local_limit - bytes_drained) ;
405-
406- Ok ( bytes_drained)
407- }
408-
409- fn min_limit ( & self ) -> u64 {
410- min ( Take :: limit ( self ) , self . get_ref ( ) . min_limit ( ) )
411- }
412-
413- fn properties ( & self ) -> CopyParams {
414- self . get_ref ( ) . properties ( )
415- }
416- }
417-
418- impl < T : CopyRead > CopyRead for BufReader < T > {
419- fn drain_to < W : Write > ( & mut self , writer : & mut W , outer_limit : u64 ) -> Result < u64 > {
420- let buf = self . buffer ( ) ;
421- let buf = & buf[ 0 ..min ( buf. len ( ) , outer_limit. try_into ( ) . unwrap_or ( usize:: MAX ) ) ] ;
422- let bytes = buf. len ( ) ;
423- writer. write_all ( buf) ?;
424- self . consume ( bytes) ;
425-
426- let remaining = outer_limit - bytes as u64 ;
427-
428- // in case of nested bufreaders we also need to drain the ones closer to the source
429- let inner_bytes = self . get_mut ( ) . drain_to ( writer, remaining) ?;
430-
431- Ok ( bytes as u64 + inner_bytes)
432- }
433-
434- fn min_limit ( & self ) -> u64 {
435- self . get_ref ( ) . min_limit ( )
436- }
437-
438- fn properties ( & self ) -> CopyParams {
439- self . get_ref ( ) . properties ( )
440- }
441- }
442-
443- impl < T : CopyWrite > CopyWrite for BufWriter < T > {
444- fn properties ( & self ) -> CopyParams {
445- self . get_ref ( ) . properties ( )
446- }
447- }
448-
449- fn fd_to_meta < T : AsRawFd > ( fd : & T ) -> FdMeta {
450- let fd = fd. as_raw_fd ( ) ;
451- let file: ManuallyDrop < File > = ManuallyDrop :: new ( unsafe { File :: from_raw_fd ( fd) } ) ;
452- match file. metadata ( ) {
453- Ok ( meta) => FdMeta :: Metadata ( meta) ,
454- Err ( _) => FdMeta :: NoneObtained ,
455- }
456- }
457- }
0 commit comments