chmod: fixed behavior for dangling symlinks (#1775)

This commit is contained in:
Jan Scheer 2021-03-29 22:07:09 +02:00
parent 5f17719a59
commit 2647a72e9e
2 changed files with 123 additions and 35 deletions

View file

@ -206,7 +206,17 @@ impl Chmoder {
let filename = &filename[..]; let filename = &filename[..];
let file = Path::new(filename); let file = Path::new(filename);
if !file.exists() { if !file.exists() {
show_error!("no such file or directory '{}'", filename); if is_symlink(file) {
println!(
"failed to change mode of '{}' from 0000 (---------) to 0000 (---------)",
filename
);
if !self.quiet {
show_error!("cannot operate on dangling symlink '{}'", filename);
}
} else {
show_error!("cannot access '{}': No such file or directory", filename);
}
return Err(1); return Err(1);
} }
if self.recursive && self.preserve_root && filename == "/" { if self.recursive && self.preserve_root && filename == "/" {
@ -240,18 +250,16 @@ impl Chmoder {
let mut fperm = match fs::metadata(file) { let mut fperm = match fs::metadata(file) {
Ok(meta) => meta.mode() & 0o7777, Ok(meta) => meta.mode() & 0o7777,
Err(err) => { Err(err) => {
if !self.quiet { if is_symlink(file) {
if is_symlink(file) { if self.verbose {
if self.verbose { println!(
show_info!( "neither symbolic link '{}' nor referent has been changed",
"neither symbolic link '{}' nor referent has been changed", file.display()
file.display() );
);
}
return Ok(());
} else {
show_error!("{}: '{}'", err, file.display());
} }
return Ok(());
} else {
show_error!("{}: '{}'", err, file.display());
} }
return Err(1); return Err(1);
} }
@ -291,11 +299,11 @@ impl Chmoder {
fn change_file(&self, fperm: u32, mode: u32, file: &Path) -> Result<(), i32> { fn change_file(&self, fperm: u32, mode: u32, file: &Path) -> Result<(), i32> {
if fperm == mode { if fperm == mode {
if self.verbose && !self.changes { if self.verbose && !self.changes {
show_info!( println!(
"mode of '{}' retained as {:o} ({})", "mode of '{}' retained as {:04o} ({})",
file.display(), file.display(),
fperm, fperm,
display_permissions_unix(fperm) display_permissions_unix(fperm),
); );
} }
Ok(()) Ok(())

View file

@ -329,10 +329,9 @@ fn test_chmod_non_existing_file() {
.arg("-r,a+w") .arg("-r,a+w")
.arg("dont-exist") .arg("dont-exist")
.fails(); .fails();
assert_eq!( assert!(result
result.stderr, .stderr
"chmod: error: no such file or directory 'dont-exist'\n" .contains("cannot access 'dont-exist': No such file or directory"));
);
} }
#[test] #[test]
@ -351,30 +350,111 @@ fn test_chmod_preserve_root() {
#[test] #[test]
fn test_chmod_symlink_non_existing_file() { fn test_chmod_symlink_non_existing_file() {
let (at, mut ucmd) = at_and_ucmd!(); let scene = TestScenario::new(util_name!());
at.symlink_file("/non-existing", "test-long.link"); let at = &scene.fixtures;
let _result = ucmd let non_existing = "test_chmod_symlink_non_existing_file";
.arg("-R") let test_symlink = "test_chmod_symlink_non_existing_file_symlink";
let expected_stdout = &format!(
"failed to change mode of '{}' from 0000 (---------) to 0000 (---------)",
test_symlink
);
let expected_stderr = &format!("cannot operate on dangling symlink '{}'", test_symlink);
at.symlink_file(non_existing, test_symlink);
let mut result;
// this cannot succeed since the symbolic link dangles
result = scene.ucmd().arg("755").arg("-v").arg(test_symlink).fails();
println!("stdout = {:?}", result.stdout);
println!("stderr = {:?}", result.stderr);
assert!(result.stdout.contains(expected_stdout));
assert!(result.stderr.contains(expected_stderr));
assert_eq!(result.code, Some(1));
// this should be the same than with just '-v' but without stderr
result = scene
.ucmd()
.arg("755") .arg("755")
.arg("-v") .arg("-v")
.arg("test-long.link") .arg("-f")
.arg(test_symlink)
.fails(); .fails();
println!("stdout = {:?}", result.stdout);
println!("stderr = {:?}", result.stderr);
assert!(result.stdout.contains(expected_stdout));
assert!(result.stderr.is_empty());
assert_eq!(result.code, Some(1));
} }
#[test] #[test]
fn test_chmod_symlink_non_existing_recursive() { fn test_chmod_symlink_non_existing_file_recursive() {
let (at, mut ucmd) = at_and_ucmd!(); let scene = TestScenario::new(util_name!());
at.mkdir("tmp"); let at = &scene.fixtures;
at.symlink_file("/non-existing", "tmp/test-long.link");
let result = ucmd.arg("-R").arg("755").arg("-v").arg("tmp").succeeds(); let non_existing = "test_chmod_symlink_non_existing_file_recursive";
// it should be a success let test_symlink = "test_chmod_symlink_non_existing_file_recursive_symlink";
println!("stderr {}", result.stderr); let test_directory = "test_chmod_symlink_non_existing_file_directory";
println!("stdout {}", result.stdout);
assert!(result at.mkdir(test_directory);
.stderr at.symlink_file(
.contains("neither symbolic link 'tmp/test-long.link' nor referent has been changed")); non_existing,
&format!("{}/{}", test_directory, test_symlink),
);
let mut result;
// this should succeed
result = scene
.ucmd()
.arg("-R")
.arg("755")
.arg(test_directory)
.succeeds();
assert_eq!(result.code, Some(0));
assert!(result.stdout.is_empty());
assert!(result.stderr.is_empty());
let expected_stdout = &format!(
"mode of '{}' retained as 0755 (rwxr-xr-x)\nneither symbolic link '{}/{}' nor referent has been changed",
test_directory, test_directory, test_symlink
);
// '-v': this should succeed without stderr
result = scene
.ucmd()
.arg("-R")
.arg("-v")
.arg("755")
.arg(test_directory)
.succeeds();
println!("stdout = {:?}", result.stdout);
println!("stderr = {:?}", result.stderr);
assert!(result.stdout.contains(expected_stdout));
assert!(result.stderr.is_empty());
assert_eq!(result.code, Some(0));
// '-vf': this should be the same than with just '-v'
result = scene
.ucmd()
.arg("-R")
.arg("-v")
.arg("-f")
.arg("755")
.arg(test_directory)
.succeeds();
println!("stdout = {:?}", result.stdout);
println!("stderr = {:?}", result.stderr);
assert!(result.stdout.contains(expected_stdout));
assert!(result.stderr.is_empty());
assert_eq!(result.code, Some(0));
} }
#[test] #[test]