diff --git a/library/std/src/sys/pal/unix/kernel_copy.rs b/library/std/src/sys/pal/unix/kernel_copy.rs index 1db86bdb180f9..4865e3296bc51 100644 --- a/library/std/src/sys/pal/unix/kernel_copy.rs +++ b/library/std/src/sys/pal/unix/kernel_copy.rs @@ -577,6 +577,23 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> ) -> libc::ssize_t } + fn probe_copy_file_range_support() -> u8 { + // In some cases, we cannot determine availability from the first + // `copy_file_range` call. In this case, we probe with an invalid file + // descriptor so that the results are easily interpretable. + match unsafe { + cvt(copy_file_range(INVALID_FD, ptr::null_mut(), INVALID_FD, ptr::null_mut(), 1, 0)) + .map_err(|e| e.raw_os_error()) + } { + Err(Some(EPERM | ENOSYS)) => UNAVAILABLE, + Err(Some(EBADF)) => AVAILABLE, + Ok(_) => panic!("unexpected copy_file_range probe success"), + // Treat other errors as the syscall + // being unavailable. + Err(_) => UNAVAILABLE, + } + } + let mut written = 0u64; while written < max_len { let bytes_to_copy = cmp::min(max_len - written, usize::MAX as u64); @@ -614,35 +631,20 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> if written == 0 => { if !have_probed { - let available = match raw_os_error { - EPERM => { - // EPERM can indicate seccomp filters or an - // immutable file. To distinguish these - // cases we probe with invalid file - // descriptors which should result in EBADF - // if the syscall is supported and EPERM or - // ENOSYS if it's not available. - match unsafe { - cvt(copy_file_range( - INVALID_FD, - ptr::null_mut(), - INVALID_FD, - ptr::null_mut(), - 1, - 0, - )) - .map_err(|e| e.raw_os_error()) - } { - Err(Some(EPERM | ENOSYS)) => UNAVAILABLE, - Err(Some(EBADF)) => AVAILABLE, - Ok(_) => panic!("unexpected copy_file_range probe success"), - // Treat other errors as the syscall - // being unavailable. - Err(_) => UNAVAILABLE, - } - } - ENOSYS => UNAVAILABLE, - _ => AVAILABLE, + let available = if matches!(raw_os_error, ENOSYS | EOPNOTSUPP | EPERM) { + // EPERM can indicate seccomp filters or an + // immutable file. To distinguish these + // cases we probe with invalid file + // descriptors which should result in EBADF + // if the syscall is supported and EPERM or + // ENOSYS if it's not available. + // + // For EOPNOTSUPP, see below. In the case of + // ENOSYS, we check even though it should be + // clear that there's no support. + probe_copy_file_range_support() + } else { + AVAILABLE }; HAS_COPY_FILE_RANGE.store(available, Ordering::Relaxed); }