1use crate::{
2 Format, Runnable,
3 util::{SizeFormat, fmt_size},
4};
5use anyhow::Result;
6use btrfs_uapi::scrub::ScrubProgress;
7use clap::Parser;
8
9mod cancel;
10mod limit;
11mod resume;
12mod start;
13mod status;
14
15pub use self::{cancel::*, limit::*, resume::*, start::*, status::*};
16
17#[derive(Parser, Debug)]
24#[allow(clippy::doc_markdown)]
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, mode: &SizeFormat) -> String {
53 if limit == 0 {
54 "unlimited".to_owned()
55 } else {
56 format!("{}/s", fmt_size(limit, mode))
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(
85 p: &ScrubProgress,
86 devid: u64,
87 path: &str,
88 mode: &SizeFormat,
89) {
90 println!(
91 " devid {devid} ({path}): scrubbed {}",
92 fmt_size(p.bytes_scrubbed(), mode)
93 );
94 print_error_summary(p);
95}
96
97fn print_error_summary(p: &ScrubProgress) {
99 if p.malloc_errors > 0 {
100 eprintln!(
101 "WARNING: memory allocation errors during scrub — results may be inaccurate"
102 );
103 }
104 print!(" Error summary: ");
105 if p.is_clean() {
106 println!(" no errors found");
107 } else {
108 if p.read_errors > 0 {
109 print!(" read={}", p.read_errors);
110 }
111 if p.super_errors > 0 {
112 print!(" super={}", p.super_errors);
113 }
114 if p.verify_errors > 0 {
115 print!(" verify={}", p.verify_errors);
116 }
117 if p.csum_errors > 0 {
118 print!(" csum={}", p.csum_errors);
119 }
120 println!();
121 println!(" Corrected: {}", p.corrected_errors);
122 println!(" Uncorrectable: {}", p.uncorrectable_errors);
123 println!(" Unverified: {}", p.unverified_errors);
124 }
125}
126
127fn print_raw_progress(p: &ScrubProgress, devid: u64, path: &str) {
129 println!(" devid {devid} ({path}):");
130 println!(" data_extents_scrubbed: {}", p.data_extents_scrubbed);
131 println!(" tree_extents_scrubbed: {}", p.tree_extents_scrubbed);
132 println!(" data_bytes_scrubbed: {}", p.data_bytes_scrubbed);
133 println!(" tree_bytes_scrubbed: {}", p.tree_bytes_scrubbed);
134 println!(" read_errors: {}", p.read_errors);
135 println!(" csum_errors: {}", p.csum_errors);
136 println!(" verify_errors: {}", p.verify_errors);
137 println!(" no_csum: {}", p.no_csum);
138 println!(" csum_discards: {}", p.csum_discards);
139 println!(" super_errors: {}", p.super_errors);
140 println!(" malloc_errors: {}", p.malloc_errors);
141 println!(" uncorrectable_errors: {}", p.uncorrectable_errors);
142 println!(" unverified_errors: {}", p.unverified_errors);
143 println!(" corrected_errors: {}", p.corrected_errors);
144 println!(" last_physical: {}", p.last_physical);
145}
146
147fn print_device_progress(
149 p: &ScrubProgress,
150 devid: u64,
151 path: &str,
152 raw: bool,
153 mode: &SizeFormat,
154) {
155 if raw {
156 print_raw_progress(p, devid, path);
157 } else {
158 print_progress_summary(p, devid, path, mode);
159 }
160}
161
162fn set_ioprio(class: i32, classdata: i32) {
168 const IOPRIO_WHO_PROCESS: i32 = 1;
169 const IOPRIO_CLASS_SHIFT: i32 = 13;
170 let value = (class << IOPRIO_CLASS_SHIFT) | classdata;
171 let ret = unsafe {
172 libc::syscall(libc::SYS_ioprio_set, IOPRIO_WHO_PROCESS, 0, value)
173 };
174 if ret < 0 {
175 eprintln!("WARNING: setting ioprio failed (ignored)");
176 }
177}