mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-02-22 00:00:03 +03:00
Rust now integrated into bcachefs binary
Rust is now required for building the bcachefs tool, and rust code is now fully integrated with the C codebase - meaning it is possible to call back and forth. The mount helper is now a subcommand, 'mount.bcachefs' is now a small shell wrapper that invokes 'bcachefs mount'. This will make it easier to start rewriting other subcommands in rust, and eventually the whole command line interface. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
da6a356895
commit
28f703cc25
4
.gitignore
vendored
4
.gitignore
vendored
@ -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.*
|
||||
|
43
Makefile
43
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
|
||||
|
17
bcachefs.c
17
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;
|
||||
}
|
||||
|
1
cmds.h
1
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 */
|
||||
|
4
mount.bcachefs
Executable file
4
mount.bcachefs
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
SDIR="$(readlink -f "$0")"
|
||||
exec "${SDIR%/*}/bcachefs" mount "$@"
|
2
rust-src/mount/Cargo.lock → rust-src/Cargo.lock
generated
2
rust-src/mount/Cargo.lock → rust-src/Cargo.lock
generated
@ -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",
|
@ -1,10 +1,11 @@
|
||||
[package]
|
||||
name = "bcachefs-mount"
|
||||
name = "bcachefs-rust"
|
||||
version = "0.3.1"
|
||||
authors = ["Yuxuan Shui <yshuiv7@gmail.com>", "Kayla Firestack <dev@kaylafire.me>"]
|
||||
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"
|
@ -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()
|
||||
|
@ -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!();
|
||||
// }
|
||||
}
|
@ -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<T, E, OE> = ::core::result::Result<::core::result::Result<T, E>, OE>;
|
||||
pub type Result<T, E> = GResult<T, E, GError>;
|
||||
}
|
||||
|
||||
#[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<KeyLocation>);
|
||||
impl std::ops::Deref for KeyLoc {
|
||||
type Target = Option<KeyLocation>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl std::str::FromStr for KeyLoc {
|
||||
type Err = anyhow::Error;
|
||||
fn from_str(s: &str) -> anyhow::Result<Self> {
|
||||
// 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<Uuid, uuid::Error> {
|
||||
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);
|
||||
}
|
||||
}
|
@ -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<KeyLocation>);
|
||||
impl std::ops::Deref for KeyLoc {
|
||||
type Target = Option<KeyLocation>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for KeyLoc {
|
||||
type Err = anyhow::Error;
|
||||
fn from_str(s: &str) -> anyhow::Result<Self> {
|
||||
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<bool> {
|
||||
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),
|
||||
}
|
||||
}
|
33
rust-src/src/lib.rs
Normal file
33
rust-src/src/lib.rs
Normal file
@ -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<T, E, OE> = ::core::result::Result<::core::result::Result<T, E>, OE>;
|
||||
pub type Result<T, E> = GResult<T, E, GError>;
|
||||
}
|
||||
|
||||
#[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 {}
|
Loading…
Reference in New Issue
Block a user