Skip to content

Commit 8a3afd2

Browse files
CSResselClifford Ressel
authored andcommitted
chmod: Correct chmod -R on dangling symlink and tests (uutils#7618)
* Correct chmod -R on dangling symlink and tests * Add tests of arg-level symlink to chmod * Add tests of all symlink flag combos on chmod dangling * Fix no traverse on dangling symlink * Add chmod recursive tests of default symlink method * Add default chmod -H flag tests * Set chmod default traversal method correctly to -H * Fix arg symlink chmod case * Remove extra chmod -H testing --------- Co-authored-by: Clifford Ressel <EMAIL@gmail.com>
1 parent 5979578 commit 8a3afd2

File tree

3 files changed

+87
-10
lines changed

3 files changed

+87
-10
lines changed

src/uu/chmod/src/chmod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
138138
return Err(UUsageError::new(1, "missing operand".to_string()));
139139
}
140140

141-
let (recursive, dereference, traverse_symlinks) = configure_symlink_and_recursion(&matches)?;
141+
let (recursive, dereference, traverse_symlinks) =
142+
configure_symlink_and_recursion(&matches, TraverseSymlinks::First)?;
142143

143144
let chmoder = Chmoder {
144145
changes,
@@ -259,6 +260,10 @@ impl Chmoder {
259260
// Don't try to change the mode of the symlink itself
260261
continue;
261262
}
263+
if self.recursive && self.traverse_symlinks == TraverseSymlinks::None {
264+
continue;
265+
}
266+
262267
if !self.quiet {
263268
show!(USimpleError::new(
264269
1,

src/uucore/src/lib/features/perms.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ type GidUidFilterOwnerParser = fn(&ArgMatches) -> UResult<GidUidOwnerFilter>;
507507
/// Returns the updated `dereference` and `traverse_symlinks` values.
508508
pub fn configure_symlink_and_recursion(
509509
matches: &ArgMatches,
510+
default_traverse_symlinks: TraverseSymlinks,
510511
) -> Result<(bool, bool, TraverseSymlinks), Box<dyn crate::error::UError>> {
511512
let mut dereference = if matches.get_flag(options::dereference::DEREFERENCE) {
512513
Some(true) // Follow symlinks
@@ -516,12 +517,13 @@ pub fn configure_symlink_and_recursion(
516517
None // Default behavior
517518
};
518519

519-
let mut traverse_symlinks = if matches.get_flag("L") {
520-
TraverseSymlinks::All
520+
let mut traverse_symlinks = default_traverse_symlinks;
521+
if matches.get_flag("L") {
522+
traverse_symlinks = TraverseSymlinks::All
521523
} else if matches.get_flag("H") {
522-
TraverseSymlinks::First
523-
} else {
524-
TraverseSymlinks::None
524+
traverse_symlinks = TraverseSymlinks::First
525+
} else if matches.get_flag("P") {
526+
traverse_symlinks = TraverseSymlinks::None
525527
};
526528

527529
let recursive = matches.get_flag(options::RECURSIVE);
@@ -597,7 +599,8 @@ pub fn chown_base(
597599
.unwrap_or_default();
598600

599601
let preserve_root = matches.get_flag(options::preserve_root::PRESERVE);
600-
let (recursive, dereference, traverse_symlinks) = configure_symlink_and_recursion(&matches)?;
602+
let (recursive, dereference, traverse_symlinks) =
603+
configure_symlink_and_recursion(&matches, TraverseSymlinks::None)?;
601604

602605
let verbosity_level = if matches.get_flag(options::verbosity::CHANGES) {
603606
VerbosityLevel::Changes

tests/by-util/test_chmod.rs

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,7 @@ fn test_chmod_symlink_target_no_dereference() {
878878
}
879879

880880
#[test]
881-
fn test_chmod_symlink_to_dangling_recursive() {
881+
fn test_chmod_symlink_recursive_final_traversal_flag() {
882882
let scene = TestScenario::new(util_name!());
883883
let at = &scene.fixtures;
884884

@@ -891,9 +891,41 @@ fn test_chmod_symlink_to_dangling_recursive() {
891891
.ucmd()
892892
.arg("755")
893893
.arg("-R")
894+
.arg("-H")
895+
.arg("-L")
896+
.arg("-H")
897+
.arg("-L")
898+
.arg("-P")
894899
.arg(symlink)
895-
.fails()
896-
.stderr_is("chmod: cannot operate on dangling symlink 'symlink'\n");
900+
.succeeds()
901+
.no_output();
902+
assert_eq!(
903+
at.symlink_metadata(symlink).permissions().mode(),
904+
get_expected_symlink_permissions(),
905+
"Expected symlink permissions: {:o}, but got: {:o}",
906+
get_expected_symlink_permissions(),
907+
at.symlink_metadata(symlink).permissions().mode()
908+
);
909+
}
910+
911+
#[test]
912+
fn test_chmod_symlink_to_dangling_recursive_no_traverse() {
913+
let scene = TestScenario::new(util_name!());
914+
let at = &scene.fixtures;
915+
916+
let dangling_target = "nonexistent_file";
917+
let symlink = "symlink";
918+
919+
at.symlink_file(dangling_target, symlink);
920+
921+
scene
922+
.ucmd()
923+
.arg("755")
924+
.arg("-R")
925+
.arg("-P")
926+
.arg(symlink)
927+
.succeeds()
928+
.no_output();
897929
assert_eq!(
898930
at.symlink_metadata(symlink).permissions().mode(),
899931
get_expected_symlink_permissions(),
@@ -903,9 +935,46 @@ fn test_chmod_symlink_to_dangling_recursive() {
903935
);
904936
}
905937

938+
#[test]
939+
fn test_chmod_dangling_symlink_recursive_combos() {
940+
let error_scenarios = [vec!["-R"], vec!["-R", "-H"], vec!["-R", "-L"]];
941+
942+
for flags in error_scenarios {
943+
let scene = TestScenario::new(util_name!());
944+
let at = &scene.fixtures;
945+
946+
let dangling_target = "nonexistent_file";
947+
let symlink = "symlink";
948+
949+
at.symlink_file(dangling_target, symlink);
950+
951+
let mut ucmd = scene.ucmd();
952+
for f in &flags {
953+
ucmd.arg(f);
954+
}
955+
ucmd.arg("u+x")
956+
.umask(0o022)
957+
.arg(symlink)
958+
.fails()
959+
.stderr_is("chmod: cannot operate on dangling symlink 'symlink'\n");
960+
assert_eq!(
961+
at.symlink_metadata(symlink).permissions().mode(),
962+
get_expected_symlink_permissions(),
963+
"Expected symlink permissions: {:o}, but got: {:o}",
964+
get_expected_symlink_permissions(),
965+
at.symlink_metadata(symlink).permissions().mode()
966+
);
967+
}
968+
}
969+
906970
#[test]
907971
fn test_chmod_traverse_symlink_combo() {
908972
let scenarios = [
973+
(
974+
vec!["-R"], // Should default to "-H"
975+
0o100_664,
976+
get_expected_symlink_permissions(),
977+
),
909978
(
910979
vec!["-R", "-H"],
911980
0o100_664,

0 commit comments

Comments
 (0)