@@ -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