Skip to content

Commit aa30c10

Browse files
committed
uucore/fsxattr: keep copy_xattrs strict; add ignore-unsupported variants
Restore strict ENOTSUP/EOPNOTSUPP propagation on copy_xattrs and copy_xattrs_fd; add copy_xattrs_ignore_unsupported and copy_xattrs_fd_ignore_unsupported wrappers for best-effort callers. mv uses the wrappers; cp --preserve=xattr exits 1 on ENOTSUP again.
1 parent 54877ac commit aa30c10

2 files changed

Lines changed: 36 additions & 45 deletions

File tree

src/uu/mv/src/mv.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,7 @@ fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> {
921921
}
922922
#[cfg(not(any(target_os = "macos", target_os = "redox")))]
923923
{
924-
let _ = fsxattr::copy_xattrs(from, to);
924+
let _ = fsxattr::copy_xattrs_ignore_unsupported(from, to);
925925
}
926926
let _ = preserve_ownership(from, to);
927927
fs::remove_file(from)
@@ -1250,7 +1250,7 @@ fn copy_file_with_hardlinks_helper(
12501250
// Copy xattrs, ignoring ENOTSUP errors (filesystem doesn't support xattrs)
12511251
#[cfg(all(unix, not(any(target_os = "macos", target_os = "redox"))))]
12521252
{
1253-
let _ = fsxattr::copy_xattrs(from, to);
1253+
let _ = fsxattr::copy_xattrs_ignore_unsupported(from, to);
12541254
}
12551255
// Preserve ownership (uid/gid) from the source
12561256
let _ = preserve_ownership(from, to);
@@ -1315,7 +1315,7 @@ fn rename_file_fallback(
13151315

13161316
#[cfg(not(any(target_os = "macos", target_os = "redox")))]
13171317
{
1318-
let _ = fsxattr::copy_xattrs_fd(&src_file, &dst_file);
1318+
let _ = fsxattr::copy_xattrs_fd_ignore_unsupported(&src_file, &dst_file);
13191319
}
13201320

13211321
// chown before chmod: chown(2) clears setuid/setgid for non-root,

src/uucore/src/lib/features/fsxattr.rs

Lines changed: 33 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@ use std::ffi::{OsStr, OsString};
1313
use std::os::unix::ffi::OsStrExt;
1414
use std::path::Path;
1515

16-
/// Returns true if the error is the kernel/filesystem signaling that
17-
/// extended attributes are not supported (`ENOTSUP` / `EOPNOTSUPP`).
18-
/// On Linux these are the same errno; on the BSDs they differ, so we
19-
/// match on either.
16+
/// True if the error is `ENOTSUP` / `EOPNOTSUPP` (same errno on Linux,
17+
/// distinct on the BSDs).
2018
#[cfg(unix)]
2119
fn is_xattr_unsupported(err: &std::io::Error) -> bool {
2220
matches!(
@@ -30,62 +28,55 @@ fn is_xattr_unsupported(_err: &std::io::Error) -> bool {
3028
false
3129
}
3230

33-
/// Copies extended attributes (xattrs) from one file or directory to another.
34-
///
35-
/// Returns `Ok(())` if the destination filesystem signals that xattrs are
36-
/// not supported (`ENOTSUP` / `EOPNOTSUPP`), since cross-filesystem moves
37-
/// onto e.g. tmpfs without xattr support are a legitimate scenario. All
38-
/// other errors propagate so the caller can surface (for example)
39-
/// permission failures on `security.*` namespaces.
40-
///
41-
/// # Arguments
42-
///
43-
/// * `source` - A reference to the source path.
44-
/// * `dest` - A reference to the destination path.
31+
/// Copies extended attributes (xattrs) from one path to another.
32+
/// All errors propagate, including `ENOTSUP` / `EOPNOTSUPP`; for
33+
/// best-effort callers see [`copy_xattrs_ignore_unsupported`].
4534
pub fn copy_xattrs<P: AsRef<Path>>(source: P, dest: P) -> std::io::Result<()> {
46-
let attrs = match xattr::list(&source) {
47-
Ok(a) => a,
48-
Err(e) if is_xattr_unsupported(&e) => return Ok(()),
49-
Err(e) => return Err(e),
50-
};
51-
for attr_name in attrs {
35+
for attr_name in xattr::list(&source)? {
5236
if let Some(value) = xattr::get(&source, &attr_name)? {
53-
if let Err(e) = xattr::set(&dest, &attr_name, &value) {
54-
if is_xattr_unsupported(&e) {
55-
return Ok(());
56-
}
57-
return Err(e);
58-
}
37+
xattr::set(&dest, &attr_name, &value)?;
5938
}
6039
}
6140
Ok(())
6241
}
6342

43+
/// Like [`copy_xattrs`], but maps `ENOTSUP` / `EOPNOTSUPP` to `Ok(())`
44+
/// for callers where xattr preservation is best-effort.
45+
pub fn copy_xattrs_ignore_unsupported<P: AsRef<Path>>(source: P, dest: P) -> std::io::Result<()> {
46+
match copy_xattrs(source, dest) {
47+
Ok(()) => Ok(()),
48+
Err(e) if is_xattr_unsupported(&e) => Ok(()),
49+
Err(e) => Err(e),
50+
}
51+
}
52+
6453
/// Copies xattrs between two open file descriptors. Pins both inodes so
6554
/// list/get/set calls cannot be redirected by a concurrent renamer, unlike
66-
/// the path-based [`copy_xattrs`]. `ENOTSUP` / `EOPNOTSUPP` is treated as
67-
/// success.
55+
/// the path-based [`copy_xattrs`].
6856
#[cfg(unix)]
6957
pub fn copy_xattrs_fd(source: &std::fs::File, dest: &std::fs::File) -> std::io::Result<()> {
7058
use xattr::FileExt;
71-
let attrs = match source.list_xattr() {
72-
Ok(a) => a,
73-
Err(e) if is_xattr_unsupported(&e) => return Ok(()),
74-
Err(e) => return Err(e),
75-
};
76-
for attr_name in attrs {
59+
for attr_name in source.list_xattr()? {
7760
if let Some(value) = source.get_xattr(&attr_name)? {
78-
if let Err(e) = dest.set_xattr(&attr_name, &value) {
79-
if is_xattr_unsupported(&e) {
80-
return Ok(());
81-
}
82-
return Err(e);
83-
}
61+
dest.set_xattr(&attr_name, &value)?;
8462
}
8563
}
8664
Ok(())
8765
}
8866

67+
/// Like [`copy_xattrs_fd`], but maps `ENOTSUP` / `EOPNOTSUPP` to `Ok(())`.
68+
#[cfg(unix)]
69+
pub fn copy_xattrs_fd_ignore_unsupported(
70+
source: &std::fs::File,
71+
dest: &std::fs::File,
72+
) -> std::io::Result<()> {
73+
match copy_xattrs_fd(source, dest) {
74+
Ok(()) => Ok(()),
75+
Err(e) if is_xattr_unsupported(&e) => Ok(()),
76+
Err(e) => Err(e),
77+
}
78+
}
79+
8980
/// Like `copy_xattrs`, but skips the security.selinux attribute.
9081
#[cfg(unix)]
9182
pub fn copy_xattrs_skip_selinux<P: AsRef<Path>>(source: P, dest: P) -> std::io::Result<()> {

0 commit comments

Comments
 (0)