fix(io): respect mode option when copying files with Bun.write() (#25906)

## Summary
- Fixes #25903 - `Bun.write()` mode option ignored when copying from
`Bun.file()`
- The destination file now correctly uses the specified `mode` option
instead of default permissions
- Works on Linux (via open flags), macOS (chmod after clonefile), and
Windows (chmod after copyfile)

## Test plan
- [x] Added regression test in `test/regression/issue/25903.test.ts`
- [x] Test passes with `bun bd test test/regression/issue/25903.test.ts`
- [x] Test fails with `USE_SYSTEM_BUN=1 bun test
test/regression/issue/25903.test.ts` (verifies the bug exists)

## Changes
- `src/bun.js/webcore/Blob.zig`: Add `mode` field to `WriteFileOptions`
and parse from options
- `src/bun.js/webcore/blob/copy_file.zig`: Use `destination_mode` in
`CopyFile` struct and `doOpenFile`
- `packages/bun-types/bun.d.ts`: Add `mode` option to BunFile copy
overloads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
robobun
2026-01-08 17:51:42 -08:00
committed by GitHub
parent c90c0e69cb
commit 50daf5df27
4 changed files with 292 additions and 3 deletions

View File

@@ -842,6 +842,20 @@ declare module "bun" {
destination: BunFile,
input: BunFile,
options?: {
/**
* Set the file permissions of the destination when it is created or overwritten.
*
* Must be a valid Unix permission mode (0 to 0o777 / 511 in decimal).
* If omitted, defaults to the system default based on umask (typically 0o644).
*
* @throws {RangeError} If the mode is outside the valid range (0 to 0o777).
*
* @example
* ```ts
* await Bun.write(Bun.file("./secret.txt"), Bun.file("./source.txt"), { mode: 0o600 });
* ```
*/
mode?: number;
/**
* If `true`, create the parent directory if it doesn't exist. By default, this is `true`.
*
@@ -875,6 +889,20 @@ declare module "bun" {
destinationPath: PathLike,
input: BunFile,
options?: {
/**
* Set the file permissions of the destination when it is created or overwritten.
*
* Must be a valid Unix permission mode (0 to 0o777 / 511 in decimal).
* If omitted, defaults to the system default based on umask (typically 0o644).
*
* @throws {RangeError} If the mode is outside the valid range (0 to 0o777).
*
* @example
* ```ts
* await Bun.write("./secret.txt", Bun.file("./source.txt"), { mode: 0o600 });
* ```
*/
mode?: number;
/**
* If `true`, create the parent directory if it doesn't exist. By default, this is `true`.
*