bug#80060: mv: move files across bind mounts using the rename syscall
On 12/23/25 20:09, Paul Eggert wrote: On 2025-12-23 04:27, Pádraig Brady wrote: 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. Plus, it's not like mv is the only app with this efficiency issue. Why should all those apps be modified to work around the inefficiency when the kernel can do the job right once and for all? yes, that's definitely kernel business. Even if it's a bind mount and therefore part of the same file system, it could e.g. be read-only on VFS level: $ date > /root/file $ mount --bind -r /root /mnt $ ls -li /root/file /mnt/file 525930 -rw-r--r-- 1 root root 29 Jan 27 22:50 /mnt/file 525930 -rw-r--r-- 1 root root 29 Jan 27 22:50 /root/file $ mv /mnt/file /root/file2 mv: cannot remove '/mnt/file': Read-only file system $ mv /root/file /mnt/file2 mv: inter-device move failed: '/root/file' to '/mnt/file2'; unable to remove target: Read-only file system $ mv /mnt/file /mnt/file2 mv: cannot move '/mnt/file' to '/mnt/file2': Read-only file system $ mv -v /root/file /root/file2 renamed '/root/file' -> '/root/file2' Have a nice day, Berny
bug#80060: mv: move files across bind mounts using the rename syscall
On Tue, 23 Dec 2025 at 10:08, Andrei Topala wrote: > 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. > How does `mv` know that it's a problem, rather than working as intended by the sysadmin? One use-case for bind mounts is specifically to prevent `rename()` and/or `link()` into or out of a directory, and defeating this could impact system security. -Martin
bug#80060: mv: move files across bind mounts using the rename syscall
On 2025-12-23 04:27, Pádraig Brady wrote: 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. Plus, it's not like mv is the only app with this efficiency issue. Why should all those apps be modified to work around the inefficiency when the kernel can do the job right once and for all?
bug#80060: mv: move files across bind mounts using the rename syscall
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/msg2.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.
bug#80060: mv: move files across bind mounts using the rename syscall
> 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
Andrei
