btrfs_cli/rescue/
super_recover.rs1use crate::{Format, Runnable, util::is_mounted};
2use anyhow::{Context, Result, bail};
3use btrfs_disk::superblock::{
4 SUPER_MIRROR_MAX, read_superblock_bytes_at, super_mirror_offset,
5 superblock_generation, superblock_is_valid, write_superblock_all_mirrors,
6};
7use clap::Parser;
8use std::{
9 fs::{File, OpenOptions},
10 io::{self, BufRead, Write},
11 path::PathBuf,
12};
13
14#[derive(Parser, Debug)]
22pub struct RescueSuperRecoverCommand {
23 device: PathBuf,
25
26 #[clap(short = 'y', long)]
28 yes: bool,
29}
30
31struct MirrorRecord {
32 bytenr: u64,
33 buf: [u8; 4096],
34 generation: u64,
35}
36
37impl Runnable for RescueSuperRecoverCommand {
38 fn run(&self, _format: Format, _dry_run: bool) -> Result<()> {
39 if is_mounted(&self.device) {
40 bail!("{} is currently mounted", self.device.display());
41 }
42
43 let mut file = File::open(&self.device).with_context(|| {
44 format!("failed to open '{}'", self.device.display())
45 })?;
46
47 let mut good: Vec<MirrorRecord> = Vec::new();
48 let mut bad: Vec<MirrorRecord> = Vec::new();
49
50 for i in 0..SUPER_MIRROR_MAX {
51 let bytenr = super_mirror_offset(i);
52 match read_superblock_bytes_at(&mut file, bytenr) {
53 Ok(buf) => {
54 if superblock_is_valid(&buf) {
55 let generation = superblock_generation(&buf);
56 good.push(MirrorRecord {
57 bytenr,
58 buf,
59 generation,
60 });
61 } else {
62 let generation = superblock_generation(&buf);
63 bad.push(MirrorRecord {
64 bytenr,
65 buf,
66 generation,
67 });
68 }
69 }
70 Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
71 }
73 Err(e) => {
74 return Err(e).with_context(|| {
75 format!(
76 "failed to read mirror {} from '{}'",
77 i,
78 self.device.display()
79 )
80 });
81 }
82 }
83 }
84
85 if let Some(max_gen) = good.iter().map(|r| r.generation).max() {
87 let (keep, demote): (Vec<_>, Vec<_>) =
88 good.into_iter().partition(|r| r.generation == max_gen);
89 good = keep;
90 bad.extend(demote);
91 }
92
93 println!("[All good supers]:");
94 for r in &good {
95 println!("\t\tdevice name = {}", self.device.display());
96 println!("\t\tsuperblock bytenr = {}", r.bytenr);
97 println!();
98 }
99 println!("[All bad supers]:");
100 for r in &bad {
101 println!("\t\tdevice name = {}", self.device.display());
102 println!("\t\tsuperblock bytenr = {}", r.bytenr);
103 println!();
104 }
105
106 if bad.is_empty() {
107 println!("All superblocks are valid, no need to recover");
108 return Ok(());
109 }
110
111 if good.is_empty() {
112 bail!("no valid superblock found on '{}'", self.device.display());
113 }
114
115 if !self.yes {
116 print!(
117 "Make sure this is a btrfs disk otherwise the tool will destroy other fs, Are you sure? (yes/no): "
118 );
119 io::stdout().flush()?;
120 let stdin = io::stdin();
121 let mut line = String::new();
122 stdin.lock().read_line(&mut line)?;
123 if line.trim() != "yes" {
124 bail!("aborted by user");
125 }
126 }
127
128 let source = &good[0];
130 let mut file_rw = OpenOptions::new()
131 .read(true)
132 .write(true)
133 .open(&self.device)
134 .with_context(|| {
135 format!(
136 "failed to open '{}' for writing",
137 self.device.display()
138 )
139 })?;
140 write_superblock_all_mirrors(&mut file_rw, &source.buf).with_context(
141 || {
142 format!(
143 "failed to write superblocks to '{}'",
144 self.device.display()
145 )
146 },
147 )?;
148
149 println!("Recovered bad superblocks successfully");
150 Ok(())
151 }
152}