1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! `vfs` feature — a reconstructed shadow copy as a `forensic_vfs::ImageSource`.
//!
//! This is the `[H]` state-history seam: forensic-vfs already speaks VSS in its
//! locator vocabulary (`Layer::Snapshot { store: SnapshotRef::VssStore(i) }`,
//! rendered `snapshot:vss,{i}`), and [`VssSnapshotSource`] is the byte source
//! that vocabulary points at — the volume as it stood when shadow copy `i` was
//! taken, addressable by offset like any other image.
//!
//! # Bridging the ownership gap
//!
//! [`crate::Snapshot`] borrows its volume and reads through `&mut self`, because
//! a `Read + Seek` cursor cannot be shared. [`ImageSource`] is the opposite: a
//! cursor-free `read_at(&self, ..)` on a `Send + Sync` handle that lives in an
//! `Arc` at every composition seam. The bridge has two halves:
//!
//! - The expensive, reader-independent half of a snapshot — the diff-area block
//! map and the store bitmap — is built once at [`VssSnapshotSource::open`] and
//! thereafter read-only, so it is shared by `&self` with no lock at all.
//! - Only the cursor needs exclusive access, so **one reader lives behind one
//! `Mutex` inside one source**. Seek-then-read is not atomic, so reads of a
//! single snapshot serialize — that is inherent to a `Read + Seek` cursor, not
//! a choice.
//!
//! **The choice that matters is the lock's granularity, and it is per-snapshot,
//! not per-volume.** [`VssSnapshotSource::open`] takes ownership of a reader
//! rather than borrowing a shared [`crate::VssVolume`], so N shadow copies of one
//! image become N sources with N independent readers and N independent locks, and
//! they are read fully in parallel. A single `Mutex<VssVolume<R>>` shared by every
//! snapshot would have been less code and would have serialized every VSS read in
//! the process behind one lock — including reads of *different* snapshots, which
//! have nothing to contend over. The cost of the choice is one reader per
//! snapshot: over a `forensic_vfs::adapters::SourceCursor` that is a cheap
//! `Arc` clone of the underlying source, and over a file it is one file
//! descriptor.
use ;
use ;
use ;
use crateVssError;
use crateSnapshotState;
use crateVssVolume;
/// An owned, reconstructed shadow copy published as a `forensic_vfs`
/// [`ImageSource`]: positioned reads of the volume as it stood at the moment
/// store `index` was taken.
///
/// Reads are copy-on-write reconstructions, not raw passthrough — a block the
/// snapshot superseded reads from the store's diff area, and a block that was
/// unallocated in the snapshot reads as zeros even where the live volume still
/// holds data.
///
/// ```no_run
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use std::sync::Arc;
/// use forensic_vfs::{DynSource, ImageSource};
/// use vsc::vfs::VssSnapshotSource;
///
/// let source: DynSource = Arc::new(VssSnapshotSource::open(
/// std::fs::File::open("volume.raw")?,
/// 0,
/// )?);
/// let mut buf = [0u8; 512];
/// let read = source.read_at(0, &mut buf)?;
/// # let _ = read;
/// # Ok(())
/// # }
/// ```