Repository navigation
mv: create cross-device directory entries relative to the destination descriptor - #14971
abendrothj wants to merge 8 commits into
Conversation
d0cf9fe to
f3fc847
Compare
f3fc847 to
e3fda2a
Compare
Merging this PR will degrade performance by 10.68%
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
Unable to generate the flame graphsThe performance report has correctly been generated, but there was an internal error while generating the flame graphs for this run. We're working on fixing the issue. Feel free to contact us on Discord or at support@codspeed.io if the issue persists. |
Open a directory readable, and on EACCES retry with O_PATH or O_SEARCH, which anchor *at calls without read access. The targets with either flag are named by the has_o_path and has_o_search cfg aliases. If the retry fails, its own error is returned.
Move the temporary name and renameat logic of replace_link into replace_entry_at, which creates the entry through a callback relative to an open directory, so other callers can replace entries the same way. The parent is now opened with DirFd::open_anchor, so ln -sf replacing a link in a directory with write and search but no read permission works, as it does with GNU, instead of failing with EACCES.
Create fifos, sockets and device nodes relative to an open directory. nix and rustix do not provide mknodat on Apple targets, so this calls libc directly.
A cross-device move of a socket or device node went through the regular file copy, which removed the destination and then failed to open the source. A fifo also removed the destination before creating the new one, and lost its mode. Fifos, sockets and device nodes are now created with their mode and ownership in a private directory next to the destination and renamed over it, so the destination is kept if the node cannot be created, and the ownership and mode cannot land on another file linked over the destination name meanwhile. The private directory is made 0700 by name right after it is created, before it is opened, without changing the umask; the check after the open rejects a directory moved there in between. As for regular files, setuid and setgid are dropped when the ownership cannot be kept. Fixes uutils#13145
Sockets and device nodes inside a directory moved across devices went through the regular file copy, which cannot open them, so the move failed. Fifos were recreated with mode 0666 minus the umask. Recreate all of them with copy_special_file, which keeps the mode.
A caller creating a file can then keep writing through the descriptor it opened. Also make link_at public, for creating links relative to an open directory, and add DirFd::open_subdir_anchor, which opens a subdirectory without following a symlink and, where the platform allows, without needing read permission, for creating entries in it.
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 while the copy was running was followed, so the content went wherever it pointed. Hold each destination directory open, search-only where possible, and create files, subdirectories, symlinks, special files and hard links relative to it without following symlinks. An entry that appears at a name is replaced through replace_entry_at. Regular files share copy_file_data with the single file fallback, which sets ownership and mode through the descriptor. The names of each source directory are read before descending, so only one descriptor stays open per level. Closes uutils#14991
b3b9dde to
52022fc
Compare
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. An entry that shows up at a name is replaced through
replace_entry_at. Regular files sharecopy_file_datawith the single-file fallback, which sets ownership and mode on the open descriptor, using libcfchownso fakeroot still works. 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 final directory xattr reopen. Run on macOS and Linux. On Debian GNU/Hurd the mv tests fail only where main does (the new ones are Linux-only), and a cross-device move of a 0640 FIFO, alone or in a directory, keeps its mode as with GNU 9.10. AIX is only cross-checked with clippy.
Based on #15123; until the stack below it merges, the diff here shows those commits too.
Closes #14991