aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar dave caruso <me@paperdave.net> 2023-08-31 23:04:19 -0700
committerGravatar GitHub <noreply@github.com> 2023-08-31 23:04:19 -0700
commit5288178c865cd9dcb500b00a65197bf2641d4e5c (patch)
tree1394a9e42f3de39c6582e14aab1f5b9d96167346
parent6ae4dd808b986649616e4ba498e70480441e3b3c (diff)
downloadbun-5288178c865cd9dcb500b00a65197bf2641d4e5c.tar.gz
bun-5288178c865cd9dcb500b00a65197bf2641d4e5c.tar.zst
bun-5288178c865cd9dcb500b00a65197bf2641d4e5c.zip
fix(runtime): `fs.cp` edge cases (#4439)
* yippee * enable cpSync tests * much better * that doesnt actually do anything * lose
-rw-r--r--src/bun.js/node/node_fs.zig13
-rw-r--r--test/js/node/fs/cp.test.ts39
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");
+ });
});
}