Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions src/uu/cp/src/copydir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,12 +618,11 @@ fn build_dir(
#[cfg(unix)]
{
use crate::Preserve;
use std::os::unix::fs::PermissionsExt;

// we need to allow trivial casts here because some systems like linux have u32 constants in
// in libc while others don't.
#[allow(clippy::unnecessary_cast)]
let mut excluded_perms = if matches!(options.attributes.ownership, Preserve::Yes { .. }) {
let excluded_perms = if matches!(options.attributes.ownership, Preserve::Yes { .. }) {
libc::S_IRWXG | libc::S_IRWXO // exclude rwx for group and other
} else if matches!(options.attributes.mode, Preserve::Yes { .. }) {
libc::S_IWGRP | libc::S_IWOTH //exclude w for group and other
Expand All @@ -634,13 +633,16 @@ fn build_dir(
let umask = if let (Some(from), Preserve::Yes { .. }) =
(copy_attributes_from, options.attributes.mode)
{
!fs::symlink_metadata(from)?.permissions().mode()
// temporary u+wx permission before true permission is set by
// `dirs_needing_permissions`.
!0o300
} else {
uucore::mode::get_umask()
};

excluded_perms |= umask;
let mode = !excluded_perms & 0o777; //use only the last three octet bits
let mode = !(excluded_perms | umask);
// use only the last three octet bits
let mode = mode & 0o777;
std::os::unix::fs::DirBuilderExt::mode(&mut builder, mode);
}

Expand Down
30 changes: 30 additions & 0 deletions tests/by-util/test_cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6470,6 +6470,36 @@ fn test_cp_preserve_xattr_readonly_source() {
);
}

#[test]
#[cfg(unix)]
fn test_cp_archive_dir_no_write_permission() {
// Test for issue #10787
let (at, mut ucmd) = at_and_ucmd!();

// test-dir/inner/test: all without write permission (500)
at.mkdir("test-dir");
at.mkdir("test-dir/inner");
at.touch("test-dir/inner/test");
at.set_mode("test-dir/inner/test", 0o500);
at.set_mode("test-dir/inner", 0o500);
at.set_mode("test-dir", 0o500);

ucmd.arg("-a")
.arg("test-dir")
.arg("test-dir-copy")
.succeeds();

assert_eq!(at.metadata("test-dir").permissions().mode() & 0o777, 0o500);
assert_eq!(
at.metadata("test-dir/inner").permissions().mode() & 0o777,
0o500
);
assert_eq!(
at.metadata("test-dir/inner/test").permissions().mode() & 0o777,
0o500
);
}

#[test]
#[cfg(unix)]
fn test_cp_archive_preserves_directory_permissions() {
Expand Down
Loading