1use crate::raw::{
11 BTRFS_SCRUB_READONLY, btrfs_ioc_scrub, btrfs_ioc_scrub_cancel, btrfs_ioc_scrub_progress,
12 btrfs_ioctl_scrub_args,
13};
14use std::{
15 mem,
16 os::{fd::AsRawFd, unix::io::BorrowedFd},
17};
18
19#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
22pub struct ScrubProgress {
23 pub data_extents_scrubbed: u64,
25 pub tree_extents_scrubbed: u64,
27 pub data_bytes_scrubbed: u64,
29 pub tree_bytes_scrubbed: u64,
31 pub read_errors: u64,
33 pub csum_errors: u64,
35 pub verify_errors: u64,
37 pub no_csum: u64,
39 pub csum_discards: u64,
41 pub super_errors: u64,
43 pub malloc_errors: u64,
45 pub uncorrectable_errors: u64,
47 pub corrected_errors: u64,
49 pub last_physical: u64,
51 pub unverified_errors: u64,
53}
54
55impl ScrubProgress {
56 pub fn error_count(&self) -> u64 {
58 self.read_errors + self.super_errors + self.verify_errors + self.csum_errors
59 }
60
61 pub fn bytes_scrubbed(&self) -> u64 {
63 self.data_bytes_scrubbed + self.tree_bytes_scrubbed
64 }
65
66 pub fn is_clean(&self) -> bool {
68 self.error_count() == 0 && self.corrected_errors == 0 && self.uncorrectable_errors == 0
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn scrub_progress_default_is_clean() {
78 let p = ScrubProgress::default();
79 assert!(p.is_clean());
80 assert_eq!(p.error_count(), 0);
81 assert_eq!(p.bytes_scrubbed(), 0);
82 }
83
84 #[test]
85 fn scrub_progress_error_count() {
86 let p = ScrubProgress {
87 read_errors: 1,
88 super_errors: 2,
89 verify_errors: 3,
90 csum_errors: 4,
91 ..ScrubProgress::default()
92 };
93 assert_eq!(p.error_count(), 10);
94 assert!(!p.is_clean());
95 }
96
97 #[test]
98 fn scrub_progress_bytes_scrubbed() {
99 let p = ScrubProgress {
100 data_bytes_scrubbed: 1000,
101 tree_bytes_scrubbed: 500,
102 ..ScrubProgress::default()
103 };
104 assert_eq!(p.bytes_scrubbed(), 1500);
105 }
106
107 #[test]
108 fn scrub_progress_corrected_errors_not_clean() {
109 let p = ScrubProgress {
110 corrected_errors: 1,
111 ..ScrubProgress::default()
112 };
113 assert!(!p.is_clean());
114 assert_eq!(p.error_count(), 0); }
116
117 #[test]
118 fn scrub_progress_uncorrectable_errors_not_clean() {
119 let p = ScrubProgress {
120 uncorrectable_errors: 1,
121 ..ScrubProgress::default()
122 };
123 assert!(!p.is_clean());
124 }
125}
126
127fn from_raw(raw: &btrfs_ioctl_scrub_args) -> ScrubProgress {
128 let p = &raw.progress;
129 ScrubProgress {
130 data_extents_scrubbed: p.data_extents_scrubbed,
131 tree_extents_scrubbed: p.tree_extents_scrubbed,
132 data_bytes_scrubbed: p.data_bytes_scrubbed,
133 tree_bytes_scrubbed: p.tree_bytes_scrubbed,
134 read_errors: p.read_errors,
135 csum_errors: p.csum_errors,
136 verify_errors: p.verify_errors,
137 no_csum: p.no_csum,
138 csum_discards: p.csum_discards,
139 super_errors: p.super_errors,
140 malloc_errors: p.malloc_errors,
141 uncorrectable_errors: p.uncorrectable_errors,
142 corrected_errors: p.corrected_errors,
143 last_physical: p.last_physical,
144 unverified_errors: p.unverified_errors,
145 }
146}
147
148pub fn scrub_start(fd: BorrowedFd, devid: u64, readonly: bool) -> nix::Result<ScrubProgress> {
156 let mut args: btrfs_ioctl_scrub_args = unsafe { mem::zeroed() };
157 args.devid = devid;
158 args.start = 0;
159 args.end = u64::MAX;
160 if readonly {
161 args.flags = BTRFS_SCRUB_READONLY as u64;
162 }
163 unsafe { btrfs_ioc_scrub(fd.as_raw_fd(), &mut args) }?;
164 Ok(from_raw(&args))
165}
166
167pub fn scrub_cancel(fd: BorrowedFd) -> nix::Result<()> {
169 unsafe { btrfs_ioc_scrub_cancel(fd.as_raw_fd()) }?;
170 Ok(())
171}
172
173pub fn scrub_progress(fd: BorrowedFd, devid: u64) -> nix::Result<Option<ScrubProgress>> {
178 let mut args: btrfs_ioctl_scrub_args = unsafe { mem::zeroed() };
179 args.devid = devid;
180 args.start = 0;
181 args.end = u64::MAX;
182
183 match unsafe { btrfs_ioc_scrub_progress(fd.as_raw_fd(), &mut args) } {
184 Err(nix::errno::Errno::ENOTCONN) => Ok(None),
185 Err(e) => Err(e),
186 Ok(_) => Ok(Some(from_raw(&args))),
187 }
188}