Skip to main content

btrfs_cli/inspect/
dump_super.rs

1use super::print_super;
2use crate::{RunContext, Runnable};
3use anyhow::{Context, Result, bail};
4use btrfs_disk::superblock::{self, SUPER_MIRROR_MAX};
5use clap::Parser;
6use std::{fs::File, path::PathBuf};
7
8/// Dump the btrfs superblock from a device or image file.
9///
10/// Reads and displays the superblock stored on a btrfs block device or
11/// filesystem image. By default only the primary superblock (mirror 0,
12/// at offset 64 KiB) is printed. Use -a to print all mirrors, or -s to
13/// select a specific one.
14#[derive(Parser, Debug)]
15#[allow(clippy::doc_markdown)]
16pub struct DumpSuperCommand {
17    /// Path to a btrfs block device or image file
18    path: PathBuf,
19
20    /// Print full information including sys_chunk_array and backup roots
21    #[clap(short = 'f', long)]
22    full: bool,
23
24    /// Print all superblock mirrors (0, 1, 2)
25    #[clap(short = 'a', long)]
26    all: bool,
27
28    /// Print only this superblock mirror (0, 1, or 2)
29    #[clap(short = 's', long = "super", value_parser = clap::value_parser!(u32).range(..SUPER_MIRROR_MAX as i64))]
30    mirror: Option<u32>,
31
32    /// Read the superblock from this byte offset instead of using a mirror index
33    #[clap(long, conflicts_with_all = ["mirror", "all"])]
34    bytenr: Option<u64>,
35
36    /// Attempt to print superblocks with bad magic
37    #[clap(short = 'F', long)]
38    force: bool,
39}
40
41impl Runnable for DumpSuperCommand {
42    fn run(&self, _ctx: &RunContext) -> Result<()> {
43        let mut file = File::open(&self.path).with_context(|| {
44            format!("failed to open '{}'", self.path.display())
45        })?;
46
47        // Collect (offset, label) pairs for each superblock to print.
48        let offsets: Vec<(u64, String)> = if let Some(bytenr) = self.bytenr {
49            vec![(bytenr, format!("bytenr={bytenr}"))]
50        } else if self.all {
51            (0..SUPER_MIRROR_MAX)
52                .map(|m| {
53                    let off = superblock::super_mirror_offset(m);
54                    (off, format!("bytenr={off}"))
55                })
56                .collect()
57        } else {
58            let m = self.mirror.unwrap_or(0);
59            let off = superblock::super_mirror_offset(m);
60            vec![(off, format!("bytenr={off}"))]
61        };
62
63        for (i, (offset, label)) in offsets.iter().enumerate() {
64            if i > 0 {
65                println!();
66            }
67
68            println!("superblock: {label}, device={}", self.path.display());
69
70            let sb = match superblock::read_superblock_at(&mut file, *offset) {
71                Ok(sb) => sb,
72                Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
73                    println!(
74                        "superblock at {label} beyond end of device, skipping"
75                    );
76                    continue;
77                }
78                Err(e) => {
79                    return Err(e).with_context(|| {
80                        format!(
81                            "failed to read superblock at {label} from '{}'",
82                            self.path.display()
83                        )
84                    });
85                }
86            };
87
88            if !sb.magic_is_valid() && !self.force {
89                if self.all {
90                    println!(
91                        "superblock at {label} has bad magic, skipping (use -F to force)"
92                    );
93                    continue;
94                }
95                bail!(
96                    "bad magic on superblock at {label} of '{}' (use -F to force)",
97                    self.path.display()
98                );
99            }
100
101            println!(
102                "---------------------------------------------------------"
103            );
104            print_super::print_superblock(&sb, self.full);
105        }
106
107        Ok(())
108    }
109}