Add a mount.bcachefs tool

This tool currently has most of the fundmental features implemented.
It can mount a filesystem specified by uuid, it can ask password for an
encrypted filesystem.

There may be some work that needs to be done to make it behave more like
a "mount.*" tool.

Related: #1

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2020-05-04 14:28:38 +01:00
parent 06c81e31b5
commit 57ada395b2
10 changed files with 1277 additions and 4 deletions

View File

@ -22,6 +22,7 @@ addons:
- libsodium-dev
- liburcu-dev
- libzstd-dev
- libudev-dev
- uuid-dev
- zlib1g-dev

View File

@ -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

695
mount/Cargo.lock generated Normal file
View File

@ -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"

34
mount/Cargo.toml Normal file
View File

@ -0,0 +1,34 @@
[package]
name = "bcachefs-mount"
version = "0.1.0"
authors = ["Yuxuan Shui <yshuiv7@gmail.com>"]
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 }

67
mount/build.rs Normal file
View File

@ -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();
}

174
mount/src/filesystem.rs Normal file
View File

@ -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<PathBuf>,
}
/// Parse a comma-separated mount options and split out mountflags and filesystem
/// specific options.
fn parse_mount_options(options: impl AsRef<str>) -> (Option<String>, 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<std::path::Path>,
options: impl AsRef<str>,
) -> 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<HashMap<Uuid, FileSystem>> {
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)
}

96
mount/src/key.rs Normal file
View File

@ -0,0 +1,96 @@
use log::info;
fn check_for_key(key_name: &std::ffi::CStr) -> anyhow::Result<bool> {
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::<LittleEndian>().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::<bch_encrypted_key>() 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::<bch_key>() 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),
}
}

View File

@ -0,0 +1 @@
#include <keyutils.h>

190
mount/src/lib.rs Normal file
View File

@ -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<Self> {
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<std::path::PathBuf>,
/// 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<bch_scrypt_flags> {
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::<LittleEndian>().unwrap();
let dword2 = internal_uuid.read_u32::<LittleEndian>().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);
}
}

View File

@ -0,0 +1,4 @@
#include <libbcachefs/super-io.h>
#include <libbcachefs/checksum.h>
#include <libbcachefs/bcachefs_format.h>
#include <crypto.h>