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)

`