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
12pub use self::{cancel::*, limit::*, resume::*, start::*, status::*};
13
14#[derive(Parser, Debug)]
21pub struct ScrubCommand {
22 #[clap(subcommand)]
23 pub subcommand: ScrubSubcommand,
24}
25
26impl Runnable for ScrubCommand {
27 fn run(&self, format: Format, dry_run: bool) -> Result<()> {
28 match &self.subcommand {
29 ScrubSubcommand::Start(cmd) => cmd.run(format, dry_run),
30 ScrubSubcommand::Cancel(cmd) => cmd.run(format, dry_run),
31 ScrubSubcommand::Resume(cmd) => cmd.run(format, dry_run),
32 ScrubSubcommand::Status(cmd) => cmd.run(format, dry_run),
33 ScrubSubcommand::Limit(cmd) => cmd.run(format, dry_run),
34 }
35 }
36}
37
38#[derive(Parser, Debug)]
39pub enum ScrubSubcommand {
40 Start(ScrubStartCommand),
41 Cancel(ScrubCancelCommand),
42 Resume(ScrubResumeCommand),
43 Status(ScrubStatusCommand),
44 Limit(ScrubLimitCommand),
45}
46
47fn format_limit(limit: u64) -> String {
49 if limit == 0 {
50 "unlimited".to_owned()
51 } else {
52 format!("{}/s", human_bytes(limit))
53 }
54}
55
56fn digits(n: u64) -> usize {
58 if n == 0 { 1 } else { n.ilog10() as usize + 1 }
59}
60
61fn accumulate(dst: &mut ScrubProgress, src: &ScrubProgress) {
63 dst.data_extents_scrubbed += src.data_extents_scrubbed;
64 dst.tree_extents_scrubbed += src.tree_extents_scrubbed;
65 dst.data_bytes_scrubbed += src.data_bytes_scrubbed;
66 dst.tree_bytes_scrubbed += src.tree_bytes_scrubbed;
67 dst.read_errors += src.read_errors;
68 dst.csum_errors += src.csum_errors;
69 dst.verify_errors += src.verify_errors;
70 dst.super_errors += src.super_errors;
71 dst.uncorrectable_errors += src.uncorrectable_errors;
72 dst.corrected_errors += src.corrected_errors;
73 dst.unverified_errors += src.unverified_errors;
74 dst.no_csum += src.no_csum;
75 dst.csum_discards += src.csum_discards;
76 dst.malloc_errors += src.malloc_errors;
77}
78
79fn print_progress_summary(p: &ScrubProgress, devid: u64, path: &str) {
81 println!(
82 " devid {devid} ({path}): scrubbed {}",
83 human_bytes(p.bytes_scrubbed())
84 );
85 print_error_summary(p);
86}
87
88fn print_error_summary(p: &ScrubProgress) {
90 if p.malloc_errors > 0 {
91 eprintln!(
92 "WARNING: memory allocation errors during scrub — results may be inaccurate"
93 );
94 }
95 print!(" Error summary: ");
96 if p.is_clean() {
97 println!(" no errors found");
98 } else {
99 if p.read_errors > 0 {
100 print!(" read={}", p.read_errors);
101 }
102 if p.super_errors > 0 {
103 print!(" super={}", p.super_errors);
104 }
105 if p.verify_errors > 0 {
106 print!(" verify={}", p.verify_errors);
107 }
108 if p.csum_errors > 0 {
109 print!(" csum={}", p.csum_errors);
110 }
111 println!();
112 println!(" Corrected: {}", p.corrected_errors);
113 println!(" Uncorrectable: {}", p.uncorrectable_errors);
114 println!(" Unverified: {}", p.unverified_errors);
115 }
116}