fuse_backend_rs/passthrough/
inode_store.rs1use std::collections::BTreeMap;
5use std::sync::Arc;
6
7use super::file_handle::FileHandle;
8use super::statx::StatExt;
9use super::{Inode, InodeData, InodeHandle};
10
11#[derive(Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq, Debug)]
12pub struct InodeId {
14 pub ino: libc::ino64_t,
15 pub dev: libc::dev_t,
16 pub mnt: u64,
17}
18
19impl InodeId {
20 #[inline]
21 pub(super) fn from_stat(st: &StatExt) -> Self {
22 InodeId {
23 ino: st.st.st_ino,
24 dev: st.st.st_dev,
25 mnt: st.mnt_id,
26 }
27 }
28}
29
30#[derive(Default)]
31pub struct InodeStore {
32 data: BTreeMap<Inode, Arc<InodeData>>,
33 by_id: BTreeMap<InodeId, Inode>,
34 by_handle: BTreeMap<Arc<FileHandle>, Inode>,
35}
36
37impl InodeStore {
38 pub fn insert(&mut self, data: Arc<InodeData>) {
43 self.by_id.insert(data.id, data.inode);
44 if let InodeHandle::Handle(handle) = &data.handle {
45 self.by_handle
46 .insert(handle.file_handle().clone(), data.inode);
47 }
48 self.data.insert(data.inode, data);
49 }
50
51 pub fn remove(&mut self, inode: &Inode, remove_data_only: bool) -> Option<Arc<InodeData>> {
53 let data = self.data.remove(inode);
54 if remove_data_only {
55 return data;
59 }
60
61 if let Some(data) = data.as_ref() {
62 if let InodeHandle::Handle(handle) = &data.handle {
63 self.by_handle.remove(handle.file_handle());
64 }
65 self.by_id.remove(&data.id);
66 }
67 data
68 }
69
70 pub fn clear(&mut self) {
71 self.data.clear();
72 self.by_handle.clear();
73 self.by_id.clear();
74 }
75
76 pub fn get(&self, inode: &Inode) -> Option<&Arc<InodeData>> {
77 self.data.get(inode)
78 }
79
80 pub fn get_by_id(&self, id: &InodeId) -> Option<&Arc<InodeData>> {
81 let inode = self.inode_by_id(id)?;
82 self.get(inode)
83 }
84
85 pub fn get_by_handle(&self, handle: &FileHandle) -> Option<&Arc<InodeData>> {
86 let inode = self.inode_by_handle(handle)?;
87 self.get(inode)
88 }
89
90 pub fn inode_by_id(&self, id: &InodeId) -> Option<&Inode> {
91 self.by_id.get(id)
92 }
93
94 pub fn inode_by_handle(&self, handle: &FileHandle) -> Option<&Inode> {
95 self.by_handle.get(handle)
96 }
97}
98
99#[cfg(test)]
100mod test {
101 use super::super::*;
102 use super::*;
103
104 use std::ffi::CStr;
105 use std::mem::MaybeUninit;
106 use std::os::unix::io::AsRawFd;
107 use std::sync::atomic::Ordering;
108 use vmm_sys_util::tempfile::TempFile;
109
110 impl PartialEq for InodeData {
111 fn eq(&self, other: &Self) -> bool {
112 if self.inode != other.inode
113 || self.id != other.id
114 || self.mode != other.mode
115 || self.refcount.load(Ordering::Relaxed) != other.refcount.load(Ordering::Relaxed)
116 {
117 return false;
118 }
119
120 match (&self.handle, &other.handle) {
121 (InodeHandle::File(f1), InodeHandle::File(f2)) => f1.as_raw_fd() == f2.as_raw_fd(),
122 (InodeHandle::Handle(h1), InodeHandle::Handle(h2)) => {
123 h1.file_handle() == h2.file_handle()
124 }
125 _ => false,
126 }
127 }
128 }
129
130 fn stat_fd(fd: &impl AsRawFd) -> io::Result<libc::stat64> {
131 let mut st = MaybeUninit::<libc::stat64>::zeroed();
132 let null_path = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") };
133
134 let res = unsafe {
136 libc::fstatat64(
137 fd.as_raw_fd(),
138 null_path.as_ptr(),
139 st.as_mut_ptr(),
140 libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW,
141 )
142 };
143 if res >= 0 {
144 Ok(unsafe { st.assume_init() })
146 } else {
147 Err(io::Error::last_os_error())
148 }
149 }
150
151 #[test]
152 fn test_inode_store() {
153 let mut m = InodeStore::default();
154 let tmpfile1 = TempFile::new().unwrap();
155 let tmpfile2 = TempFile::new().unwrap();
156
157 let inode1: Inode = 3;
158 let inode2: Inode = 4;
159 let inode_stat1 = StatExt {
160 st: stat_fd(tmpfile1.as_file()).unwrap(),
161 mnt_id: 0,
162 };
163 let inode_stat2 = StatExt {
164 st: stat_fd(tmpfile2.as_file()).unwrap(),
165 mnt_id: 0,
166 };
167 let id1 = InodeId::from_stat(&inode_stat1);
168 let id2 = InodeId::from_stat(&inode_stat2);
169 let file_or_handle1 = InodeHandle::File(tmpfile1.into_file());
170 let file_or_handle2 = InodeHandle::File(tmpfile2.into_file());
171 let data1 = InodeData::new(inode1, file_or_handle1, 2, id1, inode_stat1.st.st_mode);
172 let data2 = InodeData::new(inode2, file_or_handle2, 2, id2, inode_stat2.st.st_mode);
173 let data1 = Arc::new(data1);
174 let data2 = Arc::new(data2);
175
176 m.insert(data1.clone());
177
178 assert!(m.get(&1).is_none());
180
181 assert!(m.get_by_id(&InodeId::default()).is_none());
183 assert!(m.get_by_handle(&FileHandle::default()).is_none());
184 assert_eq!(m.get(&inode1).unwrap(), &data1);
185 assert_eq!(m.get_by_id(&id1).unwrap(), &data1);
186
187 m.insert(data2.clone());
189 assert!(m.get(&1).is_none());
190 assert!(m.get_by_id(&InodeId::default()).is_none());
191 assert!(m.get_by_handle(&FileHandle::default()).is_none());
192 assert_eq!(m.get(&inode1).unwrap(), &data1);
193 assert_eq!(m.get_by_id(&id1).unwrap(), &data1);
194 assert_eq!(m.get(&inode2).unwrap(), &data2);
195 assert_eq!(m.get_by_id(&id2).unwrap(), &data2);
196
197 assert!(m.remove(&1, false).is_none());
199
200 assert_eq!(m.remove(&inode1, false).unwrap(), data1.clone());
202 assert!(m.get(&inode1).is_none());
203 assert!(m.get_by_id(&id1).is_none());
204 assert_eq!(m.get(&inode2).unwrap(), &data2);
205 assert_eq!(m.get_by_id(&id2).unwrap(), &data2);
206
207 m.clear();
209 assert!(m.get(&1).is_none());
210 assert!(m.get_by_id(&InodeId::default()).is_none());
211 assert!(m.get_by_handle(&FileHandle::default()).is_none());
212 assert!(m.get(&inode1).is_none());
213 assert!(m.get_by_id(&id1).is_none());
214 assert!(m.get(&inode2).is_none());
215 assert!(m.get_by_id(&id2).is_none());
216 }
217}