nuclei/fs/copy_range.rs
1//! Copying a range of one file into another.
2
3use std::fs::File;
4use std::io;
5
6use super::file::{COPY_CHUNK, range_end};
7use super::ops::blocking;
8use crate::io::Handle;
9use crate::io::sys;
10
11impl Handle<File> {
12 /// Copies up to `len` bytes from this file at `from_offset` to `to` at
13 /// `to_offset` and answers how many were copied: fewer only where this
14 /// file ends, and zero at its end. Neither file's cursor is used or
15 /// moved.
16 ///
17 /// On Linux the bytes move in the kernel (`copy_file_range(2)`), in jobs
18 /// of the blocking pool that each take a chunk of at most 16 MiB, so no
19 /// job holds a pool thread for a whole copy. Where the kernel cannot copy
20 /// between the two files, or the system has no such call (macOS,
21 /// Windows, the other unix), the bytes go through a buffer of the
22 /// runtime's pool, one buffer a job.
23 ///
24 /// The copy waits for the writes accepted before it on both files. For
25 /// `to` it is a write: a read at its cursor made after the copy sees the
26 /// bytes, and a future dropped while a chunk is out leaves that chunk to
27 /// `to`, which every later call of `to` waits for; the chunks after it
28 /// are not made. A failure after some bytes were copied is not reported:
29 /// the count is, and the next call meets the failure again.
30 ///
31 /// A file that is not a regular file (a pipe, a terminal) gets the
32 /// system's answer to one call.
33 ///
34 /// # Errors
35 ///
36 /// `InvalidInput` when the end of either range is past the largest offset
37 /// a file can have, or when both handles name one file and the ranges
38 /// overlap; otherwise the system's own error, among them that of a handle
39 /// that cannot be read or written.
40 pub async fn copy_range_to(
41 &self,
42 to: &Handle<File>,
43 from_offset: u64,
44 to_offset: u64,
45 len: u64,
46 ) -> io::Result<u64> {
47 if !self.is_regular() || !to.is_regular() {
48 range_end(from_offset, len)?;
49 range_end(to_offset, len)?;
50 let (src, dst) = (self.source().lease()?, to.source().lease()?);
51 let want = usize::try_from(len.min(COPY_CHUNK)).unwrap_or(usize::MAX);
52 return blocking(move || {
53 sys::copy_file_range(src.raw(), from_offset, dst.raw(), to_offset, want)
54 .map(|n| n as u64)
55 })
56 .await;
57 }
58 self.file()?
59 .copy_range_to(to.file()?, from_offset, to_offset, len)
60 .await
61 }
62}