From 6dead834c3867e4ed1625ca1cfa080ce5e6deef0 Mon Sep 17 00:00:00 2001 From: Alessandro Passaro Date: Tue, 7 Nov 2023 15:21:02 +0000 Subject: [PATCH] Update vendored fuser to 82bd70c (v0.14.0) (#599) * Update vendored fuser to 82bd70c (v0.14.0) Signed-off-by: Alessandro Passaro * Update fuser dependency to v0.14.0 Signed-off-by: Alessandro Passaro --------- Signed-off-by: Alessandro Passaro --- Cargo.lock | 138 +++----- mountpoint-s3/Cargo.toml | 2 +- vendor/fuser/.dockerignore | 1 + vendor/fuser/.github/workflows/ci.yml | 2 - vendor/fuser/CHANGELOG.md | 5 + vendor/fuser/Cargo.toml | 27 +- vendor/fuser/README.md | 4 + vendor/fuser/examples/hello.rs | 10 +- vendor/fuser/examples/notify_inval_entry.rs | 182 +++++++++++ vendor/fuser/examples/notify_inval_inode.rs | 226 +++++++++++++ vendor/fuser/examples/poll.rs | 344 ++++++++++++++++++++ vendor/fuser/examples/poll_client.rs | 58 ++++ vendor/fuser/examples/simple.rs | 39 ++- vendor/fuser/mount_tests.Dockerfile | 4 +- vendor/fuser/pjdfs.Dockerfile | 7 +- vendor/fuser/rust-toolchain | 1 + vendor/fuser/src/lib.rs | 25 ++ vendor/fuser/src/ll/argument.rs | 8 +- vendor/fuser/src/ll/fuse_abi.rs | 90 ++--- vendor/fuser/src/ll/mod.rs | 11 + vendor/fuser/src/ll/notify.rs | 213 ++++++++++++ vendor/fuser/src/ll/reply.rs | 18 +- vendor/fuser/src/ll/request.rs | 20 +- vendor/fuser/src/notify.rs | 87 +++++ vendor/fuser/src/reply.rs | 31 ++ vendor/fuser/src/request.rs | 13 +- vendor/fuser/src/session.rs | 23 ++ vendor/fuser/xfstests.Dockerfile | 4 +- 28 files changed, 1404 insertions(+), 189 deletions(-) create mode 100644 vendor/fuser/examples/notify_inval_entry.rs create mode 100644 vendor/fuser/examples/notify_inval_inode.rs create mode 100644 vendor/fuser/examples/poll.rs create mode 100644 vendor/fuser/examples/poll_client.rs create mode 100644 vendor/fuser/rust-toolchain create mode 100644 vendor/fuser/src/ll/notify.rs create mode 100644 vendor/fuser/src/notify.rs diff --git a/Cargo.lock b/Cargo.lock index b917a27d0..339aaa269 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,16 +54,15 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.3.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] @@ -93,9 +92,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -171,17 +170,6 @@ dependencies = [ "syn 2.0.28", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "auto_impl" version = "1.1.0" @@ -843,49 +831,31 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.25" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" -dependencies = [ - "atty", - "bitflags 1.3.2", - "clap_lex 0.2.4", - "indexmap 1.9.3", - "once_cell", - "strsim", - "termcolor", - "textwrap", -] - -[[package]] -name = "clap" -version = "4.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bba77a07e4489fb41bd90e8d4201c3eb246b3c2c9ea2ba0bddd6c1d1df87db7d" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.3.9" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9b4a88bb4bc35d3d6f65a21b0f0bafe9c894fa00978de242c555ec28bea1c0" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" dependencies = [ "anstream", "anstyle", - "bitflags 1.3.2", - "clap_lex 0.5.0", + "clap_lex", "strsim", ] [[package]] name = "clap_derive" -version = "4.3.2" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2 1.0.63", @@ -895,18 +865,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "clap_lex" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "cmake" @@ -1004,7 +965,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.3.9", + "clap", "criterion-plot", "is-terminal", "itertools", @@ -1100,7 +1061,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a011bbe2c35ce9c1f143b7af6f94f29a167beb4cd1d29e6740ce836f723120e" dependencies = [ - "nix", + "nix 0.26.2", "windows-sys 0.48.0", ] @@ -1148,12 +1109,12 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "env_logger" -version = "0.9.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ - "atty", "humantime", + "is-terminal", "log", "regex", "termcolor", @@ -1260,14 +1221,15 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "fuser" -version = "0.13.0" +version = "0.14.0" dependencies = [ "bincode", - "clap 3.2.25", + "clap", "env_logger", "libc", "log", "memchr", + "nix 0.27.1", "page_size", "pkg-config", "serde", @@ -1508,15 +1470,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.1" @@ -1691,7 +1644,7 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi", "libc", "windows-sys 0.48.0", ] @@ -1702,7 +1655,7 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi", "io-lifetimes", "rustix 0.37.20", "windows-sys 0.48.0", @@ -1956,7 +1909,7 @@ dependencies = [ "bincode", "built", "bytes", - "clap 4.3.9", + "clap", "const_format", "crc32c", "ctor", @@ -1972,7 +1925,7 @@ dependencies = [ "metrics", "mountpoint-s3-client", "mountpoint-s3-crt", - "nix", + "nix 0.26.2", "once_cell", "predicates 2.1.5", "procfs", @@ -2013,7 +1966,7 @@ dependencies = [ "base64ct", "built", "bytes", - "clap 4.3.9", + "clap", "const_format", "ctor", "futures", @@ -2091,6 +2044,17 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.0", + "cfg-if", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -2143,7 +2107,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi", "libc", ] @@ -2183,12 +2147,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" -[[package]] -name = "os_str_bytes" -version = "6.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" - [[package]] name = "outref" version = "0.5.1" @@ -2209,9 +2167,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "page_size" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b7663cbd190cfd818d08efa8497f6cd383076688c49a391ef7c0d03cd12b561" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" dependencies = [ "libc", "winapi", @@ -3082,12 +3040,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - [[package]] name = "thiserror" version = "1.0.40" @@ -3807,9 +3759,9 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.6.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332f188cc1bcf1fe1064b8c58d150f497e697f49774aa846f2dc949d9a25f236" +checksum = "7a7af71d8643341260a65f89fa60c0eeaa907f34544d8f6d9b0df72f069b5e74" dependencies = [ "byteorder", "zerocopy-derive", @@ -3817,13 +3769,13 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.3.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6505e6815af7de1746a08f69c69606bb45695a17149517680f3b2149713b19a3" +checksum = "9731702e2f0617ad526794ae28fbc6f6ca8849b5ba729666c2a5bc4b6ddee2cd" dependencies = [ "proc-macro2 1.0.63", "quote 1.0.29", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] diff --git a/mountpoint-s3/Cargo.toml b/mountpoint-s3/Cargo.toml index b8b8c8722..fe3199c55 100644 --- a/mountpoint-s3/Cargo.toml +++ b/mountpoint-s3/Cargo.toml @@ -6,7 +6,7 @@ license = "Apache-2.0" publish = false [dependencies] -fuser = { path = "../vendor/fuser", version = "0.13.0", features = ["abi-7-28"] } +fuser = { path = "../vendor/fuser", version = "0.14.0", features = ["abi-7-28"] } mountpoint-s3-client = { path = "../mountpoint-s3-client", version = "0.4.0" } mountpoint-s3-crt = { path = "../mountpoint-s3-crt", version = "0.3.0" } diff --git a/vendor/fuser/.dockerignore b/vendor/fuser/.dockerignore index c82a4a8b5..49b03a586 100644 --- a/vendor/fuser/.dockerignore +++ b/vendor/fuser/.dockerignore @@ -2,6 +2,7 @@ !src/ !examples/ !./Cargo.* +!./rust-toolchain !./build.rs !./pjdfs.sh !./xfstests.sh diff --git a/vendor/fuser/.github/workflows/ci.yml b/vendor/fuser/.github/workflows/ci.yml index c2e86b122..56e985b94 100644 --- a/vendor/fuser/.github/workflows/ci.yml +++ b/vendor/fuser/.github/workflows/ci.yml @@ -20,7 +20,6 @@ jobs: - uses: actions-rs/toolchain@v1 with: - toolchain: 1.67 target: x86_64-unknown-linux-musl default: true @@ -46,7 +45,6 @@ jobs: - uses: actions-rs/toolchain@v1 with: - toolchain: 1.67 components: rustfmt, clippy default: true diff --git a/vendor/fuser/CHANGELOG.md b/vendor/fuser/CHANGELOG.md index e5f87809c..eb6d569be 100644 --- a/vendor/fuser/CHANGELOG.md +++ b/vendor/fuser/CHANGELOG.md @@ -1,5 +1,10 @@ # FUSE for Rust - Changelog +## 0.14.0 - 2023-11-04 +* Add support for poll +* Add support for notifications +* ABI 7.11 support is now complete + ## 0.13.0 - 2023-08-16 * Remove dependency on `users` crate * Performance optimizations diff --git a/vendor/fuser/Cargo.toml b/vendor/fuser/Cargo.toml index 687d17f37..dd63efeb0 100644 --- a/vendor/fuser/Cargo.toml +++ b/vendor/fuser/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "fuser" edition = "2018" -version = "0.13.0" +version = "0.14.0" authors = ["Christopher Berner "] description = "Filesystem in Userspace (FUSE) for Rust" documentation = "https://docs.rs/fuser" @@ -20,17 +20,18 @@ travis-ci = { repository = "cberner/fuser" } libc = "0.2.51" log = "0.4.6" memchr = "2" -page_size = "0.5.0" +page_size = "0.6.0" serde = { version = "1.0.102", features = ["std", "derive"], optional = true } smallvec = "1.6.1" -zerocopy = "0.6" +zerocopy = { version = "0.7", features = ["derive"] } [dev-dependencies] -env_logger = "0.9" -clap = { version = "3.0", features = ["cargo"] } +env_logger = "0.10" +clap = { version = "4.4", features = ["cargo", "derive"] } bincode = "1.3.1" serde = { version = "1.0.102", features = ["std", "derive"] } tempfile = "3" +nix = { version = "0.27.1", features = ["poll", "fs"] } [build-dependencies] pkg-config = { version = "0.3.14", optional = true } @@ -62,3 +63,19 @@ abi-7-28 = ["abi-7-27"] abi-7-29 = ["abi-7-28"] abi-7-30 = ["abi-7-29"] abi-7-31 = ["abi-7-30"] + +[[example]] +name = "poll" +required-features = ["abi-7-11"] + +[[example]] +name = "poll_client" +required-features = ["abi-7-11"] + +[[example]] +name = "notify_inval_entry" +required-features = ["abi-7-12"] + +[[example]] +name = "notify_inval_inode" +required-features = ["abi-7-12"] diff --git a/vendor/fuser/README.md b/vendor/fuser/README.md index b82af4b42..f7b2b3b52 100644 --- a/vendor/fuser/README.md +++ b/vendor/fuser/README.md @@ -120,6 +120,10 @@ Most features of libfuse up to 3.10.3 are implemented. Feel free to contribute. Developed and tested on Linux. Tested under [Linux][FUSE for Linux] and [FreeBSD][FUSE for FreeBSD] using stable [Rust] (see CI for details). +## License + +Licensed under [MIT License](LICENSE.md), except for those files in `examples/` that explicitly contain a different license. + ## Contribution 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. diff --git a/vendor/fuser/examples/hello.rs b/vendor/fuser/examples/hello.rs index 8282c2e57..09aae7181 100644 --- a/vendor/fuser/examples/hello.rs +++ b/vendor/fuser/examples/hello.rs @@ -1,4 +1,4 @@ -use clap::{crate_version, Arg, Command}; +use clap::{crate_version, Arg, ArgAction, Command}; use fuser::{ FileAttr, FileType, Filesystem, MountOption, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, Request, @@ -126,21 +126,23 @@ fn main() { .arg( Arg::new("auto_unmount") .long("auto_unmount") + .action(ArgAction::SetTrue) .help("Automatically unmount on process exit"), ) .arg( Arg::new("allow-root") .long("allow-root") + .action(ArgAction::SetTrue) .help("Allow root user to access filesystem"), ) .get_matches(); env_logger::init(); - let mountpoint = matches.value_of("MOUNT_POINT").unwrap(); + let mountpoint = matches.get_one::("MOUNT_POINT").unwrap(); let mut options = vec![MountOption::RO, MountOption::FSName("hello".to_string())]; - if matches.is_present("auto_unmount") { + if matches.get_flag("auto_unmount") { options.push(MountOption::AutoUnmount); } - if matches.is_present("allow-root") { + if matches.get_flag("allow-root") { options.push(MountOption::AllowRoot); } fuser::mount2(HelloFS, mountpoint, &options).unwrap(); diff --git a/vendor/fuser/examples/notify_inval_entry.rs b/vendor/fuser/examples/notify_inval_entry.rs new file mode 100644 index 000000000..b560a9e8d --- /dev/null +++ b/vendor/fuser/examples/notify_inval_entry.rs @@ -0,0 +1,182 @@ +// Translated from libfuse's example/notify_inval_entry.c: +// Copyright (C) 2008 SUSE Linux Products GmbH +// Copyright (C) 2008 Tejun Heo +// +// Translated to Rust/fuser by Zev Weiss +// +// Due to the above provenance, unlike the rest of fuser this file is +// licensed under the terms of the GNU GPLv2. + +use std::{ + ffi::OsStr, + sync::{ + atomic::{AtomicU64, Ordering::SeqCst}, + Arc, Mutex, + }, + thread, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; + +use libc::{ENOBUFS, ENOENT, ENOTDIR}; + +use clap::Parser; + +use fuser::{ + FileAttr, FileType, Filesystem, MountOption, ReplyAttr, ReplyDirectory, ReplyEntry, Request, + FUSE_ROOT_ID, +}; + +struct ClockFS<'a> { + file_name: Arc>, + lookup_cnt: &'a AtomicU64, + timeout: Duration, +} + +impl<'a> ClockFS<'a> { + const FILE_INO: u64 = 2; + + fn get_filename(&self) -> String { + let n = self.file_name.lock().unwrap(); + n.clone() + } + + fn stat(ino: u64) -> Option { + let (kind, perm) = match ino { + FUSE_ROOT_ID => (FileType::Directory, 0o755), + Self::FILE_INO => (FileType::RegularFile, 0o000), + _ => return None, + }; + let now = SystemTime::now(); + Some(FileAttr { + ino, + size: 0, + blocks: 0, + atime: now, + mtime: now, + ctime: now, + crtime: now, + kind, + perm, + nlink: 1, + uid: 0, + gid: 0, + rdev: 0, + flags: 0, + blksize: 0, + }) + } +} + +impl<'a> Filesystem for ClockFS<'a> { + fn lookup(&self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { + if parent != FUSE_ROOT_ID || name != AsRef::::as_ref(&self.get_filename()) { + reply.error(ENOENT); + return; + } + + self.lookup_cnt.fetch_add(1, SeqCst); + reply.entry(&self.timeout, &ClockFS::stat(ClockFS::FILE_INO).unwrap(), 0); + } + + fn forget(&self, _req: &Request, ino: u64, nlookup: u64) { + if ino == ClockFS::FILE_INO { + let prev = self.lookup_cnt.fetch_sub(nlookup, SeqCst); + assert!(prev >= nlookup); + } else { + assert!(ino == FUSE_ROOT_ID); + } + } + + fn getattr(&self, _req: &Request, ino: u64, reply: ReplyAttr) { + match ClockFS::stat(ino) { + Some(a) => reply.attr(&self.timeout, &a), + None => reply.error(ENOENT), + } + } + + fn readdir( + &self, + _req: &Request, + ino: u64, + _fh: u64, + offset: i64, + mut reply: ReplyDirectory, + ) { + if ino != FUSE_ROOT_ID { + reply.error(ENOTDIR); + return; + } + + if offset == 0 + && reply.add( + ClockFS::FILE_INO, + offset + 1, + FileType::RegularFile, + &self.get_filename(), + ) + { + reply.error(ENOBUFS); + } else { + reply.ok(); + } + } +} + +fn now_filename() -> String { + let Ok(d) = SystemTime::now().duration_since(UNIX_EPOCH) else { + panic!("Pre-epoch SystemTime"); + }; + format!("Time_is_{}", d.as_secs()) +} + +#[derive(Parser)] +struct Options { + /// Mount demo filesystem at given path + mount_point: String, + + /// Timeout for kernel caches + #[clap(short, long, default_value_t = 5.0)] + timeout: f32, + + /// Update interval for filesystem contents + #[clap(short, long, default_value_t = 1.0)] + update_interval: f32, + + /// Disable kernel notifications + #[clap(short, long)] + no_notify: bool, + + /// Expire entries instead of invalidating them + #[clap(short, long)] + only_expire: bool, +} + +fn main() { + let opts = Options::parse(); + let options = vec![MountOption::RO, MountOption::FSName("clock".to_string())]; + let fname = Arc::new(Mutex::new(now_filename())); + let lookup_cnt = Box::leak(Box::new(AtomicU64::new(0))); + let fs = ClockFS { + file_name: fname.clone(), + lookup_cnt, + timeout: Duration::from_secs_f32(opts.timeout), + }; + + let session = fuser::Session::new(fs, opts.mount_point.as_ref(), &options).unwrap(); + let notifier = session.notifier(); + let _bg = session.spawn().unwrap(); + + loop { + let mut fname = fname.lock().unwrap(); + let oldname = std::mem::replace(&mut *fname, now_filename()); + drop(fname); + if !opts.no_notify && lookup_cnt.load(SeqCst) != 0 { + if opts.only_expire { + // fuser::notify_expire_entry(_SOME_HANDLE_, FUSE_ROOT_ID, &oldname); + } else if let Err(e) = notifier.inval_entry(FUSE_ROOT_ID, oldname.as_ref()) { + eprintln!("Warning: failed to invalidate entry '{}': {}", oldname, e); + } + } + thread::sleep(Duration::from_secs_f32(opts.update_interval)); + } +} diff --git a/vendor/fuser/examples/notify_inval_inode.rs b/vendor/fuser/examples/notify_inval_inode.rs new file mode 100644 index 000000000..a88bf2c1e --- /dev/null +++ b/vendor/fuser/examples/notify_inval_inode.rs @@ -0,0 +1,226 @@ +// Translated from libfuse's example/notify_{inval_inode,store_retrieve}.c: +// Copyright (C) 2016 Nikolaus Rath +// +// Translated to Rust/fuser by Zev Weiss +// +// Due to the above provenance, unlike the rest of fuser this file is +// licensed under the terms of the GNU GPLv2. + +use std::{ + convert::TryInto, + ffi::OsStr, + sync::{ + atomic::{AtomicU64, Ordering::SeqCst}, + Arc, Mutex, + }, + thread, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; + +use libc::{EACCES, EINVAL, EISDIR, ENOBUFS, ENOENT, ENOTDIR}; + +use clap::Parser; + +use fuser::{ + consts, FileAttr, FileType, Filesystem, MountOption, ReplyAttr, ReplyData, ReplyDirectory, + ReplyEntry, ReplyOpen, Request, FUSE_ROOT_ID, +}; + +struct ClockFS<'a> { + file_contents: Arc>, + lookup_cnt: &'a AtomicU64, +} + +impl<'a> ClockFS<'a> { + const FILE_INO: u64 = 2; + const FILE_NAME: &'static str = "current_time"; + + fn stat(&self, ino: u64) -> Option { + let (kind, perm, size) = match ino { + FUSE_ROOT_ID => (FileType::Directory, 0o755, 0), + Self::FILE_INO => ( + FileType::RegularFile, + 0o444, + self.file_contents.lock().unwrap().len(), + ), + _ => return None, + }; + let now = SystemTime::now(); + Some(FileAttr { + ino, + size: size.try_into().unwrap(), + blocks: 0, + atime: now, + mtime: now, + ctime: now, + crtime: now, + kind, + perm, + nlink: 1, + uid: 0, + gid: 0, + rdev: 0, + flags: 0, + blksize: 0, + }) + } +} + +impl<'a> Filesystem for ClockFS<'a> { + fn lookup(&self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { + if parent != FUSE_ROOT_ID || name != AsRef::::as_ref(&Self::FILE_NAME) { + reply.error(ENOENT); + return; + } + + self.lookup_cnt.fetch_add(1, SeqCst); + reply.entry(&Duration::MAX, &self.stat(ClockFS::FILE_INO).unwrap(), 0); + } + + fn forget(&self, _req: &Request, ino: u64, nlookup: u64) { + if ino == ClockFS::FILE_INO { + let prev = self.lookup_cnt.fetch_sub(nlookup, SeqCst); + assert!(prev >= nlookup); + } else { + assert!(ino == FUSE_ROOT_ID); + } + } + + fn getattr(&self, _req: &Request, ino: u64, reply: ReplyAttr) { + match self.stat(ino) { + Some(a) => reply.attr(&Duration::MAX, &a), + None => reply.error(ENOENT), + } + } + + fn readdir( + &self, + _req: &Request, + ino: u64, + _fh: u64, + offset: i64, + mut reply: ReplyDirectory, + ) { + if ino != FUSE_ROOT_ID { + reply.error(ENOTDIR); + return; + } + + if offset == 0 + && reply.add( + ClockFS::FILE_INO, + offset + 1, + FileType::RegularFile, + Self::FILE_NAME, + ) + { + reply.error(ENOBUFS); + } else { + reply.ok(); + } + } + + fn open(&self, _req: &Request, ino: u64, flags: i32, reply: ReplyOpen) { + if ino == FUSE_ROOT_ID { + reply.error(EISDIR); + } else if flags & libc::O_ACCMODE != libc::O_RDONLY { + reply.error(EACCES); + } else if ino != Self::FILE_INO { + eprintln!("Got open for nonexistent inode {}", ino); + reply.error(ENOENT); + } else { + reply.opened(ino, consts::FOPEN_KEEP_CACHE); + } + } + + fn read( + &self, + _req: &Request, + ino: u64, + _fh: u64, + offset: i64, + size: u32, + _flags: i32, + _lock_owner: Option, + reply: ReplyData, + ) { + assert!(ino == Self::FILE_INO); + if offset < 0 { + reply.error(EINVAL); + return; + } + let file = self.file_contents.lock().unwrap(); + let filedata = file.as_bytes(); + let dlen = filedata.len().try_into().unwrap(); + let Ok(start) = offset.min(dlen).try_into() else { + reply.error(EINVAL); + return; + }; + let Ok(end) = (offset + size as i64).min(dlen).try_into() else { + reply.error(EINVAL); + return; + }; + eprintln!("read returning {} bytes at offset {}", end - start, offset); + reply.data(&filedata[start..end]); + } +} + +fn now_string() -> String { + let Ok(d) = SystemTime::now().duration_since(UNIX_EPOCH) else { + panic!("Pre-epoch SystemTime"); + }; + format!("The current time is {}\n", d.as_secs()) +} + +#[derive(Parser)] +struct Options { + /// Mount demo filesystem at given path + mount_point: String, + + /// Update interval for filesystem contents + #[clap(short, long, default_value_t = 1.0)] + update_interval: f32, + + /// Disable kernel notifications + #[clap(short, long)] + no_notify: bool, + + /// Use notify_store() instead of notify_inval_inode() + #[clap(short = 's', long)] + notify_store: bool, +} + +fn main() { + let opts = Options::parse(); + let options = vec![MountOption::RO, MountOption::FSName("clock".to_string())]; + let fdata = Arc::new(Mutex::new(now_string())); + let lookup_cnt = Box::leak(Box::new(AtomicU64::new(0))); + let fs = ClockFS { + file_contents: fdata.clone(), + lookup_cnt, + }; + + let session = fuser::Session::new(fs, opts.mount_point.as_ref(), &options).unwrap(); + let notifier = session.notifier(); + let _bg = session.spawn().unwrap(); + + loop { + let mut s = fdata.lock().unwrap(); + let olddata = std::mem::replace(&mut *s, now_string()); + drop(s); + if !opts.no_notify && lookup_cnt.load(SeqCst) != 0 { + if opts.notify_store { + if let Err(e) = + notifier.store(ClockFS::FILE_INO, 0, fdata.lock().unwrap().as_bytes()) + { + eprintln!("Warning: failed to update kernel cache: {}", e); + } + } else if let Err(e) = + notifier.inval_inode(ClockFS::FILE_INO, 0, olddata.len().try_into().unwrap()) + { + eprintln!("Warning: failed to invalidate inode: {}", e); + } + } + thread::sleep(Duration::from_secs_f32(opts.update_interval)); + } +} diff --git a/vendor/fuser/examples/poll.rs b/vendor/fuser/examples/poll.rs new file mode 100644 index 000000000..cf1fb330d --- /dev/null +++ b/vendor/fuser/examples/poll.rs @@ -0,0 +1,344 @@ +// Translated from libfuse's example/poll.c: +// Copyright (C) 2008 SUSE Linux Products GmbH +// Copyright (C) 2008 Tejun Heo +// +// Translated to Rust/fuser by Zev Weiss +// +// Due to the above provenance, unlike the rest of fuser this file is +// licensed under the terms of the GNU GPLv2. + +use std::{ + convert::TryInto, + ffi::OsStr, + os::unix::ffi::OsStrExt, + sync::{ + atomic::{AtomicU64, Ordering::SeqCst}, + Arc, Mutex, + }, + thread, + time::{Duration, UNIX_EPOCH}, +}; + +use libc::{EACCES, EBADF, EBUSY, EINVAL, ENOENT, ENOTDIR}; + +use fuser::{ + consts::{FOPEN_DIRECT_IO, FOPEN_NONSEEKABLE, FUSE_POLL_SCHEDULE_NOTIFY}, + FileAttr, FileType, MountOption, Request, FUSE_ROOT_ID, +}; + +const NUMFILES: u8 = 16; +const MAXBYTES: u64 = 10; + +struct FSelData { + bytecnt: [u64; NUMFILES as usize], + open_mask: u16, + notify_mask: u16, + poll_handles: [u64; NUMFILES as usize], +} + +struct FSelFS { + data: Arc>, +} + +impl FSelData { + fn idx_to_ino(idx: u8) -> u64 { + let idx: u64 = idx.into(); + FUSE_ROOT_ID + idx + 1 + } + + fn ino_to_idx(ino: u64) -> u8 { + (ino - (FUSE_ROOT_ID + 1)) + .try_into() + .expect("out-of-range inode number") + } + + fn filestat(&self, idx: u8) -> FileAttr { + assert!(idx < NUMFILES); + FileAttr { + ino: Self::idx_to_ino(idx), + size: self.bytecnt[idx as usize], + blocks: 0, + atime: UNIX_EPOCH, + mtime: UNIX_EPOCH, + ctime: UNIX_EPOCH, + crtime: UNIX_EPOCH, + kind: FileType::RegularFile, + perm: 0o444, + nlink: 1, + uid: 0, + gid: 0, + rdev: 0, + flags: 0, + blksize: 0, + } + } +} + +impl FSelFS { + fn get_data(&self) -> std::sync::MutexGuard<'_, FSelData> { + self.data.lock().unwrap() + } +} + +impl fuser::Filesystem for FSelFS { + fn lookup(&self, _req: &Request, parent: u64, name: &OsStr, reply: fuser::ReplyEntry) { + if parent != FUSE_ROOT_ID || name.len() != 1 { + reply.error(ENOENT); + return; + } + + let name = name.as_bytes(); + + let idx = match name[0] { + b'0'..=b'9' => name[0] - b'0', + b'A'..=b'F' => name[0] - b'A' + 10, + _ => { + reply.error(ENOENT); + return; + } + }; + + reply.entry(&Duration::ZERO, &self.get_data().filestat(idx), 0); + } + + fn getattr(&self, _req: &Request, ino: u64, reply: fuser::ReplyAttr) { + if ino == FUSE_ROOT_ID { + let a = FileAttr { + ino: FUSE_ROOT_ID, + size: 0, + blocks: 0, + atime: UNIX_EPOCH, + mtime: UNIX_EPOCH, + ctime: UNIX_EPOCH, + crtime: UNIX_EPOCH, + kind: FileType::Directory, + perm: 0o555, + nlink: 2, + uid: 0, + gid: 0, + rdev: 0, + flags: 0, + blksize: 0, + }; + reply.attr(&Duration::ZERO, &a); + return; + } + let idx = FSelData::ino_to_idx(ino); + if idx < NUMFILES { + reply.attr(&Duration::ZERO, &self.get_data().filestat(idx)); + } else { + reply.error(ENOENT); + } + } + + fn readdir( + &self, + _req: &Request, + ino: u64, + _fh: u64, + offset: i64, + mut reply: fuser::ReplyDirectory, + ) { + if ino != FUSE_ROOT_ID { + reply.error(ENOTDIR); + return; + } + + let Ok(offset): Result = offset.try_into() else { + reply.error(EINVAL); + return; + }; + + for idx in offset..NUMFILES { + let ascii = match idx { + 0..=9 => [b'0' + idx], + 10..=16 => [b'A' + idx - 10], + _ => panic!(), + }; + let name = OsStr::from_bytes(&ascii); + if reply.add( + FSelData::idx_to_ino(idx), + (idx + 1).into(), + FileType::RegularFile, + name, + ) { + break; + } + } + + reply.ok(); + } + + fn open(&self, _req: &Request, ino: u64, flags: i32, 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 { + reply.error(EACCES); + return; + } + + { + let mut d = self.get_data(); + + if d.open_mask & (1 << idx) != 0 { + reply.error(EBUSY); + return; + } + + d.open_mask |= 1 << idx; + } + + reply.opened(idx.into(), FOPEN_DIRECT_IO | FOPEN_NONSEEKABLE); + } + + fn release( + &self, + _req: &Request, + _ino: u64, + fh: u64, + _flags: i32, + _lock_owner: Option, + _flush: bool, + reply: fuser::ReplyEmpty, + ) { + let idx = fh; + if idx >= NUMFILES.into() { + reply.error(EBADF); + return; + } + self.get_data().open_mask &= !(1 << idx); + reply.ok(); + } + + fn read( + &self, + _req: &Request, + _ino: u64, + fh: u64, + _offset: i64, + size: u32, + _flags: i32, + _lock_owner: Option, + reply: fuser::ReplyData, + ) { + let Ok(idx): Result = fh.try_into() else { + reply.error(EINVAL); + return; + }; + if idx >= NUMFILES { + reply.error(EBADF); + return; + } + let cnt = &mut self.get_data().bytecnt[idx as usize]; + let size = (*cnt).min(size.into()); + println!("READ {:X} transferred={} cnt={}", idx, size, *cnt); + *cnt -= size; + let elt = match idx { + 0..=9 => b'0' + idx, + 10..=16 => b'A' + idx - 10, + _ => panic!(), + }; + let data = vec![elt; size.try_into().unwrap()]; + reply.data(data.as_slice()); + } + + fn poll( + &self, + _req: &Request, + _ino: u64, + fh: u64, + kh: u64, + _events: u32, + flags: u32, + reply: fuser::ReplyPoll, + ) { + static POLLED_ZERO: AtomicU64 = AtomicU64::new(0); + let Ok(idx): Result = fh.try_into() else { + reply.error(EINVAL); + return; + }; + if idx >= NUMFILES { + reply.error(EBADF); + return; + } + + let revents = { + let mut d = self.get_data(); + + if flags & FUSE_POLL_SCHEDULE_NOTIFY != 0 { + d.notify_mask |= 1 << idx; + d.poll_handles[idx as usize] = kh; + } + + let nbytes = d.bytecnt[idx as usize]; + if nbytes != 0 { + println!( + "POLL {:X} cnt={} polled_zero={}", + idx, + nbytes, + POLLED_ZERO.swap(0, SeqCst) + ); + libc::POLLIN.try_into().unwrap() + } else { + POLLED_ZERO.fetch_add(1, SeqCst); + 0 + } + }; + + reply.poll(revents); + } +} + +fn producer(data: &Mutex, notifier: &fuser::Notifier) { + let mut idx: u8 = 0; + let mut nr = 1; + loop { + { + let mut d = data.lock().unwrap(); + let mut t = idx; + + for _ in 0..nr { + let tidx = t as usize; + if d.bytecnt[tidx] != MAXBYTES { + d.bytecnt[tidx] += 1; + if d.notify_mask & (1 << t) != 0 { + println!("NOTIFY {:X}", t); + if let Err(e) = notifier.poll(d.poll_handles[tidx]) { + eprintln!("poll notification failed: {}", e); + } + d.notify_mask &= !(1 << t); + } + } + + t = (t + NUMFILES / nr) % NUMFILES; + } + + idx = (idx + 1) % NUMFILES; + if idx == 0 { + nr = (nr * 2) % 7; + } + } + thread::sleep(Duration::from_millis(250)); + } +} + +fn main() { + let options = vec![MountOption::RO, MountOption::FSName("fsel".to_string())]; + let data = Arc::new(Mutex::new(FSelData { + bytecnt: [0; NUMFILES as usize], + open_mask: 0, + notify_mask: 0, + poll_handles: [0; NUMFILES as usize], + })); + let fs = FSelFS { data: data.clone() }; + + let mntpt = std::env::args().nth(1).unwrap(); + let session = fuser::Session::new(fs, mntpt.as_ref(), &options).unwrap(); + let bg = session.spawn().unwrap(); + + producer(&data, &bg.notifier()); +} diff --git a/vendor/fuser/examples/poll_client.rs b/vendor/fuser/examples/poll_client.rs new file mode 100644 index 000000000..442d3cc14 --- /dev/null +++ b/vendor/fuser/examples/poll_client.rs @@ -0,0 +1,58 @@ +// Translated from libfuse's example/poll_client.c (using actual +// poll(2) instead of select(2) that one does, since poll(2) is more +// readily available via the nix crate) +// +// Originally: +// Copyright (C) 2008 SUSE Linux Products GmbH +// Copyright (C) 2008 Tejun Heo +// +// Translated to Rust by Zev Weiss +// +// Due to the above provenance, unlike the rest of fuser this file is +// licensed under the terms of the GNU GPLv2. + +use nix::poll; +use std::os::fd::{AsFd, AsRawFd, RawFd}; + +const NUMFILES: usize = 16; + +fn make_nonblock(fd: RawFd) { + use nix::fcntl::{fcntl, FcntlArg, OFlag}; + let arg = FcntlArg::F_SETFL(OFlag::O_NONBLOCK); + fcntl(fd, arg).expect("failed to set fd nonblocking"); +} + +fn main() -> std::io::Result<()> { + let mut files = Vec::with_capacity(NUMFILES); + for c in "0123456789ABCDEF".chars() { + let name = format!("{}", c); + let f = std::fs::File::open(name)?; + make_nonblock(f.as_raw_fd()); + files.push(f); + } + let mut readbuf = vec![0u8; 4096]; + + let mut pollfds = files + .iter() + .map(|f| poll::PollFd::new(f, poll::PollFlags::POLLIN)) + .collect::>(); + + for _ in 0..16 { + poll::poll(pollfds.as_mut_slice(), -1)?; + + for (i, pfd) in pollfds.iter().enumerate() { + let revents = pfd.revents().expect("got unknown poll flag"); + if !revents.intersects(poll::PollFlags::POLLIN) { + print!("_: "); + continue; + } + print!("{:X}:", i); + let fd = pfd.as_fd().as_raw_fd(); + let nbytes = nix::unistd::read(fd, readbuf.as_mut_slice())?; + print!("{:02} ", nbytes); + } + println!(); + } + + Ok(()) +} diff --git a/vendor/fuser/examples/simple.rs b/vendor/fuser/examples/simple.rs index 83c7eaecc..94c752422 100644 --- a/vendor/fuser/examples/simple.rs +++ b/vendor/fuser/examples/simple.rs @@ -1,12 +1,12 @@ #![allow(clippy::needless_return)] #![allow(clippy::unnecessary_cast)] // libc::S_* are u16 or u32 depending on the platform -use clap::{crate_version, Arg, Command}; +use clap::{crate_version, Arg, ArgAction, Command}; 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; +// #[cfg(feature = "abi-7-31")] +// use fuser::consts::FUSE_WRITE_KILL_PRIV; use fuser::TimeOrNow::Now; use fuser::{ Filesystem, KernelConfig, MountOption, ReplyAttr, ReplyCreate, ReplyData, ReplyDirectory, @@ -306,7 +306,7 @@ impl SimpleFS { fn allocate_next_file_handle(&self, read: bool, write: bool) -> u64 { let mut fh = self.next_file_handle.fetch_add(1, Ordering::SeqCst); // Assert that we haven't run out of file handles - assert!(fh < FILE_HANDLE_WRITE_BIT && fh < FILE_HANDLE_READ_BIT); + assert!(fh < FILE_HANDLE_READ_BIT.min(FILE_HANDLE_WRITE_BIT)); if read { fh |= FILE_HANDLE_READ_BIT; } @@ -1960,38 +1960,43 @@ fn main() { .long("data-dir") .value_name("DIR") .default_value("/tmp/fuser") - .help("Set local directory used to store data") - .takes_value(true), + .help("Set local directory used to store data"), ) .arg( Arg::new("mount-point") .long("mount-point") .value_name("MOUNT_POINT") .default_value("") - .help("Act as a client, and mount FUSE at given path") - .takes_value(true), + .help("Act as a client, and mount FUSE at given path"), ) .arg( Arg::new("direct-io") .long("direct-io") + .action(ArgAction::SetTrue) .requires("mount-point") .help("Mount FUSE with direct IO"), ) - .arg(Arg::new("fsck").long("fsck").help("Run a filesystem check")) + .arg( + Arg::new("fsck") + .long("fsck") + .action(ArgAction::SetTrue) + .help("Run a filesystem check"), + ) .arg( Arg::new("suid") .long("suid") + .action(ArgAction::SetTrue) .help("Enable setuid support when run as root"), ) .arg( Arg::new("v") .short('v') - .multiple_occurrences(true) + .action(ArgAction::Count) .help("Sets the level of verbosity"), ) .get_matches(); - let verbosity: u64 = matches.occurrences_of("v"); + let verbosity = matches.get_count("v"); let log_level = match verbosity { 0 => LevelFilter::Error, 1 => LevelFilter::Warn, @@ -2008,7 +2013,7 @@ fn main() { #[cfg(feature = "abi-7-26")] { - if matches.is_present("suid") { + if matches.get_flag("suid") { info!("setuid bit support enabled"); options.push(MountOption::Suid); } else { @@ -2027,18 +2032,18 @@ fn main() { eprintln!("Unable to read /etc/fuse.conf"); } - let data_dir: String = matches.value_of("data-dir").unwrap_or_default().to_string(); + let data_dir = matches.get_one::("data-dir").unwrap().to_string(); let mountpoint: String = matches - .value_of("mount-point") - .unwrap_or_default() + .get_one::("mount-point") + .unwrap() .to_string(); let result = fuser::mount2( SimpleFS::new( data_dir, - matches.is_present("direct-io"), - matches.is_present("suid"), + matches.get_flag("direct-io"), + matches.get_flag("suid"), ), mountpoint, &options, diff --git a/vendor/fuser/mount_tests.Dockerfile b/vendor/fuser/mount_tests.Dockerfile index 79cbdb4cc..b82d50e14 100644 --- a/vendor/fuser/mount_tests.Dockerfile +++ b/vendor/fuser/mount_tests.Dockerfile @@ -4,7 +4,9 @@ ENV DEBIAN_FRONTEND=noninteractive RUN apt update && apt install -y build-essential curl -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.67.0 +ADD rust-toolchain /code/fuser/rust-toolchain + +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=$(cat /code/fuser/rust-toolchain) ENV PATH=/root/.cargo/bin:$PATH diff --git a/vendor/fuser/pjdfs.Dockerfile b/vendor/fuser/pjdfs.Dockerfile index 75205f2f5..08e303667 100644 --- a/vendor/fuser/pjdfs.Dockerfile +++ b/vendor/fuser/pjdfs.Dockerfile @@ -12,14 +12,13 @@ RUN echo 'user_allow_other' >> /etc/fuse.conf RUN mkdir -p /code/pjdfstest && cd /code && git clone https://github.com/fleetfs/pjdfstest && cd pjdfstest \ && git checkout d3beed6f5f15c204a8af3df2f518241931a42e94 && autoreconf -ifs && ./configure && make pjdfstest -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.67.0 +ADD rust-toolchain /code/fuser/rust-toolchain + +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=$(cat /code/fuser/rust-toolchain) ENV PATH=/root/.cargo/bin:$PATH ARG BUILD_FEATURES -ADD Cargo.toml build.rs /code/fuser/ -RUN cd /code/fuser && mkdir src && touch src/lib.rs && cargo build --release --examples $BUILD_FEATURES - ADD . /code/fuser/ RUN cd /code/fuser && cargo build --release --examples $BUILD_FEATURES && cp target/release/examples/simple /bin/fuser diff --git a/vendor/fuser/rust-toolchain b/vendor/fuser/rust-toolchain new file mode 100644 index 000000000..bfe79d0bd --- /dev/null +++ b/vendor/fuser/rust-toolchain @@ -0,0 +1 @@ +1.70 diff --git a/vendor/fuser/src/lib.rs b/vendor/fuser/src/lib.rs index 78cbb9143..b32931930 100644 --- a/vendor/fuser/src/lib.rs +++ b/vendor/fuser/src/lib.rs @@ -27,6 +27,10 @@ use crate::session::MAX_WRITE_SIZE; #[cfg(feature = "abi-7-16")] pub use ll::fuse_abi::fuse_forget_one; pub use mnt::mount_options::MountOption; +#[cfg(feature = "abi-7-11")] +pub use notify::Notifier; +#[cfg(feature = "abi-7-11")] +pub use reply::ReplyPoll; #[cfg(target_os = "macos")] pub use reply::ReplyXTimes; pub use reply::ReplyXattr; @@ -45,6 +49,8 @@ use std::cmp::min; mod channel; mod ll; mod mnt; +#[cfg(feature = "abi-7-11")] +mod notify; mod reply; mod request; mod session; @@ -868,6 +874,25 @@ pub trait Filesystem { reply.error(ENOSYS); } + /// Poll for events + #[cfg(feature = "abi-7-11")] + fn poll( + &self, + _req: &Request<'_>, + ino: u64, + fh: u64, + kh: u64, + events: u32, + flags: u32, + reply: ReplyPoll, + ) { + debug!( + "[Not Implemented] poll(ino: {:#x?}, fh: {}, kh: {}, events: {}, flags: {})", + ino, fh, kh, events, flags + ); + reply.error(ENOSYS); + } + /// Preallocate or deallocate space to a file fn fallocate( &self, diff --git a/vendor/fuser/src/ll/argument.rs b/vendor/fuser/src/ll/argument.rs index af95a3b6d..412015b37 100644 --- a/vendor/fuser/src/ll/argument.rs +++ b/vendor/fuser/src/ll/argument.rs @@ -31,7 +31,7 @@ impl<'a> ArgumentIterator<'a> { /// Fetch a typed argument. Returns `None` if there's not enough data left. pub fn fetch(&mut self) -> Option<&'a T> { - match zerocopy::LayoutVerified::<_, T>::new_from_prefix(self.data) { + match zerocopy::Ref::<_, T>::new_from_prefix(self.data) { None => { if self.data.as_ptr() as usize % core::mem::align_of::() != 0 { // Panic on alignment errors as this is under the control @@ -53,7 +53,7 @@ impl<'a> ArgumentIterator<'a> { /// Fetch a slice of typed of arguments. Returns `None` if there's not enough data left. #[cfg(feature = "abi-7-16")] pub fn fetch_slice(&mut self, count: usize) -> Option<&'a [T]> { - match zerocopy::LayoutVerified::<_, [T]>::new_slice_from_prefix(self.data, count) { + match zerocopy::Ref::<_, [T]>::new_slice_from_prefix(self.data, count) { None => { if self.data.as_ptr() as usize % core::mem::align_of::() != 0 { // Panic on alignment errors as this is under the control @@ -88,13 +88,13 @@ pub mod tests { use super::super::test::AlignedData; use super::*; - use zerocopy::FromBytes; + use zerocopy::{FromBytes, FromZeroes}; const TEST_DATA: AlignedData<[u8; 10]> = AlignedData([0x66, 0x6f, 0x6f, 0x00, 0x62, 0x61, 0x72, 0x00, 0x62, 0x61]); #[repr(C)] - #[derive(FromBytes)] + #[derive(FromBytes, FromZeroes)] struct TestArgument { p1: u8, p2: u8, diff --git a/vendor/fuser/src/ll/fuse_abi.rs b/vendor/fuser/src/ll/fuse_abi.rs index 348b8ceee..95925304b 100644 --- a/vendor/fuser/src/ll/fuse_abi.rs +++ b/vendor/fuser/src/ll/fuse_abi.rs @@ -24,7 +24,7 @@ #[cfg(feature = "abi-7-9")] use crate::consts::{FATTR_ATIME_NOW, FATTR_MTIME_NOW}; use std::convert::TryFrom; -use zerocopy::{AsBytes, FromBytes}; +use zerocopy::{AsBytes, FromBytes, FromZeroes}; pub const FUSE_KERNEL_VERSION: u32 = 7; @@ -130,7 +130,7 @@ pub struct fuse_kstatfs { } #[repr(C)] -#[derive(Debug, AsBytes, FromBytes)] +#[derive(Debug, AsBytes, FromBytes, FromZeroes)] pub struct fuse_file_lock { pub start: u64, pub end: u64, @@ -511,14 +511,14 @@ pub struct fuse_entry_out { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_forget_in { pub nlookup: u64, } #[cfg(feature = "abi-7-16")] #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_forget_one { pub nodeid: u64, pub nlookup: u64, @@ -526,7 +526,7 @@ pub struct fuse_forget_one { #[cfg(feature = "abi-7-16")] #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_batch_forget_in { pub count: u32, pub dummy: u32, @@ -534,7 +534,7 @@ pub struct fuse_batch_forget_in { #[cfg(feature = "abi-7-9")] #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_getattr_in { pub getattr_flags: u32, pub dummy: u32, @@ -561,7 +561,7 @@ pub struct fuse_getxtimes_out { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_mknod_in { pub mode: u32, pub rdev: u32, @@ -572,7 +572,7 @@ pub struct fuse_mknod_in { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_mkdir_in { pub mode: u32, #[cfg(not(feature = "abi-7-12"))] @@ -582,13 +582,13 @@ pub struct fuse_mkdir_in { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_rename_in { pub newdir: u64, } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_rename2_in { pub newdir: u64, pub flags: u32, @@ -597,7 +597,7 @@ pub struct fuse_rename2_in { #[cfg(target_os = "macos")] #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_exchange_in { pub olddir: u64, pub newdir: u64, @@ -605,13 +605,13 @@ pub struct fuse_exchange_in { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_link_in { pub oldnodeid: u64, } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_setattr_in { pub valid: u32, pub padding: u32, @@ -683,7 +683,7 @@ impl fuse_setattr_in { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_open_in { // NOTE: this field is defined as u32 in fuse_kernel.h in libfuse. However, it is then cast // to an i32 when invoking the filesystem's open method and this matches the open() syscall @@ -692,7 +692,7 @@ pub struct fuse_open_in { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_create_in { // NOTE: this field is defined as u32 in fuse_kernel.h in libfuse. However, it is then cast // to an i32 when invoking the filesystem's create method and this matches the open() syscall @@ -717,7 +717,7 @@ pub struct fuse_open_out { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_release_in { pub fh: u64, // NOTE: this field is defined as u32 in fuse_kernel.h in libfuse. However, it is then cast @@ -728,7 +728,7 @@ pub struct fuse_release_in { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_flush_in { pub fh: u64, pub unused: u32, @@ -737,7 +737,7 @@ pub struct fuse_flush_in { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_read_in { pub fh: u64, // NOTE: this field is defined as u64 in fuse_kernel.h in libfuse. However, it is then cast @@ -757,7 +757,7 @@ pub struct fuse_read_in { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_write_in { pub fh: u64, // NOTE: this field is defined as u64 in fuse_kernel.h in libfuse. However, it is then cast @@ -789,7 +789,7 @@ pub struct fuse_statfs_out { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_fsync_in { pub fh: u64, pub fsync_flags: u32, @@ -797,7 +797,7 @@ pub struct fuse_fsync_in { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_setxattr_in { pub size: u32, // NOTE: this field is defined as u32 in fuse_kernel.h in libfuse. However, it is then cast @@ -810,7 +810,7 @@ pub struct fuse_setxattr_in { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_getxattr_in { pub size: u32, pub padding: u32, @@ -828,7 +828,7 @@ pub struct fuse_getxattr_out { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_lk_in { pub fh: u64, pub owner: u64, @@ -846,7 +846,7 @@ pub struct fuse_lk_out { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_access_in { // NOTE: this field is defined as u32 in fuse_kernel.h in libfuse. However, it is then cast // to an i32 when invoking the filesystem's access method @@ -855,7 +855,7 @@ pub struct fuse_access_in { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_init_in { pub major: u32, pub minor: u32, @@ -891,7 +891,7 @@ pub struct fuse_init_out { #[cfg(feature = "abi-7-12")] #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct cuse_init_in { pub major: u32, pub minor: u32, @@ -915,13 +915,13 @@ pub struct cuse_init_out { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_interrupt_in { pub unique: u64, } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_bmap_in { pub block: u64, pub blocksize: u32, @@ -936,7 +936,7 @@ pub struct fuse_bmap_out { #[cfg(feature = "abi-7-11")] #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_ioctl_in { pub fh: u64, pub flags: u32, @@ -965,7 +965,7 @@ pub struct fuse_ioctl_out { #[cfg(feature = "abi-7-11")] #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_poll_in { pub fh: u64, pub kh: u64, @@ -978,7 +978,7 @@ pub struct fuse_poll_in { #[cfg(feature = "abi-7-11")] #[repr(C)] -#[derive(Debug)] +#[derive(Debug, AsBytes)] pub struct fuse_poll_out { pub revents: u32, pub padding: u32, @@ -986,14 +986,14 @@ pub struct fuse_poll_out { #[cfg(feature = "abi-7-11")] #[repr(C)] -#[derive(Debug)] +#[derive(Debug, AsBytes)] pub struct fuse_notify_poll_wakeup_out { pub kh: u64, } #[cfg(feature = "abi-7-19")] #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_fallocate_in { pub fh: u64, // NOTE: this field is defined as u64 in fuse_kernel.h in libfuse. However, it is treated as signed @@ -1006,7 +1006,7 @@ pub struct fuse_fallocate_in { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_in_header { pub len: u32, pub opcode: u32, @@ -1046,7 +1046,7 @@ pub struct fuse_direntplus { #[cfg(feature = "abi-7-12")] #[repr(C)] -#[derive(Debug)] +#[derive(Debug, AsBytes)] pub struct fuse_notify_inval_inode_out { pub ino: u64, pub off: i64, @@ -1055,7 +1055,7 @@ pub struct fuse_notify_inval_inode_out { #[cfg(feature = "abi-7-12")] #[repr(C)] -#[derive(Debug)] +#[derive(Debug, AsBytes)] pub struct fuse_notify_inval_entry_out { pub parent: u64, pub namelen: u32, @@ -1064,17 +1064,17 @@ pub struct fuse_notify_inval_entry_out { #[cfg(feature = "abi-7-18")] #[repr(C)] -#[derive(Debug)] +#[derive(Debug, AsBytes)] pub struct fuse_notify_delete_out { - parent: u64, - child: u64, - namelen: u32, - padding: u32, + pub parent: u64, + pub child: u64, + pub namelen: u32, + pub padding: u32, } #[cfg(feature = "abi-7-15")] #[repr(C)] -#[derive(Debug)] +#[derive(Debug, AsBytes)] pub struct fuse_notify_store_out { pub nodeid: u64, pub offset: u64, @@ -1095,7 +1095,7 @@ pub struct fuse_notify_retrieve_out { #[cfg(feature = "abi-7-15")] #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_notify_retrieve_in { // matches the size of fuse_write_in pub dummy1: u64, @@ -1107,7 +1107,7 @@ pub struct fuse_notify_retrieve_in { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_lseek_in { pub fh: u64, pub offset: i64, @@ -1123,7 +1123,7 @@ pub struct fuse_lseek_out { } #[repr(C)] -#[derive(Debug, FromBytes)] +#[derive(Debug, FromBytes, FromZeroes)] pub struct fuse_copy_file_range_in { pub fh_in: u64, // NOTE: this field is defined as u64 in fuse_kernel.h in libfuse. However, it is treated as signed diff --git a/vendor/fuser/src/ll/mod.rs b/vendor/fuser/src/ll/mod.rs index 753c2d997..21b6a515e 100644 --- a/vendor/fuser/src/ll/mod.rs +++ b/vendor/fuser/src/ll/mod.rs @@ -2,6 +2,8 @@ mod argument; pub mod fuse_abi; +#[cfg(feature = "abi-7-11")] +pub(crate) mod notify; pub(crate) mod reply; mod request; @@ -267,6 +269,7 @@ impl From for u64 { #[cfg(test)] mod test { + use std::io::IoSlice; use std::ops::{Deref, DerefMut}; /// If we want to be able to cast bytes to our fuse C struct types we need it /// to be aligned. This struct helps getting &[u8]s which are 8 byte aligned. @@ -285,4 +288,12 @@ mod test { &mut self.0 } } + + pub fn ioslice_to_vec(s: &[IoSlice<'_>]) -> Vec { + let mut v = Vec::with_capacity(s.iter().map(|x| x.len()).sum()); + for x in s { + v.extend_from_slice(x); + } + v + } } diff --git a/vendor/fuser/src/ll/notify.rs b/vendor/fuser/src/ll/notify.rs new file mode 100644 index 000000000..3acddd707 --- /dev/null +++ b/vendor/fuser/src/ll/notify.rs @@ -0,0 +1,213 @@ +use std::{convert::TryInto, io::IoSlice, mem::size_of, num::TryFromIntError}; + +#[allow(unused)] +use std::{ffi::OsStr, os::unix::ffi::OsStrExt}; + +use smallvec::{smallvec, SmallVec}; +use zerocopy::AsBytes; + +use super::fuse_abi as abi; + +const INLINE_DATA_THRESHOLD: usize = size_of::() * 4; +type NotificationBuf = SmallVec<[u8; INLINE_DATA_THRESHOLD]>; + +#[derive(Debug)] +pub(crate) enum Notification<'a> { + /// For notifications with no additional data + Bare(NotificationBuf), + + /// For notifications that include a buffer of arbitrary data + WithData(NotificationBuf, &'a [u8]), + + /// For notifications that include a NUL-terminated name + /// (directory entry) + #[allow(unused)] + WithName(NotificationBuf, &'a [u8]), +} + +impl<'a> Notification<'a> { + pub(crate) fn with_iovec]) -> T, T>( + &self, + code: abi::fuse_notify_code, + f: F, + ) -> Result { + let datalen = match &self { + Notification::Bare(b) => b.len(), + Notification::WithData(b, d) => b.len() + d.len(), + Notification::WithName(b, n) => b.len() + n.len() + 1, // +1 because we need to NUL-terminate the name + }; + let header = abi::fuse_out_header { + unique: 0, + error: code as i32, + len: (size_of::() + datalen).try_into()?, + }; + let mut v: SmallVec<[IoSlice<'_>; 4]> = smallvec![IoSlice::new(header.as_bytes())]; + match &self { + Notification::Bare(b) => v.push(IoSlice::new(b)), + Notification::WithData(b, d) => { + v.push(IoSlice::new(b)); + v.push(IoSlice::new(d)); + } + Notification::WithName(b, n) => { + v.push(IoSlice::new(b)); + v.push(IoSlice::new(n)); + v.push(IoSlice::new(&[0u8])); // NUL terminator required by fuse + } + } + Ok(f(&v)) + } + + #[cfg(feature = "abi-7-12")] + pub(crate) fn new_inval_entry(parent: u64, name: &'a OsStr) -> Result { + let r = abi::fuse_notify_inval_entry_out { + parent, + namelen: name.len().try_into()?, + padding: 0, + }; + Ok(Self::from_struct_with_name(&r, name.as_bytes())) + } + + #[cfg(feature = "abi-7-12")] + pub(crate) fn new_inval_inode(ino: u64, offset: i64, len: i64) -> Self { + let r = abi::fuse_notify_inval_inode_out { + ino, + off: offset, + len, + }; + Self::from_struct(&r) + } + + #[cfg(feature = "abi-7-15")] + pub(crate) fn new_store( + ino: u64, + offset: u64, + data: &'a [u8], + ) -> Result { + let r = abi::fuse_notify_store_out { + nodeid: ino, + offset, + size: data.len().try_into()?, + padding: 0, + }; + Ok(Self::from_struct_with_data(&r, data)) + } + + #[cfg(feature = "abi-7-18")] + pub(crate) fn new_delete( + parent: u64, + child: u64, + name: &'a OsStr, + ) -> Result { + let r = abi::fuse_notify_delete_out { + parent, + child, + namelen: name.len().try_into()?, + padding: 0, + }; + Ok(Self::from_struct_with_name(&r, name.as_bytes())) + } + + #[cfg(feature = "abi-7-11")] + pub(crate) fn new_poll(kh: u64) -> Self { + let r = abi::fuse_notify_poll_wakeup_out { kh }; + Self::from_struct(&r) + } + + fn from_struct(data: &T) -> Self { + Self::Bare(data.as_bytes().into()) + } + + #[allow(unused)] + fn from_struct_with_name(buf: &T, name: &'a [u8]) -> Self { + Self::WithName(buf.as_bytes().into(), name) + } + + fn from_struct_with_data(buf: &T, data: &'a [u8]) -> Self { + Self::WithData(buf.as_bytes().into(), data) + } +} + +#[cfg(test)] +mod test { + use super::super::test::ioslice_to_vec; + use super::*; + + #[test] + #[cfg(feature = "abi-7-12")] + fn inval_entry() { + let n = Notification::new_inval_entry(0x42, OsStr::new("abc")) + .unwrap() + .with_iovec( + abi::fuse_notify_code::FUSE_NOTIFY_INVAL_ENTRY, + ioslice_to_vec, + ) + .unwrap(); + let expected = vec![ + 0x24, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x62, 0x63, 0x00, + ]; + assert_eq!(n, expected); + } + + #[test] + #[cfg(feature = "abi-7-12")] + fn inval_inode() { + let n = Notification::new_inval_inode(0x42, 100, 200) + .with_iovec( + abi::fuse_notify_code::FUSE_NOTIFY_INVAL_INODE, + ioslice_to_vec, + ) + .unwrap(); + let expected = vec![ + 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + assert_eq!(n, expected); + } + + #[test] + #[cfg(feature = "abi-7-15")] + fn store() { + let n = Notification::new_store(0x42, 50, &[0xde, 0xad, 0xbe, 0xef]) + .unwrap() + .with_iovec(abi::fuse_notify_code::FUSE_NOTIFY_STORE, ioslice_to_vec) + .unwrap(); + let expected = vec![ + 0x2c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, + 0xbe, 0xef, + ]; + assert_eq!(n, expected); + } + + #[test] + #[cfg(feature = "abi-7-18")] + fn delete() { + let n = Notification::new_inval_entry(0x42, OsStr::new("abc")) + .unwrap() + .with_iovec(abi::fuse_notify_code::FUSE_NOTIFY_DELETE, ioslice_to_vec) + .unwrap(); + let expected = vec![ + 0x24, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x62, 0x63, 0x00, + ]; + assert_eq!(n, expected); + } + + #[test] + #[cfg(feature = "abi-7-11")] + fn poll() { + let n = Notification::new_poll(0x4321) + .with_iovec(abi::fuse_notify_code::FUSE_POLL, ioslice_to_vec) + .unwrap(); + let expected = vec![ + 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + assert_eq!(n, expected); + } +} diff --git a/vendor/fuser/src/ll/reply.rs b/vendor/fuser/src/ll/reply.rs index 8a7cb64c3..9f50beb18 100644 --- a/vendor/fuser/src/ll/reply.rs +++ b/vendor/fuser/src/ll/reply.rs @@ -225,6 +225,15 @@ impl<'a> Response<'a> { Self::Data(v) } + #[cfg(feature = "abi-7-11")] + pub(crate) fn new_poll(revents: u32) -> Self { + let r = abi::fuse_poll_out { + revents, + padding: 0, + }; + Self::from_struct(&r) + } + fn new_directory(list: EntListBuf) -> Self { assert!(list.buf.len() <= list.max_size); Self::Data(list.buf) @@ -495,6 +504,7 @@ impl DirEntPlusList { mod test { use std::num::NonZeroI32; + use super::super::test::ioslice_to_vec; use super::*; #[test] @@ -869,12 +879,4 @@ mod test { expected ); } - - fn ioslice_to_vec(s: &[IoSlice<'_>]) -> Vec { - let mut v = Vec::with_capacity(s.iter().map(|x| x.len()).sum()); - for x in s { - v.extend_from_slice(x); - } - v - } } diff --git a/vendor/fuser/src/ll/request.rs b/vendor/fuser/src/ll/request.rs index c8d166f29..97bd61245 100644 --- a/vendor/fuser/src/ll/request.rs +++ b/vendor/fuser/src/ll/request.rs @@ -1317,7 +1317,7 @@ mod op { } } - /// Poll. TODO: currently unsupported by fuser + /// Poll. #[cfg(feature = "abi-7-11")] #[derive(Debug)] pub struct Poll<'a> { @@ -1332,6 +1332,24 @@ mod op { pub fn file_handle(&self) -> FileHandle { FileHandle(self.arg.fh) } + + /// The unique id used for the poll context by the kernel + pub fn kernel_handle(&self) -> u64 { + self.arg.kh + } + + /// The requested poll events + pub fn events(&self) -> u32 { + #[cfg(feature = "abi-7-21")] + return self.arg.events; + #[cfg(not(feature = "abi-7-21"))] + return 0; + } + + /// The poll request's flags + pub fn flags(&self) -> u32 { + self.arg.flags + } } /// NotifyReply. TODO: currently unsupported by fuser diff --git a/vendor/fuser/src/notify.rs b/vendor/fuser/src/notify.rs new file mode 100644 index 000000000..563807277 --- /dev/null +++ b/vendor/fuser/src/notify.rs @@ -0,0 +1,87 @@ +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}, + + // What we're sending here aren't really replies, but they + // move in the same direction (userspace->kernel), so we can + // reuse ReplySender for it. + reply::ReplySender, +}; + +/// A handle by which the application can send notifications to the server +#[derive(Debug)] +pub struct Notifier(ChannelSender); + +impl Notifier { + pub(crate) fn new(cs: ChannelSender) -> Self { + Self(cs) + } + + /// Notify poll clients of I/O readiness + #[cfg(feature = "abi-7-11")] + pub fn poll(&self, kh: u64) -> io::Result<()> { + let notif = Notification::new_poll(kh); + self.send(notify_code::FUSE_POLL, ¬if) + } + + /// Invalidate the kernel cache for a given directory entry + #[cfg(feature = "abi-7-12")] + pub fn inval_entry(&self, parent: u64, name: &OsStr) -> io::Result<()> { + let notif = Notification::new_inval_entry(parent, name).map_err(Self::too_big_err)?; + self.send_inval(notify_code::FUSE_NOTIFY_INVAL_ENTRY, ¬if) + } + + /// Invalidate the kernel cache for a given inode (metadata and + /// data in the given range) + #[cfg(feature = "abi-7-12")] + pub fn inval_inode(&self, ino: u64, offset: i64, len: i64) -> io::Result<()> { + let notif = Notification::new_inval_inode(ino, offset, len); + self.send_inval(notify_code::FUSE_NOTIFY_INVAL_INODE, ¬if) + } + + /// Update the kernel's cached copy of a given inode's data + #[cfg(feature = "abi-7-15")] + pub fn store(&self, ino: u64, offset: u64, data: &[u8]) -> io::Result<()> { + let notif = Notification::new_store(ino, offset, data).map_err(Self::too_big_err)?; + // Not strictly an invalidate, but the inode we're operating + // on may have been evicted anyway, so treat is as such + self.send_inval(notify_code::FUSE_NOTIFY_STORE, ¬if) + } + + /// Invalidate the kernel cache for a given directory entry and inform + /// inotify watchers of a file deletion. + #[cfg(feature = "abi-7-18")] + pub fn delete(&self, parent: u64, child: u64, name: &OsStr) -> io::Result<()> { + let notif = Notification::new_delete(parent, child, name).map_err(Self::too_big_err)?; + 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 + // kernel may have already dropped the cached + // entry on its own anyway), so ignore it. + Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(()), + x => x, + } + } + + fn send(&self, code: notify_code, notification: &Notification<'_>) -> io::Result<()> { + notification + .with_iovec(code, |iov| self.0.send(iov)) + .map_err(Self::too_big_err)? + } + + /// Create an error for indicating when a notification message + /// would exceed the capacity that its length descriptor field is + /// capable of encoding. + fn too_big_err(tfie: std::num::TryFromIntError) -> io::Error { + io::Error::new(io::ErrorKind::Other, format!("Data too large: {}", tfie)) + } +} diff --git a/vendor/fuser/src/reply.rs b/vendor/fuser/src/reply.rs index 30a682f62..fb2e94341 100644 --- a/vendor/fuser/src/reply.rs +++ b/vendor/fuser/src/reply.rs @@ -473,6 +473,37 @@ impl ReplyIoctl { } } +/// +/// Poll Reply +/// +#[derive(Debug)] +#[cfg(feature = "abi-7-11")] +pub struct ReplyPoll { + reply: ReplyRaw, +} + +#[cfg(feature = "abi-7-11")] +impl Reply for ReplyPoll { + fn new(unique: u64, sender: S) -> ReplyPoll { + ReplyPoll { + reply: Reply::new(unique, sender), + } + } +} + +#[cfg(feature = "abi-7-11")] +impl ReplyPoll { + /// Reply to a request with the given poll result + pub fn poll(self, revents: u32) { + self.reply.send_ll(&ll::Response::new_poll(revents)) + } + + /// Reply to a request with the given error code + pub fn error(self, err: c_int) { + self.reply.error(err); + } +} + /// /// Directory reply /// diff --git a/vendor/fuser/src/request.rs b/vendor/fuser/src/request.rs index 936c31959..0b352d96a 100644 --- a/vendor/fuser/src/request.rs +++ b/vendor/fuser/src/request.rs @@ -513,9 +513,16 @@ impl<'a> Request<'a> { } } #[cfg(feature = "abi-7-11")] - ll::Operation::Poll(_) => { - // TODO: handle FUSE_POLL - return Err(Errno::ENOSYS); + ll::Operation::Poll(x) => { + se.filesystem.poll( + self, + self.request.nodeid().into(), + x.file_handle().into(), + x.kernel_handle(), + x.events(), + x.flags(), + self.reply(), + ); } #[cfg(feature = "abi-7-15")] ll::Operation::NotifyReply(_) => { diff --git a/vendor/fuser/src/session.rs b/vendor/fuser/src/session.rs index 7f0e363ff..43ba04efe 100644 --- a/vendor/fuser/src/session.rs +++ b/vendor/fuser/src/session.rs @@ -19,6 +19,8 @@ use crate::request::Request; use crate::Filesystem; use crate::MountOption; use crate::{channel::Channel, mnt::Mount}; +#[cfg(feature = "abi-7-11")] +use crate::{channel::ChannelSender, notify::Notifier}; /// The max size of write requests from the kernel. The absolute minimum is 4k, /// FUSE recommends at least 128k, max 16M. The FUSE default is 16M on macOS @@ -177,6 +179,12 @@ impl Session { mount: self.mount.clone(), } } + + /// Returns an object that can be used to send notifications to the kernel + #[cfg(feature = "abi-7-11")] + pub fn notifier(&self) -> Notifier { + Notifier::new(self.ch.sender()) + } } #[derive(Debug)] @@ -224,6 +232,9 @@ pub struct BackgroundSession { pub mountpoint: PathBuf, /// Thread guard of the background session pub guard: JoinHandle>, + /// Object for creating Notifiers for client use + #[cfg(feature = "abi-7-11")] + sender: ChannelSender, /// Ensures the filesystem is unmounted when the session ends _mount: Mount, } @@ -234,6 +245,8 @@ impl BackgroundSession { /// the filesystem is unmounted and the given session ends. pub fn new(se: Session) -> io::Result { let mountpoint = se.mountpoint().to_path_buf(); + #[cfg(feature = "abi-7-11")] + let sender = se.ch.sender(); // Take the fuse_session, so that we can unmount it let mount = std::mem::take(&mut *se.mount.lock().unwrap()); let mount = mount.ok_or_else(|| io::Error::from_raw_os_error(libc::ENODEV))?; @@ -243,6 +256,8 @@ impl BackgroundSession { Ok(BackgroundSession { mountpoint, guard, + #[cfg(feature = "abi-7-11")] + sender, _mount: mount, }) } @@ -251,11 +266,19 @@ impl BackgroundSession { let Self { mountpoint: _, guard, + #[cfg(feature = "abi-7-11")] + sender: _, _mount, } = self; drop(_mount); guard.join().unwrap().unwrap(); } + + /// Returns an object that can be used to send notifications to the kernel + #[cfg(feature = "abi-7-11")] + pub fn notifier(&self) -> Notifier { + Notifier::new(self.sender.clone()) + } } // replace with #[derive(Debug)] if Debug ever gets implemented for diff --git a/vendor/fuser/xfstests.Dockerfile b/vendor/fuser/xfstests.Dockerfile index c169761c9..1cafecc10 100644 --- a/vendor/fuser/xfstests.Dockerfile +++ b/vendor/fuser/xfstests.Dockerfile @@ -9,7 +9,9 @@ RUN adduser --disabled-password --gecos '' fsgqa RUN echo 'user_allow_other' >> /etc/fuse.conf -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.67.0 +ADD rust-toolchain /code/fuser/rust-toolchain + +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=$(cat /code/fuser/rust-toolchain) ENV PATH=/root/.cargo/bin:$PATH