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