Compare commits

...

20 Commits

Author SHA1 Message Date
YangKeao 523c67dbd5
Bump version to v0.2.4 (#44)
Signed-off-by: Yang Keao <yangkeao@chunibyo.icu>
2024-07-07 14:22:16 +08:00
Michael Morris 4bc8404382
Fix incorrect mmap args (#42)
Signed-off-by: MichaelMorris <michael.morris@est.tech>
2024-07-07 14:14:46 +08:00
YangKeao 04ba4edc20
run test one by one (#43)
Signed-off-by: Yang Keao <yangkeao@chunibyo.icu>
2024-07-07 14:06:49 +08:00
YangKeao 67b43ee417
Bump version to v0.2.3
Signed-off-by: YangKeao <yangkeao@chunibyo.icu>
2022-04-12 13:16:28 +08:00
STRRL 7eb184edd0
fix: appending nonempty when mount fuse (#31)
Signed-off-by: STRRL <str_ruiling@outlook.com>
2022-04-02 11:39:12 +08:00
YangKeao f9c784ceac
bump version to v0.2.2
Signed-off-by: YangKeao <yangkeao@chunibyo.icu>
2022-01-05 17:09:54 +08:00
YangKeao 65071a4a6e
cancel the waiting injector
Signed-off-by: YangKeao <yangkeao@chunibyo.icu>
2022-01-05 15:12:35 +08:00
YangKeao 7ea57ef5f3 fix according to cargo clippy
Signed-off-by: YangKeao <yangkeao@chunibyo.icu>
2022-01-04 17:52:54 +08:00
YangKeao ac795a08cf Bump version to v0.2.1
Signed-off-by: YangKeao <yangkeao@chunibyo.icu>
2022-01-04 17:38:44 +08:00
YangKeao 7ca342b0b0 fix cwd replacer
Signed-off-by: YangKeao <yangkeao@chunibyo.icu>
2022-01-04 17:38:23 +08:00
Yang Keao 8734addae3 Bump version to v0.2.0
Signed-off-by: Yang Keao <keao.yang@yahoo.com>
2021-04-06 13:12:19 +08:00
AsterNighT a866988506
lock aabi to 7.19 and update examples (#24)
Signed-off-by: AsterNighT <klxjt99@outlook.com>
2021-03-25 14:12:33 +08:00
AsterNighT 62043db60a
Use jrpc server to set injectors (#23)
* Make toda call jsonrpc through stderr after startup

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Setup a jrpc server over stdio and move tracings to stderr

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Fix format issue

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Add some tracing output and a test

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Update jsonrpc using derive

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Delete useless comment

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Update test to make it work like a go client

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Report error through rpc

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Upd

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Fix bug due to instant quit of main

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Sleep longer to prevent certain issue cause by rpc latency

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Fix some ugly issues

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Delete a useless ugly sleep

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Update rpc server

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Fix test

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Apply rustfmt

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Use signal only to exit

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Update test

Signed-off-by: AsterNighT <klxjt99@outlook.com>
2021-03-24 15:32:53 +08:00
Yang Keao 4a991b5fbb Bump version to 0.1.21
Signed-off-by: Yang Keao <keao.yang@yahoo.com>
2021-03-18 15:35:06 +08:00
AsterNighT fe18e46efa
Toda jsonrpc server over stdio (#21)
* Make toda call jsonrpc through stderr after startup

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Setup a jrpc server over stdio and move tracings to stderr

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Fix format issue

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Add some tracing output and a test

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Update jsonrpc using derive

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Delete useless comment

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Update test to make it work like a go client

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Report error through rpc

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Upd

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Fix bug due to instant quit of main

Signed-off-by: AsterNighT <klxjt99@outlook.com>

* Sleep longer to prevent certain issue cause by rpc latency

Signed-off-by: AsterNighT <klxjt99@outlook.com>
2021-03-18 15:34:29 +08:00
YangKeao 5aee355f82
fix performance issue && add close (#22)
Signed-off-by: Yang Keao <keao.yang@yahoo.com>
2021-03-15 13:01:24 +08:00
Yang Keao fbf1cb7215 bump version to v0.1.20
Signed-off-by: Yang Keao <keao.yang@yahoo.com>
2021-02-24 18:39:33 +08:00
AsterNighT ab6be766d3
Remove percent field in config (#20)
Signed-off-by: AsterNighT <751841735@qq.com>
2021-02-24 18:35:19 +08:00
Yang Keao c6248aa5c1 bump version to v0.1.19
Signed-off-by: Yang Keao <keao.yang@yahoo.com>
2021-02-24 17:24:25 +08:00
AsterNighT a2ec5f7947
WIP:Fix/mistake (#19)
* Modify config structure to match need of chaos-mesh

Signed-off-by: AsterNighT <751841735@qq.com>

* Fix error and fmt

Signed-off-by: AsterNighT <751841735@qq.com>

* Fix compile errors

Signed-off-by: AsterNighT <751841735@qq.com>

* Fix compile errors

Signed-off-by: AsterNighT <751841735@qq.com>
2021-02-24 17:23:22 +08:00
36 changed files with 1005 additions and 371 deletions

View File

@ -23,7 +23,7 @@ jobs:
- name: Add user_allow_other to /etc/fuse.conf
run: echo "user_allow_other" | sudo tee -a /etc/fuse.conf
- name: Run tests
run: cargo test --verbose
run: cargo test --verbose -- --test-threads=1
clippy_check:
runs-on: ubuntu-latest
steps:

473
Cargo.lock generated
View File

@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler32"
version = "1.2.0"
@ -21,7 +23,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -30,7 +32,7 @@ version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -58,7 +60,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -85,6 +87,12 @@ version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
[[package]]
name = "bytes"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cc"
version = "1.0.66"
@ -113,7 +121,7 @@ dependencies = [
"num-integer",
"num-traits",
"time",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -202,6 +210,22 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
dependencies = [
"bitflags",
"fuchsia-zircon-sys",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]]
name = "fuser"
version = "0.6.0"
@ -214,6 +238,12 @@ dependencies = [
"users",
]
[[package]]
name = "futures"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
[[package]]
name = "futures"
version = "0.3.12"
@ -254,6 +284,7 @@ dependencies = [
"futures-core",
"futures-task",
"futures-util",
"num_cpus",
]
[[package]]
@ -295,6 +326,7 @@ version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b"
dependencies = [
"futures 0.1.31",
"futures-channel",
"futures-core",
"futures-io",
@ -366,6 +398,35 @@ dependencies = [
"serde",
]
[[package]]
name = "idna"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "instant"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "iovec"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
dependencies = [
"libc",
]
[[package]]
name = "itertools"
version = "0.9.0"
@ -381,6 +442,95 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "jsonrpc-client-transports"
version = "17.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15b6c6ad01c7354d60de493148c30ac8a82b759e22ae678c8705e9b8e0c566a4"
dependencies = [
"derive_more",
"futures 0.3.12",
"jsonrpc-core",
"jsonrpc-pubsub",
"log",
"serde",
"serde_json",
"url",
]
[[package]]
name = "jsonrpc-core"
version = "17.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07569945133257ff557eb37b015497104cea61a2c9edaf126c1cbd6e8332397f"
dependencies = [
"futures 0.3.12",
"log",
"serde",
"serde_derive",
"serde_json",
]
[[package]]
name = "jsonrpc-core-client"
version = "17.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ac9d56dc729912796637c30f475bbf834594607b27740dfea6e5fa7ba40d1f1"
dependencies = [
"futures 0.3.12",
"jsonrpc-client-transports",
]
[[package]]
name = "jsonrpc-derive"
version = "17.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b68ba7e76e5c7796cfa4d2a30e83986550c34404c6d40551c902ca6f7bd4a137"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "jsonrpc-pubsub"
version = "17.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c48dbebce7a9c88ab272a4db7d6478aa4c6d9596e6c086366e89efc4e9ed89e"
dependencies = [
"futures 0.3.12",
"jsonrpc-core",
"lazy_static",
"log",
"parking_lot",
"rand",
"serde",
]
[[package]]
name = "jsonrpc-stdio-server"
version = "17.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "369ec805537bd431d6d9a2df7297c73e214da68efc90c1b8c325d63e55aec5a7"
dependencies = [
"futures 0.3.12",
"jsonrpc-core",
"log",
"tokio 0.2.24",
"tokio-util 0.3.1",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -411,6 +561,15 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3286f09f7d4926fc486334f28d8d2e6ebe4f7f9994494b6dab27ddfad2c9b11b"
[[package]]
name = "lock_api"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.13"
@ -429,6 +588,12 @@ dependencies = [
"regex-automata",
]
[[package]]
name = "matches"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
[[package]]
name = "memchr"
version = "2.3.4"
@ -444,6 +609,81 @@ dependencies = [
"libc",
]
[[package]]
name = "mio"
version = "0.6.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
dependencies = [
"cfg-if 0.1.10",
"fuchsia-zircon",
"fuchsia-zircon-sys",
"iovec",
"kernel32-sys",
"libc",
"log",
"miow 0.2.2",
"net2",
"slab",
"winapi 0.2.8",
]
[[package]]
name = "mio-named-pipes"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656"
dependencies = [
"log",
"mio",
"miow 0.3.6",
"winapi 0.3.9",
]
[[package]]
name = "mio-uds"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
dependencies = [
"iovec",
"libc",
"mio",
]
[[package]]
name = "miow"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
dependencies = [
"kernel32-sys",
"net2",
"winapi 0.2.8",
"ws2_32-sys",
]
[[package]]
name = "miow"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
dependencies = [
"socket2",
"winapi 0.3.9",
]
[[package]]
name = "net2"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
dependencies = [
"cfg-if 0.1.10",
"libc",
"winapi 0.3.9",
]
[[package]]
name = "nix"
version = "0.18.0"
@ -491,6 +731,37 @@ version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
[[package]]
name = "parking_lot"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
dependencies = [
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi 0.3.9",
]
[[package]]
name = "percent-encoding"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
[[package]]
name = "pin-project"
version = "0.4.27"
@ -541,6 +812,15 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "proc-macro-crate"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
dependencies = [
"toml",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@ -651,6 +931,15 @@ dependencies = [
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
version = "1.4.3"
@ -700,6 +989,12 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.120"
@ -740,6 +1035,15 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "signal-hook-registry"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.2"
@ -752,6 +1056,17 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]]
name = "socket2"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
dependencies = [
"cfg-if 1.0.0",
"libc",
"winapi 0.3.9",
]
[[package]]
name = "strsim"
version = "0.8.0"
@ -848,12 +1163,27 @@ checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
"winapi 0.3.9",
]
[[package]]
name = "tinyvec"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "toda"
version = "0.1.18"
version = "0.2.4"
dependencies = [
"anyhow",
"async-trait",
@ -862,10 +1192,14 @@ dependencies = [
"dynasmrt",
"env_logger",
"fuser",
"futures",
"futures 0.3.12",
"glob",
"humantime-serde",
"itertools",
"jsonrpc-core",
"jsonrpc-core-client",
"jsonrpc-derive",
"jsonrpc-stdio-server",
"libc",
"nix",
"once_cell",
@ -878,7 +1212,8 @@ dependencies = [
"structopt",
"thiserror",
"time",
"tokio",
"tokio 0.2.24",
"tokio-util 0.6.9",
"tracing",
"tracing-futures",
"tracing-subscriber",
@ -890,12 +1225,79 @@ version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48"
dependencies = [
"bytes",
"bytes 0.5.6",
"fnv",
"futures-core",
"iovec",
"lazy_static",
"libc",
"memchr",
"mio",
"mio-named-pipes",
"mio-uds",
"num_cpus",
"pin-project-lite 0.1.11",
"signal-hook-registry",
"slab",
"tokio-macros",
"winapi 0.3.9",
]
[[package]]
name = "tokio"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838"
dependencies = [
"pin-project-lite 0.2.4",
]
[[package]]
name = "tokio-macros"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-util"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499"
dependencies = [
"bytes 0.5.6",
"futures-core",
"futures-sink",
"log",
"pin-project-lite 0.1.11",
"tokio 0.2.24",
]
[[package]]
name = "tokio-util"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0"
dependencies = [
"bytes 1.1.0",
"futures-core",
"futures-sink",
"log",
"pin-project-lite 0.2.4",
"tokio 1.15.0",
]
[[package]]
name = "toml"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
dependencies = [
"serde",
]
[[package]]
@ -983,6 +1385,24 @@ dependencies = [
"tracing-serde",
]
[[package]]
name = "unicode-bidi"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
dependencies = [
"matches",
]
[[package]]
name = "unicode-normalization"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
version = "1.7.1"
@ -1001,6 +1421,17 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "url"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
dependencies = [
"idna",
"matches",
"percent-encoding",
]
[[package]]
name = "users"
version = "0.11.0"
@ -1035,6 +1466,12 @@ version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]]
name = "winapi"
version = "0.3.9"
@ -1045,6 +1482,12 @@ dependencies = [
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
@ -1057,7 +1500,7 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1065,3 +1508,13 @@ 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 = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]

View File

@ -1,6 +1,6 @@
[package]
name = "toda"
version = "0.1.18"
version = "0.2.4"
authors = ["Yang Keao <keao.yang@yahoo.com>"]
edition = "2018"
@ -10,11 +10,12 @@ edition = "2018"
structopt = "0.3"
nix = "0.18"
anyhow = "1.0"
fuser = {version = "0.6", features = ["abi-7-31"]}
fuser = {version = "0.6", features = ["abi-7-19"]}
time = "0.1"
libc = "0.2"
async-trait = "0.1"
tokio = {version = "0.2", features = ["rt-core", "rt-threaded", "sync", "fs", "time", "blocking"]}
tokio = {version = "0.2", features = ["rt-core", "rt-threaded", "sync", "fs", "time", "blocking", "macros", "full"]}
tokio-util = "0.6"
thiserror = "1.0"
futures = "0.3"
derive_more = "0.99.9"
@ -34,6 +35,10 @@ retry = "1.2.0"
tracing = "0.1"
tracing-futures = "0.2"
tracing-subscriber = "0.2"
jsonrpc-stdio-server = "17.0.0"
jsonrpc-derive = "17.0.0"
jsonrpc-core = "17.0.0"
jsonrpc-core-client = "17.0.0"
[profile.release]
debug = true
debug = true

View File

@ -12,7 +12,7 @@ ENV https_proxy $HTTPS_PROXY
RUN apt-get update && apt-get install build-essential curl git pkg-config libfuse-dev fuse -y && rm -rf /var/lib/apt/lists/*
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain nightly-2020-07-01 -y
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain nightly-2021-12-23 -y
ENV PATH "/root/.cargo/bin:${PATH}"
RUN if [ -n "$HTTP_PROXY" ]; then echo "[http]\n\

View File

@ -1,8 +1,15 @@
[
{
"type": "latency",
"path": "/var/run/test/**/*",
"percent": 100,
"latency": "10ms"
}
]
{
"jsonrpc": "2.0",
"method": "update",
"params": [
[
{
"type": "latency",
"path": "/var/lib/postgresql/data/**/*",
"percent": 100,
"latency": "10s"
}
]
],
"id": 1
}

View File

@ -1,22 +1,23 @@
[
{
"type": "mistake",
"path": "/tmp/test/test.txt",
"methods":["READ","WRITE"],
"mistakes":[
{
"jsonrpc": "2.0",
"method": "update",
"params": [
[
{
"filling":"zero",
"percent":100,
"maxOccurrences":1,
"maxLength":10
},
{
"filling": "random",
"percent": 100,
"maxOccurrences": 1,
"maxLength": 10
"type": "mistake",
"path": "/var/test/**/*",
"methods": [
"READ",
"WRITE"
],
"mistake": {
"filling": "zero",
"maxOccurrences": 1,
"maxLength": 10000
},
"percent": 100
}
],
"percent":100
}
]
]
],
"id": 1
}

View File

@ -0,0 +1 @@
[]

View File

@ -1 +1 @@
nightly-2020-07-01
nightly-2021-12-23

4
rustfmt.toml Normal file
View File

@ -0,0 +1,4 @@
reorder_imports = true
imports_granularity = "Module"
group_imports = "StdExternalCrate"
unstable_features = true

View File

@ -1,21 +1,18 @@
use std::ffi::OsString;
use std::fmt::Debug;
use std::future::Future;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use async_trait::async_trait;
use fuser::*;
use tracing::trace_span;
use tracing_futures::Instrument;
use super::errors::Result;
use super::reply::*;
use super::runtime::spawn;
use std::ffi::OsString;
use std::fmt::Debug;
use std::sync::Arc;
use std::{
future::Future,
path::{Path, PathBuf},
};
use tracing::trace_span;
pub fn spawn_reply<F, R, V>(id: u64, reply: R, f: F)
where
F: Future<Output = Result<V>> + Send + 'static,

View File

@ -1,7 +1,6 @@
use nix::errno::Errno;
use nix::Error;
use thiserror::Error;
use tracing::error;
#[derive(Error, Debug)]
@ -69,11 +68,11 @@ impl From<tokio::task::JoinError> for HookFsError {
}
}
impl Into<libc::c_int> for HookFsError {
fn into(self) -> libc::c_int {
impl From<HookFsError> for libc::c_int {
fn from(err: HookFsError) -> libc::c_int {
use HookFsError::*;
match self {
match err {
Sys(errno) => errno as i32,
InodeNotFound { inode: _ } => libc::EFAULT,
FhNotFound { fh: _ } => libc::EFAULT,

View File

@ -4,32 +4,7 @@ mod reply;
pub mod runtime;
mod utils;
use crate::injector::Injector;
use crate::injector::Method;
use crate::injector::MultiInjector;
use utils::*;
use async_trait::async_trait;
use derive_more::{Deref, DerefMut, From};
use fuser::*;
use slab::Slab;
use libc::{c_void, lgetxattr, llistxattr, lremovexattr, lsetxattr};
use nix::dir;
use nix::errno::Errno;
use nix::fcntl::{open, readlink, renameat, OFlag};
use nix::sys::stat;
use nix::sys::statfs;
use nix::unistd::{
fchownat, fsync, linkat, mkdir, symlinkat, truncate, unlink, AccessFlags, FchownatFlags, Gid,
LinkatFlags, Uid,
};
use tracing::{debug, error, instrument, trace};
use std::{collections::{HashMap, LinkedList}};
use std::collections::{HashMap, LinkedList};
use std::ffi::{CString, OsStr, OsString};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::RawFd;
@ -37,12 +12,28 @@ use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
pub use async_fs::{AsyncFileSystem, AsyncFileSystemImpl};
use async_trait::async_trait;
use derive_more::{Deref, DerefMut, From};
pub use errors::{HookFsError as Error, Result};
use fuser::*;
use libc::{c_void, lgetxattr, llistxattr, lremovexattr, lsetxattr};
use nix::dir;
use nix::errno::Errno;
use nix::fcntl::{open, readlink, renameat, OFlag};
use nix::sys::{stat, statfs};
use nix::unistd::{
close, fchownat, fsync, linkat, mkdir, symlinkat, truncate, unlink, AccessFlags, FchownatFlags,
Gid, LinkatFlags, Uid,
};
pub use reply::Reply;
use reply::*;
use runtime::spawn_blocking;
use slab::Slab;
use tokio::sync::RwLock;
use tracing::{debug, error, instrument, trace};
use utils::*;
use crate::injector::{Injector, Method, MultiInjector};
// use fuse::consts::FOPEN_DIRECT_IO;
@ -51,6 +42,8 @@ macro_rules! inject {
if $self.enable_injection.load(Ordering::SeqCst) {
$self
.injector
.read()
.await
.inject(&Method::$method, $self.rebuild_path($path)?.as_path())
.await?;
}
@ -81,19 +74,19 @@ macro_rules! inject_with_fh {
}
macro_rules! inject_write_data {
($self:ident, $fh:ident, $data:ident) => {
{
let opened_files = $self.opened_files.read().await;
if let Ok(file) = opened_files.get($fh as usize) {
let path = file.original_path().to_owned();
trace!("Write data before inject {:?}", $data);
$self
.injector
.inject_write_data($self.rebuild_path(path)?.as_path(), &mut $data)?;
trace!("Write data after inject {:?}", $data);
}
($self:ident, $fh:ident, $data:ident) => {{
let opened_files = $self.opened_files.read().await;
if let Ok(file) = opened_files.get($fh as usize) {
let path = file.original_path().to_owned();
trace!("Write data before inject {:?}", $data);
$self
.injector
.read()
.await
.inject_write_data($self.rebuild_path(path)?.as_path(), &mut $data)?;
trace!("Write data after inject {:?}", $data);
}
};
}};
}
macro_rules! inject_with_dir_fh {
@ -124,6 +117,8 @@ macro_rules! inject_attr {
if $self.enable_injection.load(Ordering::SeqCst) {
$self
.injector
.read()
.await
.inject_attr(&mut $attr, $self.rebuild_path($path)?.as_path());
}
};
@ -133,7 +128,7 @@ macro_rules! inject_reply {
($self:ident, $method:ident, $path:expr, $reply:ident, $reply_typ:ident) => {
if $self.enable_injection.load(Ordering::SeqCst) {
trace!("before inject {:?}", $reply);
$self.injector.inject_reply(
$self.injector.read().await.inject_reply(
&Method::$method,
$self.rebuild_path($path)?.as_path(),
&mut Reply::$reply_typ(&mut $reply),
@ -154,7 +149,7 @@ pub struct HookFs {
opened_dirs: RwLock<FhMap<Dir>>,
injector: MultiInjector,
pub injector: RwLock<MultiInjector>,
// map from inode to real path
inode_map: RwLock<InodeMap>,
@ -305,7 +300,7 @@ impl HookFs {
injector: MultiInjector,
) -> HookFs {
let mut inode_map = InodeMap::from(HashMap::new());
inode_map.insert_path(1, original_path.as_ref().to_owned());
inode_map.insert_path(1, original_path.as_ref());
let inode_map = RwLock::new(inode_map);
@ -314,7 +309,7 @@ impl HookFs {
original_path: original_path.as_ref().to_owned(),
opened_files: RwLock::new(FhMap::from(Slab::new())),
opened_dirs: RwLock::new(FhMap::from(Slab::new())),
injector,
injector: RwLock::new(injector),
inode_map,
enable_injection: AtomicBool::from(false),
}
@ -326,6 +321,14 @@ impl HookFs {
pub fn disable_injection(&self) {
self.enable_injection.store(false, Ordering::SeqCst);
// TODO: create a standalone runtime only for interrupt is too ugly.
// this RWLock is actually redundant, and the injector is rarely written.
let mut rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let injector = self.injector.read().await;
injector.interrupt();
});
}
pub fn rebuild_path<P: AsRef<Path>>(&self, path: P) -> Result<PathBuf> {
@ -338,7 +341,7 @@ impl HookFs {
impl HookFs {
async fn get_file_attr(&self, path: &Path) -> Result<FileAttr> {
let mut attr = async_stat(&path)
let mut attr = async_stat(path)
.await
.map(convert_libc_stat_to_fuse_stat)??;
@ -406,7 +409,7 @@ impl AsyncFileSystemImpl for HookFs {
let inode_map = self.inode_map.read().await;
let path = inode_map.get_path(ino)?;
trace!("getting attr from path {}", path.display());
let stat = self.get_file_attr(&path).await?;
let stat = self.get_file_attr(path).await?;
trace!("return with {:?}", stat);
@ -441,21 +444,21 @@ impl AsyncFileSystemImpl for HookFs {
let inode_map = self.inode_map.read().await;
let path = inode_map.get_path(ino)?;
async_lchown(&path, uid, gid).await?;
async_lchown(path, uid, gid).await?;
if let Some(mode) = mode {
async_fchmodat(&path, mode).await?;
async_fchmodat(path, mode).await?;
}
if let Some(size) = size {
async_truncate(&path, size as i64).await?;
async_truncate(path, size as i64).await?;
}
let times = [convert_time(atime), convert_time(mtime)];
let cpath = CString::new(path.as_os_str().as_bytes())?;
async_utimensat(cpath, times).await?;
let stat = self.get_file_attr(&path).await?;
let stat = self.get_file_attr(path).await?;
trace!("return with {:?}", stat);
let mut reply = Attr::new(stat);
inject_reply!(self, GETATTR, path, reply, Attr);
@ -471,7 +474,7 @@ impl AsyncFileSystemImpl for HookFs {
let inode_map = self.inode_map.read().await;
let link_path = inode_map.get_path(ino)?;
let path = async_readlink(&link_path).await?;
let path = async_readlink(link_path).await?;
let path = CString::new(path.as_os_str().as_bytes())?;
@ -732,7 +735,7 @@ impl AsyncFileSystemImpl for HookFs {
trace!("open with flags: {:?}", filtered_flags);
let fd = async_open(&path, filtered_flags, stat::Mode::S_IRWXU).await?;
let fd = async_open(path, filtered_flags, stat::Mode::S_IRWXU).await?;
let fh = self.opened_files.write().await.insert(File::new(fd, path)) as u64;
trace!("return with fh: {}, flags: {}", fh, 0);
@ -765,7 +768,7 @@ impl AsyncFileSystemImpl for HookFs {
Ok(reply)
}
#[instrument(skip(self))]
#[instrument(skip(self, data))]
async fn write(
&self,
_ino: u64,
@ -815,6 +818,9 @@ impl AsyncFileSystemImpl for HookFs {
trace!("release");
let mut opened_files = self.opened_files.write().await;
if let Ok(file) = opened_files.get(fh as usize) {
async_close(file.fd).await?;
}
opened_files.remove(fh as usize);
Ok(())
}
@ -1330,3 +1336,7 @@ async fn async_open(path: &Path, filtered_flags: OFlag, mode: stat::Mode) -> Res
let fd = spawn_blocking(move || open(&path_clone, filtered_flags, mode)).await??;
Ok(fd)
}
async fn async_close(fd: RawFd) -> Result<()> {
Ok(spawn_blocking(move || close(fd)).await??)
}

View File

@ -1,11 +1,11 @@
use fuser::*;
use std::fmt::Debug;
use std::time::Duration;
use fuser::*;
use tracing::{debug, error, trace};
use super::errors::Result;
use std::fmt::Debug;
const TTL: Duration = Duration::from_secs(0);
#[derive(Debug)]

View File

@ -1,11 +1,9 @@
use once_cell::sync::Lazy;
use tokio::runtime::Runtime;
use tokio::task::JoinHandle;
use std::future::Future;
use std::sync::RwLock;
use once_cell::sync::Lazy;
use tokio::runtime::Runtime;
use tokio::task::JoinHandle;
use tracing::trace;
pub static RUNTIME: Lazy<RwLock<Option<Runtime>>> = Lazy::new(|| {

View File

@ -1,14 +1,12 @@
use super::filter;
use super::Injector;
use super::injector_config::{AttrOverrideConfig, FileType as ConfigFileType, FilterConfig};
use crate::hookfs::Result;
use std::path::Path;
use async_trait::async_trait;
use fuser::{FileAttr, FileType};
use tracing::{debug, trace};
use std::path::Path;
use super::injector_config::{AttrOverrideConfig, FileType as ConfigFileType, FilterConfig};
use super::{filter, Injector};
use crate::hookfs::Result;
#[derive(Debug)]
pub struct AttrOverrideInjector {

View File

@ -1,15 +1,13 @@
use super::filter;
use super::Injector;
use super::injector_config::FaultsConfig;
use crate::hookfs::{Error, Result};
use std::path::Path;
use async_trait::async_trait;
use nix::errno::Errno;
use rand::Rng;
use tracing::{debug, trace};
use std::path::Path;
use super::injector_config::FaultsConfig;
use super::{filter, Injector};
use crate::hookfs::{Error, Result};
#[derive(Debug)]
pub struct FaultInjector {

View File

@ -1,15 +1,14 @@
use std::convert::TryFrom;
use std::path::Path;
use super::injector_config::FilterConfig;
use anyhow::{anyhow, Error, Result};
use bitflags::bitflags;
use glob::{MatchOptions, Pattern};
use rand::Rng;
use tracing::{info, trace};
use super::injector_config::FilterConfig;
bitflags! {
pub struct Method: u32 {
const LOOKUP = 1;
@ -110,15 +109,13 @@ impl Filter {
.unwrap_or(Method::all());
let path_filter = conf
.path
.map(|path| -> Option<Pattern> {
.path.and_then(|path| -> Option<Pattern> {
if !path.is_empty() {
Pattern::new(&path).ok()
} else {
None
}
})
.flatten();
});
Ok(Self {
path_filter,
methods,

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use std::time::Duration;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(tag = "type")]
#[serde(rename_all = "camelCase")]
@ -88,7 +88,7 @@ pub struct Timespec {
#[serde(rename_all = "camelCase")]
pub enum MistakeType {
Zero,
Random
Random,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
@ -97,13 +97,12 @@ pub struct MistakeConfig {
pub filling: MistakeType,
pub max_length: usize,
pub max_occurrences: usize,
pub percent: usize
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct MistakesConfig {
pub mistakes: Vec<MistakeConfig>,
pub mistake: MistakeConfig,
#[serde(flatten)]
pub filter: FilterConfig,
}

View File

@ -1,20 +1,21 @@
use async_trait::async_trait;
use std::path::Path;
use std::time::Duration;
use super::filter;
use super::injector_config::LatencyConfig;
use super::Injector;
use crate::hookfs::Result;
use async_trait::async_trait;
use tokio::time::delay_for;
use tokio::select;
use tokio_util::sync::CancellationToken;
use tracing::{debug, trace};
use super::injector_config::LatencyConfig;
use super::{filter, Injector};
use crate::hookfs::Result;
#[derive(Debug)]
pub struct LatencyInjector {
latency: Duration,
filter: filter::Filter,
cancel_token: CancellationToken,
}
#[async_trait]
@ -22,13 +23,27 @@ impl Injector for LatencyInjector {
async fn inject(&self, method: &filter::Method, path: &Path) -> Result<()> {
trace!("test for filter");
if self.filter.filter(method, path) {
debug!("inject io delay {:?}", self.latency);
delay_for(self.latency).await;
let token = self.cancel_token.clone();
let latency = self.latency;
debug!("inject io delay {:?}", latency);
select! {
_ = delay_for(latency) => {}
_ = token.cancelled() => {
debug!("cancelled");
}
}
debug!("latency finished");
}
Ok(())
}
fn interrupt(&self) {
debug!("interrupt latency");
self.cancel_token.cancel();
}
}
impl LatencyInjector {
@ -38,6 +53,7 @@ impl LatencyInjector {
Ok(Self {
latency: conf.latency,
filter: filter::Filter::build(conf.filter)?,
cancel_token: CancellationToken::new(),
})
}
}

View File

@ -1,21 +1,17 @@
use std::cmp::{max, min};
use std::path::Path;
use async_trait::async_trait;
use rand::Rng;
use std::{cmp::{max, min}, path::Path};
use super::filter;
use super::injector_config::MistakeConfig;
use super::injector_config::MistakeType;
use super::injector_config::MistakesConfig;
use super::Injector;
use crate::hookfs::Reply;
use crate::hookfs::Result;
use tracing::{debug, trace};
use super::injector_config::{MistakeConfig, MistakeType, MistakesConfig};
use super::{filter, Injector};
use crate::hookfs::{Reply, Result};
#[derive(Debug)]
pub struct MistakeInjector {
mistakes: Vec<MistakeConfig>,
mistake: MistakeConfig,
filter: filter::Filter,
}
@ -31,17 +27,16 @@ impl Injector for MistakeInjector {
debug!("MI:Injecting reply");
if let Reply::Data(data) = reply {
let data = &mut data.data;
self.handle(data);
self.handle(data)?;
}
}
Ok(())
}
fn inject_write_data(&self, path: &Path, data: &mut Vec<u8>) -> Result<()> {
debug!("MI:Injecting write data???");
if self.filter.filter(&super::Method::WRITE, path) {
debug!("MI:Injecting write data");
self.handle(data);
self.handle(data)?;
}
Ok(())
}
@ -51,38 +46,40 @@ impl MistakeInjector {
pub fn build(conf: MistakesConfig) -> anyhow::Result<Self> {
trace!("build mistake injector");
Ok(Self {
mistakes: conf.mistakes,
mistake: conf.mistake,
filter: filter::Filter::build(conf.filter)?,
})
}
pub fn handle(&self, data: &mut Vec<u8>) {
pub fn handle(&self, data: &mut Vec<u8>) -> Result<()> {
trace!("sabotage data");
let mut rng = rand::thread_rng();
let data_length = data.len();
for mistake in self.mistakes.iter() {
if rng.gen_range(0, 100) >= mistake.percent {
continue;
}
let occurrence = match mistake.max_occurrences {
let mistake = &self.mistake;
let occurrence = match mistake.max_occurrences {
0 => 0,
mo => rng.gen_range(1, mo + 1),
};
for _ in 0..occurrence {
let pos = rng.gen_range(0, max(data_length, 1));
let length = match min(mistake.max_length, data_length - pos) {
0 => 0,
mo => rng.gen_range(1, mo + 1),
l => rng.gen_range(1, l + 1),
};
for _ in 0..occurrence {
let pos = rng.gen_range(0, max(data_length,1));
let length = match min(mistake.max_length, data_length-pos) {
0 => 0,
l => rng.gen_range(1, l + 1),
};
debug!("Setting index [{},{}) to {:?}",pos,pos+length,mistake.filling);
match mistake.filling {
MistakeType::Zero => {
for i in pos..pos + length {
data[i] = 0;
}
debug!(
"Setting index [{},{}) to {:?}",
pos,
pos + length,
mistake.filling
);
match mistake.filling {
MistakeType::Zero => {
for item in data.iter_mut().skip(pos).take(length) {
*item = 0;
}
MistakeType::Random => rng.fill(&mut data[pos..pos + length]),
}
MistakeType::Random => rng.fill(&mut data[pos..pos + length]),
}
}
Ok(())
}
}

View File

@ -6,15 +6,15 @@ mod latency_injector;
mod mistake_injector;
mod multi_injector;
use std::path::Path;
use async_trait::async_trait;
pub use filter::Method;
use fuser::FileAttr;
pub use injector_config::InjectorConfig;
pub use multi_injector::MultiInjector;
use crate::hookfs::{Reply, Result};
use async_trait::async_trait;
use fuser::FileAttr;
use std::{path::Path};
#[async_trait]
pub trait Injector: Send + Sync + std::fmt::Debug {
@ -28,13 +28,11 @@ pub trait Injector: Send + Sync + std::fmt::Debug {
) -> Result<()> {
Ok(())
}
fn inject_write_data(
&self,
_path: &Path,
_data: &mut Vec<u8>,
) -> Result<()> {
fn inject_write_data(&self, _path: &Path, _data: &mut Vec<u8>) -> Result<()> {
Ok(())
}
fn inject_attr(&self, _attr: &mut FileAttr, _path: &Path) {}
fn interrupt(&self) {}
}

View File

@ -1,17 +1,16 @@
use super::attr_override_injector::AttrOverrideInjector;
use super::fault_injector::FaultInjector;
use super::filter;
use super::injector_config::InjectorConfig;
use super::latency_injector::LatencyInjector;
use super::mistake_injector::MistakeInjector;
use super::Injector;
use crate::hookfs::{Reply, Result};
use std::path::Path;
use async_trait::async_trait;
use fuser::FileAttr;
use tracing::trace;
use std::path::Path;
use super::attr_override_injector::AttrOverrideInjector;
use super::fault_injector::FaultInjector;
use super::injector_config::InjectorConfig;
use super::latency_injector::LatencyInjector;
use super::mistake_injector::MistakeInjector;
use super::{filter, Injector};
use crate::hookfs::{Reply, Result};
#[derive(Debug)]
pub struct MultiInjector {
@ -69,14 +68,16 @@ impl Injector for MultiInjector {
}
}
fn inject_write_data(
&self,
path: &Path,
data: &mut Vec<u8>,
) -> Result<()> {
fn inject_write_data(&self, path: &Path, data: &mut Vec<u8>) -> Result<()> {
for injector in self.injectors.iter() {
injector.inject_write_data(path, data)?;
}
Ok(())
}
fn interrupt(&self) {
for injector in self.injectors.iter() {
injector.interrupt();
}
}
}

95
src/jsonrpc.rs Normal file
View File

@ -0,0 +1,95 @@
use std::sync::{mpsc, Arc, Mutex};
use jsonrpc_derive::rpc;
use jsonrpc_stdio_server::jsonrpc_core::*;
use jsonrpc_stdio_server::ServerBuilder;
use tracing::{info, trace};
use crate::hookfs::HookFs;
use crate::injector::{InjectorConfig, MultiInjector};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Comm {
Shutdown = 0,
}
pub async fn start_server(config: RpcImpl) {
info!("Starting jsonrpc server");
let server = new_server(config);
let server = server.build();
server.await;
}
pub fn new_server(config: RpcImpl) -> ServerBuilder {
info!("Creating jsonrpc server");
let io = new_handler(config);
ServerBuilder::new(io)
}
pub fn new_handler(config: RpcImpl) -> IoHandler {
info!("Creating jsonrpc handler");
let mut io = IoHandler::new();
io.extend_with(config.to_delegate());
io
}
#[rpc]
pub trait Rpc {
#[rpc(name = "get_status")]
fn get_status(&self, inst: String) -> Result<String>;
#[rpc(name = "update")]
fn update(&self, config: Vec<InjectorConfig>) -> Result<String>;
}
pub struct RpcImpl {
status: Mutex<anyhow::Result<()>>,
tx: Mutex<mpsc::Sender<Comm>>,
hookfs: Option<Arc<HookFs>>,
}
impl RpcImpl {
pub fn new(
status: Mutex<anyhow::Result<()>>,
tx: Mutex<mpsc::Sender<Comm>>,
hookfs: Option<Arc<HookFs>>,
) -> Self {
Self { status, tx, hookfs }
}
}
impl Drop for RpcImpl {
fn drop(&mut self) {
trace!("Dropping jrpc handler");
}
}
impl Rpc for RpcImpl {
fn get_status(&self, _inst: String) -> Result<String> {
info!("rpc get_status called");
match &*self.status.lock().unwrap() {
Ok(_) => Ok("ok".to_string()),
Err(e) => {
let tx = &self.tx.lock().unwrap();
tx.send(Comm::Shutdown)
.expect("Send through channel failed");
Ok(e.to_string())
}
}
}
fn update(&self, config: Vec<InjectorConfig>) -> Result<String> {
info!("rpc update called");
if let Err(e) = &*self.status.lock().unwrap() {
return Ok(e.to_string());
}
let injectors = MultiInjector::build(config);
if let Err(e) = &injectors {
return Ok(e.to_string());
}
futures::executor::block_on(async {
let hookfs = self.hookfs.as_ref().unwrap();
let mut current_injectors = hookfs.injector.write().await;
*current_injectors = injectors.unwrap();
});
Ok("ok".to_string())
}
}

View File

@ -19,6 +19,13 @@
#![allow(clippy::or_fun_call)]
#![allow(clippy::too_many_arguments)]
pub mod injector;
pub mod fuse_device;
pub mod hookfs;
pub mod injector;
pub mod jsonrpc;
pub mod mount;
pub mod mount_injector;
pub mod ptrace;
pub mod replacer;
pub mod stop;
pub mod utils;

View File

@ -24,6 +24,7 @@ extern crate derive_more;
mod fuse_device;
mod hookfs;
mod injector;
mod jsonrpc;
mod mount;
mod mount_injector;
mod ptrace;
@ -31,21 +32,24 @@ mod replacer;
mod stop;
mod utils;
use injector::InjectorConfig;
use mount_injector::{MountInjectionGuard, MountInjector};
use replacer::{Replacer, UnionReplacer};
use utils::encode_path;
use std::convert::TryFrom;
use std::os::unix::io::RawFd;
use std::path::PathBuf;
use std::sync::{mpsc, Mutex};
use std::{io, thread};
use anyhow::Result;
use injector::InjectorConfig;
use jsonrpc::start_server;
use mount_injector::{MountInjectionGuard, MountInjector};
use nix::sys::signal::{signal, SigHandler, Signal};
use nix::unistd::{pipe, read, write};
use replacer::{Replacer, UnionReplacer};
use structopt::StructOpt;
use tracing::{info, instrument, trace};
use tokio::runtime::Runtime;
use tracing::{info, instrument};
use tracing_subscriber::EnvFilter;
use std::path::PathBuf;
use std::{convert::TryFrom, os::unix::io::RawFd};
use utils::encode_path;
#[derive(StructOpt, Debug, Clone)]
#[structopt(name = "basic")]
@ -61,9 +65,7 @@ struct Options {
}
#[instrument(skip(option))]
fn inject(option: Options) -> Result<MountInjectionGuard> {
trace!("parse injector configs");
let injector_config: Vec<InjectorConfig> = serde_json::from_reader(std::io::stdin())?;
fn inject(option: Options, injector_config: Vec<InjectorConfig>) -> Result<MountInjectionGuard> {
info!("inject with config {:?}", injector_config);
let path = option.path.clone();
@ -72,7 +74,7 @@ fn inject(option: Options) -> Result<MountInjectionGuard> {
let path = path.canonicalize()?;
let replacer = if !option.mount_only {
let mut replacer = UnionReplacer::new();
let mut replacer = UnionReplacer::default();
replacer.prepare(&path, &path)?;
Some(replacer)
@ -114,7 +116,7 @@ fn resume(option: Options, mount_guard: MountInjectionGuard) -> Result<()> {
let (_, new_path) = encode_path(&path)?;
let replacer = if !option.mount_only {
let mut replacer = UnionReplacer::new();
let mut replacer = UnionReplacer::default();
replacer.prepare(&path, &new_path)?;
info!("running replacer");
let result = replacer.run();
@ -145,6 +147,12 @@ extern "C" fn signal_handler(_: libc::c_int) {
}
}
fn wait_for_signal(chan: RawFd) -> Result<()> {
let mut buf = vec![0u8; 6];
read(chan, buf.as_mut_slice())?;
Ok(())
}
fn main() -> Result<()> {
let (reader, writer) = pipe()?;
unsafe {
@ -155,21 +163,43 @@ fn main() -> Result<()> {
unsafe { signal(Signal::SIGTERM, SigHandler::Handler(signal_handler))? };
let option = Options::from_args();
info!("start with option: {:?}", option);
let env_filter = EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_from(&option.verbose))
.or_else(|_| EnvFilter::try_new("trace"))
.unwrap();
tracing_subscriber::fmt().with_env_filter(env_filter).init();
tracing_subscriber::fmt()
.with_writer(io::stderr)
.with_env_filter(env_filter)
.init();
info!("start with option: {:?}", option);
let mount_injector = inject(option.clone(), vec![]);
let mount_injector = inject(option.clone())?;
let status = match &mount_injector {
Ok(_) => Ok(()),
Err(e) => Err(anyhow::Error::msg(e.to_string())),
};
let (tx, _) = mpsc::channel();
{
let hookfs = match &mount_injector {
Ok(e) => Some(e.hookfs.clone()),
Err(_) => None,
};
thread::spawn(|| {
Runtime::new()
.expect("Failed to create Tokio runtime")
.block_on(start_server(jsonrpc::RpcImpl::new(
Mutex::new(status),
Mutex::new(tx),
hookfs,
)));
});
}
info!("waiting for signal to exit");
let mut buf = vec![0u8; 6];
read(reader, buf.as_mut_slice())?;
wait_for_signal(reader)?;
info!("start to recover and exit");
resume(option, mount_injector)?;
if let Ok(v) = mount_injector {
resume(option, v)?;
}
Ok(())
}

View File

@ -2,9 +2,7 @@ use std::fs::create_dir_all;
use std::path::Path;
use anyhow::{Context, Result};
use nix::mount::{mount, MsFlags};
use procfs::process::{self, Process};
#[derive(Debug, Clone)]

View File

@ -1,21 +1,16 @@
use crate::hookfs;
use crate::injector::MultiInjector;
use crate::mount;
use crate::stop;
use crate::InjectorConfig;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::thread::JoinHandle;
use anyhow::{anyhow, Result};
use nix::mount::umount;
use retry::delay::Fixed;
use retry::{retry, OperationResult};
use tracing::info;
use retry::{delay::Fixed, retry, OperationResult};
use crate::injector::{InjectorConfig, MultiInjector};
use crate::{hookfs, mount, stop};
#[derive(Debug)]
pub struct MountInjector {
@ -27,7 +22,7 @@ pub struct MountInjector {
pub struct MountInjectionGuard {
original_path: PathBuf,
new_path: PathBuf,
hookfs: Arc<hookfs::HookFs>,
pub hookfs: Arc<hookfs::HookFs>,
handler: Option<JoinHandle<Result<()>>>,
}
@ -135,7 +130,7 @@ impl MountInjector {
std::fs::create_dir_all(new_path.as_path())?;
let args = ["allow_other", "fsname=toda", "default_permissions"];
let args = ["allow_other", "fsname=toda", "default_permissions", "nonempty"];
let flags: Vec<_> = args
.iter()
.flat_map(|item| vec![OsStr::new("-o"), OsStr::new(item)])

View File

@ -1,26 +1,25 @@
use Error::Internal;
use anyhow::{anyhow, Result};
use nix::sys::ptrace;
use nix::sys::signal::Signal;
use nix::sys::uio::{process_vm_writev, IoVec, RemoteIoVec};
use nix::sys::wait;
use nix::unistd::Pid;
use nix::{
errno::Errno,
sys::mman::{MapFlags, ProtFlags},
Error::Sys,
};
use procfs::{ProcError, process::Task};
use retry::{Error::{self, Operation}, OperationResult, delay::Fixed};
use tracing::{error, info, instrument, trace, warn};
use std::{cell::RefCell, collections::HashSet};
use std::collections::HashMap;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::ffi::CString;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use anyhow::{anyhow, Result};
use nix::errno::Errno;
use nix::sys::mman::{MapFlags, ProtFlags};
use nix::sys::signal::Signal;
use nix::sys::uio::{process_vm_writev, IoVec, RemoteIoVec};
use nix::sys::{ptrace, wait};
use nix::unistd::Pid;
use nix::Error::Sys;
use procfs::process::Task;
use procfs::ProcError;
use retry::delay::Fixed;
use retry::Error::{self, Operation};
use retry::OperationResult;
use tracing::{error, info, instrument, trace, warn};
use Error::Internal;
// There should be only one PtraceManager in one thread. But as we don't implement TLS
// , we cannot use thread-local variables safely.
#[derive(Debug, Default)]
@ -56,8 +55,8 @@ fn attach_task(task: &Task) -> Result<()> {
}
Err(err) => {
warn!("attach error: {:?}", err);
return Err(err.into())
},
return Err(err.into());
}
_ => {}
}
info!("attach task: {} successfully", task.tid);
@ -91,17 +90,15 @@ impl PtraceManager {
while iterations > 0 {
let mut new_threads_found = false;
let process = procfs::process::Process::new(raw_pid)?;
for task in process.tasks()? {
if let Ok(task) = task {
if traced_tasks.contains(&task.tid) {
continue
}
if let Ok(()) = attach_task(&task) {
trace!("newly traced task: {}", task.tid);
new_threads_found = true;
traced_tasks.insert(task.tid);
}
for task in process.tasks()?.flatten() {
if traced_tasks.contains(&task.tid) {
continue;
}
if let Ok(()) = attach_task(&task) {
trace!("newly traced task: {}", task.tid);
new_threads_found = true;
traced_tasks.insert(task.tid);
}
}
@ -129,8 +126,9 @@ impl PtraceManager {
counter_ref.remove(&pid);
info!("detach process: {}", pid);
if let Err(err) = retry::retry::<_, _, _, anyhow::Error, _>(Fixed::from_millis(500).take(20), || {
match procfs::process::Process::new(pid) {
if let Err(err) = retry::retry::<_, _, _, anyhow::Error, _>(
Fixed::from_millis(500).take(20),
|| match procfs::process::Process::new(pid) {
Err(ProcError::NotFound(_)) => {
info!("process {} not found", pid);
OperationResult::Ok(())
@ -139,45 +137,38 @@ impl PtraceManager {
warn!("fail to detach task: {}, retry", pid);
OperationResult::Retry(err.into())
}
Ok(process) => {
match process.tasks() {
Err(err) => {
OperationResult::Retry(err.into())
}
Ok(tasks) => {
for task in tasks {
if let Ok(task) = task {
match ptrace::detach(Pid::from_raw(task.tid), None) {
Ok(()) => {
info!("successfully detached task: {}", task.tid);
}
Err(Sys(Errno::ESRCH)) => trace!(
"task {} doesn't exist, maybe has stopped or not traced",
task.tid
),
Err(err) => {
warn!("fail to detach: {:?}", err)
},
Ok(process) => match process.tasks() {
Err(err) => OperationResult::Retry(err.into()),
Ok(tasks) => {
for task in tasks.flatten() {
match ptrace::detach(Pid::from_raw(task.tid), None) {
Ok(()) => {
info!("successfully detached task: {}", task.tid);
}
trace!("detach task: {} successfully", task.tid);
Err(Sys(Errno::ESRCH)) => trace!(
"task {} doesn't exist, maybe has stopped or not traced",
task.tid
),
Err(err) => {
warn!("fail to detach: {:?}", err)
},
}
}
info!("detach process: {} successfully", pid);
OperationResult::Ok(())
trace!("detach task: {} successfully", task.tid);
}
info!("detach process: {} successfully", pid);
OperationResult::Ok(())
}
}
}
}) {
},
},
) {
warn!("fail to detach: {:?}", err);
match err {
Operation {error: e, total_delay:_, tries:_, } => {
return Err(e)
},
Internal(err) => {
error!("internal error: {:?}", err)
}
Operation {
error: e,
total_delay: _,
tries: _,
} => return Err(e),
Internal(err) => error!("internal error: {:?}", err),
}
};
}

View File

@ -1,18 +1,15 @@
use super::ptrace;
use super::utils::all_processes;
use super::Replacer;
use std::fmt::Debug;
use std::path::{Path, PathBuf};
use anyhow::Result;
use tracing::{error, info, trace};
use super::utils::all_processes;
use super::{ptrace, Replacer};
#[derive(Debug)]
pub struct CwdReplacer {
processes: Vec<ptrace::TracedProcess>,
new_path: PathBuf,
processes: Vec<(ptrace::TracedProcess, PathBuf)>,
}
impl CwdReplacer {
@ -36,8 +33,13 @@ impl CwdReplacer {
}
})
.filter(|(_, path)| path.starts_with(detect_path.as_ref()))
.filter_map(|(pid, _)| match ptrace::trace(pid) {
Ok(process) => Some(process),
.filter_map(|(pid, path)| match ptrace::trace(pid) {
Ok(process) => {
let mut new_path = new_path.as_ref().to_path_buf();
new_path.push(path.strip_prefix(detect_path.as_ref()).unwrap());
Some((process, new_path))
}
Err(err) => {
error!("fail to ptrace process: pid({}) with error: {:?}", pid, err);
None
@ -45,18 +47,16 @@ impl CwdReplacer {
})
.collect();
Ok(CwdReplacer {
processes,
new_path: new_path.as_ref().to_owned(),
})
Ok(CwdReplacer { processes })
}
}
impl Replacer for CwdReplacer {
fn run(&mut self) -> Result<()> {
info!("running cwd replacer");
for process in self.processes.iter() {
process.chdir(&self.new_path)?;
for (process, new_path) in self.processes.iter() {
trace!("replacing cwd: {} to {:?}", process.pid, new_path);
process.chdir(new_path)?;
}
Ok(())

View File

@ -1,21 +1,17 @@
use super::ptrace;
use super::utils::all_processes;
use super::Replacer;
use std::collections::HashMap;
use std::fmt::Debug;
use std::io::{Cursor, Read, Write};
use std::iter::FromIterator;
use std::path::{Path, PathBuf};
use std::{collections::HashMap, fmt::Debug};
use anyhow::{anyhow, Result};
use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
use itertools::Itertools;
use procfs::process::FDTarget;
use tracing::{error, info, trace};
use procfs::process::FDTarget;
use itertools::Itertools;
use super::utils::all_processes;
use super::{ptrace, Replacer};
#[derive(Clone, Copy)]
#[repr(packed)]

View File

@ -1,24 +1,18 @@
use super::ptrace;
use super::utils::all_processes;
use super::Replacer;
use std::collections::HashMap;
use std::fmt::Debug;
use std::io::{Cursor, Read, Write};
use std::iter::FromIterator;
use std::path::{Path, PathBuf};
use anyhow::{anyhow, Result};
use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
use itertools::Itertools;
use nix::sys::mman::{MapFlags, ProtFlags};
use procfs::process::MMapPath;
use tracing::{error, info, trace};
use procfs::process::MMapPath;
use anyhow::{anyhow, Result};
use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
use itertools::Itertools;
use nix::sys::mman::{MapFlags, ProtFlags};
use super::utils::all_processes;
use super::{ptrace, Replacer};
#[derive(Clone, Debug)]
struct ReplaceCase {
@ -194,6 +188,7 @@ impl ProcessAccessor {
; mov rdi, QWORD [r14+r15] // addr
; mov rsi, QWORD [r14+r15+8] // length
; mov rdx, 0x0
; push rdi
; syscall
// open
; mov rax, 0x2
@ -206,6 +201,7 @@ impl ProcessAccessor {
; mov rsi, libc::O_RDWR
; mov rdx, 0x0
; syscall
; pop rdi // addr
; push rax
; mov r8, rax // fd
// mmap

View File

@ -1,9 +1,9 @@
use crate::ptrace;
use std::path::Path;
use anyhow::Result;
use crate::ptrace;
mod cwd_replacer;
mod fd_replacer;
mod mmap_replacer;
@ -15,17 +15,12 @@ pub trait Replacer {
fn run(&mut self) -> Result<()>;
}
#[derive(Default)]
pub struct UnionReplacer<'a> {
replacers: Vec<Box<dyn Replacer + 'a>>,
}
impl<'a> UnionReplacer<'a> {
pub fn new() -> UnionReplacer<'a> {
UnionReplacer {
replacers: Vec::new(),
}
}
pub fn prepare<P1: AsRef<Path>, P2: AsRef<Path>>(
&mut self,
detect_path: P1,

View File

@ -1,5 +1,4 @@
use anyhow::Result;
use procfs::process::{self, Process};
pub fn all_processes() -> Result<impl Iterator<Item = Process>> {

View File

@ -1,5 +1,4 @@
use std::sync::Arc;
use std::sync::{Condvar, Mutex};
use std::sync::{Arc, Condvar, Mutex};
struct Stop {
inner: Mutex<bool>,

57
tests/jsonrpc_test.rs Normal file
View File

@ -0,0 +1,57 @@
use std::sync::mpsc::channel;
use std::sync::Mutex;
use anyhow::anyhow;
use toda::jsonrpc::{self, new_handler, Comm};
#[test]
fn test_status_good() {
let (tx, _rx) = channel();
let io = new_handler(jsonrpc::RpcImpl::new(
Mutex::new(Ok(())),
Mutex::new(tx),
None,
));
let request = r#"{"jsonrpc": "2.0","method":"get_status","params":[""],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":"ok","id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_string()));
}
#[test]
fn test_status_bad() {
let (tx, rx) = channel();
let io = new_handler(jsonrpc::RpcImpl::new(
Mutex::new(Err(anyhow!("Not good"))),
Mutex::new(tx),
None,
));
let request = r#"{"jsonrpc": "2.0","method":"get_status","params":[""],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":"Not good","id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_string()));
assert_eq!(rx.recv().unwrap(), Comm::Shutdown);
}
#[test]
fn test_should_not_update_config_if_status_is_failed() {
let (tx, _rx) = channel();
let request = r#"{"jsonrpc": "2.0","method":"update","params":[[]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":"Not good","id":1}"#;
let io = new_handler(jsonrpc::RpcImpl::new(
Mutex::new(Err(anyhow!("Not good"))),
Mutex::new(tx),
None,
));
assert_eq!(io.handle_request_sync(request), Some(response.to_string()));
}
#[test]
fn test_should_fail_if_config_is_bad() {
let (tx, _rx) = channel();
let request = r#"{"jsonrpc": "2.0","method":"update","params":[["blah"]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params: invalid type: string \"blah\", expected internally tagged enum."},"id":1}"#;
let io = new_handler(jsonrpc::RpcImpl::new(
Mutex::new(Ok(())),
Mutex::new(tx),
None,
));
assert_eq!(io.handle_request_sync(request), Some(response.to_string()));
}

View File

@ -11,20 +11,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use toda::hookfs;
use toda::injector::MultiInjector;
use std::ffi::OsStr;
use std::fs::{read_link, read_to_string, write, File, OpenOptions};
use std::io::{Read, Write};
use std::os::unix::fs::symlink;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::Once;
use std::sync::{Arc, Once};
use nix::fcntl;
use nix::sys::stat;
use nix::unistd;
use nix::{fcntl, unistd};
use toda::hookfs;
use toda::injector::MultiInjector;
// These tests are port from go-fuse test