Skip to content

Commit fff8399

Browse files
sylvestrecakebaker
andauthored
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 <daniel.hofstetter@42dh.com> * Update tests/by-util/test_cp.rs Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com> * Update tests/by-util/test_cp.rs Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com> --------- Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
1 parent e340d81 commit fff8399

File tree

4 files changed

+62
-3
lines changed

4 files changed

+62
-3
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,7 @@ rlimit = "0.10.1"
500500
[target.'cfg(unix)'.dev-dependencies]
501501
nix = { workspace = true, features = ["process", "signal", "user"] }
502502
rand_pcg = "0.3"
503+
xattr = { workspace = true }
503504

504505
[build-dependencies]
505506
phf_codegen = { workspace = true }

src/uu/cp/src/cp.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,7 @@ impl Attributes {
826826
ownership: Preserve::Yes { required: true },
827827
mode: Preserve::Yes { required: true },
828828
timestamps: Preserve::Yes { required: true },
829+
xattr: Preserve::Yes { required: true },
829830
..Self::NONE
830831
};
831832

@@ -1441,7 +1442,7 @@ pub(crate) fn copy_attributes(
14411442
})?;
14421443

14431444
handle_preserve(&attributes.xattr, || -> CopyResult<()> {
1444-
#[cfg(unix)]
1445+
#[cfg(all(unix, not(target_os = "android")))]
14451446
{
14461447
let xattrs = xattr::list(source)?;
14471448
for attr in xattrs {
@@ -1450,7 +1451,7 @@ pub(crate) fn copy_attributes(
14501451
}
14511452
}
14521453
}
1453-
#[cfg(not(unix))]
1454+
#[cfg(not(all(unix, not(target_os = "android"))))]
14541455
{
14551456
// The documentation for GNU cp states:
14561457
//

tests/by-util/test_cp.rs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
5-
// spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs ROOTDIR USERDIR procfs outfile uufs
5+
// spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs ROOTDIR USERDIR procfs outfile uufs xattrs
66

77
use crate::common::util::TestScenario;
88
#[cfg(not(windows))]
@@ -56,6 +56,8 @@ static TEST_MOUNT_MOUNTPOINT: &str = "mount";
5656
static TEST_MOUNT_OTHER_FILESYSTEM_FILE: &str = "mount/DO_NOT_copy_me.txt";
5757
#[cfg(unix)]
5858
static TEST_NONEXISTENT_FILE: &str = "nonexistent_file.txt";
59+
#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))]
60+
use xattr;
5961

6062
/// Assert that mode, ownership, and permissions of two metadata objects match.
6163
#[cfg(all(not(windows), not(target_os = "freebsd")))]
@@ -3736,3 +3738,57 @@ fn test_cp_no_such() {
37363738
.fails()
37373739
.stderr_is("cp: 'no-such/' is not a directory\n");
37383740
}
3741+
3742+
#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))]
3743+
fn compare_xattrs<P: AsRef<Path>>(path1: P, path2: P) -> bool {
3744+
let get_sorted_xattrs = |path: P| {
3745+
xattr::list(path)
3746+
.map(|attrs| {
3747+
let mut attrs = attrs.collect::<Vec<_>>();
3748+
attrs.sort();
3749+
attrs
3750+
})
3751+
.unwrap_or_else(|_| Vec::new())
3752+
};
3753+
3754+
get_sorted_xattrs(path1) == get_sorted_xattrs(path2)
3755+
}
3756+
3757+
#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))]
3758+
#[test]
3759+
fn test_acl_preserve() {
3760+
use std::process::Command;
3761+
3762+
let scene = TestScenario::new(util_name!());
3763+
let at = &scene.fixtures;
3764+
let path1 = "a";
3765+
let path2 = "b";
3766+
let file = "a/file";
3767+
let file_target = "b/file";
3768+
at.mkdir(path1);
3769+
at.mkdir(path2);
3770+
at.touch(file);
3771+
3772+
let path = at.plus_as_string(file);
3773+
// calling the command directly. xattr requires some dev packages to be installed
3774+
// and it adds a complex dependency just for a test
3775+
match Command::new("setfacl")
3776+
.args(["-m", "group::rwx", &path1])
3777+
.status()
3778+
.map(|status| status.code())
3779+
{
3780+
Ok(Some(0)) => {}
3781+
Ok(_) => {
3782+
println!("test skipped: setfacl failed");
3783+
return;
3784+
}
3785+
Err(e) => {
3786+
println!("test skipped: setfacl failed with {}", e);
3787+
return;
3788+
}
3789+
}
3790+
3791+
scene.ucmd().args(&["-p", &path, path2]).succeeds();
3792+
3793+
assert!(compare_xattrs(&file, &file_target));
3794+
}

0 commit comments

Comments
 (0)