easy_fuser 0.4.5

A flexible and idiomatic Fuse implementation for Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
/// The `FuseHandler` trait is the core interface for implementing a userspace filesystem via FUSE (Filesystem in Userspace).
///
/// This trait defines methods that correspond to various filesystem operations. By implementing this trait,
/// you can create custom filesystem behaviors for your FUSE-based filesystem.
///
/// # Type Parameter
///
/// - `T`: A type that implements `FileIdType`. This represents the unique identifier for files and directories in your filesystem.
///
/// # Usage
///
/// To create a custom filesystem, implement this trait for your struct. You can choose to implement all methods
/// or rely on default implementations provided by one of the provided templates (like `MirrorFs` or `DefaultFuseHandler`).
///
/// ## Example: Custom Filesystem with MirrorFs as base
///
/// ```rust, no_run
/// use easy_fuser::templates::{DefaultFuseHandler, mirror_fs::*};
/// use easy_fuser::prelude::*;
/// use std::path::{Path, PathBuf};
/// use std::ffi::OsStr;
///
/// struct MyCustomFs {
///     inner: Box<dyn FuseHandler<PathBuf>>,
///     // other fields...
/// }
///
/// impl MyCustomFs {
///     pub fn new(source_path: PathBuf) -> Self {
///         MyCustomFs { inner: Box::new(MirrorFsReadOnly::new(source_path, DefaultFuseHandler::new())) }
///
///     }
/// }
///
/// impl FuseHandler<PathBuf> for MyCustomFs {
///     fn get_inner(&self) -> &dyn FuseHandler<PathBuf> {
///         // Delegate to MirrorFsReadOnly for standard behavior
///         self.inner.as_ref()
///     }
///
///     fn lookup(&self, req: &RequestInfo, parent_id: PathBuf, name: &OsStr) -> FuseResult<FileAttribute> {
///         // Custom logic for lookup operation
///         // ...
///
///         // Delegate to inner handler for standard behavior
///         self.inner.lookup(req, parent_id, name)
///     }
///
///     // Implement other FuseHandler methods as needed, delegating to self.inner as appropriate
///     // ...
/// }
/// ```
///
/// # Important Methods
///
/// While all methods in this trait are important for a fully functional filesystem, some key methods include:
///
/// - `lookup`: Look up a directory entry by name and get its attributes.
/// - `getattr`: Get file attributes.
/// - `read`: Read data from a file.
/// - `write`: Write data to a file.
/// - `readdir`: Read directory contents.
/// - `open`: Open a file.
/// - `create`: Create and open a file.
///
/// # Default Implementations
///
/// Many methods in this trait have default implementations that delegate to the inner handler returned by `get_inner()`.
/// This allows for easy extension and customization of existing filesystem implementations by chaining/overriding their behaviors.
///
/// # Multithreading, Safety, Traits & Lifetime
///
/// ## Using serial feature
///
/// This trait is bound by the `'static` lifetime. However, it doesn't need `Send` or `Sync`.
///
/// **Note:** Using `spawn_mount` for your filesystem will require at least the `Send` trait.
///
/// ## Using parallel or async feature
///
/// This trait requires `Send + Sync` and `'static` lifetime.
///
/// **Important:** FUSE will lock some operations to run at the same time with the same inode, such as `readdir`.
/// This behaviour is not well documented and cannot be guaranteed for now.
///
//// # Additional Resources:
/// For more detailed information, refer to the fuser project documentation, which serves as the foundation for this crate: https://docs.rs/fuser
///
/// Documentation is inspired by the original fuser documentation
use std::ffi::{OsStr, OsString};
use std::path::Path;
use std::time::Duration;

use crate::types::*;

mod private {
    #[cfg(not(feature = "serial"))]
    pub trait OptionalSendSync: Sync + Send {}
    #[cfg(not(feature = "serial"))]
    impl<T: Sync + Send> OptionalSendSync for T {}
    #[cfg(feature = "serial")]
    pub trait OptionalSendSync {}
    #[cfg(feature = "serial")]
    impl<T> OptionalSendSync for T {}
}
use private::OptionalSendSync;

