1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 03:27:44 +00:00

shred: Implemented --force option (#2012)

This commit is contained in:
Aleksandar Janicijevic 2021-04-10 04:41:59 -04:00 committed by GitHub
parent 9ae4928b7b
commit 18191f9212
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 112 additions and 14 deletions

View file

@ -259,6 +259,7 @@ static AFTER_HELP: &str =
"; ";
pub mod options { pub mod options {
pub const FORCE: &str = "force";
pub const FILE: &str = "file"; pub const FILE: &str = "file";
pub const ITERATIONS: &str = "iterations"; pub const ITERATIONS: &str = "iterations";
pub const SIZE: &str = "size"; pub const SIZE: &str = "size";
@ -278,6 +279,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.about(ABOUT) .about(ABOUT)
.after_help(AFTER_HELP) .after_help(AFTER_HELP)
.usage(&usage[..]) .usage(&usage[..])
.arg(
Arg::with_name(options::FORCE)
.long(options::FORCE)
.short("f")
.help("change permissions to allow writing if necessary"),
)
.arg( .arg(
Arg::with_name(options::ITERATIONS) Arg::with_name(options::ITERATIONS)
.long(options::ITERATIONS) .long(options::ITERATIONS)
@ -354,8 +361,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
// TODO: implement --random-source // TODO: implement --random-source
// TODO: implement --force let force = matches.is_present(options::FORCE);
let remove = matches.is_present(options::REMOVE); let remove = matches.is_present(options::REMOVE);
let size_arg = match matches.value_of(options::SIZE) { let size_arg = match matches.value_of(options::SIZE) {
Some(s) => Some(s.to_string()), Some(s) => Some(s.to_string()),
@ -375,7 +381,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
} }
for path_str in matches.values_of(options::FILE).unwrap() { for path_str in matches.values_of(options::FILE).unwrap() {
wipe_file(&path_str, iterations, remove, size, exact, zero, verbose); wipe_file(
&path_str, iterations, remove, size, exact, zero, verbose, force,
);
} }
0 0
@ -439,18 +447,40 @@ fn wipe_file(
exact: bool, exact: bool,
zero: bool, zero: bool,
verbose: bool, verbose: bool,
force: bool,
) { ) {
// Get these potential errors out of the way first // Get these potential errors out of the way first
let path: &Path = Path::new(path_str); let path: &Path = Path::new(path_str);
if !path.exists() { if !path.exists() {
println!("{}: {}: No such file or directory", NAME, path.display()); show_error!("{}: No such file or directory", path.display());
return; return;
} }
if !path.is_file() { if !path.is_file() {
println!("{}: {}: Not a file", NAME, path.display()); show_error!("{}: Not a file", path.display());
return; return;
} }
// If force is true, set file permissions to not-readonly.
if force {
let metadata = match fs::metadata(path) {
Ok(m) => m,
Err(e) => {
show_error!("{}", e);
return;
}
};
let mut perms = metadata.permissions();
perms.set_readonly(false);
match fs::set_permissions(path, perms) {
Err(e) => {
show_error!("{}", e);
return;
}
_ => {}
}
}
// Fill up our pass sequence // Fill up our pass sequence
let mut pass_sequence: Vec<PassType> = Vec::new(); let mut pass_sequence: Vec<PassType> = Vec::new();
@ -489,11 +519,13 @@ fn wipe_file(
{ {
let total_passes: usize = pass_sequence.len(); let total_passes: usize = pass_sequence.len();
let mut file: File = OpenOptions::new() let mut file: File = match OpenOptions::new().write(true).truncate(false).open(path) {
.write(true) Ok(f) => f,
.truncate(false) Err(e) => {
.open(path) show_error!("{}: failed to open for writing: {}", path.display(), e);
.expect("Failed to open file for writing"); return;
}
};
// NOTE: it does not really matter what we set for total_bytes and gen_type here, so just // NOTE: it does not really matter what we set for total_bytes and gen_type here, so just
// use bogus values // use bogus values
@ -523,14 +555,23 @@ fn wipe_file(
} }
} }
// size is an optional argument for exactly how many bytes we want to shred // size is an optional argument for exactly how many bytes we want to shred
do_pass(&mut file, path, &mut generator, *pass_type, size) match do_pass(&mut file, path, &mut generator, *pass_type, size) {
.expect("File write pass failed"); Ok(_) => {}
Err(e) => {
show_error!("{}: File write pass failed: {}", path.display(), e);
}
}
// Ignore failed writes; just keep trying // Ignore failed writes; just keep trying
} }
} }
if remove { if remove {
do_remove(path, path_str, verbose).expect("Failed to remove file"); match do_remove(path, path_str, verbose) {
Ok(_) => {}
Err(e) => {
show_error!("{}: failed to remove file: {}", path.display(), e);
}
}
} }
} }

View file

@ -1 +1,51 @@
// ToDO: add tests use crate::common::util::*;
#[test]
fn test_shred_remove() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let file_a = "test_shred_remove_a";
let file_b = "test_shred_remove_b";
// Create file_a and file_b.
at.touch(file_a);
at.touch(file_b);
// Shred file_a.
scene.ucmd().arg("-u").arg(file_a).run();
// file_a was deleted, file_b exists.
assert!(!at.file_exists(file_a));
assert!(at.file_exists(file_b));
}
#[cfg(not(target_os = "freebsd"))]
#[test]
fn test_shred_force() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let file = "test_shred_force";
// Create file_a.
at.touch(file);
assert!(at.file_exists(file));
// Make file_a readonly.
at.set_readonly(file);
// Try shred -u.
let result = scene.ucmd().arg("-u").arg(file).run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
// file_a was not deleted because it is readonly.
assert!(at.file_exists(file));
// Try shred -u -f.
scene.ucmd().arg("-u").arg("-f").arg(file).run();
// file_a was deleted.
assert!(!at.file_exists(file));
}

View file

@ -351,6 +351,13 @@ impl AtPath {
String::from(self.minus(name).to_str().unwrap()) String::from(self.minus(name).to_str().unwrap())
} }
pub fn set_readonly(&self, name: &str) {
let metadata = fs::metadata(self.plus(name)).unwrap();
let mut permissions = metadata.permissions();
permissions.set_readonly(true);
fs::set_permissions(self.plus(name), permissions).unwrap();
}
pub fn open(&self, name: &str) -> File { pub fn open(&self, name: &str) -> File {
log_info("open", self.plus_as_string(name)); log_info("open", self.plus_as_string(name));
File::open(self.plus(name)).unwrap() File::open(self.plus(name)).unwrap()