feat: add crypt4ghfs-rust
This commit is contained in:
@@ -75,6 +75,7 @@ Project or files:
|
|||||||
│ ├── robusta_jni
|
│ ├── robusta_jni
|
||||||
│ └── rust_link_a
|
│ └── rust_link_a
|
||||||
├── __fs
|
├── __fs
|
||||||
|
│ ├── crypt4ghfs-rust
|
||||||
│ ├── fuse
|
│ ├── fuse
|
||||||
│ └── fuser
|
│ └── fuser
|
||||||
├── __gui
|
├── __gui
|
||||||
@@ -259,6 +260,6 @@ Project or files:
|
|||||||
├── vec.rs
|
├── vec.rs
|
||||||
└── while.rs
|
└── while.rs
|
||||||
|
|
||||||
228 directories, 38 files
|
229 directories, 38 files
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
9
__fs/crypt4ghfs-rust/.gitignore
vendored
Normal file
9
__fs/crypt4ghfs-rust/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
/target/
|
||||||
|
|
||||||
|
# These are backup files generated by rustfmt
|
||||||
|
**/*.rs.bk
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
.vscode
|
||||||
68
__fs/crypt4ghfs-rust/.rustfmt.toml
Normal file
68
__fs/crypt4ghfs-rust/.rustfmt.toml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
max_width = 120
|
||||||
|
hard_tabs = true
|
||||||
|
tab_spaces = 4
|
||||||
|
newline_style = "Auto"
|
||||||
|
use_small_heuristics = "Default"
|
||||||
|
indent_style = "Block"
|
||||||
|
wrap_comments = false
|
||||||
|
format_code_in_doc_comments = true
|
||||||
|
comment_width = 80
|
||||||
|
normalize_comments = true
|
||||||
|
normalize_doc_attributes = true
|
||||||
|
license_template_path = ""
|
||||||
|
format_strings = true
|
||||||
|
format_macro_matchers = true
|
||||||
|
format_macro_bodies = true
|
||||||
|
empty_item_single_line = true
|
||||||
|
struct_lit_single_line = true
|
||||||
|
fn_single_line = false
|
||||||
|
where_single_line = false
|
||||||
|
imports_indent = "Block"
|
||||||
|
imports_layout = "Mixed"
|
||||||
|
imports_granularity = "Module"
|
||||||
|
group_imports = "StdExternalCrate"
|
||||||
|
reorder_imports = true
|
||||||
|
reorder_modules = true
|
||||||
|
reorder_impl_items = true
|
||||||
|
type_punctuation_density = "Wide"
|
||||||
|
space_before_colon = false
|
||||||
|
space_after_colon = true
|
||||||
|
spaces_around_ranges = false
|
||||||
|
binop_separator = "Front"
|
||||||
|
remove_nested_parens = true
|
||||||
|
combine_control_expr = false
|
||||||
|
overflow_delimited_expr = false
|
||||||
|
struct_field_align_threshold = 0
|
||||||
|
enum_discrim_align_threshold = 0
|
||||||
|
match_arm_blocks = true
|
||||||
|
match_arm_leading_pipes = "Never"
|
||||||
|
force_multiline_blocks = false
|
||||||
|
fn_args_layout = "Tall"
|
||||||
|
brace_style = "SameLineWhere"
|
||||||
|
control_brace_style = "ClosingNextLine"
|
||||||
|
trailing_semicolon = true
|
||||||
|
trailing_comma = "Vertical"
|
||||||
|
match_block_trailing_comma = true
|
||||||
|
blank_lines_upper_bound = 1
|
||||||
|
blank_lines_lower_bound = 0
|
||||||
|
edition = "2021"
|
||||||
|
version = "Two"
|
||||||
|
inline_attribute_width = 0
|
||||||
|
merge_derives = true
|
||||||
|
use_try_shorthand = true
|
||||||
|
use_field_init_shorthand = true
|
||||||
|
force_explicit_abi = true
|
||||||
|
condense_wildcard_suffixes = false
|
||||||
|
color = "Auto"
|
||||||
|
required_version = "1.4.38"
|
||||||
|
unstable_features = true
|
||||||
|
disable_all_formatting = false
|
||||||
|
skip_children = false
|
||||||
|
hide_parse_errors = false
|
||||||
|
error_on_line_overflow = false
|
||||||
|
error_on_unformatted = false
|
||||||
|
report_todo = "Never"
|
||||||
|
report_fixme = "Never"
|
||||||
|
ignore = []
|
||||||
|
emit_mode = "Files"
|
||||||
|
make_backup = false
|
||||||
793
__fs/crypt4ghfs-rust/Cargo.lock
generated
Normal file
793
__fs/crypt4ghfs-rust/Cargo.lock
generated
Normal file
@@ -0,0 +1,793 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bincode"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.73"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "3.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"bitflags",
|
||||||
|
"clap_derive",
|
||||||
|
"indexmap",
|
||||||
|
"lazy_static",
|
||||||
|
"os_str_bytes",
|
||||||
|
"strsim",
|
||||||
|
"termcolor",
|
||||||
|
"textwrap",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "3.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da95d038ede1a964ce99f49cbe27a7fb538d1da595e4b4f70b8c8f338d17bf16"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypt4gh"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2a2e0c28027b2d309e3928de8fe5f02ed5a8a3e4732ddabf91067a5c67ebafa0"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"bincode",
|
||||||
|
"clap",
|
||||||
|
"itertools",
|
||||||
|
"lazy_static",
|
||||||
|
"libsodium-sys",
|
||||||
|
"log",
|
||||||
|
"pretty_env_logger",
|
||||||
|
"regex",
|
||||||
|
"rpassword",
|
||||||
|
"rust-crypto",
|
||||||
|
"serde",
|
||||||
|
"sodiumoxide",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypt4ghfs"
|
||||||
|
version = "0.3.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"crypt4gh",
|
||||||
|
"fuser",
|
||||||
|
"itertools",
|
||||||
|
"log",
|
||||||
|
"nix",
|
||||||
|
"pretty_env_logger",
|
||||||
|
"rpassword",
|
||||||
|
"serde",
|
||||||
|
"sodiumoxide",
|
||||||
|
"syslog",
|
||||||
|
"thiserror",
|
||||||
|
"toml",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ed25519"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eed12bbf7b5312f8da1c2722bc06d8c6b12c2d86a7fb35a194c7f3e6fc2bbe39"
|
||||||
|
dependencies = [
|
||||||
|
"signature",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"humantime",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
"termcolor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "error-chain"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
|
||||||
|
dependencies = [
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-cprng"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuser"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aef8400a4ea1d18a8302e2952f5137a9a21ab257825ccc7d67db4a8018b89022"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"memchr",
|
||||||
|
"page_size",
|
||||||
|
"pkg-config",
|
||||||
|
"smallvec",
|
||||||
|
"users",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gcc"
|
||||||
|
version = "0.3.55"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hostname"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"match_cfg",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||||
|
dependencies = [
|
||||||
|
"quick-error",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.120"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad5c14e80759d0939d013e6ca49930e59fc53dd8e5009132f76240c179380c09"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libsodium-sys"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6b779387cd56adfbc02ea4a668e704f729be8d6a6abd2c27ca5ee537849a92fd"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "match_cfg"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.23.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"memoffset",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_threads"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c539a50b93a303167eded6e8dff5220cd39447409fb659f4cd24b1f72fe4f133"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "os_str_bytes"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "page_size"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pretty_env_logger"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
|
||||||
|
dependencies = [
|
||||||
|
"env_logger",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-error"
|
||||||
|
version = "1.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.3.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand 0.4.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||||
|
dependencies = [
|
||||||
|
"fuchsia-cprng",
|
||||||
|
"libc",
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
"rdrand",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rdrand"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rpassword"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49b7ef46d67d4cecf32ad486814d625738e79e4ccd62531dde0548b2f242f894"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-crypto"
|
||||||
|
version = "0.2.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
|
||||||
|
dependencies = [
|
||||||
|
"gcc",
|
||||||
|
"libc",
|
||||||
|
"rand 0.3.23",
|
||||||
|
"rustc-serialize",
|
||||||
|
"time 0.1.44",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-serialize"
|
||||||
|
version = "0.3.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||||
|
|
||||||
|
[[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 = "serde"
|
||||||
|
version = "1.0.136"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.136"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signature"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f054c6c1a6e95179d6f23ed974060dcefb2d9388bb7256900badad682c499de4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sodiumoxide"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e26be3acb6c2d9a7aac28482586a7856436af4cfe7100031d219de2d2ecb0028"
|
||||||
|
dependencies = [
|
||||||
|
"ed25519",
|
||||||
|
"libc",
|
||||||
|
"libsodium-sys",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.87"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e59d925cf59d8151f25a3bedf97c9c157597c9df7324d32d68991cc399ed08b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "synstructure"
|
||||||
|
version = "0.12.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syslog"
|
||||||
|
version = "6.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "978044cc68150ad5e40083c9f6a725e6fd02d7ba1bcf691ec2ff0d66c0b41acc"
|
||||||
|
dependencies = [
|
||||||
|
"error-chain",
|
||||||
|
"hostname",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"time 0.3.7",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.1.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"libc",
|
||||||
|
"num_threads",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.5.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "users"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "332f188cc1bcf1fe1064b8c58d150f497e697f49774aa846f2dc949d9a25f236"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0fbc82b82efe24da867ee52e015e58178684bd9dd64c34e66bdf21da2582a9f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"syn",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
43
__fs/crypt4ghfs-rust/Cargo.toml
Normal file
43
__fs/crypt4ghfs-rust/Cargo.toml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
[package]
|
||||||
|
name = "crypt4ghfs"
|
||||||
|
version = "0.3.0"
|
||||||
|
authors = ["Roberto <roberto.ariosa@crg.eu>"]
|
||||||
|
edition = "2021"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
description = "Fuse layer exposing Crypt4GH-encrypted files, as if they were decrypted"
|
||||||
|
repository = "https://github.com/EGA-archive/crypt4ghfs-rust"
|
||||||
|
documentation = "https://docs.rs/crypt4ghfs"
|
||||||
|
keywords = ["crypt4gh", "genetics", "filesystem", "encryption", "c4gh"]
|
||||||
|
categories = ["filesystem", "cryptography", "encoding"]
|
||||||
|
readme = "README.md"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "crypt4ghfs"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "crypt4ghfs"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Default
|
||||||
|
fuser = "0.11"
|
||||||
|
clap = { version = "3.1", features = ["derive"] }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
thiserror = "1.0"
|
||||||
|
toml = "0.5"
|
||||||
|
itertools = "0.10"
|
||||||
|
rpassword = "6.0"
|
||||||
|
crypt4gh = "0.4"
|
||||||
|
nix = "0.23"
|
||||||
|
|
||||||
|
# Logger
|
||||||
|
pretty_env_logger = "0.4"
|
||||||
|
log = "0.4"
|
||||||
|
syslog = "6.0"
|
||||||
|
|
||||||
|
# Decrypting
|
||||||
|
sodiumoxide = "0.2"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
201
__fs/crypt4ghfs-rust/LICENSE
Normal file
201
__fs/crypt4ghfs-rust/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
75
__fs/crypt4ghfs-rust/README.md
Normal file
75
__fs/crypt4ghfs-rust/README.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
> Copied from: https://github.com/EGA-archive/crypt4ghfs-rust
|
||||||
|
|
||||||
|
# Crypt4GH File System (in Rust)
|
||||||
|
|
||||||
|
[](https://crates.io/crates/crypt4ghfs)
|
||||||
|
[](https://docs.rs/crypt4ghfs/latest/crypt4ghfs)
|
||||||
|

|
||||||
|
|
||||||
|
Crypt4GH FUSE File system in Rust. It allows to encrypt and decrypt crypt4gh files in a directory automatically.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
> Requirements: [Rust](https://www.rust-lang.org/tools/install)
|
||||||
|
|
||||||
|
Supported platforms:
|
||||||
|
|
||||||
|
- **Linux** (tested on Ubuntu 20.04)
|
||||||
|
- **macOS** (up to Big Sur, Monterey does not support FUSE yet)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo install crypt4ghfs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The usage of the command is the following:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
USAGE:
|
||||||
|
crypt4ghfs [FLAGS] <MOUNTPOINT> --conf <conf_path>
|
||||||
|
|
||||||
|
ARGS:
|
||||||
|
<MOUNTPOINT>
|
||||||
|
|
||||||
|
FLAGS:
|
||||||
|
-h, --help Prints help information
|
||||||
|
-v, --verbose Sets the level of verbosity
|
||||||
|
-V, --version Prints version information
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
--conf <conf_path>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[DEFAULT]
|
||||||
|
# Extensions to be detected as encrypted
|
||||||
|
extensions = ["c4gh"]
|
||||||
|
|
||||||
|
[LOGGER]
|
||||||
|
# Whether to use syslog or to output to stderr
|
||||||
|
use_syslog = false
|
||||||
|
# Level of the logger. Should be one of ["TRACE", "DEBUG", "INFO", "WARN", "CRITICAL"]
|
||||||
|
log_level = "DEBUG"
|
||||||
|
# Syslog facility
|
||||||
|
log_facility = "local2"
|
||||||
|
|
||||||
|
[FUSE]
|
||||||
|
# The options that will be sent to fuse. The following are available:
|
||||||
|
# "allow_other", "allow_root", "allow_unmount", "default_permissions", "dev", "no_dev", "suid", "no_suid", "ro", "rw", "exec", "no_exec", "atime", "no_atime", "dir_sync", "sync", "async"
|
||||||
|
options= ["ro", "default_permissions", "allow_other", "auto_unmount"]
|
||||||
|
# Path to the root directory of the filesystem
|
||||||
|
rootdir="tests/rootdir"
|
||||||
|
|
||||||
|
[CRYPT4GH]
|
||||||
|
# Path to the public keys of the recipients to encrypt to
|
||||||
|
recipients = ["tests/testfiles/bob.pub"]
|
||||||
|
# Include log of the crypt4gh encryption / decryption
|
||||||
|
include_crypt4gh_log = true
|
||||||
|
# Include my public key to the recipients (so I can decrypt the file too)
|
||||||
|
include_myself_as_recipient = true
|
||||||
|
# Path to my private key
|
||||||
|
seckey = "tests/configs/bob"
|
||||||
|
```
|
||||||
17
__fs/crypt4ghfs-rust/app.yaml
Normal file
17
__fs/crypt4ghfs-rust/app.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
name: crypt4ghfs
|
||||||
|
help: Fuse layer exposing Crypt4GH-encrypted files, as if they were decrypted.
|
||||||
|
args:
|
||||||
|
- verbose:
|
||||||
|
required: false
|
||||||
|
short: v
|
||||||
|
long: verbose
|
||||||
|
help: Sets the level of verbosity
|
||||||
|
takes_value: false
|
||||||
|
- conf:
|
||||||
|
required: true
|
||||||
|
long: conf
|
||||||
|
#help: TODO
|
||||||
|
takes_value: true
|
||||||
|
value_name: conf_path
|
||||||
|
- MOUNTPOINT:
|
||||||
|
required: true
|
||||||
17
__fs/crypt4ghfs-rust/justfile
Normal file
17
__fs/crypt4ghfs-rust/justfile
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
run:
|
||||||
|
echo "The passphrase is 'bob'"
|
||||||
|
cargo run -- --conf tests/configs/fs.conf tests/mountpoint
|
||||||
|
|
||||||
|
keygen:
|
||||||
|
crypt4gh keygen --pk testkey.pub --sk testkey.sec
|
||||||
|
|
||||||
|
encrypt:
|
||||||
|
crypt4gh encrypt --sk tests/keys/testkey.sec --recipient_pk tests/keys/bob.pub < tests/decrypted/file.txt > tests/rootdir/file.txt.c4gh
|
||||||
|
|
||||||
|
decrypt:
|
||||||
|
echo "The passphrase is 'bob'"
|
||||||
|
crypt4gh decrypt --sk tests/keys/bob.sec < tests/rootdir/file.txt.c4gh
|
||||||
|
|
||||||
|
umount:
|
||||||
|
umount tests/mountpoint
|
||||||
18
__fs/crypt4ghfs-rust/src/cli.rs
Normal file
18
__fs/crypt4ghfs-rust/src/cli.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
/// Fuse layer exposing Crypt4GH-encrypted files, as if they were decrypted.
|
||||||
|
#[derive(clap::Parser)]
|
||||||
|
#[clap(about, version, author)]
|
||||||
|
pub struct Args {
|
||||||
|
/// Display debug information
|
||||||
|
#[clap(short, long)]
|
||||||
|
pub verbose: bool,
|
||||||
|
|
||||||
|
/// Path to the config file
|
||||||
|
#[clap(short, long)]
|
||||||
|
pub conf: PathBuf,
|
||||||
|
|
||||||
|
/// Path to the mountpoint
|
||||||
|
#[clap()]
|
||||||
|
pub mountpoint: PathBuf,
|
||||||
|
}
|
||||||
338
__fs/crypt4ghfs-rust/src/config.rs
Normal file
338
__fs/crypt4ghfs-rust/src/config.rs
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crypt4gh::error::Crypt4GHError;
|
||||||
|
use crypt4gh::Keys;
|
||||||
|
use fuser::MountOption;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use rpassword::prompt_password;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::error::Crypt4GHFSError;
|
||||||
|
|
||||||
|
const PASSPHRASE: &str = "C4GH_PASSPHRASE";
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Copy, Clone)]
|
||||||
|
#[serde(rename_all = "UPPERCASE")]
|
||||||
|
pub enum LogLevel {
|
||||||
|
#[serde(alias = "CRITICAL")]
|
||||||
|
Critical,
|
||||||
|
Warn,
|
||||||
|
Info,
|
||||||
|
Debug,
|
||||||
|
#[serde(alias = "NOTSET")]
|
||||||
|
Trace,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Copy, Clone)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Facility {
|
||||||
|
Kern,
|
||||||
|
User,
|
||||||
|
Mail,
|
||||||
|
Daemon,
|
||||||
|
Auth,
|
||||||
|
Syslog,
|
||||||
|
Lpr,
|
||||||
|
News,
|
||||||
|
Uucp,
|
||||||
|
Cron,
|
||||||
|
Authpriv,
|
||||||
|
Ftp,
|
||||||
|
Local0,
|
||||||
|
Local1,
|
||||||
|
Local2,
|
||||||
|
Local3,
|
||||||
|
Local4,
|
||||||
|
Local5,
|
||||||
|
Local6,
|
||||||
|
Local7,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct Default {
|
||||||
|
extensions: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct Fuse {
|
||||||
|
options: Option<Vec<String>>,
|
||||||
|
rootdir: String,
|
||||||
|
cache_directories: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct Crypt4GH {
|
||||||
|
#[serde(rename = "seckey")]
|
||||||
|
seckey_path: Option<String>,
|
||||||
|
recipients: Option<Vec<String>>,
|
||||||
|
include_myself_as_recipient: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct LoggerConfig {
|
||||||
|
pub log_level: LogLevel,
|
||||||
|
pub use_syslog: bool,
|
||||||
|
pub log_facility: Option<Facility>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename_all = "UPPERCASE")]
|
||||||
|
pub struct Config {
|
||||||
|
default: Default,
|
||||||
|
pub logger: LoggerConfig,
|
||||||
|
fuse: Fuse,
|
||||||
|
crypt4gh: Crypt4GH,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn new_with_defaults(rootdir: String, seckey_path: Option<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
default: Default { extensions: vec![] },
|
||||||
|
fuse: Fuse {
|
||||||
|
rootdir,
|
||||||
|
options: Some(vec!["ro".into(), "default_permissions".into(), "auto_unmount".into()]),
|
||||||
|
cache_directories: Some(true),
|
||||||
|
},
|
||||||
|
crypt4gh: Crypt4GH {
|
||||||
|
seckey_path,
|
||||||
|
recipients: Some(vec![]),
|
||||||
|
include_myself_as_recipient: Some(true),
|
||||||
|
},
|
||||||
|
logger: LoggerConfig {
|
||||||
|
log_level: LogLevel::Info,
|
||||||
|
use_syslog: false,
|
||||||
|
log_facility: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_extensions(mut self, extensions: Vec<String>) -> Self {
|
||||||
|
self.default.extensions = extensions;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn with_log_level(mut self, log_level: LogLevel) -> Self {
|
||||||
|
self.logger.log_level = log_level;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_options(&self) -> Vec<MountOption> {
|
||||||
|
self.fuse.options.clone().map_or_else(
|
||||||
|
|| vec![MountOption::RW, MountOption::DefaultPermissions],
|
||||||
|
|options| {
|
||||||
|
options
|
||||||
|
.iter()
|
||||||
|
.map(String::as_str)
|
||||||
|
.map(str_to_mount_option)
|
||||||
|
.inspect(|option| {
|
||||||
|
log::info!("+ fuse option: {:?}", option);
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn get_cache(&self) -> bool {
|
||||||
|
if let Some(cache_directories) = self.fuse.cache_directories {
|
||||||
|
return cache_directories;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_extensions(&self) -> Vec<String> {
|
||||||
|
self.default.extensions.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_secret_key(&self) -> Result<Option<Vec<u8>>, Crypt4GHFSError> {
|
||||||
|
match &self.crypt4gh.seckey_path {
|
||||||
|
Some(seckey_path_str) => {
|
||||||
|
let seckey_path = Path::new(&seckey_path_str);
|
||||||
|
log::info!("Loading secret key from {}", seckey_path.display());
|
||||||
|
|
||||||
|
if !seckey_path.is_file() {
|
||||||
|
return Err(Crypt4GHFSError::SecretNotFound(seckey_path.into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let callback: Box<dyn Fn() -> Result<String, Crypt4GHError>> = match std::env::var(PASSPHRASE) {
|
||||||
|
Ok(_) => {
|
||||||
|
log::warn!("Warning: Using a passphrase in an environment variable is insecure");
|
||||||
|
Box::new(|| std::env::var(PASSPHRASE).map_err(|e| Crypt4GHError::NoPassphrase(e.into())))
|
||||||
|
},
|
||||||
|
Err(_) => Box::new(|| {
|
||||||
|
prompt_password(format!("Passphrase for {}: ", seckey_path.display()))
|
||||||
|
.map_err(|e| Crypt4GHError::NoPassphrase(e.into()))
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
let key = crypt4gh::keys::get_private_key(seckey_path, callback)
|
||||||
|
.map_err(|e| Crypt4GHFSError::SecretKeyError(e.to_string()))?;
|
||||||
|
|
||||||
|
Ok(Some(key))
|
||||||
|
},
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_recipients(&self, seckey: &[u8]) -> HashSet<Keys> {
|
||||||
|
let recipient_paths = &self.crypt4gh.recipients.clone().unwrap_or_default();
|
||||||
|
|
||||||
|
let mut recipient_pubkeys: HashSet<_> = recipient_paths
|
||||||
|
.iter()
|
||||||
|
.map(Path::new)
|
||||||
|
.filter(|path| path.exists())
|
||||||
|
.filter_map(|path| {
|
||||||
|
log::debug!("Recipient pubkey path: {}", path.display());
|
||||||
|
crypt4gh::keys::get_public_key(path).ok()
|
||||||
|
})
|
||||||
|
.unique()
|
||||||
|
.map(|key| Keys {
|
||||||
|
method: 0,
|
||||||
|
privkey: seckey.to_vec(),
|
||||||
|
recipient_pubkey: key,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if self.crypt4gh.include_myself_as_recipient.unwrap_or(true) {
|
||||||
|
let k = crypt4gh::keys::get_public_key_from_private_key(seckey)
|
||||||
|
.expect("Unable to extract public key from seckey");
|
||||||
|
recipient_pubkeys.insert(Keys {
|
||||||
|
method: 0,
|
||||||
|
privkey: seckey.to_vec(),
|
||||||
|
recipient_pubkey: k,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
recipient_pubkeys
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn get_log_level(&self) -> LogLevel {
|
||||||
|
self.logger.log_level
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_rootdir(&self) -> String {
|
||||||
|
self.fuse.rootdir.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_file(mut config_file: File) -> Result<Self, Crypt4GHFSError> {
|
||||||
|
let mut config_string = String::new();
|
||||||
|
config_file
|
||||||
|
.read_to_string(&mut config_string)
|
||||||
|
.map_err(|e| Crypt4GHFSError::BadConfig(e.to_string()))?;
|
||||||
|
let config_toml = toml::from_str(config_string.as_str()).map_err(|e| Crypt4GHFSError::BadConfig(e.to_string()));
|
||||||
|
config_toml
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_facility(&self) -> syslog::Facility {
|
||||||
|
match self.logger.log_facility.unwrap_or(Facility::User) {
|
||||||
|
Facility::Kern => syslog::Facility::LOG_KERN,
|
||||||
|
Facility::User => syslog::Facility::LOG_USER,
|
||||||
|
Facility::Mail => syslog::Facility::LOG_MAIL,
|
||||||
|
Facility::Daemon => syslog::Facility::LOG_DAEMON,
|
||||||
|
Facility::Auth => syslog::Facility::LOG_AUTH,
|
||||||
|
Facility::Syslog => syslog::Facility::LOG_SYSLOG,
|
||||||
|
Facility::Lpr => syslog::Facility::LOG_LPR,
|
||||||
|
Facility::News => syslog::Facility::LOG_NEWS,
|
||||||
|
Facility::Uucp => syslog::Facility::LOG_UUCP,
|
||||||
|
Facility::Cron => syslog::Facility::LOG_CRON,
|
||||||
|
Facility::Authpriv => syslog::Facility::LOG_AUTHPRIV,
|
||||||
|
Facility::Ftp => syslog::Facility::LOG_FTP,
|
||||||
|
Facility::Local0 => syslog::Facility::LOG_LOCAL0,
|
||||||
|
Facility::Local1 => syslog::Facility::LOG_LOCAL1,
|
||||||
|
Facility::Local2 => syslog::Facility::LOG_LOCAL2,
|
||||||
|
Facility::Local3 => syslog::Facility::LOG_LOCAL3,
|
||||||
|
Facility::Local4 => syslog::Facility::LOG_LOCAL4,
|
||||||
|
Facility::Local5 => syslog::Facility::LOG_LOCAL5,
|
||||||
|
Facility::Local6 => syslog::Facility::LOG_LOCAL6,
|
||||||
|
Facility::Local7 => syslog::Facility::LOG_LOCAL7,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_logger(&self) -> Result<(), Crypt4GHFSError> {
|
||||||
|
let log_level: LogLevel = if let Ok(log_level_str) = std::env::var("RUST_LOG") {
|
||||||
|
log_level_str
|
||||||
|
.as_str()
|
||||||
|
.try_into()
|
||||||
|
.expect("Unable to parse RUST_LOG environment variable")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let log_level = self.get_log_level();
|
||||||
|
let log_level_str = match log_level {
|
||||||
|
LogLevel::Critical => "error",
|
||||||
|
LogLevel::Warn => "warn",
|
||||||
|
LogLevel::Info => "info",
|
||||||
|
LogLevel::Debug => "debug",
|
||||||
|
LogLevel::Trace => "trace",
|
||||||
|
};
|
||||||
|
std::env::set_var("RUST_LOG", log_level_str);
|
||||||
|
log_level
|
||||||
|
};
|
||||||
|
|
||||||
|
// Choose logger
|
||||||
|
if self.logger.use_syslog {
|
||||||
|
syslog::init(self.get_facility(), log_level.into(), None)?;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let _ = pretty_env_logger::try_init(); // Ignore error
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for LogLevel {
|
||||||
|
type Error = Crypt4GHFSError;
|
||||||
|
|
||||||
|
fn try_from(level: &str) -> Result<Self, Self::Error> {
|
||||||
|
match level {
|
||||||
|
"error" => Ok(Self::Critical),
|
||||||
|
"warn" => Ok(Self::Warn),
|
||||||
|
"info" => Ok(Self::Info),
|
||||||
|
"debug" => Ok(Self::Debug),
|
||||||
|
"trace" => Ok(Self::Trace),
|
||||||
|
_ => Err(Crypt4GHFSError::BadConfig("Wrong log level".into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LogLevel> for log::LevelFilter {
|
||||||
|
fn from(val: LogLevel) -> Self {
|
||||||
|
match val {
|
||||||
|
LogLevel::Critical => Self::Error,
|
||||||
|
LogLevel::Warn => Self::Warn,
|
||||||
|
LogLevel::Info => Self::Info,
|
||||||
|
LogLevel::Debug => Self::Debug,
|
||||||
|
LogLevel::Trace => Self::Trace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn str_to_mount_option(s: &str) -> MountOption {
|
||||||
|
match s {
|
||||||
|
"auto_unmount" => MountOption::AutoUnmount,
|
||||||
|
"allow_other" => MountOption::AllowOther,
|
||||||
|
"allow_root" => MountOption::AllowRoot,
|
||||||
|
"default_permissions" => MountOption::DefaultPermissions,
|
||||||
|
"dev" => MountOption::Dev,
|
||||||
|
"nodev" => MountOption::NoDev,
|
||||||
|
"suid" => MountOption::Suid,
|
||||||
|
"nosuid" => MountOption::NoSuid,
|
||||||
|
"ro" => MountOption::RO,
|
||||||
|
"rw" => MountOption::RW,
|
||||||
|
"exec" => MountOption::Exec,
|
||||||
|
"noexec" => MountOption::NoExec,
|
||||||
|
"atime" => MountOption::Atime,
|
||||||
|
"noatime" => MountOption::NoAtime,
|
||||||
|
"dirsync" => MountOption::DirSync,
|
||||||
|
"sync" => MountOption::Sync,
|
||||||
|
"async" => MountOption::Async,
|
||||||
|
x if x.starts_with("fsname=") => MountOption::FSName(x[7..].into()),
|
||||||
|
x if x.starts_with("subtype=") => MountOption::Subtype(x[8..].into()),
|
||||||
|
x => MountOption::CUSTOM(x.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
74
__fs/crypt4ghfs-rust/src/directory.rs
Normal file
74
__fs/crypt4ghfs-rust/src/directory.rs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::egafile::EgaFile;
|
||||||
|
use crate::error::{Crypt4GHFSError, Result};
|
||||||
|
use crate::utils;
|
||||||
|
|
||||||
|
pub struct Directory {
|
||||||
|
pub opened_files: HashMap<u64, Box<File>>,
|
||||||
|
pub path: Box<Path>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EgaFile for Directory {
|
||||||
|
fn fh(&self) -> Vec<u64> {
|
||||||
|
self.opened_files.iter().map(|(&fh, _)| fh).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(&self) -> Box<Path> {
|
||||||
|
self.path.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open(&mut self, flags: i32) -> Result<i32> {
|
||||||
|
let path = self.path();
|
||||||
|
let directory = utils::open(&path, flags)?;
|
||||||
|
let fh = directory.as_raw_fd();
|
||||||
|
self.opened_files.insert(fh as u64, Box::new(directory));
|
||||||
|
Ok(fh)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&mut self, _fh: u64, _offset: i64, _size: u32) -> Result<Vec<u8>> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self, _fh: u64) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, _fh: u64, _data: &[u8]) -> Result<usize> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn truncate(&mut self, _fh: Option<u64>, _size: u64) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&mut self, fh: u64) -> Result<()> {
|
||||||
|
let f = self.opened_files.get(&fh).ok_or(Crypt4GHFSError::FileNotOpened)?;
|
||||||
|
assert_eq!(f.as_raw_fd(), fh as i32);
|
||||||
|
self.opened_files.remove(&fh);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rename(&mut self, new_path: &Path) {
|
||||||
|
self.path = new_path.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attrs(&self, uid: u32, gid: u32) -> Result<fuser::FileAttr> {
|
||||||
|
let stat = utils::lstat(&self.path)?;
|
||||||
|
Ok(utils::stat_to_fileatr(stat, uid, gid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Directory {
|
||||||
|
pub fn new(file: Option<Box<File>>, path: Box<Path>) -> Self {
|
||||||
|
// Build open files
|
||||||
|
let mut opened_files = HashMap::new();
|
||||||
|
if let Some(f) = file {
|
||||||
|
opened_files.insert(f.as_raw_fd() as u64, f);
|
||||||
|
}
|
||||||
|
Self { opened_files, path }
|
||||||
|
}
|
||||||
|
}
|
||||||
21
__fs/crypt4ghfs-rust/src/egafile.rs
Normal file
21
__fs/crypt4ghfs-rust/src/egafile.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use fuser::FileAttr;
|
||||||
|
|
||||||
|
use crate::error::Result;
|
||||||
|
|
||||||
|
pub trait EgaFile {
|
||||||
|
// Attributes
|
||||||
|
fn fh(&self) -> Vec<u64>;
|
||||||
|
fn path(&self) -> Box<Path>;
|
||||||
|
|
||||||
|
// Filesystem
|
||||||
|
fn open(&mut self, flags: i32) -> Result<i32>;
|
||||||
|
fn read(&mut self, fh: u64, offset: i64, size: u32) -> Result<Vec<u8>>;
|
||||||
|
fn flush(&mut self, fh: u64) -> Result<()>;
|
||||||
|
fn write(&mut self, fh: u64, data: &[u8]) -> Result<usize>;
|
||||||
|
fn truncate(&mut self, fh: Option<u64>, size: u64) -> Result<()>;
|
||||||
|
fn close(&mut self, fh: u64) -> Result<()>;
|
||||||
|
fn rename(&mut self, new_path: &Path);
|
||||||
|
fn attrs(&self, uid: u32, gid: u32) -> Result<FileAttr>;
|
||||||
|
}
|
||||||
316
__fs/crypt4ghfs-rust/src/encrypted_file.rs
Normal file
316
__fs/crypt4ghfs-rust/src/encrypted_file.rs
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use chacha20poly1305_ietf::{Key, Nonce};
|
||||||
|
use crypt4gh::header::DecryptedHeaderPackets;
|
||||||
|
use crypt4gh::{Keys, SEGMENT_SIZE};
|
||||||
|
use itertools::Itertools;
|
||||||
|
use sodiumoxide::crypto::aead::chacha20poly1305_ietf;
|
||||||
|
use sodiumoxide::randombytes::randombytes;
|
||||||
|
|
||||||
|
use crate::egafile::EgaFile;
|
||||||
|
use crate::error::{Crypt4GHFSError, Result};
|
||||||
|
use crate::utils;
|
||||||
|
|
||||||
|
const CIPHER_SEGMENT_SIZE: usize = SEGMENT_SIZE + 28;
|
||||||
|
|
||||||
|
pub struct EncryptedFile {
|
||||||
|
opened_files: HashMap<u64, Box<File>>,
|
||||||
|
path: Box<Path>,
|
||||||
|
session_key: [u8; 32],
|
||||||
|
keys: Vec<Keys>,
|
||||||
|
recipient_keys: HashSet<Keys>,
|
||||||
|
write_buffer: Vec<u8>,
|
||||||
|
only_read: bool,
|
||||||
|
|
||||||
|
// Optimization
|
||||||
|
buffer: HashMap<u64, EncryptionBuffer>,
|
||||||
|
session_keys: Vec<Vec<u8>>,
|
||||||
|
header_len: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct EncryptionBuffer {
|
||||||
|
data: Vec<u8>,
|
||||||
|
pos: usize,
|
||||||
|
valid: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EgaFile for EncryptedFile {
|
||||||
|
fn fh(&self) -> Vec<u64> {
|
||||||
|
self.opened_files.iter().map(|(&fh, _)| fh).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(&self) -> Box<std::path::Path> {
|
||||||
|
self.path.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open(&mut self, flags: i32) -> Result<i32> {
|
||||||
|
// Get path
|
||||||
|
let mut path_str = self.path().to_string_lossy().to_string();
|
||||||
|
path_str.push_str(".c4gh");
|
||||||
|
let path = Path::new(&path_str);
|
||||||
|
|
||||||
|
// Open file
|
||||||
|
let mut file = utils::open(path, flags)?;
|
||||||
|
let fh = file.as_raw_fd();
|
||||||
|
|
||||||
|
// Buffer
|
||||||
|
self.buffer.insert(fh as u64, EncryptionBuffer::default());
|
||||||
|
let (keys, header_length) = self.read_header(&mut file).unwrap();
|
||||||
|
self.session_keys = keys;
|
||||||
|
self.header_len = u64::from(header_length);
|
||||||
|
|
||||||
|
// Add to opened files
|
||||||
|
self.opened_files.insert(fh as u64, Box::new(file));
|
||||||
|
|
||||||
|
// Return
|
||||||
|
Ok(fh)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&mut self, fh: u64, offset: i64, size: u32) -> Result<Vec<u8>> {
|
||||||
|
let f = self.opened_files.get_mut(&fh).ok_or(Crypt4GHFSError::FileNotOpened)?;
|
||||||
|
|
||||||
|
let first_segment = offset as usize / SEGMENT_SIZE;
|
||||||
|
let mut off = offset as usize % SEGMENT_SIZE;
|
||||||
|
|
||||||
|
let length = off + size as usize;
|
||||||
|
let mut nsegments = length / SEGMENT_SIZE;
|
||||||
|
if length % SEGMENT_SIZE != 0 {
|
||||||
|
nsegments += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let start_pos = first_segment * SEGMENT_SIZE;
|
||||||
|
|
||||||
|
log::debug!("first_segment: {}", first_segment);
|
||||||
|
log::debug!("off: {}", off);
|
||||||
|
log::debug!("length: {}", length);
|
||||||
|
log::debug!("length % SEGMENT_SIZE != 0: {}", length % SEGMENT_SIZE != 0);
|
||||||
|
log::debug!("nsegments: {}", nsegments);
|
||||||
|
log::debug!("start_pos: {}", start_pos);
|
||||||
|
|
||||||
|
let buf = self.buffer.get(&fh).expect("No buffer");
|
||||||
|
|
||||||
|
if buf.valid && buf.pos <= start_pos && ((start_pos - buf.pos) + length) <= buf.data.len() {
|
||||||
|
log::debug!("Already have decrypted enough data");
|
||||||
|
off += start_pos - buf.pos;
|
||||||
|
|
||||||
|
Ok(buf.data[off..off + size as usize].into())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
f.seek(SeekFrom::Start(
|
||||||
|
self.header_len + first_segment as u64 * CIPHER_SEGMENT_SIZE as u64,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut output = Vec::new();
|
||||||
|
|
||||||
|
for _ in 0..nsegments {
|
||||||
|
let mut chunk = Vec::with_capacity(CIPHER_SEGMENT_SIZE);
|
||||||
|
let n = f.take(CIPHER_SEGMENT_SIZE as u64).read_to_end(&mut chunk).unwrap();
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let segment = Self::decrypt_block(&chunk, &self.session_keys);
|
||||||
|
output.extend_from_slice(&segment);
|
||||||
|
|
||||||
|
if n < CIPHER_SEGMENT_SIZE {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log::debug!("Output: {}", output.len());
|
||||||
|
|
||||||
|
Ok(output[off..(off + size as usize).min(output.len())].into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self, fh: u64) -> Result<()> {
|
||||||
|
let f = self.opened_files.get_mut(&fh).ok_or(Crypt4GHFSError::FileNotOpened)?;
|
||||||
|
if !self.write_buffer.is_empty() {
|
||||||
|
log::info!("Writing PARTIAL segment");
|
||||||
|
let nonce = Nonce::from_slice(&randombytes(12)).expect("Unable to create nonce from randombytes");
|
||||||
|
let key = Key::from_slice(&self.session_key).expect("Unable to create key from session_key");
|
||||||
|
let encrypted_segment = crypt4gh::encrypt_segment(&self.write_buffer, nonce, &key);
|
||||||
|
f.write_all(&encrypted_segment)?;
|
||||||
|
self.write_buffer.clear();
|
||||||
|
}
|
||||||
|
f.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, fh: u64, data: &[u8]) -> Result<usize> {
|
||||||
|
// Write header
|
||||||
|
if self.only_read {
|
||||||
|
// Build header
|
||||||
|
log::debug!("Writing HEADER");
|
||||||
|
let header_bytes = crypt4gh::encrypt_header(&self.recipient_keys, &Some(self.session_key))
|
||||||
|
.map_err(|e| Crypt4GHFSError::Crypt4GHError(e.to_string()))?;
|
||||||
|
log::debug!("Header size = {}", header_bytes.len());
|
||||||
|
|
||||||
|
// Write header
|
||||||
|
let f = self.opened_files.get_mut(&fh).ok_or(Crypt4GHFSError::FileNotOpened)?;
|
||||||
|
f.write_all(&header_bytes)?;
|
||||||
|
|
||||||
|
// Update status
|
||||||
|
self.only_read = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
log::debug!(
|
||||||
|
"write_buffer.len() = {}, data.len() = {}",
|
||||||
|
self.write_buffer.len(),
|
||||||
|
data.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Chain write buffer with data
|
||||||
|
let last_segment = self.write_buffer.clone();
|
||||||
|
let write_data = last_segment.into_iter().chain(data.iter().copied());
|
||||||
|
|
||||||
|
let mut new_last_segment = Vec::new();
|
||||||
|
for segment in &write_data.chunks(SEGMENT_SIZE) {
|
||||||
|
// Collect segment
|
||||||
|
let segment_slice = segment.collect::<Vec<_>>();
|
||||||
|
log::debug!("segment_slice.len() = {}", segment_slice.len());
|
||||||
|
|
||||||
|
// This is the last segment, add to the struct
|
||||||
|
if segment_slice.len() < SEGMENT_SIZE {
|
||||||
|
log::info!("Storing PARTIAL segment");
|
||||||
|
new_last_segment = segment_slice;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log::info!("Writing FULL segment");
|
||||||
|
// Full segment, write to the file
|
||||||
|
let f = self.opened_files.get_mut(&fh).ok_or(Crypt4GHFSError::FileNotOpened)?;
|
||||||
|
|
||||||
|
// Build encrypted segment
|
||||||
|
let nonce = Nonce::from_slice(&randombytes(12)).expect("Unable to create nonce from randombytes");
|
||||||
|
let key = Key::from_slice(&self.session_key).expect("Unable to create key from session_key");
|
||||||
|
let encrypted_segment = crypt4gh::encrypt_segment(&segment_slice, nonce, &key);
|
||||||
|
|
||||||
|
// Write segment
|
||||||
|
f.write_all(&encrypted_segment)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace segment buffer
|
||||||
|
self.write_buffer = new_last_segment;
|
||||||
|
|
||||||
|
// Return
|
||||||
|
Ok(data.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn truncate(&mut self, fh: Option<u64>, size: u64) -> Result<()> {
|
||||||
|
log::debug!("Truncate: size = {}", size);
|
||||||
|
self.opened_files
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|(&ffh, _)| fh.is_none() || fh == Some(ffh))
|
||||||
|
.try_for_each(|(_, f)| f.set_len(size))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&mut self, fh: u64) -> Result<()> {
|
||||||
|
let f = self.opened_files.get(&fh).ok_or(Crypt4GHFSError::FileNotOpened)?;
|
||||||
|
assert_eq!(f.as_raw_fd(), fh as i32);
|
||||||
|
self.opened_files.remove(&fh);
|
||||||
|
self.write_buffer.clear();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rename(&mut self, new_path: &Path) {
|
||||||
|
self.path = new_path.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attrs(&self, uid: u32, gid: u32) -> Result<fuser::FileAttr> {
|
||||||
|
let mut path_str = self.path.display().to_string();
|
||||||
|
path_str.push_str(".c4gh");
|
||||||
|
let stat = utils::lstat(Path::new(&path_str))?;
|
||||||
|
Ok(utils::stat_to_fileatr(stat, uid, gid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EncryptedFile {
|
||||||
|
pub fn new(file: Option<Box<File>>, path: Box<Path>, keys: &[Keys], recipient_keys: &HashSet<Keys>) -> Self {
|
||||||
|
// Build session_key
|
||||||
|
let mut session_key = [0_u8; 32];
|
||||||
|
sodiumoxide::randombytes::randombytes_into(&mut session_key);
|
||||||
|
|
||||||
|
// Build open files
|
||||||
|
let mut opened_files = HashMap::new();
|
||||||
|
if let Some(f) = file {
|
||||||
|
opened_files.insert(f.as_raw_fd() as u64, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
opened_files,
|
||||||
|
path,
|
||||||
|
session_key,
|
||||||
|
keys: keys.to_vec(),
|
||||||
|
recipient_keys: recipient_keys.clone(),
|
||||||
|
write_buffer: Vec::new(),
|
||||||
|
only_read: true,
|
||||||
|
buffer: HashMap::new(),
|
||||||
|
session_keys: Vec::new(),
|
||||||
|
header_len: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_header(&self, file: &mut File) -> Result<(Vec<Vec<u8>>, u32)> {
|
||||||
|
// Get header info
|
||||||
|
let mut header_length = 16;
|
||||||
|
let mut temp_buf = [0_u8; 16]; // Size of the header
|
||||||
|
file.read_exact(&mut temp_buf)?;
|
||||||
|
|
||||||
|
let header_info = crypt4gh::header::deconstruct_header_info(&temp_buf).unwrap();
|
||||||
|
|
||||||
|
// Calculate header packets
|
||||||
|
let encrypted_packets = (0..header_info.packets_count)
|
||||||
|
.map(|_| {
|
||||||
|
// Get length
|
||||||
|
let mut length_buffer = [0_u8; 4];
|
||||||
|
file.read_exact(&mut length_buffer).unwrap();
|
||||||
|
let length = u32::from_le_bytes(length_buffer);
|
||||||
|
header_length += length;
|
||||||
|
let length = length - 4;
|
||||||
|
log::debug!("Packet length: {}", length);
|
||||||
|
|
||||||
|
// Get data
|
||||||
|
let mut encrypted_data = vec![0_u8; length as usize];
|
||||||
|
file.read_exact(&mut encrypted_data).unwrap();
|
||||||
|
Ok(encrypted_data)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<Vec<u8>>>>()?;
|
||||||
|
|
||||||
|
let DecryptedHeaderPackets {
|
||||||
|
data_enc_packets: session_keys,
|
||||||
|
edit_list_packet: edit_list_content,
|
||||||
|
} = crypt4gh::header::deconstruct_header_body(encrypted_packets, &self.keys, &None).unwrap();
|
||||||
|
|
||||||
|
assert!(edit_list_content.is_none());
|
||||||
|
|
||||||
|
Ok((session_keys, header_length))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrypt_block(ciphersegment: &[u8], session_keys: &[Vec<u8>]) -> Vec<u8> {
|
||||||
|
let (nonce_slice, data) = ciphersegment.split_at(12);
|
||||||
|
let nonce = chacha20poly1305_ietf::Nonce::from_slice(nonce_slice).unwrap();
|
||||||
|
|
||||||
|
log::debug!("Nonce slice: {:02x?}", nonce_slice.iter().format(""));
|
||||||
|
log::debug!("Data len = {}", data.len());
|
||||||
|
for key in session_keys {
|
||||||
|
log::debug!("Session keys: {:02x?}", key.iter().format(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
session_keys
|
||||||
|
.iter()
|
||||||
|
.find_map(|key| {
|
||||||
|
chacha20poly1305_ietf::Key::from_slice(key)
|
||||||
|
.and_then(|key| chacha20poly1305_ietf::open(data, None, &nonce, &key).ok())
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
55
__fs/crypt4ghfs-rust/src/error.rs
Normal file
55
__fs/crypt4ghfs-rust/src/error.rs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
use std::io;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use nix::errno::Errno;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Crypt4GHFSError>;
|
||||||
|
const DEFAULT_LIBC_ERROR: Errno = Errno::ECANCELED;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Crypt4GHFSError {
|
||||||
|
#[error("Path does not exist (path: {0})")]
|
||||||
|
PathDoesNotExist(PathBuf),
|
||||||
|
#[error("Mounting process failed (ERROR = {0})")]
|
||||||
|
MountError(String),
|
||||||
|
#[error("Fork failed")]
|
||||||
|
ForkFailed,
|
||||||
|
#[error("Secret key not found (path: {0})")]
|
||||||
|
SecretNotFound(PathBuf),
|
||||||
|
#[error("Error reading config (ERROR = {0})")]
|
||||||
|
BadConfig(String),
|
||||||
|
#[error("Unable to extract secret key (ERROR = {0})")]
|
||||||
|
SecretKeyError(String),
|
||||||
|
#[error("Connection url bad format")]
|
||||||
|
BadConfigConnectionUrl,
|
||||||
|
#[error("Invalid checksum format")]
|
||||||
|
InvalidChecksumFormat,
|
||||||
|
#[error("No checksum found")]
|
||||||
|
NoChecksum,
|
||||||
|
#[error("Invalid connection_url scheme: {0}")]
|
||||||
|
InvalidScheme(String),
|
||||||
|
#[error("IO Error: {0}")]
|
||||||
|
IoError(#[from] io::Error),
|
||||||
|
#[error("CLI configuration failed (ERROR = {0})")]
|
||||||
|
ConfigError(#[from] clap::Error),
|
||||||
|
#[error("Config could not configure syslog (ERROR = {0})")]
|
||||||
|
SysLogError(#[from] syslog::Error),
|
||||||
|
#[error("Setting logger failed (ERROR = {0})")]
|
||||||
|
LogError(#[from] log::SetLoggerError),
|
||||||
|
#[error("File not opened")]
|
||||||
|
FileNotOpened,
|
||||||
|
#[error("Crypt4GH failed (ERROR = {0})")]
|
||||||
|
Crypt4GHError(String),
|
||||||
|
#[error("Libc failed (ERROR = {0})")]
|
||||||
|
LibcError(#[from] nix::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Crypt4GHFSError {
|
||||||
|
pub fn to_raw_os_error(&self) -> i32 {
|
||||||
|
match self {
|
||||||
|
Self::IoError(io_error) => io_error.raw_os_error().unwrap_or(DEFAULT_LIBC_ERROR as i32),
|
||||||
|
_ => DEFAULT_LIBC_ERROR as i32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
70
__fs/crypt4ghfs-rust/src/file_admin.rs
Normal file
70
__fs/crypt4ghfs-rust/src/file_admin.rs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::directory::Directory;
|
||||||
|
use crate::egafile::EgaFile;
|
||||||
|
|
||||||
|
type Inode = u64;
|
||||||
|
|
||||||
|
pub struct FileAdmin {
|
||||||
|
pub inode2file: BTreeMap<Inode, Box<dyn EgaFile>>,
|
||||||
|
path2inode: BTreeMap<Box<Path>, Inode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileAdmin {
|
||||||
|
pub fn new(rootdir: &str) -> Self {
|
||||||
|
// Create file admin
|
||||||
|
let mut file_admin = Self {
|
||||||
|
inode2file: BTreeMap::new(),
|
||||||
|
path2inode: BTreeMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add rootdir
|
||||||
|
file_admin.add(1, Box::new(Directory::new(None, Path::new(rootdir).into())));
|
||||||
|
|
||||||
|
// Return
|
||||||
|
file_admin
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, ino: u64, file: Box<dyn EgaFile>) {
|
||||||
|
self.path2inode.insert(file.path(), ino);
|
||||||
|
self.inode2file.insert(ino, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_by_path(&self, path: &Path) -> Option<&dyn EgaFile> {
|
||||||
|
self.path2inode.get(path).map(|ino| self.get_file(*ino))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_by_path_mut(&mut self, path: &Path) -> Option<&mut dyn EgaFile> {
|
||||||
|
self.path2inode
|
||||||
|
.get(path)
|
||||||
|
.copied()
|
||||||
|
.map(move |ino| self.get_file_mut(ino))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_file(&self, ino: u64) -> &dyn EgaFile {
|
||||||
|
self.inode2file.get(&ino).expect("Unable to get file").as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_file_mut(&mut self, ino: u64) -> &mut dyn EgaFile {
|
||||||
|
self.inode2file.get_mut(&ino).expect("Unable to get file").as_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, ino: u64) -> Option<Box<dyn EgaFile>> {
|
||||||
|
self.inode2file.remove(&ino)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_by_path(&mut self, path: &Path) -> Option<Box<dyn EgaFile>> {
|
||||||
|
// Find inode+
|
||||||
|
let mut inode = None;
|
||||||
|
for (ino, file) in &mut self.inode2file {
|
||||||
|
if file.path() == path.into() {
|
||||||
|
inode = Some(*ino);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove inode
|
||||||
|
self.remove(inode?)
|
||||||
|
}
|
||||||
|
}
|
||||||
458
__fs/crypt4ghfs-rust/src/filesystem.rs
Normal file
458
__fs/crypt4ghfs-rust/src/filesystem.rs
Normal file
@@ -0,0 +1,458 @@
|
|||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::fs::DirEntry;
|
||||||
|
use std::os::unix::fs::DirEntryExt;
|
||||||
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
|
use crypt4gh::Keys;
|
||||||
|
use fuser::{
|
||||||
|
Filesystem, ReplyAttr, ReplyCreate, ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry, ReplyOpen, ReplyStatfs,
|
||||||
|
ReplyWrite, Request, TimeOrNow,
|
||||||
|
};
|
||||||
|
use nix::errno::Errno;
|
||||||
|
use nix::unistd::{Gid, Uid};
|
||||||
|
|
||||||
|
use crate::file_admin::FileAdmin;
|
||||||
|
use crate::utils;
|
||||||
|
// use std::os::linux::fs::MetadataExt;
|
||||||
|
|
||||||
|
const TTL: Duration = Duration::from_secs(300);
|
||||||
|
|
||||||
|
pub struct Crypt4ghFS {
|
||||||
|
file_admin: FileAdmin,
|
||||||
|
keys: Vec<Keys>,
|
||||||
|
recipients: HashSet<Keys>,
|
||||||
|
uid: Uid,
|
||||||
|
gid: Gid,
|
||||||
|
entries: HashMap<u64, Vec<Result<DirEntry, std::io::Error>>>,
|
||||||
|
duration1: std::time::Duration,
|
||||||
|
// TODO: implement cache directories functionality
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Crypt4ghFS {
|
||||||
|
pub fn new(rootdir: &str, seckey: Vec<u8>, recipients: HashSet<Keys>, uid: Uid, gid: Gid) -> Self {
|
||||||
|
Self {
|
||||||
|
file_admin: FileAdmin::new(rootdir),
|
||||||
|
keys: vec![Keys {
|
||||||
|
method: 0,
|
||||||
|
privkey: seckey,
|
||||||
|
recipient_pubkey: vec![],
|
||||||
|
}],
|
||||||
|
recipients,
|
||||||
|
uid,
|
||||||
|
gid,
|
||||||
|
entries: HashMap::new(),
|
||||||
|
duration1: Duration::from_secs(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Filesystem for Crypt4ghFS {
|
||||||
|
// FILESYSTEM
|
||||||
|
|
||||||
|
fn destroy(&mut self) {
|
||||||
|
log::info!("1 - Elapsed: {:?}", self.duration1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getattr(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyAttr) {
|
||||||
|
let file = self.file_admin.get_file(ino);
|
||||||
|
match file.attrs(self.uid.as_raw(), self.gid.as_raw()) {
|
||||||
|
Ok(attrs) => reply.attr(&TTL, &attrs),
|
||||||
|
Err(_) => reply.error(1000),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setattr(
|
||||||
|
&mut self,
|
||||||
|
req: &Request<'_>,
|
||||||
|
ino: u64,
|
||||||
|
mode: Option<u32>,
|
||||||
|
uid: Option<u32>,
|
||||||
|
gid: Option<u32>,
|
||||||
|
size: Option<u64>,
|
||||||
|
atime: Option<TimeOrNow>,
|
||||||
|
mtime: Option<TimeOrNow>,
|
||||||
|
_ctime: Option<SystemTime>,
|
||||||
|
fh: Option<u64>,
|
||||||
|
crtime: Option<SystemTime>,
|
||||||
|
chgtime: Option<SystemTime>,
|
||||||
|
bkuptime: Option<SystemTime>,
|
||||||
|
flags: Option<u32>,
|
||||||
|
reply: ReplyAttr,
|
||||||
|
) {
|
||||||
|
let mut err = None;
|
||||||
|
|
||||||
|
if mode.is_some() {
|
||||||
|
reply.error(Errno::EPERM as i32);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if uid.is_some() || gid.is_some() {
|
||||||
|
reply.error(Errno::EPERM as i32);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if atime.is_some() || mtime.is_some() {
|
||||||
|
reply.error(Errno::EOPNOTSUPP as i32);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if crtime.is_some() || chgtime.is_some() || bkuptime.is_some() || flags.is_some() {
|
||||||
|
reply.error(Errno::EOPNOTSUPP as i32);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = self.file_admin.get_file_mut(ino);
|
||||||
|
|
||||||
|
if let Some(size) = size {
|
||||||
|
if let Err(e) = file.truncate(fh, size) {
|
||||||
|
err = Some(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match err {
|
||||||
|
None => match file.attrs(req.uid(), req.gid()) {
|
||||||
|
Ok(attrs) => reply.attr(&TTL, &attrs),
|
||||||
|
Err(e) => reply.error(e.to_raw_os_error()),
|
||||||
|
},
|
||||||
|
Some(e) => reply.error(e.to_raw_os_error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup(&mut self, req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) {
|
||||||
|
let parent_file = self.file_admin.get_file(parent);
|
||||||
|
|
||||||
|
let name_string = name.to_string_lossy().to_string();
|
||||||
|
let new_name = name_string.strip_suffix(".c4gh").unwrap_or(&name_string);
|
||||||
|
let path = parent_file.path().join(new_name);
|
||||||
|
|
||||||
|
match self.file_admin.get_by_path(path.as_path()) {
|
||||||
|
Some(child_file) => match child_file.attrs(req.uid(), req.gid()) {
|
||||||
|
Ok(attr) => reply.entry(&TTL, &attr, 0),
|
||||||
|
Err(e) => reply.error(e.to_raw_os_error()),
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
reply.error(Errno::ENOENT as i32);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn statfs(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyStatfs) {
|
||||||
|
let file = self.file_admin.get_file(ino);
|
||||||
|
match utils::statfs(&file.path()) {
|
||||||
|
Ok(statfs) => reply.statfs(
|
||||||
|
u64::from(statfs.blocks()),
|
||||||
|
u64::from(statfs.blocks_free()),
|
||||||
|
u64::from(statfs.blocks_available()),
|
||||||
|
u64::from(statfs.files()),
|
||||||
|
u64::from(statfs.files_free()),
|
||||||
|
statfs.block_size() as u32,
|
||||||
|
statfs.name_max() as u32,
|
||||||
|
statfs.fragment_size() as u32,
|
||||||
|
),
|
||||||
|
Err(e) => reply.error(e.to_raw_os_error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE
|
||||||
|
|
||||||
|
fn open(&mut self, _req: &Request<'_>, ino: u64, flags: i32, reply: ReplyOpen) {
|
||||||
|
let file = self.file_admin.get_file_mut(ino);
|
||||||
|
match file.open(flags) {
|
||||||
|
Ok(fh) => reply.opened(fh as u64, flags as u32),
|
||||||
|
Err(e) => reply.error(e.to_raw_os_error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(
|
||||||
|
&mut self,
|
||||||
|
_req: &Request<'_>,
|
||||||
|
ino: u64,
|
||||||
|
fh: u64,
|
||||||
|
offset: i64,
|
||||||
|
size: u32,
|
||||||
|
_flags: i32,
|
||||||
|
_lock_owner: Option<u64>,
|
||||||
|
reply: ReplyData,
|
||||||
|
) {
|
||||||
|
let file = self.file_admin.get_file_mut(ino);
|
||||||
|
match file.read(fh, offset, size) {
|
||||||
|
Ok(data) => reply.data(&data),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("{:?}", e);
|
||||||
|
reply.error(e.to_raw_os_error());
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self, _req: &Request<'_>, ino: u64, fh: u64, _lock_owner: u64, reply: ReplyEmpty) {
|
||||||
|
let file = self.file_admin.get_file_mut(ino);
|
||||||
|
match file.flush(fh) {
|
||||||
|
Ok(_) => reply.ok(),
|
||||||
|
Err(e) => reply.error(e.to_raw_os_error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(
|
||||||
|
&mut self,
|
||||||
|
req: &Request<'_>,
|
||||||
|
parent: u64,
|
||||||
|
name: &OsStr,
|
||||||
|
mode: u32,
|
||||||
|
umask: u32,
|
||||||
|
flags: i32,
|
||||||
|
reply: ReplyCreate,
|
||||||
|
) {
|
||||||
|
// Get path
|
||||||
|
let parent_file = self.file_admin.get_file(parent);
|
||||||
|
let parent_path = parent_file.path();
|
||||||
|
let path = parent_path.join(name).into_boxed_path();
|
||||||
|
let mut inbox_path = path.to_path_buf();
|
||||||
|
if path.extension().is_none() || path.extension().unwrap() != "c4gh" {
|
||||||
|
let mut filename = inbox_path.file_name().unwrap().to_string_lossy().to_string();
|
||||||
|
filename.push_str(".c4gh");
|
||||||
|
inbox_path.set_file_name(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create file
|
||||||
|
log::debug!("Create new file with path: {:?}", inbox_path);
|
||||||
|
let file = utils::create(&inbox_path, flags, mode & umask).unwrap();
|
||||||
|
|
||||||
|
// Build file admin entry
|
||||||
|
let ino = utils::lstat(&inbox_path).unwrap().st_ino;
|
||||||
|
let egafile = utils::wrap_file(&path, file, &self.keys, &self.recipients);
|
||||||
|
|
||||||
|
// Build reply
|
||||||
|
let attrs = egafile.attrs(req.uid(), req.gid()).unwrap();
|
||||||
|
let fh = *egafile.fh().last().unwrap();
|
||||||
|
|
||||||
|
// Add and reply
|
||||||
|
self.file_admin.add(ino, egafile);
|
||||||
|
reply.created(&TTL, &attrs, 0, fh, flags as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(
|
||||||
|
&mut self,
|
||||||
|
_req: &Request<'_>,
|
||||||
|
ino: u64,
|
||||||
|
fh: u64,
|
||||||
|
_offset: i64,
|
||||||
|
data: &[u8],
|
||||||
|
_write_flags: u32,
|
||||||
|
_flags: i32,
|
||||||
|
_lock_owner: Option<u64>,
|
||||||
|
reply: ReplyWrite,
|
||||||
|
) {
|
||||||
|
let file = self.file_admin.get_file_mut(ino);
|
||||||
|
// TODO: Warn if offset != 0 => not allowed
|
||||||
|
match file.write(fh, data) {
|
||||||
|
Ok(size) => reply.written(size as u32),
|
||||||
|
Err(e) => reply.error(e.to_raw_os_error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn release(
|
||||||
|
&mut self,
|
||||||
|
_req: &Request<'_>,
|
||||||
|
ino: u64,
|
||||||
|
fh: u64,
|
||||||
|
_flags: i32,
|
||||||
|
_lock_owner: Option<u64>,
|
||||||
|
_flush: bool,
|
||||||
|
reply: ReplyEmpty,
|
||||||
|
) {
|
||||||
|
let file = self.file_admin.get_file_mut(ino);
|
||||||
|
|
||||||
|
file.close(fh).unwrap();
|
||||||
|
|
||||||
|
reply.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rename(
|
||||||
|
&mut self,
|
||||||
|
_req: &Request<'_>,
|
||||||
|
parent: u64,
|
||||||
|
name: &OsStr,
|
||||||
|
newparent: u64,
|
||||||
|
newname: &OsStr,
|
||||||
|
_flags: u32,
|
||||||
|
reply: ReplyEmpty,
|
||||||
|
) {
|
||||||
|
// Build paths
|
||||||
|
let old_parent_file = self.file_admin.get_file(parent).path();
|
||||||
|
let new_parent_path = self.file_admin.get_file(newparent).path();
|
||||||
|
let old_path = old_parent_file.join(name);
|
||||||
|
let new_path = new_parent_path.join(newname);
|
||||||
|
|
||||||
|
// Change paths
|
||||||
|
let file = self.file_admin.get_by_path_mut(&old_path).unwrap();
|
||||||
|
file.rename(new_path.as_path());
|
||||||
|
|
||||||
|
// Rename
|
||||||
|
match std::fs::rename(&old_path, new_path) {
|
||||||
|
Ok(_) => reply.ok(),
|
||||||
|
Err(e) => reply.error(e.raw_os_error().unwrap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unlink(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) {
|
||||||
|
// Build paths
|
||||||
|
let parent_file = self.file_admin.get_file(parent);
|
||||||
|
let parent_path = parent_file.path();
|
||||||
|
let path = parent_path.join(name);
|
||||||
|
|
||||||
|
// Remove file
|
||||||
|
match std::fs::remove_file(path) {
|
||||||
|
Ok(_) => reply.ok(),
|
||||||
|
Err(e) => reply.error(e.raw_os_error().unwrap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DIRECTORY
|
||||||
|
|
||||||
|
fn readdir(&mut self, _req: &Request<'_>, ino: u64, _fh: u64, skipped: i64, mut reply: ReplyDirectory) {
|
||||||
|
let _dir = self.file_admin.get_file(ino);
|
||||||
|
let entries = self.entries.get_mut(&ino).unwrap();
|
||||||
|
|
||||||
|
// TODO: Make sure that they have always the same order
|
||||||
|
|
||||||
|
let mut last_error = None;
|
||||||
|
for (offset, entry) in entries[skipped as usize..].iter().enumerate() {
|
||||||
|
log::debug!("Entry {} - {:?}", offset, entry);
|
||||||
|
match entry {
|
||||||
|
Ok(dir_entry) => {
|
||||||
|
// Track file
|
||||||
|
let egafile = utils::wrap_path(dir_entry.path().as_path(), &self.keys, &self.recipients);
|
||||||
|
self.file_admin.add(dir_entry.ino(), egafile);
|
||||||
|
|
||||||
|
// Kind
|
||||||
|
let kind = utils::get_type(dir_entry);
|
||||||
|
|
||||||
|
// Name
|
||||||
|
let name = dir_entry.file_name().clone();
|
||||||
|
let name_str = name.to_string_lossy().to_string();
|
||||||
|
let new_name = name_str.strip_suffix(".c4gh").unwrap_or(&name_str);
|
||||||
|
|
||||||
|
// Add entry
|
||||||
|
let buffer_full = reply.add(dir_entry.ino(), (skipped + offset as i64 + 1) as i64, kind, new_name);
|
||||||
|
if buffer_full {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Error on entry {} (ERROR = {:?})", offset, e);
|
||||||
|
last_error = Some(e.raw_os_error().expect("Unable to convert to error"));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match last_error {
|
||||||
|
None => reply.ok(),
|
||||||
|
Some(e) => reply.error(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn readdirplus(&mut self, req: &Request<'_>, ino: u64, _fh: u64, offset: i64, mut reply: ReplyDirectoryPlus) {
|
||||||
|
// let _dir = self.file_admin.get_file(ino);
|
||||||
|
// // TODO: Make sure that they have always the same order
|
||||||
|
// let entries = self.entries.get_mut(&ino).unwrap();
|
||||||
|
// let mut last_error = None;
|
||||||
|
// for (offset, entry) in entries.enumerate().skip(offset as usize) {
|
||||||
|
// match entry {
|
||||||
|
// Ok(dir_entry) => {
|
||||||
|
// let egafile = utils::wrap_path(dir_entry.path().as_path(), &self.keys, &self.recipients);
|
||||||
|
// self.file_admin.add(dir_entry.ino(), egafile);
|
||||||
|
// let kind = utils::get_type(&dir_entry);
|
||||||
|
// let name = dir_entry.file_name().clone();
|
||||||
|
// let metadata = dir_entry.metadata().unwrap();
|
||||||
|
// let attrs = FileAttr {
|
||||||
|
// ino: dir_entry.ino(),
|
||||||
|
// size: dir_entry.metadata().unwrap().len(),
|
||||||
|
// blocks: metadata.blocks(),
|
||||||
|
// atime: metadata.accessed().unwrap_or(std::time::UNIX_EPOCH),
|
||||||
|
// mtime: metadata.modified().unwrap_or(std::time::UNIX_EPOCH),
|
||||||
|
// ctime: metadata.created().unwrap_or(std::time::UNIX_EPOCH),
|
||||||
|
// crtime: std::time::UNIX_EPOCH,
|
||||||
|
// kind,
|
||||||
|
// perm: metadata.permissions().mode() as u16,
|
||||||
|
// #[cfg(target_os = "linux")]
|
||||||
|
// nlink: metadata.st_nlink() as u32,
|
||||||
|
// #[cfg(target_os = "macos")]
|
||||||
|
// nlink: 0,
|
||||||
|
// uid: req.uid(),
|
||||||
|
// gid: req.gid(),
|
||||||
|
// #[cfg(target_os = "linux")]
|
||||||
|
// rdev: metadata.st_rdev() as u32,
|
||||||
|
// #[cfg(target_os = "macos")]
|
||||||
|
// rdev: metadata.rdev() as u32,
|
||||||
|
// #[cfg(target_os = "linux")]
|
||||||
|
// blksize: metadata.st_blksize() as u32,
|
||||||
|
// #[cfg(target_os = "macos")]
|
||||||
|
// blksize: metadata.blksize() as u32,
|
||||||
|
// padding: 0,
|
||||||
|
// flags: 0,
|
||||||
|
// };
|
||||||
|
// let buffer_full = reply.add(dir_entry.ino(), (offset + 1) as i64, name, &TTL, &attrs, 0);
|
||||||
|
// if buffer_full {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// Err(e) => {
|
||||||
|
// last_error = Some(e.raw_os_error().expect("Unable to convert to error"));
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// match last_error {
|
||||||
|
// None => reply.ok(),
|
||||||
|
// Some(e) => reply.error(e),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn mkdir(&mut self, req: &Request<'_>, parent: u64, name: &OsStr, _mode: u32, _umask: u32, reply: ReplyEntry) {
|
||||||
|
let parent_file = self.file_admin.get_file(parent);
|
||||||
|
let parent_path = parent_file.path();
|
||||||
|
let path = parent_path.join(name);
|
||||||
|
match std::fs::create_dir(&path) {
|
||||||
|
Ok(_) => {
|
||||||
|
let stat = utils::lstat(&path).unwrap();
|
||||||
|
let attrs = utils::stat_to_fileatr(stat, req.uid(), req.gid());
|
||||||
|
reply.entry(&TTL, &attrs, 0);
|
||||||
|
},
|
||||||
|
Err(e) => reply.error(e.raw_os_error().expect("Unable to retrieve raw OS error")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rmdir(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) {
|
||||||
|
let parent_file = self.file_admin.get_file(parent);
|
||||||
|
let parent_path = parent_file.path();
|
||||||
|
let path = parent_path.join(name);
|
||||||
|
self.file_admin.remove_by_path(&path);
|
||||||
|
match std::fs::remove_dir(path) {
|
||||||
|
Ok(_) => reply.ok(),
|
||||||
|
Err(e) => reply.error(e.raw_os_error().expect("Unable to retrieve raw OS error")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn opendir(&mut self, _req: &Request<'_>, ino: u64, flags: i32, reply: ReplyOpen) {
|
||||||
|
let file = self.file_admin.get_file_mut(ino);
|
||||||
|
self.entries.insert(
|
||||||
|
ino,
|
||||||
|
std::fs::read_dir(file.path())
|
||||||
|
.expect("Unable to read directory")
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
match file.open(flags) {
|
||||||
|
Ok(fh) => reply.opened(fh as u64, flags as u32),
|
||||||
|
Err(e) => reply.error(e.to_raw_os_error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn releasedir(&mut self, _req: &Request<'_>, ino: u64, fh: u64, _flags: i32, reply: ReplyEmpty) {
|
||||||
|
let file = self.file_admin.get_file_mut(ino);
|
||||||
|
self.entries.remove(&ino);
|
||||||
|
match file.close(fh) {
|
||||||
|
Ok(_) => reply.ok(),
|
||||||
|
Err(e) => reply.error(e.to_raw_os_error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
__fs/crypt4ghfs-rust/src/lib.rs
Normal file
63
__fs/crypt4ghfs-rust/src/lib.rs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#![allow(
|
||||||
|
clippy::upper_case_acronyms,
|
||||||
|
clippy::missing_panics_doc,
|
||||||
|
clippy::missing_errors_doc,
|
||||||
|
clippy::must_use_candidate,
|
||||||
|
clippy::module_name_repetitions,
|
||||||
|
clippy::cast_sign_loss,
|
||||||
|
clippy::cast_possible_truncation,
|
||||||
|
clippy::similar_names
|
||||||
|
)]
|
||||||
|
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use config::Config;
|
||||||
|
use error::Crypt4GHFSError;
|
||||||
|
|
||||||
|
pub mod config;
|
||||||
|
mod directory;
|
||||||
|
mod egafile;
|
||||||
|
mod encrypted_file;
|
||||||
|
pub mod error;
|
||||||
|
mod file_admin;
|
||||||
|
mod filesystem;
|
||||||
|
mod regular_file;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
pub fn run_with_config(conf: &Config, mountpoint: PathBuf) -> Result<(), Crypt4GHFSError> {
|
||||||
|
// Set log level and logger
|
||||||
|
conf.setup_logger()?;
|
||||||
|
|
||||||
|
let rootdir = conf.get_rootdir();
|
||||||
|
if !Path::new(&rootdir).exists() {
|
||||||
|
return Err(Crypt4GHFSError::PathDoesNotExist(Path::new(&rootdir).into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encryption / Decryption keys
|
||||||
|
let seckey = (conf.get_secret_key()?).map_or_else(
|
||||||
|
|| {
|
||||||
|
log::warn!("No seckey specified");
|
||||||
|
vec![0_u8; 32]
|
||||||
|
},
|
||||||
|
|key| key,
|
||||||
|
);
|
||||||
|
|
||||||
|
let recipients = conf.get_recipients(&seckey);
|
||||||
|
|
||||||
|
// Get options
|
||||||
|
let options = conf.get_options();
|
||||||
|
|
||||||
|
let fs = filesystem::Crypt4ghFS::new(
|
||||||
|
&rootdir,
|
||||||
|
seckey,
|
||||||
|
recipients,
|
||||||
|
nix::unistd::getuid(),
|
||||||
|
nix::unistd::getgid(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if !mountpoint.exists() {
|
||||||
|
return Err(Crypt4GHFSError::PathDoesNotExist(mountpoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
fuser::mount2(fs, &mountpoint, &options).map_err(|e| Crypt4GHFSError::MountError(e.to_string()))
|
||||||
|
}
|
||||||
35
__fs/crypt4ghfs-rust/src/main.rs
Normal file
35
__fs/crypt4ghfs-rust/src/main.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
|
||||||
|
use clap::StructOpt;
|
||||||
|
use crypt4ghfs::error::Crypt4GHFSError;
|
||||||
|
use crypt4ghfs::{config, run_with_config};
|
||||||
|
|
||||||
|
use crate::cli::Args;
|
||||||
|
|
||||||
|
mod cli;
|
||||||
|
|
||||||
|
fn run() -> Result<(), Crypt4GHFSError> {
|
||||||
|
// Init CLI
|
||||||
|
let matches = Args::parse();
|
||||||
|
|
||||||
|
let mountpoint = matches.mountpoint;
|
||||||
|
|
||||||
|
// Read config
|
||||||
|
let config_path = matches.conf;
|
||||||
|
log::info!("Loading config: {:?}", config_path);
|
||||||
|
let config_file = File::open(config_path)?;
|
||||||
|
|
||||||
|
let conf = config::Config::from_file(config_file)?;
|
||||||
|
log::debug!("Config = {:?}", conf);
|
||||||
|
|
||||||
|
// Run
|
||||||
|
run_with_config(&conf, mountpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let Err(err) = run() {
|
||||||
|
let _ = pretty_env_logger::try_init();
|
||||||
|
log::error!("{}", err);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
99
__fs/crypt4ghfs-rust/src/regular_file.rs
Normal file
99
__fs/crypt4ghfs-rust/src/regular_file.rs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::egafile::EgaFile;
|
||||||
|
use crate::error::{Crypt4GHFSError, Result};
|
||||||
|
use crate::utils;
|
||||||
|
|
||||||
|
pub struct RegularFile {
|
||||||
|
pub opened_files: HashMap<u64, Box<File>>,
|
||||||
|
pub path: Box<Path>,
|
||||||
|
pub only_read: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EgaFile for RegularFile {
|
||||||
|
fn fh(&self) -> Vec<u64> {
|
||||||
|
self.opened_files.iter().map(|(&fh, _)| fh).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(&self) -> Box<Path> {
|
||||||
|
self.path.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open(&mut self, flags: i32) -> Result<i32> {
|
||||||
|
let path = self.path();
|
||||||
|
let file = utils::open(&path, flags)?;
|
||||||
|
let fh = file.as_raw_fd();
|
||||||
|
self.opened_files.insert(fh as u64, Box::new(file));
|
||||||
|
Ok(fh)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&mut self, fh: u64, offset: i64, size: u32) -> Result<Vec<u8>> {
|
||||||
|
let f = self.opened_files.get_mut(&fh).ok_or(Crypt4GHFSError::FileNotOpened)?;
|
||||||
|
let mut data = Vec::new();
|
||||||
|
f.seek(SeekFrom::Start(offset as u64))?;
|
||||||
|
f.as_ref().take(u64::from(size)).read_to_end(&mut data)?;
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self, fh: u64) -> Result<()> {
|
||||||
|
let f = self.opened_files.get_mut(&fh).ok_or(Crypt4GHFSError::FileNotOpened)?;
|
||||||
|
f.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, fh: u64, data: &[u8]) -> Result<usize> {
|
||||||
|
self.only_read = false;
|
||||||
|
let f = self.opened_files.get_mut(&fh).ok_or(Crypt4GHFSError::FileNotOpened)?;
|
||||||
|
|
||||||
|
// Write data
|
||||||
|
f.write_all(data)?;
|
||||||
|
Ok(data.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn truncate(&mut self, fh: Option<u64>, size: u64) -> Result<()> {
|
||||||
|
log::debug!("Truncate: size = {}", size);
|
||||||
|
self.opened_files
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|(&ffh, _)| fh.is_none() || fh == Some(ffh))
|
||||||
|
.try_for_each(|(_, f)| f.set_len(size))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&mut self, fh: u64) -> Result<()> {
|
||||||
|
let f = self.opened_files.get(&fh).ok_or(Crypt4GHFSError::FileNotOpened)?;
|
||||||
|
assert_eq!(f.as_raw_fd(), fh as i32);
|
||||||
|
self.opened_files.remove(&fh);
|
||||||
|
self.only_read = true;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rename(&mut self, new_path: &Path) {
|
||||||
|
self.path = new_path.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attrs(&self, uid: u32, gid: u32) -> Result<fuser::FileAttr> {
|
||||||
|
let stat = utils::lstat(&self.path)?;
|
||||||
|
Ok(utils::stat_to_fileatr(stat, uid, gid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegularFile {
|
||||||
|
pub fn new(file: Option<Box<File>>, path: Box<Path>) -> Self {
|
||||||
|
// Build open files
|
||||||
|
let mut opened_files = HashMap::new();
|
||||||
|
if let Some(f) = file {
|
||||||
|
opened_files.insert(f.as_raw_fd() as u64, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build RegularFile object
|
||||||
|
Self {
|
||||||
|
opened_files,
|
||||||
|
path,
|
||||||
|
only_read: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
149
__fs/crypt4ghfs-rust/src/utils.rs
Normal file
149
__fs/crypt4ghfs-rust/src/utils.rs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
use std::fs::{DirEntry, File, OpenOptions};
|
||||||
|
use std::io;
|
||||||
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
|
use crypt4gh::Keys;
|
||||||
|
use fuser::{FileAttr, FileType};
|
||||||
|
use nix::fcntl::OFlag;
|
||||||
|
use nix::sys::stat::{FileStat, Mode, SFlag};
|
||||||
|
use nix::sys::statvfs::Statvfs;
|
||||||
|
|
||||||
|
use crate::directory::Directory;
|
||||||
|
use crate::egafile::EgaFile;
|
||||||
|
use crate::encrypted_file::EncryptedFile;
|
||||||
|
use crate::error::Result;
|
||||||
|
use crate::regular_file::RegularFile;
|
||||||
|
|
||||||
|
pub fn lstat(path: &Path) -> Result<FileStat> {
|
||||||
|
let stat = nix::sys::stat::lstat(path)?;
|
||||||
|
Ok(stat)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stat_to_fileatr(stat: FileStat, uid: u32, gid: u32) -> FileAttr {
|
||||||
|
let mut perm = Mode::from_bits_truncate(stat.st_mode);
|
||||||
|
perm.set(Mode::S_IRWXG, false);
|
||||||
|
perm.set(Mode::S_IRWXO, false);
|
||||||
|
|
||||||
|
let kind = match SFlag::from_bits_truncate(stat.st_mode) & SFlag::S_IFMT {
|
||||||
|
SFlag::S_IFDIR => FileType::Directory,
|
||||||
|
SFlag::S_IFREG => FileType::RegularFile,
|
||||||
|
SFlag::S_IFLNK => FileType::Symlink,
|
||||||
|
SFlag::S_IFBLK => FileType::BlockDevice,
|
||||||
|
SFlag::S_IFCHR => FileType::CharDevice,
|
||||||
|
SFlag::S_IFIFO => FileType::NamedPipe,
|
||||||
|
SFlag::S_IFSOCK => FileType::Socket,
|
||||||
|
_ => panic!("Unknown file type"),
|
||||||
|
};
|
||||||
|
|
||||||
|
FileAttr {
|
||||||
|
ino: stat.st_ino,
|
||||||
|
size: stat.st_size as u64,
|
||||||
|
blocks: stat.st_blocks as u64,
|
||||||
|
atime: SystemTime::UNIX_EPOCH
|
||||||
|
+ Duration::from_secs(stat.st_atime as u64)
|
||||||
|
+ Duration::from_nanos(stat.st_atime_nsec as u64),
|
||||||
|
mtime: SystemTime::UNIX_EPOCH
|
||||||
|
+ Duration::from_secs(stat.st_mtime as u64)
|
||||||
|
+ Duration::from_nanos(stat.st_mtime_nsec as u64),
|
||||||
|
ctime: SystemTime::UNIX_EPOCH
|
||||||
|
+ Duration::from_secs(stat.st_ctime as u64)
|
||||||
|
+ Duration::from_nanos(stat.st_ctime_nsec as u64),
|
||||||
|
crtime: SystemTime::UNIX_EPOCH, // TODO: Is this one okay?
|
||||||
|
kind,
|
||||||
|
perm: perm.bits() as u16,
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
nlink: u32::from(stat.st_nlink),
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
nlink: stat.st_nlink as u32,
|
||||||
|
uid,
|
||||||
|
gid,
|
||||||
|
rdev: stat.st_rdev as u32,
|
||||||
|
blksize: stat.st_blksize as u32,
|
||||||
|
flags: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_type(entry: &DirEntry) -> fuser::FileType {
|
||||||
|
let kind = entry.file_type().expect("Unable to get file type");
|
||||||
|
if kind.is_file() {
|
||||||
|
fuser::FileType::RegularFile
|
||||||
|
}
|
||||||
|
else if kind.is_dir() {
|
||||||
|
fuser::FileType::Directory
|
||||||
|
}
|
||||||
|
else if kind.is_symlink() {
|
||||||
|
fuser::FileType::Symlink
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic!("Unknown file type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open(path: &Path, flags: i32) -> io::Result<File> {
|
||||||
|
let open_flags = OFlag::from_bits_truncate(flags);
|
||||||
|
OpenOptions::new()
|
||||||
|
.custom_flags(flags)
|
||||||
|
.read(open_flags.contains(OFlag::O_RDONLY) || open_flags.contains(OFlag::O_RDWR))
|
||||||
|
.write(open_flags.contains(OFlag::O_WRONLY) || open_flags.contains(OFlag::O_RDWR))
|
||||||
|
.open(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(path: &Path, flags: i32, _mode: u32) -> io::Result<File> {
|
||||||
|
let open_flags = OFlag::from_bits_truncate(flags);
|
||||||
|
OpenOptions::new()
|
||||||
|
.custom_flags(flags)
|
||||||
|
//.mode(mode)
|
||||||
|
.read(open_flags.contains(OFlag::O_RDONLY) || open_flags.contains(OFlag::O_RDWR))
|
||||||
|
.write(open_flags.contains(OFlag::O_WRONLY) || open_flags.contains(OFlag::O_RDWR))
|
||||||
|
.open(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wrap_file(path: &Path, file: File, keys: &[Keys], recipient_keys: &HashSet<Keys>) -> Box<dyn EgaFile> {
|
||||||
|
wrapper(path.into(), Some(Box::new(file)), keys, recipient_keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wrap_path(path: &Path, keys: &[Keys], recipient_keys: &HashSet<Keys>) -> Box<dyn EgaFile> {
|
||||||
|
wrapper(path.into(), None, keys, recipient_keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrapper(
|
||||||
|
path: Box<Path>,
|
||||||
|
file: Option<Box<File>>,
|
||||||
|
keys: &[Keys],
|
||||||
|
recipient_keys: &HashSet<Keys>,
|
||||||
|
) -> Box<dyn EgaFile> {
|
||||||
|
match path.extension() {
|
||||||
|
Some(ext) if ext != "c4gh" => Box::new(RegularFile::new(file, path)),
|
||||||
|
Some(_) => {
|
||||||
|
let mut inbox_path = path.to_path_buf();
|
||||||
|
let mut filename = inbox_path.file_name().unwrap().to_string_lossy().to_string();
|
||||||
|
filename = filename.strip_suffix(".c4gh").unwrap().to_string();
|
||||||
|
inbox_path.set_file_name(filename);
|
||||||
|
Box::new(EncryptedFile::new(
|
||||||
|
file,
|
||||||
|
inbox_path.into_boxed_path(),
|
||||||
|
keys,
|
||||||
|
recipient_keys,
|
||||||
|
))
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
if path.is_file() {
|
||||||
|
Box::new(EncryptedFile::new(file, path, keys, recipient_keys))
|
||||||
|
}
|
||||||
|
else if path.is_dir() {
|
||||||
|
Box::new(Directory::new(file, path))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic!("Unknown file: {:?}", path)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn statfs(path: &Path) -> Result<Statvfs> {
|
||||||
|
let statvfs = nix::sys::statvfs::statvfs(path)?;
|
||||||
|
Ok(statvfs)
|
||||||
|
}
|
||||||
19
__fs/crypt4ghfs-rust/tests/configs/fs.conf
Normal file
19
__fs/crypt4ghfs-rust/tests/configs/fs.conf
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
extensions = ["c4gh"]
|
||||||
|
|
||||||
|
[LOGGER]
|
||||||
|
use_syslog = false
|
||||||
|
log_level = "DEBUG"
|
||||||
|
log_facility = "local2"
|
||||||
|
|
||||||
|
[FUSE]
|
||||||
|
options= ["ro", "default_permissions", "allow_other", "auto_unmount"]
|
||||||
|
rootdir="tests/rootdir"
|
||||||
|
|
||||||
|
[CRYPT4GH]
|
||||||
|
recipients = ["tests/keys/bob.pub"]
|
||||||
|
include_crypt4gh_log = true
|
||||||
|
include_myself_as_recipient = true
|
||||||
|
|
||||||
|
# The decryption key
|
||||||
|
seckey = "tests/keys/bob.sec"
|
||||||
1
__fs/crypt4ghfs-rust/tests/decrypted/file.txt
Normal file
1
__fs/crypt4ghfs-rust/tests/decrypted/file.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
This is a test file. Can you decrypt it?
|
||||||
3
__fs/crypt4ghfs-rust/tests/keys/bob.pub
Normal file
3
__fs/crypt4ghfs-rust/tests/keys/bob.pub
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-----BEGIN CRYPT4GH PUBLIC KEY-----
|
||||||
|
JXGe7vpNZpWEhsxrIE/h83xst7sQ+2INpaoiGjtLIDg=
|
||||||
|
-----END CRYPT4GH PUBLIC KEY-----
|
||||||
3
__fs/crypt4ghfs-rust/tests/keys/bob.sec
Normal file
3
__fs/crypt4ghfs-rust/tests/keys/bob.sec
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||||
|
YzRnaC12MQAGYmNyeXB0ABQAAABkb1LLjyLNrcL4IgMD+NuDDQARY2hhY2hhMjBfcG9seTEzMDUAPFfaFm7bJc+pr6IRezakf5AsP7HTZnVfhSBt7XIKQcJBJY/yrPSfLxLvPMY4Edu4r0hyJTX2CNqR7wmwYg==
|
||||||
|
-----END ENCRYPTED PRIVATE KEY-----
|
||||||
3
__fs/crypt4ghfs-rust/tests/keys/testkey.pub
Normal file
3
__fs/crypt4ghfs-rust/tests/keys/testkey.pub
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-----BEGIN CRYPT4GH PUBLIC KEY-----
|
||||||
|
hf6GsKxIePbdu+hIYpyIDSPh4h15DzTsMT4eqkw3yRw=
|
||||||
|
-----END CRYPT4GH PUBLIC KEY-----
|
||||||
3
__fs/crypt4ghfs-rust/tests/keys/testkey.sec
Normal file
3
__fs/crypt4ghfs-rust/tests/keys/testkey.sec
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-----BEGIN CRYPT4GH PRIVATE KEY-----
|
||||||
|
YzRnaC12MQAEbm9uZQAEbm9uZQAgoQNTIKvd6MOvcyCgI3qvPd+t6gT2ut6PYSri74rK0T4=
|
||||||
|
-----END CRYPT4GH PRIVATE KEY-----
|
||||||
BIN
__fs/crypt4ghfs-rust/tests/rootdir/file.txt.c4gh
Normal file
BIN
__fs/crypt4ghfs-rust/tests/rootdir/file.txt.c4gh
Normal file
Binary file not shown.
3
__fs/crypt4ghfs-rust/tests/testfiles/alice.pub
Normal file
3
__fs/crypt4ghfs-rust/tests/testfiles/alice.pub
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-----BEGIN CRYPT4GH PUBLIC KEY-----
|
||||||
|
oyERnWAhzV4MAh9XIk0xD4C+nNp2tpLUiWtQoVS/xB4=
|
||||||
|
-----END CRYPT4GH PUBLIC KEY-----
|
||||||
3
__fs/crypt4ghfs-rust/tests/testfiles/alice.sec
Normal file
3
__fs/crypt4ghfs-rust/tests/testfiles/alice.sec
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||||
|
YzRnaC12MQAGYmNyeXB0ABQAAABk8Kn90WJVzJBevxN4980aWwARY2hhY2hhMjBfcG9seTEzMDUAPBdXfpV1zOcMg5EJRlGNpKZXT4PXM2iraMGCyomRQqWaH5iBGmJXU/JROPsyoX5nqmNo8oxANvgDi1hqZQ==
|
||||||
|
-----END ENCRYPTED PRIVATE KEY-----
|
||||||
Reference in New Issue
Block a user