diff --git a/.cirrus.yml b/.cirrus.yml deleted file mode 100644 index 2c3bae7..0000000 --- a/.cirrus.yml +++ /dev/null @@ -1,59 +0,0 @@ -build: &BUILD - cargo_cache: - folder: $HOME/.cargo/registry - fingerprint_script: cat Cargo.lock || echo "" - build_script: - - . $HOME/.cargo/env || true - - rustup component add clippy - - cargo check --all-targets --features=tokio-runtime,file-lock,unprivileged - - cargo check --all-targets --features=async-io-runtime,file-lock,unprivileged - # - RUSTDOCFLAGS="--cfg docsrs" cargo doc --features=file-lock,unprivileged,tokio-runtime # disable until doc_cfg and doc_auto_cfg are stable - - cargo doc --features=file-lock,unprivileged,tokio-runtime - - cargo clippy --all-targets --features=tokio-runtime,file-lock,unprivileged - - cargo clippy --all-targets --features=async-io-runtime,file-lock,unprivileged - before_cache_script: rm -rf $HOME/.cargo/registry/index - - -task: - name: FreeBSD - freebsd_instance: - image: freebsd-14-3-release-amd64-ufs - setup_script: - - fetch https://sh.rustup.rs -o rustup.sh - - sh rustup.sh -y --profile=minimal - - . $HOME/.cargo/env - << : *BUILD - -task: - name: MacOS - macos_instance: - image: ghcr.io/cirruslabs/macos-sonoma-base:latest - setup_script: - - curl https://sh.rustup.rs -o rustup.sh - - sh rustup.sh -y --profile=minimal - - . $HOME/.cargo/env - << : *BUILD - -task: - name: Linux - container: - image: rust:latest - << : *BUILD - -minver_task: - depends_on: - - FreeBSD - - Linux - - MacOS - freebsd_instance: - image: freebsd-14-3-release-amd64-ufs - setup_script: - - pkg install -y ca_root_nss - - fetch https://sh.rustup.rs -o rustup.sh - - sh rustup.sh -y --default-toolchain nightly --profile=minimal - - . $HOME/.cargo/env - test_script: - - . $HOME/.cargo/env || true - - cargo update -Zdirect-minimal-versions - - cargo check --all-targets --features=tokio-runtime,file-lock,unprivileged - - cargo check --all-targets --features=async-io-runtime,file-lock,unprivileged diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..fdae6ed --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,100 @@ +name: CI + +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + +env: + CARGO_TERM_COLOR: always + +jobs: + linux: + name: Linux + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + components: clippy + + - name: Cache cargo + uses: Swatinem/rust-cache@v2 + + - name: Clippy (tokio-runtime) + run: cargo clippy --all-targets --features=tokio-runtime,file-lock,unprivileged + + - name: Clippy (async-io-runtime) + run: cargo clippy --all-targets --features=async-io-runtime,file-lock,unprivileged + + - name: Doc + run: cargo doc --features=file-lock,unprivileged,tokio-runtime + + macos: + name: MacOS + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + components: clippy + + - name: Cache cargo + uses: Swatinem/rust-cache@v2 + + - name: Clippy (tokio-runtime) + run: cargo clippy --all-targets --features=tokio-runtime,file-lock,unprivileged + + - name: Clippy (async-io-runtime) + run: cargo clippy --all-targets --features=async-io-runtime,file-lock,unprivileged + + - name: Doc + run: cargo doc --features=file-lock,unprivileged,tokio-runtime + + freebsd: + name: FreeBSD + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Build on FreeBSD + uses: vmactions/freebsd-vm@v1 + with: + release: "15.0" + usesh: true + prepare: | + pkg install -y curl + fetch https://sh.rustup.rs -o rustup.sh + sh rustup.sh -y --profile=minimal + run: | + . $HOME/.cargo/env || true + rustup component add clippy + cargo clippy --all-targets --features=tokio-runtime,file-lock,unprivileged + cargo clippy --all-targets --features=async-io-runtime,file-lock,unprivileged + cargo doc --features=file-lock,unprivileged,tokio-runtime + + minver: + name: MinVer + runs-on: ubuntu-latest + needs: [linux, macos, freebsd] + steps: + - uses: actions/checkout@v4 + + - name: Install Rust (nightly) + uses: dtolnay/rust-toolchain@nightly + with: + components: clippy + + - name: Cache cargo + uses: Swatinem/rust-cache@v2 + + - name: MinVer check + run: | + cargo update -Zdirect-minimal-versions + cargo clippy --all-targets --features=tokio-runtime,file-lock,unprivileged + cargo clippy --all-targets --features=async-io-runtime,file-lock,unprivileged diff --git a/.gitignore b/.gitignore index bd787a3..185ca35 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ /target -.idea \ No newline at end of file +.idea diff --git a/.rustfmt.toml b/.rustfmt.toml index dd3bd0d..f8449af 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,2 +1,2 @@ -edition = "2021" +edition = "2024" group_imports = "StdExternalCrate" diff --git a/Cargo.lock b/Cargo.lock index 3658dd5..d57a93d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,21 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] -name = "addr2line" -version = "0.25.1" +name = "anyhow" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "async-channel" @@ -24,16 +15,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", - "event-listener-strategy 0.5.4", + "event-listener-strategy", "futures-core", "pin-project-lite", ] [[package]] name = "async-executor" -version = "1.13.3" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" dependencies = [ "async-task", "concurrent-queue", @@ -45,9 +36,9 @@ dependencies = [ [[package]] name = "async-fs" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1" +checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" dependencies = [ "async-lock", "blocking", @@ -56,9 +47,9 @@ dependencies = [ [[package]] name = "async-global-executor" -version = "2.4.1" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +checksum = "13f937e26114b93193065fd44f507aa2e9169ad0cdabbb996920b1fe1ddea7ba" dependencies = [ "async-channel", "async-executor", @@ -66,66 +57,64 @@ dependencies = [ "async-lock", "blocking", "futures-lite", - "once_cell", ] [[package]] name = "async-io" -version = "2.3.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" dependencies = [ - "async-lock", + "autocfg", "cfg-if", "concurrent-queue", "futures-io", "futures-lite", "parking", "polling", - "rustix 0.38.44", + "rustix", "slab", - "tracing", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "async-lock" -version = "3.3.0" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" dependencies = [ - "event-listener 4.0.3", - "event-listener-strategy 0.4.0", + "event-listener", + "event-listener-strategy", "pin-project-lite", ] [[package]] name = "async-notify" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db96c36382f56ea91e5d012c4c7170046c7731449daa46655cbcabc594c8bee6" +checksum = "b31189a260b12d7159e91b74d0b94bb0be3705237082709611b53342b2fb65d7" dependencies = [ - "event-listener 4.0.3", + "event-listener", "futures-core", "pin-project-lite", ] [[package]] name = "async-process" -version = "2.1.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451e3cf68011bd56771c79db04a9e333095ab6349f7e47592b788e9b98720cc8" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" dependencies = [ "async-channel", "async-io", "async-lock", "async-signal", + "async-task", "blocking", "cfg-if", - "event-listener 5.4.1", + "event-listener", "futures-lite", - "rustix 0.38.44", - "windows-sys 0.52.0", + "rustix", ] [[package]] @@ -140,7 +129,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 1.0.8", + "rustix", "signal-hook-registry", "slab", "windows-sys 0.61.2", @@ -164,46 +153,11 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link", -] - -[[package]] -name = "bincode-next" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a347d3b94606a7b3d588fe1ff7f41996bbb3690ade4e79935b26565af10e03f" -dependencies = [ - "bincode_derive-next", - "serde", - "unty-next", -] - -[[package]] -name = "bincode_derive-next" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197e777c78cc9fbe8396cf82e18f84bb6026b8ec534ebb3097f4d98894e8b540" -dependencies = [ - "virtue-next", -] - [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "blocking" @@ -220,9 +174,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.5.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cfg-if" @@ -252,10 +206,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] -name = "either" -version = "1.15.0" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -267,17 +221,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "event-listener" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - [[package]] name = "event-listener" version = "5.4.1" @@ -289,23 +232,13 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "event-listener-strategy" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" -dependencies = [ - "event-listener 4.0.3", - "pin-project-lite", -] - [[package]] name = "event-listener-strategy" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.1", + "event-listener", "pin-project-lite", ] @@ -330,9 +263,15 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "fuse3" -version = "0.8.1" +version = "0.9.0" dependencies = [ "async-fs", "async-global-executor", @@ -340,9 +279,7 @@ dependencies = [ "async-lock", "async-notify", "async-process", - "bincode-next", "bytes", - "futures-channel", "futures-util", "libc", "nix", @@ -351,29 +288,20 @@ dependencies = [ "tracing", "trait-make", "which", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", + "zerocopy", ] [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -390,9 +318,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", @@ -401,21 +329,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", "futures-io", @@ -424,15 +352,42 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] [[package]] -name = "gimli" -version = "0.32.3" +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -441,14 +396,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] -name = "home" -version = "0.5.12" +name = "id-arena" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ - "windows-sys 0.61.2", + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", ] +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + [[package]] name = "lazy_static" version = "1.5.0" @@ -456,22 +426,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] -name = "libc" -version = "0.2.168" +name = "leb128fmt" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] -name = "linux-raw-sys" -version = "0.4.15" +name = "libc" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "log" @@ -481,9 +451,9 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memoffset" @@ -494,15 +464,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - [[package]] name = "mio" version = "0.8.11" @@ -517,9 +478,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "wasi", @@ -528,9 +489,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.29.0" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" dependencies = [ "bitflags", "cfg-if", @@ -548,15 +509,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -571,21 +523,15 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "piper" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" dependencies = [ "atomic-waker", "fastrand", @@ -602,59 +548,62 @@ dependencies = [ "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.0.8", + "rustix", "windows-sys 0.61.2", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.43" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] -name = "rustc-demangle" -version = "0.1.26" +name = "r-efi" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rustix" -version = "0.38.44" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "linux-raw-sys", + "windows-sys 0.61.2", ] [[package]] -name = "rustix" -version = "1.0.8" +name = "semver" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.60.2", -] +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" @@ -685,6 +634,19 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -706,12 +668,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -721,19 +680,19 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.8" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "syn" -version = "2.0.114" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -742,14 +701,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "cfg-if", "fastrand", - "rustix 0.38.44", - "windows-sys 0.52.0", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.61.2", ] [[package]] @@ -763,26 +723,25 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.0" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" dependencies = [ - "backtrace", "bytes", "libc", - "mio 1.1.0", + "mio 1.1.1", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" dependencies = [ "proc-macro2", "quote", @@ -791,9 +750,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -834,9 +793,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "nu-ansi-term", "sharded-slab", @@ -859,15 +818,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] -name = "unty-next" -version = "0.0.6" +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "169ea32c8f5801e4b397f08d2b5d3d0d3675d11c241aad7338b601beed52a291" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "valuable" @@ -875,12 +834,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" -[[package]] -name = "virtue-next" -version = "0.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4daab4b8a8c3c1408ea5e60a5d380790ff15d0cfe0333c14ce98210c1339fd8" - [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -888,115 +841,103 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "which" -version = "6.0.0" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fa5e0c10bf77f44aac573e498d1a82d5fbd5e91f6fc0a99e7be4b38e85e101c" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.44", - "windows-sys 0.52.0", + "wit-bindgen", ] [[package]] -name = "windows-link" -version = "0.2.1" +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] [[package]] -name = "windows-sys" -version = "0.48.0" +name = "wasm-encoder" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" dependencies = [ - "windows-targets 0.48.5", + "leb128fmt", + "wasmparser", ] [[package]] -name = "windows-sys" -version = "0.52.0" +name = "wasm-metadata" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ - "windows-targets 0.52.6", + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", ] [[package]] -name = "windows-sys" -version = "0.59.0" +name = "wasmparser" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "windows-targets 0.52.6", + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", ] [[package]] -name = "windows-sys" -version = "0.60.2" +name = "which" +version = "8.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +checksum = "81995fafaaaf6ae47a7d0cc83c67caf92aeb7e5331650ae6ff856f7c0c60c459" dependencies = [ - "windows-targets 0.53.5", + "libc", ] [[package]] -name = "windows-sys" -version = "0.61.2" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] -name = "windows-targets" -version = "0.52.6" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows-link", ] [[package]] name = "windows-targets" -version = "0.53.5" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -1005,66 +946,18 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -1072,67 +965,133 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] -name = "windows_i686_msvc" -version = "0.52.6" +name = "windows_x86_64_gnu" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] -name = "windows_i686_msvc" -version = "0.53.1" +name = "windows_x86_64_gnullvm" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] -name = "windows_x86_64_gnu" +name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" +name = "wit-bindgen" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] [[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" +name = "wit-bindgen-core" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] [[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" +name = "wit-bindgen-rust" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] [[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" +name = "wit-bindgen-rust-macro" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] [[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" +name = "wit-component" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +name = "wit-parser" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" +name = "zerocopy" +version = "0.8.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" +dependencies = [ + "zerocopy-derive", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" +name = "zerocopy-derive" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 0c33f34..e15cae8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,15 @@ [package] name = "fuse3" -version = "0.8.1" +version = "0.9.0" authors = ["Sherlock Holo "] -edition = "2021" +edition = "2024" readme = "README.md" keywords = ["fuse", "filesystem", "system", "bindings"] categories = ["api-bindings", "filesystem"] license = "MIT" repository = "https://github.com/Sherlock-Holo/fuse3" description = "FUSE user-space library async version implementation." -rust-version = "1.77" +rust-version = "1.91" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -23,25 +23,24 @@ file-lock = [] unprivileged = ["nix/socket", "dep:which"] [dependencies] -async-fs = { version = "2.1.1", optional = true } -async-global-executor = { version = "2.4.1", optional = true } -async-lock = { version = "3.3.0", optional = true } +async-fs = { version = "2.2.0", optional = true } +async-global-executor = { version = "3.1.0", optional = true } +async-lock = { version = "3.4.2", optional = true } async-notify = "0.3" -async-io = { version = "2.3.1", optional = true } -async-process = { version = "2.1.0", optional = true } -bincode-next = { version = "2.0.4", default-features = false, features = ["derive", "std"] } -bytes = "1.5" -futures-channel = { version = "0.3.30", features = ["sink"] } -futures-util = { version = "0.3.30", features = ["sink"] } -libc = "0.2.168" -nix = { version = "0.29.0", default-features = false, features = ["fs", "mount", "user"] } -slab = "0.4.9" -tracing = "0.1.40" +async-io = { version = "2.6.0", optional = true } +async-process = { version = "2.5.0", optional = true } +bytes = "1.11.1" +futures-util = { version = "0.3.32", features = ["sink"] } +libc = "0.2.183" +nix = { version = "0.31.2", default-features = false, features = ["fs", "mount", "user"] } +slab = "0.4.12" +tracing = "0.1.44" trait-make = "0.1" -which = { version = "6", optional = true } +which = { version = "8.0.2", optional = true } +zerocopy = { version = "0.8.42", features = ["derive", "std"] } [dependencies.tokio] -version = "1.45" +version = "1.50" features = ["fs", "rt", "sync", "net", "macros", "process", "time"] optional = true diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 00e56f0..0542ee9 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -25,11 +25,11 @@ path = "src/path_memfs/main.rs" [dependencies] fuse3 = { path = "../", features = ["tokio-runtime", "unprivileged"] } -libc = "0.2.168" -tokio = { version = "1.45", features = ["macros", "rt", "time", "signal"] } -futures-util = "0.3.31" +libc = "0.2.183" +tokio = { version = "1.50", features = ["macros", "rt", "time", "signal"] } +futures-util = "0.3.32" mio = { version = "0.8.11", features = ["os-poll"] } -tempfile = "3.10" -bytes = "1.5" -tracing = "0.1.40" -tracing-subscriber = "0.3.20" +tempfile = "3.27" +bytes = "1.11.1" +tracing = "0.1.44" +tracing-subscriber = "0.3.22" diff --git a/examples/src/helloworld/main.rs b/examples/src/helloworld/main.rs index 09a2de8..d9e1fab 100644 --- a/examples/src/helloworld/main.rs +++ b/examples/src/helloworld/main.rs @@ -33,9 +33,7 @@ struct HelloWorld; impl Filesystem for HelloWorld { async fn init(&self, _req: Request) -> Result { - Ok(ReplyInit { - max_write: NonZeroU32::new(16 * 1024).unwrap(), - }) + Ok(ReplyInit::new(NonZeroU32::new(16 * 1024).unwrap())) } async fn destroy(&self, _req: Request) {} diff --git a/examples/src/memfs/main.rs b/examples/src/memfs/main.rs index 2d69d54..6577b5b 100644 --- a/examples/src/memfs/main.rs +++ b/examples/src/memfs/main.rs @@ -198,9 +198,7 @@ impl Default for Fs { impl Filesystem for Fs { async fn init(&self, _req: Request) -> Result { - Ok(ReplyInit { - max_write: NonZeroU32::new(16 * 1024).unwrap(), - }) + Ok(ReplyInit::new(NonZeroU32::new(16 * 1024).unwrap())) } async fn destroy(&self, _req: Request) { diff --git a/examples/src/path_memfs/main.rs b/examples/src/path_memfs/main.rs index 2ab34d1..e4f0c36 100644 --- a/examples/src/path_memfs/main.rs +++ b/examples/src/path_memfs/main.rs @@ -147,9 +147,7 @@ impl Default for Fs { impl PathFilesystem for Fs { async fn init(&self, _req: Request) -> Result { - Ok(ReplyInit { - max_write: NonZeroU32::new(16 * 1024).unwrap(), - }) + Ok(ReplyInit::new(NonZeroU32::new(16 * 1024).unwrap())) } async fn destroy(&self, _req: Request) {} diff --git a/examples/src/poll/main.rs b/examples/src/poll/main.rs index f1089e2..e060dbf 100644 --- a/examples/src/poll/main.rs +++ b/examples/src/poll/main.rs @@ -32,9 +32,7 @@ struct Poll { impl Filesystem for Poll { async fn init(&self, _req: Request) -> Result { - Ok(ReplyInit { - max_write: NonZeroU32::new(16 * 1024).unwrap(), - }) + Ok(ReplyInit::new(NonZeroU32::new(16 * 1024).unwrap())) } async fn destroy(&self, _req: Request) {} diff --git a/src/helper.rs b/src/helper.rs index f7a344d..a737ab4 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -55,7 +55,3 @@ pub const fn get_padding_size(dir_entry_size: usize) -> usize { entry_size - dir_entry_size } - -pub fn get_bincode_config() -> impl bincode_next::config::Config { - bincode_next::config::legacy() -} diff --git a/src/lib.rs b/src/lib.rs index c92df96..81ce7e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,13 +18,15 @@ //! //! You must enable `async-io-runtime` or `tokio-runtime` feature. -#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(docsrs, feature(doc_cfg))] +#[cfg(target_os = "macos")] +use std::io::ErrorKind; #[cfg(any( all(target_os = "linux", feature = "unprivileged"), target_os = "macos" ))] -use std::io::{self, ErrorKind}; +use std::io::{self}; #[cfg(target_os = "macos")] use std::path::Path; #[cfg(any( @@ -39,8 +41,8 @@ pub use helper::{mode_from_kind_and_perm, perm_from_mode_and_kind}; pub use mount_options::MountOptions; use nix::sys::stat::mode_t; use raw::abi::{ - fuse_setattr_in, FATTR_ATIME, FATTR_ATIME_NOW, FATTR_CTIME, FATTR_GID, FATTR_LOCKOWNER, - FATTR_MODE, FATTR_MTIME, FATTR_MTIME_NOW, FATTR_SIZE, FATTR_UID, + FATTR_ATIME, FATTR_ATIME_NOW, FATTR_CTIME, FATTR_GID, FATTR_LOCKOWNER, FATTR_MODE, FATTR_MTIME, + FATTR_MTIME_NOW, FATTR_SIZE, FATTR_UID, fuse_setattr_in, }; #[cfg(target_os = "macos")] use raw::abi::{FATTR_BKUPTIME, FATTR_CHGTIME, FATTR_CRTIME, FATTR_FLAGS}; @@ -237,12 +239,8 @@ impl From for Timestamp { #[cfg(all(target_os = "linux", feature = "unprivileged"))] fn find_fusermount3() -> io::Result { - which::which("fusermount3").map_err(|err| { - io::Error::new( - ErrorKind::Other, - format!("find fusermount3 binary failed {err:?}"), - ) - }) + which::which("fusermount3") + .map_err(|err| io::Error::other(format!("find fusermount3 binary failed {err:?}"))) } #[cfg(target_os = "macos")] diff --git a/src/mount_options.rs b/src/mount_options.rs index c18ac1b..88e3c5c 100644 --- a/src/mount_options.rs +++ b/src/mount_options.rs @@ -1,10 +1,10 @@ use std::ffi::OsString; -#[cfg(any(target_os = "linux", target_os = "macos"))] +#[cfg(target_os = "linux")] use std::os::unix::io::RawFd; #[cfg(target_os = "freebsd")] use nix::mount::Nmount; -#[cfg(any(target_os = "macos", target_os = "linux"))] +#[cfg(target_os = "linux")] use nix::unistd; /// mount options. @@ -245,20 +245,25 @@ impl MountOptions { #[cfg(target_os = "macos")] pub(crate) fn build(&self) -> OsString { - let mut opts = vec![String::from("-o fsname=ofs")]; + // macFUSE expects a single `-o` followed by a comma-separated option + // list (like the Linux builder), NOT repeated space-separated `-o` + // flags. The old form (`-o fsname=ofs -o allow_root`) was passed to the + // mount helper as one argument, so every option after the first was + // silently dropped — e.g. a caller-supplied `nobrowse` never took effect. + let mut opts = vec![String::from("fsname=ofs")]; if self.allow_root { - opts.push("-o allow_root".to_string()); + opts.push("allow_root".to_string()); } if self.allow_other { - opts.push("-o allow_other".to_string()); + opts.push("allow_other".to_string()); } - let mut options = OsString::from(opts.join(" ")); + let mut options = OsString::from(format!("-o {}", opts.join(","))); if let Some(custom_options) = &self.custom_options { - options.push(" "); + options.push(","); options.push(custom_options); } diff --git a/src/notify.rs b/src/notify.rs index 54ee4dd..c451a01 100644 --- a/src/notify.rs +++ b/src/notify.rs @@ -1,40 +1,36 @@ //! notify kernel. use std::ffi::OsString; -use std::os::unix::ffi::OsStrExt; +use std::sync::Arc; -use bincode_next::encode_into_std_write; use bytes::{Buf, Bytes}; -use futures_channel::mpsc::UnboundedSender; -use futures_util::future::Either; -use futures_util::sink::SinkExt; +use zerocopy::IntoBytes; -use crate::helper::get_bincode_config; use crate::raw::abi::{ + FUSE_NOTIFY_DELETE_OUT_SIZE, FUSE_NOTIFY_INVAL_ENTRY_OUT_SIZE, + FUSE_NOTIFY_INVAL_INODE_OUT_SIZE, FUSE_NOTIFY_POLL_WAKEUP_OUT_SIZE, + FUSE_NOTIFY_RETRIEVE_OUT_SIZE, FUSE_NOTIFY_STORE_OUT_SIZE, FUSE_OUT_HEADER_SIZE, fuse_notify_code, fuse_notify_delete_out, fuse_notify_inval_entry_out, fuse_notify_inval_inode_out, fuse_notify_poll_wakeup_out, fuse_notify_retrieve_out, - fuse_notify_store_out, fuse_out_header, FUSE_NOTIFY_DELETE_OUT_SIZE, - FUSE_NOTIFY_INVAL_ENTRY_OUT_SIZE, FUSE_NOTIFY_INVAL_INODE_OUT_SIZE, - FUSE_NOTIFY_POLL_WAKEUP_OUT_SIZE, FUSE_NOTIFY_RETRIEVE_OUT_SIZE, FUSE_NOTIFY_STORE_OUT_SIZE, - FUSE_OUT_HEADER_SIZE, + fuse_notify_store_out, fuse_out_header, }; -use crate::raw::FuseData; +use crate::raw::session::ResponseSender; #[derive(Debug, Clone)] /// notify kernel there are something need to handle. pub struct Notify { - sender: UnboundedSender, + sender: Arc, } impl Notify { - pub(crate) fn new(sender: UnboundedSender) -> Self { + pub(crate) fn new(sender: Arc) -> Self { Self { sender } } /// notify kernel there are something need to handle. If notify failed, the `kind` will be /// return in `Err`. - async fn notify(&mut self, kind: NotifyKind) -> Result<(), NotifyKind> { - let data = match &kind { + async fn notify(&mut self, kind: NotifyKind) { + match &kind { NotifyKind::Wakeup { kh } => { let out_header = fuse_out_header { len: (FUSE_OUT_HEADER_SIZE + FUSE_NOTIFY_POLL_WAKEUP_OUT_SIZE) as u32, @@ -44,16 +40,7 @@ impl Notify { let wakeup_out = fuse_notify_poll_wakeup_out { kh: *kh }; - let mut data = - Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_NOTIFY_POLL_WAKEUP_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config) - .expect("vec size is not enough"); - encode_into_std_write(&wakeup_out, &mut data, config) - .expect("vec size is not enough"); - - Either::Left(data) + self.sender.send2(&out_header, wakeup_out.as_bytes()).await; } NotifyKind::InvalidInode { inode, offset, len } => { @@ -69,16 +56,9 @@ impl Notify { len: *len, }; - let mut data = - Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_NOTIFY_INVAL_INODE_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config) - .expect("vec size is not enough"); - encode_into_std_write(&invalid_inode_out, &mut data, config) - .expect("vec size is not enough"); - - Either::Left(data) + self.sender + .send2(&out_header, invalid_inode_out.as_bytes()) + .await; } NotifyKind::InvalidEntry { parent, name } => { @@ -94,18 +74,10 @@ impl Notify { _padding: 0, }; - let mut data = - Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_NOTIFY_INVAL_ENTRY_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config) - .expect("vec size is not enough"); - encode_into_std_write(&invalid_entry_out, &mut data, config) - .expect("vec size is not enough"); - // TODO should I add null at the end? - - Either::Right((data, Bytes::copy_from_slice(name.as_bytes()))) + self.sender + .send2(&out_header, invalid_entry_out.as_bytes()) + .await; } NotifyKind::Delete { @@ -126,18 +98,8 @@ impl Notify { _padding: 0, }; - let mut data = - Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_NOTIFY_DELETE_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config) - .expect("vec size is not enough"); - encode_into_std_write(&delete_out, &mut data, config) - .expect("vec size is not enough"); - // TODO should I add null at the end? - - Either::Right((data, Bytes::copy_from_slice(name.as_bytes()))) + self.sender.send2(&out_header, delete_out.as_bytes()).await; } NotifyKind::Store { @@ -158,16 +120,7 @@ impl Notify { _padding: 0, }; - let mut data_buf = - Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_NOTIFY_STORE_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data_buf, config) - .expect("vec size is not enough"); - encode_into_std_write(&store_out, &mut data_buf, config) - .expect("vec size is not enough"); - - Either::Right((data_buf, data.clone())) + self.sender.send2(&out_header, store_out.as_bytes()).await; } NotifyKind::Retrieve { @@ -190,71 +143,58 @@ impl Notify { _padding: 0, }; - let mut data = - Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_NOTIFY_RETRIEVE_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config) - .expect("vec size is not enough"); - encode_into_std_write(&retrieve_out, &mut data, config) - .expect("vec size is not enough"); - - Either::Left(data) + self.sender + .send2(&out_header, retrieve_out.as_bytes()) + .await; } }; - - self.sender.send(data).await.or(Err(kind)) } /// try to notify kernel the IO is ready, kernel can wakeup the waiting program. pub async fn wakeup(mut self, kh: u64) { - let _ = self.notify(NotifyKind::Wakeup { kh }).await; + self.notify(NotifyKind::Wakeup { kh }).await; } /// try to notify the cache invalidation about an inode. pub async fn invalid_inode(mut self, inode: u64, offset: i64, len: i64) { - let _ = self - .notify(NotifyKind::InvalidInode { inode, offset, len }) + self.notify(NotifyKind::InvalidInode { inode, offset, len }) .await; } /// try to notify the invalidation about a directory entry. pub async fn invalid_entry(mut self, parent: u64, name: OsString) { - let _ = self.notify(NotifyKind::InvalidEntry { parent, name }).await; + self.notify(NotifyKind::InvalidEntry { parent, name }).await; } /// try to notify a directory entry has been deleted. pub async fn delete(mut self, parent: u64, child: u64, name: OsString) { - let _ = self - .notify(NotifyKind::Delete { - parent, - child, - name, - }) - .await; + self.notify(NotifyKind::Delete { + parent, + child, + name, + }) + .await; } /// try to push the data in an inode for updating the kernel cache. pub async fn store(mut self, inode: u64, offset: u64, mut data: impl Buf) { - let _ = self - .notify(NotifyKind::Store { - inode, - offset, - data: data.copy_to_bytes(data.remaining()), - }) - .await; + self.notify(NotifyKind::Store { + inode, + offset, + data: data.copy_to_bytes(data.remaining()), + }) + .await; } /// try to retrieve data in an inode from the kernel cache. pub async fn retrieve(mut self, notify_unique: u64, inode: u64, offset: u64, size: u32) { - let _ = self - .notify(NotifyKind::Retrieve { - notify_unique, - inode, - offset, - size, - }) - .await; + self.notify(NotifyKind::Retrieve { + notify_unique, + inode, + offset, + size, + }) + .await; } } diff --git a/src/path/inode_path_bridge.rs b/src/path/inode_path_bridge.rs index b157467..3731b24 100644 --- a/src/path/inode_path_bridge.rs +++ b/src/path/inode_path_bridge.rs @@ -57,14 +57,14 @@ impl InodeNameManager { } fn remove_name(&mut self, name: &Name) { - if let Some(inode) = self.name_to_inode.remove(name) { - if let Some(names) = self.inode_to_names.get_mut(&inode) { - names.remove(name); + if let Some(inode) = self.name_to_inode.remove(name) + && let Some(names) = self.inode_to_names.get_mut(&inode) + { + names.remove(name); - if names.is_empty() { - self.inode_to_names.remove(&inode); - self.inode_generator.release_inode(inode); - } + if names.is_empty() { + self.inode_to_names.remove(&inode); + self.inode_generator.release_inode(inode); } } } @@ -149,7 +149,9 @@ where let reply_init = self.path_filesystem.init(req).await?; Ok(ReplyInit { - max_write: reply_init.max_write, + max_background: reply_init.max_background, + congestion_threshold: reply_init.congestion_threshold, + ..ReplyInit::new(reply_init.max_write) }) } diff --git a/src/path/mod.rs b/src/path/mod.rs index 5b66257..bca6b28 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -16,12 +16,12 @@ pub mod reply; mod session; pub mod prelude { - pub use super::reply::FileAttr; - pub use super::reply::*; pub use super::PathFilesystem; pub use super::Request; pub use super::Session; - pub use crate::notify::Notify; + pub use super::reply::FileAttr; + pub use super::reply::*; pub use crate::FileType; pub use crate::SetAttr; + pub use crate::notify::Notify; } diff --git a/src/path/path_filesystem.rs b/src/path/path_filesystem.rs index dea3623..e8e22fd 100644 --- a/src/path/path_filesystem.rs +++ b/src/path/path_filesystem.rs @@ -3,8 +3,8 @@ use std::ffi::OsStr; use bytes::Bytes; use futures_util::stream::{Empty, Stream}; -use super::reply::*; use super::Request; +use super::reply::*; use crate::notify::Notify; use crate::{Result, SetAttr}; diff --git a/src/path/reply.rs b/src/path/reply.rs index 724447a..6db7d0c 100644 --- a/src/path/reply.rs +++ b/src/path/reply.rs @@ -72,10 +72,37 @@ impl From<(Inode, FileAttr)> for crate::raw::reply::FileAttr { } #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +#[non_exhaustive] /// init reply pub struct ReplyInit { /// the max write size pub max_write: NonZeroU32, + + /// Optional maximum number of pending background FUSE requests. + /// + /// The kernel queues background requests (readahead, async reads) up to this + /// limit. If `None`, defaults to `DEFAULT_MAX_BACKGROUND` (12). + pub max_background: Option, + + /// Optional congestion threshold for background FUSE requests. + /// + /// When the number of pending background requests exceeds this threshold, + /// the kernel starts throttling. If `None`, defaults to + /// `DEFAULT_CONGESTION_THRESHOLD` (75% of `max_background`). + pub congestion_threshold: Option, +} + +impl ReplyInit { + /// Create a new `ReplyInit` with the given max write size. + /// + /// All optional fields default to `None`, which uses kernel defaults. + pub fn new(max_write: NonZeroU32) -> Self { + Self { + max_write, + max_background: None, + congestion_threshold: None, + } + } } #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] diff --git a/src/path/session.rs b/src/path/session.rs index 8b57801..8ec5678 100644 --- a/src/path/session.rs +++ b/src/path/session.rs @@ -1,10 +1,10 @@ use std::io; use std::path::Path; +use crate::MountOptions; use crate::path::inode_path_bridge::InodePathBridge; use crate::path::path_filesystem::PathFilesystem; use crate::raw; -use crate::MountOptions; #[cfg(any(feature = "async-io-runtime", feature = "tokio-runtime"))] #[derive(Debug)] diff --git a/src/raw/abi.rs b/src/raw/abi.rs index fa3d486..1bf4bc8 100644 --- a/src/raw/abi.rs +++ b/src/raw/abi.rs @@ -22,7 +22,7 @@ use std::error::Error; use std::fmt::{self, Debug, Display, Formatter}; use std::mem; -use bincode_next::{Decode, Encode}; +use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; /// The min size of read buffer. In Linux kernel the `FUSE_MIN_READ_BUFFER` is /// @@ -231,8 +231,9 @@ pub const FUSE_IOCTL_MAX_IOV: u32 = 256; /// request poll notify pub const FUSE_POLL_SCHEDULE_NOTIFY: u32 = 1 << 0; -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_attr { pub ino: u64, pub size: u64, @@ -259,8 +260,9 @@ pub struct fuse_attr { pub(crate) _padding: u32, } -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_kstatfs { // Total blocks (in units of frsize) pub blocks: u64, @@ -282,8 +284,9 @@ pub struct fuse_kstatfs { pub spare: [u32; 6], } -#[derive(Debug, Decode, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_file_lock { pub start: u64, pub end: u64, @@ -491,8 +494,9 @@ impl TryFrom for fuse_notify_code { pub const FUSE_ENTRY_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_entry_out { pub nodeid: u64, pub generation: u64, @@ -503,32 +507,34 @@ pub struct fuse_entry_out { pub attr: fuse_attr, } -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_forget_in { pub nlookup: u64, } pub const FUSE_FORGET_ONE_SIZE: usize = mem::size_of::(); -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_forget_one { pub nodeid: u64, pub(crate) _nlookup: u64, } -pub const FUSE_BATCH_FORGET_IN_SIZE: usize = mem::size_of::(); - -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_batch_forget_in { pub count: u32, pub(crate) _dummy: u32, } -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_getattr_in { pub getattr_flags: u32, pub dummy: u32, @@ -537,8 +543,9 @@ pub struct fuse_getattr_in { pub const FUSE_ATTR_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_attr_out { pub attr_valid: u64, pub attr_valid_nsec: u32, @@ -548,7 +555,8 @@ pub struct fuse_attr_out { #[cfg(target_os = "macos")] #[derive(Debug)] -#[allow(non_camel_case_types)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_getxtimes_out { pub bkuptime: u64, pub crtime: u64, @@ -556,10 +564,9 @@ pub struct fuse_getxtimes_out { pub crtimensec: u32, } -pub const FUSE_MKNOD_IN_SIZE: usize = mem::size_of::(); - -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_mknod_in { pub mode: u32, pub rdev: u32, @@ -567,19 +574,17 @@ pub struct fuse_mknod_in { _padding: u32, } -pub const FUSE_MKDIR_IN_SIZE: usize = mem::size_of::(); - -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_mkdir_in { pub mode: u32, pub umask: u32, } -pub const FUSE_RENAME_IN_SIZE: usize = mem::size_of::(); - -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_rename_in { pub newdir: u64, // https://github.com/osxfuse/fuse/blob/master/include/fuse_kernel.h#L448 @@ -589,10 +594,9 @@ pub struct fuse_rename_in { _padding: u32, } -pub const FUSE_RENAME2_IN_SIZE: usize = mem::size_of::(); - -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_rename2_in { pub newdir: u64, pub flags: u32, @@ -601,23 +605,24 @@ pub struct fuse_rename2_in { #[cfg(target_os = "macos")] #[derive(Debug)] -#[allow(non_camel_case_types)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_exchange_in { pub olddir: u64, pub newdir: u64, pub options: u64, } -pub const FUSE_LINK_IN_SIZE: usize = mem::size_of::(); - -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_link_in { pub oldnodeid: u64, } -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_setattr_in { pub valid: u32, _padding: u32, @@ -651,17 +656,17 @@ pub struct fuse_setattr_in { pub flags: u32, // see chflags(2) } -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_open_in { pub flags: u32, pub(crate) _unused: u32, } -pub const FUSE_CREATE_IN_SIZE: usize = mem::size_of::(); - -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_create_in { pub flags: u32, pub mode: u32, @@ -671,16 +676,18 @@ pub struct fuse_create_in { pub const FUSE_OPEN_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_open_out { pub fh: u64, pub open_flags: u32, pub(crate) _padding: u32, } -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_release_in { pub fh: u64, pub flags: u32, @@ -688,8 +695,9 @@ pub struct fuse_release_in { pub lock_owner: u64, } -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_flush_in { pub fh: u64, pub(crate) _unused: u32, @@ -697,8 +705,9 @@ pub struct fuse_flush_in { pub lock_owner: u64, } -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_read_in { pub fh: u64, pub offset: u64, @@ -711,8 +720,9 @@ pub struct fuse_read_in { pub const FUSE_WRITE_IN_SIZE: usize = mem::size_of::(); -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_write_in { pub fh: u64, pub offset: u64, @@ -725,8 +735,9 @@ pub struct fuse_write_in { pub const FUSE_WRITE_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_write_out { pub size: u32, pub(crate) _padding: u32, @@ -734,24 +745,25 @@ pub struct fuse_write_out { pub const FUSE_STATFS_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_statfs_out { pub st: fuse_kstatfs, } -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_fsync_in { pub fh: u64, pub fsync_flags: u32, _padding: u32, } -pub const FUSE_SETXATTR_IN_SIZE: usize = mem::size_of::(); - -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_setxattr_in { pub size: u32, pub flags: u32, @@ -761,10 +773,9 @@ pub struct fuse_setxattr_in { _padding: u32, } -pub const FUSE_GETXATTR_IN_SIZE: usize = mem::size_of::(); - -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_getxattr_in { pub size: u32, _padding: u32, @@ -776,16 +787,18 @@ pub struct fuse_getxattr_in { pub const FUSE_GETXATTR_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_getxattr_out { pub size: u32, pub(crate) _padding: u32, } #[cfg(feature = "file-lock")] -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_lk_in { pub fh: u64, pub owner: u64, @@ -798,21 +811,24 @@ pub struct fuse_lk_in { pub const FUSE_LK_OUT_SIZE: usize = mem::size_of::(); #[cfg(feature = "file-lock")] -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_lk_out { pub lk: fuse_file_lock, } -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_access_in { pub mask: u32, _padding: u32, } -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_init_in { pub(crate) _major: u32, pub(crate) _minor: u32, @@ -822,8 +838,9 @@ pub struct fuse_init_in { pub const FUSE_INIT_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_init_out { pub major: u32, pub minor: u32, @@ -839,7 +856,7 @@ pub struct fuse_init_out { } /*#[derive(Debug)] -#[allow(non_camel_case_types)] +#[allow(non_camel_case_types, unused)] pub struct cuse_init_in { pub major: u32, pub minor: u32, @@ -848,7 +865,7 @@ pub struct cuse_init_in { }*/ /*#[derive(Debug)] -#[allow(non_camel_case_types)] +#[allow(non_camel_case_types, unused)] pub struct cuse_init_out { pub major: u32, pub minor: u32, @@ -863,14 +880,16 @@ pub struct cuse_init_out { pub spare: [u32; 10], }*/ -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_interrupt_in { pub unique: u64, } -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_bmap_in { pub block: u64, pub blocksize: u32, @@ -879,14 +898,15 @@ pub struct fuse_bmap_in { pub const FUSE_BMAP_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_bmap_out { pub block: u64, } //#[derive(Debug, Decode)] -//#[allow(non_camel_case_types)] +//#[allow(non_camel_case_types, unused)] //pub struct fuse_ioctl_in { //pub fh: u64, //pub flags: u32, @@ -897,14 +917,14 @@ pub struct fuse_bmap_out { //} //#[derive(Debug)] -//#[allow(non_camel_case_types)] +//#[allow(non_camel_case_types, unused)] //pub struct fuse_ioctl_iovec { //pub base: u64, //pub len: u64, //} //#[derive(Debug)] -//#[allow(non_camel_case_types)] +//#[allow(non_camel_case_types, unused)] //pub struct fuse_ioctl_out { //pub result: i32, //pub flags: u32, @@ -912,8 +932,9 @@ pub struct fuse_bmap_out { //pub out_iovs: u32, //} -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_poll_in { pub fh: u64, pub kh: u64, @@ -923,8 +944,9 @@ pub struct fuse_poll_in { pub const FUSE_POLL_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_poll_out { pub revents: u32, pub(crate) _padding: u32, @@ -932,14 +954,16 @@ pub struct fuse_poll_out { pub const FUSE_NOTIFY_POLL_WAKEUP_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_notify_poll_wakeup_out { pub kh: u64, } -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_fallocate_in { pub fh: u64, pub offset: u64, @@ -950,8 +974,9 @@ pub struct fuse_fallocate_in { pub const FUSE_IN_HEADER_SIZE: usize = mem::size_of::(); -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_in_header { pub len: u32, pub opcode: u32, @@ -965,8 +990,9 @@ pub struct fuse_in_header { pub const FUSE_OUT_HEADER_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_out_header { pub len: u32, pub error: i32, @@ -975,8 +1001,9 @@ pub struct fuse_out_header { pub const FUSE_DIRENT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_dirent { pub ino: u64, pub off: u64, @@ -987,8 +1014,9 @@ pub struct fuse_dirent { pub const FUSE_DIRENTPLUS_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_direntplus { pub entry_out: fuse_entry_out, pub dirent: fuse_dirent, @@ -996,8 +1024,9 @@ pub struct fuse_direntplus { pub const FUSE_NOTIFY_INVAL_INODE_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_notify_inval_inode_out { pub ino: u64, pub off: i64, @@ -1006,8 +1035,9 @@ pub struct fuse_notify_inval_inode_out { pub const FUSE_NOTIFY_INVAL_ENTRY_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_notify_inval_entry_out { pub parent: u64, pub namelen: u32, @@ -1016,8 +1046,9 @@ pub struct fuse_notify_inval_entry_out { pub const FUSE_NOTIFY_DELETE_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_notify_delete_out { pub parent: u64, pub child: u64, @@ -1027,8 +1058,9 @@ pub struct fuse_notify_delete_out { pub const FUSE_NOTIFY_STORE_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_notify_store_out { pub nodeid: u64, pub offset: u64, @@ -1038,8 +1070,9 @@ pub struct fuse_notify_store_out { pub const FUSE_NOTIFY_RETRIEVE_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_notify_retrieve_out { pub notify_unique: u64, pub nodeid: u64, @@ -1048,11 +1081,10 @@ pub struct fuse_notify_retrieve_out { pub(crate) _padding: u32, } -pub const FUSE_NOTIFY_RETRIEVE_IN_SIZE: usize = mem::size_of::(); - -#[derive(Debug, Decode)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] // matches the size of fuse_write_in -#[allow(non_camel_case_types)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_notify_retrieve_in { _dummy1: u64, pub offset: u64, @@ -1062,8 +1094,9 @@ pub struct fuse_notify_retrieve_in { _dummy4: u64, } -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_lseek_in { pub fh: u64, pub offset: u64, @@ -1073,14 +1106,16 @@ pub struct fuse_lseek_in { pub const FUSE_LSEEK_OUT_SIZE: usize = mem::size_of::(); -#[derive(Debug, Encode)] -#[allow(non_camel_case_types)] +#[derive(Debug, IntoBytes, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_lseek_out { pub offset: u64, } -#[derive(Debug, Decode)] -#[allow(non_camel_case_types)] +#[derive(Debug, FromBytes, KnownLayout, Immutable)] +#[allow(non_camel_case_types, unused)] +#[repr(C)] pub struct fuse_copy_file_range_in { pub fh_in: u64, pub off_in: u64, diff --git a/src/raw/connection/async_io.rs b/src/raw/connection/async_io.rs index 96735af..b9dc901 100644 --- a/src/raw/connection/async_io.rs +++ b/src/raw/connection/async_io.rs @@ -43,9 +43,9 @@ use async_notify::Notify; target_os = "macos" ))] use async_process::Command; +use futures_util::{FutureExt, select}; #[cfg(target_os = "macos")] use futures_util::{join, try_join}; -use futures_util::{select, FutureExt}; #[cfg(any( all(target_os = "linux", feature = "unprivileged"), target_os = "macos" @@ -63,14 +63,14 @@ use nix::sys::uio; ))] use tracing::debug; -#[cfg(all(target_os = "linux", feature = "unprivileged"))] -use crate::find_fusermount3; -use crate::raw::connection::CompleteIoResult; #[cfg(any( all(target_os = "linux", feature = "unprivileged"), target_os = "macos" ))] use crate::MountOptions; +#[cfg(all(target_os = "linux", feature = "unprivileged"))] +use crate::find_fusermount3; +use crate::raw::connection::CompleteIoResult; #[derive(Debug)] pub struct FuseConnection { unmount_notify: Arc, @@ -266,10 +266,7 @@ impl BlockFuseConnection { .args(vec![options, mount_path]) .spawn()?; if !child.status().await?.success() { - return Err(io::Error::new( - io::ErrorKind::Other, - "fusermount run failed", - )); + return Err(io::Error::other("fusermount run failed")); } Ok(()) }); @@ -306,12 +303,12 @@ impl BlockFuseConnection { let fd = if let Some(ControlMessageOwned::ScmRights(fds)) = cmsgs.next() { if fds.is_empty() { - return Err(io::Error::new(io::ErrorKind::Other, "no fuse fd")); + return Err(io::Error::other("no fuse fd")); } fds[0] } else { - return Err(io::Error::new(io::ErrorKind::Other, "get fuse fd failed")); + return Err(io::Error::other("get fuse fd failed")); }; Ok(fd) @@ -449,10 +446,7 @@ impl NonBlockFuseConnection { .spawn()?; if !child.status().await?.success() { - return Err(io::Error::new( - io::ErrorKind::Other, - "fusermount run failed", - )); + return Err(io::Error::other("fusermount run failed")); } let fd1 = sock1.as_raw_fd(); @@ -477,12 +471,12 @@ impl NonBlockFuseConnection { let fd = if let Some(ControlMessageOwned::ScmRights(fds)) = msg.cmsgs()?.next() { if fds.is_empty() { - return Err(io::Error::new(io::ErrorKind::Other, "no fuse fd")); + return Err(io::Error::other("no fuse fd")); } fds[0] } else { - return Err(io::Error::new(io::ErrorKind::Other, "get fuse fd failed")); + return Err(io::Error::other("get fuse fd failed")); }; Ok(fd) diff --git a/src/raw/connection/tokio.rs b/src/raw/connection/tokio.rs index 493c01b..dbfb394 100644 --- a/src/raw/connection/tokio.rs +++ b/src/raw/connection/tokio.rs @@ -1,15 +1,12 @@ #[cfg(target_os = "macos")] use std::env; +#[cfg(all(target_os = "linux", feature = "unprivileged"))] +use std::ffi::OsString; #[cfg(any(target_os = "linux", target_os = "macos"))] use std::fs::File; +#[cfg(any(target_os = "linux", target_os = "freebsd"))] use std::fs::OpenOptions; use std::io; -#[cfg(any( - all(target_os = "linux", feature = "unprivileged"), - target_os = "freebsd", - target_os = "macos", -))] -use std::io::ErrorKind; #[cfg(any(target_os = "linux", target_os = "macos"))] use std::io::Write; use std::io::{IoSlice, IoSliceMut}; @@ -17,11 +14,10 @@ use std::ops::{Deref, DerefMut}; #[cfg(any( all(target_os = "linux", feature = "unprivileged"), target_os = "freebsd", - target_os = "macos", ))] use std::os::fd::OwnedFd; use std::os::fd::{AsFd, BorrowedFd}; -#[cfg(any(target_os = "freebsd", target_os = "macos"))] +#[cfg(target_os = "freebsd")] use std::os::unix::fs::OpenOptionsExt; #[cfg(any( all(target_os = "linux", feature = "unprivileged"), @@ -30,42 +26,39 @@ use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::RawFd; #[cfg(target_os = "macos")] use std::os::unix::io::{AsRawFd, FromRawFd}; -use std::pin::pin; -use std::sync::Arc; #[cfg(any( all(target_os = "linux", feature = "unprivileged"), - target_os = "macos" + target_os = "macos", ))] -use std::{ffi::OsString, path::Path}; +use std::path::Path; +use std::pin::pin; +use std::sync::Arc; use async_notify::Notify; use futures_util::lock::Mutex; -use futures_util::{select, FutureExt}; +use futures_util::{FutureExt, select}; +#[cfg(all(target_os = "linux", feature = "unprivileged"))] +use nix::fcntl::{FcntlArg, OFlag}; #[cfg(any( all(target_os = "linux", feature = "unprivileged"), - target_os = "freebsd", target_os = "macos", ))] -use nix::sys::uio; +use nix::sys::socket::{self, AddressFamily, ControlMessageOwned, MsgFlags, SockFlag, SockType}; #[cfg(any( all(target_os = "linux", feature = "unprivileged"), - target_os = "macos" + target_os = "freebsd", ))] -use nix::{ - fcntl::{FcntlArg, OFlag}, - sys::socket::{self, AddressFamily, ControlMessageOwned, MsgFlags, SockFlag, SockType}, -}; +use nix::sys::uio; #[cfg(any( all(target_os = "linux", feature = "unprivileged"), target_os = "freebsd", - target_os = "macos", ))] -use tokio::io::unix::AsyncFd; +use tokio::io::Interest; #[cfg(any( all(target_os = "linux", feature = "unprivileged"), target_os = "freebsd", ))] -use tokio::io::Interest; +use tokio::io::unix::AsyncFd; #[cfg(any( all(target_os = "linux", feature = "unprivileged"), target_os = "macos" @@ -78,17 +71,17 @@ use tokio::task; target_os = "macos" ))] use tracing::debug; -#[cfg(any(target_os = "freebsd", target_os = "macos"))] +#[cfg(target_os = "freebsd")] use tracing::warn; use super::CompleteIoResult; -#[cfg(all(target_os = "linux", feature = "unprivileged"))] -use crate::find_fusermount3; #[cfg(any( all(target_os = "linux", feature = "unprivileged"), target_os = "macos" ))] use crate::MountOptions; +#[cfg(all(target_os = "linux", feature = "unprivileged"))] +use crate::find_fusermount3; #[derive(Debug)] pub struct FuseConnection { @@ -246,8 +239,6 @@ impl BlockFuseConnection { ) -> io::Result { use std::{thread, time::Duration}; - use tokio::time::sleep; - use crate::find_macfuse_mount; let (sock0, sock1) = match socket::socketpair( @@ -291,10 +282,7 @@ impl BlockFuseConnection { if status.success() { Ok(()) } else { - Err(io::Error::new( - io::ErrorKind::Other, - "fusermount run failed", - )) + Err(io::Error::other("fusermount run failed")) } }); @@ -330,12 +318,12 @@ impl BlockFuseConnection { }; let fd = if let Some(ControlMessageOwned::ScmRights(fds)) = cmsgs.next() { if fds.is_empty() { - return Err(io::Error::new(ErrorKind::Other, "no fuse fd")); + return Err(io::Error::other("no fuse fd")); } fds[0] } else { - return Err(io::Error::new(ErrorKind::Other, "get fuse fd failed")); + return Err(io::Error::other("get fuse fd failed")); }; Ok(fd) @@ -424,7 +412,7 @@ struct NonBlockFuseConnection { target_os = "freebsd", ))] impl NonBlockFuseConnection { - #[cfg(any(target_os = "freebsd", target_os = "macos"))] + #[cfg(target_os = "freebsd")] fn new() -> io::Result { #[cfg(target_os = "freebsd")] const DEV_FUSE: &str = "/dev/fuse"; @@ -436,7 +424,7 @@ impl NonBlockFuseConnection { .open(DEV_FUSE) { Err(e) => { - if e.kind() == ErrorKind::NotFound { + if e.kind() == io::ErrorKind::NotFound { warn!("Cannot open {}. Is the module loaded?", DEV_FUSE); } warn!("Cannot open {}. err: {:?}", DEV_FUSE, e); @@ -485,10 +473,7 @@ impl NonBlockFuseConnection { .spawn()?; if !child.wait().await?.success() { - return Err(io::Error::new( - io::ErrorKind::Other, - "fusermount run failed", - )); + return Err(io::Error::other("fusermount run failed")); } let fd1 = sock1.as_raw_fd(); @@ -513,12 +498,12 @@ impl NonBlockFuseConnection { let fd = if let Some(ControlMessageOwned::ScmRights(fds)) = msg.cmsgs()?.next() { if fds.is_empty() { - return Err(io::Error::new(ErrorKind::Other, "no fuse fd")); + return Err(io::Error::other("no fuse fd")); } fds[0] } else { - return Err(io::Error::new(ErrorKind::Other, "get fuse fd failed")); + return Err(io::Error::other("get fuse fd failed")); }; Ok(fd) @@ -526,11 +511,11 @@ impl NonBlockFuseConnection { .await .unwrap()?; - Self::set_fd_non_blocking(fd)?; - // Safety: fd is valid let fd = unsafe { OwnedFd::from_raw_fd(fd) }; + Self::set_fd_non_blocking(fd.as_fd())?; + Ok(Self { fd: AsyncFd::new(fd)?, read: Mutex::new(()), @@ -538,11 +523,8 @@ impl NonBlockFuseConnection { }) } - #[cfg(any( - all(target_os = "linux", feature = "unprivileged"), - target_os = "macos" - ))] - fn set_fd_non_blocking(fd: RawFd) -> io::Result<()> { + #[cfg(all(target_os = "linux", feature = "unprivileged"))] + fn set_fd_non_blocking(fd: BorrowedFd) -> io::Result<()> { let flags = nix::fcntl::fcntl(fd, FcntlArg::F_GETFL).map_err(io::Error::from)?; debug!( "set fd {:?} to non-blocking", diff --git a/src/raw/mod.rs b/src/raw/mod.rs index 064878b..e9aff35 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -6,15 +6,11 @@ //! want to control the inode or do the path<->inode map on yourself, [`Filesystem`] is the only one //! choose. -use bytes::Bytes; pub use filesystem::Filesystem; -use futures_util::future::Either; pub use request::Request; #[cfg(any(feature = "async-io-runtime", feature = "tokio-runtime"))] pub use session::{MountHandle, Session}; -pub(crate) type FuseData = Either, (Vec, Bytes)>; - pub(crate) mod abi; mod connection; mod filesystem; @@ -24,12 +20,12 @@ mod request; pub(crate) mod session; pub mod prelude { - pub use super::reply::FileAttr; - pub use super::reply::*; pub use super::Filesystem; pub use super::Request; pub use super::Session; - pub use crate::notify::Notify; + pub use super::reply::FileAttr; + pub use super::reply::*; pub use crate::FileType; pub use crate::SetAttr; + pub use crate::notify::Notify; } diff --git a/src/raw/reply.rs b/src/raw/reply.rs index 77f435c..79e2546 100644 --- a/src/raw/reply.rs +++ b/src/raw/reply.rs @@ -84,10 +84,37 @@ impl From for fuse_attr { } #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +#[non_exhaustive] /// init reply pub struct ReplyInit { /// the max write size pub max_write: NonZeroU32, + + /// Optional maximum number of pending background FUSE requests. + /// + /// The kernel queues background requests (readahead, async reads) up to this + /// limit. If `None`, defaults to `DEFAULT_MAX_BACKGROUND` (12). + pub max_background: Option, + + /// Optional congestion threshold for background FUSE requests. + /// + /// When the number of pending background requests exceeds this threshold, + /// the kernel starts throttling. If `None`, defaults to + /// `DEFAULT_CONGESTION_THRESHOLD` (75% of `max_background`). + pub congestion_threshold: Option, +} + +impl ReplyInit { + /// Create a new `ReplyInit` with the given max write size. + /// + /// All optional fields default to `None`, which uses kernel defaults. + pub fn new(max_write: NonZeroU32) -> Self { + Self { + max_write, + max_background: None, + congestion_threshold: None, + } + } } #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] diff --git a/src/raw/session.rs b/src/raw/session.rs index d4bbf21..d1bd714 100644 --- a/src/raw/session.rs +++ b/src/raw/session.rs @@ -7,12 +7,14 @@ use std::io::Error as IoError; use std::io::ErrorKind; use std::io::Result as IoResult; use std::num::NonZeroU32; +#[cfg(any(target_os = "linux", target_os = "freebsd"))] use std::os::fd::AsFd; use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStringExt; +#[cfg(any(target_os = "linux", target_os = "freebsd"))] use std::os::unix::io::AsRawFd; use std::path::{Path, PathBuf}; -use std::pin::{pin, Pin}; +use std::pin::{Pin, pin}; use std::sync::Arc; use std::task::Context; use std::task::Poll; @@ -28,12 +30,8 @@ use async_global_executor::{self as task, Task as JoinHandle}; feature = "unprivileged" ))] use async_process::Command; -use bincode_next::{decode_from_slice, encode_into_std_write, encode_to_vec}; -use bytes::Bytes; -use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; -use futures_util::future::{Either, FutureExt}; +use futures_util::future::FutureExt; use futures_util::select; -use futures_util::sink::{Sink, SinkExt}; use futures_util::stream::StreamExt; use nix::mount; #[cfg(any(target_os = "freebsd", target_os = "macos"))] @@ -49,8 +47,10 @@ use tokio::process::Command; use tokio::task::JoinHandle; #[cfg(all(not(feature = "async-io-runtime"), feature = "tokio-runtime"))] use tokio::{fs::read_dir, task}; -use tracing::{debug, debug_span, error, instrument, warn, Instrument, Span}; +use tracing::{Instrument, Span, debug, debug_span, error, instrument, warn}; +use zerocopy::{FromBytes, IntoBytes}; +use crate::MountOptions; #[cfg(all(target_os = "linux", feature = "unprivileged"))] use crate::find_fusermount3; use crate::helper::*; @@ -61,8 +61,6 @@ use crate::raw::connection::FuseConnection; use crate::raw::filesystem::Filesystem; use crate::raw::reply::ReplyXAttr; use crate::raw::request::Request; -use crate::raw::FuseData; -use crate::MountOptions; use crate::{Errno, SetAttr}; /// A Future which returns when a file system is unmounted @@ -104,6 +102,111 @@ impl Drop for MountHandle { } } +/// A sender for sending response to kernel, it will send response immediately when call send method, and notify session when send failed. +#[derive(Debug)] +pub(crate) struct ResponseSender { + /// notify session when send failed, session will stop and unmount when receive this notify + send_failed: Arc, + /// connection to write response + connection: Arc, +} + +impl ResponseSender { + /// Handle a failure while writing a reply to the kernel. + /// + /// A failed reply to a *single* request must not tear down the whole + /// session — only a genuinely dead connection should. We therefore treat + /// transient, per-request errors as recoverable (drop this one reply and + /// keep serving): + /// - `NotFound` (ENOENT): the request was interrupted. + /// - `InvalidInput` (EINVAL), `WouldBlock` (EAGAIN), `Interrupted` (EINTR): + /// macFUSE can transiently reject a device write under heavy concurrent + /// load (e.g. while Spotlight crawls the mount). Killing the mount over + /// one such error left the volume wedged (every later op `ENXIO`). + /// + /// Connection-gone errors (e.g. ENODEV/EBADF on unmount) map to other + /// kinds and still notify `send_failed`, so a clean unmount stops the + /// session as before. + fn handle_send_error(&self, err: IoError) { + if Self::is_recoverable_reply_error(err.kind()) { + // Expected and harmless under normal load: macOS file browsers + // (Finder, Spotlight, open/save panels) constantly crawl the mount + // and routinely interrupt their own requests, so a steady stream of + // recoverable drops is the norm. Logging each at WARN floods the log + // (tens per second). Keep it at debug for diagnostics only — the + // drop itself is already handled by continuing the session. + debug!("dropping a failed fuse reply, session continues: {}", err); + } else { + error!("reply fuse failed {}", err); + self.send_failed.notify(); + } + } + + /// Whether a failed reply write is a transient, per-request error (session + /// keeps running) rather than a dead connection (fatal). Pure function so + /// the policy is unit-testable. + fn is_recoverable_reply_error(kind: ErrorKind) -> bool { + matches!( + kind, + ErrorKind::NotFound + | ErrorKind::InvalidInput + | ErrorKind::WouldBlock + | ErrorKind::Interrupted + ) + } + + /// send response with only header, no data + pub(crate) async fn send1(&self, header: &fuse_out_header) { + if let Err(err) = self + .connection + .write_vectored::<_, &[u8]>(header.as_bytes(), None) + .await + .1 + { + self.handle_send_error(err); + } + } + + /// send response with header and data + pub(crate) async fn send2(&self, header: &fuse_out_header, data: &[u8]) { + if let Err(err) = self + .connection + .write_vectored::<_, &[u8]>(header.as_bytes(), Some(data)) + .await + .1 + { + self.handle_send_error(err); + } + } + + /// send response with header and two parts of data. + pub(crate) async fn send3(&self, header: &fuse_out_header, d1: &[u8], d2: &[u8]) { + // TODO: Implement a write_vectored in FuseConnection to avoid this copy + let mut data = Vec::with_capacity(d1.len() + d2.len()); + data.extend_from_slice(d1); + data.extend_from_slice(d2); + if let Err(err) = self + .connection + .write_vectored::<_, &[u8]>(header.as_bytes(), Some(&data)) + .await + .1 + { + self.handle_send_error(err); + } + } + + /// send error response + #[inline] + async fn send_err(&self, request: &Request, err: Errno) { + let out_header = fuse_out_header { + len: FUSE_OUT_HEADER_SIZE as u32, + error: err.into(), + unique: request.unique, + }; + self.send1(&out_header).await + } +} + #[derive(Debug)] struct MountHandleInner { task: JoinHandle>, @@ -151,10 +254,7 @@ impl MountHandleInner { .args([OsStr::new("-u"), self.mount_path.as_os_str()]) .spawn()?; if !child.status().await?.success() { - return Err(IoError::new( - ErrorKind::Other, - "call fusermount3 -u to unmount failed", - )); + return Err(IoError::other("call fusermount3 -u to unmount failed")); } return Ok(()); @@ -196,10 +296,7 @@ impl MountHandleInner { .args([OsStr::new("-u"), self.mount_path.as_os_str()]) .spawn()?; if !child.wait().await?.success() { - return Err(IoError::new( - ErrorKind::Other, - "call fusermount3 -u to unmount failed", - )); + return Err(IoError::other("call fusermount3 -u to unmount failed")); } return Ok(()); @@ -239,8 +336,7 @@ impl Future for MountHandle { pub struct Session { fuse_connection: Option>, filesystem: Option>, - response_sender: UnboundedSender, - response_receiver: Option>, + response_sender: Option>, mount_options: MountOptions, } @@ -269,27 +365,24 @@ impl Debug for ReadResult { impl Session { /// new a fuse filesystem session. pub fn new(mount_options: MountOptions) -> Self { - let (sender, receiver) = unbounded(); - Self { fuse_connection: None, filesystem: None, - response_sender: sender, - response_receiver: Some(receiver), + response_sender: None, mount_options, } } +} +#[cfg(any(feature = "async-io-runtime", feature = "tokio-runtime"))] +impl Session { /// get a [`notify`]. /// /// [`notify`]: Notify fn get_notify(&self) -> Notify { - Notify::new(self.response_sender.clone()) + Notify::new(self.response_sender().clone()) } -} -#[cfg(any(feature = "async-io-runtime", feature = "tokio-runtime"))] -impl Session { async fn mount_empty_check(&self, mount_path: &Path) -> IoResult<()> { #[cfg(all(not(feature = "async-io-runtime"), feature = "tokio-runtime"))] if !self.mount_options.nonempty @@ -488,32 +581,35 @@ impl Session { } #[cfg(target_os = "macos")] - pub async fn mount>(mut self, fs: FS, mount_path: P) -> IoResult { + pub async fn mount>(self, fs: FS, mount_path: P) -> IoResult { self.mount_with_unprivileged(fs, mount_path).await } + /// get response sender, it should be called after mount, otherwise it will panic + fn response_sender(&self) -> &Arc { + self.response_sender + .as_ref() + .expect("response_sender should be Some()") + } + async fn inner_mount(mut self) -> IoResult<()> { let fuse_write_connection = self.fuse_connection.as_ref().unwrap().clone(); - let receiver = self.response_receiver.take().unwrap(); + let send_failed = Arc::new(async_notify::Notify::new()); + self.response_sender.replace(Arc::new(ResponseSender { + send_failed: send_failed.clone(), + connection: fuse_write_connection, + })); let dispatch_task = self.dispatch().fuse(); let mut dispatch_task = pin!(dispatch_task); - #[cfg(all(not(feature = "tokio-runtime"), feature = "async-io-runtime"))] - let reply_task = - task::spawn(async move { Self::reply_fuse(fuse_write_connection, receiver).await }) - .fuse(); - #[cfg(all(not(feature = "async-io-runtime"), feature = "tokio-runtime"))] - let reply_task = task::spawn(Self::reply_fuse(fuse_write_connection, receiver)) - .map(Result::unwrap) - .fuse(); - - let mut reply_task = pin!(reply_task); + let send_failed = send_failed.notified().fuse(); + let mut send_failed = pin!(send_failed); select! { - reply_result = reply_task => { - reply_result?; + _ = send_failed => { + return Err(std::io::Error::other("send response failed")) } dispatch_result = dispatch_task => { @@ -524,34 +620,6 @@ impl Session { Ok(()) } - async fn reply_fuse( - fuse_connection: Arc, - mut response_receiver: UnboundedReceiver, - ) -> IoResult<()> { - while let Some(response) = response_receiver.next().await { - let (data, extend_data) = match response { - Either::Left(data) => (data, None), - Either::Right((data, extend_data)) => (data, Some(extend_data)), - }; - if let Err(err) = fuse_connection.write_vectored(data, extend_data).await.1 { - if err.kind() == ErrorKind::NotFound { - warn!( - "may reply interrupted fuse request, ignore this error {}", - err - ); - - continue; - } - - error!("reply fuse failed {}", err); - - return Err(err); - } - } - - Ok(()) - } - #[instrument(level = "debug", skip(self, fs), ret, err)] async fn init_filesystem( &mut self, @@ -588,11 +656,11 @@ impl Session { Err(err) => { debug!("receive unknown opcode {}", err.0); - reply_error_in_place(libc::ENOSYS.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::ENOSYS.into()) + .await; - return Err(IoError::other( - format!("receive unknown opcode {}", err.0), - )); + return Err(IoError::other(format!("receive unknown opcode {}", err.0))); } Ok(opcode) => opcode, @@ -603,9 +671,7 @@ impl Session { if opcode != fuse_opcode::FUSE_INIT { error!(?opcode, "received unexpected opcode"); - return Err(IoError::other( - format!("unexpected opcode {opcode:?}"), - )); + return Err(IoError::other(format!("unexpected opcode {opcode:?}"))); } let data_size = in_header.len as usize - FUSE_IN_HEADER_SIZE; @@ -637,12 +703,12 @@ impl Session { }; let n = match res { Err(err) => { - if let Some(errno) = err.raw_os_error() { - if errno == libc::ENODEV { - debug!("read from /dev/fuse failed with ENODEV"); + if let Some(errno) = err.raw_os_error() + && errno == libc::ENODEV + { + debug!("read from /dev/fuse failed with ENODEV"); - return ReadResult::Destroy; - } + return ReadResult::Destroy; } error!("read from /dev/fuse failed {}", err); @@ -674,18 +740,18 @@ impl Session { }; } - let in_header = match decode_from_slice::(&header_buffer, get_bincode_config()) { + let in_header = match fuse_in_header::read_from_bytes(&header_buffer) { Err(err) => { error!("deserialize fuse_in_header failed {}", err); return ReadResult::Request { - in_header: Err(IoError::other(err)), + in_header: Err(IoError::other("deserialize fuse_in_header failed")), header_buffer, data_buffer, }; } - Ok((in_header, _)) => in_header, + Ok(in_header) => in_header, }; ReadResult::Request { @@ -744,7 +810,9 @@ impl Session { Err(err) => { debug!("receive unknown opcode {}", err.0); - reply_error_in_place(libc::ENOSYS.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::ENOSYS.into()) + .await; continue; } @@ -923,13 +991,13 @@ impl Session { } /*fuse_opcode::FUSE_IOCTL => { - let mut resp_sender = self.response_sender.clone(); + let mut resp_sender = self.response_sender().clone(); - let ioctl_in = decode_from_slice::(data, get_bincode_config()) { + let ioctl_in = match fuse_ioctl_in::read_from_prefix(data) { Err(err) => { error!("deserialize fuse_ioctl_in failed {}", err); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender2.send_err(&request, libc::EINVAL.into()).await; continue; } @@ -998,7 +1066,7 @@ impl Session { fuse_connection: &FuseConnection, fs: &FS, ) -> IoResult { - let init_in = match decode_from_slice::(data, get_bincode_config()) { + let init_in = match fuse_init_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_init_in failed {}, request unique {}", @@ -1011,11 +1079,8 @@ impl Session { unique: request.unique, }; - let init_out_header_data = encode_to_vec(&init_out_header, get_bincode_config()) - .expect("won't happened"); - if let Err(err) = fuse_connection - .write_vectored::<_, Vec>(init_out_header_data, None) + .write_vectored::<_, &[u8]>(init_out_header.as_bytes(), None) .await .1 { @@ -1224,11 +1289,8 @@ impl Session { unique: request.unique, }; - let init_out_header_data = encode_to_vec(&init_out_header, get_bincode_config()) - .expect("won't happened"); - if let Err(err) = fuse_connection - .write_vectored::<_, Vec>(init_out_header_data, None) + .write_vectored::<_, &[u8]>(init_out_header.as_bytes(), None) .await .1 { @@ -1246,8 +1308,10 @@ impl Session { minor: FUSE_KERNEL_MINOR_VERSION, max_readahead: init_in.max_readahead, flags: reply_flags, - max_background: DEFAULT_MAX_BACKGROUND, - congestion_threshold: DEFAULT_CONGESTION_THRESHOLD, + max_background: reply.max_background.unwrap_or(DEFAULT_MAX_BACKGROUND), + congestion_threshold: reply + .congestion_threshold + .unwrap_or(DEFAULT_CONGESTION_THRESHOLD), max_write: reply.max_write.get(), time_gran: DEFAULT_TIME_GRAN, max_pages: DEFAULT_MAX_PAGES, @@ -1263,14 +1327,8 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_INIT_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&init_out, &mut data, config).unwrap(); - if let Err(err) = fuse_connection - .write_vectored::<_, Vec>(data, None) + .write_vectored(out_header.as_bytes(), Some(init_out.as_bytes())) .await .1 { @@ -1296,7 +1354,9 @@ impl Session { None => { error!("lookup body has no null, request unique {}", request.unique); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -1304,7 +1364,7 @@ impl Session { Some(index) => OsString::from_vec(data[..index].to_vec()), }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_lookup"), async move { @@ -1313,18 +1373,10 @@ impl Session { request.unique, name, in_header.nodeid ); - let data = match fs.lookup(request, in_header.nodeid, &name).await { + match fs.lookup(request, in_header.nodeid, &name).await { Err(err) => { - let out_header = fuse_out_header { - len: FUSE_OUT_HEADER_SIZE as u32, - error: err.into(), - unique: request.unique, - }; - - encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened") + resp_sender.send_err(&request, err).await; } - Ok(entry) => { let entry_out: fuse_entry_out = entry.into(); @@ -1336,17 +1388,9 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_ENTRY_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&entry_out, &mut data, config).unwrap(); - - data + resp_sender.send2(&out_header, entry_out.as_bytes()).await; } }; - - let _ = resp_sender.send(Either::Left(data)).await; }); } @@ -1359,7 +1403,7 @@ impl Session { data: &[u8], fs: &Arc, ) { - let forget_in = match decode_from_slice::(data, get_bincode_config()) { + let forget_in = match fuse_forget_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_forget_in failed {}, request unique {}", @@ -1394,14 +1438,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let getattr_in = match decode_from_slice::(data, get_bincode_config()) { + let getattr_in = match fuse_getattr_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_forget_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -1409,7 +1455,7 @@ impl Session { Ok((getattr_in, _)) => getattr_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_getattr"), async move { @@ -1424,21 +1470,13 @@ impl Session { None }; - let data = match fs + match fs .getattr(request, in_header.nodeid, fh, getattr_in.getattr_flags) .await { Err(err) => { - let out_header = fuse_out_header { - len: FUSE_OUT_HEADER_SIZE as u32, - error: err.into(), - unique: request.unique, - }; - - encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened") + resp_sender.send_err(&request, err).await; } - Ok(attr) => { let attr_out = fuse_attr_out { attr_valid: attr.ttl.as_secs(), @@ -1453,17 +1491,9 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_ATTR_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&attr_out, &mut data, config).unwrap(); - - data + resp_sender.send2(&out_header, attr_out.as_bytes()).await; } }; - - let _ = resp_sender.send(Either::Left(data)).await; }); } @@ -1475,14 +1505,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let setattr_in = match decode_from_slice::(data, get_bincode_config()) { + let setattr_in = match fuse_setattr_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_setattr_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -1490,7 +1522,7 @@ impl Session { Ok((setattr_in, _)) => setattr_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_setattr"), async move { @@ -1507,18 +1539,10 @@ impl Session { request.unique, in_header.nodeid, set_attr ); - let data = match fs.setattr(request, in_header.nodeid, fh, set_attr).await { + match fs.setattr(request, in_header.nodeid, fh, set_attr).await { Err(err) => { - let out_header = fuse_out_header { - len: FUSE_OUT_HEADER_SIZE as u32, - error: err.into(), - unique: request.unique, - }; - - encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened") + resp_sender.send_err(&request, err).await; } - Ok(attr) => { let attr_out: fuse_attr_out = attr.into(); @@ -1528,23 +1552,15 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_ATTR_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&attr_out, &mut data, config).unwrap(); - - data + resp_sender.send2(&out_header, attr_out.as_bytes()).await; } }; - - let _ = resp_sender.send(Either::Left(data)).await; }); } #[instrument(skip(self, fs))] async fn handle_readlink(&mut self, request: Request, in_header: fuse_in_header, fs: &Arc) { - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_readlink"), async move { @@ -1553,20 +1569,10 @@ impl Session { request.unique, in_header.nodeid ); - let data = match fs.readlink(request, in_header.nodeid).await { + match fs.readlink(request, in_header.nodeid).await { Err(err) => { - let out_header = fuse_out_header { - len: FUSE_OUT_HEADER_SIZE as u32, - error: err.into(), - unique: request.unique, - }; - - Either::Left( - encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened"), - ) + resp_sender.send_err(&request, err).await; } - Ok(data) => { let out_header = fuse_out_header { len: (FUSE_OUT_HEADER_SIZE + data.data.len()) as u32, @@ -1574,16 +1580,9 @@ impl Session { unique: request.unique, }; - let mut data_buf = Vec::with_capacity(FUSE_OUT_HEADER_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data_buf, config).unwrap(); - - Either::Right((data_buf, data.data)) + resp_sender.send2(&out_header, &data.data).await; } }; - - let _ = resp_sender.send(data).await; }); } @@ -1599,7 +1598,9 @@ impl Session { None => { error!("symlink has no null, request unique {}", request.unique); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -1616,7 +1617,9 @@ impl Session { request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -1624,7 +1627,7 @@ impl Session { Some(index) => OsString::from_vec(data[..index].to_vec()), }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_symlink"), async move { @@ -1633,19 +1636,12 @@ impl Session { request.unique, in_header.nodeid, name, link_name ); - let data = match fs + match fs .symlink(request, in_header.nodeid, &name, &link_name) .await { Err(err) => { - let out_header = fuse_out_header { - len: FUSE_OUT_HEADER_SIZE as u32, - error: err.into(), - unique: request.unique, - }; - - encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened") + resp_sender.send_err(&request, err).await; } Ok(entry) => { @@ -1657,17 +1653,9 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_ENTRY_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&entry_out, &mut data, config).unwrap(); - - data + resp_sender.send2(&out_header, entry_out.as_bytes()).await; } }; - - let _ = resp_sender.send(Either::Left(data)).await; }); } @@ -1676,26 +1664,26 @@ impl Session { &mut self, request: Request, in_header: fuse_in_header, - mut data: &[u8], + data: &[u8], fs: &Arc, ) { - let mknod_in = match decode_from_slice::(data, get_bincode_config()) { + let (mknod_in, data) = match fuse_mknod_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_mknod_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } - Ok((mknod_in, _)) => mknod_in, + Ok(r) => r, }; - data = &data[FUSE_MKNOD_IN_SIZE..]; - let name = match get_first_null_position(data) { None => { error!( @@ -1703,7 +1691,9 @@ impl Session { request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -1711,7 +1701,7 @@ impl Session { Some(index) => OsString::from_vec(data[..index].to_vec()), }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_mknod"), async move { @@ -1731,7 +1721,7 @@ impl Session { .await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; + resp_sender.send_err(&request, err).await; } Ok(entry) => { @@ -1743,13 +1733,7 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_ENTRY_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&entry_out, &mut data, config).unwrap(); - - let _ = resp_sender.send(Either::Left(data)).await; + resp_sender.send2(&out_header, entry_out.as_bytes()).await; } } }); @@ -1760,26 +1744,26 @@ impl Session { &mut self, request: Request, in_header: fuse_in_header, - mut data: &[u8], + data: &[u8], fs: &Arc, ) { - let mkdir_in = match decode_from_slice::(data, get_bincode_config()) { + let (mkdir_in, data) = match fuse_mkdir_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_mknod_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } - Ok((mkdir_in, _)) => mkdir_in, + Ok(r) => r, }; - data = &data[FUSE_MKDIR_IN_SIZE..]; - let name = match get_first_null_position(data) { None => { error!( @@ -1787,7 +1771,9 @@ impl Session { request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -1795,7 +1781,7 @@ impl Session { Some(index) => OsString::from_vec(data[..index].to_vec()), }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_mkdir"), async move { @@ -1815,7 +1801,7 @@ impl Session { .await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; + resp_sender.send_err(&request, err).await; } Ok(entry) => { @@ -1829,11 +1815,10 @@ impl Session { let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_ENTRY_OUT_SIZE); - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&entry_out, &mut data, config).unwrap(); + out_header.write_to_io(&mut data).unwrap(); + entry_out.write_to_io(&mut data).unwrap(); - let _ = resp_sender.send(Either::Left(data)).await; + resp_sender.send2(&out_header, entry_out.as_bytes()).await; } } }); @@ -1854,7 +1839,9 @@ impl Session { request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -1862,7 +1849,7 @@ impl Session { Some(index) => OsString::from_vec(data[..index].to_vec()), }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_unlink"), async move { @@ -1883,11 +1870,7 @@ impl Session { unique: request.unique, }; - - let data = encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened"); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender.send1(&out_header).await; }); } @@ -1906,7 +1889,9 @@ impl Session { request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -1914,7 +1899,7 @@ impl Session { Some(index) => OsString::from_vec(data[..index].to_vec()), }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_rmdir"), async move { @@ -1935,10 +1920,7 @@ impl Session { unique: request.unique, }; - let data = encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened"); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender.send1(&out_header).await; }); } @@ -1947,26 +1929,26 @@ impl Session { &mut self, request: Request, in_header: fuse_in_header, - mut data: &[u8], + data: &[u8], fs: &Arc, ) { - let rename_in = match decode_from_slice::(data, get_bincode_config()) { + let (rename_in, data) = match fuse_rename_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_rename_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } - Ok((rename_in, _)) => rename_in, + Ok(r) => r, }; - data = &data[FUSE_RENAME_IN_SIZE..]; - let (name, first_null_index) = match get_first_null_position(data) { None => { error!( @@ -1974,7 +1956,9 @@ impl Session { request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -1982,7 +1966,7 @@ impl Session { Some(index) => (OsString::from_vec(data[..index].to_vec()), index), }; - data = &data[first_null_index + 1..]; + let data = &data[first_null_index + 1..]; let new_name = match get_first_null_position(data) { None => { @@ -1991,7 +1975,9 @@ impl Session { request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -1999,7 +1985,7 @@ impl Session { Some(index) => OsString::from_vec(data[..index].to_vec()), }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_rename"), async move { @@ -2029,10 +2015,7 @@ impl Session { unique: request.unique, }; - let data = encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened"); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender.send1(&out_header).await; }); } @@ -2041,26 +2024,26 @@ impl Session { &mut self, request: Request, in_header: fuse_in_header, - mut data: &[u8], + data: &[u8], fs: &Arc, ) { - let link_in = match decode_from_slice::(data, get_bincode_config()) { + let (link_in, data) = match fuse_link_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_link_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } - Ok((link_in, _)) => link_in, + Ok((link_in, data)) => (link_in, data), }; - data = &data[FUSE_LINK_IN_SIZE..]; - let name = match get_first_null_position(data) { None => { error!( @@ -2068,7 +2051,9 @@ impl Session { request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -2076,7 +2061,7 @@ impl Session { Some(index) => OsString::from_vec(data[..index].to_vec()), }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_link"), async move { @@ -2090,7 +2075,7 @@ impl Session { .await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; + resp_sender.send_err(&request, err).await; } Ok(entry) => { @@ -2102,13 +2087,7 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_ENTRY_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&entry_out, &mut data, config).unwrap(); - - let _ = resp_sender.send(Either::Left(data)).await; + resp_sender.send2(&out_header, entry_out.as_bytes()).await; } } }); @@ -2122,14 +2101,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let open_in = match decode_from_slice::(data, get_bincode_config()) { + let open_in = match fuse_open_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_open_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -2137,7 +2118,7 @@ impl Session { Ok((open_in, _)) => open_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_open"), async move { @@ -2148,8 +2129,7 @@ impl Session { let opened = match fs.open(request, in_header.nodeid, open_in.flags).await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } @@ -2164,13 +2144,7 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_OPEN_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&open_out, &mut data, config).unwrap(); - - let _ = resp_sender.send(Either::Left(data)).await; + resp_sender.send2(&out_header, open_out.as_bytes()).await; }); } @@ -2182,14 +2156,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let read_in = match decode_from_slice::(data, get_bincode_config()) { + let read_in = match fuse_read_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_read_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -2197,7 +2173,7 @@ impl Session { Ok((read_in, _)) => read_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_read"), async move { @@ -2217,8 +2193,7 @@ impl Session { .await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } @@ -2235,14 +2210,7 @@ impl Session { unique: request.unique, }; - let mut data_buf = Vec::with_capacity(FUSE_OUT_HEADER_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data_buf, config).unwrap(); - - let _ = resp_sender - .send(Either::Right((data_buf, reply_data))) - .await; + resp_sender.send2(&out_header, &reply_data).await; }); } @@ -2251,37 +2219,39 @@ impl Session { &mut self, request: Request, in_header: fuse_in_header, - mut data: &[u8], + data: &[u8], fs: &Arc, ) { - let write_in = match decode_from_slice::(data, get_bincode_config()) { + let (write_in, data) = match fuse_write_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_write_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } - Ok((write_in, _)) => write_in, + Ok(r) => r, }; - data = &data[FUSE_WRITE_IN_SIZE..]; - if write_in.size as usize != data.len() { error!("fuse_write_in body len is invalid"); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } let data = data.to_vec(); - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_write"), async move { @@ -2303,8 +2273,7 @@ impl Session { .await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } @@ -2319,19 +2288,13 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_WRITE_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&write_out, &mut data, config).unwrap(); - - let _ = resp_sender.send(Either::Left(data)).await; + resp_sender.send2(&out_header, write_out.as_bytes()).await; }); } #[instrument(skip(self, fs))] async fn handle_statfs(&mut self, request: Request, in_header: fuse_in_header, fs: &Arc) { - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_statfs"), async move { @@ -2342,8 +2305,7 @@ impl Session { let fs_stat = match fs.statfs(request, in_header.nodeid).await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } @@ -2358,13 +2320,7 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_STATFS_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&statfs_out, &mut data, config).unwrap(); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender.send2(&out_header, statfs_out.as_bytes()).await; }); } @@ -2376,14 +2332,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let release_in = match decode_from_slice::(data, get_bincode_config()) { + let release_in = match fuse_release_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_release_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -2391,7 +2349,7 @@ impl Session { Ok((release_in, _)) => release_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_release"), async move { @@ -2429,10 +2387,7 @@ impl Session { unique: request.unique, }; - let data = encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened"); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender.send1(&out_header).await; }); } @@ -2444,14 +2399,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let fsync_in = match decode_from_slice::(data, get_bincode_config()) { + let fsync_in = match fuse_fsync_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_fsync_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -2459,7 +2416,7 @@ impl Session { Ok((fsync_in, _)) => fsync_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_fsync"), async move { @@ -2485,10 +2442,7 @@ impl Session { unique: request.unique, }; - let data = encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened"); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender.send1(&out_header).await; }); } @@ -2497,26 +2451,26 @@ impl Session { &mut self, request: Request, in_header: fuse_in_header, - mut data: &[u8], + data: &[u8], fs: &Arc, ) { - let setxattr_in = match decode_from_slice::(data, get_bincode_config()) { + let (setxattr_in, data) = match fuse_setxattr_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_setxattr_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } - Ok((setxattr_in, _)) => setxattr_in, + Ok(r) => r, }; - data = &data[FUSE_SETXATTR_IN_SIZE..]; - let (name, first_null_index) = match get_first_null_position(data) { None => { error!( @@ -2524,7 +2478,9 @@ impl Session { request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -2532,21 +2488,27 @@ impl Session { Some(index) => (OsString::from_vec(data[..index].to_vec()), index), }; - data = &data[first_null_index + 1..]; + let data = &data[first_null_index + 1..]; // setxattr "size" field specifies size of only "Value" part of data if setxattr_in.size as usize != data.len() { error!( - "fuse_setxattr_in value field data length is not right, request unique {} setxattr_in.size={} data.len={}", request.unique, setxattr_in.size, data.len()); + "fuse_setxattr_in value field data length is not right, request unique {} setxattr_in.size={} data.len={}", + request.unique, + setxattr_in.size, + data.len() + ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } let data = data.to_vec(); - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_setxattr"), async move { @@ -2578,10 +2540,7 @@ impl Session { unique: request.unique, }; - let data = encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened"); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender.send1(&out_header).await; }); } @@ -2590,31 +2549,33 @@ impl Session { &mut self, request: Request, in_header: fuse_in_header, - mut data: &[u8], + data: &[u8], fs: &Arc, ) { - let getxattr_in = match decode_from_slice::(data, get_bincode_config()) { + let (getxattr_in, data) = match fuse_getxattr_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_getxattr_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } - Ok((getxattr_in, _)) => getxattr_in, + Ok(r) => r, }; - data = &data[FUSE_GETXATTR_IN_SIZE..]; - let name = match get_first_null_position(data) { None => { error!("fuse_getxattr_in body has no null {}", request.unique); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -2622,7 +2583,7 @@ impl Session { Some(index) => OsString::from_vec(data[..index].to_vec()), }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_getxattr"), async move { @@ -2636,31 +2597,26 @@ impl Session { .await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } Ok(xattr) => xattr, }; - let data = match xattr { + match xattr { ReplyXAttr::Size(size) => { let getxattr_out = fuse_getxattr_out { size, _padding: 0 }; let out_header = fuse_out_header { len: (FUSE_OUT_HEADER_SIZE + FUSE_GETXATTR_OUT_SIZE) as u32, - error: libc::ERANGE, + error: 0, unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_STATFS_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&getxattr_out, &mut data, config).unwrap(); - - Either::Left(data) + resp_sender + .send2(&out_header, getxattr_out.as_bytes()) + .await; } ReplyXAttr::Data(xattr_data) => { @@ -2671,17 +2627,9 @@ impl Session { error: 0, unique: request.unique, }; - - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - - Either::Right((data, xattr_data)) + resp_sender.send2(&out_header, &xattr_data).await; } }; - - let _ = resp_sender.send(data).await; }); } @@ -2693,14 +2641,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let listxattr_in = match decode_from_slice::(data, get_bincode_config()) { + let listxattr_in = match fuse_getxattr_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_getxattr_in in listxattr failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -2708,7 +2658,7 @@ impl Session { Ok((listxattr_in, _)) => listxattr_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_listxattr"), async move { @@ -2722,15 +2672,14 @@ impl Session { .await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } Ok(xattr) => xattr, }; - let data = match xattr { + match xattr { ReplyXAttr::Size(size) => { let getxattr_out = fuse_getxattr_out { size, _padding: 0 }; @@ -2740,13 +2689,9 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_STATFS_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&getxattr_out, &mut data, config).unwrap(); - - Either::Left(data) + resp_sender + .send2(&out_header, getxattr_out.as_bytes()) + .await; } ReplyXAttr::Data(xattr_data) => { @@ -2758,16 +2703,9 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - - Either::Right((data, xattr_data)) + resp_sender.send2(&out_header, &xattr_data).await; } }; - - let _ = resp_sender.send(data).await; }); } @@ -2786,7 +2724,9 @@ impl Session { request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -2794,7 +2734,7 @@ impl Session { Some(index) => OsString::from_vec(data[..index].to_vec()), }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_removexattr"), async move { @@ -2816,10 +2756,7 @@ impl Session { unique: request.unique, }; - let data = encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened"); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender.send1(&out_header).await; }); } @@ -2831,14 +2768,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let flush_in = match decode_from_slice::(data, get_bincode_config()) { + let flush_in = match fuse_flush_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_flush_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -2846,7 +2785,7 @@ impl Session { Ok((flush_in, _)) => flush_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_flush"), async move { @@ -2870,10 +2809,7 @@ impl Session { unique: request.unique, }; - let data = encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened"); - - let _ = resp_sender.send(Either::Left(data)).await; + resp_sender.send1(&out_header).await; }); } @@ -2885,14 +2821,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let open_in = match decode_from_slice::(data, get_bincode_config()) { + let open_in = match fuse_open_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_open_in in opendir failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -2900,7 +2838,7 @@ impl Session { Ok((open_in, _)) => open_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_opendir"), async move { @@ -2911,8 +2849,7 @@ impl Session { let reply_open = match fs.opendir(request, in_header.nodeid, open_in.flags).await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } @@ -2927,13 +2864,7 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_OPEN_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&open_out, &mut data, config).unwrap(); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender.send2(&out_header, open_out.as_bytes()).await; }); } @@ -2946,27 +2877,30 @@ impl Session { fs: &Arc, ) { if self.mount_options.force_readdir_plus { - reply_error_in_place(libc::ENOSYS.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::ENOSYS.into()) + .await; return; } - let read_in = match decode_from_slice::(data, get_bincode_config()) { + let read_in = match fuse_read_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_read_in in readdir failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; - + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } Ok((read_in, _)) => read_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_readdir"), async move { @@ -2980,8 +2914,7 @@ impl Session { .await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } @@ -2998,8 +2931,7 @@ impl Session { while let Some(entry) = entries.next().await { let entry = match entry { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } @@ -3024,8 +2956,7 @@ impl Session { r#type: mode_from_kind_and_perm(entry.kind, 0) >> 12, }; - let config = get_bincode_config(); - encode_into_std_write(&dir_entry, &mut entry_data, config).unwrap(); + dir_entry.write_to_io(&mut entry_data).unwrap(); entry_data.extend_from_slice(name.as_bytes()); @@ -3033,22 +2964,13 @@ impl Session { entry_data.resize(entry_data.len() + padding_size, 0); } - // TODO find a way to avoid multi allocate - let out_header = fuse_out_header { len: (FUSE_OUT_HEADER_SIZE + entry_data.len()) as u32, error: 0, unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - - let _ = resp_sender - .send(Either::Right((data, entry_data.into()))) - .await; + let _ = resp_sender.send2(&out_header, &entry_data).await; }); } @@ -3060,14 +2982,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let release_in = match decode_from_slice::(data, get_bincode_config()) { + let release_in = match fuse_release_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_release_in in releasedir failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -3075,7 +2999,7 @@ impl Session { Ok((release_in, _)) => release_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_releasedir"), async move { @@ -3099,10 +3023,7 @@ impl Session { unique: request.unique, }; - let data = encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened"); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender.send1(&out_header).await; }); } @@ -3114,14 +3035,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let fsync_in = match decode_from_slice::(data, get_bincode_config()) { + let fsync_in = match fuse_fsync_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_fsync_in in fsyncdir failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -3129,7 +3052,7 @@ impl Session { Ok((fsync_in, _)) => fsync_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_fsyncdir"), async move { @@ -3155,10 +3078,7 @@ impl Session { unique: request.unique, }; - let data = encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened"); - - let _ = resp_sender.send(Either::Left(data)).await; + resp_sender.send1(&out_header).await; }); } @@ -3171,14 +3091,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let getlk_in = match decode_from_slice::(data, get_bincode_config()) { + let getlk_in = match fuse_lk_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_lk_in in getlk failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -3186,7 +3108,7 @@ impl Session { Ok((getlk_in, _)) => getlk_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_getlk"), async move { @@ -3209,8 +3131,7 @@ impl Session { .await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } @@ -3225,13 +3146,7 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_LK_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&getlk_out, &mut data, config).unwrap(); - - let _ = resp_sender.send(Either::Left(data)).await; + resp_sender.send2(&out_header, getlk_out.as_bytes()).await; }); } @@ -3245,7 +3160,7 @@ impl Session { block: bool, fs: &Arc, ) { - let setlk_in = match decode_from_slice::(data, get_bincode_config()) { + let setlk_in = match fuse_lk_in::read_from_prefix(data) { Err(err) => { let opcode = if block { fuse_opcode::FUSE_SETLKW @@ -3258,7 +3173,9 @@ impl Session { opcode, err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -3266,7 +3183,7 @@ impl Session { Ok((setlk_in, _)) => setlk_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_setlk"), async move { @@ -3300,10 +3217,7 @@ impl Session { unique: request.unique, }; - let data = encode_to_vec(&out_header, get_bincode_config()) - .expect("can't serialize into vec"); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender.send1(&out_header).await; }); } @@ -3315,14 +3229,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let access_in = match decode_from_slice::(data, get_bincode_config()) { + let access_in = match fuse_access_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_access_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -3330,7 +3246,7 @@ impl Session { Ok((access_in, _)) => access_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_access"), async move { @@ -3354,10 +3270,7 @@ impl Session { debug!("access response {}", resp_value); - let data = encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened"); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender.send1(&out_header).await; }); } @@ -3366,26 +3279,26 @@ impl Session { &mut self, request: Request, in_header: fuse_in_header, - mut data: &[u8], + data: &[u8], fs: &Arc, ) { - let create_in = match decode_from_slice::(data, get_bincode_config()) { + let (create_in, data) = match fuse_create_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_create_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } - Ok((create_in, _)) => create_in, + Ok(r) => r, }; - data = &data[FUSE_CREATE_IN_SIZE..]; - let name = match get_first_null_position(data) { None => { error!( @@ -3393,7 +3306,9 @@ impl Session { request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -3401,7 +3316,7 @@ impl Session { Some(index) => OsString::from_vec(data[..index].to_vec()), }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_create"), async move { @@ -3421,8 +3336,7 @@ impl Session { .await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } @@ -3437,28 +3351,24 @@ impl Session { unique: request.unique, }; - let mut data = - Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_ENTRY_OUT_SIZE + FUSE_OPEN_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&entry_out, &mut data, config).unwrap(); - encode_into_std_write(&open_out, &mut data, config).unwrap(); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender + .send3(&out_header, entry_out.as_bytes(), open_out.as_bytes()) + .await; }); } #[instrument(skip(self, data, fs))] async fn handle_interrupt(&mut self, request: Request, data: &[u8], fs: &Arc) { - let interrupt_in = match decode_from_slice::(data, get_bincode_config()) { + let interrupt_in = match fuse_interrupt_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_interrupt_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -3466,7 +3376,7 @@ impl Session { Ok((interrupt_in, _)) => interrupt_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_interrupt"), async move { @@ -3487,10 +3397,7 @@ impl Session { unique: request.unique, }; - let data = encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened"); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender.send1(&out_header).await; }); } @@ -3502,14 +3409,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let bmap_in = match decode_from_slice::(data, get_bincode_config()) { + let bmap_in = match fuse_bmap_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_bmap_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -3517,7 +3426,7 @@ impl Session { Ok((bmap_in, _)) => bmap_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_bmap"), async move { @@ -3531,8 +3440,7 @@ impl Session { .await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } @@ -3547,13 +3455,7 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_BMAP_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&bmap_out, &mut data, config).unwrap(); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender.send2(&out_header, bmap_out.as_bytes()).await; }); } @@ -3565,14 +3467,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let poll_in = match decode_from_slice::(data, get_bincode_config()) { + let poll_in = match fuse_poll_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_poll_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -3580,7 +3484,7 @@ impl Session { Ok((poll_in, _)) => poll_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); let notify = self.get_notify(); @@ -3610,8 +3514,7 @@ impl Session { .await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } @@ -3626,13 +3529,7 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_POLL_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&poll_out, &mut data, config).unwrap(); - - let _ = resp_sender.send(Either::Left(data)).await; + let _ = resp_sender.send2(&out_header, poll_out.as_bytes()).await; }); } @@ -3641,26 +3538,24 @@ impl Session { &mut self, request: Request, in_header: fuse_in_header, - mut data: &[u8], + data: &[u8], fs: &Arc, ) { - let resp_sender = self.response_sender.clone(); - - let notify_retrieve_in: fuse_notify_retrieve_in = match decode_from_slice(data, get_bincode_config()) { - Err(err) => { - error!( - "deserialize fuse_notify_retrieve_in failed {}, request unique {}", - err, request.unique - ); + let resp_sender = self.response_sender().clone(); - // TODO need to reply or not? - return; - } + let (notify_retrieve_in, data) = match fuse_notify_retrieve_in::read_from_prefix(data) { + Err(err) => { + error!( + "deserialize fuse_notify_retrieve_in failed {}, request unique {}", + err, request.unique + ); - Ok((notify_retrieve_in, _)) => notify_retrieve_in, - }; + // TODO need to reply or not? + return; + } - data = &data[FUSE_NOTIFY_RETRIEVE_IN_SIZE..]; + Ok(r) => r, + }; if data.len() < notify_retrieve_in.size as usize { error!( @@ -3686,20 +3581,20 @@ impl Session { ) .await { - reply_error_in_place(err, request, resp_sender).await; + resp_sender.send_err(&request, err).await; } }); } - #[instrument(skip(self, data, fs))] + #[instrument(level = "debug", skip(self, data, fs))] async fn handle_batch_forget( &mut self, request: Request, _in_header: fuse_in_header, - mut data: &[u8], + data: &[u8], fs: &Arc, ) { - let batch_forget_in: fuse_batch_forget_in = match decode_from_slice(data, get_bincode_config()) { + let (batch_forget_in, mut data) = match fuse_batch_forget_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_batch_forget_in failed {}, request unique {}", @@ -3710,25 +3605,26 @@ impl Session { return; } - Ok((batch_forget_in, _)) => batch_forget_in, + Ok(r) => r, }; let mut forgets = vec![]; - data = &data[FUSE_BATCH_FORGET_IN_SIZE..]; - // TODO if has less data, should I return error? while data.len() >= FUSE_FORGET_ONE_SIZE { - match decode_from_slice::(data, get_bincode_config()) { + match fuse_forget_one::read_from_prefix(data) { Err(err) => { - error!("deserialize fuse_batch_forget_in body fuse_forget_one failed {}, request unique {}", err, request.unique); + error!( + "deserialize fuse_batch_forget_in body fuse_forget_one failed {}, request unique {}", + err, request.unique + ); // no need to reply return; } - Ok((forget_one, _)) => { - data = &data[FUSE_FORGET_ONE_SIZE..]; + Ok((forget_one, remaining_data)) => { + data = remaining_data; forgets.push(forget_one); } @@ -3766,14 +3662,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let fallocate_in: fuse_fallocate_in = match decode_from_slice(data, get_bincode_config()) { + let fallocate_in: fuse_fallocate_in = match fuse_fallocate_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_fallocate_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -3781,7 +3679,7 @@ impl Session { Ok((fallocate_in, _)) => fallocate_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_fallocate"), async move { @@ -3812,11 +3710,7 @@ impl Session { unique: request.unique, }; - let config = get_bincode_config(); - let data = encode_to_vec(&out_header, config) - .expect("won't happened"); - - let _ = resp_sender.send(Either::Left(data)).await; + resp_sender.send1(&out_header).await; }); } @@ -3828,14 +3722,16 @@ impl Session { data: &[u8], fs: &Arc, ) { - let readdirplus_in: fuse_read_in = match decode_from_slice(data, get_bincode_config()) { + let readdirplus_in: fuse_read_in = match fuse_read_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_read_in in readdirplus failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -3843,7 +3739,7 @@ impl Session { Ok((readdirplus_in, _)) => readdirplus_in, }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_readdirplus"), async move { @@ -3863,8 +3759,7 @@ impl Session { .await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } @@ -3881,8 +3776,7 @@ impl Session { while let Some(entry) = entries.next().await { let entry = match entry { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } @@ -3920,9 +3814,7 @@ impl Session { }, }; - let config = get_bincode_config(); - encode_into_std_write(&dir_entry, &mut entry_data, config) - .expect("won't happened"); + dir_entry.write_to_io(&mut entry_data).unwrap(); entry_data.extend_from_slice(name.as_bytes()); @@ -3930,23 +3822,13 @@ impl Session { entry_data.resize(entry_data.len() + padding_size, 0); } - // TODO find a way to avoid multi allocate - let out_header = fuse_out_header { len: (FUSE_OUT_HEADER_SIZE + entry_data.len()) as u32, error: 0, unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config) - .expect("won't happened"); - - let _ = resp_sender - .send(Either::Right((data, entry_data.into()))) - .await; + resp_sender.send2(&out_header, &entry_data).await; }); } @@ -3955,26 +3837,26 @@ impl Session { &mut self, request: Request, in_header: fuse_in_header, - mut data: &[u8], + data: &[u8], fs: &Arc, ) { - let rename2_in: fuse_rename2_in = match decode_from_slice(data, get_bincode_config()) { + let (rename2_in, data) = match fuse_rename2_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_rename2_in failed {}, request unique {}", err, request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } - Ok((rename2_in, _)) => rename2_in, + Ok(r) => r, }; - data = &data[FUSE_RENAME2_IN_SIZE..]; - let (old_name, index) = match get_first_null_position(data) { None => { error!( @@ -3982,7 +3864,9 @@ impl Session { request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -3990,7 +3874,7 @@ impl Session { Some(index) => (OsString::from_vec(data[..index].to_vec()), index), }; - data = &data[index + 1..]; + let data = &data[index + 1..]; let new_name = match get_first_null_position(data) { None => { @@ -3999,7 +3883,9 @@ impl Session { request.unique ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; return; } @@ -4007,7 +3893,7 @@ impl Session { Some(index) => OsString::from_vec(data[..index].to_vec()), }; - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); let fs = fs.clone(); spawn(debug_span!("fuse_rename2"), async move { @@ -4043,10 +3929,7 @@ impl Session { unique: request.unique, }; - let config = get_bincode_config(); - let data = encode_to_vec(&out_header, config).unwrap(); - - let _ = resp_sender.send(Either::Left(data)).await; + resp_sender.send1(&out_header).await; }); } @@ -4058,17 +3941,15 @@ impl Session { data: &[u8], fs: &Arc, ) { - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); - let lseek_in = match decode_from_slice::(data, get_bincode_config()) { + let lseek_in = match fuse_lseek_in::read_from_prefix(data) { Err(err) => { error!( "deserialize fuse_lseek_in failed {}, request unique {}", err, request.unique ); - - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; - + resp_sender.send_err(&request, libc::EINVAL.into()).await; return; } @@ -4094,8 +3975,7 @@ impl Session { .await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } @@ -4109,14 +3989,7 @@ impl Session { error: 0, unique: request.unique, }; - - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_OPEN_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&lseek_out, &mut data, config).unwrap(); - - let _ = resp_sender.send(Either::Left(data)).await; + resp_sender.send2(&out_header, lseek_out.as_bytes()).await; }); } @@ -4128,22 +4001,24 @@ impl Session { data: &[u8], fs: &Arc, ) { - let mut resp_sender = self.response_sender.clone(); + let resp_sender = self.response_sender().clone(); - let copy_file_range_in: fuse_copy_file_range_in = match decode_from_slice(data, get_bincode_config()) { - Err(err) => { - error!( - "deserialize fuse_copy_file_range_in failed {}, request unique {}", - err, request.unique - ); + let copy_file_range_in = match fuse_copy_file_range_in::read_from_prefix(data) { + Err(err) => { + error!( + "deserialize fuse_copy_file_range_in failed {}, request unique {}", + err, request.unique + ); - reply_error_in_place(libc::EINVAL.into(), request, &self.response_sender).await; + self.response_sender() + .send_err(&request, libc::EINVAL.into()) + .await; - return; - } + return; + } - Ok((copy_file_range_in, _)) => copy_file_range_in, - }; + Ok((copy_file_range_in, _)) => copy_file_range_in, + }; let fs = fs.clone(); @@ -4168,8 +4043,7 @@ impl Session { .await { Err(err) => { - reply_error_in_place(err, request, resp_sender).await; - + resp_sender.send_err(&request, err).await; return; } @@ -4184,33 +4058,11 @@ impl Session { unique: request.unique, }; - let mut data = Vec::with_capacity(FUSE_OUT_HEADER_SIZE + FUSE_WRITE_OUT_SIZE); - - let config = get_bincode_config(); - encode_into_std_write(&out_header, &mut data, config).unwrap(); - encode_into_std_write(&write_out, &mut data, config).unwrap(); - - let _ = resp_sender.send(Either::Left(data)).await; + resp_sender.send2(&out_header, write_out.as_bytes()).await; }); } } -async fn reply_error_in_place(err: Errno, request: Request, sender: S) -where - S: Sink, (Vec, Bytes)>>, -{ - let out_header = fuse_out_header { - len: FUSE_OUT_HEADER_SIZE as u32, - error: err.into(), - unique: request.unique, - }; - - let data = encode_to_vec(&out_header, get_bincode_config()) - .expect("won't happened"); - - let _ = pin!(sender).send(Either::Left(data)).await; -} - #[inline] fn spawn(span: Span, fut: F) where @@ -4223,3 +4075,37 @@ where #[cfg(all(not(feature = "tokio-runtime"), feature = "async-io-runtime"))] task::spawn(fut.instrument(span)).detach() } + +#[cfg(test)] +mod reply_error_policy_tests { + use std::io::Error as IoError; + + use super::ResponseSender; + + /// A failed reply to a single request caused by a transient/per-request + /// condition must NOT tear down the session. These are the errors macFUSE + /// can hand back under heavy concurrent load. + #[test] + fn transient_reply_errors_are_recoverable() { + for code in [libc::ENOENT, libc::EINVAL, libc::EAGAIN, libc::EINTR] { + let kind = IoError::from_raw_os_error(code).kind(); + assert!( + ResponseSender::is_recoverable_reply_error(kind), + "errno {code} ({kind:?}) should be recoverable" + ); + } + } + + /// A dead connection must stay fatal so a clean unmount still stops the + /// session (otherwise it would spin forever). + #[test] + fn connection_gone_errors_are_fatal() { + for code in [libc::ENODEV, libc::EBADF, libc::EPIPE, libc::ECONNRESET] { + let kind = IoError::from_raw_os_error(code).kind(); + assert!( + !ResponseSender::is_recoverable_reply_error(kind), + "errno {code} ({kind:?}) should be fatal" + ); + } + } +}