1use super::*;
6use crate::io_buffers::IoBuffer;
7
8pub(super) struct PreparedCluster {
10 pub host_cluster: HostCluster,
12
13 pub set_mapping: Option<(usize, Option<PendingClusters>)>,
18}
19
20#[maybe_async]
21impl<S: Storage, F: WrappedFormat<S>> Qcow2<S, F> {
22 pub(super) async fn prepare_cow_cluster(
53 &self,
54 cluster: GuestCluster,
55 mandatory_host_cluster: Option<HostCluster>,
56 partial_skip_cow: Option<Range<usize>>,
57 l2_table: &L2TableWriteGuard,
58 ) -> io::Result<Option<PreparedCluster>> {
59 let full_skip_cow = if let Some(skip) = partial_skip_cow.as_ref() {
61 skip.start == 0 && skip.end == self.header.cluster_size()
62 } else {
63 false
64 };
65
66 let existing_mapping = l2_table.get_mapping(cluster)?;
67 if let L2Mapping::DataFile {
68 host_cluster,
69 copied: true,
70 } = existing_mapping
71 {
72 if let Some(mandatory_host_cluster) = mandatory_host_cluster {
73 if host_cluster != mandatory_host_cluster {
74 return Ok(None);
75 }
76 }
77 return Ok(Some(PreparedCluster {
78 host_cluster,
79 set_mapping: None,
80 }));
81 };
82
83 self.need_writable()?;
84
85 let new_cluster: Option<(HostCluster, Option<PendingClusters>)> = if let L2Mapping::Zero {
87 host_cluster: Some(host_cluster),
88 copied: true,
89 } = existing_mapping
90 {
91 if let Some(mandatory_host_cluster) = mandatory_host_cluster {
92 if host_cluster == mandatory_host_cluster {
93 Some((host_cluster, None))
94 } else {
95 self.allocate_data_cluster_at(cluster, Some(mandatory_host_cluster))
97 .await?
98 .map(|allocation| (allocation.cluster(), Some(allocation)))
99 }
100 } else {
101 Some((host_cluster, None))
102 }
103 } else {
104 self.allocate_data_cluster_at(cluster, mandatory_host_cluster)
105 .await?
106 .map(|allocation| (allocation.cluster(), Some(allocation)))
107 };
108
109 let Some((new_cluster, new_allocation)) = new_cluster else {
110 return Ok(None);
112 };
113
114 if !full_skip_cow {
115 match existing_mapping {
116 L2Mapping::DataFile {
117 host_cluster: _,
118 copied: true,
119 } => unreachable!(),
120
121 L2Mapping::DataFile {
122 host_cluster,
123 copied: false,
124 } => {
125 self.cow_copy_storage(
126 self.storage(),
127 host_cluster,
128 new_cluster,
129 partial_skip_cow,
130 )
131 .await?
132 }
133
134 L2Mapping::Backing { backing_offset } => {
135 if let Some(backing) = self.backing.as_ref() {
136 self.cow_copy_format(backing, backing_offset, new_cluster, partial_skip_cow)
137 .await?
138 } else {
139 self.cow_zero(new_cluster, partial_skip_cow).await?
140 }
141 }
142
143 L2Mapping::Zero {
144 host_cluster: _,
145 copied: _,
146 } => self.cow_zero(new_cluster, partial_skip_cow).await?,
147
148 L2Mapping::Compressed {
149 host_offset: _,
150 length: _,
151 } => self.cow_compressed(l2_table, cluster, new_cluster).await?,
152 }
153 }
154
155 let l2i = cluster.l2_index(self.header.cluster_bits());
156 Ok(Some(PreparedCluster {
157 host_cluster: new_cluster,
158 set_mapping: Some((l2i, new_allocation)),
159 }))
160 }
161
162 fn get_cow_range(
170 &self,
171 partial_skip_cow: Option<Range<usize>>,
172 alignment: usize,
173 ) -> Option<Range<usize>> {
174 let mut copy_range = 0..self.header.cluster_size();
175 if let Some(partial_skip_cow) = partial_skip_cow {
176 if partial_skip_cow.start == copy_range.start {
177 copy_range.start = partial_skip_cow.end;
178 } else if partial_skip_cow.end == copy_range.end {
179 copy_range.end = partial_skip_cow.start;
180 }
181 }
182
183 if copy_range.is_empty() {
184 return None;
185 }
186
187 let alignment = cmp::min(alignment, self.header.cluster_size());
188 debug_assert!(alignment.is_power_of_two());
189 let mask = alignment - 1;
190
191 if copy_range.start & mask != 0 {
192 copy_range.start &= !mask;
193 }
194 if copy_range.end & mask != 0 {
195 copy_range.end = (copy_range.end & !mask) + alignment;
196 }
197
198 Some(copy_range)
199 }
200
201 async fn cow_copy_storage(
205 &self,
206 from: &S,
207 from_cluster: HostCluster,
208 to_cluster: HostCluster,
209 partial_skip_cow: Option<Range<usize>>,
210 ) -> io::Result<()> {
211 let to = self.storage();
212
213 let align = cmp::max(from.req_align(), to.req_align());
214 let Some(cow_range) = self.get_cow_range(partial_skip_cow, align) else {
215 return Ok(());
216 };
217
218 let mut buf = IoBuffer::new(cow_range.end - cow_range.start, from.mem_align())?;
219
220 let cb = self.header.cluster_bits();
221 let from_offset = from_cluster.offset(cb);
222 let to_offset = to_cluster.offset(cb);
223
224 from.read(&mut buf, from_offset.0 + cow_range.start as u64)
225 .await?;
226
227 to.write(&buf, to_offset.0 + cow_range.start as u64).await?;
228
229 Ok(())
230 }
231
232 async fn cow_copy_format(
236 &self,
237 from: &F,
238 from_offset: u64,
239 to_cluster: HostCluster,
240 partial_skip_cow: Option<Range<usize>>,
241 ) -> io::Result<()> {
242 let to = self.storage();
243 let from = from.inner();
244
245 let align = cmp::max(from.req_align(), to.req_align());
246 let Some(cow_range) = self.get_cow_range(partial_skip_cow, align) else {
247 return Ok(());
248 };
249
250 let mut buf = IoBuffer::new(cow_range.end - cow_range.start, from.mem_align())?;
251
252 let to_offset = to_cluster.offset(self.header.cluster_bits());
253
254 from.read(&mut buf, from_offset + cow_range.start as u64)
255 .await?;
256
257 to.write(&buf, to_offset.0 + cow_range.start as u64).await?;
258
259 Ok(())
260 }
261
262 async fn cow_zero(
266 &self,
267 to_cluster: HostCluster,
268 partial_skip_cow: Option<Range<usize>>,
269 ) -> io::Result<()> {
270 let to = self.storage();
271
272 let align = to.req_align();
273 let Some(cow_range) = self.get_cow_range(partial_skip_cow, align) else {
274 return Ok(());
275 };
276
277 let to_offset = to_cluster.offset(self.header.cluster_bits());
278 to.write_zeroes(
279 to_offset.0 + cow_range.start as u64,
280 (cow_range.end - cow_range.start) as u64,
281 )
282 .await?;
283
284 Ok(())
285 }
286
287 async fn cow_compressed(
291 &self,
292 l2_table: &L2TableWriteGuard,
293 cluster: GuestCluster,
294 to_cluster: HostCluster,
295 ) -> io::Result<()> {
296 let to = self.storage();
297
298 let Some(compressed_guard) = l2_table.compressed_read_guard(cluster)? else {
299 unreachable!();
302 };
303
304 let mut buf = IoBuffer::new(self.header.cluster_size(), to.mem_align())?;
305 compressed_guard
306 .read_compressed(to, buf.as_mut().into_slice())
307 .await?;
308
309 let to_offset = to_cluster.offset(self.header.cluster_bits());
310 to.write(&buf, to_offset.0).await?;
311
312 Ok(())
313 }
314}