@@ -61,6 +61,7 @@ use crate::process::{ChildStderr, ChildStdin, ChildStdout};
61
61
use crate :: ptr;
62
62
use crate :: sync:: atomic:: { AtomicBool , AtomicU8 , Ordering } ;
63
63
use crate :: sys:: cvt;
64
+ use libc:: { EBADF , EINVAL , ENOSYS , EOPNOTSUPP , EOVERFLOW , EPERM , EXDEV } ;
64
65
65
66
#[ cfg( test) ]
66
67
mod tests;
@@ -535,7 +536,7 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
535
536
cvt ( copy_file_range ( INVALID_FD , ptr:: null_mut ( ) , INVALID_FD , ptr:: null_mut ( ) , 1 , 0 ) )
536
537
} ;
537
538
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 ) ) ) {
539
540
HAS_COPY_FILE_RANGE . store ( AVAILABLE , Ordering :: Relaxed ) ;
540
541
} else {
541
542
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) ->
573
574
Err ( err) => {
574
575
return match err. raw_os_error ( ) {
575
576
// 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 ) => {
580
579
// Try fallback io::copy if either:
581
580
// - Kernel version is < 4.5 (ENOSYS¹)
582
581
// - Files are mounted on different fs (EXDEV)
583
582
// - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP)
584
583
// - copy_file_range file is immutable or syscall is blocked by seccomp¹ (EPERM)
585
584
// - copy_file_range cannot be used with pipes or device nodes (EINVAL)
585
+ // - the writer fd was opened with O_APPEND (EBADF²)
586
586
//
587
587
// ¹ these cases should be detected by the initial probe but we handle them here
588
588
// 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
589
591
assert_eq ! ( written, 0 ) ;
590
592
CopyResult :: Fallback ( 0 )
591
593
}
@@ -649,7 +651,7 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) ->
649
651
Ok ( ret) => written += ret as u64 ,
650
652
Err ( err) => {
651
653
return match err. raw_os_error ( ) {
652
- Some ( libc :: ENOSYS | libc :: EPERM ) => {
654
+ Some ( ENOSYS | EPERM ) => {
653
655
// syscall not supported (ENOSYS)
654
656
// syscall is disallowed, e.g. by seccomp (EPERM)
655
657
match mode {
@@ -659,12 +661,12 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) ->
659
661
assert_eq ! ( written, 0 ) ;
660
662
CopyResult :: Fallback ( 0 )
661
663
}
662
- Some ( libc :: EINVAL ) => {
664
+ Some ( EINVAL ) => {
663
665
// splice/sendfile do not support this particular file descriptor (EINVAL)
664
666
assert_eq ! ( written, 0 ) ;
665
667
CopyResult :: Fallback ( 0 )
666
668
}
667
- Some ( os_err) if mode == SpliceMode :: Sendfile && os_err == libc :: EOVERFLOW => {
669
+ Some ( os_err) if mode == SpliceMode :: Sendfile && os_err == EOVERFLOW => {
668
670
CopyResult :: Fallback ( written)
669
671
}
670
672
_ => CopyResult :: Error ( err, written) ,
0 commit comments