Bun.write: resolve to the byte count on the macOS fcopyfile path
Merged
RO
2mo ago+58+58+01234567890123456789-13-13-01234567890123456789
Problem
- On macOS,
await Bun.write(dest, Bun.file(src))resolves to0when the destination already exists or is on another volume. The file on disk is complete. Only the resolved number is wrong. Fixes #42060. - The cause is
do_fcopy_file_with_read_write_loop_fallbackinsrc/runtime/webcore/blob/copy_file.rs:523. It never writesself.read_len, andthen()resolves the promise withself.read_len(line 163). Theclonefilepath sets it, so a fresh same-volume destination was correct.
Fix
- The helper now returns the number of bytes copied: the source size after a successful
fcopyfile, ortotal_writtenfrom the read/write fallback. run_asyncstores that count inread_len. When the destination is a slice and the source is longer, it clamps the count tomax_length, which is also the sizeftruncatetrims to. This mirrors the FreeBSD branch at lines 917-927.- Verified:
test/js/bun/io/bun-write.test.js(new test: existing destination and a sliced destination). The test only fails before the fix on macOS. On Linux thecopy_file_rangepath already records the count, so the test passes before and after there.cargo check -p bun_runtime --target aarch64-apple-darwinpasses.
Background
CopyFileis the work pool job behindBun.write(fileA, fileB)on POSIX. It picks a copy strategy per platform and stores the byte count inread_len. The JS promise resolves to that field.- On macOS,
clonefileis tried first when both sides are paths and the destination does not exist. It fails withEEXISTfor an existing destination andEXDEVacross volumes. Both cases fall back tofcopyfile, which copies the whole source through the two open fds. max_lengthis the destination window.run_asyncclamps it to the source size before the copy, somin(copied, max_length)is the number of bytes that remain after the trim.
Notes
- The issue also asks about cloning over an existing destination. That is a copy strategy change and is out of scope here. This PR only fixes the resolved value.
- While writing the test I saw that a slice on the source side (
Bun.write(dest, Bun.file(src).slice(0, n))) is ignored on the file to file path on Linux too.Blob.rs:4489passes the destination blob's offset and size toCopyFile. The test uses a destination slice, like the existingBun.file -> Bun.filetest. That is a separate bug. Bun.write > should work when copyFileRange is not available > on large filestimes out under the debug ASAN build in this container on main as well. It does not touch the macOS code.
no test proof · iteration 2 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/bun/io/bun-write.test.js