Skip to main content

btrfs_cli/rescue/
zero_log.rs

1use crate::{Format, Runnable, util::is_mounted};
2use anyhow::{Context, Result, bail};
3use btrfs_disk::{
4    raw,
5    superblock::{read_superblock_bytes, write_superblock_all_mirrors},
6};
7use clap::Parser;
8use std::{fs::OpenOptions, mem, path::PathBuf};
9
10/// Clear the tree log (usable if it's corrupted and prevents mount)
11///
12/// The log tree is used for fsync durability. If it becomes corrupted it can
13/// prevent the filesystem from mounting. Clearing it forces a full fsync of
14/// all previously synced data on the next mount. No data is lost — only the
15/// durability guarantee of uncommitted fsyncs.
16///
17/// The device must not be mounted.
18#[derive(Parser, Debug)]
19pub struct RescueZeroLogCommand {
20    /// Path to the btrfs device
21    device: PathBuf,
22}
23
24impl Runnable for RescueZeroLogCommand {
25    fn run(&self, _format: Format, _dry_run: bool) -> Result<()> {
26        if is_mounted(&self.device) {
27            bail!("{} is currently mounted", self.device.display());
28        }
29
30        let mut file = OpenOptions::new()
31            .read(true)
32            .write(true)
33            .open(&self.device)
34            .with_context(|| {
35                format!("failed to open '{}'", self.device.display())
36            })?;
37
38        let mut buf = read_superblock_bytes(&mut file).with_context(|| {
39            format!(
40                "failed to read superblock from '{}'",
41                self.device.display()
42            )
43        })?;
44
45        let log_root_off = mem::offset_of!(raw::btrfs_super_block, log_root);
46        let log_root_level_off =
47            mem::offset_of!(raw::btrfs_super_block, log_root_level);
48
49        let log_root = u64::from_le_bytes(
50            buf[log_root_off..log_root_off + 8].try_into().unwrap(),
51        );
52        let log_root_level = buf[log_root_level_off];
53
54        println!(
55            "Clearing log on {}, previous log_root {log_root}, level {log_root_level}",
56            self.device.display()
57        );
58
59        buf[log_root_off..log_root_off + 8].fill(0);
60        buf[log_root_level_off] = 0;
61
62        write_superblock_all_mirrors(&mut file, &buf).with_context(|| {
63            format!("failed to write superblock to '{}'", self.device.display())
64        })?;
65
66        Ok(())
67    }
68}