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::superblock::{
4    read_superblock_bytes, write_superblock_all_mirrors,
5};
6use clap::Parser;
7use std::{fs::OpenOptions, mem, path::PathBuf};
8
9/// Clear the tree log (usable if it's corrupted and prevents mount)
10///
11/// The log tree is used for fsync durability. If it becomes corrupted it can
12/// prevent the filesystem from mounting. Clearing it forces a full fsync of
13/// all previously synced data on the next mount. No data is lost — only the
14/// durability guarantee of uncommitted fsyncs.
15///
16/// The device must not be mounted.
17#[derive(Parser, Debug)]
18pub struct RescueZeroLogCommand {
19    /// Path to the btrfs device
20    device: PathBuf,
21}
22
23impl Runnable for RescueZeroLogCommand {
24    fn run(&self, _format: Format, _dry_run: bool) -> Result<()> {
25        if is_mounted(&self.device) {
26            bail!("{} is currently mounted", self.device.display());
27        }
28
29        let mut file = OpenOptions::new()
30            .read(true)
31            .write(true)
32            .open(&self.device)
33            .with_context(|| {
34                format!("failed to open '{}'", self.device.display())
35            })?;
36
37        let mut buf = read_superblock_bytes(&mut file).with_context(|| {
38            format!(
39                "failed to read superblock from '{}'",
40                self.device.display()
41            )
42        })?;
43
44        use btrfs_disk::raw;
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}