diff --git a/Cargo.lock b/Cargo.lock index d2e786e..1818e11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + [[package]] name = "adler" version = "1.0.2" @@ -43,6 +49,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + [[package]] name = "ambient-authority" version = "0.0.1" @@ -67,6 +79,18 @@ dependencies = [ "serde", ] +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + +[[package]] +name = "array-init" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" + [[package]] name = "async-trait" version = "0.1.68" @@ -84,7 +108,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -95,6 +119,22 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "awaitable" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70af449c9a763cb655c6a1e5338b42d99c67190824ff90658c1e30be844c0775" +dependencies = [ + "awaitable-error", + "cfg-if 1.0.0", +] + +[[package]] +name = "awaitable-error" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5b3469636cdf8543cceab175efca534471f36eee12fb8374aba00eb5e7e7f8a" + [[package]] name = "bare-metrics-core" version = "0.1.0" @@ -136,6 +176,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" + [[package]] name = "blake" version = "2.0.2" @@ -166,7 +212,7 @@ checksum = "e54b86398b5852ddd45784b1d9b196b98beb39171821bad4b8b44534a1e87927" dependencies = [ "cap-primitives", "cap-std", - "io-lifetimes", + "io-lifetimes 0.5.3", "winapi", ] @@ -177,13 +223,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb8fca3e81fae1d91a36e9784ca22a39ef623702b5f7904d89dc31f10184a178" dependencies = [ "ambient-authority", - "errno", + "errno 0.2.8", "fs-set-times", "io-extras", - "io-lifetimes", + "io-lifetimes 0.5.3", "ipnet", "maybe-owned", - "rustix", + "rustix 0.33.7", "winapi", "winapi-util", "winx", @@ -197,9 +243,9 @@ checksum = "2247568946095c7765ad2b441a56caffc08027734c634a6d5edda648f04e32eb" dependencies = [ "cap-primitives", "io-extras", - "io-lifetimes", + "io-lifetimes 0.5.3", "ipnet", - "rustix", + "rustix 0.33.7", ] [[package]] @@ -243,7 +289,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df386a2d0f35bdefc0642fd8bcb2cd28243959f028abfd22fbade6f7d30980e" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "indexmap", @@ -287,6 +333,19 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "concurrent_arena" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24bfeb060a299f86521bb3940344800fc861cc506356e44a273a42cb552afde5" +dependencies = [ + "arc-swap", + "array-init", + "const_fn_assert", + "parking_lot", + "triomphe", +] + [[package]] name = "console" version = "0.15.0" @@ -324,6 +383,12 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "const_fn_assert" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27d614f23f34f7b5165a77dc1591f497e2518f9cec4b4f4b92bfc4dc6cf7a190" + [[package]] name = "crc32fast" version = "1.3.2" @@ -384,7 +449,7 @@ version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2102ea4f781910f8a5b98dd061f4c2023f479ce7bb1236330099ceb5a93cf17" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crossterm_winapi", "libc", "mio", @@ -461,6 +526,26 @@ dependencies = [ "syn 1.0.96", ] +[[package]] +name = "derive_destructure2" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35cb7e5875e1028a73e551747d6d0118f25c3d6dbba2dadf97cc0f4d0c53f2f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.96", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -471,6 +556,17 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -533,6 +629,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "errno" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "errno-dragonfly" version = "0.1.2" @@ -561,6 +668,15 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "861d7b3427fbf3e06300b4aca5c430a2e263b7a7b6821faff8b200d3dc4a61cb" +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "flate2" version = "1.0.24" @@ -577,8 +693,8 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7df62ee66ee2d532ea8d567b5a3f0d03ecd64636b98bad5be1e93dcc918b92aa" dependencies = [ - "io-lifetimes", - "rustix", + "io-lifetimes 0.5.3", + "rustix 0.33.7", "winapi", ] @@ -592,6 +708,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + [[package]] name = "fxhash" version = "0.2.1" @@ -671,6 +799,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hostname" version = "0.3.1" @@ -719,13 +853,22 @@ dependencies = [ "regex", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "io-extras" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0c937cc9891c12eaa8c63ad347e4a288364b1328b924886970b47a14ab8f8f8" dependencies = [ - "io-lifetimes", + "io-lifetimes 0.5.3", "os_pipe", "winapi", ] @@ -739,6 +882,17 @@ dependencies = [ "os_pipe", ] +[[package]] +name = "io-lifetimes" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "io-streams" version = "0.11.0" @@ -747,11 +901,11 @@ checksum = "fba6685e8e5efa7bd0ce8c4e92ac113b3b059b2e18bd1bf51f7cfab0f61b4b19" dependencies = [ "duplex", "io-extras", - "io-lifetimes", + "io-lifetimes 0.5.3", "memchr", "os_pipe", "parking", - "rustix", + "rustix 0.33.7", "system-interface", ] @@ -802,9 +956,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "libsodium-sys" @@ -834,6 +988,12 @@ version = "0.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5284f00d480e1c39af34e72f8ad60b94f47007e3481cd3b731c1d67190ddc7b7" +[[package]] +name = "linux-raw-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" + [[package]] name = "lock_api" version = "0.4.7" @@ -920,14 +1080,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.36.1", + "windows-sys 0.45.0", ] [[package]] @@ -936,7 +1096,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cc", "cfg-if 0.1.10", "libc", @@ -949,7 +1109,7 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cc", "cfg-if 1.0.0", "libc", @@ -965,6 +1125,17 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.96", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -990,7 +1161,7 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", ] @@ -1012,6 +1183,95 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" +[[package]] +name = "openssh" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca6c277973fb549b36dd8980941b5ea3ecebea026f5b1f0060acde74d893c22" +dependencies = [ + "dirs", + "libc", + "once_cell", + "shell-escape", + "tempfile", + "thiserror", + "tokio", + "tokio-pipe", +] + +[[package]] +name = "openssh-sftp-client" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa8e5f26e549bd266d9bcd9e5b4fd344729985ef1a7f5ac3e51f3f96a4a620" +dependencies = [ + "bytes", + "derive_destructure2", + "once_cell", + "openssh-sftp-client-lowlevel", + "openssh-sftp-error", + "scopeguard", + "tokio", + "tokio-io-utility", + "tokio-util", +] + +[[package]] +name = "openssh-sftp-client-lowlevel" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406bf41d8372365497d5645e802a8dfe22008b8183edbe6c79e4b75614431daa" +dependencies = [ + "awaitable", + "bytes", + "concurrent_arena", + "derive_destructure2", + "openssh-sftp-error", + "openssh-sftp-protocol", + "pin-project", + "tokio", + "tokio-io-utility", +] + +[[package]] +name = "openssh-sftp-error" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d836b428ead150165d1178ed0aa672791c13b3ae9616ea1e34d13730a2cb486" +dependencies = [ + "awaitable-error", + "openssh-sftp-protocol-error", + "ssh_format_error", + "thiserror", + "tokio", +] + +[[package]] +name = "openssh-sftp-protocol" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf38532d784978966f95d241226223823f351d5bb2a4bebcf6b20b9cb1e393e0" +dependencies = [ + "bitflags 2.0.2", + "num-derive", + "num-traits", + "openssh-sftp-protocol-error", + "serde", + "ssh_format", + "vec-strings", +] + +[[package]] +name = "openssh-sftp-protocol-error" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0719269eb3f037866ae07ec89cb44ed2c1d63b72b2390cef8e1aa3016a956ff8" +dependencies = [ + "serde", + "thiserror", + "vec-strings", +] + [[package]] name = "os_pipe" version = "1.0.1" @@ -1028,6 +1288,29 @@ version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" +[[package]] +name = "ouroboros" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" +dependencies = [ + "aliasable", + "ouroboros_macro", +] + +[[package]] +name = "ouroboros_macro" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" +dependencies = [ + "Inflector", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.96", +] + [[package]] name = "parking" version = "2.0.0" @@ -1052,11 +1335,31 @@ checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.13", "smallvec", "windows-sys 0.36.1", ] +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.96", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -1189,7 +1492,16 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", ] [[package]] @@ -1198,7 +1510,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" dependencies = [ - "redox_syscall", + "redox_syscall 0.2.13", ] [[package]] @@ -1208,7 +1520,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.13", "thiserror", ] @@ -1235,7 +1547,7 @@ version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38ee71cbab2c827ec0ac24e76f82eca723cee92c509a65f67dee393c25112" dependencies = [ - "bitflags", + "bitflags 1.3.2", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -1250,16 +1562,30 @@ version = "0.33.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "938a344304321a9da4973b9ff4f9f8db9caf4597dfd9dda6a60b523340a0fff0" dependencies = [ - "bitflags", - "errno", - "io-lifetimes", + "bitflags 1.3.2", + "errno 0.2.8", + "io-lifetimes 0.5.3", "itoa", "libc", - "linux-raw-sys", + "linux-raw-sys 0.0.42", "once_cell", "winapi", ] +[[package]] +name = "rustix" +version = "0.37.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d097081ed288dfe45699b72f5b5d648e5f15d64d900c7080273baa20c16a6849" +dependencies = [ + "bitflags 1.3.2", + "errno 0.3.0", + "io-lifetimes 1.0.9", + "libc", + "linux-raw-sys 0.3.1", + "windows-sys 0.45.0", +] + [[package]] name = "rustversion" version = "1.0.6" @@ -1272,7 +1598,7 @@ version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8227301bfc717136f0ecbd3d064ba8199e44497a0bdd46bb01ede4387cfd2cec" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if 1.0.0", "dirs-next", "fs2", @@ -1357,6 +1683,12 @@ dependencies = [ "serde", ] +[[package]] +name = "shell-escape" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" + [[package]] name = "signal-hook" version = "0.3.14" @@ -1399,6 +1731,16 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "socketpair" version = "0.14.0" @@ -1406,8 +1748,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f0c0b3fc17356799222affc5a40345b7cc25b548c489c5a31eca0888ee2404c" dependencies = [ "io-extras", - "io-lifetimes", - "rustix", + "io-lifetimes 0.5.3", + "rustix 0.33.7", "uuid", "winapi", ] @@ -1424,6 +1766,32 @@ dependencies = [ "serde", ] +[[package]] +name = "ssh_format" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ab31081d1c9097c327ec23550858cb5ffb4af6b866c1ef4d728455f01f3304" +dependencies = [ + "bytes", + "serde", + "ssh_format_error", +] + +[[package]] +name = "ssh_format_error" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be3c6519de7ca611f71ef7e8a56eb57aa1c818fecb5242d0a0f39c83776c210c" +dependencies = [ + "serde", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -1484,11 +1852,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e09bb3fb4e02ec4b87e182ea9718fadbc0fa3e50085b40a9af9690572b67f9e" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "cap-fs-ext", - "io-lifetimes", + "io-lifetimes 0.5.3", "os_pipe", - "rustix", + "rustix 0.33.7", "socketpair", "winapi", "winx", @@ -1500,6 +1868,19 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af547b166dd1ea4b472165569fc456cfb6818116f854690b0ff205e636523dab" +[[package]] +name = "tempfile" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "redox_syscall 0.3.5", + "rustix 0.37.6", + "windows-sys 0.45.0", +] + [[package]] name = "termcolor" version = "1.1.3" @@ -1527,7 +1908,7 @@ checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e" dependencies = [ "libc", "numtoa", - "redox_syscall", + "redox_syscall 0.2.13", "redox_termios", ] @@ -1538,23 +1919,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] -name = "thiserror" -version = "1.0.31" +name = "thin-vec" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "aac81b6fd6beb5884b0cf3321b8117e6e5d47ecb6fc89f414cfdcca8b2fe2dd8" + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 1.0.96", + "syn 2.0.13", ] [[package]] @@ -1585,10 +1972,59 @@ checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", "bytes", + "libc", + "mio", "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", "windows-sys 0.45.0", ] +[[package]] +name = "tokio-io-utility" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6948d15bb5da6f565846828025df4d7b503df3890f0fcdb9a667de1c81bc1976" +dependencies = [ + "bytes", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "tokio-pipe" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f213a84bffbd61b8fa0ba8a044b4bbe35d471d0b518867181e82bd5c15542784" +dependencies = [ + "libc", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.5.9" @@ -1598,6 +2034,17 @@ dependencies = [ "serde", ] +[[package]] +name = "triomphe" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1ee9bd9239c339d714d657fac840c6d2a4f9c45f4f9ec7b0975113458be78db" +dependencies = [ + "arc-swap", + "serde", + "stable_deref_trait", +] + [[package]] name = "twox-hash" version = "1.6.3" @@ -1657,6 +2104,16 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vec-strings" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8509489e2a7ee219522238ad45fd370bec6808811ac15ac6b07453804e77659" +dependencies = [ + "serde", + "thin-vec", +] + [[package]] name = "version_check" version = "0.9.4" @@ -1838,8 +2295,8 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d5973cb8cd94a77d03ad7e23bbe14889cb29805da1cec0e4aff75e21aebded" dependencies = [ - "bitflags", - "io-lifetimes", + "bitflags 1.3.2", + "io-lifetimes 0.5.3", "winapi", ] @@ -1903,6 +2360,16 @@ version = "0.1.0" [[package]] name = "yama_wormfile_sftp" version = "0.1.0" +dependencies = [ + "async-trait", + "openssh", + "openssh-sftp-client", + "ouroboros", + "rand", + "thiserror", + "tokio", + "yama_wormfile", +] [[package]] name = "zstd" diff --git a/yama_wormfile/src/paths.rs b/yama_wormfile/src/paths.rs index cf9b8eb..860b554 100644 --- a/yama_wormfile/src/paths.rs +++ b/yama_wormfile/src/paths.rs @@ -1,4 +1,5 @@ use std::borrow::Borrow; +use std::fmt::{Debug, Formatter}; /// Simplified version of `Path` for use in WormFile situations. /// The Path is guaranteed to remain within the root and does not contain any `.` or `..` elements. @@ -7,6 +8,12 @@ pub struct WormPath { inner: str, } +impl Debug for WormPath { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "<{}>", &self.inner) + } +} + impl WormPath { pub fn new(path_str: &str) -> Option<&WormPath> { if path_str @@ -70,6 +77,12 @@ pub struct WormPathBuf { inner: String, } +impl Debug for WormPathBuf { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "<{}>", self.inner) + } +} + impl WormPathBuf { pub fn new(path_string: String) -> Option { if path_string diff --git a/yama_wormfile_sftp/Cargo.toml b/yama_wormfile_sftp/Cargo.toml index cc1cc03..f53b32b 100644 --- a/yama_wormfile_sftp/Cargo.toml +++ b/yama_wormfile_sftp/Cargo.toml @@ -6,3 +6,12 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +yama_wormfile = { version = "0.1.0", path = "../yama_wormfile" } + +ouroboros = "0.15.6" +openssh = "0.9.9" +openssh-sftp-client = "0.12.2" +async-trait = "0.1.68" +tokio = { version = "1.27.0", features = ["io-std"] } +rand = "0.8.5" +thiserror = "1.0.40" \ No newline at end of file diff --git a/yama_wormfile_sftp/src/lib.rs b/yama_wormfile_sftp/src/lib.rs index e69de29..3c2ebf8 100644 --- a/yama_wormfile_sftp/src/lib.rs +++ b/yama_wormfile_sftp/src/lib.rs @@ -0,0 +1,463 @@ +extern crate core; + +use async_trait::async_trait; +use openssh::{KnownHosts, RemoteChild, Session, Stdio}; +use openssh_sftp_client::error::SftpErrorKind; +use openssh_sftp_client::file::TokioCompatFile; +use openssh_sftp_client::fs::Fs; +use openssh_sftp_client::Error::SftpError; +use openssh_sftp_client::Sftp; +use ouroboros::self_referencing; +use std::fmt::{Debug, Formatter}; +use std::io; +use std::io::{ErrorKind, SeekFrom}; +use std::path::{Path, PathBuf}; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; +use thiserror::Error; +use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite, AsyncWriteExt, ReadBuf}; +use yama_wormfile::paths::{WormPath, WormPathBuf}; +use yama_wormfile::{WormFileProvider, WormFileReader, WormFileWriter}; + +/// WormFileProvider that uses an SFTP connection, in a given root directory. +#[derive(Debug)] +pub struct SftpWormFilesystem { + conn: Arc, + + /// The root directory. + root_dir: PathBuf, +} + +#[self_referencing] +struct SftpConn { + /// The SSH session. + ssh: Session, + + /// The SSH child process + #[borrows(ssh)] + #[covariant] + ssh_child: RemoteChild<'this>, + + /// The SFTP client. + #[borrows(mut ssh_child)] + sftp: Sftp, + + root_dir: PathBuf, + // #[borrows(sftp)] + // #[covariant] + // fs: Fs<'this>, +} + +#[self_referencing] +struct FileWithSftpConn { + conn: Arc, + #[borrows(conn)] + #[covariant] + file: Option>, +} + +impl SftpConn { + pub async fn create(ssh_connect: &str, root_dir: impl Into) -> YWSResult { + let root_dir = root_dir.into(); + let session = Session::connect(ssh_connect, KnownHosts::Strict).await?; + + let res = SftpConnAsyncTryBuilder { + ssh: session, + ssh_child_builder: |ssh| { + Box::pin(async move { + ssh.subsystem("sftp") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .await + .map_err(SftpWormFileError::from) + }) + }, + sftp_builder: |ssh_child| { + Box::pin(async move { + Sftp::new( + ssh_child.stdin().take().unwrap(), + ssh_child.stdout().take().unwrap(), + Default::default(), + ) + .await + .map_err(SftpWormFileError::from) + }) + }, + // fs_builder: |sftp| Box::pin(async move { + // let mut fs = sftp.fs(); + // fs.set_cwd(&root_dir); + // Ok(fs) + // }) + root_dir, + } + .try_build() + .await?; + Ok(res) + } + + pub fn get_fs(&self) -> Fs<'_> { + let mut fs = self.borrow_sftp().fs(); + fs.set_cwd(&self.borrow_root_dir()); + fs + } + + async fn create_dir_all(&self, worm_path_as_pathbuf: PathBuf) -> YWSResult<()> { + let mut fs = self.get_fs(); + let mut stack = vec![]; + + let mut at = Some(worm_path_as_pathbuf); + while let Some(at_path) = at { + match fs.metadata(&at_path).await { + Ok(_) => { + break; + } + Err(SftpError(SftpErrorKind::NoSuchFile, _)) => { + at = at_path.parent().map(Path::to_owned); + stack.push(at_path); + } + Err(sftp_err) => { + return Err(SftpWormFileError::SftpError(sftp_err)); + } + } + } + + while let Some(path) = stack.pop() { + fs.create_dir(path).await?; + } + + Ok(()) + } +} + +impl Debug for SftpConn { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "") + } +} + +#[derive(Debug, Error)] +pub enum SftpWormFileError { + #[error("ssh error: {0:?}")] + SshError(#[from] openssh::Error), + + #[error("sftp error: {0:?}")] + SftpError(#[from] openssh_sftp_client::Error), + + #[error("error: {0}")] + Message(String), +} + +type YWSResult = Result; + +impl SftpWormFilesystem { + pub async fn new( + ssh_connect: &str, + root_dir: impl Into, + ) -> YWSResult { + let root_dir = root_dir.into(); + + let conn = Arc::new(SftpConn::create(ssh_connect, &root_dir).await?); + + if !conn + .get_fs() + .metadata(".") + .await? + .file_type() + .unwrap() + .is_dir() + { + return Err(SftpWormFileError::Message(format!( + "{root_dir:?} is not a dir on SFTP remote." + ))); + } + + Ok(SftpWormFilesystem { conn, root_dir }) + } + + fn get_fs(&self) -> Fs<'_> { + let mut fs = self.conn.borrow_sftp().fs(); + fs.set_cwd(&self.root_dir); + fs + } +} + +#[async_trait] +impl WormFileProvider for SftpWormFilesystem { + type WormFileReader = SftpWormReader; + type WormFileWriter = SftpWormWriter; + type Error = SftpWormFileError; + + async fn is_dir(&self, path: impl AsRef + Send) -> Result { + let path = path.as_ref().as_str(); + let mut fs = self.get_fs(); + match fs.metadata(path).await { + Ok(meta) => Ok(meta.file_type().unwrap().is_dir()), + Err(SftpError(SftpErrorKind::NoSuchFile, _)) => Ok(false), + Err(sftp_err) => Err(SftpWormFileError::SftpError(sftp_err)), + } + } + + async fn is_regular_file( + &self, + path: impl AsRef + Send, + ) -> Result { + let path = path.as_ref().as_str(); + let mut fs = self.get_fs(); + match fs.metadata(path).await { + Ok(meta) => Ok(meta.file_type().unwrap().is_file()), + Err(SftpError(SftpErrorKind::NoSuchFile, _)) => Ok(false), + Err(sftp_err) => Err(SftpWormFileError::SftpError(sftp_err)), + } + } + + async fn list( + &self, + path: impl AsRef + Send, + ) -> Result, Self::Error> { + let worm_path = path.as_ref(); + let path = worm_path.as_str(); + let mut fs = self.get_fs(); + + let mut remote_dir = fs.open_dir(path).await?; + let dir_reader = remote_dir.read_dir().await?; + + Ok(dir_reader + .iter() + .filter_map(|entry| { + if let Some(name_str) = entry.filename().as_os_str().to_str() { + if name_str.is_empty() || name_str == "." || name_str == ".." { + None + } else { + Some(worm_path.join(name_str).expect("pre-checked")) + } + } else { + None + } + }) + .collect()) + } + + async fn read( + &self, + path: impl AsRef + Send, + ) -> Result { + let real_path = self.root_dir.join(path.as_ref().as_str()); + + let real_path2 = real_path.clone(); + // the `Send` in the below line is very important... + let file_with_conn: FileWithSftpConn = FileWithSftpConnAsyncSendTryBuilder { + conn: self.conn.clone(), + file_builder: |conn| { + Box::pin(async move { + let file = conn + .borrow_sftp() + .open(real_path) + .await + .map_err(SftpWormFileError::from)?; + Ok::<_, SftpWormFileError>(Some(TokioCompatFile::new(file))) + }) + }, + } + .try_build() + .await?; + + Ok(SftpWormReader { + path: real_path2, + file_with_conn, + }) + } + + async fn write(&self) -> Result { + // let tmp_dir = self.root_dir.join("tmp"); + // if !tokio::fs::try_exists(&tmp_dir).await? { + // tokio::fs::create_dir(&tmp_dir).await?; + // } + // + // let (tmp_path, file) = loop { + // let rand_num: u32 = rand::random(); + // let pid = std::process::id(); + // + // let try_fn = format!("pid{pid}-{rand_num:08X}.writing"); + // let try_path = tmp_dir.join(try_fn); + // match OpenOptions::new().create_new(true).open(&try_path).await { + // Ok(file) => break (try_path, file), + // Err(err) => { + // if err.kind() == ErrorKind::AlreadyExists { + // continue; + // } else { + // return Err(err); + // } + // } + // } + // }; + // + // Ok(SftpWormWriter { + // temp_path: tmp_path, + // file, + // root_dir: self.root_dir.clone(), + // }) + todo!() + } +} + +pub struct SftpWormReader { + path: PathBuf, + file_with_conn: FileWithSftpConn, +} + +impl Debug for SftpWormReader { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "FileWormReader({:?})", self.path) + } +} + +impl AsyncRead for SftpWormReader { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + self.file_with_conn + .with_file_mut(|file| Pin::new(file.as_mut().unwrap()).poll_read(cx, buf)) + } +} + +impl AsyncSeek for SftpWormReader { + fn start_seek(mut self: Pin<&mut Self>, position: SeekFrom) -> io::Result<()> { + self.file_with_conn + .with_file_mut(|file| Pin::new(file.as_mut().unwrap()).start_seek(position)) + } + + fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.file_with_conn + .with_file_mut(|file| Pin::new(file.as_mut().unwrap()).poll_complete(cx)) + } +} + +impl WormFileReader for SftpWormReader {} + +pub struct SftpWormWriter { + temp_path: PathBuf, + file_with_conn: FileWithSftpConn, + root_dir: PathBuf, +} + +impl Debug for SftpWormWriter { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "FileWormWriter({:?})", self.temp_path) + } +} + +impl AsyncWrite for SftpWormWriter { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + self.file_with_conn + .with_file_mut(|file| Pin::new(file.as_mut().unwrap()).poll_write(cx, buf)) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.file_with_conn + .with_file_mut(|file| Pin::new(file.as_mut().unwrap()).poll_flush(cx)) + } + + fn poll_shutdown( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + self.file_with_conn + .with_file_mut(|file| Pin::new(file.as_mut().unwrap()).poll_shutdown(cx)) + } +} + +#[async_trait] +impl WormFileWriter for SftpWormWriter { + async fn finalise(mut self, target_path: &WormPath, replace: bool) -> io::Result<()> { + self.flush().await?; + + let SftpWormWriter { + root_dir, + temp_path, + mut file_with_conn, + .. + } = self; + + let file = file_with_conn.with_file_mut(|file| file.take().unwrap()); + file.close() + .await + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + + let conn: Arc = file_with_conn.into_heads().conn; + let mut fs = conn.get_fs(); + + let worm_path = target_path; + + // Directories will be created as needed. + if let Some(parent) = PathBuf::from(worm_path.as_str()).parent() { + conn.create_dir_all(parent.to_owned()) + .await + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + } + + // Avoid allowing a replacement if not intended. + // But this is currently not atomic, so it's just a sanity check rather than a foolproof + // safeguard! + if !replace { + match fs.metadata(worm_path.as_str()).await { + Ok(_) => { + return Err(io::Error::new( + ErrorKind::AlreadyExists, + "finalise()ing a writer: dest already exists and replace = false", + )); + } + Err(SftpError(SftpErrorKind::NoSuchFile, _)) => { + // ideal. nop. + } + Err(sftp_err) => { + return Err(SftpWormFileError::SftpError(sftp_err)) + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + } + } + } + + // Perform the move. + fs.rename(root_dir.join(&temp_path), root_dir.join(worm_path.as_str())) + .await + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + Ok(()) + } +} + +#[tokio::test] +async fn test_lol() { + let swf = SftpWormFilesystem::new("scone@sallie", "").await.unwrap(); + let _ = swf.is_dir(WormPath::new("maddy").unwrap()).await; + + match swf.is_dir(WormPath::new("maddyss").unwrap()).await { + Ok(x) => eprintln!("{x:?}"), + Err(SftpWormFileError::SftpError(openssh_sftp_client::Error::SftpError( + openssh_sftp_client::error::SftpErrorKind::NoSuchFile, + _, + ))) => { + eprintln!("NSF"); + } + Err(other) => { + eprintln!("other {other:?}"); + } + } + + let x = swf.list(WormPath::new("maddy").unwrap()).await.unwrap(); + eprintln!("{x:?}"); + + // if let Err(e) = swf.sftp.close().await { + // eprintln!("sftp {e:?}"); + // } + // + // if let Err(e) = swf.ssh.close().await { + // eprintln!("sftp {e:?}"); + // } +}