async_fusex/
virtual_fs.rs

1//! The `FileSystem` trait
2use std::{path::Path, time::Duration};
3use crate::error::{AsyncFusexError, AsyncFusexResult};
4use crate::fs_util::{*};
5use async_trait::async_trait;
6use tracing::{error, warn};
7use serde::{Deserialize, Serialize};
8use nix::sys::stat::SFlag;
9/// Represents a directory entry in a filesystem.
10#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
11pub struct DirEntry {
12    /// The inode number of the child
13    ino: INum,
14    /// The name of the child
15    name: String,
16    /// The type of the child
17    file_type: FileType,
18}
19
20/// Represents the type of a file in a filesystem.
21///
22/// This enum is used to distinguish between directories, files, and symlinks.
23#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
24pub enum FileType {
25    /// A directory.
26    Dir,
27    /// A regular file.
28    File,
29    /// A symbolic link.
30    Symlink,
31}
32
33impl TryFrom<SFlag> for FileType {
34    type Error = AsyncFusexError;
35
36    /// Attempts to convert an `SFlag` value into a `FileType`.
37    ///
38    /// # Arguments
39    ///
40    /// * `value` - The `SFlag` value representing file type at the OS level.
41    ///
42    /// # Returns
43    ///
44    /// * `Ok(FileType)` - If the conversion is successful.
45    /// * `Err(AsyncFusexError)` - If the `SFlag` value does not correspond to a
46    ///   known `FileType`.
47    fn try_from(value: SFlag) -> Result<Self, Self::Error> {
48        match value {
49            SFlag::S_IFDIR => Ok(Self::Dir),
50            SFlag::S_IFREG => Ok(Self::File),
51            SFlag::S_IFLNK => Ok(Self::Symlink),
52            _ => {
53                error!("Try convert {:?} to FileType failed.", value);
54                Err(AsyncFusexError::ArgumentInvalid { context: vec![] })
55            }
56        }
57    }
58}
59
60impl From<FileType> for SFlag {
61    /// Converts a `FileType` value into an `SFlag`.
62    ///
63    /// # Arguments
64    ///
65    /// * `value` - The `FileType` value representing file type at the OS level.
66    ///
67    /// # Returns
68    ///
69    /// * `SFlag` - The `SFlag` value corresponding to the `FileType`.
70    fn from(value: FileType) -> Self {
71        match value {
72            FileType::Dir => SFlag::S_IFDIR,
73            FileType::File => SFlag::S_IFREG,
74            FileType::Symlink => SFlag::S_IFLNK,
75        }
76    }
77}
78impl DirEntry {
79    /// Creates a new `DirEntry`.
80    ///
81    /// # Arguments
82    ///
83    /// * `inum` - The inode number of the file or directory.
84    /// * `name` - The name of the file or directory.
85    /// * `file_type` - The type of the file (directory, file, or symlink).
86    ///
87    /// # Returns
88    ///
89    /// * `DirEntry` - The new `DirEntry` instance.
90    #[must_use]
91    pub fn new(inum: INum, name: String, file_type: FileType) -> Self {
92        Self {
93            ino: inum,
94            name,
95            file_type,
96        }
97    }
98
99    /// Returns the inode number of the file or directory.
100    #[must_use]
101    pub fn ino(&self) -> INum {
102        self.ino
103    }
104
105    /// Returns the name of the file or directory.
106    #[must_use]
107    pub fn name(&self) -> &str {
108        &self.name
109    }
110
111    /// Returns the type of the file (directory, file, or symlink).
112    #[must_use]
113    pub fn file_type(&self) -> FileType {
114        self.file_type.clone()
115    }
116}
117
118/// Virtual filesystem trait
119#[async_trait]
120pub trait VirtualFs: Sync + Send {
121    /// Initialize filesystem
122    async fn init(&self) -> AsyncFusexResult<()> {
123        Err(AsyncFusexError::Unimplemented {
124            context: vec!["init unimplemented".to_owned()],
125        })
126    }
127
128    /// Clean up filesystem
129    async fn destroy(&self) -> AsyncFusexResult<()> {
130        Err(AsyncFusexError::Unimplemented {
131            context: vec!["destroy unimplemented".to_owned()],
132        })
133    }
134
135    /// Interrupt another request, especially for FUSE
136    /// This is a no-op for other filesystems
137    async fn interrupt(&self, unique: u64) {
138        warn!(
139            "INTERRUPT received, request w/ unique={} interrupted",
140            unique
141        );
142    }
143
144    /// Look up a directory entry by name and get its attributes.
145    async fn lookup(
146        &self,
147        uid: u32,
148        gid: u32,
149        parent: INum,
150        name: &str,
151    ) -> AsyncFusexResult<(Duration, FileAttr, u64)>;
152
153    /// Forget about an inode
154    async fn forget(&self, ino: u64, nlookup: u64);
155
156    /// Get file attributes.
157    async fn getattr(&self, ino: u64) -> AsyncFusexResult<(Duration, FileAttr)>;
158
159    /// Set file attributes.
160    async fn setattr(
161        &self,
162        uid: u32,
163        gid: u32,
164        ino: u64,
165        param: SetAttrParam,
166    ) -> AsyncFusexResult<(Duration, FileAttr)>;
167
168    /// Read symbolic link.
169    async fn readlink(&self, ino: u64) -> AsyncFusexResult<Vec<u8>>;
170
171    /// Create file node.
172    async fn mknod(&self, param: CreateParam) -> AsyncFusexResult<(Duration, FileAttr, u64)>;
173
174    /// Create a directory
175    async fn mkdir(&self, param: CreateParam) -> AsyncFusexResult<(Duration, FileAttr, u64)>;
176
177    /// Remove a file
178    async fn unlink(&self, uid: u32, gid: u32, parent: INum, name: &str) -> AsyncFusexResult<()>;
179
180    /// Remove a directory
181    async fn rmdir(
182        &self,
183        uid: u32,
184        gid: u32,
185        parent: INum,
186        dir_name: &str,
187    ) -> AsyncFusexResult<Option<INum>>;
188
189    /// Create a symbolic link
190    async fn symlink(
191        &self,
192        uid: u32,
193        gid: u32,
194        parent: INum,
195        name: &str,
196        target_path: &Path,
197    ) -> AsyncFusexResult<(Duration, FileAttr, u64)>;
198
199    /// Rename a file
200    async fn rename(&self, uid: u32, gid: u32, param: RenameParam) -> AsyncFusexResult<()>;
201
202    /// Create a hard link
203    #[allow(unused_variables)]
204    async fn link(&self, newparent: u64, newname: &str) -> AsyncFusexResult<()> {
205        Err(AsyncFusexError::Unimplemented {
206            context: vec!["link unimplemented".to_owned()],
207        })
208    }
209
210    /// Open a file
211    async fn open(&self, uid: u32, gid: u32, ino: u64, flags: u32) -> AsyncFusexResult<u64>;
212
213    /// Read data with the given buffer, return current offset and the number of bytes read
214    async fn read(
215        &self,
216        ino: u64,
217        offset: u64,
218        size: u32,
219        buf: &mut Vec<u8>,
220    ) -> AsyncFusexResult<usize>;
221
222    /// Write data
223    async fn write(&self, ino: u64, offset: i64, data: &[u8], flags: u32) -> AsyncFusexResult<()>;
224
225    /// Flush method
226    async fn flush(&self, ino: u64, lock_owner: u64) -> AsyncFusexResult<()>;
227
228    /// Release an open file
229    async fn release(
230        &self,
231        ino: u64,
232        flags: u32, // same as the open flags
233        lock_owner: u64,
234        flush: bool,
235    ) -> AsyncFusexResult<()>;
236
237    /// Synchronize file contents
238    async fn fsync(&self, ino: u64, datasync: bool) -> AsyncFusexResult<()>;
239
240    /// Open a directory
241    async fn opendir(&self, uid: u32, gid: u32, ino: u64, flags: u32) -> AsyncFusexResult<u64>;
242
243    /// Read directory
244    async fn readdir(
245        &self,
246        uid: u32,
247        gid: u32,
248        ino: u64,
249        fh: u64,
250        offset: i64,
251    ) -> AsyncFusexResult<Vec<DirEntry>>;
252
253    /// Release an open directory
254    async fn releasedir(&self, ino: u64, fh: u64, flags: u32) -> AsyncFusexResult<()>;
255
256    /// Synchronize directory contents
257    async fn fsyncdir(&self, ino: u64, fh: u64, datasync: bool) -> AsyncFusexResult<()>;
258
259    /// Get file system statistics
260    async fn statfs(&self, uid: u32, gid: u32, ino: u64) -> AsyncFusexResult<StatFsParam>;
261
262    /// Set an extended attribute
263    #[allow(unused_variables)]
264    async fn setxattr(
265        &self,
266        ino: u64,
267        name: &str,
268        value: &[u8],
269        flags: u32,
270        position: u32,
271    ) -> AsyncFusexResult<()> {
272        Err(AsyncFusexError::Unimplemented {
273            context: vec!["setxattr unimplemented".to_owned()],
274        })
275    }
276
277    /// Get an extended attribute
278    #[allow(unused_variables)]
279    async fn getxattr(&self, ino: u64, name: &str, size: u32) -> AsyncFusexResult<()> {
280        Err(AsyncFusexError::Unimplemented {
281            context: vec!["getxattr unimplemented".to_owned()],
282        })
283    }
284
285    /// Get an extended attribute
286    #[allow(unused_variables)]
287    async fn listxattr(&self, ino: u64, size: u32) -> AsyncFusexResult<()> {
288        Err(AsyncFusexError::Unimplemented {
289            context: vec!["listxattr unimplemented".to_owned()],
290        })
291    }
292
293    /// Remove an extended attribute
294    #[allow(unused_variables)]
295    async fn removexattr(&self, ino: u64, name: &str) -> AsyncFusexResult<()> {
296        Err(AsyncFusexError::Unimplemented {
297            context: vec!["removexattr unimplemented".to_owned()],
298        })
299    }
300
301    /// Check file access permissions
302    ///
303    /// For FUSE:
304    /// This will be called for the `access()` system call. If the
305    /// `default_permissions` mount option is given, self method is not
306    /// called. This method is not called under Linux kernel versions 2.4.x
307    #[allow(unused_variables)]
308    async fn access(&self, uid: u32, gid: u32, ino: u64, mask: u32) -> AsyncFusexResult<()> {
309        Err(AsyncFusexError::Unimplemented {
310            context: vec!["access unimplemented".to_owned()],
311        })
312    }
313
314    /// Create and open a file
315    ///
316    /// For FUSE:
317    /// If the file does not exist, first create it with the specified mode, and
318    /// then open it. Open flags (with the exception of `O_NOCTTY`) are
319    /// available in flags. Filesystem may store an arbitrary file handle
320    /// (pointer, index, etc) in fh, and use self in other all other file
321    /// operations (read, write, flush, release, fsync). There are also some
322    /// flags (`direct_io`, `keep_cache`) which the filesystem may set, to
323    /// change the way the file is opened. See `fuse_file_info` structure in
324    /// `fuse_common.h` for more details. If self method is not implemented
325    /// or under Linux kernel versions earlier than 2.6.15, the mknod()
326    /// and open() methods will be called instead.
327    #[allow(unused_variables)]
328    #[allow(clippy::too_many_arguments)]
329    async fn create(
330        &self,
331        uid: u32,
332        gid: u32,
333        ino: u64,
334        parent: u64,
335        name: &str,
336        mode: u32,
337        flags: u32,
338    ) -> AsyncFusexResult<()> {
339        Err(AsyncFusexError::Unimplemented {
340            context: vec!["create unimplemented".to_owned()],
341        })
342    }
343
344    /// Test for a POSIX file lock
345    #[allow(unused_variables)]
346    async fn getlk(
347        &self,
348        uid: u32,
349        gid: u32,
350        ino: u64,
351        lk_param: FileLockParam,
352    ) -> AsyncFusexResult<()> {
353        Err(AsyncFusexError::Unimplemented {
354            context: vec!["getlk unimplemented".to_owned()],
355        })
356    }
357
358    /// Acquire, modify or release a POSIX file lock
359    ///
360    /// For FUSE:
361    /// For POSIX threads (NPTL) there's a 1-1 relation between pid and owner,
362    /// but otherwise self is not always the case.  For checking lock
363    /// ownership, `fi->owner` must be used. The `l_pid` field in `struct
364    /// flock` should only be used to fill in self field in `getlk()`. Note:
365    /// if the locking methods are not implemented, the kernel will still
366    /// allow file locking to work locally. Hence these are only interesting
367    /// for network filesystems and similar.
368    #[allow(unused_variables)]
369    async fn setlk(
370        &self,
371        uid: u32,
372        gid: u32,
373        ino: u64,
374        lk_param: FileLockParam,
375        sleep: bool,
376    ) -> AsyncFusexResult<()> {
377        Err(AsyncFusexError::Unimplemented {
378            context: vec!["setlk unimplemented".to_owned()],
379        })
380    }
381
382    /// Map block index within file to block index within device
383    /// Note: This makes sense only for block device backed filesystems mounted
384    /// with the `blkdev` option
385    #[allow(unused_variables)]
386    async fn bmap(
387        &self,
388        uid: u32,
389        gid: u32,
390        ino: u64,
391        blocksize: u32,
392        idx: u64,
393    ) -> AsyncFusexResult<()> {
394        Err(AsyncFusexError::Unimplemented {
395            context: vec!["bmap unimplemented".to_owned()],
396        })
397    }
398}