From e19f8f45a59446be231142971de814de5fe3bae5 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 15 Jan 2024 10:59:04 +0100 Subject: [PATCH] cp: --preserve should keep xattr (#5834) * cp: --preserve should keep xattr Should help with tests/cp/acl.sh * Update tests/by-util/test_cp.rs Co-authored-by: Daniel Hofstetter * Update tests/by-util/test_cp.rs Co-authored-by: Daniel Hofstetter * Update tests/by-util/test_cp.rs Co-authored-by: Daniel Hofstetter --------- Co-authored-by: Daniel Hofstetter --- Cargo.lock | 1 + Cargo.toml | 1 + src/uu/cp/src/cp.rs | 5 ++-- tests/by-util/test_cp.rs | 58 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 55ec59e2d0a..978c7fc3fae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -507,6 +507,7 @@ dependencies = [ "uucore", "uuhelp_parser", "walkdir", + "xattr", "zip", ] diff --git a/Cargo.toml b/Cargo.toml index 17efcb6a747..a8e9c399b5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -500,6 +500,7 @@ rlimit = "0.10.1" [target.'cfg(unix)'.dev-dependencies] nix = { workspace = true, features = ["process", "signal", "user"] } rand_pcg = "0.3" +xattr = { workspace = true } [build-dependencies] phf_codegen = { workspace = true } diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 52d59ff6198..28e9b678471 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -826,6 +826,7 @@ impl Attributes { ownership: Preserve::Yes { required: true }, mode: Preserve::Yes { required: true }, timestamps: Preserve::Yes { required: true }, + xattr: Preserve::Yes { required: true }, ..Self::NONE }; @@ -1441,7 +1442,7 @@ pub(crate) fn copy_attributes( })?; handle_preserve(&attributes.xattr, || -> CopyResult<()> { - #[cfg(unix)] + #[cfg(all(unix, not(target_os = "android")))] { let xattrs = xattr::list(source)?; for attr in xattrs { @@ -1450,7 +1451,7 @@ pub(crate) fn copy_attributes( } } } - #[cfg(not(unix))] + #[cfg(not(all(unix, not(target_os = "android"))))] { // The documentation for GNU cp states: // diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 5d78c5996c0..1271909ecb9 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -2,7 +2,7 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs ROOTDIR USERDIR procfs outfile uufs +// spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs ROOTDIR USERDIR procfs outfile uufs xattrs use crate::common::util::TestScenario; #[cfg(not(windows))] @@ -56,6 +56,8 @@ static TEST_MOUNT_MOUNTPOINT: &str = "mount"; static TEST_MOUNT_OTHER_FILESYSTEM_FILE: &str = "mount/DO_NOT_copy_me.txt"; #[cfg(unix)] static TEST_NONEXISTENT_FILE: &str = "nonexistent_file.txt"; +#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))] +use xattr; /// Assert that mode, ownership, and permissions of two metadata objects match. #[cfg(all(not(windows), not(target_os = "freebsd")))] @@ -3736,3 +3738,57 @@ fn test_cp_no_such() { .fails() .stderr_is("cp: 'no-such/' is not a directory\n"); } + +#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))] +fn compare_xattrs>(path1: P, path2: P) -> bool { + let get_sorted_xattrs = |path: P| { + xattr::list(path) + .map(|attrs| { + let mut attrs = attrs.collect::>(); + attrs.sort(); + attrs + }) + .unwrap_or_else(|_| Vec::new()) + }; + + get_sorted_xattrs(path1) == get_sorted_xattrs(path2) +} + +#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))] +#[test] +fn test_acl_preserve() { + use std::process::Command; + + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + let path1 = "a"; + let path2 = "b"; + let file = "a/file"; + let file_target = "b/file"; + at.mkdir(path1); + at.mkdir(path2); + at.touch(file); + + let path = at.plus_as_string(file); + // calling the command directly. xattr requires some dev packages to be installed + // and it adds a complex dependency just for a test + match Command::new("setfacl") + .args(["-m", "group::rwx", &path1]) + .status() + .map(|status| status.code()) + { + Ok(Some(0)) => {} + Ok(_) => { + println!("test skipped: setfacl failed"); + return; + } + Err(e) => { + println!("test skipped: setfacl failed with {}", e); + return; + } + } + + scene.ucmd().args(&["-p", &path, path2]).succeeds(); + + assert!(compare_xattrs(&file, &file_target)); +}