@@ -58,6 +58,7 @@ use crate::os::unix::fs::FileTypeExt;
5858use crate :: os:: unix:: io:: { AsRawFd , FromRawFd , RawFd } ;
5959use crate :: process:: { ChildStderr , ChildStdin , ChildStdout } ;
6060use crate :: ptr;
61+ use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
6162use crate :: sys:: cvt;
6263
6364#[ cfg( test) ]
@@ -440,7 +441,6 @@ pub(super) enum CopyResult {
440441/// If the initial file offset was 0 then `Fallback` will only contain `0`.
441442pub ( super ) fn copy_regular_files ( reader : RawFd , writer : RawFd , max_len : u64 ) -> CopyResult {
442443 use crate :: cmp;
443- use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
444444
445445 // Kernel prior to 4.5 don't have copy_file_range
446446 // We store the availability in a global to avoid unnecessary syscalls
@@ -534,6 +534,30 @@ enum SpliceMode {
534534/// performs splice or sendfile between file descriptors
535535/// Does _not_ fall back to a generic copy loop.
536536fn sendfile_splice ( mode : SpliceMode , reader : RawFd , writer : RawFd , len : u64 ) -> CopyResult {
537+ static HAS_SENDFILE : AtomicBool = AtomicBool :: new ( true ) ;
538+ static HAS_SPLICE : AtomicBool = AtomicBool :: new ( true ) ;
539+
540+ syscall ! {
541+ fn splice(
542+ srcfd: libc:: c_int,
543+ src_offset: * const i64 ,
544+ dstfd: libc:: c_int,
545+ dst_offset: * const i64 ,
546+ len: libc:: size_t,
547+ flags: libc:: c_int
548+ ) -> libc:: ssize_t
549+ }
550+
551+ match mode {
552+ SpliceMode :: Sendfile if !HAS_SENDFILE . load ( Ordering :: Relaxed ) => {
553+ return CopyResult :: Fallback ( 0 ) ;
554+ }
555+ SpliceMode :: Splice if !HAS_SPLICE . load ( Ordering :: Relaxed ) => {
556+ return CopyResult :: Fallback ( 0 ) ;
557+ }
558+ _ => ( ) ,
559+ }
560+
537561 let mut written = 0u64 ;
538562 while written < len {
539563 let chunk_size = crate :: cmp:: min ( len - written, 0x7ffff000_u64 ) as usize ;
@@ -543,7 +567,7 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) ->
543567 cvt ( unsafe { libc:: sendfile ( writer, reader, ptr:: null_mut ( ) , chunk_size) } )
544568 }
545569 SpliceMode :: Splice => cvt ( unsafe {
546- libc :: splice ( reader, ptr:: null_mut ( ) , writer, ptr:: null_mut ( ) , chunk_size, 0 )
570+ splice ( reader, ptr:: null_mut ( ) , writer, ptr:: null_mut ( ) , chunk_size, 0 )
547571 } ) ,
548572 } ;
549573
@@ -552,8 +576,18 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) ->
552576 Ok ( ret) => written += ret as u64 ,
553577 Err ( err) => {
554578 return match err. raw_os_error ( ) {
555- Some ( os_err) if os_err == libc:: EINVAL => {
556- // splice/sendfile do not support this particular file descritor (EINVAL)
579+ Some ( libc:: ENOSYS | libc:: EPERM ) => {
580+ // syscall not supported (ENOSYS)
581+ // syscall is disallowed, e.g. by seccomp (EPERM)
582+ match mode {
583+ SpliceMode :: Sendfile => HAS_SENDFILE . store ( false , Ordering :: Relaxed ) ,
584+ SpliceMode :: Splice => HAS_SPLICE . store ( false , Ordering :: Relaxed ) ,
585+ }
586+ assert_eq ! ( written, 0 ) ;
587+ CopyResult :: Fallback ( 0 )
588+ }
589+ Some ( libc:: EINVAL ) => {
590+ // splice/sendfile do not support this particular file descriptor (EINVAL)
557591 assert_eq ! ( written, 0 ) ;
558592 CopyResult :: Fallback ( 0 )
559593 }
0 commit comments