fix(cp): always create dest dirs with owner write bit before fixing permissions

This commit is contained in:
Babur Ayanlar
2026-03-13 17:19:35 +01:00
committed by Dorian Péron
parent 59288a76b1
commit 8bff321cfb
2 changed files with 24 additions and 1 deletions
+3 -1
View File
@@ -667,7 +667,9 @@ fn build_dir(
};
excluded_perms |= umask;
let mode = !excluded_perms & 0o777; //use only the last three octet bits
// Always keep the owner write bit so we can copy files into the directory.
// The correct final permissions are applied afterward by dirs_needing_permissions.
let mode = (!excluded_perms & 0o777) | 0o200; // mask to permission bits, always keep owner write
std::os::unix::fs::DirBuilderExt::mode(&mut builder, mode);
}
+21
View File
@@ -3795,6 +3795,27 @@ fn test_copy_dir_preserve_subdir_permissions() {
assert_metadata_eq!(at.metadata("a1/a2"), at.metadata("b1/a2"));
}
/// cp should successfully copy a read-only source directory containing files.
/// Regression test: build_dir previously created the destination with the source's
/// read-only mode, causing EPERM when copying files into it.
#[cfg(all(not(windows), not(target_os = "freebsd"), not(target_os = "openbsd")))]
#[test]
fn test_copy_dir_preserve_readonly_source_with_files() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("src");
at.write("src/file.txt", "hello");
at.set_mode("src", 0o0555);
ucmd.args(&["-p", "-r", "src", "dest"])
.succeeds()
.no_stderr()
.no_stdout();
assert!(at.dir_exists("dest"));
assert_eq!(at.read("dest/file.txt"), "hello");
assert_metadata_eq!(at.metadata("src"), at.metadata("dest"));
}
/// Test for preserving permissions when copying a directory, even in
/// the face of an inaccessible file in that directory.
#[cfg(all(not(windows), not(target_os = "freebsd"), not(target_os = "openbsd")))]