@@ -560,6 +560,12 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
560
560
// We store the availability in a global to avoid unnecessary syscalls
561
561
static HAS_COPY_FILE_RANGE : AtomicU8 = AtomicU8 :: new ( NOT_PROBED ) ;
562
562
563
+ let mut have_probed = match HAS_COPY_FILE_RANGE . load ( Ordering :: Relaxed ) {
564
+ NOT_PROBED => false ,
565
+ UNAVAILABLE => return CopyResult :: Fallback ( 0 ) ,
566
+ _ => true ,
567
+ } ;
568
+
563
569
syscall ! {
564
570
fn copy_file_range(
565
571
fd_in: libc:: c_int,
@@ -571,25 +577,22 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
571
577
) -> libc:: ssize_t
572
578
}
573
579
574
- match HAS_COPY_FILE_RANGE . load ( Ordering :: Relaxed ) {
575
- NOT_PROBED => {
576
- // EPERM can indicate seccomp filters or an immutable file.
577
- // To distinguish these cases we probe with invalid file descriptors which should result in EBADF if the syscall is supported
578
- // and some other error (ENOSYS or EPERM) if it's not available
579
- let result = unsafe {
580
- cvt ( copy_file_range ( INVALID_FD , ptr:: null_mut ( ) , INVALID_FD , ptr:: null_mut ( ) , 1 , 0 ) )
581
- } ;
582
-
583
- if matches ! ( result. map_err( |e| e. raw_os_error( ) ) , Err ( Some ( EBADF ) ) ) {
584
- HAS_COPY_FILE_RANGE . store ( AVAILABLE , Ordering :: Relaxed ) ;
585
- } else {
586
- HAS_COPY_FILE_RANGE . store ( UNAVAILABLE , Ordering :: Relaxed ) ;
587
- return CopyResult :: Fallback ( 0 ) ;
588
- }
580
+ fn probe_copy_file_range_support ( ) -> u8 {
581
+ // In some cases, we cannot determine availability from the first
582
+ // `copy_file_range` call. In this case, we probe with an invalid file
583
+ // descriptor so that the results are easily interpretable.
584
+ match unsafe {
585
+ cvt ( copy_file_range ( INVALID_FD , ptr:: null_mut ( ) , INVALID_FD , ptr:: null_mut ( ) , 1 , 0 ) )
586
+ . map_err ( |e| e. raw_os_error ( ) )
587
+ } {
588
+ Err ( Some ( EPERM | ENOSYS ) ) => UNAVAILABLE ,
589
+ Err ( Some ( EBADF ) ) => AVAILABLE ,
590
+ Ok ( _) => panic ! ( "unexpected copy_file_range probe success" ) ,
591
+ // Treat other errors as the syscall
592
+ // being unavailable.
593
+ Err ( _) => UNAVAILABLE ,
589
594
}
590
- UNAVAILABLE => return CopyResult :: Fallback ( 0 ) ,
591
- _ => { }
592
- } ;
595
+ }
593
596
594
597
let mut written = 0u64 ;
595
598
while written < max_len {
@@ -604,6 +607,11 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
604
607
cvt ( copy_file_range ( reader, ptr:: null_mut ( ) , writer, ptr:: null_mut ( ) , bytes_to_copy, 0 ) )
605
608
} ;
606
609
610
+ if !have_probed && copy_result. is_ok ( ) {
611
+ have_probed = true ;
612
+ HAS_COPY_FILE_RANGE . store ( AVAILABLE , Ordering :: Relaxed ) ;
613
+ }
614
+
607
615
match copy_result {
608
616
Ok ( 0 ) if written == 0 => {
609
617
// fallback to work around several kernel bugs where copy_file_range will fail to
@@ -619,7 +627,28 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
619
627
return match err. raw_os_error ( ) {
620
628
// when file offset + max_length > u64::MAX
621
629
Some ( EOVERFLOW ) => CopyResult :: Fallback ( written) ,
622
- Some ( ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF ) if written == 0 => {
630
+ Some ( raw_os_error @ ( ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF ) )
631
+ if written == 0 =>
632
+ {
633
+ if !have_probed {
634
+ let available = if matches ! ( raw_os_error, ENOSYS | EOPNOTSUPP | EPERM ) {
635
+ // EPERM can indicate seccomp filters or an
636
+ // immutable file. To distinguish these
637
+ // cases we probe with invalid file
638
+ // descriptors which should result in EBADF
639
+ // if the syscall is supported and EPERM or
640
+ // ENOSYS if it's not available.
641
+ //
642
+ // For EOPNOTSUPP, see below. In the case of
643
+ // ENOSYS, we try to cover for faulty FUSE
644
+ // drivers.
645
+ probe_copy_file_range_support ( )
646
+ } else {
647
+ AVAILABLE
648
+ } ;
649
+ HAS_COPY_FILE_RANGE . store ( available, Ordering :: Relaxed ) ;
650
+ }
651
+
623
652
// Try fallback io::copy if either:
624
653
// - Kernel version is < 4.5 (ENOSYS¹)
625
654
// - Files are mounted on different fs (EXDEV)
0 commit comments