From cd0a1527f2257621f4a6ee41e54f1333a4af3997 Mon Sep 17 00:00:00 2001 From: rowan Date: Mon, 3 Mar 2025 12:45:04 -0600 Subject: [PATCH] fuse-overlayfs --- Cargo.lock | 828 +++++++++++++++++++++++++++++++ Cargo.toml | 4 + crates/char_enum/src/lib.rs | 26 +- src/fs/id_mapping.rs | 2 +- src/fs/mount.rs | 48 +- src/fs/permission.rs | 13 + src/fs/stackable/fuse_overlay.rs | 496 ++++++++++++++++-- src/fs/stackable/mod.rs | 43 +- src/fs/stackable/uwf_overlay.rs | 0 src/lib.rs | 1 + src/macros.rs | 45 ++ 11 files changed, 1395 insertions(+), 111 deletions(-) create mode 100644 src/fs/stackable/uwf_overlay.rs create mode 100644 src/macros.rs diff --git a/Cargo.lock b/Cargo.lock index 1c1e7db..ed54969 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,101 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bindgen" +version = "0.68.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytemuck" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" + +[[package]] +name = "cc" +version = "1.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +dependencies = [ + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "char_enum" version = "0.1.0" @@ -18,6 +113,36 @@ dependencies = [ "syn", ] +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "either" version = "1.13.0" @@ -44,6 +169,143 @@ dependencies = [ "syn", ] +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "itertools" version = "0.14.0" @@ -53,6 +315,127 @@ dependencies = [ "either", ] +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.170" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" + +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "log" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "prettyplease" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.93" @@ -71,6 +454,101 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "serde" +version = "1.0.218" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.218" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "syn" version = "2.0.98" @@ -82,6 +560,46 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-ident" version = "1.0.17" @@ -95,4 +613,314 @@ dependencies = [ "char_enum", "enumflags2", "itertools", + "winfsp", + "wmi", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-targets", +] + +[[package]] +name = "windows" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddf874e74c7a99773e62b1c671427abf01a425e77c3d3fb9fb1e4883ea934529" +dependencies = [ + "windows-collections", + "windows-core 0.60.1", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5467f79cc1ba3f52ebb2ed41dbb459b8e7db636cc3429458d9a852e15bc24dec" +dependencies = [ + "windows-core 0.60.1", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.60.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca21a92a9cae9bf4ccae5cf8368dce0837100ddf6e6d57936749e85f152f6247" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a787db4595e7eb80239b74ce8babfb1363d8e343ab072f2ffe901400c03349f0" +dependencies = [ + "windows-core 0.60.1", + "windows-link", +] + +[[package]] +name = "windows-implement" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" + +[[package]] +name = "windows-numerics" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "005dea54e2f6499f2cee279b8f703b3cf3b5734a2d8d21867c8f44003182eeed" +dependencies = [ + "windows-core 0.60.1", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06374efe858fab7e4f881500e6e86ec8bc28f9462c47e5a9941a0142ad86b189" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winfsp" +version = "0.11.3+winfsp-2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583f578b24fce35aedd77408b096d78740bd9bddf213f7859dc3571b2f3ff828" +dependencies = [ + "bytemuck", + "paste", + "static_assertions", + "thiserror 1.0.69", + "widestring", + "windows 0.52.0", + "winfsp-sys", +] + +[[package]] +name = "winfsp-sys" +version = "0.2.2+winfsp-2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbabd94628121ac484d3ab764430e1ded8d5fdf5f940e1f0d08971c394dbd59f" +dependencies = [ + "bindgen", +] + +[[package]] +name = "wmi" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae33d16f05f9b4b819abe7a9472bad4acb17f5b5d52d3f422762ebec613c65a6" +dependencies = [ + "chrono", + "futures", + "log", + "serde", + "thiserror 2.0.11", + "windows 0.60.0", + "windows-core 0.60.1", ] diff --git a/Cargo.toml b/Cargo.toml index 01e4706..71b202a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,7 @@ edition = "2021" char_enum = { version = "0.1.0", path = "crates/char_enum", features = ["derive"] } enumflags2 = "0.7.11" itertools = "0.14.0" + +[target.'cfg(windows)'.dependencies] +winfsp = "0.11.3" +wmi = "0.15.1" diff --git a/crates/char_enum/src/lib.rs b/crates/char_enum/src/lib.rs index 69556c0..b725902 100644 --- a/crates/char_enum/src/lib.rs +++ b/crates/char_enum/src/lib.rs @@ -4,38 +4,22 @@ pub use char_enum_derive; use std::{error::Error, fmt::Display}; #[derive(Debug, Clone, PartialEq)] -pub struct FromCharError { - message: String, -} - -impl FromCharError { - pub fn new(message: String) -> Self { - Self { message } - } -} +pub struct FromCharError(pub char); impl Display for FromCharError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.message) + write!(f, "invalid char: {}", self.0) } } impl Error for FromCharError {} #[derive(Debug, Clone, PartialEq)] -pub struct FromStrError { - message: String, -} - -impl FromStrError { - pub fn new(message: String) -> Self { - Self { message } - } -} +pub struct FromStrError(pub String); impl Display for FromStrError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.message) + write!(f, "invalid string: {}", self.0) } } @@ -43,7 +27,7 @@ impl Error for FromStrError {} impl From for FromStrError { fn from(value: FromCharError) -> Self { - Self::new(value.to_string()) + Self(value.to_string()) } } diff --git a/src/fs/id_mapping.rs b/src/fs/id_mapping.rs index 41d7d90..aa02dd8 100644 --- a/src/fs/id_mapping.rs +++ b/src/fs/id_mapping.rs @@ -135,7 +135,7 @@ impl From for UntypedIdRange { } } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ParseIdRangeError(String); impl Display for ParseIdRangeError { diff --git a/src/fs/mount.rs b/src/fs/mount.rs index 577f773..9bae8dc 100644 --- a/src/fs/mount.rs +++ b/src/fs/mount.rs @@ -70,34 +70,52 @@ impl Display for KeyValuePair { } } -#[derive(Debug, Default)] -pub struct MountOptions(Vec); +#[derive(Debug)] +pub struct MountOptions(pub Vec); -impl Deref for MountOptions { - type Target = Vec; +impl Clone for MountOptions { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl Default for MountOptions { + fn default() -> Self { + Self(Vec::new()) + } +} + +impl Deref for MountOptions { + type Target = Vec; fn deref(&self) -> &Self::Target { &self.0 } } -impl DerefMut for MountOptions { +impl DerefMut for MountOptions { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } -impl FromStr for MountOptions { - type Err = ParseMountOptionError; +impl FromIterator for MountOptions { + fn from_iter>(iter: I) -> Self { + Self(iter.into_iter().collect()) + } +} + +impl FromStr for MountOptions { + type Err = T::Err; fn from_str(s: &str) -> Result { s.split(',') - .map(MountOption::from_str) + .map(T::from_str) .process_results(|it| MountOptions(it.collect())) } } -impl Display for MountOptions { +impl Display for MountOptions { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let options = self.0.iter().map(|x| x.to_string()).join(" "); write!(f, "{options}") @@ -850,18 +868,18 @@ impl Display for MountOperation { } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Mount { source: DeviceId, target: PathBuf, - options: MountOptions, + options: MountOptions, } impl Mount { pub fn new( src: impl Into, target: impl Into, - options: impl Into, + options: impl Into>, ) -> Self { Self { source: src.into(), @@ -870,7 +888,7 @@ impl Mount { } } - pub fn exec(&self) -> std::io::Result { + pub fn mount(&self) -> std::io::Result { Command::new("mount") .arg(self.options.to_string()) .arg(self.source.to_string()) @@ -901,7 +919,7 @@ mod tests { use crate::fs::{ id_mapping::UntypedIdRange, mount::{ParseDeviceIdTypeError, ParseOptionsSourceError}, - permission::{OctalPermissions, Permissions}, + permission::Permissions, }; use super::{ @@ -1248,7 +1266,7 @@ mod tests { } #[test] - fn it_works() { + fn mount() { let mut options = MountOptions::default(); options.push(MountOption::FsType(vec!["overlay"].into())); options.push(MountOption::MakeDir(Some(Permissions::Octal( diff --git a/src/fs/permission.rs b/src/fs/permission.rs index ed75abb..87d7532 100644 --- a/src/fs/permission.rs +++ b/src/fs/permission.rs @@ -466,6 +466,19 @@ mod tests { ); } + #[test] + fn modes() { + let sym_modes = Modes::::from_str("rwx").unwrap(); + let ids = Modes::::from_str("ugo").unwrap(); + + assert_eq!( + sym_modes.0, + SymbolicMode::Read | SymbolicMode::Write | SymbolicMode::Execute + ); + + assert_eq!(ids.0, GroupId::User | GroupId::Group | GroupId::Other); + } + #[test] fn symbolic_permissions() { let sym = Permissions::from_str("ug+w").unwrap(); diff --git a/src/fs/stackable/fuse_overlay.rs b/src/fs/stackable/fuse_overlay.rs index bfb4fdf..8f633b6 100644 --- a/src/fs/stackable/fuse_overlay.rs +++ b/src/fs/stackable/fuse_overlay.rs @@ -1,65 +1,471 @@ -use std::fmt::Display; +use std::{ + convert::Infallible, + error::Error, + fmt::Display, + num::ParseIntError, + ops::Deref, + path::{Path, PathBuf}, + process::Command, + str::FromStr, +}; -use crate::fs::{id_mapping::IdMapping, AsIter, FileSystem, Mountpoint}; +use crate::fs::{ + id_mapping::{IdMapping, ParseIdRangeError}, + mount::MountOptions, + AsIter, FileSystem, Mountpoint, +}; +use crate::macros::*; -use super::{Stack, Stackable}; +use super::Stack; + +macro_rules! impl_wrapper { + ($name:ident($inner:ty), $err:ty, $from_str:block) => { + impl Deref for $name { + type Target = $inner; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl FromStr for $name { + type Err = $err; + + fn from_str(s: &str) -> Result { + $from_str + //Ok(Self($target::from_str(s)?)) + } + } + + impl Display for $name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } + } + }; +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Test(usize); + +impl_wrapper!(Test(usize), ParseIntError, { + Ok(Test(usize::from_str(s)?)) +}); + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ParseMaxIdleThreadsError(ParseIntError); + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ParseMaxThreadsError(ParseIntError); + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ParseSquashToIdError(ParseIntError); + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ParseSquashToUidError(ParseSquashToIdError); + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ParseSquashToGidError(ParseSquashToIdError); + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ParseUidMappingError(ParseIdRangeError); + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ParseGidMappingError(ParseIdRangeError); + +impl_wrapper_err_for!( + ParseMaxIdleThreadsError(ParseIntError), + "invalid max idle threads" +); + +impl_wrapper_err_for!(ParseMaxThreadsError(ParseIntError), "invalid max threads"); +impl_wrapper_err_for!(ParseSquashToIdError(ParseIntError), "invalid id"); +impl_wrapper_err_for!(ParseSquashToUidError(ParseSquashToIdError), "invalid uid"); +impl_wrapper_err_for!(ParseSquashToGidError(ParseSquashToIdError), "invalid gid"); +impl_wrapper_err_for!(ParseUidMappingError(ParseIdRangeError), "invalid uid"); +impl_wrapper_err_for!(ParseGidMappingError(ParseIdRangeError), "invalid gid"); + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct LowerDirs(Vec); + +impl Deref for LowerDirs { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Display for LowerDirs { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + self.0 + .iter() + .map(|v| v.to_string_lossy()) + .fold(String::new(), |a, b| a + &b) + ) + } +} + +impl FromStr for LowerDirs { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + Ok(Self(s.split(',').map(Into::into).collect())) + } +} + +impl From> for LowerDirs { + fn from(value: Vec<&str>) -> Self { + Self(value.into_iter().map(PathBuf::from).collect()) + } +} + +impl From> for LowerDirs { + fn from(value: Vec) -> Self { + Self(value.into_iter().map(PathBuf::from).collect()) + } +} + +impl From> for LowerDirs { + fn from(value: Vec<&Path>) -> Self { + Self(value.into_iter().map(Path::to_path_buf).collect()) + } +} + +impl From> for LowerDirs { + fn from(value: Vec) -> Self { + Self(value) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct MaxIdleThreads(Option); + +impl Default for MaxIdleThreads { + fn default() -> Self { + Self(None) + } +} + +impl FromStr for MaxIdleThreads { + type Err = ParseMaxIdleThreadsError; + + fn from_str(s: &str) -> Result { + match s { + "-1" => Ok(Self(None)), + s => Ok(usize::from_str_radix(s, 10).map(Some).map(Self)?), + } + } +} + +impl Display for MaxIdleThreads { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.0 { + Some(n) => write!(f, "{n}"), + None => write!(f, "-1"), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct MaxThreads(usize); + +impl Default for MaxThreads { + fn default() -> Self { + Self(10) + } +} + +impl FromStr for MaxThreads { + type Err = ParseMaxThreadsError; + + fn from_str(s: &str) -> Result { + Ok(usize::from_str_radix(s, 10).map(Self)?) + } +} + +impl Display for MaxThreads { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct SquashToId(usize); + +impl FromStr for SquashToId { + type Err = ParseSquashToIdError; + + fn from_str(s: &str) -> Result { + Ok(usize::from_str_radix(s, 10).map(Self)?) + } +} + +impl Display for SquashToId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct SquashToUid(SquashToId); + +impl Deref for SquashToUid { + type Target = SquashToId; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl FromStr for SquashToUid { + type Err = ParseSquashToUidError; + + fn from_str(s: &str) -> Result { + Ok(Self(SquashToId::from_str(s)?)) + } +} + +impl Display for SquashToUid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct SquashToGid(SquashToId); + +impl Deref for SquashToGid { + type Target = SquashToId; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl FromStr for SquashToGid { + type Err = ParseSquashToGidError; + + fn from_str(s: &str) -> Result { + Ok(Self(SquashToId::from_str(s)?)) + } +} + +impl Display for SquashToGid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct GidMapping(IdMapping); + +impl Deref for GidMapping { + type Target = IdMapping; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl FromStr for GidMapping { + type Err = ParseGidMappingError; + + fn from_str(s: &str) -> Result { + Ok(Self(IdMapping::from_str(s)?)) + } +} + +impl Display for GidMapping { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct GidMapping(IdMapping); + +impl Deref for GidMapping { + type Target = IdMapping; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl FromStr for GidMapping { + type Err = ParseGidMappingError; + + fn from_str(s: &str) -> Result { + Ok(Self(IdMapping::from_str(s)?)) + } +} + +impl Display for GidMapping { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ParseFuseOverlayOptionError { + MaxIdleThreads(ParseMaxIdleThreadsError), + MaxThreads(ParseMaxThreadsError), + SquashToUid(ParseSquashToUidError), + SquashToGid(ParseSquashToGidError), + UidMapping(ParseUidMappingError), + GidMapping(ParseGidMappingError), +} + +impl Display for ParseFuseOverlayOptionError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::MaxIdleThreads(e) => write!(f, "{e}"), + Self::MaxThreads(e) => write!(f, "{e}"), + Self::SquashToUid(e) => write!(f, "{e}"), + Self::SquashToGid(e) => write!(f, "{e}"), + Self::UidMapping(e) => write!(f, "{e}"), + Self::GidMapping(e) => write!(f, "{e}"), + } + } +} + +impl Error for ParseFuseOverlayOptionError {} + +impl_from_variants!( + ParseFuseOverlayOptionError, + MaxIdleThreads(ParseMaxIdleThreadsError), + MaxThreads(ParseMaxThreadsError) +); #[derive(Clone, Debug, PartialEq, Eq)] pub enum FuseOverlayOption { + LowerDir(LowerDirs), + UpperDir(PathBuf), + WorkDir(PathBuf), CloneFd, - MaxIdleThreads(isize), - MaxThreads(usize), + MaxIdleThreads(MaxIdleThreads), + MaxThreads(MaxThreads), AllowOther, AllowRoot, SquashToRoot, - SquashToUid(usize), - SquashToGid(usize), + SquashToUid(SquashToUid), + SquashToGid(SquashToGid), StaticNLink, NoAcl, UidMapping(IdMapping), GidMapping(IdMapping), } +impl_from_variants!( + FuseOverlayOption, + LowerDir(LowerDirs), + MaxIdleThreads(MaxIdleThreads), + MaxThreads(MaxThreads), + SquashToUid(SquashToUid), + SquashToGid(SquashToGid) +); + +impl FromStr for FuseOverlayOption { + type Err = ParseFuseOverlayOptionError; + + fn from_str(s: &str) -> Result { + let s = s.trim().trim_start_matches('-'); + + let option = match s.split_once(|delim| delim == ' ' || delim == '=') { + Some((option, value)) => (option, Some(value)), + None => (s, None), + }; + + match option { + ("lowerdir", Some(args)) => Ok(LowerDirs::from_str(args).unwrap().into()), + ("upperdir", Some(args)) => Ok(Self::UpperDir(PathBuf::from(args))), + ("workdir", Some(args)) => Ok(Self::WorkDir(PathBuf::from(args))), + ("clonefd", None) => Ok(Self::CloneFd), + ("max_idle_threads", Some(args)) => Ok(MaxIdleThreads::from_str(args).into()?), + ("max_threads", Some(args)) => Ok(MaxThreads::from_str(args).into()?), + ("allow_other", None) => Ok(Self::AllowOther), + ("allow_root", None) => Ok(Self::AllowRoot), + ("squash_to_root", None) => Ok(Self::SquashToRoot), + ("squash_to_uid", Some(uid)) => Ok(SquashToUid::from_str(uid).into()?), + ("squash_to_gid", Some(gid)) => Ok(SquashToGid::from_str(gid).into()?), + ("static_nlink", None) => Ok(Self::StaticNLink), + ("noacl", None) => Ok(Self::NoAcl), + ("uidmapping", Some(ids)) => Ok(Self::UidMapping(IdMapping::from_str(ids)?)), + ("gidmapping", Some(ids)) => Ok(Self::GidMapping(IdMapping::from_str(ids)?)), + } + } +} + impl Display for FuseOverlayOption { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - FuseOverlayOption::CloneFd => "clone_fd".to_string(), - FuseOverlayOption::MaxIdleThreads(n) => format!("max_idle_threads={n}"), - FuseOverlayOption::MaxThreads(n) => format!("max_threads={n}"), - FuseOverlayOption::AllowOther => "allow_other".to_string(), - FuseOverlayOption::AllowRoot => "allow_root".to_string(), - FuseOverlayOption::SquashToRoot => "squash_to_root".to_string(), - FuseOverlayOption::SquashToUid(uid) => format!("squash_to_uid={uid}"), - FuseOverlayOption::SquashToGid(gid) => format!("squash_to_gid={gid}"), - FuseOverlayOption::StaticNLink => "static_nlink".to_string(), - FuseOverlayOption::NoAcl => "noacl".to_string(), - FuseOverlayOption::UidMapping(map) => format!("uidmapping={map}"), - FuseOverlayOption::GidMapping(map) => format!("gidmapping={map}"), - } - ) + write!(f, "-o ")?; + + match self { + FuseOverlayOption::LowerDir(lower) => write!(f, "{lower}"), + FuseOverlayOption::UpperDir(path) => write!(f, "{}", path.to_string_lossy()), + FuseOverlayOption::WorkDir(path) => write!(f, "{}", path.to_string_lossy()), + FuseOverlayOption::CloneFd => write!(f, "clone_fd"), + FuseOverlayOption::MaxIdleThreads(n) => write!(f, "max_idle_threads={n}"), + FuseOverlayOption::MaxThreads(n) => write!(f, "max_threads={n}"), + FuseOverlayOption::AllowOther => write!(f, "allow_other"), + FuseOverlayOption::AllowRoot => write!(f, "allow_root"), + FuseOverlayOption::SquashToRoot => write!(f, "squash_to_root"), + FuseOverlayOption::SquashToUid(uid) => write!(f, "squash_to_uid={uid}"), + FuseOverlayOption::SquashToGid(gid) => write!(f, "squash_to_gid={gid}"), + FuseOverlayOption::StaticNLink => write!(f, "static_nlink"), + FuseOverlayOption::NoAcl => write!(f, "noacl"), + FuseOverlayOption::UidMapping(map) => write!(f, "uidmapping={map}"), + FuseOverlayOption::GidMapping(map) => write!(f, "gidmapping={map}"), + } } } #[derive(Debug)] pub struct FuseOverlay { - fs: Stackable, - options: Vec, + lower_dir: usize, + upper_dir: Option, + work_dir: Option, + options: MountOptions, +} + +impl FuseOverlay { + pub fn new(lowerdir: impl Into) -> Self { + Self { + lower_dir: 0, + upper_dir: None, + work_dir: None, + options: MountOptions(vec![FuseOverlayOption::LowerDir(lowerdir.into())]), + } + } } impl Stack for FuseOverlay { fn lower_dirs(&self) -> impl Iterator { - self.fs.lower_dirs() + match self.options.get(self.lower_dir) { + Some(FuseOverlayOption::LowerDir(lower)) => lower.as_iter().map(AsRef::as_ref), + _ => panic!("invalid lowerdir option"), + } } fn upper_dir(&self) -> Option<&std::path::Path> { - self.fs.upper_dir() + let upper = self.upper_dir.and_then(|i| self.options.get(i)); + + match upper { + Some(FuseOverlayOption::UpperDir(upper)) => Some(upper), + _ => None, + } } fn work_dir(&self) -> Option<&std::path::Path> { - self.fs.work_dir() + let work = self.work_dir.and_then(|i| self.options.get(i)); + + match work { + Some(FuseOverlayOption::WorkDir(work)) => Some(work), + _ => None, + } } } @@ -71,10 +477,36 @@ impl Mountpoint for FuseOverlay { impl FileSystem for FuseOverlay { fn mount(&mut self) -> std::io::Result<()> { - todo!() + let mut cmd = Command::new("fuse-overlayfs").arg(self.options.to_string()); + + if self.fs.lower.len() > 0 { + cmd.arg(format!("-o lowerdir={}", self.fs.lower)); + }; + + if let Some(upper) = &self.fs.upper { + cmd.arg(format!("-o upperdir={}", upper.to_string_lossy())); + }; + + if let Some(work) = &self.fs.work { + cmd.arg(format!("-o workdir={}", work.to_string_lossy())); + }; + + cmd.output()?; + + Ok(()) } fn unmount(&mut self) -> std::io::Result<()> { - todo!() + if let Some(upper) = &self.fs.upper { + Command::new("fusermount").arg("-u").arg(upper).output()?; + } + + Ok(()) + } +} + +impl Display for FuseOverlay { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "fuse-overlayfs") } } diff --git a/src/fs/stackable/mod.rs b/src/fs/stackable/mod.rs index b4981eb..42e6417 100644 --- a/src/fs/stackable/mod.rs +++ b/src/fs/stackable/mod.rs @@ -1,51 +1,10 @@ pub mod fuse_overlay; pub mod overlay; -use std::path::{Path, PathBuf}; +use std::path::Path; pub trait Stack { fn lower_dirs(&self) -> impl Iterator; fn upper_dir(&self) -> Option<&Path>; fn work_dir(&self) -> Option<&Path>; } - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Stackable { - lower: Vec, - upper: Option, - work: Option, -} - -impl Stackable { - pub fn new(lower: Vec) -> Self { - Self { - lower, - upper: None, - work: None, - } - } - - pub fn with_upper_dir(&mut self, upper: impl Into) -> &mut Self { - self.upper = Some(upper.into()); - self - } - - pub fn with_work_dir(&mut self, work: impl Into) -> &mut Self { - self.work = Some(work.into()); - self - } -} - -impl Stack for Stackable { - fn lower_dirs(&self) -> impl Iterator { - self.lower.iter().map(AsRef::as_ref) - } - - fn upper_dir(&self) -> Option<&Path> { - self.upper.as_deref() - } - - fn work_dir(&self) -> Option<&Path> { - self.work.as_deref() - } -} diff --git a/src/fs/stackable/uwf_overlay.rs b/src/fs/stackable/uwf_overlay.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/lib.rs b/src/lib.rs index b3b52a9..3d6f76e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod fs; +mod macros; mod utils; pub fn add(left: u64, right: u64) -> u64 { diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..2caec69 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,45 @@ +macro_rules! impl_from_variants { + ($enum_type:ident, $($variant:ident($inner_type:ty)),*) => { + $( + impl From<$inner_type> for $enum_type { + fn from(inner: $inner_type) -> Self { + $enum_type::$variant(inner) + } + } + )* + }; +} + +macro_rules! impl_wrapper_err_for { + ($name:ident($inner:ty), $err:literal) => { + impl_wrapper_err!($name, $err); + impl_from_ty!($name, $inner); + }; +} + +macro_rules! impl_from_ty { + ($name:ty, $from:ty) => { + impl From<$from> for $name { + fn from(value: $from) -> Self { + Self(value) + } + } + }; +} + +macro_rules! impl_wrapper_err { + ($type:ty, $err:literal) => { + impl Display for $type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}: {}", self.0, $err) + } + } + + impl Error for $type {} + }; +} + +pub(crate) use impl_from_ty; +pub(crate) use impl_from_variants; +pub(crate) use impl_wrapper_err; +pub(crate) use impl_wrapper_err_for;