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