bug#80060: mv: move files across bind mounts using the rename syscall

Pádraig Brady <[email protected]> Tue, 23 Dec 2025 12:27:34 +0000
Newsgroups gmane.comp.gnu.core-utils.bugs
Message-ID <[email protected]>
On 23/12/2025 00:08, Andrei Topala wrote:
>> Is there a downside to always trying rename() first and doing the copy
> 
>> if it fails? That's approximately what toybox's mv does...
> 
> 
> mv already tries rename() first. The problem is that rename() returns
> 
> EXDEV even when source and destination are on the same physical device
> 
> but accessed through different bind mount points.
> 
> 
> For example, with these bind mounts on the same device:
> 
> 
>    /mnt/full      root=/
> 
>    /mnt/data      root=/data
> 
> 
> Moving /mnt/data/file to /mnt/full/backup/ fails with EXDEV because the
> 
> kernel sees different mount points, forcing an unnecessary copy+delete.
> 
> 
> [1] solves this by iterating through all mount/realpath combinations
> 
> until rename() succeeds. I've implemented an approach using gnulib's
> 
> mountlist.h [2].
> 
> 
> I'm thinking of:
> 
> 1. Parse /proc/self/mountinfo via read_file_system_list()
> 
> 2. Find mount entries for source and destination paths
> 
> 3. If different me_dev, fall back to copy (truly different devices)
> 
> 4. Compute device_path = me_mntroot + path_relative_to_mountpoint
> 
> 5. Translate one path through the other's mount:
> 
>     - If dst's device_path is under src_mount's root, translate dst
> 
>     - Otherwise translate src through dst_mount
> 
> 6. rename() with translated path succeeds - both paths now resolve
> 
>       through the same mount point
> 
> 
> Example translation:
> 
>    /mnt/data/file
> 
>    → mount: root="/data", mountdir="/mnt/data"
> 
>    → device_path: /data/file
> 
>    → through /mnt/full (root="/"): /mnt/full/data/file
> 
> 
> 
>    rename("/mnt/full/data/file", "/mnt/full/backup/file") succeeds
> 
> 
> This avoids the brute-force iteration in [1] by computing which mount
> 
> can "see" both paths (the one with the wider root).
> 
> 
> I haven't signed FSF copyright assignment yet but am willing to do so
> 
> if there's interest.
> 
> 
> [1] https://github.com/milahu/move-files-across-bind-mounts
> 
> [2]
> https://github.com/coreutils/coreutils/commit/a961fc317951b92c2468771f8a0d2895e94ac2a3

Referencing the original discussion:
https://lists.gnu.org/archive/html/coreutils/2024-12/msg00002.html
The code is good, and thanks for talking time to propose a solution.
However my conclusion from the original thread is still the same.
I don't think it's appropriate for mv to do this expensive/error prone
path manipulation for each file being copied across devices.
IMHO this is more appropriate for the kernel,
which has a more holistic view of the mount space.

thanks,
Padraig.