summaryrefslogtreecommitdiff
path: root/src/commands/debug/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/commands/debug/mod.rs')
-rw-r--r--src/commands/debug/mod.rs108
1 files changed, 108 insertions, 0 deletions
diff --git a/src/commands/debug/mod.rs b/src/commands/debug/mod.rs
new file mode 100644
index 00000000..e5651921
--- /dev/null
+++ b/src/commands/debug/mod.rs
@@ -0,0 +1,108 @@
+use clap::Parser;
+use std::ffi::{c_char, CString};
+use std::io::{BufRead, Write};
+
+use bch_bindgen::bcachefs;
+use bch_bindgen::c;
+use bch_bindgen::fs::Fs;
+
+mod bkey_types;
+mod parser;
+
+use bch_bindgen::c::bpos;
+
+/// Debug a bcachefs filesystem.
+#[derive(Parser, Debug)]
+pub struct Cli {
+ #[arg(required(true))]
+ devices: Vec<std::path::PathBuf>,
+}
+
+#[derive(Debug)]
+enum DebugCommand {
+ Dump(DumpCommand),
+ Update(UpdateCommand),
+}
+
+#[derive(Debug)]
+struct DumpCommand {
+ btree: String,
+ bpos: bpos,
+}
+
+#[derive(Debug)]
+struct UpdateCommand {
+ btree: String,
+ bpos: bpos,
+ bkey: String,
+ field: String,
+ value: u64,
+}
+
+fn update(fs: &Fs, type_list: &bkey_types::BkeyTypes, cmd: UpdateCommand) {
+ let bkey = CString::new(cmd.bkey.clone()).unwrap();
+ let bkey = bkey.as_ptr() as *const c_char;
+
+ let id: bch_bindgen::c::btree_id = cmd.btree.parse().expect("no such btree");
+
+ if let Some((size, offset)) = type_list.get_member_layout(&cmd.bkey, &cmd.field) {
+ let update = c::bkey_update {
+ id,
+ bkey,
+ offset,
+ size,
+ value: cmd.value,
+ };
+ unsafe {
+ c::cmd_update_bkey(fs.raw, update, cmd.bpos);
+ }
+ } else {
+ println!("unknown field '{}'", cmd.field);
+ }
+}
+
+fn dump(fs: &Fs, cmd: DumpCommand) {
+ let id: bch_bindgen::c::btree_id = cmd.btree.parse().expect("no such btree");
+
+ unsafe {
+ c::cmd_dump_bkey(fs.raw, id, cmd.bpos);
+ }
+}
+
+pub fn debug(argv: Vec<String>) -> i32 {
+ fn prompt() {
+ print!("bcachefs> ");
+ std::io::stdout().flush().unwrap();
+ }
+
+ let opt = Cli::parse_from(argv);
+
+ let fs_opts: bcachefs::bch_opts = Default::default();
+ let fs = match Fs::open(&opt.devices, fs_opts) {
+ Ok(fs) => fs,
+ Err(_) => {
+ return 1;
+ }
+ };
+
+ let type_list = bkey_types::get_bkey_type_info();
+
+ prompt();
+ let stdin = std::io::stdin();
+ for line in stdin.lock().lines() {
+ let line = line.unwrap();
+ if let Some(cmd) = parser::parse_command(&line) {
+ match cmd {
+ // usage: dump <btree_type> <bpos>
+ DebugCommand::Dump(cmd) => dump(&fs, cmd),
+ // usage: update <btree_type> <bpos> <bkey_type>.<field>=<value>
+ DebugCommand::Update(cmd) => update(&fs, &type_list, cmd),
+ }
+ } else {
+ println!("failed to parse a command");
+ };
+ prompt();
+ }
+
+ 0
+}