1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
//! 该模块包含一些专门知识,可以将包含类型 (`File`,`TcpStream` 等) 的文件描述符上的 `io::copy()` 操作卸载到比 `read(2)` 和 `write(2)` 更有效的系统调用中。
//!
//! 专业化仅适用于 std 完全拥有的类型,因此用户代码无法观察到未使用 `Read` 和 `Write` traits。
//!
//! 由于复制操作涉及 reader 和 writer 端,其中每个端可以由不同类型组成,并且还涉及泛型包装器 (例如 `Take`,`BufReader`),因此在所有可能的组合上专门使用单个方法是不切实际的。
//!
//! 取而代之的是,由 `CopyRead` 和 `CopyWrite` 专业化 traits 分别处理 readers 和 writers,然后由 `Copier::copy` 方法进行专门化。
//!
//! `Copier` 使用专门化 traits 来解开包装的底层文件描述符,以及包装器类型强加的附加先决条件和约束。
//!
//! 一旦获得了所有必需的片段并将所有包装器类型置于可以安全绕过它们的状态,它将尝试使用 `copy_file_range(2)`,`sendfile(2)` 或 `splice(2)` 系统调用在文件描述符之间直接移动数据。
//!
//! 由于这些系统调用具有无法提前完全检查的要求,因此它会尝试一个接一个地使用它们 (在提示的指导下) 以确定哪个有效,如果没有一个有效,则回退到泛型读写复制循环。
//! 一旦找到了一对文件描述符的有效系统调用,它将在循环中被调用,直到复制操作完成为止。
//!
//! 使用这些系统调用的优点:
//!
//! * 更少的上下文切换,因为将读和写合并为一个系统调用,并且每个系统调用传输更多的字节。
//! 至少对于足够大的传输以摊销初始探测,这意味着更高的吞吐量和更少的 CPU 周期。
//! * `copy_file_range` 在 CoW 文件系统上创建 reflink 副本,从而移动更少的数据并消耗更少的磁盘空间
//! * `sendfile` 和 `splice` 在某些情况下可以执行零拷贝输入输出,而简单的拷贝循环会通过 CPU 移动每个字节。
//!
//! Drawbacks:
//!
//! * 在某些情况下 (特别是在较早的内核上),小于默认缓冲区大小的复制操作可能会比幼稚的方法引起更多的系统调用。
//! 如上所述,系统调用选择受提示的指导,以最大程度地减少这种可能性,但它们并不完美。
//! * 优化仅适用于 std 类型。如果用户添加自定义包装器类型 (例如,以报告进度),则他们可能会遇到性能问题。
//! * complexity
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!

use crate::cmp::min;
use crate::fs::{File, Metadata};
use crate::io::copy::generic_copy;
use crate::io::{
    BufRead, BufReader, BufWriter, Error, Read, Result, StderrLock, StdinLock, StdoutLock, Take,
    Write,
};
use crate::mem::ManuallyDrop;
use crate::net::TcpStream;
use crate::os::unix::fs::FileTypeExt;
use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use crate::os::unix::net::UnixStream;
use crate::process::{ChildStderr, ChildStdin, ChildStdout};
use crate::ptr;
use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use crate::sys::cvt;
use crate::sys::weak::syscall;
#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
use libc::sendfile as sendfile64;
#[cfg(all(target_os = "linux", target_env = "gnu"))]
use libc::sendfile64;
use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV};

#[cfg(test)]
mod tests;

pub(crate) fn copy_spec<R: Read + ?Sized, W: Write + ?Sized>(
    read: &mut R,
    write: &mut W,
) -> Result<u64> {
    let copier = Copier { read, write };
    SpecCopy::copy(copier)
}

/// 此类型表示根据提取来源的类型推断的 `RawFd` 的 `FileType` 或实际的元数据
///
///
/// 此类型的方法仅提供提示,由于 `AsRawFd` 和 `FromRawFd` 推断的类型可能是错误的。
///
enum FdMeta {
    Metadata(Metadata),
    Socket,
    Pipe,
    /// 我们没有任何元数据,因为统计 syscall 失败
    NoneObtained,
}

impl FdMeta {
    fn maybe_fifo(&self) -> bool {
        match self {
            FdMeta::Metadata(meta) => meta.file_type().is_fifo(),
            FdMeta::Socket => false,
            FdMeta::Pipe => true,
            FdMeta::NoneObtained => true,
        }
    }

    fn potential_sendfile_source(&self) -> bool {
        match self {
            // procfs 错误地在非空可读文件上显示 0 长度。
            // 如果文件确实为空,则 `read` syscall 将确定该文件并跳过写 syscall,因此尝试发送 sendfile 将有好处
            //
            FdMeta::Metadata(meta)
                if meta.file_type().is_file() && meta.len() > 0
                    || meta.file_type().is_block_device() =>
            {
                true
            }
            _ => false,
        }
    }

    fn copy_file_range_candidate(&self) -> bool {
        match self {
            // 对于空的 procfs 文件,copy_file_range 将失败。
            // `read` 可以确定是否已经达到 EOF 而不需要额外的成本,并跳过写入,因此尝试 copy_file_range 没有任何好处
            FdMeta::Metadata(meta) if meta.is_file() && meta.len() > 0 => true,
            FdMeta::NoneObtained => true,
            _ => false,
        }
    }
}

/// 如果在 sendfile/splice 调整后对源所做的更改在 sink 中不可见或源已明确选择这种行为 (例如
/// 通过将文件拼接到管道中,管道在这种情况下是源)。
///
/// 这将阻止文件 -> 管道和文件 -> 套接字 splicing/sendfile 优化以维护 io::copy 的 Read/Write API 语义。
///
/// Note: 这不是 100% 的无懈可击,调用者可以使用 RawFd 的转换方法将一个普通的文件变成一个 TcpSocket,这里会被当做一个套接字而不用检查。
///
///
///
fn safe_kernel_copy(source: &FdMeta, sink: &FdMeta) -> bool {
    match (source, sink) {
        // 来自套接字的数据是安全的,因为发送方无法修改套接字缓冲区。
        // 来自管道的数据是 safe(-ish),因为发送方将字节*复制*到管道中,或者显式执行启用零复制的操作,从而承诺以后不会修改数据。
        //
        //
        (FdMeta::Socket, _) => true,
        (FdMeta::Pipe, _) => true,
        (FdMeta::Metadata(meta), _)
            if meta.file_type().is_fifo() || meta.file_type().is_socket() =>
        {
            true
        }
        // 进入 non-pipes/non-sockets 的数据是安全的,因为 "later changes may become visible" 问题只发生在发送缓冲区或管道中的页面。
        //
        (_, FdMeta::Metadata(meta))
            if !meta.file_type().is_fifo() && !meta.file_type().is_socket() =>
        {
            true
        }
        _ => false,
    }
}

struct CopyParams(FdMeta, Option<RawFd>);

struct Copier<'a, 'b, R: Read + ?Sized, W: Write + ?Sized> {
    read: &'a mut R,
    write: &'b mut W,
}

trait SpecCopy {
    fn copy(self) -> Result<u64>;
}

impl<R: Read + ?Sized, W: Write + ?Sized> SpecCopy for Copier<'_, '_, R, W> {
    default fn copy(self) -> Result<u64> {
        generic_copy(self.read, self.write)
    }
}

impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
    fn copy(self) -> Result<u64> {
        let (reader, writer) = (self.read, self.write);
        let r_cfg = reader.properties();
        let w_cfg = writer.properties();

        // 在对文件描述符进行直接操作之前,请确保所有源缓冲区和 sink 缓冲区为空
        let mut flush = || -> crate::io::Result<u64> {
            let bytes = reader.drain_to(writer, u64::MAX)?;
            // 在较早的 write() 调用中已经考虑了 BufWriter 缓冲的字节
            writer.flush()?;
            Ok(bytes)
        };

        let mut written = 0u64;

        if let (CopyParams(input_meta, Some(readfd)), CopyParams(output_meta, Some(writefd))) =
            (r_cfg, w_cfg)
        {
            written += flush()?;
            let max_write = reader.min_limit();

            if input_meta.copy_file_range_candidate() && output_meta.copy_file_range_candidate() {
                let result = copy_regular_files(readfd, writefd, max_write);
                result.update_take(reader);

                match result {
                    CopyResult::Ended(bytes_copied) => return Ok(bytes_copied + written),
                    CopyResult::Error(e, _) => return Err(e),
                    CopyResult::Fallback(bytes) => written += bytes,
                }
            }

            // 在现代内核上,sendfile 可以从任何可映射类型 (某些但不是全部常规文件和块设备) 复制到任何可写文件描述符。
            // 在较早的内核上,writer 端只能是一个套接字。
            // 因此,我们只是在需要时尝试回退。
            // 如果当前文件偏移量 + 写入大小溢出,它也可能会失败,我们不会尝试解决此问题,而是会退回到泛型复制循环。
            //
            if input_meta.potential_sendfile_source() && safe_kernel_copy(&input_meta, &output_meta)
            {
                let result = sendfile_splice(SpliceMode::Sendfile, readfd, writefd, max_write);
                result.update_take(reader);

                match result {
                    CopyResult::Ended(bytes_copied) => return Ok(bytes_copied + written),
                    CopyResult::Error(e, _) => return Err(e),
                    CopyResult::Fallback(bytes) => written += bytes,
                }
            }

            if (input_meta.maybe_fifo() || output_meta.maybe_fifo())
                && safe_kernel_copy(&input_meta, &output_meta)
            {
                let result = sendfile_splice(SpliceMode::Splice, readfd, writefd, max_write);
                result.update_take(reader);

                match result {
                    CopyResult::Ended(bytes_copied) => return Ok(bytes_copied + written),
                    CopyResult::Error(e, _) => return Err(e),
                    CopyResult::Fallback(0) => { /* use the fallback below */ }
                    CopyResult::Fallback(_) => {
                        unreachable!("splice should not return > 0 bytes on the fallback path")
                    }
                }
            }
        }

        // 如果没有更专业的 syscall 想要使用这些文件描述符,则回退
        match generic_copy(reader, writer) {
            Ok(bytes) => Ok(bytes + written),
            err => err,
        }
    }
}

#[rustc_specialization_trait]
trait CopyRead: Read {
    /// 包含缓冲区的实现 (即
    /// `BufReader`) 必须将数据从其内部缓冲区传输到 `writer` 中,直到清空缓冲区或传输 `limit` 字节为止 (以较早发生者为准)。
    ///
    /// 如果存在嵌套缓冲区,则必须先清空外部缓冲区。
    ///
    /// 当直接对底层文件描述符进行操作时,在保留数据顺序的同时直接绕过包装器类型是必要的。
    ///
    fn drain_to<W: Write>(&mut self, _writer: &mut W, _limit: u64) -> Result<u64> {
        Ok(0)
    }

    /// 更新 `Take` 包装器以删除复制的字节数。
    fn taken(&mut self, _bytes: u64) {}

    /// 所有 `Take<_>` 包装器的最小限制,否则为 `u64::MAX`。
    /// 此方法不考虑数据 `BufReader` 缓冲区,并且会低估 `Take<BufReader<Take<_>>>` 类型的限制。
    /// 因此,其结果仅在通过 `drain_to` 对缓冲区进行 draining 之后才有效。
    ///
    fn min_limit(&self) -> u64 {
        u64::MAX
    }

    /// 提取文件描述符和 hints/metadata,必要时通过包装委派。
    fn properties(&self) -> CopyParams;
}

#[rustc_specialization_trait]
trait CopyWrite: Write {
    /// 提取文件描述符和 hints/metadata,必要时通过包装委派。
    fn properties(&self) -> CopyParams;
}

impl<T> CopyRead for &mut T
where
    T: CopyRead,
{
    fn drain_to<W: Write>(&mut self, writer: &mut W, limit: u64) -> Result<u64> {
        (**self).drain_to(writer, limit)
    }

    fn taken(&mut self, bytes: u64) {
        (**self).taken(bytes);
    }

    fn min_limit(&self) -> u64 {
        (**self).min_limit()
    }

    fn properties(&self) -> CopyParams {
        (**self).properties()
    }
}

impl<T> CopyWrite for &mut T
where
    T: CopyWrite,
{
    fn properties(&self) -> CopyParams {
        (**self).properties()
    }
}

impl CopyRead for File {
    fn properties(&self) -> CopyParams {
        CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
    }
}

impl CopyRead for &File {
    fn properties(&self) -> CopyParams {
        CopyParams(fd_to_meta(*self), Some(self.as_raw_fd()))
    }
}

