Skip to main content

forensic_mount/
lib.rs

1#![forbid(unsafe_code)]
2
3pub mod detect;
4pub mod filter;
5pub mod fusefs;
6pub mod inode_map;
7pub mod session;
8pub mod types;
9
10#[cfg(unix)]
11pub mod fuse_unix;
12pub mod fuse_windows;
13
14#[cfg(feature = "ext4")]
15pub mod fs_ext4;
16
17#[cfg(feature = "iso")]
18pub mod fs_iso;
19
20pub mod fs_raw;
21
22pub use types::*;
23
24use std::io;
25use std::path::Path;
26
27/// Mount options for the FUSE filesystem.
28///
29/// Platform-agnostic configuration consumed by both the Unix (fuser)
30/// and Windows (`WinFSP`) mount backends.
31pub struct MountOptions {
32    pub read_only: bool,
33    pub daemon: bool,
34    pub fs_name: String,
35}
36
37impl Default for MountOptions {
38    fn default() -> Self {
39        Self {
40            read_only: false,
41            daemon: false,
42            fs_name: "4n6mount".to_string(),
43        }
44    }
45}
46
47/// The core trait that filesystem crates implement.
48///
49/// Provides both standard filesystem access (required methods) and
50/// forensic operations (optional, with sensible defaults).
51pub trait ForensicFs {
52    // --- Core filesystem ops (required) ---
53
54    /// The root directory inode number for this filesystem.
55    fn root_ino(&self) -> u64;
56
57    /// List directory entries for the given inode.
58    fn read_dir(&mut self, ino: u64) -> FsResult<Vec<FsDirEntry>>;
59
60    /// Look up a name in a directory, returning the child inode if found.
61    fn lookup(&mut self, parent_ino: u64, name: &[u8]) -> FsResult<Option<u64>>;
62
63    /// Get file/directory metadata for an inode.
64    fn metadata(&mut self, ino: u64) -> FsResult<FsMetadata>;
65
66    /// Read the entire contents of a file.
67    fn read_file(&mut self, ino: u64) -> FsResult<Vec<u8>>;
68
69    /// Read a range of bytes from a file.
70    fn read_file_range(&mut self, ino: u64, offset: u64, len: u64) -> FsResult<Vec<u8>>;
71
72    /// Read the target of a symbolic link.
73    fn read_link(&mut self, ino: u64) -> FsResult<Vec<u8>>;
74
75    // --- Forensic ops (optional) ---
76
77    /// List deleted inodes.
78    fn deleted_inodes(&mut self) -> FsResult<Vec<FsDeletedInode>> {
79        Ok(vec![])
80    }
81
82    /// Attempt to recover a deleted file by inode number.
83    fn recover_file(&mut self, _ino: u64) -> FsResult<FsRecoveryResult> {
84        Err(not_supported("recover_file"))
85    }
86
87    /// Generate a forensic timeline of all filesystem events.
88    fn timeline(&mut self) -> FsResult<Vec<FsTimelineEvent>> {
89        Ok(vec![])
90    }
91
92    /// Get all unallocated block ranges.
93    fn unallocated_blocks(&mut self) -> FsResult<Vec<FsBlockRange>> {
94        Ok(vec![])
95    }
96
97    /// Read raw data from an unallocated block range.
98    fn read_unallocated(&mut self, _range: &FsBlockRange) -> FsResult<Vec<u8>> {
99        Err(not_supported("read_unallocated"))
100    }
101
102    /// List journal transactions.
103    fn journal_transactions(&mut self) -> FsResult<Vec<FsTransaction>> {
104        Ok(vec![])
105    }
106
107    /// Get filesystem-specific info as JSON (superblock, volume label, etc.).
108    fn fs_info(&self) -> FsResult<serde_json::Value> {
109        Ok(serde_json::Value::Null)
110    }
111
112    /// The block size of this filesystem.
113    fn block_size(&self) -> u64 {
114        4096
115    }
116}
117
118/// Mount a forensic filesystem via FUSE (or `WinFSP` on Windows).
119///
120/// This is the main entry point for consumers.  Pass a `ForensicFs`
121/// implementation and a `MountOptions`, and this dispatches to the
122/// correct platform backend.
123///
124/// On Unix the mount is handled by `fuser`.  On Windows it will be
125/// handled by `winfsp-wrs` (currently a stub that returns
126/// `Unsupported`).
127pub fn mount(
128    fs: Box<dyn ForensicFs + Send>,
129    mountpoint: &Path,
130    session: Option<session::Session>,
131    options: &MountOptions,
132) -> io::Result<()> {
133    #[cfg(unix)]
134    {
135        fuse_unix::mount_unix(fs, mountpoint, session, options)
136    }
137    #[cfg(windows)]
138    {
139        fuse_windows::mount_windows(fs, mountpoint, session, options)
140    }
141    #[cfg(not(any(unix, windows)))]
142    {
143        let _ = (fs, mountpoint, session, options);
144        Err(io::Error::new(
145            io::ErrorKind::Unsupported,
146            "no FUSE support on this platform",
147        ))
148    }
149}