1use bitvec::prelude::*;
21use std::cell::RefCell;
22use std::sync::Arc;
23
24use std::io::{self, Read, Seek, SeekFrom, Write};
25
26use crate::bat::{Bat, BatState, PayloadBlockState, SectorBitmapState};
27use crate::constants::MIB;
28use crate::error::{Error, Result};
29use crate::log_replay::ReplayOverlay;
30use crate::medium::ReadSemanticsPolicy;
31use crate::medium::{
32 Len, Medium, ParentMedium, ParentRequest, ParentResolver, SetLen, SyncData, read_exact_at,
33};
34use crate::metadata::Metadata;
35
36pub struct IO<'a, T = std::fs::File> {
50 pub(super) file: &'a mut Medium<T>,
51 pub(super) block_size: u32,
52 pub(super) logical_sector_size: u32,
53 pub(super) chunk_ratio: u64,
54 max_sector: u64,
55 pub(super) has_parent: bool,
56 pub(super) overlay: Option<Arc<ReplayOverlay>>,
58 parent_medium: RefCell<Option<Box<dyn ParentMedium>>>,
59}
60
61#[derive(Clone, Copy, Debug)]
62pub(super) struct ResolvedBatEntry {
63 pub(super) state: BatState,
64 file_offset_mb: u64,
65}
66
67impl ResolvedBatEntry {
68 pub(super) fn file_offset_mb(&self) -> u64 {
69 self.file_offset_mb
70 }
71}
72
73impl<'a, T> IO<'a, T>
74where
75 T: Read + Seek,
76{
77 pub(crate) fn new(file: &'a mut Medium<T>) -> Result<Self> {
82 let overlay = file.replay_overlay_arc().cloned();
83 let meta_buf = file.metadata_buf()?.to_vec();
84 let metadata = Metadata::new(&meta_buf)?;
85 let items = metadata.items();
86
87 let fp = items
88 .file_parameters()
89 .map_err(|_| Error::InvalidMetadata("FileParameters metadata item not found".into()))?;
90 let block_size = fp.block_size();
91 let has_parent = fp.has_parent();
92 if block_size == 0 {
93 return Err(Error::InvalidMetadata("block size must be non-zero".into()));
94 }
95
96 let logical_sector_size = items.logical_sector_size().ok().unwrap_or(512);
97 if logical_sector_size == 0 {
98 return Err(Error::InvalidMetadata(
99 "logical sector size must be non-zero".into(),
100 ));
101 }
102
103 let virtual_size = items.virtual_disk_size().map_err(|_| {
104 Error::InvalidMetadata("VirtualDiskSize metadata item not found".into())
105 })?;
106
107 let max_sector = virtual_size / u64::from(logical_sector_size);
108
109 let chunk_ratio = (1u64 << 23) * u64::from(logical_sector_size) / u64::from(block_size);
111
112 Ok(Self {
113 file,
114 block_size,
115 logical_sector_size,
116 chunk_ratio,
117 max_sector: max_sector.saturating_sub(1),
118 has_parent,
119 overlay,
120 parent_medium: RefCell::new(None),
121 })
122 }
123
124 pub fn sector<'io>(&'io mut self, start: u64, count: u64) -> Result<Sector<'io, 'a, T>> {
133 if count == 0 {
134 return Err(Error::InvalidParameter("count must be >= 1".into()));
135 }
136
137 let end_sector = start
138 .checked_add(count)
139 .ok_or_else(|| Error::InvalidParameter("start + count overflow".into()))?;
140
141 if end_sector - 1 > self.max_sector {
142 return Err(Error::SectorOutOfBounds {
143 sector: start,
144 max: self.max_sector,
145 });
146 }
147
148 let logical_sector_size = self.logical_sector_size;
149 let block_size = self.block_size;
150 let chunk_ratio = self.chunk_ratio;
151 let range_bytes = count
152 .checked_mul(u64::from(logical_sector_size))
153 .ok_or_else(|| Error::InvalidParameter("sector_count * lss overflow".into()))?;
154
155 Ok(Sector {
156 io: self,
157 start,
158 count,
159 logical_sector_size,
160 block_size,
161 chunk_ratio,
162 pos: 0,
163 range_bytes,
164 semantics: ReadSemanticsPolicy::default(),
165 })
166 }
167}
168
169pub struct Sector<'io, 'medium, T = std::fs::File> {
177 pub(super) io: &'io mut IO<'medium, T>,
178 pub(super) start: u64,
179 pub(super) count: u64,
180 pub(super) logical_sector_size: u32,
181 pub(super) block_size: u32,
182 pub(super) chunk_ratio: u64,
183 pub(super) pos: u64,
184 pub(super) range_bytes: u64,
185 pub(super) semantics: ReadSemanticsPolicy,
186}
187
188impl<'medium, T> Sector<'_, 'medium, T>
189where
190 T: Read + Seek,
191{
192 pub(super) fn io(&self) -> &IO<'medium, T> {
193 self.io
194 }
195
196 pub(super) fn io_mut(&mut self) -> &mut IO<'medium, T> {
197 self.io
198 }
199
200 #[must_use]
207 pub fn semantics(mut self, policy: ReadSemanticsPolicy) -> Self {
208 self.semantics = policy;
209 self
210 }
211
212 fn read_at(&mut self, buf: &mut [u8], byte_offset: u64) -> Result<()> {
225 let lss = self.logical_sector_size as usize;
226 let range_bytes = self.count * lss as u64;
227
228 if buf.is_empty() {
230 return Ok(());
231 }
232
233 let byte_end = byte_offset
235 .checked_add(buf.len() as u64)
236 .ok_or_else(|| Error::InvalidParameter("byte_offset + buf.len() overflow".into()))?;
237 if byte_end > range_bytes {
238 return Err(Error::InvalidParameter(format!(
239 "byte range [{byte_offset}, {byte_end}) exceeds sector range of {range_bytes} bytes"
240 )));
241 }
242
243 let start_byte = usize::try_from(byte_offset)
244 .map_err(|_| Error::InvalidParameter("byte_offset does not fit usize".into()))?;
245 let end_byte = start_byte + buf.len();
246
247 let first_sector_rel = start_byte / lss; let first_skip = start_byte % lss; let aligned_end = end_byte.is_multiple_of(lss);
250
251 if first_skip == 0 && aligned_end {
253 let sectors_to_read = buf.len() / lss;
254 return self.read_full_sectors(
255 buf,
256 self.start + u64::try_from(first_sector_rel).expect("sector index fits u64"),
257 u64::try_from(sectors_to_read).expect("sector count fits u64"),
258 );
259 }
260
261 let last_sector_rel = (end_byte - 1) / lss;
263 let affected_count = last_sector_rel - first_sector_rel + 1;
264 let mut temp = vec![0u8; affected_count * lss];
265 self.read_full_sectors(
266 &mut temp,
267 self.start + u64::try_from(first_sector_rel).expect("sector index fits u64"),
268 u64::try_from(affected_count).expect("sector count fits u64"),
269 )?;
270 buf.copy_from_slice(&temp[first_skip..first_skip + buf.len()]);
271 Ok(())
272 }
273
274 pub(super) fn read_full_sectors(
283 &mut self, buf: &mut [u8], start_sector: u64, sector_count: u64,
284 ) -> Result<()> {
285 let lss = self.logical_sector_size as usize;
286 let spb = self.sectors_per_block();
287 let mut buf_offset = 0usize;
288 let mut current_sector = start_sector;
289 let mut remaining = sector_count;
290
291 while remaining > 0 {
292 let block_idx = current_sector / spb;
293 let sector_in_block = current_sector % spb;
294 let remaining_in_block = spb - sector_in_block;
295 let sectors_this_round = remaining.min(remaining_in_block);
296 let bytes_this_round =
297 usize::try_from(sectors_this_round).expect("sector count fits usize") * lss;
298
299 let entry = self.resolve_bat_entry_for_block(block_idx)?;
300 let state = entry.state;
301
302 match state {
303 BatState::Payload(payload_state) => match payload_state {
304 PayloadBlockState::FullyPresent => {
305 self.read_block_range_from_file(
306 entry.file_offset_mb(),
307 sector_in_block,
308 sectors_this_round,
309 &mut buf[buf_offset..buf_offset + bytes_this_round],
310 )?;
311 }
312 PayloadBlockState::PartiallyPresent => {
313 self.read_partially_present_range(
314 entry,
315 block_idx,
316 sector_in_block,
317 sectors_this_round,
318 &mut buf[buf_offset..buf_offset + bytes_this_round],
319 )?;
320 }
321 PayloadBlockState::Unmapped => {
322 if self.semantics == ReadSemanticsPolicy::RawDataPreferred {
323 if self
324 .read_block_range_from_file(
325 entry.file_offset_mb(),
326 sector_in_block,
327 sectors_this_round,
328 &mut buf[buf_offset..buf_offset + bytes_this_round],
329 )
330 .is_err()
331 {
332 buf[buf_offset..buf_offset + bytes_this_round].fill(0);
333 }
334 } else {
335 buf[buf_offset..buf_offset + bytes_this_round].fill(0);
336 }
337 }
338 PayloadBlockState::NotPresent if self.io().has_parent => {
339 self.read_parent_range(
340 block_idx,
341 sector_in_block,
342 sectors_this_round,
343 &mut buf[buf_offset..buf_offset + bytes_this_round],
344 )?;
345 }
346 PayloadBlockState::Zero
347 | PayloadBlockState::NotPresent
348 | PayloadBlockState::Undefined => {
349 buf[buf_offset..buf_offset + bytes_this_round].fill(0);
350 }
351 },
352 BatState::SectorBitmap(_) => {
353 return Err(Error::BlockNotPresent {
354 block_idx,
355 state: "sector bitmap entry (expected payload)".into(),
356 });
357 }
358 }
359
360 buf_offset += bytes_this_round;
361 current_sector += sectors_this_round;
362 remaining -= sectors_this_round;
363 }
364
365 Ok(())
366 }
367
368 pub(super) fn sectors_per_block(&self) -> u64 {
372 u64::from(self.block_size) / u64::from(self.logical_sector_size)
373 }
374
375 pub(super) fn sector_bitmap_bat_index(&self, block_idx: u64) -> u64 {
376 let stride = self.chunk_ratio + 1;
377 let chunk_idx = block_idx / self.chunk_ratio;
378 chunk_idx * stride + self.chunk_ratio
379 }
380
381 fn read_parent_range(
382 &mut self, block_idx: u64, start_sector_in_block: u64, sector_count: u64, buf: &mut [u8],
383 ) -> Result<()> {
384 let lss = self.logical_sector_size as usize;
385 let spb = self.sectors_per_block();
386 for i in 0..sector_count {
387 let offset = usize::try_from(i).expect("sector offset fits usize") * lss;
388 self.read_from_parent_sector(
389 block_idx * spb + start_sector_in_block + i,
390 &mut buf[offset..offset + lss],
391 )?;
392 }
393 Ok(())
394 }
395
396 #[cfg(test)]
398 pub(super) fn resolve_bat_entry(&mut self) -> Result<ResolvedBatEntry> {
399 let block_idx = self.start / self.sectors_per_block();
400 self.resolve_bat_entry_for_block(block_idx)
401 }
402
403 pub(super) fn resolve_bat_entry_for_block(
405 &mut self, block_idx: u64,
406 ) -> Result<ResolvedBatEntry> {
407 let bat_buf = self.io_mut().file.bat_buf()?;
408 let bat = Bat::new(&bat_buf, self.chunk_ratio);
409 let bat_array_idx = block_idx + block_idx / self.chunk_ratio;
410 let entry = bat.entry(bat_array_idx)?;
411 Ok(ResolvedBatEntry {
412 state: entry.state()?,
413 file_offset_mb: entry.file_offset_mb(),
414 })
415 }
416
417 fn read_block_range_from_file(
423 &mut self, file_offset_mb: u64, sector_in_block: u64, _sector_count: u64, buf: &mut [u8],
424 ) -> Result<()> {
425 let lss = self.logical_sector_size as usize;
426 let file_offset = file_offset_mb * u64::from(MIB) + sector_in_block * lss as u64;
427
428 if let Some(ref overlay) = self.io().overlay {
430 let n = overlay.read(file_offset, buf);
431 if n > 0 {
432 return Ok(());
433 }
434
435 let last_file_offset = overlay.last_file_offset();
437 if last_file_offset > 0 && file_offset < last_file_offset {
438 buf.fill(0);
439 return Ok(());
440 }
441 }
442
443 read_exact_at(self.io_mut().file.inner_mut(), file_offset, buf)?;
444 Ok(())
445 }
446
447 fn read_partially_present_range(
457 &mut self, entry: ResolvedBatEntry, block_idx: u64, start_sector_in_block: u64,
458 sector_count: u64, buf: &mut [u8],
459 ) -> Result<()> {
460 let lss = self.logical_sector_size as usize;
461
462 let stride = self.chunk_ratio + 1;
464 let chunk_idx = block_idx / self.chunk_ratio;
465 let sb_bat_idx = chunk_idx * stride + self.chunk_ratio;
466
467 let bat_buf = self.io_mut().file.bat_buf()?;
468 let bat = Bat::new(&bat_buf, self.chunk_ratio);
469 let sb_entry = bat.entry(sb_bat_idx)?;
470
471 let sb_state = sb_entry
472 .sector_bitmap_state()
473 .ok_or(Error::InvalidSectorBitmapState(sb_entry.raw_state()))?;
474 if sb_state != SectorBitmapState::Present {
475 return Err(Error::StateMismatch {
476 state: sb_entry.raw_state(),
477 description: "sector bitmap not Present for PartiallyPresent payload".into(),
478 });
479 }
480
481 let sb_file_offset = sb_entry.file_offset_mb() * u64::from(MIB);
482 let bitmap_size = MIB as usize;
483 let mut bitmap = vec![0u8; bitmap_size];
484 read_exact_at(self.io_mut().file.inner_mut(), sb_file_offset, &mut bitmap)?;
485
486 let spb = self.sectors_per_block();
487 let block_in_chunk = block_idx % self.chunk_ratio;
488
489 for i in 0..sector_count {
490 let sib = start_sector_in_block + i;
491 let sector_in_chunk = block_in_chunk * spb + sib;
492 let byte_idx =
493 usize::try_from(sector_in_chunk / 8).expect("bitmap byte index fits usize");
494
495 if byte_idx >= bitmap.len() {
496 return Err(Error::InvalidMetadata(format!(
497 "sector bitmap index out of range: byte {byte_idx}"
498 )));
499 }
500
501 let in_child = bitmap.view_bits::<Lsb0>()
502 [usize::try_from(sector_in_chunk).expect("bitmap bit index fits usize")];
503 let offset = usize::try_from(i).expect("sector offset fits usize") * lss;
504
505 if in_child {
506 self.read_block_range_from_file(
507 entry.file_offset_mb(),
508 sib,
509 1,
510 &mut buf[offset..offset + lss],
511 )?;
512 } else {
513 self.read_from_parent_sector(
514 block_idx * spb + start_sector_in_block + i,
515 &mut buf[offset..offset + lss],
516 )?;
517 }
518 }
519
520 Ok(())
521 }
522
523 fn read_from_parent_sector(&mut self, global_sector: u64, buf: &mut [u8]) -> Result<()> {
532 self.ensure_parent_resolved()?;
533 let mut parent_ref = self.io().parent_medium.borrow_mut();
534 let parent = parent_ref.as_mut().ok_or(Error::ParentResolverRequired)?;
535 let parent_lss = parent.logical_sector_size()?;
536 if parent_lss != self.logical_sector_size {
537 return Err(Error::ParentSectorSizeMismatch {
538 child: self.logical_sector_size,
539 parent: parent_lss,
540 });
541 }
542 parent.read_sector(global_sector, buf)
543 }
544
545 fn ensure_parent_resolved(&mut self) -> Result<()> {
546 if self.io().parent_medium.borrow().is_some() {
547 return Ok(());
548 }
549
550 let meta_buf = self.io_mut().file.metadata_buf()?.to_vec();
551 let meta = Metadata::new(&meta_buf)?;
552 let items = meta.items();
553 let locator = items.parent_locator().map_err(|_| Error::ParentNotFound)?;
554 let expected_data_write_guid = locator
555 .entries()
556 .find_map(|entry| {
557 let kv_data = locator.key_value_data();
558 let key = entry.key(kv_data).ok()?;
559 if key == "parent_linkage" {
560 let value = entry.value(kv_data).ok()?;
561 crate::types::Guid::parse_braced(&value).ok()
562 } else {
563 None
564 }
565 })
566 .ok_or_else(|| {
567 Error::InvalidParentLocator("parent_linkage missing or invalid".into())
568 })?;
569 let child_virtual_disk_size = items.virtual_disk_size()?;
570 let request = ParentRequest {
571 locator,
572 expected_data_write_guid,
573 child_logical_sector_size: self.logical_sector_size,
574 child_virtual_disk_size,
575 };
576 let mut resolver_ref = self
577 .io()
578 .file
579 .parent_resolver
580 .lock()
581 .map_err(|_| Error::InvalidFile("parent resolver lock poisoned".into()))?;
582 let resolver = resolver_ref.as_mut().ok_or(Error::ParentResolverRequired)?;
583 let mut parent = resolver.resolve_parent(request)?;
584 if parent.data_write_guid()? != expected_data_write_guid {
585 return Err(Error::ParentLocatorGuidMismatch {
586 expected: expected_data_write_guid,
587 actual: parent.data_write_guid()?,
588 });
589 }
590 let parent_lss = parent.logical_sector_size()?;
591 if parent_lss != self.logical_sector_size {
592 return Err(Error::ParentSectorSizeMismatch {
593 child: self.logical_sector_size,
594 parent: parent_lss,
595 });
596 }
597 *self.io().parent_medium.borrow_mut() = Some(parent);
598 Ok(())
599 }
600}
601
602impl<T> ParentMedium for Medium<T>
603where
604 T: Read + Seek,
605{
606 fn data_write_guid(&mut self) -> Result<crate::types::Guid> {
607 let header_buf = self.header_buf_arc()?;
608 let header = crate::header::Header::new(&header_buf)?;
609 Ok(header.header(0)?.data_write_guid())
610 }
611
612 fn logical_sector_size(&mut self) -> Result<u32> {
613 let meta_buf = self.metadata_buf()?;
614 let meta = Metadata::new(&meta_buf)?;
615 meta.items().logical_sector_size()
616 }
617
618 fn read_sector(&mut self, sector: u64, buf: &mut [u8]) -> Result<()> {
619 let logical_sector_size = self.logical_sector_size()?;
620 if buf.len() != logical_sector_size as usize {
621 return Err(Error::InvalidParameter(format!(
622 "parent sector buffer length must equal logical sector size: got {}, expected {logical_sector_size}",
623 buf.len()
624 )));
625 }
626 let mut io = self.io()?;
627 io.sector(sector, 1)?.read_exact(buf)?;
628 Ok(())
629 }
630}
631
632impl<F, T> ParentResolver for F
633where
634 F: Fn(ParentRequest<'_>) -> Result<Medium<T>> + 'static,
635 T: Read + Seek + 'static,
636{
637 fn resolve_parent(&mut self, request: ParentRequest<'_>) -> Result<Box<dyn ParentMedium>> {
638 Ok(Box::new(std::cell::RefCell::new(self(request)?)))
639 }
640}
641
642impl<T> std::fmt::Debug for Sector<'_, '_, T> {
643 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
644 f.debug_struct("Sector")
645 .field("start", &self.start)
646 .field("count", &self.count)
647 .field("logical_sector_size", &self.logical_sector_size)
648 .field("block_size", &self.block_size)
649 .field("chunk_ratio", &self.chunk_ratio)
650 .field("pos", &self.pos)
651 .field("range_bytes", &self.range_bytes)
652 .field("semantics", &self.semantics)
653 .finish_non_exhaustive()
654 }
655}
656
657impl<T> PartialEq for Sector<'_, '_, T> {
659 fn eq(&self, other: &Self) -> bool {
660 std::ptr::eq(self.io, other.io) && self.start == other.start && self.count == other.count
661 }
662}
663
664impl<T> io::Read for Sector<'_, '_, T>
669where
670 T: Read + Seek,
671{
672 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
673 if self.pos >= self.range_bytes {
674 return Ok(0); }
676 let available = usize::try_from(self.range_bytes - self.pos).unwrap_or(usize::MAX);
677 let to_read = buf.len().min(available);
678 self.read_at(&mut buf[..to_read], self.pos)?;
679 self.pos += to_read as u64;
680 Ok(to_read)
681 }
682}
683
684impl<T> io::Write for Sector<'_, '_, T>
685where
686 T: Read + Write + Seek + Len + SetLen + SyncData,
687{
688 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
689 if self.pos >= self.range_bytes {
690 return Ok(0); }
692 let available = usize::try_from(self.range_bytes - self.pos).unwrap_or(usize::MAX);
693 let to_write = buf.len().min(available);
694 self.write_at(&buf[..to_write], self.pos)?;
695 self.pos += to_write as u64;
696 Ok(to_write)
697 }
698 fn flush(&mut self) -> io::Result<()> {
699 Ok(()) }
701}
702
703impl<T> io::Seek for Sector<'_, '_, T> {
704 fn seek(&mut self, from: SeekFrom) -> io::Result<u64> {
705 let new_pos = match from {
706 SeekFrom::Start(offset) => offset,
707 SeekFrom::End(offset) => {
708 i64::try_from(self.range_bytes)
710 .ok()
711 .and_then(|v| v.checked_add(offset))
712 .and_then(|v| u64::try_from(v.max(0)).ok())
713 .unwrap_or(0)
714 }
715 SeekFrom::Current(offset) => i64::try_from(self.pos)
716 .ok()
717 .and_then(|v| v.checked_add(offset))
718 .and_then(|v| u64::try_from(v.max(0)).ok())
719 .unwrap_or(0),
720 };
721 self.pos = new_pos.min(self.range_bytes);
722 Ok(self.pos)
723 }
724}