Have FilesystemShell#rm correctly delete symlinks (#1550)

This commit is contained in:
Jason Gedge 2020-04-05 17:17:52 -04:00 committed by GitHub
parent 6a604491f5
commit 0a198b9bd0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 86 deletions

View File

@ -16,7 +16,6 @@ use rustyline::completion::FilenameCompleter;
use rustyline::hint::{Hinter, HistoryHinter}; use rustyline::hint::{Hinter, HistoryHinter};
use std::collections::HashMap; use std::collections::HashMap;
use std::path::{Component, Path, PathBuf}; use std::path::{Component, Path, PathBuf};
use trash as SendToTrash;
#[cfg(unix)] #[cfg(unix)]
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
@ -995,10 +994,15 @@ impl Shell for FilesystemShell {
let mut all_targets: HashMap<PathBuf, Tag> = HashMap::new(); let mut all_targets: HashMap<PathBuf, Tag> = HashMap::new();
for target in targets { for target in targets {
if target.item.to_str() == Some(".") || target.item.to_str() == Some("..") { let all_dots = target
.item
.to_str()
.map_or(false, |v| v.chars().all(|c| c == '.'));
if all_dots {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Remove aborted. \".\" or \"..\" may not be removed.", "Cannot remove any parent directory",
"\".\" or \"..\" may not be removed", "cannot remove any parent directory",
target.tag, target.tag,
)); ));
} }
@ -1015,9 +1019,8 @@ impl Shell for FilesystemShell {
.or_insert_with(|| target.tag.clone()); .or_insert_with(|| target.tag.clone());
} }
Err(e) => { Err(e) => {
let msg = format!("Could not remove {:}", path.to_string_lossy());
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
msg, format!("Could not remove {:}", path.to_string_lossy()),
e.to_string(), e.to_string(),
&target.tag, &target.tag,
)); ));
@ -1027,7 +1030,7 @@ impl Shell for FilesystemShell {
} }
Err(e) => { Err(e) => {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
format!("Remove aborted. {:}", e.to_string()), e.to_string(),
e.to_string(), e.to_string(),
&name_tag, &name_tag,
)) ))
@ -1036,91 +1039,63 @@ impl Shell for FilesystemShell {
} }
if all_targets.is_empty() { if all_targets.is_empty() {
Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Remove aborted. No valid paths", "No valid paths",
"no valid paths", "no valid paths",
name_tag, name_tag,
)) ));
} else { }
let stream = async_stream! {
for (f, tag) in all_targets.iter() {
let is_empty = match f.read_dir() {
Ok(mut p) => p.next().is_none(),
Err(_) => false
};
let valid_target = let stream = async_stream! {
f.exists() && (!f.is_dir() || (is_empty || recursive.item)); for (f, tag) in all_targets.iter() {
if valid_target { let is_empty = || match f.read_dir() {
if trash.item { Ok(mut p) => p.next().is_none(),
match SendToTrash::remove(f) { Err(_) => false
Err(e) => { };
let msg = format!(
"Could not delete {:}", if let Ok(metadata) = f.symlink_metadata() {
f.to_string_lossy() if metadata.is_file() || metadata.file_type().is_symlink() || recursive.item || is_empty() {
); let result = if trash.item {
let label = format!("{:?}", e); trash::remove(f)
yield Err(ShellError::labeled_error( .map_err(|e| f.to_string_lossy())
msg, } else if metadata.is_file() {
label, std::fs::remove_file(f)
tag, .map_err(|e| f.to_string_lossy())
))
},
Ok(()) => {
let val = format!("deleted {:}", f.to_string_lossy()).into();
yield Ok(ReturnSuccess::Value(val))
},
}
} else { } else {
let success = if f.is_dir() { std::fs::remove_dir_all(f)
std::fs::remove_dir_all(f) .map_err(|e| f.to_string_lossy())
} else { };
std::fs::remove_file(f)
}; if let Err(e) = result {
match success { let msg = format!("Could not delete {:}", e);
Err(e) => { yield Err(ShellError::labeled_error(msg, e, tag))
let msg = format!( } else {
"Could not delete {:}", let val = format!("deleted {:}", f.to_string_lossy()).into();
f.to_string_lossy() yield Ok(ReturnSuccess::Value(val))
);
yield Err(ShellError::labeled_error(
msg,
e.to_string(),
tag,
))
},
Ok(()) => {
let val = format!("deleted {:}", f.to_string_lossy()).into();
yield Ok(ReturnSuccess::Value(
val,
))
},
}
} }
} else { } else {
if f.is_dir() { let msg = format!(
let msg = format!( "Cannot remove {:}. try --recursive",
"Cannot remove {:}. try --recursive", f.to_string_lossy()
f.to_string_lossy() );
); yield Err(ShellError::labeled_error(
yield Err(ShellError::labeled_error( msg,
msg, "cannot remove non-empty directory",
"cannot remove non-empty directory", tag,
tag, ))
))
} else {
let msg = format!("Invalid file: {:}", f.to_string_lossy());
yield Err(ShellError::labeled_error(
msg,
"invalid file",
tag,
))
}
} }
} else {
let msg = format!("no such file or directory: {:}", f.to_string_lossy());
yield Err(ShellError::labeled_error(
msg,
"no such file or directory",
tag,
))
} }
}; }
Ok(stream.to_output_stream()) };
}
Ok(stream.to_output_stream())
} }
fn path(&self) -> String { fn path(&self) -> String {

View File

@ -144,7 +144,7 @@ fn errors_if_attempting_to_delete_single_dot_as_argument() {
"rm ." "rm ."
); );
assert!(actual.contains("may not be removed")); assert!(actual.contains("cannot remove any parent directory"));
}) })
} }
@ -156,7 +156,7 @@ fn errors_if_attempting_to_delete_two_dot_as_argument() {
"rm .." "rm .."
); );
assert!(actual.contains("may not be removed")); assert!(actual.contains("cannot remove any parent directory"));
}) })
} }