use crate::{
CommandGroup, Runnable,
util::{SizeFormat, fmt_size},
};
use btrfs_uapi::scrub::ScrubProgress;
use clap::Parser;
mod cancel;
mod limit;
mod resume;
mod start;
mod status;
pub use self::{cancel::*, limit::*, resume::*, start::*, status::*};
#[derive(Parser, Debug)]
#[allow(clippy::doc_markdown)]
#[clap(arg_required_else_help = true)]
pub struct ScrubCommand {
#[clap(subcommand)]
pub subcommand: ScrubSubcommand,
}
impl CommandGroup for ScrubCommand {
fn leaf(&self) -> &dyn Runnable {
match &self.subcommand {
ScrubSubcommand::Start(cmd) => cmd,
ScrubSubcommand::Cancel(cmd) => cmd,
ScrubSubcommand::Resume(cmd) => cmd,
ScrubSubcommand::Status(cmd) => cmd,
ScrubSubcommand::Limit(cmd) => cmd,
}
}
}
#[derive(Parser, Debug)]
pub enum ScrubSubcommand {
Start(ScrubStartCommand),
Cancel(ScrubCancelCommand),
Resume(ScrubResumeCommand),
Status(ScrubStatusCommand),
Limit(ScrubLimitCommand),
}
fn format_limit(limit: u64, mode: &SizeFormat) -> String {
if limit == 0 {
"unlimited".to_owned()
} else {
format!("{}/s", fmt_size(limit, mode))
}
}
fn digits(n: u64) -> usize {
if n == 0 { 1 } else { n.ilog10() as usize + 1 }
}
fn accumulate(dst: &mut ScrubProgress, src: &ScrubProgress) {
dst.data_extents_scrubbed += src.data_extents_scrubbed;
dst.tree_extents_scrubbed += src.tree_extents_scrubbed;
dst.data_bytes_scrubbed += src.data_bytes_scrubbed;
dst.tree_bytes_scrubbed += src.tree_bytes_scrubbed;
dst.read_errors += src.read_errors;
dst.csum_errors += src.csum_errors;
dst.verify_errors += src.verify_errors;
dst.super_errors += src.super_errors;
dst.uncorrectable_errors += src.uncorrectable_errors;
dst.corrected_errors += src.corrected_errors;
dst.unverified_errors += src.unverified_errors;
dst.no_csum += src.no_csum;
dst.csum_discards += src.csum_discards;
dst.malloc_errors += src.malloc_errors;
}
fn print_progress_summary(
p: &ScrubProgress,
devid: u64,
path: &str,
mode: &SizeFormat,
) {
println!(
" devid {devid} ({path}): scrubbed {}",
fmt_size(p.bytes_scrubbed(), mode)
);
print_error_summary(p);
}
fn print_error_summary(p: &ScrubProgress) {
if p.malloc_errors > 0 {
eprintln!(
"WARNING: memory allocation errors during scrub — results may be inaccurate"
);
}
print!(" Error summary: ");
if p.is_clean() {
println!(" no errors found");
} else {
if p.read_errors > 0 {
print!(" read={}", p.read_errors);
}
if p.super_errors > 0 {
print!(" super={}", p.super_errors);
}
if p.verify_errors > 0 {
print!(" verify={}", p.verify_errors);
}
if p.csum_errors > 0 {
print!(" csum={}", p.csum_errors);
}
println!();
println!(" Corrected: {}", p.corrected_errors);
println!(" Uncorrectable: {}", p.uncorrectable_errors);
println!(" Unverified: {}", p.unverified_errors);
}
}
fn print_raw_progress(p: &ScrubProgress, devid: u64, path: &str) {
println!(" devid {devid} ({path}):");
println!(" data_extents_scrubbed: {}", p.data_extents_scrubbed);
println!(" tree_extents_scrubbed: {}", p.tree_extents_scrubbed);
println!(" data_bytes_scrubbed: {}", p.data_bytes_scrubbed);
println!(" tree_bytes_scrubbed: {}", p.tree_bytes_scrubbed);
println!(" read_errors: {}", p.read_errors);
println!(" csum_errors: {}", p.csum_errors);
println!(" verify_errors: {}", p.verify_errors);
println!(" no_csum: {}", p.no_csum);
println!(" csum_discards: {}", p.csum_discards);
println!(" super_errors: {}", p.super_errors);
println!(" malloc_errors: {}", p.malloc_errors);
println!(" uncorrectable_errors: {}", p.uncorrectable_errors);
println!(" unverified_errors: {}", p.unverified_errors);
println!(" corrected_errors: {}", p.corrected_errors);
println!(" last_physical: {}", p.last_physical);
}
fn print_device_progress(
p: &ScrubProgress,
devid: u64,
path: &str,
raw: bool,
mode: &SizeFormat,
) {
if raw {
print_raw_progress(p, devid, path);
} else {
print_progress_summary(p, devid, path, mode);
}
}
fn set_ioprio(class: i32, classdata: i32) {
const IOPRIO_WHO_PROCESS: i32 = 1;
const IOPRIO_CLASS_SHIFT: i32 = 13;
let value = (class << IOPRIO_CLASS_SHIFT) | classdata;
let ret = unsafe {
libc::syscall(libc::SYS_ioprio_set, IOPRIO_WHO_PROCESS, 0, value)
};
if ret < 0 {
eprintln!("WARNING: setting ioprio failed (ignored)");
}
}