@@ -45,15 +45,17 @@ where
45
45
R : Read ,
46
46
W : Write ,
47
47
{
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
+ }
51
54
}
52
-
53
- #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
54
- generic_copy ( reader, writer)
55
55
}
56
56
57
+ /// The general read-write-loop implementation of
58
+ /// `io::copy` that is used when specializations are not available or not applicable.
57
59
pub ( crate ) fn generic_copy < R : ?Sized , W : ?Sized > ( reader : & mut R , writer : & mut W ) -> io:: Result < u64 >
58
60
where
59
61
R : Read ,
@@ -84,374 +86,3 @@ where
84
86
written += len as u64 ;
85
87
}
86
88
}
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