@@ -560,6 +560,12 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
560560    // We store the availability in a global to avoid unnecessary syscalls 
561561    static  HAS_COPY_FILE_RANGE :  AtomicU8  = AtomicU8 :: new ( NOT_PROBED ) ; 
562562
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+ 
563569    syscall !  { 
564570        fn  copy_file_range( 
565571            fd_in:  libc:: c_int, 
@@ -571,25 +577,22 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
571577        )  -> libc:: ssize_t
572578    } 
573579
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 , 
589594        } 
590-         UNAVAILABLE  => return  CopyResult :: Fallback ( 0 ) , 
591-         _ => { } 
592-     } ; 
595+     } 
593596
594597    let  mut  written = 0u64 ; 
595598    while  written < max_len { 
@@ -604,6 +607,11 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
604607            cvt ( copy_file_range ( reader,  ptr:: null_mut ( ) ,  writer,  ptr:: null_mut ( ) ,  bytes_to_copy,  0 ) ) 
605608        } ; 
606609
610+         if  !have_probed && copy_result. is_ok ( )  { 
611+             have_probed = true ; 
612+             HAS_COPY_FILE_RANGE . store ( AVAILABLE ,  Ordering :: Relaxed ) ; 
613+         } 
614+ 
607615        match  copy_result { 
608616            Ok ( 0 )  if  written == 0  => { 
609617                // 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) ->
619627                return  match  err. raw_os_error ( )  { 
620628                    // when file offset + max_length > u64::MAX 
621629                    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+ 
623652                        // Try fallback io::copy if either: 
624653                        // - Kernel version is < 4.5 (ENOSYS¹) 
625654                        // - Files are mounted on different fs (EXDEV) 
0 commit comments