1use std::sync::Arc;
12
13use crate::error::VfsResult;
14
15#[non_exhaustive]
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub enum FileId {
20 NtfsRef { entry: u64, seq: u16 },
22 ExtInode { ino: u64, gen: u32 },
24 ApfsOid { oid: u64, xid: u64 },
26 FatDirEntry { cluster: u32, index: u16 },
28 IsoExtent { block: u32 },
30 Opaque(u64),
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct StreamInfo {
39 pub id: StreamId,
40 pub name: Option<Vec<u8>>,
41 pub size: u64,
42 pub residency: ResidencyKind,
43 pub kind: StreamKind,
44}
45
46#[non_exhaustive]
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49pub enum StreamId {
50 Default,
51 Named(u16),
52 ResourceFork,
53 Xattr(u16),
54 Slack,
55}
56
57#[non_exhaustive]
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub enum StreamKind {
62 NtfsData,
63 NtfsAds,
64 HfsResourceFork,
65 ApfsNamed,
66 Xattr,
67 SyntheticSlack,
68}
69
70#[non_exhaustive]
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum ResidencyKind {
74 Resident { inline_len: u32 },
75 NonResident,
76}
77
78#[non_exhaustive]
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub enum Allocation {
83 Allocated,
84 Deleted,
85 Orphan,
86}
87
88#[non_exhaustive]
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub enum NodeKind {
92 File,
93 Dir,
94 Symlink,
95 Device,
96 Other,
97}
98
99#[non_exhaustive]
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub enum TimeSource {
104 Si,
106 Fn,
108 InodeTable,
110 DirEntry,
112 Unspecified,
114}
115
116#[non_exhaustive]
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120pub enum TimeResolution {
121 WinFileTime,
123 Nanos,
124 Micros,
125 Seconds,
126 TwoSeconds,
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub struct TimeStamp {
133 pub unix_nanos: i128,
135 pub source: TimeSource,
136 pub resolution: TimeResolution,
137}
138
139#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
142pub struct MacbTimes {
143 pub modified: Option<TimeStamp>,
144 pub accessed: Option<TimeStamp>,
145 pub changed: Option<TimeStamp>,
147 pub born: Option<TimeStamp>,
149}
150
151#[non_exhaustive]
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
155pub enum TimeZonePolicy {
156 Utc,
157 LocalUnknown,
158 Local { minutes_east: i16 },
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub struct SectorSizes {
164 pub logical: u32,
165 pub physical: u32,
166 pub cluster_or_block: u32,
167}
168
169#[allow(clippy::struct_excessive_bools)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
172pub struct RunFlags {
173 pub sparse: bool,
174 pub encrypted: bool,
175 pub compressed: bool,
176 pub filler: bool,
178}
179
180#[derive(Debug, Clone, Copy, PartialEq, Eq)]
182pub struct ByteRun {
183 pub image_offset: u64,
184 pub len: u64,
185 pub flags: RunFlags,
186}
187
188#[non_exhaustive]
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192pub enum RunAlloc {
193 Allocated,
194 Unallocated,
195 Overwritten,
196 Unknown,
197}
198
199#[derive(Debug, Clone, Copy, PartialEq, Eq)]
201pub struct RunInfo {
202 pub run: ByteRun,
203 pub alloc: RunAlloc,
204}
205
206#[derive(Debug, Clone, PartialEq, Eq)]
208pub struct DirEntry {
209 pub name: Vec<u8>,
210 pub id: FileId,
211 pub kind: NodeKind,
212}
213
214#[derive(Debug, Clone, PartialEq, Eq)]
216pub struct HardLink {
217 pub parent: FileId,
218 pub name: Vec<u8>,
219}
220
221#[derive(Debug, Clone, PartialEq, Eq)]
225pub struct FsMeta {
226 pub ino: u64,
228 pub kind: NodeKind,
229 pub allocated: Allocation,
231 pub size: u64,
232 pub nlink: u32,
233 pub uid: Option<u32>,
234 pub gid: Option<u32>,
235 pub mode: Option<u32>,
236 pub times: MacbTimes,
237 pub streams: Vec<StreamInfo>,
239 pub residency: ResidencyKind,
240 pub link_target: Option<Vec<u8>>,
241}
242
243pub struct DirStream(Box<dyn Iterator<Item = VfsResult<DirEntry>> + Send>);
246pub struct ExtentStream(Box<dyn Iterator<Item = VfsResult<RunInfo>> + Send>);
248pub struct NodeStream(Box<dyn Iterator<Item = VfsResult<FsMeta>> + Send>);
250
251impl DirStream {
252 pub fn new(it: impl Iterator<Item = VfsResult<DirEntry>> + Send + 'static) -> Self {
254 Self(Box::new(it))
255 }
256 #[must_use]
258 pub fn empty() -> Self {
259 Self(Box::new(std::iter::empty()))
260 }
261}
262impl ExtentStream {
263 pub fn new(it: impl Iterator<Item = VfsResult<RunInfo>> + Send + 'static) -> Self {
264 Self(Box::new(it))
265 }
266 #[must_use]
267 pub fn empty() -> Self {
268 Self(Box::new(std::iter::empty()))
269 }
270}
271impl NodeStream {
272 pub fn new(it: impl Iterator<Item = VfsResult<FsMeta>> + Send + 'static) -> Self {
273 Self(Box::new(it))
274 }
275 #[must_use]
276 pub fn empty() -> Self {
277 Self(Box::new(std::iter::empty()))
278 }
279}
280impl Iterator for DirStream {
281 type Item = VfsResult<DirEntry>;
282 fn next(&mut self) -> Option<Self::Item> {
283 self.0.next()
284 }
285}
286impl Iterator for ExtentStream {
287 type Item = VfsResult<RunInfo>;
288 fn next(&mut self) -> Option<Self::Item> {
289 self.0.next()
290 }
291}
292impl Iterator for NodeStream {
293 type Item = VfsResult<FsMeta>;
294 fn next(&mut self) -> Option<Self::Item> {
295 self.0.next()
296 }
297}
298
299pub use forensicnomicon_core::filesystems::FsKind;
302
303pub trait FileSystem: Send + Sync {
307 fn kind(&self) -> FsKind;
308 fn root(&self) -> FileId;
309 fn sector_sizes(&self) -> SectorSizes;
310 fn timestamp_zone(&self) -> TimeZonePolicy;
311
312 fn read_dir(&self, ino: FileId) -> VfsResult<DirStream>;
314 fn extents(&self, ino: FileId, stream: StreamId) -> VfsResult<ExtentStream>;
316
317 fn lookup(&self, parent: FileId, name: &[u8]) -> VfsResult<Option<FileId>>;
318 fn meta(&self, ino: FileId) -> VfsResult<FsMeta>;
319 fn read_at(&self, ino: FileId, stream: StreamId, off: u64, buf: &mut [u8]) -> VfsResult<usize>;
320 fn read_link(&self, ino: FileId, cap: usize) -> VfsResult<Vec<u8>>;
323
324 fn data_streams(&self, ino: FileId) -> VfsResult<Vec<StreamInfo>> {
327 let _ = ino;
328 Ok(Vec::new())
329 }
330 fn hardlinks(&self, ino: FileId) -> VfsResult<Vec<HardLink>> {
332 let _ = ino;
333 Ok(Vec::new())
334 }
335 fn deleted(&self) -> VfsResult<NodeStream>;
337 fn unallocated(&self) -> VfsResult<ExtentStream>;
339 fn slack(&self, ino: FileId, stream: StreamId) -> VfsResult<Option<ByteRun>> {
341 let _ = (ino, stream);
342 Ok(None)
343 }
344
345 #[cfg(feature = "findings")]
348 fn findings(&self) -> VfsResult<Vec<forensicnomicon::report::Finding>> {
349 Ok(Vec::new())
350 }
351}
352
353pub type DynFs = Arc<dyn FileSystem>;