Skip to content

Commit

Permalink
Be stricter with copy_file_range probe results
Browse files Browse the repository at this point in the history
  • Loading branch information
tbu- committed Mar 6, 2024
1 parent 214c498 commit d5c3c46
Showing 1 changed file with 39 additions and 33 deletions.
72 changes: 39 additions & 33 deletions library/std/src/sys/pal/unix/kernel_copy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,42 +607,48 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
Ok(0) => return CopyResult::Ended(written), // reached EOF
Ok(ret) => written += ret as u64,
Err(err) => {
let raw_os_error = match err.raw_os_error() {
Some(raw) => raw,
_ => return CopyResult::Error(err, written),
};
return match raw_os_error {
return match err.raw_os_error() {
// when file offset + max_length > u64::MAX
EOVERFLOW => CopyResult::Fallback(written),
ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF if written == 0 => {
Some(EOVERFLOW) => CopyResult::Fallback(written),
Some(raw_os_error @ (ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF))
if written == 0 =>
{
if !have_probed {
if raw_os_error == ENOSYS {
HAS_COPY_FILE_RANGE.store(UNAVAILABLE, Ordering::Relaxed);
} else {
// 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 some other error (ENOSYS or
// EPERM) if it's not available.
let result = unsafe {
cvt(copy_file_range(
INVALID_FD,
ptr::null_mut(),
INVALID_FD,
ptr::null_mut(),
1,
0,
))
};

if matches!(result.map_err(|e| e.raw_os_error()), Err(Some(EBADF)))
{
HAS_COPY_FILE_RANGE.store(AVAILABLE, Ordering::Relaxed);
} else {
HAS_COPY_FILE_RANGE.store(UNAVAILABLE, Ordering::Relaxed);
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 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())
} {
// The syscall can't fail with ENOSYS
// if it previously returned a
// different error.
Err(Some(EPERM)) => UNAVAILABLE,
Err(Some(EBADF)) => AVAILABLE,
other => {
panic!(
"unexpected copy_file_range probe result: {other:?}"
);
}
}
}
}
ENOSYS => UNAVAILABLE,
_ => AVAILABLE,
};
HAS_COPY_FILE_RANGE.store(available, Ordering::Relaxed);
}

// Try fallback io::copy if either:
Expand Down

0 comments on commit d5c3c46

Please sign in to comment.