diff --git a/README.md b/README.md index 1e051d0c..b063b8d8 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,9 @@ of continuing development. In particular adding features from ABIs after 7.19 A working FUSE filesystem consists of three parts: -1. The **kernel driver** that registers as a filesystem and forwards operations into a communication channel to a userspace process that handles them. -1. The **userspace library** (libfuse) that helps the userspace process to establish and run communication with the kernel driver. -1. The **userspace implementation** that actually processes the filesystem operations. +1. The **kernel driver** (part of the operating system) that registers as a filesystem and forwards operations into a communication channel to a userspace process that handles them. +1. The **userspace library** (e.g., `fuser` and/or `libfuse`) that helps the userspace process to establish and run communication with the kernel driver. +1. The **userspace implementation** (your code here) that actually processes the filesystem operations. The kernel driver is provided by the FUSE project, the userspace implementation needs to be provided by the developer. FUSE-Rust provides a replacement for the libfuse userspace library between these two. This way, a developer can fully take advantage of the Rust type interface and runtime features when building a FUSE filesystem in Rust. @@ -107,6 +107,13 @@ fuser = "0.15" To create a new filesystem, implement the trait `fuser::Filesystem`. See the [documentation] for details or the `examples` directory for some basic examples. +### Feature Gates + +The crate uses feature gates to manage optional functionality and dependencies. Some key features include: +* **`abi-7-x`**: A set of features to select the FUSE protocol version. Recommended to select the highest version. +* **`libfuse`**: Use libfuse bindings for some very low-level operations. An older alternative to the newer Rust-native implementations. +* **`serializable`**: Enable conversion between `fuser` data structures and raw bytes, for saving to disk or transmission over a network. + ## To Do Most features of libfuse up to 3.10.3 are implemented. Feel free to contribute. See the [list of issues][issues] on GitHub and search the source files for comments containing "`TODO`" or "`FIXME`" to see what's still missing. @@ -119,10 +126,26 @@ Developed and tested on Linux. Tested under [Linux][FUSE for Linux] and [FreeBSD Licensed under [MIT License](LICENSE.md), except for those files in `examples/` that explicitly contain a different license. -## Contribution +## Contributing Fork, hack, submit pull request. Make sure to make it useful for the target audience, keep the project's philosophy and Rust coding standards in mind. For larger or essential changes, you may want to open an issue for discussion first. Also remember to update the [Changelog] if your changes are relevant to the users. +### Concepts + +A brief overview of Fuser concepts for new contributors. + +* **`Session`**: The core struct which saves configuration options. Its provides methods to start and end event handling loops. +* **`Request`** and **`Reply`**: These structures represents one FUSE operation initiated by the Kernel. The Request methods handle unpacks this message, and directs it to the filesystem. The Reply methods packege the response and pass it back to the kernel. +* **`Notification`**: This structure represents a message for the Kernel initiated by the User application (i.e., not in response to a `Request`). +* **`Filesystem`**: User application code. + +### Subdirectories + +A bried overview of repository organization for new contributors. + +* **`src/mnt/`**: Code for establishing communication with the fuse device, which is called mounting. +* **`src/ll/`**: The low-level FUSE message interface. This module contains the raw FUSE ABI definitions and is responsible for the translating between Rust-based data structures and byte-based fuse kernel messages. It is not recommended for applications to use this code directly. + [Rust]: https://rust-lang.org [Homebrew]: https://brew.sh [Changelog]: https://keepachangelog.com/en/1.0.0/ diff --git a/examples/ioctl.rs b/examples/ioctl.rs index 94f4cc51..381ea7b6 100644 --- a/examples/ioctl.rs +++ b/examples/ioctl.rs @@ -1,6 +1,6 @@ // This example requires fuse 7.11 or later. Run with: // -// cargo run --example ioctl --features abi-7-11 /tmp/foobar +// cargo run --example ioctl DIR use clap::{Arg, ArgAction, Command, crate_version}; use fuser::{ diff --git a/examples/passthrough.rs b/examples/passthrough.rs index a7277430..1248f81f 100644 --- a/examples/passthrough.rs +++ b/examples/passthrough.rs @@ -1,6 +1,6 @@ // This example requires fuse 7.40 or later. Run with: // -// cargo run --example passthrough --features abi-7-40 /tmp/foobar +// cargo run --example passthrough --features abi-7-40 DIR use clap::{Arg, ArgAction, Command, crate_version}; use fuser::{ diff --git a/examples/simple.rs b/examples/simple.rs index eb9c7bc5..70b18b58 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -5,8 +5,11 @@ use clap::{Arg, ArgAction, Command, crate_version}; use fuser::consts::FOPEN_DIRECT_IO; #[cfg(feature = "abi-7-26")] use fuser::consts::FUSE_HANDLE_KILLPRIV; -// #[cfg(feature = "abi-7-31")] -// use fuser::consts::FUSE_WRITE_KILL_PRIV; +/* +// Note: see fn write(). +#[cfg(feature = "abi-7-31")] +use fuser::consts::FUSE_WRITE_KILL_PRIV; +*/ use fuser::TimeOrNow::Now; use fuser::{ FUSE_ROOT_ID, Filesystem, KernelConfig, MountOption, ReplyAttr, ReplyCreate, ReplyData, @@ -188,6 +191,7 @@ fn system_time_from_time(secs: i64, nsecs: u32) -> SystemTime { } } +// Time as i64: see fuse_abi::fuse_attr fn time_from_system_time(system_time: &SystemTime) -> (i64, u32) { // Convert to signed 64-bit time with epoch at 0 match system_time.duration_since(UNIX_EPOCH) { @@ -1438,7 +1442,7 @@ impl Filesystem for SimpleFS { offset: i64, data: &[u8], _write_flags: u32, - #[allow(unused_variables)] flags: i32, + _flags: i32, _lock_owner: Option, reply: ReplyWrite, ) { diff --git a/src/lib.rs b/src/lib.rs index 4885f387..cb029516 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,15 +29,27 @@ pub use mnt::mount_options::MountOption; pub use notify::{Notifier, PollHandle}; #[cfg(feature = "abi-7-40")] pub use passthrough::BackingId; +pub use reply::Reply; +pub use reply::ReplyAttr; +pub use reply::ReplyBmap; +pub use reply::ReplyCreate; +pub use reply::ReplyData; +pub use reply::ReplyDirectory; +#[cfg(feature = "abi-7-21")] +pub use reply::ReplyDirectoryPlus; +pub use reply::ReplyEmpty; +pub use reply::ReplyEntry; +pub use reply::ReplyIoctl; +pub use reply::ReplyLock; +#[cfg(feature = "abi-7-24")] +pub use reply::ReplyLseek; +pub use reply::ReplyOpen; pub use reply::ReplyPoll; +pub use reply::ReplyStatfs; +pub use reply::ReplyWrite; #[cfg(target_os = "macos")] pub use reply::ReplyXTimes; pub use reply::ReplyXattr; -pub use reply::{Reply, ReplyAttr, ReplyData, ReplyEmpty, ReplyEntry, ReplyOpen}; -pub use reply::{ - ReplyBmap, ReplyCreate, ReplyDirectory, ReplyDirectoryPlus, ReplyIoctl, ReplyLock, ReplyLseek, - ReplyStatfs, ReplyWrite, -}; pub use request::Request; pub use session::{BackgroundSession, Session, SessionACL, SessionUnmounter}; #[cfg(feature = "abi-7-28")] @@ -324,11 +336,12 @@ impl KernelConfig { /// implementations are provided here to get a mountable filesystem that does /// nothing. #[allow(clippy::too_many_arguments)] +#[allow(unused_variables)] // This is the main API, so variables are named without the underscore for generality. pub trait Filesystem { /// Initialize filesystem. /// Called before any other filesystem method. /// The kernel module connection can be configured using the `KernelConfig` object - fn init(&mut self, _req: &Request<'_>, _config: &mut KernelConfig) -> Result<(), c_int> { + fn init(&mut self, req: &Request<'_>, config: &mut KernelConfig) -> Result<(), c_int> { Ok(()) } @@ -349,7 +362,7 @@ pub trait Filesystem { /// each forget. The filesystem may ignore forget calls, if the inodes don't need to /// have a limited lifetime. On unmount it is not guaranteed, that all referenced /// inodes will receive a forget message. - fn forget(&mut self, _req: &Request<'_>, _ino: u64, _nlookup: u64) {} + fn forget(&mut self, req: &Request<'_>, ino: u64, nlookup: u64) {} /// Like forget, but take multiple forget requests at once for performance. The default /// implementation will fallback to forget. @@ -360,7 +373,7 @@ pub trait Filesystem { } /// Get file attributes. - fn getattr(&mut self, _req: &Request<'_>, ino: u64, fh: Option, reply: ReplyAttr) { + fn getattr(&mut self, req: &Request<'_>, ino: u64, fh: Option, reply: ReplyAttr) { warn!("[Not Implemented] getattr(ino: {ino:#x?}, fh: {fh:#x?})"); reply.error(ENOSYS); } @@ -368,19 +381,19 @@ pub trait Filesystem { /// Set file attributes. fn setattr( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino: u64, mode: Option, uid: Option, gid: Option, size: Option, - _atime: Option, - _mtime: Option, - _ctime: Option, + atime: Option, + mtime: Option, + ctime: Option, fh: Option, - _crtime: Option, - _chgtime: Option, - _bkuptime: Option, + crtime: Option, + chgtime: Option, + bkuptime: Option, flags: Option, reply: ReplyAttr, ) { @@ -392,7 +405,7 @@ pub trait Filesystem { } /// Read symbolic link. - fn readlink(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyData) { + fn readlink(&mut self, req: &Request<'_>, ino: u64, reply: ReplyData) { warn!("[Not Implemented] readlink(ino: {ino:#x?})"); reply.error(ENOSYS); } @@ -401,7 +414,7 @@ pub trait Filesystem { /// Create a regular file, character device, block device, fifo or socket node. fn mknod( &mut self, - _req: &Request<'_>, + req: &Request<'_>, parent: u64, name: &OsStr, mode: u32, @@ -419,7 +432,7 @@ pub trait Filesystem { /// Create a directory. fn mkdir( &mut self, - _req: &Request<'_>, + req: &Request<'_>, parent: u64, name: &OsStr, mode: u32, @@ -433,13 +446,13 @@ pub trait Filesystem { } /// Remove a file. - fn unlink(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) { + fn unlink(&mut self, req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) { warn!("[Not Implemented] unlink(parent: {parent:#x?}, name: {name:?})",); reply.error(ENOSYS); } /// Remove a directory. - fn rmdir(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) { + fn rmdir(&mut self, req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) { warn!("[Not Implemented] rmdir(parent: {parent:#x?}, name: {name:?})",); reply.error(ENOSYS); } @@ -447,7 +460,7 @@ pub trait Filesystem { /// Create a symbolic link. fn symlink( &mut self, - _req: &Request<'_>, + req: &Request<'_>, parent: u64, link_name: &OsStr, target: &Path, @@ -462,7 +475,7 @@ pub trait Filesystem { /// Rename a file. fn rename( &mut self, - _req: &Request<'_>, + req: &Request<'_>, parent: u64, name: &OsStr, newparent: u64, @@ -480,7 +493,7 @@ pub trait Filesystem { /// Create a hard link. fn link( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino: u64, newparent: u64, newname: &OsStr, @@ -500,7 +513,7 @@ pub trait Filesystem { /// 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(&mut self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) { + fn open(&mut self, req: &Request<'_>, ino: u64, flags: i32, reply: ReplyOpen) { reply.opened(0, 0); } @@ -516,7 +529,7 @@ pub trait Filesystem { /// `lock_owner`: only supported with ABI >= 7.9 fn read( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino: u64, fh: u64, offset: i64, @@ -546,7 +559,7 @@ pub trait Filesystem { /// `lock_owner`: only supported with ABI >= 7.9 fn write( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino: u64, fh: u64, offset: i64, @@ -575,7 +588,7 @@ pub trait Filesystem { /// is not forced to flush pending writes. One reason to flush data, is if the /// filesystem wants to return write errors. If the filesystem supports file locking /// operations (`setlk`, `getlk`) it should remove all locks belonging to `lock_owner`. - fn flush(&mut self, _req: &Request<'_>, ino: u64, fh: u64, lock_owner: u64, reply: ReplyEmpty) { + fn flush(&mut self, req: &Request<'_>, ino: u64, fh: u64, lock_owner: u64, reply: ReplyEmpty) { warn!("[Not Implemented] flush(ino: {ino:#x?}, fh: {fh}, lock_owner: {lock_owner:?})"); reply.error(ENOSYS); } @@ -590,12 +603,12 @@ pub trait Filesystem { /// open. fn release( &mut self, - _req: &Request<'_>, - _ino: u64, - _fh: u64, - _flags: i32, - _lock_owner: Option, - _flush: bool, + req: &Request<'_>, + ino: u64, + fh: u64, + flags: i32, + lock_owner: Option, + flush: bool, reply: ReplyEmpty, ) { reply.ok(); @@ -604,7 +617,7 @@ pub trait Filesystem { /// Synchronize file contents. /// If the datasync parameter is non-zero, then only the user data should be flushed, /// not the meta data. - fn fsync(&mut self, _req: &Request<'_>, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { + fn fsync(&mut self, req: &Request<'_>, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { warn!("[Not Implemented] fsync(ino: {ino:#x?}, fh: {fh}, datasync: {datasync})"); reply.error(ENOSYS); } @@ -616,7 +629,7 @@ pub trait Filesystem { /// anything in fh, though that makes it impossible to implement standard conforming /// directory stream operations in case the contents of the directory can change /// between opendir and releasedir. - fn opendir(&mut self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) { + fn opendir(&mut self, req: &Request<'_>, ino: u64, flags: i32, reply: ReplyOpen) { reply.opened(0, 0); } @@ -627,7 +640,7 @@ pub trait Filesystem { /// didn't set any value. fn readdir( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino: u64, fh: u64, offset: i64, @@ -642,9 +655,10 @@ pub trait Filesystem { /// requested size. Send an empty buffer on end of stream. fh will contain the /// value set by the opendir method, or will be undefined if the opendir method /// didn't set any value. + #[cfg(feature = "abi-7-21")] fn readdirplus( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino: u64, fh: u64, offset: i64, @@ -658,14 +672,7 @@ pub trait Filesystem { /// For every opendir call there will be exactly one releasedir call. fh will /// contain the value set by the opendir method, or will be undefined if the /// opendir method didn't set any value. - fn releasedir( - &mut self, - _req: &Request<'_>, - _ino: u64, - _fh: u64, - _flags: i32, - reply: ReplyEmpty, - ) { + fn releasedir(&mut self, req: &Request<'_>, ino: u64, fh: u64, flags: i32, reply: ReplyEmpty) { reply.ok(); } @@ -675,7 +682,7 @@ pub trait Filesystem { /// method, or will be undefined if the opendir method didn't set any value. fn fsyncdir( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino: u64, fh: u64, datasync: bool, @@ -686,17 +693,17 @@ pub trait Filesystem { } /// Get file system statistics. - fn statfs(&mut self, _req: &Request<'_>, _ino: u64, reply: ReplyStatfs) { + fn statfs(&mut self, req: &Request<'_>, ino: u64, reply: ReplyStatfs) { reply.statfs(0, 0, 0, 0, 0, 512, 255, 0); } /// Set an extended attribute. fn setxattr( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino: u64, name: &OsStr, - _value: &[u8], + value: &[u8], flags: i32, position: u32, reply: ReplyEmpty, @@ -714,7 +721,7 @@ pub trait Filesystem { /// `reply.error(ERANGE)` if it doesn't. fn getxattr( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino: u64, name: &OsStr, size: u32, @@ -728,13 +735,13 @@ pub trait Filesystem { /// If `size` is 0, the size of the value should be sent with `reply.size()`. /// If `size` is not 0, and the value fits, send it with `reply.data()`, or /// `reply.error(ERANGE)` if it doesn't. - fn listxattr(&mut self, _req: &Request<'_>, ino: u64, size: u32, reply: ReplyXattr) { + fn listxattr(&mut self, req: &Request<'_>, ino: u64, size: u32, reply: ReplyXattr) { warn!("[Not Implemented] listxattr(ino: {ino:#x?}, size: {size})"); reply.error(ENOSYS); } /// Remove an extended attribute. - fn removexattr(&mut self, _req: &Request<'_>, ino: u64, name: &OsStr, reply: ReplyEmpty) { + fn removexattr(&mut self, req: &Request<'_>, ino: u64, name: &OsStr, reply: ReplyEmpty) { warn!("[Not Implemented] removexattr(ino: {ino:#x?}, name: {name:?})"); reply.error(ENOSYS); } @@ -743,7 +750,7 @@ pub trait Filesystem { /// This will be 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(&mut self, _req: &Request<'_>, ino: u64, mask: i32, reply: ReplyEmpty) { + fn access(&mut self, req: &Request<'_>, ino: u64, mask: i32, reply: ReplyEmpty) { warn!("[Not Implemented] access(ino: {ino:#x?}, mask: {mask})"); reply.error(ENOSYS); } @@ -760,7 +767,7 @@ pub trait Filesystem { /// 2.6.15, the `mknod()` and `open()` methods will be called instead. fn create( &mut self, - _req: &Request<'_>, + req: &Request<'_>, parent: u64, name: &OsStr, mode: u32, @@ -778,7 +785,7 @@ pub trait Filesystem { /// Test for a POSIX file lock. fn getlk( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino: u64, fh: u64, lock_owner: u64, @@ -804,7 +811,7 @@ pub trait Filesystem { /// Hence these are only interesting for network filesystems and similar. fn setlk( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino: u64, fh: u64, lock_owner: u64, @@ -825,7 +832,7 @@ pub trait Filesystem { /// 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(&mut self, _req: &Request<'_>, ino: u64, blocksize: u32, idx: u64, reply: ReplyBmap) { + fn bmap(&mut self, req: &Request<'_>, ino: u64, blocksize: u32, idx: u64, reply: ReplyBmap) { warn!("[Not Implemented] bmap(ino: {ino:#x?}, blocksize: {blocksize}, idx: {idx})",); reply.error(ENOSYS); } @@ -833,7 +840,7 @@ pub trait Filesystem { /// control device fn ioctl( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino: u64, fh: u64, flags: u32, @@ -853,7 +860,7 @@ pub trait Filesystem { /// Poll for events fn poll( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino: u64, fh: u64, ph: PollHandle, @@ -871,7 +878,7 @@ pub trait Filesystem { /// Preallocate or deallocate space to a file fn fallocate( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino: u64, fh: u64, offset: i64, @@ -887,9 +894,10 @@ pub trait Filesystem { } /// Reposition read/write file offset + #[cfg(feature = "abi-7-24")] fn lseek( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino: u64, fh: u64, offset: i64, @@ -906,7 +914,7 @@ pub trait Filesystem { /// Copy the specified range from the source inode to the destination inode fn copy_file_range( &mut self, - _req: &Request<'_>, + req: &Request<'_>, ino_in: u64, fh_in: u64, offset_in: i64, @@ -928,7 +936,7 @@ pub trait Filesystem { /// macOS only: Rename the volume. Set `fuse_init_out.flags` during init to /// `FUSE_VOL_RENAME` to enable #[cfg(target_os = "macos")] - fn setvolname(&mut self, _req: &Request<'_>, name: &OsStr, reply: ReplyEmpty) { + fn setvolname(&mut self, req: &Request<'_>, name: &OsStr, reply: ReplyEmpty) { warn!("[Not Implemented] setvolname(name: {name:?})"); reply.error(ENOSYS); } @@ -937,7 +945,7 @@ pub trait Filesystem { #[cfg(target_os = "macos")] fn exchange( &mut self, - _req: &Request<'_>, + req: &Request<'_>, parent: u64, name: &OsStr, newparent: u64, @@ -955,7 +963,7 @@ pub trait Filesystem { /// macOS only: Query extended times (`bkuptime` and `crtime`). Set `fuse_init_out.flags` /// during init to `FUSE_XTIMES` to enable #[cfg(target_os = "macos")] - fn getxtimes(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyXTimes) { + fn getxtimes(&mut self, req: &Request<'_>, ino: u64, reply: ReplyXTimes) { warn!("[Not Implemented] getxtimes(ino: {ino:#x?})"); reply.error(ENOSYS); } diff --git a/src/ll/fuse_abi.rs b/src/ll/fuse_abi.rs index f35a09af..f1628a34 100644 --- a/src/ll/fuse_abi.rs +++ b/src/ll/fuse_abi.rs @@ -113,10 +113,15 @@ pub struct fuse_kstatfs { #[repr(C)] #[derive(Debug, IntoBytes, FromBytes, KnownLayout, Immutable)] pub struct fuse_file_lock { + /// start of locked byte range pub start: u64, + /// end of locked byte range pub end: u64, // NOTE: this field is defined as u32 in fuse_kernel.h in libfuse. However, it is treated as signed + // TODO enum {F_RDLCK, F_WRLCK, F_UNLCK} + /// kind of lock (read and/or write) pub typ: i32, + /// PID of process blocking our lock pub pid: u32, } @@ -629,6 +634,7 @@ pub struct fuse_open_out { pub open_flags: u32, #[cfg(not(feature = "abi-7-40"))] pub padding: u32, + /// The `backing_id` field is used to pass a backing file descriptor to the kernel. #[cfg(feature = "abi-7-40")] pub backing_id: u32, } diff --git a/src/ll/mod.rs b/src/ll/mod.rs index 624b01c2..b7e08e24 100644 --- a/src/ll/mod.rs +++ b/src/ll/mod.rs @@ -228,6 +228,7 @@ impl Errno { #[cfg(not(target_os = "linux"))] pub const NO_XATTR: Errno = Self::ENOATTR; + /// Convert libc-style error codes into `fuser::Errno` pub fn from_i32(err: i32) -> Errno { err.try_into().ok().map_or(Errno::EIO, Errno) } diff --git a/src/ll/reply.rs b/src/ll/reply.rs index c7590f83..2bdd2f20 100644 --- a/src/ll/reply.rs +++ b/src/ll/reply.rs @@ -257,6 +257,7 @@ impl<'a> Response<'a> { Self::from_struct(&r) } + #[cfg(feature = "abi-7-24")] pub(crate) fn new_lseek(offset: i64) -> Self { let r = abi::fuse_lseek_out { offset }; Self::from_struct(&r) @@ -437,6 +438,7 @@ impl DirEntList { } } +#[cfg(feature = "abi-7-21")] #[derive(Debug)] pub struct DirEntryPlus> { #[allow(unused)] // We use `attr.ino` instead @@ -449,6 +451,7 @@ pub struct DirEntryPlus> { attr_valid: Duration, } +#[cfg(feature = "abi-7-21")] impl> DirEntryPlus { pub fn new( ino: INodeNo, @@ -472,8 +475,11 @@ impl> DirEntryPlus { } /// Data buffer used to respond to [`ReaddirPlus`] requests. +#[cfg(feature = "abi-7-21")] #[derive(Debug)] pub struct DirEntPlusList(EntListBuf); + +#[cfg(feature = "abi-7-21")] impl From for Response<'_> { fn from(l: DirEntPlusList) -> Self { assert!(l.0.buf.len() <= l.0.max_size); @@ -481,6 +487,7 @@ impl From for Response<'_> { } } +#[cfg(feature = "abi-7-21")] impl DirEntPlusList { pub(crate) fn new(max_size: usize) -> Self { Self(EntListBuf::new(max_size)) diff --git a/src/notify.rs b/src/notify.rs index e332cd36..71e137d9 100644 --- a/src/notify.rs +++ b/src/notify.rs @@ -1,8 +1,6 @@ +use std::ffi::OsStr; use std::io; -#[allow(unused)] -use std::{convert::TryInto, ffi::OsStr}; - use crate::{ channel::ChannelSender, ll::{fuse_abi::fuse_notify_code as notify_code, notify::Notification}, @@ -105,7 +103,6 @@ impl Notifier { self.send_inval(notify_code::FUSE_NOTIFY_DELETE, ¬if) } - #[allow(unused)] fn send_inval(&self, code: notify_code, notification: &Notification<'_>) -> io::Result<()> { match self.send(code, notification) { // ENOENT is harmless for an invalidation (the diff --git a/src/reply.rs b/src/reply.rs index 7e7ff8f1..50d3901e 100644 --- a/src/reply.rs +++ b/src/reply.rs @@ -6,14 +6,10 @@ //! data without cloning the data. A reply *must always* be used (by calling either `ok()` or //! `error()` exactly once). -use crate::ll::{ - self, Generation, - reply::{DirEntPlusList, DirEntryPlus}, -}; -use crate::ll::{ - INodeNo, - reply::{DirEntList, DirEntOffset, DirEntry}, -}; +use crate::ll; // too many structs to list +use crate::ll::reply::{DirEntList, DirEntOffset, DirEntry}; +#[cfg(feature = "abi-7-21")] +use crate::ll::reply::{DirEntPlusList, DirEntryPlus}; #[cfg(feature = "abi-7-40")] use crate::{consts::FOPEN_PASSTHROUGH, passthrough::BackingId}; use libc::c_int; @@ -561,7 +557,7 @@ impl ReplyDirectory { pub fn add>(&mut self, ino: u64, offset: i64, kind: FileType, name: T) -> bool { let name = name.as_ref(); self.data.push(&DirEntry::new( - INodeNo(ino), + ll::INodeNo(ino), DirEntOffset(offset), kind, name, @@ -582,12 +578,14 @@ impl ReplyDirectory { /// /// `DirectoryPlus` reply /// +#[cfg(feature = "abi-7-21")] #[derive(Debug)] pub struct ReplyDirectoryPlus { reply: ReplyRaw, buf: DirEntPlusList, } +#[cfg(feature = "abi-7-21")] impl ReplyDirectoryPlus { /// Creates a new `ReplyDirectory` with a specified buffer size. pub fn new(unique: u64, sender: S, size: usize) -> ReplyDirectoryPlus { @@ -611,8 +609,8 @@ impl ReplyDirectoryPlus { ) -> bool { let name = name.as_ref(); self.buf.push(&DirEntryPlus::new( - INodeNo(ino), - Generation(generation), + ll::INodeNo(ino), + ll::Generation(generation), DirEntOffset(offset), name, *ttl, @@ -668,11 +666,13 @@ impl ReplyXattr { /// /// Lseek Reply /// +#[cfg(feature = "abi-7-24")] #[derive(Debug)] pub struct ReplyLseek { reply: ReplyRaw, } +#[cfg(feature = "abi-7-24")] impl Reply for ReplyLseek { fn new(unique: u64, sender: S) -> ReplyLseek { ReplyLseek { @@ -681,6 +681,7 @@ impl Reply for ReplyLseek { } } +#[cfg(feature = "abi-7-24")] impl ReplyLseek { /// Reply to a request with seeked offset pub fn offset(self, offset: i64) {