diff --git a/src/uu/chmod/src/chmod.rs b/src/uu/chmod/src/chmod.rs index b77de93f2b7..43760b45017 100644 --- a/src/uu/chmod/src/chmod.rs +++ b/src/uu/chmod/src/chmod.rs @@ -407,7 +407,7 @@ impl Chmoder { // should not change the permissions in this case continue; } - if self.recursive && self.preserve_root && file == Path::new("/") { + if self.recursive && self.preserve_root && Self::is_root(file) { return Err(ChmodError::PreserveRoot("/".into()).into()); } if self.recursive { @@ -419,6 +419,10 @@ impl Chmoder { r } + fn is_root(file: impl AsRef) -> bool { + matches!(fs::canonicalize(&file), Ok(p) if p == Path::new("/")) + } + #[cfg(not(target_os = "linux"))] fn walk_dir_with_context(&self, file_path: &Path, is_command_line_arg: bool) -> UResult<()> { let mut r = self.chmod_file(file_path); diff --git a/tests/by-util/test_chmod.rs b/tests/by-util/test_chmod.rs index 6d242020ce3..a17fc4a2ca3 100644 --- a/tests/by-util/test_chmod.rs +++ b/tests/by-util/test_chmod.rs @@ -508,6 +508,17 @@ fn test_chmod_preserve_root() { .stderr_contains("chmod: it is dangerous to operate recursively on '/'"); } +#[test] +fn test_chmod_preserve_root_with_paths_that_resolve_to_root() { + new_ucmd!() + .arg("-R") + .arg("--preserve-root") + .arg("755") + .arg("/../") + .fails_with_code(1) + .stderr_contains("chmod: it is dangerous to operate recursively on '/'"); +} + #[test] fn test_chmod_symlink_non_existing_file() { let scene = TestScenario::new(util_name!());