Initial import
This commit is contained in:
commit
30de09b7d2
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
499
Cargo.lock
generated
Normal file
499
Cargo.lock
generated
Normal file
@ -0,0 +1,499 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg_aliases"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.5.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "corruptfs-fuse"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"env_logger",
|
||||||
|
"fuser",
|
||||||
|
"log",
|
||||||
|
"nix",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_filter"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.11.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"env_filter",
|
||||||
|
"humantime",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuser"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e697f6f62c20b6fad1ba0f84ae909f25971cf16e735273524e3977c94604cf8"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"memchr",
|
||||||
|
"page_size",
|
||||||
|
"pkg-config",
|
||||||
|
"smallvec",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.161"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"cfg_aliases",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "page_size"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.82"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[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-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "corruptfs-fuse"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.5.20", features = ["derive"] }
|
||||||
|
env_logger = "0.11.5"
|
||||||
|
fuser = { version = "0.14.0", features = ["abi-7-30"] }
|
||||||
|
log = { version = "0.4.22", features = ["std"] }
|
||||||
|
nix = { version = "0.29.0", features = ["user"] }
|
||||||
|
rand = "0.8.5"
|
30
README.md
Normal file
30
README.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
This is a FUSE filesystem that always returns random corrupted data when
|
||||||
|
you read back files you write.
|
||||||
|
|
||||||
|
The filesystem will tell the kernel to use its filesystem buffer cache,
|
||||||
|
so you won't see the corruption unless you read with `O_DIRECT`, or if
|
||||||
|
you tell the kernel to drop its caches.
|
||||||
|
|
||||||
|
Run with:
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir ~/corrupt-fs
|
||||||
|
cargo run -- --allow-direct-io ~/corrupt-fs
|
||||||
|
```
|
||||||
|
|
||||||
|
If you don't specify `--allow-direct-io`, direct I/O will not be
|
||||||
|
supported.
|
||||||
|
|
||||||
|
To test from the cmdline:
|
||||||
|
|
||||||
|
```
|
||||||
|
echo "hello world" > ~/corrupt-fs/hello.txt
|
||||||
|
cat ~/corrupt-fs/hello.txt # returns good data from fscache
|
||||||
|
dd if=~/corrupt-fs/hello.txt iflag=direct # returns bad data
|
||||||
|
```
|
||||||
|
|
||||||
|
Limitations:
|
||||||
|
|
||||||
|
* Subdirectories are not supported.
|
||||||
|
* `lseek()` modes other than `SEEK_SET` are not supported.
|
||||||
|
* There are probably bugs, especially with large files.
|
683
src/fs.rs
Normal file
683
src/fs.rs
Normal file
@ -0,0 +1,683 @@
|
|||||||
|
use std::{
|
||||||
|
cmp::min, collections::HashMap, ffi::{OsStr, OsString}, iter::repeat, time::{Duration, SystemTime}
|
||||||
|
};
|
||||||
|
|
||||||
|
use fuser::{consts::{FOPEN_DIRECT_IO, FOPEN_KEEP_CACHE, FUSE_WRITEBACK_CACHE}, FileAttr, FileType, Filesystem, TimeOrNow};
|
||||||
|
use nix::{
|
||||||
|
libc::{
|
||||||
|
EBADF, EBUSY, EEXIST, EINVAL, EISDIR, ENOENT, ENOTDIR, EPERM, O_DIRECT, O_RDONLY, SEEK_SET
|
||||||
|
},
|
||||||
|
unistd::{getgid, getuid},
|
||||||
|
};
|
||||||
|
|
||||||
|
const TTL: Duration = Duration::from_secs(1);
|
||||||
|
|
||||||
|
const BLOCK_SIZE: u32 = 512;
|
||||||
|
const ROOT_INODE: u64 = 1;
|
||||||
|
|
||||||
|
struct OpenFile {
|
||||||
|
ino: u64,
|
||||||
|
flags: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OpenFd {
|
||||||
|
File(OpenFile),
|
||||||
|
Directory(OpenFile),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct File {
|
||||||
|
ino: u64,
|
||||||
|
kind: FileType,
|
||||||
|
data: Vec<u8>,
|
||||||
|
mode: u32,
|
||||||
|
crtime: SystemTime,
|
||||||
|
atime: SystemTime,
|
||||||
|
mtime: SystemTime,
|
||||||
|
ctime: SystemTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl File {
|
||||||
|
fn as_file_attr(&self) -> FileAttr {
|
||||||
|
let nlink = if self.ino == ROOT_INODE { 2 } else { 1 };
|
||||||
|
let attr = FileAttr {
|
||||||
|
ino: self.ino,
|
||||||
|
size: self.data.len().try_into().unwrap(),
|
||||||
|
blocks: block_count(self.data.len()),
|
||||||
|
atime: self.atime,
|
||||||
|
mtime: self.mtime,
|
||||||
|
ctime: self.ctime,
|
||||||
|
kind: self.kind,
|
||||||
|
crtime: self.crtime,
|
||||||
|
perm: self.mode as u16,
|
||||||
|
nlink,
|
||||||
|
uid: getuid().as_raw(),
|
||||||
|
gid: getgid().as_raw(),
|
||||||
|
rdev: 0,
|
||||||
|
blksize: BLOCK_SIZE,
|
||||||
|
flags: 0,
|
||||||
|
};
|
||||||
|
debug!("attr: {:?}", attr);
|
||||||
|
attr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CorruptFs {
|
||||||
|
ino_counter: u64,
|
||||||
|
fh_counter: u64,
|
||||||
|
files: HashMap<u64, File>, // inode -> File
|
||||||
|
inode_map: HashMap<OsString, u64>, // name -> inode
|
||||||
|
open_fds: HashMap<u64, OpenFd>, // fh -> OpenFd
|
||||||
|
allow_direct_io: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CorruptFs {
|
||||||
|
pub fn new(allow_direct_io: bool) -> Self {
|
||||||
|
let mut fs = Self {
|
||||||
|
ino_counter: 2,
|
||||||
|
fh_counter: 1,
|
||||||
|
files: HashMap::new(),
|
||||||
|
inode_map: HashMap::new(),
|
||||||
|
open_fds: HashMap::new(),
|
||||||
|
allow_direct_io,
|
||||||
|
};
|
||||||
|
|
||||||
|
let now = SystemTime::now();
|
||||||
|
let rootdir = File {
|
||||||
|
ino: ROOT_INODE,
|
||||||
|
kind: FileType::Directory,
|
||||||
|
data: Vec::new(),
|
||||||
|
mode: 0o755,
|
||||||
|
crtime: now,
|
||||||
|
atime: now,
|
||||||
|
mtime: now,
|
||||||
|
ctime: now,
|
||||||
|
};
|
||||||
|
fs.inode_map.insert(OsString::from("."), rootdir.ino);
|
||||||
|
fs.inode_map.insert(OsString::from(".."), rootdir.ino);
|
||||||
|
fs.files.insert(rootdir.ino, rootdir);
|
||||||
|
|
||||||
|
fs
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_inode(&mut self) -> u64 {
|
||||||
|
let ino = self.ino_counter;
|
||||||
|
self.ino_counter += 1;
|
||||||
|
ino
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_fh(&mut self) -> u64 {
|
||||||
|
let fh = self.fh_counter;
|
||||||
|
self.fh_counter += 1;
|
||||||
|
fh
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_file(&self, fh: u64) -> Option<&OpenFile> {
|
||||||
|
self.open_fds.get(&fh).and_then(|open_fd| match open_fd {
|
||||||
|
OpenFd::File(open_file) => Some(open_file),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_file_with_file_mut(&mut self, fh: u64) -> Option<(&mut File, &OpenFile)> {
|
||||||
|
self.open_fds
|
||||||
|
.get(&fh)
|
||||||
|
.and_then(|open_fd| match open_fd {
|
||||||
|
OpenFd::File(open_file) => Some(open_file),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.and_then(|open_file| {
|
||||||
|
self.files
|
||||||
|
.get_mut(&open_file.ino)
|
||||||
|
.map(|file| (file, open_file))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_open_dir(&self, fh: u64) -> bool {
|
||||||
|
self.open_fds
|
||||||
|
.get(&fh)
|
||||||
|
.map(|open_fd| match open_fd {
|
||||||
|
OpenFd::Directory(_) => true,
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Filesystem for CorruptFs {
|
||||||
|
fn init(&mut self, _req: &fuser::Request<'_>, config: &mut fuser::KernelConfig) -> Result<(), nix::libc::c_int> {
|
||||||
|
if let Err(unsup) = config.add_capabilities(FUSE_WRITEBACK_CACHE) {
|
||||||
|
warn!("Unsupported kernel caps: 0x{:08x}", unsup);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn statfs(&mut self, _req: &fuser::Request<'_>, _ino: u64, reply: fuser::ReplyStatfs) {
|
||||||
|
reply.statfs(
|
||||||
|
u64::MAX,
|
||||||
|
u64::MAX,
|
||||||
|
u64::MAX,
|
||||||
|
self.files.len() as u64,
|
||||||
|
u64::MAX,
|
||||||
|
BLOCK_SIZE,
|
||||||
|
4096,
|
||||||
|
BLOCK_SIZE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup(
|
||||||
|
&mut self,
|
||||||
|
req: &fuser::Request<'_>,
|
||||||
|
parent: u64,
|
||||||
|
name: &OsStr,
|
||||||
|
reply: fuser::ReplyEntry,
|
||||||
|
) {
|
||||||
|
if req.uid() != getuid().as_raw() {
|
||||||
|
reply.error(EPERM);
|
||||||
|
} else if parent != ROOT_INODE {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
} else if let Some(file) = self
|
||||||
|
.inode_map
|
||||||
|
.get(&name.to_os_string())
|
||||||
|
.and_then(|inode| self.files.get(inode))
|
||||||
|
{
|
||||||
|
reply.entry(&TTL, &file.as_file_attr(), 0);
|
||||||
|
} else {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getattr(&mut self, _req: &fuser::Request<'_>, ino: u64, reply: fuser::ReplyAttr) {
|
||||||
|
debug!("getattr");
|
||||||
|
if let Some(file) = self.files.get(&ino) {
|
||||||
|
reply.attr(&TTL, &file.as_file_attr());
|
||||||
|
} else {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn access(&mut self, req: &fuser::Request<'_>, ino: u64, _mask: i32, reply: fuser::ReplyEmpty) {
|
||||||
|
if req.uid() != getuid().as_raw() {
|
||||||
|
reply.error(EPERM);
|
||||||
|
} else if self.files.contains_key(&ino) {
|
||||||
|
debug!("returning OK to access request");
|
||||||
|
reply.ok();
|
||||||
|
} else {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setattr(
|
||||||
|
&mut self,
|
||||||
|
req: &fuser::Request<'_>,
|
||||||
|
ino: u64,
|
||||||
|
mode: Option<u32>,
|
||||||
|
uid: Option<u32>,
|
||||||
|
gid: Option<u32>,
|
||||||
|
size: Option<u64>,
|
||||||
|
atime: Option<fuser::TimeOrNow>,
|
||||||
|
mtime: Option<fuser::TimeOrNow>,
|
||||||
|
ctime: Option<SystemTime>,
|
||||||
|
fh: Option<u64>,
|
||||||
|
crtime: Option<SystemTime>,
|
||||||
|
_chgtime: Option<SystemTime>,
|
||||||
|
_bkuptime: Option<SystemTime>,
|
||||||
|
_flags: Option<u32>,
|
||||||
|
reply: fuser::ReplyAttr,
|
||||||
|
) {
|
||||||
|
if req.uid() != getuid().as_raw() {
|
||||||
|
reply.error(EPERM);
|
||||||
|
} else if let Some((file, _)) = fh.and_then(|fh| self.open_file_with_file_mut(fh)) {
|
||||||
|
do_setattr(file, mode, uid, gid, size, atime, mtime, ctime, crtime, reply);
|
||||||
|
} else if let Some(file) = self.files.get_mut(&ino) {
|
||||||
|
do_setattr(file, mode, uid, gid, size, atime, mtime, ctime, crtime, reply);
|
||||||
|
} else {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open(&mut self, req: &fuser::Request<'_>, ino: u64, flags: i32, reply: fuser::ReplyOpen) {
|
||||||
|
if req.uid() != getuid().as_raw() {
|
||||||
|
reply.error(EPERM);
|
||||||
|
} else if (flags & O_DIRECT) != 0 && !self.allow_direct_io {
|
||||||
|
reply.error(EINVAL);
|
||||||
|
} else if self.files.contains_key(&ino) {
|
||||||
|
let fh = self.next_fh();
|
||||||
|
let open_file = OpenFile { ino, flags };
|
||||||
|
self.open_fds.insert(fh, OpenFd::File(open_file));
|
||||||
|
|
||||||
|
let mut o_flags = FOPEN_KEEP_CACHE;
|
||||||
|
if (flags & O_DIRECT) != 0 {
|
||||||
|
o_flags |= FOPEN_DIRECT_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply.opened(fh, o_flags);
|
||||||
|
} else {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(
|
||||||
|
&mut self,
|
||||||
|
req: &fuser::Request<'_>,
|
||||||
|
_parent: u64,
|
||||||
|
name: &OsStr,
|
||||||
|
mode: u32,
|
||||||
|
_umask: u32,
|
||||||
|
flags: i32,
|
||||||
|
reply: fuser::ReplyCreate,
|
||||||
|
) {
|
||||||
|
if req.uid() != getuid().as_raw() {
|
||||||
|
reply.error(EPERM);
|
||||||
|
} else if self.inode_map.contains_key(&name.to_os_string()) {
|
||||||
|
reply.error(EEXIST);
|
||||||
|
} else if (flags & O_DIRECT) != 0 && !self.allow_direct_io {
|
||||||
|
reply.error(EINVAL);
|
||||||
|
} else {
|
||||||
|
debug!("create with mode {:o}", mode);
|
||||||
|
let now = SystemTime::now();
|
||||||
|
let file = File {
|
||||||
|
ino: self.next_inode(),
|
||||||
|
kind: FileType::RegularFile,
|
||||||
|
data: Vec::new(),
|
||||||
|
mode,
|
||||||
|
crtime: now,
|
||||||
|
atime: now,
|
||||||
|
mtime: now,
|
||||||
|
ctime: now,
|
||||||
|
};
|
||||||
|
let attr = file.as_file_attr();
|
||||||
|
self.inode_map.insert(name.to_os_string(), file.ino);
|
||||||
|
self.files.insert(file.ino, file);
|
||||||
|
|
||||||
|
let fh = self.next_fh();
|
||||||
|
let open_file = OpenFile {
|
||||||
|
ino: attr.ino,
|
||||||
|
flags,
|
||||||
|
};
|
||||||
|
self.open_fds.insert(fh, OpenFd::File(open_file));
|
||||||
|
|
||||||
|
let mut cr_flags = FOPEN_KEEP_CACHE;
|
||||||
|
if (flags & O_DIRECT) != 0 {
|
||||||
|
cr_flags |= FOPEN_DIRECT_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply.created(&TTL, &attr, 0, fh, cr_flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lseek(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
_ino: u64,
|
||||||
|
fh: u64,
|
||||||
|
offset: i64,
|
||||||
|
whence: i32,
|
||||||
|
reply: fuser::ReplyLseek,
|
||||||
|
) {
|
||||||
|
if let Some((file, _)) = self.open_file_with_file_mut(fh) {
|
||||||
|
if whence == SEEK_SET {
|
||||||
|
if (offset as usize) < file.data.len() {
|
||||||
|
reply.offset(offset);
|
||||||
|
} else {
|
||||||
|
reply.error(EINVAL);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.error(EINVAL);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.error(EBADF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
_ino: u64,
|
||||||
|
fh: u64,
|
||||||
|
offset: i64,
|
||||||
|
size: u32,
|
||||||
|
_flags: i32,
|
||||||
|
_lock_owner: Option<u64>,
|
||||||
|
reply: fuser::ReplyData,
|
||||||
|
) {
|
||||||
|
if let Some(file) = self
|
||||||
|
.open_file(fh)
|
||||||
|
.and_then(|open_file| self.files.get(&open_file.ino))
|
||||||
|
{
|
||||||
|
if offset as usize >= file.data.len() {
|
||||||
|
reply.error(EINVAL);
|
||||||
|
} else {
|
||||||
|
let bytes = min(size as usize, file.data.len() - (offset as usize)) as usize;
|
||||||
|
let data = std::iter::repeat_with(|| rand::random());
|
||||||
|
reply.data(&data.take(bytes).collect::<Vec<u8>>());
|
||||||
|
//reply.data(&file.data[(offset as usize)..bytes]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.error(EBADF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
_ino: u64,
|
||||||
|
fh: u64,
|
||||||
|
offset: i64,
|
||||||
|
data: &[u8],
|
||||||
|
_write_flags: u32,
|
||||||
|
_flags: i32,
|
||||||
|
_lock_owner: Option<u64>,
|
||||||
|
reply: fuser::ReplyWrite,
|
||||||
|
) {
|
||||||
|
if let Some((file, open_file)) = self.open_file_with_file_mut(fh) {
|
||||||
|
if (open_file.flags & O_RDONLY) != 0 {
|
||||||
|
reply.error(EBADF);
|
||||||
|
} else {
|
||||||
|
let offset = offset as usize;
|
||||||
|
if offset == file.data.len() {
|
||||||
|
file.data.extend_from_slice(data);
|
||||||
|
reply.written(data.len() as u32);
|
||||||
|
} else if offset > file.data.len() {
|
||||||
|
file.data.extend(repeat(0).take(offset - file.data.len()));
|
||||||
|
file.data.extend_from_slice(data);
|
||||||
|
reply.written(data.len() as u32);
|
||||||
|
} else {
|
||||||
|
let range = offset..min(file.data.len(), offset + data.len());
|
||||||
|
file.data.splice(range, data.iter().map(|v| *v));
|
||||||
|
reply.written(data.len() as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.error(EBADF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
_ino: u64,
|
||||||
|
fh: u64,
|
||||||
|
_lock_owner: u64,
|
||||||
|
reply: fuser::ReplyEmpty,
|
||||||
|
) {
|
||||||
|
if let Some(open_file) = self.open_file(fh) {
|
||||||
|
if (open_file.flags & O_RDONLY) != 0 {
|
||||||
|
reply.error(EBADF);
|
||||||
|
} else {
|
||||||
|
reply.ok();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.error(EBADF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn release(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
_ino: u64,
|
||||||
|
fh: u64,
|
||||||
|
_flags: i32,
|
||||||
|
_lock_owner: Option<u64>,
|
||||||
|
_flush: bool,
|
||||||
|
reply: fuser::ReplyEmpty,
|
||||||
|
) {
|
||||||
|
if let Some(open_fd) = self.open_fds.remove(&fh) {
|
||||||
|
match open_fd {
|
||||||
|
OpenFd::File(_) => reply.ok(),
|
||||||
|
OpenFd::Directory(open_dir) => {
|
||||||
|
self.open_fds.insert(fh, OpenFd::Directory(open_dir));
|
||||||
|
reply.error(EBADF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.error(EBADF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn opendir(&mut self, req: &fuser::Request<'_>, ino: u64, flags: i32, reply: fuser::ReplyOpen) {
|
||||||
|
if req.uid() != getuid().as_raw() {
|
||||||
|
debug!("opendir: bad user");
|
||||||
|
reply.error(EPERM);
|
||||||
|
} else if ino != ROOT_INODE {
|
||||||
|
debug!("opendir: bad ino");
|
||||||
|
if self.files.contains_key(&ino) {
|
||||||
|
reply.error(ENOTDIR);
|
||||||
|
} else {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug!("opendir: ok");
|
||||||
|
let fh = self.next_fh();
|
||||||
|
let open_file = OpenFile {
|
||||||
|
ino: ROOT_INODE,
|
||||||
|
flags,
|
||||||
|
};
|
||||||
|
self.open_fds.insert(fh, OpenFd::Directory(open_file));
|
||||||
|
reply.opened(fh, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readdir(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
_ino: u64,
|
||||||
|
fh: u64,
|
||||||
|
offset: i64,
|
||||||
|
mut reply: fuser::ReplyDirectory,
|
||||||
|
) {
|
||||||
|
if self.is_open_dir(fh) {
|
||||||
|
for (i, (name, inode)) in self.inode_map.iter().skip(offset as usize).enumerate() {
|
||||||
|
let file = self.files.get(inode).unwrap();
|
||||||
|
debug!("readdir: adding file {:?}", name);
|
||||||
|
if reply.add(*inode, (i + 1) as i64, file.kind, name) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reply.ok();
|
||||||
|
} else {
|
||||||
|
reply.error(EBADF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readdirplus(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
_ino: u64,
|
||||||
|
fh: u64,
|
||||||
|
offset: i64,
|
||||||
|
mut reply: fuser::ReplyDirectoryPlus,
|
||||||
|
) {
|
||||||
|
if self.is_open_dir(fh) {
|
||||||
|
for (i, (name, inode)) in self.inode_map.iter().skip(offset as usize).enumerate() {
|
||||||
|
let file = self.files.get(inode).unwrap();
|
||||||
|
debug!("readdirplus: adding file {:?}", name);
|
||||||
|
if reply.add(*inode, (i + 1) as i64, name, &TTL, &file.as_file_attr(), 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reply.ok();
|
||||||
|
} else {
|
||||||
|
reply.error(EBADF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn releasedir(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
_ino: u64,
|
||||||
|
fh: u64,
|
||||||
|
_flags: i32,
|
||||||
|
reply: fuser::ReplyEmpty,
|
||||||
|
) {
|
||||||
|
if let Some(open_fd) = self.open_fds.remove(&fh) {
|
||||||
|
match open_fd {
|
||||||
|
OpenFd::Directory(_) => reply.ok(),
|
||||||
|
OpenFd::File(open_file) => {
|
||||||
|
self.open_fds.insert(fh, OpenFd::File(open_file));
|
||||||
|
reply.error(EBADF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.error(EBADF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fsync(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
_ino: u64,
|
||||||
|
_fh: u64,
|
||||||
|
_datasync: bool,
|
||||||
|
reply: fuser::ReplyEmpty,
|
||||||
|
) {
|
||||||
|
reply.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fsyncdir(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
_ino: u64,
|
||||||
|
_fh: u64,
|
||||||
|
_datasync: bool,
|
||||||
|
reply: fuser::ReplyEmpty,
|
||||||
|
) {
|
||||||
|
reply.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unlink(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
parent: u64,
|
||||||
|
name: &OsStr,
|
||||||
|
reply: fuser::ReplyEmpty,
|
||||||
|
) {
|
||||||
|
if parent != ROOT_INODE {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
} else if let Some(file) = self
|
||||||
|
.inode_map
|
||||||
|
.get(&name.to_os_string())
|
||||||
|
.and_then(|ino| self.files.get(ino))
|
||||||
|
{
|
||||||
|
if file.ino == ROOT_INODE {
|
||||||
|
reply.error(EISDIR);
|
||||||
|
} else if file.kind == FileType::RegularFile {
|
||||||
|
let ino = file.ino;
|
||||||
|
self.inode_map.remove(&name.to_os_string());
|
||||||
|
self.files.remove(&ino);
|
||||||
|
reply.ok();
|
||||||
|
} else {
|
||||||
|
reply.error(EPERM);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rmdir(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
_parent: u64,
|
||||||
|
name: &OsStr,
|
||||||
|
reply: fuser::ReplyEmpty,
|
||||||
|
) {
|
||||||
|
if name == "." {
|
||||||
|
reply.error(EBUSY);
|
||||||
|
} else {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rename(
|
||||||
|
&mut self,
|
||||||
|
req: &fuser::Request<'_>,
|
||||||
|
parent: u64,
|
||||||
|
name: &OsStr,
|
||||||
|
newparent: u64,
|
||||||
|
newname: &OsStr,
|
||||||
|
_flags: u32,
|
||||||
|
reply: fuser::ReplyEmpty,
|
||||||
|
) {
|
||||||
|
if req.uid() != getuid().as_raw() {
|
||||||
|
reply.error(EPERM);
|
||||||
|
} else if parent != ROOT_INODE || newparent != ROOT_INODE {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
} else if let Some(file) = self
|
||||||
|
.inode_map
|
||||||
|
.get(&name.to_os_string())
|
||||||
|
.and_then(|ino| self.files.get(ino))
|
||||||
|
{
|
||||||
|
if file.ino == ROOT_INODE {
|
||||||
|
reply.error(EBUSY);
|
||||||
|
} else if file.kind == FileType::RegularFile {
|
||||||
|
self.inode_map.remove(&name.to_os_string());
|
||||||
|
self.inode_map.insert(newname.to_os_string(), file.ino);
|
||||||
|
reply.ok();
|
||||||
|
} else {
|
||||||
|
reply.error(EPERM);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_count(size: usize) -> u64 {
|
||||||
|
let rem = if size % (BLOCK_SIZE as usize) != 0 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
(size / (BLOCK_SIZE as usize) + rem).try_into().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_setattr(
|
||||||
|
file: &mut File,
|
||||||
|
mode: Option<u32>,
|
||||||
|
uid: Option<u32>,
|
||||||
|
gid: Option<u32>,
|
||||||
|
size: Option<u64>,
|
||||||
|
atime: Option<TimeOrNow>,
|
||||||
|
mtime: Option<TimeOrNow>,
|
||||||
|
ctime: Option<SystemTime>,
|
||||||
|
crtime: Option<SystemTime>,
|
||||||
|
reply: fuser::ReplyAttr,
|
||||||
|
) {
|
||||||
|
if uid.filter(|uid| *uid != getuid().as_raw()).is_some() || gid.filter(|gid| *gid != getgid().as_raw()).is_some() {
|
||||||
|
reply.error(EPERM);
|
||||||
|
} else {
|
||||||
|
if let Some(mode) = mode {
|
||||||
|
file.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(size) = size {
|
||||||
|
let size = size as usize;
|
||||||
|
if file.data.len() < size {
|
||||||
|
file.data.extend(repeat(0).take(size - file.data.len()))
|
||||||
|
} else if file.data.len() > size {
|
||||||
|
file.data.truncate(size as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(atime) = parse_timeornow(atime) {
|
||||||
|
file.atime = atime;
|
||||||
|
}
|
||||||
|
if let Some(mtime) = parse_timeornow(mtime) {
|
||||||
|
file.mtime = mtime;
|
||||||
|
}
|
||||||
|
if let Some(ctime) = ctime {
|
||||||
|
file.ctime = ctime;
|
||||||
|
}
|
||||||
|
if let Some(crtime) = crtime {
|
||||||
|
file.crtime = crtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply.attr(&TTL, &file.as_file_attr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_timeornow(t: Option<TimeOrNow>) -> Option<SystemTime> {
|
||||||
|
t.map(|t| match t {
|
||||||
|
TimeOrNow::Now => SystemTime::now(),
|
||||||
|
TimeOrNow::SpecificTime(st) => st,
|
||||||
|
})
|
||||||
|
}
|
45
src/main.rs
Normal file
45
src/main.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use env_logger::Env;
|
||||||
|
use fuser::MountOption;
|
||||||
|
|
||||||
|
mod fs;
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
/// Pass FUSE options to library
|
||||||
|
#[arg(short = 'o', value_name = "OPT")]
|
||||||
|
options: Vec<String>,
|
||||||
|
|
||||||
|
/// Allow opening files with O_DIRECT
|
||||||
|
#[arg(long = "allow-direct-io")]
|
||||||
|
allow_direct_io: bool,
|
||||||
|
|
||||||
|
/// Mount point
|
||||||
|
#[arg(value_name = "MOUNT_POINT")]
|
||||||
|
mount_point: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
env_logger::init_from_env(Env::new().filter("CORRUPTFS_LOG"));
|
||||||
|
|
||||||
|
let args = Args::parse();
|
||||||
|
debug!("extra options: {:?}", args.options);
|
||||||
|
|
||||||
|
let mut options = vec![
|
||||||
|
MountOption::FSName("corruptfs".to_string()),
|
||||||
|
MountOption::NoDev,
|
||||||
|
MountOption::NoSuid,
|
||||||
|
MountOption::RW,
|
||||||
|
];
|
||||||
|
options.extend(
|
||||||
|
args.options
|
||||||
|
.iter()
|
||||||
|
.map(|opt| MountOption::CUSTOM(opt.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
|
fuser::mount2(fs::CorruptFs::new(args.allow_direct_io), args.mount_point, &options).unwrap();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user