impl CopyWrite for File {
    fn properties(&self) -> CopyParams {
        CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
    }
}

impl CopyWrite for &File {
    fn properties(&self) -> CopyParams {
        CopyParams(fd_to_meta(*self), Some(self.as_raw_fd()))
    }
}

impl CopyRead for TcpStream {
    fn properties(&self) -> CopyParams {
        // 避免 stat syscall,因为我们可以确定它是一个套接字
        CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
    }
}

impl CopyRead for &TcpStream {
    fn properties(&self) -> CopyParams {
        // 避免 stat syscall,因为我们可以确定它是一个套接字
        CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
    }
}

impl CopyWrite for TcpStream {
    fn properties(&self) -> CopyParams {
        // 避免 stat syscall,因为我们可以确定它是一个套接字
        CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
    }
}

impl CopyWrite for &TcpStream {
    fn properties(&self) -> CopyParams {
        // 避免 stat syscall,因为我们可以确定它是一个套接字
        CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
    }
}

impl CopyRead for UnixStream {
    fn properties(&self) -> CopyParams {
        // 避免 stat syscall,因为我们可以确定它是一个套接字
        CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
    }
}

impl CopyRead for &UnixStream {
    fn properties(&self) -> CopyParams {
        // 避免 stat syscall,因为我们可以确定它是一个套接字
        CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
    }
}

impl CopyWrite for UnixStream {
    fn properties(&self) -> CopyParams {
        // 避免 stat syscall,因为我们可以确定它是一个套接字
        CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
    }
}

impl CopyWrite for &UnixStream {
    fn properties(&self) -> CopyParams {
        // 避免 stat syscall,因为我们可以确定它是一个套接字
        CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
    }
}

impl CopyWrite for ChildStdin {
    fn properties(&self) -> CopyParams {
        CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
    }
}

impl CopyRead for ChildStdout {
    fn properties(&self) -> CopyParams {
        CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
    }
}

impl CopyRead for ChildStderr {
    fn properties(&self) -> CopyParams {
        CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
    }
}

impl CopyRead for StdinLock<'_> {
    fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
        let buf_reader = self.as_mut_buf();
        let buf = buf_reader.buffer();
        let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))];
        let bytes_drained = buf.len();
        writer.write_all(buf)?;
        buf_reader.consume(bytes_drained);

        Ok(bytes_drained as u64)
    }

    fn properties(&self) -> CopyParams {
        CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
    }
}

impl CopyWrite for StdoutLock<'_> {
    fn properties(&self) -> CopyParams {
        CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
    }
}

impl CopyWrite for StderrLock<'_> {
    fn properties(&self) -> CopyParams {
        CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
    }
}

impl<T: CopyRead> CopyRead for Take<T> {
    fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
        let local_limit = self.limit();
        let combined_limit = min(outer_limit, local_limit);
        let bytes_drained = self.get_mut().drain_to(writer, combined_limit)?;
        // 由于 read() 被绕过而导致的更新限制
        self.set_limit(local_limit - bytes_drained);

        Ok(bytes_drained)
    }

    fn taken(&mut self, bytes: u64) {
        self.set_limit(self.limit() - bytes);
        self.get_mut().taken(bytes);
    }

    fn min_limit(&self) -> u64 {
        min(Take::limit(self), self.get_ref().min_limit())
    }

    fn properties(&self) -> CopyParams {
        self.get_ref().properties()
    }
}

impl<T: CopyRead> CopyRead for BufReader<T> {
    fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
        let buf = self.buffer();
        let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))];
        let bytes = buf.len();
        writer.write_all(buf)?;
        self.consume(bytes);

        let remaining = outer_limit - bytes as u64;

        // 如果是嵌套的 bufreader,我们还需要 drain 靠近源
        let inner_bytes = self.get_mut().drain_to(writer, remaining)?;

        Ok(bytes as u64 + inner_bytes)
    }

    fn taken(&mut self, bytes: u64) {
        self.get_mut().taken(bytes);
    }

    fn min_limit(&self) -> u64 {
        self.get_ref().min_limit()
    }

    fn properties(&self) -> CopyParams {
        self.get_ref().properties()
    }
}

