Fast path read mapping under read lock only. Returns None on cache miss. All access here is through shared reference. CacheMap::get, VecCache::get and index operations are all shared reference compatible.
(
&self,
address: u64,
count: usize,
has_backing_file: bool,
)
| 353 | /// All access here is through shared reference. CacheMap::get, |
| 354 | /// VecCache::get and index operations are all shared reference compatible. |
| 355 | fn try_map_read( |
| 356 | &self, |
| 357 | address: u64, |
| 358 | count: usize, |
| 359 | has_backing_file: bool, |
| 360 | ) -> io::Result<Option<ClusterReadMapping>> { |
| 361 | if address >= self.header.size { |
| 362 | return Err(io::Error::from_raw_os_error(EINVAL)); |
| 363 | } |
| 364 | |
| 365 | let l1_index = self.l1_table_index(address) as usize; |
| 366 | let l2_addr_disk = match self.l1_table.get(l1_index) { |
| 367 | Some(&addr) => addr, |
| 368 | None => return Err(io::Error::from_raw_os_error(EINVAL)), |
| 369 | }; |
| 370 | |
| 371 | if l2_addr_disk == 0 { |
| 372 | return Ok(Some(self.unallocated_read_mapping( |
| 373 | address, |
| 374 | count, |
| 375 | has_backing_file, |
| 376 | ))); |
| 377 | } |
| 378 | |
| 379 | let l2_table = match self.l2_cache.get(l1_index) { |
| 380 | Some(table) => table, |
| 381 | None => return Ok(None), // cache miss, need write lock |
| 382 | }; |
| 383 | |
| 384 | let l2_index = self.l2_table_index(address) as usize; |
| 385 | let l2_entry = l2_table[l2_index]; |
| 386 | |
| 387 | // Compressed entries: extract layout from L2 entry under read lock. |
| 388 | // The caller reads and decompresses on its own fd without holding |
| 389 | // the metadata lock. |
| 390 | if l2_entry_is_compressed(l2_entry) { |
| 391 | let (host_offset, compressed_size) = |
| 392 | l2_entry_compressed_cluster_layout(l2_entry, self.header.cluster_bits); |
| 393 | let cluster_offset = self.raw_file.cluster_offset(address) as usize; |
| 394 | return Ok(Some(ClusterReadMapping::Compressed { |
| 395 | host_offset, |
| 396 | compressed_size, |
| 397 | cluster_offset, |
| 398 | length: count, |
| 399 | })); |
| 400 | } |
| 401 | |
| 402 | if l2_entry_is_empty(l2_entry) { |
| 403 | Ok(Some(self.unallocated_read_mapping( |
| 404 | address, |
| 405 | count, |
| 406 | has_backing_file, |
| 407 | ))) |
| 408 | } else if l2_entry_is_zero(l2_entry) { |
| 409 | // Match original QcowFile::file_read semantics where zero flagged |
| 410 | // entries fall through to backing file when one exists or return |
| 411 | // zeros otherwise. |
| 412 | Ok(Some(self.unallocated_read_mapping( |
no test coverage detected