1use crate::raw::{
11 BTRFS_SCRUB_READONLY, btrfs_ioc_scrub, btrfs_ioc_scrub_cancel,
12 btrfs_ioc_scrub_progress, 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
59 + self.super_errors
60 + self.verify_errors
61 + self.csum_errors
62 }
63
64 pub fn bytes_scrubbed(&self) -> u64 {
66 self.data_bytes_scrubbed + self.tree_bytes_scrubbed
67 }
68
69 pub fn is_clean(&self) -> bool {
71 self.error_count() == 0
72 && self.corrected_errors == 0
73 && self.uncorrectable_errors == 0
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn scrub_progress_default_is_clean() {
83 let p = ScrubProgress::default();
84 assert!(p.is_clean());
85 assert_eq!(p.error_count(), 0);
86 assert_eq!(p.bytes_scrubbed(), 0);
87 }
88
89 #[test]
90 fn scrub_progress_error_count() {
91 let p = ScrubProgress {
92 read_errors: 1,
93 super_errors: 2,
94 verify_errors: 3,
95 csum_errors: 4,
96 ..ScrubProgress::default()
97 };
98 assert_eq!(p.error_count(), 10);
99 assert!(!p.is_clean());
100 }
101
102 #[test]
103 fn scrub_progress_bytes_scrubbed() {
104 let p = ScrubProgress {
105 data_bytes_scrubbed: 1000,
106 tree_bytes_scrubbed: 500,
107 ..ScrubProgress::default()
108 };
109 assert_eq!(p.bytes_scrubbed(), 1500);
110 }
111
112 #[test]
113 fn scrub_progress_corrected_errors_not_clean() {
114 let p = ScrubProgress {
115 corrected_errors: 1,
116 ..ScrubProgress::default()
117 };
118 assert!(!p.is_clean());
119 assert_eq!(p.error_count(), 0); }
121
122 #[test]
123 fn scrub_progress_uncorrectable_errors_not_clean() {
124 let p = ScrubProgress {
125 uncorrectable_errors: 1,
126 ..ScrubProgress::default()
127 };
128 assert!(!p.is_clean());
129 }
130}
131
132fn from_raw(raw: &btrfs_ioctl_scrub_args) -> ScrubProgress {
133 let p = &raw.progress;
134 ScrubProgress {
135 data_extents_scrubbed: p.data_extents_scrubbed,
136 tree_extents_scrubbed: p.tree_extents_scrubbed,
137 data_bytes_scrubbed: p.data_bytes_scrubbed,
138 tree_bytes_scrubbed: p.tree_bytes_scrubbed,
139 read_errors: p.read_errors,
140 csum_errors: p.csum_errors,
141 verify_errors: p.verify_errors,
142 no_csum: p.no_csum,
143 csum_discards: p.csum_discards,
144 super_errors: p.super_errors,
145 malloc_errors: p.malloc_errors,
146 uncorrectable_errors: p.uncorrectable_errors,
147 corrected_errors: p.corrected_errors,
148 last_physical: p.last_physical,
149 unverified_errors: p.unverified_errors,
150 }
151}
152
153pub fn scrub_start(
161 fd: BorrowedFd,
162 devid: u64,
163 readonly: bool,
164) -> nix::Result<ScrubProgress> {
165 let mut args: btrfs_ioctl_scrub_args = unsafe { mem::zeroed() };
166 args.devid = devid;
167 args.start = 0;
168 args.end = u64::MAX;
169 if readonly {
170 args.flags = BTRFS_SCRUB_READONLY as u64;
171 }
172 unsafe { btrfs_ioc_scrub(fd.as_raw_fd(), &mut args) }?;
173 Ok(from_raw(&args))
174}
175
176pub fn scrub_cancel(fd: BorrowedFd) -> nix::Result<()> {
178 unsafe { btrfs_ioc_scrub_cancel(fd.as_raw_fd()) }?;
179 Ok(())
180}
181
182pub fn scrub_progress(
187 fd: BorrowedFd,
188 devid: u64,
189) -> nix::Result<Option<ScrubProgress>> {
190 let mut args: btrfs_ioctl_scrub_args = unsafe { mem::zeroed() };
191 args.devid = devid;
192 args.start = 0;
193 args.end = u64::MAX;
194
195 match unsafe { btrfs_ioc_scrub_progress(fd.as_raw_fd(), &mut args) } {
196 Err(nix::errno::Errno::ENOTCONN) => Ok(None),
197 Err(e) => Err(e),
198 Ok(_) => Ok(Some(from_raw(&args))),
199 }
200}