use super::print_super;
use crate::{RunContext, Runnable};
use anyhow::{Context, Result, bail};
use btrfs_disk::superblock::{self, SUPER_MIRROR_MAX};
use clap::Parser;
use std::{fs::File, path::PathBuf};
#[derive(Parser, Debug)]
#[allow(clippy::doc_markdown)]
pub struct DumpSuperCommand {
path: PathBuf,
#[clap(short = 'f', long)]
full: bool,
#[clap(short = 'a', long)]
all: bool,
#[clap(short = 's', long = "super", value_parser = clap::value_parser!(u32).range(..SUPER_MIRROR_MAX as i64))]
mirror: Option<u32>,
#[clap(long, conflicts_with_all = ["mirror", "all"])]
bytenr: Option<u64>,
#[clap(short = 'F', long)]
force: bool,
}
impl Runnable for DumpSuperCommand {
fn run(&self, _ctx: &RunContext) -> Result<()> {
let mut file = File::open(&self.path).with_context(|| {
format!("failed to open '{}'", self.path.display())
})?;
let offsets: Vec<(u64, String)> = if let Some(bytenr) = self.bytenr {
vec![(bytenr, format!("bytenr={bytenr}"))]
} else if self.all {
(0..SUPER_MIRROR_MAX)
.map(|m| {
let off = superblock::super_mirror_offset(m);
(off, format!("bytenr={off}"))
})
.collect()
} else {
let m = self.mirror.unwrap_or(0);
let off = superblock::super_mirror_offset(m);
vec![(off, format!("bytenr={off}"))]
};
for (i, (offset, label)) in offsets.iter().enumerate() {
if i > 0 {
println!();
}
println!("superblock: {label}, device={}", self.path.display());
let sb = match superblock::read_superblock_at(&mut file, *offset) {
Ok(sb) => sb,
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
println!(
"superblock at {label} beyond end of device, skipping"
);
continue;
}
Err(e) => {
return Err(e).with_context(|| {
format!(
"failed to read superblock at {label} from '{}'",
self.path.display()
)
});
}
};
if !sb.magic_is_valid() && !self.force {
if self.all {
println!(
"superblock at {label} has bad magic, skipping (use -F to force)"
);
continue;
}
bail!(
"bad magic on superblock at {label} of '{}' (use -F to force)",
self.path.display()
);
}
println!(
"---------------------------------------------------------"
);
print_super::print_superblock(&sb, self.full);
}
Ok(())
}
}