Skip to content

Commit aad33db

Browse files
committed
rm: don't treat symlinks as write-protected
Detect symlinks in prompt_file() using fs::symlink_metadata() and handle them differently: - InteractiveMode::Always should still prompt with "remove symbolic link ...?". - For all other interactive modes, do not follow the symlink target or use the target's permissions to decide a write-protected prompt. Adds a unit test test_symlink_not_write_protected_prompt to verify that a symlink to a read-only file is not treated as a write-protected regular file.
1 parent fd9157a commit aad33db

File tree

1 file changed

+35
-5
lines changed

1 file changed

+35
-5
lines changed

src/uu/rm/src/rm.rs

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -800,11 +800,10 @@ fn prompt_file(path: &Path, options: &Options) -> bool {
800800
return true;
801801
}
802802
// If interactive is Always we want to check if the file is symlink to prompt the right message
803-
if options.interactive == InteractiveMode::Always {
804-
if let Ok(metadata) = fs::symlink_metadata(path) {
805-
if metadata.is_symlink() {
806-
return prompt_yes!("remove symbolic link {}?", path.quote());
807-
}
803+
if let Ok(symlink_md) = fs::symlink_metadata(path) {
804+
if symlink_md.file_type().is_symlink() {
805+
return options.interactive != InteractiveMode::Always
806+
|| prompt_yes!("remove symbolic link {}?", path.quote());
808807
}
809808
}
810809

@@ -819,6 +818,7 @@ fn prompt_file(path: &Path, options: &Options) -> bool {
819818
prompt_yes!("remove file {}?", path.quote())
820819
};
821820
}
821+
822822
prompt_file_permission_readonly(path, options)
823823
}
824824

@@ -975,4 +975,34 @@ mod tests {
975975

976976
assert_eq!(Path::new("/"), clean_trailing_slashes(path));
977977
}
978+
979+
#[test]
980+
#[cfg(unix)]
981+
fn test_symlink_not_write_protected_prompt() {
982+
use std::fs::{self, File};
983+
use std::os::unix::fs::{PermissionsExt, symlink};
984+
use tempfile::tempdir;
985+
986+
use crate::InteractiveMode;
987+
use crate::Options;
988+
use crate::prompt_file;
989+
990+
let dir = tempdir().expect("temp dir");
991+
let foo = dir.path().join("foo");
992+
File::create(&foo).expect("create foo");
993+
994+
let mut perms = fs::metadata(&foo).expect("meta").permissions();
995+
perms.set_mode(0o444);
996+
fs::set_permissions(&foo, perms).expect("set perms");
997+
998+
let bar = dir.path().join("bar");
999+
symlink(&foo, &bar).expect("symlink");
1000+
1001+
let options = Options {
1002+
__presume_input_tty: None,
1003+
interactive: InteractiveMode::PromptProtected,
1004+
..Options::default()
1005+
};
1006+
assert!(prompt_file(&bar, &options));
1007+
}
9781008
}

0 commit comments

Comments
 (0)