mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 11:07:44 +00:00
chmod: Correct chmod -R on dangling symlink and tests (#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>
This commit is contained in:
parent
1b250fa75c
commit
b7bf8c9467
3 changed files with 87 additions and 10 deletions
|
@ -138,7 +138,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
return Err(UUsageError::new(1, "missing operand".to_string()));
|
return Err(UUsageError::new(1, "missing operand".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (recursive, dereference, traverse_symlinks) = configure_symlink_and_recursion(&matches)?;
|
let (recursive, dereference, traverse_symlinks) =
|
||||||
|
configure_symlink_and_recursion(&matches, TraverseSymlinks::First)?;
|
||||||
|
|
||||||
let chmoder = Chmoder {
|
let chmoder = Chmoder {
|
||||||
changes,
|
changes,
|
||||||
|
@ -259,6 +260,10 @@ impl Chmoder {
|
||||||
// Don't try to change the mode of the symlink itself
|
// Don't try to change the mode of the symlink itself
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if self.recursive && self.traverse_symlinks == TraverseSymlinks::None {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if !self.quiet {
|
if !self.quiet {
|
||||||
show!(USimpleError::new(
|
show!(USimpleError::new(
|
||||||
1,
|
1,
|
||||||
|
|
|
@ -507,6 +507,7 @@ type GidUidFilterOwnerParser = fn(&ArgMatches) -> UResult<GidUidOwnerFilter>;
|
||||||
/// Returns the updated `dereference` and `traverse_symlinks` values.
|
/// Returns the updated `dereference` and `traverse_symlinks` values.
|
||||||
pub fn configure_symlink_and_recursion(
|
pub fn configure_symlink_and_recursion(
|
||||||
matches: &ArgMatches,
|
matches: &ArgMatches,
|
||||||
|
default_traverse_symlinks: TraverseSymlinks,
|
||||||
) -> Result<(bool, bool, TraverseSymlinks), Box<dyn crate::error::UError>> {
|
) -> Result<(bool, bool, TraverseSymlinks), Box<dyn crate::error::UError>> {
|
||||||
let mut dereference = if matches.get_flag(options::dereference::DEREFERENCE) {
|
let mut dereference = if matches.get_flag(options::dereference::DEREFERENCE) {
|
||||||
Some(true) // Follow symlinks
|
Some(true) // Follow symlinks
|
||||||
|
@ -516,12 +517,13 @@ pub fn configure_symlink_and_recursion(
|
||||||
None // Default behavior
|
None // Default behavior
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut traverse_symlinks = if matches.get_flag("L") {
|
let mut traverse_symlinks = default_traverse_symlinks;
|
||||||
TraverseSymlinks::All
|
if matches.get_flag("L") {
|
||||||
|
traverse_symlinks = TraverseSymlinks::All
|
||||||
} else if matches.get_flag("H") {
|
} else if matches.get_flag("H") {
|
||||||
TraverseSymlinks::First
|
traverse_symlinks = TraverseSymlinks::First
|
||||||
} else {
|
} else if matches.get_flag("P") {
|
||||||
TraverseSymlinks::None
|
traverse_symlinks = TraverseSymlinks::None
|
||||||
};
|
};
|
||||||
|
|
||||||
let recursive = matches.get_flag(options::RECURSIVE);
|
let recursive = matches.get_flag(options::RECURSIVE);
|
||||||
|
@ -597,7 +599,8 @@ pub fn chown_base(
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let preserve_root = matches.get_flag(options::preserve_root::PRESERVE);
|
let preserve_root = matches.get_flag(options::preserve_root::PRESERVE);
|
||||||
let (recursive, dereference, traverse_symlinks) = configure_symlink_and_recursion(&matches)?;
|
let (recursive, dereference, traverse_symlinks) =
|
||||||
|
configure_symlink_and_recursion(&matches, TraverseSymlinks::None)?;
|
||||||
|
|
||||||
let verbosity_level = if matches.get_flag(options::verbosity::CHANGES) {
|
let verbosity_level = if matches.get_flag(options::verbosity::CHANGES) {
|
||||||
VerbosityLevel::Changes
|
VerbosityLevel::Changes
|
||||||
|
|
|
@ -878,7 +878,7 @@ fn test_chmod_symlink_target_no_dereference() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_chmod_symlink_to_dangling_recursive() {
|
fn test_chmod_symlink_recursive_final_traversal_flag() {
|
||||||
let scene = TestScenario::new(util_name!());
|
let scene = TestScenario::new(util_name!());
|
||||||
let at = &scene.fixtures;
|
let at = &scene.fixtures;
|
||||||
|
|
||||||
|
@ -891,9 +891,14 @@ fn test_chmod_symlink_to_dangling_recursive() {
|
||||||
.ucmd()
|
.ucmd()
|
||||||
.arg("755")
|
.arg("755")
|
||||||
.arg("-R")
|
.arg("-R")
|
||||||
|
.arg("-H")
|
||||||
|
.arg("-L")
|
||||||
|
.arg("-H")
|
||||||
|
.arg("-L")
|
||||||
|
.arg("-P")
|
||||||
.arg(symlink)
|
.arg(symlink)
|
||||||
.fails()
|
.succeeds()
|
||||||
.stderr_is("chmod: cannot operate on dangling symlink 'symlink'\n");
|
.no_output();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
at.symlink_metadata(symlink).permissions().mode(),
|
at.symlink_metadata(symlink).permissions().mode(),
|
||||||
get_expected_symlink_permissions(),
|
get_expected_symlink_permissions(),
|
||||||
|
@ -903,9 +908,73 @@ fn test_chmod_symlink_to_dangling_recursive() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_chmod_symlink_to_dangling_recursive_no_traverse() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
|
||||||
|
let dangling_target = "nonexistent_file";
|
||||||
|
let symlink = "symlink";
|
||||||
|
|
||||||
|
at.symlink_file(dangling_target, symlink);
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("755")
|
||||||
|
.arg("-R")
|
||||||
|
.arg("-P")
|
||||||
|
.arg(symlink)
|
||||||
|
.succeeds()
|
||||||
|
.no_output();
|
||||||
|
assert_eq!(
|
||||||
|
at.symlink_metadata(symlink).permissions().mode(),
|
||||||
|
get_expected_symlink_permissions(),
|
||||||
|
"Expected symlink permissions: {:o}, but got: {:o}",
|
||||||
|
get_expected_symlink_permissions(),
|
||||||
|
at.symlink_metadata(symlink).permissions().mode()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_chmod_dangling_symlink_recursive_combos() {
|
||||||
|
let error_scenarios = [vec!["-R"], vec!["-R", "-H"], vec!["-R", "-L"]];
|
||||||
|
|
||||||
|
for flags in error_scenarios {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
|
||||||
|
let dangling_target = "nonexistent_file";
|
||||||
|
let symlink = "symlink";
|
||||||
|
|
||||||
|
at.symlink_file(dangling_target, symlink);
|
||||||
|
|
||||||
|
let mut ucmd = scene.ucmd();
|
||||||
|
for f in &flags {
|
||||||
|
ucmd.arg(f);
|
||||||
|
}
|
||||||
|
ucmd.arg("u+x")
|
||||||
|
.umask(0o022)
|
||||||
|
.arg(symlink)
|
||||||
|
.fails()
|
||||||
|
.stderr_is("chmod: cannot operate on dangling symlink 'symlink'\n");
|
||||||
|
assert_eq!(
|
||||||
|
at.symlink_metadata(symlink).permissions().mode(),
|
||||||
|
get_expected_symlink_permissions(),
|
||||||
|
"Expected symlink permissions: {:o}, but got: {:o}",
|
||||||
|
get_expected_symlink_permissions(),
|
||||||
|
at.symlink_metadata(symlink).permissions().mode()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_chmod_traverse_symlink_combo() {
|
fn test_chmod_traverse_symlink_combo() {
|
||||||
let scenarios = [
|
let scenarios = [
|
||||||
|
(
|
||||||
|
vec!["-R"], // Should default to "-H"
|
||||||
|
0o100_664,
|
||||||
|
get_expected_symlink_permissions(),
|
||||||
|
),
|
||||||
(
|
(
|
||||||
vec!["-R", "-H"],
|
vec!["-R", "-H"],
|
||||||
0o100_664,
|
0o100_664,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue