1use super::*;
6use crate::io_buffers::IoBuffer;
7
8#[maybe_async]
9impl<S: Storage, F: WrappedFormat<S>> Qcow2<S, F> {
10 pub(super) async fn cow_cluster(
40 &self,
41 cluster: GuestCluster,
42 mandatory_host_cluster: Option<HostCluster>,
43 partial_skip_cow: Option<Range<usize>>,
44 l2_table: &mut L2TableWriteGuard<'_>,
45 leaked_allocations: &mut Vec<(HostCluster, ClusterCount)>,
46 ) -> io::Result<Option<HostCluster>> {
47 let full_skip_cow = if let Some(skip) = partial_skip_cow.as_ref() {
49 skip.start == 0 && skip.end == self.header.cluster_size()
50 } else {
51 false
52 };
53
54 let existing_mapping = l2_table.get_mapping(cluster)?;
55 if let L2Mapping::DataFile {
56 host_cluster,
57 copied: true,
58 } = existing_mapping
59 {
60 if let Some(mandatory_host_cluster) = mandatory_host_cluster {
61 if host_cluster != mandatory_host_cluster {
62 return Ok(None);
63 }
64 }
65 return Ok(Some(host_cluster));
66 };
67
68 self.need_writable()?;
69
70 let new_cluster = if let L2Mapping::Zero {
71 host_cluster: Some(host_cluster),
72 copied: true,
73 } = existing_mapping
74 {
75 if let Some(mandatory_host_cluster) = mandatory_host_cluster {
76 if host_cluster == mandatory_host_cluster {
77 Some(host_cluster)
78 } else {
79 self.allocate_data_cluster_at(cluster, Some(mandatory_host_cluster))
81 .await?
82 }
83 } else {
84 Some(host_cluster)
85 }
86 } else {
87 self.allocate_data_cluster_at(cluster, mandatory_host_cluster)
88 .await?
89 };
90 let Some(new_cluster) = new_cluster else {
91 return Ok(None);
93 };
94
95 if !full_skip_cow {
96 match existing_mapping {
97 L2Mapping::DataFile {
98 host_cluster: _,
99 copied: true,
100 } => unreachable!(),
101
102 L2Mapping::DataFile {
103 host_cluster,
104 copied: false,
105 } => {
106 self.cow_copy_storage(
107 self.storage(),
108 host_cluster,
109 new_cluster,
110 partial_skip_cow,
111 )
112 .await?
113 }
114
115 L2Mapping::Backing { backing_offset } => {
116 if let Some(backing) = self.backing.as_ref() {
117 self.cow_copy_format(backing, backing_offset, new_cluster, partial_skip_cow)
118 .await?
119 } else {
120 self.cow_zero(new_cluster, partial_skip_cow).await?
121 }
122 }
123
124 L2Mapping::Zero {
125 host_cluster: _,
126 copied: _,
127 } => self.cow_zero(new_cluster, partial_skip_cow).await?,
128
129 L2Mapping::Compressed {
130 host_offset,
131 length,
132 } => {
133 self.cow_compressed(host_offset, length, new_cluster)
134 .await?
135 }
136 }
137 }
138
139 let l2i = cluster.l2_index(self.header.cluster_bits());
140 if let Some(leaked) = l2_table.map_cluster(l2i, new_cluster) {
141 leaked_allocations.push(leaked);
142 }
143
144 Ok(Some(new_cluster))
145 }
146
147 fn get_cow_range(
155 &self,
156 partial_skip_cow: Option<Range<usize>>,
157 alignment: usize,
158 ) -> Option<Range<usize>> {
159 let mut copy_range = 0..self.header.cluster_size();
160 if let Some(partial_skip_cow) = partial_skip_cow {
161 if partial_skip_cow.start == copy_range.start {
162 copy_range.start = partial_skip_cow.end;
163 } else if partial_skip_cow.end == copy_range.end {
164 copy_range.end = partial_skip_cow.start;
165 }
166 }
167
168 if copy_range.is_empty() {
169 return None;
170 }
171
172 let alignment = cmp::min(alignment, self.header.cluster_size());
173 debug_assert!(alignment.is_power_of_two());
174 let mask = alignment - 1;
175
176 if copy_range.start & mask != 0 {
177 copy_range.start &= !mask;
178 }
179 if copy_range.end & mask != 0 {
180 copy_range.end = (copy_range.end & !mask) + alignment;
181 }
182
183 Some(copy_range)
184 }
185
186 async fn cow_copy_storage(
190 &self,
191 from: &S,
192 from_cluster: HostCluster,
193 to_cluster: HostCluster,
194 partial_skip_cow: Option<Range<usize>>,
195 ) -> io::Result<()> {
196 let to = self.storage();
197
198 let align = cmp::max(from.req_align(), to.req_align());
199 let Some(cow_range) = self.get_cow_range(partial_skip_cow, align) else {
200 return Ok(());
201 };
202
203 let mut buf = IoBuffer::new(cow_range.end - cow_range.start, from.mem_align())?;
204
205 let cb = self.header.cluster_bits();
206 let from_offset = from_cluster.offset(cb);
207 let to_offset = to_cluster.offset(cb);
208
209 from.read(&mut buf, from_offset.0 + cow_range.start as u64)
210 .await?;
211
212 to.write(&buf, to_offset.0 + cow_range.start as u64).await?;
213
214 Ok(())
215 }
216
217 async fn cow_copy_format(
221 &self,
222 from: &F,
223 from_offset: u64,
224 to_cluster: HostCluster,
225 partial_skip_cow: Option<Range<usize>>,
226 ) -> io::Result<()> {
227 let to = self.storage();
228 let from = from.inner();
229
230 let align = cmp::max(from.req_align(), to.req_align());
231 let Some(cow_range) = self.get_cow_range(partial_skip_cow, align) else {
232 return Ok(());
233 };
234
235 let mut buf = IoBuffer::new(cow_range.end - cow_range.start, from.mem_align())?;
236
237 let to_offset = to_cluster.offset(self.header.cluster_bits());
238
239 from.read(&mut buf, from_offset + cow_range.start as u64)
240 .await?;
241
242 to.write(&buf, to_offset.0 + cow_range.start as u64).await?;
243
244 Ok(())
245 }
246
247 async fn cow_zero(
251 &self,
252 to_cluster: HostCluster,
253 partial_skip_cow: Option<Range<usize>>,
254 ) -> io::Result<()> {
255 let to = self.storage();
256
257 let align = to.req_align();
258 let Some(cow_range) = self.get_cow_range(partial_skip_cow, align) else {
259 return Ok(());
260 };
261
262 let to_offset = to_cluster.offset(self.header.cluster_bits());
263 to.write_zeroes(
264 to_offset.0 + cow_range.start as u64,
265 (cow_range.end - cow_range.start) as u64,
266 )
267 .await?;
268
269 Ok(())
270 }
271
272 async fn cow_compressed(
276 &self,
277 compressed_offset: HostOffset,
278 compressed_length: u64,
279 to_cluster: HostCluster,
280 ) -> io::Result<()> {
281 let to = self.storage();
282
283 let mut buf = IoBuffer::new(self.header.cluster_size(), to.mem_align())?;
284 self.read_compressed_cluster(
285 buf.as_mut().into_slice(),
286 compressed_offset,
287 compressed_length,
288 )
289 .await?;
290
291 let to_offset = to_cluster.offset(self.header.cluster_bits());
292 to.write(&buf, to_offset.0).await?;
293
294 Ok(())
295 }
296}