diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 68bb144b8..ca21421e3 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -57,7 +57,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" - name: Build run: cargo build --release @@ -141,7 +141,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" - name: Build run: cargo build --release @@ -207,7 +207,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" - name: Build run: cargo build --release diff --git a/.github/workflows/bench_s3express.yml b/.github/workflows/bench_s3express.yml index b044dee5c..9dfb830c2 100644 --- a/.github/workflows/bench_s3express.yml +++ b/.github/workflows/bench_s3express.yml @@ -57,7 +57,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" - name: Build run: cargo build --release @@ -140,7 +140,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" - name: Build run: cargo build --release diff --git a/.github/workflows/crates.yml b/.github/workflows/crates.yml index 3b90b55ee..591ed221e 100644 --- a/.github/workflows/crates.yml +++ b/.github/workflows/crates.yml @@ -27,7 +27,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" - name: Package ${{ matrix.crate }} crate # `--no-verify` avoids building using crates.io dependencies, which for local packages may not be updated yet diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 248da85db..abbfe7021 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -81,7 +81,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" - name: Install operating system dependencies uses: ./.github/actions/install-dependencies @@ -133,7 +133,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" - name: Install operating system dependencies uses: ./.github/actions/install-dependencies @@ -174,7 +174,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" components: rust-src - name: Install operating system dependencies diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 461d05be2..37a1a616f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -35,7 +35,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" - name: Cargo cache uses: actions/cache@v3 @@ -74,7 +74,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" - name: Cargo cache uses: actions/cache@v3 @@ -108,7 +108,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" - name: Cargo cache uses: actions/cache@v3 @@ -140,7 +140,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" - name: Cargo cache uses: actions/cache@v3 @@ -168,7 +168,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" - name: Install operating system dependencies uses: ./.github/actions/install-dependencies @@ -194,11 +194,13 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + submodules: true - name: Set up Rust toolchain uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" components: rustfmt - name: Check format @@ -221,7 +223,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" components: clippy - name: Cargo cache @@ -250,7 +252,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" components: rust-docs - name: Cargo cache @@ -279,7 +281,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: # setup-rust-toolchain sets "-D warnings" by default, and Rust treats any warning as compile error. - # We need to this currently because `vendor/fuser` contains some warnings and it breaks the build. + # We need to this currently because `mountpoint-s3-fuser` contains some warnings and it breaks the build. rustflags: "" - name: Install operating system dependencies uses: ./.github/actions/install-dependencies @@ -306,5 +308,7 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + submodules: true - name: Run cargo deny uses: EmbarkStudios/cargo-deny-action@v2 diff --git a/.gitmodules b/.gitmodules index 7274b0e73..45122cb54 100644 --- a/.gitmodules +++ b/.gitmodules @@ -33,5 +33,5 @@ url = https://github.com/awslabs/aws-c-s3.git [submodule "mountpoint-s3-fuser"] path = mountpoint-s3-fuser - url = git@github.com:awslabs/mountpoint-s3.git + url = https://github.com/awslabs/mountpoint-s3.git branch = fuser/fork diff --git a/Cargo.toml b/Cargo.toml index c65ca60d8..d87b365e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = [ "mountpoint-s3-crt-sys", "mountpoint-s3-client", "mountpoint-s3", - "vendor/fuser", + "mountpoint-s3-fuser", ] resolver = "2" diff --git a/Makefile b/Makefile index 07b80f44a..d213fbfb8 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -# Most of these targets just exist so we can avoid running clippy and rustfmt on the vendored -# fuser crate. If those tools ever get stabilized `ignore` features, or we stop vendoring, this can +# Most of these targets just exist so we can avoid running clippy and rustfmt on the +# `fuser`'s fork crate (`mountpoint-s3-fuser`). If those tools ever get stabilized `ignore` features, or we stop vendoring, this can # go away. CRATES = mountpoint-s3-crt-sys mountpoint-s3-crt mountpoint-s3-client mountpoint-s3 diff --git a/mountpoint-s3/Cargo.toml b/mountpoint-s3/Cargo.toml index cb75f0f30..393f1c6ff 100644 --- a/mountpoint-s3/Cargo.toml +++ b/mountpoint-s3/Cargo.toml @@ -7,7 +7,7 @@ publish = false default-run = "mount-s3" [dependencies] -fuser = { path = "../vendor/fuser", version = "0.15.0", features = ["abi-7-28"] } +fuser = { path = "../mountpoint-s3-fuser", version = "0.15.0", features = ["abi-7-28"] } mountpoint-s3-client = { path = "../mountpoint-s3-client", version = "0.13.1" } anyhow = { version = "1.0.95", features = ["backtrace"] } diff --git a/vendor-fuser.sh b/vendor-fuser.sh index 0090a71e9..73326b6d0 100755 --- a/vendor-fuser.sh +++ b/vendor-fuser.sh @@ -1,22 +1,13 @@ #!/usr/bin/env bash set -euo pipefail -FUSER_VENDOR_PATH="vendor/fuser" +FUSER_PATH="mountpoint-s3-fuser" BASE_PATH=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -FUSER_FULL_PATH="$BASE_PATH/$FUSER_VENDOR_PATH" - -STATUS=$(git status --porcelain $FUSER_FULL_PATH) -if [ -n "$STATUS" ]; then - echo >&2 "Refusing to re-vendor because the fuser directory is dirty" - exit 1 -fi +FUSER_FULL_PATH="$BASE_PATH/$FUSER_PATH" rm -rf $FUSER_FULL_PATH -git clone --branch fuser/fork ssh://git@github.com/awslabs/mountpoint-s3.git $FUSER_FULL_PATH -COMMIT=$(git -C $FUSER_FULL_PATH rev-parse --short HEAD) - -rm -rf $FUSER_FULL_PATH/.git +git submodule update --remote $FUSER_FULL_PATH git add $FUSER_FULL_PATH diff --git a/vendor/fuser/.cirrus.yml b/vendor/fuser/.cirrus.yml deleted file mode 100644 index cc315e53b..000000000 --- a/vendor/fuser/.cirrus.yml +++ /dev/null @@ -1,14 +0,0 @@ -freebsd_instance: - image_family: freebsd-14-0 - -task: - name: FreeBSD - setup_script: - - pkg install -y pkgconf fusefs-libs fusefs-libs3 rust - build_script: - - cargo build --all --all-targets - doc_script: - - cargo doc --all --no-deps --features=abi-7-21 - test_script: - - cargo test --all --all-targets -- --skip=mnt::test::mount_unmount - - cargo test --all --all-targets --features=abi-7-21 -- --skip=mnt::test::mount_unmount diff --git a/vendor/fuser/.dockerignore b/vendor/fuser/.dockerignore deleted file mode 100644 index 49b03a586..000000000 --- a/vendor/fuser/.dockerignore +++ /dev/null @@ -1,10 +0,0 @@ -* -!src/ -!examples/ -!./Cargo.* -!./rust-toolchain -!./build.rs -!./pjdfs.sh -!./xfstests.sh -!./mount_tests.sh -!./simplefs_tests.sh diff --git a/vendor/fuser/.github/workflows/ci.yml b/vendor/fuser/.github/workflows/ci.yml deleted file mode 100644 index 629dce657..000000000 --- a/vendor/fuser/.github/workflows/ci.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: CI - -on: - pull_request: - push: # required for actions/cache to work - branches: - - master - -jobs: - compile: - runs-on: ubuntu-22.04 - strategy: - matrix: - libfuse: [libfuse-dev, libfuse3-dev] - features: [ '', 'abi-7-19' ] - - steps: - - uses: actions/checkout@v4 - - name: Install packages - run: | - sudo apt update - sudo apt install -y ${{ matrix.libfuse }} build-essential - - - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - target: x86_64-unknown-linux-musl - - - name: Run tests - run: | - cargo build --all --all-targets --features=${{ matrix.features }} - cargo build --all --all-targets --no-default-features - cargo build --target=x86_64-unknown-linux-musl --no-default-features - cargo test --all --features=${{ matrix.features }} - cargo doc --all --no-deps --features=${{ matrix.features }} - ci: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - name: Install packages - run: | - sudo apt update - sudo apt install -y libfuse-dev libfuse3-dev build-essential - - - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - components: rustfmt, clippy - - - uses: taiki-e/install-action@v2 - with: - tool: cargo-deny@0.14 - - - name: Run tests - run: INTERACTIVE="" make pre - - test: - runs-on: ubuntu-22.04 - strategy: - matrix: - test_group: [mount_tests, pjdfs_tests, xfstests] - - steps: - - uses: actions/checkout@v4 - - - name: Run tests - run: INTERACTIVE="" make ${{ matrix.test_group }} diff --git a/vendor/fuser/.gitignore b/vendor/fuser/.gitignore deleted file mode 100644 index 4c3ebad99..000000000 --- a/vendor/fuser/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/Cargo.lock -/target -.idea/ -logs/ diff --git a/vendor/fuser/CHANGELOG.md b/vendor/fuser/CHANGELOG.md deleted file mode 100644 index 347680644..000000000 --- a/vendor/fuser/CHANGELOG.md +++ /dev/null @@ -1,178 +0,0 @@ -# FUSE for Rust - Changelog - -## 0.15.0 - 2024-10-25 -* Add file handle argument to `getattr()` -* Change `poll()` to take a `PollHandle` instead of a `u64` -* Add low level API for manually mounting or wrapping a fuse file descriptor into a `Session` -* Fix compatibility with MacFUSE 4.x -* Performance optimizations - -## 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 - -## 0.12.0 - 2022-12-13 -* Add method to `Session` to unmount non-`Send` `Filesystem`s - -## 0.11.1 - 2022-08-24 -* Improve an error message when using libfuse2 - -## 0.11.0 - 2022-03-05 -* Add `spawn_mount2()` -* Deprecate `spawn_mount()` - -## 0.10.0 - 2022-01-06 -* Improve error messages -* Support compiling with musl -* Default `link()` & `symlink()` now return EPERM instead of ENOSYS - -## 0.9.1 - 2021-09-07 -* `forget` and `batch_forget` no longer require that `AllowRoot` be set - -## 0.9.0 - 2021-08-31 -* Ensure that `Filesystem::destroy` is always called, when the filesystem is unmounted -* Remove request parameter from `Filesystem::destroy`. -* Make `fuse_forget_one` public, so that `Filesystem::batch_forget` can be implemented by users. -* Fix `batch_forget`. Previously, it always received an empty list of inodes. -* Fix `MountOption::AllowRoot`. Previously, using it resulted in a crash. -* Fix `MountOption::AutoUnmount` so that it works when `AllowRoot` and `AllowOther` are both not set. -* Make log messages more verbose (now includes the operation) - -## 0.8.0 - 2021-06-11 -* Deprecate `mount()` -* Remove `FileAttr.padding`. This field was added by mistake, and does nothing -* Fix crash when receiving an unknown FUSE operation type -* Minor performance optimizations - -## 0.7.0 - 2021-01-10 -* Support building with MacFuse 4.x on OSX -* Support configuring max_write & max_readahead via `KernelConfig` during `init` -* Support configuring filesystem timestamp granularity via `KernelConfig.set_time_granularity` during `init` -* Support requesting additional capability flags via `KernelConfig.add_capabilities` during `init` - -## 0.6.0 - 2020-11-22 -* Make `spawn_mount()` safe -* Change `flags` parameter of `create()`, `open()`, `opendir()`, `release()`, `releasedir()` to be signed, so that it matches - libfuse and the associated constants in libc -* Change `flags` parameter of `setxattr()` to be signed, so that it matches libfuse -* Change `mask` parameter of `access()` to be signed, so that it matches libfuse and the associated constants in libc -* Change lock type parameter of `getlk()` and `setlk()` to be signed, so that it matches libfuse and the associated constants in libc -* Change atime & atime_now and mtime & mtime_now parameters of `setattr()` to make their relationship more obvious -* Add `lock_owner` and file `flags` parameters to `read()` and `write()` -* Add `umask` parameter to `mknod()`, `mkdir()` and `create()` -* Add `KernelConfig` parameter to `init()` to allow `Filesystem` to configure the kernel connection attributes -* Add support for `fallocate()`, `ioctl()`, `copy_file_range()`, and `lseek()` -* Add support for FUSE_BATCH_FORGET -* Add support for FUSE_READDIRPLUS -* Add support for FUSE_RENAME2 -* Add FUSE_WRITE_KILL_PRIV flag for `write()` -* Add FUSE_WRITEBACK_CACHE flag -* Add FUSE_NO_OPEN_SUPPORT flag -* Add FUSE_PARALLEL_DIROPS flag -* Add FUSE_HANDLE_KILLPRIV flag -* Add FUSE_POSIX_ACL flag -* Add FUSE_ABORT_ERROR flag -* Add FUSE_NO_OPENDIR_SUPPORT flag -* Add FUSE_CACHE_SYMLINKS flag -* Add FUSE_EXPLICIT_INVAL_DATA flag -* Add FUSE_IOCTL_COMPAT_X32 flag -* Add FOPEN_CACHE_DIR flag -* Add FOPEN_STREAM flag -* Add FUSE_MAX_PAGES flag -* Add max_pages, and time_gran support to init code path (these are not currently configurable) -* Add support for ctime in `setattr()` -* Add support for timestamps before the unix epoch in `getattr()` and `setattr()` - -## 0.5.0 - 2020-10-17 - -* Enable FUSE_BIG_WRITES for ABI >= 7.10 -* Add FUSE_AUTO_INVAL_DATA constant -* Add ABI 7.20 to 7.31 feature flags. Support for these are incomplete. -* Add support for building with libfuse3 -* Add support for building without libfuse/libfuse3 on Linux (i.e. there's now a pure Rust implementation of all features) -* Add `mount2()` with improved option API - -## 0.4.1 - 2020-10-12 - -* Added new feature `serializable` that will enable serde serialization/deserialization for `FileType`, `FileAttr` - -## 0.4.0 - 2020-06-18 - -* Forked as `fuser` crate, at https://github.com/cberner/fuser -* Add ATIME_NOW and MTIME_NOW support -* Add stubs for ioctl, fallocate, and poll for ABI 7.11 - -## 0.3.1 - 2017-11-08 - -* Offsets to `read`, `write` and `readdir` methods are signed integers now (breaking change, sorry) -* Link `libosxfuse` on macOS, `libfuse` on all other systems - -## 0.3.0 - 2017-01-06 - -* Fix extended attribute handling (`getxattr` and `listxattr` methods changed and `ReplyXattr` was added) -* `mount` now also returns a `Result` since it may fail if the session fails to run -* Filenames are now passed as `&OsStr` in the filesystem interface -* Removed publishing of documentation on GitHub pages. Docs are now available on https://docs.rs/fuse -* Add `FileType::Socket` - -## 0.2.8 - 2016-07-31 - -* Documentation of releases is build by CI now and made available at https://zargony.github.io/rust-fuse -* Fix `unmount` on BSD systems -* Simplified `libfuse` detection with `pkg-config` -* `ReplyDirectory::sized` was removed since it was impossible to use it safely - -## 0.2.7 - 2015-09-08 - -* Update to latest Rust stable - no longer needs nightly Rust -* A filesystem implementation doesn't need to be `Send` anymore to be mounted synchronously -* A filesystem implementation doesn't need to be 'static anymore to be mounted asynchronously -* CI tests are covering nightly, beta and stable Rust under OSX and Linux now - -## 0.2.6 - 2015-04-23 - -* Update to latest Rust nightly -* Fix mounting of filesystems as non-root on Linux systems - -## 0.2.5 - 2015-03-21 - -* Update to latest Rust nightly -* `unmount` returns a `Result` now since unmounting may fail internally -* Fix `unmount` on Linux systems -* Remove deprecated file types from interface (got rid of `std::old_io`) -* Introducing `FileType` - -## 0.2.4 - 2015-02-22 - -* Update to latest Rust nightly -* `spawn_mount` returns a `Result` now since starting a new thread may fail -* Paths are now passed using `std::path::Path` (got rid of `std::old_path`) -* FUSE options are now passed as a slice of `OsStr` rather than a slice of bytes - -## 0.2.3 - 2015-01-17 - -* Update to latest Rust nightly - -## 0.2.2 - 2015-01-14 - -* Update to latest Rust nightly -* Ensure that `Reply` is `Send` to support asynchronous processing -* Add CI testing under Linux - -## 0.2.1 - 2015-01-07 - -* Update to latest Rust nightly -* Use `build.rs` and `pkg-config` to discover `libfuse` / `libosxfuse` - -## 0.2.0 - 2014-12-25 - -Initial release - -## pre-0.2.0 - 2013-10-03 - -No versioning (based on make, cargo and crates.io didn't exist yet) diff --git a/vendor/fuser/Cargo.toml b/vendor/fuser/Cargo.toml deleted file mode 100644 index 93befc3c0..000000000 --- a/vendor/fuser/Cargo.toml +++ /dev/null @@ -1,84 +0,0 @@ -[package] -name = "fuser" -description = "Filesystem in Userspace (FUSE) for Rust" -license = "MIT" -repository = "https://github.com/cberner/fuser" -documentation = "https://docs.rs/fuser" -homepage = "https://github.com/cberner/fuser" -version = "0.15.0" -edition = "2021" -readme = "README.md" -authors = ["Christopher Berner "] -keywords = ["fuse", "filesystem", "system", "bindings"] -categories = ["external-ffi-bindings", "api-bindings", "filesystem", "os::unix-apis"] -build = "build.rs" - -[dependencies] -libc = "0.2.51" -log = "0.4.6" -memchr = "2.7.2" -page_size = "0.6.0" -serde = { version = "1.0.102", features = ["std", "derive"], optional = true } -smallvec = "1.6.1" -zerocopy = { version = "0.8", features = ["derive"] } -nix = { version = "0.29.0", features = ["fs", "user"] } - -[dev-dependencies] -env_logger = "0.11.3" -clap = { version = "4.4", features = ["cargo", "derive"] } -bincode = "1.3.1" -serde = { version = "1.0.102", features = ["std", "derive"] } -tempfile = "3.10.1" -nix = { version = "0.29.0", features = ["poll", "fs", "ioctl"] } - -[build-dependencies] -pkg-config = { version = "0.3.14", optional = true } - -[features] -default = ["libfuse"] -libfuse = ["pkg-config"] -serializable = ["serde"] -macfuse-4-compat = [] -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"] -abi-7-20 = ["abi-7-19"] -abi-7-21 = ["abi-7-20"] -abi-7-22 = ["abi-7-21"] -abi-7-23 = ["abi-7-22"] -abi-7-24 = ["abi-7-23"] -abi-7-25 = ["abi-7-24"] -abi-7-26 = ["abi-7-25"] -abi-7-27 = ["abi-7-26"] -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-15"] - -[[example]] -name = "ioctl" -required-features = ["abi-7-11"] diff --git a/vendor/fuser/LICENSE.md b/vendor/fuser/LICENSE.md deleted file mode 100644 index 74bbec2f2..000000000 --- a/vendor/fuser/LICENSE.md +++ /dev/null @@ -1,23 +0,0 @@ -The MIT License (MIT) -===================== - -Copyright (c) 2020-present Christopher Berner - -Copyright © `2013-2019` `Andreas Neuhaus` `https://zargony.com/` - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the “Software”), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/fuser/Makefile b/vendor/fuser/Makefile deleted file mode 100644 index e6bd37407..000000000 --- a/vendor/fuser/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -VERSION = $(shell git describe --tags --always --dirty) -INTERACTIVE ?= i - - -build: pre - cargo build --examples - -pre: - cargo fmt --all -- --check - cargo deny check licenses - cargo clippy --all-targets - cargo clippy --all-targets --no-default-features - cargo clippy --all-targets --features=abi-7-30 - -xfstests: - docker build -t fuser:xfstests -f xfstests.Dockerfile . - # Additional permissions are needed to be able to mount FUSE - docker run --rm -$(INTERACTIVE)t --cap-add SYS_ADMIN --cap-add IPC_OWNER --device /dev/fuse --security-opt apparmor:unconfined \ - --memory=2g --kernel-memory=200m \ - -v "$(shell pwd)/logs:/code/logs" fuser:xfstests bash -c "cd /code/fuser && ./xfstests.sh" - -pjdfs_tests: pjdfs_tests_fuse2 pjdfs_tests_fuse3 pjdfs_tests_pure - -pjdfs_tests_fuse2: - docker build --build-arg BUILD_FEATURES='--features=abi-7-19' -t fuser:pjdfs-2 -f pjdfs.Dockerfile . - # Additional permissions are needed to be able to mount FUSE - docker run --rm -$(INTERACTIVE)t --cap-add SYS_ADMIN --device /dev/fuse --security-opt apparmor:unconfined \ - -v "$(shell pwd)/logs:/code/logs" fuser:pjdfs-2 bash -c "cd /code/fuser && ./pjdfs.sh" - -pjdfs_tests_fuse3: - docker build --build-arg BUILD_FEATURES='--features=abi-7-31' -t fuser:pjdfs-3 -f pjdfs.Dockerfile . - # Additional permissions are needed to be able to mount FUSE - docker run --rm -$(INTERACTIVE)t --cap-add SYS_ADMIN --device /dev/fuse --security-opt apparmor:unconfined \ - -v "$(shell pwd)/logs:/code/logs" fuser:pjdfs-3 bash -c "cd /code/fuser && ./pjdfs.sh" - -pjdfs_tests_pure: - docker build --build-arg BUILD_FEATURES='--no-default-features --features=abi-7-19' -t fuser:pjdfs-pure -f pjdfs.Dockerfile . - # Additional permissions are needed to be able to mount FUSE - docker run --rm -$(INTERACTIVE)t --cap-add SYS_ADMIN --device /dev/fuse --security-opt apparmor:unconfined \ - -v "$(shell pwd)/logs:/code/logs" fuser:pjdfs-pure bash -c "cd /code/fuser && ./pjdfs.sh" - -mount_tests: - docker build -t fuser:mount_tests -f mount_tests.Dockerfile . - # Additional permissions are needed to be able to mount FUSE - docker run --rm -$(INTERACTIVE)t --cap-add SYS_ADMIN --device /dev/fuse --security-opt apparmor:unconfined \ - fuser:mount_tests bash -c "cd /code/fuser && bash ./simplefs_tests.sh" - docker run --rm -$(INTERACTIVE)t --cap-add SYS_ADMIN --device /dev/fuse --security-opt apparmor:unconfined \ - fuser:mount_tests bash -c "cd /code/fuser && bash ./mount_tests.sh" - -test: pre mount_tests pjdfs_tests xfstests - cargo test diff --git a/vendor/fuser/README.md b/vendor/fuser/README.md deleted file mode 100644 index 3807c7201..000000000 --- a/vendor/fuser/README.md +++ /dev/null @@ -1,156 +0,0 @@ -## Fork of fuser for Mountpoint - -This is a fork of the excellent [`fuser`](https://github.com/cberner/fuser) Rust crate for FUSE bindings, with some Mountpoint-specific changes to improve performance of concurrent operations. We'll be working to upstream these changes soon. - -### Fork Maintenance - -This fork should be maintained in the `fuser/fork` branch of the [awslabs/mountpoint-s3 repository on GitHub](https://github.com/awslabs/mountpoint-s3/tree/main/vendor/fuser). - -To pull in new changes from upstream, you should fetch and rebase the changes locally and then force push to the `fuser/fork` branch (- not ideal!). -Once the `fuser/fork` branch is as desired, create a new branch from Mountpoint's `main` branch. -Run the [`vendor-fuser.sh` script](https://github.com/awslabs/mountpoint-s3/blob/main/vendor-fuser.sh). -You should open a pull request with the new commit created by the script. -This pull request is where the changes are reviewed. - -If you wish to add new divergent changes to this fork of Fuser, -open a pull request on the Mountpoint repository branching from the `fuser/fork` branch and use `fuser/fork` as the base branch when creating the pull request. - ---- - -# FUSE (Filesystem in Userspace) for Rust - -![CI](https://github.com/cberner/fuser/actions/workflows/ci.yml/badge.svg) -[![Crates.io](https://img.shields.io/crates/v/fuser.svg)](https://crates.io/crates/fuser) -[![Documentation](https://docs.rs/fuser/badge.svg)](https://docs.rs/fuser) -[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/cberner/fuser/blob/master/LICENSE.md) -[![dependency status](https://deps.rs/repo/github/cberner/fuser/status.svg)](https://deps.rs/repo/github/cberner/fuser) - -## About - -**FUSE-Rust** is a [Rust] library crate for easy implementation of [FUSE filesystems][FUSE for Linux] in userspace. - -FUSE-Rust does not just provide bindings, it is a rewrite of the original FUSE C library to fully take advantage of Rust's architecture. - -This library was originally forked from the [`fuse` crate](https://github.com/zargony/fuse-rs) with the intention -of continuing development. In particular adding features from ABIs after 7.19 - -## Documentation - -[FUSE-Rust reference][Documentation] - -## Details - -A working FUSE filesystem consists of three parts: - -1. The **kernel driver** that registers as a filesystem and forwards operations into a communication channel to a userspace process that handles them. -1. The **userspace library** (libfuse) that helps the userspace process to establish and run communication with the kernel driver. -1. The **userspace implementation** that actually processes the filesystem operations. - -The kernel driver is provided by the FUSE project, the userspace implementation needs to be provided by the developer. FUSE-Rust provides a replacement for the libfuse userspace library between these two. This way, a developer can fully take advantage of the Rust type interface and runtime features when building a FUSE filesystem in Rust. - -Except for a single setup (mount) function call and a final teardown (umount) function call to libfuse, everything runs in Rust, and on Linux these calls to libfuse are optional. They can be removed by building without the "libfuse" feature flag. - -## Dependencies - -FUSE must be installed to build or run programs that use FUSE-Rust (i.e. kernel driver and libraries. Some platforms may also require userland utils like `fusermount`). A default installation of FUSE is usually sufficient. - -To build FUSE-Rust or any program that depends on it, `pkg-config` needs to be installed as well. - -### Linux - -[FUSE for Linux] is available in most Linux distributions and usually called `fuse` or `fuse3` (this crate is compatible with both). To install on a Debian based system: - -```sh -sudo apt-get install fuse3 libfuse3-dev -``` - -Install on CentOS: - -```sh -sudo yum install fuse -``` - -To build, FUSE libraries and headers are required. The package is usually called `libfuse-dev` or `fuse-devel`. Also `pkg-config` is required for locating libraries and headers. - -```sh -sudo apt-get install libfuse-dev pkg-config -``` - -```sh -sudo yum install fuse-devel pkgconfig -``` - -### macOS (untested) - -Installer packages can be downloaded from the [FUSE for macOS homepage][FUSE for macOS]. This is the *kernel* part that needs to be installed always. - -#### To install using Homebrew - -```sh -brew install macfuse -``` - -#### To install using Nix - -``` sh -nix-env -iA nixos.osxfuse -``` - -And `pkg-config` (required for building): - -``` sh -nix-env -iA nixos.pkg-config -``` - -When using `nix` it is required that you specify `PKG_CONFIG_PATH` environment variable to point at where `osxfuse` is installed: - -``` sh -export PKG_CONFIG_PATH=${HOME}/.nix-profile/lib/pkgconfig -``` - -### FreeBSD - -Install packages `fusefs-libs` and `pkgconf`. - -```sh -pkg install fusefs-libs pkgconf -``` - -## Usage - -Put this in your `Cargo.toml`: - -```toml -[dependencies] -fuser = "0.7" -``` - -To create a new filesystem, implement the trait `fuser::Filesystem`. See the [documentation] for details or the `examples` directory for some basic examples. - -## To Do - -Most features of libfuse up to 3.10.3 are implemented. Feel free to contribute. See the [list of issues][issues] on GitHub and search the source files for comments containing "`TODO`" or "`FIXME`" to see what's still missing. - -## Compatibility - -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. - -[Rust]: https://rust-lang.org -[Homebrew]: https://brew.sh -[Changelog]: https://keepachangelog.com/en/1.0.0/ - -[FUSE-Rust]: https://github.com/cberner/fuser -[issues]: https://github.com/cberner/fuser/issues -[Documentation]: https://docs.rs/fuser - -[FUSE for Linux]: https://github.com/libfuse/libfuse/ -[FUSE for macOS]: https://osxfuse.github.io -[FUSE for FreeBSD]: https://wiki.freebsd.org/FUSEFS diff --git a/vendor/fuser/build.rs b/vendor/fuser/build.rs deleted file mode 100644 index 92ffe7fbf..000000000 --- a/vendor/fuser/build.rs +++ /dev/null @@ -1,52 +0,0 @@ -fn main() { - // Register rustc cfg for switching between mount implementations. - // When fuser MSRV is updated to v1.77 or above, we should switch from 'cargo:' to 'cargo::' syntax. - println!("cargo:rustc-check-cfg=cfg(fuser_mount_impl, values(\"pure-rust\", \"libfuse2\", \"libfuse3\"))"); - - #[cfg(all(not(feature = "libfuse"), not(target_os = "linux")))] - unimplemented!("Building without libfuse is only supported on Linux"); - - #[cfg(not(feature = "libfuse"))] - { - println!("cargo:rustc-cfg=fuser_mount_impl=\"pure-rust\""); - } - #[cfg(feature = "libfuse")] - { - if cfg!(target_os = "macos") { - if pkg_config::Config::new() - .atleast_version("2.6.0") - .probe("fuse") // for macFUSE 4.x - .map_err(|e| eprintln!("{}", e)) - .is_ok() - { - println!("cargo:rustc-cfg=fuser_mount_impl=\"libfuse2\""); - println!("cargo:rustc-cfg=feature=\"macfuse-4-compat\""); - } else { - pkg_config::Config::new() - .atleast_version("2.6.0") - .probe("osxfuse") // for osxfuse 3.x - .map_err(|e| eprintln!("{}", e)) - .unwrap(); - println!("cargo:rustc-cfg=fuser_mount_impl=\"libfuse2\""); - } - } else { - // First try to link with libfuse3 - if pkg_config::Config::new() - .atleast_version("3.0.0") - .probe("fuse3") - .map_err(|e| eprintln!("{e}")) - .is_ok() - { - println!("cargo:rustc-cfg=fuser_mount_impl=\"libfuse3\""); - } else { - // Fallback to libfuse - pkg_config::Config::new() - .atleast_version("2.6.0") - .probe("fuse") - .map_err(|e| eprintln!("{e}")) - .unwrap(); - println!("cargo:rustc-cfg=fuser_mount_impl=\"libfuse2\""); - } - } - } -} diff --git a/vendor/fuser/deny.toml b/vendor/fuser/deny.toml deleted file mode 100644 index 3e3f3e771..000000000 --- a/vendor/fuser/deny.toml +++ /dev/null @@ -1,142 +0,0 @@ -# This template contains all of the possible sections and their default values - -# Note that all fields that take a lint level have these possible values: -# * deny - An error will be produced and the check will fail -# * warn - A warning will be produced, but the check will not fail -# * allow - No warning or error will be produced, though in some cases a note -# will be - -# The values provided in this template are the default values that will be used -# when any section or field is not specified in your own configuration - -# This section is considered when running `cargo deny check advisories` -# More documentation for the advisories section can be found here: -# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html -[advisories] -# The path where the advisory database is cloned/fetched into -db-path = "~/.cargo/advisory-db" -# The url of the advisory database to use -db-urls = ["https://github.com/rustsec/advisory-db"] -# The lint level for crates that have been yanked from their source registry -yanked = "warn" -# A list of advisory IDs to ignore. Note that ignored advisories will still -# output a note when they are encountered. -ignore = [ - #"RUSTSEC-0000-0000", -] -# Threshold for security vulnerabilities, any vulnerability with a CVSS score -# lower than the range specified will be ignored. Note that ignored advisories -# will still output a note when they are encountered. -# * None - CVSS Score 0.0 -# * Low - CVSS Score 0.1 - 3.9 -# * Medium - CVSS Score 4.0 - 6.9 -# * High - CVSS Score 7.0 - 8.9 -# * Critical - CVSS Score 9.0 - 10.0 -#severity-threshold = - -# This section is considered when running `cargo deny check licenses` -# More documentation for the licenses section can be found here: -# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html -[licenses] -# List of explictly allowed licenses -# See https://spdx.org/licenses/ for list of possible licenses -# [possible values: any SPDX 3.7 short identifier (+ optional exception)]. -allow = [ - "BSD-2-Clause", -# "BSD-3-Clause", - "MIT", -] -# The confidence threshold for detecting a license from license text. -# The higher the value, the more closely the license text must be to the -# canonical license text of a valid SPDX license file. -# [possible values: any between 0.0 and 1.0]. -confidence-threshold = 0.8 -# Allow 1 or more licenses on a per-crate basis, so that particular licenses -# aren't accepted for every possible crate as with the normal allow list -exceptions = [ - # Each entry is the crate and version constraint, and its specific allow - # list - #{ allow = ["Zlib"], name = "adler32", version = "*" }, - { allow = ["Unicode-3.0"], name = "unicode-ident", version = "*" }, -] - -# Some crates don't have (easily) machine readable licensing information, -# adding a clarification entry for it allows you to manually specify the -# licensing information -#[[licenses.clarify]] -# The name of the crate the clarification applies to -#name = "ring" -# THe optional version constraint for the crate -#version = "*" -# The SPDX expression for the license requirements of the crate -#expression = "MIT AND ISC AND OpenSSL" -# One or more files in the crate's source used as the "source of truth" for -# the license expression. If the contents match, the clarification will be used -# when running the license check, otherwise the clarification will be ignored -# and the crate will be checked normally, which may produce warnings or errors -# depending on the rest of your configuration -#license-files = [ - # Each entry is a crate relative path, and the (opaque) hash of its contents - #{ path = "LICENSE", hash = 0xbd0eed23 } -#] - -[licenses.private] -# If true, ignores workspace crates that aren't published, or are only -# published to private registries -ignore = false -# One or more private registries that you might publish crates to, if a crate -# is only published to private registries, and ignore is true, the crate will -# not have its license(s) checked -registries = [ - #"https://sekretz.com/registry -] - -# This section is considered when running `cargo deny check bans`. -# More documentation about the 'bans' section can be found here: -# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html -[bans] -# Lint level for when multiple versions of the same crate are detected -multiple-versions = "warn" -# The graph highlighting used when creating dotgraphs for crates -# with multiple versions -# * lowest-version - The path to the lowest versioned duplicate is highlighted -# * simplest-path - The path to the version with the fewest edges is highlighted -# * all - Both lowest-version and simplest-path are used -highlight = "all" -# List of crates that are allowed. Use with care! -allow = [ - #{ name = "ansi_term", version = "=0.11.0" }, -] -# List of crates to deny -deny = [ - # Each entry the name of a crate and a version range. If version is - # not specified, all versions will be matched. - #{ name = "ansi_term", version = "=0.11.0" }, -] -# Certain crates/versions that will be skipped when doing duplicate detection. -skip = [ - #{ name = "ansi_term", version = "=0.11.0" }, -] -# Similarly to `skip` allows you to skip certain crates during duplicate -# detection. Unlike skip, it also includes the entire tree of transitive -# dependencies starting at the specified crate, up to a certain depth, which is -# by default infinite -skip-tree = [ - #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, -] - -# This section is considered when running `cargo deny check sources`. -# More documentation about the 'sources' section can be found here: -# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html -[sources] -# Lint level for what to happen when a crate from a crate registry that is not -# in the allow list is encountered -unknown-registry = "warn" -# Lint level for what to happen when a crate from a git repository that is not -# in the allow list is encountered -unknown-git = "warn" -# List of URLs for allowed crate registries. Defaults to the crates.io index -# if not specified. If it is specified but empty, no registries are allowed. -allow-registry = ["https://github.com/rust-lang/crates.io-index"] -# List of URLs for allowed Git repositories -allow-git = [] diff --git a/vendor/fuser/examples/hello.rs b/vendor/fuser/examples/hello.rs deleted file mode 100644 index 6a69cbbe0..000000000 --- a/vendor/fuser/examples/hello.rs +++ /dev/null @@ -1,149 +0,0 @@ -use clap::{crate_version, Arg, ArgAction, Command}; -use fuser::{ - FileAttr, FileType, Filesystem, MountOption, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, - Request, -}; -use libc::ENOENT; -use std::ffi::OsStr; -use std::time::{Duration, UNIX_EPOCH}; - -const TTL: Duration = Duration::from_secs(1); // 1 second - -const HELLO_DIR_ATTR: FileAttr = FileAttr { - ino: 1, - size: 0, - blocks: 0, - atime: UNIX_EPOCH, // 1970-01-01 00:00:00 - mtime: UNIX_EPOCH, - ctime: UNIX_EPOCH, - crtime: UNIX_EPOCH, - kind: FileType::Directory, - perm: 0o755, - nlink: 2, - uid: 501, - gid: 20, - rdev: 0, - flags: 0, - blksize: 512, -}; - -const HELLO_TXT_CONTENT: &str = "Hello World!\n"; - -const HELLO_TXT_ATTR: FileAttr = FileAttr { - ino: 2, - size: 13, - blocks: 1, - atime: UNIX_EPOCH, // 1970-01-01 00:00:00 - mtime: UNIX_EPOCH, - ctime: UNIX_EPOCH, - crtime: UNIX_EPOCH, - kind: FileType::RegularFile, - perm: 0o644, - nlink: 1, - uid: 501, - gid: 20, - rdev: 0, - flags: 0, - blksize: 512, -}; - -struct HelloFS; - -impl Filesystem for HelloFS { - fn lookup(&self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { - if parent == 1 && name.to_str() == Some("hello.txt") { - reply.entry(&TTL, &HELLO_TXT_ATTR, 0); - } else { - reply.error(ENOENT); - } - } - - fn getattr(&self, _req: &Request, ino: u64, _fh: Option, reply: ReplyAttr) { - match ino { - 1 => reply.attr(&TTL, &HELLO_DIR_ATTR), - 2 => reply.attr(&TTL, &HELLO_TXT_ATTR), - _ => reply.error(ENOENT), - } - } - - fn read( - &self, - _req: &Request, - ino: u64, - _fh: u64, - offset: i64, - _size: u32, - _flags: i32, - _lock: Option, - reply: ReplyData, - ) { - if ino == 2 { - reply.data(&HELLO_TXT_CONTENT.as_bytes()[offset as usize..]); - } else { - reply.error(ENOENT); - } - } - - fn readdir( - &self, - _req: &Request, - ino: u64, - _fh: u64, - offset: i64, - mut reply: ReplyDirectory, - ) { - if ino != 1 { - reply.error(ENOENT); - return; - } - - let entries = vec![ - (1, FileType::Directory, "."), - (1, FileType::Directory, ".."), - (2, FileType::RegularFile, "hello.txt"), - ]; - - for (i, entry) in entries.into_iter().enumerate().skip(offset as usize) { - // i + 1 means the index of the next entry - if reply.add(entry.0, (i + 1) as i64, entry.1, entry.2) { - break; - } - } - reply.ok(); - } -} - -fn main() { - let matches = Command::new("hello") - .version(crate_version!()) - .author("Christopher Berner") - .arg( - Arg::new("MOUNT_POINT") - .required(true) - .index(1) - .help("Act as a client, and mount FUSE at given path"), - ) - .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.get_one::("MOUNT_POINT").unwrap(); - let mut options = vec![MountOption::RO, MountOption::FSName("hello".to_string())]; - if matches.get_flag("auto_unmount") { - options.push(MountOption::AutoUnmount); - } - if matches.get_flag("allow-root") { - options.push(MountOption::AllowRoot); - } - fuser::mount2(HelloFS, mountpoint, &options).unwrap(); -} diff --git a/vendor/fuser/examples/ioctl.rs b/vendor/fuser/examples/ioctl.rs deleted file mode 100644 index d329d2d51..000000000 --- a/vendor/fuser/examples/ioctl.rs +++ /dev/null @@ -1,211 +0,0 @@ -// This example requires fuse 7.11 or later. Run with: -// -// cargo run --example ioctl --features abi-7-11 /tmp/foobar - -use clap::{crate_version, Arg, ArgAction, Command}; -use fuser::{ - FileAttr, FileType, Filesystem, MountOption, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, - Request, -}; -use libc::{EINVAL, ENOENT}; -use log::debug; -use std::ffi::OsStr; -use std::sync::Mutex; -use std::time::{Duration, UNIX_EPOCH}; - -const TTL: Duration = Duration::from_secs(1); // 1 second - -struct FiocFS { - content: Mutex>, - root_attr: FileAttr, - fioc_file_attr: FileAttr, -} - -impl FiocFS { - fn new() -> Self { - let uid = unsafe { libc::getuid() }; - let gid = unsafe { libc::getgid() }; - - let root_attr = FileAttr { - ino: 1, - size: 0, - blocks: 0, - atime: UNIX_EPOCH, // 1970-01-01 00:00:00 - mtime: UNIX_EPOCH, - ctime: UNIX_EPOCH, - crtime: UNIX_EPOCH, - kind: FileType::Directory, - perm: 0o755, - nlink: 2, - uid, - gid, - rdev: 0, - flags: 0, - blksize: 512, - }; - - let fioc_file_attr = FileAttr { - ino: 2, - size: 0, - blocks: 1, - atime: UNIX_EPOCH, // 1970-01-01 00:00:00 - mtime: UNIX_EPOCH, - ctime: UNIX_EPOCH, - crtime: UNIX_EPOCH, - kind: FileType::RegularFile, - perm: 0o644, - nlink: 1, - uid, - gid, - rdev: 0, - flags: 0, - blksize: 512, - }; - - Self { - content: vec![].into(), - root_attr, - fioc_file_attr, - } - } -} - -impl Filesystem for FiocFS { - fn lookup(&self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { - if parent == 1 && name.to_str() == Some("fioc") { - reply.entry(&TTL, &self.fioc_file_attr, 0); - } else { - reply.error(ENOENT); - } - } - - fn getattr(&self, _req: &Request, ino: u64, _fh: Option, reply: ReplyAttr) { - match ino { - 1 => reply.attr(&TTL, &self.root_attr), - 2 => reply.attr(&TTL, &self.fioc_file_attr), - _ => reply.error(ENOENT), - } - } - - fn read( - &self, - _req: &Request, - ino: u64, - _fh: u64, - offset: i64, - _size: u32, - _flags: i32, - _lock: Option, - reply: ReplyData, - ) { - if ino == 2 { - let content = self.content.lock().unwrap(); - reply.data(&content[offset as usize..]) - } else { - reply.error(ENOENT); - } - } - - fn readdir( - &self, - _req: &Request, - ino: u64, - _fh: u64, - offset: i64, - mut reply: ReplyDirectory, - ) { - if ino != 1 { - reply.error(ENOENT); - return; - } - - let entries = vec![ - (1, FileType::Directory, "."), - (1, FileType::Directory, ".."), - (2, FileType::RegularFile, "fioc"), - ]; - - for (i, entry) in entries.into_iter().enumerate().skip(offset as usize) { - // i + 1 means the index of the next entry - if reply.add(entry.0, (i + 1) as i64, entry.1, entry.2) { - break; - } - } - reply.ok(); - } - - fn ioctl( - &self, - _req: &Request<'_>, - ino: u64, - _fh: u64, - _flags: u32, - cmd: u32, - in_data: &[u8], - _out_size: u32, - reply: fuser::ReplyIoctl, - ) { - if ino != 2 { - reply.error(EINVAL); - return; - } - - const FIOC_GET_SIZE: u64 = nix::request_code_read!('E', 0, std::mem::size_of::()); - const FIOC_SET_SIZE: u64 = nix::request_code_write!('E', 1, std::mem::size_of::()); - - match cmd.into() { - FIOC_GET_SIZE => { - let size_bytes = self.content.lock().unwrap().len().to_ne_bytes(); - reply.ioctl(0, &size_bytes); - } - FIOC_SET_SIZE => { - let new_size = usize::from_ne_bytes(in_data.try_into().unwrap()); - *self.content.lock().unwrap() = vec![0_u8; new_size]; - reply.ioctl(0, &[]); - } - _ => { - debug!("unknown ioctl: {}", cmd); - reply.error(EINVAL); - } - } - } -} - -fn main() { - let matches = Command::new("hello") - .version(crate_version!()) - .author("Colin Marc") - .arg( - Arg::new("MOUNT_POINT") - .required(true) - .index(1) - .help("Act as a client, and mount FUSE at given path"), - ) - .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.get_one::("MOUNT_POINT").unwrap(); - let mut options = vec![MountOption::FSName("fioc".to_string())]; - if matches.get_flag("auto_unmount") { - options.push(MountOption::AutoUnmount); - } - if matches.get_flag("allow-root") { - options.push(MountOption::AllowRoot); - } - - let fs = FiocFS::new(); - fuser::mount2(fs, mountpoint, &options).unwrap(); -} diff --git a/vendor/fuser/examples/notify_inval_entry.rs b/vendor/fuser/examples/notify_inval_entry.rs deleted file mode 100644 index 9b7770ee0..000000000 --- a/vendor/fuser/examples/notify_inval_entry.rs +++ /dev/null @@ -1,182 +0,0 @@ -// 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, _fh: Option, 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, &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 deleted file mode 100644 index c95284e73..000000000 --- a/vendor/fuser/examples/notify_inval_inode.rs +++ /dev/null @@ -1,226 +0,0 @@ -// 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, _fh: Option, 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, &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/null.rs b/vendor/fuser/examples/null.rs deleted file mode 100644 index 6b4feecde..000000000 --- a/vendor/fuser/examples/null.rs +++ /dev/null @@ -1,12 +0,0 @@ -use fuser::{Filesystem, MountOption}; -use std::env; - -struct NullFS; - -impl Filesystem for NullFS {} - -fn main() { - env_logger::init(); - let mountpoint = env::args_os().nth(1).unwrap(); - fuser::mount2(NullFS, mountpoint, &[MountOption::AutoUnmount]).unwrap(); -} diff --git a/vendor/fuser/examples/poll.rs b/vendor/fuser/examples/poll.rs deleted file mode 100644 index 6df475668..000000000 --- a/vendor/fuser/examples/poll.rs +++ /dev/null @@ -1,344 +0,0 @@ -// 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, PollHandle, 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, _fh: Option, 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, - ph: PollHandle, - _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] = ph.into(); - } - - 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, &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 deleted file mode 100644 index 273d19b2e..000000000 --- a/vendor/fuser/examples/poll_client.rs +++ /dev/null @@ -1,58 +0,0 @@ -// 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.as_fd(), poll::PollFlags::POLLIN)) - .collect::>(); - - for _ in 0..16 { - poll::poll(pollfds.as_mut_slice(), poll::PollTimeout::NONE)?; - - 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 deleted file mode 100644 index 6a6fdc3ff..000000000 --- a/vendor/fuser/examples/simple.rs +++ /dev/null @@ -1,2062 +0,0 @@ -#![allow(clippy::needless_return)] -#![allow(clippy::unnecessary_cast)] // libc::S_* are u16 or u32 depending on the platform - -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; -use fuser::TimeOrNow::Now; -use fuser::{ - Filesystem, KernelConfig, MountOption, ReplyAttr, ReplyCreate, ReplyData, ReplyDirectory, - ReplyEmpty, ReplyEntry, ReplyOpen, ReplyStatfs, ReplyWrite, ReplyXattr, Request, TimeOrNow, - FUSE_ROOT_ID, -}; -#[cfg(feature = "abi-7-26")] -use log::info; -use log::{debug, warn}; -use log::{error, LevelFilter}; -use serde::{Deserialize, Serialize}; -use std::cmp::min; -use std::collections::BTreeMap; -use std::ffi::OsStr; -use std::fs::{File, OpenOptions}; -use std::io::{BufRead, BufReader, ErrorKind, Read, Seek, SeekFrom, Write}; -use std::os::raw::c_int; -use std::os::unix::ffi::OsStrExt; -use std::os::unix::fs::FileExt; -#[cfg(target_os = "linux")] -use std::os::unix::io::IntoRawFd; -use std::path::{Path, PathBuf}; -use std::sync::atomic::{AtomicU64, Ordering}; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use std::{env, fs, io}; - -const BLOCK_SIZE: u64 = 512; -const MAX_NAME_LENGTH: u32 = 255; -const MAX_FILE_SIZE: u64 = 1024 * 1024 * 1024 * 1024; - -// Top two file handle bits are used to store permissions -// Note: This isn't safe, since the client can modify those bits. However, this implementation -// is just a toy -const FILE_HANDLE_READ_BIT: u64 = 1 << 63; -const FILE_HANDLE_WRITE_BIT: u64 = 1 << 62; - -const FMODE_EXEC: i32 = 0x20; - -type Inode = u64; - -type DirectoryDescriptor = BTreeMap, (Inode, FileKind)>; - -#[derive(Serialize, Deserialize, Copy, Clone, PartialEq)] -enum FileKind { - File, - Directory, - Symlink, -} - -impl From for fuser::FileType { - fn from(kind: FileKind) -> Self { - match kind { - FileKind::File => fuser::FileType::RegularFile, - FileKind::Directory => fuser::FileType::Directory, - FileKind::Symlink => fuser::FileType::Symlink, - } - } -} - -#[derive(Debug)] -enum XattrNamespace { - Security, - System, - Trusted, - User, -} - -fn parse_xattr_namespace(key: &[u8]) -> Result { - let user = b"user."; - if key.len() < user.len() { - return Err(libc::ENOTSUP); - } - if key[..user.len()].eq(user) { - return Ok(XattrNamespace::User); - } - - let system = b"system."; - if key.len() < system.len() { - return Err(libc::ENOTSUP); - } - if key[..system.len()].eq(system) { - return Ok(XattrNamespace::System); - } - - let trusted = b"trusted."; - if key.len() < trusted.len() { - return Err(libc::ENOTSUP); - } - if key[..trusted.len()].eq(trusted) { - return Ok(XattrNamespace::Trusted); - } - - let security = b"security"; - if key.len() < security.len() { - return Err(libc::ENOTSUP); - } - if key[..security.len()].eq(security) { - return Ok(XattrNamespace::Security); - } - - return Err(libc::ENOTSUP); -} - -fn clear_suid_sgid(attr: &mut InodeAttributes) { - attr.mode &= !libc::S_ISUID as u16; - // SGID is only suppose to be cleared if XGRP is set - if attr.mode & libc::S_IXGRP as u16 != 0 { - attr.mode &= !libc::S_ISGID as u16; - } -} - -fn creation_gid(parent: &InodeAttributes, gid: u32) -> u32 { - if parent.mode & libc::S_ISGID as u16 != 0 { - return parent.gid; - } - - gid -} - -fn xattr_access_check( - key: &[u8], - access_mask: i32, - inode_attrs: &InodeAttributes, - request: &Request<'_>, -) -> Result<(), c_int> { - match parse_xattr_namespace(key)? { - XattrNamespace::Security => { - if access_mask != libc::R_OK && request.uid() != 0 { - return Err(libc::EPERM); - } - } - XattrNamespace::Trusted => { - if request.uid() != 0 { - return Err(libc::EPERM); - } - } - XattrNamespace::System => { - if key.eq(b"system.posix_acl_access") { - if !check_access( - inode_attrs.uid, - inode_attrs.gid, - inode_attrs.mode, - request.uid(), - request.gid(), - access_mask, - ) { - return Err(libc::EPERM); - } - } else if request.uid() != 0 { - return Err(libc::EPERM); - } - } - XattrNamespace::User => { - if !check_access( - inode_attrs.uid, - inode_attrs.gid, - inode_attrs.mode, - request.uid(), - request.gid(), - access_mask, - ) { - return Err(libc::EPERM); - } - } - } - - Ok(()) -} - -fn time_now() -> (i64, u32) { - time_from_system_time(&SystemTime::now()) -} - -fn system_time_from_time(secs: i64, nsecs: u32) -> SystemTime { - if secs >= 0 { - UNIX_EPOCH + Duration::new(secs as u64, nsecs) - } else { - UNIX_EPOCH - Duration::new((-secs) as u64, nsecs) - } -} - -fn time_from_system_time(system_time: &SystemTime) -> (i64, u32) { - // Convert to signed 64-bit time with epoch at 0 - match system_time.duration_since(UNIX_EPOCH) { - Ok(duration) => (duration.as_secs() as i64, duration.subsec_nanos()), - Err(before_epoch_error) => ( - -(before_epoch_error.duration().as_secs() as i64), - before_epoch_error.duration().subsec_nanos(), - ), - } -} - -#[derive(Serialize, Deserialize)] -struct InodeAttributes { - pub inode: Inode, - pub open_file_handles: u64, // Ref count of open file handles to this inode - pub size: u64, - pub last_accessed: (i64, u32), - pub last_modified: (i64, u32), - pub last_metadata_changed: (i64, u32), - pub kind: FileKind, - // Permissions and special mode bits - pub mode: u16, - pub hardlinks: u32, - pub uid: u32, - pub gid: u32, - pub xattrs: BTreeMap, Vec>, -} - -impl From for fuser::FileAttr { - fn from(attrs: InodeAttributes) -> Self { - fuser::FileAttr { - ino: attrs.inode, - size: attrs.size, - blocks: (attrs.size + BLOCK_SIZE - 1) / BLOCK_SIZE, - atime: system_time_from_time(attrs.last_accessed.0, attrs.last_accessed.1), - mtime: system_time_from_time(attrs.last_modified.0, attrs.last_modified.1), - ctime: system_time_from_time( - attrs.last_metadata_changed.0, - attrs.last_metadata_changed.1, - ), - crtime: SystemTime::UNIX_EPOCH, - kind: attrs.kind.into(), - perm: attrs.mode, - nlink: attrs.hardlinks, - uid: attrs.uid, - gid: attrs.gid, - rdev: 0, - blksize: BLOCK_SIZE as u32, - flags: 0, - } - } -} - -// Stores inode metadata data in "$data_dir/inodes" and file contents in "$data_dir/contents" -// Directory data is stored in the file's contents, as a serialized DirectoryDescriptor -struct SimpleFS { - data_dir: String, - next_file_handle: AtomicU64, - direct_io: bool, - suid_support: bool, -} - -impl SimpleFS { - fn new( - data_dir: String, - direct_io: bool, - #[allow(unused_variables)] suid_support: bool, - ) -> SimpleFS { - #[cfg(feature = "abi-7-26")] - { - SimpleFS { - data_dir, - next_file_handle: AtomicU64::new(1), - direct_io, - suid_support, - } - } - #[cfg(not(feature = "abi-7-26"))] - { - SimpleFS { - data_dir, - next_file_handle: AtomicU64::new(1), - direct_io, - suid_support: false, - } - } - } - - fn creation_mode(&self, mode: u32) -> u16 { - if !self.suid_support { - (mode & !(libc::S_ISUID | libc::S_ISGID) as u32) as u16 - } else { - mode as u16 - } - } - - fn allocate_next_inode(&self) -> Inode { - let path = Path::new(&self.data_dir).join("superblock"); - let current_inode = if let Ok(file) = File::open(&path) { - bincode::deserialize_from(file).unwrap() - } else { - fuser::FUSE_ROOT_ID - }; - - let file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(&path) - .unwrap(); - bincode::serialize_into(file, &(current_inode + 1)).unwrap(); - - current_inode + 1 - } - - 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_READ_BIT.min(FILE_HANDLE_WRITE_BIT)); - if read { - fh |= FILE_HANDLE_READ_BIT; - } - if write { - fh |= FILE_HANDLE_WRITE_BIT; - } - - fh - } - - fn check_file_handle_read(&self, file_handle: u64) -> bool { - (file_handle & FILE_HANDLE_READ_BIT) != 0 - } - - fn check_file_handle_write(&self, file_handle: u64) -> bool { - (file_handle & FILE_HANDLE_WRITE_BIT) != 0 - } - - fn content_path(&self, inode: Inode) -> PathBuf { - Path::new(&self.data_dir) - .join("contents") - .join(inode.to_string()) - } - - fn get_directory_content(&self, inode: Inode) -> Result { - let path = Path::new(&self.data_dir) - .join("contents") - .join(inode.to_string()); - if let Ok(file) = File::open(path) { - Ok(bincode::deserialize_from(file).unwrap()) - } else { - Err(libc::ENOENT) - } - } - - fn write_directory_content(&self, inode: Inode, entries: DirectoryDescriptor) { - let path = Path::new(&self.data_dir) - .join("contents") - .join(inode.to_string()); - let file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(path) - .unwrap(); - bincode::serialize_into(file, &entries).unwrap(); - } - - fn get_inode(&self, inode: Inode) -> Result { - let path = Path::new(&self.data_dir) - .join("inodes") - .join(inode.to_string()); - if let Ok(file) = File::open(path) { - Ok(bincode::deserialize_from(file).unwrap()) - } else { - Err(libc::ENOENT) - } - } - - fn write_inode(&self, inode: &InodeAttributes) { - let path = Path::new(&self.data_dir) - .join("inodes") - .join(inode.inode.to_string()); - let file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(path) - .unwrap(); - bincode::serialize_into(file, inode).unwrap(); - } - - // Check whether a file should be removed from storage. Should be called after decrementing - // the link count, or closing a file handle - fn gc_inode(&self, inode: &InodeAttributes) -> bool { - if inode.hardlinks == 0 && inode.open_file_handles == 0 { - let inode_path = Path::new(&self.data_dir) - .join("inodes") - .join(inode.inode.to_string()); - fs::remove_file(inode_path).unwrap(); - let content_path = Path::new(&self.data_dir) - .join("contents") - .join(inode.inode.to_string()); - fs::remove_file(content_path).unwrap(); - - return true; - } - - return false; - } - - fn truncate( - &self, - inode: Inode, - new_length: u64, - uid: u32, - gid: u32, - ) -> Result { - if new_length > MAX_FILE_SIZE { - return Err(libc::EFBIG); - } - - let mut attrs = self.get_inode(inode)?; - - if !check_access(attrs.uid, attrs.gid, attrs.mode, uid, gid, libc::W_OK) { - return Err(libc::EACCES); - } - - let path = self.content_path(inode); - let file = OpenOptions::new().write(true).open(path).unwrap(); - file.set_len(new_length).unwrap(); - - attrs.size = new_length; - attrs.last_metadata_changed = time_now(); - attrs.last_modified = time_now(); - - // Clear SETUID & SETGID on truncate - clear_suid_sgid(&mut attrs); - - self.write_inode(&attrs); - - Ok(attrs) - } - - fn lookup_name(&self, parent: u64, name: &OsStr) -> Result { - let entries = self.get_directory_content(parent)?; - if let Some((inode, _)) = entries.get(name.as_bytes()) { - return self.get_inode(*inode); - } else { - return Err(libc::ENOENT); - } - } - - fn insert_link( - &self, - req: &Request, - parent: u64, - name: &OsStr, - inode: u64, - kind: FileKind, - ) -> Result<(), c_int> { - if self.lookup_name(parent, name).is_ok() { - return Err(libc::EEXIST); - } - - let mut parent_attrs = self.get_inode(parent)?; - - if !check_access( - parent_attrs.uid, - parent_attrs.gid, - parent_attrs.mode, - req.uid(), - req.gid(), - libc::W_OK, - ) { - return Err(libc::EACCES); - } - parent_attrs.last_modified = time_now(); - parent_attrs.last_metadata_changed = time_now(); - self.write_inode(&parent_attrs); - - let mut entries = self.get_directory_content(parent).unwrap(); - entries.insert(name.as_bytes().to_vec(), (inode, kind)); - self.write_directory_content(parent, entries); - - Ok(()) - } -} - -impl Filesystem for SimpleFS { - fn init( - &self, - _req: &Request, - #[allow(unused_variables)] config: &mut KernelConfig, - ) -> Result<(), c_int> { - #[cfg(feature = "abi-7-26")] - config.add_capabilities(FUSE_HANDLE_KILLPRIV).unwrap(); - - fs::create_dir_all(Path::new(&self.data_dir).join("inodes")).unwrap(); - fs::create_dir_all(Path::new(&self.data_dir).join("contents")).unwrap(); - if self.get_inode(FUSE_ROOT_ID).is_err() { - // Initialize with empty filesystem - let root = InodeAttributes { - inode: FUSE_ROOT_ID, - open_file_handles: 0, - size: 0, - last_accessed: time_now(), - last_modified: time_now(), - last_metadata_changed: time_now(), - kind: FileKind::Directory, - mode: 0o777, - hardlinks: 2, - uid: 0, - gid: 0, - xattrs: Default::default(), - }; - self.write_inode(&root); - let mut entries = BTreeMap::new(); - entries.insert(b".".to_vec(), (FUSE_ROOT_ID, FileKind::Directory)); - self.write_directory_content(FUSE_ROOT_ID, entries); - } - Ok(()) - } - - fn lookup(&self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { - if name.len() > MAX_NAME_LENGTH as usize { - reply.error(libc::ENAMETOOLONG); - return; - } - let parent_attrs = self.get_inode(parent).unwrap(); - if !check_access( - parent_attrs.uid, - parent_attrs.gid, - parent_attrs.mode, - req.uid(), - req.gid(), - libc::X_OK, - ) { - reply.error(libc::EACCES); - return; - } - - match self.lookup_name(parent, name) { - Ok(attrs) => reply.entry(&Duration::new(0, 0), &attrs.into(), 0), - Err(error_code) => reply.error(error_code), - } - } - - fn forget(&self, _req: &Request, _ino: u64, _nlookup: u64) {} - - fn getattr(&self, _req: &Request, inode: u64, _fh: Option, reply: ReplyAttr) { - match self.get_inode(inode) { - Ok(attrs) => reply.attr(&Duration::new(0, 0), &attrs.into()), - Err(error_code) => reply.error(error_code), - } - } - - fn setattr( - &self, - req: &Request, - inode: u64, - mode: Option, - uid: Option, - gid: Option, - size: Option, - atime: Option, - mtime: Option, - _ctime: Option, - fh: Option, - _crtime: Option, - _chgtime: Option, - _bkuptime: Option, - _flags: Option, - reply: ReplyAttr, - ) { - let mut attrs = match self.get_inode(inode) { - Ok(attrs) => attrs, - Err(error_code) => { - reply.error(error_code); - return; - } - }; - - if let Some(mode) = mode { - debug!("chmod() called with {:?}, {:o}", inode, mode); - if req.uid() != 0 && req.uid() != attrs.uid { - reply.error(libc::EPERM); - return; - } - if req.uid() != 0 - && req.gid() != attrs.gid - && !get_groups(req.pid()).contains(&attrs.gid) - { - // If SGID is set and the file belongs to a group that the caller is not part of - // then the SGID bit is suppose to be cleared during chmod - attrs.mode = (mode & !libc::S_ISGID as u32) as u16; - } else { - attrs.mode = mode as u16; - } - attrs.last_metadata_changed = time_now(); - self.write_inode(&attrs); - reply.attr(&Duration::new(0, 0), &attrs.into()); - return; - } - - if uid.is_some() || gid.is_some() { - debug!("chown() called with {:?} {:?} {:?}", inode, uid, gid); - if let Some(gid) = gid { - // Non-root users can only change gid to a group they're in - if req.uid() != 0 && !get_groups(req.pid()).contains(&gid) { - reply.error(libc::EPERM); - return; - } - } - if let Some(uid) = uid { - if req.uid() != 0 - // but no-op changes by the owner are not an error - && !(uid == attrs.uid && req.uid() == attrs.uid) - { - reply.error(libc::EPERM); - return; - } - } - // Only owner may change the group - if gid.is_some() && req.uid() != 0 && req.uid() != attrs.uid { - reply.error(libc::EPERM); - return; - } - - if attrs.mode & (libc::S_IXUSR | libc::S_IXGRP | libc::S_IXOTH) as u16 != 0 { - // SUID & SGID are suppose to be cleared when chown'ing an executable file - clear_suid_sgid(&mut attrs); - } - - if let Some(uid) = uid { - attrs.uid = uid; - // Clear SETUID on owner change - attrs.mode &= !libc::S_ISUID as u16; - } - if let Some(gid) = gid { - attrs.gid = gid; - // Clear SETGID unless user is root - if req.uid() != 0 { - attrs.mode &= !libc::S_ISGID as u16; - } - } - attrs.last_metadata_changed = time_now(); - self.write_inode(&attrs); - reply.attr(&Duration::new(0, 0), &attrs.into()); - return; - } - - if let Some(size) = size { - debug!("truncate() called with {:?} {:?}", inode, size); - if let Some(handle) = fh { - // If the file handle is available, check access locally. - // This is important as it preserves the semantic that a file handle opened - // with W_OK will never fail to truncate, even if the file has been subsequently - // chmod'ed - if self.check_file_handle_write(handle) { - if let Err(error_code) = self.truncate(inode, size, 0, 0) { - reply.error(error_code); - return; - } - } else { - reply.error(libc::EACCES); - return; - } - } else if let Err(error_code) = self.truncate(inode, size, req.uid(), req.gid()) { - reply.error(error_code); - return; - } - } - - let now = time_now(); - if let Some(atime) = atime { - debug!("utimens() called with {:?}, atime={:?}", inode, atime); - - if attrs.uid != req.uid() && req.uid() != 0 && atime != Now { - reply.error(libc::EPERM); - return; - } - - if attrs.uid != req.uid() - && !check_access( - attrs.uid, - attrs.gid, - attrs.mode, - req.uid(), - req.gid(), - libc::W_OK, - ) - { - reply.error(libc::EACCES); - return; - } - - attrs.last_accessed = match atime { - TimeOrNow::SpecificTime(time) => time_from_system_time(&time), - Now => now, - }; - attrs.last_metadata_changed = now; - self.write_inode(&attrs); - } - if let Some(mtime) = mtime { - debug!("utimens() called with {:?}, mtime={:?}", inode, mtime); - - if attrs.uid != req.uid() && req.uid() != 0 && mtime != Now { - reply.error(libc::EPERM); - return; - } - - if attrs.uid != req.uid() - && !check_access( - attrs.uid, - attrs.gid, - attrs.mode, - req.uid(), - req.gid(), - libc::W_OK, - ) - { - reply.error(libc::EACCES); - return; - } - - attrs.last_modified = match mtime { - TimeOrNow::SpecificTime(time) => time_from_system_time(&time), - Now => now, - }; - attrs.last_metadata_changed = now; - self.write_inode(&attrs); - } - - let attrs = self.get_inode(inode).unwrap(); - reply.attr(&Duration::new(0, 0), &attrs.into()); - return; - } - - fn readlink(&self, _req: &Request, inode: u64, reply: ReplyData) { - debug!("readlink() called on {:?}", inode); - let path = self.content_path(inode); - if let Ok(mut file) = File::open(path) { - let file_size = file.metadata().unwrap().len(); - let mut buffer = vec![0; file_size as usize]; - file.read_exact(&mut buffer).unwrap(); - reply.data(&buffer); - } else { - reply.error(libc::ENOENT); - } - } - - fn mknod( - &self, - req: &Request, - parent: u64, - name: &OsStr, - mut mode: u32, - _umask: u32, - _rdev: u32, - reply: ReplyEntry, - ) { - let file_type = mode & libc::S_IFMT as u32; - - if file_type != libc::S_IFREG as u32 - && file_type != libc::S_IFLNK as u32 - && file_type != libc::S_IFDIR as u32 - { - // TODO - warn!("mknod() implementation is incomplete. Only supports regular files, symlinks, and directories. Got {:o}", mode); - reply.error(libc::ENOSYS); - return; - } - - if self.lookup_name(parent, name).is_ok() { - reply.error(libc::EEXIST); - return; - } - - let mut parent_attrs = match self.get_inode(parent) { - Ok(attrs) => attrs, - Err(error_code) => { - reply.error(error_code); - return; - } - }; - - if !check_access( - parent_attrs.uid, - parent_attrs.gid, - parent_attrs.mode, - req.uid(), - req.gid(), - libc::W_OK, - ) { - reply.error(libc::EACCES); - return; - } - parent_attrs.last_modified = time_now(); - parent_attrs.last_metadata_changed = time_now(); - self.write_inode(&parent_attrs); - - if req.uid() != 0 { - mode &= !(libc::S_ISUID | libc::S_ISGID) as u32; - } - - let inode = self.allocate_next_inode(); - let attrs = InodeAttributes { - inode, - open_file_handles: 0, - size: 0, - last_accessed: time_now(), - last_modified: time_now(), - last_metadata_changed: time_now(), - kind: as_file_kind(mode), - mode: self.creation_mode(mode), - hardlinks: 1, - uid: req.uid(), - gid: creation_gid(&parent_attrs, req.gid()), - xattrs: Default::default(), - }; - self.write_inode(&attrs); - File::create(self.content_path(inode)).unwrap(); - - if as_file_kind(mode) == FileKind::Directory { - let mut entries = BTreeMap::new(); - entries.insert(b".".to_vec(), (inode, FileKind::Directory)); - entries.insert(b"..".to_vec(), (parent, FileKind::Directory)); - self.write_directory_content(inode, entries); - } - - let mut entries = self.get_directory_content(parent).unwrap(); - entries.insert(name.as_bytes().to_vec(), (inode, attrs.kind)); - self.write_directory_content(parent, entries); - - // TODO: implement flags - reply.entry(&Duration::new(0, 0), &attrs.into(), 0); - } - - fn mkdir( - &self, - req: &Request, - parent: u64, - name: &OsStr, - mut mode: u32, - _umask: u32, - reply: ReplyEntry, - ) { - debug!("mkdir() called with {:?} {:?} {:o}", parent, name, mode); - if self.lookup_name(parent, name).is_ok() { - reply.error(libc::EEXIST); - return; - } - - let mut parent_attrs = match self.get_inode(parent) { - Ok(attrs) => attrs, - Err(error_code) => { - reply.error(error_code); - return; - } - }; - - if !check_access( - parent_attrs.uid, - parent_attrs.gid, - parent_attrs.mode, - req.uid(), - req.gid(), - libc::W_OK, - ) { - reply.error(libc::EACCES); - return; - } - parent_attrs.last_modified = time_now(); - parent_attrs.last_metadata_changed = time_now(); - self.write_inode(&parent_attrs); - - if req.uid() != 0 { - mode &= !(libc::S_ISUID | libc::S_ISGID) as u32; - } - if parent_attrs.mode & libc::S_ISGID as u16 != 0 { - mode |= libc::S_ISGID as u32; - } - - let inode = self.allocate_next_inode(); - let attrs = InodeAttributes { - inode, - open_file_handles: 0, - size: BLOCK_SIZE, - last_accessed: time_now(), - last_modified: time_now(), - last_metadata_changed: time_now(), - kind: FileKind::Directory, - mode: self.creation_mode(mode), - hardlinks: 2, // Directories start with link count of 2, since they have a self link - uid: req.uid(), - gid: creation_gid(&parent_attrs, req.gid()), - xattrs: Default::default(), - }; - self.write_inode(&attrs); - - let mut entries = BTreeMap::new(); - entries.insert(b".".to_vec(), (inode, FileKind::Directory)); - entries.insert(b"..".to_vec(), (parent, FileKind::Directory)); - self.write_directory_content(inode, entries); - - let mut entries = self.get_directory_content(parent).unwrap(); - entries.insert(name.as_bytes().to_vec(), (inode, FileKind::Directory)); - self.write_directory_content(parent, entries); - - reply.entry(&Duration::new(0, 0), &attrs.into(), 0); - } - - fn unlink(&self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { - debug!("unlink() called with {:?} {:?}", parent, name); - let mut attrs = match self.lookup_name(parent, name) { - Ok(attrs) => attrs, - Err(error_code) => { - reply.error(error_code); - return; - } - }; - - let mut parent_attrs = match self.get_inode(parent) { - Ok(attrs) => attrs, - Err(error_code) => { - reply.error(error_code); - return; - } - }; - - if !check_access( - parent_attrs.uid, - parent_attrs.gid, - parent_attrs.mode, - req.uid(), - req.gid(), - libc::W_OK, - ) { - reply.error(libc::EACCES); - return; - } - - let uid = req.uid(); - // "Sticky bit" handling - if parent_attrs.mode & libc::S_ISVTX as u16 != 0 - && uid != 0 - && uid != parent_attrs.uid - && uid != attrs.uid - { - reply.error(libc::EACCES); - return; - } - - parent_attrs.last_metadata_changed = time_now(); - parent_attrs.last_modified = time_now(); - self.write_inode(&parent_attrs); - - attrs.hardlinks -= 1; - attrs.last_metadata_changed = time_now(); - self.write_inode(&attrs); - self.gc_inode(&attrs); - - let mut entries = self.get_directory_content(parent).unwrap(); - entries.remove(name.as_bytes()); - self.write_directory_content(parent, entries); - - reply.ok(); - } - - fn rmdir(&self, req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { - debug!("rmdir() called with {:?} {:?}", parent, name); - let mut attrs = match self.lookup_name(parent, name) { - Ok(attrs) => attrs, - Err(error_code) => { - reply.error(error_code); - return; - } - }; - - let mut parent_attrs = match self.get_inode(parent) { - Ok(attrs) => attrs, - Err(error_code) => { - reply.error(error_code); - return; - } - }; - - // Directories always have a self and parent link - if self.get_directory_content(attrs.inode).unwrap().len() > 2 { - reply.error(libc::ENOTEMPTY); - return; - } - if !check_access( - parent_attrs.uid, - parent_attrs.gid, - parent_attrs.mode, - req.uid(), - req.gid(), - libc::W_OK, - ) { - reply.error(libc::EACCES); - return; - } - - // "Sticky bit" handling - if parent_attrs.mode & libc::S_ISVTX as u16 != 0 - && req.uid() != 0 - && req.uid() != parent_attrs.uid - && req.uid() != attrs.uid - { - reply.error(libc::EACCES); - return; - } - - parent_attrs.last_metadata_changed = time_now(); - parent_attrs.last_modified = time_now(); - self.write_inode(&parent_attrs); - - attrs.hardlinks = 0; - attrs.last_metadata_changed = time_now(); - self.write_inode(&attrs); - self.gc_inode(&attrs); - - let mut entries = self.get_directory_content(parent).unwrap(); - entries.remove(name.as_bytes()); - self.write_directory_content(parent, entries); - - reply.ok(); - } - - fn symlink( - &self, - req: &Request, - parent: u64, - link_name: &OsStr, - target: &Path, - reply: ReplyEntry, - ) { - debug!( - "symlink() called with {:?} {:?} {:?}", - parent, link_name, target - ); - let mut parent_attrs = match self.get_inode(parent) { - Ok(attrs) => attrs, - Err(error_code) => { - reply.error(error_code); - return; - } - }; - - if !check_access( - parent_attrs.uid, - parent_attrs.gid, - parent_attrs.mode, - req.uid(), - req.gid(), - libc::W_OK, - ) { - reply.error(libc::EACCES); - return; - } - parent_attrs.last_modified = time_now(); - parent_attrs.last_metadata_changed = time_now(); - self.write_inode(&parent_attrs); - - let inode = self.allocate_next_inode(); - let attrs = InodeAttributes { - inode, - open_file_handles: 0, - size: target.as_os_str().as_bytes().len() as u64, - last_accessed: time_now(), - last_modified: time_now(), - last_metadata_changed: time_now(), - kind: FileKind::Symlink, - mode: 0o777, - hardlinks: 1, - uid: req.uid(), - gid: creation_gid(&parent_attrs, req.gid()), - xattrs: Default::default(), - }; - - if let Err(error_code) = self.insert_link(req, parent, link_name, inode, FileKind::Symlink) - { - reply.error(error_code); - return; - } - self.write_inode(&attrs); - - let path = self.content_path(inode); - let mut file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(path) - .unwrap(); - file.write_all(target.as_os_str().as_bytes()).unwrap(); - - reply.entry(&Duration::new(0, 0), &attrs.into(), 0); - } - - fn rename( - &self, - req: &Request, - parent: u64, - name: &OsStr, - new_parent: u64, - new_name: &OsStr, - flags: u32, - reply: ReplyEmpty, - ) { - debug!( - "rename() called with: source {parent:?} {name:?}, \ - destination {new_parent:?} {new_name:?}, flags {flags:#b}", - ); - let mut inode_attrs = match self.lookup_name(parent, name) { - Ok(attrs) => attrs, - Err(error_code) => { - reply.error(error_code); - return; - } - }; - - let mut parent_attrs = match self.get_inode(parent) { - Ok(attrs) => attrs, - Err(error_code) => { - reply.error(error_code); - return; - } - }; - - if !check_access( - parent_attrs.uid, - parent_attrs.gid, - parent_attrs.mode, - req.uid(), - req.gid(), - libc::W_OK, - ) { - reply.error(libc::EACCES); - return; - } - - // "Sticky bit" handling - if parent_attrs.mode & libc::S_ISVTX as u16 != 0 - && req.uid() != 0 - && req.uid() != parent_attrs.uid - && req.uid() != inode_attrs.uid - { - reply.error(libc::EACCES); - return; - } - - let mut new_parent_attrs = match self.get_inode(new_parent) { - Ok(attrs) => attrs, - Err(error_code) => { - reply.error(error_code); - return; - } - }; - - if !check_access( - new_parent_attrs.uid, - new_parent_attrs.gid, - new_parent_attrs.mode, - req.uid(), - req.gid(), - libc::W_OK, - ) { - reply.error(libc::EACCES); - return; - } - - // "Sticky bit" handling in new_parent - if new_parent_attrs.mode & libc::S_ISVTX as u16 != 0 { - if let Ok(existing_attrs) = self.lookup_name(new_parent, new_name) { - if req.uid() != 0 - && req.uid() != new_parent_attrs.uid - && req.uid() != existing_attrs.uid - { - reply.error(libc::EACCES); - return; - } - } - } - - #[cfg(target_os = "linux")] - if flags & libc::RENAME_EXCHANGE as u32 != 0 { - let mut new_inode_attrs = match self.lookup_name(new_parent, new_name) { - Ok(attrs) => attrs, - Err(error_code) => { - reply.error(error_code); - return; - } - }; - - let mut entries = self.get_directory_content(new_parent).unwrap(); - entries.insert( - new_name.as_bytes().to_vec(), - (inode_attrs.inode, inode_attrs.kind), - ); - self.write_directory_content(new_parent, entries); - - let mut entries = self.get_directory_content(parent).unwrap(); - entries.insert( - name.as_bytes().to_vec(), - (new_inode_attrs.inode, new_inode_attrs.kind), - ); - self.write_directory_content(parent, entries); - - parent_attrs.last_metadata_changed = time_now(); - parent_attrs.last_modified = time_now(); - self.write_inode(&parent_attrs); - new_parent_attrs.last_metadata_changed = time_now(); - new_parent_attrs.last_modified = time_now(); - self.write_inode(&new_parent_attrs); - inode_attrs.last_metadata_changed = time_now(); - self.write_inode(&inode_attrs); - new_inode_attrs.last_metadata_changed = time_now(); - self.write_inode(&new_inode_attrs); - - if inode_attrs.kind == FileKind::Directory { - let mut entries = self.get_directory_content(inode_attrs.inode).unwrap(); - entries.insert(b"..".to_vec(), (new_parent, FileKind::Directory)); - self.write_directory_content(inode_attrs.inode, entries); - } - if new_inode_attrs.kind == FileKind::Directory { - let mut entries = self.get_directory_content(new_inode_attrs.inode).unwrap(); - entries.insert(b"..".to_vec(), (parent, FileKind::Directory)); - self.write_directory_content(new_inode_attrs.inode, entries); - } - - reply.ok(); - return; - } - - // Only overwrite an existing directory if it's empty - if let Ok(new_name_attrs) = self.lookup_name(new_parent, new_name) { - if new_name_attrs.kind == FileKind::Directory - && self - .get_directory_content(new_name_attrs.inode) - .unwrap() - .len() - > 2 - { - reply.error(libc::ENOTEMPTY); - return; - } - } - - // Only move an existing directory to a new parent, if we have write access to it, - // because that will change the ".." link in it - if inode_attrs.kind == FileKind::Directory - && parent != new_parent - && !check_access( - inode_attrs.uid, - inode_attrs.gid, - inode_attrs.mode, - req.uid(), - req.gid(), - libc::W_OK, - ) - { - reply.error(libc::EACCES); - return; - } - - // If target already exists decrement its hardlink count - if let Ok(mut existing_inode_attrs) = self.lookup_name(new_parent, new_name) { - let mut entries = self.get_directory_content(new_parent).unwrap(); - entries.remove(new_name.as_bytes()); - self.write_directory_content(new_parent, entries); - - if existing_inode_attrs.kind == FileKind::Directory { - existing_inode_attrs.hardlinks = 0; - } else { - existing_inode_attrs.hardlinks -= 1; - } - existing_inode_attrs.last_metadata_changed = time_now(); - self.write_inode(&existing_inode_attrs); - self.gc_inode(&existing_inode_attrs); - } - - let mut entries = self.get_directory_content(parent).unwrap(); - entries.remove(name.as_bytes()); - self.write_directory_content(parent, entries); - - let mut entries = self.get_directory_content(new_parent).unwrap(); - entries.insert( - new_name.as_bytes().to_vec(), - (inode_attrs.inode, inode_attrs.kind), - ); - self.write_directory_content(new_parent, entries); - - parent_attrs.last_metadata_changed = time_now(); - parent_attrs.last_modified = time_now(); - self.write_inode(&parent_attrs); - new_parent_attrs.last_metadata_changed = time_now(); - new_parent_attrs.last_modified = time_now(); - self.write_inode(&new_parent_attrs); - inode_attrs.last_metadata_changed = time_now(); - self.write_inode(&inode_attrs); - - if inode_attrs.kind == FileKind::Directory { - let mut entries = self.get_directory_content(inode_attrs.inode).unwrap(); - entries.insert(b"..".to_vec(), (new_parent, FileKind::Directory)); - self.write_directory_content(inode_attrs.inode, entries); - } - - reply.ok(); - } - - fn link( - &self, - req: &Request, - inode: u64, - new_parent: u64, - new_name: &OsStr, - reply: ReplyEntry, - ) { - debug!( - "link() called for {}, {}, {:?}", - inode, new_parent, new_name - ); - let mut attrs = match self.get_inode(inode) { - Ok(attrs) => attrs, - Err(error_code) => { - reply.error(error_code); - return; - } - }; - if let Err(error_code) = self.insert_link(req, new_parent, new_name, inode, attrs.kind) { - reply.error(error_code); - } else { - attrs.hardlinks += 1; - attrs.last_metadata_changed = time_now(); - self.write_inode(&attrs); - reply.entry(&Duration::new(0, 0), &attrs.into(), 0); - } - } - - fn open(&self, req: &Request, inode: u64, flags: i32, reply: ReplyOpen) { - debug!("open() called for {:?}", inode); - let (access_mask, read, write) = match flags & libc::O_ACCMODE { - libc::O_RDONLY => { - // Behavior is undefined, but most filesystems return EACCES - if flags & libc::O_TRUNC != 0 { - reply.error(libc::EACCES); - return; - } - if flags & 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; - } - }; - - match self.get_inode(inode) { - Ok(mut attr) => { - if check_access( - attr.uid, - attr.gid, - attr.mode, - req.uid(), - req.gid(), - access_mask, - ) { - attr.open_file_handles += 1; - self.write_inode(&attr); - let open_flags = if self.direct_io { FOPEN_DIRECT_IO } else { 0 }; - reply.opened(self.allocate_next_file_handle(read, write), open_flags); - } else { - reply.error(libc::EACCES); - } - return; - } - Err(error_code) => reply.error(error_code), - } - } - - fn read( - &self, - _req: &Request, - inode: u64, - fh: u64, - offset: i64, - size: u32, - _flags: i32, - _lock_owner: Option, - reply: ReplyData, - ) { - debug!( - "read() called on {:?} offset={:?} size={:?}", - inode, offset, size - ); - assert!(offset >= 0); - if !self.check_file_handle_read(fh) { - reply.error(libc::EACCES); - return; - } - - let path = self.content_path(inode); - if let Ok(file) = File::open(path) { - let file_size = file.metadata().unwrap().len(); - // Could underflow if file length is less than local_start - let read_size = min(size, file_size.saturating_sub(offset as u64) as u32); - - let mut buffer = vec![0; read_size as usize]; - file.read_exact_at(&mut buffer, offset as u64).unwrap(); - reply.data(&buffer); - } else { - reply.error(libc::ENOENT); - } - } - - fn write( - &self, - _req: &Request, - inode: u64, - fh: u64, - offset: i64, - data: &[u8], - _write_flags: u32, - #[allow(unused_variables)] flags: i32, - _lock_owner: Option, - reply: ReplyWrite, - ) { - debug!("write() called with {:?} size={:?}", inode, data.len()); - assert!(offset >= 0); - if !self.check_file_handle_write(fh) { - reply.error(libc::EACCES); - return; - } - - let path = self.content_path(inode); - if let Ok(mut file) = OpenOptions::new().write(true).open(path) { - file.seek(SeekFrom::Start(offset as u64)).unwrap(); - file.write_all(data).unwrap(); - - let mut attrs = self.get_inode(inode).unwrap(); - attrs.last_metadata_changed = time_now(); - attrs.last_modified = time_now(); - if data.len() + offset as usize > attrs.size as usize { - attrs.size = (data.len() + offset as usize) as u64; - } - // #[cfg(feature = "abi-7-31")] - // if flags & FUSE_WRITE_KILL_PRIV as i32 != 0 { - // clear_suid_sgid(&mut attrs); - // } - // XXX: In theory we should only need to do this when WRITE_KILL_PRIV is set for 7.31+ - // However, xfstests fail in that case - clear_suid_sgid(&mut attrs); - self.write_inode(&attrs); - - reply.written(data.len() as u32); - } else { - reply.error(libc::EBADF); - } - } - - fn release( - &self, - _req: &Request<'_>, - inode: u64, - _fh: u64, - _flags: i32, - _lock_owner: Option, - _flush: bool, - reply: ReplyEmpty, - ) { - if let Ok(mut attrs) = self.get_inode(inode) { - attrs.open_file_handles -= 1; - } - reply.ok(); - } - - fn opendir(&self, req: &Request, inode: u64, flags: i32, reply: ReplyOpen) { - debug!("opendir() called on {:?}", inode); - let (access_mask, read, write) = match flags & libc::O_ACCMODE { - libc::O_RDONLY => { - // Behavior is undefined, but most filesystems return EACCES - if flags & libc::O_TRUNC != 0 { - reply.error(libc::EACCES); - return; - } - (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; - } - }; - - match self.get_inode(inode) { - Ok(mut attr) => { - if check_access( - attr.uid, - attr.gid, - attr.mode, - req.uid(), - req.gid(), - access_mask, - ) { - attr.open_file_handles += 1; - self.write_inode(&attr); - let open_flags = if self.direct_io { FOPEN_DIRECT_IO } else { 0 }; - reply.opened(self.allocate_next_file_handle(read, write), open_flags); - } else { - reply.error(libc::EACCES); - } - return; - } - Err(error_code) => reply.error(error_code), - } - } - - fn readdir( - &self, - _req: &Request, - inode: u64, - _fh: u64, - offset: i64, - mut reply: ReplyDirectory, - ) { - debug!("readdir() called with {:?}", inode); - assert!(offset >= 0); - let entries = match self.get_directory_content(inode) { - Ok(entries) => entries, - Err(error_code) => { - reply.error(error_code); - return; - } - }; - - for (index, entry) in entries.iter().skip(offset as usize).enumerate() { - let (name, (inode, file_type)) = entry; - - let buffer_full: bool = reply.add( - *inode, - offset + index as i64 + 1, - (*file_type).into(), - OsStr::from_bytes(name), - ); - - if buffer_full { - break; - } - } - - reply.ok(); - } - - fn releasedir( - &self, - _req: &Request<'_>, - inode: u64, - _fh: u64, - _flags: i32, - reply: ReplyEmpty, - ) { - if let Ok(mut attrs) = self.get_inode(inode) { - attrs.open_file_handles -= 1; - } - reply.ok(); - } - - fn statfs(&self, _req: &Request, _ino: u64, reply: ReplyStatfs) { - warn!("statfs() implementation is a stub"); - // TODO: real implementation of this - reply.statfs( - 10_000, - 10_000, - 10_000, - 1, - 10_000, - BLOCK_SIZE as u32, - MAX_NAME_LENGTH, - BLOCK_SIZE as u32, - ); - } - - fn setxattr( - &self, - request: &Request<'_>, - inode: u64, - key: &OsStr, - value: &[u8], - _flags: i32, - _position: u32, - reply: ReplyEmpty, - ) { - if let Ok(mut attrs) = self.get_inode(inode) { - if let Err(error) = xattr_access_check(key.as_bytes(), libc::W_OK, &attrs, request) { - reply.error(error); - return; - } - - attrs.xattrs.insert(key.as_bytes().to_vec(), value.to_vec()); - attrs.last_metadata_changed = time_now(); - self.write_inode(&attrs); - reply.ok(); - } else { - reply.error(libc::EBADF); - } - } - - fn getxattr( - &self, - request: &Request<'_>, - inode: u64, - key: &OsStr, - size: u32, - reply: ReplyXattr, - ) { - if let Ok(attrs) = self.get_inode(inode) { - if let Err(error) = xattr_access_check(key.as_bytes(), libc::R_OK, &attrs, request) { - reply.error(error); - return; - } - - if let Some(data) = attrs.xattrs.get(key.as_bytes()) { - if size == 0 { - reply.size(data.len() as u32); - } else if data.len() <= size as usize { - reply.data(data); - } else { - reply.error(libc::ERANGE); - } - } else { - #[cfg(target_os = "linux")] - reply.error(libc::ENODATA); - #[cfg(not(target_os = "linux"))] - reply.error(libc::ENOATTR); - } - } else { - reply.error(libc::EBADF); - } - } - - fn listxattr(&self, _req: &Request<'_>, inode: u64, size: u32, reply: ReplyXattr) { - if let Ok(attrs) = self.get_inode(inode) { - let mut bytes = vec![]; - // Convert to concatenated null-terminated strings - for key in attrs.xattrs.keys() { - bytes.extend(key); - bytes.push(0); - } - if size == 0 { - reply.size(bytes.len() as u32); - } else if bytes.len() <= size as usize { - reply.data(&bytes); - } else { - reply.error(libc::ERANGE); - } - } else { - reply.error(libc::EBADF); - } - } - - fn removexattr(&self, request: &Request<'_>, inode: u64, key: &OsStr, reply: ReplyEmpty) { - if let Ok(mut attrs) = self.get_inode(inode) { - if let Err(error) = xattr_access_check(key.as_bytes(), libc::W_OK, &attrs, request) { - reply.error(error); - return; - } - - if attrs.xattrs.remove(key.as_bytes()).is_none() { - #[cfg(target_os = "linux")] - reply.error(libc::ENODATA); - #[cfg(not(target_os = "linux"))] - reply.error(libc::ENOATTR); - return; - } - attrs.last_metadata_changed = time_now(); - self.write_inode(&attrs); - reply.ok(); - } else { - reply.error(libc::EBADF); - } - } - - fn access(&self, req: &Request, inode: u64, mask: i32, reply: ReplyEmpty) { - debug!("access() called with {:?} {:?}", inode, mask); - match self.get_inode(inode) { - Ok(attr) => { - if check_access(attr.uid, attr.gid, attr.mode, req.uid(), req.gid(), mask) { - reply.ok(); - } else { - reply.error(libc::EACCES); - } - } - Err(error_code) => reply.error(error_code), - } - } - - fn create( - &self, - req: &Request, - parent: u64, - name: &OsStr, - mut mode: u32, - _umask: u32, - flags: i32, - reply: ReplyCreate, - ) { - debug!("create() called with {:?} {:?}", parent, name); - if self.lookup_name(parent, name).is_ok() { - reply.error(libc::EEXIST); - return; - } - - let (read, write) = match flags & libc::O_ACCMODE { - libc::O_RDONLY => (true, false), - libc::O_WRONLY => (false, true), - libc::O_RDWR => (true, true), - // Exactly one access mode flag must be specified - _ => { - reply.error(libc::EINVAL); - return; - } - }; - - let mut parent_attrs = match self.get_inode(parent) { - Ok(attrs) => attrs, - Err(error_code) => { - reply.error(error_code); - return; - } - }; - - if !check_access( - parent_attrs.uid, - parent_attrs.gid, - parent_attrs.mode, - req.uid(), - req.gid(), - libc::W_OK, - ) { - reply.error(libc::EACCES); - return; - } - parent_attrs.last_modified = time_now(); - parent_attrs.last_metadata_changed = time_now(); - self.write_inode(&parent_attrs); - - if req.uid() != 0 { - mode &= !(libc::S_ISUID | libc::S_ISGID) as u32; - } - - let inode = self.allocate_next_inode(); - let attrs = InodeAttributes { - inode, - open_file_handles: 1, - size: 0, - last_accessed: time_now(), - last_modified: time_now(), - last_metadata_changed: time_now(), - kind: as_file_kind(mode), - mode: self.creation_mode(mode), - hardlinks: 1, - uid: req.uid(), - gid: creation_gid(&parent_attrs, req.gid()), - xattrs: Default::default(), - }; - self.write_inode(&attrs); - File::create(self.content_path(inode)).unwrap(); - - if as_file_kind(mode) == FileKind::Directory { - let mut entries = BTreeMap::new(); - entries.insert(b".".to_vec(), (inode, FileKind::Directory)); - entries.insert(b"..".to_vec(), (parent, FileKind::Directory)); - self.write_directory_content(inode, entries); - } - - let mut entries = self.get_directory_content(parent).unwrap(); - entries.insert(name.as_bytes().to_vec(), (inode, attrs.kind)); - self.write_directory_content(parent, entries); - - // TODO: implement flags - reply.created( - &Duration::new(0, 0), - &attrs.into(), - 0, - self.allocate_next_file_handle(read, write), - 0, - ); - } - - #[cfg(target_os = "linux")] - fn fallocate( - &self, - _req: &Request<'_>, - inode: u64, - _fh: u64, - offset: i64, - length: i64, - mode: i32, - reply: ReplyEmpty, - ) { - let path = self.content_path(inode); - if let Ok(file) = OpenOptions::new().write(true).open(path) { - unsafe { - libc::fallocate64(file.into_raw_fd(), mode, offset, length); - } - if mode & libc::FALLOC_FL_KEEP_SIZE == 0 { - let mut attrs = self.get_inode(inode).unwrap(); - attrs.last_metadata_changed = time_now(); - attrs.last_modified = time_now(); - if (offset + length) as u64 > attrs.size { - attrs.size = (offset + length) as u64; - } - self.write_inode(&attrs); - } - reply.ok(); - } else { - reply.error(libc::ENOENT); - } - } - - fn copy_file_range( - &self, - _req: &Request<'_>, - src_inode: u64, - src_fh: u64, - src_offset: i64, - dest_inode: u64, - dest_fh: u64, - dest_offset: i64, - size: u64, - _flags: u32, - reply: ReplyWrite, - ) { - debug!( - "copy_file_range() called with src ({}, {}, {}) dest ({}, {}, {}) size={}", - src_fh, src_inode, src_offset, dest_fh, dest_inode, dest_offset, size - ); - if !self.check_file_handle_read(src_fh) { - reply.error(libc::EACCES); - return; - } - if !self.check_file_handle_write(dest_fh) { - reply.error(libc::EACCES); - return; - } - - let src_path = self.content_path(src_inode); - if let Ok(file) = File::open(src_path) { - let file_size = file.metadata().unwrap().len(); - // Could underflow if file length is less than local_start - let read_size = min(size, file_size.saturating_sub(src_offset as u64)); - - let mut data = vec![0; read_size as usize]; - file.read_exact_at(&mut data, src_offset as u64).unwrap(); - - let dest_path = self.content_path(dest_inode); - if let Ok(mut file) = OpenOptions::new().write(true).open(dest_path) { - file.seek(SeekFrom::Start(dest_offset as u64)).unwrap(); - file.write_all(&data).unwrap(); - - let mut attrs = self.get_inode(dest_inode).unwrap(); - attrs.last_metadata_changed = time_now(); - attrs.last_modified = time_now(); - if data.len() + dest_offset as usize > attrs.size as usize { - attrs.size = (data.len() + dest_offset as usize) as u64; - } - self.write_inode(&attrs); - - reply.written(data.len() as u32); - } else { - reply.error(libc::EBADF); - } - } else { - reply.error(libc::ENOENT); - } - } -} - -pub fn check_access( - file_uid: u32, - file_gid: u32, - file_mode: u16, - uid: u32, - gid: u32, - mut access_mask: i32, -) -> bool { - // F_OK tests for existence of file - if access_mask == libc::F_OK { - return true; - } - let file_mode = i32::from(file_mode); - - // root is allowed to read & write anything - if uid == 0 { - // root only allowed to exec if one of the X bits is set - access_mask &= libc::X_OK; - access_mask -= access_mask & (file_mode >> 6); - access_mask -= access_mask & (file_mode >> 3); - access_mask -= access_mask & file_mode; - return access_mask == 0; - } - - if uid == file_uid { - access_mask -= access_mask & (file_mode >> 6); - } else if gid == file_gid { - access_mask -= access_mask & (file_mode >> 3); - } else { - access_mask -= access_mask & file_mode; - } - - return access_mask == 0; -} - -fn as_file_kind(mut mode: u32) -> FileKind { - mode &= libc::S_IFMT as u32; - - if mode == libc::S_IFREG as u32 { - return FileKind::File; - } else if mode == libc::S_IFLNK as u32 { - return FileKind::Symlink; - } else if mode == libc::S_IFDIR as u32 { - return FileKind::Directory; - } else { - unimplemented!("{}", mode); - } -} - -fn get_groups(pid: u32) -> Vec { - if cfg!(not(target_os = "macos")) { - let path = format!("/proc/{pid}/task/{pid}/status"); - let file = File::open(path).unwrap(); - for line in BufReader::new(file).lines() { - let line = line.unwrap(); - if line.starts_with("Groups:") { - return line["Groups: ".len()..] - .split(' ') - .filter(|x| !x.trim().is_empty()) - .map(|x| x.parse::().unwrap()) - .collect(); - } - } - } - - vec![] -} - -fn fuse_allow_other_enabled() -> io::Result { - let file = File::open("/etc/fuse.conf")?; - for line in BufReader::new(file).lines() { - if line?.trim_start().starts_with("user_allow_other") { - return Ok(true); - } - } - Ok(false) -} - -fn main() { - let matches = Command::new("Fuser") - .version(crate_version!()) - .author("Christopher Berner") - .arg( - Arg::new("data-dir") - .long("data-dir") - .value_name("DIR") - .default_value("/tmp/fuser") - .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"), - ) - .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") - .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') - .action(ArgAction::Count) - .help("Sets the level of verbosity"), - ) - .get_matches(); - - let verbosity = matches.get_count("v"); - let log_level = match verbosity { - 0 => LevelFilter::Error, - 1 => LevelFilter::Warn, - 2 => LevelFilter::Info, - 3 => LevelFilter::Debug, - _ => LevelFilter::Trace, - }; - env_logger::builder() - .format_timestamp_nanos() - .filter_level(log_level) - .init(); - - let mut options = vec![MountOption::FSName("fuser".to_string())]; - - #[cfg(feature = "abi-7-26")] - { - if matches.get_flag("suid") { - info!("setuid bit support enabled"); - options.push(MountOption::Suid); - } else { - options.push(MountOption::AutoUnmount); - } - } - #[cfg(not(feature = "abi-7-26"))] - { - options.push(MountOption::AutoUnmount); - } - if let Ok(enabled) = fuse_allow_other_enabled() { - if enabled { - options.push(MountOption::AllowOther); - } - } else { - eprintln!("Unable to read /etc/fuse.conf"); - } - - let data_dir = matches.get_one::("data-dir").unwrap().to_string(); - - let mountpoint: String = matches - .get_one::("mount-point") - .unwrap() - .to_string(); - - let result = fuser::mount2( - SimpleFS::new( - data_dir, - matches.get_flag("direct-io"), - matches.get_flag("suid"), - ), - mountpoint, - &options, - ); - if let Err(e) = result { - // Return a special error code for permission denied, which usually indicates that - // "user_allow_other" is missing from /etc/fuse.conf - if e.kind() == ErrorKind::PermissionDenied { - error!("{}", e.to_string()); - std::process::exit(2); - } - } -} diff --git a/vendor/fuser/mount_tests.Dockerfile b/vendor/fuser/mount_tests.Dockerfile deleted file mode 100644 index b82d50e14..000000000 --- a/vendor/fuser/mount_tests.Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM ubuntu:20.04 - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt update && apt install -y build-essential curl - -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 - -ADD . /code/fuser/ diff --git a/vendor/fuser/mount_tests.sh b/vendor/fuser/mount_tests.sh deleted file mode 100755 index ef9994597..000000000 --- a/vendor/fuser/mount_tests.sh +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env bash - -set -x - -exit_handler() { - exit "${TEST_EXIT_STATUS:-1}" -} -trap exit_handler TERM -trap 'kill $(jobs -p); exit $TEST_EXIT_STATUS' INT EXIT - -export RUST_BACKTRACE=1 - -NC="\e[39m" -GREEN="\e[32m" -RED="\e[31m" - -function run_allow_root_test { - useradd fusertest1 - useradd fusertest2 - DIR=$(su fusertest1 -c "mktemp --directory") - cargo build --example hello --features libfuse,abi-7-30 > /dev/null 2>&1 - su fusertest1 -c "target/debug/examples/hello $DIR --allow-root" & - FUSE_PID=$! - sleep 2 - - echo "mounting at $DIR" - # Make sure FUSE was successfully mounted - mount | grep hello || exit 1 - - if [[ $(su root -c "cat ${DIR}/hello.txt") = "Hello World!" ]]; then - echo -e "$GREEN OK root can read $NC" - else - echo -e "$RED FAILED root can't read $NC" - export TEST_EXIT_STATUS=1 - exit 1 - fi - - if [[ $(su fusertest1 -c "cat ${DIR}/hello.txt") = "Hello World!" ]]; then - echo -e "$GREEN OK owner can read $NC" - else - echo -e "$RED FAILED owner can't read $NC" - export TEST_EXIT_STATUS=1 - exit 1 - fi - - if [[ $(su fusertest2 -c "cat ${DIR}/hello.txt") = "Hello World!" ]]; then - echo -e "$RED FAILED other user can read $NC" - export TEST_EXIT_STATUS=1 - exit 1 - else - echo -e "$GREEN OK other user can't read $NC" - fi - - kill $FUSE_PID - wait $FUSE_PID -} - -function test_no_user_allow_other { - sed -i '/user_allow_other/d' /etc/fuse.conf - - useradd fusertestnoallow - DIR=$(su fusertestnoallow -c "mktemp --directory") - DATA_DIR=$(su fusertestnoallow -c "mktemp --directory") - cargo build --example simple $1 > /dev/null 2>&1 - su fusertestnoallow -c "target/debug/examples/simple -vvv --data-dir $DATA_DIR --mount-point $DIR" - exitCode=$? - if [[ $exitCode -eq 2 ]]; then - echo -e "$GREEN OK Detected lack of user_allow_other: $2 $NC" - else - echo -e "$RED FAILED Did not detect lack of user_allow_other: $2 $NC" - export TEST_EXIT_STATUS=1 - exit 1 - fi - - # Make sure the FUSE mount did not mount - if [[ $(mount | grep hello) ]]; then - umount $DIR - echo -e "$RED FAILED Mount exists: $2 $NC" - export TEST_EXIT_STATUS=1 - exit 1 - else - echo -e "$GREEN OK Mount does not exist: $2 $NC" - fi - - # Restore fuse.conf - echo 'user_allow_other' >> /etc/fuse.conf -} - -function run_test { - DIR=$(mktemp --directory) - cargo build --example hello $1 > /dev/null 2>&1 - cargo run --example hello $1 -- $DIR $3 & - FUSE_PID=$! - sleep 2 - - echo "mounting at $DIR" - # Make sure FUSE was successfully mounted - mount | grep hello || exit 1 - - if [[ $(cat ${DIR}/hello.txt) = "Hello World!" ]]; then - echo -e "$GREEN OK $2 $3 $NC" - else - echo -e "$RED FAILED $2 $3 $NC" - export TEST_EXIT_STATUS=1 - exit 1 - fi - - kill $FUSE_PID - wait $FUSE_PID - - if [[ "$3" == "--auto_unmount" ]]; then - # Make sure the FUSE mount automatically unmounted - if [[ $(mount | grep hello) ]]; then - echo -e "$RED FAILED Mount not cleaned up: $2 $3 $NC" - export TEST_EXIT_STATUS=1 - exit 1 - else - echo -e "$GREEN OK Mount cleaned up: $2 $3 $NC" - fi - else - umount $DIR - fi -} - -apt update -apt install -y fuse -echo 'user_allow_other' >> /etc/fuse.conf - -run_test --no-default-features 'without libfuse, with fusermount' -run_test --no-default-features 'without libfuse, with fusermount' --auto_unmount -test_no_user_allow_other --no-default-features 'without libfuse, with fusermount' - -apt remove --purge -y fuse -apt autoremove -y -apt install -y fuse3 -echo 'user_allow_other' >> /etc/fuse.conf - -run_test --no-default-features 'without libfuse, with fusermount3' -run_test --no-default-features 'without libfuse, with fusermount3' --auto_unmount -test_no_user_allow_other --no-default-features 'without libfuse, with fusermount3' - -apt remove --purge -y fuse3 -apt autoremove -y -apt install -y libfuse-dev pkg-config fuse -echo 'user_allow_other' >> /etc/fuse.conf - -run_test --features=libfuse 'with libfuse' -run_test --features=libfuse 'with libfuse' --auto_unmount - -apt remove --purge -y libfuse-dev fuse -apt autoremove -y -apt install -y libfuse3-dev fuse3 -echo 'user_allow_other' >> /etc/fuse.conf - -run_test --features=libfuse,abi-7-30 'with libfuse3' -run_test --features=libfuse,abi-7-30 'with libfuse3' --auto_unmount - -run_allow_root_test - -export TEST_EXIT_STATUS=0 diff --git a/vendor/fuser/osx_mount_tests.sh b/vendor/fuser/osx_mount_tests.sh deleted file mode 100755 index f03465e10..000000000 --- a/vendor/fuser/osx_mount_tests.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -set -x - -exit_handler() { - exit "${TEST_EXIT_STATUS:-1}" -} -trap exit_handler TERM -trap 'kill $(jobs -p); exit $TEST_EXIT_STATUS' INT EXIT - -export RUST_BACKTRACE=1 - -NC="\e[39m" -GREEN="\e[32m" -RED="\e[31m" - -function run_test { - DIR=$(mktemp -d) - cargo build --example hello $1 > /dev/null 2>&1 - cargo run --example hello $1 -- $DIR $3 & - FUSE_PID=$! - sleep 2 - - echo "mounting at $DIR" - # Make sure FUSE was successfully mounted - mount | grep hello || exit 1 - - if [[ $(cat ${DIR}/hello.txt) = "Hello World!" ]]; then - echo -e "$GREEN OK $2 $3 $NC" - else - echo -e "$RED FAILED $2 $3 $NC" - export TEST_EXIT_STATUS=1 - exit 1 - fi - - kill $FUSE_PID - wait $FUSE_PID -} - -run_test --features=libfuse 'with libfuse' - -# TODO: re-enable this test. It seems to hang on OSX -#run_test --features=libfuse 'with libfuse' --auto_unmount - -export TEST_EXIT_STATUS=0 diff --git a/vendor/fuser/pjdfs.Dockerfile b/vendor/fuser/pjdfs.Dockerfile deleted file mode 100644 index 08e303667..000000000 --- a/vendor/fuser/pjdfs.Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -FROM ubuntu:20.04 - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt update && apt install -y git build-essential autoconf curl cmake libfuse-dev pkg-config fuse3 bc libtool \ - uuid-dev xfslibs-dev libattr1-dev libacl1-dev libaio-dev attr acl quota bsdmainutils dbench psmisc libfuse3-dev - -RUN adduser --disabled-password --gecos '' fsgqa - -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 - -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 . /code/fuser/ - -RUN cd /code/fuser && cargo build --release --examples $BUILD_FEATURES && cp target/release/examples/simple /bin/fuser diff --git a/vendor/fuser/pjdfs.sh b/vendor/fuser/pjdfs.sh deleted file mode 100755 index 4187faf0b..000000000 --- a/vendor/fuser/pjdfs.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -exit_handler() { - exit "$PJDFS_EXIT_STATUS" -} -trap exit_handler TERM -trap "kill 0" INT EXIT - -export RUST_BACKTRACE=1 - -DATA_DIR=$(mktemp --directory) -DIR=$(mktemp --directory) - -fuser -vvv --suid --data-dir $DATA_DIR --mount-point $DIR > /code/logs/mount.log 2>&1 & -FUSE_PID=$! -sleep 0.5 - -echo "mounting at $DIR" -# Make sure FUSE was successfully mounted -mount | grep fuser - -set +e -cd ${DIR} -prove -rf /code/pjdfstest/tests | tee /code/logs/pjdfs.log -export PJDFS_EXIT_STATUS=${PIPESTATUS[0]} -echo "Total failed:" -cat /code/logs/pjdfs.log | egrep -o 'Failed: [0-9]+' | egrep -o '[0-9]+' | paste -s -d+ | bc - -rm -rf ${DATA_DIR} - -kill $FUSE_PID -wait $FUSE_PID diff --git a/vendor/fuser/rust-toolchain b/vendor/fuser/rust-toolchain deleted file mode 100644 index bc8a6589c..000000000 --- a/vendor/fuser/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -1.74 diff --git a/vendor/fuser/rustfmt.toml b/vendor/fuser/rustfmt.toml deleted file mode 100644 index 758d4179d..000000000 --- a/vendor/fuser/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -max_width = 100 diff --git a/vendor/fuser/simplefs_tests.sh b/vendor/fuser/simplefs_tests.sh deleted file mode 100755 index 136b7fdbb..000000000 --- a/vendor/fuser/simplefs_tests.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash - -set -x - -exit_handler() { - exit "${TEST_EXIT_STATUS:-1}" -} -trap exit_handler TERM -trap 'kill $(jobs -p); exit $TEST_EXIT_STATUS' INT EXIT - -export RUST_BACKTRACE=1 - -NC="\e[39m" -GREEN="\e[32m" -RED="\e[31m" - -apt update -apt install -y fuse3 -echo 'user_allow_other' >> /etc/fuse.conf - -DATA_DIR=$(mktemp --directory) -DIR=$(mktemp --directory) -cargo build --example simple --no-default-features > /dev/null 2>&1 -cargo run --example simple --no-default-features -- -vvv --data-dir $DATA_DIR --mount-point $DIR 2>&1 & -FUSE_PID=$! -sleep 2 - -echo "mounting at $DIR" -# Make sure FUSE was successfully mounted -mount | grep fuser || exit 1 - -if touch $DIR/a && touch $DIR/b; then - echo -e "$GREEN OK touch file $NC" -else - echo -e "$RED FAILED touch file $NC" - export TEST_EXIT_STATUS=1 - exit 1 -fi - -umount $DIR - -kill $FUSE_PID -wait $FUSE_PID - - -export TEST_EXIT_STATUS=0 diff --git a/vendor/fuser/src/channel.rs b/vendor/fuser/src/channel.rs deleted file mode 100644 index fae35afb7..000000000 --- a/vendor/fuser/src/channel.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::{ - fs::File, - io, - os::{ - fd::{AsFd, BorrowedFd}, - unix::prelude::AsRawFd, - }, - sync::Arc, -}; - -use libc::{c_int, c_void, size_t}; - -use crate::reply::ReplySender; - -/// A raw communication channel to the FUSE kernel driver -#[derive(Debug)] -pub struct Channel(Arc); - -impl AsFd for Channel { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl Channel { - /// Create a new communication channel to the kernel driver by mounting the - /// given path. The kernel driver will delegate filesystem operations of - /// the given path to the channel. - pub(crate) fn new(device: Arc) -> Self { - Self(device) - } - - /// Receives data up to the capacity of the given buffer (can block). - pub fn receive(&self, buffer: &mut [u8]) -> io::Result { - let rc = unsafe { - libc::read( - self.0.as_raw_fd(), - buffer.as_ptr() as *mut c_void, - buffer.len() as size_t, - ) - }; - if rc < 0 { - Err(io::Error::last_os_error()) - } else { - Ok(rc as usize) - } - } - - /// Returns a sender object for this channel. The sender object can be - /// used to send to the channel. Multiple sender objects can be used - /// and they can safely be sent to other threads. - pub fn sender(&self) -> ChannelSender { - // Since write/writev syscalls are threadsafe, we can simply create - // a sender by using the same file and use it in other threads. - ChannelSender(self.0.clone()) - } -} - -#[derive(Clone, Debug)] -pub struct ChannelSender(Arc); - -impl ReplySender for ChannelSender { - fn send(&self, bufs: &[io::IoSlice<'_>]) -> io::Result<()> { - let rc = unsafe { - libc::writev( - self.0.as_raw_fd(), - bufs.as_ptr() as *const libc::iovec, - bufs.len() as c_int, - ) - }; - if rc < 0 { - Err(io::Error::last_os_error()) - } else { - debug_assert_eq!(bufs.iter().map(|b| b.len()).sum::(), rc as usize); - Ok(()) - } - } -} diff --git a/vendor/fuser/src/lib.rs b/vendor/fuser/src/lib.rs deleted file mode 100644 index 09ca12ba7..000000000 --- a/vendor/fuser/src/lib.rs +++ /dev/null @@ -1,1056 +0,0 @@ -//! FUSE userspace library implementation -//! -//! This is an improved rewrite of the FUSE userspace library (lowlevel interface) to fully take -//! advantage of Rust's architecture. The only thing we rely on in the real libfuse are mount -//! and unmount calls which are needed to establish a fd to talk to the kernel driver. - -#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] - -use libc::{c_int, ENOSYS, EPERM}; -use log::{debug, warn}; -use mnt::mount_options::parse_options_from_args; -#[cfg(feature = "serializable")] -use serde::{Deserialize, Serialize}; -use std::ffi::OsStr; -use std::io; -use std::path::Path; -#[cfg(feature = "abi-7-23")] -use std::time::Duration; -use std::time::SystemTime; -use std::{convert::AsRef, io::ErrorKind}; - -use crate::ll::fuse_abi::consts::*; -pub use crate::ll::fuse_abi::FUSE_ROOT_ID; -pub use crate::ll::{fuse_abi::consts, TimeOrNow}; -use crate::mnt::mount_options::check_option_conflicts; -use crate::session::MAX_WRITE_SIZE; -#[cfg(feature = "abi-7-16")] -pub use ll::fuse_abi::fuse_forget_one; -pub use mnt::{Mount, mount_options::MountOption}; -#[cfg(feature = "abi-7-11")] -pub use notify::{Notifier, PollHandle}; -#[cfg(feature = "abi-7-11")] -pub use reply::ReplyPoll; -#[cfg(target_os = "macos")] -pub use reply::ReplyXTimes; -pub use reply::ReplyXattr; -pub use reply::{Reply, ReplyAttr, ReplyData, ReplyEmpty, ReplyEntry, ReplyOpen}; -pub use reply::{ - ReplyBmap, ReplyCreate, ReplyDirectory, ReplyDirectoryPlus, ReplyIoctl, ReplyLock, ReplyLseek, - ReplyStatfs, ReplyWrite, -}; -pub use request::Request; -pub use session::{BackgroundSession, Session, SessionACL, SessionUnmounter}; -#[cfg(feature = "abi-7-28")] -use std::cmp::max; -#[cfg(feature = "abi-7-13")] -use std::cmp::min; - -mod channel; -mod ll; -mod mnt; -#[cfg(feature = "abi-7-11")] -mod notify; -mod reply; -mod request; -mod session; - -/// We generally support async reads -#[cfg(all(not(target_os = "macos"), not(feature = "abi-7-10")))] -const INIT_FLAGS: u32 = FUSE_ASYNC_READ; -#[cfg(all(not(target_os = "macos"), feature = "abi-7-10"))] -const INIT_FLAGS: u32 = FUSE_ASYNC_READ | FUSE_BIG_WRITES; -// TODO: Add FUSE_EXPORT_SUPPORT - -/// 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) - -const fn default_init_flags(#[allow(unused_variables)] capabilities: u32) -> u32 { - #[cfg(not(feature = "abi-7-28"))] - { - INIT_FLAGS - } - - #[cfg(feature = "abi-7-28")] - { - let mut flags = INIT_FLAGS; - if capabilities & FUSE_MAX_PAGES != 0 { - flags |= FUSE_MAX_PAGES; - } - flags - } -} - -/// File types -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))] -pub enum FileType { - /// Named pipe (S_IFIFO) - NamedPipe, - /// Character device (S_IFCHR) - CharDevice, - /// Block device (S_IFBLK) - BlockDevice, - /// Directory (S_IFDIR) - Directory, - /// Regular file (S_IFREG) - RegularFile, - /// Symbolic link (S_IFLNK) - Symlink, - /// Unix domain socket (S_IFSOCK) - Socket, -} - -/// File attributes -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))] -pub struct FileAttr { - /// Inode number - pub ino: u64, - /// Size in bytes - pub size: u64, - /// Size in blocks - pub blocks: u64, - /// Time of last access - pub atime: SystemTime, - /// Time of last modification - pub mtime: SystemTime, - /// Time of last change - pub ctime: SystemTime, - /// Time of creation (macOS only) - pub crtime: SystemTime, - /// Kind of file (directory, file, pipe, etc) - pub kind: FileType, - /// Permissions - pub perm: u16, - /// Number of hard links - pub nlink: u32, - /// User id - pub uid: u32, - /// Group id - pub gid: u32, - /// Rdev - pub rdev: u32, - /// Block size - pub blksize: u32, - /// Flags (macOS only, see chflags(2)) - pub flags: u32, -} - -/// Configuration of the fuse kernel module connection -#[derive(Debug)] -pub struct KernelConfig { - capabilities: u32, - requested: u32, - max_readahead: u32, - max_max_readahead: u32, - #[cfg(feature = "abi-7-13")] - max_background: u16, - #[cfg(feature = "abi-7-13")] - congestion_threshold: Option, - max_write: u32, - #[cfg(feature = "abi-7-23")] - time_gran: Duration, -} - -impl KernelConfig { - fn new(capabilities: u32, max_readahead: u32) -> Self { - Self { - capabilities, - requested: default_init_flags(capabilities), - max_readahead, - max_max_readahead: max_readahead, - #[cfg(feature = "abi-7-13")] - max_background: 16, - #[cfg(feature = "abi-7-13")] - congestion_threshold: None, - // use a max write size that fits into the session's buffer - max_write: MAX_WRITE_SIZE as u32, - // 1ns means nano-second granularity. - #[cfg(feature = "abi-7-23")] - time_gran: Duration::new(0, 1), - } - } - - /// Set the timestamp granularity - /// - /// Must be a power of 10 nanoseconds. i.e. 1s, 0.1s, 0.01s, 1ms, 0.1ms...etc - /// - /// On success returns the previous value. On error returns the nearest value which will succeed - #[cfg(feature = "abi-7-23")] - pub fn set_time_granularity(&mut self, value: Duration) -> Result { - if value.as_nanos() == 0 { - return Err(Duration::new(0, 1)); - } - if value.as_secs() > 1 || (value.as_secs() == 1 && value.subsec_nanos() > 0) { - return Err(Duration::new(1, 0)); - } - let mut power_of_10 = 1; - while power_of_10 < value.as_nanos() { - if value.as_nanos() < power_of_10 * 10 { - // value must not be a power of ten, since power_of_10 < value < power_of_10 * 10 - return Err(Duration::new(0, power_of_10 as u32)); - } - power_of_10 *= 10; - } - let previous = self.time_gran; - self.time_gran = value; - Ok(previous) - } - - /// Set the maximum write size for a single request - /// - /// On success returns the previous value. On error returns the nearest value which will succeed - pub fn set_max_write(&mut self, value: u32) -> Result { - if value == 0 { - return Err(1); - } - if value > MAX_WRITE_SIZE as u32 { - return Err(MAX_WRITE_SIZE as u32); - } - let previous = self.max_write; - self.max_write = value; - Ok(previous) - } - - /// Set the maximum readahead size - /// - /// On success returns the previous value. On error returns the nearest value which will succeed - pub fn set_max_readahead(&mut self, value: u32) -> Result { - if value == 0 { - return Err(1); - } - if value > self.max_max_readahead { - return Err(self.max_max_readahead); - } - let previous = self.max_readahead; - self.max_readahead = value; - Ok(previous) - } - - /// Add a set of capabilities. - /// - /// On success returns Ok, else return bits of capabilities not supported when capabilities you provided are not all supported by kernel. - pub fn add_capabilities(&mut self, capabilities_to_add: u32) -> Result<(), u32> { - if capabilities_to_add & self.capabilities != capabilities_to_add { - return Err(capabilities_to_add - (capabilities_to_add & self.capabilities)); - } - self.requested |= capabilities_to_add; - Ok(()) - } - - /// Set the maximum number of pending background requests. Such as readahead requests. - /// - /// On success returns the previous value. On error returns the nearest value which will succeed - #[cfg(feature = "abi-7-13")] - pub fn set_max_background(&mut self, value: u16) -> Result { - if value == 0 { - return Err(1); - } - let previous = self.max_background; - self.max_background = value; - Ok(previous) - } - - /// Set the threshold of background requests at which the kernel will consider the filesystem - /// request queue congested. (it may then switch to sleeping instead of spin-waiting, for example) - /// - /// On success returns the previous value. On error returns the nearest value which will succeed - #[cfg(feature = "abi-7-13")] - pub fn set_congestion_threshold(&mut self, value: u16) -> Result { - if value == 0 { - return Err(1); - } - let previous = self.congestion_threshold(); - self.congestion_threshold = Some(value); - Ok(previous) - } - - #[cfg(feature = "abi-7-13")] - fn congestion_threshold(&self) -> u16 { - match self.congestion_threshold { - // Default to a threshold of 3/4 of the max background threads - None => (self.max_background as u32 * 3 / 4) as u16, - Some(value) => min(value, self.max_background), - } - } - - #[cfg(feature = "abi-7-28")] - fn max_pages(&self) -> u16 { - ((max(self.max_write, self.max_readahead) - 1) / page_size::get() as u32) as u16 + 1 - } -} - -/// Filesystem trait. -/// -/// This trait must be implemented to provide a userspace filesystem via FUSE. -/// These methods correspond to fuse_lowlevel_ops in libfuse. Reasonable default -/// implementations are provided here to get a mountable filesystem that does -/// nothing. -#[allow(clippy::too_many_arguments)] -pub trait Filesystem { - /// Initialize filesystem. - /// Called before any other filesystem method. - /// The kernel module connection can be configured using the KernelConfig object - fn init(&self, _req: &Request<'_>, _config: &mut KernelConfig) -> Result<(), c_int> { - Ok(()) - } - - /// Clean up filesystem. - /// Called on filesystem exit. - fn destroy(&self) {} - - /// Look up a directory entry by name and get its attributes. - fn lookup(&self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) { - warn!( - "[Not Implemented] lookup(parent: {:#x?}, name {:?})", - parent, name - ); - reply.error(ENOSYS); - } - - /// Forget about an inode. - /// The nlookup parameter indicates the number of lookups previously performed on - /// this inode. If the filesystem implements inode lifetimes, it is recommended that - /// inodes acquire a single reference on each lookup, and lose nlookup references on - /// each forget. The filesystem may ignore forget calls, if the inodes don't need to - /// have a limited lifetime. On unmount it is not guaranteed, that all referenced - /// inodes will receive a forget message. - fn forget(&self, _req: &Request<'_>, _ino: u64, _nlookup: u64) {} - - /// Like forget, but take multiple forget requests at once for performance. The default - /// implementation will fallback to forget. - #[cfg(feature = "abi-7-16")] - fn batch_forget(&self, req: &Request<'_>, nodes: &[fuse_forget_one]) { - for node in nodes { - self.forget(req, node.nodeid, node.nlookup); - } - } - - /// Get file attributes. - fn getattr(&self, _req: &Request<'_>, ino: u64, fh: Option, reply: ReplyAttr) { - warn!( - "[Not Implemented] getattr(ino: {:#x?}, fh: {:#x?})", - ino, fh - ); - reply.error(ENOSYS); - } - - /// Set file attributes. - fn setattr( - &self, - _req: &Request<'_>, - ino: u64, - mode: Option, - uid: Option, - gid: Option, - size: Option, - _atime: Option, - _mtime: Option, - _ctime: Option, - fh: Option, - _crtime: Option, - _chgtime: Option, - _bkuptime: Option, - flags: Option, - reply: ReplyAttr, - ) { - debug!( - "[Not Implemented] setattr(ino: {:#x?}, mode: {:?}, uid: {:?}, \ - gid: {:?}, size: {:?}, fh: {:?}, flags: {:?})", - ino, mode, uid, gid, size, fh, flags - ); - reply.error(ENOSYS); - } - - /// Read symbolic link. - fn readlink(&self, _req: &Request<'_>, ino: u64, reply: ReplyData) { - debug!("[Not Implemented] readlink(ino: {:#x?})", ino); - reply.error(ENOSYS); - } - - /// Create file node. - /// Create a regular file, character device, block device, fifo or socket node. - fn mknod( - &self, - _req: &Request<'_>, - parent: u64, - name: &OsStr, - mode: u32, - umask: u32, - rdev: u32, - reply: ReplyEntry, - ) { - debug!( - "[Not Implemented] mknod(parent: {:#x?}, name: {:?}, mode: {}, \ - umask: {:#x?}, rdev: {})", - parent, name, mode, umask, rdev - ); - reply.error(ENOSYS); - } - - /// Create a directory. - fn mkdir( - &self, - _req: &Request<'_>, - parent: u64, - name: &OsStr, - mode: u32, - umask: u32, - reply: ReplyEntry, - ) { - debug!( - "[Not Implemented] mkdir(parent: {:#x?}, name: {:?}, mode: {}, umask: {:#x?})", - parent, name, mode, umask - ); - reply.error(ENOSYS); - } - - /// Remove a file. - fn unlink(&self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) { - debug!( - "[Not Implemented] unlink(parent: {:#x?}, name: {:?})", - parent, name, - ); - reply.error(ENOSYS); - } - - /// Remove a directory. - fn rmdir(&self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) { - debug!( - "[Not Implemented] rmdir(parent: {:#x?}, name: {:?})", - parent, name, - ); - reply.error(ENOSYS); - } - - /// Create a symbolic link. - fn symlink( - &self, - _req: &Request<'_>, - parent: u64, - link_name: &OsStr, - target: &Path, - reply: ReplyEntry, - ) { - debug!( - "[Not Implemented] symlink(parent: {:#x?}, link_name: {:?}, target: {:?})", - parent, link_name, target, - ); - reply.error(EPERM); - } - - /// Rename a file. - fn rename( - &self, - _req: &Request<'_>, - parent: u64, - name: &OsStr, - newparent: u64, - newname: &OsStr, - flags: u32, - reply: ReplyEmpty, - ) { - debug!( - "[Not Implemented] rename(parent: {:#x?}, name: {:?}, newparent: {:#x?}, \ - newname: {:?}, flags: {})", - parent, name, newparent, newname, flags, - ); - reply.error(ENOSYS); - } - - /// Create a hard link. - fn link( - &self, - _req: &Request<'_>, - ino: u64, - newparent: u64, - newname: &OsStr, - reply: ReplyEntry, - ) { - debug!( - "[Not Implemented] link(ino: {:#x?}, newparent: {:#x?}, newname: {:?})", - ino, newparent, newname - ); - reply.error(EPERM); - } - - /// Open a file. - /// Open flags (with the exception of O_CREAT, O_EXCL, O_NOCTTY and O_TRUNC) are - /// available in flags. Filesystem may store an arbitrary file handle (pointer, index, - /// etc) in fh, and use this in other all other file operations (read, write, flush, - /// release, fsync). Filesystem may also implement stateless file I/O and not store - /// anything in fh. There are also some flags (direct_io, keep_cache) which the - /// filesystem may set, to change the way the file is opened. See fuse_file_info - /// structure in for more details. - fn open(&self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) { - reply.opened(0, 0); - } - - /// Read data. - /// Read should send exactly the number of bytes requested except on EOF or error, - /// otherwise the rest of the data will be substituted with zeroes. An exception to - /// this is when the file has been opened in 'direct_io' mode, in which case the - /// return value of the read system call will reflect the return value of this - /// operation. fh will contain the value set by the open method, or will be undefined - /// if the open method didn't set any value. - /// - /// flags: these are the file flags, such as O_SYNC. Only supported with ABI >= 7.9 - /// lock_owner: only supported with ABI >= 7.9 - fn read( - &self, - _req: &Request<'_>, - ino: u64, - fh: u64, - offset: i64, - size: u32, - flags: i32, - lock_owner: Option, - reply: ReplyData, - ) { - warn!( - "[Not Implemented] read(ino: {:#x?}, fh: {}, offset: {}, size: {}, \ - flags: {:#x?}, lock_owner: {:?})", - ino, fh, offset, size, flags, lock_owner - ); - reply.error(ENOSYS); - } - - /// Write data. - /// Write should return exactly the number of bytes requested except on error. An - /// exception to this is when the file has been opened in 'direct_io' mode, in - /// which case the return value of the write system call will reflect the return - /// value of this operation. fh will contain the value set by the open method, or - /// will be undefined if the open method didn't set any value. - /// - /// write_flags: will contain FUSE_WRITE_CACHE, if this write is from the page cache. If set, - /// the pid, uid, gid, and fh may not match the value that would have been sent if write cachin - /// is disabled - /// flags: these are the file flags, such as O_SYNC. Only supported with ABI >= 7.9 - /// lock_owner: only supported with ABI >= 7.9 - fn write( - &self, - _req: &Request<'_>, - ino: u64, - fh: u64, - offset: i64, - data: &[u8], - write_flags: u32, - flags: i32, - lock_owner: Option, - reply: ReplyWrite, - ) { - debug!( - "[Not Implemented] write(ino: {:#x?}, fh: {}, offset: {}, data.len(): {}, \ - write_flags: {:#x?}, flags: {:#x?}, lock_owner: {:?})", - ino, - fh, - offset, - data.len(), - write_flags, - flags, - lock_owner - ); - reply.error(ENOSYS); - } - - /// Flush method. - /// This is called on each close() of the opened file. Since file descriptors can - /// be duplicated (dup, dup2, fork), for one open call there may be many flush - /// calls. Filesystems shouldn't assume that flush will always be called after some - /// writes, or that if will be called at all. fh will contain the value set by the - /// open method, or will be undefined if the open method didn't set any value. - /// NOTE: the name of the method is misleading, since (unlike fsync) the filesystem - /// is not forced to flush pending writes. One reason to flush data, is if the - /// filesystem wants to return write errors. If the filesystem supports file locking - /// operations (setlk, getlk) it should remove all locks belonging to 'lock_owner'. - fn flush(&self, _req: &Request<'_>, ino: u64, fh: u64, lock_owner: u64, reply: ReplyEmpty) { - debug!( - "[Not Implemented] flush(ino: {:#x?}, fh: {}, lock_owner: {:?})", - ino, fh, lock_owner - ); - reply.error(ENOSYS); - } - - /// Release an open file. - /// Release is called when there are no more references to an open file: all file - /// descriptors are closed and all memory mappings are unmapped. For every open - /// call there will be exactly one release call. The filesystem may reply with an - /// error, but error values are not returned to close() or munmap() which triggered - /// the release. fh will contain the value set by the open method, or will be undefined - /// if the open method didn't set any value. flags will contain the same flags as for - /// open. - fn release( - &self, - _req: &Request<'_>, - _ino: u64, - _fh: u64, - _flags: i32, - _lock_owner: Option, - _flush: bool, - reply: ReplyEmpty, - ) { - reply.ok(); - } - - /// Synchronize file contents. - /// If the datasync parameter is non-zero, then only the user data should be flushed, - /// not the meta data. - fn fsync(&self, _req: &Request<'_>, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { - debug!( - "[Not Implemented] fsync(ino: {:#x?}, fh: {}, datasync: {})", - ino, fh, datasync - ); - reply.error(ENOSYS); - } - - /// Open a directory. - /// Filesystem may store an arbitrary file handle (pointer, index, etc) in fh, and - /// use this in other all other directory stream operations (readdir, releasedir, - /// fsyncdir). Filesystem may also implement stateless directory I/O and not store - /// anything in fh, though that makes it impossible to implement standard conforming - /// directory stream operations in case the contents of the directory can change - /// between opendir and releasedir. - fn opendir(&self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) { - reply.opened(0, 0); - } - - /// Read directory. - /// Send a buffer filled using buffer.fill(), with size not exceeding the - /// requested size. Send an empty buffer on end of stream. fh will contain the - /// value set by the opendir method, or will be undefined if the opendir method - /// didn't set any value. - fn readdir( - &self, - _req: &Request<'_>, - ino: u64, - fh: u64, - offset: i64, - reply: ReplyDirectory, - ) { - warn!( - "[Not Implemented] readdir(ino: {:#x?}, fh: {}, offset: {})", - ino, fh, offset - ); - reply.error(ENOSYS); - } - - /// Read directory. - /// Send a buffer filled using buffer.fill(), with size not exceeding the - /// requested size. Send an empty buffer on end of stream. fh will contain the - /// value set by the opendir method, or will be undefined if the opendir method - /// didn't set any value. - fn readdirplus( - &self, - _req: &Request<'_>, - ino: u64, - fh: u64, - offset: i64, - reply: ReplyDirectoryPlus, - ) { - debug!( - "[Not Implemented] readdirplus(ino: {:#x?}, fh: {}, offset: {})", - ino, fh, offset - ); - reply.error(ENOSYS); - } - - /// Release an open directory. - /// For every opendir call there will be exactly one releasedir call. fh will - /// contain the value set by the opendir method, or will be undefined if the - /// opendir method didn't set any value. - fn releasedir( - &self, - _req: &Request<'_>, - _ino: u64, - _fh: u64, - _flags: i32, - reply: ReplyEmpty, - ) { - reply.ok(); - } - - /// Synchronize directory contents. - /// If the datasync parameter is set, then only the directory contents should - /// be flushed, not the meta data. fh will contain the value set by the opendir - /// method, or will be undefined if the opendir method didn't set any value. - fn fsyncdir( - &self, - _req: &Request<'_>, - ino: u64, - fh: u64, - datasync: bool, - reply: ReplyEmpty, - ) { - debug!( - "[Not Implemented] fsyncdir(ino: {:#x?}, fh: {}, datasync: {})", - ino, fh, datasync - ); - reply.error(ENOSYS); - } - - /// Get file system statistics. - fn statfs(&self, _req: &Request<'_>, _ino: u64, reply: ReplyStatfs) { - reply.statfs(0, 0, 0, 0, 0, 512, 255, 0); - } - - /// Set an extended attribute. - fn setxattr( - &self, - _req: &Request<'_>, - ino: u64, - name: &OsStr, - _value: &[u8], - flags: i32, - position: u32, - reply: ReplyEmpty, - ) { - debug!( - "[Not Implemented] setxattr(ino: {:#x?}, name: {:?}, flags: {:#x?}, position: {})", - ino, name, flags, position - ); - reply.error(ENOSYS); - } - - /// Get an extended attribute. - /// If `size` is 0, the size of the value should be sent with `reply.size()`. - /// If `size` is not 0, and the value fits, send it with `reply.data()`, or - /// `reply.error(ERANGE)` if it doesn't. - fn getxattr( - &self, - _req: &Request<'_>, - ino: u64, - name: &OsStr, - size: u32, - reply: ReplyXattr, - ) { - debug!( - "[Not Implemented] getxattr(ino: {:#x?}, name: {:?}, size: {})", - ino, name, size - ); - reply.error(ENOSYS); - } - - /// List extended attribute names. - /// If `size` is 0, the size of the value should be sent with `reply.size()`. - /// If `size` is not 0, and the value fits, send it with `reply.data()`, or - /// `reply.error(ERANGE)` if it doesn't. - fn listxattr(&self, _req: &Request<'_>, ino: u64, size: u32, reply: ReplyXattr) { - debug!( - "[Not Implemented] listxattr(ino: {:#x?}, size: {})", - ino, size - ); - reply.error(ENOSYS); - } - - /// Remove an extended attribute. - fn removexattr(&self, _req: &Request<'_>, ino: u64, name: &OsStr, reply: ReplyEmpty) { - debug!( - "[Not Implemented] removexattr(ino: {:#x?}, name: {:?})", - ino, name - ); - reply.error(ENOSYS); - } - - /// Check file access permissions. - /// This will be called for the access() system call. If the 'default_permissions' - /// mount option is given, this method is not called. This method is not called - /// under Linux kernel versions 2.4.x - fn access(&self, _req: &Request<'_>, ino: u64, mask: i32, reply: ReplyEmpty) { - debug!("[Not Implemented] access(ino: {:#x?}, mask: {})", ino, mask); - reply.error(ENOSYS); - } - - /// Create and open a file. - /// If the file does not exist, first create it with the specified mode, and then - /// open it. Open flags (with the exception of O_NOCTTY) are available in flags. - /// Filesystem may store an arbitrary file handle (pointer, index, etc) in fh, - /// and use this in other all other file operations (read, write, flush, release, - /// fsync). 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 for more details. If this method is not - /// implemented or under Linux kernel versions earlier than 2.6.15, the mknod() - /// and open() methods will be called instead. - fn create( - &self, - _req: &Request<'_>, - parent: u64, - name: &OsStr, - mode: u32, - umask: u32, - flags: i32, - reply: ReplyCreate, - ) { - debug!( - "[Not Implemented] create(parent: {:#x?}, name: {:?}, mode: {}, umask: {:#x?}, \ - flags: {:#x?})", - parent, name, mode, umask, flags - ); - reply.error(ENOSYS); - } - - /// Test for a POSIX file lock. - fn getlk( - &self, - _req: &Request<'_>, - ino: u64, - fh: u64, - lock_owner: u64, - start: u64, - end: u64, - typ: i32, - pid: u32, - reply: ReplyLock, - ) { - debug!( - "[Not Implemented] getlk(ino: {:#x?}, fh: {}, lock_owner: {}, start: {}, \ - end: {}, typ: {}, pid: {})", - ino, fh, lock_owner, start, end, typ, pid - ); - reply.error(ENOSYS); - } - - /// Acquire, modify or release a POSIX file lock. - /// For POSIX threads (NPTL) there's a 1-1 relation between pid and owner, but - /// otherwise this is not always the case. For checking lock ownership, - /// 'fi->owner' must be used. The l_pid field in 'struct flock' should only be - /// used to fill in this field in getlk(). Note: if the locking methods are not - /// implemented, the kernel will still allow file locking to work locally. - /// Hence these are only interesting for network filesystems and similar. - fn setlk( - &self, - _req: &Request<'_>, - ino: u64, - fh: u64, - lock_owner: u64, - start: u64, - end: u64, - typ: i32, - pid: u32, - sleep: bool, - reply: ReplyEmpty, - ) { - debug!( - "[Not Implemented] setlk(ino: {:#x?}, fh: {}, lock_owner: {}, start: {}, \ - end: {}, typ: {}, pid: {}, sleep: {})", - ino, fh, lock_owner, start, end, typ, pid, sleep - ); - reply.error(ENOSYS); - } - - /// Map block index within file to block index within device. - /// Note: This makes sense only for block device backed filesystems mounted - /// with the 'blkdev' option - fn bmap(&self, _req: &Request<'_>, ino: u64, blocksize: u32, idx: u64, reply: ReplyBmap) { - debug!( - "[Not Implemented] bmap(ino: {:#x?}, blocksize: {}, idx: {})", - ino, blocksize, idx, - ); - reply.error(ENOSYS); - } - - /// control device - fn ioctl( - &self, - _req: &Request<'_>, - ino: u64, - fh: u64, - flags: u32, - cmd: u32, - in_data: &[u8], - out_size: u32, - reply: ReplyIoctl, - ) { - debug!( - "[Not Implemented] ioctl(ino: {:#x?}, fh: {}, flags: {}, cmd: {}, \ - in_data.len(): {}, out_size: {})", - ino, - fh, - flags, - cmd, - in_data.len(), - out_size, - ); - reply.error(ENOSYS); - } - - /// Poll for events - #[cfg(feature = "abi-7-11")] - fn poll( - &self, - _req: &Request<'_>, - ino: u64, - fh: u64, - ph: PollHandle, - events: u32, - flags: u32, - reply: ReplyPoll, - ) { - debug!( - "[Not Implemented] poll(ino: {:#x?}, fh: {}, ph: {:?}, events: {}, flags: {})", - ino, fh, ph, events, flags - ); - reply.error(ENOSYS); - } - - /// Preallocate or deallocate space to a file - fn fallocate( - &self, - _req: &Request<'_>, - ino: u64, - fh: u64, - offset: i64, - length: i64, - mode: i32, - reply: ReplyEmpty, - ) { - debug!( - "[Not Implemented] fallocate(ino: {:#x?}, fh: {}, offset: {}, \ - length: {}, mode: {})", - ino, fh, offset, length, mode - ); - reply.error(ENOSYS); - } - - /// Reposition read/write file offset - fn lseek( - &self, - _req: &Request<'_>, - ino: u64, - fh: u64, - offset: i64, - whence: i32, - reply: ReplyLseek, - ) { - debug!( - "[Not Implemented] lseek(ino: {:#x?}, fh: {}, offset: {}, whence: {})", - ino, fh, offset, whence - ); - reply.error(ENOSYS); - } - - /// Copy the specified range from the source inode to the destination inode - fn copy_file_range( - &self, - _req: &Request<'_>, - ino_in: u64, - fh_in: u64, - offset_in: i64, - ino_out: u64, - fh_out: u64, - offset_out: i64, - len: u64, - flags: u32, - reply: ReplyWrite, - ) { - debug!( - "[Not Implemented] copy_file_range(ino_in: {:#x?}, fh_in: {}, \ - offset_in: {}, ino_out: {:#x?}, fh_out: {}, offset_out: {}, \ - len: {}, flags: {})", - ino_in, fh_in, offset_in, ino_out, fh_out, offset_out, len, flags - ); - reply.error(ENOSYS); - } - - /// macOS only: Rename the volume. Set fuse_init_out.flags during init to - /// FUSE_VOL_RENAME to enable - #[cfg(target_os = "macos")] - fn setvolname(&self, _req: &Request<'_>, name: &OsStr, reply: ReplyEmpty) { - debug!("[Not Implemented] setvolname(name: {:?})", name); - reply.error(ENOSYS); - } - - /// macOS only (undocumented) - #[cfg(target_os = "macos")] - fn exchange( - &self, - _req: &Request<'_>, - parent: u64, - name: &OsStr, - newparent: u64, - newname: &OsStr, - options: u64, - reply: ReplyEmpty, - ) { - debug!( - "[Not Implemented] exchange(parent: {:#x?}, name: {:?}, newparent: {:#x?}, \ - newname: {:?}, options: {})", - parent, name, newparent, newname, options - ); - reply.error(ENOSYS); - } - - /// macOS only: Query extended times (bkuptime and crtime). Set fuse_init_out.flags - /// during init to FUSE_XTIMES to enable - #[cfg(target_os = "macos")] - fn getxtimes(&self, _req: &Request<'_>, ino: u64, reply: ReplyXTimes) { - debug!("[Not Implemented] getxtimes(ino: {:#x?})", ino); - reply.error(ENOSYS); - } -} - -/// Mount the given filesystem to the given mountpoint. This function will -/// not return until the filesystem is unmounted. -/// -/// Note that you need to lead each option with a separate `"-o"` string. See -/// `examples/hello.rs`. -#[deprecated(note = "use mount2() instead")] -pub fn mount>( - filesystem: FS, - mountpoint: P, - options: &[&OsStr], -) -> io::Result<()> { - let options = parse_options_from_args(options)?; - mount2(filesystem, mountpoint, options.as_ref()) -} - -/// Mount the given filesystem to the given mountpoint. This function will -/// not return until the filesystem is unmounted. -/// -/// NOTE: This will eventually replace mount(), once the API is stable -pub fn mount2>( - filesystem: FS, - mountpoint: P, - options: &[MountOption], -) -> io::Result<()> { - check_option_conflicts(options)?; - Session::new(filesystem, mountpoint.as_ref(), options).and_then(|se| se.run()) -} - -/// Mount the given filesystem to the given mountpoint. This function spawns -/// a background thread to handle filesystem operations while being mounted -/// and therefore returns immediately. The returned handle should be stored -/// to reference the mounted filesystem. If it's dropped, the filesystem will -/// be unmounted. -#[deprecated(note = "use spawn_mount2() instead")] -pub fn spawn_mount<'a, FS: Filesystem + Send + 'static + 'a, P: AsRef>( - filesystem: FS, - mountpoint: P, - options: &[&OsStr], -) -> io::Result { - let options: Option> = options - .iter() - .map(|x| Some(MountOption::from_str(x.to_str()?))) - .collect(); - let options = options.ok_or(ErrorKind::InvalidData)?; - Session::new(filesystem, mountpoint.as_ref(), options.as_ref()).and_then(|se| se.spawn()) -} - -/// Mount the given filesystem to the given mountpoint. This function spawns -/// a background thread to handle filesystem operations while being mounted -/// and therefore returns immediately. The returned handle should be stored -/// to reference the mounted filesystem. If it's dropped, the filesystem will -/// be unmounted. -/// -/// NOTE: This is the corresponding function to mount2. -pub fn spawn_mount2<'a, FS: Filesystem + Send + 'static + 'a, P: AsRef>( - filesystem: FS, - mountpoint: P, - options: &[MountOption], -) -> io::Result { - check_option_conflicts(options)?; - Session::new(filesystem, mountpoint.as_ref(), options).and_then(|se| se.spawn()) -} diff --git a/vendor/fuser/src/ll/argument.rs b/vendor/fuser/src/ll/argument.rs deleted file mode 100644 index aba183eda..000000000 --- a/vendor/fuser/src/ll/argument.rs +++ /dev/null @@ -1,163 +0,0 @@ -//! Argument decomposition for FUSE operation requests. -//! -//! Helper to decompose a slice of binary data (incoming FUSE request) into multiple data -//! structures (request arguments). - -use std::ffi::OsStr; -use std::os::unix::ffi::OsStrExt; -use zerocopy::{FromBytes, Immutable, KnownLayout}; - -/// An iterator that can be used to fetch typed arguments from a byte slice. -pub struct ArgumentIterator<'a> { - data: &'a [u8], -} - -impl<'a> ArgumentIterator<'a> { - /// Create a new argument iterator for the given byte slice. - pub fn new(data: &'a [u8]) -> ArgumentIterator<'a> { - ArgumentIterator { data } - } - - /// Returns the size of the remaining data. - pub fn len(&self) -> usize { - self.data.len() - } - - /// Fetch a slice of all remaining bytes. - pub fn fetch_all(&mut self) -> &'a [u8] { - let bytes = self.data; - self.data = &[]; - bytes - } - - /// Fetch a typed argument. Returns `None` if there's not enough data left. - pub fn fetch(&mut self) -> Option<&'a T> { - match zerocopy::Ref::<_, T>::from_prefix(self.data) { - Err(_err) => { - // TODO: do something with _err - if self.data.as_ptr() as usize % core::mem::align_of::() != 0 { - // Panic on alignment errors as this is under the control - // of the programmer, we can still return None for size - // failures as this may be caused by insufficient external - // data. - panic!("Data unaligned"); - } else { - None - } - } - Ok((x, rest)) => { - self.data = rest; - Some(zerocopy::Ref::<&[u8], T>::into_ref(x)) - } - } - } - - /// 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::Ref::<_, [T]>::from_prefix_with_elems(self.data, count) { - Err(_err) => { - // TODO: do something with _err - if self.data.as_ptr() as usize % core::mem::align_of::() != 0 { - // Panic on alignment errors as this is under the control - // of the programmer, we can still return None for size - // failures as this may be caused by insufficient external - // data. - panic!("Data unaligned"); - } else { - None - } - } - Ok((x, rest)) => { - self.data = rest; - Some(zerocopy::Ref::<&[u8], [T]>::into_ref(x)) - } - } - } - - /// Fetch a (zero-terminated) string (can be non-utf8). Returns `None` if there's not enough - /// data left or no zero-termination could be found. - pub fn fetch_str(&mut self) -> Option<&'a OsStr> { - let len = memchr::memchr(0, self.data)?; - let (out, rest) = self.data.split_at(len); - self.data = &rest[1..]; - Some(OsStr::from_bytes(out)) - } -} - -#[cfg(test)] -pub mod tests { - use std::ops::Deref; - - use super::super::test::AlignedData; - use super::*; - use zerocopy::FromBytes; - - const TEST_DATA: AlignedData<[u8; 10]> = - AlignedData([0x66, 0x6f, 0x6f, 0x00, 0x62, 0x61, 0x72, 0x00, 0x62, 0x61]); - - #[repr(C)] - #[derive(FromBytes, KnownLayout, Immutable)] - struct TestArgument { - p1: u8, - p2: u8, - p3: u16, - } - - #[test] - fn all_data() { - let mut it = ArgumentIterator::new(TEST_DATA.deref()); - it.fetch_str().unwrap(); - let arg = it.fetch_all(); - assert_eq!(arg, [0x62, 0x61, 0x72, 0x00, 0x62, 0x61]); - } - - #[test] - fn generic_argument() { - let mut it = ArgumentIterator::new(TEST_DATA.deref()); - let arg: &TestArgument = it.fetch().unwrap(); - assert_eq!(arg.p1, 0x66); - assert_eq!(arg.p2, 0x6f); - assert_eq!(arg.p3, 0x006f); - let arg: &TestArgument = it.fetch().unwrap(); - assert_eq!(arg.p1, 0x62); - assert_eq!(arg.p2, 0x61); - assert_eq!(arg.p3, 0x0072); - assert_eq!(it.len(), 2); - } - - #[test] - fn string_argument() { - let mut it = ArgumentIterator::new(TEST_DATA.deref()); - let arg = it.fetch_str().unwrap(); - assert_eq!(arg, "foo"); - let arg = it.fetch_str().unwrap(); - assert_eq!(arg, "bar"); - assert_eq!(it.len(), 2); - } - - #[test] - fn mixed_arguments() { - let mut it = ArgumentIterator::new(TEST_DATA.deref()); - let arg: &TestArgument = it.fetch().unwrap(); - assert_eq!(arg.p1, 0x66); - assert_eq!(arg.p2, 0x6f); - assert_eq!(arg.p3, 0x006f); - let arg = it.fetch_str().unwrap(); - assert_eq!(arg, "bar"); - let arg = it.fetch_all(); - assert_eq!(arg, [0x62, 0x61]); - } - - #[test] - fn out_of_data() { - let mut it = ArgumentIterator::new(TEST_DATA.deref()); - it.fetch::().unwrap(); - let arg: Option<&TestArgument> = it.fetch(); - assert!(arg.is_none()); - assert_eq!(it.len(), 2); - let arg = it.fetch_str(); - assert!(arg.is_none()); - assert_eq!(it.len(), 2); - } -} diff --git a/vendor/fuser/src/ll/fuse_abi.rs b/vendor/fuser/src/ll/fuse_abi.rs deleted file mode 100644 index 38c205eb2..000000000 --- a/vendor/fuser/src/ll/fuse_abi.rs +++ /dev/null @@ -1,1141 +0,0 @@ -//! FUSE kernel interface. -//! -//! Types and definitions used for communication between the kernel driver and the userspace -//! part of a FUSE filesystem. Since the kernel driver may be installed independently, the ABI -//! interface is versioned and capabilities are exchanged during the initialization (mounting) -//! of a filesystem. -//! -//! OSXFUSE (macOS): -//! - supports ABI 7.8 in OSXFUSE 2.x -//! - supports ABI 7.19 since OSXFUSE 3.0.0 -//! -//! libfuse (Linux/BSD): -//! - supports ABI 7.8 since FUSE 2.6.0 -//! - supports ABI 7.12 since FUSE 2.8.0 -//! - supports ABI 7.18 since FUSE 2.9.0 -//! - supports ABI 7.19 since FUSE 2.9.1 -//! - supports ABI 7.26 since FUSE 3.0.0 -//! -//! Items without a version annotation are valid with ABI 7.8 and later - -#![warn(missing_debug_implementations)] -#![allow(missing_docs)] - -#[cfg(feature = "abi-7-9")] -use crate::consts::{FATTR_ATIME_NOW, FATTR_MTIME_NOW}; -use std::convert::TryFrom; -use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; - -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(all(feature = "abi-7-19", not(feature = "abi-7-20")))] -pub const FUSE_KERNEL_MINOR_VERSION: u32 = 19; -#[cfg(all(feature = "abi-7-20", not(feature = "abi-7-21")))] -pub const FUSE_KERNEL_MINOR_VERSION: u32 = 20; -#[cfg(all(feature = "abi-7-21", not(feature = "abi-7-22")))] -pub const FUSE_KERNEL_MINOR_VERSION: u32 = 21; -#[cfg(all(feature = "abi-7-22", not(feature = "abi-7-23")))] -pub const FUSE_KERNEL_MINOR_VERSION: u32 = 22; -#[cfg(all(feature = "abi-7-23", not(feature = "abi-7-24")))] -pub const FUSE_KERNEL_MINOR_VERSION: u32 = 23; -#[cfg(all(feature = "abi-7-24", not(feature = "abi-7-25")))] -pub const FUSE_KERNEL_MINOR_VERSION: u32 = 24; -#[cfg(all(feature = "abi-7-25", not(feature = "abi-7-26")))] -pub const FUSE_KERNEL_MINOR_VERSION: u32 = 25; -#[cfg(all(feature = "abi-7-26", not(feature = "abi-7-27")))] -pub const FUSE_KERNEL_MINOR_VERSION: u32 = 26; -#[cfg(all(feature = "abi-7-27", not(feature = "abi-7-28")))] -pub const FUSE_KERNEL_MINOR_VERSION: u32 = 27; -#[cfg(all(feature = "abi-7-28", not(feature = "abi-7-29")))] -pub const FUSE_KERNEL_MINOR_VERSION: u32 = 28; -#[cfg(all(feature = "abi-7-29", not(feature = "abi-7-30")))] -pub const FUSE_KERNEL_MINOR_VERSION: u32 = 29; -#[cfg(all(feature = "abi-7-30", not(feature = "abi-7-31")))] -pub const FUSE_KERNEL_MINOR_VERSION: u32 = 30; -#[cfg(feature = "abi-7-31")] -pub const FUSE_KERNEL_MINOR_VERSION: u32 = 31; - -pub const FUSE_ROOT_ID: u64 = 1; - -#[repr(C)] -#[derive(Debug, IntoBytes, Clone, Copy, KnownLayout, Immutable)] -pub struct fuse_attr { - pub ino: u64, - pub size: u64, - pub blocks: u64, - // NOTE: this field is defined as u64 in fuse_kernel.h in libfuse. However, it is treated as signed - // to match stat.st_atime - pub atime: i64, - // NOTE: this field is defined as u64 in fuse_kernel.h in libfuse. However, it is treated as signed - // to match stat.st_mtime - pub mtime: i64, - // NOTE: this field is defined as u64 in fuse_kernel.h in libfuse. However, it is treated as signed - // to match stat.st_ctime - pub ctime: i64, - #[cfg(target_os = "macos")] - pub crtime: u64, - pub atimensec: u32, - pub mtimensec: u32, - pub ctimensec: u32, - #[cfg(target_os = "macos")] - pub crtimensec: u32, - pub mode: u32, - pub nlink: u32, - pub uid: u32, - pub gid: u32, - 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)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_kstatfs { - pub blocks: u64, // Total blocks (in units of frsize) - pub bfree: u64, // Free blocks - pub bavail: u64, // Free blocks for unprivileged users - pub files: u64, // Total inodes - pub ffree: u64, // Free inodes - pub bsize: u32, // Filesystem block size - pub namelen: u32, // Maximum filename length - pub frsize: u32, // Fundamental file system block size - pub padding: u32, - pub spare: [u32; 6], -} - -#[repr(C)] -#[derive(Debug, IntoBytes, FromBytes, KnownLayout, Immutable)] -pub struct fuse_file_lock { - pub start: u64, - pub end: u64, - // NOTE: this field is defined as u32 in fuse_kernel.h in libfuse. However, it is treated as signed - pub typ: i32, - pub pid: u32, -} - -pub mod consts { - // Bitmasks for fuse_setattr_in.valid - pub const FATTR_MODE: u32 = 1 << 0; - pub const FATTR_UID: u32 = 1 << 1; - pub const FATTR_GID: u32 = 1 << 2; - pub const FATTR_SIZE: u32 = 1 << 3; - 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(feature = "abi-7-23")] - pub const FATTR_CTIME: u32 = 1 << 10; - - #[cfg(target_os = "macos")] - pub const FATTR_CRTIME: u32 = 1 << 28; - #[cfg(target_os = "macos")] - pub const FATTR_CHGTIME: u32 = 1 << 29; - #[cfg(target_os = "macos")] - pub const FATTR_BKUPTIME: u32 = 1 << 30; - #[cfg(target_os = "macos")] - pub const FATTR_FLAGS: u32 = 1 << 31; - - // 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(feature = "abi-7-28")] - pub const FOPEN_CACHE_DIR: u32 = 1 << 3; // allow caching this directory - #[cfg(feature = "abi-7-31")] - pub const FOPEN_STREAM: u32 = 1 << 4; // the file is stream-like (no file position at all) - - #[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; // 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(feature = "abi-7-20")] - pub const FUSE_AUTO_INVAL_DATA: u32 = 1 << 12; // automatically invalidate cached pages - #[cfg(feature = "abi-7-21")] - pub const FUSE_DO_READDIRPLUS: u32 = 1 << 13; // do READDIRPLUS (READDIR+LOOKUP in one) - #[cfg(feature = "abi-7-21")] - pub const FUSE_READDIRPLUS_AUTO: u32 = 1 << 14; // adaptive readdirplus - #[cfg(feature = "abi-7-22")] - pub const FUSE_ASYNC_DIO: u32 = 1 << 15; // asynchronous direct I/O submission - #[cfg(feature = "abi-7-23")] - pub const FUSE_WRITEBACK_CACHE: u32 = 1 << 16; // use writeback cache for buffered writes - #[cfg(feature = "abi-7-23")] - pub const FUSE_NO_OPEN_SUPPORT: u32 = 1 << 17; // kernel supports zero-message opens - #[cfg(feature = "abi-7-25")] - pub const FUSE_PARALLEL_DIROPS: u32 = 1 << 18; // allow parallel lookups and readdir - #[cfg(feature = "abi-7-26")] - pub const FUSE_HANDLE_KILLPRIV: u32 = 1 << 19; // fs handles killing suid/sgid/cap on write/chown/trunc - #[cfg(feature = "abi-7-26")] - pub const FUSE_POSIX_ACL: u32 = 1 << 20; // filesystem supports posix acls - #[cfg(feature = "abi-7-27")] - pub const FUSE_ABORT_ERROR: u32 = 1 << 21; // reading the device after abort returns ECONNABORTED - #[cfg(feature = "abi-7-28")] - pub const FUSE_MAX_PAGES: u32 = 1 << 22; // init_out.max_pages contains the max number of req pages - #[cfg(feature = "abi-7-28")] - pub const FUSE_CACHE_SYMLINKS: u32 = 1 << 23; // cache READLINK responses - #[cfg(feature = "abi-7-29")] - pub const FUSE_NO_OPENDIR_SUPPORT: u32 = 1 << 24; // kernel supports zero-message opendir - #[cfg(feature = "abi-7-30")] - pub const FUSE_EXPLICIT_INVAL_DATA: u32 = 1 << 25; // only invalidate cached pages on explicit request - - #[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")] - pub const FUSE_VOL_RENAME: u32 = 1 << 30; - #[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 - #[cfg(feature = "abi-7-31")] - pub const FUSE_WRITE_KILL_PRIV: u32 = 1 << 2; // kill suid and sgid bits - - // 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-30")] - pub const FUSE_IOCTL_COMPAT_X32: u32 = 1 << 5; // x32 compat ioctl on 64bit machine (64bit time_t) - #[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 - - // fsync flags - pub const FUSE_FSYNC_FDATASYNC: u32 = 1 << 0; // Sync data only, not metadata - - // The read buffer is required to be at least 8k, but may be much larger - pub const FUSE_MIN_READ_BUFFER: usize = 8192; -} - -/// Invalid opcode error. -#[derive(Debug)] -pub struct InvalidOpcodeError; - -#[repr(C)] -#[derive(Debug)] -#[allow(non_camel_case_types)] -pub enum fuse_opcode { - FUSE_LOOKUP = 1, - FUSE_FORGET = 2, // no reply - FUSE_GETATTR = 3, - FUSE_SETATTR = 4, - FUSE_READLINK = 5, - FUSE_SYMLINK = 6, - FUSE_MKNOD = 8, - FUSE_MKDIR = 9, - FUSE_UNLINK = 10, - FUSE_RMDIR = 11, - FUSE_RENAME = 12, - FUSE_LINK = 13, - FUSE_OPEN = 14, - FUSE_READ = 15, - FUSE_WRITE = 16, - FUSE_STATFS = 17, - FUSE_RELEASE = 18, - FUSE_FSYNC = 20, - FUSE_SETXATTR = 21, - FUSE_GETXATTR = 22, - FUSE_LISTXATTR = 23, - FUSE_REMOVEXATTR = 24, - FUSE_FLUSH = 25, - FUSE_INIT = 26, - FUSE_OPENDIR = 27, - FUSE_READDIR = 28, - FUSE_RELEASEDIR = 29, - FUSE_FSYNCDIR = 30, - FUSE_GETLK = 31, - FUSE_SETLK = 32, - FUSE_SETLKW = 33, - FUSE_ACCESS = 34, - FUSE_CREATE = 35, - 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(feature = "abi-7-21")] - FUSE_READDIRPLUS = 44, - #[cfg(feature = "abi-7-23")] - FUSE_RENAME2 = 45, - #[cfg(feature = "abi-7-24")] - FUSE_LSEEK = 46, - #[cfg(feature = "abi-7-28")] - FUSE_COPY_FILE_RANGE = 47, - - #[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, -} - -impl TryFrom for fuse_opcode { - type Error = InvalidOpcodeError; - - fn try_from(n: u32) -> Result { - match n { - 1 => Ok(fuse_opcode::FUSE_LOOKUP), - 2 => Ok(fuse_opcode::FUSE_FORGET), - 3 => Ok(fuse_opcode::FUSE_GETATTR), - 4 => Ok(fuse_opcode::FUSE_SETATTR), - 5 => Ok(fuse_opcode::FUSE_READLINK), - 6 => Ok(fuse_opcode::FUSE_SYMLINK), - 8 => Ok(fuse_opcode::FUSE_MKNOD), - 9 => Ok(fuse_opcode::FUSE_MKDIR), - 10 => Ok(fuse_opcode::FUSE_UNLINK), - 11 => Ok(fuse_opcode::FUSE_RMDIR), - 12 => Ok(fuse_opcode::FUSE_RENAME), - 13 => Ok(fuse_opcode::FUSE_LINK), - 14 => Ok(fuse_opcode::FUSE_OPEN), - 15 => Ok(fuse_opcode::FUSE_READ), - 16 => Ok(fuse_opcode::FUSE_WRITE), - 17 => Ok(fuse_opcode::FUSE_STATFS), - 18 => Ok(fuse_opcode::FUSE_RELEASE), - 20 => Ok(fuse_opcode::FUSE_FSYNC), - 21 => Ok(fuse_opcode::FUSE_SETXATTR), - 22 => Ok(fuse_opcode::FUSE_GETXATTR), - 23 => Ok(fuse_opcode::FUSE_LISTXATTR), - 24 => Ok(fuse_opcode::FUSE_REMOVEXATTR), - 25 => Ok(fuse_opcode::FUSE_FLUSH), - 26 => Ok(fuse_opcode::FUSE_INIT), - 27 => Ok(fuse_opcode::FUSE_OPENDIR), - 28 => Ok(fuse_opcode::FUSE_READDIR), - 29 => Ok(fuse_opcode::FUSE_RELEASEDIR), - 30 => Ok(fuse_opcode::FUSE_FSYNCDIR), - 31 => Ok(fuse_opcode::FUSE_GETLK), - 32 => Ok(fuse_opcode::FUSE_SETLK), - 33 => Ok(fuse_opcode::FUSE_SETLKW), - 34 => Ok(fuse_opcode::FUSE_ACCESS), - 35 => Ok(fuse_opcode::FUSE_CREATE), - 36 => Ok(fuse_opcode::FUSE_INTERRUPT), - 37 => Ok(fuse_opcode::FUSE_BMAP), - 38 => Ok(fuse_opcode::FUSE_DESTROY), - #[cfg(feature = "abi-7-11")] - 39 => Ok(fuse_opcode::FUSE_IOCTL), - #[cfg(feature = "abi-7-11")] - 40 => Ok(fuse_opcode::FUSE_POLL), - #[cfg(feature = "abi-7-15")] - 41 => Ok(fuse_opcode::FUSE_NOTIFY_REPLY), - #[cfg(feature = "abi-7-16")] - 42 => Ok(fuse_opcode::FUSE_BATCH_FORGET), - #[cfg(feature = "abi-7-19")] - 43 => Ok(fuse_opcode::FUSE_FALLOCATE), - #[cfg(feature = "abi-7-21")] - 44 => Ok(fuse_opcode::FUSE_READDIRPLUS), - #[cfg(feature = "abi-7-23")] - 45 => Ok(fuse_opcode::FUSE_RENAME2), - #[cfg(feature = "abi-7-24")] - 46 => Ok(fuse_opcode::FUSE_LSEEK), - #[cfg(feature = "abi-7-28")] - 47 => Ok(fuse_opcode::FUSE_COPY_FILE_RANGE), - - #[cfg(target_os = "macos")] - 61 => Ok(fuse_opcode::FUSE_SETVOLNAME), - #[cfg(target_os = "macos")] - 62 => Ok(fuse_opcode::FUSE_GETXTIMES), - #[cfg(target_os = "macos")] - 63 => Ok(fuse_opcode::FUSE_EXCHANGE), - - #[cfg(feature = "abi-7-12")] - 4096 => Ok(fuse_opcode::CUSE_INIT), - - _ => Err(InvalidOpcodeError), - } - } -} - -/// Invalid notify code error. -#[cfg(feature = "abi-7-11")] -#[derive(Debug)] -pub struct InvalidNotifyCodeError; - -#[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, -} - -#[cfg(feature = "abi-7-11")] -impl TryFrom for fuse_notify_code { - type Error = InvalidNotifyCodeError; - - fn try_from(n: u32) -> Result { - match n { - #[cfg(feature = "abi-7-11")] - 1 => Ok(fuse_notify_code::FUSE_POLL), - #[cfg(feature = "abi-7-12")] - 2 => Ok(fuse_notify_code::FUSE_NOTIFY_INVAL_INODE), - #[cfg(feature = "abi-7-12")] - 3 => Ok(fuse_notify_code::FUSE_NOTIFY_INVAL_ENTRY), - #[cfg(feature = "abi-7-15")] - 4 => Ok(fuse_notify_code::FUSE_NOTIFY_STORE), - #[cfg(feature = "abi-7-15")] - 5 => Ok(fuse_notify_code::FUSE_NOTIFY_RETRIEVE), - #[cfg(feature = "abi-7-18")] - 6 => Ok(fuse_notify_code::FUSE_NOTIFY_DELETE), - - _ => Err(InvalidNotifyCodeError), - } - } -} - -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_entry_out { - pub nodeid: u64, - pub generation: u64, - pub entry_valid: u64, - pub attr_valid: u64, - pub entry_valid_nsec: u32, - pub attr_valid_nsec: u32, - pub attr: fuse_attr, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_forget_in { - pub nlookup: u64, -} - -#[cfg(feature = "abi-7-16")] -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_forget_one { - pub nodeid: u64, - pub nlookup: u64, -} - -#[cfg(feature = "abi-7-16")] -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_batch_forget_in { - pub count: u32, - pub dummy: u32, -} - -#[cfg(feature = "abi-7-9")] -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_getattr_in { - pub getattr_flags: u32, - pub dummy: u32, - pub fh: u64, -} - -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_attr_out { - pub attr_valid: u64, - pub attr_valid_nsec: u32, - pub dummy: u32, - pub attr: fuse_attr, -} - -#[cfg(target_os = "macos")] -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_getxtimes_out { - pub bkuptime: u64, - pub crtime: u64, - pub bkuptimensec: u32, - pub crtimensec: u32, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -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, FromBytes, KnownLayout, Immutable)] -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)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_rename_in { - pub newdir: u64, - #[cfg(feature = "macfuse-4-compat")] - pub flags: u32, - #[cfg(feature = "macfuse-4-compat")] - pub padding: u32, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_rename2_in { - pub newdir: u64, - pub flags: u32, - pub padding: u32, -} - -#[cfg(target_os = "macos")] -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_exchange_in { - pub olddir: u64, - pub newdir: u64, - pub options: u64, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_link_in { - pub oldnodeid: u64, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_setattr_in { - pub valid: u32, - 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, - // NOTE: this field is defined as u64 in fuse_kernel.h in libfuse. However, it is treated as signed - // to match stat.st_atime - pub atime: i64, - // NOTE: this field is defined as u64 in fuse_kernel.h in libfuse. However, it is treated as signed - // to match stat.st_mtime - pub mtime: i64, - #[cfg(not(feature = "abi-7-23"))] - pub unused2: u64, - #[cfg(feature = "abi-7-23")] - // NOTE: this field is defined as u64 in fuse_kernel.h in libfuse. However, it is treated as signed - // to match stat.st_ctime - pub ctime: i64, - pub atimensec: u32, - pub mtimensec: u32, - #[cfg(not(feature = "abi-7-23"))] - pub unused3: u32, - #[cfg(feature = "abi-7-23")] - pub ctimensec: u32, - pub mode: u32, - pub unused4: u32, - pub uid: u32, - pub gid: u32, - pub unused5: u32, - #[cfg(target_os = "macos")] - pub bkuptime: u64, - #[cfg(target_os = "macos")] - pub chgtime: u64, - #[cfg(target_os = "macos")] - pub crtime: u64, - #[cfg(target_os = "macos")] - pub bkuptimensec: u32, - #[cfg(target_os = "macos")] - pub chgtimensec: u32, - #[cfg(target_os = "macos")] - pub crtimensec: u32, - #[cfg(target_os = "macos")] - pub flags: u32, // see chflags(2) -} - -impl fuse_setattr_in { - #[cfg(feature = "abi-7-9")] - pub fn atime_now(&self) -> bool { - self.valid & FATTR_ATIME_NOW != 0 - } - - #[cfg(not(feature = "abi-7-9"))] - pub fn atime_now(&self) -> bool { - false - } - - #[cfg(feature = "abi-7-9")] - pub fn mtime_now(&self) -> bool { - self.valid & FATTR_MTIME_NOW != 0 - } - - #[cfg(not(feature = "abi-7-9"))] - pub fn mtime_now(&self) -> bool { - false - } -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -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 - pub flags: i32, - pub unused: u32, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -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 - pub flags: i32, - pub mode: u32, - #[cfg(feature = "abi-7-12")] - pub umask: u32, - #[cfg(feature = "abi-7-12")] - pub padding: u32, -} - -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_create_out(pub fuse_entry_out, pub fuse_open_out); - -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_open_out { - pub fh: u64, - pub open_flags: u32, - pub padding: u32, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -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 - // to an i32 when invoking the filesystem's read method - pub flags: i32, - pub release_flags: u32, - pub lock_owner: u64, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_flush_in { - pub fh: u64, - pub unused: u32, - pub padding: u32, - pub lock_owner: u64, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -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 - // to an i64 when invoking the filesystem's read method - 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")] - // 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 read method - pub flags: i32, - #[cfg(feature = "abi-7-9")] - pub padding: u32, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -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 - // to an i64 when invoking the filesystem's write method - pub offset: i64, - pub size: u32, - pub write_flags: u32, - #[cfg(feature = "abi-7-9")] - pub lock_owner: u64, - #[cfg(feature = "abi-7-9")] - // 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 read method - pub flags: i32, - #[cfg(feature = "abi-7-9")] - pub padding: u32, -} - -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_write_out { - pub size: u32, - pub padding: u32, -} - -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_statfs_out { - pub st: fuse_kstatfs, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_fsync_in { - pub fh: u64, - pub fsync_flags: u32, - pub padding: u32, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -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 - // to an i32 when invoking the filesystem's setxattr method - pub flags: i32, - #[cfg(target_os = "macos")] - pub position: u32, - #[cfg(target_os = "macos")] - pub padding: u32, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_getxattr_in { - pub size: u32, - pub padding: u32, - #[cfg(target_os = "macos")] - pub position: u32, - #[cfg(target_os = "macos")] - pub padding2: u32, -} - -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_getxattr_out { - pub size: u32, - pub padding: u32, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -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)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_lk_out { - pub lk: fuse_file_lock, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -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 - pub mask: i32, - pub padding: u32, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_init_in { - pub major: u32, - pub minor: u32, - pub max_readahead: u32, - pub flags: u32, -} - -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_init_out { - pub major: u32, - 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-23")] - pub time_gran: u32, - #[cfg(all(feature = "abi-7-23", not(feature = "abi-7-28")))] - pub reserved: [u32; 9], - #[cfg(feature = "abi-7-28")] - pub max_pages: u16, - #[cfg(feature = "abi-7-28")] - pub unused2: u16, - #[cfg(feature = "abi-7-28")] - pub reserved: [u32; 8], -} - -#[cfg(feature = "abi-7-12")] -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -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, KnownLayout, Immutable)] -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)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_interrupt_in { - pub unique: u64, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_bmap_in { - pub block: u64, - pub blocksize: u32, - pub padding: u32, -} - -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_bmap_out { - pub block: u64, -} - -#[cfg(feature = "abi-7-11")] -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_ioctl_in { - pub fh: u64, - pub flags: u32, - pub cmd: u32, - pub arg: u64, // TODO: this is currently unused, but is defined as a void* in libfuse - pub in_size: u32, - pub out_size: u32, -} - -#[cfg(feature = "abi-7-16")] -#[repr(C)] -#[derive(Debug, KnownLayout, Immutable)] -pub struct fuse_ioctl_iovec { - pub base: u64, - pub len: u64, -} - -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -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, FromBytes, KnownLayout, Immutable)] -pub struct fuse_poll_in { - pub fh: u64, - pub kh: u64, - pub flags: u32, - #[cfg(not(feature = "abi-7-21"))] - pub padding: u32, - #[cfg(feature = "abi-7-21")] - pub events: u32, -} - -#[cfg(feature = "abi-7-11")] -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_poll_out { - pub revents: u32, - pub padding: u32, -} - -#[cfg(feature = "abi-7-11")] -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_notify_poll_wakeup_out { - pub kh: u64, -} - -#[cfg(feature = "abi-7-19")] -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -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 - pub offset: i64, - // NOTE: this field is defined as u64 in fuse_kernel.h in libfuse. However, it is treated as signed - pub length: i64, - // NOTE: this field is defined as u32 in fuse_kernel.h in libfuse. However, it is treated as signed - pub mode: i32, - pub padding: u32, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_in_header { - pub len: u32, - pub opcode: u32, - pub unique: u64, - pub nodeid: u64, - pub uid: u32, - pub gid: u32, - pub pid: u32, - pub padding: u32, -} - -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_out_header { - pub len: u32, - pub error: i32, - pub unique: u64, -} - -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_dirent { - pub ino: u64, - // NOTE: this field is defined as u64 in fuse_kernel.h in libfuse. However, it is treated as signed - pub off: i64, - pub namelen: u32, - pub typ: u32, - // followed by name of namelen bytes -} - -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_direntplus { - pub entry_out: fuse_entry_out, - pub dirent: fuse_dirent, -} - -#[cfg(feature = "abi-7-12")] -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -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, IntoBytes, KnownLayout, Immutable)] -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, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_notify_delete_out { - pub parent: u64, - pub child: u64, - pub namelen: u32, - pub padding: u32, -} - -#[cfg(feature = "abi-7-15")] -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -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, KnownLayout, Immutable)] -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, FromBytes, KnownLayout, Immutable)] -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, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -pub struct fuse_lseek_in { - pub fh: u64, - pub offset: i64, - // NOTE: this field is defined as u32 in fuse_kernel.h in libfuse. However, it is treated as signed - pub whence: i32, - pub padding: u32, -} - -#[repr(C)] -#[derive(Debug, IntoBytes, KnownLayout, Immutable)] -pub struct fuse_lseek_out { - pub offset: i64, -} - -#[repr(C)] -#[derive(Debug, FromBytes, KnownLayout, Immutable)] -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 - pub off_in: i64, - pub nodeid_out: u64, - pub fh_out: u64, - // NOTE: this field is defined as u64 in fuse_kernel.h in libfuse. However, it is treated as signed - pub off_out: i64, - pub len: u64, - pub flags: u64, -} diff --git a/vendor/fuser/src/ll/mod.rs b/vendor/fuser/src/ll/mod.rs deleted file mode 100644 index 860d0283a..000000000 --- a/vendor/fuser/src/ll/mod.rs +++ /dev/null @@ -1,297 +0,0 @@ -//! Low-level kernel communication. - -mod argument; -pub mod fuse_abi; -#[cfg(feature = "abi-7-11")] -pub(crate) mod notify; -pub(crate) mod reply; -mod request; - -use std::{convert::TryInto, num::NonZeroI32, time::SystemTime}; - -pub use reply::Response; -pub use request::{AnyRequest, FileHandle, INodeNo, Lock, Operation, Request, RequestId, Version}; - -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -/// Possible input arguments for atime & mtime, which can either be set to a specified time, -/// or to the current time -pub enum TimeOrNow { - /// Specific time provided - SpecificTime(SystemTime), - /// Current time - Now, -} - -macro_rules! errno { - ($x: expr) => { - Errno(unsafe { - // This is a static assertion that the constant $x is > 0 - const _X: [(); 0 - !{ - const ASSERT: bool = ($x > 0); - ASSERT - } as usize] = []; - // Which makes this safe - NonZeroI32::new_unchecked($x) - }) - }; -} - -/// Represents an error code to be returned to the caller -#[derive(Debug)] -pub struct Errno(pub NonZeroI32); -impl Errno { - /// Operation not permitted - pub const EPERM: Errno = errno!(libc::EPERM); - /// No such file or directory - pub const ENOENT: Errno = errno!(libc::ENOENT); - /// No such process - pub const ESRCH: Errno = errno!(libc::ESRCH); - /// Interrupted system call - pub const EINTR: Errno = errno!(libc::EINTR); - /// Input/output error - pub const EIO: Errno = errno!(libc::EIO); - /// No such device or address - pub const ENXIO: Errno = errno!(libc::ENXIO); - /// Argument list too long - pub const E2BIG: Errno = errno!(libc::E2BIG); - /// Exec format error - pub const ENOEXEC: Errno = errno!(libc::ENOEXEC); - /// Bad file descriptor - pub const EBADF: Errno = errno!(libc::EBADF); - /// No child processes - pub const ECHILD: Errno = errno!(libc::ECHILD); - /// Resource temporarily unavailable - pub const EAGAIN: Errno = errno!(libc::EAGAIN); - /// Cannot allocate memory - pub const ENOMEM: Errno = errno!(libc::ENOMEM); - /// Permission denied - pub const EACCES: Errno = errno!(libc::EACCES); - /// Bad address - pub const EFAULT: Errno = errno!(libc::EFAULT); - /// Block device required - pub const ENOTBLK: Errno = errno!(libc::ENOTBLK); - /// Device or resource busy - pub const EBUSY: Errno = errno!(libc::EBUSY); - /// File exists - pub const EEXIST: Errno = errno!(libc::EEXIST); - /// Invalid cross-device link - pub const EXDEV: Errno = errno!(libc::EXDEV); - /// No such device - pub const ENODEV: Errno = errno!(libc::ENODEV); - /// Not a directory - pub const ENOTDIR: Errno = errno!(libc::ENOTDIR); - /// Is a directory - pub const EISDIR: Errno = errno!(libc::EISDIR); - /// Invalid argument - pub const EINVAL: Errno = errno!(libc::EINVAL); - /// Too many open files in system - pub const ENFILE: Errno = errno!(libc::ENFILE); - /// Too many open files - pub const EMFILE: Errno = errno!(libc::EMFILE); - /// Inappropriate ioctl for device - pub const ENOTTY: Errno = errno!(libc::ENOTTY); - /// Text file busy - pub const ETXTBSY: Errno = errno!(libc::ETXTBSY); - /// File too large - pub const EFBIG: Errno = errno!(libc::EFBIG); - /// No space left on device - pub const ENOSPC: Errno = errno!(libc::ENOSPC); - /// Illegal seek - pub const ESPIPE: Errno = errno!(libc::ESPIPE); - /// Read-only file system - pub const EROFS: Errno = errno!(libc::EROFS); - /// Too many links - pub const EMLINK: Errno = errno!(libc::EMLINK); - /// Broken pipe - pub const EPIPE: Errno = errno!(libc::EPIPE); - /// Numerical argument out of domain - pub const EDOM: Errno = errno!(libc::EDOM); - /// Numerical result out of range - pub const ERANGE: Errno = errno!(libc::ERANGE); - /// Resource deadlock avoided - pub const EDEADLK: Errno = errno!(libc::EDEADLK); - /// File name too long - pub const ENAMETOOLONG: Errno = errno!(libc::ENAMETOOLONG); - /// No locks available - pub const ENOLCK: Errno = errno!(libc::ENOLCK); - /// Function not implemented - pub const ENOSYS: Errno = errno!(libc::ENOSYS); - /// Directory not empty - pub const ENOTEMPTY: Errno = errno!(libc::ENOTEMPTY); - /// Too many levels of symbolic links - pub const ELOOP: Errno = errno!(libc::ELOOP); - /// Resource temporarily unavailable - pub const EWOULDBLOCK: Errno = errno!(libc::EWOULDBLOCK); - /// No message of desired type - pub const ENOMSG: Errno = errno!(libc::ENOMSG); - /// Identifier removed - pub const EIDRM: Errno = errno!(libc::EIDRM); - /// Object is remote - pub const EREMOTE: Errno = errno!(libc::EREMOTE); - /// Link has been severed - pub const ENOLINK: Errno = errno!(libc::ENOLINK); - /// Protocol error - pub const EPROTO: Errno = errno!(libc::EPROTO); - /// Multihop attempted - pub const EMULTIHOP: Errno = errno!(libc::EMULTIHOP); - /// Bad message - pub const EBADMSG: Errno = errno!(libc::EBADMSG); - /// Value too large for defined data type - pub const EOVERFLOW: Errno = errno!(libc::EOVERFLOW); - /// Invalid or incomplete multibyte or wide character - pub const EILSEQ: Errno = errno!(libc::EILSEQ); - /// Too many users - pub const EUSERS: Errno = errno!(libc::EUSERS); - /// Socket operation on non-socket - pub const ENOTSOCK: Errno = errno!(libc::ENOTSOCK); - /// Destination address required - pub const EDESTADDRREQ: Errno = errno!(libc::EDESTADDRREQ); - /// Message too long - pub const EMSGSIZE: Errno = errno!(libc::EMSGSIZE); - /// Protocol wrong type for socket - pub const EPROTOTYPE: Errno = errno!(libc::EPROTOTYPE); - /// Protocol not available - pub const ENOPROTOOPT: Errno = errno!(libc::ENOPROTOOPT); - /// Protocol not supported - pub const EPROTONOSUPPORT: Errno = errno!(libc::EPROTONOSUPPORT); - /// Socket type not supported - pub const ESOCKTNOSUPPORT: Errno = errno!(libc::ESOCKTNOSUPPORT); - /// Operation not supported - pub const EOPNOTSUPP: Errno = errno!(libc::EOPNOTSUPP); - /// Protocol family not supported - pub const EPFNOSUPPORT: Errno = errno!(libc::EPFNOSUPPORT); - /// Address family not supported by protocol - pub const EAFNOSUPPORT: Errno = errno!(libc::EAFNOSUPPORT); - /// Address already in use - pub const EADDRINUSE: Errno = errno!(libc::EADDRINUSE); - /// Cannot assign requested address - pub const EADDRNOTAVAIL: Errno = errno!(libc::EADDRNOTAVAIL); - /// Network is down - pub const ENETDOWN: Errno = errno!(libc::ENETDOWN); - /// Network is unreachable - pub const ENETUNREACH: Errno = errno!(libc::ENETUNREACH); - /// Network dropped connection on reset - pub const ENETRESET: Errno = errno!(libc::ENETRESET); - /// Software caused connection abort - pub const ECONNABORTED: Errno = errno!(libc::ECONNABORTED); - /// Connection reset by peer - pub const ECONNRESET: Errno = errno!(libc::ECONNRESET); - /// No buffer space available - pub const ENOBUFS: Errno = errno!(libc::ENOBUFS); - /// Transport endpoint is already connected - pub const EISCONN: Errno = errno!(libc::EISCONN); - /// Transport endpoint is not connected - pub const ENOTCONN: Errno = errno!(libc::ENOTCONN); - /// Cannot send after transport endpoint shutdown - pub const ESHUTDOWN: Errno = errno!(libc::ESHUTDOWN); - /// Too many references: cannot splice - pub const ETOOMANYREFS: Errno = errno!(libc::ETOOMANYREFS); - /// Connection timed out - pub const ETIMEDOUT: Errno = errno!(libc::ETIMEDOUT); - /// Connection refused - pub const ECONNREFUSED: Errno = errno!(libc::ECONNREFUSED); - /// Host is down - pub const EHOSTDOWN: Errno = errno!(libc::EHOSTDOWN); - /// No route to host - pub const EHOSTUNREACH: Errno = errno!(libc::EHOSTUNREACH); - /// Operation already in progress - pub const EALREADY: Errno = errno!(libc::EALREADY); - /// Operation now in progress - pub const EINPROGRESS: Errno = errno!(libc::EINPROGRESS); - /// Stale file handle - pub const ESTALE: Errno = errno!(libc::ESTALE); - /// Disk quota exceeded - pub const EDQUOT: Errno = errno!(libc::EDQUOT); - /// Operation cancelled - pub const ECANCELED: Errno = errno!(libc::ECANCELED); - /// Owner died - pub const EOWNERDEAD: Errno = errno!(libc::EOWNERDEAD); - /// State not recoverable - pub const ENOTRECOVERABLE: Errno = errno!(libc::ENOTRECOVERABLE); - /// Operation not supported - pub const ENOTSUP: Errno = errno!(libc::ENOTSUP); - - /// No data available - #[cfg(target_os = "linux")] - pub const ENODATA: Errno = errno!(libc::ENODATA); - /// Attribute not found - #[cfg(not(target_os = "linux"))] - pub const ENOATTR: Errno = errno!(libc::ENOATTR); - - /// Use this as an error return from getxattr/removexattr to indicate that the xattr doesn't - /// exist. This resolves to the appropriate platform specific error code. - #[cfg(target_os = "linux")] - pub const NO_XATTR: Errno = Self::ENODATA; - #[cfg(not(target_os = "linux"))] - pub const NO_XATTR: Errno = Self::ENOATTR; - - pub fn from_i32(err: i32) -> Errno { - err.try_into().ok().map(Errno).unwrap_or(Errno::EIO) - } -} -impl From for Errno { - fn from(err: std::io::Error) -> Self { - let errno = err.raw_os_error().unwrap_or(0); - match errno.try_into() { - Ok(i) => Errno(i), - Err(_) => Errno::EIO, - } - } -} -impl From for Errno { - fn from(x: std::io::ErrorKind) -> Self { - let err: std::io::Error = x.into(); - err.into() - } -} -impl From for i32 { - fn from(x: Errno) -> Self { - x.0.into() - } -} - -/// A newtype for generation numbers -/// -/// If the file system will be exported over NFS, the (ino, generation) pairs -/// need to be unique over the file system's lifetime (rather than just the -/// mount time). So if the file system reuses an inode after it has been -/// deleted, it must assign a new, previously unused generation number to the -/// inode at the same time. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct Generation(pub u64); -impl From for u64 { - fn from(fh: Generation) -> Self { - fh.0 - } -} - -#[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. - #[cfg(test)] - #[repr(align(8))] - pub(crate) struct AlignedData(pub T); - impl Deref for AlignedData { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - impl DerefMut for AlignedData { - fn deref_mut(&mut self) -> &mut Self::Target { - &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 deleted file mode 100644 index 668739b79..000000000 --- a/vendor/fuser/src/ll/notify.rs +++ /dev/null @@ -1,215 +0,0 @@ -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::{Immutable, IntoBytes}; - -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 - #[allow(dead_code)] - 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) - } - - #[allow(dead_code)] - 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 deleted file mode 100644 index 9cc5bd240..000000000 --- a/vendor/fuser/src/ll/reply.rs +++ /dev/null @@ -1,882 +0,0 @@ -use std::{ - convert::TryInto, - io::IoSlice, - mem::size_of, - os::unix::prelude::OsStrExt, - path::Path, - time::{Duration, SystemTime, UNIX_EPOCH}, -}; - -use crate::FileType; - -use super::{fuse_abi as abi, Errno, FileHandle, Generation, INodeNo}; -use super::{Lock, RequestId}; -use smallvec::{smallvec, SmallVec}; -use zerocopy::{Immutable, IntoBytes}; - -const INLINE_DATA_THRESHOLD: usize = size_of::() * 4; -pub(crate) type ResponseBuf = SmallVec<[u8; INLINE_DATA_THRESHOLD]>; - -#[derive(Debug)] -pub enum Response<'a> { - Error(i32), - Data(ResponseBuf), - Slice(&'a [u8]), -} - -impl<'a> Response<'a> { - pub(crate) fn with_iovec]) -> T, T>( - &self, - unique: RequestId, - f: F, - ) -> T { - let datalen = match &self { - Response::Error(_) => 0, - Response::Data(v) => v.len(), - Response::Slice(d) => d.len(), - }; - let header = abi::fuse_out_header { - unique: unique.0, - error: if let Response::Error(errno) = self { - -errno - } else { - 0 - }, - len: (size_of::() + datalen) - .try_into() - .expect("Too much data"), - }; - let mut v: SmallVec<[IoSlice<'_>; 3]> = smallvec![IoSlice::new(header.as_bytes())]; - match &self { - Response::Error(_) => {} - Response::Data(d) => v.push(IoSlice::new(d)), - Response::Slice(d) => v.push(IoSlice::new(d)), - } - f(&v) - } - - // Constructors - pub(crate) fn new_empty() -> Self { - Self::Error(0) - } - - pub(crate) fn new_error(error: Errno) -> Self { - Self::Error(error.into()) - } - - pub(crate) fn new_data + Into>>(data: T) -> Self { - Self::Data(if data.as_ref().len() <= INLINE_DATA_THRESHOLD { - ResponseBuf::from_slice(data.as_ref()) - } else { - ResponseBuf::from_vec(data.into()) - }) - } - - pub(crate) fn new_slice(data: &'a [u8]) -> Self { - Self::Slice(data) - } - - pub(crate) fn new_entry( - ino: INodeNo, - generation: Generation, - attr: &Attr, - attr_ttl: Duration, - entry_ttl: Duration, - ) -> Self { - let d = abi::fuse_entry_out { - nodeid: ino.into(), - generation: generation.into(), - entry_valid: entry_ttl.as_secs(), - attr_valid: attr_ttl.as_secs(), - entry_valid_nsec: entry_ttl.subsec_nanos(), - attr_valid_nsec: attr_ttl.subsec_nanos(), - attr: attr.attr, - }; - Self::from_struct(d.as_bytes()) - } - - pub(crate) fn new_attr(ttl: &Duration, attr: &Attr) -> Self { - let r = abi::fuse_attr_out { - attr_valid: ttl.as_secs(), - attr_valid_nsec: ttl.subsec_nanos(), - dummy: 0, - attr: attr.attr, - }; - Self::from_struct(&r) - } - - #[cfg(target_os = "macos")] - pub(crate) fn new_xtimes(bkuptime: SystemTime, crtime: SystemTime) -> Self { - let (bkuptime_secs, bkuptime_nanos) = time_from_system_time(&bkuptime); - let (crtime_secs, crtime_nanos) = time_from_system_time(&crtime); - let r = abi::fuse_getxtimes_out { - bkuptime: bkuptime_secs as u64, - crtime: crtime_secs as u64, - bkuptimensec: bkuptime_nanos, - crtimensec: crtime_nanos, - }; - Self::from_struct(&r) - } - - // TODO: Could flags be more strongly typed? - pub(crate) fn new_open(fh: FileHandle, flags: u32) -> Self { - let r = abi::fuse_open_out { - fh: fh.into(), - open_flags: flags, - padding: 0, - }; - Self::from_struct(&r) - } - - pub(crate) fn new_lock(lock: &Lock) -> Self { - let r = abi::fuse_lk_out { - lk: abi::fuse_file_lock { - start: lock.range.0, - end: lock.range.1, - typ: lock.typ, - pid: lock.pid, - }, - }; - Self::from_struct(&r) - } - - pub(crate) fn new_bmap(block: u64) -> Self { - let r = abi::fuse_bmap_out { block }; - Self::from_struct(&r) - } - - pub(crate) fn new_write(written: u32) -> Self { - let r = abi::fuse_write_out { - size: written, - padding: 0, - }; - Self::from_struct(&r) - } - - #[allow(clippy::too_many_arguments)] - pub(crate) fn new_statfs( - blocks: u64, - bfree: u64, - bavail: u64, - files: u64, - ffree: u64, - bsize: u32, - namelen: u32, - frsize: u32, - ) -> Self { - let r = abi::fuse_statfs_out { - st: abi::fuse_kstatfs { - blocks, - bfree, - bavail, - files, - ffree, - bsize, - namelen, - frsize, - padding: 0, - spare: [0; 6], - }, - }; - Self::from_struct(&r) - } - - // TODO: Can flags be more strongly typed? - pub(crate) fn new_create( - ttl: &Duration, - attr: &Attr, - generation: Generation, - fh: FileHandle, - flags: u32, - ) -> Self { - let r = abi::fuse_create_out( - abi::fuse_entry_out { - nodeid: attr.attr.ino, - generation: generation.into(), - entry_valid: ttl.as_secs(), - attr_valid: ttl.as_secs(), - entry_valid_nsec: ttl.subsec_nanos(), - attr_valid_nsec: ttl.subsec_nanos(), - attr: attr.attr, - }, - abi::fuse_open_out { - fh: fh.into(), - open_flags: flags, - padding: 0, - }, - ); - Self::from_struct(&r) - } - - // TODO: Are you allowed to send data while result != 0? - pub(crate) fn new_ioctl(result: i32, data: &[IoSlice<'_>]) -> Self { - let r = abi::fuse_ioctl_out { - result, - // these fields are only needed for unrestricted ioctls - flags: 0, - in_iovs: 1, - out_iovs: if !data.is_empty() { 1 } else { 0 }, - }; - // TODO: Don't copy this data - let mut v: ResponseBuf = ResponseBuf::from_slice(r.as_bytes()); - for x in data { - v.extend_from_slice(x) - } - 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) - } - - pub(crate) fn new_xattr_size(size: u32) -> Self { - let r = abi::fuse_getxattr_out { size, padding: 0 }; - Self::from_struct(&r) - } - - pub(crate) fn new_lseek(offset: i64) -> Self { - let r = abi::fuse_lseek_out { offset }; - Self::from_struct(&r) - } - - fn from_struct(data: &T) -> Self { - Self::Data(SmallVec::from_slice(data.as_bytes())) - } -} - -pub(crate) fn time_from_system_time(system_time: &SystemTime) -> (i64, u32) { - // Convert to signed 64-bit time with epoch at 0 - match system_time.duration_since(UNIX_EPOCH) { - Ok(duration) => (duration.as_secs() as i64, duration.subsec_nanos()), - Err(before_epoch_error) => ( - -(before_epoch_error.duration().as_secs() as i64), - before_epoch_error.duration().subsec_nanos(), - ), - } -} -// Some platforms like Linux x86_64 have mode_t = u32, and lint warns of a trivial_numeric_casts. -// But others like macOS x86_64 have mode_t = u16, requiring a typecast. So, just silence lint. -#[allow(trivial_numeric_casts)] -#[allow(clippy::unnecessary_cast)] -/// Returns the mode for a given file kind and permission -pub(crate) fn mode_from_kind_and_perm(kind: FileType, perm: u16) -> u32 { - (match kind { - FileType::NamedPipe => libc::S_IFIFO, - FileType::CharDevice => libc::S_IFCHR, - FileType::BlockDevice => libc::S_IFBLK, - FileType::Directory => libc::S_IFDIR, - FileType::RegularFile => libc::S_IFREG, - FileType::Symlink => libc::S_IFLNK, - FileType::Socket => libc::S_IFSOCK, - }) as u32 - | perm as u32 -} -/// Returns a fuse_attr from FileAttr -pub(crate) fn fuse_attr_from_attr(attr: &crate::FileAttr) -> abi::fuse_attr { - let (atime_secs, atime_nanos) = time_from_system_time(&attr.atime); - let (mtime_secs, mtime_nanos) = time_from_system_time(&attr.mtime); - let (ctime_secs, ctime_nanos) = time_from_system_time(&attr.ctime); - #[cfg(target_os = "macos")] - let (crtime_secs, crtime_nanos) = time_from_system_time(&attr.crtime); - - abi::fuse_attr { - ino: attr.ino, - size: attr.size, - blocks: attr.blocks, - atime: atime_secs, - mtime: mtime_secs, - ctime: ctime_secs, - #[cfg(target_os = "macos")] - crtime: crtime_secs as u64, - atimensec: atime_nanos, - mtimensec: mtime_nanos, - ctimensec: ctime_nanos, - #[cfg(target_os = "macos")] - crtimensec: crtime_nanos, - mode: mode_from_kind_and_perm(attr.kind, attr.perm), - nlink: attr.nlink, - uid: attr.uid, - gid: attr.gid, - rdev: attr.rdev, - #[cfg(target_os = "macos")] - flags: attr.flags, - #[cfg(feature = "abi-7-9")] - blksize: attr.blksize, - #[cfg(feature = "abi-7-9")] - padding: 0, - } -} - -// TODO: Add methods for creating this without making a `FileAttr` first. -#[derive(Debug, Clone, Copy)] -pub struct Attr { - pub(crate) attr: abi::fuse_attr, -} -impl From<&crate::FileAttr> for Attr { - fn from(attr: &crate::FileAttr) -> Self { - Self { - attr: fuse_attr_from_attr(attr), - } - } -} -impl From for Attr { - fn from(attr: crate::FileAttr) -> Self { - Self { - attr: fuse_attr_from_attr(&attr), - } - } -} - -#[derive(Debug)] -struct EntListBuf { - max_size: usize, - buf: ResponseBuf, -} -impl EntListBuf { - fn new(max_size: usize) -> Self { - Self { - max_size, - buf: ResponseBuf::new(), - } - } - - /// Add an entry to the directory reply buffer. Returns true if the buffer is full. - /// A transparent offset value can be provided for each entry. The kernel uses these - /// value to request the next entries in further readdir calls - #[must_use] - fn push(&mut self, ent: [&[u8]; 2]) -> bool { - let entlen = ent[0].len() + ent[1].len(); - let entsize = (entlen + size_of::() - 1) & !(size_of::() - 1); // 64bit align - if self.buf.len() + entsize > self.max_size { - return true; - } - self.buf.extend_from_slice(ent[0]); - self.buf.extend_from_slice(ent[1]); - let padlen = entsize - entlen; - self.buf.extend_from_slice(&[0u8; 8][..padlen]); - false - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] -pub struct DirEntOffset(pub i64); -impl From for i64 { - fn from(x: DirEntOffset) -> Self { - x.0 - } -} - -#[derive(Debug)] -pub struct DirEntry> { - ino: INodeNo, - offset: DirEntOffset, - kind: FileType, - name: T, -} - -impl> DirEntry { - pub fn new(ino: INodeNo, offset: DirEntOffset, kind: FileType, name: T) -> DirEntry { - DirEntry:: { - ino, - offset, - kind, - name, - } - } -} - -/// Used to respond to [ReadDirPlus] requests. -#[derive(Debug)] -pub struct DirEntList(EntListBuf); -impl From for Response<'_> { - fn from(l: DirEntList) -> Self { - assert!(l.0.buf.len() <= l.0.max_size); - Response::new_directory(l.0) - } -} - -impl DirEntList { - pub(crate) fn new(max_size: usize) -> Self { - Self(EntListBuf::new(max_size)) - } - /// Add an entry to the directory reply buffer. Returns true if the buffer is full. - /// A transparent offset value can be provided for each entry. The kernel uses these - /// value to request the next entries in further readdir calls - #[must_use] - pub fn push>(&mut self, ent: &DirEntry) -> bool { - let name = ent.name.as_ref().as_os_str().as_bytes(); - let header = abi::fuse_dirent { - ino: ent.ino.into(), - off: ent.offset.0, - namelen: name.len().try_into().expect("Name too long"), - typ: mode_from_kind_and_perm(ent.kind, 0) >> 12, - }; - self.0.push([header.as_bytes(), name]) - } -} - -#[derive(Debug)] -pub struct DirEntryPlus> { - #[allow(unused)] // We use `attr.ino` instead - ino: INodeNo, - generation: Generation, - offset: DirEntOffset, - name: T, - entry_valid: Duration, - attr: Attr, - attr_valid: Duration, -} - -impl> DirEntryPlus { - pub fn new( - ino: INodeNo, - generation: Generation, - offset: DirEntOffset, - name: T, - entry_valid: Duration, - attr: Attr, - attr_valid: Duration, - ) -> Self { - Self { - ino, - generation, - offset, - name, - entry_valid, - attr, - attr_valid, - } - } -} - -/// Used to respond to [ReadDir] requests. -#[derive(Debug)] -pub struct DirEntPlusList(EntListBuf); -impl From for Response<'_> { - fn from(l: DirEntPlusList) -> Self { - assert!(l.0.buf.len() <= l.0.max_size); - Response::new_directory(l.0) - } -} - -impl DirEntPlusList { - pub(crate) fn new(max_size: usize) -> Self { - Self(EntListBuf::new(max_size)) - } - /// Add an entry to the directory reply buffer. Returns true if the buffer is full. - /// A transparent offset value can be provided for each entry. The kernel uses these - /// value to request the next entries in further readdir calls - #[must_use] - pub fn push>(&mut self, x: &DirEntryPlus) -> bool { - let name = x.name.as_ref().as_os_str().as_bytes(); - let header = abi::fuse_direntplus { - entry_out: abi::fuse_entry_out { - nodeid: x.attr.attr.ino, - generation: x.generation.into(), - entry_valid: x.entry_valid.as_secs(), - attr_valid: x.attr_valid.as_secs(), - entry_valid_nsec: x.entry_valid.subsec_nanos(), - attr_valid_nsec: x.attr_valid.subsec_nanos(), - attr: x.attr.attr, - }, - dirent: abi::fuse_dirent { - ino: x.attr.attr.ino, - off: x.offset.into(), - namelen: name.len().try_into().expect("Name too long"), - typ: x.attr.attr.mode >> 12, - }, - }; - self.0.push([header.as_bytes(), name]) - } -} - -#[cfg(test)] -mod test { - use std::num::NonZeroI32; - - use super::super::test::ioslice_to_vec; - use super::*; - - #[test] - fn reply_empty() { - let r = Response::new_empty(); - assert_eq!( - r.with_iovec(RequestId(0xdeadbeef), ioslice_to_vec), - vec![ - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, - ], - ); - } - - #[test] - fn reply_error() { - let r = Response::new_error(Errno(NonZeroI32::new(66).unwrap())); - assert_eq!( - r.with_iovec(RequestId(0xdeadbeef), ioslice_to_vec), - vec![ - 0x10, 0x00, 0x00, 0x00, 0xbe, 0xff, 0xff, 0xff, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, - ], - ); - } - - #[test] - fn reply_data() { - let r = Response::new_data([0xde, 0xad, 0xbe, 0xef].as_ref()); - assert_eq!( - r.with_iovec(RequestId(0xdeadbeef), ioslice_to_vec), - vec![ - 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, - ], - ); - } - - #[test] - fn reply_entry() { - let mut expected = if cfg!(target_os = "macos") { - vec![ - 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, - 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, - ] - } else { - vec![ - 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, - 0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00, - 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, - ] - }; - - if cfg!(feature = "abi-7-9") { - expected.extend(vec![0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - } - expected[0] = (expected.len()) as u8; - - let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678); - let ttl = Duration::new(0x8765, 0x4321); - let attr = crate::FileAttr { - ino: 0x11, - size: 0x22, - blocks: 0x33, - atime: time, - mtime: time, - ctime: time, - crtime: time, - kind: FileType::RegularFile, - perm: 0o644, - nlink: 0x55, - uid: 0x66, - gid: 0x77, - rdev: 0x88, - flags: 0x99, - blksize: 0xbb, - }; - let r = Response::new_entry(INodeNo(0x11), Generation(0xaa), &attr.into(), ttl, ttl); - assert_eq!( - r.with_iovec(RequestId(0xdeadbeef), ioslice_to_vec), - expected - ); - } - - #[test] - fn reply_attr() { - let mut expected = if cfg!(target_os = "macos") { - vec![ - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, - 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x99, 0x00, - 0x00, 0x00, - ] - } else { - vec![ - 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, - 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, - 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, - ] - }; - - if cfg!(feature = "abi-7-9") { - expected.extend_from_slice(&[0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - } - expected[0] = expected.len() as u8; - - let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678); - let ttl = Duration::new(0x8765, 0x4321); - let attr = crate::FileAttr { - ino: 0x11, - size: 0x22, - blocks: 0x33, - atime: time, - mtime: time, - ctime: time, - crtime: time, - kind: FileType::RegularFile, - perm: 0o644, - nlink: 0x55, - uid: 0x66, - gid: 0x77, - rdev: 0x88, - flags: 0x99, - blksize: 0xbb, - }; - let r = Response::new_attr(&ttl, &attr.into()); - assert_eq!( - r.with_iovec(RequestId(0xdeadbeef), ioslice_to_vec), - expected - ); - } - - #[test] - #[cfg(target_os = "macos")] - fn reply_xtimes() { - let expected = vec![ - 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, - ]; - let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678); - let r = Response::new_xtimes(time, time); - assert_eq!( - r.with_iovec(RequestId(0xdeadbeef), ioslice_to_vec), - expected - ); - } - - #[test] - fn reply_open() { - let expected = vec![ - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ]; - let r = Response::new_open(FileHandle(0x1122), 0x33); - assert_eq!( - r.with_iovec(RequestId(0xdeadbeef), ioslice_to_vec), - expected - ); - } - - #[test] - fn reply_write() { - let expected = vec![ - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let r = Response::new_write(0x1122); - assert_eq!( - r.with_iovec(RequestId(0xdeadbeef), ioslice_to_vec), - expected - ); - } - - #[test] - fn reply_statfs() { - let expected = vec![ - 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let r = Response::new_statfs(0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88); - assert_eq!( - r.with_iovec(RequestId(0xdeadbeef), ioslice_to_vec), - expected - ); - } - - #[test] - fn reply_create() { - let mut expected = if cfg!(target_os = "macos") { - vec![ - 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, - 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0xbb, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ] - } else { - vec![ - 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, - 0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00, - 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ] - }; - - if cfg!(feature = "abi-7-9") { - let insert_at = expected.len() - 16; - expected.splice( - insert_at..insert_at, - vec![0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], - ); - } - expected[0] = (expected.len()) as u8; - - let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678); - let ttl = Duration::new(0x8765, 0x4321); - let attr = crate::FileAttr { - ino: 0x11, - size: 0x22, - blocks: 0x33, - atime: time, - mtime: time, - ctime: time, - crtime: time, - kind: FileType::RegularFile, - perm: 0o644, - nlink: 0x55, - uid: 0x66, - gid: 0x77, - rdev: 0x88, - flags: 0x99, - blksize: 0xdd, - }; - let r = Response::new_create(&ttl, &attr.into(), Generation(0xaa), FileHandle(0xbb), 0xcc); - assert_eq!( - r.with_iovec(RequestId(0xdeadbeef), ioslice_to_vec), - expected - ); - } - - #[test] - fn reply_lock() { - let expected = vec![ - 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, - ]; - let r = Response::new_lock(&Lock { - range: (0x11, 0x22), - typ: 0x33, - pid: 0x44, - }); - assert_eq!( - r.with_iovec(RequestId(0xdeadbeef), ioslice_to_vec), - expected - ); - } - - #[test] - fn reply_bmap() { - let expected = vec![ - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let r = Response::new_bmap(0x1234); - assert_eq!( - r.with_iovec(RequestId(0xdeadbeef), ioslice_to_vec), - expected - ); - } - - #[test] - fn reply_xattr_size() { - let expected = vec![ - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, - 0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, - ]; - let r = Response::new_xattr_size(0x12345678); - assert_eq!( - r.with_iovec(RequestId(0xdeadbeef), ioslice_to_vec), - expected - ); - } - - #[test] - fn reply_xattr_data() { - let expected = vec![ - 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, - ]; - let r = Response::new_data([0x11, 0x22, 0x33, 0x44].as_ref()); - assert_eq!( - r.with_iovec(RequestId(0xdeadbeef), ioslice_to_vec), - expected - ); - } - - #[test] - fn reply_directory() { - let expected = vec![ - 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0xbb, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x68, 0x65, - 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x00, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x72, 0x73, - ]; - let mut buf = DirEntList::new(4096); - assert!(!buf.push(&DirEntry::new( - INodeNo(0xaabb), - DirEntOffset(1), - FileType::Directory, - "hello" - ))); - assert!(!buf.push(&DirEntry::new( - INodeNo(0xccdd), - DirEntOffset(2), - FileType::RegularFile, - "world.rs" - ))); - let r: Response<'_> = buf.into(); - assert_eq!( - r.with_iovec(RequestId(0xdeadbeef), ioslice_to_vec), - expected - ); - } -} diff --git a/vendor/fuser/src/ll/request.rs b/vendor/fuser/src/ll/request.rs deleted file mode 100644 index b65b01b3b..000000000 --- a/vendor/fuser/src/ll/request.rs +++ /dev/null @@ -1,2337 +0,0 @@ -//! Low-level filesystem operation request. -//! -//! A request represents information about a filesystem operation the kernel driver wants us to -//! perform. - -use super::fuse_abi::{fuse_in_header, fuse_opcode, InvalidOpcodeError}; - -use super::{fuse_abi as abi, Errno, Response}; -#[cfg(feature = "serializable")] -use serde::{Deserialize, Serialize}; -use std::{convert::TryFrom, fmt::Display, path::Path}; -use std::{error, fmt, mem}; - -use super::argument::ArgumentIterator; - -/// Error that may occur while reading and parsing a request from the kernel driver. -#[derive(Debug)] -pub enum RequestError { - /// Not enough data for parsing header (short read). - ShortReadHeader(usize), - /// Kernel requested an unknown operation. - UnknownOperation(u32), - /// Not enough data for arguments (short read). - ShortRead(usize, usize), - /// Insufficient argument data. - InsufficientData, -} - -/// Unique ID for a request from the kernel -/// -/// The FUSE kernel driver assigns a unique id to every concurrent request. This allows to -/// distinguish between multiple concurrent requests. The unique id of a request may be -/// reused in later requests after it has completed. -/// -/// This can be retrieve for any request using [Request::unique]. The kernel -/// will send an [Interrupt] request to cancel requests in progress. It's -/// important to handle this for any requests that may block indefinitely, like -/// [SetLkW]. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))] -pub struct RequestId(pub u64); -impl From for u64 { - fn from(fh: RequestId) -> Self { - fh.0 - } -} - -/// A newtype for inode numbers -/// -/// These are generated by the filesystem implementation and returned to the -/// kernel in response to a call to [Lookup], [Create], [MkNod], [MkDir] or -/// [SymLink]. The kernel will then pass these numbers back to the filesystem -/// implementation when it needs to refer to a given file. Every request has -/// an associated [INodeNo], accessible as [Request::nodeid]. -/// -/// Reference Counting -/// ------------------ -/// -/// Every time the kernel receives a given inode number in a response to a -/// [Lookup], [Create], [MkNod], [MkDir] or [SymLink] request it increments an -/// internal counter for that inode. The filesystem implementation should do -/// the same. When the kernel is no longer interested in this inode it will -/// send a [Forget] message with that counter. The filesystem implementation -/// should decrement its own counter and if it reaches 0 then the inode number -/// may be recycled and your filesystem implementation may clean up its -/// internal data-structures relating to that inode. -/// -/// We implement conversion from [INodeNo] to [u64] but not vice-versa because -/// not all [u64]s are valid [INodeNo]s, but the reverse is true. So to produce -/// a [INodeNo] from a [u64] we must be explicit. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))] -pub struct INodeNo(pub u64); -impl From for u64 { - fn from(fh: INodeNo) -> Self { - fh.0 - } -} - -/// A newtype for file handles -/// -/// This corresponds to a single file description in a client program. These -/// are generated by the filesystem implementation in replies to [Open], -/// [OpenDir] and [Create] requests. It's used as a correlation id across -/// [Read], [Write], [FSync], [IoCtl], [Poll], [FAllocate], [ReadDir], -/// [FSyncDir], [GetLk], [SetLk], [SetLkW], [ReadDirPlus], [Lseek] and -/// [CopyFileRange] requests. -/// -/// A filesystem implementation may store arbitrary data as the [FileHandle], as -/// long as it fits into 64-bits and doesn't need to change for over the lifetime -/// of the [FileHandle]. Typically this might consist of an index into an array -/// of [FileHandle]s that the filesystem implementation maintains. -/// -/// Filesystems may instead implement stateless file I/O and use `0` as the -/// [FileHandle] - although this makes it impossible to correctly implement -/// resumable [ReadDir] in the presence of mutable directories (see [OpenDir]). -/// -/// Lifecycle -/// --------- -/// -/// A [FileHandle] is owned by one or more file-descriptors (or memory -/// mappings) in the client program. Multiple file descriptors can point to -/// the same [FileHandle], just as a single INode can have multiple -/// [FileHandle]s open at one time. Every time a single file-descriptor is -/// closed a [Flush] request is made. This gives filesystem implementations -/// an opportunity to return an error message from that `close()` call. After -/// all the file-descriptors are closed that own a given [FileHandle] the -/// [Release]/[ReleaseDir] request will be made. This is an opportunity for -/// the filesystem implementation to free any internal per-FileHandle data -/// structures it has allocated. -/// -/// We implement conversion from FileHandle to u64 but not vice-versa because -/// not all u64s are valid FileHandles, but the reverse is true. So to produce -/// a FileHandle from a u64 we must be explicit. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))] -pub struct FileHandle(pub u64); - -impl From for u64 { - fn from(fh: FileHandle) -> Self { - fh.0 - } -} - -/// A newtype for lock owners -/// -/// TODO: Document lock lifecycle and how and when to implement file locking. -/// -/// See [Read], [Write], [Release], [Flush], [GetLk], [SetLk], [SetLkW]. -/// -/// We implement conversion from [LockOwner] to [u64] but not vice-versa -/// because all LockOwners are valid [u64]s, but not vice-versa. So to produce -/// a [LockOwner] from a [u64] we must be explicit. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))] -pub struct LockOwner(pub u64); - -impl From for u64 { - fn from(fh: LockOwner) -> Self { - fh.0 - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct Lock { - // Unfortunately this can't be a std::ops::Range because Range is not Copy: - // https://github.com/rust-lang/rfcs/issues/2848 - pub range: (u64, u64), - // TODO: Make typ an enum - pub typ: i32, - pub pid: u32, -} -impl Lock { - fn from_abi(x: &abi::fuse_file_lock) -> Lock { - Lock { - range: (x.start, x.end), - typ: x.typ, - pid: x.pid, - } - } -} - -/// A newtype for ABI version -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))] -pub struct Version(pub u32, pub u32); -impl Version { - pub fn major(&self) -> u32 { - self.0 - } - pub fn minor(&self) -> u32 { - self.1 - } -} -impl Display for Version { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}.{}", self.0, self.1) - } -} - -/// Represents a filename in a directory -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -pub struct FilenameInDir<'a> { - /// The Inode number of the directory - pub dir: INodeNo, - /// Name of the file. This refers to a name directly in this directory, rather than any - /// subdirectory so is guaranteed not to contain '\0' or '/'. It may be literally "." or ".." - /// however. - pub name: &'a Path, -} - -impl fmt::Display for RequestError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - RequestError::ShortReadHeader(len) => write!( - f, - "Short read of FUSE request header ({len} < {})", - mem::size_of::() - ), - RequestError::UnknownOperation(opcode) => write!(f, "Unknown FUSE opcode ({opcode})"), - RequestError::ShortRead(len, total) => { - write!(f, "Short read of FUSE request ({len} < {total})") - } - RequestError::InsufficientData => write!(f, "Insufficient argument data"), - } - } -} - -impl error::Error for RequestError {} -pub trait Request: Sized { - /// Returns the unique identifier of this request. - /// - /// The FUSE kernel driver assigns a unique id to every concurrent request. This allows to - /// distinguish between multiple concurrent requests. The unique id of a request may be - /// reused in later requests after it has completed. - fn unique(&self) -> RequestId; - - /// Returns the node id of the inode this request is targeted to. - fn nodeid(&self) -> INodeNo; - - /// Returns the UID that the process that triggered this request runs under. - fn uid(&self) -> u32; - - /// Returns the GID that the process that triggered this request runs under. - fn gid(&self) -> u32; - - /// Returns the PID of the process that triggered this request. - fn pid(&self) -> u32; - - /// Create an error response for this Request - fn reply_err(&self, errno: Errno) -> Response<'_> { - Response::new_error(errno) - } -} - -macro_rules! impl_request { - ($structname: ty) => { - impl<'a> super::Request for $structname { - #[inline] - fn unique(&self) -> RequestId { - RequestId(self.header.unique) - } - - #[inline] - fn nodeid(&self) -> INodeNo { - INodeNo(self.header.nodeid) - } - - #[inline] - fn uid(&self) -> u32 { - self.header.uid - } - - #[inline] - fn gid(&self) -> u32 { - self.header.gid - } - - #[inline] - fn pid(&self) -> u32 { - self.header.pid - } - } - }; -} - -mod op { - use crate::ll::Response; - - use super::{ - super::{argument::ArgumentIterator, TimeOrNow}, - FilenameInDir, Request, - }; - use super::{ - abi::consts::*, abi::*, FileHandle, INodeNo, Lock, LockOwner, Operation, RequestId, - }; - use std::{ - convert::TryInto, - ffi::OsStr, - fmt::Display, - num::NonZeroU32, - path::Path, - time::{Duration, SystemTime}, - }; - use zerocopy::IntoBytes; - - /// Look up a directory entry by name and get its attributes. - /// - /// Implementations allocate and assign [INodeNo]s in this request. Learn more - /// about INode lifecycle and the relationship between [Lookup] and [Forget] in the - /// documentation for [INodeNo]. - #[derive(Debug)] - pub struct Lookup<'a> { - header: &'a fuse_in_header, - name: &'a OsStr, - } - impl_request!(Lookup<'_>); - impl<'a> Lookup<'a> { - pub fn name(&self) -> &'a Path { - self.name.as_ref() - } - } - /// Forget about an inode. - /// - /// The nlookup parameter indicates the number of lookups previously performed on - /// this inode. If the filesystem implements inode lifetimes, it is recommended that - /// inodes acquire a single reference on each lookup, and lose nlookup references on - /// each forget. The filesystem may ignore forget calls, if the inodes don't need to - /// have a limited lifetime. - /// - /// Learn more about INode lifecycle in the documentation for [INodeNo]. - /// - /// On unmount it is not guaranteed, that all referenced inodes will receive a forget - /// message. - #[derive(Debug)] - pub struct Forget<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_forget_in, - } - impl_request!(Forget<'_>); - impl<'a> Forget<'a> { - /// The number of lookups previously performed on this inode - pub fn nlookup(&self) -> u64 { - self.arg.nlookup - } - } - - /// Get file attributes. - #[derive(Debug)] - pub struct GetAttr<'a> { - header: &'a fuse_in_header, - - #[cfg(feature = "abi-7-9")] - arg: &'a fuse_getattr_in, - } - impl_request!(GetAttr<'_>); - - #[cfg(feature = "abi-7-9")] - impl<'a> GetAttr<'a> { - pub fn file_handle(&self) -> Option { - if self.arg.getattr_flags & crate::FUSE_GETATTR_FH != 0 { - Some(FileHandle(self.arg.fh)) - } else { - None - } - } - } - - /// Set file attributes. - #[derive(Debug)] - pub struct SetAttr<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_setattr_in, - } - impl_request!(SetAttr<'_>); - impl<'a> SetAttr<'a> { - pub fn mode(&self) -> Option { - match self.arg.valid & FATTR_MODE { - 0 => None, - _ => Some(self.arg.mode), - } - } - pub fn uid(&self) -> Option { - match self.arg.valid & FATTR_UID { - 0 => None, - _ => Some(self.arg.uid), - } - } - pub fn gid(&self) -> Option { - match self.arg.valid & FATTR_GID { - 0 => None, - _ => Some(self.arg.gid), - } - } - pub fn size(&self) -> Option { - match self.arg.valid & FATTR_SIZE { - 0 => None, - _ => Some(self.arg.size), - } - } - pub fn atime(&self) -> Option { - match self.arg.valid & FATTR_ATIME { - 0 => None, - _ => Some(if self.arg.atime_now() { - TimeOrNow::Now - } else { - TimeOrNow::SpecificTime(system_time_from_time( - self.arg.atime, - self.arg.atimensec, - )) - }), - } - } - pub fn mtime(&self) -> Option { - match self.arg.valid & FATTR_MTIME { - 0 => None, - _ => Some(if self.arg.mtime_now() { - TimeOrNow::Now - } else { - TimeOrNow::SpecificTime(system_time_from_time( - self.arg.mtime, - self.arg.mtimensec, - )) - }), - } - } - pub fn ctime(&self) -> Option { - #[cfg(feature = "abi-7-23")] - match self.arg.valid & FATTR_CTIME { - 0 => None, - _ => Some(system_time_from_time(self.arg.ctime, self.arg.ctimensec)), - } - #[cfg(not(feature = "abi-7-23"))] - None - } - /// The value set by the [Open] method. See [FileHandle]. - /// - /// This will only be set if the user passed a file-descriptor to set the - /// attributes - i.e. they used [libc::fchmod] rather than [libc::chmod]. - pub fn file_handle(&self) -> Option { - match self.arg.valid & FATTR_FH { - 0 => None, - _ => Some(FileHandle(self.arg.fh)), - } - } - pub fn crtime(&self) -> Option { - #[cfg(target_os = "macos")] - match self.arg.valid & FATTR_CRTIME { - 0 => None, - _ => Some( - SystemTime::UNIX_EPOCH + Duration::new(self.arg.crtime, self.arg.crtimensec), - ), - } - #[cfg(not(target_os = "macos"))] - None - } - pub fn chgtime(&self) -> Option { - #[cfg(target_os = "macos")] - match self.arg.valid & FATTR_CHGTIME { - 0 => None, - _ => Some( - SystemTime::UNIX_EPOCH + Duration::new(self.arg.chgtime, self.arg.chgtimensec), - ), - } - #[cfg(not(target_os = "macos"))] - None - } - pub fn bkuptime(&self) -> Option { - #[cfg(target_os = "macos")] - match self.arg.valid & FATTR_BKUPTIME { - 0 => None, - _ => Some( - SystemTime::UNIX_EPOCH - + Duration::new(self.arg.bkuptime, self.arg.bkuptimensec), - ), - } - #[cfg(not(target_os = "macos"))] - None - } - pub fn flags(&self) -> Option { - #[cfg(target_os = "macos")] - match self.arg.valid & FATTR_FLAGS { - 0 => None, - _ => Some(self.arg.flags), - } - #[cfg(not(target_os = "macos"))] - None - } - - // TODO: Why does *set*attr want to have an attr response? - } - impl<'a> Display for SetAttr<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "SETATTR mode: {:?}, uid: {:?}, gid: {:?}, size: {:?}, atime: {:?}, \ - mtime: {:?}, ctime: {:?}, file_handle: {:?}, crtime: {:?}, chgtime: {:?}, \ - bkuptime: {:?}, flags: {:?}", - self.mode(), - self.uid(), - self.gid(), - self.size(), - self.atime(), - self.mtime(), - self.ctime(), - self.file_handle(), - self.crtime(), - self.chgtime(), - self.bkuptime(), - self.flags() - ) - } - } - - /// Read symbolic link. - #[derive(Debug)] - pub struct ReadLink<'a> { - header: &'a fuse_in_header, - } - impl_request!(ReadLink<'_>); - - /// Create a symbolic link. - #[derive(Debug)] - pub struct SymLink<'a> { - header: &'a fuse_in_header, - target: &'a Path, - link_name: &'a Path, - } - impl_request!(SymLink<'_>); - impl<'a> SymLink<'a> { - pub fn target(&self) -> &'a Path { - self.target - } - pub fn link_name(&self) -> &'a Path { - self.link_name - } - } - - /// Create file node. - /// Create a regular file, character device, block device, fifo or socket node. - #[derive(Debug)] - pub struct MkNod<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_mknod_in, - name: &'a Path, - } - impl_request!(MkNod<'_>); - impl<'a> MkNod<'a> { - pub fn name(&self) -> &'a Path { - self.name - } - pub fn mode(&self) -> u32 { - self.arg.mode - } - pub fn umask(&self) -> u32 { - #[cfg(not(feature = "abi-7-12"))] - return 0; - #[cfg(feature = "abi-7-12")] - self.arg.umask - } - pub fn rdev(&self) -> u32 { - self.arg.rdev - } - } - - /// Create a directory. - #[derive(Debug)] - pub struct MkDir<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_mkdir_in, - name: &'a Path, - } - impl_request!(MkDir<'_>); - impl<'a> MkDir<'a> { - pub fn name(&self) -> &'a Path { - self.name - } - pub fn mode(&self) -> u32 { - self.arg.mode - } - pub fn umask(&self) -> u32 { - #[cfg(not(feature = "abi-7-12"))] - return 0; - #[cfg(feature = "abi-7-12")] - self.arg.umask - } - } - - /// Remove a file. - #[derive(Debug)] - pub struct Unlink<'a> { - header: &'a fuse_in_header, - name: &'a Path, - } - impl_request!(Unlink<'_>); - impl<'a> Unlink<'a> { - pub fn name(&self) -> &'a Path { - self.name - } - } - - /// Remove a directory. - #[derive(Debug)] - pub struct RmDir<'a> { - header: &'a fuse_in_header, - pub name: &'a Path, - } - impl_request!(RmDir<'_>); - impl<'a> RmDir<'a> { - pub fn name(&self) -> &'a Path { - self.name - } - } - - /// Rename a file. - #[derive(Debug)] - pub struct Rename<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_rename_in, - name: &'a Path, - newname: &'a Path, - } - impl_request!(Rename<'_>); - impl<'a> Rename<'a> { - pub fn src(&self) -> FilenameInDir<'a> { - FilenameInDir::<'a> { - dir: self.nodeid(), - name: self.name, - } - } - pub fn dest(&self) -> FilenameInDir<'a> { - FilenameInDir::<'a> { - dir: INodeNo(self.arg.newdir), - name: self.newname, - } - } - } - - /// Create a hard link. - #[derive(Debug)] - pub struct Link<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_link_in, - name: &'a Path, - } - impl_request!(Link<'_>); - impl<'a> Link<'a> { - /// This is the inode no of the file to be linked. The inode number in - /// the fuse header is of the directory that it will be linked into. - pub fn inode_no(&self) -> INodeNo { - INodeNo(self.arg.oldnodeid) - } - pub fn dest(&self) -> FilenameInDir<'a> { - FilenameInDir::<'a> { - dir: self.nodeid(), - name: self.name, - } - } - } - - /// Open a file. - /// - /// Open flags (with the exception of `O_CREAT`, `O_EXCL`, `O_NOCTTY` and `O_TRUNC`) are - /// available in flags. Filesystem may store an arbitrary file handle (pointer, index, - /// etc) in fh, and use this in other all other file operations (read, write, flush, - /// release, fsync). Filesystem may also implement stateless file I/O and not store - /// anything in fh. There are also some flags (direct_io, keep_cache) which the - /// filesystem may set, to change the way the file is opened. See fuse_file_info - /// structure in for more details. - #[derive(Debug)] - pub struct Open<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_open_in, - } - impl_request!(Open<'_>); - impl<'a> Open<'a> { - pub fn flags(&self) -> i32 { - self.arg.flags - } - } - - /// Read data. - /// - /// Read should send exactly the number of bytes requested except on EOF or error, - /// otherwise the rest of the data will be substituted with zeroes. An exception to - /// this is when the file has been opened in 'direct_io' mode, in which case the - /// return value of the read system call will reflect the return value of this - /// operation. - #[derive(Debug)] - pub struct Read<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_read_in, - } - impl_request!(Read<'_>); - impl<'a> Read<'a> { - /// The value set by the [Open] method. - pub fn file_handle(&self) -> FileHandle { - FileHandle(self.arg.fh) - } - pub fn offset(&self) -> i64 { - self.arg.offset - } - pub fn size(&self) -> u32 { - self.arg.size - } - /// Only supported with ABI >= 7.9 - pub fn lock_owner(&self) -> Option { - #[cfg(not(feature = "abi-7-9"))] - return None; - #[cfg(feature = "abi-7-9")] - if self.arg.read_flags & FUSE_READ_LOCKOWNER != 0 { - Some(LockOwner(self.arg.lock_owner)) - } else { - None - } - } - /// The file flags, such as `O_SYNC`. Only supported with ABI >= 7.9 - pub fn flags(&self) -> i32 { - #[cfg(not(feature = "abi-7-9"))] - return 0; - #[cfg(feature = "abi-7-9")] - self.arg.flags - } - } - - /// Write data. - /// - /// Write should return exactly the number of bytes requested except on error. An - /// exception to this is when the file has been opened in 'direct_io' mode, in - /// which case the return value of the write system call will reflect the return - /// value of this operation. - #[derive(Debug)] - pub struct Write<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_write_in, - data: &'a [u8], - } - impl_request!(Write<'_>); - impl<'a> Write<'a> { - /// The value set by the [Open] method. - pub fn file_handle(&self) -> FileHandle { - FileHandle(self.arg.fh) - } - pub fn offset(&self) -> i64 { - self.arg.offset - } - pub fn data(&self) -> &'a [u8] { - self.data - } - /// Will contain FUSE_WRITE_CACHE, if this write is from the page cache. If set, - /// the pid, uid, gid, and fh may not match the value that would have been sent if write caching - /// is disabled - /// - /// TODO: WriteFlags type or remove this - pub fn write_flags(&self) -> u32 { - self.arg.write_flags - } - /// lock_owner: only supported with ABI >= 7.9 - pub fn lock_owner(&self) -> Option { - #[cfg(feature = "abi-7-9")] - if self.arg.write_flags & FUSE_WRITE_LOCKOWNER != 0 { - Some(LockOwner(self.arg.lock_owner)) - } else { - None - } - #[cfg(not(feature = "abi-7-9"))] - None - } - /// flags: these are the file flags, such as O_SYNC. Only supported with ABI >= 7.9 - /// TODO: Make a Flags type specifying valid values - pub fn flags(&self) -> i32 { - #[cfg(feature = "abi-7-9")] - return self.arg.flags; - #[cfg(not(feature = "abi-7-9"))] - 0 - } - } - - /// Get file system statistics. - #[derive(Debug)] - pub struct StatFs<'a> { - header: &'a fuse_in_header, - } - impl_request!(StatFs<'_>); - - /// Release an open file. - /// - /// Release is called when there are no more references to an open file: all file - /// descriptors are closed and all memory mappings are unmapped. For every [Open] - /// call there will be exactly one release call. The filesystem may reply with an - /// error, but error values are not returned to `close()` or `munmap()` which - /// triggered the release. - #[derive(Debug)] - pub struct Release<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_release_in, - } - impl_request!(Release<'_>); - impl<'a> Release<'a> { - pub fn flush(&self) -> bool { - self.arg.release_flags & FUSE_RELEASE_FLUSH != 0 - } - /// The value set by the [Open] method. - pub fn file_handle(&self) -> FileHandle { - FileHandle(self.arg.fh) - } - /// The same flags as for open. - /// TODO: Document what flags are valid, or remove this - pub fn flags(&self) -> i32 { - self.arg.flags - } - pub fn lock_owner(&self) -> Option { - #[cfg(not(feature = "abi-7-17"))] - return Some(LockOwner(self.arg.lock_owner)); - #[cfg(feature = "abi-7-17")] - if self.arg.release_flags & FUSE_RELEASE_FLOCK_UNLOCK != 0 { - Some(LockOwner(self.arg.lock_owner)) - } else { - None - } - } - } - - /// Synchronize file contents. - #[derive(Debug)] - pub struct FSync<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_fsync_in, - } - impl_request!(FSync<'a>); - impl<'a> FSync<'a> { - /// The value set by the [Open] method. - pub fn file_handle(&self) -> FileHandle { - FileHandle(self.arg.fh) - } - /// If set only the user data should be flushed, not the meta data. - pub fn fdatasync(&self) -> bool { - self.arg.fsync_flags & consts::FUSE_FSYNC_FDATASYNC != 0 - } - } - - /// Set an extended attribute. - #[derive(Debug)] - pub struct SetXAttr<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_setxattr_in, - name: &'a OsStr, - value: &'a [u8], - } - impl_request!(SetXAttr<'a>); - impl<'a> SetXAttr<'a> { - pub fn name(&self) -> &'a OsStr { - self.name - } - pub fn value(&self) -> &'a [u8] { - self.value - } - // TODO: Document what are valid flags - pub fn flags(&self) -> i32 { - self.arg.flags - } - /// This will always be 0 except on MacOS. It's recommended that - /// implementations return EINVAL if this is not 0. - pub fn position(&self) -> u32 { - #[cfg(target_os = "macos")] - return self.arg.position; - #[cfg(not(target_os = "macos"))] - 0 - } - } - - /// Get an extended attribute. - /// - /// If the requested XAttr doesn't exist return [Err(Errno::NO_XATTR)] which will - /// map to the right platform-specific error code. - #[derive(Debug)] - pub struct GetXAttr<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_getxattr_in, - name: &'a OsStr, - } - impl_request!(GetXAttr<'a>); - - /// Type for [GetXAttrSizeEnum::GetSize]. - /// - /// Represents a request from the user to get the size of the data stored in the XAttr. - #[derive(Debug)] - pub struct GetXAttrSize(); - - #[derive(Debug)] - /// Return type for [GetXAttr::size]. - pub enum GetXAttrSizeEnum { - /// User is requesting the size of the data stored in the XAttr - GetSize(GetXAttrSize), - /// User is requesting the data stored in the XAttr. If the data will fit - /// in this number of bytes it should be returned, otherwise return [Err(Errno::ERANGE)]. - #[allow(dead_code)] - Size(NonZeroU32), - } - impl<'a> GetXAttr<'a> { - /// Name of the XAttr - pub fn name(&self) -> &'a OsStr { - self.name - } - /// See [GetXAttrSizeEnum]. - /// - /// You only need to check this value as an optimisation where there's a - /// cost difference between checking the size of the data stored in an XAttr - /// and actually providing the data. Otherwise just call [reply()] with the - /// data and it will do the right thing. - pub fn size(&self) -> GetXAttrSizeEnum { - let s: Result = self.arg.size.try_into(); - match s { - Ok(s) => GetXAttrSizeEnum::Size(s), - Err(_) => GetXAttrSizeEnum::GetSize(GetXAttrSize()), - } - } - /// The size of the buffer the user has allocated to store the XAttr value. - pub(crate) fn size_u32(&self) -> u32 { - self.arg.size - } - } - - /// List extended attribute names. - #[derive(Debug)] - pub struct ListXAttr<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_getxattr_in, - } - impl_request!(ListXAttr<'a>); - impl<'a> ListXAttr<'a> { - /// The size of the buffer the caller has allocated to receive the list of - /// XAttrs. If this is 0 the user is just probing to find how much space is - /// required to fit the whole list. - /// - /// You don't need to worry about this except as an optimisation. - pub fn size(&self) -> u32 { - self.arg.size - } - } - - /// Remove an extended attribute. - /// - /// Return [Err(Errno::NO_XATTR)] if the xattr doesn't exist - /// Return [Err(Errno::ENOTSUP)] if this filesystem doesn't support XAttrs - #[derive(Debug)] - pub struct RemoveXAttr<'a> { - header: &'a fuse_in_header, - name: &'a OsStr, - } - impl_request!(RemoveXAttr<'a>); - impl<'a> RemoveXAttr<'a> { - /// Name of the XAttr to remove - pub fn name(&self) -> &'a OsStr { - self.name - } - } - - /// Flush method. - /// - /// This is called on each close() of the opened file. Since file descriptors can - /// be duplicated (dup, dup2, fork), for one open call there may be many flush - /// calls. Filesystems shouldn't assume that flush will always be called after some - /// writes, or that if will be called at all. - /// - /// NOTE: the name of the method is misleading, since (unlike fsync) the filesystem - /// is not forced to flush pending writes. One reason to flush data, is if the - /// filesystem wants to return write errors. If the filesystem supports file locking - /// operations (setlk, getlk) it should remove all locks belonging to 'lock_owner'. - #[derive(Debug)] - pub struct Flush<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_flush_in, - } - impl_request!(Flush<'a>); - impl<'a> Flush<'a> { - /// The value set by the open method - pub fn file_handle(&self) -> FileHandle { - FileHandle(self.arg.fh) - } - pub fn lock_owner(&self) -> LockOwner { - LockOwner(self.arg.lock_owner) - } - } - - #[derive(Debug)] - pub struct Init<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_init_in, - } - impl_request!(Init<'a>); - impl<'a> Init<'a> { - pub fn capabilities(&self) -> u32 { - self.arg.flags - } - pub fn max_readahead(&self) -> u32 { - self.arg.max_readahead - } - pub fn version(&self) -> super::Version { - super::Version(self.arg.major, self.arg.minor) - } - - pub fn reply(&self, config: &crate::KernelConfig) -> Response<'a> { - let init = fuse_init_out { - major: FUSE_KERNEL_VERSION, - minor: FUSE_KERNEL_MINOR_VERSION, - max_readahead: config.max_readahead, - flags: self.capabilities() & config.requested, // use requested features and reported as capable - #[cfg(not(feature = "abi-7-13"))] - unused: 0, - #[cfg(feature = "abi-7-13")] - max_background: config.max_background, - #[cfg(feature = "abi-7-13")] - congestion_threshold: config.congestion_threshold(), - max_write: config.max_write, - #[cfg(feature = "abi-7-23")] - time_gran: config.time_gran.as_nanos() as u32, - #[cfg(all(feature = "abi-7-23", not(feature = "abi-7-28")))] - reserved: [0; 9], - #[cfg(feature = "abi-7-28")] - max_pages: config.max_pages(), - #[cfg(feature = "abi-7-28")] - unused2: 0, - #[cfg(feature = "abi-7-28")] - reserved: [0; 8], - }; - Response::new_data(init.as_bytes()) - } - } - - /// Open a directory. - /// - /// Filesystem may store an arbitrary file handle (pointer, index, etc) in fh, and - /// use this in other all other directory stream operations ([ReadDir], [ReleaseDir], - /// [FSyncDir]). Filesystem may also implement stateless directory I/O and not store - /// anything in fh, though that makes it impossible to implement standard conforming - /// directory stream operations in case the contents of the directory can change - /// between [OpenDir] and [ReleaseDir]. - /// - /// TODO: Document how to implement "standard conforming directory stream operations" - #[derive(Debug)] - pub struct OpenDir<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_open_in, - } - impl_request!(OpenDir<'a>); - impl<'a> OpenDir<'a> { - /// Flags as passed to open - pub fn flags(&self) -> i32 { - self.arg.flags - } - } - - /// Read directory. - #[derive(Debug)] - pub struct ReadDir<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_read_in, - } - impl_request!(ReadDir<'a>); - impl<'a> ReadDir<'a> { - /// The value set by the [OpenDir] method. - pub fn file_handle(&self) -> FileHandle { - FileHandle(self.arg.fh) - } - pub fn offset(&self) -> i64 { - self.arg.offset - } - pub fn size(&self) -> u32 { - self.arg.size - } - } - - /// Release an open directory. - /// - /// For every [OpenDir] call there will be exactly one [ReleaseDir] call. - #[derive(Debug)] - pub struct ReleaseDir<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_release_in, - } - impl_request!(ReleaseDir<'a>); - impl<'a> ReleaseDir<'a> { - /// The value set by the [OpenDir] method. - pub fn file_handle(&self) -> FileHandle { - FileHandle(self.arg.fh) - } - pub fn flush(&self) -> bool { - self.arg.release_flags & consts::FUSE_RELEASE_FLUSH != 0 - } - pub fn lock_owner(&self) -> Option { - #[cfg(not(feature = "abi-7-17"))] - return Some(LockOwner(self.arg.lock_owner)); - #[cfg(feature = "abi-7-17")] - if self.arg.release_flags & FUSE_RELEASE_FLOCK_UNLOCK != 0 { - Some(LockOwner(self.arg.lock_owner)) - } else { - None - } - } - /// TODO: Document what values this may take - pub fn flags(&self) -> i32 { - self.arg.flags - } - } - - /// Synchronize directory contents. - #[derive(Debug)] - pub struct FSyncDir<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_fsync_in, - } - impl_request!(FSyncDir<'a>); - impl<'a> FSyncDir<'a> { - /// The value set by the [OpenDir] method. See [FileHandle]. - pub fn file_handle(&self) -> FileHandle { - FileHandle(self.arg.fh) - } - /// If set, then only the directory contents should be flushed, not the meta data. - pub fn fdatasync(&self) -> bool { - self.arg.fsync_flags & consts::FUSE_FSYNC_FDATASYNC != 0 - } - } - - /// Test for a POSIX file lock. - #[derive(Debug)] - pub struct GetLk<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_lk_in, - } - impl_request!(GetLk<'a>); - impl<'a> GetLk<'a> { - /// The value set by the [Open] method. See [FileHandle]. - pub fn file_handle(&self) -> FileHandle { - FileHandle(self.arg.fh) - } - pub fn lock(&self) -> Lock { - Lock::from_abi(&self.arg.lk) - } - pub fn lock_owner(&self) -> LockOwner { - LockOwner(self.arg.owner) - } - } - - /// Acquire, modify or release a POSIX file lock. - /// - /// For POSIX threads (NPTL) there's a 1-1 relation between pid and owner, but - /// otherwise this is not always the case. For checking lock ownership, - /// 'fi->owner' must be used. The l_pid field in 'struct flock' should only be - /// used to fill in this field in getlk(). Note: if the locking methods are not - /// implemented, the kernel will still allow file locking to work locally. - /// Hence these are only interesting for network filesystems and similar. - #[derive(Debug)] - pub struct SetLk<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_lk_in, - } - impl_request!(SetLk<'a>); - impl<'a> SetLk<'a> { - /// The value set by the [Open] method. See [FileHandle]. - pub fn file_handle(&self) -> FileHandle { - FileHandle(self.arg.fh) - } - pub fn lock(&self) -> Lock { - Lock::from_abi(&self.arg.lk) - } - pub fn lock_owner(&self) -> LockOwner { - LockOwner(self.arg.owner) - } - } - #[derive(Debug)] - pub struct SetLkW<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_lk_in, - } - impl_request!(SetLkW<'a>); - impl<'a> SetLkW<'a> { - /// The value set by the [Open] method. See [FileHandle]. - pub fn file_handle(&self) -> FileHandle { - FileHandle(self.arg.fh) - } - pub fn lock(&self) -> Lock { - Lock::from_abi(&self.arg.lk) - } - pub fn lock_owner(&self) -> LockOwner { - LockOwner(self.arg.owner) - } - } - - /// Check file access permissions. - /// - /// This will be called for the `access()` system call. If the 'default_permissions' - /// mount option is given, this method is not called. - #[derive(Debug)] - pub struct Access<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_access_in, - } - impl_request!(Access<'a>); - impl<'a> Access<'a> { - pub fn mask(&self) -> i32 { - self.arg.mask - } - } - - /// Create and open a file. - /// - /// If the file does not exist, first create it with the specified mode, and then - /// open it. Open flags (with the exception of `O_NOCTTY`) are available in flags. - /// Filesystem may store an arbitrary file handle (pointer, index, etc) in fh, - /// and use this in other all other file operations ([Read], [Write], [Flush], [Release], - /// [FSync]). 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 for more details. If this method is not - /// implemented or under Linux kernel versions earlier than 2.6.15, the [MkNod] - /// and [Open] methods will be called instead. - #[derive(Debug)] - pub struct Create<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_create_in, - name: &'a Path, - } - impl_request!(Create<'a>); - impl<'a> Create<'a> { - pub fn name(&self) -> &'a Path { - self.name - } - pub fn mode(&self) -> u32 { - self.arg.mode - } - /// Flags as passed to the creat() call - pub fn flags(&self) -> i32 { - self.arg.flags - } - pub fn umask(&self) -> u32 { - #[cfg(not(feature = "abi-7-12"))] - return 0; - #[cfg(feature = "abi-7-12")] - self.arg.umask - } - } - - /// If a process issuing a FUSE filesystem request is interrupted, the - /// following will happen: - /// - /// 1) If the request is not yet sent to userspace AND the signal is - /// fatal (SIGKILL or unhandled fatal signal), then the request is - /// dequeued and returns immediately. - /// - /// 2) If the request is not yet sent to userspace AND the signal is not - /// fatal, then an 'interrupted' flag is set for the request. When - /// the request has been successfully transferred to userspace and - /// this flag is set, an INTERRUPT request is queued. - /// - /// 3) If the request is already sent to userspace, then an INTERRUPT - /// request is queued. - /// - /// [Interrupt] requests take precedence over other requests, so the - /// userspace filesystem will receive queued [Interrupt]s before any others. - /// - /// The userspace filesystem may ignore the [Interrupt] requests entirely, - /// or may honor them by sending a reply to the **original** request, with - /// the error set to [Errno::EINTR]. - /// - /// It is also possible that there's a race between processing the - /// original request and its [Interrupt] request. There are two - /// possibilities: - /// - /// 1. The [Interrupt] request is processed before the original request is - /// processed - /// - /// 2. The [Interrupt] request is processed after the original request has - /// been answered - /// - /// If the filesystem cannot find the original request, it should wait for - /// some timeout and/or a number of new requests to arrive, after which it - /// should reply to the [Interrupt] request with an [Errno::EAGAIN] error. - /// In case (1) the [Interrupt] request will be requeued. In case (2) the - /// [Interrupt] reply will be ignored. - #[derive(Debug)] - pub struct Interrupt<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_interrupt_in, - } - impl_request!(Interrupt<'a>); - impl<'a> Interrupt<'a> { - pub fn unique(&self) -> RequestId { - RequestId(self.arg.unique) - } - } - - /// Map block index within file to block index within device. - /// Note: This makes sense only for block device backed filesystems mounted - /// with the 'blkdev' option - #[derive(Debug)] - pub struct BMap<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_bmap_in, - } - impl_request!(BMap<'a>); - impl<'a> BMap<'a> { - pub fn block_size(&self) -> u32 { - self.arg.blocksize - } - pub fn block(&self) -> u64 { - self.arg.block - } - } - - #[derive(Debug)] - pub struct Destroy<'a> { - header: &'a fuse_in_header, - } - impl_request!(Destroy<'a>); - impl<'a> Destroy<'a> { - pub fn reply(&self) -> Response<'a> { - Response::new_empty() - } - } - - /// Control device - #[cfg(feature = "abi-7-11")] - #[derive(Debug)] - pub struct IoCtl<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_ioctl_in, - data: &'a [u8], - } - #[cfg(feature = "abi-7-11")] - impl_request!(IoCtl<'a>); - #[cfg(feature = "abi-7-11")] - impl<'a> IoCtl<'a> { - pub fn in_data(&self) -> &[u8] { - &self.data[..self.arg.in_size as usize] - } - pub fn unrestricted(&self) -> bool { - self.arg.flags & consts::FUSE_IOCTL_UNRESTRICTED != 0 - } - /// The value set by the [Open] method. See [FileHandle]. - pub fn file_handle(&self) -> FileHandle { - FileHandle(self.arg.fh) - } - /// TODO: What are valid values here? - pub fn flags(&self) -> u32 { - self.arg.flags - } - /// TODO: What does this mean? - pub fn command(&self) -> u32 { - self.arg.cmd - } - pub fn out_size(&self) -> u32 { - self.arg.out_size - } - } - - /// Poll. - #[cfg(feature = "abi-7-11")] - #[derive(Debug)] - pub struct Poll<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_poll_in, - } - #[cfg(feature = "abi-7-11")] - impl_request!(Poll<'a>); - #[cfg(feature = "abi-7-11")] - impl<'a> Poll<'a> { - /// The value set by the [Open] method. See [FileHandle]. - 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 - #[cfg(feature = "abi-7-15")] - #[derive(Debug)] - pub struct NotifyReply<'a> { - header: &'a fuse_in_header, - #[allow(unused)] - arg: &'a [u8], - } - #[cfg(feature = "abi-7-15")] - impl_request!(NotifyReply<'a>); - - /// BatchForget: TODO: merge with Forget - #[cfg(feature = "abi-7-16")] - #[derive(Debug)] - pub struct BatchForget<'a> { - header: &'a fuse_in_header, - #[allow(unused)] - arg: &'a fuse_batch_forget_in, - nodes: &'a [fuse_forget_one], - } - #[cfg(feature = "abi-7-16")] - impl_request!(BatchForget<'a>); - #[cfg(feature = "abi-7-16")] - impl<'a> BatchForget<'a> { - /// TODO: Don't return fuse_forget_one, this should be private - pub fn nodes(&self) -> &'a [fuse_forget_one] { - self.nodes - } - } - - /// Preallocate or deallocate space to a file - /// - /// Implementations should return EINVAL if offset or length are < 0 - #[cfg(feature = "abi-7-19")] - #[derive(Debug)] - pub struct FAllocate<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_fallocate_in, - } - #[cfg(feature = "abi-7-19")] - impl_request!(FAllocate<'a>); - #[cfg(feature = "abi-7-19")] - impl<'a> FAllocate<'a> { - /// The value set by the [Open] method. See [FileHandle]. - pub fn file_handle(&self) -> FileHandle { - FileHandle(self.arg.fh) - } - pub fn offset(&self) -> i64 { - self.arg.offset - } - pub fn len(&self) -> i64 { - self.arg.length - } - /// `mode` as passed to fallocate. See `man 2 fallocate` - pub fn mode(&self) -> i32 { - self.arg.mode - } - } - - /// Read directory. - /// - /// TODO: Document when this is called rather than ReadDirectory - #[cfg(feature = "abi-7-21")] - #[derive(Debug)] - pub struct ReadDirPlus<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_read_in, - } - #[cfg(feature = "abi-7-21")] - impl_request!(ReadDirPlus<'a>); - #[cfg(feature = "abi-7-21")] - impl<'a> ReadDirPlus<'a> { - /// The value set by the [Open] method. See [FileHandle]. - pub fn file_handle(&self) -> FileHandle { - FileHandle(self.arg.fh) - } - pub fn offset(&self) -> i64 { - self.arg.offset - } - pub fn size(&self) -> u32 { - self.arg.size - } - } - - /// Rename a file. - /// - /// TODO: Document the differences to [Rename] and [Exchange] - #[cfg(feature = "abi-7-23")] - #[derive(Debug)] - pub struct Rename2<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_rename2_in, - name: &'a Path, - newname: &'a Path, - old_parent: INodeNo, - } - #[cfg(feature = "abi-7-23")] - impl_request!(Rename2<'a>); - #[cfg(feature = "abi-7-23")] - impl<'a> Rename2<'a> { - pub fn from(&self) -> FilenameInDir<'a> { - FilenameInDir::<'a> { - dir: self.old_parent, - name: self.name, - } - } - pub fn to(&self) -> FilenameInDir<'a> { - FilenameInDir::<'a> { - dir: INodeNo(self.arg.newdir), - name: self.newname, - } - } - /// Flags as passed to renameat2. As of Linux 3.18 this is - /// [libc::RENAME_EXCHANGE], [libc::RENAME_NOREPLACE] and - /// [libc::RENAME_WHITEOUT]. If you don't handle a particular flag - /// reply with an EINVAL error. - /// - /// TODO: Replace with enum/flags type - pub fn flags(&self) -> u32 { - self.arg.flags - } - } - - /// Reposition read/write file offset - /// - /// TODO: Document when you need to implement this. Read and Write provide the offset anyway. - #[cfg(feature = "abi-7-24")] - #[derive(Debug)] - pub struct Lseek<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_lseek_in, - } - #[cfg(feature = "abi-7-24")] - impl_request!(Lseek<'a>); - #[cfg(feature = "abi-7-24")] - impl<'a> Lseek<'a> { - /// The value set by the [Open] method. See [FileHandle]. - pub fn file_handle(&self) -> FileHandle { - FileHandle(self.arg.fh) - } - pub fn offset(&self) -> i64 { - self.arg.offset - } - /// TODO: Make this return an enum - pub fn whence(&self) -> i32 { - self.arg.whence - } - } - - /// Copy the specified range from the source inode to the destination inode - #[cfg(feature = "abi-7-28")] - #[derive(Debug, Clone, Copy)] - pub struct CopyFileRangeFile { - pub inode: INodeNo, - /// The value set by the [Open] method. See [FileHandle]. - pub file_handle: FileHandle, - pub offset: i64, - } - #[cfg(feature = "abi-7-28")] - #[derive(Debug)] - pub struct CopyFileRange<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_copy_file_range_in, - } - #[cfg(feature = "abi-7-28")] - impl_request!(CopyFileRange<'a>); - #[cfg(feature = "abi-7-28")] - impl<'a> CopyFileRange<'a> { - /// File and offset to copy data from - pub fn src(&self) -> CopyFileRangeFile { - CopyFileRangeFile { - inode: self.nodeid(), - file_handle: FileHandle(self.arg.fh_in), - offset: self.arg.off_in, - } - } - /// File and offset to copy data to - pub fn dest(&self) -> CopyFileRangeFile { - CopyFileRangeFile { - inode: INodeNo(self.arg.nodeid_out), - file_handle: FileHandle(self.arg.fh_out), - offset: self.arg.off_out, - } - } - /// Number of bytes to copy - pub fn len(&self) -> u64 { - self.arg.len - } - // API TODO: Return a specific flags type - pub fn flags(&self) -> u64 { - self.arg.flags - } - } - - /// MacOS only: Rename the volume. Set `fuse_init_out.flags` during init to - /// `FUSE_VOL_RENAME` to enable - #[cfg(target_os = "macos")] - #[derive(Debug)] - pub struct SetVolName<'a> { - header: &'a fuse_in_header, - name: &'a OsStr, - } - #[cfg(target_os = "macos")] - impl_request!(SetVolName<'a>); - #[cfg(target_os = "macos")] - impl<'a> SetVolName<'a> { - pub fn name(&self) -> &'a OsStr { - self.name - } - } - - /// macOS only: Query extended times (bkuptime and crtime). Set fuse_init_out.flags - /// during init to FUSE_XTIMES to enable - #[cfg(target_os = "macos")] - #[derive(Debug)] - pub struct GetXTimes<'a> { - header: &'a fuse_in_header, - } - #[cfg(target_os = "macos")] - impl_request!(GetXTimes<'a>); - // API TODO: Consider rename2(RENAME_EXCHANGE) - /// macOS only (undocumented) - #[cfg(target_os = "macos")] - #[derive(Debug)] - pub struct Exchange<'a> { - header: &'a fuse_in_header, - arg: &'a fuse_exchange_in, - oldname: &'a Path, - newname: &'a Path, - } - #[cfg(target_os = "macos")] - impl_request!(Exchange<'a>); - #[cfg(target_os = "macos")] - impl<'a> Exchange<'a> { - pub fn from(&self) -> FilenameInDir<'a> { - FilenameInDir::<'a> { - dir: INodeNo(self.arg.olddir), - name: self.oldname, - } - } - pub fn to(&self) -> FilenameInDir<'a> { - FilenameInDir::<'a> { - dir: INodeNo(self.arg.newdir), - name: self.newname, - } - } - pub fn options(&self) -> u64 { - self.arg.options - } - } - /// TODO: Document - #[cfg(feature = "abi-7-12")] - #[derive(Debug)] - pub struct CuseInit<'a> { - header: &'a fuse_in_header, - #[allow(unused)] - arg: &'a fuse_init_in, - } - #[cfg(feature = "abi-7-12")] - impl_request!(CuseInit<'a>); - - fn system_time_from_time(secs: i64, nsecs: u32) -> SystemTime { - if secs >= 0 { - SystemTime::UNIX_EPOCH + Duration::new(secs as u64, nsecs) - } else { - SystemTime::UNIX_EPOCH - Duration::new((-secs) as u64, nsecs) - } - } - pub(crate) fn parse<'a>( - header: &'a fuse_in_header, - opcode: &fuse_opcode, - data: &'a [u8], - ) -> Option> { - let mut data = ArgumentIterator::new(data); - Some(match opcode { - fuse_opcode::FUSE_LOOKUP => Operation::Lookup(Lookup { - header, - name: data.fetch_str()?, - }), - fuse_opcode::FUSE_FORGET => Operation::Forget(Forget { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_GETATTR => Operation::GetAttr(GetAttr { - header, - - #[cfg(feature = "abi-7-9")] - arg: data.fetch()?, - }), - fuse_opcode::FUSE_SETATTR => Operation::SetAttr(SetAttr { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_READLINK => Operation::ReadLink(ReadLink { header }), - fuse_opcode::FUSE_SYMLINK => Operation::SymLink(SymLink { - header, - link_name: data.fetch_str()?.as_ref(), - target: data.fetch_str()?.as_ref(), - }), - fuse_opcode::FUSE_MKNOD => Operation::MkNod(MkNod { - header, - arg: data.fetch()?, - name: data.fetch_str()?.as_ref(), - }), - fuse_opcode::FUSE_MKDIR => Operation::MkDir(MkDir { - header, - arg: data.fetch()?, - name: data.fetch_str()?.as_ref(), - }), - fuse_opcode::FUSE_UNLINK => Operation::Unlink(Unlink { - header, - name: data.fetch_str()?.as_ref(), - }), - fuse_opcode::FUSE_RMDIR => Operation::RmDir(RmDir { - header, - name: data.fetch_str()?.as_ref(), - }), - fuse_opcode::FUSE_RENAME => Operation::Rename(Rename { - header, - arg: data.fetch()?, - name: data.fetch_str()?.as_ref(), - newname: data.fetch_str()?.as_ref(), - }), - fuse_opcode::FUSE_LINK => Operation::Link(Link { - header, - arg: data.fetch()?, - name: data.fetch_str()?.as_ref(), - }), - fuse_opcode::FUSE_OPEN => Operation::Open(Open { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_READ => Operation::Read(Read { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_WRITE => Operation::Write({ - let out = Write { - header, - arg: data.fetch()?, - data: data.fetch_all(), - }; - assert!(out.data().len() == out.arg.size as usize); - out - }), - fuse_opcode::FUSE_STATFS => Operation::StatFs(StatFs { header }), - fuse_opcode::FUSE_RELEASE => Operation::Release(Release { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_FSYNC => Operation::FSync(FSync { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_SETXATTR => Operation::SetXAttr({ - let out = SetXAttr { - header, - arg: data.fetch()?, - name: data.fetch_str()?, - value: data.fetch_all(), - }; - assert!(out.value.len() == out.arg.size as usize); - out - }), - fuse_opcode::FUSE_GETXATTR => Operation::GetXAttr(GetXAttr { - header, - arg: data.fetch()?, - name: data.fetch_str()?, - }), - fuse_opcode::FUSE_LISTXATTR => Operation::ListXAttr(ListXAttr { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_REMOVEXATTR => Operation::RemoveXAttr(RemoveXAttr { - header, - name: data.fetch_str()?, - }), - fuse_opcode::FUSE_FLUSH => Operation::Flush(Flush { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_INIT => Operation::Init(Init { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_OPENDIR => Operation::OpenDir(OpenDir { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_READDIR => Operation::ReadDir(ReadDir { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_RELEASEDIR => Operation::ReleaseDir(ReleaseDir { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_FSYNCDIR => Operation::FSyncDir(FSyncDir { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_GETLK => Operation::GetLk(GetLk { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_SETLK => Operation::SetLk(SetLk { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_SETLKW => Operation::SetLkW(SetLkW { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_ACCESS => Operation::Access(Access { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_CREATE => Operation::Create(Create { - header, - arg: data.fetch()?, - name: data.fetch_str()?.as_ref(), - }), - fuse_opcode::FUSE_INTERRUPT => Operation::Interrupt(Interrupt { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_BMAP => Operation::BMap(BMap { - header, - arg: data.fetch()?, - }), - fuse_opcode::FUSE_DESTROY => Operation::Destroy(Destroy { header }), - #[cfg(feature = "abi-7-11")] - fuse_opcode::FUSE_IOCTL => Operation::IoCtl(IoCtl { - header, - arg: data.fetch()?, - data: data.fetch_all(), - }), - #[cfg(feature = "abi-7-11")] - fuse_opcode::FUSE_POLL => Operation::Poll(Poll { - header, - arg: data.fetch()?, - }), - #[cfg(feature = "abi-7-15")] - fuse_opcode::FUSE_NOTIFY_REPLY => Operation::NotifyReply(NotifyReply { - header, - arg: data.fetch_all(), - }), - #[cfg(feature = "abi-7-16")] - fuse_opcode::FUSE_BATCH_FORGET => { - let arg = data.fetch()?; - Operation::BatchForget(BatchForget { - header, - arg, - nodes: data.fetch_slice(arg.count as usize)?, - }) - } - #[cfg(feature = "abi-7-19")] - fuse_opcode::FUSE_FALLOCATE => Operation::FAllocate(FAllocate { - header, - arg: data.fetch()?, - }), - #[cfg(feature = "abi-7-21")] - fuse_opcode::FUSE_READDIRPLUS => Operation::ReadDirPlus(ReadDirPlus { - header, - arg: data.fetch()?, - }), - #[cfg(feature = "abi-7-23")] - fuse_opcode::FUSE_RENAME2 => Operation::Rename2(Rename2 { - header, - arg: data.fetch()?, - name: data.fetch_str()?.as_ref(), - newname: data.fetch_str()?.as_ref(), - old_parent: INodeNo(header.nodeid), - }), - #[cfg(feature = "abi-7-24")] - fuse_opcode::FUSE_LSEEK => Operation::Lseek(Lseek { - header, - arg: data.fetch()?, - }), - #[cfg(feature = "abi-7-28")] - fuse_opcode::FUSE_COPY_FILE_RANGE => Operation::CopyFileRange(CopyFileRange { - header, - arg: data.fetch()?, - }), - - #[cfg(target_os = "macos")] - fuse_opcode::FUSE_SETVOLNAME => Operation::SetVolName(SetVolName { - header, - name: data.fetch_str()?, - }), - #[cfg(target_os = "macos")] - fuse_opcode::FUSE_GETXTIMES => Operation::GetXTimes(GetXTimes { header }), - #[cfg(target_os = "macos")] - fuse_opcode::FUSE_EXCHANGE => Operation::Exchange(Exchange { - header, - arg: data.fetch()?, - oldname: data.fetch_str()?.as_ref(), - newname: data.fetch_str()?.as_ref(), - }), - - #[cfg(feature = "abi-7-12")] - fuse_opcode::CUSE_INIT => Operation::CuseInit(CuseInit { - header, - arg: data.fetch()?, - }), - }) - } -} -use op::*; - -/// Filesystem operation (and arguments) the kernel driver wants us to perform. The fields of each -/// variant needs to match the actual arguments the kernel driver sends for the specific operation. -#[derive(Debug)] -#[allow(missing_docs)] -pub enum Operation<'a> { - Lookup(Lookup<'a>), - Forget(Forget<'a>), - GetAttr(GetAttr<'a>), - SetAttr(SetAttr<'a>), - #[allow(dead_code)] - ReadLink(ReadLink<'a>), - SymLink(SymLink<'a>), - MkNod(MkNod<'a>), - MkDir(MkDir<'a>), - Unlink(Unlink<'a>), - RmDir(RmDir<'a>), - Rename(Rename<'a>), - Link(Link<'a>), - Open(Open<'a>), - Read(Read<'a>), - Write(Write<'a>), - #[allow(dead_code)] - StatFs(StatFs<'a>), - Release(Release<'a>), - FSync(FSync<'a>), - SetXAttr(SetXAttr<'a>), - GetXAttr(GetXAttr<'a>), - ListXAttr(ListXAttr<'a>), - RemoveXAttr(RemoveXAttr<'a>), - Flush(Flush<'a>), - Init(Init<'a>), - OpenDir(OpenDir<'a>), - ReadDir(ReadDir<'a>), - ReleaseDir(ReleaseDir<'a>), - FSyncDir(FSyncDir<'a>), - GetLk(GetLk<'a>), - SetLk(SetLk<'a>), - SetLkW(SetLkW<'a>), - Access(Access<'a>), - Create(Create<'a>), - Interrupt(Interrupt<'a>), - BMap(BMap<'a>), - Destroy(Destroy<'a>), - #[cfg(feature = "abi-7-11")] - IoCtl(IoCtl<'a>), - #[cfg(feature = "abi-7-11")] - Poll(Poll<'a>), - #[cfg(feature = "abi-7-15")] - #[allow(dead_code)] - NotifyReply(NotifyReply<'a>), - #[cfg(feature = "abi-7-16")] - BatchForget(BatchForget<'a>), - #[cfg(feature = "abi-7-19")] - FAllocate(FAllocate<'a>), - #[cfg(feature = "abi-7-21")] - ReadDirPlus(ReadDirPlus<'a>), - #[cfg(feature = "abi-7-23")] - Rename2(Rename2<'a>), - #[cfg(feature = "abi-7-24")] - Lseek(Lseek<'a>), - #[cfg(feature = "abi-7-28")] - CopyFileRange(CopyFileRange<'a>), - - #[cfg(target_os = "macos")] - SetVolName(SetVolName<'a>), - #[cfg(target_os = "macos")] - GetXTimes(GetXTimes<'a>), - #[cfg(target_os = "macos")] - Exchange(Exchange<'a>), - - #[cfg(feature = "abi-7-12")] - #[allow(dead_code)] - CuseInit(CuseInit<'a>), -} - -impl<'a> fmt::Display for Operation<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Operation::Lookup(x) => write!(f, "LOOKUP name {:?}", x.name()), - Operation::Forget(x) => write!(f, "FORGET nlookup {}", x.nlookup()), - Operation::GetAttr(_) => write!(f, "GETATTR"), - Operation::SetAttr(x) => x.fmt(f), - Operation::ReadLink(_) => write!(f, "READLINK"), - Operation::SymLink(x) => { - write!( - f, - "SYMLINK target {:?}, link_name {:?}", - x.target(), - x.link_name() - ) - } - Operation::MkNod(x) => write!( - f, - "MKNOD name {:?}, mode {:#05o}, rdev {}", - x.name(), - x.mode(), - x.rdev() - ), - Operation::MkDir(x) => write!(f, "MKDIR name {:?}, mode {:#05o}", x.name(), x.mode()), - Operation::Unlink(x) => write!(f, "UNLINK name {:?}", x.name()), - Operation::RmDir(x) => write!(f, "RMDIR name {:?}", x.name), - Operation::Rename(x) => write!(f, "RENAME src {:?}, dest {:?}", x.src(), x.dest()), - Operation::Link(x) => write!(f, "LINK ino {:?}, dest {:?}", x.inode_no(), x.dest()), - Operation::Open(x) => write!(f, "OPEN flags {:#x}", x.flags()), - Operation::Read(x) => write!( - f, - "READ fh {:?}, offset {}, size {}", - x.file_handle(), - x.offset(), - x.size() - ), - Operation::Write(x) => write!( - f, - "WRITE fh {:?}, offset {}, size {}, write flags {:#x}", - x.file_handle(), - x.offset(), - x.data().len(), - x.write_flags() - ), - Operation::StatFs(_) => write!(f, "STATFS"), - Operation::Release(x) => write!( - f, - "RELEASE fh {:?}, flags {:#x}, flush {}, lock owner {:?}", - x.file_handle(), - x.flags(), - x.flush(), - x.lock_owner() - ), - Operation::FSync(x) => write!( - f, - "FSYNC fh {:?}, fsync fdatasync {}", - x.file_handle(), - x.fdatasync() - ), - Operation::SetXAttr(x) => write!( - f, - "SETXATTR name {:?}, size {}, flags {:#x}", - x.name(), - x.value().len(), - x.flags() - ), - Operation::GetXAttr(x) => { - write!(f, "GETXATTR name {:?}, size {:?}", x.name(), x.size()) - } - Operation::ListXAttr(x) => write!(f, "LISTXATTR size {}", x.size()), - Operation::RemoveXAttr(x) => write!(f, "REMOVEXATTR name {:?}", x.name()), - Operation::Flush(x) => write!( - f, - "FLUSH fh {:?}, lock owner {:?}", - x.file_handle(), - x.lock_owner() - ), - Operation::Init(x) => write!( - f, - "INIT kernel ABI {}, capabilities {:#x}, max readahead {}", - x.version(), - x.capabilities(), - x.max_readahead() - ), - Operation::OpenDir(x) => write!(f, "OPENDIR flags {:#x}", x.flags()), - Operation::ReadDir(x) => write!( - f, - "READDIR fh {:?}, offset {}, size {}", - x.file_handle(), - x.offset(), - x.size() - ), - Operation::ReleaseDir(x) => write!( - f, - "RELEASEDIR fh {:?}, flags {:#x}, flush {}, lock owner {:?}", - x.file_handle(), - x.flags(), - x.flush(), - x.lock_owner() - ), - Operation::FSyncDir(x) => write!( - f, - "FSYNCDIR fh {:?}, fsync fdatasync: {}", - x.file_handle(), - x.fdatasync() - ), - Operation::GetLk(x) => write!( - f, - "GETLK fh {:?}, lock owner {:?}", - x.file_handle(), - x.lock_owner() - ), - Operation::SetLk(x) => write!( - f, - "SETLK fh {:?}, lock owner {:?}", - x.file_handle(), - x.lock_owner() - ), - Operation::SetLkW(x) => write!( - f, - "SETLKW fh {:?}, lock owner {:?}", - x.file_handle(), - x.lock_owner() - ), - Operation::Access(x) => write!(f, "ACCESS mask {:#05o}", x.mask()), - Operation::Create(x) => write!( - f, - "CREATE name {:?}, mode {:#05o}, flags {:#x}", - x.name(), - x.mode(), - x.flags() - ), - Operation::Interrupt(x) => write!(f, "INTERRUPT unique {:?}", x.unique()), - Operation::BMap(x) => write!(f, "BMAP blocksize {}, ids {}", x.block_size(), x.block()), - Operation::Destroy(_) => write!(f, "DESTROY"), - #[cfg(feature = "abi-7-11")] - Operation::IoCtl(x) => write!( - f, - "IOCTL fh {:?}, cmd {}, data size {}, flags {:#x}", - x.file_handle(), - x.command(), - x.in_data().len(), - x.flags() - ), - #[cfg(feature = "abi-7-11")] - Operation::Poll(x) => write!(f, "POLL fh {:?}", x.file_handle()), - #[cfg(feature = "abi-7-15")] - Operation::NotifyReply(_) => write!(f, "NOTIFYREPLY"), - #[cfg(feature = "abi-7-16")] - Operation::BatchForget(x) => write!(f, "BATCHFORGET nodes {:?}", x.nodes()), - #[cfg(feature = "abi-7-19")] - Operation::FAllocate(_) => write!(f, "FALLOCATE"), - #[cfg(feature = "abi-7-21")] - Operation::ReadDirPlus(x) => write!( - f, - "READDIRPLUS fh {:?}, offset {}, size {}", - x.file_handle(), - x.offset(), - x.size() - ), - #[cfg(feature = "abi-7-23")] - Operation::Rename2(x) => write!(f, "RENAME2 from {:?}, to {:?}", x.from(), x.to()), - #[cfg(feature = "abi-7-24")] - Operation::Lseek(x) => write!( - f, - "LSEEK fh {:?}, offset {}, whence {}", - x.file_handle(), - x.offset(), - x.whence() - ), - #[cfg(feature = "abi-7-28")] - Operation::CopyFileRange(x) => write!( - f, - "COPY_FILE_RANGE src {:?}, dest {:?}, len {}", - x.src(), - x.dest(), - x.len() - ), - - #[cfg(target_os = "macos")] - Operation::SetVolName(x) => write!(f, "SETVOLNAME name {:?}", x.name()), - #[cfg(target_os = "macos")] - Operation::GetXTimes(_) => write!(f, "GETXTIMES"), - #[cfg(target_os = "macos")] - Operation::Exchange(x) => write!( - f, - "EXCHANGE from {:?}, to {:?}, options {:#x}", - x.from(), - x.to(), - x.options() - ), - - #[cfg(feature = "abi-7-12")] - Operation::CuseInit(_) => write!(f, "CUSE_INIT"), - } - } -} - -/// Low-level request of a filesystem operation the kernel driver wants to perform. -#[derive(Debug)] -pub struct AnyRequest<'a> { - header: &'a fuse_in_header, - data: &'a [u8], -} -impl_request!(AnyRequest<'_>); - -impl<'a> AnyRequest<'a> { - pub fn operation(&self) -> Result, RequestError> { - // Parse/check opcode - let opcode = self.opcode() - .map_err(|_: InvalidOpcodeError| RequestError::UnknownOperation(self.header.opcode))?; - // Parse/check operation arguments - op::parse(self.header, &opcode, self.data).ok_or(RequestError::InsufficientData) - } - - pub fn opcode(&self) -> Result { - fuse_opcode::try_from(self.header.opcode) - } -} - -impl<'a> fmt::Display for AnyRequest<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Ok(op) = self.operation() { - write!( - f, - "FUSE({:3}) ino {:#018x} {}", - self.header.unique, self.header.nodeid, op - ) - } else { - write!( - f, - "FUSE({:3}) ino {:#018x}", - self.header.unique, self.header.nodeid - ) - } - } -} - -impl<'a> TryFrom<&'a [u8]> for AnyRequest<'a> { - type Error = RequestError; - - fn try_from(data: &'a [u8]) -> Result { - // Parse a raw packet as sent by the kernel driver into typed data. Every request always - // begins with a `fuse_in_header` struct followed by arguments depending on the opcode. - let data_len = data.len(); - let mut arg_iter = ArgumentIterator::new(data); - // Parse header - let header: &fuse_in_header = arg_iter - .fetch() - .ok_or_else(|| RequestError::ShortReadHeader(arg_iter.len()))?; - // Check data size - if data_len < header.len as usize { - return Err(RequestError::ShortRead(data_len, header.len as usize)); - } - Ok(Self { - header, - data: &data[mem::size_of::()..header.len as usize], - }) - } -} - -#[cfg(test)] -mod tests { - use super::super::test::AlignedData; - use super::*; - use std::ffi::OsStr; - - #[cfg(target_endian = "big")] - const INIT_REQUEST: AlignedData<[u8; 56]> = AlignedData([ - 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1a, // len, opcode - 0xde, 0xad, 0xbe, 0xef, 0xba, 0xad, 0xd0, 0x0d, // unique - 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, // nodeid - 0xc0, 0x01, 0xd0, 0x0d, 0xc0, 0x01, 0xca, 0xfe, // uid, gid - 0xc0, 0xde, 0xba, 0x5e, 0x00, 0x00, 0x00, 0x00, // pid, padding - 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, // major, minor - 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, // max_readahead, flags - ]); - - #[cfg(target_endian = "little")] - const INIT_REQUEST: AlignedData<[u8; 56]> = AlignedData([ - 0x38, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, // len, opcode - 0x0d, 0xf0, 0xad, 0xba, 0xef, 0xbe, 0xad, 0xde, // unique - 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // nodeid - 0x0d, 0xd0, 0x01, 0xc0, 0xfe, 0xca, 0x01, 0xc0, // uid, gid - 0x5e, 0xba, 0xde, 0xc0, 0x00, 0x00, 0x00, 0x00, // pid, padding - 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // major, minor - 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // max_readahead, flags - ]); - - #[cfg(target_endian = "big")] - const MKNOD_REQUEST: AlignedData<[u8; 56]> = [ - 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x08, // len, opcode - 0xde, 0xad, 0xbe, 0xef, 0xba, 0xad, 0xd0, 0x0d, // unique - 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, // nodeid - 0xc0, 0x01, 0xd0, 0x0d, 0xc0, 0x01, 0xca, 0xfe, // uid, gid - 0xc0, 0xde, 0xba, 0x5e, 0x00, 0x00, 0x00, 0x00, // pid, padding - 0x00, 0x00, 0x01, 0xa4, 0x00, 0x00, 0x00, 0x00, // mode, rdev - 0x66, 0x6f, 0x6f, 0x2e, 0x74, 0x78, 0x74, 0x00, // name - ]; - - #[cfg(all(target_endian = "little", not(feature = "abi-7-12")))] - const MKNOD_REQUEST: AlignedData<[u8; 56]> = AlignedData([ - 0x38, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // len, opcode - 0x0d, 0xf0, 0xad, 0xba, 0xef, 0xbe, 0xad, 0xde, // unique - 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // nodeid - 0x0d, 0xd0, 0x01, 0xc0, 0xfe, 0xca, 0x01, 0xc0, // uid, gid - 0x5e, 0xba, 0xde, 0xc0, 0x00, 0x00, 0x00, 0x00, // pid, padding - 0xa4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mode, rdev - 0x66, 0x6f, 0x6f, 0x2e, 0x74, 0x78, 0x74, 0x00, // name - ]); - - #[cfg(all(target_endian = "little", feature = "abi-7-12"))] - const MKNOD_REQUEST: AlignedData<[u8; 64]> = AlignedData([ - 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // len, opcode - 0x0d, 0xf0, 0xad, 0xba, 0xef, 0xbe, 0xad, 0xde, // unique - 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // nodeid - 0x0d, 0xd0, 0x01, 0xc0, 0xfe, 0xca, 0x01, 0xc0, // uid, gid - 0x5e, 0xba, 0xde, 0xc0, 0x00, 0x00, 0x00, 0x00, // pid, padding - 0xa4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mode, rdev - 0xed, 0x01, 0x00, 0x00, 0xe7, 0x03, 0x00, 0x00, // umask, padding - 0x66, 0x6f, 0x6f, 0x2e, 0x74, 0x78, 0x74, 0x00, // name - ]); - - #[test] - fn short_read_header() { - match AnyRequest::try_from(&INIT_REQUEST[..20]) { - Err(RequestError::ShortReadHeader(20)) => (), - _ => panic!("Unexpected request parsing result"), - } - } - - #[test] - fn short_read() { - match AnyRequest::try_from(&INIT_REQUEST[..48]) { - Err(RequestError::ShortRead(48, 56)) => (), - _ => panic!("Unexpected request parsing result"), - } - } - - #[test] - fn init() { - let req = AnyRequest::try_from(&INIT_REQUEST[..]).unwrap(); - assert_eq!(req.header.len, 56); - assert_eq!(req.header.opcode, 26); - assert_eq!(req.unique(), RequestId(0xdead_beef_baad_f00d)); - assert_eq!(req.nodeid(), INodeNo(0x1122_3344_5566_7788)); - assert_eq!(req.uid(), 0xc001_d00d); - assert_eq!(req.gid(), 0xc001_cafe); - assert_eq!(req.pid(), 0xc0de_ba5e); - match req.operation().unwrap() { - Operation::Init(x) => { - assert_eq!(x.version(), Version(7, 8)); - assert_eq!(x.max_readahead(), 4096); - } - _ => panic!("Unexpected request operation"), - } - } - - #[test] - fn mknod() { - let req = AnyRequest::try_from(&MKNOD_REQUEST[..]).unwrap(); - #[cfg(not(feature = "abi-7-12"))] - assert_eq!(req.header.len, 56); - #[cfg(feature = "abi-7-12")] - assert_eq!(req.header.len, 64); - assert_eq!(req.header.opcode, 8); - assert_eq!(req.unique(), RequestId(0xdead_beef_baad_f00d)); - assert_eq!(req.nodeid(), INodeNo(0x1122_3344_5566_7788)); - assert_eq!(req.uid(), 0xc001_d00d); - assert_eq!(req.gid(), 0xc001_cafe); - assert_eq!(req.pid(), 0xc0de_ba5e); - match req.operation().unwrap() { - Operation::MkNod(x) => { - assert_eq!(x.mode(), 0o644); - #[cfg(feature = "abi-7-12")] - assert_eq!(x.umask(), 0o755); - assert_eq!(x.name(), OsStr::new("foo.txt")); - } - _ => panic!("Unexpected request operation"), - } - } -} diff --git a/vendor/fuser/src/mnt/fuse2.rs b/vendor/fuser/src/mnt/fuse2.rs deleted file mode 100644 index ccdf0d819..000000000 --- a/vendor/fuser/src/mnt/fuse2.rs +++ /dev/null @@ -1,74 +0,0 @@ -use super::{fuse2_sys::*, with_fuse_args, MountOption}; -use log::warn; -use std::{ - ffi::CString, - fs::File, - io, - os::unix::prelude::{FromRawFd, OsStrExt}, - path::Path, - sync::Arc, -}; - -/// Ensures that an os error is never 0/Success -fn ensure_last_os_error() -> io::Error { - let err = io::Error::last_os_error(); - match err.raw_os_error() { - Some(0) => io::Error::new(io::ErrorKind::Other, "Unspecified Error"), - _ => err, - } -} - -/// An active FUSE mount. -/// -/// This struct manages the lifecycle of the mount, unmounting when dropped. -#[derive(Debug)] -pub struct Mount { - mountpoint: CString, -} -impl Mount { - /// Mounts the filesystem at the given path, with the given options. - /// - /// Returns the mounted FUSE file descriptor along with a [Mount] for handling the mount lifecycle. - pub fn new(mountpoint: &Path, options: &[MountOption]) -> io::Result<(Arc, Mount)> { - let mountpoint = CString::new(mountpoint.as_os_str().as_bytes()).unwrap(); - with_fuse_args(options, |args| { - let fd = unsafe { fuse_mount_compat25(mountpoint.as_ptr(), args) }; - if fd < 0 { - Err(ensure_last_os_error()) - } else { - let file = unsafe { File::from_raw_fd(fd) }; - Ok((Arc::new(file), Mount { mountpoint })) - } - }) - } -} -impl Drop for Mount { - fn drop(&mut self) { - use std::io::ErrorKind::PermissionDenied; - - // fuse_unmount_compat22 unfortunately doesn't return a status. Additionally, - // it attempts to call realpath, which in turn calls into the filesystem. So - // if the filesystem returns an error, the unmount does not take place, with - // no indication of the error available to the caller. So we call unmount - // directly, which is what osxfuse does anyway, since we already converted - // to the real path when we first mounted. - if let Err(err) = super::libc_umount(&self.mountpoint) { - // Linux always returns EPERM for non-root users. We have to let the - // library go through the setuid-root "fusermount -u" to unmount. - if err.kind() == PermissionDenied { - #[cfg(not(any( - target_os = "macos", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "openbsd", - target_os = "netbsd" - )))] - unsafe { - fuse_unmount_compat22(self.mountpoint.as_ptr()); - return; - } - } - warn!("umount failed with {:?}", err); - } - } -} diff --git a/vendor/fuser/src/mnt/fuse2_sys.rs b/vendor/fuser/src/mnt/fuse2_sys.rs deleted file mode 100644 index 406bcbd99..000000000 --- a/vendor/fuser/src/mnt/fuse2_sys.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Native FFI bindings to libfuse2. -//! -//! This is a small set of bindings that are required to mount/unmount FUSE filesystems and -//! open/close a fd to the FUSE kernel driver. - -#![warn(missing_debug_implementations)] -#![allow(missing_docs)] - -use libc::{c_char, c_int}; - -#[repr(C)] -#[derive(Debug)] -pub struct fuse_args { - pub argc: c_int, - pub argv: *const *const c_char, - pub allocated: c_int, -} - -#[cfg(fuser_mount_impl = "libfuse2")] -extern "C" { - // *_compat25 functions were introduced in FUSE 2.6 when function signatures changed. - // Therefore, the minimum version requirement for *_compat25 functions is libfuse-2.6.0. - - pub fn fuse_mount_compat25(mountpoint: *const c_char, args: *const fuse_args) -> c_int; - #[cfg(not(any( - target_os = "macos", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "openbsd", - target_os = "netbsd" - )))] - pub fn fuse_unmount_compat22(mountpoint: *const c_char); -} diff --git a/vendor/fuser/src/mnt/fuse3.rs b/vendor/fuser/src/mnt/fuse3.rs deleted file mode 100644 index c1ef1f4e8..000000000 --- a/vendor/fuser/src/mnt/fuse3.rs +++ /dev/null @@ -1,68 +0,0 @@ -use super::fuse3_sys::{ - fuse_session_destroy, fuse_session_fd, fuse_session_mount, fuse_session_new, - fuse_session_unmount, -}; -use super::{with_fuse_args, MountOption}; -use std::{ - ffi::{c_void, CString}, - fs::File, - io, - os::unix::{ffi::OsStrExt, io::FromRawFd}, - path::Path, - ptr, - sync::Arc, -}; - -/// Ensures that an os error is never 0/Success -fn ensure_last_os_error() -> io::Error { - let err = io::Error::last_os_error(); - match err.raw_os_error() { - Some(0) => io::Error::new(io::ErrorKind::Other, "Unspecified Error"), - _ => err, - } -} - -/// An active FUSE mount. -/// -/// This struct manages the lifecycle of the mount, unmounting and destroying the session when dropped. -#[derive(Debug)] -pub struct Mount { - fuse_session: *mut c_void, -} -impl Mount { - /// Mounts the filesystem at the given path, with the given options. - /// - /// Returns the mounted FUSE file descriptor along with a [Mount] for handling the mount lifecycle. - pub fn new(mnt: &Path, options: &[MountOption]) -> io::Result<(Arc, Mount)> { - let mnt = CString::new(mnt.as_os_str().as_bytes()).unwrap(); - with_fuse_args(options, |args| { - let fuse_session = unsafe { fuse_session_new(args, ptr::null(), 0, ptr::null_mut()) }; - if fuse_session.is_null() { - return Err(io::Error::last_os_error()); - } - let mount = Mount { fuse_session }; - let result = unsafe { fuse_session_mount(mount.fuse_session, mnt.as_ptr()) }; - if result != 0 { - return Err(ensure_last_os_error()); - } - let fd = unsafe { fuse_session_fd(mount.fuse_session) }; - if fd < 0 { - return Err(io::Error::last_os_error()); - } - // We dup the fd here as the existing fd is owned by the fuse_session, and we - // don't want it being closed out from under us: - let fd = nix::fcntl::fcntl(fd, nix::fcntl::FcntlArg::F_DUPFD_CLOEXEC(0))?; - let file = unsafe { File::from_raw_fd(fd) }; - Ok((Arc::new(file), mount)) - }) - } -} -impl Drop for Mount { - fn drop(&mut self) { - unsafe { - fuse_session_unmount(self.fuse_session); - fuse_session_destroy(self.fuse_session); - } - } -} -unsafe impl Send for Mount {} diff --git a/vendor/fuser/src/mnt/fuse3_sys.rs b/vendor/fuser/src/mnt/fuse3_sys.rs deleted file mode 100644 index c1af61314..000000000 --- a/vendor/fuser/src/mnt/fuse3_sys.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! Native FFI bindings to libfuse3. -//! -//! This is a small set of bindings that are required to mount/unmount FUSE filesystems and -//! open/close a fd to the FUSE kernel driver. - -#![warn(missing_debug_implementations)] -#![allow(missing_docs)] - -use super::fuse2_sys::fuse_args; -use libc::c_void; -use libc::{c_char, c_int}; - -extern "C" { - // Really this returns *fuse_session, but we don't need to access its fields - pub fn fuse_session_new( - args: *const fuse_args, - op: *const c_void, // This argument is really a *const fuse_lowlevel_ops, but we don't use them - op_size: libc::size_t, - userdata: *mut c_void, - ) -> *mut c_void; - pub fn fuse_session_mount( - se: *mut c_void, // This argument is really a *fuse_session - mountpoint: *const c_char, - ) -> c_int; - // This function's argument is really a *fuse_session - pub fn fuse_session_fd(se: *mut c_void) -> c_int; - // This function's argument is really a *fuse_session - pub fn fuse_session_unmount(se: *mut c_void); - // This function's argument is really a *fuse_session - pub fn fuse_session_destroy(se: *mut c_void); -} diff --git a/vendor/fuser/src/mnt/fuse_pure.rs b/vendor/fuser/src/mnt/fuse_pure.rs deleted file mode 100644 index 73c497740..000000000 --- a/vendor/fuser/src/mnt/fuse_pure.rs +++ /dev/null @@ -1,520 +0,0 @@ -//! Native FFI bindings to libfuse. -//! -//! This is a small set of bindings that are required to mount/unmount FUSE filesystems and -//! open/close a fd to the FUSE kernel driver. - -#![warn(missing_debug_implementations)] -#![allow(missing_docs)] - -use super::is_mounted; -use super::mount_options::{option_to_string, MountOption}; -use libc::c_int; -use log::{debug, error}; -use std::ffi::{CStr, CString, OsStr}; -use std::fs::{File, OpenOptions}; -use std::io; -use std::io::{Error, ErrorKind, Read}; -use std::os::unix::ffi::OsStrExt; -use std::os::unix::fs::PermissionsExt; -use std::os::unix::io::{AsRawFd, FromRawFd}; -use std::os::unix::net::UnixStream; -use std::path::Path; -use std::process::{Command, Stdio}; -use std::sync::Arc; -use std::{mem, ptr}; - -const FUSERMOUNT_BIN: &str = "fusermount"; -const FUSERMOUNT3_BIN: &str = "fusermount3"; -const FUSERMOUNT_COMM_ENV: &str = "_FUSE_COMMFD"; - -#[derive(Debug)] -pub struct Mount { - mountpoint: CString, - auto_unmount_socket: Option, - fuse_device: Arc, -} -impl Mount { - pub fn new(mountpoint: &Path, options: &[MountOption]) -> io::Result<(Arc, Mount)> { - let mountpoint = mountpoint.canonicalize()?; - let (file, sock) = fuse_mount_pure(mountpoint.as_os_str(), options)?; - let file = Arc::new(file); - Ok(( - file.clone(), - Mount { - mountpoint: CString::new(mountpoint.as_os_str().as_bytes())?, - auto_unmount_socket: sock, - fuse_device: file, - }, - )) - } -} - -impl Drop for Mount { - fn drop(&mut self) { - use std::io::ErrorKind::PermissionDenied; - if !is_mounted(&self.fuse_device) { - // If the filesystem has already been unmounted, avoid unmounting it again. - // Unmounting it a second time could cause a race with a newly mounted filesystem - // living at the same mountpoint - return; - } - if let Some(sock) = mem::take(&mut self.auto_unmount_socket) { - drop(sock); - // fusermount in auto-unmount mode, no more work to do. - return; - } - if let Err(err) = super::libc_umount(&self.mountpoint) { - if err.kind() == PermissionDenied { - // Linux always returns EPERM for non-root users. We have to let the - // library go through the setuid-root "fusermount -u" to unmount. - fuse_unmount_pure(&self.mountpoint) - } else { - error!("Unmount failed: {}", err) - } - } - } -} - -fn fuse_mount_pure( - mountpoint: &OsStr, - options: &[MountOption], -) -> Result<(File, Option), io::Error> { - if options.contains(&MountOption::AutoUnmount) { - // Auto unmount is only supported via fusermount - return fuse_mount_fusermount(mountpoint, options); - } - - let res = fuse_mount_sys(mountpoint, options)?; - if let Some(file) = res { - Ok((file, None)) - } else { - // Retry - fuse_mount_fusermount(mountpoint, options) - } -} - -fn fuse_unmount_pure(mountpoint: &CStr) { - #[cfg(target_os = "linux")] - unsafe { - let result = libc::umount2(mountpoint.as_ptr(), libc::MNT_DETACH); - if result == 0 { - return; - } - } - #[cfg(target_os = "macos")] - unsafe { - let result = libc::unmount(mountpoint.as_ptr(), libc::MNT_FORCE); - if result == 0 { - return; - } - } - - let mut builder = Command::new(detect_fusermount_bin()); - builder.stdout(Stdio::piped()).stderr(Stdio::piped()); - builder - .arg("-u") - .arg("-q") - .arg("-z") - .arg("--") - .arg(OsStr::new(&mountpoint.to_string_lossy().into_owned())); - - if let Ok(output) = builder.output() { - debug!("fusermount: {}", String::from_utf8_lossy(&output.stdout)); - debug!("fusermount: {}", String::from_utf8_lossy(&output.stderr)); - } -} - -fn detect_fusermount_bin() -> String { - for name in [ - FUSERMOUNT3_BIN.to_string(), - FUSERMOUNT_BIN.to_string(), - format!("/bin/{FUSERMOUNT3_BIN}"), - format!("/bin/{FUSERMOUNT_BIN}"), - ] - .iter() - { - if Command::new(name).arg("-h").output().is_ok() { - return name.to_string(); - } - } - // Default to fusermount3 - FUSERMOUNT3_BIN.to_string() -} - -fn receive_fusermount_message(socket: &UnixStream) -> Result { - let mut io_vec_buf = [0u8]; - let mut io_vec = libc::iovec { - iov_base: io_vec_buf.as_mut_ptr() as *mut libc::c_void, - iov_len: io_vec_buf.len(), - }; - let cmsg_buffer_len = unsafe { libc::CMSG_SPACE(mem::size_of::() as libc::c_uint) }; - let mut cmsg_buffer = vec![0u8; cmsg_buffer_len as usize]; - let mut message: libc::msghdr; - #[cfg(all(target_os = "linux", not(target_env = "musl")))] - { - message = libc::msghdr { - msg_name: ptr::null_mut(), - msg_namelen: 0, - msg_iov: &mut io_vec, - msg_iovlen: 1, - msg_control: cmsg_buffer.as_mut_ptr() as *mut libc::c_void, - msg_controllen: cmsg_buffer.len(), - msg_flags: 0, - }; - } - #[cfg(all(target_os = "linux", target_env = "musl"))] - { - message = unsafe { std::mem::MaybeUninit::zeroed().assume_init() }; - message.msg_name = ptr::null_mut(); - message.msg_namelen = 0; - message.msg_iov = &mut io_vec; - message.msg_iovlen = 1; - message.msg_control = (&mut cmsg_buffer).as_mut_ptr() as *mut libc::c_void; - message.msg_controllen = cmsg_buffer.len() as u32; - message.msg_flags = 0; - } - #[cfg(any( - target_os = "macos", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "openbsd", - target_os = "netbsd" - ))] - { - message = libc::msghdr { - msg_name: ptr::null_mut(), - msg_namelen: 0, - msg_iov: &mut io_vec, - msg_iovlen: 1, - msg_control: (&mut cmsg_buffer).as_mut_ptr() as *mut libc::c_void, - msg_controllen: cmsg_buffer.len() as u32, - msg_flags: 0, - }; - } - - let mut result; - loop { - unsafe { - result = libc::recvmsg(socket.as_raw_fd(), &mut message, 0); - } - if result != -1 { - break; - } - let err = Error::last_os_error(); - if err.kind() != ErrorKind::Interrupted { - return Err(err); - } - } - if result == 0 { - return Err(Error::new( - ErrorKind::UnexpectedEof, - "Unexpected EOF reading from fusermount", - )); - } - - unsafe { - let control_msg = libc::CMSG_FIRSTHDR(&message); - if (*control_msg).cmsg_type != libc::SCM_RIGHTS { - return Err(Error::new( - ErrorKind::InvalidData, - format!( - "Unknown control message from fusermount: {}", - (*control_msg).cmsg_type - ), - )); - } - let fd_data = libc::CMSG_DATA(control_msg); - - let fd = *(fd_data as *const c_int); - if fd < 0 { - Err(ErrorKind::InvalidData.into()) - } else { - Ok(File::from_raw_fd(fd)) - } - } -} - -fn fuse_mount_fusermount( - mountpoint: &OsStr, - options: &[MountOption], -) -> Result<(File, Option), Error> { - let (child_socket, receive_socket) = UnixStream::pair()?; - - unsafe { - libc::fcntl(child_socket.as_raw_fd(), libc::F_SETFD, 0); - } - - let mut builder = Command::new(detect_fusermount_bin()); - builder.stdout(Stdio::piped()).stderr(Stdio::piped()); - if !options.is_empty() { - builder.arg("-o"); - let options_strs: Vec = options.iter().map(option_to_string).collect(); - builder.arg(options_strs.join(",")); - } - builder - .arg("--") - .arg(mountpoint) - .env(FUSERMOUNT_COMM_ENV, child_socket.as_raw_fd().to_string()); - - let fusermount_child = builder.spawn()?; - - drop(child_socket); // close socket in parent - - let file = match receive_fusermount_message(&receive_socket) { - Ok(f) => f, - Err(_) => { - // Drop receive socket, since fusermount has exited with an error - drop(receive_socket); - let output = fusermount_child.wait_with_output().unwrap(); - let stderr_string = String::from_utf8_lossy(&output.stderr).to_string(); - return if stderr_string.contains("only allowed if 'user_allow_other' is set") { - Err(io::Error::new(ErrorKind::PermissionDenied, stderr_string)) - } else { - Err(io::Error::new(ErrorKind::Other, stderr_string)) - }; - } - }; - let mut receive_socket = Some(receive_socket); - - if !options.contains(&MountOption::AutoUnmount) { - // Only close the socket, if auto unmount is not set. - // fusermount will keep running until the socket is closed, if auto unmount is set - drop(mem::take(&mut receive_socket)); - let output = fusermount_child.wait_with_output()?; - debug!("fusermount: {}", String::from_utf8_lossy(&output.stdout)); - debug!("fusermount: {}", String::from_utf8_lossy(&output.stderr)); - } else { - if let Some(mut stdout) = fusermount_child.stdout { - let stdout_fd = stdout.as_raw_fd(); - unsafe { - let mut flags = libc::fcntl(stdout_fd, libc::F_GETFL, 0); - flags |= libc::O_NONBLOCK; - libc::fcntl(stdout_fd, libc::F_SETFL, flags); - } - let mut buf = vec![0; 64 * 1024]; - if let Ok(len) = stdout.read(&mut buf) { - debug!("fusermount: {}", String::from_utf8_lossy(&buf[..len])); - } - } - if let Some(mut stderr) = fusermount_child.stderr { - let stderr_fd = stderr.as_raw_fd(); - unsafe { - let mut flags = libc::fcntl(stderr_fd, libc::F_GETFL, 0); - flags |= libc::O_NONBLOCK; - libc::fcntl(stderr_fd, libc::F_SETFL, flags); - } - let mut buf = vec![0; 64 * 1024]; - if let Ok(len) = stderr.read(&mut buf) { - debug!("fusermount: {}", String::from_utf8_lossy(&buf[..len])); - } - } - } - - unsafe { - libc::fcntl(file.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC); - } - - Ok((file, receive_socket)) -} - -// If returned option is none. Then fusermount binary should be tried -fn fuse_mount_sys(mountpoint: &OsStr, options: &[MountOption]) -> Result, Error> { - let fuse_device_name = "/dev/fuse"; - - let mountpoint_mode = File::open(mountpoint)?.metadata()?.permissions().mode(); - - // Auto unmount requests must be sent to fusermount binary - assert!(!options.contains(&MountOption::AutoUnmount)); - - let file = match OpenOptions::new() - .read(true) - .write(true) - .open(fuse_device_name) - { - Ok(file) => file, - Err(error) => { - if error.kind() == ErrorKind::NotFound { - error!("{} not found. Try 'modprobe fuse'", fuse_device_name); - } - return Err(error); - } - }; - assert!( - file.as_raw_fd() > 2, - "Conflict with stdin/stdout/stderr. fd={}", - file.as_raw_fd() - ); - - let mut mount_options = format!( - "fd={},rootmode={:o},user_id={},group_id={}", - file.as_raw_fd(), - mountpoint_mode, - nix::unistd::getuid(), - nix::unistd::getgid() - ); - - for option in options - .iter() - .filter(|x| option_group(x) == MountOptionGroup::KernelOption) - { - mount_options.push(','); - mount_options.push_str(&option_to_string(option)); - } - - let mut flags = 0; - if !options.contains(&MountOption::Dev) { - // Default to nodev - #[cfg(target_os = "linux")] - { - flags |= libc::MS_NODEV; - } - #[cfg(target_os = "macos")] - { - flags |= libc::MNT_NODEV; - } - } - if !options.contains(&MountOption::Suid) { - // Default to nosuid - #[cfg(target_os = "linux")] - { - flags |= libc::MS_NOSUID; - } - #[cfg(target_os = "macos")] - { - flags |= libc::MNT_NOSUID; - } - } - for flag in options - .iter() - .filter(|x| option_group(x) == MountOptionGroup::KernelFlag) - { - flags |= option_to_flag(flag); - } - - // Default name is "/dev/fuse", then use the subtype, and lastly prefer the name - let mut source = fuse_device_name; - if let Some(MountOption::Subtype(subtype)) = options - .iter() - .find(|x| matches!(**x, MountOption::Subtype(_))) - { - source = subtype; - } - if let Some(MountOption::FSName(name)) = options - .iter() - .find(|x| matches!(**x, MountOption::FSName(_))) - { - source = name; - } - - let c_source = CString::new(source).unwrap(); - let c_mountpoint = CString::new(mountpoint.as_bytes()).unwrap(); - - let result = unsafe { - #[cfg(target_os = "linux")] - { - let c_options = CString::new(mount_options).unwrap(); - let c_type = CString::new("fuse").unwrap(); - libc::mount( - c_source.as_ptr(), - c_mountpoint.as_ptr(), - c_type.as_ptr(), - flags, - c_options.as_ptr() as *const libc::c_void, - ) - } - #[cfg(target_os = "macos")] - { - let mut c_options = CString::new(mount_options).unwrap(); - libc::mount( - c_source.as_ptr(), - c_mountpoint.as_ptr(), - flags, - c_options.as_ptr() as *mut libc::c_void, - ) - } - }; - if result == -1 { - let err = Error::last_os_error(); - if err.kind() == ErrorKind::PermissionDenied { - return Ok(None); // Retry with fusermount - } else { - return Err(Error::new( - err.kind(), - format!("Error calling mount() at {mountpoint:?}: {err}"), - )); - } - } - - Ok(Some(file)) -} - -#[derive(PartialEq)] -pub enum MountOptionGroup { - KernelOption, - KernelFlag, - Fusermount, -} - -pub fn option_group(option: &MountOption) -> MountOptionGroup { - match option { - MountOption::FSName(_) => MountOptionGroup::Fusermount, - MountOption::Subtype(_) => MountOptionGroup::Fusermount, - MountOption::CUSTOM(_) => MountOptionGroup::KernelOption, - MountOption::AutoUnmount => MountOptionGroup::Fusermount, - MountOption::AllowOther => MountOptionGroup::KernelOption, - MountOption::Dev => MountOptionGroup::KernelFlag, - MountOption::NoDev => MountOptionGroup::KernelFlag, - MountOption::Suid => MountOptionGroup::KernelFlag, - MountOption::NoSuid => MountOptionGroup::KernelFlag, - MountOption::RO => MountOptionGroup::KernelFlag, - MountOption::RW => MountOptionGroup::KernelFlag, - MountOption::Exec => MountOptionGroup::KernelFlag, - MountOption::NoExec => MountOptionGroup::KernelFlag, - MountOption::Atime => MountOptionGroup::KernelFlag, - MountOption::NoAtime => MountOptionGroup::KernelFlag, - MountOption::DirSync => MountOptionGroup::KernelFlag, - MountOption::Sync => MountOptionGroup::KernelFlag, - MountOption::Async => MountOptionGroup::KernelFlag, - MountOption::AllowRoot => MountOptionGroup::KernelOption, - MountOption::DefaultPermissions => MountOptionGroup::KernelOption, - } -} - -#[cfg(target_os = "linux")] -pub fn option_to_flag(option: &MountOption) -> libc::c_ulong { - match option { - MountOption::Dev => 0, // There is no option for dev. It's the absence of NoDev - MountOption::NoDev => libc::MS_NODEV, - MountOption::Suid => 0, - MountOption::NoSuid => libc::MS_NOSUID, - MountOption::RW => 0, - MountOption::RO => libc::MS_RDONLY, - MountOption::Exec => 0, - MountOption::NoExec => libc::MS_NOEXEC, - MountOption::Atime => 0, - MountOption::NoAtime => libc::MS_NOATIME, - MountOption::Async => 0, - MountOption::Sync => libc::MS_SYNCHRONOUS, - MountOption::DirSync => libc::MS_DIRSYNC, - _ => unreachable!(), - } -} - -#[cfg(target_os = "macos")] -pub fn option_to_flag(option: &MountOption) -> libc::c_int { - match option { - MountOption::Dev => 0, // There is no option for dev. It's the absence of NoDev - MountOption::NoDev => libc::MNT_NODEV, - MountOption::Suid => 0, - MountOption::NoSuid => libc::MNT_NOSUID, - MountOption::RW => 0, - MountOption::RO => libc::MNT_RDONLY, - MountOption::Exec => 0, - MountOption::NoExec => libc::MNT_NOEXEC, - MountOption::Atime => 0, - MountOption::NoAtime => libc::MNT_NOATIME, - MountOption::Async => 0, - MountOption::Sync => libc::MNT_SYNCHRONOUS, - _ => unreachable!(), - } -} diff --git a/vendor/fuser/src/mnt/mod.rs b/vendor/fuser/src/mnt/mod.rs deleted file mode 100644 index ee8001cd0..000000000 --- a/vendor/fuser/src/mnt/mod.rs +++ /dev/null @@ -1,187 +0,0 @@ -//! FUSE kernel driver communication -//! -//! Raw communication channel to the FUSE kernel driver. - -#[cfg(fuser_mount_impl = "libfuse2")] -mod fuse2; -#[cfg(any(feature = "libfuse", test))] -mod fuse2_sys; -#[cfg(fuser_mount_impl = "libfuse3")] -mod fuse3; -#[cfg(fuser_mount_impl = "libfuse3")] -mod fuse3_sys; - -#[cfg(fuser_mount_impl = "pure-rust")] -mod fuse_pure; -pub mod mount_options; - -#[cfg(any(test, feature = "libfuse"))] -use fuse2_sys::fuse_args; -#[cfg(any(test, not(feature = "libfuse")))] -use std::fs::File; -#[cfg(any(test, fuser_mount_impl = "pure-rust", fuser_mount_impl = "libfuse2"))] -use std::io; - -#[cfg(any(feature = "libfuse", test))] -use mount_options::MountOption; - -/// Helper function to provide options as a fuse_args struct -/// (which contains an argc count and an argv pointer) -#[cfg(any(feature = "libfuse", test))] -fn with_fuse_args T>(options: &[MountOption], f: F) -> T { - use mount_options::option_to_string; - use std::ffi::CString; - - let mut args = vec![CString::new("rust-fuse").unwrap()]; - for x in options { - args.extend_from_slice(&[ - CString::new("-o").unwrap(), - CString::new(option_to_string(x)).unwrap(), - ]); - } - let argptrs: Vec<_> = args.iter().map(|s| s.as_ptr()).collect(); - f(&fuse_args { - argc: argptrs.len() as i32, - argv: argptrs.as_ptr(), - allocated: 0, - }) -} - -#[cfg(fuser_mount_impl = "libfuse2")] -pub use fuse2::Mount; -#[cfg(fuser_mount_impl = "libfuse3")] -pub use fuse3::Mount; -#[cfg(fuser_mount_impl = "pure-rust")] -pub use fuse_pure::Mount; -#[cfg(not(fuser_mount_impl = "libfuse3"))] -use std::ffi::CStr; - -#[cfg(not(fuser_mount_impl = "libfuse3"))] -#[inline] -fn libc_umount(mnt: &CStr) -> io::Result<()> { - #[cfg(any( - target_os = "macos", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "openbsd", - target_os = "netbsd" - ))] - let r = unsafe { libc::unmount(mnt.as_ptr(), 0) }; - - #[cfg(not(any( - target_os = "macos", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "openbsd", - target_os = "netbsd" - )))] - let r = unsafe { libc::umount(mnt.as_ptr()) }; - if r < 0 { - Err(io::Error::last_os_error()) - } else { - Ok(()) - } -} - -/// Warning: This will return true if the filesystem has been detached (lazy unmounted), but not -/// yet destroyed by the kernel. -#[cfg(any(test, fuser_mount_impl = "pure-rust"))] -fn is_mounted(fuse_device: &File) -> bool { - use libc::{poll, pollfd}; - use std::os::unix::prelude::AsRawFd; - - let mut poll_result = pollfd { - fd: fuse_device.as_raw_fd(), - events: 0, - revents: 0, - }; - loop { - let res = unsafe { poll(&mut poll_result, 1, 0) }; - break match res { - 0 => true, - 1 => (poll_result.revents & libc::POLLERR) != 0, - -1 => { - let err = io::Error::last_os_error(); - if err.kind() == io::ErrorKind::Interrupted { - continue; - } else { - // This should never happen. The fd is guaranteed good as `File` owns it. - // According to man poll ENOMEM is the only error code unhandled, so we panic - // consistent with rust's usual ENOMEM behaviour. - panic!("Poll failed with error {}", err) - } - } - _ => unreachable!(), - }; - } -} - -#[cfg(test)] -mod test { - use super::*; - use std::{ffi::CStr, mem::ManuallyDrop}; - - #[test] - fn fuse_args() { - with_fuse_args( - &[ - MountOption::CUSTOM("foo".into()), - MountOption::CUSTOM("bar".into()), - ], - |args| { - let v: Vec<_> = (0..args.argc) - .map(|n| unsafe { - CStr::from_ptr(*args.argv.offset(n as isize)) - .to_str() - .unwrap() - }) - .collect(); - assert_eq!(*v, ["rust-fuse", "-o", "foo", "-o", "bar"]); - }, - ); - } - fn cmd_mount() -> String { - std::str::from_utf8( - std::process::Command::new("sh") - .arg("-c") - .arg("mount | grep fuse") - .output() - .unwrap() - .stdout - .as_ref(), - ) - .unwrap() - .to_owned() - } - - #[test] - fn mount_unmount() { - // We use ManuallyDrop here to leak the directory on test failure. We don't - // want to try and clean up the directory if it's a mountpoint otherwise we'll - // deadlock. - let tmp = ManuallyDrop::new(tempfile::tempdir().unwrap()); - let (file, mount) = Mount::new(tmp.path(), &[]).unwrap(); - let mnt = cmd_mount(); - eprintln!("Our mountpoint: {:?}\nfuse mounts:\n{}", tmp.path(), mnt,); - assert!(mnt.contains(&*tmp.path().to_string_lossy())); - assert!(is_mounted(&file)); - drop(mount); - let mnt = cmd_mount(); - eprintln!("Our mountpoint: {:?}\nfuse mounts:\n{}", tmp.path(), mnt,); - - let detached = !mnt.contains(&*tmp.path().to_string_lossy()); - // Linux supports MNT_DETACH, so we expect unmount to succeed even if the FS - // is busy. Other systems don't so the unmount may fail and we will still - // have the mount listed. The mount will get cleaned up later. - #[cfg(target_os = "linux")] - assert!(detached); - - if detached { - // We've detached successfully, it's safe to clean up: - std::mem::ManuallyDrop::<_>::into_inner(tmp); - } - - // Filesystem may have been lazy unmounted, so we can't assert this: - // assert!(!is_mounted(&file)); - } -} diff --git a/vendor/fuser/src/mnt/mount_options.rs b/vendor/fuser/src/mnt/mount_options.rs deleted file mode 100644 index 1778d28e4..000000000 --- a/vendor/fuser/src/mnt/mount_options.rs +++ /dev/null @@ -1,241 +0,0 @@ -use std::io; -use std::io::ErrorKind; -use std::{collections::HashSet, ffi::OsStr}; - -/// Mount options accepted by the FUSE filesystem type -/// See 'man mount.fuse' for details -// TODO: add all options that 'man mount.fuse' documents and libfuse supports -#[derive(Debug, Eq, PartialEq, Hash, Clone)] -pub enum MountOption { - /// Set the name of the source in mtab - FSName(String), - /// Set the filesystem subtype in mtab - Subtype(String), - /// Allows passing an option which is not otherwise supported in these enums - #[allow(clippy::upper_case_acronyms)] - CUSTOM(String), - - /* Parameterless options */ - /// Allow all users to access files on this filesystem. By default access is restricted to the - /// user who mounted it - AllowOther, - /// Allow the root user to access this filesystem, in addition to the user who mounted it - AllowRoot, - /// Automatically unmount when the mounting process exits - /// - /// `AutoUnmount` requires `AllowOther` or `AllowRoot`. If `AutoUnmount` is set and neither `Allow...` is set, the FUSE configuration must permit `allow_other`, otherwise mounting will fail. - AutoUnmount, - /// Enable permission checking in the kernel - DefaultPermissions, - - /* Flags */ - /// Enable special character and block devices - Dev, - /// Disable special character and block devices - NoDev, - /// Honor set-user-id and set-groupd-id bits on files - Suid, - /// Don't honor set-user-id and set-groupd-id bits on files - NoSuid, - /// Read-only filesystem - RO, - /// Read-write filesystem - RW, - /// Allow execution of binaries - Exec, - /// Don't allow execution of binaries - NoExec, - /// Support inode access time - Atime, - /// Don't update inode access time - NoAtime, - /// All modifications to directories will be done synchronously - DirSync, - /// All I/O will be done synchronously - Sync, - /// All I/O will be done asynchronously - Async, - /* libfuse library options, such as "direct_io", are not included since they are specific - to libfuse, and not part of the kernel ABI */ -} - -impl MountOption { - pub(crate) fn from_str(s: &str) -> MountOption { - match s { - "auto_unmount" => MountOption::AutoUnmount, - "allow_other" => MountOption::AllowOther, - "allow_root" => MountOption::AllowRoot, - "default_permissions" => MountOption::DefaultPermissions, - "dev" => MountOption::Dev, - "nodev" => MountOption::NoDev, - "suid" => MountOption::Suid, - "nosuid" => MountOption::NoSuid, - "ro" => MountOption::RO, - "rw" => MountOption::RW, - "exec" => MountOption::Exec, - "noexec" => MountOption::NoExec, - "atime" => MountOption::Atime, - "noatime" => MountOption::NoAtime, - "dirsync" => MountOption::DirSync, - "sync" => MountOption::Sync, - "async" => MountOption::Async, - x if x.starts_with("fsname=") => MountOption::FSName(x[7..].into()), - x if x.starts_with("subtype=") => MountOption::Subtype(x[8..].into()), - x => MountOption::CUSTOM(x.into()), - } - } -} - -pub fn check_option_conflicts(options: &[MountOption]) -> Result<(), io::Error> { - let mut options_set = HashSet::new(); - options_set.extend(options.iter().cloned()); - let conflicting: HashSet = options.iter().flat_map(conflicts_with).collect(); - let intersection: Vec = conflicting.intersection(&options_set).cloned().collect(); - if !intersection.is_empty() { - Err(io::Error::new( - ErrorKind::InvalidInput, - format!("Conflicting mount options found: {intersection:?}"), - )) - } else { - Ok(()) - } -} - -fn conflicts_with(option: &MountOption) -> Vec { - match option { - MountOption::FSName(_) => vec![], - MountOption::Subtype(_) => vec![], - MountOption::CUSTOM(_) => vec![], - MountOption::AllowOther => vec![MountOption::AllowRoot], - MountOption::AllowRoot => vec![MountOption::AllowOther], - MountOption::AutoUnmount => vec![], - MountOption::DefaultPermissions => vec![], - MountOption::Dev => vec![MountOption::NoDev], - MountOption::NoDev => vec![MountOption::Dev], - MountOption::Suid => vec![MountOption::NoSuid], - MountOption::NoSuid => vec![MountOption::Suid], - MountOption::RO => vec![MountOption::RW], - MountOption::RW => vec![MountOption::RO], - MountOption::Exec => vec![MountOption::NoExec], - MountOption::NoExec => vec![MountOption::Exec], - MountOption::Atime => vec![MountOption::NoAtime], - MountOption::NoAtime => vec![MountOption::Atime], - MountOption::DirSync => vec![], - MountOption::Sync => vec![MountOption::Async], - MountOption::Async => vec![MountOption::Sync], - } -} - -// Format option to be passed to libfuse or kernel -pub fn option_to_string(option: &MountOption) -> String { - match option { - MountOption::FSName(name) => format!("fsname={name}"), - MountOption::Subtype(subtype) => format!("subtype={subtype}"), - MountOption::CUSTOM(value) => value.to_string(), - MountOption::AutoUnmount => "auto_unmount".to_string(), - MountOption::AllowOther => "allow_other".to_string(), - // AllowRoot is implemented by allowing everyone access and then restricting to - // root + owner within fuser - MountOption::AllowRoot => "allow_other".to_string(), - MountOption::DefaultPermissions => "default_permissions".to_string(), - MountOption::Dev => "dev".to_string(), - MountOption::NoDev => "nodev".to_string(), - MountOption::Suid => "suid".to_string(), - MountOption::NoSuid => "nosuid".to_string(), - MountOption::RO => "ro".to_string(), - MountOption::RW => "rw".to_string(), - MountOption::Exec => "exec".to_string(), - MountOption::NoExec => "noexec".to_string(), - MountOption::Atime => "atime".to_string(), - MountOption::NoAtime => "noatime".to_string(), - MountOption::DirSync => "dirsync".to_string(), - MountOption::Sync => "sync".to_string(), - MountOption::Async => "async".to_string(), - } -} - -/// Parses mount command args. -/// -/// Input: ["-o", "suid", "-o", "ro,nodev,noexec", "-osync"] -/// Output Ok([Suid, RO, NoDev, NoExec, Sync]) -pub(crate) fn parse_options_from_args(args: &[&OsStr]) -> io::Result> { - let err = |x| io::Error::new(ErrorKind::InvalidInput, x); - let args: Option> = args.iter().map(|x| x.to_str()).collect(); - let args = args.ok_or_else(|| err("Error parsing args: Invalid UTF-8".to_owned()))?; - let mut it = args.iter(); - let mut out = vec![]; - loop { - let opt = match it.next() { - None => break, - Some(&"-o") => *it.next().ok_or_else(|| { - err("Error parsing args: Expected option, reached end of args".to_owned()) - })?, - Some(x) if x.starts_with("-o") => &x[2..], - Some(x) => return Err(err(format!("Error parsing args: expected -o, got {x}"))), - }; - for x in opt.split(',') { - out.push(MountOption::from_str(x)) - } - } - Ok(out) -} - -#[cfg(test)] -mod test { - use std::os::unix::prelude::OsStrExt; - - use super::*; - - #[test] - fn option_checking() { - assert!(check_option_conflicts(&[MountOption::Suid, MountOption::NoSuid]).is_err()); - assert!(check_option_conflicts(&[MountOption::Suid, MountOption::NoExec]).is_ok()); - } - #[test] - fn option_round_trip() { - use super::MountOption::*; - for x in [ - FSName("Blah".to_owned()), - Subtype("Bloo".to_owned()), - CUSTOM("bongos".to_owned()), - AllowOther, - AutoUnmount, - DefaultPermissions, - Dev, - NoDev, - Suid, - NoSuid, - RO, - RW, - Exec, - NoExec, - Atime, - NoAtime, - DirSync, - Sync, - Async, - ] - .iter() - { - assert_eq!(*x, MountOption::from_str(option_to_string(x).as_ref())) - } - } - - #[test] - fn test_parse_options() { - use super::MountOption::*; - - assert_eq!(parse_options_from_args(&[]).unwrap(), &[]); - - let o: Vec<_> = "-o suid -o ro,nodev,noexec -osync" - .split(' ') - .map(OsStr::new) - .collect(); - let out = parse_options_from_args(o.as_ref()).unwrap(); - assert_eq!(out, [Suid, RO, NoDev, NoExec, Sync]); - - assert!(parse_options_from_args(&[OsStr::new("-o")]).is_err()); - assert!(parse_options_from_args(&[OsStr::new("not o")]).is_err()); - assert!(parse_options_from_args(&[OsStr::from_bytes(b"-o\xc3\x28")]).is_err()); - } -} diff --git a/vendor/fuser/src/notify.rs b/vendor/fuser/src/notify.rs deleted file mode 100644 index 4442252a7..000000000 --- a/vendor/fuser/src/notify.rs +++ /dev/null @@ -1,121 +0,0 @@ -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 to a pending poll() request. Can be saved and used to notify the -/// kernel when a poll is ready. -#[derive(Clone)] -pub struct PollHandle { - handle: u64, - notifier: Notifier, -} - -impl PollHandle { - pub(crate) fn new(cs: ChannelSender, kh: u64) -> Self { - Self { - handle: kh, - notifier: Notifier::new(cs), - } - } - - /// Notify the kernel that the associated file handle is ready to be polled. - pub fn notify(self) -> io::Result<()> { - self.notifier.poll(self.handle) - } -} - -impl From for u64 { - fn from(value: PollHandle) -> Self { - value.handle - } -} - -impl std::fmt::Debug for PollHandle { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("PollHandle").field(&self.handle).finish() - } -} - -/// A handle by which the application can send notifications to the server -#[derive(Debug, Clone)] -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 deleted file mode 100644 index a154b71f7..000000000 --- a/vendor/fuser/src/reply.rs +++ /dev/null @@ -1,1094 +0,0 @@ -//! Filesystem operation reply -//! -//! A reply is passed to filesystem operation implementations and must be used to send back the -//! result of an operation. The reply can optionally be sent to another thread to asynchronously -//! work on an operation and provide the result later. Also it allows replying with a block of -//! data without cloning the data. A reply *must always* be used (by calling either ok() or -//! error() exactly once). - -use crate::ll::{ - self, - reply::{DirEntPlusList, DirEntryPlus}, - Generation, -}; -use crate::ll::{ - reply::{DirEntList, DirEntOffset, DirEntry}, - INodeNo, -}; -use libc::c_int; -use log::{error, warn}; -use std::convert::AsRef; -use std::ffi::OsStr; -use std::fmt; -use std::io::IoSlice; -use std::time::Duration; - -#[cfg(target_os = "macos")] -use std::time::SystemTime; - -use crate::{FileAttr, FileType}; - -/// Generic reply callback to send data -pub trait ReplySender: Send + Sync + Unpin + 'static { - /// Send data. - fn send(&self, data: &[IoSlice<'_>]) -> std::io::Result<()>; -} - -impl fmt::Debug for Box { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "Box") - } -} - -/// Generic reply trait -pub trait Reply { - /// Create a new reply for the given request - fn new(unique: u64, sender: S) -> Self; -} - -/// -/// Raw reply -/// -#[derive(Debug)] -pub(crate) struct ReplyRaw { - /// Unique id of the request to reply to - unique: ll::RequestId, - /// Closure to call for sending the reply - sender: Option>, -} - -impl Reply for ReplyRaw { - fn new(unique: u64, sender: S) -> ReplyRaw { - let sender = Box::new(sender); - ReplyRaw { - unique: ll::RequestId(unique), - sender: Some(sender), - } - } -} - -impl ReplyRaw { - /// Reply to a request with the given error code and data. Must be called - /// only once (the `ok` and `error` methods ensure this by consuming `self`) - fn send_ll_mut(&mut self, response: &ll::Response<'_>) { - assert!(self.sender.is_some()); - let sender = self.sender.take().unwrap(); - let res = response.with_iovec(self.unique, |iov| sender.send(iov)); - if let Err(err) = res { - error!("Failed to send FUSE reply: {}", err); - } - } - fn send_ll(mut self, response: &ll::Response<'_>) { - self.send_ll_mut(response) - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - assert_ne!(err, 0); - self.send_ll(&ll::Response::new_error(ll::Errno::from_i32(err))); - } -} - -impl Drop for ReplyRaw { - fn drop(&mut self) { - if self.sender.is_some() { - warn!( - "Reply not sent for operation {}, replying with I/O error", - self.unique.0 - ); - self.send_ll_mut(&ll::Response::new_error(ll::Errno::EIO)); - } - } -} - -/// -/// Empty reply -/// -#[derive(Debug)] -pub struct ReplyEmpty { - reply: ReplyRaw, -} - -impl Reply for ReplyEmpty { - fn new(unique: u64, sender: S) -> ReplyEmpty { - ReplyEmpty { - reply: Reply::new(unique, sender), - } - } -} - -impl ReplyEmpty { - /// Reply to a request with nothing - pub fn ok(self) { - self.reply.send_ll(&ll::Response::new_empty()); - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -/// -/// Data reply -/// -#[derive(Debug)] -pub struct ReplyData { - reply: ReplyRaw, -} - -impl Reply for ReplyData { - fn new(unique: u64, sender: S) -> ReplyData { - ReplyData { - reply: Reply::new(unique, sender), - } - } -} - -impl ReplyData { - /// Reply to a request with the given data - pub fn data(self, data: &[u8]) { - self.reply.send_ll(&ll::Response::new_slice(data)); - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -/// -/// Entry reply -/// -#[derive(Debug)] -pub struct ReplyEntry { - reply: ReplyRaw, -} - -impl Reply for ReplyEntry { - fn new(unique: u64, sender: S) -> ReplyEntry { - ReplyEntry { - reply: Reply::new(unique, sender), - } - } -} - -impl ReplyEntry { - /// Reply to a request with the given entry - pub fn entry(self, ttl: &Duration, attr: &FileAttr, generation: u64) { - self.reply.send_ll(&ll::Response::new_entry( - ll::INodeNo(attr.ino), - ll::Generation(generation), - &attr.into(), - *ttl, - *ttl, - )); - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -/// -/// Attribute Reply -/// -#[derive(Debug)] -pub struct ReplyAttr { - reply: ReplyRaw, -} - -impl Reply for ReplyAttr { - fn new(unique: u64, sender: S) -> ReplyAttr { - ReplyAttr { - reply: Reply::new(unique, sender), - } - } -} - -impl ReplyAttr { - /// Reply to a request with the given attribute - pub fn attr(self, ttl: &Duration, attr: &FileAttr) { - self.reply - .send_ll(&ll::Response::new_attr(ttl, &attr.into())); - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -/// -/// XTimes Reply -/// -#[cfg(target_os = "macos")] -#[derive(Debug)] -pub struct ReplyXTimes { - reply: ReplyRaw, -} - -#[cfg(target_os = "macos")] -impl Reply for ReplyXTimes { - fn new(unique: u64, sender: S) -> ReplyXTimes { - ReplyXTimes { - reply: Reply::new(unique, sender), - } - } -} - -#[cfg(target_os = "macos")] -impl ReplyXTimes { - /// Reply to a request with the given xtimes - pub fn xtimes(self, bkuptime: SystemTime, crtime: SystemTime) { - self.reply - .send_ll(&ll::Response::new_xtimes(bkuptime, crtime)) - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -/// -/// Open Reply -/// -#[derive(Debug)] -pub struct ReplyOpen { - reply: ReplyRaw, -} - -impl Reply for ReplyOpen { - fn new(unique: u64, sender: S) -> ReplyOpen { - ReplyOpen { - reply: Reply::new(unique, sender), - } - } -} - -impl ReplyOpen { - /// Reply to a request with the given open result - pub fn opened(self, fh: u64, flags: u32) { - self.reply - .send_ll(&ll::Response::new_open(ll::FileHandle(fh), flags)) - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -/// -/// Write Reply -/// -#[derive(Debug)] -pub struct ReplyWrite { - reply: ReplyRaw, -} - -impl Reply for ReplyWrite { - fn new(unique: u64, sender: S) -> ReplyWrite { - ReplyWrite { - reply: Reply::new(unique, sender), - } - } -} - -impl ReplyWrite { - /// Reply to a request with the given open result - pub fn written(self, size: u32) { - self.reply.send_ll(&ll::Response::new_write(size)) - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -/// -/// Statfs Reply -/// -#[derive(Debug)] -pub struct ReplyStatfs { - reply: ReplyRaw, -} - -impl Reply for ReplyStatfs { - fn new(unique: u64, sender: S) -> ReplyStatfs { - ReplyStatfs { - reply: Reply::new(unique, sender), - } - } -} - -impl ReplyStatfs { - /// Reply to a request with the given open result - #[allow(clippy::too_many_arguments)] - pub fn statfs( - self, - blocks: u64, - bfree: u64, - bavail: u64, - files: u64, - ffree: u64, - bsize: u32, - namelen: u32, - frsize: u32, - ) { - self.reply.send_ll(&ll::Response::new_statfs( - blocks, bfree, bavail, files, ffree, bsize, namelen, frsize, - )) - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -/// -/// Create reply -/// -#[derive(Debug)] -pub struct ReplyCreate { - reply: ReplyRaw, -} - -impl Reply for ReplyCreate { - fn new(unique: u64, sender: S) -> ReplyCreate { - ReplyCreate { - reply: Reply::new(unique, sender), - } - } -} - -impl ReplyCreate { - /// Reply to a request with the given entry - pub fn created(self, ttl: &Duration, attr: &FileAttr, generation: u64, fh: u64, flags: u32) { - self.reply.send_ll(&ll::Response::new_create( - ttl, - &attr.into(), - ll::Generation(generation), - ll::FileHandle(fh), - flags, - )) - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -/// -/// Lock Reply -/// -#[derive(Debug)] -pub struct ReplyLock { - reply: ReplyRaw, -} - -impl Reply for ReplyLock { - fn new(unique: u64, sender: S) -> ReplyLock { - ReplyLock { - reply: Reply::new(unique, sender), - } - } -} - -impl ReplyLock { - /// Reply to a request with the given open result - pub fn locked(self, start: u64, end: u64, typ: i32, pid: u32) { - self.reply.send_ll(&ll::Response::new_lock(&ll::Lock { - range: (start, end), - typ, - pid, - })) - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -/// -/// Bmap Reply -/// -#[derive(Debug)] -pub struct ReplyBmap { - reply: ReplyRaw, -} - -impl Reply for ReplyBmap { - fn new(unique: u64, sender: S) -> ReplyBmap { - ReplyBmap { - reply: Reply::new(unique, sender), - } - } -} - -impl ReplyBmap { - /// Reply to a request with the given open result - pub fn bmap(self, block: u64) { - self.reply.send_ll(&ll::Response::new_bmap(block)) - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -/// -/// Ioctl Reply -/// -#[derive(Debug)] -pub struct ReplyIoctl { - reply: ReplyRaw, -} - -impl Reply for ReplyIoctl { - fn new(unique: u64, sender: S) -> ReplyIoctl { - ReplyIoctl { - reply: Reply::new(unique, sender), - } - } -} - -impl ReplyIoctl { - /// Reply to a request with the given open result - pub fn ioctl(self, result: i32, data: &[u8]) { - self.reply - .send_ll(&ll::Response::new_ioctl(result, &[IoSlice::new(data)])); - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -/// -/// 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 -/// -#[derive(Debug)] -pub struct ReplyDirectory { - reply: ReplyRaw, - data: DirEntList, -} - -impl ReplyDirectory { - /// Creates a new ReplyDirectory with a specified buffer size. - pub fn new(unique: u64, sender: S, size: usize) -> ReplyDirectory { - ReplyDirectory { - reply: Reply::new(unique, sender), - data: DirEntList::new(size), - } - } - - /// Add an entry to the directory reply buffer. Returns true if the buffer is full. - /// A transparent offset value can be provided for each entry. The kernel uses these - /// value to request the next entries in further readdir calls - #[must_use] - pub fn add>(&mut self, ino: u64, offset: i64, kind: FileType, name: T) -> bool { - let name = name.as_ref(); - self.data.push(&DirEntry::new( - INodeNo(ino), - DirEntOffset(offset), - kind, - name, - )) - } - - /// Reply to a request with the filled directory buffer - pub fn ok(self) { - self.reply.send_ll(&self.data.into()); - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -/// -/// DirectoryPlus reply -/// -#[derive(Debug)] -pub struct ReplyDirectoryPlus { - reply: ReplyRaw, - buf: DirEntPlusList, -} - -impl ReplyDirectoryPlus { - /// Creates a new ReplyDirectory with a specified buffer size. - pub fn new(unique: u64, sender: S, size: usize) -> ReplyDirectoryPlus { - ReplyDirectoryPlus { - reply: Reply::new(unique, sender), - buf: DirEntPlusList::new(size), - } - } - - /// Add an entry to the directory reply buffer. Returns true if the buffer is full. - /// A transparent offset value can be provided for each entry. The kernel uses these - /// value to request the next entries in further readdir calls - pub fn add>( - &mut self, - ino: u64, - offset: i64, - name: T, - ttl: &Duration, - attr: &FileAttr, - generation: u64, - ) -> bool { - let name = name.as_ref(); - self.buf.push(&DirEntryPlus::new( - INodeNo(ino), - Generation(generation), - DirEntOffset(offset), - name, - *ttl, - attr.into(), - *ttl, - )) - } - - /// Reply to a request with the filled directory buffer - pub fn ok(self) { - self.reply.send_ll(&self.buf.into()); - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -/// -/// Xattr reply -/// -#[derive(Debug)] -pub struct ReplyXattr { - reply: ReplyRaw, -} - -impl Reply for ReplyXattr { - fn new(unique: u64, sender: S) -> ReplyXattr { - ReplyXattr { - reply: Reply::new(unique, sender), - } - } -} - -impl ReplyXattr { - /// Reply to a request with the size of the xattr. - pub fn size(self, size: u32) { - self.reply.send_ll(&ll::Response::new_xattr_size(size)) - } - - /// Reply to a request with the data in the xattr. - pub fn data(self, data: &[u8]) { - self.reply.send_ll(&ll::Response::new_data(data)) - } - - /// Reply to a request with the given error code. - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -/// -/// Lseek Reply -/// -#[derive(Debug)] -pub struct ReplyLseek { - reply: ReplyRaw, -} - -impl Reply for ReplyLseek { - fn new(unique: u64, sender: S) -> ReplyLseek { - ReplyLseek { - reply: Reply::new(unique, sender), - } - } -} - -impl ReplyLseek { - /// Reply to a request with seeked offset - pub fn offset(self, offset: i64) { - self.reply.send_ll(&ll::Response::new_lseek(offset)) - } - - /// Reply to a request with the given error code - pub fn error(self, err: c_int) { - self.reply.error(err); - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{FileAttr, FileType}; - use std::io::IoSlice; - use std::sync::mpsc::{sync_channel, SyncSender}; - use std::thread; - use std::time::{Duration, UNIX_EPOCH}; - use zerocopy::{Immutable, IntoBytes}; - - #[derive(Debug, IntoBytes, Immutable)] - #[repr(C)] - struct Data { - a: u8, - b: u8, - c: u16, - } - - #[test] - fn serialize_empty() { - assert!(().as_bytes().is_empty()); - } - - #[test] - fn serialize_slice() { - let data: [u8; 4] = [0x12, 0x34, 0x56, 0x78]; - assert_eq!(data.as_bytes(), [0x12, 0x34, 0x56, 0x78]); - } - - #[test] - fn serialize_struct() { - let data = Data { - a: 0x12, - b: 0x34, - c: 0x5678, - }; - assert_eq!(data.as_bytes(), [0x12, 0x34, 0x78, 0x56]); - } - - struct AssertSender { - expected: Vec, - } - - impl super::ReplySender for AssertSender { - fn send(&self, data: &[IoSlice<'_>]) -> std::io::Result<()> { - let mut v = vec![]; - for x in data { - v.extend_from_slice(x) - } - assert_eq!(self.expected, v); - Ok(()) - } - } - - #[test] - fn reply_raw() { - let data = Data { - a: 0x12, - b: 0x34, - c: 0x5678, - }; - let sender = AssertSender { - expected: vec![ - 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x12, 0x34, 0x78, 0x56, - ], - }; - let reply: ReplyRaw = Reply::new(0xdeadbeef, sender); - reply.send_ll(&ll::Response::new_data(data.as_bytes())); - } - - #[test] - fn reply_error() { - let sender = AssertSender { - expected: vec![ - 0x10, 0x00, 0x00, 0x00, 0xbe, 0xff, 0xff, 0xff, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, - ], - }; - let reply: ReplyRaw = Reply::new(0xdeadbeef, sender); - reply.error(66); - } - - #[test] - fn reply_empty() { - let sender = AssertSender { - expected: vec![ - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, - ], - }; - let reply: ReplyEmpty = Reply::new(0xdeadbeef, sender); - reply.ok(); - } - - #[test] - fn reply_data() { - let sender = AssertSender { - expected: vec![ - 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, - ], - }; - let reply: ReplyData = Reply::new(0xdeadbeef, sender); - reply.data(&[0xde, 0xad, 0xbe, 0xef]); - } - - #[test] - fn reply_entry() { - let mut expected = if cfg!(target_os = "macos") { - vec![ - 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, - 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, - ] - } else { - vec![ - 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, - 0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00, - 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, - ] - }; - - if cfg!(feature = "abi-7-9") { - expected.extend(vec![0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - } - expected[0] = (expected.len()) as u8; - - let sender = AssertSender { expected }; - let reply: ReplyEntry = Reply::new(0xdeadbeef, sender); - let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678); - let ttl = Duration::new(0x8765, 0x4321); - let attr = FileAttr { - ino: 0x11, - size: 0x22, - blocks: 0x33, - atime: time, - mtime: time, - ctime: time, - crtime: time, - kind: FileType::RegularFile, - perm: 0o644, - nlink: 0x55, - uid: 0x66, - gid: 0x77, - rdev: 0x88, - flags: 0x99, - blksize: 0xbb, - }; - reply.entry(&ttl, &attr, 0xaa); - } - - #[test] - fn reply_attr() { - let mut expected = if cfg!(target_os = "macos") { - vec![ - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, - 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x99, 0x00, - 0x00, 0x00, - ] - } else { - vec![ - 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, - 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, - 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, - ] - }; - - if cfg!(feature = "abi-7-9") { - expected.extend_from_slice(&[0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - } - expected[0] = expected.len() as u8; - - let sender = AssertSender { expected }; - let reply: ReplyAttr = Reply::new(0xdeadbeef, sender); - let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678); - let ttl = Duration::new(0x8765, 0x4321); - let attr = FileAttr { - ino: 0x11, - size: 0x22, - blocks: 0x33, - atime: time, - mtime: time, - ctime: time, - crtime: time, - kind: FileType::RegularFile, - perm: 0o644, - nlink: 0x55, - uid: 0x66, - gid: 0x77, - rdev: 0x88, - flags: 0x99, - blksize: 0xbb, - }; - reply.attr(&ttl, &attr); - } - - #[test] - #[cfg(target_os = "macos")] - fn reply_xtimes() { - let sender = AssertSender { - expected: vec![ - 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, - ], - }; - let reply: ReplyXTimes = Reply::new(0xdeadbeef, sender); - let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678); - reply.xtimes(time, time); - } - - #[test] - fn reply_open() { - let sender = AssertSender { - expected: vec![ - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ], - }; - let reply: ReplyOpen = Reply::new(0xdeadbeef, sender); - reply.opened(0x1122, 0x33); - } - - #[test] - fn reply_write() { - let sender = AssertSender { - expected: vec![ - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ], - }; - let reply: ReplyWrite = Reply::new(0xdeadbeef, sender); - reply.written(0x1122); - } - - #[test] - fn reply_statfs() { - let sender = AssertSender { - expected: vec![ - 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ], - }; - let reply: ReplyStatfs = Reply::new(0xdeadbeef, sender); - reply.statfs(0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88); - } - - #[test] - fn reply_create() { - let mut expected = if cfg!(target_os = "macos") { - vec![ - 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, - 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0xbb, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ] - } else { - vec![ - 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, - 0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00, - 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ] - }; - - if cfg!(feature = "abi-7-9") { - let insert_at = expected.len() - 16; - expected.splice( - insert_at..insert_at, - vec![0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], - ); - } - expected[0] = (expected.len()) as u8; - - let sender = AssertSender { expected }; - let reply: ReplyCreate = Reply::new(0xdeadbeef, sender); - let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678); - let ttl = Duration::new(0x8765, 0x4321); - let attr = FileAttr { - ino: 0x11, - size: 0x22, - blocks: 0x33, - atime: time, - mtime: time, - ctime: time, - crtime: time, - kind: FileType::RegularFile, - perm: 0o644, - nlink: 0x55, - uid: 0x66, - gid: 0x77, - rdev: 0x88, - flags: 0x99, - blksize: 0xdd, - }; - reply.created(&ttl, &attr, 0xaa, 0xbb, 0xcc); - } - - #[test] - fn reply_lock() { - let sender = AssertSender { - expected: vec![ - 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, - ], - }; - let reply: ReplyLock = Reply::new(0xdeadbeef, sender); - reply.locked(0x11, 0x22, 0x33, 0x44); - } - - #[test] - fn reply_bmap() { - let sender = AssertSender { - expected: vec![ - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ], - }; - let reply: ReplyBmap = Reply::new(0xdeadbeef, sender); - reply.bmap(0x1234); - } - - #[test] - fn reply_directory() { - let sender = AssertSender { - expected: vec![ - 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, - 0x00, 0x00, 0xbb, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x68, 0x65, - 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x00, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x72, 0x73, - ], - }; - let mut reply = ReplyDirectory::new(0xdeadbeef, sender, 4096); - assert!(!reply.add(0xaabb, 1, FileType::Directory, "hello")); - assert!(!reply.add(0xccdd, 2, FileType::RegularFile, "world.rs")); - reply.ok(); - } - - #[test] - fn reply_xattr_size() { - let sender = AssertSender { - expected: vec![ - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, - 0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, - ], - }; - let reply = ReplyXattr::new(0xdeadbeef, sender); - reply.size(0x12345678); - } - - #[test] - fn reply_xattr_data() { - let sender = AssertSender { - expected: vec![ - 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, - ], - }; - let reply = ReplyXattr::new(0xdeadbeef, sender); - reply.data(&[0x11, 0x22, 0x33, 0x44]); - } - - impl super::ReplySender for SyncSender<()> { - fn send(&self, _: &[IoSlice<'_>]) -> std::io::Result<()> { - self.send(()).unwrap(); - Ok(()) - } - } - - #[test] - fn async_reply() { - let (tx, rx) = sync_channel::<()>(1); - let reply: ReplyEmpty = Reply::new(0xdeadbeef, tx); - thread::spawn(move || { - reply.ok(); - }); - rx.recv().unwrap(); - } -} diff --git a/vendor/fuser/src/request.rs b/vendor/fuser/src/request.rs deleted file mode 100644 index 8036af798..000000000 --- a/vendor/fuser/src/request.rs +++ /dev/null @@ -1,685 +0,0 @@ -//! Filesystem operation request -//! -//! A request represents information about a filesystem operation the kernel driver wants us to -//! perform. -//! -//! TODO: This module is meant to go away soon in favor of `ll::Request`. - -use crate::ll::{fuse_abi as abi, Errno, Response}; -use log::{debug, error, warn}; -use std::convert::TryFrom; -#[cfg(feature = "abi-7-28")] -use std::convert::TryInto; -use std::path::Path; -use std::sync::atomic::Ordering; - -use crate::channel::ChannelSender; -use crate::ll::Request as _; -#[cfg(feature = "abi-7-21")] -use crate::reply::ReplyDirectoryPlus; -use crate::reply::{Reply, ReplyDirectory, ReplySender}; -use crate::session::{Session, SessionACL}; -use crate::Filesystem; -#[cfg(feature = "abi-7-11")] -use crate::PollHandle; -use crate::{ll, KernelConfig}; - -/// Request data structure -#[derive(Debug)] -pub struct Request<'a> { - /// Channel sender for sending the reply - ch: ChannelSender, - /// Request raw data - #[allow(unused)] - data: &'a [u8], - /// Parsed request - request: ll::AnyRequest<'a>, -} - -impl<'a> Request<'a> { - /// Create a new request from the given data - pub(crate) fn new(ch: ChannelSender, data: &'a [u8]) -> Option> { - let request = match ll::AnyRequest::try_from(data) { - Ok(request) => request, - Err(err) => { - error!("{}", err); - return None; - } - }; - - Some(Self { ch, data, request }) - } - - /// Dispatch request to the given filesystem. - /// This calls the appropriate filesystem operation method for the - /// request and sends back the returned reply to the kernel - pub(crate) fn dispatch(&self, se: &Session) { - debug!("{}", self.request); - let unique = self.request.unique(); - - let res = match self.dispatch_req(se) { - Ok(Some(resp)) => resp, - Ok(None) => return, - Err(errno) => self.request.reply_err(errno), - } - .with_iovec(unique, |iov| self.ch.send(iov)); - - if let Err(err) = res { - warn!("Request {:?}: Failed to send reply: {}", unique, err) - } - } - - fn dispatch_req( - &self, - se: &Session, - ) -> Result>, Errno> { - let op = self.request.operation().map_err(|_| Errno::ENOSYS)?; - // Implement allow_root & access check for auto_unmount - if (se.allowed == SessionACL::RootAndOwner - && self.request.uid() != se.session_owner - && self.request.uid() != 0) - || (se.allowed == SessionACL::Owner && self.request.uid() != se.session_owner) - { - #[cfg(feature = "abi-7-21")] - { - match op { - // Only allow operations that the kernel may issue without a uid set - ll::Operation::Init(_) - | ll::Operation::Destroy(_) - | ll::Operation::Read(_) - | ll::Operation::ReadDir(_) - | ll::Operation::ReadDirPlus(_) - | ll::Operation::BatchForget(_) - | ll::Operation::Forget(_) - | ll::Operation::Write(_) - | ll::Operation::FSync(_) - | ll::Operation::FSyncDir(_) - | ll::Operation::Release(_) - | ll::Operation::ReleaseDir(_) => {} - _ => { - return Err(Errno::EACCES); - } - } - } - #[cfg(all(feature = "abi-7-16", not(feature = "abi-7-21")))] - { - match op { - // Only allow operations that the kernel may issue without a uid set - ll::Operation::Init(_) - | ll::Operation::Destroy(_) - | ll::Operation::Read(_) - | ll::Operation::ReadDir(_) - | ll::Operation::BatchForget(_) - | ll::Operation::Forget(_) - | ll::Operation::Write(_) - | ll::Operation::FSync(_) - | ll::Operation::FSyncDir(_) - | ll::Operation::Release(_) - | ll::Operation::ReleaseDir(_) => {} - _ => { - return Err(Errno::EACCES); - } - } - } - #[cfg(not(feature = "abi-7-16"))] - { - match op { - // Only allow operations that the kernel may issue without a uid set - ll::Operation::Init(_) - | ll::Operation::Destroy(_) - | ll::Operation::Read(_) - | ll::Operation::ReadDir(_) - | ll::Operation::Forget(_) - | ll::Operation::Write(_) - | ll::Operation::FSync(_) - | ll::Operation::FSyncDir(_) - | ll::Operation::Release(_) - | ll::Operation::ReleaseDir(_) => {} - _ => { - return Err(Errno::EACCES); - } - } - } - } - match op { - // Filesystem initialization - ll::Operation::Init(x) => { - // We don't support ABI versions before 7.6 - let v = x.version(); - if v < ll::Version(7, 6) { - error!("Unsupported FUSE ABI version {}", v); - return Err(Errno::EPROTO); - } - // Remember ABI version supported by kernel - se.proto_major.store(v.major(), Ordering::SeqCst); - se.proto_minor.store(v.minor(), Ordering::SeqCst); - - let mut config = KernelConfig::new(x.capabilities(), x.max_readahead()); - // Call filesystem init method and give it a chance to return an error - se.filesystem - .init(self, &mut config) - .map_err(Errno::from_i32)?; - - // Reply with our desired version and settings. If the kernel supports a - // larger major version, it'll re-send a matching init message. If it - // supports only lower major versions, we replied with an error above. - debug!( - "INIT response: ABI {}.{}, flags {:#x}, max readahead {}, max write {}", - abi::FUSE_KERNEL_VERSION, - abi::FUSE_KERNEL_MINOR_VERSION, - x.capabilities() & config.requested, - config.max_readahead, - config.max_write - ); - se.initialized.store(true, Ordering::SeqCst); - return Ok(Some(x.reply(&config))); - } - // Any operation is invalid before initialization - _ if !se.initialized.load(Ordering::SeqCst) => { - warn!("Ignoring FUSE operation before init: {}", self.request); - return Err(Errno::EIO); - } - // Filesystem destroyed - ll::Operation::Destroy(x) => { - if !se.destroyed.swap(true, Ordering::SeqCst) { - se.filesystem.destroy(); - } - return Ok(Some(x.reply())); - } - // Any operation is invalid after destroy - _ if se.destroyed.load(Ordering::SeqCst) => { - warn!("Ignoring FUSE operation after destroy: {}", self.request); - return Err(Errno::EIO); - } - - ll::Operation::Interrupt(_) => { - // TODO: handle FUSE_INTERRUPT - return Err(Errno::ENOSYS); - } - - ll::Operation::Lookup(x) => { - se.filesystem.lookup( - self, - self.request.nodeid().into(), - x.name().as_ref(), - self.reply(), - ); - } - ll::Operation::Forget(x) => { - se.filesystem - .forget(self, self.request.nodeid().into(), x.nlookup()); // no reply - } - ll::Operation::GetAttr(_attr) => { - #[cfg(feature = "abi-7-9")] - se.filesystem.getattr( - self, - self.request.nodeid().into(), - _attr.file_handle().map(|fh| fh.into()), - self.reply(), - ); - - // Pre-abi-7-9 does not support providing a file handle. - #[cfg(not(feature = "abi-7-9"))] - se.filesystem - .getattr(self, self.request.nodeid().into(), None, self.reply()); - } - ll::Operation::SetAttr(x) => { - se.filesystem.setattr( - self, - self.request.nodeid().into(), - x.mode(), - x.uid(), - x.gid(), - x.size(), - x.atime(), - x.mtime(), - x.ctime(), - x.file_handle().map(|fh| fh.into()), - x.crtime(), - x.chgtime(), - x.bkuptime(), - x.flags(), - self.reply(), - ); - } - ll::Operation::ReadLink(_) => { - se.filesystem - .readlink(self, self.request.nodeid().into(), self.reply()); - } - ll::Operation::MkNod(x) => { - se.filesystem.mknod( - self, - self.request.nodeid().into(), - x.name().as_ref(), - x.mode(), - x.umask(), - x.rdev(), - self.reply(), - ); - } - ll::Operation::MkDir(x) => { - se.filesystem.mkdir( - self, - self.request.nodeid().into(), - x.name().as_ref(), - x.mode(), - x.umask(), - self.reply(), - ); - } - ll::Operation::Unlink(x) => { - se.filesystem.unlink( - self, - self.request.nodeid().into(), - x.name().as_ref(), - self.reply(), - ); - } - ll::Operation::RmDir(x) => { - se.filesystem.rmdir( - self, - self.request.nodeid().into(), - x.name().as_ref(), - self.reply(), - ); - } - ll::Operation::SymLink(x) => { - se.filesystem.symlink( - self, - self.request.nodeid().into(), - x.link_name().as_ref(), - Path::new(x.target()), - self.reply(), - ); - } - ll::Operation::Rename(x) => { - se.filesystem.rename( - self, - self.request.nodeid().into(), - x.src().name.as_ref(), - x.dest().dir.into(), - x.dest().name.as_ref(), - 0, - self.reply(), - ); - } - ll::Operation::Link(x) => { - se.filesystem.link( - self, - x.inode_no().into(), - self.request.nodeid().into(), - x.dest().name.as_ref(), - self.reply(), - ); - } - ll::Operation::Open(x) => { - se.filesystem - .open(self, self.request.nodeid().into(), x.flags(), self.reply()); - } - ll::Operation::Read(x) => { - se.filesystem.read( - self, - self.request.nodeid().into(), - x.file_handle().into(), - x.offset(), - x.size(), - x.flags(), - x.lock_owner().map(|l| l.into()), - self.reply(), - ); - } - ll::Operation::Write(x) => { - se.filesystem.write( - self, - self.request.nodeid().into(), - x.file_handle().into(), - x.offset(), - x.data(), - x.write_flags(), - x.flags(), - x.lock_owner().map(|l| l.into()), - self.reply(), - ); - } - ll::Operation::Flush(x) => { - se.filesystem.flush( - self, - self.request.nodeid().into(), - x.file_handle().into(), - x.lock_owner().into(), - self.reply(), - ); - } - ll::Operation::Release(x) => { - se.filesystem.release( - self, - self.request.nodeid().into(), - x.file_handle().into(), - x.flags(), - x.lock_owner().map(|x| x.into()), - x.flush(), - self.reply(), - ); - } - ll::Operation::FSync(x) => { - se.filesystem.fsync( - self, - self.request.nodeid().into(), - x.file_handle().into(), - x.fdatasync(), - self.reply(), - ); - } - ll::Operation::OpenDir(x) => { - se.filesystem - .opendir(self, self.request.nodeid().into(), x.flags(), self.reply()); - } - ll::Operation::ReadDir(x) => { - se.filesystem.readdir( - self, - self.request.nodeid().into(), - x.file_handle().into(), - x.offset(), - ReplyDirectory::new( - self.request.unique().into(), - self.ch.clone(), - x.size() as usize, - ), - ); - } - ll::Operation::ReleaseDir(x) => { - se.filesystem.releasedir( - self, - self.request.nodeid().into(), - x.file_handle().into(), - x.flags(), - self.reply(), - ); - } - ll::Operation::FSyncDir(x) => { - se.filesystem.fsyncdir( - self, - self.request.nodeid().into(), - x.file_handle().into(), - x.fdatasync(), - self.reply(), - ); - } - ll::Operation::StatFs(_) => { - se.filesystem - .statfs(self, self.request.nodeid().into(), self.reply()); - } - ll::Operation::SetXAttr(x) => { - se.filesystem.setxattr( - self, - self.request.nodeid().into(), - x.name(), - x.value(), - x.flags(), - x.position(), - self.reply(), - ); - } - ll::Operation::GetXAttr(x) => { - se.filesystem.getxattr( - self, - self.request.nodeid().into(), - x.name(), - x.size_u32(), - self.reply(), - ); - } - ll::Operation::ListXAttr(x) => { - se.filesystem - .listxattr(self, self.request.nodeid().into(), x.size(), self.reply()); - } - ll::Operation::RemoveXAttr(x) => { - se.filesystem.removexattr( - self, - self.request.nodeid().into(), - x.name(), - self.reply(), - ); - } - ll::Operation::Access(x) => { - se.filesystem - .access(self, self.request.nodeid().into(), x.mask(), self.reply()); - } - ll::Operation::Create(x) => { - se.filesystem.create( - self, - self.request.nodeid().into(), - x.name().as_ref(), - x.mode(), - x.umask(), - x.flags(), - self.reply(), - ); - } - ll::Operation::GetLk(x) => { - se.filesystem.getlk( - self, - self.request.nodeid().into(), - x.file_handle().into(), - x.lock_owner().into(), - x.lock().range.0, - x.lock().range.1, - x.lock().typ, - x.lock().pid, - self.reply(), - ); - } - ll::Operation::SetLk(x) => { - se.filesystem.setlk( - self, - self.request.nodeid().into(), - x.file_handle().into(), - x.lock_owner().into(), - x.lock().range.0, - x.lock().range.1, - x.lock().typ, - x.lock().pid, - false, - self.reply(), - ); - } - ll::Operation::SetLkW(x) => { - se.filesystem.setlk( - self, - self.request.nodeid().into(), - x.file_handle().into(), - x.lock_owner().into(), - x.lock().range.0, - x.lock().range.1, - x.lock().typ, - x.lock().pid, - true, - self.reply(), - ); - } - ll::Operation::BMap(x) => { - se.filesystem.bmap( - self, - self.request.nodeid().into(), - x.block_size(), - x.block(), - self.reply(), - ); - } - - #[cfg(feature = "abi-7-11")] - ll::Operation::IoCtl(x) => { - if x.unrestricted() { - return Err(Errno::ENOSYS); - } else { - se.filesystem.ioctl( - self, - self.request.nodeid().into(), - x.file_handle().into(), - x.flags(), - x.command(), - x.in_data(), - x.out_size(), - self.reply(), - ); - } - } - #[cfg(feature = "abi-7-11")] - ll::Operation::Poll(x) => { - let ph = PollHandle::new(se.ch.sender(), x.kernel_handle()); - - se.filesystem.poll( - self, - self.request.nodeid().into(), - x.file_handle().into(), - ph, - x.events(), - x.flags(), - self.reply(), - ); - } - #[cfg(feature = "abi-7-15")] - ll::Operation::NotifyReply(_) => { - // TODO: handle FUSE_NOTIFY_REPLY - return Err(Errno::ENOSYS); - } - #[cfg(feature = "abi-7-16")] - ll::Operation::BatchForget(x) => { - se.filesystem.batch_forget(self, x.nodes()); // no reply - } - #[cfg(feature = "abi-7-19")] - ll::Operation::FAllocate(x) => { - se.filesystem.fallocate( - self, - self.request.nodeid().into(), - x.file_handle().into(), - x.offset(), - x.len(), - x.mode(), - self.reply(), - ); - } - #[cfg(feature = "abi-7-21")] - ll::Operation::ReadDirPlus(x) => { - se.filesystem.readdirplus( - self, - self.request.nodeid().into(), - x.file_handle().into(), - x.offset(), - ReplyDirectoryPlus::new( - self.request.unique().into(), - self.ch.clone(), - x.size() as usize, - ), - ); - } - #[cfg(feature = "abi-7-23")] - ll::Operation::Rename2(x) => { - se.filesystem.rename( - self, - x.from().dir.into(), - x.from().name.as_ref(), - x.to().dir.into(), - x.to().name.as_ref(), - x.flags(), - self.reply(), - ); - } - #[cfg(feature = "abi-7-24")] - ll::Operation::Lseek(x) => { - se.filesystem.lseek( - self, - self.request.nodeid().into(), - x.file_handle().into(), - x.offset(), - x.whence(), - self.reply(), - ); - } - #[cfg(feature = "abi-7-28")] - ll::Operation::CopyFileRange(x) => { - let (i, o) = (x.src(), x.dest()); - se.filesystem.copy_file_range( - self, - i.inode.into(), - i.file_handle.into(), - i.offset, - o.inode.into(), - o.file_handle.into(), - o.offset, - x.len(), - x.flags().try_into().unwrap(), - self.reply(), - ); - } - #[cfg(target_os = "macos")] - ll::Operation::SetVolName(x) => { - se.filesystem.setvolname(self, x.name(), self.reply()); - } - #[cfg(target_os = "macos")] - ll::Operation::GetXTimes(x) => { - se.filesystem - .getxtimes(self, x.nodeid().into(), self.reply()); - } - #[cfg(target_os = "macos")] - ll::Operation::Exchange(x) => { - se.filesystem.exchange( - self, - x.from().dir.into(), - x.from().name.as_ref(), - x.to().dir.into(), - x.to().name.as_ref(), - x.options(), - self.reply(), - ); - } - - #[cfg(feature = "abi-7-12")] - ll::Operation::CuseInit(_) => { - // TODO: handle CUSE_INIT - return Err(Errno::ENOSYS); - } - } - Ok(None) - } - - /// Create a reply object for this request that can be passed to the filesystem - /// implementation and makes sure that a request is replied exactly once - fn reply(&self) -> T { - Reply::new(self.request.unique().into(), self.ch.clone()) - } - - /// Returns the unique identifier of this request - #[inline] - pub fn unique(&self) -> u64 { - self.request.unique().into() - } - - /// Returns the uid of this request - #[inline] - pub fn uid(&self) -> u32 { - self.request.uid() - } - - /// Returns the gid of this request - #[inline] - pub fn gid(&self) -> u32 { - self.request.gid() - } - - /// Returns the pid of this request - #[inline] - pub fn pid(&self) -> u32 { - self.request.pid() - } - - /// Returns whether this is a forget request - pub fn is_forget(&self) -> bool { - match self.request.opcode() { - Ok(abi::fuse_opcode::FUSE_FORGET) => true, - #[cfg(feature = "abi-7-16")] - Ok(abi::fuse_opcode::FUSE_BATCH_FORGET) => true, - _ => false, - } - } -} diff --git a/vendor/fuser/src/session.rs b/vendor/fuser/src/session.rs deleted file mode 100644 index 86daed561..000000000 --- a/vendor/fuser/src/session.rs +++ /dev/null @@ -1,310 +0,0 @@ -//! Filesystem session -//! -//! A session runs a filesystem implementation while it is being mounted to a specific mount -//! point. A session begins by mounting the filesystem and ends by unmounting it. While the -//! filesystem is mounted, the session loop receives, dispatches and replies to kernel requests -//! for filesystem operations under its mount point. - -use libc::{EAGAIN, EINTR, ENODEV, ENOENT}; -use log::{info, warn}; -use nix::unistd::geteuid; -use std::fmt; -use std::os::fd::{AsFd, BorrowedFd, OwnedFd}; -use std::path::{Path, PathBuf}; -use std::sync::atomic::{AtomicU32, AtomicBool, Ordering}; -use std::sync::{Arc, Mutex}; -use std::thread::{self, JoinHandle}; -use std::{io, ops::DerefMut}; - -use crate::ll::fuse_abi as abi; -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 -/// and 128k on other systems. -pub const MAX_WRITE_SIZE: usize = 16 * 1024 * 1024; - -/// Size of the buffer for reading a request from the kernel. Since the kernel may send -/// up to MAX_WRITE_SIZE bytes in a write request, we use that value plus some extra space. -const BUFFER_SIZE: usize = MAX_WRITE_SIZE + 4096; - -#[derive(Default, Debug, Eq, PartialEq)] -/// How requests should be filtered based on the calling UID. -pub enum SessionACL { - /// Allow requests from any user. Corresponds to the `allow_other` mount option. - All, - /// Allow requests from root. Corresponds to the `allow_root` mount option. - RootAndOwner, - /// Allow requests from the owning UID. This is FUSE's default mode of operation. - #[default] - Owner, -} - -/// The session data structure -#[derive(Debug)] -pub struct Session { - /// Filesystem operation implementations - pub(crate) filesystem: FS, - /// Communication channel to the kernel driver - pub(crate) ch: Channel, - /// Handle to the mount. Dropping this unmounts. - mount: Arc>>, - /// Whether to restrict access to owner, root + owner, or unrestricted - /// Used to implement allow_root and auto_unmount - pub(crate) allowed: SessionACL, - /// User that launched the fuser process - pub(crate) session_owner: u32, - /// FUSE protocol major version - pub(crate) proto_major: AtomicU32, - /// FUSE protocol minor version - pub(crate) proto_minor: AtomicU32, - /// True if the filesystem is initialized (init operation done) - pub(crate) initialized: AtomicBool, - /// True if the filesystem was destroyed (destroy operation done) - pub(crate) destroyed: AtomicBool, -} - -impl AsFd for Session { - fn as_fd(&self) -> BorrowedFd<'_> { - self.ch.as_fd() - } -} - -impl Session { - /// Create a new session by mounting the given filesystem to the given mountpoint - pub fn new>( - filesystem: FS, - mountpoint: P, - options: &[MountOption], - ) -> io::Result> { - let mountpoint = mountpoint.as_ref(); - info!("Mounting {}", mountpoint.display()); - // If AutoUnmount is requested, but not AllowRoot or AllowOther we enforce the ACL - // ourself and implicitly set AllowOther because fusermount needs allow_root or allow_other - // to handle the auto_unmount option - let (file, mount) = if options.contains(&MountOption::AutoUnmount) - && !(options.contains(&MountOption::AllowRoot) - || options.contains(&MountOption::AllowOther)) - { - warn!("Given auto_unmount without allow_root or allow_other; adding allow_other, with userspace permission handling"); - let mut modified_options = options.to_vec(); - modified_options.push(MountOption::AllowOther); - Mount::new(mountpoint, &modified_options)? - } else { - Mount::new(mountpoint, options)? - }; - - let ch = Channel::new(file); - let allowed = if options.contains(&MountOption::AllowRoot) { - SessionACL::RootAndOwner - } else if options.contains(&MountOption::AllowOther) { - SessionACL::All - } else { - SessionACL::Owner - }; - - Ok(Session { - filesystem, - ch, - mount: Arc::new(Mutex::new(Some((mountpoint.to_owned(), mount)))), - allowed, - session_owner: geteuid().as_raw(), - proto_major: AtomicU32::new(0), - proto_minor: AtomicU32::new(0), - initialized: AtomicBool::new(false), - destroyed: AtomicBool::new(false), - }) - } - - /// Wrap an existing /dev/fuse file descriptor. This doesn't mount the - /// filesystem anywhere; that must be done separately. - pub fn from_fd(filesystem: FS, fd: OwnedFd, acl: SessionACL) -> Self { - let ch = Channel::new(Arc::new(fd.into())); - Session { - filesystem, - ch, - mount: Arc::new(Mutex::new(None)), - allowed: acl, - session_owner: geteuid().as_raw(), - proto_major: AtomicU32::new(0), - proto_minor: AtomicU32::new(0), - initialized: AtomicBool::new(false), - destroyed: AtomicBool::new(false), - } - } - - /// Run the session loop that receives kernel requests and dispatches them to method - /// calls into the filesystem. - pub fn run(&self) -> io::Result<()> { - self.run_with_callbacks(|_| {}, |_| {}) - } - - /// Run the session loop that receives kernel requests and dispatches them to method - /// calls into the filesystem. - /// This version also notifies callers of kernel requests before and after they - /// are dispatched to the filesystem. - pub fn run_with_callbacks(&self, mut before_dispatch: FB, mut after_dispatch: FA) -> io::Result<()> - where - FB: FnMut(&Request<'_>), - FA: FnMut(&Request<'_>), - { - // Buffer for receiving requests from the kernel. Only one is allocated and - // it is reused immediately after dispatching to conserve memory and allocations. - let mut buffer = vec![0; BUFFER_SIZE]; - let buf = aligned_sub_buf( - buffer.deref_mut(), - std::mem::align_of::(), - ); - loop { - // Read the next request from the given channel to kernel driver - // The kernel driver makes sure that we get exactly one request per read - match self.ch.receive(buf) { - Ok(size) => match Request::new(self.ch.sender(), &buf[..size]) { - // Dispatch request - Some(req) => { - before_dispatch(&req); - req.dispatch(self); - after_dispatch(&req); - }, - // Quit loop on illegal request - None => break, - }, - Err(err) => match err.raw_os_error() { - // Operation interrupted. Accordingly to FUSE, this is safe to retry - Some(ENOENT) => continue, - // Interrupted system call, retry - Some(EINTR) => continue, - // Explicitly try again - Some(EAGAIN) => continue, - // Filesystem was unmounted, quit the loop - Some(ENODEV) => break, - // Unhandled error - _ => return Err(err), - }, - } - } - Ok(()) - } - - /// Unmount the filesystem - pub fn unmount(&mut self) { - drop(std::mem::take(&mut *self.mount.lock().unwrap())); - } - - /// Returns a thread-safe object that can be used to unmount the Filesystem - pub fn unmount_callable(&mut self) -> SessionUnmounter { - SessionUnmounter { - 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)] -/// A thread-safe object that can be used to unmount a Filesystem -pub struct SessionUnmounter { - mount: Arc>>, -} - -impl SessionUnmounter { - /// Unmount the filesystem - pub fn unmount(&mut self) -> io::Result<()> { - drop(std::mem::take(&mut *self.mount.lock().unwrap())); - Ok(()) - } -} - -fn aligned_sub_buf(buf: &mut [u8], alignment: usize) -> &mut [u8] { - let off = alignment - (buf.as_ptr() as usize) % alignment; - if off == alignment { - buf - } else { - &mut buf[off..] - } -} - -impl Session { - /// Run the session loop in a background thread - pub fn spawn(self) -> io::Result { - BackgroundSession::new(self) - } -} - -impl Drop for Session { - fn drop(&mut self) { - if !self.destroyed.swap(true, Ordering::SeqCst) { - self.filesystem.destroy(); - } - - if let Some((mountpoint, _mount)) = std::mem::take(&mut *self.mount.lock().unwrap()) { - info!("unmounting session at {}", mountpoint.display()); - } - } -} - -/// The background session data structure -pub struct BackgroundSession { - /// 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: Option, -} - -impl BackgroundSession { - /// Create a new background session for the given session by running its - /// session loop in a background thread. If the returned handle is dropped, - /// the filesystem is unmounted and the given session ends. - pub fn new(se: Session) -> io::Result { - #[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()).map(|(_, mount)| mount); - let guard = thread::spawn(move || { - se.run() - }); - Ok(BackgroundSession { - guard, - #[cfg(feature = "abi-7-11")] - sender, - _mount: mount, - }) - } - /// Unmount the filesystem and join the background thread. - pub fn join(self) { - let Self { - 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 -// thread_scoped::JoinGuard -impl fmt::Debug for BackgroundSession { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "BackgroundSession {{ guard: JoinGuard<()> }}",) - } -} diff --git a/vendor/fuser/tests/integration_tests.rs b/vendor/fuser/tests/integration_tests.rs deleted file mode 100644 index 0bbc84517..000000000 --- a/vendor/fuser/tests/integration_tests.rs +++ /dev/null @@ -1,28 +0,0 @@ -// No integration tests for non-Linux targets, so turn off the module for now. -#![cfg(target_os = "linux")] - -use fuser::{Filesystem, Session}; -use std::rc::Rc; -use std::thread; -use std::time::Duration; -use tempfile::TempDir; - -#[test] -#[cfg(target_os = "linux")] -fn unmount_no_send() { - struct NoSendFS( - // Rc to make this !Send - #[allow(dead_code)] Rc<()>, - ); - - impl Filesystem for NoSendFS {} - - let tmpdir: TempDir = tempfile::tempdir().unwrap(); - let mut session = Session::new(NoSendFS(Rc::new(())), tmpdir.path(), &[]).unwrap(); - let mut unmounter = session.unmount_callable(); - thread::spawn(move || { - thread::sleep(Duration::from_secs(1)); - unmounter.unmount().unwrap(); - }); - session.run().unwrap(); -} diff --git a/vendor/fuser/xfstests.Dockerfile b/vendor/fuser/xfstests.Dockerfile deleted file mode 100644 index 1cafecc10..000000000 --- a/vendor/fuser/xfstests.Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM ubuntu:20.04 - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt update && apt install -y git build-essential autoconf curl cmake libfuse-dev pkg-config fuse bc libtool \ - uuid-dev xfslibs-dev libattr1-dev libacl1-dev libaio-dev attr acl quota bsdmainutils dbench psmisc - -RUN adduser --disabled-password --gecos '' fsgqa - -RUN echo 'user_allow_other' >> /etc/fuse.conf - -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 - -RUN mkdir -p /code && cd /code && git clone https://github.com/fleetfs/fuse-xfstests && cd fuse-xfstests \ - && git checkout c123d014fcca48cf340be78d6712eff80ee4e8d6 && make - -ADD . /code/fuser/ - -RUN cd /code/fuser && cargo build --release --examples --features=abi-7-31 && cp target/release/examples/simple /bin/fuser diff --git a/vendor/fuser/xfstests.sh b/vendor/fuser/xfstests.sh deleted file mode 100755 index dd894bf82..000000000 --- a/vendor/fuser/xfstests.sh +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -exit_handler() { - exit "$XFSTESTS_EXIT_STATUS" -} -trap exit_handler TERM -trap "kill 0" INT EXIT - -export RUST_BACKTRACE=1 - -TEST_DATA_DIR=$(mktemp --directory) -SCRATCH_DATA_DIR=$(mktemp --directory) -TEST_DIR=$(mktemp --directory) -SCRATCH_DIR=$(mktemp --directory) - -set +e -# Clear mount log file, since the tests append to it -echo "" > /code/logs/xfstests_mount.log -DIR=/var/tmp/fuse-xfstests/check-fuser -mkdir -p $DIR -cd /code/fuse-xfstests - -# requires OFD & POSIX locks. OFD locks are not supported by fuse -echo "generic/478" >> xfs_excludes.txt - -# TODO: requires supporting orphaned files, that have an open file handle, but no links -echo "generic/484" >> xfs_excludes.txt - -# Writes directly to scratch block dev -echo "generic/062" >> xfs_excludes.txt - -# TODO: looks like it requires character file support -echo "generic/078" >> xfs_excludes.txt - -# TODO: takes > 10min -echo "generic/069" >> xfs_excludes.txt - -# TODO: needs fallocate which is missing from Linux FUSE driver (https://github.com/libfuse/libfuse/issues/395) -echo "generic/263" >> xfs_excludes.txt - -# TODO: Passes, but takes ~30min -echo "generic/127" >> xfs_excludes.txt - -# TODO: requires more complete falloc support. Also fills up the entire hard disk... -echo "generic/103" >> xfs_excludes.txt - -# TODO: requires support for mknod on character files -echo "generic/184" >> xfs_excludes.txt -echo "generic/401" >> xfs_excludes.txt - -# TODO: requires fifo support -echo "generic/423" >> xfs_excludes.txt -echo "generic/434" >> xfs_excludes.txt - -# TODO: requires ulimit support for limiting file size -echo "generic/394" >> xfs_excludes.txt - -# requires BSD lock support, and checks /proc/locks. fuse locks don't seem to show up in /proc/locks -echo "generic/504" >> xfs_excludes.txt - -# TODO: requires support for system.posix_acl_access xattr sync'ing to file permissions -# Some information about it linked from here: https://stackoverflow.com/questions/29569408/documentation-of-posix-acl-access-and-friends -echo "generic/099" >> xfs_excludes.txt -echo "generic/105" >> xfs_excludes.txt -echo "generic/375" >> xfs_excludes.txt - -# TODO: requires support for mounting read-only -echo "generic/294" >> xfs_excludes.txt -echo "generic/306" >> xfs_excludes.txt -echo "generic/452" >> xfs_excludes.txt - -# TODO: requires atime support -echo "generic/003" >> xfs_excludes.txt -echo "generic/192" >> xfs_excludes.txt - -# TODO: Passes, but takes ~10min and writes > 20GB. Needs support for writing files with large holes, -# for this test to be fast -echo "generic/130" >> xfs_excludes.txt - -# TODO: uses namespaces and inodes don't seem to get mapped properly -# this test ends up trying to chmod "/" (the root inode) -echo "generic/317" >> xfs_excludes.txt - -# TODO: requires more complete ACL support -echo "generic/319" >> xfs_excludes.txt -echo "generic/444" >> xfs_excludes.txt - -# TODO: Seems to cause a host OOM (even from inside Docker), when run with 84, 87, 88, 100, and 109 -echo "generic/089" >> xfs_excludes.txt - -# TODO: very slow. Passes, but takes > 30min -echo "generic/074" >> xfs_excludes.txt - -# TODO: very slow. Ran for > 3hrs without completing -echo "generic/339" >> xfs_excludes.txt - -# TODO: Passes, but takes ~60min on CI -echo "generic/006" >> xfs_excludes.txt -echo "generic/011" >> xfs_excludes.txt -echo "generic/070" >> xfs_excludes.txt - -# TODO: very slow. Passes, but takes 20min -echo "generic/438" >> xfs_excludes.txt - -# TODO: seems to crash host -echo "generic/476" >> xfs_excludes.txt - -# TODO: writing to /proc/sys/vm/drop_caches is not allowed inside Docker -echo "generic/086" >> xfs_excludes.txt -echo "generic/391" >> xfs_excludes.txt -echo "generic/426" >> xfs_excludes.txt -echo "generic/467" >> xfs_excludes.txt -echo "generic/477" >> xfs_excludes.txt - -# TODO: permission failure invoking FIBMAP -echo "generic/519" >> xfs_excludes.txt - -# TODO: Tries to create 50k+ files, which OOMs -echo "generic/531" >> xfs_excludes.txt - -# Test requires mounting a loopback device -echo "generic/564" >> xfs_excludes.txt - - -FUSER_EXTRA_MOUNT_OPTIONS="" TEST_DEV="$TEST_DATA_DIR" TEST_DIR="$TEST_DIR" SCRATCH_DEV="$SCRATCH_DATA_DIR" SCRATCH_MNT="$SCRATCH_DIR" \ -./check-fuser -E xfs_excludes.txt "$@" \ -| tee /code/logs/xfstests.log - -export XFSTESTS_EXIT_STATUS=${PIPESTATUS[0]} - -if [ $XFSTESTS_EXIT_STATUS ] -then - cat /code/fuse-xfstests/results/generic/*.bad - cp /code/fuse-xfstests/results/generic/*.bad /code/logs/ -fi - -rm -rf ${TEST_DATA_DIR} -rm -rf ${TEST_DIR} -rm -rf ${SCRATCH_DATA_DIR} -rm -rf ${SCRATCH_DIR}