> 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

Reply via email to