Write path mapping. Always called under write lock.
(
&mut self,
address: u64,
backing_data: Option<Vec<u8>>,
)
| 521 | |
| 522 | /// Write path mapping. Always called under write lock. |
| 523 | fn map_write( |
| 524 | &mut self, |
| 525 | address: u64, |
| 526 | backing_data: Option<Vec<u8>>, |
| 527 | ) -> io::Result<ClusterWriteMapping> { |
| 528 | if address >= self.header.size { |
| 529 | return Err(io::Error::from_raw_os_error(EINVAL)); |
| 530 | } |
| 531 | |
| 532 | let l1_index = self.l1_table_index(address) as usize; |
| 533 | let l2_addr_disk = match self.l1_table.get(l1_index) { |
| 534 | Some(&addr) => addr, |
| 535 | None => return Err(io::Error::from_raw_os_error(EINVAL)), |
| 536 | }; |
| 537 | let l2_index = self.l2_table_index(address) as usize; |
| 538 | |
| 539 | let mut set_refcounts = Vec::new(); |
| 540 | |
| 541 | if let Some(new_addr) = self.cache_l2_cluster_alloc(l1_index, l2_addr_disk)? { |
| 542 | set_refcounts.push((new_addr, 1)); |
| 543 | } |
| 544 | |
| 545 | let l2_entry = self.l2_cache.get(l1_index).unwrap()[l2_index]; |
| 546 | let cluster_addr = if l2_entry_is_compressed(l2_entry) { |
| 547 | let decompressed_cluster = self.decompress_l2_cluster(l2_entry)?; |
| 548 | let cluster_addr = self.append_data_cluster(None)?; |
| 549 | self.update_cluster_addr(l1_index, l2_index, cluster_addr, &mut set_refcounts)?; |
| 550 | self.raw_file |
| 551 | .file_mut() |
| 552 | .seek(io::SeekFrom::Start(cluster_addr))?; |
| 553 | let nwritten = io::Write::write(self.raw_file.file_mut(), &decompressed_cluster)?; |
| 554 | if nwritten != decompressed_cluster.len() { |
| 555 | self.set_corrupt_bit_best_effort(); |
| 556 | return Err(io::Error::from_raw_os_error(EIO)); |
| 557 | } |
| 558 | self.deallocate_compressed_cluster(l2_entry)?; |
| 559 | cluster_addr |
| 560 | } else if l2_entry_is_empty(l2_entry) || l2_entry_is_zero(l2_entry) { |
| 561 | let cluster_addr = self.append_data_cluster(backing_data)?; |
| 562 | self.update_cluster_addr(l1_index, l2_index, cluster_addr, &mut set_refcounts)?; |
| 563 | cluster_addr |
| 564 | } else { |
| 565 | // Already allocated - validate alignment |
| 566 | let cluster_addr = l2_entry_std_cluster_addr(l2_entry); |
| 567 | if cluster_addr & (self.raw_file.cluster_size() - 1) != 0 { |
| 568 | self.set_corrupt_bit_best_effort(); |
| 569 | return Err(io::Error::from_raw_os_error(EIO)); |
| 570 | } |
| 571 | cluster_addr |
| 572 | }; |
| 573 | |
| 574 | // Apply deferred refcount updates |
| 575 | for (addr, refcount) in set_refcounts { |
| 576 | self.set_cluster_refcount_track_freed(addr, refcount)?; |
| 577 | } |
| 578 | |
| 579 | let intra_offset = self.raw_file.cluster_offset(address); |
| 580 | Ok(ClusterWriteMapping::Allocated { |
no test coverage detected