diff --git a/.gitignore b/.gitignore index 7ba6add8..15accd00 100644 --- a/.gitignore +++ b/.gitignore @@ -6,8 +6,6 @@ bcachefs.5 *.so *.d *.a -/rust-src/mount/result -/rust-src/bch_bindgen/result tags TAGS cscope* @@ -21,6 +19,4 @@ tests/__pycache__/ !.travis.yml !.editorconfig -mount/target -mount.bcachefs bcachefs-principles-of-operation.* diff --git a/Makefile b/Makefile index bce10d5b..8846e127 100644 --- a/Makefile +++ b/Makefile @@ -90,10 +90,11 @@ else endif .PHONY: all -all: bcachefs lib +all: bcachefs -.PHONY: lib -lib: libbcachefs.so +.PHONY: debug +debug: CFLAGS+=-Werror -DCONFIG_BCACHEFS_DEBUG=y -DCONFIG_VALGRIND=y +debug: bcachefs .PHONY: tests tests: tests/test_helper @@ -123,30 +124,17 @@ OBJS=$(SRCS:.c=.o) @echo " [CC] $@" $(Q)$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< -bcachefs: $(filter-out ./tests/%.o, $(OBJS)) +bcachefs: libbcachefs.a rust-src/target/release/libbcachefs_rust.a @echo " [LD] $@" - $(Q)$(CC) $(LDFLAGS) $+ $(LOADLIBES) $(LDLIBS) -o $@ + $(Q)$(CC) $(LDFLAGS) -Wl,--whole-archive $+ $(LOADLIBES) -Wl,--no-whole-archive $(LDLIBS) -o $@ + +libbcachefs.a: $(filter-out ./tests/%.o, $(OBJS)) + @echo " [AR] $@" + $(Q)ar -rc $@ $+ RUST_SRCS=$(shell find rust-src/ -type f -iname '*.rs') -MOUNT_SRCS=$(filter %mount, $(RUST_SRCS)) - -debug: CFLAGS+=-Werror -DCONFIG_BCACHEFS_DEBUG=y -DCONFIG_VALGRIND=y -debug: bcachefs - -MOUNT_OBJ=$(filter-out ./bcachefs.o ./tests/%.o ./cmd_%.o , $(OBJS)) -libbcachefs.so: LDFLAGS+=-shared -libbcachefs.so: $(MOUNT_OBJ) - @echo " [CC] $@" - $(Q)$(CC) $(LDFLAGS) $+ -o $@ $(LDLIBS) - -MOUNT_TOML=rust-src/mount/Cargo.toml -mount.bcachefs: lib $(MOUNT_SRCS) - LIBBCACHEFS_LIB=$(CURDIR) \ - LIBBCACHEFS_INCLUDE=$(CURDIR) \ - $(CARGO_BUILD) --manifest-path $(MOUNT_TOML) - - ln -f rust-src/mount/target/$(CARGO_PROFILE)/bcachefs-mount $@ - +rust-src/target/release/libbcachefs_rust.a: libbcachefs.a $(RUST_SRCS) + $(CARGO_BUILD) --manifest-path rust-src/Cargo.toml tests/test_helper: $(filter ./tests/%.o, $(OBJS)) @echo " [LD] $@" @@ -166,15 +154,14 @@ cmd_version.o : .version .PHONY: install install: INITRAMFS_HOOK=$(INITRAMFS_DIR)/hooks/bcachefs install: INITRAMFS_SCRIPT=$(INITRAMFS_DIR)/scripts/local-premount/bcachefs -install: bcachefs lib +install: bcachefs $(INSTALL) -m0755 -D bcachefs -t $(DESTDIR)$(ROOT_SBINDIR) $(INSTALL) -m0755 fsck.bcachefs $(DESTDIR)$(ROOT_SBINDIR) $(INSTALL) -m0755 mkfs.bcachefs $(DESTDIR)$(ROOT_SBINDIR) + $(INSTALL) -m0755 mount.bcachefs $(DESTDIR)$(ROOT_SBINDIR) $(INSTALL) -m0644 -D bcachefs.8 -t $(DESTDIR)$(PREFIX)/share/man/man8/ $(INSTALL) -m0755 -D initramfs/script $(DESTDIR)$(INITRAMFS_SCRIPT) $(INSTALL) -m0755 -D initramfs/hook $(DESTDIR)$(INITRAMFS_HOOK) - $(INSTALL) -m0755 -D mount.bcachefs.sh $(DESTDIR)$(ROOT_SBINDIR) - $(INSTALL) -m0755 -D libbcachefs.so -t $(DESTDIR)$(PREFIX)/lib/ sed -i '/^# Note: make install replaces/,$$d' $(DESTDIR)$(INITRAMFS_HOOK) echo "copy_exec $(ROOT_SBINDIR)/bcachefs /sbin/bcachefs" >> $(DESTDIR)$(INITRAMFS_HOOK) @@ -182,7 +169,7 @@ install: bcachefs lib .PHONY: clean clean: @echo "Cleaning all" - $(Q)$(RM) bcachefs mount.bcachefs libbcachefs.so libbcachefs_mount.a tests/test_helper .version *.tar.xz $(OBJS) $(DEPS) $(DOCGENERATED) + $(Q)$(RM) bcachefs libbcachefs.a tests/test_helper .version *.tar.xz $(OBJS) $(DEPS) $(DOCGENERATED) $(Q)$(RM) -rf rust-src/*/target .PHONY: deb diff --git a/bcachefs.c b/bcachefs.c index 31d96287..1876823c 100644 --- a/bcachefs.c +++ b/bcachefs.c @@ -177,8 +177,21 @@ int main(int argc, char *argv[]) setvbuf(stdout, NULL, _IOLBF, 0); - char *cmd = pop_cmd(&argc, argv); - if (argc < 1) { + if (argc < 2) { + puts("missing command\n"); + goto usage; + } + + /* Rust commands first - rust can't handle us mutating argv */ + char *cmd = argv[1]; + + if (!strcmp(cmd, "mount")) { + cmd_mount(); + return 0; + } + + cmd = pop_cmd(&argc, argv); + if (!cmd) { puts("missing command\n"); goto usage; } diff --git a/cmds.h b/cmds.h index c18a87fd..52b6e0bb 100644 --- a/cmds.h +++ b/cmds.h @@ -61,5 +61,6 @@ int cmd_subvolume_delete(int argc, char *argv[]); int cmd_subvolume_snapshot(int argc, char *argv[]); int cmd_fusemount(int argc, char *argv[]); +void cmd_mount(void); #endif /* _CMDS_H */ diff --git a/mount.bcachefs b/mount.bcachefs new file mode 100755 index 00000000..59002328 --- /dev/null +++ b/mount.bcachefs @@ -0,0 +1,4 @@ +#!/bin/sh + +SDIR="$(readlink -f "$0")" +exec "${SDIR%/*}/bcachefs" mount "$@" diff --git a/rust-src/mount/.gitignore b/rust-src/.gitignore similarity index 100% rename from rust-src/mount/.gitignore rename to rust-src/.gitignore diff --git a/rust-src/mount/Cargo.lock b/rust-src/Cargo.lock similarity index 99% rename from rust-src/mount/Cargo.lock rename to rust-src/Cargo.lock index 0b193360..9e5fd2a5 100644 --- a/rust-src/mount/Cargo.lock +++ b/rust-src/Cargo.lock @@ -44,7 +44,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "bcachefs-mount" +name = "bcachefs-rust" version = "0.3.1" dependencies = [ "anyhow", diff --git a/rust-src/mount/Cargo.toml b/rust-src/Cargo.toml similarity index 73% rename from rust-src/mount/Cargo.toml rename to rust-src/Cargo.toml index 40113945..64b195ca 100644 --- a/rust-src/mount/Cargo.toml +++ b/rust-src/Cargo.toml @@ -1,10 +1,11 @@ [package] -name = "bcachefs-mount" +name = "bcachefs-rust" version = "0.3.1" authors = ["Yuxuan Shui ", "Kayla Firestack "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +crate-type = ["staticlib"] [dependencies] atty = "0.2.14" @@ -23,5 +24,5 @@ parse-display = "0.1" errno = "0.2" either = "1.5" rpassword = "4" -bch_bindgen = { path = "../bch_bindgen" } +bch_bindgen = { path = "bch_bindgen" } byteorder = "1.3" diff --git a/rust-src/mount/README.md b/rust-src/README.md similarity index 100% rename from rust-src/mount/README.md rename to rust-src/README.md diff --git a/rust-src/bch_bindgen/build.rs b/rust-src/bch_bindgen/build.rs index da1541b1..a2cb8957 100644 --- a/rust-src/bch_bindgen/build.rs +++ b/rust-src/bch_bindgen/build.rs @@ -7,13 +7,8 @@ fn main() { let top_dir: PathBuf = std::env::var_os("CARGO_MANIFEST_DIR") .expect("ENV Var 'CARGO_MANIFEST_DIR' Expected") .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()); - println!("cargo:rustc-link-lib=dylib=bcachefs"); - println!("cargo:rustc-link-search={}", env!("LIBBCACHEFS_LIB")); + let libbcachefs_inc_dir = std::path::Path::new("../.."); let _libbcachefs_dir = top_dir.join("libbcachefs").join("libbcachefs"); let bindings = bindgen::builder() diff --git a/rust-src/mount/default.nix b/rust-src/default.nix similarity index 100% rename from rust-src/mount/default.nix rename to rust-src/default.nix diff --git a/rust-src/mount/module.nix b/rust-src/module.nix similarity index 100% rename from rust-src/mount/module.nix rename to rust-src/module.nix diff --git a/rust-src/mount/src/main.rs b/rust-src/mount/src/main.rs deleted file mode 100644 index 66ec8acc..00000000 --- a/rust-src/mount/src/main.rs +++ /dev/null @@ -1,53 +0,0 @@ -use bcachefs_mount::Cli; -use bch_bindgen::{error, info}; -use clap::Parser; -use colored::Colorize; - -fn main() { - let opt = Cli::parse(); - bch_bindgen::log::set_verbose_level(opt.verbose + bch_bindgen::log::ERROR); - colored::control::set_override(opt.colorize); - if let Err(e) = crate::main_inner(opt) { - error!("Fatal error: {}", e); - } -} - -pub fn main_inner(opt: Cli) -> anyhow::Result<()> { - use bcachefs_mount::{filesystem, key}; - unsafe { - libc::setvbuf(filesystem::stdout, std::ptr::null_mut(), libc::_IONBF, 0); - // libc::fflush(filesystem::stdout); - } - - let fss = filesystem::probe_filesystems()?; - let fs = fss - .get(&opt.uuid) - .ok_or_else(|| anyhow::anyhow!("filesystem was not found"))?; - - info!("found filesystem {}", fs); - if fs.encrypted() { - let key = opt - .key_location - .0 - .ok_or_else(|| anyhow::anyhow!("no keyoption specified for locked filesystem"))?; - - key::prepare_key(&fs, key)?; - } - - let mountpoint = opt - .mountpoint - .ok_or_else(|| anyhow::anyhow!("mountpoint option was not specified"))?; - - fs.mount(&mountpoint, &opt.options)?; - - Ok(()) -} - -#[cfg(test)] -mod test { - // use insta::assert_debug_snapshot; - // #[test] - // fn snapshot_testing() { - // insta::assert_debug_snapshot!(); - // } -} diff --git a/rust-src/mount/rustfmt.toml b/rust-src/rustfmt.toml similarity index 100% rename from rust-src/mount/rustfmt.toml rename to rust-src/rustfmt.toml diff --git a/rust-src/mount/src/lib.rs b/rust-src/src/cmd_mount.rs similarity index 51% rename from rust-src/mount/src/lib.rs rename to rust-src/src/cmd_mount.rs index 68acde03..7748b199 100644 --- a/rust-src/mount/src/lib.rs +++ b/rust-src/src/cmd_mount.rs @@ -1,66 +1,11 @@ -use anyhow::anyhow; -use atty::Stream; +use bch_bindgen::{error, info}; use clap::Parser; +use colored::Colorize; +use atty::Stream; use uuid::Uuid; - -pub mod err { - pub enum GError { - Unknown { - message: std::borrow::Cow<'static, String>, - }, - } - pub type GResult = ::core::result::Result<::core::result::Result, OE>; - pub type Result = GResult; -} - -#[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(Clone, Debug)] -pub enum KeyLocation { - Fail, - Wait, - Ask, -} - -#[derive(Clone, Debug)] -pub struct KeyLoc(pub Option); -impl std::ops::Deref for KeyLoc { - type Target = Option; - fn deref(&self) -> &Self::Target { - &self.0 - } -} -impl std::str::FromStr for KeyLoc { - type Err = anyhow::Error; - fn from_str(s: &str) -> anyhow::Result { - // use anyhow::anyhow; - match s { - "" => Ok(KeyLoc(None)), - "fail" => Ok(KeyLoc(Some(KeyLocation::Fail))), - "wait" => Ok(KeyLoc(Some(KeyLocation::Wait))), - "ask" => Ok(KeyLoc(Some(KeyLocation::Ask))), - _ => Err(anyhow!("invalid password option")), - } - } -} +use crate::filesystem; +use crate::key; +use crate::key::KeyLoc; fn parse_fstab_uuid(uuid_raw: &str) -> Result { let mut uuid = String::from(uuid_raw); @@ -114,12 +59,41 @@ pub struct Cli { pub verbose: u8, } -pub mod filesystem; -pub mod key; -// pub fn mnt_in_use() +pub fn cmd_mount_inner(opt: Cli) -> anyhow::Result<()> { + unsafe { + libc::setvbuf(filesystem::stdout, std::ptr::null_mut(), libc::_IONBF, 0); + } -#[test] -fn verify_cli() { - use clap::CommandFactory; - Cli::command().debug_assert() + let fss = filesystem::probe_filesystems()?; + let fs = fss + .get(&opt.uuid) + .ok_or_else(|| anyhow::anyhow!("filesystem was not found"))?; + + info!("found filesystem {}", fs); + if fs.encrypted() { + let key = opt + .key_location + .0 + .ok_or_else(|| anyhow::anyhow!("no keyoption specified for locked filesystem"))?; + + key::prepare_key(&fs, key)?; + } + + let mountpoint = opt + .mountpoint + .ok_or_else(|| anyhow::anyhow!("mountpoint option was not specified"))?; + + fs.mount(&mountpoint, &opt.options)?; + + Ok(()) +} + +#[no_mangle] +pub extern "C" fn cmd_mount() { + let opt = Cli::parse(); + bch_bindgen::log::set_verbose_level(opt.verbose + bch_bindgen::log::ERROR); + colored::control::set_override(opt.colorize); + if let Err(e) = cmd_mount_inner(opt) { + error!("Fatal error: {}", e); + } } diff --git a/rust-src/mount/src/filesystem.rs b/rust-src/src/filesystem.rs similarity index 100% rename from rust-src/mount/src/filesystem.rs rename to rust-src/src/filesystem.rs diff --git a/rust-src/mount/src/key.rs b/rust-src/src/key.rs similarity index 75% rename from rust-src/mount/src/key.rs rename to rust-src/src/key.rs index a49baa68..e2d0e4c0 100644 --- a/rust-src/mount/src/key.rs +++ b/rust-src/src/key.rs @@ -1,5 +1,36 @@ use bch_bindgen::info; use colored::Colorize; +use crate::c_str; +use anyhow::anyhow; + +#[derive(Clone, Debug)] +pub enum KeyLocation { + Fail, + Wait, + Ask, +} + +#[derive(Clone, Debug)] +pub struct KeyLoc(pub Option); +impl std::ops::Deref for KeyLoc { + type Target = Option; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::str::FromStr for KeyLoc { + type Err = anyhow::Error; + fn from_str(s: &str) -> anyhow::Result { + match s { + "" => Ok(KeyLoc(None)), + "fail" => Ok(KeyLoc(Some(KeyLocation::Fail))), + "wait" => Ok(KeyLoc(Some(KeyLocation::Wait))), + "ask" => Ok(KeyLoc(Some(KeyLocation::Ask))), + _ => Err(anyhow!("invalid password option")), + } + } +} fn check_for_key(key_name: &std::ffi::CStr) -> anyhow::Result { use bch_bindgen::keyutils::{self, keyctl_search}; @@ -31,7 +62,6 @@ fn wait_for_key(uuid: &uuid::Uuid) -> anyhow::Result<()> { const BCH_KEY_MAGIC: &str = "bch**key"; use crate::filesystem::FileSystem; fn ask_for_key(fs: &FileSystem) -> anyhow::Result<()> { - use anyhow::anyhow; use bch_bindgen::bcachefs::{self, bch2_chacha_encrypt_key, bch_encrypted_key, bch_key}; use byteorder::{LittleEndian, ReadBytesExt}; use std::os::raw::c_char; @@ -84,14 +114,11 @@ fn ask_for_key(fs: &FileSystem) -> anyhow::Result<()> { } } -pub fn prepare_key(fs: &FileSystem, password: crate::KeyLocation) -> anyhow::Result<()> { - use crate::KeyLocation::*; - use anyhow::anyhow; - +pub fn prepare_key(fs: &FileSystem, password: KeyLocation) -> anyhow::Result<()> { info!("checking if key exists for filesystem {}", fs.uuid()); match password { - Fail => Err(anyhow!("no key available")), - Wait => Ok(wait_for_key(fs.uuid())?), - Ask => ask_for_key(fs), + KeyLocation::Fail => Err(anyhow!("no key available")), + KeyLocation::Wait => Ok(wait_for_key(fs.uuid())?), + KeyLocation::Ask => ask_for_key(fs), } } diff --git a/rust-src/src/lib.rs b/rust-src/src/lib.rs new file mode 100644 index 00000000..b2f0aaa7 --- /dev/null +++ b/rust-src/src/lib.rs @@ -0,0 +1,33 @@ +pub mod filesystem; +pub mod key; +pub mod cmd_mount; + +pub mod err { + pub enum GError { + Unknown { + message: std::borrow::Cow<'static, String>, + }, + } + pub type GResult = ::core::result::Result<::core::result::Result, OE>; + pub type Result = GResult; +} + +#[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 {}