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)]
24pub struct ScrubCommand {
25 #[clap(subcommand)]
26 pub subcommand: ScrubSubcommand,
27}
28
29impl Runnable for ScrubCommand {
30 fn run(&self, format: Format, dry_run: bool) -> Result<()> {
31 match &self.subcommand {
32 ScrubSubcommand::Start(cmd) => cmd.run(format, dry_run),
33 ScrubSubcommand::Cancel(cmd) => cmd.run(format, dry_run),
34 ScrubSubcommand::Resume(cmd) => cmd.run(format, dry_run),
35 ScrubSubcommand::Status(cmd) => cmd.run(format, dry_run),
36 ScrubSubcommand::Limit(cmd) => cmd.run(format, dry_run),
37 }
38 }
39}
40
41#[derive(Parser, Debug)]
42pub enum ScrubSubcommand {
43 Start(ScrubStartCommand),
44 Cancel(ScrubCancelCommand),
45 Resume(ScrubResumeCommand),
46 Status(ScrubStatusCommand),
47 Limit(ScrubLimitCommand),
48}
49
50fn format_limit(limit: u64, mode: &SizeFormat) -> String {
52 if limit == 0 {
53 "unlimited".to_owned()
54 } else {
55 format!("{}/s", fmt_size(limit, mode))
56 }
57}
58
59fn digits(n: u64) -> usize {
61 if n == 0 { 1 } else { n.ilog10() as usize + 1 }
62}
63
64fn accumulate(dst: &mut ScrubProgress, src: &ScrubProgress) {
66 dst.data_extents_scrubbed += src.data_extents_scrubbed;
67 dst.tree_extents_scrubbed += src.tree_extents_scrubbed;
68 dst.data_bytes_scrubbed += src.data_bytes_scrubbed;
69 dst.tree_bytes_scrubbed += src.tree_bytes_scrubbed;
70 dst.read_errors += src.read_errors;
71 dst.csum_errors += src.csum_errors;
72 dst.verify_errors += src.verify_errors;
73 dst.super_errors += src.super_errors;
74 dst.uncorrectable_errors += src.uncorrectable_errors;
75 dst.corrected_errors += src.corrected_errors;
76 dst.unverified_errors += src.unverified_errors;
77 dst.no_csum += src.no_csum;
78 dst.csum_discards += src.csum_discards;
79 dst.malloc_errors += src.malloc_errors;
80}
81
82fn print_progress_summary(
84 p: &ScrubProgress,
85 devid: u64,
86 path: &str,
87 mode: &SizeFormat,
88) {
89 println!(
90 " devid {devid} ({path}): scrubbed {}",
91 fmt_size(p.bytes_scrubbed(), mode)
92 );
93 print_error_summary(p);
94}
95
96fn print_error_summary(p: &ScrubProgress) {
98 if p.malloc_errors > 0 {
99 eprintln!(
100 "WARNING: memory allocation errors during scrub — results may be inaccurate"
101 );
102 }
103 print!(" Error summary: ");
104 if p.is_clean() {
105 println!(" no errors found");
106 } else {
107 if p.read_errors > 0 {
108 print!(" read={}", p.read_errors);
109 }
110 if p.super_errors > 0 {
111 print!(" super={}", p.super_errors);
112 }
113 if p.verify_errors > 0 {
114 print!(" verify={}", p.verify_errors);
115 }
116 if p.csum_errors > 0 {
117 print!(" csum={}", p.csum_errors);
118 }
119 println!();
120 println!(" Corrected: {}", p.corrected_errors);
121 println!(" Uncorrectable: {}", p.uncorrectable_errors);
122 println!(" Unverified: {}", p.unverified_errors);
123 }
124}
125
126fn print_raw_progress(p: &ScrubProgress, devid: u64, path: &str) {
128 println!(" devid {devid} ({path}):");
129 println!(" data_extents_scrubbed: {}", p.data_extents_scrubbed);
130 println!(" tree_extents_scrubbed: {}", p.tree_extents_scrubbed);
131 println!(" data_bytes_scrubbed: {}", p.data_bytes_scrubbed);
132 println!(" tree_bytes_scrubbed: {}", p.tree_bytes_scrubbed);
133 println!(" read_errors: {}", p.read_errors);
134 println!(" csum_errors: {}", p.csum_errors);
135 println!(" verify_errors: {}", p.verify_errors);
136 println!(" no_csum: {}", p.no_csum);
137 println!(" csum_discards: {}", p.csum_discards);
138 println!(" super_errors: {}", p.super_errors);
139 println!(" malloc_errors: {}", p.malloc_errors);
140 println!(" uncorrectable_errors: {}", p.uncorrectable_errors);
141 println!(" unverified_errors: {}", p.unverified_errors);
142 println!(" corrected_errors: {}", p.corrected_errors);
143 println!(" last_physical: {}", p.last_physical);
144}
145
146fn print_device_progress(
148 p: &ScrubProgress,
149 devid: u64,
150 path: &str,
151 raw: bool,
152 mode: &SizeFormat,
153) {
154 if raw {
155 print_raw_progress(p, devid, path);
156 } else {
157 print_progress_summary(p, devid, path, mode);
158 }
159}
160
161fn set_ioprio(class: i32, classdata: i32) {
167 const IOPRIO_WHO_PROCESS: i32 = 1;
168 const IOPRIO_CLASS_SHIFT: i32 = 13;
169 let value = (class << IOPRIO_CLASS_SHIFT) | classdata;
170 let ret = unsafe {
171 libc::syscall(libc::SYS_ioprio_set, IOPRIO_WHO_PROCESS, 0, value)
172 };
173 if ret < 0 {
174 eprintln!("WARNING: setting ioprio failed (ignored)");
175 }
176}