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

2026-01-27 Thread Bernhard Voelker

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

2025-12-28 Thread Martin D Kealey
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

2025-12-23 Thread Paul Eggert

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

2025-12-23 Thread Pádraig Brady

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

2025-12-23 Thread Andrei Topala
> 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