Repository navigation
Conversation
…allback When renameat returns EXDEV, the mv builtin copies the source and removes it. The copy now gets the atime and mtime of the source, for a regular file, a directory, and a symbolic link. The new symbolic link also gets the owner of the source. Adds bun_sys::fchownat and bun_sys::utimensat.
There was a problem hiding this comment.
Beyond the inline findings, I also checked the lutimens refactor in src/sys/lib.rs for behavior drift — Fd::cwd() is Fd(libc::AT_FDCWD) on unix and the path/flags/timespec arguments are unchanged, so existing callers still issue the same utimensat(AT_FDCWD, ..., AT_SYMLINK_NOFOLLOW) call.
Extended reasoning...
Findings were reported inline, so this body only records what else was examined. The lutimens -> utimensat refactor was the one non-mv change that could silently alter existing behavior; tracing Fd::cwd() to src/bun_core/util.rs:980 confirms it wraps AT_FDCWD on unix, and the new wrapper passes the caller's flags verbatim, so the refactor is behavior-preserving. The mv.rs call sites are all #[cfg(unix)]-gated and the new wrappers live in posix_impl, consistent with the PR's claim of a Windows cargo check. The inline findings (symlink chown race, unix-only coverage) still warrant a human look.
|
Both inline findings are answered in their threads. No code change.
|
|
Updated 11:16 PM PT - Sep 18th, 2026
✅ @robobun, your commit 9f237fba3e665999781a05ab47afdbec1172623f passed in 🧪 To try this PR locally: bunx bun-pr 43397That installs a local version of the PR into your bun-43397 --bun |
Stacked on #43394. The base of this PR is that branch. The diff here is the last commit only.
Problem
mvcopies and removes the source whenrenameatreturnsEXDEV. The copy keeps the mode and the owner. It keeps no timestamps, so the result ofmvdepends on the filesystem layout. A move from/tmpto/dev/shmgives the copy the time of the copy, not themtimeof the source. Arenameon one filesystem keeps every attribute. coreutilsmvkeeps the times on its copy path too.mv. The symlink branch ofmove_across_devices(src/runtime/shell/builtin/mv.rs:532) callssymlinkatalone.Fix
copy_metadata(thecopy_owner_and_modehelper of shell mv: drop set-uid and set-gid across devices when the owner cannot be kept #43394, renamed) callsfutimenswith theatimeandmtimeof the source, thenfchown, thenfchmod. Times come first: afterfchowngives the copy to another user, the mover may lack the right to set them. coreutils uses the same order. For a directory the helper runs after the children are copied, because each child copy bumps themtimeof the new directory.utimensatandfchownat, both withAT_SYMLINK_NOFOLLOW, aftersymlinkat.fchownandfchmod.bun_sysgetsfchownatand a flag-takingutimensat.lutimensnow calls the latter, with the same arguments as before.test/js/bun/shell/commands/mv.test.ts, two new cases incross-device (EXDEV). Both fail on bun 1.4.3 and on the base branch. Also the rest oftest/js/bun/shell/commands/, andcargo checkforaarch64-apple-darwinandx86_64-pc-windows-msvc.Background
EXDEVis the errno for arenameacross filesystems. The kernel cannot move the inode, somvhas to copy the data and every attribute it can, then remove the source.futimenssets the access and modification times of an open file.utimensatdoes the same through a directory fd and a name. WithAT_SYMLINK_NOFOLLOWbothutimensatandfchownatact on the link itself, not on its target. A symbolic link has no fd, so the link branch needs theatforms.CAP_FOWNER. The owner of the copy is the mover untilfchownruns.Notes
Not in this PR: POSIX ACLs and extended attributes. #43393 names them. They need new
bun_syswrappers with different signatures on Linux and macOS, so they are a separate change.Repro on 1.4.3 (Linux,
/tmpand/dev/shmon different filesystems):Before: the time of the copy. After:
2001-02-03 04:05:06. The same holds for a directory (touch -don the directory) and a symbolic link (touch -h -d).The symlink owner test runs as root only, because only root can give a link to another user. It sets the owner of the source link to
65534withlchownSyncand checkslstatof the copy.The
atimeof a regular file comes from thefstattaken beforecopy_filereads the data, so the copy gets theatimefrom before the move.The test file also has two pre-existing
lsfailures when run as root (permission denied directory). They fail on main as root too and are unrelated.Self-reviewed: the review found the conflict with #43394 on the same
fchown/fchmodlines and asked for the times-first order. Both are addressed by the stack and the helper order.no test proof · iteration 0 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/bun/shell/commands/mv.test.ts