diff --git a/src/js/node/wasi.ts b/src/js/node/wasi.ts index a0ae57df5c5c..0a8de0c85b1a 100644 --- a/src/js/node/wasi.ts +++ b/src/js/node/wasi.ts @@ -1437,7 +1437,7 @@ var require_wasi = __commonJS({ path_open: wrap( (dirfd, _dirflags, pathPtr, pathLen, oflags, fsRightsBase, fsRightsInheriting, fsFlags, fdPtr) => { try { - CHECK_FD(dirfd, constants_1.WASI_RIGHT_PATH_OPEN); + const stats = CHECK_FD(dirfd, constants_1.WASI_RIGHT_PATH_OPEN); fsRightsBase = BigInt(fsRightsBase); fsRightsInheriting = BigInt(fsRightsInheriting); const read = @@ -1512,7 +1512,7 @@ var require_wasi = __commonJS({ if (p.startsWith("proc/")) { throw new types_1.WASIError(constants_1.WASI_EBADF); } - const fullUnresolved = path.resolve(p); + const fullUnresolved = path.resolve(stats.path, p); let full; try { full = fs.realpathSync(fullUnresolved); diff --git a/test/js/bun/wasm/read-file.wasm b/test/js/bun/wasm/read-file.wasm new file mode 100755 index 000000000000..603b46273337 Binary files /dev/null and b/test/js/bun/wasm/read-file.wasm differ diff --git a/test/js/bun/wasm/wasi.test.js b/test/js/bun/wasm/wasi.test.js index 5e6994a50457..3e0f0b5f328a 100644 --- a/test/js/bun/wasm/wasi.test.js +++ b/test/js/bun/wasm/wasi.test.js @@ -1,6 +1,6 @@ import { spawnSync } from "bun"; import { expect, it } from "bun:test"; -import { bunEnv, bunExe } from "harness"; +import { bunEnv, bunExe, tempDirWithFiles } from "harness"; it("Should support printing 'hello world'", () => { const { stdout, stderr, exitCode } = spawnSync({ @@ -20,3 +20,43 @@ it("Should support printing 'hello world'", () => { exitCode: 0, }); }); + +it("path_open should resolve paths against preopens, not cwd", () => { + const tmp = tempDirWithFiles("bun-wasi-test", { + "input.txt": "hello from preopens\n", + }); + const testFile = `${tmp}/input.txt`; + + // read-file.wasm: minimal Rust WASI binary (wasm32-wasip1) that reads a file + // path from args and prints its contents to stdout. + // + // Built with: + // cargo init --name read-file-wasi + // # src/main.rs: + // # use std::{env, fs, process}; + // # fn main() { + // # let args: Vec = env::args().collect(); + // # if args.len() < 2 { eprintln!("usage: read-file "); process::exit(1); } + // # match fs::read_to_string(&args[1]) { + // # Ok(c) => print!("{}", c), + // # Err(e) => { eprintln!("error: {}: {}", args[1], e); process::exit(1); } + // # } + // # } + // cargo build --target wasm32-wasip1 --release + const { stdout, stderr, exitCode } = spawnSync({ + cmd: [bunExe(), import.meta.dir + "/read-file.wasm", testFile], + stdout: "pipe", + stderr: "pipe", + env: bunEnv, + }); + + expect({ + stdout: stdout.toString(), + stderr: stderr.toString(), + exitCode: exitCode, + }).toEqual({ + stdout: "hello from preopens\n", + stderr: "", + exitCode: 0, + }); +});