cp: ensured UID/GID stripping only for unix systems and added test

Signed-off-by: Darius-Constantin <darius.constantin04@stud.acs.upb.ro>
This commit is contained in:
Darius-Constantin 2025-12-22 18:41:07 +02:00
parent e9870f9325
commit 308ef1f7cb
2 changed files with 63 additions and 4 deletions

View file

@ -1663,6 +1663,7 @@ pub(crate) fn copy_attributes(
let context = &*format!("{} -> {}", source.quote(), dest.quote());
let source_metadata =
fs::symlink_metadata(source).map_err(|e| CpError::IoErrContext(e, context.to_owned()))?;
#[cfg(unix)]
let mut ownership_failed = false;
// Ownership must be changed first to avoid interfering with mode change.
@ -1710,10 +1711,33 @@ pub(crate) fn copy_attributes(
// do nothing, since every symbolic link has the same
// permissions.
if !dest.is_symlink() {
let mut permissions = source_metadata.permissions();
if ownership_failed {
permissions.set_mode(permissions.mode() & !(libc::S_ISUID | libc::S_ISGID));
}
let permissions = source_metadata.permissions();
#[cfg(unix)]
let permissions = {
let mut perms = permissions;
if ownership_failed {
#[cfg(not(any(
target_os = "android",
target_os = "macos",
target_os = "freebsd",
target_os = "redox",
)))]
let mask = libc::S_ISUID | libc::S_ISGID;
#[cfg(any(
target_os = "android",
target_os = "macos",
target_os = "freebsd",
target_os = "redox",
))]
let mask = (libc::S_ISUID | libc::S_ISGID) as u32;
perms.set_mode(perms.mode() & !mask);
}
perms
};
fs::set_permissions(dest, permissions)
.map_err(|e| CpError::IoErrContext(e, context.to_owned()))?;
// FIXME: Implement this for windows as well

View file

@ -7400,3 +7400,38 @@ fn test_cp_recurse_verbose_output_with_symlink_already_exists() {
.no_stderr()
.stdout_is(output);
}
#[test]
#[cfg(unix)]
fn test_cp_preserve_strip_setuid() {
let (at, mut ucmd) = at_and_ucmd!();
let source = "/bin/sh";
let destination = "sh_copy";
ucmd.arg("-p").arg(source).arg(destination).succeeds();
use std::os::unix::fs::PermissionsExt;
let mode = at.metadata(destination).permissions().mode();
#[cfg(not(any(
target_os = "android",
target_os = "macos",
target_os = "freebsd",
target_os = "redox",
)))]
let mask = libc::S_ISUID | libc::S_ISGID;
#[cfg(any(
target_os = "android",
target_os = "macos",
target_os = "freebsd",
target_os = "redox",
))]
let mask = (libc::S_ISUID | libc::S_ISGID) as u32;
assert_eq!(
mode & mask,
0,
"SetUID/SetGID need to be stripped if ownership preservation fails."
);
}