@@ -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,26 +577,6 @@ 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
- }
589
- }
590
- UNAVAILABLE => return CopyResult :: Fallback ( 0 ) ,
591
- _ => { }
592
- } ;
593
-
594
580
let mut written = 0u64 ;
595
581
while written < max_len {
596
582
let bytes_to_copy = cmp:: min ( max_len - written, usize:: MAX as u64 ) ;
@@ -604,6 +590,11 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
604
590
cvt ( copy_file_range ( reader, ptr:: null_mut ( ) , writer, ptr:: null_mut ( ) , bytes_to_copy, 0 ) )
605
591
} ;
606
592
593
+ if !have_probed && copy_result. is_ok ( ) {
594
+ have_probed = true ;
595
+ HAS_COPY_FILE_RANGE . store ( AVAILABLE , Ordering :: Relaxed ) ;
596
+ }
597
+
607
598
match copy_result {
608
599
Ok ( 0 ) if written == 0 => {
609
600
// fallback to work around several kernel bugs where copy_file_range will fail to
@@ -616,10 +607,44 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
616
607
Ok ( 0 ) => return CopyResult :: Ended ( written) , // reached EOF
617
608
Ok ( ret) => written += ret as u64 ,
618
609
Err ( err) => {
619
- return match err. raw_os_error ( ) {
610
+ let raw_os_error = match err. raw_os_error ( ) {
611
+ Some ( raw) => raw,
612
+ _ => return CopyResult :: Error ( err, written) ,
613
+ } ;
614
+ return match raw_os_error {
620
615
// when file offset + max_length > u64::MAX
621
- Some ( EOVERFLOW ) => CopyResult :: Fallback ( written) ,
622
- Some ( ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF ) if written == 0 => {
616
+ EOVERFLOW => CopyResult :: Fallback ( written) ,
617
+ ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF if written == 0 => {
618
+ if !have_probed {
619
+ if raw_os_error == ENOSYS {
620
+ HAS_COPY_FILE_RANGE . store ( UNAVAILABLE , Ordering :: Relaxed ) ;
621
+ } else {
622
+ // EPERM can indicate seccomp filters or an
623
+ // immutable file. To distinguish these cases
624
+ // we probe with invalid file descriptors which
625
+ // should result in EBADF if the syscall is
626
+ // supported and some other error (ENOSYS or
627
+ // EPERM) if it's not available.
628
+ let result = unsafe {
629
+ cvt ( copy_file_range (
630
+ INVALID_FD ,
631
+ ptr:: null_mut ( ) ,
632
+ INVALID_FD ,
633
+ ptr:: null_mut ( ) ,
634
+ 1 ,
635
+ 0 ,
636
+ ) )
637
+ } ;
638
+
639
+ if matches ! ( result. map_err( |e| e. raw_os_error( ) ) , Err ( Some ( EBADF ) ) )
640
+ {
641
+ HAS_COPY_FILE_RANGE . store ( AVAILABLE , Ordering :: Relaxed ) ;
642
+ } else {
643
+ HAS_COPY_FILE_RANGE . store ( UNAVAILABLE , Ordering :: Relaxed ) ;
644
+ }
645
+ }
646
+ }
647
+
623
648
// Try fallback io::copy if either:
624
649
// - Kernel version is < 4.5 (ENOSYS¹)
625
650
// - Files are mounted on different fs (EXDEV)
0 commit comments