mv: cross-device move drops the rest of the xattrs after one failure, and reports success
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 24.1k
- Forks
- 2k
- Avg merge
- 1d 5h
- Merged PRs (30d)
- 365
Description
On a cross-filesystem move, the xattr copy loop stops at the first per-attribute error. mv then discards that error, unlinks the source and exits 0 with no diagnostic — so the destination silently ends up with fewer attributes than the source, including attributes the destination filesystem fully supports.
Line references are against main at be00b4c4e.
Two separable defects
1. The loop aborts the whole list (src/uucore/src/lib/features/fsxattr.rs)
copy_xattrs (:34), copy_xattrs_fd (:56) and copy_xattrs_skip_selinux (:80) all ? out on the first failure:
for attr_name in xattr::list(&source)? {
if let Some(value) = xattr::get(&source, &attr_name)? {
xattr::set(&dest, &attr_name, &value)?; // aborts the remaining attributes
}
}
The *_ignore_unsupported wrappers only map ENOTSUP/EOPNOTSUPP, so ENOSPC, EPERM, EACCES, EDQUOT and E2BIG abort the rest of the list. GNU reports the failing attribute and continues with the others.
This is shared uucore code, so it also affects cp --preserve=xattr / cp -a.
2. mv throws the result away
src/uu/mv/src/mv.rs:1043, :1423 and :1488 are each let _ = fsxattr::copy_xattrs*(...). This is what makes the loss silent. cp does propagate the error (cp.rs:1847), so this half is mv-only.
Reproduction
Source on tmpfs (accepts a 60 KB value), destination on ext4 (~4 KB per-attribute limit), file carrying both user.big and security.capability in that order:
$ setcap cap_net_bind_service=ep src/art # plus a 60000-byte user.big
$ uutils-mv src/art dst/art ; echo "exit: $?"
exit: 0 # no output at all
$ listxattr dst/art -> ['security.selinux']
$ getcap dst/art -> (nothing)
$ gnu-mv src/art2 dst/art2 ; echo "exit: $?"
mv: setting attribute 'user.big': No space left on device
exit: 0
$ listxattr dst/art2 -> ['security.selinux', 'security.capability']
$ getcap dst/art2 -> cap_net_bind_service=ep
Both remove the source. Only GNU preserves the capability, which the destination supports and which failed to be copied only because an unrelated earlier attribute did.
Control: GNU coreutils 9.11.130 (master).
Expected
Record the failure, keep going, and report each failed attribute on stderr, while keeping exit 0 for a completed move — GNU's behaviour.
Suggested test
Two attributes in a known order; force the first destination setxattr to fail; assert the second is still attempted and preserved.
Reported by Hongkai Chen (SEFCOM Lab, Arizona State University). Triaged as a correctness bug rather than a security issue — the demonstrated outcome is a deployed file with fewer privileges than intended.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with copy_xattrs, copy_xattrs_fd, and copy_xattrs_skip_selinux in src/uucore/src/lib/features/fsxattr.rs, then inspect the let _ calls at src/uu/mv/src/mv.rs:1043, :1423, and :1488. Run the suggested two-attribute reproduction or relevant mv tests. Done means failed attributes are reported, later attributes are still attempted, and mv no longer silently discards the result.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cli, operating-systems
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100