fuse_backend_rs/passthrough/
util.rs

1// Use of this source code is governed by a BSD-style license that can be
2// found in the LICENSE-BSD-3-Clause file.
3// Copyright (C) 2023 Alibaba Cloud. All rights reserved.
4
5use 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
19/// the 56th bit used to set the inode to 1 indicates virtual inode
20const VIRTUAL_INODE_FLAG: u64 = 1 << 55;
21
22/// Used to form a pair of dev and mntid as the key of the map
23#[derive(Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq, Debug)]
24struct DevMntIDPair(libc::dev_t, u64);
25
26// Used to generate a unique inode with a maximum of 56 bits. the format is
27// |1bit|8bit|47bit
28// when the highest bit is equal to 0, it means the host inode format, and the lower 47 bits normally store no more than 47-bit inode
29// When the highest bit is equal to 1, it indicates the virtual inode format,
30// which is used to store more than 47 bits of inodes
31// the middle 8bit is used to store the unique ID produced by the combination of dev+mntid
32pub struct UniqueInodeGenerator {
33    // Mapping (dev, mnt_id) pair to another small unique id
34    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
131/// Safe wrapper around libc::openat().
132pub fn openat(
133    dir_fd: &impl AsRawFd,
134    path: &CStr,
135    flags: libc::c_int,
136    mode: u32,
137) -> io::Result<File> {
138    // Safe because:
139    // - CString::new() has returned success and thus guarantees `path_cstr` is a valid
140    //   NUL-terminated string
141    // - this does not modify any memory
142    // - we check the return value
143    // We do not check `flags` because if the kernel cannot handle poorly specified flags then we
144    // have much bigger problems.
145    let fd = if flags & libc::O_CREAT == libc::O_CREAT {
146        // The mode argument is used only when O_CREAT is specified
147        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        // Safe because we just opened this fd
153        Ok(unsafe { File::from_raw_fd(fd) })
154    } else {
155        Err(io::Error::last_os_error())
156    }
157}
158
159/// Open `/proc/self/fd/{fd}` with the given flags to effectively duplicate the given `fd` with new
160/// flags (e.g. to turn an `O_PATH` file descriptor into one that can be used for I/O).
161pub 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    // Clear the `O_NOFOLLOW` flag if it is set since we need to follow the `/proc/self/fd` symlink
168    // to get the file.
169    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    // Safe because this is a constant value and a valid C string.
179    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    // Safe because the kernel will only write data in `st` and we check the return value.
184    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        // Safe because the kernel guarantees that the struct is now fully initialized.
194        Ok(unsafe { st.assume_init() })
195    } else {
196        Err(io::Error::last_os_error())
197    }
198}
199
200/// Returns true if it's safe to open this inode without O_PATH.
201pub fn is_safe_inode(mode: u32) -> bool {
202    // Only regular files and directories are considered safe to be opened from the file
203    // server without O_PATH.
204    matches!(mode & libc::S_IFMT, libc::S_IFREG | libc::S_IFDIR)
205}
206
207/// Returns true if the mode is for a directory.
208pub 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        // use normal inode format
268        {
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            // 56 bit = 0
278            // 55~48 bit = 0000 0001
279            // 47~1 bit  = 1
280            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            // 56 bit = 0
291            // 55~48 bit = 0000 0010
292            // 47~1 bit  = 1
293            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            // 56 bit = 0
304            // 55~48 bit = 0000 0010
305            // 47~1 bit  = 2
306            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            // 56 bit = 0
317            // 55~48 bit = 0000 0010
318            // 47~1 bit  = 0x7fffffffffff
319            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        // use virtual inode format
325        {
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            // 56 bit = 1
334            // 55~48 bit = 0000 0001
335            // 47~1 bit  = 2 virtual inode start from 2~MAX_HOST_INO
336            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            // 56 bit = 1
345            // 55~48 bit = 0000 0001
346            // 47~1 bit  = 3
347            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            // 56 bit = 1
356            // 55~48 bit = 0000 0010
357            // 47~1 bit  = 4
358            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            // 56 bit = 1
367            // 55~48 bit = 0000 0001
368            // 47~1 bit  = 5
369            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}