mv:fix file ownership changes when a file is mv'ed by root to a different file system#9672
mv:fix file ownership changes when a file is mv'ed by root to a different file system#9672mattsu2020 wants to merge 9 commits intouutils:mainfrom
Conversation
|
GNU testsuite comparison: |
This comment was marked as resolved.
This comment was marked as resolved.
| .env("BASE", &base) | ||
| .env("UUTILS", &scene.bin_path) | ||
| .output() | ||
| .expect("failed to run unshare"); |
There was a problem hiding this comment.
It might better to create a new function for uhshare (at different PR).
Ref: #9973
|
GNU testsuite comparison: |
| } | ||
|
|
||
| #[cfg(unix)] | ||
| fn try_preserve_ownership(from_meta: &fs::Metadata, to: &Path, follow_symlinks: bool) { |
There was a problem hiding this comment.
document this function please
src/uu/mv/src/mv.rs
Outdated
|
|
||
| unsafe { | ||
| if follow_symlinks { | ||
| let _ = libc::chown(to_cstr.as_ptr(), uid, gid); |
There was a problem hiding this comment.
please manage the errors
| } | ||
|
|
||
| #[cfg(unix)] | ||
| fn try_preserve_permissions(from_meta: &fs::Metadata, to: &Path) { |
There was a problem hiding this comment.
same, please document it
src/uu/mv/src/mv.rs
Outdated
|
|
||
| // Keep mode bits only (file type bits are not allowed in chmod). | ||
| let mode = from_meta.mode() & 0o7777; | ||
| let _ = fs::set_permissions(to, fs::Permissions::from_mode(mode)); |
Merging this PR will not alter performance
Comparing Footnotes
|
|
GNU testsuite comparison: |
src/uu/mv/src/mv.rs
Outdated
| fn copy_symlink(from: &Path, to: &Path) -> io::Result<()> { | ||
| let from_meta = from.symlink_metadata()?; | ||
| let path_symlink_points_to = fs::read_link(from)?; | ||
| unix::fs::symlink(path_symlink_points_to, to).map(|_| { |
There was a problem hiding this comment.
it should copy xattrs like rename_symlink_fallback does. no ?
|
GNU testsuite comparison: |
|
GNU testsuite comparison: |
|
I think we have a chmod helper that we can use here that implements all of the logic in the PR already in the uucore perms lib |
|
and it conflicts |
wrap_chown is too high-level for mv compatibility logic. It returns formatted Result<String, String>, so mv cannot inspect errno to implement GNU-like behavior (owner+group attempt, optional group-only fallback, and selective suppression for EPERM/EACCES/EINVAL). To avoid behavior regressions, I’d like to add a public low-level helper in uucore::perms that returns io::Result<()> (preserving errno), then switch mv to that helper while keeping GNU-compatible branching in mv. This still removes syscall duplication, but without changing user-visible behavior. |
… Unix Add functions to preserve file ownership (UID/GID) and permissions (mode) during mv operations when fs::rename fails and falls back to copy+remove. This ensures consistency with GNU mv behavior across filesystems, applying preservation in rename fallbacks for files, directories, symlinks, FIFOs, and recursive copies. Changes are Unix-specific, using libc::chown/lchown and fs::set_permissions.
Add a new Linux-only test to verify cross-device move behavior using user namespaces and tmpfs mounts, avoiding sudo. This mirrors GNU's part-fail scenario for directories containing dangling symlinks, ensuring the mv command preserves symlinks correctly in rootless environments.
- Modified HardlinkGroupScanner to skip symlinks using symlink_metadata, as hardlink preservation does not apply to them - Added copy_symlink functions for Unix, Windows, and other platforms to copy symlinks without dereferencing - Updated copy_dir_contents_recursive to detect and copy symlinks, including verbose output, preventing dereferencing during directory moves
Updated the copy_symlink function to use .map() combinator instead of .and_then() for clarity, as the closure performs a side effect (preserving ownership) and returns a unit value without chaining Results. This improves code readability and appropriateness of the combinator used.
Simplify the map closure by removing the explicit return of an empty tuple, as it is implicit in Rust when no value is needed. This improves code clarity without affecting functionality.
…ervation - Capture return values from chown/lchown and set_permissions calls - Emit warnings for non-permission errors to inform users of preservation failures - Keep operations non-fatal as preservation is best-effort on Unix systems
Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
Replace the duplicated symlink handling logic in rename_symlink_fallback functions across Unix, Windows, and other platforms with a call to the existing copy_symlink function. This reduces code duplication and centralizes symlink copying logic while maintaining the same functionality of copying the symlink and then removing the original file.
|
GNU testsuite comparison: |
Add a check to skip the inter_partition_copying test when source and destination directories are on the same filesystem, preventing false test failures when /dev/shm is not available or mounted on the same device as the source.
|
GNU testsuite comparison: |
Summary
Fix cross-filesystem
mv(EXDEV copy+delete fallback) so that file ownership does not change to the invoking user (e.g.root) when moving a file across filesystems.Fixes #9635.
Background / Problem
When
mvcannotrename(2)across devices (EXDEV), uutils falls back to copy+delete. The copy path usedstd::fs::copy, which creates the destination owned by the caller. Ifrootmoves a file owned by another user to a different filesystem, the destination ends up owned byroot(compatibility + security concern).Changes
lchown, do not follow)chownto keep correct permission bits (sincechownmay clear setuid/setgid)./dev/shmtmpfs).bind_instead_of_map) in the copy path.Testing
cargo test -p uu_mvcargo clippy -p uu_mv -- -D warningsrelated
#9635