sys_shred/core/
overwrite.rs1use crate::cli::args::ShredMethod;
8use crate::error::{ShredError, ShredResult};
9use rand::{rngs::StdRng, RngCore, SeedableRng};
10use std::fs::File;
11use std::io::{Read, Seek, SeekFrom, Write};
12use std::sync::atomic::{AtomicBool, Ordering};
13use std::sync::Arc;
14
15pub struct Overwriter<'a> {
17 file: &'a mut File,
18 buffer_size: usize,
19 verify: bool,
20 cancelled: Arc<AtomicBool>,
21}
22
23impl<'a> Overwriter<'a> {
24 pub fn new(file: &'a mut File, verify: bool, cancelled: Arc<AtomicBool>) -> Self {
26 Self {
27 file,
28 buffer_size: 64 * 1024,
29 verify,
30 cancelled,
31 }
32 }
33
34 fn is_cancelled(&self) -> bool {
35 self.cancelled.load(Ordering::Relaxed)
36 }
37
38 pub fn execute(&mut self, method: ShredMethod, passes: u32) -> ShredResult<()> {
40 match method {
41 ShredMethod::Zero => self.run_zero_pass()?,
42 ShredMethod::Random => {
43 for _ in 0..passes {
44 if self.is_cancelled() {
45 break;
46 }
47 self.run_random_pass()?;
48 }
49 }
50 ShredMethod::Dod => {
51 if !self.is_cancelled() {
52 self.run_fixed_pass(0x00)?;
53 }
54 if !self.is_cancelled() {
55 self.run_fixed_pass(0xFF)?;
56 }
57 if !self.is_cancelled() {
58 self.run_random_pass()?;
59 }
60 }
61 ShredMethod::Gutmann => {
62 for _ in 0..4 {
64 if self.is_cancelled() {
65 return Ok(());
66 }
67 self.run_random_pass()?;
68 }
69 let patterns = [
70 0x55, 0xAA, 0x92, 0x49, 0x24, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
71 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x92, 0x49, 0x24, 0x6D, 0xB6,
72 0xDB,
73 ];
74 for &p in &patterns {
75 if self.is_cancelled() {
76 return Ok(());
77 }
78 self.run_fixed_pass(p)?;
79 }
80 for _ in 0..4 {
81 if self.is_cancelled() {
82 return Ok(());
83 }
84 self.run_random_pass()?;
85 }
86 }
87 }
88 Ok(())
89 }
90
91 fn run_zero_pass(&mut self) -> ShredResult<()> {
92 self.run_fixed_pass(0x00)
93 }
94
95 fn run_fixed_pass(&mut self, byte: u8) -> ShredResult<()> {
96 let file_size = self.file.metadata()?.len();
97 self.file.seek(SeekFrom::Start(0))?;
98
99 let buffer = vec![byte; self.buffer_size];
100 let mut total_written: u64 = 0;
101
102 while total_written < file_size {
103 if self.is_cancelled() {
104 return Ok(());
105 }
106 let remaining = file_size - total_written;
107 let current_chunk = std::cmp::min(self.buffer_size as u64, remaining) as usize;
108 self.file.write_all(&buffer[..current_chunk])?;
109 total_written += current_chunk as u64;
110 }
111
112 self.file.sync_all()?;
113
114 if self.verify {
115 self.verify_pass(byte)?;
116 }
117
118 Ok(())
119 }
120
121 fn run_random_pass(&mut self) -> ShredResult<()> {
122 let file_size = self.file.metadata()?.len();
123 self.file.seek(SeekFrom::Start(0))?;
124
125 let mut rng = StdRng::from_entropy();
126 let mut buffer = vec![0u8; self.buffer_size];
127 let mut total_written: u64 = 0;
128
129 while total_written < file_size {
130 if self.is_cancelled() {
131 return Ok(());
132 }
133 let remaining = file_size - total_written;
134 let current_chunk = std::cmp::min(self.buffer_size as u64, remaining) as usize;
135 rng.fill_bytes(&mut buffer[..current_chunk]);
136 self.file.write_all(&buffer[..current_chunk])?;
137 total_written += current_chunk as u64;
138 }
139
140 self.file.sync_all()?;
141
142 Ok(())
149 }
150
151 fn verify_pass(&mut self, expected_byte: u8) -> ShredResult<()> {
152 let file_size = self.file.metadata()?.len();
153 self.file.seek(SeekFrom::Start(0))?;
154
155 let mut buffer = vec![0u8; self.buffer_size];
156 let mut total_read: u64 = 0;
157
158 while total_read < file_size {
159 if self.is_cancelled() {
160 return Ok(());
161 }
162 let remaining = file_size - total_read;
163 let current_chunk = std::cmp::min(self.buffer_size as u64, remaining) as usize;
164 self.file.read_exact(&mut buffer[..current_chunk])?;
165
166 for &byte in &buffer[..current_chunk] {
167 if byte != expected_byte {
168 return Err(ShredError::Obfuscation(format!(
169 "Verification failed: Expected 0x{:02X}, found 0x{:02X} at offset {}",
170 expected_byte, byte, total_read
171 )));
172 }
173 }
174 total_read += current_chunk as u64;
175 }
176 Ok(())
177 }
178}