Skip to content

Commit f623a40

Browse files
the8472Mark-Simulacrum
authored andcommitted
fix io::copy specialization when writer was opened with O_APPEND
1 parent af7cfb4 commit f623a40

File tree

1 file changed

+10
-8
lines changed

1 file changed

+10
-8
lines changed

library/std/src/sys/unix/kernel_copy.rs

+10-8
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ use crate::process::{ChildStderr, ChildStdin, ChildStdout};
6161
use crate::ptr;
6262
use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering};
6363
use crate::sys::cvt;
64+
use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV};
6465

6566
#[cfg(test)]
6667
mod tests;
@@ -535,7 +536,7 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
535536
cvt(copy_file_range(INVALID_FD, ptr::null_mut(), INVALID_FD, ptr::null_mut(), 1, 0))
536537
};
537538

538-
if matches!(result.map_err(|e| e.raw_os_error()), Err(Some(libc::EBADF))) {
539+
if matches!(result.map_err(|e| e.raw_os_error()), Err(Some(EBADF))) {
539540
HAS_COPY_FILE_RANGE.store(AVAILABLE, Ordering::Relaxed);
540541
} else {
541542
HAS_COPY_FILE_RANGE.store(UNAVAILABLE, Ordering::Relaxed);
@@ -573,19 +574,20 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
573574
Err(err) => {
574575
return match err.raw_os_error() {
575576
// when file offset + max_length > u64::MAX
576-
Some(libc::EOVERFLOW) => CopyResult::Fallback(written),
577-
Some(
578-
libc::ENOSYS | libc::EXDEV | libc::EINVAL | libc::EPERM | libc::EOPNOTSUPP,
579-
) => {
577+
Some(EOVERFLOW) => CopyResult::Fallback(written),
578+
Some(ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF) => {
580579
// Try fallback io::copy if either:
581580
// - Kernel version is < 4.5 (ENOSYS¹)
582581
// - Files are mounted on different fs (EXDEV)
583582
// - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP)
584583
// - copy_file_range file is immutable or syscall is blocked by seccomp¹ (EPERM)
585584
// - copy_file_range cannot be used with pipes or device nodes (EINVAL)
585+
// - the writer fd was opened with O_APPEND (EBADF²)
586586
//
587587
// ¹ these cases should be detected by the initial probe but we handle them here
588588
// anyway in case syscall interception changes during runtime
589+
// ² actually invalid file descriptors would cause this too, but in that case
590+
// the fallback code path is expected to encounter the same error again
589591
assert_eq!(written, 0);
590592
CopyResult::Fallback(0)
591593
}
@@ -649,7 +651,7 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) ->
649651
Ok(ret) => written += ret as u64,
650652
Err(err) => {
651653
return match err.raw_os_error() {
652-
Some(libc::ENOSYS | libc::EPERM) => {
654+
Some(ENOSYS | EPERM) => {
653655
// syscall not supported (ENOSYS)
654656
// syscall is disallowed, e.g. by seccomp (EPERM)
655657
match mode {
@@ -659,12 +661,12 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) ->
659661
assert_eq!(written, 0);
660662
CopyResult::Fallback(0)
661663
}
662-
Some(libc::EINVAL) => {
664+
Some(EINVAL) => {
663665
// splice/sendfile do not support this particular file descriptor (EINVAL)
664666
assert_eq!(written, 0);
665667
CopyResult::Fallback(0)
666668
}
667-
Some(os_err) if mode == SpliceMode::Sendfile && os_err == libc::EOVERFLOW => {
669+
Some(os_err) if mode == SpliceMode::Sendfile && os_err == EOVERFLOW => {
668670
CopyResult::Fallback(written)
669671
}
670672
_ => CopyResult::Error(err, written),

0 commit comments

Comments
 (0)