mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
shred: Implemented --force option (#2012)
This commit is contained in:
parent
9ae4928b7b
commit
18191f9212
3 changed files with 112 additions and 14 deletions
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue