From 2c87dfa02a94905c86c47a93a34d1c2e300ba737 Mon Sep 17 00:00:00 2001 From: Alessio Attilio Date: Thu, 17 Sep 2026 23:39:15 +0200 Subject: [PATCH] cp: ignore unsupported xattr errors on platforms without xattr support --- src/uu/cp/src/cp.rs | 50 ++++++++++++++++++-------- src/uucore/src/lib/features/fsxattr.rs | 35 +++++++++++++----- 2 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 1aea1711349..e66ada34d4f 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1383,26 +1383,28 @@ fn parse_path_args( Ok((paths, target)) } -/// Check if an error is ENOTSUP/EOPNOTSUPP (operation not supported). -/// This is used to suppress xattr errors on filesystems that don't support them. fn is_enotsup_error(error: &CpError) -> bool { - #[cfg(unix)] - const EOPNOTSUPP: i32 = libc::EOPNOTSUPP; - #[cfg(not(unix))] - const EOPNOTSUPP: i32 = 95; - match error { CpError::IoErr(e) | CpError::IoErrContext(e, _) | CpError::SelinuxContextIoErr(e, _) => { - let raw = e.raw_os_error(); - // WASI's sandbox has no chmod/chown syscalls at all (not merely an - // unsupported combination of flags), so `fs::set_permissions` and - // friends always fail with ENOSYS there. Treat that the same as - // EOPNOTSUPP for optional preservation. + if e.kind() == io::ErrorKind::Unsupported { + return true; + } + #[cfg(unix)] + if matches!( + e.raw_os_error(), + Some(code) if code == libc::ENOTSUP || code == libc::EOPNOTSUPP || code == libc::ENOSYS + ) { + return true; + } #[cfg(target_os = "wasi")] - if raw == Some(libc::ENOSYS) { + if e.raw_os_error() == Some(libc::ENOSYS) { + return true; + } + #[cfg(not(any(unix, target_os = "wasi")))] + if matches!(e.raw_os_error(), Some(95)) { return true; } - raw == Some(EOPNOTSUPP) + false } _ => false, } @@ -3169,7 +3171,7 @@ fn disk_usage_directory(p: &Path) -> io::Result { #[cfg(test)] mod tests { - use crate::{Attributes, Preserve, aligned_ancestors, localize_to_target}; + use super::*; use std::path::Path; #[test] @@ -3261,4 +3263,22 @@ mod tests { assert_eq!(unioned.mode, Preserve::No { explicit: true }); assert_eq!(unioned.timestamps, Preserve::Yes { required: true }); } + + #[test] + fn test_is_enotsup_error() { + let unsupported_err = + CpError::IoErr(io::Error::new(io::ErrorKind::Unsupported, "unsupported")); + assert!(is_enotsup_error(&unsupported_err)); + + let other_err = CpError::IoErr(io::Error::new(io::ErrorKind::NotFound, "not found")); + assert!(!is_enotsup_error(&other_err)); + + #[cfg(unix)] + { + for errno in [libc::ENOTSUP, libc::EOPNOTSUPP, libc::ENOSYS] { + let err = CpError::IoErr(io::Error::from_raw_os_error(errno)); + assert!(is_enotsup_error(&err)); + } + } + } } diff --git a/src/uucore/src/lib/features/fsxattr.rs b/src/uucore/src/lib/features/fsxattr.rs index b3762ced37c..3f2bdf427cb 100644 --- a/src/uucore/src/lib/features/fsxattr.rs +++ b/src/uucore/src/lib/features/fsxattr.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore getxattr posix_acl_default posix_acl_access ENOTSUP EOPNOTSUPP renamer +// spell-checker:ignore getxattr posix_acl_default posix_acl_access ENOTSUP EOPNOTSUPP ENOSYS renamer //! Set of functions to manage xattr on files and dirs use itertools::Itertools; @@ -13,19 +13,18 @@ use std::ffi::{OsStr, OsString}; use std::os::unix::ffi::OsStrExt; use std::path::Path; -/// True if the error is `ENOTSUP` / `EOPNOTSUPP` (same errno on Linux, -/// distinct on the BSDs). #[cfg(unix)] fn is_xattr_unsupported(err: &std::io::Error) -> bool { - matches!( - err.raw_os_error(), - Some(e) if e == libc::ENOTSUP || e == libc::EOPNOTSUPP - ) + err.kind() == std::io::ErrorKind::Unsupported + || matches!( + err.raw_os_error(), + Some(e) if e == libc::ENOTSUP || e == libc::EOPNOTSUPP || e == libc::ENOSYS + ) } #[cfg(not(unix))] -fn is_xattr_unsupported(_err: &std::io::Error) -> bool { - false +fn is_xattr_unsupported(err: &std::io::Error) -> bool { + err.kind() == std::io::ErrorKind::Unsupported } /// Copies extended attributes (xattrs) from one path to another. @@ -499,4 +498,22 @@ mod tests { test_value ); } + + #[test] + fn test_is_xattr_unsupported() { + let unsupported = + std::io::Error::new(std::io::ErrorKind::Unsupported, "unsupported platform"); + assert!(is_xattr_unsupported(&unsupported)); + + let other = std::io::Error::new(std::io::ErrorKind::NotFound, "not found"); + assert!(!is_xattr_unsupported(&other)); + + #[cfg(unix)] + { + for errno in [libc::ENOTSUP, libc::EOPNOTSUPP, libc::ENOSYS] { + let err = std::io::Error::from_raw_os_error(errno); + assert!(is_xattr_unsupported(&err)); + } + } + } }