Auto merge of #122079 - tbu-:pr_copy_file_range_probe, r=the8472 · model-checking/verify-rust-std@bc346a0 (original) (raw)
`@@ -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)
`