forensic_mount/
inode_map.rs1#![forbid(unsafe_code)]
2
3pub const FUSE_ROOT_INO: u64 = 1;
5pub const FUSE_RO_INO: u64 = 2;
6pub const FUSE_RW_INO: u64 = 3;
7pub const FUSE_DELETED_INO: u64 = 4;
8pub const FUSE_JOURNAL_INO: u64 = 5;
9pub const FUSE_METADATA_INO: u64 = 6;
10pub const FUSE_UNALLOCATED_INO: u64 = 7;
11pub const FUSE_SESSION_INO: u64 = 8;
12pub const FUSE_EVIDENCE_INO: u64 = 9;
13
14const RO_INODE_OFFSET: u64 = 1_000;
16const RW_INODE_OFFSET: u64 = 10_000_000;
18const DELETED_INODE_OFFSET: u64 = 20_000_000;
20const METADATA_INODE_OFFSET: u64 = 30_000_000;
22const JOURNAL_INODE_OFFSET: u64 = 40_000_000;
24const EVIDENCE_INODE_OFFSET: u64 = 50_000_000;
26const UNALLOCATED_INODE_OFFSET: u64 = 60_000_000;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum InodeNamespace {
32 Virtual(u64),
34 Ro(u64),
36 Rw(u64),
38 Deleted(u64),
40 Metadata(u64),
42 Journal(u64),
44 Evidence(u64),
46 Unallocated(u64),
48}
49
50pub fn decode_fuse_ino(ino: u64) -> InodeNamespace {
52 if ino <= FUSE_EVIDENCE_INO {
53 InodeNamespace::Virtual(ino)
54 } else if ino >= UNALLOCATED_INODE_OFFSET {
55 InodeNamespace::Unallocated(ino - UNALLOCATED_INODE_OFFSET)
56 } else if ino >= EVIDENCE_INODE_OFFSET {
57 InodeNamespace::Evidence(ino - EVIDENCE_INODE_OFFSET)
58 } else if ino >= JOURNAL_INODE_OFFSET {
59 InodeNamespace::Journal(ino - JOURNAL_INODE_OFFSET)
60 } else if ino >= METADATA_INODE_OFFSET {
61 InodeNamespace::Metadata(ino - METADATA_INODE_OFFSET)
62 } else if ino >= DELETED_INODE_OFFSET {
63 InodeNamespace::Deleted(ino - DELETED_INODE_OFFSET)
64 } else if ino >= RW_INODE_OFFSET {
65 InodeNamespace::Rw(ino - RW_INODE_OFFSET)
66 } else {
67 InodeNamespace::Ro(ino - RO_INODE_OFFSET)
68 }
69}
70
71pub fn ro_ino(ext4_ino: u64) -> u64 {
73 ext4_ino.saturating_add(RO_INODE_OFFSET)
74}
75
76pub fn rw_ino(ext4_ino: u64) -> u64 {
78 ext4_ino.saturating_add(RW_INODE_OFFSET)
79}
80
81pub fn deleted_ino(ext4_ino: u64) -> u64 {
83 ext4_ino.saturating_add(DELETED_INODE_OFFSET)
84}
85
86pub fn metadata_ino(id: u64) -> u64 {
88 id.saturating_add(METADATA_INODE_OFFSET)
89}
90
91pub fn journal_ino(seq: u64) -> u64 {
93 seq.saturating_add(JOURNAL_INODE_OFFSET)
94}
95
96pub fn evidence_ino(ext4_ino: u64) -> u64 {
98 ext4_ino.saturating_add(EVIDENCE_INODE_OFFSET)
99}
100
101pub fn unallocated_ino(id: u64) -> u64 {
103 id.saturating_add(UNALLOCATED_INODE_OFFSET)
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn roundtrip_ro_inode() {
112 let ext4_ino = 42;
113 let fuse = ro_ino(ext4_ino);
114 assert_eq!(decode_fuse_ino(fuse), InodeNamespace::Ro(ext4_ino));
115 }
116
117 #[test]
118 fn roundtrip_rw_inode() {
119 let ext4_ino = 42;
120 let fuse = rw_ino(ext4_ino);
121 assert_eq!(decode_fuse_ino(fuse), InodeNamespace::Rw(ext4_ino));
122 }
123
124 #[test]
125 fn roundtrip_deleted_inode() {
126 let ext4_ino = 21;
127 let fuse = deleted_ino(ext4_ino);
128 assert_eq!(decode_fuse_ino(fuse), InodeNamespace::Deleted(ext4_ino));
129 }
130
131 #[test]
132 fn virtual_root() {
133 assert_eq!(decode_fuse_ino(FUSE_ROOT_INO), InodeNamespace::Virtual(1));
134 }
135
136 #[test]
137 fn virtual_dirs() {
138 assert_eq!(decode_fuse_ino(FUSE_RO_INO), InodeNamespace::Virtual(2));
139 assert_eq!(decode_fuse_ino(FUSE_RW_INO), InodeNamespace::Virtual(3));
140 assert_eq!(
141 decode_fuse_ino(FUSE_DELETED_INO),
142 InodeNamespace::Virtual(4)
143 );
144 }
145
146 #[test]
147 fn roundtrip_metadata_inode() {
148 let id = 1;
149 let fuse = metadata_ino(id);
150 assert_eq!(decode_fuse_ino(fuse), InodeNamespace::Metadata(id));
151 }
152
153 #[test]
154 fn roundtrip_journal_inode() {
155 let seq = 42;
156 let fuse = journal_ino(seq);
157 assert_eq!(decode_fuse_ino(fuse), InodeNamespace::Journal(seq));
158 }
159
160 #[test]
161 fn roundtrip_unallocated_inode() {
162 let id = 7;
163 let fuse = unallocated_ino(id);
164 assert_eq!(decode_fuse_ino(fuse), InodeNamespace::Unallocated(id));
165 }
166
167 #[test]
168 fn namespaces_do_not_overlap() {
169 let max_ro = ro_ino(9_000_000);
171 let min_rw = rw_ino(0);
172 assert!(max_ro < min_rw);
173 }
174}