Skip to main content

btrfs_cli/
scrub.rs

1use crate::{Format, Runnable, util::human_bytes};
2use anyhow::Result;
3use btrfs_uapi::scrub::ScrubProgress;
4use clap::Parser;
5
6mod cancel;
7mod limit;
8mod resume;
9mod start;
10mod status;
11
12use cancel::ScrubCancelCommand;
13use limit::ScrubLimitCommand;
14use resume::ScrubResumeCommand;
15use start::ScrubStartCommand;
16use status::ScrubStatusCommand;
17
18/// Verify checksums of data and metadata.
19///
20/// Scrub reads all data and metadata on a filesystem and verifies checksums.
21/// This detects hardware errors and bit rot. Scrub is typically a long-running
22/// operation and can be paused, resumed, or cancelled. Progress and status can
23/// be queried, and speed limits can be configured. Requires CAP_SYS_ADMIN.
24#[derive(Parser, Debug)]
25pub struct ScrubCommand {
26    #[clap(subcommand)]
27    pub subcommand: ScrubSubcommand,
28}
29
30impl Runnable for ScrubCommand {
31    fn run(&self, format: Format, dry_run: bool) -> Result<()> {
32        match &self.subcommand {
33            ScrubSubcommand::Start(cmd) => cmd.run(format, dry_run),
34            ScrubSubcommand::Cancel(cmd) => cmd.run(format, dry_run),
35            ScrubSubcommand::Resume(cmd) => cmd.run(format, dry_run),
36            ScrubSubcommand::Status(cmd) => cmd.run(format, dry_run),
37            ScrubSubcommand::Limit(cmd) => cmd.run(format, dry_run),
38        }
39    }
40}
41
42#[derive(Parser, Debug)]
43pub enum ScrubSubcommand {
44    Start(ScrubStartCommand),
45    Cancel(ScrubCancelCommand),
46    Resume(ScrubResumeCommand),
47    Status(ScrubStatusCommand),
48    Limit(ScrubLimitCommand),
49}
50
51/// Format a bytes-per-second limit for display; `0` means unlimited.
52fn format_limit(limit: u64) -> String {
53    if limit == 0 {
54        "unlimited".to_owned()
55    } else {
56        format!("{}/s", human_bytes(limit))
57    }
58}
59
60/// Number of decimal digits in `n` (minimum 1).
61fn digits(n: u64) -> usize {
62    if n == 0 { 1 } else { n.ilog10() as usize + 1 }
63}
64
65/// Add progress counters from `src` into `dst`.
66fn accumulate(dst: &mut ScrubProgress, src: &ScrubProgress) {
67    dst.data_extents_scrubbed += src.data_extents_scrubbed;
68    dst.tree_extents_scrubbed += src.tree_extents_scrubbed;
69    dst.data_bytes_scrubbed += src.data_bytes_scrubbed;
70    dst.tree_bytes_scrubbed += src.tree_bytes_scrubbed;
71    dst.read_errors += src.read_errors;
72    dst.csum_errors += src.csum_errors;
73    dst.verify_errors += src.verify_errors;
74    dst.super_errors += src.super_errors;
75    dst.uncorrectable_errors += src.uncorrectable_errors;
76    dst.corrected_errors += src.corrected_errors;
77    dst.unverified_errors += src.unverified_errors;
78    dst.no_csum += src.no_csum;
79    dst.csum_discards += src.csum_discards;
80    dst.malloc_errors += src.malloc_errors;
81}
82
83/// Print a single-device progress summary.
84fn print_progress_summary(p: &ScrubProgress, devid: u64, path: &str) {
85    println!(
86        "  devid {devid} ({path}): scrubbed {}",
87        human_bytes(p.bytes_scrubbed())
88    );
89    print_error_summary(p);
90}
91
92/// Print the error summary line.
93fn print_error_summary(p: &ScrubProgress) {
94    if p.malloc_errors > 0 {
95        eprintln!("WARNING: memory allocation errors during scrub — results may be inaccurate");
96    }
97    print!("  Error summary:  ");
98    if p.is_clean() {
99        println!(" no errors found");
100    } else {
101        if p.read_errors > 0 {
102            print!(" read={}", p.read_errors);
103        }
104        if p.super_errors > 0 {
105            print!(" super={}", p.super_errors);
106        }
107        if p.verify_errors > 0 {
108            print!(" verify={}", p.verify_errors);
109        }
110        if p.csum_errors > 0 {
111            print!(" csum={}", p.csum_errors);
112        }
113        println!();
114        println!("    Corrected:      {}", p.corrected_errors);
115        println!("    Uncorrectable:  {}", p.uncorrectable_errors);
116        println!("    Unverified:     {}", p.unverified_errors);
117    }
118}