Repository navigation
mv: create cross-device directory entries relative to the destination descriptor - #14971
abendrothj wants to merge 9 commits into
Conversation
d0cf9fe to
f3fc847
Compare
f3fc847 to
e3fda2a
Compare
Merging this PR will regress 1 benchmark1 warning, 1 note
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
|
GNU testsuite comparison: |
e3fda2a to
8d392d1
Compare
|
Opened #14991. |
8d392d1 to
5e3cb57
Compare
5e3cb57 to
42731bd
Compare
|
this is quite a large patch, can we make it smaller? |
42731bd to
7e5ee68
Compare
|
Smaller now: it reuses |
b125862 to
b3b9dde
Compare
52022fc to
6903ee6
Compare
d767c93 to
4d87582
Compare
|
Trimmed a bit more: the fakeroot test moved into |
4d87582 to
e4aae21
Compare
|
sorry but it needs to be rebased |
| fn dest_appeared(path: &Path, e: io::Error) -> io::Error { | ||
| if e.kind() == io::ErrorKind::AlreadyExists { | ||
| io::Error::new( | ||
| io::ErrorKind::AlreadyExists, |
There was a problem hiding this comment.
two full impl DestDir blocks per platform is a lot of duplication, could the path fallback live in uucore instead?
There was a problem hiding this comment.
There's one DestDir struct now, with two small create_dest_dir versions. The path versions are only for platforms where safe_traversal isn't built at all (Redox, Windows, WASI), so uucore has nowhere to put them; elsewhere DirAnchor already falls back to the path where it can't hold the directory open. Building a path-only DirAnchor on Redox would let mv drop them; I can do that in a follow-up if you'd like.
e4aae21 to
0b6fdcb
Compare
For ln -f/-sf, cp -lf/-sf and mv's cross-device symlink. Without O_PATH or O_SEARCH (OpenBSD, Hurd) it falls back to the directory's path.
ln and mv's cross-device move use it next. The no-unique-temp-name error is now translated.
On Apple targets mknodat is looked up at run time, as it exists only from macOS 13; before that the node is created by the directory's path.
0b6fdcb to
f0b4cef
Compare
Sockets and device nodes are recreated like fifos, with their mode, and the destination is replaced only once the new node exists. Fixes uutils#13145
Sockets and device nodes in the directory no longer fail the move, and fifos keep their mode.
Entries are created through the open destination directory, so a symlink that appears in it during the copy is replaced, not written through. Closes uutils#14991
f0b4cef to
7f40614
Compare
Based on #15123; only the last three commits are new here.
When a directory move falls back to copying across filesystems, each destination entry was created by joining a path, and regular files went through
fs::copy. A symlink that appeared in the destination during the copy was followed, so content went wherever it pointed.Each destination directory is now held open (search-only where possible), and files, subdirectories, symlinks, special files and hard links are created relative to it without following symlinks. A file, link or special file that shows up at a name is replaced; a directory there fails ("dest appeared"). Regular files share
copy_file_datawith the single-file fallback, which sets ownership and mode on the open descriptor, using libcfchownso fakeroot still works;util/check-libc-interposition.shnow checks this for mv. On Linux it copies withio::copy, so a move between btrfs subvolumes still reflinks, asfs::copydid, and single-file moves now do too. On macOS,fcopyfilecopies the data and then the ACLs, extended attributes and times, whichfs::copykept for files in a directory and single-file moves didn't; if the second part fails, mv warns ("setting attributes for ...") and goes on, as for xattrs elsewhere. Source directory names are read before descending, so one descriptor stays open per level. Still by path: directory ownership, symlink xattrs, the hard link source, and the reopen that sets each directory's xattrs and mode.On Linux, a 512 MiB file moved between two btrfs subvolumes takes 1 ms against 238 ms on main, as it now reflinks; a directory already did (0.9 ms on both). Across filesystems it is about the same: 4 x 128 MiB took 238 ms against 240 ms (btrfs to tmpfs) and 97.5 against 96.8 ms on macOS (APFS to a RAM disk); 5000 small files took 236 against 248 ms on Linux and 1580 against 1530 ms on macOS, where the separate metadata call costs about 3%.
test_mv_cross_device_dir_replaces_symlink_entry_created_mid_copyplants symlinks from a thread while 200 files are copied. It can't force the timing, so a run where none lands in time passes without proving anything; on Linux it failed 10 of 10 runs without the change and passed 30 of 30 with it, in about 0.02 s. A unit test checks the same replacement deterministically, with the symlink planted beforehand. The other new tests cover a deep tree under a low descriptor limit, an owner-read-masked directory and a write/search-only destination. Run on macOS and Linux.Closes #14991