mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
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.
This commit is contained in:
+3
-3
@@ -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,
|
||||
|
||||
@@ -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<P: AsRef<Path>>(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<P: AsRef<Path>>(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<P: AsRef<Path>>(source: P, dest: P) -> std::io::Result<()> {
|
||||
|
||||
Reference in New Issue
Block a user