@@ -10,7 +10,7 @@ use clap::{Arg, ArgAction, Command};
10
10
use libc:: S_IWUSR ;
11
11
use rand:: { Rng , SeedableRng , rngs:: StdRng , seq:: SliceRandom } ;
12
12
use std:: fs:: { self , File , OpenOptions } ;
13
- use std:: io:: { self , Seek , Write } ;
13
+ use std:: io:: { self , Read , Seek , Write } ;
14
14
#[ cfg( unix) ]
15
15
use std:: os:: unix:: prelude:: PermissionsExt ;
16
16
use std:: path:: { Path , PathBuf } ;
@@ -34,6 +34,7 @@ pub mod options {
34
34
pub const VERBOSE : & str = "verbose" ;
35
35
pub const EXACT : & str = "exact" ;
36
36
pub const ZERO : & str = "zero" ;
37
+ pub const RANDOM_SOURCE : & str = "random-source" ;
37
38
38
39
pub mod remove {
39
40
pub const UNLINK : & str = "unlink" ;
@@ -152,16 +153,25 @@ impl Iterator for FilenameIter {
152
153
}
153
154
}
154
155
156
+ enum RandomSource {
157
+ System ,
158
+ Read ( File ) ,
159
+ }
160
+
155
161
/// Used to generate blocks of bytes of size <= BLOCK_SIZE based on either a give pattern
156
162
/// or randomness
157
163
// The lint warns about a large difference because StdRng is big, but the buffers are much
158
164
// larger anyway, so it's fine.
159
165
#[ allow( clippy:: large_enum_variant) ]
160
- enum BytesWriter {
166
+ enum BytesWriter < ' a > {
161
167
Random {
162
168
rng : StdRng ,
163
169
buffer : [ u8 ; BLOCK_SIZE ] ,
164
170
} ,
171
+ RandomFile {
172
+ rng_file : & ' a File ,
173
+ buffer : [ u8 ; BLOCK_SIZE ] ,
174
+ } ,
165
175
// To write patterns we only write to the buffer once. To be able to do
166
176
// this, we need to extend the buffer with 2 bytes. We can then easily
167
177
// obtain a buffer starting with any character of the pattern that we
@@ -177,12 +187,18 @@ enum BytesWriter {
177
187
} ,
178
188
}
179
189
180
- impl BytesWriter {
181
- fn from_pass_type ( pass : & PassType ) -> Self {
190
+ impl < ' a > BytesWriter < ' a > {
191
+ fn from_pass_type ( pass : & PassType , random_source : & ' a RandomSource ) -> Self {
182
192
match pass {
183
- PassType :: Random => Self :: Random {
184
- rng : StdRng :: from_os_rng ( ) ,
185
- buffer : [ 0 ; BLOCK_SIZE ] ,
193
+ PassType :: Random => match random_source {
194
+ RandomSource :: System => Self :: Random {
195
+ rng : StdRng :: from_os_rng ( ) ,
196
+ buffer : [ 0 ; BLOCK_SIZE ] ,
197
+ } ,
198
+ RandomSource :: Read ( file) => Self :: RandomFile {
199
+ rng_file : file,
200
+ buffer : [ 0 ; BLOCK_SIZE ] ,
201
+ } ,
186
202
} ,
187
203
PassType :: Pattern ( pattern) => {
188
204
// Copy the pattern in chunks rather than simply one byte at a time
@@ -203,17 +219,22 @@ impl BytesWriter {
203
219
}
204
220
}
205
221
206
- fn bytes_for_pass ( & mut self , size : usize ) -> & [ u8 ] {
222
+ fn bytes_for_pass ( & mut self , size : usize ) -> Result < & [ u8 ] , io :: Error > {
207
223
match self {
208
224
Self :: Random { rng, buffer } => {
209
225
let bytes = & mut buffer[ ..size] ;
210
226
rng. fill ( bytes) ;
211
- bytes
227
+ Ok ( bytes)
228
+ }
229
+ Self :: RandomFile { rng_file, buffer } => {
230
+ let bytes = & mut buffer[ ..size] ;
231
+ rng_file. read_exact ( bytes) ?;
232
+ Ok ( bytes)
212
233
}
213
234
Self :: Pattern { offset, buffer } => {
214
235
let bytes = & buffer[ * offset..size + * offset] ;
215
236
* offset = ( * offset + size) % PATTERN_LENGTH ;
216
- bytes
237
+ Ok ( bytes)
217
238
}
218
239
}
219
240
}
@@ -240,6 +261,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
240
261
None => unreachable ! ( ) ,
241
262
} ;
242
263
264
+ let random_source = match matches. get_one :: < String > ( options:: RANDOM_SOURCE ) {
265
+ Some ( filepath) => RandomSource :: Read ( File :: open ( filepath) . map_err ( |_| {
266
+ USimpleError :: new (
267
+ 1 ,
268
+ format ! ( "cannot open random source: {}" , filepath. quote( ) ) ,
269
+ )
270
+ } ) ?) ,
271
+ None => RandomSource :: System ,
272
+ } ;
243
273
// TODO: implement --random-source
244
274
245
275
let remove_method = if matches. get_flag ( options:: WIPESYNC ) {
@@ -275,6 +305,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
275
305
size,
276
306
exact,
277
307
zero,
308
+ & random_source,
278
309
verbose,
279
310
force,
280
311
) ) ;
@@ -356,6 +387,13 @@ pub fn uu_app() -> Command {
356
387
. help ( "add a final overwrite with zeros to hide shredding" )
357
388
. action ( ArgAction :: SetTrue ) ,
358
389
)
390
+ . arg (
391
+ Arg :: new ( options:: RANDOM_SOURCE )
392
+ . long ( options:: RANDOM_SOURCE )
393
+ . help ( "take random bytes from FILE" )
394
+ . value_hint ( clap:: ValueHint :: FilePath )
395
+ . action ( ArgAction :: Set ) ,
396
+ )
359
397
// Positional arguments
360
398
. arg (
361
399
Arg :: new ( options:: FILE )
@@ -395,6 +433,7 @@ fn wipe_file(
395
433
size : Option < u64 > ,
396
434
exact : bool ,
397
435
zero : bool ,
436
+ random_source : & RandomSource ,
398
437
verbose : bool ,
399
438
force : bool ,
400
439
) -> UResult < ( ) > {
@@ -501,7 +540,7 @@ fn wipe_file(
501
540
// size is an optional argument for exactly how many bytes we want to shred
502
541
// Ignore failed writes; just keep trying
503
542
show_if_err ! (
504
- do_pass( & mut file, & pass_type, exact, size)
543
+ do_pass( & mut file, & pass_type, exact, random_source , size)
505
544
. map_err_context( || format!( "{}: File write pass failed" , path. maybe_quote( ) ) )
506
545
) ;
507
546
}
@@ -529,22 +568,23 @@ fn do_pass(
529
568
file : & mut File ,
530
569
pass_type : & PassType ,
531
570
exact : bool ,
571
+ random_source : & RandomSource ,
532
572
file_size : u64 ,
533
573
) -> Result < ( ) , io:: Error > {
534
574
// We might be at the end of the file due to a previous iteration, so rewind.
535
575
file. rewind ( ) ?;
536
576
537
- let mut writer = BytesWriter :: from_pass_type ( pass_type) ;
577
+ let mut writer = BytesWriter :: from_pass_type ( pass_type, random_source ) ;
538
578
let ( number_of_blocks, bytes_left) = split_on_blocks ( file_size, exact) ;
539
579
540
580
// We start by writing BLOCK_SIZE times as many time as possible.
541
581
for _ in 0 ..number_of_blocks {
542
- let block = writer. bytes_for_pass ( BLOCK_SIZE ) ;
582
+ let block = writer. bytes_for_pass ( BLOCK_SIZE ) ? ;
543
583
file. write_all ( block) ?;
544
584
}
545
585
546
586
// Then we write remaining data which is smaller than the BLOCK_SIZE
547
- let block = writer. bytes_for_pass ( bytes_left as usize ) ;
587
+ let block = writer. bytes_for_pass ( bytes_left as usize ) ? ;
548
588
file. write_all ( block) ?;
549
589
550
590
file. sync_data ( ) ?;
0 commit comments