diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..7561a06b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,19 @@ +target +.git +.github +.devcontainer +.fleet +.vscode +.gitignore +.gitmodules +.gitattributes +.dockerignore +.editorconfig +.env +.env.example +.envrc +.envrc.example +.envrc.local +.envrc.local.example +.envrc.local.template +.envrc.template \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md similarity index 99% rename from CODE_OF_CONDUCT.md rename to .github/CODE_OF_CONDUCT.md index 41719f52..c1caa7c9 100644 --- a/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -60,7 +60,7 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -radumarias@gmail.com. +hello@xorio.rs. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..bc67abd5 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## How to contribute + +> [!IMPORTANT] +> **These steps are more particular to this repo but mainly apply to all repositories; change the specifics.** + +Unless you explicitly state otherwise, any Contribution intentionally submitted for inclusion in this project by you, as +defined in the Apache License shall be dual-licensed as above, without any additional terms or conditions. + +1. Join the project's [slack](https://bit.ly/3UU1oXi) (this is for the `rencfs` project, please use the one for your project) and join the `#dev-beginners` channel (or any relevant ones) +2. **Ask the owner of the repository to add your GitHub username to the repository** so that you can work on issues and + be able to create your own branches and not need to fork the repo + +# Devs & QA automation (which steps apply) + +3. Become familiar with docs and code by reading the [ramp-up](../docs/readme/Ramp-up.md) guide +4. Pick an open issue or a task in the corresponding [project](https://github.com/users/radumarias/projects/1) for the + repo you'll work on. You can + see [good for first issues](https://github.com/xorio/rencfs/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) + that you can pick from +5. **Assign the issues you are working on to you and move them to the corresponding status column as you progress + . If the task is not an issue yet, convert it to an issue first** +6. Make the changes in your branch +7. Add docs as they apply +8. Add tests, benchmarks, and examples for your changes, if applicable +9. `cargo fmt --all` to format the code. You can configure your `IDE` to do this on + save, [RustRover](https://www.jetbrains.com/help/rust/rustfmt.html) + and [VSCode](https://code.visualstudio.com/docs/languages/rust#_formatting) +10. `cargo clippy --all --release` and fix any errors +11. **DON'T INCREASE THE VERSION NUMBER IN `Cargo.toml`, WE WILL DO THAT WHEN RELEASING** +12. Create a `git` `push hook` file in `.git/hooks/pre-push` with [pre-push](scripts/git-hooks/linux-macos/pre-push) + content + on `Linux` and `macOS`, and [pre-push](scripts/git-hooks/windows/pre-push) on `Windows`. + Make it executable in Linux and macOS + with `chmod +x .git/hooks/pre-push` .This will run when you do `git push` and will make the push quite + slow, but please give it time to complete as this helps to fix any issues locally and not rely just on + running `ci` on GitHub when you create the PR +13. Commit the changes with the commit message following the [Commit Message Guidelines](https://gist.github.com/radumarias/5b5374f3ed022c99d617eb849aafd069) +14. Push your changes, and if there are any errors, fix them before you push them +15. Create a `PR` back to the `parent` repo targeting the `main` branch with the title as the GitHub issue title, including `#ID`. Also, include the link to the GitHub issue in the description, saying like `Fix for ` for bugs or `Implementation for ` for features and others +16. Request review from owners of the repository by adding them to the `Reviewers` field +17. After you create the PR, link it to the GH issue from the mid-right of the page, press the gear icon from the below image, select `xoriors/rencfs`, then write the issue number and select it. This will link the two, and when the PR is merged, it will close the issue too + ![image](https://github.com/user-attachments/assets/5ac0313d-4175-44d1-8d1e-d18da773ab32) +18. In the project, move the item to `In Code Review` +19. Monitor the checks (GitHub actions runs) and fix the code if they are failing +20. Respond to any comments +21. **DON'T MERGE THE PR YOURSELF. LEAVE THAT TO REPOSITORY OWNERS** +22. In the end, ideally, it will be merged into `main` + +# QA manual + +Please follow these [steps](../docs/readme/Testing.md). diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea78..c0bc109f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -17,21 +17,26 @@ Steps to reproduce the behavior: 3. Scroll down to '....' 4. See error +**Actual behavior** +A clear and concise description of what you exaclty is happening. + **Expected behavior** A clear and concise description of what you expected to happen. -**Screenshots** -If applicable, add screenshots to help explain your problem. +**Screenshots or screen recordings** +If applicable, add screenshots or screen recordings to help explain your problem. + +**Logs** +Collectt last 100 lines from terminal and attach those to the issue. **Desktop (please complete the following information):** - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - Version [e.g. 22] + - Last 100 lines from logs **Smartphone (please complete the following information):** - Device: [e.g. iPhone6] - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - Version [e.g. 22] **Additional context** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..f186b482 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,44 @@ +## Title + +If this is related to a GitHub issue, please have use the issue's title, inlcude `#ID` too. + +## Description + +Please include a summary of the changes and the related issue. Describe **why** this change is needed. + +Fixes # (issue) + +--- + +## Type of change + +Please delete options that are not relevant. + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Documentation update + +--- + +## Checklist: + +- [ ] I have performed a self-review of my code +- [ ] I have tested my code on different platforms (if applicable) +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have added necessary documentation (if appropriate) +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes + +--- + +## Screenshots (if applicable) + +Add screenshots to illustrate changes if necessary. + +--- + +## Additional Context + +Add any additional context or information here. diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 00000000..2c1ea16c --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,11 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 0.x.y | :white_check_mark: | + +## Reporting a Vulnerability + +[Report a security vulnerability](https://github.com/radumarias/rencfs/security/advisories/new): Report a security vulnerability. diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 00000000..babe9012 --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,13 @@ +# Support + +For any issues you might have, feel free to contact us on: +- hello@xorio.rs +- [slack](https://bit.ly/3UU1oXi) in the `#support` channel +- [LinkedIn](https://www.linkedin.com/company/xorio) + +# Issues + +If you find any issues or vulnerabilities or you'd like a feature, please follow these steps: +- [Open a bug](https://github.com/radumarias/rencfs/issues/new?assignees=&labels=&projects=&template=bug_report.md&title=): Create a report to help us improve. +- [Report a security vulnerability](https://github.com/radumarias/rencfs/security/advisories/new): Report a security vulnerability. +- [Feature request](https://github.com/radumarias/rencfs/issues/new?assignees=&labels=&projects=&template=feature_request.md&title=): Suggest an idea for this project. diff --git a/.github/workflows/build_and_tests_reusable.yaml b/.github/workflows/build_and_tests_reusable.yaml index 578a524b..ae206ce7 100644 --- a/.github/workflows/build_and_tests_reusable.yaml +++ b/.github/workflows/build_and_tests_reusable.yaml @@ -51,12 +51,16 @@ jobs: -A clippy::type_complexity shell: bash - - name: doc - run: cargo doc --workspace --all-features --no-deps - - name: tests if: matrix.os != 'windows-latest' - run: cargo test --release --all --all-features + run: cargo test --release --all --all-features -- --skip keyring + + - name: bench + if: matrix.os != 'windows-latest' + run: cargo bench --workspace --all-targets --all-features -- --skip keyring + + - name: doc + run: cargo doc --workspace --all-features --no-deps - name: test package if: matrix.os == 'ubuntu-latest' @@ -82,6 +86,7 @@ jobs: - name: java-bridge clippy run: | + cd java-bridge cargo clippy --all-targets --release -- \ -A clippy::similar_names \ -A clippy::too_many_arguments \ @@ -91,11 +96,19 @@ jobs: -A clippy::type_complexity shell: bash - - name: java-bridge doc - run: cargo doc --workspace --all-features --no-deps - - name: java-bridge tests if: matrix.os != 'windows-latest' run: | cd java-bridge cargo test --release --all --all-features + + - name: java-bridge bench + if: matrix.os != 'windows-latest' + run: | + cd java-bridge + cargo bench --workspace --all-targets --all-features + + - name: java-bridge doc + run: | + cd java-bridge + cargo doc --workspace --all-features --no-deps diff --git a/.github/workflows/package_reusable.yaml b/.github/workflows/package_reusable.yaml index 4b9c8621..c4b09861 100644 --- a/.github/workflows/package_reusable.yaml +++ b/.github/workflows/package_reusable.yaml @@ -81,6 +81,7 @@ jobs: - name: build push = ${{ inputs.upload_artifacts }} uses: docker/build-push-action@v6 with: + file: docker/Dockerfile push: ${{ inputs.upload_artifacts }} tags: ${{ env.REGISTRY_USER }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} sbom: true diff --git a/.idea/rencfs.iml b/.idea/rencfs.iml index cfd3cf35..0ecc648e 100644 --- a/.idea/rencfs.iml +++ b/.idea/rencfs.iml @@ -8,6 +8,7 @@ + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 19863ebd..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,24 +0,0 @@ -## How to contribute - -Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache License, shall be dual-licensed as above, without any additional terms or conditions. - -1. Join [slack](https://join.slack.com/t/rencfs/shared_invite/zt-2o4l1tdkk-VJeWIbO2p6zgeafDISPHbQ) -2. Become familiar with docs and code by reading the [ramp-up](Ramp-up.md) guide -3. **Ask the owner of the repository to add your GitHub username to the repository** so that you can work on issues and be able to create your own branches and not needing to fork the repo -4. Pick an open issue or a task in the corresponding [project](https://github.com/users/radumarias/projects/1) for the repo that you'll be working on. You can see [good for first issues](https://github.com/radumarias/rencfs/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) that you can pick from -5. **Assign the issues you are working on to you and move them to the corresponding status column as you are progressing on them. If the taks is not an issue yet, convert it to issue first** -6. Make the changes in your branch -7. Add docs as they apply -8. Add tests, benchmarks and examples for your changes, if applicable -9. `cargo fmt --all` to format the code. You can configure your `IDE` to do this on - save, [RustRover](https://www.jetbrains.com/help/rust/rustfmt.html) - and [VSCode](https://code.visualstudio.com/docs/languages/rust#_formatting) -10. `cargo clippy --all --release` and fix any errors -11. **DON'T INCREASE THE VERSION NUMBER IN `Cargo.toml`, WE WILL DO THAN WHEN RELEASING** -12. Create a `git` `commit hook` file in `.git/hooks/pre-commit` with [this](hooks/linux-macos/pre-commit) content on `Linux` and `MacOS`, and [this](hooks/windows/pre-commit) on `Windows`. Make it executable in Linux and macOS with `chmod +x .git/hooks/pre-commit` .This will run when you do `git commit` and will make the commit to be quite slow, but please give it time to complete as this helps to fix any issues locally and not relying just on running `ci` on GitHub when you create the PR -13. Commit your changes and if there are any errors fix them before you push them -14. Push your changes and create a `PR` back to the `parent` repo targeting the `main` branch and request review from owners of the repository -15. Monitor the checks (GitHub actions runs) and fix the code if they are failing -16. Respond to any comments -17. **DON'T MERGE THE PR YOURSELF, LEAVE THAT TO REPOSITORY OWNERS** -18. In the end, ideally, it will be merged to `main` diff --git a/Cargo.lock b/Cargo.lock index 144ccb3c..39b59179 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,6 +43,12 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anstream" version = "0.6.15" @@ -159,7 +165,7 @@ dependencies = [ "async-task", "concurrent-queue", "fastrand 2.1.1", - "futures-lite 2.3.0", + "futures-lite 2.5.0", "slab", ] @@ -197,17 +203,17 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock 3.4.0", "cfg-if 1.0.0", "concurrent-queue", "futures-io", - "futures-lite 2.3.0", + "futures-lite 2.5.0", "parking", - "polling 3.7.3", + "polling 3.7.4", "rustix 0.38.37", "slab", "tracing", @@ -270,7 +276,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -279,7 +285,7 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" dependencies = [ - "async-io 2.3.4", + "async-io 2.4.0", "async-lock 3.4.0", "atomic-waker", "cfg-if 1.0.0", @@ -316,7 +322,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -327,11 +333,11 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atomic-write-file" -version = "0.1.4" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf54d4588732bdfc5ebc3eb9f74f20e027112fc31de412fc7ff0cd1c6896dae" +checksum = "23e32862ecc63d580f4a5e1436a685f51e0629caeb7a7933e4f017d5e2099e13" dependencies = [ - "nix 0.28.0", + "nix 0.29.0", "rand", ] @@ -438,15 +444,15 @@ dependencies = [ "async-channel", "async-task", "futures-io", - "futures-lite 2.3.0", + "futures-lite 2.5.0", "piper", ] [[package]] name = "bon" -version = "2.3.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97493a391b4b18ee918675fb8663e53646fd09321c58b46afa04e8ce2499c869" +checksum = "f265cdb2e8501f1c952749e78babe8f1937be92c98120e5f78fc72d634682bad" dependencies = [ "bon-macros", "rustversion", @@ -454,15 +460,17 @@ dependencies = [ [[package]] name = "bon-macros" -version = "2.3.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2af3eac944c12cdf4423eab70d310da0a8e5851a18ffb192c0a5e3f7ae1663" +checksum = "38aa5c627cd7706490e5b003d685f8b9d69bc343b1a00b9fdd01e75fdf6827cf" dependencies = [ "darling", "ident_case", + "prettyplease", "proc-macro2", "quote", - "syn 2.0.79", + "rustversion", + "syn 2.0.90", ] [[package]] @@ -483,6 +491,12 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cbc" version = "0.1.2" @@ -515,15 +529,36 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] -name = "cfg_aliases" -version = "0.2.1" +name = "ciborium" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] [[package]] name = "cipher" @@ -566,7 +601,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -630,6 +665,42 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + [[package]] name = "crossbeam-channel" version = "0.5.13" @@ -639,12 +710,37 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -686,7 +782,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -697,7 +793,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -755,7 +851,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -815,9 +911,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ "event-listener 5.3.1", "pin-project-lite", @@ -864,9 +960,9 @@ checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" [[package]] name = "fuse3" -version = "0.7.3" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3175b0f85e74bbabc3bbb1135f9c37a429a26a3fb16169ee6d9608138b0697d8" +checksum = "335dd07e2826edad49c2599ed8151c394d39d2f8efd62c07183816904414ac5c" dependencies = [ "async-notify", "bincode", @@ -922,9 +1018,9 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ "fastrand 2.1.1", "futures-core", @@ -941,7 +1037,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -1002,11 +1098,21 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if 1.0.0", + "crunchy", +] + [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "allocator-api2", "equivalent", @@ -1072,9 +1178,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown", @@ -1110,12 +1216,32 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1279,18 +1405,6 @@ dependencies = [ "memoffset 0.7.1", ] -[[package]] -name = "nix" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" -dependencies = [ - "bitflags 2.6.0", - "cfg-if 1.0.0", - "cfg_aliases 0.1.1", - "libc", -] - [[package]] name = "nix" version = "0.29.0" @@ -1299,7 +1413,7 @@ checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags 2.6.0", "cfg-if 1.0.0", - "cfg_aliases 0.2.1", + "cfg_aliases", "libc", "memoffset 0.9.1", ] @@ -1429,6 +1543,12 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "oorandom" +version = "11.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" + [[package]] name = "ordered-stream" version = "0.2.0" @@ -1508,6 +1628,34 @@ dependencies = [ "futures-io", ] +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + [[package]] name = "polling" version = "2.8.0" @@ -1526,9 +1674,9 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if 1.0.0", "concurrent-queue", @@ -1554,6 +1702,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn 2.0.90", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -1566,9 +1724,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -1612,6 +1770,26 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.5.7" @@ -1667,7 +1845,7 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rencfs" -version = "0.13.86" +version = "0.13.87" dependencies = [ "anyhow", "argon2", @@ -1679,6 +1857,7 @@ dependencies = [ "bon", "bytes", "clap", + "criterion", "ctrlc", "fuse3", "futures-util", @@ -1700,7 +1879,7 @@ dependencies = [ "strum_macros", "subtle", "tempfile", - "thiserror", + "thiserror 2.0.6", "thread_local", "tokio", "tokio-stream", @@ -1806,6 +1985,21 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1846,9 +2040,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -1877,7 +2071,19 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", +] + +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", ] [[package]] @@ -1888,7 +2094,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -2021,7 +2227,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -2043,9 +2249,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -2071,7 +2277,16 @@ version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.64", +] + +[[package]] +name = "thiserror" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +dependencies = [ + "thiserror-impl 2.0.6", ] [[package]] @@ -2082,7 +2297,18 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] @@ -2126,6 +2352,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tokio" version = "1.40.0" @@ -2152,7 +2388,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -2201,7 +2437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror", + "thiserror 1.0.64", "time", "tracing-subscriber", ] @@ -2214,7 +2450,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -2274,7 +2510,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" dependencies = [ "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -2285,7 +2521,7 @@ checksum = "96cbd06a7b648f1603e60d75d9ed295d096b340d30e9f9324f4b512b5d40cd92" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -2341,6 +2577,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2369,7 +2615,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", "wasm-bindgen-shared", ] @@ -2391,7 +2637,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2402,6 +2648,16 @@ version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +[[package]] +name = "web-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "which" version = "6.0.3" @@ -2430,6 +2686,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2693,7 +2958,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9d246909..0830694f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ cargo-features = ["profile-rustflags"] [package] name = "rencfs" description = "An encrypted file system that is mounted with FUSE on Linux. It can be used to create encrypted directories." -version = "0.13.86" +version = "0.13.87" edition = "2021" license = "MIT OR Apache-2.0" authors = ["Radu Marias "] @@ -20,7 +20,7 @@ clap = { version = "4.5.4", features = ["derive", "cargo"] } libc = "0.2.153" serde = { version = "1.0.197", features = ["derive"] } bincode = "1.3.3" -thiserror = "1.0.58" +thiserror = "2.0.6" rand = "0.8.5" rand_core = "0.6.4" base64 = "0.22.1" @@ -49,20 +49,185 @@ hex = "0.4.3" rand_chacha = "0.3.1" lru = "0.12.3" okaywal = "0.3.1" -atomic-write-file = "0.1.4" +atomic-write-file = "0.2.2" tempfile = "3.10.1" async-trait = "0.1.80" blake3 = "=0.1.3" thread_local = "1.1.8" subtle = "2.6.1" -bon = "2.2.0" +bon = "3.3.0" shush-rs = "0.1.10" +criterion = { version = "0.5.1", features = ["html_reports"] } [target.'cfg(target_os = "linux")'.dependencies] -fuse3 = { version = "0.7.2", features = ["tokio-runtime", "unprivileged"] } +fuse3 = { version = "0.8.1", features = ["tokio-runtime", "unprivileged"] } + +[[bench]] +name = "crypto_read" +harness = false + +[lints.rust] +#unsafe_code = "deny" + +[lints.rustdoc] +#all = "warn" +#missing_crate_level_docs = "warn" + +# See also clippy.toml +[lints.clippy] +#as_ptr_cast_mut = "warn" +#await_holding_lock = "warn" +#bool_to_int_with_if = "warn" +#char_lit_as_u8 = "warn" +#checked_conversions = "warn" +#clear_with_drain = "warn" +#cloned_instead_of_copied = "warn" +#dbg_macro = "warn" +#debug_assert_with_mut_call = "warn" +#derive_partial_eq_without_eq = "warn" +#disallowed_macros = "warn" # See clippy.toml +#disallowed_methods = "warn" # See clippy.toml +#disallowed_names = "warn" # See clippy.toml +#disallowed_script_idents = "warn" # See clippy.toml +#disallowed_types = "warn" # See clippy.toml +#doc_link_with_quotes = "warn" +#doc_markdown = "warn" +#empty_enum = "warn" +#empty_enum_variants_with_brackets = "warn" +#enum_glob_use = "warn" +#equatable_if_let = "warn" +#exit = "warn" +#expl_impl_clone_on_copy = "warn" +#explicit_deref_methods = "warn" +#explicit_into_iter_loop = "warn" +#explicit_iter_loop = "warn" +#fallible_impl_from = "warn" +#filter_map_next = "warn" +#flat_map_option = "warn" +#float_cmp_const = "warn" +#fn_params_excessive_bools = "warn" +#fn_to_numeric_cast_any = "warn" +#from_iter_instead_of_collect = "warn" +#get_unwrap = "warn" +#if_let_mutex = "warn" +#implicit_clone = "warn" +#imprecise_flops = "warn" +#index_refutable_slice = "warn" +#inefficient_to_string = "warn" +#infinite_loop = "warn" +#into_iter_without_iter = "warn" +#invalid_upcast_comparisons = "warn" +#iter_filter_is_ok = "warn" +#iter_filter_is_some = "warn" +#iter_not_returning_iterator = "warn" +#iter_on_empty_collections = "warn" +#iter_on_single_items = "warn" +#iter_without_into_iter = "warn" +#large_digit_groups = "warn" +#large_include_file = "warn" +#large_stack_arrays = "warn" +#large_stack_frames = "warn" +#large_types_passed_by_value = "warn" +#let_unit_value = "warn" +#linkedlist = "warn" +#lossy_float_literal = "warn" +#macro_use_imports = "warn" +#manual_assert = "warn" +#manual_clamp = "warn" +#manual_instant_elapsed = "warn" +#manual_is_variant_and = "warn" +#manual_let_else = "warn" +#manual_ok_or = "warn" +#manual_string_new = "warn" +#map_err_ignore = "warn" +#map_flatten = "warn" +#match_on_vec_items = "warn" +#match_same_arms = "warn" +#match_wild_err_arm = "warn" +#match_wildcard_for_single_variants = "warn" +#mem_forget = "warn" +#mismatched_target_os = "warn" +#mismatching_type_param_order = "warn" +#missing_enforced_import_renames = "warn" +#missing_safety_doc = "warn" +#mixed_attributes_style = "warn" +#mut_mut = "warn" +#mutex_integer = "warn" +#needless_borrow = "warn" +#needless_continue = "warn" +#needless_for_each = "warn" +#needless_pass_by_ref_mut = "warn" +#needless_pass_by_value = "warn" +#negative_feature_names = "warn" +#nonstandard_macro_braces = "warn" +#option_as_ref_cloned = "warn" +#option_option = "warn" +#path_buf_push_overwrite = "warn" +#ptr_as_ptr = "warn" +#ptr_cast_constness = "warn" +#pub_underscore_fields = "warn" +#pub_without_shorthand = "warn" +#rc_mutex = "warn" +#readonly_write_lock = "warn" +#redundant_type_annotations = "warn" +#ref_as_ptr = "warn" +#ref_option_ref = "warn" +#rest_pat_in_fully_bound_structs = "warn" +#same_functions_in_if_condition = "warn" +#semicolon_if_nothing_returned = "warn" +#should_panic_without_expect = "warn" +#single_char_pattern = "warn" +#single_match_else = "warn" +#str_split_at_newline = "warn" +#str_to_string = "warn" +#string_add = "warn" +#string_add_assign = "warn" +#string_lit_as_bytes = "warn" +#string_lit_chars_any = "warn" +#string_to_string = "warn" +#suspicious_command_arg_space = "warn" +#suspicious_xor_used_as_pow = "warn" +#ktodo = "warn" +#too_many_lines = "warn" +#trailing_empty_array = "warn" +#trait_duplication_in_bounds = "warn" +#tuple_array_conversions = "warn" +#unchecked_duration_subtraction = "warn" +#undocumented_unsafe_blocks = "warn" +#unimplemented = "warn" +#uninhabited_references = "warn" +#uninlined_format_args = "warn" +#unnecessary_box_returns = "warn" +#unnecessary_safety_doc = "warn" +#unnecessary_struct_initialization = "warn" +#unnecessary_wraps = "warn" +#unnested_or_patterns = "warn" +#unused_peekable = "warn" +#unused_rounding = "warn" +#unused_self = "warn" +#unwrap_used = "warn" +#use_self = "warn" +#useless_transmute = "warn" +#verbose_file_reads = "warn" +#wildcard_dependencies = "warn" +#wildcard_imports = "warn" +#zero_sized_map_values = "warn" +## Disabled waiting on https://github.com/rust-lang/rust-clippy/issues/9602 +##self_named_module_files = "warn" +# +#assigning_clones = "allow" # Too much for too little +#manual_range_contains = "allow" # this one is just worse imho +#map_unwrap_or = "allow" # so is this one +#ref_patterns = "allow" # It's nice to avoid ref pattern, but there are some situations that are hard (impossible?) to express without. +# +#iter_over_hash_type = "allow" +#let_underscore_untyped = "allow" +#missing_assert_message = "allow" +#missing_errors_doc = "allow" +#significant_drop_tightening = "allow" # An update of parking_lot made this trigger in a lot of places. [profile.release] -#panic = "abort" +panic = "abort" # This leads to better optimizations and smaller binaries (and is the default in Wasm anyways). # Treat warnings as errors in release builds rustflags = ["-Dwarnings"] lto = true diff --git a/README.md b/README.md index 25a83fce..89057a50 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,13 @@ -# [![](favicon.png)](https://github.com/radumarias/rencfs) rencfs +# [![](favicon.png)](https://github.com/xoriors/rencfs) rencfs [![rencfs-bin](https://img.shields.io/aur/version/rencfs-bin?color=1793d1&label=rencfs-bin&logo=arch-linux)](https://aur.archlinux.org/packages/rencfs-bin/) [![crates.io](https://img.shields.io/crates/v/rencfs.svg)](https://crates.io/crates/rencfs) [![docs.rs](https://img.shields.io/docsrs/rencfs?label=docs.rs)](https://docs.rs/rencfs/) -[![build-and-tests](https://github.com/radumarias/rencfs/actions/workflows/build_and_tests.yaml/badge.svg)](https://github.com/radumarias/rencfs/actions/workflows/build_and_tests.yaml) -[![release](https://github.com/radumarias/rencfs/actions/workflows/release.yaml/badge.svg)](https://github.com/radumarias/rencfs/actions/workflows/release.yaml) -[![codecov](https://codecov.io/gh/radumarias/rencfs/graph/badge.svg?token=NUQI6XGF2Y)](https://codecov.io/gh/radumarias/rencfs) - -[![Matrix](https://img.shields.io/matrix/rencfs%3Amatrix.org?label=Matrix)](https://matrix.to/#/#rencfs:matrix.org) -[![Discord](https://img.shields.io/discord/1236855443486277653?label=Discord)](https://discord.com/channels/1236855443486277653/1236855448515252306) -[![Zulip](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg?label=Zulip)](https://rencfs.zulipchat.com) -[![Open Source Helpers](https://www.codetriage.com/radumarias/rencfs/badges/users.svg)](https://www.codetriage.com/radumarias/rencfs) +[![build-and-tests](https://github.com/xoriors/rencfs/actions/workflows/build_and_tests.yaml/badge.svg)](https://github.com/xoriors/rencfs/actions/workflows/build_and_tests.yaml) +[![release](https://github.com/xoriors/rencfs/actions/workflows/release.yaml/badge.svg)](https://github.com/xoriors/rencfs/actions/workflows/release.yaml) +[![codecov](https://codecov.io/gh/xoriors/rencfs/graph/badge.svg?token=NUQI6XGF2Y)](https://codecov.io/gh/xoriors/rencfs) + +[![Open Source Helpers](https://www.codetriage.com/xoriors/rencfs/badges/users.svg?count=20)](https://www.codetriage.com/xoriors/rencfs) > [!WARNING] > **This crate hasn't been audited; it's using `ring` crate, which is a well-known audited library, so in principle, at @@ -19,524 +16,116 @@ least the primitives should offer a similar level of security. stable release. > It's mostly ideal for experimental and learning projects.** -An encrypted file system written in Rust that is mounted with FUSE on Linux. It can be used to create encrypted directories. +An encrypted file system written in Rust mounted with FUSE on Linux. It can be used to create encrypted +directories. -You can then safely back up the encrypted directory to an untrusted server without worrying about the data being exposed. -You can also store it in a cloud storage service like Google Drive, Dropbox, etc., and have it synced across multiple devices. +You can then safely back up the encrypted directory to an untrusted server without worrying about the data being +exposed. +You can also store it in a cloud storage service like Google Drive, Dropbox, etc., and sync it across multiple devices. -You can use it as CLI or as a library to build your custom FUSE implementation or other apps that work with encrypted data. +You can use it as CLI or as a library to build your custom FUSE implementation or other apps that work with encrypted +data. -# Motivation +# Introduction -Create a `simple,` `performant,` `modular` and `ergonomic` yet `very secure` `encrypted filesystem` to protect your `privacy`, which is also `open source` and is correctly and safely using `well-known audited` crates as `cryptographic primitives.` - -# A short story - -[The Hitchhiker’s Guide to Building an Encrypted Filesystem in Rust](docs/The_Hitchhiker_s_Guide_to_Building_an_Encrypted_Filesystem_in_Rust_2.pdf) - -# Blog and tutorial - -There will be a [series](https://medium.com/@xorio42/list/828492b94c23) of articles about the evolution of this project, trying to keep it like a tutorial. This is the [first one](https://systemweakness.com/the-hitchhikers-guide-to-building-an-encrypted-filesystem-in-rust-4d678c57d65c). - -# Crate of the week in [This Week in Rust](https://this-week-in-rust.org/) - -It was [crate of the week](https://this-week-in-rust.org/blog/2024/08/14/this-week-in-rust-560/#crate-of-the-week) in Aug 2024. - -# Talks - -- [The Hitchhiker’s Guide to Building an Encrypted Filesystem in Rust](https://startech-rd.io/hitchhikers-guide-to/) -- [Basics of cryptography and FUSE for building a filesystem in Rust](https://miro.com/app/board/uXjVLccxeCE=/?share_link_id=342563218323) +- Motivation + Create a `simple,` `performant,` `modular` and `ergonomic` yet `very secure` `encrypted filesystem` to protect + your `privacy`, which is also `open source` and is correctly and safely using `well-known audited` crates + as `cryptographic primitives`. +- Read an article [The Hitchhiker’s Guide to Building an Encrypted Filesystem in Rust](https://medium.com/system-weakness/hitchhikers-guide-to-building-a-distributed-filesystem-in-rust-the-very-beginning-2c02eb7313e7). +- A one-pager [The Hitchhiker’s Guide to Building an Encrypted Filesystem in Rust](docs/The_Hitchhiker_s_Guide_to_Building_an_Encrypted_Filesystem_in_Rust.pdf). +- Talks + - [The Hitchhiker’s Guide to Building an Encrypted Filesystem in Rust](https://startech-rd.io/hitchhikers-guide-to/) @[meetup.com/star-tech-rd-reloaded](https://www.meetup.com/star-tech-rd-reloaded/) and @[OmniOpenCon](https://omniopencon.org/). + - [Basics of cryptography, Authenticated Encryption, Rust in cryptography and how to build an encrypted filesystem](https://www.youtube.com/live/HwmVxOl3pQg) @ITDays and [slides](https://miro.com/app/board/uXjVLccxeCE=/?share_link_id=342563218323). +- It was [crate of the week](https://this-week-in-rust.org/blog/2024/08/14/this-week-in-rust-560/#crate-of-the-week) in Aug 2024. # Key features Some of these are still being worked on and marked with `[WIP]`. - `Security` using well-known audited `AEAD` cryptography primitives; -- `[WIP]` `Data integrity`, data is written with `WAL` to ensure integrity even on crash or power loss; -- `[WIP]` Hide all info for enhanced `privacy`, all `metadata`, `content`, `file name`, `file size`, `*time` fields, `files count`, and directory structure is encrypted; -- `Safely` manage `credentials` in memory with `mlock(2)`, `mprotect`, `zeroize`, and `expiry` to mitigate cold boot attacks; +- `[WIP]` [Data integrity, data is written with WAL to ensure integrity even on crash or power loss](https://github.com/xoriors/rencfs/issues/48) +- `[WIP]` [Hide all info for enhanced privacy; all metadata, content, file name, file size, *time fields, files count, and directory structure is encrypted](https://github.com/xoriors/rencfs/issues/53) +- `Safely` manage `credentials` in memory with `mlock(2)`, `mprotect`, `zeroize`, and `expiry` to mitigate cold boot + attacks; - `Memory safety`, `performance`, and `optimized` for `concurrency` with Rust; - Simplicity; - Encryption key generated from password; - Password saved in OS's `keyring`; - `Change password` without re-encrypting all data; -- `[WIP]` Generate `unique nonce` in `offline mode`; +- `[WIP]` [Generate unique nonce in offline mode](https://github.com/xoriors/rencfs/issues/47) +- `[WIP]` [Add file inode and chunk index to AAD](https://github.com/xoriors/rencfs/issues/49) This prevents blocks + from being copied between or within files by an attacker; - `Fast seek` on both reads and writes; - `Writes in parallel`; - Exposed with `FUSE`; - Fully `concurrent` for all operations; -- `[WIP]` Handle `long file names`; -- `[WIP]` Abstraction layer for `Rust File` and `fs` API to use it as lib to `switch to using encrypted files` by just `changing the use statements`; -- `[WIP]` Abstraction layer to `access the storage` with implementations for desktop, Wasm, Android, and iOS and the ability to write your own implementation. - -# Functionality - -Some of these are still being worked on and marked with `[WIP]`. - -- It keeps all `encrypted` data and `master encryption key` in a dedicated directory with files structured on `inodes` (with - metadata info), files for binary content, and directories with files/directories entries. All data, metadata, and filenames - are encrypted. It generates unique inodes for new files in a multi-instance run and offline mode. -- The password is collected from CLI and saved in the OS's `keyring` while the app runs. This is because, for security concerns, we - clear the password from memory on inactivity, and we derive it again from the password just when needed. -- Master encryption key is also encrypted with another key derived from the password. This gives the ability to change - the - password without re-encrypting all data, we just `re-encrypt` the `master key`. -- Files are `encrypted` in `chunks` of `256KB`, so when making a change, we just re-encrypt that chunks. -- `Fast seek` on read and write, so if you're watching a movie, you can seek any position, and that would be instant. - This is because we can seek a particular chunk. -- The encryption key is `zeroize` in the mem when disposing and idle. Also, it's `mlock`ed while used to prevent being moved to swap. It's - also `mprotect`ed while not in use. -- `[WIP]` Ensure file integrity by saving each change to WAL, so for crashes or power loss, we apply the pending -changes at the next start. This makes the write operations atomic. -- Multiple writes in parallel to the same file, ideal for torrent-like applications. - -# Docs - -[![rencfs](website/resources/layers.png)](website/resources/layers.png) - -For detailed description of the various sequence flows please look into [Flows](docs/flows.md). - -# Stack - -- it's fully async built upon [tokio](https://crates.io/crates/tokio) and [fuse3](https://crates.io/crates/fuse3) -- [ring](https://crates.io/crates/ring) for encryption and [argon2](https://crates.io/crates/argon2) for key derivation - function (generating key from password used to encrypt the master encryption key) -- [rand_chacha](https://crates.io/crates/rand_chacha) for random generators -- [shush-rs](https://crates.io/crates/shush-rs) keeps pass and encryption keys safe in memory and zero them when - not used. It keeps encryption keys in memory only while being used, and when not active, it will release and zeroing - them in memory. It locks the memory page as well, preventing it from being written to swap. -- [blake3](https://crates.io/crates/blake3) for hashing -- password saved in OS keyring using [keyring](https://crates.io/crates/keyring) -- [tracing](https://crates.io/crates/tracing) for logs - -# Alternatives - -- [Alternatives](https://www.libhunt.com/r/rencfs) -- [EncFS](https://vgough.github.io/encfs/) and [alternatives](https://alternativeto.net/software/encfs/) -- [CryFS](https://www.cryfs.org/) -- [gocryptfs](https://nuetzlich.net/gocryptfs/) -- [fscrypt](https://www.kernel.org/doc/html/v4.18/filesystems/fscrypt.html) -- [VeraCrypt](https://www.veracrypt.fr/code/VeraCrypt/?h=NewSysEncWizard) -- [Cryptomator](https://cryptomator.org/) -- [TrueCrypt](https://truecrypt.sourceforge.net/) -- [DroidFS, F-Droid](https://f-droid.org/en/packages/sushi.hardcore.droidfs/) -- [LUKS, dm-crypt](https://guardianproject.info/archive/luks/) -- [AES Crypt](https://www.aescrypt.com/) -- [Windows BitLocker](https://learn.microsoft.com/en-us/windows/security/operating-system-security/data-protection/bitlocker/) -- [File Lock PEA](https://eck.cologne/peafactory/en/html/file_pea.html) -- [ZenCrypt](https://play.google.com/store/apps/details?id=com.zestas.cryptmyfiles&hl=en) -- [Hat.sh](https://hat.sh/) - -## What separates us - -[Asked](https://chatgpt.com/share/66e7a5a5-d254-8003-9359-9b1556b75fe9) ChatGPT if there are other solutions out there which offer all the key functionalities we do, seems like there are none :) -You can see the [key features](README.md#key-features) that separate us. - -# Usage - - - -## Give it a quick try with Docker - -Get the image - -```bash -docker pull xorio42/rencfs -``` - -Start a container to set up mount in it - -```bash -docker run -it --device /dev/fuse --cap-add SYS_ADMIN --security-opt apparmor:unconfined xorio42/rencfs:latest /bin/sh -``` - -In the container, create mount and data directories - -```bash -mkdir fsmnt && mkdir fsdata -``` - -Start `rencfs` - -```bash -rencfs mount --mount-point fsmnt --data-dir fsdata -``` - -Enter a password for encryption. - -Get the container ID - -```bash -docker ps -``` - -In another terminal, attach to the running container with the above ID - -```bash -docker exec -it /bin/sh -``` - -From here, you can play with it by creating files in `fsmnt` directory - -```bash -cd fsmnt -mkdir 1 -ls -echo "test" > 1/test -cat 1/test -``` - -## As a library - -For the library, you can follow the [documentation](https://docs.rs/rencfs/latest/rencfs/). - -## Command Line Tool - -### Dependencies - -To use the encrypted file system, you need to have FUSE installed on your system. You can install it by running the -following command (or based on your distribution). - -Arch - -```bash -sudo pacman -Syu && sudo pacman -S fuse3 -``` - -Ubuntu - -```bash -sudo apt-get update && sudo apt-get -y install fuse3 -``` - -### Install from AUR - -You can install the encrypted file system binary using the following command - -```bash -yay -Syu && yay -S rencfs -``` - -### Install with cargo - -You can install the encrypted file system binary using the following command - -```bash -cargo install rencfs -``` - -### Usage - -A basic example of how to use the encrypted file system is shown below - -``` -rencfs mount --mount-point MOUNT_POINT --data-dir DATA_DIR -``` - -- `MOUNT_POINT` act as a client, and mount FUSE at the given path -- `DATA_DIR` where to store the encrypted data - with the sync provider. But it needs to be on the same filesystem as the data-dir - -It will prompt you to enter a password to encrypt/decrypt the data. - -### Change Password - -The master encryption key is stored in a file and encrypted with a key derived from the password. -This offers the possibility to change the password without needing to re-encrypt the whole data. This is done by -decrypting the master key with the old password and re-encrypting it with the new password. - -To change the password, you can run the following command - -```bash -rencfs passwd --data-dir DATA_DIR -``` +- `[WIP]` [Handle long file names](https://github.com/xoriors/rencfs/issues/47) +- `[WIP]` [Abstraction layer for Rust File and fs API to use it as lib to switch to using encrypted files by just changing the use statements](https://github.com/xoriors/rencfs/issues/97) +- `[WIP]` [Abstraction layer to access the storage with implementations for desktop, Wasm, Android, and iOS and the ability to write your own implementation](https://github.com/xoriors/rencfs/issues/111) -`DATA_DIR` where the encrypted data is stored +# [Alternatives](docs/readme/Alternatives.md) -It will prompt you to enter the old password and then the new password. +# Implementation -### Encryption info +- [Functionality](docs/readme/Functionality.md) +- [Stack](docs/readme/Stack.md) -You can specify the encryption algorithm by adding this argument to the command line +# Documentation -```bash ---cipher CIPHER ... -``` +- [Docs](docs/) -Where `CIPHER` is the encryption algorithm. You can check the available ciphers with `rencfs --help`. -The default value is `ChaCha20Poly1305`. - -### Log level - -You can specify the log level by adding the `--log-level` argument to the command line. Possible -values: `TRACE`, `DEBUG`, `INFO` (default), `WARN`, `ERROR`. - -```bash -rencfs --log-level LEVEL ... -``` - -## Use it in Rust - -You can see more [here](https://crates.io/crates/rencfs) - -# Build from source - -## Browser - -If you want to give it a quick try and not setup anything locally you can -[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/radumarias/rencfs) - -[![Open Rustlings On Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new/?repo=radumarias%2Frencfs&ref=main) - -You can compile it, run it, and give it a quick try in the browser. After you start it from above - -```bash -sudo apt-get update && sudo apt-get install fuse3 -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -mkdir mnt && mkdir data -cargo run --release -- mount -m mnt -d data -``` - -Open another terminal - -```bash -cd mnt -mkdir a && cd a -echo "test" > test.txt -cat test.txt -``` - -## Locally - -For now, the `FUSE` (`fuse3` crate) only works on `Linux`, so to start the project, you will need to be on Linux. -Instead, you can [Develop inside a Container](#developing-inside-a-container), which will start a local Linux container, the IDE will connect to it, -and you can build and start the app there and also use the terminal to test it. -On Windows, you can start it in [WSL](https://harsimranmaan.medium.com/install-and-setup-rust-development-environment-on-wsl2-dccb4bf63700). - -### Getting the sources - -```bash -git clone git@github.com:radumarias/rencfs.git && cd rencfs -```` - -### Dependencies - -#### Rust - -To build from source, you need to have Rust installed, you can see more details on how to install -it [here](https://www.rust-lang.org/tools/install). - -```bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -```` - -Accordingly, it is customary for Rust developers to include this directory in their `PATH` environment variable. -During installation `rustup` will attempt to configure the `PATH`. Because of differences between platforms, command -shells, -and bugs in `rustup`, the modifications to `PATH` may not take effect until the console is restarted, or the user is -logged out, or it may not succeed at all. - -If, after installation, running `rustc --version` in the console fails, this is the most likely reason. -In that case please add it to the `PATH` manually. - -The project is set up to use the `nightly` toolchain in `rust-toolchain. tool`; on the first build, you will see it fetch the nightly. - -Make sure to add this to your `$PATH` too - -```bash -export PATH="$PATH::$HOME/.cargo/bin" -``` - -```bash -cargo install cargo-aur -cargo install cargo-generate-rpm -``` - -### Other dependencies - -Also, these dependencies are required (or based on your distribution): - -#### Arch - -```bash -sudo pacman -Syu && sudo pacman -S fuse3 base-devel act -``` - -#### Ubuntu - -```bash -sudo apt-get update && sudo apt-get install fuse3 build-essential act -``` - -#### Fedora - -```bash -sudo dnf update && sudo dnf install fuse3 && dnf install @development-tools act -``` - -### Build for debug - -```bash -cargo build -``` - -### Build release - -```bash -cargo build --release -``` - -### Run - -```bash -cargo run --release -- mount --mount-point MOUNT_POINT --data-dir DATA_DIR -``` - -#### Dev settings - -If you don't want to be prompted for a password, you can set this env var and run it like this: - -```bash -RENCFS_PASSWORD=PASS cargo run --release -- mount --mount-point MOUNT_POINT --data-dir DATA_DIR -``` - -For dev mode it is recommended to run with `DEBUG` log level: - -```bash -cargo run --release -- --log-level DEBUG mount --mount-point MOUNT_POINT --data-dir DATA_DIR -``` - -### Build local RPM for Fedora - -This is using [cargo-generate-rpm](https://crates.io/crates/cargo-generate-rpm) - -```bash -cargo install cargo-generate-rpm -cargo build --release -cargo generate-rpm -``` - -The generated RPM will be located here: `target/generate-rpm`. - -#### Install and run local RPM - -```bash -cd target/generate-rpm/ -sudo dnf localinstall rencfs-xxx.x86_64.rpm -``` - -## Developing inside a Container - -See here how to configure for [RustRover](https://www.jetbrains.com/help/rust/connect-to-devcontainer.html) and for [VsCode](https://code.visualstudio.com/docs/devcontainers/containers). +[![rencfs](website/resources/layers.png)](website/resources/layers.png) -You can use the `.devcontainer` directory from the project to start a container with all the necessary tools to build -and run the app. +Please look into [Flows](docs/readme/flows.md) for a detailed sequence flow description. -# Minimum Supported Rust Version (MSRV) +# Usage and Development -The minimum supported version is `1.75`. +- [Usage](docs/readme/Usage.md) +- [Build from Source](docs/readme/Build_from_Source.md) +- Minimum Supported Rust Version (MSRV). The minimum supported version is `1.75`. -# Future +# Next steps -The plan is to implement it also on macOS and Windows +- The plan is to implement it also on macOS and Windows - **Systemd service** is being worked on [rencfs-daemon](https://github.com/radumarias/rencfs-daemon) -- **GUI** is being worked on [rencfs-desktop](https://github.com/radumarias/rencfs-desktop) and [rencfs-kotlin](https://github.com/radumarias/rencfs-kotlin) -- **Mobile apps** for **Android** and **iOS** are being worked on [rencfs-kotlin](https://github.com/radumarias/rencfs-kotlin) - -# Performance +- **GUI** is being worked on [rencfs-desktop](https://github.com/radumarias/rencfs-desktop) + and [ciphershell-kotlin](https://github.com/radumarias/ciphershell-kotlin) +- **Mobile apps** for **Android** and **iOS** are being worked + on [ciphershell-kotlin](https://github.com/radumarias/ciphershell-kotlin) -`Aes256Gcm` is slightly faster than `ChaCha20Poly1305` by a factor of **1.28** on average. This is because of the hardware acceleration of AES -on most CPUs via AES-NI. However, where hardware acceleration is not available, `ChaCha20Poly1305` is faster. Also `ChaChaPoly1305` is better at `SIMD`. - -# Cipher comparison - -## AES-GCM vs. ChaCha20-Poly1305 - -- If you have hardware acceleration (e.g. `AES-NI`), then `AES-GCM` provides better performance. On my benchmarks, it was - faster by a factor of **1.28** on average. - If you do not have hardware acceleration, `AES-GCM` is either slower than `ChaCha20-Poly1305`, or it leaks your - encryption - keys in cache timing. -- `AES-GCM` can target multiple security levels (`128-bit`, `192-bit`, `256-bit`), whereas `ChaCha20-Poly1305` is only defined at - the `256-bit` security level. -- Nonce size: - - `AES-GCM`: Varies, but the standard is `96 bits` (`12 bytes`). - If you supply a longer nonce, this gets hashed down to `16 bytes`. - - `ChaCha20-Poly1305`: The standardized version uses `96-bit` nonce (`12 bytes`), but the original used `64-bit` - nonce (`8 bytes`). -- Wear-out of a single (key, nonce) pair: - - `AES-GCM`: Messages must be less than `2^32 – 2` blocks (a.k.a. `2^36 – 32 bytes`, a.k.a. `2^39 – 256 bits`), that's - roughly `64GB`. - This also makes the security analysis of `AES-GCM` with long nonces complicated since the hashed nonce doesn’t - start - with the lower `4 bytes` set to `00 00 00 02`. - - `ChaCha20-Poly1305`: `ChaCha` has an internal counter (`32 bits` in the standardized IETF variant, `64 bits` in the - original design). Max message length is `2^39 - 256 bits`, about `256GB` -- Neither algorithm is **nonce misuse-resistant**. -- `ChaChaPoly1305` is better at `SIMD` - -### Conclusion +# Considerations -Both are good options. `AES-GCM` can be faster with **hardware support**, but **pure-software** implementations of -`ChaCha20-Poly1305` are almost always **fast** and **constant-time**. +- Performance + `Aes256Gcm` is slightly faster than `ChaCha20Poly1305` by an average factor of **1.28**. This is because of the + hardware acceleration of AES + on most CPUs via AES-NI. However, where hardware acceleration is unavailable, `ChaCha20Poly1305` is faster. + Also, `ChaChaPoly1305` is better at `SIMD.` +- [⚠️ Security ](docs/readme/Security.md) +- [Cipher comparison](docs/readme/Cipher_comparison.md) +- [Others](docs/readme/Considerations.md) -# ⚠️ Security Warning: Hazmat! +# Contribute -- **Phantom reads**: Reading older content from a file is not possible. Data is written with WAL and periodically - flushed to file. This ensures data integrity and maintains change order. - One problem that may occur is if we do a truncation, we change the content of the file, but the process is killed before - we write the metadata with the new file size. In this case, the next time we mount the system, we will still see the old -files. However, the content of the file could be bigger, and we read until the old size offset, so we would not - pick up - the new zeros bytes are written on truncating by increasing the size. If content is smaller, the read would stop and - end-of-file of the actual content, so this would not be such a big issue -- **What kind of metadata does it leak**: close to none. The filename, actual file size and other file attrs (times, - permissions, other flags) are kept encrypted. What it could possibly leak is the following - - If a directory has children, we keep those children in a directory with name as inode number and encrypted names - of children as files in it. - So we could see how many children a directory has. - However, we can't identify that actual directory name; - We can just see its inode number (internal representation like an ID for each file), but we cannot see the actual - filenames of the directory or children. - Also, we cannot identify which file content corresponds to a directory child - - Each file content is saved in a separate file, so we can see the size of the encrypted content but not the - actual filesize - - We can also see the last time the file was accessed -- It's always recommended to use encrypted disks for at least your sensitive data; this project is not a replacement for - that -- To reduce the risk of the encryption key being exposed from memory, it's recommended to disable memory dumps on the - OS level. Please see [here](https://www.cyberciti.biz/faq/disable-core-dumps-in-linux-with-systemd-sysctl/) how to do - it on Linux -- **Cold boot attacks**: to reduce the risk of this, we keep the encryption key in memory just as long as we really - need it to encrypt/decrypt data, and we are zeroing it after that. We also remove it from memory after a period of - inactivity -- Please note that no security expert audited this project. It's built with security in mind and tries to - follow all the best practices, but it's not guaranteed to be secure -- **Also, please back up your data; the project is still in development, and there might be bugs that can lead to data - loss** +If you find any issues or vulnerabilities or you'd like a feature, please follow these steps: +- [Open a bug](https://github.com/xoriors/rencfs/issues/new?assignees=&labels=&projects=&template=bug_report.md&title=): Create a report to help us improve. +- [Report a security vulnerability](https://github.com/xoriors/rencfs/security/advisories/new): Report a security vulnerability. +- [Feature request](https://github.com/xoriors/rencfs/issues/new?assignees=&labels=&projects=&template=feature_request.md&title=): Suggest an idea for this project. -# Considerations +Feel free to fork, change, and use it however you want. We always appreciate it if you build something interesting and feel like sharing pull requests. -- Please note that this project doesn't try to reinvent the wheel or be better than already proven implementations -- This project doesn't want to be a replacement in any way for already proven file encryption solutions. If you really - want to be close to bulletproof solutions, then maybe this is not the ideal one for you. But is trying to offer a simple use - of an encryption solution that should be used, taking into consideration all the security concerns from above -- It started as a learning project of Rust programming language, and I feel like I keep building more on it -- It's a fairly simple and standard implementation that tries to respect all security standards and correctly use secure and robust - primitives so that it can be extended from this. Indeed, it doesn't have the maturity yet to "fight" other well-known - implementations. - But it can be a project from which others can learn or build upon, or why not for some to actually use it, keeping in - mind all the above +How to contribute: +- Please see [CONTRIBUTING.md](.github/CONTRIBUTING.md) -# Contribute +# Follow us -Feel free to fork it, change and use it however you want. If you build something interesting and feel like sharing -pull requests are always appreciated. +- Blog and tutorial + There is a [series](https://medium.com/@xorio42/list/828492b94c23) of articles about the evolution of this + project, trying to keep it like a tutorial. This is + the [first one](https://systemweakness.com/the-hitchhikers-guide-to-building-an-encrypted-filesystem-in-rust-4d678c57d65c). -## How to contribute +# Get in touch -Please see [CONTRIBUTING.md](CONTRIBUTING.md). +- [Slack](https://bit.ly/3UU1oXi) +- [hello@xorio.rs](mailto:hello@xorio.rs) +- [LinkedIn](https://www.linkedin.com/company/xorio) diff --git a/Ramp-up.md b/Ramp-up.md deleted file mode 100644 index abffbbbf..00000000 --- a/Ramp-up.md +++ /dev/null @@ -1,14 +0,0 @@ -# Ramp-up guide - -1. Read an [article](https://medium.com/system-weakness/hitchhikers-guide-to-building-a-distributed-filesystem-in-rust-the-very-beginning-2c02eb7313e7) and an [one pager](The_Hitchhiker_s_Guide_to_Building_an_Encrypted_Filesystem_in_Rust-1.pdf) to get more details about the project -2. Become familiar with the [concepts and features](https://github.com/radumarias/rencfs) and [lib docs](https://docs.rs/rencfs/latest/rencfs) -3. Understand the [layers](https://github.com/radumarias/rencfs/blob/main/website/resources/layers.png) -4. Detailed [sequence flows](docs/flows.md) -5. [Talks](https://startech-rd.io/hitchhikers-guide-to/) -6. Another presentation about [cryptography and FUSE](https://miro.com/app/board/uXjVLccxeCE=/?share_link_id=919818831100) -7. Give it a [quick try](https://github.com/radumarias/rencfs#give-it-a-quick-try-with-docker) with Docker -8. Or run it as [CLI](https://github.com/radumarias/rencfs?tab=readme-ov-file#command-line-tool) app -9. Clone or fork the repo. You can work in your branches there after you're added to the repo. No need to fork it if you don't want to -10. [Build](https://github.com/radumarias/rencfs?tab=readme-ov-file#build-from-source) from source and start it. If you don't have Linux, you can [develop inside a container](https://github.com/radumarias/rencfs?tab=readme-ov-file#developing-inside-a-container). This will start a new Linux container and remotely connecting the local IDE to the container, you can also connect with IDE's terminal to it and run the code. On Windows, you can use [WSL](https://harsimranmaan.medium.com/install-and-setup-rust-development-environment-on-wsl2-dccb4bf63700). As a last resort, you can [develop in browser](https://github.com/radumarias/rencfs/blob/main/README.md#browser). -11. Run and understand the [examples](examples). You can write some new ones to better understand the flow and code. If you do, please create a `PR` back to the parent repo targeting the `main` branch to include those for others too -12. Become familiar with some [tests](https://github.com/radumarias/rencfs/blob/main/src/encryptedfs/test.rs) and benchmarks. You can write some new ones to better understand the flow and code. If you do, please create a `PR` back to the parent repo targeting the `main` branch to include those for others too diff --git a/src/crypto/read/bench.rs b/benches/crypto_read.rs similarity index 56% rename from src/crypto/read/bench.rs rename to benches/crypto_read.rs index 91b46a30..edb326c3 100644 --- a/src/crypto/read/bench.rs +++ b/benches/crypto_read.rs @@ -1,17 +1,13 @@ -#[allow(unused_imports)] -use test::Bencher; - -#[bench] -fn bench_read_1mb_chacha_file(b: &mut Bencher) { - use crate::crypto; - use crate::crypto::write::CryptoWrite; - use crate::crypto::Cipher; - use rand::RngCore; - use shush_rs::SecretVec; - use std::io; - use std::io::Seek; - use test::black_box; - +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use rand_core::RngCore; +use rencfs::crypto; +use rencfs::crypto::write::CryptoWrite; +use rencfs::crypto::Cipher; +use shush_rs::SecretVec; +use std::io; +use std::io::Seek; + +fn bench_read_1mb_chacha_file(c: &mut Criterion) { let cipher = Cipher::ChaCha20Poly1305; let len = 1024 * 1024; @@ -27,27 +23,18 @@ fn bench_read_1mb_chacha_file(b: &mut Bencher) { io::copy(&mut cursor_random, &mut writer).unwrap(); let file = writer.finish().unwrap(); - b.iter(|| { - black_box({ + c.bench_function("bench_read_1mb_chacha_file", |b| { + b.iter(|| { let mut file = file.try_clone().unwrap(); file.seek(io::SeekFrom::Start(0)).unwrap(); let mut reader = crypto::create_read(file, cipher, &key); - io::copy(&mut reader, &mut io::sink()).unwrap() + black_box(&reader); + io::copy(&mut reader, &mut io::sink()).unwrap(); }); }); } -#[bench] -fn bench_read_1mb_aes_file(b: &mut Bencher) { - use crate::crypto; - use crate::crypto::write::CryptoWrite; - use crate::crypto::Cipher; - use rand::RngCore; - use shush_rs::SecretVec; - use std::io; - use std::io::Seek; - use test::black_box; - +fn bench_read_1mb_aes_file(c: &mut Criterion) { let cipher = Cipher::Aes256Gcm; let len = 1024 * 1024; @@ -55,35 +42,21 @@ fn bench_read_1mb_aes_file(b: &mut Bencher) { rand::thread_rng().fill_bytes(&mut key); let key = SecretVec::new(Box::new(key)); - let file = tempfile::tempfile().unwrap(); - let mut writer = crypto::create_write(file, cipher, &key); - let mut cursor_random = io::Cursor::new(vec![0; len]); - rand::thread_rng().fill_bytes(cursor_random.get_mut()); - cursor_random.seek(io::SeekFrom::Start(0)).unwrap(); - io::copy(&mut cursor_random, &mut writer).unwrap(); - let file = writer.finish().unwrap(); - - b.iter(|| { - black_box({ - let mut file = file.try_clone().unwrap(); - file.seek(io::SeekFrom::Start(0)).unwrap(); - let mut reader = crypto::create_read(file, cipher, &key); - io::copy(&mut reader, &mut io::sink()).unwrap() + c.bench_function("bench_read_1mb_aes_file", |b| { + b.iter(|| { + let file = tempfile::tempfile().unwrap(); + let mut writer = crypto::create_write(file, cipher, &key); + let mut cursor_random = io::Cursor::new(vec![0; len]); + rand::thread_rng().fill_bytes(cursor_random.get_mut()); + cursor_random.seek(io::SeekFrom::Start(0)).unwrap(); + io::copy(&mut cursor_random, &mut writer).unwrap(); + black_box(&writer); + writer.finish().unwrap(); }); }); } -#[bench] -fn bench_read_1mb_chacha_ram(b: &mut Bencher) { - use crate::crypto; - use crate::crypto::write::CryptoWrite; - use crate::crypto::Cipher; - use rand::RngCore; - use shush_rs::SecretVec; - use std::io; - use std::io::Seek; - use test::black_box; - +fn bench_read_1mb_chacha_ram(c: &mut Criterion) { let cipher = Cipher::ChaCha20Poly1305; let len = 1024 * 1024; @@ -99,27 +72,18 @@ fn bench_read_1mb_chacha_ram(b: &mut Bencher) { io::copy(&mut cursor_random, &mut writer).unwrap(); let cursor_write = writer.finish().unwrap(); - b.iter(|| { - black_box({ + c.bench_function("bench_read_1mb_chacha_ram", |b| { + b.iter(|| { let mut cursor = cursor_write.clone(); cursor.seek(io::SeekFrom::Start(0)).unwrap(); let mut reader = crypto::create_read(cursor, cipher, &key); - io::copy(&mut reader, &mut io::sink()).unwrap() + black_box(&reader); + io::copy(&mut reader, &mut io::sink()).unwrap(); }); }); } -#[bench] -fn bench_read_1mb_aes_ram(b: &mut Bencher) { - use crate::crypto; - use crate::crypto::write::CryptoWrite; - use crate::crypto::Cipher; - use rand::RngCore; - use shush_rs::SecretVec; - use std::io; - use std::io::Seek; - use test::black_box; - +fn bench_read_1mb_aes_ram(c: &mut Criterion) { let cipher = Cipher::Aes256Gcm; let len = 1024 * 1024; @@ -135,12 +99,22 @@ fn bench_read_1mb_aes_ram(b: &mut Bencher) { io::copy(&mut cursor_random, &mut writer).unwrap(); let cursor_write = writer.finish().unwrap(); - b.iter(|| { - black_box({ + c.bench_function("bench_read_1mb_aes_file", |b| { + b.iter(|| { let mut cursor = cursor_write.clone(); cursor.seek(io::SeekFrom::Start(0)).unwrap(); let mut reader = crypto::create_read(cursor, cipher, &key); - io::copy(&mut reader, &mut io::sink()).unwrap() + black_box(&reader); + io::copy(&mut reader, &mut io::sink()).unwrap(); }); }); } + +criterion_group!( + benches, + bench_read_1mb_chacha_file, + bench_read_1mb_aes_file, + bench_read_1mb_chacha_ram, + bench_read_1mb_aes_ram +); +criterion_main!(benches); diff --git a/check-before-push.sh b/check-before-push.sh deleted file mode 100755 index 90d7204f..00000000 --- a/check-before-push.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/zsh - -set -e - -export CARGO_TERM_COLOR=always -export RUSTFLAGS="-Dwarnings" -export RUSTDOCFLAGS="-Dwarnings" - -# Filepath to the Cargo.toml file -CARGO_FILE="Cargo.toml" -BACKUP_FILE="Cargo_backup.txt" - -# Function to add -dryRun to the version -add_dryrun_to_version() { - if [[ -f $CARGO_FILE ]]; then - # Backup the original Cargo.toml before modification - cp "$CARGO_FILE" "$BACKUP_FILE" - - # Extract the current version from the file - ORIGINAL_VERSION=$(grep -oP '^version\s*=\s*"\K[^\"]+' "$CARGO_FILE") - - if [[ -n $ORIGINAL_VERSION ]]; then - # Modify the version and write it back to the Cargo.toml file - sed -i "s/version = \"$ORIGINAL_VERSION\"/version = \"$ORIGINAL_VERSION-dryRun\"/" "$CARGO_FILE" - echo "Version modified to: $ORIGINAL_VERSION-dryRun" - else - echo "No version found in the file." - fi - else - echo "Cargo.toml file not found!" - fi -} - -# Function to revert the version to its original state by restoring from backup -revert_version() { - if [[ -f $BACKUP_FILE ]]; then - # Restore the original Cargo.toml from the backup - mv "$BACKUP_FILE" "$CARGO_FILE" - echo "Cargo.toml reverted to the original version." - else - echo "Backup file not found! Cannot revert." - fi -} - -cargo fmt --all - -cargo build --all-targets --all-features --target x86_64-unknown-linux-gnu -cargo build --release --all-targets --all-features --target x86_64-unknown-linux-gnu -cargo clippy --release --all-targets --fix --allow-dirty --allow-staged --target x86_64-unknown-linux-gnu -cargo fmt --all -- --check -cargo check --all --target x86_64-unknown-linux-gnu -cargo clippy --all-targets --release --target x86_64-unknown-linux-gnu -- \ - -A clippy::similar_names \ - -A clippy::too_many_arguments \ - -A clippy::significant_drop_tightening \ - -A clippy::redundant_closure \ - -A clippy::missing_errors_doc \ - -A clippy::type_complexity -cargo test --release --all --all-features --target x86_64-unknown-linux-gnu -cargo doc --workspace --all-features --no-deps --target x86_64-unknown-linux-gnu - -pwd -ls -l -add_dryrun_to_version -cargo publish --dry-run --allow-dirty --target x86_64-unknown-linux-gnu -pwd -ls -l -revert_version -pwd -ls -l - -cargo aur -cargo generate-rpm - -cd java-bridge -cargo fmt --all -cargo build --all-targets --all-features --target x86_64-unknown-linux-gnu -cargo build --release --all-targets --all-features --target x86_64-unknown-linux-gnu -cargo clippy --release --all-targets --fix --allow-dirty --allow-staged --target x86_64-unknown-linux-gnu -cargo fmt --all -- --check -cargo check --all --target x86_64-unknown-linux-gnu -cargo clippy --all-targets --release --target x86_64-unknown-linux-gnu -- \ - -A clippy::similar_names \ - -A clippy::too_many_arguments \ - -A clippy::significant_drop_tightening \ - -A clippy::redundant_closure \ - -A clippy::missing_errors_doc \ - -A clippy::type_complexity -cargo test --release --all --all-features --target x86_64-unknown-linux-gnu -cargo doc --workspace --all-features --no-deps --target x86_64-unknown-linux-gnu -cd .. diff --git a/clippy.toml b/clippy.toml index 381888bc..6e413f56 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,2 +1,60 @@ allowed-duplicate-crates = ["syn", "socket2", "windows-sys", "windows-targets", "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", 'windows_x86_64_gnu', "windows_x86_64_gnullvm", "windows_x86_64_msvc", "memoffset", "nix", "parking_lot_core", "polling", "redox_syscall", "regex-automata", "regex-syntax", "rustix", "sha2", "bitflags", "block-buffer", "crypto-common", "digest", "event-listener", "event-listener-strategy", "fastrand", "futures-lite", "async-io", "async-lock", "heck", "linux-raw-sys", "arrayvec", "cfg-if", "hermit-abi"] too-many-arguments-threshold = 10 +# +##msrv = "1.75" + +allow-unwrap-in-tests = true + +## https://doc.rust-lang.org/nightly/clippy/lint_configuration.html#avoid-breaking-exported-api +# We want suggestions, even if it changes public API. +#avoid-breaking-exported-api = false + +excessive-nesting-threshold = 16 + +#max-fn-params-bools = 2 + +# https://rust-lang.github.io/rust-clippy/master/index.html#/large_include_file +#max-include-file-size = 1000000 + +# https://rust-lang.github.io/rust-clippy/master/index.html#/large_stack_frames +stack-size-threshold = 512000 + +#too-many-lines-threshold = 600 + +# ----------------------------------------------------------------------------- + +# https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros +#disallowed-macros = [ +# 'dbg', +# +# # 'std::eprint', +# # 'std::eprintln', +# # 'std::print', +# # 'std::println', +# +# # 'std::unimplemented', # generated by ArrowDeserialize derive-macro :( +#] + +# https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names +disallowed-names = [] + +# Allow-list of words for markdown in docstrings https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown +doc-valid-idents = [ + "GitHub", + "GLB", + "GLTF", + "iOS", + "macOS", + "MessagePack", + "MiMalloc", + "NaN", + "OBJ", + "OpenGL", + "PyPI", + "sRGB", + "sRGBA", + "WebGL", + "WebGPU", + "WebSocket", + "WebSockets", +] diff --git a/Dockerfile b/docker/Dockerfile similarity index 100% rename from Dockerfile rename to docker/Dockerfile diff --git a/docker/Dockerfile-deb b/docker/Dockerfile-deb new file mode 100644 index 00000000..c05f040b --- /dev/null +++ b/docker/Dockerfile-deb @@ -0,0 +1,28 @@ +# Use the official Rust image as a base +FROM rust:latest + +# Set the working directory inside the container +WORKDIR /usr/src/app + +# Copy the current directory's contents to the container +COPY . . + +# Install dependencies (if needed) +RUN apt-get update && apt-get upgrade -y && apt-get install -y \ + binutils \ + build-essential \ + ca-certificates \ + curl \ + file \ + g++ \ + gcc \ + make \ + patch \ + --no-install-recommends && \ + rm -rf /var/lib/apt/lists/* + +# Build the Rust project +RUN cargo build --release + +# Set the command to run the built binary +CMD ["./target/release/rencfs"] diff --git a/docker/Dockerfile_from_scratch b/docker/Dockerfile_from_scratch new file mode 100644 index 00000000..66d76419 --- /dev/null +++ b/docker/Dockerfile_from_scratch @@ -0,0 +1,53 @@ +################ +##### Builder +FROM alpine:3.19.1 AS builder + +RUN apk update && apk upgrade && apk add binutils build-base ca-certificates curl file g++ gcc make patch fuse3 + +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + +RUN . ~/.cargo/env && rustup target add x86_64-unknown-linux-musl + +RUN . ~/.cargo/env && rustup default nightly && rustup update + +# Cache downloaded+built dependencies +#COPY Cargo.toml Cargo.lock /usr/src/rencfs/ +#RUN mkdir /usr/src/rencfs/src && \ +# echo 'fn main() {}' > /usr/src/rencfs/src/main.rs +# +#RUN . ~/.cargo/env && cd /usr/src/rencfs/ && cargo build --release && \ +# rm -Rvf /usr/src/rencfs/src + +# Build our actual code +#COPY Cargo.toml Cargo.lock /usr/src/rencfs/ +#COPY src /usr/src/rencfs +COPY . /usr/src/rencfs +#COPY examples /usr/src/rencfs/examples +RUN . ~/.cargo/env && \ + cd /usr/src/rencfs/ && \ + cargo build --target x86_64-unknown-linux-musl --release + +#Copy the fusermount3 binary and libraries into a directory +RUN mkdir /fusermount3dep && \ + cp $(which fusermount3) /fusermount3dep/ && \ + ldd $(which fusermount3) | awk '{ print $3 }' | xargs -I {} cp {} /fusermount3dep/ + + +################ +##### Runtime +FROM scratch AS runtime + +# Copy fusermount3 +COPY --from=builder /fusermount3dep/fusermount3 /usr/bin/ + +# Copy busybox +COPY --from=builder /bin/ /bin/ + +# Copy ld-musl (fusermount3 & busybox dep) +COPY --from=builder /fusermount3dep/ld* /lib/ + +# Copy application binary from builder image +COPY --from=builder /usr/src/rencfs/target/x86_64-unknown-linux-musl/release/rencfs /usr/bin/ + +# Run the application +CMD ["rencfs", "--help"] \ No newline at end of file diff --git a/The_Hitchhiker_s_Guide_to_Building_an_Encrypted_Filesystem_in_Rust.pdf b/docs/The_Hitchhiker_s_Guide_to_Building_an_Encrypted_Filesystem_in_Rust.pdf similarity index 100% rename from The_Hitchhiker_s_Guide_to_Building_an_Encrypted_Filesystem_in_Rust.pdf rename to docs/The_Hitchhiker_s_Guide_to_Building_an_Encrypted_Filesystem_in_Rust.pdf diff --git a/docs/The_Hitchhiker_s_Guide_to_Building_an_Encrypted_Filesystem_in_Rust_2.pdf b/docs/The_Hitchhiker_s_Guide_to_Building_an_Encrypted_Filesystem_in_Rust_2.pdf deleted file mode 100644 index 788144fd..00000000 Binary files a/docs/The_Hitchhiker_s_Guide_to_Building_an_Encrypted_Filesystem_in_Rust_2.pdf and /dev/null differ diff --git a/docs/flows.md b/docs/flows.md deleted file mode 100644 index 8a44efb0..00000000 --- a/docs/flows.md +++ /dev/null @@ -1,22 +0,0 @@ - -# Sequence flow diagrams - -The following diagrams depict the main flows supported by the current implementation. They depict the high-level interactions between the various components of the filesystem which means some details have been omitted. - -> [!WARNING] -> The single source of truth for in-depth interactions is the source code itself. - -- [Mount](uml/mount.md) -- [Change Password](uml/change_pass.md) -- [Open File](uml/open_file.md) -- [Close File](uml/close_file.md) -- [Read](uml/read.md) -- [Write](uml/write.md) -- [Create File](uml/create_file.md) -- [Search File](uml/search_file.md) - -Usage flows: - -- [Cli usage](uml/cli_usage.md) -- [Rencfs as a lib](uml/lib_rencfs_usage.md) -- [Encryptedfs as a lib](uml/lib_encryptedfs_usage.md) \ No newline at end of file diff --git a/docs/readme/Alternatives.md b/docs/readme/Alternatives.md new file mode 100644 index 00000000..dd2d48c9 --- /dev/null +++ b/docs/readme/Alternatives.md @@ -0,0 +1,22 @@ +# Alternatives + +- [Alternatives](https://www.libhunt.com/r/rencfs) +- [Cryptomator](https://cryptomator.org/) +- [gocryptfs](https://nuetzlich.net/gocryptfs/) +- [VeraCrypt](https://www.veracrypt.fr/code/VeraCrypt/?h=NewSysEncWizard) +- [TrueCrypt](https://truecrypt.sourceforge.net/) +- [DroidFS, F-Droid](https://f-droid.org/en/packages/sushi.hardcore.droidfs/) +- [EncFS](https://vgough.github.io/encfs/) and [alternatives](https://alternativeto.net/software/encfs/) +- [CryFS](https://www.cryfs.org/) +- [fscrypt](https://www.kernel.org/doc/html/v4.18/filesystems/fscrypt.html) +- [LUKS, dm-crypt](https://guardianproject.info/archive/luks/) +- [AES Crypt](https://www.aescrypt.com/) +- [Windows BitLocker](https://learn.microsoft.com/en-us/windows/security/operating-system-security/data-protection/bitlocker/) +- [File Lock PEA](https://eck.cologne/peafactory/en/html/file_pea.html) +- [ZenCrypt](https://play.google.com/store/apps/details?id=com.zestas.cryptmyfiles&hl=en) +- [Hat.sh](https://hat.sh/) + +## What separates us + +[Asked](https://chatgpt.com/share/66e7a5a5-d254-8003-9359-9b1556b75fe9) ChatGPT if there are other solutions out there which offer all the key functionalities we do, seems like there are none :) +You can see the [key features](README.md#key-features) that separate us. diff --git a/docs/readme/Build_from_Source.md b/docs/readme/Build_from_Source.md new file mode 100644 index 00000000..5a05ebbf --- /dev/null +++ b/docs/readme/Build_from_Source.md @@ -0,0 +1,155 @@ +# Build from source + +## In the browser + +If you want to give it a quick try and not setup anything locally, you can +[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/radumarias/rencfs) + +[![Open Rustlings On Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new/?repo=radumarias%2Frencfs&ref=main) + +You can compile, run, and try it quickly in the browser. After you start it from above + +```bash +apt-get update && apt-get install fuse3 +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +mkdir final && mkdir data +cargo run --release -- mount -m final -d data +``` + +Open another terminal + +```bash +cd final +mkdir a && cd a +echo "test" > test.txt +cat test.txt +``` + +You can also: +- Copy files and folders from your local machine to `tmp` folder in VSCode in the browser. So that we eliminate network errors when testing +- Then copy files and folders from `tmp` to `final` and then do your operations in the `final` folder +- Ensure files were copied successfully by right-clicking a file and then `Download...` and saving it to the local machine to ensure it opens correctly. For popular formats like image text, you can preview them in the browser editor + +## Locally + +For now, the `FUSE` (`fuse3` crate) only works on `Linux`, so you must be on Linux to start the project. +Instead, you can [Develop inside a Container](#developing-inside-a-container) by starting a local Linux container to which the IDE will connect. You can build, run, and debug the app there and use the terminal to test it. +On Windows, you can start it in [WSL](https://harsimranmaan.medium.com/install-and-setup-rust-development-environment-on-wsl2-dccb4bf63700). + +### Getting the sources + +```bash +git clone git@github.com:radumarias/rencfs.git && cd rencfs +```` + +### Dependencies + +#### Rust + +To build from the source, you need to have Rust installed. You can see more details on installing it [here](https://www.rust-lang.org/tools/install). + +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +```` + +Accordingly, it is customary for Rust developers to include this directory in their `PATH` environment variable. +During installation, `rustup` will attempt to configure the `PATH`. Because of differences between platforms, command +shells, +and bugs in `rustup`, the modifications to `PATH` may not take effect until the console is restarted or the user is +logged out, or it may not succeed at all. + +If, after installation, running `rustc --version` in the console fails, this is the most likely reason. +In that case, please manually add it to the `PATH`. + +The project is set up to use the `nightly` toolchain in `rust-toolchain. tool`; on the first build, you will see it fetch the nightly. + +Make sure to add this to your `$PATH` too. + +```bash +export PATH="$PATH::$HOME/.cargo/bin" +``` + +```bash +cargo install cargo-aur +cargo install cargo-generate-rpm +``` + +### Other dependencies + +Also, these dependencies are required (or based on your distribution): + +#### Arch + +```bash +sudo pacman -Syu && sudo pacman -S fuse3 base-devel act +``` + +#### Ubuntu + +```bash +sudo apt-get update && sudo apt-get install fuse3 build-essential act +``` + +#### Fedora + +```bash +sudo dnf update && sudo dnf install fuse3 && dnf install @development-tools act +``` + +### Build for debug + +```bash +cargo build +``` + +### Build release + +```bash +cargo build --release +``` + +### Run + +```bash +cargo run --release -- mount --mount-point MOUNT_POINT --data-dir DATA_DIR +``` + +#### Dev settings + +If you don't want to be prompted for a password, you can set this env var and run it like this: + +```bash +RENCFS_PASSWORD=PASS cargo run --release -- mount --mount-point MOUNT_POINT --data-dir DATA_DIR +``` + +For dev mode, it is recommended to run with `DEBUG` log level: + +```bash +cargo run --release -- --log-level DEBUG mount --mount-point MOUNT_POINT --data-dir DATA_DIR +``` + +### Build local RPM for Fedora + +This is using [cargo-generate-rpm](https://crates.io/crates/cargo-generate-rpm) + +```bash +cargo install cargo-generate-rpm +cargo build --release +cargo generate-rpm +``` + +The generated RPM will be located here: `target/generate-rpm`. + +#### Install and run local RPM + +```bash +cd target/generate-rpm/ +sudo dnf localinstall rencfs-xxx.x86_64.rpm +``` + +## Developing inside a Container + +See here how to configure for [RustRover](https://www.jetbrains.com/help/rust/connect-to-devcontainer.html) and for [VSCode](https://code.visualstudio.com/docs/devcontainers/containers). + +You can use the `.devcontainer` directory from the project to start a container with all the necessary tools to build +and run the app. diff --git a/docs/readme/Cipher_comparison.md b/docs/readme/Cipher_comparison.md new file mode 100644 index 00000000..4681235c --- /dev/null +++ b/docs/readme/Cipher_comparison.md @@ -0,0 +1,31 @@ +# Cipher comparison + +## AES-GCM vs. ChaCha20-Poly1305 + +- If you have hardware acceleration (e.g. `AES-NI`), then `AES-GCM` provides better performance. On my benchmarks, it was + faster by a factor of **1.28** on average. + If you do not have hardware acceleration, `AES-GCM` is either slower than `ChaCha20-Poly1305`, or it leaks your + encryption + keys in cache timing. +- `AES-GCM` can target multiple security levels (`128-bit`, `192-bit`, `256-bit`), whereas `ChaCha20-Poly1305` is only defined at + the `256-bit` security level. +- Nonce size: + - `AES-GCM`: Varies, but the standard is `96-bit` (`12 bytes`). + If you supply a longer nonce, this gets hashed down to `16 bytes`. + - `ChaCha20-Poly1305`: The standardized version uses `96-bit` nonce (`12 bytes`), but the original used `64-bit` + nonce (`8 bytes`). +- Wear-out of a single (key, nonce) pair: + - `AES-GCM`: Messages must be less than `2^32 – 2` blocks (a.k.a. `2^36 – 32 bytes`, a.k.a. `2^39 – 256-bit`), that's + roughly `64GB`. + This also makes the security analysis of `AES-GCM` with long nonces complicated since the hashed nonce doesn’t + start + with the lower `4 bytes` set to `00 00 00 02`. + - `ChaCha20-Poly1305`: `ChaCha` has an internal counter (`32-bit` in the standardized IETF variant, `64-bit` in the + original design). Max message length is `2^39 - 256-bit`, about `256GB` +- Neither algorithm is **nonce misuse-resistant**. +- `ChaChaPoly1305` is better at `SIMD` + +### Conclusion + +Both are good options. `AES-GCM` can be faster with **hardware support**, but **pure-software** implementations of +`ChaCha20-Poly1305` are almost always **fast** and **constant-time**. \ No newline at end of file diff --git a/docs/readme/Considerations.md b/docs/readme/Considerations.md new file mode 100644 index 00000000..4b6f680d --- /dev/null +++ b/docs/readme/Considerations.md @@ -0,0 +1,12 @@ +# Considerations + +- Please note that this project doesn't try to reinvent the wheel or be better than already proven implementations +- This project doesn't want to be a replacement in any way for already proven file encryption solutions. If you really + want to be close to bulletproof solutions, then maybe this is not the ideal one for you. But is trying to offer a simple use + of an encryption solution that should be used, taking into consideration all the security concerns from above +- It started as a learning project of Rust programming language, and I feel like I keep building more on it +- It's a fairly simple and standard implementation that tries to respect all security standards and correctly use secure and robust + primitives so that it can be extended from this. Indeed, it doesn't have the maturity yet to "fight" other well-known + implementations. + But it can be a project from which others can learn or build upon, or why not for some to actually use it, keeping in + mind all the above \ No newline at end of file diff --git a/docs/readme/Functionality.md b/docs/readme/Functionality.md new file mode 100644 index 00000000..2d814f07 --- /dev/null +++ b/docs/readme/Functionality.md @@ -0,0 +1,20 @@ +# Functionality + +Some of these are still being worked on and marked with `[WIP]`. + +- It keeps all `encrypted` data and `master encryption key` in a dedicated directory with files structured on `inodes` (with + metadata info), files for binary content, and directories with files/directories entries. All data, metadata, and filenames + are encrypted. It generates unique inodes for new files in a multi-instance run and offline mode. +- The password is collected from CLI and saved in the OS's `keyring` while the app runs. This is because, for security concerns, we + clear the password from memory on inactivity, and we derive it again from the password just when needed. +- Master encryption key is also encrypted with another key derived from the password. This gives the ability to change + the + password without re-encrypting all data, we just `re-encrypt` the `master key`. +- Files are `encrypted` in `chunks` of `256KB`, so when making a change, we just re-encrypt that chunks. +- `Fast seek` on read and write, so if you're watching a movie, you can seek any position, and that would be instant. + This is because we can seek a particular chunk. +- The encryption key is `zeroize` in the mem when disposing and idle. Also, it's `mlock`ed while used to prevent being moved to swap. It's + also `mprotect`ed while not in use. +- `[WIP]` Ensure file integrity by saving each change to WAL, so for crashes or power loss, we apply the pending +changes at the next start. This makes the write operations atomic. +- Multiple writes in parallel to the same file, ideal for torrent-like applications. \ No newline at end of file diff --git a/docs/readme/Key_features.md b/docs/readme/Key_features.md new file mode 100644 index 00000000..ded8589c --- /dev/null +++ b/docs/readme/Key_features.md @@ -0,0 +1,21 @@ +## Key features + +Some of these are still being worked on and marked with `[WIP]`. +- `Security` using well-known audited `AEAD` cryptography primitives; +- [WIP] [Data integrity, data is written with WAL to ensure integrity even on crash or power loss](https://github.com/radumarias/rencfs/issues/48) +- [WIP] [Hide all info for enhanced privacy; all metadata, content, file name, file size, *time fields, files count, and directory structure is encrypted](https://github.com/radumarias/rencfs/issues/53) +- `Safely` manage `credentials` in memory with `mlock(2)`, `mprotect`, `zeroize`, and `expiry` to mitigate cold boot attacks; +- `Memory safety`, `performance`, and `optimized` for `concurrency` with Rust; +- Simplicity; +- Encryption key generated from password; +- Password saved in OS's `keyring`; +- `Change password` without re-encrypting all data; +- [WIP] [Generate unique nonce in offline mode](https://github.com/radumarias/rencfs/issues/47) +- [WIP] [Add file inode and chunk index to AAD](https://github.com/radumarias/rencfs/issues/49) This prevents blocks from being copied between or within files by an attacker. +- `Fast seek` on both reads and writes; +- `Writes in parallel`; +- Exposed with `FUSE`; +- Fully `concurrent` for all operations; +- [WIP] [Handle long file names](https://github.com/radumarias/rencfs/issues/47) +- [WIP] [Abstraction layer for Rust File and fs API to use it as lib to switch to using encrypted files by just changing the use statements](https://github.com/radumarias/rencfs/issues/97) +- [WIP] [Abstraction layer to access the storage with implementations for desktop, Wasm, Android, and iOS and the ability to write your own implementation](https://github.com/radumarias/rencfs/issues/111) \ No newline at end of file diff --git a/docs/readme/Ramp-up.md b/docs/readme/Ramp-up.md new file mode 100644 index 00000000..08edf47f --- /dev/null +++ b/docs/readme/Ramp-up.md @@ -0,0 +1,13 @@ +# Ramp-up guide + +1. Become familiar with the [basic concepts and features](https://github.com/xoriors/rencfs?tab=readme-ov-file#-rencfs) and [lib docs](https://docs.rs/rencfs/latest/rencfs). +2. Give it a [quick try](https://github.com/xoriors/rencfs#give-it-a-quick-try-with-docker) with Docker. +3. Or run it as [CLI app](https://github.com/xoriors/rencfs?tab=readme-ov-file#command-line-tool). +4. Clone or fork the repo. After being added to it, you can create your branches in the original repo, no need to fork it, only if you want to. +5. [Build from source](Build_from_Source.md) and start it. +6. For now the app works only on Linux, but if you don't have Linux, you can [develop inside a container](Build_from_Source.md#developing-inside-a-container). + This will start a new Linux container and remotely connecting the local IDE to it. You can also connect with the IDE's terminal to it, run and debug the code. + On Windows, you can use [WSL](https://harsimranmaan.medium.com/install-and-setup-rust-development-environment-on-wsl2-dccb4bf63700). +7. As a last resort, you can [develop in browser](Build_from_Source.md#browser). +8. Understand and run the [examples](../../examples). You can write your own to better understand the flow and code. If you do, please create a `PR` back to the parent repo targeting the `main` branch to include those for others too. +9. Become familiar with [tests](../src/encryptedfs/test.rs) and [benchmarks](../benches), and in other files. You can write your own to better understand the flow and code. If you do, please create a `PR` back to the parent repo targeting the `main` branch to include those for others too. diff --git a/docs/readme/Security.md b/docs/readme/Security.md new file mode 100644 index 00000000..7c71fd89 --- /dev/null +++ b/docs/readme/Security.md @@ -0,0 +1,23 @@ +# ⚠️ Security Warning: Hazmat! + +- **Phantom reads**: Reading older content from a file is not possible. Data is written with WAL and periodically + flushed to file. This ensures data integrity and maintains change order. + One problem that may occur is if we do a truncation, we change the content of the file, but the process is killed before + we write the metadata with the new file size. In this case, the next time we mount the system, we will still see the old +files. However, the content of the file could be bigger, and we read until the old size offset, so we would not + pick up + the new zeros bytes are written on truncating by increasing the size. If content is smaller, the read would stop and + end-of-file of the actual content, so this would not be such a big issue +- **What kind of metadata does it leak**: None, we encrypt filename, content, and metadata and we hide file count, size, and all-time fields +- It's always recommended to use encrypted disks for at least your sensitive data; this project is not a replacement for + that +- To reduce the risk of the encryption key being exposed from memory, it's recommended to disable memory dumps on the + OS level. Please see [here](https://www.cyberciti.biz/faq/disable-core-dumps-in-linux-with-systemd-sysctl/) how to do + it on Linux +- **Cold boot attacks**: to reduce the risk of this, we keep the encryption key in memory just as long as we really + need it to encrypt/decrypt data, and we are zeroing it after that. We also remove it from memory after a period of + inactivity +- Please note that no security expert audited this project. It's built with security in mind and tries to + follow all the best practices, but it's not guaranteed to be secure +- **Also, please back up your data; the project is still in development, and there might be bugs that can lead to data + loss** \ No newline at end of file diff --git a/docs/readme/Stack.md b/docs/readme/Stack.md new file mode 100644 index 00000000..b58d62b1 --- /dev/null +++ b/docs/readme/Stack.md @@ -0,0 +1,12 @@ +# Stack + +- it's fully async built upon [tokio](https://crates.io/crates/tokio) and [fuse3](https://crates.io/crates/fuse3) +- [ring](https://crates.io/crates/ring) for encryption and [argon2](https://crates.io/crates/argon2) for key derivation + function (generating key from password used to encrypt the master encryption key) +- [rand_chacha](https://crates.io/crates/rand_chacha) for random generators +- [shush-rs](https://crates.io/crates/shush-rs) keeps pass and encryption keys safe in memory and zero them when + not used. It keeps encryption keys in memory only while being used, and when not active, it will release and zeroing + them in memory. It locks the memory page as well, preventing it from being written to swap. +- [blake3](https://crates.io/crates/blake3) for hashing +- password saved in OS keyring using [keyring](https://crates.io/crates/keyring) +- [tracing](https://crates.io/crates/tracing) for logs \ No newline at end of file diff --git a/docs/readme/Testing.md b/docs/readme/Testing.md new file mode 100644 index 00000000..fdac71d2 --- /dev/null +++ b/docs/readme/Testing.md @@ -0,0 +1,110 @@ +# Testing + +We'd appreciate it if you could help test the app. For now, the filesystem mounting works only on Linux, so the cleanest way is to test on Linux. + +Here are some ways you can do it. + +## Testing in VSCode in browser or local + +You'll need a GitHub account for this. + +This will create a Codespace instance on GitHub, a Linux container, so we can test it. +The instance config is 2 CPUs and 4 GB RAM. You have 120 CPU hours per month free for Codespace, which means 60 hours for that instance. We will connect to it from the browser and the local VSCode. + +### First setup + +1. Open the [repo](https://github.com/radumarias/rencfs) +2. Press `Code` button + ![image](https://github.com/user-attachments/assets/7c0e8872-fe1f-44b9-a833-2586ade4f618) +3. Create codespace on main + ![image](https://github.com/user-attachments/assets/5fee55f6-ef54-427c-b790-c135312d3355) +4. This will create the container on GitHub. If it asks you to setup config, select the minimum possible CPU and RAM +5. Start it and leave it to finish. This could take a bit longer. This will open a VSCode in the browser +6. Goto terminal in the browser version of the VSCode editor you're presented with. It should be at the bottom. If not, open it from the `Terminal -> New Terminal` menu. You can find the menu in the top left, with 3 lines icon + ![image](https://github.com/user-attachments/assets/48681023-e450-49b3-8526-ec0323be0d40) +7. Install Rust by pasting these in the terminal: + ```bash + apt-get update && apt-get install fuse3 + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + ``` + Press 1 and Enter on Rust installation, accepting all defaults. + + If installation aborts, then run these + ```bash + apt update + apt install rustc + rustc + ``` +8. Create needed folders and a `tmp` folder, which we will use to copy files from our machine, by pasting this in the terminal: + ```bash + mkdir tmp && mkdir final && mkdir data + ``` + +### Each resume and after the first setup + +1. Open the [repo](https://github.com/radumarias/rencfs) +2. Press `Code` button + ![image](https://github.com/user-attachments/assets/7c0e8872-fe1f-44b9-a833-2586ade4f618) +3. Press ```...``` right to the instance in the list + ![image](https://github.com/user-attachments/assets/c621c258-009d-46bf-adb7-f81a3d7131f6) + +#### VSCode in Browser + +4. Press `Open in Browser`, or directly click on the container name + +#### In local VSCode + +Make sure you have VSCode installed locally, based on your OS. + +4. Press `Open in Visual Studio Code` + +#### Continue + +Do step 11 from above. + +5. Type this in the VSCode terminal, which will fetch the changes from the repo (if there are conflicts, accept Theirs): + ```bash + git pull + git checkout --theirs . + rm -rf final; cargo run --release -- mount -m final -d data + ``` +6. Input a password and confirm it the first time + +You can now perform two types of tests; see below. In both cases, follow these steps. + +7. Copy files and folders from your local machine to `tmp` folder in VSCode +8. Copy files and folders from `tmp` to `final` and then do your operations the data in `nnt` folder +9. Make sure files were copied successfully by right-clicking a file and then `Download...` and save it to local machine and making sure it opens correctly + +#### Exploratory testing + +That is, testing anything that comes to mind. + +Repeat steps 7-9 in various ways. + +#### Test specific issues + +Test specific issues from the [project](https://github.com/users/radumarias/projects/1). You can take the ones from `Ready for QA` column: +1. Assign the issue to you and move it to `In QA` +2. Test it +3. When you finished, move it to `Tested` + +- [ ] Testing on Linux +- [ ] Testing on macOS +- [ ] Testing on Windows + +## Tests + +I created some [files](https://drive.google.com/drive/folders/1N-2KhGNo7f23tQ9Si4yWa9dlFtxUnsoM?usp=sharing) to keep our tests until we migrate to browserstack or similar. + +- `test cases`: generic test cases +- `smoke tests`: short, small tests used to test a build quickly +- `acceptance`: tests that must be passed to consider a build stable. These we will run for prod builds + +## Open a bug + +Please use [this](https://github.com/radumarias/rencfs/issues/new?assignees=&labels=&projects=&template=bug_report.md&title=) and follow the steps in there. + +## Creating a test case + +Please add a new row in the `test cases` file and follow the template of the first row, for example. The same applies to smoke tests and acceptance tests. diff --git a/docs/readme/Usage.md b/docs/readme/Usage.md new file mode 100644 index 00000000..bbc69dff --- /dev/null +++ b/docs/readme/Usage.md @@ -0,0 +1,160 @@ +# Usage + + + +## Give it a quick try with Docker + +Get the image + +```bash +docker pull xorio42/rencfs +``` + +Start a container to set up mount in it + +```bash +docker run -v ~/Downloads:/share -it --device /dev/fuse --cap-add SYS_ADMIN --security-opt apparmor:unconfined xorio42/rencfs:latest /bin/sh +``` + +**Replace `~/Downloads` with a path you want to share with the container.** + +In the container, create mount and data directories + +```bash +mkdir mnt && mkdir data +``` + +Start `rencfs` + +```bash +rencfs mount --mount-point mnt --data-dir data -l WARN +``` + +Enter a password for encryption. + +Get the container ID + +```bash +docker ps +``` + +In another terminal, attach to the running container with the above ID + +```bash +docker exec -it /bin/sh +``` + +From here, you can play with it by creating files in `mnt` directory + +```bash +cd mnt +mkdir 1 +ls +echo "test" > 1/test +cat 1/test +``` + +You can also copy files from `/share`. + +```bash +cd mnt +cp /share/file1.txt . +file file1.txt +``` + +## As a library + +For the library, you can follow the [documentation](https://docs.rs/rencfs/latest/rencfs/). + +## Command Line Tool + +### Dependencies + +To use the encrypted file system, you need to have FUSE installed on your system. You can install it by running the +following command (or based on your distribution). + +Arch + +```bash +sudo pacman -Syu && sudo pacman -S fuse3 +``` + +Ubuntu + +```bash +sudo apt-get update && sudo apt-get -y install fuse3 +``` + +### Install from AUR + +You can install the encrypted file system binary using the following command + +```bash +yay -Syu && yay -S rencfs +``` + +### Install with cargo + +You can install the encrypted file system binary using the following command + +```bash +cargo install rencfs +``` + +### Usage + +A basic example of how to use the encrypted file system is shown below + +``` +rencfs mount --mount-point MOUNT_POINT --data-dir DATA_DIR +``` + +- `MOUNT_POINT` act as a client, and mount FUSE at the given path +- `DATA_DIR` where to store the encrypted data + with the sync provider. But it needs to be on the same filesystem as the data-dir + +It will prompt you to enter a password to encrypt/decrypt the data. + +### Change Password + +The master encryption key is stored in a file and encrypted with a key derived from the password. +This offers the possibility to change the password without needing to re-encrypt the whole data. This is done by +decrypting the master key with the old password and re-encrypting it with the new password. + +To change the password, you can run the following command + +```bash +rencfs passwd --data-dir DATA_DIR +``` + +`DATA_DIR` where the encrypted data is stored + +It will prompt you to enter the old password and then the new password. + +### Encryption info + +You can specify the encryption algorithm by adding this argument to the command line + +```bash +--cipher CIPHER ... +``` + +Where `CIPHER` is the encryption algorithm. You can check the available ciphers with `rencfs --help`. +The default value is `ChaCha20Poly1305`. + +### Log level + +You can specify the log level by adding the `--log-level` argument to the command line. Possible +values: `TRACE`, `DEBUG`, `INFO` (default), `WARN`, `ERROR`. + +```bash +rencfs --log-level LEVEL ... +``` + +## Use it in Rust + +You can see more [here](https://crates.io/crates/rencfs) \ No newline at end of file diff --git a/docs/readme/flows.md b/docs/readme/flows.md new file mode 100644 index 00000000..71d7f9f8 --- /dev/null +++ b/docs/readme/flows.md @@ -0,0 +1,22 @@ +# Sequence flow diagrams + +The following diagrams depict the main flows supported by the current implementation. They depict the high-level +interactions between the various components of the filesystem which means some details have been omitted. + +> [!WARNING] +> The single source of truth for in-depth interactions is the source code itself. + +- [Mount](../uml/mount.md) +- [Change Password](../uml/change_pass.md) +- [Open File](../uml/open_file.md) +- [Close File](../uml/close_file.md) +- [Read](../uml/read.md) +- [Write](../uml/write.md) +- [Create File](../uml/create_file.md) +- [Search File](../uml/search_file.md) + +Usage flows: + +- [Cli usage](../uml/cli_usage.md) +- [Rencfs as a lib](../uml/lib_rencfs_usage.md) +- [Encryptedfs as a lib](../uml/lib_encryptedfs_usage.md) \ No newline at end of file diff --git a/docs/uml/lib_encryptedfs_usage.md b/docs/uml/lib_encryptedfs_usage.md index 1e1089d8..189cfcb9 100644 --- a/docs/uml/lib_encryptedfs_usage.md +++ b/docs/uml/lib_encryptedfs_usage.md @@ -2,34 +2,34 @@ sequenceDiagram participant application participant enc_new as EncryptedFs::new - - application -->> enc_new : data_dir,password_provider,cipher,read_only + application -->> enc_new: data_dir,password_provider,cipher,read_only create participant EncryptedFs - enc_new -->> EncryptedFs : - enc_new -->> application : EncryptedFs - Note left of application : create file under root_inode
and open for read and/or write - application -->> EncryptedFs : create(root_inode,file_name,file_attributes,read_flag,write_flag) - Note left of application : extract file_inode from file_attributes - EncryptedFs -->> application : (file_handle, file_attributes) - Note left of application : write data buffer into file at offset - application -->> EncryptedFs : write(file_inode,offset,data_buffer,file_handle) - EncryptedFs -->> application : bytes_written - Note left of application : flush file contents on storage - application -->> EncryptedFs : flush(file_handle) - EncryptedFs -->> application : - Note left of application : close the file - application -->> EncryptedFs : release(file_handle) - EncryptedFs -->> application : - Note left of application : open the file with file_inode
for read and/or write - application -->> EncryptedFs : open(file_inode,read,write) - EncryptedFs -->> application : file_handle - Note left of application : read from file with file_inode
at offset into data buffer - application -->> EncryptedFs : read(file_inode,offset,data_buffer,file_handle) - EncryptedFs -->> application : read_bytes - Note left of application : close the file - application -->> EncryptedFs : release(file_handle) - EncryptedFs -->> application : - application --x application : exit +enc_new -->> EncryptedFs: + enc_new -->> application: EncryptedFs +Note left of application: create file under root_inode
and open for read and/or write +application -->> EncryptedFs: create(root_inode,file_name,file_attributes,read_flag,write_flag) +Note left of application: extract file_inode from file_attributes +EncryptedFs -->> application : (file_handle, file_attributes) +Note left of application: write data buffer into file at offset +application -->> EncryptedFs: write(file_inode,offset,data_buffer,file_handle) +EncryptedFs -->> application: bytes_written +Note left of application: flush file contents on storage +application -->> EncryptedFs: flush(file_handle) +EncryptedFs -->> application: +Note left of application: close the file +application -->> EncryptedFs : release(file_handle) +EncryptedFs -->> application: +Note left of application: open the file with file_inode
for read and/or write +application -->> EncryptedFs: open(file_inode,read,write) +EncryptedFs -->> application: file_handle +Note left of application: read from file with file_inode
at offset into data buffer +application -->> EncryptedFs: read(file_inode,offset,data_buffer,file_handle) +EncryptedFs -->> application : read_bytes +Note left of application: close the file +application -->> EncryptedFs: release(file_handle) +EncryptedFs -->> application: +application --x application: exit ``` -Further details about the internals of create, open, close, read and write flows can be found in [flows](../flows.md). +Further details about the internals of create, open, close, read and write flows can be found +in [flows](../readme/flows.md). diff --git a/examples/file_handling.rs b/examples/file_handling.rs new file mode 100644 index 00000000..3482ef81 --- /dev/null +++ b/examples/file_handling.rs @@ -0,0 +1,86 @@ +use core::str::FromStr; + +use anyhow::Result; +use rencfs::{ + crypto::Cipher, + encryptedfs::{ + write_all_string_to_fs, CreateFileAttr, EncryptedFs, FileType, PasswordProvider, + }, +}; +use shush_rs::SecretString; +use std::{ + fs, + path::{Path, PathBuf}, +}; + +const ROOT_INODE: u64 = 1; + +struct PasswordProviderImpl; + +impl PasswordProvider for PasswordProviderImpl { + fn get_password(&self) -> Option { + Some(SecretString::from_str("password").unwrap()) + } +} + +#[tokio::main] +async fn main() -> Result<()> { + tracing_subscriber::fmt().init(); + + let data_dir = Path::new("/tmp/data_test").to_path_buf(); + clean_up_directory(&data_dir)?; + + let cipher = Cipher::ChaCha20Poly1305; + let fs = EncryptedFs::new( + data_dir.clone(), + Box::new(PasswordProviderImpl), + cipher, + false, + ) + .await?; + + let file_name = SecretString::from_str("file1").unwrap(); + let (file_handle, attr) = fs + .create(ROOT_INODE, &file_name, file_attributes(), false, true) + .await?; + + let data = "Hello, world!"; + write_all_string_to_fs(&fs, attr.ino, 0, data, file_handle).await?; + + fs.flush(file_handle).await?; + fs.release(file_handle).await?; + + let file_handle = fs.open(attr.ino, true, false).await?; + let mut buffer = vec![0; data.len()]; + fs.read(attr.ino, 0, &mut buffer, file_handle).await?; + fs.release(file_handle).await?; + + assert_eq!(data, String::from_utf8(buffer)?); + + assert!(fs.exists_by_name(ROOT_INODE, &file_name)?); + fs.remove_file(ROOT_INODE, &file_name).await?; + assert!(!fs.exists_by_name(ROOT_INODE, &file_name)?); + + clean_up_directory(&data_dir)?; + + Ok(()) +} + +const fn file_attributes() -> CreateFileAttr { + CreateFileAttr { + kind: FileType::RegularFile, + perm: 0o644, // Permissions + uid: 0, // User ID + gid: 0, // Group ID + rdev: 0, // Device ID + flags: 0, // File flags + } +} + +fn clean_up_directory(dir: &PathBuf) -> Result<()> { + if dir.exists() { + fs::remove_dir_all(dir)?; + } + + Ok(()) +} diff --git a/examples/magic_of_blanket_impl.rs b/examples/magic_of_blanket_impl.rs index 3c98ab4b..0d6b8cfc 100644 --- a/examples/magic_of_blanket_impl.rs +++ b/examples/magic_of_blanket_impl.rs @@ -1,8 +1,8 @@ /// This demonstrates the magic of blanket implementation. /// -/// If we have a wrapper that wraps [Read] we don't have access to [Seek::seek] method, but +/// If we have a wrapper that wraps [Read] we don't have access to [`Seek::seek`] method, but /// if we wrap [Read] + [Seek] we do. -/// We use this in [rencfs::crypto::read] and also in [rencfs::crypto::write]. +/// We use this in [`rencfs::crypto::read`] and also in [`rencfs::crypto::write`]. use std::io; use std::io::{Read, Seek, SeekFrom}; diff --git a/examples/wal.rs b/examples/wal.rs new file mode 100644 index 00000000..19f1a73f --- /dev/null +++ b/examples/wal.rs @@ -0,0 +1,124 @@ +use std::io::{self, Read}; + +use okaywal::{Entry, EntryId, LogManager, SegmentReader, WriteAheadLog}; + +fn main() -> io::Result<()> { + // begin rustme snippet: readme-example + // Open a log using an Checkpointer that echoes the information passed into each + // function that the Checkpointer trait defines. + let log = WriteAheadLog::recover("/tmp/rencfs/wal/my-log", LoggingCheckpointer)?; + + // Begin writing an entry to the log. + let mut writer = log.begin_entry()?; + + // Each entry is one or more chunks of data. Each chunk can be individually + // addressed using its LogPosition. + let record = writer.write_chunk("this is the first entry".as_bytes())?; + + // To fully flush all written bytes to disk and make the new entry + // resilliant to a crash, the writer must be committed. + writer.commit()?; + // end rustme snippet + + log.checkpoint_active()?; + + { + // Begin writing an entry to the log. + let mut writer = log.begin_entry()?; + + // Each entry is one or more chunks of data. Each chunk can be individually + // addressed using its LogPosition. + let _ = writer.write_chunk("this is the second entry".as_bytes())?; + // panic!("this will cause the entry to be rolled back"); + } + + // don't commit this entry + + // Let's reopen the log. During this process, + // LoggingCheckpointer::should_recover_segment will be invoked for each segment + // file that has not been checkpointed yet. In this example, it will be called + // once. Once the Checkpointer confirms the data should be recovered, + // LoggingCheckpointer::recover will be invoked once for each entry in the WAL + // that hasn't been previously checkpointed. + drop(log); + let log = WriteAheadLog::recover("/tmp/rencfs/wal/my-log", LoggingCheckpointer)?; + + // We can use the previously returned DataRecord to read the original data. + let mut reader = log.read_at(record.position)?; + let mut buffer = vec![0; usize::try_from(record.length).unwrap()]; + reader.read_exact(&mut buffer)?; + println!( + "Data read from log: {}", + String::from_utf8(buffer).expect("invalid utf-8") + ); + + // Cleanup + drop(reader); + drop(log); + std::fs::remove_dir_all("/tmp/rencfs/wal/my-log-log")?; + + Ok(()) +} + +#[derive(Debug)] +struct LoggingCheckpointer; + +impl LogManager for LoggingCheckpointer { + fn recover(&mut self, entry: &mut Entry<'_>) -> io::Result<()> { + // This example uses read_all_chunks to load the entire entry into + // memory for simplicity. The crate also supports reading each chunk + // individually to minimize memory usage. + if let Some(all_chunks) = entry.read_all_chunks()? { + // Convert the Vec's to Strings. + let all_chunks = all_chunks + .into_iter() + .map(String::from_utf8) + .collect::, _>>() + .expect("invalid utf-8"); + println!( + "LoggingCheckpointer::recover(entry_id: {:?}, data: {:?})", + entry.id(), + all_chunks, + ); + } else { + // This entry wasn't completely written. This could happen if a + // power outage or crash occurs while writing an entry. + println!( + "LoggingCheckpointer::recover(entry_id: {:?}, data: not fully written)", + entry.id(), + ); + } + + Ok(()) + } + + fn checkpoint_to( + &mut self, + last_checkpointed_id: EntryId, + _checkpointed_entries: &mut SegmentReader, + _wal: &WriteAheadLog, + ) -> io::Result<()> { + // checkpoint_to is called once enough data has been written to the + // WriteAheadLog. After this function returns, the log will recycle the + // file containing the entries being checkpointed. + // + // This function is where the entries must be persisted to the storage + // layer the WriteAheadLog is sitting in front of. To ensure ACID + // compliance of the combination of the WAL and the storage layer, the + // storage layer must be fully resilliant to losing any changes made by + // the checkpointed entries before this function returns. + println!("LoggingCheckpointer::checkpoint_to({last_checkpointed_id:?}"); + Ok(()) + } +} + +#[test] +fn test() -> io::Result<()> { + // Clean up any previous runs of this example. + let path = std::path::Path::new("my-log"); + if path.exists() { + std::fs::remove_dir_all("my-log")?; + } + + main() +} diff --git a/java-bridge/Cargo.lock b/java-bridge/Cargo.lock index c6b4f8ba..d009d18c 100644 --- a/java-bridge/Cargo.lock +++ b/java-bridge/Cargo.lock @@ -55,6 +55,12 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anstream" version = "0.6.15" @@ -164,14 +170,14 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ "async-task", "concurrent-queue", "fastrand 2.1.0", - "futures-lite 2.3.0", + "futures-lite 2.5.0", "slab", ] @@ -209,21 +215,21 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock 3.4.0", "cfg-if 1.0.0", "concurrent-queue", "futures-io", - "futures-lite 2.3.0", + "futures-lite 2.5.0", "parking", - "polling 3.7.2", + "polling 3.7.4", "rustix 0.38.34", "slab", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -282,16 +288,16 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] name = "async-signal" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb3634b73397aa844481f814fad23bbf07fdb0eabec10f2eb95e58944b1ec32" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" dependencies = [ - "async-io 2.3.3", + "async-io 2.4.0", "async-lock 3.4.0", "atomic-waker", "cfg-if 1.0.0", @@ -300,7 +306,7 @@ dependencies = [ "rustix 0.38.34", "signal-hook-registry", "slab", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -328,7 +334,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -339,11 +345,11 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atomic-write-file" -version = "0.1.4" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf54d4588732bdfc5ebc3eb9f74f20e027112fc31de412fc7ff0cd1c6896dae" +checksum = "23e32862ecc63d580f4a5e1436a685f51e0629caeb7a7933e4f017d5e2099e13" dependencies = [ - "nix 0.28.0", + "nix 0.29.0", "rand", ] @@ -450,15 +456,15 @@ dependencies = [ "async-channel", "async-task", "futures-io", - "futures-lite 2.3.0", + "futures-lite 2.5.0", "piper", ] [[package]] name = "bon" -version = "2.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "811d7882589e047896e5974d039dd8823a67973a63d559e6ad1e87ff5c42ed4f" +checksum = "f265cdb2e8501f1c952749e78babe8f1937be92c98120e5f78fc72d634682bad" dependencies = [ "bon-macros", "rustversion", @@ -466,15 +472,17 @@ dependencies = [ [[package]] name = "bon-macros" -version = "2.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e745a763e579a5ce70130e66f9dd35abf77cfeb9f418f305aeab8d1ae54c43" +checksum = "38aa5c627cd7706490e5b003d685f8b9d69bc343b1a00b9fdd01e75fdf6827cf" dependencies = [ "darling", "ident_case", + "prettyplease", "proc-macro2", "quote", - "syn 2.0.72", + "rustversion", + "syn 2.0.90", ] [[package]] @@ -495,6 +503,12 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cbc" version = "0.1.2" @@ -534,6 +548,39 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "cipher" version = "0.4.4" @@ -575,7 +622,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -627,9 +674,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" @@ -649,6 +696,42 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + [[package]] name = "crossbeam-channel" version = "0.5.13" @@ -658,12 +741,37 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -674,16 +782,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "cstr" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68523903c8ae5aacfa32a0d9ae60cadeb764e1da14ee0d26b1f3089f13a54636" -dependencies = [ - "proc-macro2", - "quote", -] - [[package]] name = "ctrlc" version = "3.4.4" @@ -715,7 +813,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -726,7 +824,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -784,7 +882,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -844,9 +942,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ "event-listener 5.3.1", "pin-project-lite", @@ -887,18 +985,17 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fuse3" -version = "0.7.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02ca1b211677ee014a10b94ab6aea31e622ad1ea35f48b1670ac492a4f88b1af" +checksum = "335dd07e2826edad49c2599ed8151c394d39d2f8efd62c07183816904414ac5c" dependencies = [ "async-notify", "bincode", "bytes", - "cstr", "futures-channel", "futures-util", "libc", - "nix 0.28.0", + "nix 0.29.0", "serde", "slab", "tokio", @@ -925,9 +1022,9 @@ checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" @@ -946,9 +1043,9 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ "fastrand 2.1.0", "futures-core", @@ -965,7 +1062,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -1026,6 +1123,16 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if 1.0.0", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -1036,6 +1143,12 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + [[package]] name = "heck" version = "0.5.0" @@ -1095,12 +1208,12 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.3.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.2", ] [[package]] @@ -1133,12 +1246,32 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1170,7 +1303,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.63", "walkdir", "windows-sys 0.45.0", ] @@ -1260,7 +1393,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -1346,7 +1479,19 @@ checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags 2.6.0", "cfg-if 1.0.0", - "cfg_aliases", + "cfg_aliases 0.1.1", + "libc", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.6.0", + "cfg-if 1.0.0", + "cfg_aliases 0.2.1", "libc", "memoffset 0.9.1", ] @@ -1476,6 +1621,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "oorandom" +version = "11.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" + [[package]] name = "ordered-stream" version = "0.2.0" @@ -1546,15 +1697,43 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", "fastrand 2.1.0", "futures-io", ] +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + [[package]] name = "polling" version = "2.8.0" @@ -1573,9 +1752,9 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.2" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if 1.0.0", "concurrent-queue", @@ -1583,7 +1762,7 @@ dependencies = [ "pin-project-lite", "rustix 0.38.34", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1601,6 +1780,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn 2.0.90", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -1613,9 +1802,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -1659,6 +1848,26 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.5.3" @@ -1714,7 +1923,7 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rencfs" -version = "0.13.86" +version = "0.13.87" dependencies = [ "anyhow", "argon2", @@ -1726,6 +1935,7 @@ dependencies = [ "bon", "bytes", "clap", + "criterion", "ctrlc", "fuse3", "futures-util", @@ -1747,7 +1957,7 @@ dependencies = [ "strum_macros", "subtle", "tempfile", - "thiserror", + "thiserror 2.0.6", "thread_local", "tokio", "tokio-stream", @@ -1853,6 +2063,12 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "same-file" version = "1.0.6" @@ -1902,9 +2118,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -1933,7 +2149,19 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", +] + +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", ] [[package]] @@ -1944,7 +2172,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -2071,7 +2299,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -2093,9 +2321,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.72" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -2121,7 +2349,16 @@ version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.63", +] + +[[package]] +name = "thiserror" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +dependencies = [ + "thiserror-impl 2.0.6", ] [[package]] @@ -2132,7 +2369,18 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] @@ -2176,6 +2424,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tokio" version = "1.39.2" @@ -2202,7 +2460,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -2251,7 +2509,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror", + "thiserror 1.0.63", "time", "tracing-subscriber", ] @@ -2264,7 +2522,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -2324,7 +2582,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" dependencies = [ "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -2335,7 +2593,7 @@ checksum = "96cbd06a7b648f1603e60d75d9ed295d096b340d30e9f9324f4b512b5d40cd92" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] @@ -2428,7 +2686,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", "wasm-bindgen-shared", ] @@ -2450,7 +2708,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2461,6 +2719,16 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "which" version = "6.0.2" @@ -2735,12 +3003,12 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "xdg-home" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca91dcf8f93db085f3a0a29358cd0b9d670915468f4290e8b85d118a34211ab8" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2827,7 +3095,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.90", ] [[package]] diff --git a/java-bridge/src/lib.rs b/java-bridge/src/lib.rs index 030f5810..235058e9 100644 --- a/java-bridge/src/lib.rs +++ b/java-bridge/src/lib.rs @@ -17,7 +17,6 @@ use shush_rs::SecretString; use std::collections::BTreeMap; use std::ops::Add; use std::path::Path; -use std::str::FromStr; use std::sync::LazyLock; use std::{io, process}; use tokio::runtime::Runtime; @@ -119,6 +118,7 @@ pub extern "system" fn Java_RustLibrary_mount( let mount_path: String = env.get_string(&mnt).unwrap().into(); let data_dir_path: String = env.get_string(&data_dir).unwrap().into(); let password: String = env.get_string(&password).unwrap().into(); + let new_pass = SecretString::new(Box::new(password)); info!("mount_path: {}", mount_path); info!("data_dir_path: {}", data_dir_path); @@ -171,16 +171,17 @@ pub extern "system" fn Java_RustLibrary_mount( }); } - struct PasswordProviderImpl(String); + struct PasswordProviderImpl(SecretString); // use secretvec instead of string impl PasswordProvider for PasswordProviderImpl { fn get_password(&self) -> Option { - Some(SecretString::from_str(&self.0).unwrap()) + Some(self.0.clone()) } } + let mount_point = create_mount_point( Path::new(&mount_path), Path::new(&data_dir_path), - Box::new(PasswordProviderImpl(password)), + Box::new(PasswordProviderImpl(new_pass)), // use the pass one time Cipher::ChaCha20Poly1305, false, false, diff --git a/scripts/check-before-push-act.sh b/scripts/check-before-push-act.sh new file mode 100755 index 00000000..1f26ae9d --- /dev/null +++ b/scripts/check-before-push-act.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e + +export CARGO_TERM_COLOR=always +export RUSTFLAGS="-Dwarnings" +export RUSTDOCFLAGS="-Dwarnings" + +cargo fmt --all +cargo clippy --release --all-targets --fix --allow-dirty --allow-staged +sudo act --action-offline-mode -W .github/workflows/build_and_tests.yaml + diff --git a/check-before-push.bat b/scripts/check-before-push.bat similarity index 90% rename from check-before-push.bat rename to scripts/check-before-push.bat index 42f6eb42..39ea86dd 100755 --- a/check-before-push.bat +++ b/scripts/check-before-push.bat @@ -2,8 +2,10 @@ setlocal set CARGO_TERM_COLOR=always -set RUSTFLAGS=-Dwarnings +gsset RUSTFLAGS=-Dwarnings set RUSTDOCFLAGS=-Dwarnings +set RUST_TEST_THREADS=14 +set CARGO_BUILD_JOBS=14 if %errorlevel% neq 0 exit /b %errorlevel% @@ -28,18 +30,15 @@ REM if %errorlevel% neq 0 exit /b %errorlevel% cargo test --release --all --all-features if %errorlevel% neq 0 exit /b %errorlevel% -cargo doc --workspace --all-features --no-deps +REM cargo bench --workspace --all-targets --all-features -j 14 if %errorlevel% neq 0 exit /b %errorlevel% -call :add_dryrun_to_version +cargo doc --workspace --all-features --no-deps if %errorlevel% neq 0 exit /b %errorlevel% REM cargo publish --dry-run --allow-dirty REM if %errorlevel% neq 0 exit /b %errorlevel% -call :revert_version -if %errorlevel% neq 0 exit /b %errorlevel% - cd java-bridge cargo fmt --all if %errorlevel% neq 0 exit /b %errorlevel% @@ -74,4 +73,7 @@ if %errorlevel% neq 0 exit /b %errorlevel% cargo doc --workspace --all-features --no-deps if %errorlevel% neq 0 exit /b %errorlevel% +REM cargo bench --workspace --all-targets --all-features -j 14 +if %errorlevel% neq 0 exit /b %errorlevel% + cd .. diff --git a/scripts/check-before-push.sh b/scripts/check-before-push.sh new file mode 100755 index 00000000..cb090c8d --- /dev/null +++ b/scripts/check-before-push.sh @@ -0,0 +1,51 @@ +#!/bin/zsh + +set -e + +export CARGO_TERM_COLOR=always +export RUSTFLAGS="-Dwarnings" +export RUSTDOCFLAGS="-Dwarnings" +export RUST_TEST_THREADS=14 +export CARGO_BUILD_JOBS=14 + +cargo fmt --all + +cargo build --all-targets --all-features +cargo build --release --all-targets --all-features +cargo clippy --release --all-targets --fix --allow-dirty --allow-staged +cargo fmt --all -- --check +cargo check --all +cargo clippy --all-targets --release -- \ + -A clippy::similar_names \ + -A clippy::too_many_arguments \ + -A clippy::significant_drop_tightening \ + -A clippy::redundant_closure \ + -A clippy::missing_errors_doc \ + -A clippy::type_complexity +cargo test --release --all --all-features +cargo bench --workspace --all-targets --all-features -j 14 +cargo doc --workspace --all-features --no-deps + +# cargo publish --dry-run --allow-dirty + +cargo aur +cargo generate-rpm + +cd java-bridge +cargo fmt --all +cargo build --all-targets --all-features +cargo build --release --all-targets --all-features +cargo clippy --release --all-targets --fix --allow-dirty --allow-staged +cargo fmt --all -- --check +cargo check --all +cargo clippy --all-targets --release -- \ + -A clippy::similar_names \ + -A clippy::too_many_arguments \ + -A clippy::significant_drop_tightening \ + -A clippy::redundant_closure \ + -A clippy::missing_errors_doc \ + -A clippy::type_complexity +cargo test --release --all --all-features +cargo bench --workspace --all-targets --all-features -j 14 +cargo doc --workspace --all-features --no-deps +cd .. diff --git a/hooks/linux-macos/pre-commit b/scripts/git-hooks/linux-macos/pre-push similarity index 95% rename from hooks/linux-macos/pre-commit rename to scripts/git-hooks/linux-macos/pre-push index 7ce60a58..8f16a5f6 100644 --- a/hooks/linux-macos/pre-commit +++ b/scripts/git-hooks/linux-macos/pre-push @@ -48,6 +48,6 @@ EOF # exec git diff-index --check --cached $against -- fi -if [ -e "./check-before-push.sh" ]; then - ./check-before-push.sh +if [ -e "./scripts/check-before-push.sh" ]; then + ./scripts/check-before-push.sh fi diff --git a/hooks/windows/pre-commit b/scripts/git-hooks/windows/pre-push similarity index 94% rename from hooks/windows/pre-commit rename to scripts/git-hooks/windows/pre-push index 842f923d..d6bec32d 100644 --- a/hooks/windows/pre-commit +++ b/scripts/git-hooks/windows/pre-push @@ -38,8 +38,8 @@ if ($allownonascii -ne $true) { # Call an additional script for checks before pushing (if applicable) # Ensure that `check-before-push.ps1` exists in the repo -if (Test-Path "./check-before-push.bat") { - & "./check-before-push.bat" +if (Test-Path ".\scripts\check-before-push.bat") { + & ".\scripts\check-before-push.bat" } exit 0 diff --git a/src/crypto.rs b/src/crypto.rs index a1e8f8f4..72ca489a 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -374,7 +374,6 @@ where { let parent = file.parent().ok_or(Error::Generic("file has no parent"))?; let mut file = fs_util::open_atomic_write(file)?; - // println!("file: {:#?}", file.as_file_mut().metadata()?); file = serialize_encrypt_into(file, value, cipher, key)?; file.commit()?; File::open(parent)?.sync_all()?; @@ -540,7 +539,7 @@ mod tests { #[test] fn test_encrypt_decrypt_empty_string() { let key = SecretVec::from(vec![0; 32]); - let secret = SecretString::new(Box::new("".to_string())); + let secret = SecretString::new(Box::new(String::new())); let encrypted = encrypt(&secret, Cipher::ChaCha20Poly1305, &key).unwrap(); let decrypted = decrypt(&encrypted, Cipher::ChaCha20Poly1305, &key).unwrap(); @@ -550,24 +549,24 @@ mod tests { #[test] fn test_hash_file_name_special_cases() { - let expected = "$.".to_string(); + let expected = "$.".to_owned(); let name = SecretString::new(Box::new(expected.clone())); let result = hash_file_name(&name); assert_eq!(result, expected); - let expected = "$..".to_string(); + let expected = "$..".to_owned(); let name = SecretString::new(Box::new(expected.clone())); let result = hash_file_name(&name); assert_eq!(result, expected); - let input = ".".to_string(); - let expected = "$.".to_string(); + let input = ".".to_owned(); + let expected = "$.".to_owned(); let name = SecretString::new(Box::new(input)); let result = hash_file_name(&name); assert_eq!(result, expected); - let input = "..".to_string(); - let expected = "$..".to_string(); + let input = "..".to_owned(); + let expected = "$..".to_owned(); let name = SecretString::new(Box::new(input)); let result = hash_file_name(&name); assert_eq!(result, expected); @@ -575,7 +574,7 @@ mod tests { #[test] fn test_hash_file_name_regular_case() { - let name = SecretString::new(Box::new("filename.txt".to_string())); + let name = SecretString::new(Box::new("filename.txt".to_owned())); let result = hash_file_name(&name); let expected_hash = hex::encode(hash_secret_string(&name)); assert_eq!(result, expected_hash); @@ -583,7 +582,7 @@ mod tests { #[test] fn test_hash_secret_string() { - let secret = SecretString::new(Box::new("hash this secret".to_string())); + let secret = SecretString::new(Box::new("hash this secret".to_owned())); let expected_hash_hex = "d820cbf278fc742d8ec30e43947674689cd06d5aa9b71a2f9afe162a4ce408dc"; let hash_hex = hex::encode(hash_secret_string(&secret)); diff --git a/src/crypto/read.rs b/src/crypto/read.rs index 91ad1e35..74368154 100644 --- a/src/crypto/read.rs +++ b/src/crypto/read.rs @@ -13,7 +13,6 @@ use crate::crypto::buf_mut::BufMut; use crate::crypto::write::BLOCK_SIZE; use crate::stream_util; -mod bench; mod test; /// Reads encrypted content from the wrapped Reader. diff --git a/src/crypto/write.rs b/src/crypto/write.rs index 9de2785d..1a673ec0 100644 --- a/src/crypto/write.rs +++ b/src/crypto/write.rs @@ -24,13 +24,13 @@ pub(crate) const BLOCK_SIZE: usize = 100; // round value easier for debugging #[cfg(not(test))] pub(crate) const BLOCK_SIZE: usize = 256 * 1024; // 256 KB block size -/// If you have your custom [Write] + [Seek] you want to pass to [CryptoWrite] it needs to implement this trait. +/// If you have your custom [Write] + [Seek] you want to pass to [`CryptoWrite`] it needs to implement this trait. /// It has a blanket implementation for [Write] + [Seek] + [Read]. pub trait WriteSeekRead: Write + Seek + Read {} impl WriteSeekRead for T {} -/// If you have your custom implementation for [Write] you want to pass to [CryptoWrite] it needs to implement this trait. +/// If you have your custom implementation for [Write] you want to pass to [`CryptoWrite`] it needs to implement this trait. /// /// It has a blanket implementation for [Write] + [Seek] + [Read] + [`'static`] but in case your implementation is only [Write] it needs to implement this. pub trait CryptoInnerWriter: Write + Any { diff --git a/src/encryptedfs.rs b/src/encryptedfs.rs index 5179f346..7c2da5a4 100644 --- a/src/encryptedfs.rs +++ b/src/encryptedfs.rs @@ -407,7 +407,7 @@ impl From for SetFileAttr { #[derive(Debug, Clone)] pub struct DirectoryEntry { pub ino: u64, - pub name: SecretBox, + pub name: SecretString, pub kind: FileType, } @@ -423,7 +423,7 @@ impl PartialEq for DirectoryEntry { #[derive(Debug)] pub struct DirectoryEntryPlus { pub ino: u64, - pub name: SecretBox, + pub name: SecretString, pub kind: FileType, pub attr: FileAttr, } @@ -567,7 +567,7 @@ pub struct EncryptedFs { self_weak: std::sync::Mutex>>, attr_cache: ExpireValue>, FsError, AttrCacheProvider>, dir_entries_name_cache: - ExpireValue>>, FsError, DirEntryNameCacheProvider>, + ExpireValue>, FsError, DirEntryNameCacheProvider>, dir_entries_meta_cache: ExpireValue, FsError, DirEntryMetaCacheProvider>, sizes_write: Mutex>, @@ -657,6 +657,17 @@ impl EncryptedFs { self.read_only } + fn validate_filename(&self, secret_filename: &SecretBox) -> FsResult<()> { + let filename = secret_filename.expose_secret().to_string(); + if filename.contains('/') { + Err(FsError::InvalidInput("'/' not allowed in the filename")) + } else if filename.contains('\\') { + Err(FsError::InvalidInput("'\\' not allowed in the filename")) + } else { + Ok(()) + } + } + /// Create a new node in the filesystem #[allow(clippy::missing_panics_doc)] #[allow(clippy::missing_errors_doc)] @@ -669,6 +680,9 @@ impl EncryptedFs { read: bool, write: bool, ) -> FsResult<(u64, FileAttr)> { + if self.read_only { + return Err(FsError::ReadOnly); + } if *name.expose_secret() == "." || *name.expose_secret() == ".." { return Err(FsError::InvalidInput("name cannot be '.' or '..'")); } @@ -678,9 +692,7 @@ impl EncryptedFs { if self.exists_by_name(parent, name)? { return Err(FsError::AlreadyExists); } - if self.read_only { - return Err(FsError::ReadOnly); - } + self.validate_filename(name)?; // spawn on a dedicated runtime to not interfere with other higher priority tasks let self_clone = self @@ -838,7 +850,7 @@ impl EncryptedFs { } let lock = self .serialize_dir_entries_hash_locks - .get_or_insert_with(hash_path.to_str().unwrap().to_string(), || { + .get_or_insert_with(hash_path.to_str().unwrap().to_owned(), || { RwLock::new(false) }); let guard = lock.read().await; @@ -872,12 +884,12 @@ impl EncryptedFs { #[allow(clippy::missing_panics_doc)] #[allow(clippy::missing_errors_doc)] pub async fn remove_dir(&self, parent: u64, name: &SecretString) -> FsResult<()> { - if !self.is_dir(parent) { - return Err(FsError::InvalidInodeType); - } if self.read_only { return Err(FsError::ReadOnly); } + if !self.is_dir(parent) { + return Err(FsError::InvalidInodeType); + } if !self.exists_by_name(parent, name)? { return Err(FsError::NotFound("name not found")); @@ -949,15 +961,15 @@ impl EncryptedFs { #[allow(clippy::missing_panics_doc)] #[allow(clippy::missing_errors_doc)] pub async fn remove_file(&self, parent: u64, name: &SecretString) -> FsResult<()> { + if self.read_only { + return Err(FsError::ReadOnly); + } if !self.is_dir(parent) { return Err(FsError::InvalidInodeType); } if !self.exists_by_name(parent, name)? { return Err(FsError::NotFound("name not found")); } - if self.read_only { - return Err(FsError::ReadOnly); - } let attr = self .find_by_name(parent, name) @@ -966,6 +978,7 @@ impl EncryptedFs { if !matches!(attr.kind, FileType::RegularFile) { return Err(FsError::InvalidInodeType); } + // todo move to method let self_clone = self .self_weak .lock() @@ -1150,7 +1163,10 @@ impl EncryptedFs { } } }; - let file_path = entry.path().to_str().unwrap().to_string(); + + self.validate_filename(&name)?; + + let file_path = entry.path().to_str().unwrap().to_owned(); // try from cache let lock = self.dir_entries_meta_cache.get().await?; let mut cache = lock.lock().await; @@ -1529,7 +1545,7 @@ impl EncryptedFs { .load(Ordering::SeqCst); info!("written for {ino} {write_size}"); if attr.size != write_size { - error!("size mismatch write {} {}", write_size, attr.size); + // error!("size mismatch write {} {}", write_size, attr.size); } let requested_read = self .requested_read @@ -1650,7 +1666,7 @@ impl EncryptedFs { (writer.stream_position()?, len) }; - let size = ctx.attr.size; + // let size = ctx.attr.size; if pos > ctx.attr.size { // if we write pass file size set the new size debug!("setting new file size {}", pos); @@ -1672,20 +1688,20 @@ impl EncryptedFs { .unwrap() .fetch_add(len as u64, Ordering::SeqCst); if buf.len() != len { - error!( - "size mismatch in write(), size {size} offset {offset} buf_len {} len {len}", - buf.len() - ); - } - info!( - "written uncommited for {ino} size {}", - self.sizes_write - .lock() - .await - .get(&ino) - .unwrap() - .load(Ordering::SeqCst) - ); + // error!( + // "size mismatch in write(), size {size} offset {offset} buf_len {} len {len}", + // buf.len() + // ); + } + // warn!( + // "written uncommited for {ino} size {}", + // self.sizes_write + // .lock() + // .await + // .get(&ino) + // .unwrap() + // .load(Ordering::SeqCst) + // ); Ok(len) } @@ -1693,13 +1709,13 @@ impl EncryptedFs { /// Flush the data to the underlying storage. #[allow(clippy::missing_panics_doc)] pub async fn flush(&self, handle: u64) -> FsResult<()> { + if self.read_only { + return Err(FsError::ReadOnly); + } if handle == 0 { // in the case of directory or if the file was crated without being opened we don't use a handle return Ok(()); } - if self.read_only { - return Err(FsError::ReadOnly); - } let lock = self.read_handles.read().await; let mut valid_fh = lock.get(&handle).is_some(); let lock = self.write_handles.read().await; @@ -1732,12 +1748,12 @@ impl EncryptedFs { file_range_req: &CopyFileRangeReq, size: usize, ) -> FsResult { - if self.is_dir(file_range_req.src_ino) || self.is_dir(file_range_req.dest_ino) { - return Err(FsError::InvalidInodeType); - } if self.read_only { return Err(FsError::ReadOnly); } + if self.is_dir(file_range_req.src_ino) || self.is_dir(file_range_req.dest_ino) { + return Err(FsError::InvalidInodeType); + } let mut buf = vec![0; size]; let len = self @@ -1976,9 +1992,9 @@ impl EncryptedFs { pub async fn rename( &self, parent: u64, - name: &SecretBox, + name: &SecretString, new_parent: u64, - new_name: &SecretBox, + new_name: &SecretString, ) -> FsResult<()> { if self.read_only { return Err(FsError::ReadOnly); @@ -1998,6 +2014,7 @@ impl EncryptedFs { if !self.exists_by_name(parent, name)? { return Err(FsError::NotFound("name not found")); } + self.validate_filename(new_name)?; if parent == new_parent && name.expose_secret() == new_name.expose_secret() { // no-op @@ -2038,7 +2055,7 @@ impl EncryptedFs { attr.ino, &DirectoryEntry { ino: new_parent, - name: SecretBox::new(Box::new("$..".to_string())), + name: SecretBox::new(Box::new("$..".to_owned())), kind: FileType::Directory, }, ) @@ -2115,8 +2132,8 @@ impl EncryptedFs { /// Change the password of the filesystem used to access the encryption key. pub async fn passwd( data_dir: &Path, - old_password: SecretBox, - new_password: SecretBox, + old_password: SecretString, + new_password: SecretString, cipher: Cipher, ) -> FsResult<()> { check_structure(data_dir, false).await?; @@ -2165,10 +2182,7 @@ impl EncryptedFs { // read let lock = self.opened_files_for_read.read().await; if let Some(set) = lock.get(&ino) { - for handle in set - .iter() - .filter(|h| skip_write_fh.map_or(true, |fh| **h != fh)) - { + for handle in set.iter().filter(|h| skip_write_fh != Some(**h)) { let guard = self.read_handles.read().await; let ctx = guard.get(handle).unwrap().lock().await; let set_attr: SetFileAttr = ctx.attr.clone().into(); @@ -2350,7 +2364,7 @@ impl EncryptedFs { .join(encrypted_name_clone.clone()); let lock = self_clone .serialize_dir_entries_ls_locks - .get_or_insert_with(file_path.to_str().unwrap().to_string(), || { + .get_or_insert_with(file_path.to_str().unwrap().to_owned(), || { RwLock::new(false) }); let _guard = lock.write().await; @@ -2379,7 +2393,7 @@ impl EncryptedFs { let file_path = parent_path.join(HASH_DIR).join(name); let lock = self_clone .serialize_dir_entries_hash_locks - .get_or_insert_with(file_path.to_str().unwrap().to_string(), || { + .get_or_insert_with(file_path.to_str().unwrap().to_owned(), || { RwLock::new(false) }); let _guard = lock.write().await; @@ -2414,7 +2428,7 @@ impl EncryptedFs { let path = parent_path.join(HASH_DIR).join(name); let lock = self .serialize_dir_entries_hash_locks - .get_or_insert_with(path.to_str().unwrap().to_string(), || RwLock::new(false)); + .get_or_insert_with(path.to_str().unwrap().to_owned(), || RwLock::new(false)); let guard = lock.write().await; let (_, _, name): (u64, FileType, String) = bincode::deserialize_from(crypto::create_read( @@ -2428,7 +2442,7 @@ impl EncryptedFs { let path = parent_path.join(LS_DIR).join(name); let lock = self .serialize_dir_entries_ls_locks - .get_or_insert_with(path.to_str().unwrap().to_string(), || RwLock::new(false)); + .get_or_insert_with(path.to_str().unwrap().to_owned(), || RwLock::new(false)); let _guard = lock.write().await; fs::remove_file(path)?; Ok(()) diff --git a/src/encryptedfs/test.rs b/src/encryptedfs/test.rs index 783cab77..81e33d05 100644 --- a/src/encryptedfs/test.rs +++ b/src/encryptedfs/test.rs @@ -638,7 +638,7 @@ async fn test_read_dir() { .await .unwrap(); - let test_file_3 = SecretString::from_str("test/file/3").unwrap(); + let test_file_3 = SecretString::from_str("test_file_3").unwrap(); let (_fh, file_attr_2) = fs .create( parent, @@ -650,7 +650,7 @@ async fn test_read_dir() { .await .unwrap(); - let test_dir_2 = SecretString::from_str("test\\dir//2").unwrap(); + let test_dir_2 = SecretString::from_str("test_dir_2").unwrap(); let (_fh, dir_attr) = fs .create( parent, @@ -966,7 +966,7 @@ async fn test_find_by_name() { ); }, ) - .await + .await; } #[tokio::test] @@ -981,7 +981,7 @@ async fn test_exists_by_name() { async { let fs = get_fs().await; - for file in ["test-file", "test//\\file"] { + for file in ["test-file", "test--file"] { let test_file = SecretString::from_str(file).unwrap(); let _ = fs .create( @@ -1002,7 +1002,7 @@ async fn test_exists_by_name() { } }, ) - .await + .await; } #[tokio::test] @@ -1016,7 +1016,7 @@ async fn test_remove_dir() { }, async { let fs = get_fs().await; - for dir in ["test-dir", "test-dir\\", "test-dir/"] { + for dir in ["test-dir", "test-dir_", "test-dir-"] { let test_dir = SecretString::from_str(dir).unwrap(); let _ = fs .create( @@ -1046,7 +1046,7 @@ async fn test_remove_dir() { } }, ) - .await + .await; } #[tokio::test] @@ -1061,7 +1061,7 @@ async fn test_remove_file() { async { let fs = get_fs().await; - for dir in ["test-dir", "test-dir\\", "test-dir/"] { + for dir in ["test-dir", "test-dir_", "test-dir-clear"] { let test_file = SecretString::from_str(dir).unwrap(); let _ = fs .create( @@ -1092,7 +1092,7 @@ async fn test_remove_file() { } }, ) - .await + .await; } #[tokio::test] @@ -1121,7 +1121,7 @@ async fn test_find_by_name_exists_by_name100files() { .unwrap(); } - let special_test_file = SecretString::from_str("test//\\file").unwrap(); + let special_test_file = SecretString::from_str("test-_file").unwrap(); let _ = fs .create( ROOT_INODE, @@ -1149,7 +1149,7 @@ async fn test_find_by_name_exists_by_name100files() { .is_some()); }, ) - .await + .await; } #[tokio::test] @@ -1185,7 +1185,7 @@ async fn test_create_structure_and_root() { assert!(fs.data_dir.join(CONTENTS_DIR).join(ROOT_INODE_STR).is_dir()); }, ) - .await + .await; } #[tokio::test] @@ -1376,7 +1376,7 @@ async fn test_create() { )); }, ) - .await + .await; } #[tokio::test] @@ -1591,7 +1591,7 @@ async fn test_rename() { ) .await .unwrap(); - let dir_2 = SecretString::from_str("dir-\\2").unwrap(); + let dir_2 = SecretString::from_str("dir-_2").unwrap(); fs.rename(ROOT_INODE, &dir_1, new_parent, &dir_2) .await .unwrap(); @@ -1663,7 +1663,7 @@ async fn test_rename() { // file to existing file in same directory let file_1 = SecretString::from_str("file-1").unwrap(); - let file_2 = SecretString::from_str("file-/2").unwrap(); + let file_2 = SecretString::from_str("file--2").unwrap(); let new_parent = ROOT_INODE; let (_, attr) = fs .create( @@ -2271,7 +2271,7 @@ async fn test_rename() { )); }, ) - .await + .await; } #[tokio::test] @@ -2311,7 +2311,7 @@ async fn test_open() { )); }, ) - .await + .await; } // #[tokio::test] @@ -2356,7 +2356,7 @@ async fn test_read_only_create() { assert!(matches!(create_file_result, Err(FsError::ReadOnly))); }, ) - .await + .await; } #[tokio::test] @@ -2374,8 +2374,7 @@ async fn test_read_only_write() { let cipher = Cipher::ChaCha20Poly1305; let file1 = SecretString::from_str("file1").unwrap(); let file_dest = SecretString::from_str("file_dest").unwrap(); - let dir1 = data_dir.clone().join("dir1"); - let dir1 = SecretString::from_str(dir1.to_str().unwrap()).unwrap(); + let dir1 = SecretString::from_str("dir1").unwrap(); let data = "Hello, world!"; let (fh, attr) = fs_rw @@ -2480,5 +2479,5 @@ async fn test_read_only_write() { assert!(matches!(flush_result, Err(FsError::ReadOnly))); }, ) - .await + .await; } diff --git a/src/expire_value.rs b/src/expire_value.rs index 80669340..b64937bb 100644 --- a/src/expire_value.rs +++ b/src/expire_value.rs @@ -1,6 +1,5 @@ use std::error::Error; use std::marker::PhantomData; -use std::string::ToString; use std::sync::{Arc, Weak}; use std::time::Duration; @@ -63,7 +62,7 @@ impl< let value = self.provider.provide().await?; let v = Arc::new(value); self.cache - .insert(KEY.to_string(), v.clone(), self.duration) + .insert(KEY.to_owned(), v.clone(), self.duration) .await; let mut weak = self.weak.write().await; *weak = Some(Arc::downgrade(&v)); @@ -78,7 +77,7 @@ impl< return Some(v.clone()); } // try to take it from cache - if let Some(v) = self.cache.get(&KEY.to_string()).await { + if let Some(v) = self.cache.get(&KEY.to_owned()).await { return Some(v.clone()); } } @@ -114,7 +113,7 @@ mod tests { impl ValueProvider for TestProvider { async fn provide(&self) -> Result { self.called.fetch_add(1, Ordering::SeqCst); - Ok("test".to_string()) + Ok("test".to_owned()) } } @@ -149,6 +148,6 @@ mod tests { let _ = expire_value.get().await.unwrap(); // ensure provider was called again let called = called.clone(); - assert_eq!(called.load(Ordering::SeqCst), 3) + assert_eq!(called.load(Ordering::SeqCst), 3); } } diff --git a/src/keyring.rs b/src/keyring.rs index b2fb96e2..9970dc76 100644 --- a/src/keyring.rs +++ b/src/keyring.rs @@ -6,7 +6,7 @@ use shush_rs::{ExposeSecret, SecretString}; #[allow(dead_code)] const KEYRING_SERVICE: &str = "rencfs"; #[allow(dead_code)] -const KEYRING_USER: &str = "encrypted_fs"; +const KEYRING_USER: &str = "rencfs"; #[allow(dead_code)] pub(crate) fn save(password: &SecretString, suffix: &str) -> Result<(), keyring::Error> { @@ -25,3 +25,33 @@ pub(crate) fn get(suffix: &str) -> Result { let entry = Entry::new(KEYRING_SERVICE, &format!("{KEYRING_USER}.{suffix}"))?; Ok(SecretString::from_str(&entry.get_password()?).unwrap()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_save() { + let password = SecretString::from_str("password").unwrap(); + assert!(save(&password, "test1").is_ok()); + remove("test1").unwrap(); + } + + #[test] + fn test_get() { + let password = SecretString::from_str("password").unwrap(); + save(&password, "test2").unwrap(); + assert_eq!( + get("test2").unwrap().expose_secret(), + password.expose_secret() + ); + remove("test2").unwrap(); + } + + #[test] + fn test_remove() { + let password = SecretString::from_str("password").unwrap(); + save(&password, "test3").unwrap(); + assert!(remove("test3").is_ok()); + } +} diff --git a/src/lib.rs b/src/lib.rs index 79715b4f..4dfc2c88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,6 @@ //! ```no_run //! use std::env::args; //! use std::path::Path; -//! use std::str::FromStr; //! use std::io; //! use tracing::info; //! @@ -43,13 +42,13 @@ //! args.next(); // skip program name //! let mount_path = args.next().expect("mount_path expected"); //! let data_path = args.next().expect("data_path expected"); -//! use rencfs::crypto::Cipher; +//! use rencfs::crypto::Cipher; //! //! struct PasswordProviderImpl {} //! impl PasswordProvider for PasswordProviderImpl { //! fn get_password(&self) -> Option { -//! // dummy password, use some secure way to get the password like with [keyring](https://crates.io/crates/keyring) crate -//! Some(SecretString::from_str("pass42").unwrap()) +//! // placeholder password, use some secure way to get the password like with [keyring](https://crates.io/crates/keyring) crate +//! Some(SecretString::new(Box::new(String::from("pass42")))) //! } //! } //! let mount_point = create_mount_point( @@ -77,15 +76,15 @@ //! You need to specify several parameters to create an encrypted file system: //! - `data_dir`: The directory where the file system will be mounted. //! - `password`: The password to encrypt/decrypt the data. -//! - `cipher`: The encryption algorithm to use. +//! - `Cipher`: The encryption algorithm to use. //! //! Currently, it supports these ciphers [Cipher](crypto::Cipher). //! //! ### Example //! //! ``` +//! #![allow(unused_imports)] //! use std::fs; -//! use std::str::FromStr; //! use shush_rs::SecretString; //! use rencfs::encryptedfs::{EncryptedFs, FileType, PasswordProvider, CreateFileAttr}; //! use rencfs::crypto::Cipher; @@ -98,8 +97,8 @@ //! struct PasswordProviderImpl {} //! impl PasswordProvider for PasswordProviderImpl { //! fn get_password(&self) -> Option { -//! // dummy password, use some secure way to get the password like with [keyring](https://crates.io/crates/keyring) crate -//! Some(SecretString::from_str("pass42").unwrap()) +//! // placeholder password, use some secure way to get the password like with [keyring](https://crates.io/crates/keyring) crate +//! Some(SecretString::new(Box::new(String::from("pass42")))) //! } //! } //! @@ -112,7 +111,7 @@ //! let cipher = Cipher::ChaCha20Poly1305; //! let mut fs = EncryptedFs::new(data_dir.clone(), Box::new(PasswordProviderImpl{}), cipher, false).await?; //! -//! let file1 = SecretString::from_str("file1").unwrap(); +//! let file1 = SecretString::new(Box::new(String::from("file-1"))); //! let (fh, attr) = fs.create(ROOT_INODE, &file1, file_attr(), false, true).await?; //! let data = "Hello, world!"; //! write_all_string_to_fs( &fs, attr.ino, 0,data, fh).await?; @@ -142,13 +141,13 @@ //! ## Change password from code //! //! ### Example +//! //! ```no_run //! use rencfs::crypto::Cipher; //! use rencfs::encryptedfs::{EncryptedFs, FsError}; //! use shush_rs::SecretString; //! use std::env::args; //! use std::path::Path; -//! use std::str::FromStr; //! //! #[tokio::main] //! async fn main() { @@ -160,8 +159,8 @@ //! //! match EncryptedFs::passwd( //! Path::new(&data_dir), -//! SecretString::from_str("old-pass").unwrap(), -//! SecretString::from_str("new-pass").unwrap(), +//! SecretString::new(Box::new(String::from("old-pass"))), +//! SecretString::new(Box::new(String::from("new-pass"))), //! Cipher::ChaCha20Poly1305, //! ) //! .await @@ -181,7 +180,6 @@ //! use std::env::args; //! use std::io; //! use std::io::Write; -//! use std::str::FromStr; //! //! use rpassword::read_password; //! use shush_rs::{ExposeSecret, SecretString}; @@ -201,13 +199,13 @@ //! use rencfs::crypto::Cipher; //! print!("Enter old password: "); //! io::stdout().flush().unwrap(); -//! let old_password = SecretString::from_str(&read_password().unwrap()).unwrap(); +//! let old_password = SecretString::new(Box::new(read_password().unwrap())); //! print!("Enter new password: "); //! io::stdout().flush().unwrap(); -//! let new_password = SecretString::from_str(&read_password().unwrap()).unwrap(); +//! let new_password = SecretString::new(Box::new(read_password().unwrap())); //! print!("Confirm new password: "); //! io::stdout().flush().unwrap(); -//! let new_password2 = SecretString::from_str(&read_password().unwrap()).unwrap(); +//! let new_password2 = SecretString::new(Box::new(read_password().unwrap())); //! if new_password.expose_secret() != new_password2.expose_secret() { //! error!("Passwords do not match"); //! return; diff --git a/src/mount/linux.rs b/src/mount/linux.rs index f65c779b..37aa7946 100644 --- a/src/mount/linux.rs +++ b/src/mount/linux.rs @@ -1446,6 +1446,7 @@ async fn mount_fuse( .read_only(read_only) .allow_root(allow_root) .allow_other(allow_other) + .fs_name("rencfs") .clone(); let mount_path = OsStr::new(mountpoint.to_str().unwrap()); diff --git a/src/test_common.rs b/src/test_common.rs index bd047378..f8e18611 100644 --- a/src/test_common.rs +++ b/src/test_common.rs @@ -30,7 +30,7 @@ pub static TESTS_DATA_DIR: LazyLock = LazyLock::new(|| { fs::remove_file(tmp.to_str().unwrap()).expect("cannot remove tmp file"); tmp.to_path_buf() }; - println!("tmp {}", tmp.to_path_buf().to_string_lossy()); + println!("tmp {}", tmp.clone().to_string_lossy()); tmp.parent() .expect("oops, we don't have a parent") .join("rencfs-test-data") diff --git a/tests/linux_mount_setup/mod.rs b/tests/linux_mount_setup/mod.rs index a89899ee..e5a06b7c 100644 --- a/tests/linux_mount_setup/mod.rs +++ b/tests/linux_mount_setup/mod.rs @@ -62,10 +62,12 @@ impl Drop for TestResource { let res = self.runtime.block_on(async { mh.umount().await }); match res { Ok(_) => println!("Succesfully unmounted"), - Err(e) => panic!( - "Something went wrong when unmounting {}.You may need to manually unmount", - e - ), + Err(e) => { + panic!( + "Something went wrong when unmounting {}.You may need to manually unmount", + e + ) + } } } } @@ -86,7 +88,7 @@ impl TestGuard { }); } RESOURCE_COUNT.fetch_add(1, Ordering::SeqCst); - TestGuard + Self } } diff --git a/tests/rencfs_linux_itest.rs b/tests/rencfs_linux_itest.rs index 23f07cdb..ca3ff27b 100644 --- a/tests/rencfs_linux_itest.rs +++ b/tests/rencfs_linux_itest.rs @@ -27,7 +27,7 @@ fn it_create_and_write_file() { { let res = File::create_new(path); assert!(res.is_ok(), "failed to create [{}]", res.err().unwrap()); - let bytes_written = res.unwrap().write_all("test".as_bytes()); + let bytes_written = res.unwrap().write_all(b"test"); assert!( bytes_written.is_ok(), "failed to write [{}]", diff --git a/website/resources/download.png b/website/resources/download.png new file mode 100644 index 00000000..b23b3227 Binary files /dev/null and b/website/resources/download.png differ diff --git a/website/resources/slack.png b/website/resources/slack.png new file mode 100644 index 00000000..5b05833e Binary files /dev/null and b/website/resources/slack.png differ diff --git a/website/resources/slack3.png b/website/resources/slack3.png deleted file mode 100644 index 044c657d..00000000 Binary files a/website/resources/slack3.png and /dev/null differ