1#[cfg(not(any(all(target_os = "linux", target_env = "gnu"), target_os = "hurd")))]
46use libc::sendfile as sendfile64;
47#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "hurd"))]
48use libc::sendfile64;
49use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV};
50
51use crate::cmp::min;
52use crate::fs::{File, Metadata};
53use crate::io::{
54 self, BufRead, BufReader, BufWriter, CopyState, Error, PipeReader, PipeWriter, Read, Result,
55 StderrLock, StdinLock, StdoutLock, Take, Write,
56};
57use crate::mem::ManuallyDrop;
58use crate::net::TcpStream;
59use crate::os::unix::fs::FileTypeExt;
60use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd};
61use crate::os::unix::net::UnixStream;
62use crate::process::{ChildStderr, ChildStdin, ChildStdout};
63use crate::ptr;
64use crate::sync::atomic::{Atomic, AtomicBool, AtomicU8, Ordering};
65use crate::sys::cvt;
66use crate::sys::fs::CachedFileMetadata;
67use crate::sys::weak::syscall;
68
69#[cfg(test)]
70mod tests;
71
72#[doc(hidden)]
73#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")]
74impl io::SpecCopy for File {
75 fn copy<R: Read + ?Sized, W: Write + ?Sized>(read: &mut R, write: &mut W) -> Result<CopyState> {
76 SpecCopy::copy(Copier { read, write })
77 }
78}
79
80#[doc(hidden)]
81#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")]
82impl io::SpecCopy for &File {
83 fn copy<R: Read + ?Sized, W: Write + ?Sized>(read: &mut R, write: &mut W) -> Result<CopyState> {
84 SpecCopy::copy(Copier { read, write })
85 }
86}
87
88#[doc(hidden)]
89#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")]
90impl io::SpecCopy for TcpStream {
91 fn copy<R: Read + ?Sized, W: Write + ?Sized>(read: &mut R, write: &mut W) -> Result<CopyState> {
92 SpecCopy::copy(Copier { read, write })
93 }
94}
95
96#[doc(hidden)]
97#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")]
98impl io::SpecCopy for &TcpStream {
99 fn copy<R: Read + ?Sized, W: Write + ?Sized>(read: &mut R, write: &mut W) -> Result<CopyState> {
100 SpecCopy::copy(Copier { read, write })
101 }
102}
103
104#[doc(hidden)]
105#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")]
106impl io::SpecCopy for UnixStream {
107 fn copy<R: Read + ?Sized, W: Write + ?Sized>(read: &mut R, write: &mut W) -> Result<CopyState> {
108 SpecCopy::copy(Copier { read, write })
109 }
110}
111
112#[doc(hidden)]
113#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")]
114impl io::SpecCopy for &UnixStream {
115 fn copy<R: Read + ?Sized, W: Write + ?Sized>(read: &mut R, write: &mut W) -> Result<CopyState> {
116 SpecCopy::copy(Copier { read, write })
117 }
118}
119
120#[doc(hidden)]
121#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")]
122impl io::SpecCopy for PipeReader {
123 fn copy<R: Read + ?Sized, W: Write + ?Sized>(read: &mut R, write: &mut W) -> Result<CopyState> {
124 SpecCopy::copy(Copier { read, write })
125 }
126}
127
128#[doc(hidden)]
129#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")]
130impl io::SpecCopy for &PipeReader {
131 fn copy<R: Read + ?Sized, W: Write + ?Sized>(read: &mut R, write: &mut W) -> Result<CopyState> {
132 SpecCopy::copy(Copier { read, write })
133 }
134}
135
136#[doc(hidden)]
137#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")]
138impl io::SpecCopy for ChildStdout {
139 fn copy<R: Read + ?Sized, W: Write + ?Sized>(read: &mut R, write: &mut W) -> Result<CopyState> {
140 SpecCopy::copy(Copier { read, write })
141 }
142}
143
144#[doc(hidden)]
145#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")]
146impl io::SpecCopy for ChildStderr {
147 fn copy<R: Read + ?Sized, W: Write + ?Sized>(read: &mut R, write: &mut W) -> Result<CopyState> {
148 SpecCopy::copy(Copier { read, write })
149 }
150}
151
152#[doc(hidden)]
153#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")]
154impl io::SpecCopy for StdinLock<'_> {
155 fn copy<R: Read + ?Sized, W: Write + ?Sized>(read: &mut R, write: &mut W) -> Result<CopyState> {
156 SpecCopy::copy(Copier { read, write })
157 }
158}
159
160impl io::SpecCopy for CachedFileMetadata {
161 fn copy<R: Read + ?Sized, W: Write + ?Sized>(read: &mut R, write: &mut W) -> Result<CopyState> {
162 SpecCopy::copy(Copier { read, write })
163 }
164}
165
166enum FdMeta {
172 Metadata(Metadata),
173 Socket,
174 Pipe,
175 NoneObtained,
177}
178
179#[derive(#[automatically_derived]
impl ::core::marker::StructuralPartialEq for FdHandle { }
#[automatically_derived]
impl ::core::cmp::PartialEq for FdHandle {
#[inline]
fn eq(&self, other: &FdHandle) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq)]
180enum FdHandle {
181 Input,
182 Output,
183}
184
185impl FdMeta {
186 fn maybe_fifo(&self) -> bool {
187 match self {
188 FdMeta::Metadata(meta) => meta.file_type().is_fifo(),
189 FdMeta::Socket => false,
190 FdMeta::Pipe => true,
191 FdMeta::NoneObtained => true,
192 }
193 }
194
195 fn potential_sendfile_source(&self) -> bool {
196 match self {
197 FdMeta::Metadata(meta)
201 if meta.file_type().is_file() && meta.len() > 0
202 || meta.file_type().is_block_device() =>
203 {
204 true
205 }
206 _ => false,
207 }
208 }
209
210 fn copy_file_range_candidate(&self, f: FdHandle) -> bool {
211 match self {
212 FdMeta::Metadata(meta) if f == FdHandle::Input && meta.is_file() && meta.len() > 0 => {
215 true
216 }
217 FdMeta::Metadata(meta) if f == FdHandle::Output && meta.is_file() => true,
218 _ => false,
219 }
220 }
221}
222
223fn safe_kernel_copy(source: &FdMeta, sink: &FdMeta) -> bool {
233 match (source, sink) {
234 (FdMeta::Socket, _) => true,
239 (FdMeta::Pipe, _) => true,
240 (FdMeta::Metadata(meta), _)
241 if meta.file_type().is_fifo() || meta.file_type().is_socket() =>
242 {
243 true
244 }
245 (_, FdMeta::Metadata(meta))
248 if !meta.file_type().is_fifo() && !meta.file_type().is_socket() =>
249 {
250 true
251 }
252 _ => false,
253 }
254}
255
256struct CopyParams(FdMeta, Option<RawFd>);
257
258struct Copier<'a, 'b, R: Read + ?Sized, W: Write + ?Sized> {
259 read: &'a mut R,
260 write: &'b mut W,
261}
262
263trait SpecCopy {
264 fn copy(self) -> Result<CopyState>;
265}
266
267impl<R: Read + ?Sized, W: Write + ?Sized> SpecCopy for Copier<'_, '_, R, W> {
268 default fn copy(self) -> Result<CopyState> {
269 Ok(CopyState::Fallback(0))
270 }
271}
272
273impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
274 fn copy(self) -> Result<CopyState> {
275 let (reader, writer) = (self.read, self.write);
276 let r_cfg = reader.properties();
277 let w_cfg = writer.properties();
278
279 let mut flush = || -> Result<u64> {
281 let bytes = reader.drain_to(writer, u64::MAX)?;
282 writer.flush()?;
284 Ok(bytes)
285 };
286
287 let mut written = 0u64;
288
289 if let (CopyParams(input_meta, Some(readfd)), CopyParams(output_meta, Some(writefd))) =
290 (r_cfg, w_cfg)
291 {
292 written += flush()?;
293 let max_write = reader.min_limit();
294
295 if input_meta.copy_file_range_candidate(FdHandle::Input)
296 && output_meta.copy_file_range_candidate(FdHandle::Output)
297 {
298 let result = copy_regular_files(readfd, writefd, max_write);
299 result.update_take(reader);
300
301 match result {
302 CopyResult::Ended(bytes_copied) => {
303 return Ok(CopyState::Ended(bytes_copied + written));
304 }
305 CopyResult::Error(e, _) => return Err(e),
306 CopyResult::Fallback(bytes) => written += bytes,
307 }
308 }
309
310 if input_meta.potential_sendfile_source() && safe_kernel_copy(&input_meta, &output_meta)
316 {
317 let result = sendfile_splice(SpliceMode::Sendfile, readfd, writefd, max_write);
318 result.update_take(reader);
319
320 match result {
321 CopyResult::Ended(bytes_copied) => {
322 return Ok(CopyState::Ended(bytes_copied + written));
323 }
324 CopyResult::Error(e, _) => return Err(e),
325 CopyResult::Fallback(bytes) => written += bytes,
326 }
327 }
328
329 if (input_meta.maybe_fifo() || output_meta.maybe_fifo())
330 && safe_kernel_copy(&input_meta, &output_meta)
331 {
332 let result = sendfile_splice(SpliceMode::Splice, readfd, writefd, max_write);
333 result.update_take(reader);
334
335 match result {
336 CopyResult::Ended(bytes_copied) => {
337 return Ok(CopyState::Ended(bytes_copied + written));
338 }
339 CopyResult::Error(e, _) => return Err(e),
340 CopyResult::Fallback(0) => { }
341 CopyResult::Fallback(_) => {
342 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("splice should not return > 0 bytes on the fallback path")));
}unreachable!("splice should not return > 0 bytes on the fallback path")
343 }
344 }
345 }
346 }
347
348 Ok(CopyState::Fallback(written))
350 }
351}
352
353#[rustc_specialization_trait]
354trait CopyRead: Read {
355 fn drain_to<W: Write>(&mut self, _writer: &mut W, _limit: u64) -> Result<u64> {
363 Ok(0)
364 }
365
366 fn taken(&mut self, _bytes: u64) {}
368
369 fn min_limit(&self) -> u64 {
374 u64::MAX
375 }
376
377 fn properties(&self) -> CopyParams;
379}
380
381#[rustc_specialization_trait]
382trait CopyWrite: Write {
383 fn properties(&self) -> CopyParams;
385}
386
387impl<T> CopyRead for &mut T
388where
389 T: CopyRead,
390{
391 fn drain_to<W: Write>(&mut self, writer: &mut W, limit: u64) -> Result<u64> {
392 (**self).drain_to(writer, limit)
393 }
394
395 fn taken(&mut self, bytes: u64) {
396 (**self).taken(bytes);
397 }
398
399 fn min_limit(&self) -> u64 {
400 (**self).min_limit()
401 }
402
403 fn properties(&self) -> CopyParams {
404 (**self).properties()
405 }
406}
407
408impl<T> CopyWrite for &mut T
409where
410 T: CopyWrite,
411{
412 fn properties(&self) -> CopyParams {
413 (**self).properties()
414 }
415}
416
417impl CopyRead for File {
418 fn properties(&self) -> CopyParams {
419 CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
420 }
421}
422
423impl CopyRead for &File {
424 fn properties(&self) -> CopyParams {
425 CopyParams(fd_to_meta(*self), Some(self.as_raw_fd()))
426 }
427}
428
429impl CopyWrite for File {
430 fn properties(&self) -> CopyParams {
431 CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
432 }
433}
434
435impl CopyWrite for &File {
436 fn properties(&self) -> CopyParams {
437 CopyParams(fd_to_meta(*self), Some(self.as_raw_fd()))
438 }
439}
440
441impl CopyRead for TcpStream {
442 fn properties(&self) -> CopyParams {
443 CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
445 }
446}
447
448impl CopyRead for &TcpStream {
449 fn properties(&self) -> CopyParams {
450 CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
452 }
453}
454
455impl CopyWrite for TcpStream {
456 fn properties(&self) -> CopyParams {
457 CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
459 }
460}
461
462impl CopyWrite for &TcpStream {
463 fn properties(&self) -> CopyParams {
464 CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
466 }
467}
468
469impl CopyRead for UnixStream {
470 fn properties(&self) -> CopyParams {
471 CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
473 }
474}
475
476impl CopyRead for &UnixStream {
477 fn properties(&self) -> CopyParams {
478 CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
480 }
481}
482
483impl CopyWrite for UnixStream {
484 fn properties(&self) -> CopyParams {
485 CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
487 }
488}
489
490impl CopyWrite for &UnixStream {
491 fn properties(&self) -> CopyParams {
492 CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
494 }
495}
496
497impl CopyRead for PipeReader {
498 fn properties(&self) -> CopyParams {
499 CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
500 }
501}
502
503impl CopyRead for &PipeReader {
504 fn properties(&self) -> CopyParams {
505 CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
506 }
507}
508
509impl CopyWrite for PipeWriter {
510 fn properties(&self) -> CopyParams {
511 CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
512 }
513}
514
515impl CopyWrite for &PipeWriter {
516 fn properties(&self) -> CopyParams {
517 CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
518 }
519}
520
521impl CopyWrite for ChildStdin {
522 fn properties(&self) -> CopyParams {
523 CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
524 }
525}
526
527impl CopyRead for ChildStdout {
528 fn properties(&self) -> CopyParams {
529 CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
530 }
531}
532
533impl CopyRead for ChildStderr {
534 fn properties(&self) -> CopyParams {
535 CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
536 }
537}
538
539impl CopyRead for StdinLock<'_> {
540 fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
541 let buf_reader = self.as_mut_buf();
542 let buf = buf_reader.buffer();
543 let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))];
544 let bytes_drained = buf.len();
545 writer.write_all(buf)?;
546 buf_reader.consume(bytes_drained);
547
548 Ok(bytes_drained as u64)
549 }
550
551 fn properties(&self) -> CopyParams {
552 CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
553 }
554}
555
556impl CopyWrite for StdoutLock<'_> {
557 fn properties(&self) -> CopyParams {
558 CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
559 }
560}
561
562impl CopyWrite for StderrLock<'_> {
563 fn properties(&self) -> CopyParams {
564 CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
565 }
566}
567
568impl<T: CopyRead> CopyRead for Take<T> {
569 fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
570 let local_limit = self.limit();
571 let combined_limit = min(outer_limit, local_limit);
572 let bytes_drained = self.get_mut().drain_to(writer, combined_limit)?;
573 self.set_limit(local_limit - bytes_drained);
575
576 Ok(bytes_drained)
577 }
578
579 fn taken(&mut self, bytes: u64) {
580 self.set_limit(self.limit() - bytes);
581 self.get_mut().taken(bytes);
582 }
583
584 fn min_limit(&self) -> u64 {
585 min(Take::limit(self), self.get_ref().min_limit())
586 }
587
588 fn properties(&self) -> CopyParams {
589 self.get_ref().properties()
590 }
591}
592
593impl<T: ?Sized + CopyRead> CopyRead for BufReader<T> {
594 fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
595 let buf = self.buffer();
596 let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))];
597 let bytes = buf.len();
598 writer.write_all(buf)?;
599 self.consume(bytes);
600
601 let remaining = outer_limit - bytes as u64;
602
603 let inner_bytes = self.get_mut().drain_to(writer, remaining)?;
605
606 Ok(bytes as u64 + inner_bytes)
607 }
608
609 fn taken(&mut self, bytes: u64) {
610 self.get_mut().taken(bytes);
611 }
612
613 fn min_limit(&self) -> u64 {
614 self.get_ref().min_limit()
615 }
616
617 fn properties(&self) -> CopyParams {
618 self.get_ref().properties()
619 }
620}
621
622impl<T: ?Sized + CopyWrite> CopyWrite for BufWriter<T> {
623 fn properties(&self) -> CopyParams {
624 self.get_ref().properties()
625 }
626}
627
628impl CopyRead for CachedFileMetadata {
629 fn properties(&self) -> CopyParams {
630 CopyParams(FdMeta::Metadata(self.1.clone()), Some(self.0.as_raw_fd()))
631 }
632}
633
634impl CopyWrite for CachedFileMetadata {
635 fn properties(&self) -> CopyParams {
636 CopyParams(FdMeta::Metadata(self.1.clone()), Some(self.0.as_raw_fd()))
637 }
638}
639
640fn fd_to_meta<T: AsRawFd>(fd: &T) -> FdMeta {
641 let fd = fd.as_raw_fd();
642 let file: ManuallyDrop<File> = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) });
643 match file.metadata() {
644 Ok(meta) => FdMeta::Metadata(meta),
645 Err(_) => FdMeta::NoneObtained,
646 }
647}
648
649enum CopyResult {
650 Ended(u64),
651 Error(Error, u64),
652 Fallback(u64),
653}
654
655impl CopyResult {
656 fn update_take(&self, reader: &mut impl CopyRead) {
657 match *self {
658 CopyResult::Fallback(bytes)
659 | CopyResult::Ended(bytes)
660 | CopyResult::Error(_, bytes) => reader.taken(bytes),
661 }
662 }
663}
664
665const INVALID_FD: RawFd = -1;
671
672fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> CopyResult {
679 use crate::cmp;
680
681 const NOT_PROBED: u8 = 0;
682 const UNAVAILABLE: u8 = 1;
683 const AVAILABLE: u8 = 2;
684
685 static HAS_COPY_FILE_RANGE: Atomic<u8> = AtomicU8::new(NOT_PROBED);
688
689 let mut have_probed = match HAS_COPY_FILE_RANGE.load(Ordering::Relaxed) {
690 NOT_PROBED => false,
691 UNAVAILABLE => return CopyResult::Fallback(0),
692 _ => true,
693 };
694
695 unsafe fn copy_file_range(fd_in: libc::c_int, off_in: *mut libc::loff_t,
fd_out: libc::c_int, off_out: *mut libc::loff_t, len: libc::size_t,
flags: libc::c_uint) -> libc::ssize_t {
let ref copy_file_range:
ExternWeak<unsafe extern "C" fn(libc::c_int, *mut libc::loff_t,
libc::c_int, *mut libc::loff_t, libc::size_t, libc::c_uint)
-> libc::ssize_t> =
{
unsafe extern "C" {
#[linkage = "extern_weak"]
static copy_file_range:
Option<unsafe extern "C" fn(libc::c_int, *mut libc::loff_t,
libc::c_int, *mut libc::loff_t, libc::size_t, libc::c_uint)
-> libc::ssize_t>;
}
#[allow(unused_unsafe)]
ExternWeak::new(unsafe { copy_file_range })
};
if let Some(fun) = copy_file_range.get() {
unsafe { fun(fd_in, off_in, fd_out, off_out, len, flags) }
} else {
unsafe {
libc::syscall(libc::SYS_copy_file_range, fd_in, off_in, fd_out,
off_out, len, flags) as libc::ssize_t
}
}
}syscall!(
696 fn copy_file_range(
697 fd_in: libc::c_int,
698 off_in: *mut libc::loff_t,
699 fd_out: libc::c_int,
700 off_out: *mut libc::loff_t,
701 len: libc::size_t,
702 flags: libc::c_uint,
703 ) -> libc::ssize_t;
704 );
705
706 fn probe_copy_file_range_support() -> u8 {
707 match unsafe {
711 cvt(copy_file_range(INVALID_FD, ptr::null_mut(), INVALID_FD, ptr::null_mut(), 1, 0))
712 .map_err(|e| e.raw_os_error())
713 } {
714 Err(Some(EPERM | ENOSYS)) => UNAVAILABLE,
715 Err(Some(EBADF)) => AVAILABLE,
716 Ok(_) => {
::core::panicking::panic_fmt(format_args!("unexpected copy_file_range probe success"));
}panic!("unexpected copy_file_range probe success"),
717 Err(_) => UNAVAILABLE,
720 }
721 }
722
723 let mut written = 0u64;
724 while written < max_len {
725 let bytes_to_copy = cmp::min(max_len - written, usize::MAX as u64);
726 let bytes_to_copy = cmp::min(bytes_to_copy as usize, 0x4000_0000usize);
730 let copy_result = unsafe {
731 cvt(copy_file_range(reader, ptr::null_mut(), writer, ptr::null_mut(), bytes_to_copy, 0))
734 };
735
736 if !have_probed && copy_result.is_ok() {
737 have_probed = true;
738 HAS_COPY_FILE_RANGE.store(AVAILABLE, Ordering::Relaxed);
739 }
740
741 match copy_result {
742 Ok(0) if written == 0 => {
743 return CopyResult::Fallback(0);
749 }
750 Ok(0) => return CopyResult::Ended(written), Ok(ret) => written += ret as u64,
752 Err(err) => {
753 return match err.raw_os_error() {
754 Some(EOVERFLOW) => CopyResult::Fallback(written),
756 Some(raw_os_error @ (ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF))
757 if written == 0 =>
758 {
759 if !have_probed {
760 let available = if #[allow(non_exhaustive_omitted_patterns)] match raw_os_error {
ENOSYS | EOPNOTSUPP | EPERM => true,
_ => false,
}matches!(raw_os_error, ENOSYS | EOPNOTSUPP | EPERM) {
761 probe_copy_file_range_support()
772 } else {
773 AVAILABLE
774 };
775 HAS_COPY_FILE_RANGE.store(available, Ordering::Relaxed);
776 }
777
778 CopyResult::Fallback(0)
794 }
795 _ => CopyResult::Error(err, written),
796 };
797 }
798 }
799 }
800 CopyResult::Ended(written)
801}
802
803#[derive(#[automatically_derived]
impl ::core::marker::StructuralPartialEq for SpliceMode { }
#[automatically_derived]
impl ::core::cmp::PartialEq for SpliceMode {
#[inline]
fn eq(&self, other: &SpliceMode) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq)]
804enum SpliceMode {
805 Sendfile,
806 Splice,
807}
808
809fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) -> CopyResult {
812 static HAS_SENDFILE: Atomic<bool> = AtomicBool::new(true);
813 static HAS_SPLICE: Atomic<bool> = AtomicBool::new(true);
814
815 #[cfg(target_os = "android")]
818 syscall!(
819 fn splice(
820 srcfd: libc::c_int,
821 src_offset: *const i64,
822 dstfd: libc::c_int,
823 dst_offset: *const i64,
824 len: libc::size_t,
825 flags: libc::c_int,
826 ) -> libc::ssize_t;
827 );
828
829 #[cfg(target_os = "linux")]
830 use libc::splice;
831
832 match mode {
833 SpliceMode::Sendfile if !HAS_SENDFILE.load(Ordering::Relaxed) => {
834 return CopyResult::Fallback(0);
835 }
836 SpliceMode::Splice if !HAS_SPLICE.load(Ordering::Relaxed) => {
837 return CopyResult::Fallback(0);
838 }
839 _ => (),
840 }
841
842 let mut written = 0u64;
843 while written < len {
844 let chunk_size = crate::cmp::min(len - written, 0x7ffff000_u64) as usize;
846
847 let result = match mode {
848 SpliceMode::Sendfile => {
849 cvt(unsafe { sendfile64(writer, reader, ptr::null_mut(), chunk_size) })
850 }
851 SpliceMode::Splice => cvt(unsafe {
852 splice(reader, ptr::null_mut(), writer, ptr::null_mut(), chunk_size, 0)
853 }),
854 };
855
856 match result {
857 Ok(0) => break, Ok(ret) => written += ret as u64,
859 Err(err) => {
860 return match err.raw_os_error() {
861 Some(ENOSYS | EPERM) => {
862 match mode {
865 SpliceMode::Sendfile => HAS_SENDFILE.store(false, Ordering::Relaxed),
866 SpliceMode::Splice => HAS_SPLICE.store(false, Ordering::Relaxed),
867 }
868 {
match (&written, &0) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};assert_eq!(written, 0);
869 CopyResult::Fallback(0)
870 }
871 Some(EINVAL) => {
872 {
match (&written, &0) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};assert_eq!(written, 0);
874 CopyResult::Fallback(0)
875 }
876 Some(os_err) if mode == SpliceMode::Sendfile && os_err == EOVERFLOW => {
877 CopyResult::Fallback(written)
878 }
879 _ => CopyResult::Error(err, written),
880 };
881 }
882 }
883 }
884 CopyResult::Ended(written)
885}