pub trait FuseHandler<TId: FileIdType>: OptionalSendSync + 'static {
    /// Delegate unprovided methods to another FuseHandler, enabling composition
    fn get_inner(&self) -> &dyn FuseHandler<TId>;

    /// Provide a default Time-To-Live for file metadata
    ///
    /// Can be overriden for each FileAttributes returned.
    fn get_default_ttl(&self) -> Duration {
        Duration::from_secs(1)
    }

    /// Initialize the filesystem and configure kernel connection
    fn init(&self, req: &RequestInfo, config: &mut KernelConfig) -> FuseResult<()> {
        self.get_inner().init(req, config)
    }

    /// Perform cleanup operations on filesystem exit
    fn destroy(&self) {
        self.get_inner().destroy();
    }

    /// Check file access permissions
    ///
    /// This method is called for the access() system call. If the 'default_permissions'
    /// mount option is given, this method is not called. This method is not called
    /// under Linux kernel versions 2.4.x
    fn access(&self, req: &RequestInfo, file_id: TId, mask: AccessMask) -> FuseResult<()> {
        self.get_inner().access(req, file_id, mask)
    }

    /// Map block index within file to block index within device
    ///
    /// Note: This makes sense only for block device backed filesystems mounted
    /// with the 'blkdev' option
    fn bmap(&self, req: &RequestInfo, file_id: TId, blocksize: u32, idx: u64) -> FuseResult<u64> {
        self.get_inner().bmap(req, file_id, blocksize, idx)
    }

    /// Copy the specified range from the source inode to the destination inode
    fn copy_file_range(
        &self,
        req: &RequestInfo,
        file_in: TId,
        file_handle_in: BorrowedFileHandle,
        offset_in: i64,
        file_out: TId,
        file_handle_out: BorrowedFileHandle,
        offset_out: i64,
        len: u64,
        flags: u32, // Not implemented yet in standard
    ) -> FuseResult<u32> {
        self.get_inner().copy_file_range(
            req,
            file_in,
            file_handle_in,
            offset_in,
            file_out,
            file_handle_out,
            offset_out,
            len,
            flags,
        )
    }

    /// Create and open a file
    ///
    /// If the file does not exist, first create it with the specified mode, and then
    /// open it. Open flags (with the exception of O_NOCTTY) are available in flags.
    /// If this method is not implemented or under Linux kernel versions earlier than
    /// 2.6.15, the mknod() and open() methods will be called instead.
    fn create(
        &self,
        req: &RequestInfo,
        parent_id: TId,
        name: &OsStr,
        mode: u32,
        umask: u32,
        flags: OpenFlags,
    ) -> FuseResult<(OwnedFileHandle, TId::Metadata, FUSEOpenResponseFlags)> {
        self.get_inner()
            .create(req, parent_id, name, mode, umask, flags)
    }

    /// Preallocate or deallocate space to a file
    fn fallocate(
        &self,
        req: &RequestInfo,
        file_id: TId,
        file_handle: BorrowedFileHandle,
        offset: i64,
        length: i64,
        mode: FallocateFlags,
    ) -> FuseResult<()> {
        self.get_inner()
            .fallocate(req, file_id, file_handle, offset, length, mode)
    }

    /// Flush cached data for an open file
    ///
    /// Called on each close() of the opened file. Not guaranteed to be called after writes or at all.
    /// Used for returning write errors or removing file locks.
    fn flush(
        &self,
        req: &RequestInfo,
        file_id: TId,
        file_handle: BorrowedFileHandle,
        lock_owner: u64,
    ) -> FuseResult<()> {
        self.get_inner()
            .flush(req, file_id, file_handle, lock_owner)
    }

    /// Release references to an inode, if the nlookup count reaches zero (to substract from the number of lookups).
    fn forget(&self, req: &RequestInfo, file_id: TId, nlookup: u64) {
        self.get_inner().forget(req, file_id, nlookup);
    }

    /// Synchronize file contents
    ///
    /// If datasync is true, only flush user data, not metadata.
    fn fsync(
        &self,
        req: &RequestInfo,
        file_id: TId,
        file_handle: BorrowedFileHandle,
        datasync: bool,
    ) -> FuseResult<()> {
        self.get_inner().fsync(req, file_id, file_handle, datasync)
    }

    /// Synchronize directory contents
    ///
    /// If the datasync parameter is true, then only the directory contents should
    /// be flushed, not the metadata. The file_handle will contain the value set
    /// by the opendir method, or will be undefined if the opendir method didn't
    /// set any value.
    fn fsyncdir(
        &self,
        req: &RequestInfo,
        file_id: TId,
        file_handle: BorrowedFileHandle,
        datasync: bool,
    ) -> FuseResult<()> {
        self.get_inner()
            .fsyncdir(req, file_id, file_handle, datasync)
    }

    /// Modify file attributes
    fn getattr(
        &self,
        req: &RequestInfo,
        file_id: TId,
        file_handle: Option<BorrowedFileHandle>,
    ) -> FuseResult<FileAttribute> {
        self.get_inner().getattr(req, file_id, file_handle)
    }

    /// Test for a POSIX file lock.
    fn getlk(
        &self,
        req: &RequestInfo,
        file_id: TId,
        file_handle: BorrowedFileHandle,
        lock_owner: u64,
        lock_info: LockInfo,
    ) -> FuseResult<LockInfo> {
        self.get_inner()
            .getlk(req, file_id, file_handle, lock_owner, lock_info)
    }

    /// Get an extended attribute
    fn getxattr(
        &self,
        req: &RequestInfo,
        file_id: TId,
        name: &OsStr,
        size: u32,
    ) -> FuseResult<Vec<u8>> {
        self.get_inner().getxattr(req, file_id, name, size)
    }

    /// control device
    fn ioctl(
        &self,
        req: &RequestInfo,
        file_id: TId,
        file_handle: BorrowedFileHandle,
        flags: IOCtlFlags,
        cmd: u32,
        in_data: Vec<u8>,
        out_size: u32,
    ) -> FuseResult<(i32, Vec<u8>)> {
        self.get_inner()
            .ioctl(req, file_id, file_handle, flags, cmd, in_data, out_size)
    }

    /// Create a hard link.
    fn link(
        &self,
        req: &RequestInfo,
        file_id: TId,
        newparent: TId,
        newname: &OsStr,
    ) -> FuseResult<TId::Metadata> {
        self.get_inner().link(req, file_id, newparent, newname)
    }

    /// List extended attribute names
    fn listxattr(&self, req: &RequestInfo, file_id: TId, size: u32) -> FuseResult<Vec<u8>> {
        self.get_inner().listxattr(req, file_id, size)
    }

    /// Retrieve file attributes for a directory entry by name and increment the lookup count associated with the inode.
    fn lookup(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<TId::Metadata> {
        self.get_inner().lookup(req, parent_id, name)
    }

    /// Reposition read/write file offset
    fn lseek(
        &self,
        req: &RequestInfo,
        file_id: TId,
        file_handle: BorrowedFileHandle,
        seek: SeekFrom,
    ) -> FuseResult<i64> {
        self.get_inner().lseek(req, file_id, file_handle, seek)
    }

    /// Create a new directory
    fn mkdir(
        &self,
        req: &RequestInfo,
        parent_id: TId,
        name: &OsStr,
        mode: u32,
        umask: u32,
    ) -> FuseResult<TId::Metadata> {
        self.get_inner().mkdir(req, parent_id, name, mode, umask)
    }

    /// Create a new file node (regular file, device, FIFO, socket, etc)
    fn mknod(
        &self,
        req: &RequestInfo,
        parent_id: TId,
        name: &OsStr,
        mode: u32,
        umask: u32,
        rdev: DeviceType,
    ) -> FuseResult<TId::Metadata> {
        self.get_inner()
            .mknod(req, parent_id, name, mode, umask, rdev)
    }

    /// Open a file and return a file handle.
    ///
    /// Open flags (with the exception of O_CREAT, O_EXCL, O_NOCTTY and O_TRUNC) are available in flags. You may store an arbitrary file handle (pointer, index, etc) in file_handle response, and use this in other all other file operations (read, write, flush, release, fsync). Filesystem may also implement stateless file I/O and not store anything in fh. There are also some flags (direct_io, keep_cache) which the filesystem may set, to change the way the file is opened. See fuse_file_info structure in <fuse_common.h> for more details.
    fn open(
        &self,
        req: &RequestInfo,
        file_id: TId,
        flags: OpenFlags,
    ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> {
        self.get_inner().open(req, file_id, flags)
    }

    /// Open a directory
    ///
    /// Allows storing a file handle for use in subsequent directory operations.
    fn opendir(
        &self,
        req: &RequestInfo,
        file_id: TId,
        flags: OpenFlags,
    ) -> FuseResult<(OwnedFileHandle, FUSEOpenResponseFlags)> {
        self.get_inner().opendir(req, file_id, flags)
    }

    /// Read data from a file
    ///
    /// Read should send exactly the number of bytes requested except on EOF or error, otherwise the rest of the data will be substituted with zeroes. An exception to this is when the file has been opened in ‘direct_io’ mode, in which case the return value of the read system call will reflect the return value of this operation. fh will contain the value set by the open method, or will be undefined if the open method didn’t set any value.
    ///
    /// flags: these are the file flags, such as O_SYNC. Only supported with ABI >= 7.9 lock_owner: only supported with ABI >= 7.9
    fn read(
        &self,
        req: &RequestInfo,
        file_id: TId,
        file_handle: BorrowedFileHandle,
        seek: SeekFrom,
        size: u32,
        flags: FUSEOpenFlags,
        lock_owner: Option<u64>,
    ) -> FuseResult<Vec<u8>> {
        self.get_inner()
            .read(req, file_id, file_handle, seek, size, flags, lock_owner)
    }

    /// Read directory contents
    ///
    /// Returns a list of directory entries with minimal metadata.
    ///
    /// Important: The returned file names (OsString) must not contain any slashes ('/').
    /// Including slashes in the file names will result in undefined behavior.
    fn readdir(
        &self,
        req: &RequestInfo,
        file_id: TId,
        file_handle: BorrowedFileHandle,
    ) -> FuseResult<Vec<(OsString, TId::MinimalMetadata)>> {
        self.get_inner().readdir(req, file_id, file_handle)
    }

    /// Read directory contents with full file attributes
    ///
    /// Default implementation combines readdir and lookup operations.
    ///
    /// Important: The returned file names (OsString) must not contain any slashes ('/').
    /// Including slashes in the file names will result in undefined behavior.
    fn readdirplus(
        &self,
        req: &RequestInfo,
        file_id: TId,
        file_handle: BorrowedFileHandle,
    ) -> FuseResult<Vec<(OsString, TId::Metadata)>> {
        let readdir_result = self.readdir(req, file_id.clone(), file_handle)?;
        let mut result = Vec::with_capacity(readdir_result.len());
        for (name, _) in readdir_result.into_iter() {
            let metadata = self.lookup(req, file_id.clone(), &name)?;
            result.push((name, metadata));
        }
        Ok(result)
    }

    /// Read the target of a symbolic link
    fn readlink(&self, req: &RequestInfo, file_id: TId) -> FuseResult<Vec<u8>> {
        self.get_inner().readlink(req, file_id)
    }

    /// Release an open file
    ///
    /// Called when all file descriptors are closed and all memory mappings are unmapped.
    /// Guaranteed to be called once for every open() call.
    fn release(
        &self,
        req: &RequestInfo,
        file_id: TId,
        file_handle: OwnedFileHandle,
        flags: OpenFlags,
        lock_owner: Option<u64>,
        flush: bool,
    ) -> FuseResult<()> {
        self.get_inner()
            .release(req, file_id, file_handle, flags, lock_owner, flush)
    }

    /// Release an open directory
    ///
    /// This method is called exactly once for every successful opendir operation.
    /// The file_handle parameter will contain the value set by the opendir method,
    /// or will be undefined if the opendir method didn't set any value.
    fn releasedir(
        &self,
        req: &RequestInfo,
        file_id: TId,
        file_handle: OwnedFileHandle,
        flags: OpenFlags,
    ) -> FuseResult<()> {
        self.get_inner()
            .releasedir(req, file_id, file_handle, flags)
    }

    /// Remove an extended attribute.
    fn removexattr(&self, req: &RequestInfo, file_id: TId, name: &OsStr) -> FuseResult<()> {
        self.get_inner().removexattr(req, file_id, name)
    }

    /// Rename a file or directory
    fn rename(
        &self,
        req: &RequestInfo,
        parent_id: TId,
        name: &OsStr,
        newparent: TId,
        newname: &OsStr,
        flags: RenameFlags,
    ) -> FuseResult<()> {
        self.get_inner()
            .rename(req, parent_id, name, newparent, newname, flags)
    }

    /// Remove a directory
    fn rmdir(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> {
        self.get_inner().rmdir(req, parent_id, name)
    }

    /// Set file attributes.
    fn setattr(
        &self,
        req: &RequestInfo,
        file_id: TId,
        attrs: SetAttrRequest,
    ) -> FuseResult<FileAttribute> {
        self.get_inner().setattr(req, file_id, attrs)
    }

    /// Acquire, modify or release a POSIX file lock
    ///
    /// For POSIX threads (NPTL) there's a 1-1 relation between pid and owner, but
    /// otherwise this is not always the case. For checking lock ownership, 'fi->owner'
    /// must be used. The l_pid field in 'struct flock' should only be used to fill
    /// in this field in getlk().
    fn setlk(
        &self,
        req: &RequestInfo,
        file_id: TId,
        file_handle: BorrowedFileHandle,
        lock_owner: u64,
        lock_info: LockInfo,
        sleep: bool,
    ) -> FuseResult<()> {
        self.get_inner()
            .setlk(req, file_id, file_handle, lock_owner, lock_info, sleep)
    }

    /// Set an extended attribute
    fn setxattr(
        &self,
        req: &RequestInfo,
        file_id: TId,
        name: &OsStr,
        value: Vec<u8>,
        flags: FUSESetXAttrFlags,
        position: u32,
    ) -> FuseResult<()> {
        self.get_inner()
            .setxattr(req, file_id, name, value, flags, position)
    }

    /// Get file system statistics
    fn statfs(&self, req: &RequestInfo, file_id: TId) -> FuseResult<StatFs> {
        self.get_inner().statfs(req, file_id)
    }

    /// Create a symbolic link.
    fn symlink(
        &self,
        req: &RequestInfo,
        parent_id: TId,
        link_name: &OsStr,
        target: &Path,
    ) -> FuseResult<TId::Metadata> {
        self.get_inner().symlink(req, parent_id, link_name, target)
    }

    /// Write data to a file
    ///
    /// Write should return exactly the number of bytes requested except on error. An exception to this is when the file has been opened in ‘direct_io’ mode, in which case the return value of the write system call will reflect the return value of this operation. fh will contain the value set by the open method, or will be undefined if the open method didn’t set any value.
    ///
    /// write_flags: will contain FUSE_WRITE_CACHE, if this write is from the page cache. If set, the pid, uid, gid, and fh may not match the value that would have been sent if write cachin is disabled flags: these are the file flags, such as O_SYNC. Only supported with ABI >= 7.9 lock_owner: only supported with ABI >= 7.9
    fn write(
        &self,
        req: &RequestInfo,
        file_id: TId,
        file_handle: BorrowedFileHandle,
        seek: SeekFrom,
        data: Vec<u8>,
        write_flags: FUSEWriteFlags,
        flags: OpenFlags,
        lock_owner: Option<u64>,
    ) -> FuseResult<u32> {
        self.get_inner().write(
            req,
            file_id,
            file_handle,
            seek,
            data,
            write_flags,
            flags,
            lock_owner,
        )
    }

    /// Remove a file
    fn unlink(&self, req: &RequestInfo, parent_id: TId, name: &OsStr) -> FuseResult<()> {
        self.get_inner().unlink(req, parent_id, name)
    }
}