diff --git a/CHANGELOG.md b/CHANGELOG.md index cdae650a..09ed1760 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # FUSE for Rust - Changelog +## 0.17 - Unreleased + +* Change integers to newtypes in various public APIs + ## 0.16.0 - 2025-09-12 * Add support for passthrough file descriptors * Change `KernelConfig` capabilities flags parameters to `u64` diff --git a/examples/notify_inval_inode.rs b/examples/notify_inval_inode.rs index d7ab155c..3b8940c6 100644 --- a/examples/notify_inval_inode.rs +++ b/examples/notify_inval_inode.rs @@ -22,8 +22,8 @@ use libc::{EACCES, EINVAL, EISDIR, ENOBUFS, ENOENT, ENOTDIR}; use clap::Parser; use fuser::{ - FUSE_ROOT_ID, FileAttr, FileType, Filesystem, MountOption, ReplyAttr, ReplyData, - ReplyDirectory, ReplyEntry, ReplyOpen, Request, consts, + FUSE_ROOT_ID, FileAttr, FileType, Filesystem, MountOption, OpenAccMode, OpenFlags, ReplyAttr, + ReplyData, ReplyDirectory, ReplyEntry, ReplyOpen, Request, consts, }; struct ClockFS<'a> { @@ -120,10 +120,10 @@ impl Filesystem for ClockFS<'_> { } } - fn open(&mut self, _req: &Request, ino: u64, flags: i32, reply: ReplyOpen) { + fn open(&mut self, _req: &Request, ino: u64, flags: OpenFlags, reply: ReplyOpen) { if ino == FUSE_ROOT_ID { reply.error(EISDIR); - } else if flags & libc::O_ACCMODE != libc::O_RDONLY { + } else if flags.acc_mode() != OpenAccMode::O_RDONLY { reply.error(EACCES); } else if ino != Self::FILE_INO { eprintln!("Got open for nonexistent inode {ino}"); diff --git a/examples/passthrough.rs b/examples/passthrough.rs index 842a9357..0228ef64 100644 --- a/examples/passthrough.rs +++ b/examples/passthrough.rs @@ -4,7 +4,7 @@ use clap::{Arg, ArgAction, Command, crate_version}; use fuser::{ - BackingId, FileAttr, FileType, Filesystem, KernelConfig, MountOption, ReplyAttr, + BackingId, FileAttr, FileType, Filesystem, KernelConfig, MountOption, OpenFlags, ReplyAttr, ReplyDirectory, ReplyEmpty, ReplyEntry, ReplyOpen, Request, consts, }; use libc::ENOENT; @@ -163,7 +163,7 @@ impl Filesystem for PassthroughFs { } } - fn open(&mut self, _req: &Request, ino: u64, _flags: i32, reply: ReplyOpen) { + fn open(&mut self, _req: &Request, ino: u64, _flags: OpenFlags, reply: ReplyOpen) { if ino != 2 { reply.error(ENOENT); return; diff --git a/examples/poll.rs b/examples/poll.rs index aebdf5b5..b3c86dd9 100644 --- a/examples/poll.rs +++ b/examples/poll.rs @@ -22,7 +22,7 @@ use std::{ use libc::{EACCES, EBADF, EBUSY, EINVAL, ENOENT, ENOTDIR}; use fuser::{ - FUSE_ROOT_ID, FileAttr, FileType, MountOption, PollHandle, Request, + FUSE_ROOT_ID, FileAttr, FileType, MountOption, OpenAccMode, OpenFlags, PollHandle, Request, consts::{FOPEN_DIRECT_IO, FOPEN_NONSEEKABLE, FUSE_POLL_SCHEDULE_NOTIFY}, }; @@ -169,14 +169,14 @@ impl fuser::Filesystem for FSelFS { reply.ok(); } - fn open(&mut self, _req: &Request, ino: u64, flags: i32, reply: fuser::ReplyOpen) { + fn open(&mut self, _req: &Request, ino: u64, flags: OpenFlags, reply: fuser::ReplyOpen) { let idx = FSelData::ino_to_idx(ino); if idx >= NUMFILES { reply.error(ENOENT); return; } - if (flags & libc::O_ACCMODE) != libc::O_RDONLY { + if flags.acc_mode() != OpenAccMode::O_RDONLY { reply.error(EACCES); return; } diff --git a/examples/simple.rs b/examples/simple.rs index eb9c7bc5..3eb50f0d 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -9,9 +9,9 @@ use fuser::consts::FUSE_HANDLE_KILLPRIV; // use fuser::consts::FUSE_WRITE_KILL_PRIV; use fuser::TimeOrNow::Now; use fuser::{ - FUSE_ROOT_ID, Filesystem, KernelConfig, MountOption, ReplyAttr, ReplyCreate, ReplyData, - ReplyDirectory, ReplyEmpty, ReplyEntry, ReplyOpen, ReplyStatfs, ReplyWrite, ReplyXattr, - Request, TimeOrNow, + FUSE_ROOT_ID, Filesystem, KernelConfig, MountOption, OpenAccMode, OpenFlags, ReplyAttr, + ReplyCreate, ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry, ReplyOpen, ReplyStatfs, + ReplyWrite, ReplyXattr, Request, TimeOrNow, }; #[cfg(feature = "abi-7-26")] use log::info; @@ -1347,29 +1347,24 @@ impl Filesystem for SimpleFS { } } - fn open(&mut self, req: &Request, inode: u64, flags: i32, reply: ReplyOpen) { + fn open(&mut self, req: &Request, inode: u64, flags: OpenFlags, reply: ReplyOpen) { debug!("open() called for {inode:?}"); - let (access_mask, read, write) = match flags & libc::O_ACCMODE { - libc::O_RDONLY => { + let (access_mask, read, write) = match flags.acc_mode() { + OpenAccMode::O_RDONLY => { // Behavior is undefined, but most filesystems return EACCES - if flags & libc::O_TRUNC != 0 { + if flags.0 & libc::O_TRUNC != 0 { reply.error(libc::EACCES); return; } - if flags & FMODE_EXEC != 0 { + if flags.0 & FMODE_EXEC != 0 { // Open is from internal exec syscall (libc::X_OK, true, false) } else { (libc::R_OK, true, false) } } - libc::O_WRONLY => (libc::W_OK, false, true), - libc::O_RDWR => (libc::R_OK | libc::W_OK, true, true), - // Exactly one access mode flag must be specified - _ => { - reply.error(libc::EINVAL); - return; - } + OpenAccMode::O_WRONLY => (libc::W_OK, false, true), + OpenAccMode::O_RDWR => (libc::R_OK | libc::W_OK, true, true), }; match self.get_inode(inode) { diff --git a/src/lib.rs b/src/lib.rs index 12128904..3a3ad1ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,8 @@ use crate::ll::fuse_abi::InitFlags; use crate::ll::fuse_abi::consts::*; pub use crate::ll::{TimeOrNow, fuse_abi::consts}; use crate::mnt::mount_options::check_option_conflicts; +pub use crate::open_flags::OpenAccMode; +pub use crate::open_flags::OpenFlags; use crate::session::MAX_WRITE_SIZE; pub use ll::fuse_abi::fuse_forget_one; pub use mnt::mount_options::MountOption; @@ -58,6 +60,7 @@ pub mod experimental; mod ll; mod mnt; mod notify; +mod open_flags; #[cfg(feature = "abi-7-40")] mod passthrough; mod reply; @@ -515,7 +518,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: OpenFlags, reply: ReplyOpen) { reply.opened(0, 0); } diff --git a/src/ll/request.rs b/src/ll/request.rs index c1c0907f..e6ad4dd6 100644 --- a/src/ll/request.rs +++ b/src/ll/request.rs @@ -266,6 +266,7 @@ mod op { use super::{ FileHandle, INodeNo, Lock, LockOwner, Operation, RequestId, abi::consts::*, abi::*, }; + use crate::OpenFlags; use std::{ convert::TryInto, ffi::OsStr, @@ -642,8 +643,8 @@ mod op { } impl_request!(Open<'_>); impl Open<'_> { - pub(crate) fn flags(&self) -> i32 { - self.arg.flags + pub(crate) fn flags(&self) -> OpenFlags { + OpenFlags(self.arg.flags) } } diff --git a/src/open_flags.rs b/src/open_flags.rs new file mode 100644 index 00000000..62f353f5 --- /dev/null +++ b/src/open_flags.rs @@ -0,0 +1,47 @@ +use std::fmt; +use std::fmt::{Formatter, LowerHex, UpperHex}; + +/// How the file should be opened: read-only, write-only, or read-write. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[repr(i32)] +#[allow(non_camel_case_types)] +pub enum OpenAccMode { + /// Open file for reading only. + O_RDONLY = libc::O_RDONLY, + /// Open file for writing only. + O_WRONLY = libc::O_WRONLY, + /// Open file for reading and writing. + O_RDWR = libc::O_RDWR, +} + +/// Open flags as passed to open operation. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct OpenFlags(pub i32); + +impl LowerHex for OpenFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + LowerHex::fmt(&self.0, f) + } +} + +impl UpperHex for OpenFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + UpperHex::fmt(&self.0, f) + } +} + +impl OpenFlags { + /// File access mode. + pub fn acc_mode(self) -> OpenAccMode { + match self.0 & libc::O_ACCMODE { + libc::O_RDONLY => OpenAccMode::O_RDONLY, + libc::O_WRONLY => OpenAccMode::O_WRONLY, + libc::O_RDWR => OpenAccMode::O_RDWR, + _ => { + // Impossible combination of flags. + // Do not panic because the field is public. + OpenAccMode::O_RDONLY + } + } + } +}