Skip to main content

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}