fuse_backend_rs/passthrough/
util.rs1use std::collections::{btree_map, BTreeMap};
6use std::ffi::{CStr, CString};
7use std::fs::File;
8use std::io;
9use std::mem::MaybeUninit;
10use std::os::unix::io::{AsRawFd, FromRawFd};
11use std::sync::atomic::{AtomicU64, AtomicU8, Ordering};
12use std::sync::Mutex;
13
14use super::inode_store::InodeId;
15use super::MAX_HOST_INO;
16use crate::abi::fuse_abi as fuse;
17use crate::api::EMPTY_CSTR;
18
19const VIRTUAL_INODE_FLAG: u64 = 1 << 55;
21
22#[derive(Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq, Debug)]
24struct DevMntIDPair(libc::dev_t, u64);
25
26pub struct UniqueInodeGenerator {
33 dev_mntid_map: Mutex<BTreeMap<DevMntIDPair, u8>>,
35 next_unique_id: AtomicU8,
36 next_virtual_inode: AtomicU64,
37}
38
39impl UniqueInodeGenerator {
40 pub fn new() -> Self {
41 UniqueInodeGenerator {
42 dev_mntid_map: Mutex::new(Default::default()),
43 next_unique_id: AtomicU8::new(1),
44 next_virtual_inode: AtomicU64::new(fuse::ROOT_ID + 1),
45 }
46 }
47
48 pub fn get_unique_inode(&self, id: &InodeId) -> io::Result<libc::ino64_t> {
49 let unique_id = {
50 let id: DevMntIDPair = DevMntIDPair(id.dev, id.mnt);
51 let mut id_map_guard = self.dev_mntid_map.lock().unwrap();
52 match id_map_guard.entry(id) {
53 btree_map::Entry::Occupied(v) => *v.get(),
54 btree_map::Entry::Vacant(v) => {
55 if self.next_unique_id.load(Ordering::Relaxed) == u8::MAX {
56 return Err(io::Error::new(
57 io::ErrorKind::Other,
58 "the number of combinations of dev and mntid exceeds 255",
59 ));
60 }
61 let next_id = self.next_unique_id.fetch_add(1, Ordering::Relaxed);
62 v.insert(next_id);
63 next_id
64 }
65 }
66 };
67
68 let inode = if id.ino <= MAX_HOST_INO {
69 id.ino
70 } else {
71 if self.next_virtual_inode.load(Ordering::Relaxed) > MAX_HOST_INO {
72 return Err(io::Error::new(
73 io::ErrorKind::Other,
74 format!("the virtual inode excess {}", MAX_HOST_INO),
75 ));
76 }
77 self.next_virtual_inode.fetch_add(1, Ordering::Relaxed) | VIRTUAL_INODE_FLAG
78 };
79
80 Ok((unique_id as u64) << 47 | inode)
81 }
82
83 #[cfg(test)]
84 fn decode_unique_inode(&self, inode: libc::ino64_t) -> io::Result<InodeId> {
85 if inode > crate::api::VFS_MAX_INO {
86 return Err(io::Error::new(
87 io::ErrorKind::InvalidInput,
88 format!("the inode {} excess {}", inode, crate::api::VFS_MAX_INO),
89 ));
90 }
91
92 let dev_mntid = (inode >> 47) as u8;
93 if dev_mntid == u8::MAX {
94 return Err(io::Error::new(
95 io::ErrorKind::InvalidInput,
96 format!("invalid dev and mntid {} excess 255", dev_mntid),
97 ));
98 }
99
100 let mut dev: libc::dev_t = 0;
101 let mut mnt: u64 = 0;
102
103 let mut found = false;
104 let id_map_guard = self.dev_mntid_map.lock().unwrap();
105 for (k, v) in id_map_guard.iter() {
106 if *v == dev_mntid {
107 found = true;
108 dev = k.0;
109 mnt = k.1;
110 break;
111 }
112 }
113
114 if !found {
115 return Err(io::Error::new(
116 io::ErrorKind::InvalidInput,
117 format!(
118 "invalid dev and mntid {},there is no record in memory ",
119 dev_mntid
120 ),
121 ));
122 }
123 Ok(InodeId {
124 ino: inode & MAX_HOST_INO,
125 dev,
126 mnt,
127 })
128 }
129}
130
131pub fn openat(
133 dir_fd: &impl AsRawFd,
134 path: &CStr,
135 flags: libc::c_int,
136 mode: u32,
137) -> io::Result<File> {
138 let fd = if flags & libc::O_CREAT == libc::O_CREAT {
146 unsafe { libc::openat(dir_fd.as_raw_fd(), path.as_ptr(), flags, mode) }
148 } else {
149 unsafe { libc::openat(dir_fd.as_raw_fd(), path.as_ptr(), flags) }
150 };
151 if fd >= 0 {
152 Ok(unsafe { File::from_raw_fd(fd) })
154 } else {
155 Err(io::Error::last_os_error())
156 }
157}
158
159pub fn reopen_fd_through_proc(
162 fd: &impl AsRawFd,
163 flags: libc::c_int,
164 proc_self_fd: &impl AsRawFd,
165) -> io::Result<File> {
166 let name = CString::new(format!("{}", fd.as_raw_fd()).as_str())?;
167 openat(
170 proc_self_fd,
171 &name,
172 flags & !libc::O_NOFOLLOW & !libc::O_CREAT,
173 0,
174 )
175}
176
177pub fn stat_fd(dir: &impl AsRawFd, path: Option<&CStr>) -> io::Result<libc::stat64> {
178 let pathname =
180 path.unwrap_or_else(|| unsafe { CStr::from_bytes_with_nul_unchecked(EMPTY_CSTR) });
181 let mut st = MaybeUninit::<libc::stat64>::zeroed();
182
183 let res = unsafe {
185 libc::fstatat64(
186 dir.as_raw_fd(),
187 pathname.as_ptr(),
188 st.as_mut_ptr(),
189 libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW,
190 )
191 };
192 if res >= 0 {
193 Ok(unsafe { st.assume_init() })
195 } else {
196 Err(io::Error::last_os_error())
197 }
198}
199
200pub fn is_safe_inode(mode: u32) -> bool {
202 matches!(mode & libc::S_IFMT, libc::S_IFREG | libc::S_IFDIR)
205}
206
207pub fn is_dir(mode: u32) -> bool {
209 (mode & libc::S_IFMT) == libc::S_IFDIR
210}
211
212pub fn ebadf() -> io::Error {
213 io::Error::from_raw_os_error(libc::EBADF)
214}
215
216pub fn einval() -> io::Error {
217 io::Error::from_raw_os_error(libc::EINVAL)
218}
219
220pub fn enosys() -> io::Error {
221 io::Error::from_raw_os_error(libc::ENOSYS)
222}
223
224pub fn eperm() -> io::Error {
225 io::Error::from_raw_os_error(libc::EPERM)
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 #[test]
233 fn test_is_safe_inode() {
234 let mode = libc::S_IFREG;
235 assert!(is_safe_inode(mode));
236
237 let mode = libc::S_IFDIR;
238 assert!(is_safe_inode(mode));
239
240 let mode = libc::S_IFBLK;
241 assert!(!is_safe_inode(mode));
242
243 let mode = libc::S_IFCHR;
244 assert!(!is_safe_inode(mode));
245
246 let mode = libc::S_IFIFO;
247 assert!(!is_safe_inode(mode));
248
249 let mode = libc::S_IFLNK;
250 assert!(!is_safe_inode(mode));
251
252 let mode = libc::S_IFSOCK;
253 assert!(!is_safe_inode(mode));
254 }
255
256 #[test]
257 fn test_is_dir() {
258 let mode = libc::S_IFREG;
259 assert!(!is_dir(mode));
260
261 let mode = libc::S_IFDIR;
262 assert!(is_dir(mode));
263 }
264
265 #[test]
266 fn test_generate_unique_inode() {
267 {
269 let generator = UniqueInodeGenerator::new();
270
271 let inode_alt_key = InodeId {
272 ino: 1,
273 dev: 0,
274 mnt: 0,
275 };
276 let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
277 assert_eq!(unique_inode, 0x00800000000001);
281 let expect_inode_alt_key = generator.decode_unique_inode(unique_inode).unwrap();
282 assert_eq!(expect_inode_alt_key, inode_alt_key);
283
284 let inode_alt_key = InodeId {
285 ino: 1,
286 dev: 0,
287 mnt: 1,
288 };
289 let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
290 assert_eq!(unique_inode, 0x01000000000001);
294 let expect_inode_alt_key = generator.decode_unique_inode(unique_inode).unwrap();
295 assert_eq!(expect_inode_alt_key, inode_alt_key);
296
297 let inode_alt_key = InodeId {
298 ino: 2,
299 dev: 0,
300 mnt: 1,
301 };
302 let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
303 assert_eq!(unique_inode, 0x01000000000002);
307 let expect_inode_alt_key = generator.decode_unique_inode(unique_inode).unwrap();
308 assert_eq!(expect_inode_alt_key, inode_alt_key);
309
310 let inode_alt_key = InodeId {
311 ino: MAX_HOST_INO,
312 dev: 0,
313 mnt: 1,
314 };
315 let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
316 assert_eq!(unique_inode, 0x017fffffffffff);
320 let expect_inode_alt_key = generator.decode_unique_inode(unique_inode).unwrap();
321 assert_eq!(expect_inode_alt_key, inode_alt_key);
322 }
323
324 {
326 let generator = UniqueInodeGenerator::new();
327 let inode_alt_key = InodeId {
328 ino: MAX_HOST_INO + 1,
329 dev: u64::MAX,
330 mnt: u64::MAX,
331 };
332 let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
333 assert_eq!(unique_inode, 0x80800000000002);
337
338 let inode_alt_key = InodeId {
339 ino: MAX_HOST_INO + 2,
340 dev: u64::MAX,
341 mnt: u64::MAX,
342 };
343 let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
344 assert_eq!(unique_inode, 0x80800000000003);
348
349 let inode_alt_key = InodeId {
350 ino: MAX_HOST_INO + 3,
351 dev: u64::MAX,
352 mnt: 0,
353 };
354 let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
355 assert_eq!(unique_inode, 0x81000000000004);
359
360 let inode_alt_key = InodeId {
361 ino: u64::MAX,
362 dev: u64::MAX,
363 mnt: u64::MAX,
364 };
365 let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
366 assert_eq!(unique_inode, 0x80800000000005);
370 }
371 }
372
373 #[test]
374 fn test_stat_fd() {
375 let topdir = env!("CARGO_MANIFEST_DIR");
376 let dir = File::open(topdir).unwrap();
377 let filename = CString::new("build.rs").unwrap();
378
379 let st1 = stat_fd(&dir, None).unwrap();
380 let st2 = stat_fd(&dir, Some(&filename)).unwrap();
381
382 assert_eq!(st1.st_dev, st2.st_dev);
383 assert_ne!(st1.st_ino, st2.st_ino);
384 }
385}