reflink_copy/
reflink_block.rs

1use crate::sys;
2use std::fs::File;
3use std::io;
4use std::num::NonZeroU64;
5
6/// Creates a reflink of a specified block from one file to another.
7///
8/// This functionality is designed to be highly performant and does not perform any extra API calls.
9/// It is expected that the user takes care of necessary preliminary checks and preparations.
10///
11/// If you need to clone an entire file, consider using the [`reflink`] or [`reflink_or_copy`]
12/// functions instead.
13///
14/// > Note: Currently the function works only for windows and linux platforms. It returns `Err` for
15///   any other platform.
16///
17/// # General restrictions
18///
19/// - The source and destination regions must begin and end at a cluster boundary.
20/// - If the source and destination regions are in the same file, they must not overlap. (The
21///   application may able to proceed by splitting up the block clone operation into multiple block
22///   clones that no longer overlap.)
23/// - `src_length` equal to 0 is not supported.
24///
25/// # Linux specific restrictions and remarks
26///
27/// - If the file size is not aligned to the cluster size, the reflink operation must not exceed
28///   the file length. For example, to reflink the whole file with size of 7000 bytes, `src_length`
29///   should be 7000 bytes.
30///
31/// More information about block cloning on Linux can be found by the
32/// [link](https://www.man7.org/linux/man-pages/man2/ioctl_ficlonerange.2.html).
33///
34/// # Windows specific restrictions and remarks
35///
36/// - The destination region must not extend past the end of file. If the application wishes to
37///   extend the destination with cloned data, it must first call
38///   [`File::set_len`](fn@std::fs::File::set_len).
39/// - The source and destination files must be on the same ReFS volume.
40/// - The source and destination files must have the same Integrity Streams setting (that is,
41///   Integrity Streams must be enabled in both files, or disabled in both files).
42/// - If the source file is sparse, the destination file must also be sparse.
43/// - The block clone operation will break Shared Opportunistic Locks (also known as Level 2
44///   Opportunistic Locks).
45/// - The ReFS volume must have been formatted with Windows Server 2016, and if Windows Failover
46///   Clustering is in use, the Clustering Functional Level must have been Windows Server 2016 or
47///   later at format time.
48/// - If the file size is not aligned to the cluster size, the reflink operation should still
49///   be aligned by the cluster size. For example, to reflink the whole file with size of 7000 bytes
50///   and a cluster size of 4096 bytes, `src_length` should be 8192 bytes.
51///
52/// > Note: In order to handle blocks larger than 4GB,
53///   [`ReflinkBlockBuilder::reflink_block`] splits these big blocks into smaller ones.
54///   Each smaller block is 4GB minus the cluster size. This means there might be more than one API
55///   call needed for the larger blocks.
56///
57/// More information about block cloning on Windows can be found by the
58/// [link](https://learn.microsoft.com/en-us/windows/win32/fileio/block-cloning).
59///
60/// # Examples
61///
62/// The example below demonstrates how to create a new file reusing blocks from another file.
63/// ```no_run
64/// use std::fs::File;
65/// use std::num::NonZeroU64;
66///
67/// fn shuffle() -> std::io::Result<()> {
68///     let from_file = File::open("source.bin")?;
69///     let to_file = File::create("destination.bin")?;
70///     let cluster_size = NonZeroU64::new(4096).unwrap();
71///     let len = cluster_size.get() * 2;
72///
73///     to_file.set_len(len)?;
74///
75///     reflink_copy::ReflinkBlockBuilder::new(&from_file, &to_file, cluster_size)
76///         .from_offset(0)
77///         .to_offset(cluster_size.get())
78///         .reflink_block()?;
79///
80///     reflink_copy::ReflinkBlockBuilder::new(&from_file, &to_file, cluster_size)
81///         .from_offset(cluster_size.get())
82///         .to_offset(0)
83///         .reflink_block()?;
84///
85///     Ok(())
86/// }
87/// ```
88/// [`reflink`]: crate::reflink
89/// [`reflink_or_copy`]: crate::reflink_or_copy
90#[derive(Debug)]
91pub struct ReflinkBlockBuilder<'from, 'to> {
92    from: &'from File,
93    from_offset: u64,
94    to: &'to File,
95    to_offset: u64,
96    src_length: u64,
97    cluster_size: Option<NonZeroU64>,
98}
99
100impl<'from, 'to> ReflinkBlockBuilder<'from, 'to> {
101    /// Creates a new instance of [`ReflinkBlockBuilder`].
102    pub fn new(from: &'from File, to: &'to File, src_length: NonZeroU64) -> Self {
103        Self {
104            from,
105            from_offset: 0,
106            to,
107            to_offset: 0,
108            src_length: src_length.get(),
109            cluster_size: None,
110        }
111    }
112
113    /// Sets the offset within the source file.
114    #[must_use]
115    pub fn from_offset(mut self, from_offset: u64) -> Self {
116        self.from_offset = from_offset;
117        self
118    }
119
120    /// Sets the offset within the destination file.
121    #[must_use]
122    pub fn to_offset(mut self, to_offset: u64) -> Self {
123        self.to_offset = to_offset;
124        self
125    }
126
127    /// Sets the cluster size. It is used to calculate the max block size of a single reflink call
128    /// on Windows.
129    #[must_use]
130    pub fn cluster_size(mut self, cluster_size: NonZeroU64) -> Self {
131        self.cluster_size = Some(cluster_size);
132        self
133    }
134
135    /// Performs reflink operation for the specified block of data.
136    pub fn reflink_block(self) -> io::Result<()> {
137        sys::reflink_block(
138            self.from,
139            self.from_offset,
140            self.to,
141            self.to_offset,
142            self.src_length,
143            self.cluster_size,
144        )
145    }
146}