snix_castore/fs/
inode_tracker.rs1use std::{collections::HashMap, sync::Arc};
2
3use super::inodes::{DirectoryInodeData, InodeData};
4use crate::B3Digest;
5
6pub struct InodeTracker {
9 data: HashMap<u64, Arc<InodeData>>,
10
11 blob_digest_to_inode: HashMap<B3Digest, u64>,
13
14 symlink_target_to_inode: HashMap<bytes::Bytes, u64>,
16
17 directory_digest_to_inode: HashMap<B3Digest, u64>,
20
21 next_inode: u64,
23}
24
25impl Default for InodeTracker {
26 fn default() -> Self {
27 Self {
28 data: Default::default(),
29
30 blob_digest_to_inode: Default::default(),
31 symlink_target_to_inode: Default::default(),
32 directory_digest_to_inode: Default::default(),
33
34 next_inode: 2,
35 }
36 }
37}
38
39impl InodeTracker {
40 pub fn get(&self, ino: u64) -> Option<Arc<InodeData>> {
42 self.data.get(&ino).cloned()
43 }
44
45 pub fn replace(&mut self, ino: u64, data: Arc<InodeData>) {
48 if self.data.insert(ino, data).is_none() {
49 panic!("replace called on unknown inode");
50 }
51 }
52
53 pub fn put(&mut self, data: InodeData) -> u64 {
59 match data {
60 InodeData::Regular(ref digest, _, _) => {
61 match self.blob_digest_to_inode.get(digest) {
62 Some(found_ino) => {
63 *found_ino
65 }
66 None => self.insert_and_increment(data),
67 }
68 }
69 InodeData::Symlink(ref target) => {
70 match self.symlink_target_to_inode.get(target) {
71 Some(found_ino) => {
72 *found_ino
74 }
75 None => self.insert_and_increment(data),
76 }
77 }
78 InodeData::Directory(DirectoryInodeData::Sparse(ref digest, _size)) => {
79 match self.directory_digest_to_inode.get(digest) {
81 Some(found_ino) => {
82 *found_ino
84 }
85 None => {
86 self.insert_and_increment(data)
88 }
89 }
90 }
91 InodeData::Directory(DirectoryInodeData::Populated(..)) => {
94 unreachable!("should never be called with DirectoryInodeData::Populated")
95 }
96 }
97 }
98
99 fn insert_and_increment(&mut self, data: InodeData) -> u64 {
102 let ino = self.next_inode;
103 match data {
105 InodeData::Regular(ref digest, _, _) => {
106 self.blob_digest_to_inode.insert(digest.clone(), ino);
107 }
108 InodeData::Symlink(ref target) => {
109 self.symlink_target_to_inode.insert(target.clone(), ino);
110 }
111 InodeData::Directory(DirectoryInodeData::Sparse(ref digest, _size)) => {
112 self.directory_digest_to_inode.insert(digest.clone(), ino);
113 }
114 InodeData::Directory(DirectoryInodeData::Populated(ref digest, _)) => {
121 self.directory_digest_to_inode.insert(digest.clone(), ino);
122 }
123 }
124 self.data.insert(ino, Arc::new(data));
126
127 self.next_inode += 1;
129 ino
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use crate::fixtures;
136
137 use super::InodeData;
138 use super::InodeTracker;
139
140 #[test]
142 fn get_nonexistent() {
143 let inode_tracker = InodeTracker::default();
144 assert!(inode_tracker.get(1).is_none());
145 }
146
147 #[test]
149 fn put_regular() {
150 let mut inode_tracker = InodeTracker::default();
151 let f = InodeData::Regular(
152 fixtures::BLOB_A_DIGEST.clone(),
153 fixtures::BLOB_A.len() as u64,
154 false,
155 );
156
157 let ino = inode_tracker.put(f.clone());
159
160 let data = inode_tracker.get(ino).expect("must be some");
162 match *data {
163 InodeData::Regular(ref digest, _, _) => {
164 assert_eq!(&fixtures::BLOB_A_DIGEST.clone(), digest);
165 }
166 InodeData::Symlink(_) | InodeData::Directory(..) => panic!("wrong type"),
167 }
168
169 assert_eq!(ino, inode_tracker.put(f));
171
172 assert_ne!(
174 ino,
175 inode_tracker.put(InodeData::Regular(
176 fixtures::BLOB_B_DIGEST.clone(),
177 fixtures::BLOB_B.len() as u64,
178 false,
179 ))
180 );
181 }
182
183 #[test]
185 fn put_symlink() {
186 let mut inode_tracker = InodeTracker::default();
187 let f = InodeData::Symlink("target".into());
188
189 let ino = inode_tracker.put(f.clone());
191
192 let data = inode_tracker.get(ino).expect("must be some");
194 match *data {
195 InodeData::Symlink(ref target) => {
196 assert_eq!(b"target".to_vec(), *target);
197 }
198 InodeData::Regular(..) | InodeData::Directory(..) => panic!("wrong type"),
199 }
200
201 assert_eq!(ino, inode_tracker.put(f));
203
204 assert_ne!(ino, inode_tracker.put(InodeData::Symlink("target2".into())));
206 }
207}