impl<T: CopyWrite> CopyWrite for BufWriter<T> {
    fn properties(&self) -> CopyParams {
        self.get_ref().properties()
    }
}

fn fd_to_meta<T: AsRawFd>(fd: &T) -> FdMeta {
    let fd = fd.as_raw_fd();
    let file: ManuallyDrop<File> = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) });
    match file.metadata() {
        Ok(meta) => FdMeta::Metadata(meta),
        Err(_) => FdMeta::NoneObtained,
    }
}

pub(super) enum CopyResult {
    Ended(u64),
    Error(Error, u64),
    Fallback(u64),
}

impl CopyResult {
    fn update_take(&self, reader: &mut impl CopyRead) {
        match *self {
            CopyResult::Fallback(bytes)
            | CopyResult::Ended(bytes)
            | CopyResult::Error(_, bytes) => reader.taken(bytes),
        }
    }
}

/// 无效的文件描述符。
///
/// 有效的文件描述符保证为正数 (请参见 `open()` 联机帮助页),而负值用于指示错误。
///
/// 因此,-1 永远不会与有效的打开文件重叠。
const INVALID_FD: RawFd = -1;

/// 特定于 Linux 的实现,将尝试使用 copy_file_range 进行副本分载。
/// 顾名思义,它仅适用于常规文件。
///
/// 调用者必须处理回退到泛型复制循环的操作。
/// 如果文件的游标 +`max_len` 之一将超过 u64::MAX (`EOVERFLOW`),则 `Fallback` 可能表示已写入的字节数为非零。
///
pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> CopyResult {
    use crate::cmp;

    const NOT_PROBED: u8 = 0;
    const UNAVAILABLE: u8 = 1;
    const AVAILABLE: u8 = 2;

    // 4.5 之前的内核没有 copy_file_range 我们将可用性存储在一个以避免不必要的 syscalls
    //
    static HAS_COPY_FILE_RANGE: AtomicU8 = AtomicU8::new(NOT_PROBED);

    syscall! {
        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
    }

    match HAS_COPY_FILE_RANGE.load(Ordering::Relaxed) {
        NOT_PROBED => {
            // EPERM 可以指示 seccomp 过滤器或不可变文件。
            // 为了区分这些情况,我们使用无效的文件描述符进行探测,如果系统调用受支持,则文件描述符将导致 EBADF; 如果不可用,则将导致其他错误 (ENOSYS 或 EPERM)
            //
            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);
                return CopyResult::Fallback(0);
            }
        }
        UNAVAILABLE => return CopyResult::Fallback(0),
        _ => {}
    };

    let mut written = 0u64;
    while written < max_len {
        let bytes_to_copy = cmp::min(max_len - written, usize::MAX as u64);
        // 如果将 u64::MAX 作为 max_len 传递,并且文件具有非零查找位置,则上限为 1GB 块,这使我们能够复制大块而不会达到 EOVERFLOW,除非有人将文件偏移量设置为接近 u64::MAX-1GB,在这种情况下回退需要
        //
        //
        let bytes_to_copy = cmp::min(bytes_to_copy as usize, 0x4000_0000usize);
        let copy_result = unsafe {
            // 实际上,我们不必调整偏移量,因为 copy_file_range 会自动调整文件偏移量
            //
            cvt(copy_file_range(reader, ptr::null_mut(), writer, ptr::null_mut(), bytes_to_copy, 0))
        };

        match copy_result {
            Ok(0) if written == 0 => {
                // 回退以解决几个内核错误,其中 copy_file_range 将无法复制任何字节并返回 0 而不是错误,如果
                //
                // - 从 proc 文件系统中读取虚拟文件,这些虚拟文件的大小似乎为 0,但不为空。
                // 在 coreutils 中指出,至少会影响 5.6.19 的内核。
                // - 从 docker 中的覆盖文件系统复制。据报道发生在 fedora 32 上。
                return CopyResult::Fallback(0);
            }
            Ok(0) => return CopyResult::Ended(written), // 达到了 EOF
            Ok(ret) => written += ret as u64,
            Err(err) => {
                return match err.raw_os_error() {
                    // 当文件 `offset + max_length > u64::MAX` 时
                    Some(EOVERFLOW) => CopyResult::Fallback(written),
                    Some(ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF) if written == 0 => {
                        // 如果出现以下情况,请尝试使用备用 io::copy:
                        // - 内核版本 < 4.5 (ENOSYS¹)
                        // - 文件安装在不同的 fs (EXDEV) 上
                        // - RHEL/CentOS 7 (EOPNOTSUPP) 上的 copy_file_range 被各种方式破坏
                        // - seccomp¹ (EPERM) 的 copy_file_range 文件不可变或 syscall 被阻止
                        // - copy_file_range 不能与管道或设备节点 (EINVAL) 一起使用
                        // - writer fd 是用 O_APPEND (EBADF²) 打开的,但尚未成功写入任何字节。(如果已经写入了某些内容,则不应返回所有这些 errnos,但它们会在野外发生,请参见 #91152。)
                        //
                        // ¹ 这些情况应该由初始探针检测到,但是无论如何我们还是在这里处理它们,以防在运行时发生系统调用拦截变化 ² 实际上无效的文件描述符也会导致这种情况,但是在这种情况下,后备代码路径有望再次遇到相同的错误
                        //
                        //
                        //
                        //
                        //
                        //
                        CopyResult::Fallback(0)
                    }
                    _ => CopyResult::Error(err, written),
                };
            }
        }
    }
    CopyResult::Ended(written)
}

