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#[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
51fn format_limit(limit: u64) -> String {
53 if limit == 0 {
54 "unlimited".to_owned()
55 } else {
56 format!("{}/s", human_bytes(limit))
57 }
58}
59
60fn digits(n: u64) -> usize {
62 if n == 0 { 1 } else { n.ilog10() as usize + 1 }
63}
64
65fn 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
83fn 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
92fn 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}