Skip to main content

libfuse_fs/util/
mod.rs

1#![allow(clippy::unnecessary_cast)]
2pub mod bind_mount;
3pub mod mapping;
4pub mod open_options;
5
6use tracing::error;
7
8use std::{fmt::Display, path::PathBuf};
9
10#[cfg(target_os = "macos")]
11use libc::stat as stat64;
12#[cfg(target_os = "linux")]
13use libc::stat64;
14use rfuse3::{FileType, Timestamp, raw::reply::FileAttr};
15use serde::{Deserialize, Serialize};
16
17#[derive(Debug, Deserialize, Serialize, Clone, Default)]
18pub struct GPath {
19    pub path: Vec<String>,
20}
21
22impl GPath {
23    pub fn new() -> GPath {
24        GPath { path: Vec::new() }
25    }
26    pub fn push(&mut self, path: String) {
27        self.path.push(path);
28    }
29    pub fn pop(&mut self) -> Option<String> {
30        self.path.pop()
31    }
32    pub fn name(&self) -> String {
33        self.path.last().unwrap().clone()
34    }
35    pub fn part(&self, i: usize, j: usize) -> String {
36        self.path[i..j].join("/")
37    }
38}
39
40impl From<String> for GPath {
41    fn from(mut s: String) -> GPath {
42        if s.starts_with('/') {
43            s.remove(0);
44        }
45        GPath {
46            path: s.split('/').map(String::from).collect(),
47        }
48    }
49}
50
51impl From<GPath> for PathBuf {
52    fn from(val: GPath) -> Self {
53        let path_str = val.path.join("/");
54        PathBuf::from(path_str)
55    }
56}
57impl Display for GPath {
58    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
59        write!(f, "{}", self.path.join("/"))
60    }
61}
62
63pub fn convert_stat64_to_file_attr(stat: stat64) -> FileAttr {
64    FileAttr {
65        ino: stat.st_ino,
66        size: stat.st_size as u64,
67        blocks: stat.st_blocks as u64,
68        atime: Timestamp::new(stat.st_atime, stat.st_atime_nsec.try_into().unwrap()),
69        mtime: Timestamp::new(stat.st_mtime, stat.st_mtime_nsec.try_into().unwrap()),
70        ctime: Timestamp::new(stat.st_ctime, stat.st_ctime_nsec.try_into().unwrap()),
71        #[cfg(target_os = "macos")]
72        crtime: Timestamp::new(0, 0), // Set crtime to 0 for non-macOS platforms
73        kind: filetype_from_mode(stat.st_mode as u32),
74        perm: (stat.st_mode & 0o7777) as u16,
75        nlink: stat.st_nlink as u32,
76        uid: stat.st_uid,
77        gid: stat.st_gid,
78        rdev: stat.st_rdev as u32,
79        #[cfg(target_os = "macos")]
80        flags: 0, // Set flags to 0 for non-macOS platforms
81        blksize: stat.st_blksize as u32,
82    }
83}
84
85pub fn filetype_from_mode(st_mode: u32) -> FileType {
86    let st_mode = st_mode & (libc::S_IFMT as u32);
87    if st_mode == (libc::S_IFIFO as u32) {
88        return FileType::NamedPipe;
89    }
90    if st_mode == (libc::S_IFCHR as u32) {
91        return FileType::CharDevice;
92    }
93    if st_mode == (libc::S_IFBLK as u32) {
94        return FileType::BlockDevice;
95    }
96    if st_mode == (libc::S_IFDIR as u32) {
97        return FileType::Directory;
98    }
99    if st_mode == (libc::S_IFREG as u32) {
100        return FileType::RegularFile;
101    }
102    if st_mode == (libc::S_IFLNK as u32) {
103        return FileType::Symlink;
104    }
105    if st_mode == (libc::S_IFSOCK as u32) {
106        return FileType::Socket;
107    }
108    // Handle whiteout files on macOS (0xE000 / 57344)
109    // rfuse3 doesn't seem to have a specific Whiteout variant exposed or we don't have it imported.
110    // Treating as regular file or simply not panicking.
111    // Ideally we should filter these out if they are not real files, or map to closest.
112    #[cfg(target_os = "macos")]
113    if st_mode == 0xE000 {
114        return FileType::RegularFile;
115    }
116    error!("wrong st mode : {st_mode}");
117    unreachable!();
118}
119#[cfg(test)]
120mod tests {
121    use super::GPath;
122
123    #[test]
124    fn test_from_string() {
125        let path = String::from("/release");
126        let gapth = GPath::from(path);
127        assert_eq!(gapth.to_string(), String::from("release"))
128    }
129}