cmd_show_super: Look up and print names of member devices

bch2_sb_to_text_with_names(): print device paths and models of each
member.

Fixes: https://github.com/koverstreet/bcachefs/issues/801
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2025-08-02 11:54:09 -04:00
parent 4174fe0bae
commit 6704e252ef
7 changed files with 131 additions and 25 deletions

View File

@ -85,6 +85,7 @@ fn main() {
.opaque_type("gc_stripe")
.opaque_type("open_bucket.*")
.opaque_type("replicas_delta_list")
.allowlist_type("sb_names")
.no_copy("btree_trans")
.no_copy("printbuf")
.no_partialeq("bkey")

View File

@ -13,6 +13,7 @@
#include "include/linux/blkdev.h"
#include "cmds.h"
#include "raid/raid.h"
#include "src/rust_to_c.h"
/* Fix753 is a workaround for https://github.com/rust-lang/rust-bindgen/issues/753
* Functional macro are not expanded with bindgen, e.g. ioctl are automatically ignored

View File

@ -21,6 +21,7 @@
#include <uuid/uuid.h>
#include "cmds.h"
#include "cmd_super.h"
#include "tools-util.h"
#include "posix_to_bcachefs.h"
#include "libbcachefs.h"
@ -293,7 +294,7 @@ int cmd_format(int argc, char *argv[])
struct printbuf buf = PRINTBUF;
buf.human_readable_units = true;
bch2_sb_to_text(&buf, sb, false, 1 << BCH_SB_FIELD_members_v2);
bch2_sb_to_text_with_names(&buf, sb, false, 1 << BCH_SB_FIELD_members_v2, -1);
printf("%s", buf.buf);
printbuf_exit(&buf);
}

View File

@ -21,6 +21,7 @@
#include <uuid/uuid.h>
#include "cmds.h"
#include "cmd_super.h"
#include "libbcachefs.h"
#include "libbcachefs/opts.h"
#include "libbcachefs/super-io.h"
@ -28,6 +29,8 @@
#include "libbcachefs/darray.h"
#include "src/rust_to_c.h"
static void show_super_usage(void)
{
puts("bcachefs show-super \n"
@ -42,6 +45,72 @@ static void show_super_usage(void)
exit(EXIT_SUCCESS);
}
static struct sb_name *sb_dev_to_name(sb_names sb_names, unsigned idx)
{
darray_for_each(sb_names, i)
if (i->sb.sb->dev_idx == idx)
return i;
return NULL;
}
static void print_one_member(struct printbuf *out, sb_names sb_names,
struct bch_sb *sb,
struct bch_sb_field_disk_groups *gi,
struct bch_member m, unsigned idx)
{
struct sb_name *name = sb_dev_to_name(sb_names, idx);
prt_printf(out, "Device %u:\t%s\t", idx, name ? name->name : "(not found)");
if (name) {
char *model = fd_to_dev_model(name->sb.bdev->bd_fd);
prt_str(out, model);
free(model);
}
prt_newline(out);
printbuf_indent_add(out, 2);
bch2_member_to_text(out, &m, gi, sb, idx);
printbuf_indent_sub(out, 2);
}
void bch2_sb_to_text_with_names(struct printbuf *out, struct bch_sb *sb,
bool print_layout, unsigned fields, int field_only)
{
CLASS(printbuf, uuid_buf)();
prt_str(&uuid_buf, "UUID=");
pr_uuid(&uuid_buf, sb->user_uuid.b);
sb_names sb_names = {};
bch2_scan_device_sbs(uuid_buf.buf, &sb_names);
if (field_only >= 0) {
struct bch_sb_field *f = bch2_sb_field_get_id(sb, field_only);
if (f)
__bch2_sb_field_to_text(out, sb, f);
} else {
printbuf_tabstop_push(out, 44);
bch2_sb_to_text(out, sb, print_layout,
fields & ~(BIT(BCH_SB_FIELD_members_v1)|
BIT(BCH_SB_FIELD_members_v2)));
struct bch_sb_field_disk_groups *gi = bch2_sb_field_get(sb, disk_groups);
struct bch_sb_field_members_v1 *mi1;
if ((fields & BIT(BCH_SB_FIELD_members_v1)) &&
(mi1 = bch2_sb_field_get(sb, members_v1)))
for (unsigned i = 0; i < sb->nr_devices; i++)
print_one_member(out, sb_names, sb, gi, bch2_members_v1_get(mi1, i), i);
struct bch_sb_field_members_v2 *mi2;
if ((fields & BIT(BCH_SB_FIELD_members_v2)) &&
(mi2 = bch2_sb_field_get(sb, members_v2)))
for (unsigned i = 0; i < sb->nr_devices; i++)
print_one_member(out, sb_names, sb, gi, bch2_members_v2_get(mi2, i), i);
}
}
int cmd_show_super(int argc, char *argv[])
{
static const struct option longopts[] = {
@ -98,32 +167,16 @@ int cmd_show_super(int argc, char *argv[])
if (print_default_fields) {
fields |= bch2_sb_field_get(sb.sb, members_v2)
? 1 << BCH_SB_FIELD_members_v2
: 1 << BCH_SB_FIELD_members_v1;
fields |= 1 << BCH_SB_FIELD_errors;
? BIT(BCH_SB_FIELD_members_v2)
: BIT(BCH_SB_FIELD_members_v1);
fields |= BIT(BCH_SB_FIELD_errors);
}
struct printbuf buf = PRINTBUF;
buf.human_readable_units = true;
if (field_only >= 0) {
struct bch_sb_field *f = bch2_sb_field_get_id(sb.sb, field_only);
if (f)
__bch2_sb_field_to_text(&buf, sb.sb, f);
} else {
printbuf_tabstop_push(&buf, 44);
char *model = fd_to_dev_model(sb.bdev->bd_fd);
prt_str(&buf, "Device:");
prt_tab(&buf);
prt_str(&buf, model);
prt_newline(&buf);
free(model);
bch2_sb_to_text(&buf, sb.sb, print_layout, fields);
}
bch2_sb_to_text_with_names(&buf, sb.sb, print_layout, fields, field_only);
printf("%s", buf.buf);
bch2_free_super(&sb);

8
c_src/cmd_super.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef _TOOLS_CMD_SHOW_SUPER_H
#define _TOOLS_CMD_SHOW_SUPER_H
#include "libbcachefs/super-io.h"
void bch2_sb_to_text_with_names(struct printbuf *, struct bch_sb *, bool, unsigned, int);
#endif /* _TOOLS_CMD_SHOW_SUPER_H */

View File

@ -1,5 +1,5 @@
use std::{
ffi::{CStr, CString, c_char},
ffi::{CStr, CString, c_char, c_int},
collections::HashMap,
env,
path::{Path, PathBuf},
@ -7,7 +7,12 @@ use std::{
};
use anyhow::Result;
use bch_bindgen::{bcachefs, bcachefs::bch_sb_handle, opt_set};
use bch_bindgen::{bcachefs, opt_set};
use bcachefs::{
bch_sb_handle,
sb_name,
sb_names,
};
use bcachefs::bch_opts;
use uuid::Uuid;
use log::debug;
@ -185,13 +190,39 @@ pub fn scan_devices(device: &String, opts: &bch_opts) -> Result<String> {
Ok(joined_device_str(&sbs))
}
#[no_mangle]
pub extern "C" fn bch2_scan_device_sbs(device: *const c_char, ret: *mut sb_names) -> c_int {
let device = unsafe { CStr::from_ptr(device) };
let device = device.to_str().unwrap().to_string();
// how to initialize to default/empty?
let opts = bch_bindgen::opts::parse_mount_opts(None, None, true).unwrap_or_default();
let sbs = scan_sbs(&device, &opts).unwrap();
let mut sbs = sbs.iter()
.map(|(name, sb)| sb_name {
name: CString::new(name.clone().into_os_string().into_string().unwrap()).unwrap().into_raw(),
sb: *sb } )
.collect::<Vec<sb_name>>();
unsafe {
(*ret).data = sbs.as_mut_ptr();
(*ret).nr = sbs.len();
(*ret).size = sbs.capacity();
std::mem::forget(sbs);
}
0
}
#[no_mangle]
pub extern "C" fn bch2_scan_devices(device: *const c_char) -> *mut c_char {
let device = unsafe { CStr::from_ptr(device) };
let device = device.to_str().unwrap().to_string();
let opts = bch_bindgen::opts::parse_mount_opts(None, None, true)
.unwrap_or_default();
// how to initialize to default/empty?
let opts = bch_bindgen::opts::parse_mount_opts(None, None, true).unwrap_or_default();
CString::new(scan_devices(&device, &opts).unwrap()).unwrap().into_raw()
}

View File

@ -1,6 +1,17 @@
#ifndef _BCACHEFS_TOOLS_RUST_TO_C_H
#define _BCACHEFS_TOOLS_RUST_TO_C_H
#include "libbcachefs/super_types.h"
#include "libbcachefs/darray.h"
struct sb_name {
const char *name;
struct bch_sb_handle sb;
};
typedef DARRAY(struct sb_name) sb_names;
int bch2_scan_device_sbs(char *, sb_names *ret);
char *bch2_scan_devices(char *);
#endif /* _BCACHEFS_TOOLS_RUST_TO_C_H */