1use super::*;
4use crate::sync_primitives::RwLockWriteGuard;
5
6#[maybe_async]
7impl<S: Storage, F: WrappedFormat<S>> Qcow2<S, F> {
8 pub(super) async fn do_get_mapping(
12 &self,
13 offset: GuestOffset,
14 max_length: u64,
15 ) -> io::Result<(ShallowMapping<'_, S>, u64)> {
16 let Some(l2_table) = self.get_l2(offset, false).await? else {
17 let cb = self.header.cluster_bits();
18 let len = cmp::min(offset.remaining_in_l2_table(cb), max_length);
19 let mapping = if let Some(backing) = self.backing.as_ref() {
20 ShallowMapping::Indirect {
21 layer: backing.inner(),
22 offset: offset.0,
23 writable: false,
24 }
25 } else {
26 ShallowMapping::Zero { explicit: false }
27 };
28 return Ok((mapping, len));
29 };
30
31 self.do_get_mapping_with_l2(offset, max_length, &l2_table)
32 .await
33 }
34
35 pub(super) async fn do_get_mapping_with_l2(
37 &self,
38 offset: GuestOffset,
39 max_length: u64,
40 l2_table: &L2Table,
41 ) -> io::Result<(ShallowMapping<'_, S>, u64)> {
42 let cb = self.header.cluster_bits();
43
44 let mut current_guest_cluster = offset.cluster(cb);
46 let first_mapping = l2_table.get_mapping(current_guest_cluster)?;
47 let return_mapping = match first_mapping {
48 L2Mapping::DataFile {
49 host_cluster,
50 copied,
51 } => ShallowMapping::Raw {
52 storage: self.storage(),
53 offset: host_cluster.relative_offset(offset, cb).0,
54 writable: copied,
55 },
56
57 L2Mapping::Backing { backing_offset } => {
58 if let Some(backing) = self.backing.as_ref() {
59 ShallowMapping::Indirect {
60 layer: backing.inner(),
61 offset: backing_offset + offset.in_cluster_offset(cb) as u64,
62 writable: false,
63 }
64 } else {
65 ShallowMapping::Zero { explicit: false }
66 }
67 }
68
69 L2Mapping::Zero {
70 host_cluster: _,
71 copied: _,
72 } => ShallowMapping::Zero { explicit: true },
73
74 L2Mapping::Compressed {
75 host_offset: _,
76 length: _,
77 } => ShallowMapping::Special { offset: offset.0 },
78 };
79
80 let mut consecutive_length = offset.remaining_in_cluster(cb);
82 let mut preceding_mapping = first_mapping;
83 while consecutive_length < max_length {
84 let Some(next) = current_guest_cluster.next_in_l2(cb) else {
85 break;
86 };
87 current_guest_cluster = next;
88
89 let mapping = l2_table.get_mapping(current_guest_cluster)?;
90 if !mapping.is_consecutive(&preceding_mapping, cb) {
91 break;
92 }
93
94 preceding_mapping = mapping;
95 consecutive_length += self.header.cluster_size() as u64;
96 }
97
98 consecutive_length = cmp::min(consecutive_length, max_length);
99 Ok((return_mapping, consecutive_length))
100 }
101
102 pub(super) async fn do_ensure_data_mapping(
111 &self,
112 offset: GuestOffset,
113 length: u64,
114 skip_cow: bool,
115 skip_cow_to_eof: bool,
116 ) -> io::Result<(&S, u64, u64)> {
117 let l2_table = self.ensure_l2(offset).await?;
118
119 let existing = self
126 .do_get_mapping_with_l2(offset, length, &l2_table)
127 .await?;
128 if let ShallowMapping::Raw {
129 storage,
130 offset,
131 writable: true,
132 } = existing.0
133 {
134 if existing.1 >= length {
135 return Ok((storage, offset, existing.1));
136 }
137 }
138
139 let l2_table = l2_table.lock_write().await;
140 let mut leaked_allocations = Vec::<(HostCluster, ClusterCount)>::new();
141
142 let res = self
143 .ensure_data_mapping_no_cleanup(
144 offset,
145 length,
146 skip_cow,
147 skip_cow_to_eof,
148 l2_table,
149 &mut leaked_allocations,
150 )
151 .await;
152
153 for alloc in leaked_allocations {
154 self.free_data_clusters(alloc.0, alloc.1).await;
155 }
156 let (host_offset, length) = res?;
157
158 Ok((self.storage(), host_offset, length))
159 }
160
161 pub(super) async fn ensure_fixed_mapping(
167 &self,
168 offset: GuestOffset,
169 length: u64,
170 mapping: FixedMapping,
171 ) -> io::Result<(GuestOffset, u64)> {
172 match mapping {
173 FixedMapping::ZeroDiscard | FixedMapping::ZeroRetainAllocation => {
174 self.header.require_version(3)?;
175 }
176 FixedMapping::FullDiscard => (),
177 }
178
179 let cb = self.header.cluster_bits();
180
181 let cluster_align_mask = self.header.cluster_size() as u64 - 1;
183 let end = (offset + length).0;
184 let aligned_end = if end == self.header.size() {
185 (end + cluster_align_mask) & !cluster_align_mask
188 } else {
189 end & !cluster_align_mask
191 };
192 let aligned_offset = (offset + cluster_align_mask).0 & !cluster_align_mask;
193 let aligned_length = aligned_end.saturating_sub(aligned_offset);
194
195 let first_cluster = GuestOffset(aligned_offset).checked_cluster(cb).unwrap();
197 let cluster_count = ClusterCount::checked_from_byte_size(aligned_length, cb).unwrap();
198
199 if cluster_count.0 == 0 {
200 return Ok((GuestOffset(aligned_offset), 0));
201 }
202
203 let l2_table = self.ensure_l2(first_cluster.offset(cb)).await?;
204 let l2_table = l2_table.lock_write().await;
205 let mut leaked_allocations = Vec::<(HostCluster, ClusterCount)>::new();
206
207 let res = self
208 .ensure_fixed_mapping_no_cleanup(
209 first_cluster,
210 cluster_count,
211 mapping,
212 l2_table,
213 &mut leaked_allocations,
214 )
215 .await;
216
217 for alloc in leaked_allocations {
218 self.free_data_clusters(alloc.0, alloc.1).await;
219 }
220
221 let count = res?;
222
223 let affected_offset = first_cluster.offset(cb);
224 let affected_length = count.byte_size(cb);
225
226 let head = affected_offset - offset;
227 let affected_length = cmp::min(affected_length, length.saturating_sub(head));
230
231 Ok((affected_offset, affected_length))
232 }
233
234 pub(super) async fn get_l2(
241 &self,
242 offset: GuestOffset,
243 writable: bool,
244 ) -> io::Result<Option<Arc<L2Table>>> {
245 let cb = self.header.cluster_bits();
246
247 let l1_entry = self.l1_table.read().await.get(offset.l1_index(cb));
248 if let Some(l2_offset) = l1_entry.l2_offset() {
249 if writable && !l1_entry.is_copied() {
250 return Ok(None);
251 }
252 let l2_cluster = l2_offset.checked_cluster(cb).ok_or_else(|| {
253 invalid_data(format!(
254 "Unaligned L2 table for {offset:?}; L1 entry: {l1_entry:?}"
255 ))
256 })?;
257
258 self.caches.l2_get_or_insert(l2_cluster).await.map(Some)
259 } else {
260 Ok(None)
261 }
262 }
263
264 pub(super) async fn ensure_l2(&self, offset: GuestOffset) -> io::Result<Arc<L2Table>> {
269 let cb = self.header.cluster_bits();
270
271 if let Some(l2) = self.get_l2(offset, true).await? {
272 return Ok(l2);
273 }
274
275 self.need_writable()?;
276
277 let mut l1_locked = self.l1_table.write().await;
278 let l1_index = offset.l1_index(cb);
279 if !l1_locked.in_bounds(l1_index) {
280 l1_locked = self.grow_l1_table(l1_locked, l1_index).await?;
281 }
282
283 let l1_entry = l1_locked.get(l1_index);
284 let mut l2_table = if let Some(l2_offset) = l1_entry.l2_offset() {
285 let l2_cluster = l2_offset.checked_cluster(cb).ok_or_else(|| {
286 invalid_data(format!(
287 "Unaligned L2 table for {offset:?}; L1 entry: {l1_entry:?}"
288 ))
289 })?;
290
291 let l2 = self.caches.l2_get_or_insert(l2_cluster).await?;
292 if l1_entry.is_copied() {
293 return Ok(l2);
294 }
295
296 L2Table::clone(&l2)
297 } else {
298 L2Table::new_cleared(&self.header)
299 };
300
301 let l2_cluster = self.allocate_meta_cluster().await?;
302 l2_table.set_cluster(l2_cluster);
303 l2_table.write(self.metadata.as_ref()).await?;
304
305 l1_locked.enter_l2_table(l1_index, &l2_table)?;
306 l1_locked
307 .write_entry(self.metadata.as_ref(), l1_index)
308 .await?;
309
310 if let Some(l2_offset) = l1_entry.l2_offset() {
312 self.free_meta_clusters(l2_offset.cluster(cb), ClusterCount(1))
313 .await;
314 }
315
316 let l2_table = Arc::new(l2_table);
317 self.caches
318 .l2_insert(l2_cluster, Arc::clone(&l2_table))
319 .await?;
320 Ok(l2_table)
321 }
322
323 pub(super) async fn grow_l1_table<'a>(
327 &self,
328 mut l1_locked: RwLockWriteGuard<'a, L1Table>,
329 at_least_index: usize,
330 ) -> io::Result<RwLockWriteGuard<'a, L1Table>> {
331 let mut new_l1 = l1_locked.clone_and_grow(at_least_index, &self.header)?;
332
333 let l1_start = self.allocate_meta_clusters(new_l1.cluster_count()).await?;
334
335 new_l1.set_cluster(l1_start);
336 new_l1.write(self.metadata.as_ref()).await?;
337
338 self.header.set_l1_table(&new_l1)?;
339 self.header
340 .write_l1_table_pointer(self.metadata.as_ref())
341 .await?;
342
343 if let Some(old_l1_cluster) = l1_locked.get_cluster() {
344 let old_l1_size = l1_locked.cluster_count();
345 l1_locked.unset_cluster();
346 self.free_meta_clusters(old_l1_cluster, old_l1_size).await;
347 }
348
349 *l1_locked = new_l1;
350
351 Ok(l1_locked)
352 }
353
354 async fn ensure_data_mapping_no_cleanup(
362 &self,
363 offset: GuestOffset,
364 full_length: u64,
365 skip_cow: bool,
366 skip_cow_to_eof: bool,
367 mut l2_table: L2TableWriteGuard<'_>,
368 leaked_allocations: &mut Vec<(HostCluster, ClusterCount)>,
369 ) -> io::Result<(u64, u64)> {
370 let cb = self.header.cluster_bits();
371
372 let partial_skip_cow = skip_cow.then(|| {
373 let start = offset.in_cluster_offset(cb);
374 let end = if skip_cow_to_eof {
375 1 << cb
376 } else {
377 cmp::min(start as u64 + full_length, 1 << cb) as usize
378 };
379 start..end
380 });
381
382 let mut current_guest_cluster = offset.cluster(cb);
383
384 let host_cluster = self
386 .cow_cluster(
387 current_guest_cluster,
388 None,
389 partial_skip_cow,
390 &mut l2_table,
391 leaked_allocations,
392 )
393 .await?
394 .ok_or_else(|| io::Error::other("Internal allocation error"))?;
395
396 let host_offset_start = host_cluster.relative_offset(offset, cb);
397 let mut allocated_length = offset.remaining_in_cluster(cb);
398 let mut current_host_cluster = host_cluster;
399
400 while allocated_length < full_length {
401 let Some(next) = current_guest_cluster.next_in_l2(cb) else {
402 break;
403 };
404 current_guest_cluster = next;
405
406 let chunk_length = cmp::min(full_length - allocated_length, 1 << cb) as usize;
407 let partial_skip_cow = match (skip_cow, skip_cow_to_eof) {
408 (false, _) => None,
409 (true, false) => Some(0..chunk_length),
410 (true, true) => Some(0..(1 << cb)),
411 };
412
413 let next_host_cluster = current_host_cluster + ClusterCount(1);
414 let host_cluster = self
415 .cow_cluster(
416 current_guest_cluster,
417 Some(next_host_cluster),
418 partial_skip_cow,
419 &mut l2_table,
420 leaked_allocations,
421 )
422 .await?;
423
424 let Some(host_cluster) = host_cluster else {
425 break;
427 };
428 assert!(host_cluster == next_host_cluster);
429 current_host_cluster = host_cluster;
430
431 allocated_length += chunk_length as u64;
432 }
433
434 Ok((host_offset_start.0, allocated_length))
435 }
436
437 async fn ensure_fixed_mapping_no_cleanup(
447 &self,
448 first_cluster: GuestCluster,
449 count: ClusterCount,
450 mapping: FixedMapping,
451 mut l2_table: L2TableWriteGuard<'_>,
452 leaked_allocations: &mut Vec<(HostCluster, ClusterCount)>,
453 ) -> io::Result<ClusterCount> {
454 self.header.require_version(3)?;
455
456 let cb = self.header.cluster_bits();
457 let mut cluster = first_cluster;
458 let end_cluster = first_cluster + count;
459 let mut done = ClusterCount(0);
460
461 while cluster < end_cluster {
462 let l2i = cluster.l2_index(cb);
463 let leaked = match mapping {
464 FixedMapping::ZeroDiscard => l2_table.zero_cluster(l2i, false)?,
465 FixedMapping::ZeroRetainAllocation => l2_table.zero_cluster(l2i, true)?,
466 FixedMapping::FullDiscard => l2_table.discard_cluster(l2i),
467 };
468 if let Some(leaked) = leaked {
469 leaked_allocations.push(leaked);
470 }
471
472 done += ClusterCount(1);
473 let Some(next) = cluster.next_in_l2(cb) else {
474 break;
475 };
476 cluster = next;
477 }
478
479 Ok(done)
480 }
481}
482
483#[derive(Clone, Copy, Debug, Eq, PartialEq)]
485pub(super) enum FixedMapping {
486 ZeroDiscard,
491
492 ZeroRetainAllocation,
501
502 FullDiscard,
507}