#[derive(PartialEq)]
enum SpliceMode {
    Sendfile,
    Splice,
}

/// 在文件描述符之间执行拼接或 sendfile _not_ 是否回退到泛型复制循环。
///
fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) -> CopyResult {
    static HAS_SENDFILE: AtomicBool = AtomicBool::new(true);
    static HAS_SPLICE: AtomicBool = AtomicBool::new(true);

    // Android 构建使用特性级别 14,但用于拼接的 libc 包装器在特性级别 21+ 上进行设限,因此我们必须直接调用 syscall。
    //
    #[cfg(target_os = "android")]
    syscall! {
        fn splice(
            srcfd: libc::c_int,
            src_offset: *const i64,
            dstfd: libc::c_int,
            dst_offset: *const i64,
            len: libc::size_t,
            flags: libc::c_int
        ) -> libc::ssize_t
    }

    #[cfg(target_os = "linux")]
    use libc::splice;

    match mode {
        SpliceMode::Sendfile if !HAS_SENDFILE.load(Ordering::Relaxed) => {
            return CopyResult::Fallback(0);
        }
        SpliceMode::Splice if !HAS_SPLICE.load(Ordering::Relaxed) => {
            return CopyResult::Fallback(0);
        }
        _ => (),
    }

    let mut written = 0u64;
    while written < len {
        // 根据其手册,这是每次调用 sendfile() 将复制的最大大小
        let chunk_size = crate::cmp::min(len - written, 0x7ffff000_u64) as usize;

        let result = match mode {
            SpliceMode::Sendfile => {
                cvt(unsafe { sendfile64(writer, reader, ptr::null_mut(), chunk_size) })
            }
            SpliceMode::Splice => cvt(unsafe {
                splice(reader, ptr::null_mut(), writer, ptr::null_mut(), chunk_size, 0)
            }),
        };

        match result {
            Ok(0) => break, // EOF
            Ok(ret) => written += ret as u64,
            Err(err) => {
                return match err.raw_os_error() {
                    Some(ENOSYS | EPERM) => {
                        // 不支持 syscall (ENOSYS) 不允许进行 syscall,例如
                        // 通过 seccomp (EPERM)
                        match mode {
                            SpliceMode::Sendfile => HAS_SENDFILE.store(false, Ordering::Relaxed),
                            SpliceMode::Splice => HAS_SPLICE.store(false, Ordering::Relaxed),
                        }
                        assert_eq!(written, 0);
                        CopyResult::Fallback(0)
                    }
                    Some(EINVAL) => {
                        // splice/sendfile 不支持这个特定的文件描述符 (EINVAL)
                        assert_eq!(written, 0);
                        CopyResult::Fallback(0)
                    }
                    Some(os_err) if mode == SpliceMode::Sendfile && os_err == EOVERFLOW => {
                        CopyResult::Fallback(written)
                    }
                    _ => CopyResult::Error(err, written),
                };
            }
        }
    }
    CopyResult::Ended(written)
}