diff --git a/fuse-sys/Cargo.toml b/fuse-sys/Cargo.toml index e434d18..2f89d25 100644 --- a/fuse-sys/Cargo.toml +++ b/fuse-sys/Cargo.toml @@ -16,3 +16,16 @@ links = "fuse" pkg-config = "0.3" [dependencies] + +[features] +abi-7-9 = [] +abi-7-10 = ["abi-7-9"] +abi-7-11 = ["abi-7-10"] +abi-7-12 = ["abi-7-11"] +abi-7-13 = ["abi-7-12"] +abi-7-14 = ["abi-7-13"] +abi-7-15 = ["abi-7-14"] +abi-7-16 = ["abi-7-15"] +abi-7-17 = ["abi-7-16"] +abi-7-18 = ["abi-7-17"] +abi-7-19 = ["abi-7-18"] diff --git a/fuse-sys/src/abi.rs b/fuse-sys/src/abi.rs index a336684..75e555a 100644 --- a/fuse-sys/src/abi.rs +++ b/fuse-sys/src/abi.rs @@ -18,10 +18,32 @@ //! //! Items without a version annotation are valid with ABI 7.8 and later -// TODO: We currently target ABI 7.8, which is very conservative and missing many newer -// features. We still need to figure out a way to support different ABI version without hazzle. pub const FUSE_KERNEL_VERSION: u32 = 7; + +#[cfg(not(feature = "abi-7-9"))] pub const FUSE_KERNEL_MINOR_VERSION: u32 = 8; +#[cfg(all(feature = "abi-7-9", not(feature = "abi-7-10")))] +pub const FUSE_KERNEL_MINOR_VERSION: u32 = 9; +#[cfg(all(feature = "abi-7-10", not(feature = "abi-7-11")))] +pub const FUSE_KERNEL_MINOR_VERSION: u32 = 10; +#[cfg(all(feature = "abi-7-11", not(feature = "abi-7-12")))] +pub const FUSE_KERNEL_MINOR_VERSION: u32 = 11; +#[cfg(all(feature = "abi-7-12", not(feature = "abi-7-13")))] +pub const FUSE_KERNEL_MINOR_VERSION: u32 = 12; +#[cfg(all(feature = "abi-7-13", not(feature = "abi-7-14")))] +pub const FUSE_KERNEL_MINOR_VERSION: u32 = 13; +#[cfg(all(feature = "abi-7-14", not(feature = "abi-7-15")))] +pub const FUSE_KERNEL_MINOR_VERSION: u32 = 14; +#[cfg(all(feature = "abi-7-15", not(feature = "abi-7-16")))] +pub const FUSE_KERNEL_MINOR_VERSION: u32 = 15; +#[cfg(all(feature = "abi-7-16", not(feature = "abi-7-17")))] +pub const FUSE_KERNEL_MINOR_VERSION: u32 = 16; +#[cfg(all(feature = "abi-7-17", not(feature = "abi-7-18")))] +pub const FUSE_KERNEL_MINOR_VERSION: u32 = 17; +#[cfg(all(feature = "abi-7-18", not(feature = "abi-7-19")))] +pub const FUSE_KERNEL_MINOR_VERSION: u32 = 18; +#[cfg(feature = "abi-7-19")] +pub const FUSE_KERNEL_MINOR_VERSION: u32 = 19; pub const FUSE_ROOT_ID: u64 = 1; @@ -48,6 +70,10 @@ pub struct fuse_attr { pub rdev: u32, #[cfg(target_os = "macos")] pub flags: u32, // see chflags(2) + #[cfg(feature = "abi-7-9")] + pub blksize: u32, + #[cfg(feature = "abi-7-9")] + pub padding: u32, } #[repr(C)] @@ -83,6 +109,13 @@ pub mod consts { pub const FATTR_ATIME: u32 = 1 << 4; pub const FATTR_MTIME: u32 = 1 << 5; pub const FATTR_FH: u32 = 1 << 6; + #[cfg(feature = "abi-7-9")] + pub const FATTR_ATIME_NOW: u32 = 1 << 7; + #[cfg(feature = "abi-7-9")] + pub const FATTR_MTIME_NOW: u32 = 1 << 8; + #[cfg(feature = "abi-7-9")] + pub const FATTR_LOCKOWNER: u32 = 1 << 9; + #[cfg(target_os = "macos")] pub const FATTR_CRTIME: u32 = 1 << 28; #[cfg(target_os = "macos")] @@ -95,14 +128,45 @@ pub mod consts { // Flags returned by the open request pub const FOPEN_DIRECT_IO: u32 = 1 << 0; // bypass page cache for this open file pub const FOPEN_KEEP_CACHE: u32 = 1 << 1; // don't invalidate the data cache on open + #[cfg(feature = "abi-7-10")] + pub const FOPEN_NONSEEKABLE: u32 = 1 << 2; // the file is not seekable + #[cfg(target_os = "macos")] pub const FOPEN_PURGE_ATTR: u32 = 1 << 30; #[cfg(target_os = "macos")] pub const FOPEN_PURGE_UBC: u32 = 1 << 31; // Init request/reply flags - pub const FUSE_ASYNC_READ: u32 = 1 << 0; - pub const FUSE_POSIX_LOCKS: u32 = 1 << 1; + pub const FUSE_ASYNC_READ: u32 = 1 << 0; // asynchronous read requests + pub const FUSE_POSIX_LOCKS: u32 = 1 << 1; // remote locking for POSIX file locks + #[cfg(feature = "abi-7-9")] + pub const FUSE_FILE_OPS: u32 = 1 << 2; // kernel sends file handle for fstat, etc... + #[cfg(feature = "abi-7-9")] + pub const FUSE_ATOMIC_O_TRUNC: u32 = 1 << 3; // handles the O_TRUNC open flag in the filesystem + #[cfg(feature = "abi-7-10")] + pub const FUSE_EXPORT_SUPPORT: u32 = 1 << 4; // filesystem handles lookups of "." and ".." + #[cfg(feature = "abi-7-9")] + pub const FUSE_BIG_WRITES: u32 = 1 << 5; // filesystem can handle write size larger than 4kB + #[cfg(feature = "abi-7-12")] + pub const FUSE_DONT_MASK: u32 = 1 << 6; // don't apply umask to file mode on create operations + + #[cfg(all(feature = "abi-7-14", not(target_os = "macos")))] + pub const FUSE_SPLICE_WRITE: u32 = 1 << 7; // kernel supports splice write on the device + #[cfg(all(feature = "abi-7-14", not(target_os = "macos")))] + pub const FUSE_SPLICE_MOVE: u32 = 1 << 8; // kernel supports splice move on the device + #[cfg(not(target_os = "macos"))] + + #[cfg(feature = "abi-7-14")] + pub const FUSE_SPLICE_READ: u32 = 1 << 9; // kernel supports splice read on the device + #[cfg(feature = "abi-7-17")] + pub const FUSE_FLOCK_LOCKS: u32 = 1 << 10; // remote locking for BSD style file locks + #[cfg(feature = "abi-7-18")] + pub const FUSE_HAS_IOCTL_DIR: u32 = 1 << 11; // kernel supports ioctl on directories + + #[cfg(target_os = "macos")] + pub const FUSE_ALLOCATE: u32 = 1 << 27; + #[cfg(target_os = "macos")] + pub const FUSE_EXCHANGE_DATA: u32 = 1 << 28; #[cfg(target_os = "macos")] pub const FUSE_CASE_INSENSITIVE: u32 = 1 << 29; #[cfg(target_os = "macos")] @@ -110,8 +174,50 @@ pub mod consts { #[cfg(target_os = "macos")] pub const FUSE_XTIMES: u32 = 1 << 31; + // CUSE init request/reply flags + #[cfg(feature = "abi-7-12")] + pub const CUSE_UNRESTRICTED_IOCTL: u32 = 1 << 0; // use unrestricted ioctl + // Release flags pub const FUSE_RELEASE_FLUSH: u32 = 1 << 0; + #[cfg(feature = "abi-7-17")] + pub const FUSE_RELEASE_FLOCK_UNLOCK: u32= 1 << 1; + + // Getattr flags + #[cfg(feature = "abi-7-9")] + pub const FUSE_GETATTR_FH: u32 = 1 << 0; + + // Lock flags + #[cfg(feature = "abi-7-9")] + pub const FUSE_LK_FLOCK: u32 = 1 << 0; + + // Write flags + #[cfg(feature = "abi-7-9")] + pub const FUSE_WRITE_CACHE: u32 = 1 << 0; // delayed write from page cache, file handle is guessed + #[cfg(feature = "abi-7-9")] + pub const FUSE_WRITE_LOCKOWNER: u32 = 1 << 1; // lock_owner field is valid + + // Read flags + #[cfg(feature = "abi-7-9")] + pub const FUSE_READ_LOCKOWNER: u32 = 1 << 1; + + // IOCTL flags + #[cfg(feature = "abi-7-11")] + pub const FUSE_IOCTL_COMPAT: u32 = 1 << 0; // 32bit compat ioctl on 64bit machine + #[cfg(feature = "abi-7-11")] + pub const FUSE_IOCTL_UNRESTRICTED: u32 = 1 << 1; // not restricted to well-formed ioctls, retry allowed + #[cfg(feature = "abi-7-11")] + pub const FUSE_IOCTL_RETRY: u32 = 1 << 2; // retry with new iovecs + #[cfg(feature = "abi-7-16")] + pub const FUSE_IOCTL_32BIT: u32 = 1 << 3; // 32bit ioctl + #[cfg(feature = "abi-7-18")] + pub const FUSE_IOCTL_DIR: u32 = 1 << 4; // is a directory + #[cfg(feature = "abi-7-11")] + pub const FUSE_IOCTL_MAX_IOV: u32 = 256; // maximum of in_iovecs + out_iovecs + + // Poll flags + #[cfg(feature = "abi-7-9")] + pub const FUSE_POLL_SCHEDULE_NOTIFY: u32= 1 << 0; // request poll notify // The read buffer is required to be at least 8k, but may be much larger pub const FUSE_MIN_READ_BUFFER: usize = 8192; @@ -157,15 +263,29 @@ pub enum fuse_opcode { FUSE_INTERRUPT = 36, FUSE_BMAP = 37, FUSE_DESTROY = 38, + #[cfg(feature = "abi-7-11")] + FUSE_IOCTL = 39, + #[cfg(feature = "abi-7-11")] + FUSE_POLL = 40, + #[cfg(feature = "abi-7-15")] + FUSE_NOTIFY_REPLY = 41, + #[cfg(feature = "abi-7-16")] + FUSE_BATCH_FORGET = 42, + #[cfg(feature = "abi-7-19")] + FUSE_FALLOCATE = 43, + #[cfg(target_os = "macos")] FUSE_SETVOLNAME = 61, #[cfg(target_os = "macos")] FUSE_GETXTIMES = 62, #[cfg(target_os = "macos")] FUSE_EXCHANGE = 63, + + #[cfg(feature = "abi-7-12")] + CUSE_INIT = 4096, } -// FIXME: Hopefully Rust will once have a more convenient way of converting primitive to enum +// FIXME: Replace with `impl From for fuse_opcode` or `impl TryFrom for fuse_opcode` impl fuse_opcode { pub fn from_u32(n: u32) -> Option { match n { @@ -205,12 +325,69 @@ impl fuse_opcode { 36 => Some(fuse_opcode::FUSE_INTERRUPT), 37 => Some(fuse_opcode::FUSE_BMAP), 38 => Some(fuse_opcode::FUSE_DESTROY), + #[cfg(feature = "abi-7-11")] + 39 => Some(fuse_opcode::FUSE_IOCTL), + #[cfg(feature = "abi-7-11")] + 40 => Some(fuse_opcode::FUSE_POLL), + #[cfg(feature = "abi-7-15")] + 41 => Some(fuse_opcode::FUSE_NOTIFY_REPLY), + #[cfg(feature = "abi-7-16")] + 42 => Some(fuse_opcode::FUSE_BATCH_FORGET), + #[cfg(feature = "abi-7-19")] + 43 => Some(fuse_opcode::FUSE_FALLOCATE), + #[cfg(target_os = "macos")] 61 => Some(fuse_opcode::FUSE_SETVOLNAME), #[cfg(target_os = "macos")] 62 => Some(fuse_opcode::FUSE_GETXTIMES), #[cfg(target_os = "macos")] 63 => Some(fuse_opcode::FUSE_EXCHANGE), + + #[cfg(feature = "abi-7-12")] + 4096 => Some(fuse_opcode::CUSE_INIT), + + _ => None, + } + } +} + +#[cfg(feature = "abi-7-11")] +#[repr(C)] +#[derive(Debug)] +#[allow(non_camel_case_types)] +pub enum fuse_notify_code { + #[cfg(feature = "abi-7-11")] + FUSE_POLL = 1, + #[cfg(feature = "abi-7-12")] + FUSE_NOTIFY_INVAL_INODE = 2, + #[cfg(feature = "abi-7-12")] + FUSE_NOTIFY_INVAL_ENTRY = 3, + #[cfg(feature = "abi-7-15")] + FUSE_NOTIFY_STORE = 4, + #[cfg(feature = "abi-7-15")] + FUSE_NOTIFY_RETRIEVE = 5, + #[cfg(feature = "abi-7-18")] + FUSE_NOTIFY_DELETE = 6, +} + +// FIXME: Replace with `impl From for fuse_notify_code` or `impl TryFrom for fuse_notify_code` +#[cfg(feature = "abi-7-11")] +impl fuse_notify_code { + pub fn from_u32 (n: u32) -> Option { + match n { + #[cfg(feature = "abi-7-11")] + 1 => Some(fuse_notify_code::FUSE_POLL), + #[cfg(feature = "abi-7-12")] + 2 => Some(fuse_notify_code::FUSE_NOTIFY_INVAL_INODE), + #[cfg(feature = "abi-7-12")] + 3 => Some(fuse_notify_code::FUSE_NOTIFY_INVAL_ENTRY), + #[cfg(feature = "abi-7-15")] + 4 => Some(fuse_notify_code::FUSE_NOTIFY_STORE), + #[cfg(feature = "abi-7-15")] + 5 => Some(fuse_notify_code::FUSE_NOTIFY_RETRIEVE), + #[cfg(feature = "abi-7-18")] + 6 => Some(fuse_notify_code::FUSE_NOTIFY_DELETE), + _ => None, } } @@ -234,6 +411,31 @@ pub struct fuse_forget_in { pub nlookup: u64, } +#[cfg(feature = "abi-7-16")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_forget_one { + pub nodeid: u64, + pub nlookup: u64, +} + +#[cfg(feature = "abi-7-16")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_batch_forget_in { + pub count: u32, + pub dummy: u32, +} + +#[cfg(feature = "abi-7-9")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_getattr_in { + pub getattr_flags: u32, + pub dummy: u32, + pub fh: u64, +} + #[repr(C)] #[derive(Debug)] pub struct fuse_attr_out { @@ -258,13 +460,20 @@ pub struct fuse_getxtimes_out { pub struct fuse_mknod_in { pub mode: u32, pub rdev: u32, + #[cfg(feature = "abi-7-12")] + pub umask: u32, + #[cfg(feature = "abi-7-12")] + pub padding: u32, } #[repr(C)] #[derive(Debug)] pub struct fuse_mkdir_in { pub mode: u32, + #[cfg(not(feature = "abi-7-12"))] pub padding: u32, + #[cfg(feature = "abi-7-12")] + pub umask: u32, } #[repr(C)] @@ -295,7 +504,10 @@ pub struct fuse_setattr_in { pub padding: u32, pub fh: u64, pub size: u64, + #[cfg(not(feature = "abi-7-9"))] pub unused1: u64, + #[cfg(feature = "abi-7-9")] + pub lock_owner: u64, pub atime: i64, pub mtime: i64, pub unused2: u64, @@ -326,8 +538,19 @@ pub struct fuse_setattr_in { #[repr(C)] #[derive(Debug)] pub struct fuse_open_in { + pub flags: u32, + pub unused: u32, +} + +#[repr(C)] +#[derive(Debug)] +pub struct fuse_create_in { pub flags: u32, pub mode: u32, + #[cfg(feature = "abi-7-12")] + pub umask: u32, + #[cfg(feature = "abi-7-12")] + pub padding: u32, } #[repr(C)] @@ -362,6 +585,13 @@ pub struct fuse_read_in { pub fh: u64, pub offset: i64, pub size: u32, + #[cfg(feature = "abi-7-9")] + pub read_flags: u32, + #[cfg(feature = "abi-7-9")] + pub lock_owner: u64, + #[cfg(feature = "abi-7-9")] + pub flags: u32, + #[cfg(feature = "abi-7-9")] pub padding: u32, } @@ -372,6 +602,12 @@ pub struct fuse_write_in { pub offset: i64, pub size: u32, pub write_flags: u32, + #[cfg(feature = "abi-7-9")] + pub lock_owner: u64, + #[cfg(feature = "abi-7-9")] + pub flags: u32, + #[cfg(feature = "abi-7-9")] + pub padding: u32, } #[repr(C)] @@ -430,6 +666,10 @@ pub struct fuse_lk_in { pub fh: u64, pub owner: u64, pub lk: fuse_file_lock, + #[cfg(feature = "abi-7-9")] + pub lk_flags: u32, + #[cfg(feature = "abi-7-9")] + pub padding: u32, } #[repr(C)] @@ -461,8 +701,38 @@ pub struct fuse_init_out { pub minor: u32, pub max_readahead: u32, pub flags: u32, + #[cfg(not(feature = "abi-7-13"))] + pub unused: u32, + #[cfg(feature = "abi-7-13")] + pub max_background: u16, + #[cfg(feature = "abi-7-13")] + pub congestion_threshold: u16, + pub max_write: u32, +} + +#[cfg(feature = "abi-7-12")] +#[repr(C)] +#[derive(Debug)] +pub struct cuse_init_in { + pub major: u32, + pub minor: u32, + pub unused: u32, + pub flags: u32, +} + +#[cfg(feature = "abi-7-12")] +#[repr(C)] +#[derive(Debug)] +pub struct cuse_init_out { + pub major: u32, + pub minor: u32, pub unused: u32, + pub flags: u32, + pub max_read: u32, pub max_write: u32, + pub dev_major: u32, // chardev major + pub dev_minor: u32, // chardev minor + pub spare: [u32; 10], } #[repr(C)] @@ -485,6 +755,72 @@ pub struct fuse_bmap_out { pub block: u64, } +#[cfg(feature = "abi-7-11")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_ioctl_in { + pub fh: u64, + pub flags: u32, + pub cmd: u32, + pub arg: u64, + pub in_size: u32, + pub out_size: u32, +} + +#[cfg(feature = "abi-7-16")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_ioctl_iovec { + pub base: u64, + pub len: u64, +} + +#[cfg(feature = "abi-7-11")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_ioctl_out { + pub result: i32, + pub flags: u32, + pub in_iovs: u32, + pub out_iovs: u32, +} + +#[cfg(feature = "abi-7-11")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_poll_in { + pub fh: u64, + pub kh: u64, + pub flags: u32, + pub padding: u32, +} + +#[cfg(feature = "abi-7-11")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_poll_out { + pub revents: u32, + pub padding: u32, +} + +#[cfg(feature = "abi-7-11")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_notify_poll_wakeup_out { + pub kh: u64, +} + +#[cfg(feature = "abi-7-19")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_fallocate_in { + fh: u64, + offset: u64, + length: u64, + mode: u32, + padding: u32, +} + #[repr(C)] #[derive(Debug)] pub struct fuse_in_header { @@ -515,3 +851,64 @@ pub struct fuse_dirent { pub typ: u32, // followed by name of namelen bytes } + +#[cfg(feature = "abi-7-12")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_notify_inval_inode_out { + pub ino: u64, + pub off: i64, + pub len: i64, +} + +#[cfg(feature = "abi-7-12")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_notify_inval_entry_out { + pub parent: u64, + pub namelen: u32, + pub padding: u32, +} + +#[cfg(feature = "abi-7-18")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_notify_delete_out { + parent: u64, + child: u64, + namelen: u32, + padding: u32, +} + +#[cfg(feature = "abi-7-15")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_notify_store_out { + pub nodeid: u64, + pub offset: u64, + pub size: u32, + pub padding: u32, +} + +#[cfg(feature = "abi-7-15")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_notify_retrieve_out { + pub notify_unique: u64, + pub nodeid: u64, + pub offset: u64, + pub size: u32, + pub padding: u32, +} + +#[cfg(feature = "abi-7-15")] +#[repr(C)] +#[derive(Debug)] +pub struct fuse_notify_retrieve_in { // matches the size of fuse_write_in + pub dummy1: u64, + pub offset: u64, + pub size: u32, + pub dummy2: u32, + pub dummy3: u64, + pub dummy4: u64, +} diff --git a/src/request.rs b/src/request.rs index e2c5f00..3fa58ac 100644 --- a/src/request.rs +++ b/src/request.rs @@ -19,11 +19,13 @@ use Filesystem; /// We generally support async reads #[cfg(not(target_os = "macos"))] const INIT_FLAGS: u32 = FUSE_ASYNC_READ; +// TODO: Add FUSE_EXPORT_SUPPORT and FUSE_BIG_WRITES (requires ABI 7.10) /// On macOS, we additionally support case insensitiveness, volume renames and xtimes /// TODO: we should eventually let the filesystem implementation decide which flags to set #[cfg(target_os = "macos")] const INIT_FLAGS: u32 = FUSE_ASYNC_READ | FUSE_CASE_INSENSITIVE | FUSE_VOL_RENAME | FUSE_XTIMES; +// TODO: Add FUSE_EXPORT_SUPPORT and FUSE_BIG_WRITES (requires ABI 7.10) /// Create a new request from the given buffer pub fn request<'a>(ch: ChannelSender, buffer: &'a [u8]) -> Option> { @@ -360,7 +362,7 @@ impl<'a> Request<'a> { se.filesystem.access(self, self.header.nodeid, arg.mask, self.reply()); } FUSE_CREATE => { - let arg: &fuse_open_in = data.fetch(); + let arg: &fuse_create_in = data.fetch(); let name = data.fetch_str(); debug!("CREATE({}) parent {:#018x}, name {:?}, mode {:#05o}, flags {:#x}", self.header.unique, self.header.nodeid, name, arg.mode, arg.flags); se.filesystem.create(self, self.header.nodeid, &name, arg.mode, arg.flags, self.reply());