diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index 97dbe3e6e..0e6cbaa36 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -921,7 +921,7 @@ fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> { } #[cfg(not(any(target_os = "macos", target_os = "redox")))] { - let _ = fsxattr::copy_xattrs(from, to); + let _ = fsxattr::copy_xattrs_ignore_unsupported(from, to); } let _ = preserve_ownership(from, to); fs::remove_file(from) @@ -1250,7 +1250,7 @@ fn copy_file_with_hardlinks_helper( // Copy xattrs, ignoring ENOTSUP errors (filesystem doesn't support xattrs) #[cfg(all(unix, not(any(target_os = "macos", target_os = "redox"))))] { - let _ = fsxattr::copy_xattrs(from, to); + let _ = fsxattr::copy_xattrs_ignore_unsupported(from, to); } // Preserve ownership (uid/gid) from the source let _ = preserve_ownership(from, to); @@ -1315,7 +1315,7 @@ fn rename_file_fallback( #[cfg(not(any(target_os = "macos", target_os = "redox")))] { - let _ = fsxattr::copy_xattrs_fd(&src_file, &dst_file); + let _ = fsxattr::copy_xattrs_fd_ignore_unsupported(&src_file, &dst_file); } // chown before chmod: chown(2) clears setuid/setgid for non-root, diff --git a/src/uucore/src/lib/features/fsxattr.rs b/src/uucore/src/lib/features/fsxattr.rs index 0c581690a..1217d58e3 100644 --- a/src/uucore/src/lib/features/fsxattr.rs +++ b/src/uucore/src/lib/features/fsxattr.rs @@ -13,10 +13,8 @@ use std::ffi::{OsStr, OsString}; use std::os::unix::ffi::OsStrExt; use std::path::Path; -/// Returns true if the error is the kernel/filesystem signaling that -/// extended attributes are not supported (`ENOTSUP` / `EOPNOTSUPP`). -/// On Linux these are the same errno; on the BSDs they differ, so we -/// match on either. +/// 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!( @@ -30,62 +28,55 @@ fn is_xattr_unsupported(_err: &std::io::Error) -> bool { false } -/// Copies extended attributes (xattrs) from one file or directory to another. -/// -/// Returns `Ok(())` if the destination filesystem signals that xattrs are -/// not supported (`ENOTSUP` / `EOPNOTSUPP`), since cross-filesystem moves -/// onto e.g. tmpfs without xattr support are a legitimate scenario. All -/// other errors propagate so the caller can surface (for example) -/// permission failures on `security.*` namespaces. -/// -/// # Arguments -/// -/// * `source` - A reference to the source path. -/// * `dest` - A reference to the destination path. +/// Copies extended attributes (xattrs) from one path to another. +/// All errors propagate, including `ENOTSUP` / `EOPNOTSUPP`; for +/// best-effort callers see [`copy_xattrs_ignore_unsupported`]. pub fn copy_xattrs>(source: P, dest: P) -> std::io::Result<()> { - let attrs = match xattr::list(&source) { - Ok(a) => a, - Err(e) if is_xattr_unsupported(&e) => return Ok(()), - Err(e) => return Err(e), - }; - for attr_name in attrs { + for attr_name in xattr::list(&source)? { if let Some(value) = xattr::get(&source, &attr_name)? { - if let Err(e) = xattr::set(&dest, &attr_name, &value) { - if is_xattr_unsupported(&e) { - return Ok(()); - } - return Err(e); - } + xattr::set(&dest, &attr_name, &value)?; } } Ok(()) } +/// Like [`copy_xattrs`], but maps `ENOTSUP` / `EOPNOTSUPP` to `Ok(())` +/// for callers where xattr preservation is best-effort. +pub fn copy_xattrs_ignore_unsupported>(source: P, dest: P) -> std::io::Result<()> { + match copy_xattrs(source, dest) { + Ok(()) => Ok(()), + Err(e) if is_xattr_unsupported(&e) => Ok(()), + Err(e) => Err(e), + } +} + /// Copies xattrs between two open file descriptors. Pins both inodes so /// list/get/set calls cannot be redirected by a concurrent renamer, unlike -/// the path-based [`copy_xattrs`]. `ENOTSUP` / `EOPNOTSUPP` is treated as -/// success. +/// the path-based [`copy_xattrs`]. #[cfg(unix)] pub fn copy_xattrs_fd(source: &std::fs::File, dest: &std::fs::File) -> std::io::Result<()> { use xattr::FileExt; - let attrs = match source.list_xattr() { - Ok(a) => a, - Err(e) if is_xattr_unsupported(&e) => return Ok(()), - Err(e) => return Err(e), - }; - for attr_name in attrs { + for attr_name in source.list_xattr()? { if let Some(value) = source.get_xattr(&attr_name)? { - if let Err(e) = dest.set_xattr(&attr_name, &value) { - if is_xattr_unsupported(&e) { - return Ok(()); - } - return Err(e); - } + dest.set_xattr(&attr_name, &value)?; } } Ok(()) } +/// Like [`copy_xattrs_fd`], but maps `ENOTSUP` / `EOPNOTSUPP` to `Ok(())`. +#[cfg(unix)] +pub fn copy_xattrs_fd_ignore_unsupported( + source: &std::fs::File, + dest: &std::fs::File, +) -> std::io::Result<()> { + match copy_xattrs_fd(source, dest) { + Ok(()) => Ok(()), + Err(e) if is_xattr_unsupported(&e) => Ok(()), + Err(e) => Err(e), + } +} + /// Like `copy_xattrs`, but skips the security.selinux attribute. #[cfg(unix)] pub fn copy_xattrs_skip_selinux>(source: P, dest: P) -> std::io::Result<()> {