use crate::raw::{
BTRFS_SCRUB_READONLY, btrfs_ioc_scrub, btrfs_ioc_scrub_cancel,
btrfs_ioc_scrub_progress, btrfs_ioctl_scrub_args,
};
use std::{
mem,
os::{fd::AsRawFd, unix::io::BorrowedFd},
};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ScrubProgress {
pub data_extents_scrubbed: u64,
pub tree_extents_scrubbed: u64,
pub data_bytes_scrubbed: u64,
pub tree_bytes_scrubbed: u64,
pub read_errors: u64,
pub csum_errors: u64,
pub verify_errors: u64,
pub no_csum: u64,
pub csum_discards: u64,
pub super_errors: u64,
pub malloc_errors: u64,
pub uncorrectable_errors: u64,
pub corrected_errors: u64,
pub last_physical: u64,
pub unverified_errors: u64,
}
impl ScrubProgress {
#[must_use]
pub fn error_count(&self) -> u64 {
self.read_errors
+ self.super_errors
+ self.verify_errors
+ self.csum_errors
}
#[must_use]
pub fn bytes_scrubbed(&self) -> u64 {
self.data_bytes_scrubbed + self.tree_bytes_scrubbed
}
#[must_use]
pub fn is_clean(&self) -> bool {
self.error_count() == 0
&& self.corrected_errors == 0
&& self.uncorrectable_errors == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scrub_progress_default_is_clean() {
let p = ScrubProgress::default();
assert!(p.is_clean());
assert_eq!(p.error_count(), 0);
assert_eq!(p.bytes_scrubbed(), 0);
}
#[test]
fn scrub_progress_error_count() {
let p = ScrubProgress {
read_errors: 1,
super_errors: 2,
verify_errors: 3,
csum_errors: 4,
..ScrubProgress::default()
};
assert_eq!(p.error_count(), 10);
assert!(!p.is_clean());
}
#[test]
fn scrub_progress_bytes_scrubbed() {
let p = ScrubProgress {
data_bytes_scrubbed: 1000,
tree_bytes_scrubbed: 500,
..ScrubProgress::default()
};
assert_eq!(p.bytes_scrubbed(), 1500);
}
#[test]
fn scrub_progress_corrected_errors_not_clean() {
let p = ScrubProgress {
corrected_errors: 1,
..ScrubProgress::default()
};
assert!(!p.is_clean());
assert_eq!(p.error_count(), 0); }
#[test]
fn scrub_progress_uncorrectable_errors_not_clean() {
let p = ScrubProgress {
uncorrectable_errors: 1,
..ScrubProgress::default()
};
assert!(!p.is_clean());
}
}
fn from_raw(raw: &btrfs_ioctl_scrub_args) -> ScrubProgress {
let p = &raw.progress;
ScrubProgress {
data_extents_scrubbed: p.data_extents_scrubbed,
tree_extents_scrubbed: p.tree_extents_scrubbed,
data_bytes_scrubbed: p.data_bytes_scrubbed,
tree_bytes_scrubbed: p.tree_bytes_scrubbed,
read_errors: p.read_errors,
csum_errors: p.csum_errors,
verify_errors: p.verify_errors,
no_csum: p.no_csum,
csum_discards: p.csum_discards,
super_errors: p.super_errors,
malloc_errors: p.malloc_errors,
uncorrectable_errors: p.uncorrectable_errors,
corrected_errors: p.corrected_errors,
last_physical: p.last_physical,
unverified_errors: p.unverified_errors,
}
}
pub fn scrub_start(
fd: BorrowedFd,
devid: u64,
readonly: bool,
) -> nix::Result<ScrubProgress> {
let mut args: btrfs_ioctl_scrub_args = unsafe { mem::zeroed() };
args.devid = devid;
args.start = 0;
args.end = u64::MAX;
if readonly {
args.flags = u64::from(BTRFS_SCRUB_READONLY);
}
unsafe { btrfs_ioc_scrub(fd.as_raw_fd(), &raw mut args) }?;
Ok(from_raw(&args))
}
pub fn scrub_cancel(fd: BorrowedFd) -> nix::Result<()> {
unsafe { btrfs_ioc_scrub_cancel(fd.as_raw_fd()) }?;
Ok(())
}
pub fn scrub_progress(
fd: BorrowedFd,
devid: u64,
) -> nix::Result<Option<ScrubProgress>> {
let mut args: btrfs_ioctl_scrub_args = unsafe { mem::zeroed() };
args.devid = devid;
args.start = 0;
args.end = u64::MAX;
match unsafe { btrfs_ioc_scrub_progress(fd.as_raw_fd(), &raw mut args) } {
Err(nix::errno::Errno::ENOTCONN) => Ok(None),
Err(e) => Err(e),
Ok(_) => Ok(Some(from_raw(&args))),
}
}