* [PATCH TOOLS 0/2] convert main() from C to Rust
@ 2024-01-15 5:24 Thomas Bertschinger
2024-01-15 5:24 ` [PATCH TOOLS 1/2] " Thomas Bertschinger
` (3 more replies)
0 siblings, 4 replies; 16+ messages in thread
From: Thomas Bertschinger @ 2024-01-15 5:24 UTC (permalink / raw)
To: kent.overstreet, linux-bcachefs, bfoster; +Cc: Thomas Bertschinger
This converts the top-level main() of the bcachefs command line tool
from C to Rust.
The first patch does all the work of implementing a new main(), updating
the build process, and making things look for the executable in its new
location which is now rust-src/target/release/bcachefs.
The second patch removes the Library crate from the Rust package. This
was needed previously to allow the C program to link in the Rust
functions, but that doesn't happen anymore.
It seems likely that a bcachefs-tools library will be needed in the
future; for example see this request [1] on GitHub. This library will
have a different external-focused API as opposed to the previous library
that provided an internal API for the bcachefs tool itself. Considering
Rust's structure of one library crate per package, removing the current
library makes room for that requested library.
I tried to update everything to account for the new location of the
bcachefs executable, but let me know if anyone sees something I missed.
I could not figure out how this is set for the Debian package so it's
possible I missed something there...
[1] https://github.com/koverstreet/bcachefs-tools/issues/188
Thomas Bertschinger (2):
convert main() from C to Rust
remove library from bcachefs-tools Rust package
Makefile | 21 +--
bcachefs.c | 127 +-----------------
cmds.h | 6 +-
rust-src/Cargo.lock | 2 +-
rust-src/Cargo.toml | 7 +-
rust-src/bch_bindgen/build.rs | 6 +
.../bch_bindgen/src/libbcachefs_wrapper.h | 2 +
rust-src/build.rs | 21 +++
rust-src/src/bcachefs.rs | 120 +++++++++++++++++
rust-src/src/cmd_main.rs | 34 -----
.../src/{ => commands}/cmd_completions.rs | 3 +-
rust-src/src/{ => commands}/cmd_list.rs | 3 +-
rust-src/src/{ => commands}/cmd_mount.rs | 11 +-
rust-src/src/{ => commands}/logger.rs | 0
rust-src/src/commands/mod.rs | 31 +++++
rust-src/src/lib.rs | 54 --------
tests/util.py | 2 +-
17 files changed, 214 insertions(+), 236 deletions(-)
create mode 100644 rust-src/build.rs
create mode 100644 rust-src/src/bcachefs.rs
delete mode 100644 rust-src/src/cmd_main.rs
rename rust-src/src/{ => commands}/cmd_completions.rs (84%)
rename rust-src/src/{ => commands}/cmd_list.rs (98%)
rename rust-src/src/{ => commands}/cmd_mount.rs (94%)
rename rust-src/src/{ => commands}/logger.rs (100%)
create mode 100644 rust-src/src/commands/mod.rs
delete mode 100644 rust-src/src/lib.rs
--
2.43.0
^ permalink raw reply [flat|nested] 16+ messages in thread* [PATCH TOOLS 1/2] convert main() from C to Rust 2024-01-15 5:24 [PATCH TOOLS 0/2] convert main() from C to Rust Thomas Bertschinger @ 2024-01-15 5:24 ` Thomas Bertschinger 2024-01-15 5:24 ` [PATCH TOOLS 2/2] remove library from bcachefs-tools Rust package Thomas Bertschinger ` (2 subsequent siblings) 3 siblings, 0 replies; 16+ messages in thread From: Thomas Bertschinger @ 2024-01-15 5:24 UTC (permalink / raw) To: kent.overstreet, linux-bcachefs, bfoster; +Cc: Thomas Bertschinger This moves the main() function from C to Rust. Running the bcachefs tool out of the development tree is now: $ ./rust-src/target/release/bcachefs command or $ cargo run --profile release --manifest-path rust-src/Cargo.toml --command instead of: $ ./bcachefs command Building and installing is still: $ make && make install Signed-off-by: Thomas Bertschinger <tahbertschinger@gmail.com> --- Makefile | 21 +-- bcachefs.c | 127 +----------------- cmds.h | 6 +- rust-src/Cargo.lock | 2 +- rust-src/Cargo.toml | 8 +- rust-src/bch_bindgen/build.rs | 6 + .../bch_bindgen/src/libbcachefs_wrapper.h | 2 + rust-src/build.rs | 21 +++ rust-src/src/bcachefs.rs | 107 +++++++++++++++ rust-src/src/cmd_completions.rs | 3 +- rust-src/src/cmd_list.rs | 3 +- rust-src/src/cmd_main.rs | 34 ----- rust-src/src/cmd_mount.rs | 11 +- rust-src/src/lib.rs | 13 -- tests/util.py | 2 +- 15 files changed, 172 insertions(+), 194 deletions(-) create mode 100644 rust-src/build.rs create mode 100644 rust-src/src/bcachefs.rs delete mode 100644 rust-src/src/cmd_main.rs diff --git a/Makefile b/Makefile index a38f074..d283c7b 100644 --- a/Makefile +++ b/Makefile @@ -77,6 +77,7 @@ PKGCONFIG_LIBS="blkid uuid liburcu libsodium zlib liblz4 libzstd libudev libkeyu ifdef BCACHEFS_FUSE PKGCONFIG_LIBS+="fuse3 >= 3.7" CFLAGS+=-DBCACHEFS_FUSE + export RUSTFLAGS=--cfg fuse endif PKGCONFIG_CFLAGS:=$(shell $(PKG_CONFIG) --cflags $(PKGCONFIG_LIBS)) @@ -172,25 +173,15 @@ OBJS:=$(SRCS:.c=.o) $(Q)$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< BCACHEFS_DEPS=libbcachefs.a +RUST_SRCS:=$(shell find rust-src/src rust-src/bch_bindgen/src -type f -iname '*.rs') -ifndef NO_RUST - BCACHEFS_DEPS+=rust-src/target/release/libbcachefs_rust.a -else - CFLAGS+=-DBCACHEFS_NO_RUST -endif - -bcachefs: $(BCACHEFS_DEPS) - @echo " [LD] $@" - $(Q)$(CC) $(LDFLAGS) -Wl,--whole-archive $+ $(LOADLIBES) -Wl,--no-whole-archive $(LDLIBS) -o $@ +bcachefs: $(BCACHEFS_DEPS) $(RUST_SRCS) + $(CARGO_BUILD) libbcachefs.a: $(filter-out ./tests/%.o, $(OBJS)) @echo " [AR] $@" $(Q)ar -rc $@ $+ -RUST_SRCS:=$(shell find rust-src/src rust-src/bch_bindgen/src -type f -iname '*.rs') -rust-src/target/release/libbcachefs_rust.a: $(RUST_SRCS) - $(CARGO_BUILD) - tests/test_helper: $(filter ./tests/%.o, $(OBJS)) @echo " [LD] $@" $(Q)$(CC) $(LDFLAGS) $+ $(LOADLIBES) $(LDLIBS) -o $@ @@ -210,7 +201,7 @@ cmd_version.o : .version install: INITRAMFS_HOOK=$(INITRAMFS_DIR)/hooks/bcachefs install: INITRAMFS_SCRIPT=$(INITRAMFS_DIR)/scripts/local-premount/bcachefs install: bcachefs $(optional_install) - $(INSTALL) -m0755 -D bcachefs -t $(DESTDIR)$(ROOT_SBINDIR) + $(INSTALL) -m0755 -D rust-src/target/release/bcachefs -t $(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) @@ -233,7 +224,7 @@ install_systemd: $(systemd_services) $(systemd_libexecfiles) .PHONY: clean clean: @echo "Cleaning all" - $(Q)$(RM) bcachefs libbcachefs.a tests/test_helper .version *.tar.xz $(OBJS) $(DEPS) $(DOCGENERATED) + $(Q)$(RM) libbcachefs.a tests/test_helper .version *.tar.xz $(OBJS) $(DEPS) $(DOCGENERATED) $(Q)$(CARGO_CLEAN) $(Q)$(RM) -f $(built_scripts) diff --git a/bcachefs.c b/bcachefs.c index 7ca79ad..70af2c3 100644 --- a/bcachefs.c +++ b/bcachefs.c @@ -25,7 +25,7 @@ #include "cmds.h" -static void usage(void) +void bcachefs_usage(void) { puts("bcachefs - tool for managing bcachefs filesystems\n" "usage: bcachefs <command> [<args>]\n" @@ -36,11 +36,9 @@ static void usage(void) " set-option Set a filesystem option\n" " reset-counters Reset all counters on an unmounted device\n" "\n" -#ifndef BCACHEFS_NO_RUST "Mount:\n" " mount Mount a filesystem\n" "\n" -#endif "Repair:\n" " fsck Check an existing filesystem for errors\n" "\n" @@ -89,18 +87,14 @@ static void usage(void) "Debug:\n" "These commands work on offline, unmounted filesystems\n" " dump Dump filesystem metadata to a qcow2 image\n" -#ifndef BCACHEFS_NO_RUST " list List filesystem metadata in textual form\n" -#endif " list_journal List contents of journal\n" "\n" "FUSE:\n" " fusemount Mount a filesystem via FUSE\n" "\n" "Miscellaneous:\n" -#ifndef BCACHEFS_NO_RUST " completions Generate shell completions\n" -#endif " version Display the version of the invoked bcachefs tool\n"); } @@ -115,12 +109,12 @@ static char *pop_cmd(int *argc, char *argv[]) return cmd; } -static int fs_cmds(int argc, char *argv[]) +int fs_cmds(int argc, char *argv[]) { char *cmd = pop_cmd(&argc, argv); if (argc < 1) { - usage(); + bcachefs_usage(); exit(EXIT_FAILURE); } if (!strcmp(cmd, "usage")) @@ -129,7 +123,7 @@ static int fs_cmds(int argc, char *argv[]) return 0; } -static int device_cmds(int argc, char *argv[]) +int device_cmds(int argc, char *argv[]) { char *cmd = pop_cmd(&argc, argv); @@ -155,7 +149,7 @@ static int device_cmds(int argc, char *argv[]) return 0; } -static int data_cmds(int argc, char *argv[]) +int data_cmds(int argc, char *argv[]) { char *cmd = pop_cmd(&argc, argv); @@ -169,7 +163,7 @@ static int data_cmds(int argc, char *argv[]) return 0; } -static int subvolume_cmds(int argc, char *argv[]) +int subvolume_cmds(int argc, char *argv[]) { char *cmd = pop_cmd(&argc, argv); if (argc < 1) @@ -183,112 +177,3 @@ static int subvolume_cmds(int argc, char *argv[]) return 0; } - -int main(int argc, char *argv[]) -{ - raid_init(); - - char *full_cmd = argv[0]; - - /* Are we being called via a symlink? */ - - if (strstr(full_cmd, "mkfs")) - return cmd_format(argc, argv); - - if (strstr(full_cmd, "fsck")) - return cmd_fsck(argc, argv); - -#ifdef BCACHEFS_FUSE - if (strstr(full_cmd, "mount.fuse")) - return cmd_fusemount(argc, argv); -#endif - -#ifndef BCACHEFS_NO_RUST - if (strstr(full_cmd, "mount")) - return rust_main(argc, argv, "mount"); -#endif - - setvbuf(stdout, NULL, _IOLBF, 0); - - char *cmd = pop_cmd(&argc, argv); - if (!cmd) { - puts("missing command\n"); - goto usage; - } - - /* these subcommands display usage when argc < 2 */ - if (!strcmp(cmd, "device")) - return device_cmds(argc, argv); - if (!strcmp(cmd, "fs")) - return fs_cmds(argc, argv); - if (!strcmp(cmd, "data")) - return data_cmds(argc, argv); - if (!strcmp(cmd, "subvolume")) - return subvolume_cmds(argc, argv); - if (!strcmp(cmd, "format")) - return cmd_format(argc, argv); - if (!strcmp(cmd, "fsck")) - return cmd_fsck(argc, argv); - if (!strcmp(cmd, "version")) - return cmd_version(argc, argv); - if (!strcmp(cmd, "show-super")) - return cmd_show_super(argc, argv); - if (!strcmp(cmd, "set-option")) - return cmd_set_option(argc, argv); - if (!strcmp(cmd, "reset-counters")) - return cmd_reset_counters(argc, argv); - -#if 0 - if (!strcmp(cmd, "assemble")) - return cmd_assemble(argc, argv); - if (!strcmp(cmd, "incremental")) - return cmd_incremental(argc, argv); - if (!strcmp(cmd, "run")) - return cmd_run(argc, argv); - if (!strcmp(cmd, "stop")) - return cmd_stop(argc, argv); -#endif - - if (!strcmp(cmd, "unlock")) - return cmd_unlock(argc, argv); - if (!strcmp(cmd, "set-passphrase")) - return cmd_set_passphrase(argc, argv); - if (!strcmp(cmd, "remove-passphrase")) - return cmd_remove_passphrase(argc, argv); - - if (!strcmp(cmd, "migrate")) - return cmd_migrate(argc, argv); - if (!strcmp(cmd, "migrate-superblock")) - return cmd_migrate_superblock(argc, argv); - - if (!strcmp(cmd, "dump")) - return cmd_dump(argc, argv); - if (!strcmp(cmd, "list_journal")) - return cmd_list_journal(argc, argv); - if (!strcmp(cmd, "kill_btree_node")) - return cmd_kill_btree_node(argc, argv); - - if (!strcmp(cmd, "setattr")) - return cmd_setattr(argc, argv); -#ifndef BCACHEFS_NO_RUST - if (!strcmp(cmd, "list") || - !strcmp(cmd, "mount") || - !strcmp(cmd, "completions")) - return rust_main(argc, argv, cmd); -#endif - -#ifdef BCACHEFS_FUSE - if (!strcmp(cmd, "fusemount")) - return cmd_fusemount(argc, argv); -#endif - - if (!strcmp(cmd, "--help")) { - usage(); - return 0; - } - - printf("Unknown command %s\n", cmd); -usage: - usage(); - exit(EXIT_FAILURE); -} diff --git a/cmds.h b/cmds.h index 88b03f3..64267dc 100644 --- a/cmds.h +++ b/cmds.h @@ -54,6 +54,10 @@ int cmd_subvolume_snapshot(int argc, char *argv[]); int cmd_fusemount(int argc, char *argv[]); -int rust_main(int argc, char *argv[], char *cmd); +void bcachefs_usage(void); +int device_cmds(int argc, char *argv[]); +int fs_cmds(int argc, char *argv[]); +int data_cmds(int argc, char *argv[]); +int subvolume_cmds(int argc, char *argv[]); #endif /* _CMDS_H */ diff --git a/rust-src/Cargo.lock b/rust-src/Cargo.lock index 091f760..62eaa3d 100644 --- a/rust-src/Cargo.lock +++ b/rust-src/Cargo.lock @@ -84,7 +84,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "bcachefs-rust" +name = "bcachefs-tools" version = "0.3.1" dependencies = [ "anyhow", diff --git a/rust-src/Cargo.toml b/rust-src/Cargo.toml index 84107d4..1e96f85 100644 --- a/rust-src/Cargo.toml +++ b/rust-src/Cargo.toml @@ -1,12 +1,16 @@ [package] -name = "bcachefs-rust" +name = "bcachefs-tools" version = "0.3.1" authors = ["Yuxuan Shui <yshuiv7@gmail.com>", "Kayla Firestack <dev@kaylafire.me>", "Kent Overstreet <kent.overstreet@linux.dev>" ] edition = "2021" rust-version = "1.65" +[[bin]] +name = "bcachefs" +path = "src/bcachefs.rs" + [lib] -crate-type = ["staticlib"] +name = "bcachefs" [dependencies] atty = "0.2.14" diff --git a/rust-src/bch_bindgen/build.rs b/rust-src/bch_bindgen/build.rs index 79337a0..d9805a8 100644 --- a/rust-src/bch_bindgen/build.rs +++ b/rust-src/bch_bindgen/build.rs @@ -10,6 +10,8 @@ impl bindgen::callbacks::ParseCallbacks for Fix753 { fn main() { use std::path::PathBuf; + println!("cargo:rerun-if-changed=src/libbcachefs_wrapper.h"); + let out_dir: PathBuf = std::env::var_os("OUT_DIR") .expect("ENV Var 'OUT_DIR' Expected") .into(); @@ -44,6 +46,10 @@ fn main() { .default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: true, }) + .allowlist_function("bcachefs_usage") + .allowlist_function("raid_init") + .allowlist_function("cmd_.*") + .allowlist_function(".*_cmds") .allowlist_function(".*bch2_.*") .allowlist_function("bio_.*") .allowlist_function("derive_passphrase") diff --git a/rust-src/bch_bindgen/src/libbcachefs_wrapper.h b/rust-src/bch_bindgen/src/libbcachefs_wrapper.h index e68de66..5fb4261 100644 --- a/rust-src/bch_bindgen/src/libbcachefs_wrapper.h +++ b/rust-src/bch_bindgen/src/libbcachefs_wrapper.h @@ -11,6 +11,8 @@ #include "../crypto.h" #include "../include/linux/bio.h" #include "../include/linux/blkdev.h" +#include "../cmds.h" +#include "../raid/raid.h" #define MARK_FIX_753(req_name) const blk_mode_t Fix753_##req_name = req_name; diff --git a/rust-src/build.rs b/rust-src/build.rs new file mode 100644 index 0000000..e4662bd --- /dev/null +++ b/rust-src/build.rs @@ -0,0 +1,21 @@ +fn main() { + println!("cargo:rustc-link-search=.."); + println!("cargo:rerun-if-changed=../libbcachefs.a"); + println!("cargo:rustc-link-lib=static:+whole-archive=bcachefs"); + + println!("cargo:rustc-link-lib=urcu"); + println!("cargo:rustc-link-lib=zstd"); + println!("cargo:rustc-link-lib=blkid"); + println!("cargo:rustc-link-lib=uuid"); + println!("cargo:rustc-link-lib=sodium"); + println!("cargo:rustc-link-lib=z"); + println!("cargo:rustc-link-lib=lz4"); + println!("cargo:rustc-link-lib=zstd"); + println!("cargo:rustc-link-lib=udev"); + println!("cargo:rustc-link-lib=keyutils"); + println!("cargo:rustc-link-lib=aio"); + + if std::env::var("BCACHEFS_FUSE").is_ok() { + println!("cargo:rustc-link-lib=fuse3"); + } +} diff --git a/rust-src/src/bcachefs.rs b/rust-src/src/bcachefs.rs new file mode 100644 index 0000000..ed5fd1d --- /dev/null +++ b/rust-src/src/bcachefs.rs @@ -0,0 +1,107 @@ +use std::ffi::CString; + +use bcachefs::cmd_completions::cmd_completions; +use bcachefs::cmd_list::cmd_list; +use bcachefs::cmd_mount::cmd_mount; +use bcachefs::logger::SimpleLogger; +use bch_bindgen::c; + +fn handle_c_command(args: Vec<String>, symlink_cmd: Option<&str>) -> i32 { + let mut argv: Vec<_> = args.clone(); + + let cmd = match symlink_cmd { + Some(s) => s.to_string(), + None => argv.remove(1), + }; + + let argc: i32 = argv.len().try_into().unwrap(); + + let argv: Vec<_> = argv + .iter() + .map(|s| CString::new(s.as_str()).unwrap()) + .collect(); + let argv: Vec<_> = argv.iter().map(|s| s.as_ptr()).collect(); + let argv = argv.as_ptr() as *mut *mut i8; + + // The C functions will mutate argv. It shouldn't be used after this block. + unsafe { + match cmd.as_str() { + "--help" => { + c::bcachefs_usage(); + 0 + }, + "data" => c::data_cmds(argc, argv), + "device" => c::device_cmds(argc, argv), + "dump" => c::cmd_dump(argc, argv), + "format" => c::cmd_format(argc, argv), + "fs" => c::fs_cmds(argc, argv), + "fsck" => c::cmd_fsck(argc, argv), + "list_journal" => c::cmd_list_journal(argc, argv), + "kill_btree_node" => c::cmd_kill_btree_node(argc, argv), + "migrate" => c::cmd_migrate(argc, argv), + "migrate-superblock" => c::cmd_migrate_superblock(argc, argv), + "mkfs" => c::cmd_format(argc, argv), + "remove-passphrase" => c::cmd_remove_passphrase(argc, argv), + "reset-counters" => c::cmd_reset_counters(argc, argv), + "set-option" => c::cmd_set_option(argc, argv), + "set-passphrase" => c::cmd_set_passphrase(argc, argv), + "setattr" => c::cmd_setattr(argc, argv), + "show-super" => c::cmd_show_super(argc, argv), + "subvolume" => c::subvolume_cmds(argc, argv), + "unlock" => c::cmd_unlock(argc, argv), + "version" => c::cmd_version(argc, argv), + + #[cfg(fuse)] + "fusemount" => c::cmd_fusemount(argc, argv), + + _ => { + println!("Unknown command {}", cmd); + c::bcachefs_usage(); + 1 + } + } + } +} + +fn main() { + let args: Vec<String> = std::env::args().collect(); + + let symlink_cmd: Option<&str> = if args[0].contains("mkfs") { + Some("mkfs") + } else if args[0].contains("fsck") { + Some("fsck") + } else if args[0].contains("mount.fuse") { + Some("fusemount") + } else if args[0].contains("mount") { + Some("mount") + } else { + None + }; + + if symlink_cmd.is_none() && args.len() < 2 { + println!("missing command"); + unsafe { c::bcachefs_usage() }; + std::process::exit(1); + } + + unsafe { c::raid_init() }; + + log::set_boxed_logger(Box::new(SimpleLogger)).unwrap(); + log::set_max_level(log::LevelFilter::Warn); + + let cmd = match symlink_cmd { + Some(s) => s, + None => args[1].as_str(), + }; + + let ret = match cmd { + "completions" => cmd_completions(args[1..].to_vec()), + "list" => cmd_list(args[1..].to_vec()), + "mount" => cmd_mount(args, symlink_cmd), + _ => handle_c_command(args, symlink_cmd), + }; + + if ret != 0 { + std::process::exit(1); + } +} diff --git a/rust-src/src/cmd_completions.rs b/rust-src/src/cmd_completions.rs index 3e839fe..53cdd64 100644 --- a/rust-src/src/cmd_completions.rs +++ b/rust-src/src/cmd_completions.rs @@ -1,6 +1,5 @@ use clap::{Command, CommandFactory, Parser}; use clap_complete::{generate, Generator, Shell}; -use std::ffi::{c_int, OsStr}; use std::io; /// Generate shell completions @@ -13,7 +12,7 @@ fn print_completions<G: Generator>(gen: G, cmd: &mut Command) { generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout()); } -pub fn cmd_completions(argv: Vec<&OsStr>) -> c_int { +pub fn cmd_completions(argv: Vec<String>) -> i32 { let cli = Cli::parse_from(argv); print_completions(cli.shell, &mut super::Cli::command()); 0 diff --git a/rust-src/src/cmd_list.rs b/rust-src/src/cmd_list.rs index fa9c2f2..cb35291 100644 --- a/rust-src/src/cmd_list.rs +++ b/rust-src/src/cmd_list.rs @@ -9,7 +9,6 @@ use bch_bindgen::btree::BtreeIter; use bch_bindgen::btree::BtreeNodeIter; use bch_bindgen::btree::BtreeIterFlags; use clap::{Parser}; -use std::ffi::{c_int, OsStr}; fn list_keys(fs: &Fs, opt: Cli) -> anyhow::Result<()> { let trans = BtreeTrans::new(fs); @@ -157,7 +156,7 @@ fn cmd_list_inner(opt: Cli) -> anyhow::Result<()> { } } -pub fn cmd_list(argv: Vec<&OsStr>) -> c_int { +pub fn cmd_list(argv: Vec<String>) -> i32 { let opt = Cli::parse_from(argv); colored::control::set_override(opt.colorize); if let Err(e) = cmd_list_inner(opt) { diff --git a/rust-src/src/cmd_main.rs b/rust-src/src/cmd_main.rs deleted file mode 100644 index baedc85..0000000 --- a/rust-src/src/cmd_main.rs +++ /dev/null @@ -1,34 +0,0 @@ -use log::{error, LevelFilter}; -use std::ffi::{CStr, c_int, c_char}; -use crate::transform_c_args; -use crate::logger::SimpleLogger; -use crate::cmd_completions::cmd_completions; -use crate::cmd_list::cmd_list; -use crate::cmd_mount::cmd_mount; - -#[no_mangle] -pub extern "C" fn rust_main(argc: c_int, argv: *const *const c_char, cmd: *const c_char) -> c_int { - transform_c_args!(argv, argc, argv); - - log::set_boxed_logger(Box::new(SimpleLogger)).unwrap(); - log::set_max_level(LevelFilter::Warn); - - let cmd: &CStr = unsafe { CStr::from_ptr(cmd) }; - let cmd = match cmd.to_str() { - Ok(c) => c, - Err(e) => { - error!("could not parse command: {}", e); - return 1; - } - }; - - match cmd { - "completions" => cmd_completions(argv), - "list" => cmd_list(argv), - "mount" => cmd_mount(argv), - _ => { - error!("unknown command: {}", cmd); - 1 - } - } -} diff --git a/rust-src/src/cmd_mount.rs b/rust-src/src/cmd_mount.rs index 4f6021c..3b8b01f 100644 --- a/rust-src/src/cmd_mount.rs +++ b/rust-src/src/cmd_mount.rs @@ -6,7 +6,7 @@ use uuid::Uuid; use std::path::PathBuf; use crate::key; use crate::key::KeyLocation; -use std::ffi::{CString, c_int, c_char, c_void, OsStr}; +use std::ffi::{CString, c_char, c_void}; use std::os::unix::ffi::OsStrExt; fn mount_inner( @@ -223,7 +223,14 @@ fn cmd_mount_inner(opt: Cli) -> anyhow::Result<()> { Ok(()) } -pub fn cmd_mount(argv: Vec<&OsStr>) -> c_int { +pub fn cmd_mount(mut argv: Vec<String>, symlink_cmd: Option<&str>) -> i32 { + // If the bcachefs tool is being called as "bcachefs mount dev ..." (as opposed to via a + // symlink like "/usr/sbin/mount.bcachefs dev ...", then we need to pop the 0th argument + // ("bcachefs") since the CLI parser here expects the device at position 1. + if symlink_cmd.is_none() { + argv.remove(0); + } + let opt = Cli::parse_from(argv); // @TODO : more granular log levels via mount option diff --git a/rust-src/src/lib.rs b/rust-src/src/lib.rs index 026cca4..f8b508d 100644 --- a/rust-src/src/lib.rs +++ b/rust-src/src/lib.rs @@ -2,7 +2,6 @@ use clap::Subcommand; pub mod key; pub mod logger; -pub mod cmd_main; pub mod cmd_mount; pub mod cmd_list; pub mod cmd_completions; @@ -32,18 +31,6 @@ macro_rules! c_str { }; } -#[macro_export] -macro_rules! transform_c_args { - ($var:ident, $argc:expr, $argv:expr) => { - // TODO: `OsStr::from_bytes` only exists on *nix - use ::std::os::unix::ffi::OsStrExt; - let $var: Vec<_> = (0..$argc) - .map(|i| unsafe { ::std::ffi::CStr::from_ptr(*$argv.add(i as usize)) }) - .map(|i| ::std::ffi::OsStr::from_bytes(i.to_bytes())) - .collect(); - }; -} - #[derive(Debug)] struct ErrnoError(errno::Errno); impl std::fmt::Display for ErrnoError { diff --git a/tests/util.py b/tests/util.py index 00314f4..3ec38b2 100644 --- a/tests/util.py +++ b/tests/util.py @@ -11,7 +11,7 @@ import time from pathlib import Path BASE_PATH= os.path.dirname(__file__) -BCH_PATH = os.path.abspath(os.path.join(BASE_PATH, '..', 'bcachefs')) +BCH_PATH = os.path.abspath(os.path.join(BASE_PATH, '../rust-src/target/release', 'bcachefs')) VALGRIND_PATH= os.path.abspath(os.path.join(BASE_PATH, 'valgrind-suppressions.txt')) -- 2.43.0 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH TOOLS 2/2] remove library from bcachefs-tools Rust package 2024-01-15 5:24 [PATCH TOOLS 0/2] convert main() from C to Rust Thomas Bertschinger 2024-01-15 5:24 ` [PATCH TOOLS 1/2] " Thomas Bertschinger @ 2024-01-15 5:24 ` Thomas Bertschinger 2024-01-15 17:32 ` [PATCH TOOLS 0/2] convert main() from C to Rust Kent Overstreet 2024-01-15 17:48 ` [PATCH TOOLS 0/2] convert main() from C to Rust Kent Overstreet 3 siblings, 0 replies; 16+ messages in thread From: Thomas Bertschinger @ 2024-01-15 5:24 UTC (permalink / raw) To: kent.overstreet, linux-bcachefs, bfoster; +Cc: Thomas Bertschinger When bcachefs was a C program that had some functions implemented in Rust, it was necessary to make a static library containing the Rust functions for the C program to link. Now that bcachefs is a Rust program, that library is no longer needed. Instead, the Rust executable links in libbachefs.a. This patch updates the crate structure to reflect that. The command functions are moved into their own module. There could be a need to create a "libbachefs-tools" library in the future that exposes an API for bcachefs functionality to other userspace programs. That will be a different, external API as opposed to the previous library functions which were an internal API for the bcachefs tool itself. Signed-off-by: Thomas Bertschinger <tahbertschinger@gmail.com> --- rust-src/Cargo.toml | 3 --- rust-src/src/bcachefs.rs | 21 +++++++++++++++---- .../src/{ => commands}/cmd_completions.rs | 0 rust-src/src/{ => commands}/cmd_list.rs | 0 rust-src/src/{ => commands}/cmd_mount.rs | 0 rust-src/src/{ => commands}/logger.rs | 0 rust-src/src/{lib.rs => commands/mod.rs} | 10 --------- 7 files changed, 17 insertions(+), 17 deletions(-) rename rust-src/src/{ => commands}/cmd_completions.rs (100%) rename rust-src/src/{ => commands}/cmd_list.rs (100%) rename rust-src/src/{ => commands}/cmd_mount.rs (100%) rename rust-src/src/{ => commands}/logger.rs (100%) rename rust-src/src/{lib.rs => commands/mod.rs} (72%) diff --git a/rust-src/Cargo.toml b/rust-src/Cargo.toml index 1e96f85..f50523b 100644 --- a/rust-src/Cargo.toml +++ b/rust-src/Cargo.toml @@ -9,9 +9,6 @@ rust-version = "1.65" name = "bcachefs" path = "src/bcachefs.rs" -[lib] -name = "bcachefs" - [dependencies] atty = "0.2.14" log = { version = "0.4", features = ["std"] } diff --git a/rust-src/src/bcachefs.rs b/rust-src/src/bcachefs.rs index ed5fd1d..0a76a92 100644 --- a/rust-src/src/bcachefs.rs +++ b/rust-src/src/bcachefs.rs @@ -1,11 +1,24 @@ +mod commands; +mod key; + use std::ffi::CString; -use bcachefs::cmd_completions::cmd_completions; -use bcachefs::cmd_list::cmd_list; -use bcachefs::cmd_mount::cmd_mount; -use bcachefs::logger::SimpleLogger; +use commands::cmd_completions::cmd_completions; +use commands::cmd_list::cmd_list; +use commands::cmd_mount::cmd_mount; +use commands::logger::SimpleLogger; use bch_bindgen::c; +#[derive(Debug)] +pub struct ErrnoError(pub 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 {} + fn handle_c_command(args: Vec<String>, symlink_cmd: Option<&str>) -> i32 { let mut argv: Vec<_> = args.clone(); diff --git a/rust-src/src/cmd_completions.rs b/rust-src/src/commands/cmd_completions.rs similarity index 100% rename from rust-src/src/cmd_completions.rs rename to rust-src/src/commands/cmd_completions.rs diff --git a/rust-src/src/cmd_list.rs b/rust-src/src/commands/cmd_list.rs similarity index 100% rename from rust-src/src/cmd_list.rs rename to rust-src/src/commands/cmd_list.rs diff --git a/rust-src/src/cmd_mount.rs b/rust-src/src/commands/cmd_mount.rs similarity index 100% rename from rust-src/src/cmd_mount.rs rename to rust-src/src/commands/cmd_mount.rs diff --git a/rust-src/src/logger.rs b/rust-src/src/commands/logger.rs similarity index 100% rename from rust-src/src/logger.rs rename to rust-src/src/commands/logger.rs diff --git a/rust-src/src/lib.rs b/rust-src/src/commands/mod.rs similarity index 72% rename from rust-src/src/lib.rs rename to rust-src/src/commands/mod.rs index f8b508d..e05a084 100644 --- a/rust-src/src/lib.rs +++ b/rust-src/src/commands/mod.rs @@ -1,6 +1,5 @@ use clap::Subcommand; -pub mod key; pub mod logger; pub mod cmd_mount; pub mod cmd_list; @@ -30,12 +29,3 @@ macro_rules! c_str { } }; } - -#[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 {} -- 2.43.0 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH TOOLS 0/2] convert main() from C to Rust 2024-01-15 5:24 [PATCH TOOLS 0/2] convert main() from C to Rust Thomas Bertschinger 2024-01-15 5:24 ` [PATCH TOOLS 1/2] " Thomas Bertschinger 2024-01-15 5:24 ` [PATCH TOOLS 2/2] remove library from bcachefs-tools Rust package Thomas Bertschinger @ 2024-01-15 17:32 ` Kent Overstreet 2024-01-15 17:55 ` Thomas Bertschinger 2024-01-15 17:48 ` [PATCH TOOLS 0/2] convert main() from C to Rust Kent Overstreet 3 siblings, 1 reply; 16+ messages in thread From: Kent Overstreet @ 2024-01-15 17:32 UTC (permalink / raw) To: Thomas Bertschinger; +Cc: linux-bcachefs, bfoster On Sun, Jan 14, 2024 at 10:24:49PM -0700, Thomas Bertschinger wrote: > This converts the top-level main() of the bcachefs command line tool > from C to Rust. > > The first patch does all the work of implementing a new main(), updating > the build process, and making things look for the executable in its new > location which is now rust-src/target/release/bcachefs. > > The second patch removes the Library crate from the Rust package. This > was needed previously to allow the C program to link in the Rust > functions, but that doesn't happen anymore. > > It seems likely that a bcachefs-tools library will be needed in the > future; for example see this request [1] on GitHub. This library will > have a different external-focused API as opposed to the previous library > that provided an internal API for the bcachefs tool itself. Considering > Rust's structure of one library crate per package, removing the current > library makes room for that requested library. > > I tried to update everything to account for the new location of the > bcachefs executable, but let me know if anyone sees something I missed. > I could not figure out how this is set for the Debian package so it's > possible I missed something there... > > [1] https://github.com/koverstreet/bcachefs-tools/issues/188 Nice work :) I think there's some more rearraging we could do - it'd be cleaner if we moved the C sources out of the top level directory, perhaps to c_src, and then moved everything in rust-src/ to the toplevel. Top level directory is getting a bit crowded, and then the new bcachefs binary won't be quite so out of the way; I frequently build and run without installing when I'm debugging. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH TOOLS 0/2] convert main() from C to Rust 2024-01-15 17:32 ` [PATCH TOOLS 0/2] convert main() from C to Rust Kent Overstreet @ 2024-01-15 17:55 ` Thomas Bertschinger 2024-01-15 18:45 ` Kent Overstreet 0 siblings, 1 reply; 16+ messages in thread From: Thomas Bertschinger @ 2024-01-15 17:55 UTC (permalink / raw) To: Kent Overstreet; +Cc: linux-bcachefs, bfoster On Mon, Jan 15, 2024 at 12:32:23PM -0500, Kent Overstreet wrote: > I think there's some more rearraging we could do - it'd be cleaner if we > moved the C sources out of the top level directory, perhaps to c_src, > and then moved everything in rust-src/ to the toplevel. > > Top level directory is getting a bit crowded, and then the new bcachefs > binary won't be quite so out of the way; I frequently build and run > without installing when I'm debugging. I agree this makes sense. I'll work on a v2 that does this. Thoughts on keeping Make as the top-level build tool vs. using Cargo? I am still fairly new to Rust/Cargo so I'm not sure how easy it would be to replicate all of the Makefile targets in Cargo. It does appear to be possible to use Cargo to invoke gcc to build the C library. I can see three approaches: - keep Make as the primary build tool - hybrid: use Cargo for source code building but keep the Makefile around for the supplemental targets like "make doc", "make rpm", etc. - use Cargo for everything (not sure if this is easy / possible, but I could look into it if desired) I'm leaning towards keeping Make as the primary build tool for now and perhaps a future patch can move towards using Cargo. But other opinions are welcome. - Thomas Bertschinger ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH TOOLS 0/2] convert main() from C to Rust 2024-01-15 17:55 ` Thomas Bertschinger @ 2024-01-15 18:45 ` Kent Overstreet 2024-01-15 19:10 ` Thomas Bertschinger 0 siblings, 1 reply; 16+ messages in thread From: Kent Overstreet @ 2024-01-15 18:45 UTC (permalink / raw) To: Thomas Bertschinger; +Cc: linux-bcachefs, bfoster On Mon, Jan 15, 2024 at 10:55:09AM -0700, Thomas Bertschinger wrote: > On Mon, Jan 15, 2024 at 12:32:23PM -0500, Kent Overstreet wrote: > > I think there's some more rearraging we could do - it'd be cleaner if we > > moved the C sources out of the top level directory, perhaps to c_src, > > and then moved everything in rust-src/ to the toplevel. > > > > Top level directory is getting a bit crowded, and then the new bcachefs > > binary won't be quite so out of the way; I frequently build and run > > without installing when I'm debugging. > > I agree this makes sense. I'll work on a v2 that does this. > > Thoughts on keeping Make as the top-level build tool vs. using Cargo? I > am still fairly new to Rust/Cargo so I'm not sure how easy it would be > to replicate all of the Makefile targets in Cargo. It does appear to be > possible to use Cargo to invoke gcc to build the C library. # > I can see three approaches: > - keep Make as the primary build tool > - hybrid: use Cargo for source code building but keep the Makefile around > for the supplemental targets like "make doc", "make rpm", etc. > - use Cargo for everything (not sure if this is easy / possible, but I > could look into it if desired) > > I'm leaning towards keeping Make as the primary build tool for now and > perhaps a future patch can move towards using Cargo. But other opinions > are welcome. Let's hold off on that for now. I'm planning on pullling the Rust code in bch_bindgen into the kernel soon, and I want to see how that interacts with the kernel build system. If there's potential for commonality with the kernel build system, we might want to take advantage of that. (Also, if you're interested in that project, let me know! Getting that ready for the next merge window is a high priority item, but I have a lot of high priority items at the moment... :) ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH TOOLS 0/2] convert main() from C to Rust 2024-01-15 18:45 ` Kent Overstreet @ 2024-01-15 19:10 ` Thomas Bertschinger 2024-01-15 19:22 ` Kent Overstreet 0 siblings, 1 reply; 16+ messages in thread From: Thomas Bertschinger @ 2024-01-15 19:10 UTC (permalink / raw) To: Kent Overstreet; +Cc: linux-bcachefs, bfoster On Mon, Jan 15, 2024 at 01:45:22PM -0500, Kent Overstreet wrote: > On Mon, Jan 15, 2024 at 10:55:09AM -0700, Thomas Bertschinger wrote: > > I'm leaning towards keeping Make as the primary build tool for now and > > perhaps a future patch can move towards using Cargo. But other opinions > > are welcome. > > Let's hold off on that for now. > > I'm planning on pullling the Rust code in bch_bindgen into the kernel > soon, and I want to see how that interacts with the kernel build system. > If there's potential for commonality with the kernel build system, we > might want to take advantage of that. > > (Also, if you're interested in that project, let me know! Getting that > ready for the next merge window is a high priority item, but I have a > lot of high priority items at the moment... :) Sure, I would definitely be interested in helping out with that. I don't want to make any promises regarding timeline, since this combines two worlds--Rust, and Linux Kernel development--that are both still very new to me. But I can certainly give it an honest try :) as long as you, or someone else experienced, can help with any questions I might run into. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH TOOLS 0/2] convert main() from C to Rust 2024-01-15 19:10 ` Thomas Bertschinger @ 2024-01-15 19:22 ` Kent Overstreet 2024-01-19 19:05 ` Trevor Gross 0 siblings, 1 reply; 16+ messages in thread From: Kent Overstreet @ 2024-01-15 19:22 UTC (permalink / raw) To: Thomas Bertschinger; +Cc: linux-bcachefs, bfoster, Miguel Ojeda, rust-for-linux On Mon, Jan 15, 2024 at 12:10:22PM -0700, Thomas Bertschinger wrote: > On Mon, Jan 15, 2024 at 01:45:22PM -0500, Kent Overstreet wrote: > > On Mon, Jan 15, 2024 at 10:55:09AM -0700, Thomas Bertschinger wrote: > > > I'm leaning towards keeping Make as the primary build tool for now and > > > perhaps a future patch can move towards using Cargo. But other opinions > > > are welcome. > > > > Let's hold off on that for now. > > > > I'm planning on pullling the Rust code in bch_bindgen into the kernel > > soon, and I want to see how that interacts with the kernel build system. > > If there's potential for commonality with the kernel build system, we > > might want to take advantage of that. > > > > (Also, if you're interested in that project, let me know! Getting that > > ready for the next merge window is a high priority item, but I have a > > lot of high priority items at the moment... :) > > Sure, I would definitely be interested in helping out with that. I don't > want to make any promises regarding timeline, since this combines two > worlds--Rust, and Linux Kernel development--that are both still very > new to me. But I can certainly give it an honest try :) as long as you, > or someone else experienced, can help with any questions I might run > into. Of course :) Join the IRC channel - irc.oftc.net#bcache, we've got quite a few people acquainted with Rust there. The main thing that needs to be sorted out is that we require a patched version of bindgen (since rustc can't yet handle types that are both packed and aligned); we need to talk to the rust-for-linux people about whether they'll be ok with switching the kernel to the patched bindgen until we can get a proper fix into rustc. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH TOOLS 0/2] convert main() from C to Rust 2024-01-15 19:22 ` Kent Overstreet @ 2024-01-19 19:05 ` Trevor Gross 2024-01-19 21:37 ` Kent Overstreet 0 siblings, 1 reply; 16+ messages in thread From: Trevor Gross @ 2024-01-19 19:05 UTC (permalink / raw) To: Kent Overstreet Cc: Thomas Bertschinger, linux-bcachefs, bfoster, Miguel Ojeda, rust-for-linux, Benno Lossin On Mon, Jan 15, 2024 at 1:22 PM Kent Overstreet <kent.overstreet@linux.dev> wrote: > > On Mon, Jan 15, 2024 at 12:10:22PM -0700, Thomas Bertschinger wrote: > > On Mon, Jan 15, 2024 at 01:45:22PM -0500, Kent Overstreet wrote: > > > On Mon, Jan 15, 2024 at 10:55:09AM -0700, Thomas Bertschinger wrote: > > > > I'm leaning towards keeping Make as the primary build tool for now and > > > > perhaps a future patch can move towards using Cargo. But other opinions > > > > are welcome. > > > > > > Let's hold off on that for now. > > > > > > I'm planning on pullling the Rust code in bch_bindgen into the kernel > > > soon, and I want to see how that interacts with the kernel build system. > > > If there's potential for commonality with the kernel build system, we > > > might want to take advantage of that. > > > > > > (Also, if you're interested in that project, let me know! Getting that > > > ready for the next merge window is a high priority item, but I have a > > > lot of high priority items at the moment... :) > > > > Sure, I would definitely be interested in helping out with that. I don't > > want to make any promises regarding timeline, since this combines two > > worlds--Rust, and Linux Kernel development--that are both still very > > new to me. But I can certainly give it an honest try :) as long as you, > > or someone else experienced, can help with any questions I might run > > into. > > Of course :) > > Join the IRC channel - irc.oftc.net#bcache, we've got quite a few people > acquainted with Rust there. > > The main thing that needs to be sorted out is that we require a patched > version of bindgen (since rustc can't yet handle types that are both > packed and aligned); we need to talk to the rust-for-linux people about > whether they'll be ok with switching the kernel to the patched bindgen > until we can get a proper fix into rustc. > Do you have a link to the patches needed? We are wondering if this is something that could be upstreamed. Miguel found [1] which seems to be the rustc fix but I am curious how you work around this. [1]: https://github.com/rust-lang/rust/issues/59154#issuecomment-1899563996 ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH TOOLS 0/2] convert main() from C to Rust 2024-01-19 19:05 ` Trevor Gross @ 2024-01-19 21:37 ` Kent Overstreet 2024-01-21 16:11 ` packed, aligned structs in bcachefs (was: Re: [PATCH TOOLS 0/2] convert main() from C to Rust) Thomas Bertschinger 0 siblings, 1 reply; 16+ messages in thread From: Kent Overstreet @ 2024-01-19 21:37 UTC (permalink / raw) To: Trevor Gross Cc: Thomas Bertschinger, linux-bcachefs, bfoster, Miguel Ojeda, rust-for-linux, Benno Lossin On Fri, Jan 19, 2024 at 01:05:17PM -0600, Trevor Gross wrote: > On Mon, Jan 15, 2024 at 1:22 PM Kent Overstreet > <kent.overstreet@linux.dev> wrote: > > > > On Mon, Jan 15, 2024 at 12:10:22PM -0700, Thomas Bertschinger wrote: > > > On Mon, Jan 15, 2024 at 01:45:22PM -0500, Kent Overstreet wrote: > > > > On Mon, Jan 15, 2024 at 10:55:09AM -0700, Thomas Bertschinger wrote: > > > > > I'm leaning towards keeping Make as the primary build tool for now and > > > > > perhaps a future patch can move towards using Cargo. But other opinions > > > > > are welcome. > > > > > > > > Let's hold off on that for now. > > > > > > > > I'm planning on pullling the Rust code in bch_bindgen into the kernel > > > > soon, and I want to see how that interacts with the kernel build system. > > > > If there's potential for commonality with the kernel build system, we > > > > might want to take advantage of that. > > > > > > > > (Also, if you're interested in that project, let me know! Getting that > > > > ready for the next merge window is a high priority item, but I have a > > > > lot of high priority items at the moment... :) > > > > > > Sure, I would definitely be interested in helping out with that. I don't > > > want to make any promises regarding timeline, since this combines two > > > worlds--Rust, and Linux Kernel development--that are both still very > > > new to me. But I can certainly give it an honest try :) as long as you, > > > or someone else experienced, can help with any questions I might run > > > into. > > > > Of course :) > > > > Join the IRC channel - irc.oftc.net#bcache, we've got quite a few people > > acquainted with Rust there. > > > > The main thing that needs to be sorted out is that we require a patched > > version of bindgen (since rustc can't yet handle types that are both > > packed and aligned); we need to talk to the rust-for-linux people about > > whether they'll be ok with switching the kernel to the patched bindgen > > until we can get a proper fix into rustc. > > > > Do you have a link to the patches needed? We are wondering if this is > something that could be upstreamed. The current workaround is to just drop #[repr(align)] if the type is both packed and aligned - but that leads to rustc and gcc disagreeing on the memory layout of certain types; it only works as long as we're not mutating types where this matters from rust code. ^ permalink raw reply [flat|nested] 16+ messages in thread
* packed, aligned structs in bcachefs (was: Re: [PATCH TOOLS 0/2] convert main() from C to Rust) 2024-01-19 21:37 ` Kent Overstreet @ 2024-01-21 16:11 ` Thomas Bertschinger 2024-01-21 18:19 ` Kent Overstreet 0 siblings, 1 reply; 16+ messages in thread From: Thomas Bertschinger @ 2024-01-21 16:11 UTC (permalink / raw) To: Kent Overstreet Cc: Trevor Gross, linux-bcachefs, bfoster, Miguel Ojeda, rust-for-linux, Benno Lossin On Fri, Jan 19, 2024 at 04:37:51PM -0500, Kent Overstreet wrote: > On Fri, Jan 19, 2024 at 01:05:17PM -0600, Trevor Gross wrote: > > On Mon, Jan 15, 2024 at 1:22 PM Kent Overstreet > > <kent.overstreet@linux.dev> wrote: > > > The main thing that needs to be sorted out is that we require a patched > > > version of bindgen (since rustc can't yet handle types that are both > > > packed and aligned); we need to talk to the rust-for-linux people about > > > whether they'll be ok with switching the kernel to the patched bindgen > > > until we can get a proper fix into rustc. > > > > Do you have a link to the patches needed? We are wondering if this is > > something that could be upstreamed. > > The current workaround is to just drop #[repr(align)] if the type is > both packed and aligned - but that leads to rustc and gcc disagreeing on > the memory layout of certain types; it only works as long as we're not > mutating types where this matters from rust code. I made a script to compare the size and alignment of bcachefs structs in C vs. in Rust generated by the patched, lossy bindgen. All sizes were the same, but the following types had different alignment: bkey (8 in C, 4 in Rust) bch_extent_crc32 (8 in C, 4 in Rust) bch_extent_ptr (8 in C, 1 in Rust) I didn't explicitly compare the offsets of all struct members between Rust and C as I couldn't come up with a trivial way to automate this. However, I think that if the C struct is packed, and the Rust and C structs have the same size, you can conclude that all member offsets are the same. This is because Rust could not increase the offset of a member (or else the Rust size would be larger), it could not decrease the offset of a member (because it's already packed), and it could not reorder the members (because #[repr(C)] prohibits this in Rust). Given the above logic, I think it's safe to conclude that if a struct is packed in C, and the size and alignment are the same between C and Rust, then they are fully ABI compatible. Does anyone see an error in my reasoning? Taking a look at struct bkey, the alignment difference arises from a semantic difference between gcc's __attribute__((packed, aligned(N))) and rustc's #[repr(C, packed(N))]. In Rust, > By specifying this attribute, the alignment of the struct would be > the smaller of the specified packing and the default alignment of the > struct. [1] We can get the same alignment for struct bkey in Rust by modifying the code from bindgen to use align(8) instead of packed(8). On my x86_64 Linux system, this results in a struct with the same size, alignment, and offsets of all members as the original C struct. This works because the struct happens to be packed already, so the #[repr(packed)] attribute is superfluous. I can't say if this works in any other environment with different architecture, endianness, etc. But introducing an automated test for comparing the size and alignment of bindgen-generated structs could help with validating other environments. In general, given a C structure like struct A { ... } __packed __aligned(N); I think a Rust structure can be created with the same size, alignment, and member offsets in the following cases: (1) #[repr(packed(N))] will have the same layout if N is <= the structure's default alignment and all members with a default alignment >= N naturally have an offset that is a multiple of N. Also, no member can have an alignment attribute as rustc forbids this [2]. (2) #[repr(align(N))] will have the same layout if the structure's size without the packed attribute is equal to the sum of the size of all its members (i.e., it is "naturally packed"), and the structure's default alignment is <= N. (I haven't formally proven these rules and I welcome counterexamples if someone can spot an error in my logic.) An example of a struct that I think the current rustc cannot represent: struct rust_cannot_represent_this { u8 a; u32 b; } __packed __aligned(2); Luckily, I do not think bcachefs has any structs like this. TL;DR -- even without a rustc fix to support packed and aligned structs, I think it is possible in many cases to create Rust structs with the same memory layout, although it may take some manual effort. Perhaps bindgen could be enhanced with awareness of rule (2) from above, to automatically cover more cases than it does now? Of course, a fix to rustc is the best option. It would avoid the need for error-prone manual work. Specifying both #[repr(packed, align(N))] also communicates semantic intent more than specifying just one even when the result is the same. But we could be waiting a while for that fix to arrive. [1] https://rust-lang.github.io/rfcs/1399-repr-pack.html [2] https://github.com/rust-lang/rust/issues/100743 - Thomas Bertschinger ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: packed, aligned structs in bcachefs (was: Re: [PATCH TOOLS 0/2] convert main() from C to Rust) 2024-01-21 16:11 ` packed, aligned structs in bcachefs (was: Re: [PATCH TOOLS 0/2] convert main() from C to Rust) Thomas Bertschinger @ 2024-01-21 18:19 ` Kent Overstreet 2024-01-21 19:32 ` Thomas Bertschinger 0 siblings, 1 reply; 16+ messages in thread From: Kent Overstreet @ 2024-01-21 18:19 UTC (permalink / raw) To: Thomas Bertschinger Cc: Trevor Gross, linux-bcachefs, bfoster, Miguel Ojeda, rust-for-linux, Benno Lossin On Sun, Jan 21, 2024 at 09:11:24AM -0700, Thomas Bertschinger wrote: > On Fri, Jan 19, 2024 at 04:37:51PM -0500, Kent Overstreet wrote: > > On Fri, Jan 19, 2024 at 01:05:17PM -0600, Trevor Gross wrote: > > > On Mon, Jan 15, 2024 at 1:22 PM Kent Overstreet > > > <kent.overstreet@linux.dev> wrote: > > > > The main thing that needs to be sorted out is that we require a patched > > > > version of bindgen (since rustc can't yet handle types that are both > > > > packed and aligned); we need to talk to the rust-for-linux people about > > > > whether they'll be ok with switching the kernel to the patched bindgen > > > > until we can get a proper fix into rustc. > > > > > > Do you have a link to the patches needed? We are wondering if this is > > > something that could be upstreamed. > > > > The current workaround is to just drop #[repr(align)] if the type is > > both packed and aligned - but that leads to rustc and gcc disagreeing on > > the memory layout of certain types; it only works as long as we're not > > mutating types where this matters from rust code. > > I made a script to compare the size and alignment of bcachefs structs > in C vs. in Rust generated by the patched, lossy bindgen. All sizes > were the same, but the following types had different alignment: > > bkey (8 in C, 4 in Rust) > bch_extent_crc32 (8 in C, 4 in Rust) > bch_extent_ptr (8 in C, 1 in Rust) That right there is really good news. If we can add that script to the tests in bcachefs-tools, we'll already be in better shape than we were. I wonder if it would be possible to upstream that check into bindgen. Also, note: the differing alignment isn't a correctness issue, but it is definitely a performance issue on arm - the compiler will then have to generate code to handle misaligned loads and stores. > I didn't explicitly compare the offsets of all struct members between > Rust and C as I couldn't come up with a trivial way to automate this. > However, I think that if the C struct is packed, and the Rust and C > structs have the same size, you can conclude that all member offsets > are the same. This is because Rust could not increase the offset of a > member (or else the Rust size would be larger), it could not decrease > the offset of a member (because it's already packed), and it could not > reorder the members (because #[repr(C)] prohibits this in Rust). > > Given the above logic, I think it's safe to conclude that if a struct > is packed in C, and the size and alignment are the same between C and > Rust, then they are fully ABI compatible. Does anyone see an error in > my reasoning? Agreed. It'd be great to have an easy way of checking member offsets as well, like we were discussing on IRC, but I don't think it's necessary. > Taking a look at struct bkey, the alignment difference arises from a > semantic difference between gcc's __attribute__((packed, aligned(N))) > and rustc's #[repr(C, packed(N))]. In Rust, > > > By specifying this attribute, the alignment of the struct would be > > the smaller of the specified packing and the default alignment of the > > struct. [1] > > We can get the same alignment for struct bkey in Rust by modifying > the code from bindgen to use align(8) instead of packed(8). On my > x86_64 Linux system, this results in a struct with the same size, > alignment, and offsets of all members as the original C struct. This > works because the struct happens to be packed already, so the > #[repr(packed)] attribute is superfluous. I can't say if this works in > any other environment with different architecture, endianness, etc. But > introducing an automated test for comparing the size and alignment of > bindgen-generated structs could help with validating other environments. > > > In general, given a C structure like > > struct A { > ... > } __packed __aligned(N); > > I think a Rust structure can be created with the same size, alignment, > and member offsets in the following cases: > > (1) #[repr(packed(N))] will have the same layout if N is <= the > structure's default alignment and all members with a default > alignment >= N naturally have an offset that is a multiple of N. > Also, no member can have an alignment attribute as rustc forbids > this [2]. > > (2) #[repr(align(N))] will have the same layout if the structure's size > without the packed attribute is equal to the sum of the size of all > its members (i.e., it is "naturally packed"), and the structure's > default alignment is <= N. > > (I haven't formally proven these rules and I welcome counterexamples if > someone can spot an error in my logic.) That feels right to me; and if we can get your test committed I won't be too concerned about getting a formal proof :) > An example of a struct that I think the current rustc cannot represent: > > struct rust_cannot_represent_this { > u8 a; > u32 b; > } __packed __aligned(2); > > Luckily, I do not think bcachefs has any structs like this. *nod* > TL;DR -- even without a rustc fix to support packed and aligned structs, > I think it is possible in many cases to create Rust structs with the > same memory layout, although it may take some manual effort. Perhaps > bindgen could be enhanced with awareness of rule (2) from above, to > automatically cover more cases than it does now? > > Of course, a fix to rustc is the best option. It would avoid the need > for error-prone manual work. Specifying both #[repr(packed, align(N))] > also communicates semantic intent more than specifying just one even > when the result is the same. But we could be waiting a while for that > fix to arrive. > > [1] https://rust-lang.github.io/rfcs/1399-repr-pack.html > [2] https://github.com/rust-lang/rust/issues/100743 Thanks for the writeup, this really helps. Agreed that if at all possible we should aim for better support from rustc, and forwarding this to the rustc people might help get this the necessary attention before we dive in with patching bindgen. Another thought - the interface between bindgen and rustc feels a bit fragile here, since the C attributes and Rust reprs don't exactly match. Can we come up with something better? I wonder if we could get support in rustc for specifying the offset of each member directly? Also - you tracked down the difference between microsoft and gcc __attribute__((align)), let me try to recap (and tell me if I get it wrong): with gcc, __align() works like any naturally aligned type, but Microsoft's version overrides __packed on a containing structure. I think there's a strong argument to be made that Microsoft is the weird one here and Rustc should just provide the gcc behaviour when mixing packed and align... ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: packed, aligned structs in bcachefs (was: Re: [PATCH TOOLS 0/2] convert main() from C to Rust) 2024-01-21 18:19 ` Kent Overstreet @ 2024-01-21 19:32 ` Thomas Bertschinger 2024-01-22 2:47 ` Thomas Bertschinger 0 siblings, 1 reply; 16+ messages in thread From: Thomas Bertschinger @ 2024-01-21 19:32 UTC (permalink / raw) To: Kent Overstreet Cc: Trevor Gross, linux-bcachefs, bfoster, Miguel Ojeda, rust-for-linux, Benno Lossin On Sun, Jan 21, 2024 at 01:19:24PM -0500, Kent Overstreet wrote: > On Sun, Jan 21, 2024 at 09:11:24AM -0700, Thomas Bertschinger wrote: > > I made a script to compare the size and alignment of bcachefs structs > > in C vs. in Rust generated by the patched, lossy bindgen. All sizes > > were the same, but the following types had different alignment: > > > That right there is really good news. If we can add that script to the > tests in bcachefs-tools, we'll already be in better shape than we were. > > I wonder if it would be possible to upstream that check into bindgen. I did this with an awk script that grabs the structs from the bcachefs C headers and outputs code in Rust and C to dump the sizes and alignments. I then manually added calls to the generated functions into the bcachefs utility and diffed the output. I'll try to get this into a form more suitable for an automated test, and hopefully submit a patch to bcachefs-tools soon... Enhancing bindgen to do something like this automatically also sounds like a good idea but it will take me longer to figure out how to do that. I think it could also be good to incorporate explicit member offset checks with this. Handling that with an awk script or similar seems challenging to me with how bindgen mangles bitfield names. > Also - you tracked down the difference between microsoft and gcc > __attribute__((align)), let me try to recap (and tell me if I get it > wrong): with gcc, __align() works like any naturally aligned type, but > Microsoft's version overrides __packed on a containing structure. > > I think there's a strong argument to be made that Microsoft is the weird > one here and Rustc should just provide the gcc behaviour when mixing > packed and align... I don't have access to a Microsoft environment to test anything, so everything that I think I know about Microsoft semantics is based on what I've read on various rust-lang threads, like [1][2] With that disclaimer, from what I can tell it seems like Microsoft's semantics are strictly less expressive than gcc's. I think gcc can represent any struct that Microsoft can (adding padding by hand may be necessary in some cases, but it's always possible), whereas there are structures representable in gcc that cannot be represented with the Microsoft semantics as presented in the cited rust-lang threads. (Given that I haven't tested the MS compiler, I could be totally wrong about this.) [1] https://github.com/rust-lang/rust/issues/59154#issuecomment-476408300 [2] https://github.com/rust-lang/rust/issues/33158 - Thomas Bertschinger ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: packed, aligned structs in bcachefs (was: Re: [PATCH TOOLS 0/2] convert main() from C to Rust) 2024-01-21 19:32 ` Thomas Bertschinger @ 2024-01-22 2:47 ` Thomas Bertschinger 0 siblings, 0 replies; 16+ messages in thread From: Thomas Bertschinger @ 2024-01-22 2:47 UTC (permalink / raw) To: Kent Overstreet Cc: Trevor Gross, linux-bcachefs, bfoster, Miguel Ojeda, rust-for-linux, Benno Lossin On Sun, Jan 21, 2024 at 12:32:53PM -0700, Thomas Bertschinger wrote: > On Sun, Jan 21, 2024 at 01:19:24PM -0500, Kent Overstreet wrote: > > On Sun, Jan 21, 2024 at 09:11:24AM -0700, Thomas Bertschinger wrote: > > > I made a script to compare the size and alignment of bcachefs structs > > > in C vs. in Rust generated by the patched, lossy bindgen. All sizes > > > were the same, but the following types had different alignment: > > > > > That right there is really good news. If we can add that script to the > > tests in bcachefs-tools, we'll already be in better shape than we were. > > > > I wonder if it would be possible to upstream that check into bindgen. > > I did this with an awk script that grabs the structs from the bcachefs > C headers and outputs code in Rust and C to dump the sizes and > alignments. I then manually added calls to the generated functions into > the bcachefs utility and diffed the output. I'll try to get this into a > form more suitable for an automated test, and hopefully submit a patch > to bcachefs-tools soon... So after looking into bindgen more... it turns out that bindgen already does this! All you have to do is run "cargo test" from the bcachefs-tools/bch_bindgen/ directory. Reading the documentation helps :) The bindgen tests basically agree with my findings, that struct bkey gets the wrong alignment (4 instead of 8). There's one other problem that follows from this, btree_iter.k (a struct bkey) gets the wrong offset. Manually adjusting struct bkey to use #[repr(align(8))] as described previously fixes both of these problems. The other problem types mentioned earlier, bch_extent_crc32 and bch_extent_ptr, are handled manually outside of bindgen so they don't show up in the bindgen test. However, they can be moved under bindgen control and fixed with the same technique, using align(8) instead of packed(8). This works immediately for bch_extent_crc32 but bch_extent_ptr is a little trickier. It is a member of struct btree_node which has a "packed" attribute so we run into rustc disallowing embedding aligned types within packed types. Luckily, replacing the packed(8) attribute on struct btree_node with align(8) appears unproblematic. Given all that, I think it should be possible to move forward with an unpatched bindgen as long as we're OK with treating a few types specially. We can move all of the above types under bindgen control (so that they are covered by the automatic tests) and post-process the bindgen output, as suggested in this comment [1], and use align(8) instead of packed(8) for these types. What do you think? [1] https://github.com/koverstreet/bcachefs-tools/issues/202#issuecomment-1886962791 - Thomas Bertschinger ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH TOOLS 0/2] convert main() from C to Rust 2024-01-15 5:24 [PATCH TOOLS 0/2] convert main() from C to Rust Thomas Bertschinger ` (2 preceding siblings ...) 2024-01-15 17:32 ` [PATCH TOOLS 0/2] convert main() from C to Rust Kent Overstreet @ 2024-01-15 17:48 ` Kent Overstreet 2024-01-15 17:57 ` Thomas Bertschinger 3 siblings, 1 reply; 16+ messages in thread From: Kent Overstreet @ 2024-01-15 17:48 UTC (permalink / raw) To: Thomas Bertschinger; +Cc: linux-bcachefs, bfoster On Sun, Jan 14, 2024 at 10:24:49PM -0700, Thomas Bertschinger wrote: > This converts the top-level main() of the bcachefs command line tool > from C to Rust. > > The first patch does all the work of implementing a new main(), updating > the build process, and making things look for the executable in its new > location which is now rust-src/target/release/bcachefs. > > The second patch removes the Library crate from the Rust package. This > was needed previously to allow the C program to link in the Rust > functions, but that doesn't happen anymore. > > It seems likely that a bcachefs-tools library will be needed in the > future; for example see this request [1] on GitHub. This library will > have a different external-focused API as opposed to the previous library > that provided an internal API for the bcachefs tool itself. Considering > Rust's structure of one library crate per package, removing the current > library makes room for that requested library. > > I tried to update everything to account for the new location of the > bcachefs executable, but let me know if anyone sees something I missed. > I could not figure out how this is set for the Debian package so it's > possible I missed something there... > > [1] https://github.com/koverstreet/bcachefs-tools/issues/188 We've got a new CI failure; it looks like perhaps the MSRV environment variable isn't being set correctly in the test environment. Can you take a look? https://github.com/koverstreet/bcachefs-tools/actions/runs/7532280737/job/20502558560 ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH TOOLS 0/2] convert main() from C to Rust 2024-01-15 17:48 ` [PATCH TOOLS 0/2] convert main() from C to Rust Kent Overstreet @ 2024-01-15 17:57 ` Thomas Bertschinger 0 siblings, 0 replies; 16+ messages in thread From: Thomas Bertschinger @ 2024-01-15 17:57 UTC (permalink / raw) To: Kent Overstreet; +Cc: linux-bcachefs, bfoster On Mon, Jan 15, 2024 at 12:48:28PM -0500, Kent Overstreet wrote: > We've got a new CI failure; it looks like perhaps the MSRV environment > variable isn't being set correctly in the test environment. Can you take > a look? > > https://github.com/koverstreet/bcachefs-tools/actions/runs/7532280737/job/20502558560 Sure, I'll take a look. - Thomas Bertschinger ^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2024-01-22 2:47 UTC | newest] Thread overview: 16+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-01-15 5:24 [PATCH TOOLS 0/2] convert main() from C to Rust Thomas Bertschinger 2024-01-15 5:24 ` [PATCH TOOLS 1/2] " Thomas Bertschinger 2024-01-15 5:24 ` [PATCH TOOLS 2/2] remove library from bcachefs-tools Rust package Thomas Bertschinger 2024-01-15 17:32 ` [PATCH TOOLS 0/2] convert main() from C to Rust Kent Overstreet 2024-01-15 17:55 ` Thomas Bertschinger 2024-01-15 18:45 ` Kent Overstreet 2024-01-15 19:10 ` Thomas Bertschinger 2024-01-15 19:22 ` Kent Overstreet 2024-01-19 19:05 ` Trevor Gross 2024-01-19 21:37 ` Kent Overstreet 2024-01-21 16:11 ` packed, aligned structs in bcachefs (was: Re: [PATCH TOOLS 0/2] convert main() from C to Rust) Thomas Bertschinger 2024-01-21 18:19 ` Kent Overstreet 2024-01-21 19:32 ` Thomas Bertschinger 2024-01-22 2:47 ` Thomas Bertschinger 2024-01-15 17:48 ` [PATCH TOOLS 0/2] convert main() from C to Rust Kent Overstreet 2024-01-15 17:57 ` Thomas Bertschinger
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox