mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
rm: don't treat symlinks as write-protected
GNU rm does not check for write-protection on symbolic links; it instead prompts to "remove symbolic link" regardless of the link's permissions or its target's status. This change: - Ensures `prompt_file` checks for symlinks specifically using `symlink_metadata`, avoiding the incorrect "write-protected" prompt. - Refactors permission checks into `is_writable_metadata` to allow using the already-fetched metadata, which also optimizes performance by reducing redundant `stat` calls. - Updates `prompt_file_permission_readonly` to operate on metadata directly.
This commit is contained in:
committed by
Sylvestre Ledru
parent
f4ed162cf3
commit
fc17efe7be
+15
-28
@@ -549,19 +549,8 @@ fn is_writable_metadata(metadata: &Metadata) -> bool {
|
||||
(mode & 0o200) > 0
|
||||
}
|
||||
|
||||
/// Whether the given file or directory is writable.
|
||||
#[cfg(unix)]
|
||||
fn is_writable(path: &Path) -> bool {
|
||||
match fs::metadata(path) {
|
||||
Err(_) => false,
|
||||
Ok(metadata) => is_writable_metadata(&metadata),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the given file or directory is writable.
|
||||
#[cfg(not(unix))]
|
||||
fn is_writable(_path: &Path) -> bool {
|
||||
// TODO Not yet implemented.
|
||||
fn is_writable_metadata(_metadata: &Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
@@ -799,35 +788,33 @@ fn prompt_file(path: &Path, options: &Options) -> bool {
|
||||
if options.interactive == InteractiveMode::Never {
|
||||
return true;
|
||||
}
|
||||
// If interactive is Always we want to check if the file is symlink to prompt the right message
|
||||
if options.interactive == InteractiveMode::Always {
|
||||
if let Ok(metadata) = fs::symlink_metadata(path) {
|
||||
if metadata.is_symlink() {
|
||||
return prompt_yes!("remove symbolic link {}?", path.quote());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let Ok(metadata) = fs::metadata(path) else {
|
||||
let Ok(metadata) = fs::symlink_metadata(path) else {
|
||||
return true;
|
||||
};
|
||||
|
||||
if options.interactive == InteractiveMode::Always && is_writable(path) {
|
||||
if metadata.is_symlink() {
|
||||
return options.interactive != InteractiveMode::Always
|
||||
|| prompt_yes!("remove symbolic link {}?", path.quote());
|
||||
}
|
||||
|
||||
if options.interactive == InteractiveMode::Always && is_writable_metadata(&metadata) {
|
||||
return if metadata.len() == 0 {
|
||||
prompt_yes!("remove regular empty file {}?", path.quote())
|
||||
} else {
|
||||
prompt_yes!("remove file {}?", path.quote())
|
||||
};
|
||||
}
|
||||
prompt_file_permission_readonly(path, options)
|
||||
|
||||
prompt_file_permission_readonly(path, options, &metadata)
|
||||
}
|
||||
|
||||
fn prompt_file_permission_readonly(path: &Path, options: &Options) -> bool {
|
||||
fn prompt_file_permission_readonly(path: &Path, options: &Options, metadata: &Metadata) -> bool {
|
||||
let stdin_ok = options.__presume_input_tty.unwrap_or(false) || stdin().is_terminal();
|
||||
match (stdin_ok, fs::metadata(path), options.interactive) {
|
||||
(false, _, InteractiveMode::PromptProtected) => true,
|
||||
(_, Ok(_), _) if is_writable(path) => true,
|
||||
(_, Ok(metadata), _) if metadata.len() == 0 => prompt_yes!(
|
||||
match (stdin_ok, options.interactive) {
|
||||
(false, InteractiveMode::PromptProtected) => true,
|
||||
_ if is_writable_metadata(metadata) => true,
|
||||
_ if metadata.len() == 0 => prompt_yes!(
|
||||
"remove write-protected regular empty file {}?",
|
||||
path.quote()
|
||||
),
|
||||
|
||||
@@ -1217,3 +1217,20 @@ fn test_progress_no_output_on_error() {
|
||||
.stderr_contains("cannot remove")
|
||||
.stderr_contains("No such file or directory");
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_symlink_to_readonly_no_prompt() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
at.touch("foo");
|
||||
at.set_mode("foo", 0o444);
|
||||
at.symlink_file("foo", "bar");
|
||||
|
||||
ucmd.arg("---presume-input-tty")
|
||||
.arg("bar")
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(!at.symlink_exists("bar"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user