diff options
-rw-r--r-- | src/bun.js/node/node_fs.zig | 13 | ||||
-rw-r--r-- | test/js/node/fs/cp.test.ts | 39 |
2 files changed, 44 insertions, 8 deletions
diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig index 8f822448b..5407c2d5c 100644 --- a/src/bun.js/node/node_fs.zig +++ b/src/bun.js/node/node_fs.zig @@ -564,6 +564,11 @@ pub const AsyncCpTask = struct { } this.result = result; + + if (this.result == .err) { + this.result.err.path = bun.default_allocator.dupe(u8, this.result.err.path) catch ""; + } + this.globalObject.bunVMConcurrently().eventLoop().enqueueTaskConcurrent(JSC.ConcurrentTask.fromCallback(this, runFromJSThread)); } @@ -3584,7 +3589,7 @@ pub const NodeFS = struct { const dest_fd = switch (Syscall.open(dest, flags, JSC.Node.default_permission)) { .result => |result| result, - .err => |err| return Maybe(Return.CopyFile){ .err = err }, + .err => |err| return Maybe(Return.CopyFile){ .err = err.withPath(args.dest.slice()) }, }; defer { _ = std.c.ftruncate(dest_fd, @as(std.c.off_t, @intCast(@as(u63, @truncate(wrote))))); @@ -5392,11 +5397,10 @@ pub const NodeFS = struct { .ACCES, .NAMETOOLONG, .ROFS, - .NOENT, .PERM, .INVAL, => { - @memcpy(this.sync_error_buf[0..src.len], dest); + @memcpy(this.sync_error_buf[0..src.len], src); return .{ .err = err.err.withPath(this.sync_error_buf[0..src.len]) }; }, // Other errors may be due to clonefile() not being supported @@ -5792,11 +5796,10 @@ pub const NodeFS = struct { .ACCES, .NAMETOOLONG, .ROFS, - .NOENT, .PERM, .INVAL, => { - @memcpy(this.sync_error_buf[0..src.len], dest); + @memcpy(this.sync_error_buf[0..src.len], src); task.finishConcurrently(.{ .err = err.err.withPath(this.sync_error_buf[0..src.len]) }); return false; }, diff --git a/test/js/node/fs/cp.test.ts b/test/js/node/fs/cp.test.ts index 37da58843..eae8a0e77 100644 --- a/test/js/node/fs/cp.test.ts +++ b/test/js/node/fs/cp.test.ts @@ -3,7 +3,7 @@ import { describe, test, expect, jest } from "bun:test"; import { tempDirWithFiles } from "harness"; const impls = [ - // ["cpSync", fs.cpSync], + ["cpSync", fs.cpSync], ["cp", fs.promises.cp], ] as const; @@ -40,7 +40,9 @@ for (const [name, copy] of impls) { "from/a.txt": "a", }); - await copyShouldThrow(basename + "/from", basename + "/result"); + const e = await copyShouldThrow(basename + "/from", basename + "/result"); + expect(e.code).toBe("EISDIR"); + expect(e.path).toBe(basename + "/from"); }); test("recursive directory structure - no destination", async () => { @@ -129,7 +131,12 @@ for (const [name, copy] of impls) { "result/a.txt": "win", }); - await copyShouldThrow(basename + "/from/a.txt", basename + "/result/a.txt", { force: false, errorOnExist: true }); + const e = await copyShouldThrow(basename + "/from/a.txt", basename + "/result/a.txt", { + force: false, + errorOnExist: true, + }); + expect(e.code).toBe("EEXIST"); + expect(e.path).toBe(basename + "/result/a.txt"); assertContent(basename + "/result/a.txt", "win"); }); @@ -251,5 +258,31 @@ for (const [name, copy] of impls) { [basename + "/from/b.txt", basename + "/result/b.txt"], ]); }); + + test("trailing slash", async () => { + const basename = tempDirWithFiles("cp", { + "from/a.txt": "a", + "from/b.txt": "b", + }); + + await copy(basename + "/from/", basename + "/result/", { recursive: true }); + + assertContent(basename + "/result/a.txt", "a"); + assertContent(basename + "/result/b.txt", "b"); + }); + + test("copy directory will ensure directory exists", async () => { + const basename = tempDirWithFiles("cp", { + "from/a.txt": "a", + "from/b.txt": "b", + }); + + fs.mkdirSync(basename + "/result/"); + + await copy(basename + "/from/", basename + "/hello/world/", { recursive: true }); + + assertContent(basename + "/hello/world/a.txt", "a"); + assertContent(basename + "/hello/world/b.txt", "b"); + }); }); } |