diff --git a/.travis.yml b/.travis.yml index 5f347ba4..3b90b73c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,7 @@ addons: - libsodium-dev - liburcu-dev - libzstd-dev + - libudev-dev - uuid-dev - zlib1g-dev diff --git a/Makefile b/Makefile index 25de546a..fe8b3fd5 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ ifdef D CFLAGS+=-DCONFIG_VALGRIND=y endif -PKGCONFIG_LIBS="blkid uuid liburcu libsodium zlib liblz4 libzstd" +PKGCONFIG_LIBS="blkid uuid liburcu libsodium zlib liblz4 libzstd libudev" ifdef BCACHEFS_FUSE PKGCONFIG_LIBS+="fuse3 >= 3.7" CFLAGS+=-DBCACHEFS_FUSE @@ -57,7 +57,7 @@ endif CFLAGS+=$(PKGCONFIG_CFLAGS) LDLIBS+=$(PKGCONFIG_LDLIBS) -LDLIBS+=-lm -lpthread -lrt -lscrypt -lkeyutils -laio +LDLIBS+=-lm -lpthread -lrt -lscrypt -lkeyutils -laio -ldl LDLIBS+=$(EXTRA_LDLIBS) ifeq ($(PREFIX),/usr) @@ -69,7 +69,7 @@ else endif .PHONY: all -all: bcachefs +all: bcachefs mount.bcachefs .PHONY: tests tests: tests/test_helper @@ -92,6 +92,16 @@ DEPS=$(SRCS:.c=.d) OBJS=$(SRCS:.c=.o) bcachefs: $(filter-out ./tests/%.o, $(OBJS)) +MOUNT_SRCS=$(shell find mount/src -type f -iname '*.rs') \ + mount/Cargo.toml mount/Cargo.lock mount/build.rs +libbcachefs_mount.a: $(MOUNT_SRCS) + LIBBCACHEFS_INCLUDE=$(CURDIR) cargo build --manifest-path mount/Cargo.toml --release + cp mount/target/release/libbcachefs_mount.a $@ + +MOUNT_OBJ=$(filter-out ./bcachefs.o ./tests/%.o ./cmd_%.o , $(OBJS)) +mount.bcachefs: libbcachefs_mount.a $(MOUNT_OBJ) + $(CC) -Wl,--gc-sections libbcachefs_mount.a $(MOUNT_OBJ) -o $@ $(LDLIBS) + tests/test_helper: $(filter ./tests/%.o, $(OBJS)) # If the version string differs from the last build, update the last version @@ -119,7 +129,8 @@ install: bcachefs .PHONY: clean clean: - $(RM) bcachefs tests/test_helper .version $(OBJS) $(DEPS) + $(RM) bcachefs mount.bcachefs libbcachefs_mount.a tests/test_helper .version $(OBJS) $(DEPS) + $(RM) -rf mount/target .PHONY: deb deb: all diff --git a/mount/Cargo.lock b/mount/Cargo.lock new file mode 100644 index 00000000..77ccbba7 --- /dev/null +++ b/mount/Cargo.lock @@ -0,0 +1,695 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "bcachefs-mount" +version = "0.1.0" +dependencies = [ + "anyhow", + "bindgen", + "bitfield", + "byteorder", + "clap", + "either", + "env_logger", + "errno", + "gag", + "getset", + "itertools", + "libc", + "log", + "memoffset", + "parse-display", + "pkg-config", + "rpassword", + "structopt", + "udev", + "uuid", +] + +[[package]] +name = "bindgen" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb26d6a69a335b8cb0e7c7e9775cd5666611dc50a37177c3f2cedcfc040e8c8" +dependencies = [ + "bitflags", + "cexpr", + "cfg-if", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", +] + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cexpr" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "clang-sys" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a" +dependencies = [ + "glob", + "libc", +] + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "term_size", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "log", +] + +[[package]] +name = "errno" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b480f641ccf0faf324e20c1d3e53d81b7484c698b42ea677f6907ae4db195371" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" +dependencies = [ + "gcc", + "libc", +] + +[[package]] +name = "gag" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cc0b9f53275dc5fada808f1d2f82e3688a6c14d735633d1590b7be8eb2307b5" +dependencies = [ + "libc", + "tempfile", +] + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getset" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f62a139c59ae846c3964c392f12aac68f1997d1a40e9d3b40a89a4ab553e04a0" +dependencies = [ + "proc-macro-error 0.4.12", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" +dependencies = [ + "libc", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" + +[[package]] +name = "libc" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" + +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "memoffset" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" +dependencies = [ + "autocfg", +] + +[[package]] +name = "nom" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6" +dependencies = [ + "memchr", + "version_check", +] + +[[package]] +name = "parse-display" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718b422bc6b056b6374f7ffc3b2d9b55180a4af59a089835df1963994676d8b6" +dependencies = [ + "lazy_static", + "parse-display-derive", + "regex", +] + +[[package]] +name = "parse-display-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7cf2deb364a60cc0f633c1ffe619b42463993c91352ae367010b8420e442655" +dependencies = [ + "lazy_static", + "proc-macro2", + "quote", + "regex", + "regex-syntax", + "syn", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pkg-config" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" + +[[package]] +name = "proc-macro-error" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" +dependencies = [ + "proc-macro-error-attr 0.4.12", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678" +dependencies = [ + "proc-macro-error-attr 1.0.2", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "syn-mid", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "syn-mid", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + +[[package]] +name = "regex" +version = "1.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" + +[[package]] +name = "remove_dir_all" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +dependencies = [ + "winapi", +] + +[[package]] +name = "rpassword" +version = "4.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99371657d3c8e4d816fb6221db98fa408242b0b53bac08f8676a41f8554fe99f" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "shlex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863246aaf5ddd0d6928dfeb1a9ca65f505599e4e1b399935ef7e75107516b4ef" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d239ca4b13aee7a2142e6795cbd69e457665ff8037aed33b3effdc430d2f927a" +dependencies = [ + "heck", + "proc-macro-error 1.0.2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "syn-mid" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tempfile" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +dependencies = [ + "cfg-if", + "libc", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "term_size" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "term_size", + "unicode-width", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "udev" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24953d50a3bce0f5f5a9a2766567072dc9af8096f8c40ea81815da651066bc9f" +dependencies = [ + "libc", + "libudev-sys", +] + +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "uuid" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" + +[[package]] +name = "version_check" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/mount/Cargo.toml b/mount/Cargo.toml new file mode 100644 index 00000000..4fd0d497 --- /dev/null +++ b/mount/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "bcachefs-mount" +version = "0.1.0" +authors = ["Yuxuan Shui "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +log = "0.4" +clap = { version = "2.33", features = [ "wrap_help" ] } +env_logger = { version = "0.7", default-features = false } +anyhow = "1.0" +udev = "0.4" +uuid = "0.8" +libc = "0.2.69" +gag = "0.1" +bitfield = "0.13" +memoffset = "0.5" +getset = "0.1" +itertools = "0.9" +structopt = "0.3" +parse-display = "0.1" +errno = "0.2" +either = "1.5" +rpassword = "4" +byteorder = "1.3" + +[lib] +crate-type = ["staticlib"] + +[build-dependencies] +pkg-config = "0.3" +bindgen = { version = "0.53", default-features = false } diff --git a/mount/build.rs b/mount/build.rs new file mode 100644 index 00000000..65428897 --- /dev/null +++ b/mount/build.rs @@ -0,0 +1,67 @@ +fn main() { + use std::path::PathBuf; + use std::process::Command; + + let out_dir: PathBuf = std::env::var_os("OUT_DIR").unwrap().into(); + let top_dir: PathBuf = std::env::var_os("CARGO_MANIFEST_DIR").unwrap().into(); + let libbcachefs_inc_dir = std::env::var("LIBBCACHEFS_INCLUDE") + .unwrap_or_else(|_| top_dir.join("libbcachefs").display().to_string()); + let libbcachefs_inc_dir = std::path::Path::new(&libbcachefs_inc_dir); + println!("{}", libbcachefs_inc_dir.display()); + + let libbcachefs_dir = top_dir.join("libbcachefs").join("libbcachefs"); + let bindings = bindgen::builder() + .header(top_dir + .join("src") + .join("libbcachefs_wrapper.h") + .display() + .to_string()) + .clang_arg(format!( + "-I{}", + libbcachefs_inc_dir.join("include").display() + )) + .clang_arg(format!("-I{}", libbcachefs_inc_dir.display())) + .clang_arg("-DZSTD_STATIC_LINKING_ONLY") + .clang_arg("-DNO_BCACHEFS_FS") + .clang_arg("-D_GNU_SOURCE") + .derive_debug(false) + .derive_default(true) + .default_enum_style(bindgen::EnumVariation::Rust { + non_exhaustive: true, + }) + .whitelist_function("bch2_read_super") + .whitelist_function("bch2_sb_field_.*") + .whitelist_function("bch2_chacha_encrypt_key") + .whitelist_function("derive_passphrase") + .whitelist_function("request_key") + .whitelist_function("add_key") + .whitelist_function("keyctl_search") + .whitelist_var("BCH_.*") + .whitelist_var("KEY_SPEC_.*") + .whitelist_type("bch_kdf_types") + .whitelist_type("bch_sb_field_.*") + .whitelist_type("bch_encrypted_key") + .whitelist_type("nonce") + .rustified_enum("bch_kdf_types") + .opaque_type("gendisk") + .opaque_type("bkey") + .generate() + .unwrap(); + bindings.write_to_file(out_dir.join("bcachefs.rs")).unwrap(); + + let keyutils = pkg_config::probe_library("libkeyutils").unwrap(); + let bindings = bindgen::builder() + .header(top_dir + .join("src") + .join("keyutils_wrapper.h") + .display() + .to_string()) + .clang_args( + keyutils.include_paths + .iter() + .map(|p| format!("-I{}", p.display())), + ) + .generate() + .unwrap(); + bindings.write_to_file(out_dir.join("keyutils.rs")).unwrap(); +} diff --git a/mount/src/filesystem.rs b/mount/src/filesystem.rs new file mode 100644 index 00000000..36af8c05 --- /dev/null +++ b/mount/src/filesystem.rs @@ -0,0 +1,174 @@ +extern "C" { + pub static stdout: *mut libc::FILE; +} + +use getset::{CopyGetters, Getters}; +use std::path::PathBuf; +#[derive(Getters, CopyGetters)] +pub struct FileSystem { + /// External UUID of the bcachefs + #[getset(get = "pub")] + uuid: uuid::Uuid, + /// Whether filesystem is encrypted + #[getset(get_copy = "pub")] + encrypted: bool, + /// Super block + #[getset(get = "pub")] + sb: bcachefs::bch_sb_handle, + /// Member devices for this filesystem + #[getset(get = "pub")] + devices: Vec, +} + +/// Parse a comma-separated mount options and split out mountflags and filesystem +/// specific options. +fn parse_mount_options(options: impl AsRef) -> (Option, u64) { + use either::Either::*; + let (opts, flags) = options + .as_ref() + .split(",") + .map(|o| match o { + "dirsync" => Left(libc::MS_DIRSYNC), + "lazytime" => Left(1 << 25), // MS_LAZYTIME + "mand" => Left(libc::MS_MANDLOCK), + "noatime" => Left(libc::MS_NOATIME), + "nodev" => Left(libc::MS_NODEV), + "nodiratime" => Left(libc::MS_NODIRATIME), + "noexec" => Left(libc::MS_NOEXEC), + "nosuid" => Left(libc::MS_NOSUID), + "ro" => Left(libc::MS_RDONLY), + "rw" => Left(0), + "relatime" => Left(libc::MS_RELATIME), + "strictatime" => Left(libc::MS_STRICTATIME), + "sync" => Left(libc::MS_SYNCHRONOUS), + "" => Left(0), + o @ _ => Right(o), + }) + .fold((Vec::new(), 0), |(mut opts, flags), next| match next { + Left(f) => (opts, flags | f), + Right(o) => { + opts.push(o); + (opts, flags) + } + }); + + use itertools::Itertools; + ( + if opts.len() == 0 { + None + } else { + Some(opts.iter().join(",")) + }, + flags, + ) +} + +impl FileSystem { + pub(crate) fn new(sb: bcachefs::bch_sb_handle) -> Self { + Self { + uuid: sb.sb().uuid(), + encrypted: sb.sb().crypt().is_some(), + sb: sb, + devices: vec![], + } + } + + pub fn mount( + &self, + target: impl AsRef, + options: impl AsRef, + ) -> anyhow::Result<()> { + use itertools::Itertools; + use std::ffi::c_void; + use std::os::raw::c_char; + use std::os::unix::ffi::OsStrExt; + let src = self.devices.iter().map(|d| d.display()).join(":"); + let (data, mountflags) = parse_mount_options(options); + let fstype = c_str!("bcachefs"); + + let src = std::ffi::CString::new(src)?; // bind the CString to keep it alive + let target = std::ffi::CString::new(target.as_ref().as_os_str().as_bytes())?; // ditto + let data = data.map(|data| std::ffi::CString::new(data)).transpose()?; // ditto + + let src = src.as_c_str().to_bytes_with_nul().as_ptr() as *const c_char; + let target = target.as_c_str().to_bytes_with_nul().as_ptr() as *const c_char; + let data = data.as_ref().map_or(std::ptr::null(), |data| { + data.as_c_str().to_bytes_with_nul().as_ptr() as *const c_void + }); + + let ret = unsafe { libc::mount(src, target, fstype, mountflags, data) }; + if ret == 0 { + Ok(()) + } else { + Err(crate::ErrnoError(errno::errno()).into()) + } + } +} + +use crate::bcachefs; +use std::collections::HashMap; +use uuid::Uuid; +pub fn probe_filesystems() -> anyhow::Result> { + use std::os::unix::ffi::OsStrExt; + let mut udev = udev::Enumerator::new()?; + let mut fss = HashMap::new(); + udev.match_subsystem("block")?; + + { + // Stop libbcachefs from spamming the output + let _gag = gag::Gag::stdout().unwrap(); + for dev in udev.scan_devices()? { + if let Some(p) = dev.devnode() { + let path = + std::ffi::CString::new(p.as_os_str().as_bytes()).unwrap(); + let result = unsafe { + let mut opts = std::mem::MaybeUninit::zeroed(); + let mut sb = std::mem::MaybeUninit::zeroed(); + let ret = bcachefs::bch2_read_super( + path.as_ptr(), + opts.as_mut_ptr(), + sb.as_mut_ptr(), + ); + if ret == -libc::EACCES { + Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "no permission", + )) + } else if ret != 0 { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "failed to read super", + )) + } else { + Ok((opts.assume_init(), sb.assume_init())) + } + }; + match result { + Ok((_, sb)) => match fss.get_mut(&sb.sb().uuid()) { + None => { + let mut fs = FileSystem::new(sb); + fs.devices.push(p.to_owned()); + fss.insert(fs.uuid, fs); + } + Some(fs) => { + fs.devices.push(p.to_owned()); + } + }, + Err(e) if e.kind() + != std::io::ErrorKind::PermissionDenied => + { + () + } + e @ Err(_) => { + e?; + } + } + } + } + // Flush stdout so buffered output don't get printed after we remove the gag + unsafe { + libc::fflush(stdout); + } + } + Ok(fss) +} diff --git a/mount/src/key.rs b/mount/src/key.rs new file mode 100644 index 00000000..6769f52c --- /dev/null +++ b/mount/src/key.rs @@ -0,0 +1,96 @@ +use log::info; + +fn check_for_key(key_name: &std::ffi::CStr) -> anyhow::Result { + use crate::keyutils::{self, keyctl_search}; + let key_name = key_name.to_bytes_with_nul().as_ptr() as *const _; + let key_type = c_str!("logon"); + + let key_id = + unsafe { keyctl_search(keyutils::KEY_SPEC_USER_KEYRING, key_type, key_name, 0) }; + if key_id > 0 { + info!("Key has became avaiable"); + Ok(true) + } else if errno::errno().0 != libc::ENOKEY { + Err(crate::ErrnoError(errno::errno()).into()) + } else { + Ok(false) + } +} + +fn wait_for_key(uuid: &uuid::Uuid) -> anyhow::Result<()> { + let key_name = std::ffi::CString::new(format!("bcachefs:{}", uuid)).unwrap(); + loop { + if check_for_key(&key_name)? { + break Ok(()); + } + + std::thread::sleep(std::time::Duration::from_secs(1)); + } +} + +const BCH_KEY_MAGIC: &str = "bch**key"; +use crate::filesystem::FileSystem; +fn ask_for_key(fs: &FileSystem) -> anyhow::Result<()> { + use crate::bcachefs::{self, bch2_chacha_encrypt_key, bch_encrypted_key, bch_key}; + use anyhow::anyhow; + use byteorder::{LittleEndian, ReadBytesExt}; + use std::os::raw::c_char; + + let key_name = std::ffi::CString::new(format!("bcachefs:{}", fs.uuid())).unwrap(); + if check_for_key(&key_name)? { + return Ok(()); + } + + let bch_key_magic = BCH_KEY_MAGIC.as_bytes().read_u64::().unwrap(); + let crypt = fs.sb().sb().crypt().unwrap(); + let pass = rpassword::read_password_from_tty(Some("Enter passphrase: "))?; + let pass = std::ffi::CString::new(pass.trim_end())?; // bind to keep the CString alive + let mut output: bch_key = unsafe { + bcachefs::derive_passphrase( + crypt as *const _ as *mut _, + pass.as_c_str().to_bytes_with_nul().as_ptr() as *const _, + ) + }; + + let mut key = crypt.key().clone(); + let ret = unsafe { + bch2_chacha_encrypt_key( + &mut output as *mut _, + fs.sb().sb().nonce(), + &mut key as *mut _ as *mut _, + std::mem::size_of::() as u64, + ) + }; + if ret != 0 { + Err(anyhow!("chache decryption failure")) + } else if key.magic != bch_key_magic { + Err(anyhow!("failed to verify the password")) + } else { + let key_type = c_str!("logon"); + let ret = unsafe { + crate::keyutils::add_key( + key_type, + key_name.as_c_str().to_bytes_with_nul() as *const _ + as *const c_char, + &output as *const _ as *const _, + std::mem::size_of::() as u64, + crate::keyutils::KEY_SPEC_USER_KEYRING, + ) + }; + if ret == -1 { + Err(anyhow!("failed to add key to keyring: {}", errno::errno())) + } else { + Ok(()) + } + } +} + +pub(crate) fn prepare_key(fs: &FileSystem, password: crate::KeyLocation) -> anyhow::Result<()> { + use crate::KeyLocation::*; + use anyhow::anyhow; + match password { + Fail => Err(anyhow!("no key available")), + Wait => Ok(wait_for_key(fs.uuid())?), + Ask => ask_for_key(fs), + } +} diff --git a/mount/src/keyutils_wrapper.h b/mount/src/keyutils_wrapper.h new file mode 100644 index 00000000..857cee2e --- /dev/null +++ b/mount/src/keyutils_wrapper.h @@ -0,0 +1 @@ +#include diff --git a/mount/src/lib.rs b/mount/src/lib.rs new file mode 100644 index 00000000..751eab38 --- /dev/null +++ b/mount/src/lib.rs @@ -0,0 +1,190 @@ +use structopt::StructOpt; +use anyhow::anyhow; + +#[macro_export] +macro_rules! c_str { + ($lit:expr) => { + unsafe { std::ffi::CStr::from_ptr(concat!($lit, "\0").as_ptr() as *const std::os::raw::c_char) + .to_bytes_with_nul() + .as_ptr() as *const std::os::raw::c_char } + }; +} + +#[derive(Debug)] +struct ErrnoError(errno::Errno); +impl std::fmt::Display for ErrnoError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + self.0.fmt(f) + } +} +impl std::error::Error for ErrnoError {} + +#[derive(Debug)] +pub(crate) enum KeyLocation { + Fail, + Wait, + Ask, +} + +impl std::str::FromStr for KeyLocation { + type Err = anyhow::Error; + fn from_str(s: &str) -> anyhow::Result { + use anyhow::anyhow; + match s { + "fail" => Ok(Self::Fail), + "wait" => Ok(Self::Wait), + "ask" => Ok(Self::Ask), + _ => Err(anyhow!("invalid password option")) + } + } +} + +#[derive(StructOpt, Debug)] +/// Mount a bcachefs filesystem by its UUID. +struct Options { + /// Where the password would be loaded from. + /// + /// Possible values are: + /// "fail" - don't ask for password, fail if filesystem is encrypted; + /// "wait" - wait for password to become available before mounting; + /// "ask" - prompt the user for password; + #[structopt(short, long, default_value = "fail")] + key_location: KeyLocation, + + /// External UUID of the bcachefs filesystem + uuid: uuid::Uuid, + + /// Where the filesystem should be mounted. If not set, then the filesystem + /// won't actually be mounted. But all steps preceeding mounting the + /// filesystem (e.g. asking for passphrase) will still be performed. + mountpoint: Option, + + /// Mount options + #[structopt(short, default_value = "")] + options: String, +} + +mod filesystem; +mod key; +mod keyutils { + #![allow(non_upper_case_globals)] + #![allow(non_camel_case_types)] + #![allow(non_snake_case)] + #![allow(unused)] + + include!(concat!(env!("OUT_DIR"), "/keyutils.rs")); +} + +mod bcachefs { + #![allow(non_upper_case_globals)] + #![allow(non_camel_case_types)] + #![allow(non_snake_case)] + #![allow(unused)] + + include!(concat!(env!("OUT_DIR"), "/bcachefs.rs")); + + use bitfield::bitfield; + bitfield! { + pub struct bch_scrypt_flags(u64); + pub N, _: 15, 0; + pub R, _: 31, 16; + pub P, _: 47, 32; + } + bitfield! { + pub struct bch_crypt_flags(u64); + TYPE, _: 4, 0; + } + use memoffset::offset_of; + impl bch_sb_field_crypt { + pub fn scrypt_flags(&self) -> Option { + let t = bch_crypt_flags(self.flags); + if t.TYPE() != bch_kdf_types::BCH_KDF_SCRYPT as u64 { + None + } else { + Some(bch_scrypt_flags(self.kdf_flags)) + } + } + pub fn key(&self) -> &bch_encrypted_key { + &self.key + } + } + impl bch_sb { + pub fn crypt(&self) -> Option<&bch_sb_field_crypt> { + unsafe { + let ptr = bch2_sb_field_get( + self as *const _ as *mut _, + bch_sb_field_type::BCH_SB_FIELD_crypt, + ) as *const u8; + if ptr.is_null() { + None + } else { + let offset = offset_of!(bch_sb_field_crypt, field); + Some(&*((ptr.sub(offset)) as *const _)) + } + } + } + pub fn uuid(&self) -> uuid::Uuid { + uuid::Uuid::from_bytes(self.user_uuid.b) + } + + /// Get the nonce used to encrypt the superblock + pub fn nonce(&self) -> nonce { + use byteorder::{ReadBytesExt, LittleEndian}; + let mut internal_uuid = &self.uuid.b[..]; + let dword1 = internal_uuid.read_u32::().unwrap(); + let dword2 = internal_uuid.read_u32::().unwrap(); + nonce { d: [0, 0, dword1, dword2] } + } + } + impl bch_sb_handle { + pub fn sb(&self) -> &bch_sb { + unsafe { &*self.sb } + } + } +} + +fn main_inner() -> anyhow::Result<()> { + use itertools::Itertools; + use log::{info, trace}; + + env_logger::init(); + let opt = Options::from_args(); + trace!("{:?}", opt); + + let fss = filesystem::probe_filesystems()?; + info!("Found {} bcachefs filesystems: ", fss.len()); + for fs in fss.values() { + info!( + "{} ({}): {}", + fs.uuid(), + if fs.encrypted() { + "encrypted" + } else { + "unencrypted" + }, + fs.devices().iter().map(|d| d.display()).join(" ") + ); + } + + if let Some(fs) = fss.get(&opt.uuid) { + if fs.encrypted() { + info!("Making sure key is loaded for this filesystem"); + key::prepare_key(&fs, opt.key_location)?; + } + + if let Some(p) = opt.mountpoint { + fs.mount(&p, &opt.options) + } else { + Ok(()) + } + } else { + Err(anyhow!("Filesystem {} is not found", opt.uuid)) + } +} + +#[no_mangle] +pub extern "C" fn main() { + if let Err(e) = main_inner() { + println!("Error: {:?}", e); + } +} diff --git a/mount/src/libbcachefs_wrapper.h b/mount/src/libbcachefs_wrapper.h new file mode 100644 index 00000000..fd76a6c0 --- /dev/null +++ b/mount/src/libbcachefs_wrapper.h @@ -0,0 +1,4 @@ +#include +#include +#include +#include