#603
Output redirection that fails with EROFS or EACCES still rejects bash.exec() instead of failing the command
just-bash version
3.6.0
Reproduction
echo before; echo x > /ro/f.txt; echo "after rc=$?"echo x > /ro/f.txt || echo handledfor i in 1 2; do echo $i > /ro/f.txt; echo "loop $i"; done; echo endHow you run it
Each script runs in its own exec(). /ro behaves like a read-only mount:
import { Bash, InMemoryFs } from "just-bash";
class ReadOnlyBranch extends InMemoryFs {
async writeFile(path, ...rest) {
if (path.startsWith("/ro/")) {
throw Object.assign(new Error(`EROFS: read-only file system, open '${path}'`), { code: "EROFS" });
}
return super.writeFile(path, ...rest);
}
}
const bash = new Bash({ fs: new ReadOnlyBranch({ "/ro/f.txt": "x\n" }) });
const result = await bash.exec(script);Expected behavior
bash fails just that command, with status 1, and keeps going:
before
bash: /ro/f.txt: Read-only file system
after rc=1bash: /ro/f.txt: Read-only file system
handledbash: /ro/f.txt: Read-only file system
loop 1
bash: /ro/f.txt: Read-only file system
loop 2
endActual behavior
- The first two scripts reject
exec()withEROFS: read-only file system, open '/ro/f.txt'. The output frombeforeis lost, and|| echo handlednever runs. - The loop prints only
endand exits 0. Neitherloopline runs, and the error appears once on stderr.
Throwing EACCES instead of EROFS behaves the same way.
This is the same class of problem as #557. #561 fixed it for ENOENT and ENOTDIR by mapping the error in openOutputEntry. Could the other open errors go through the same path, e.g. EROFS, EACCES, EISDIR, ENAMETOOLONG, or EINVAL from a filesystem that refuses the content? The write would still be refused; only the scope of the failure changes. process-substitution.readonly.test.ts ("still refuses ordinary writes") asserts the rejection for a read-only OverlayFs, so that test would need to change too.
Environment
- Runtime (Node, Bun, browser, CLI): Node
- Runtime version: 26.8.2
- Operating system: macOS 15
- Bundler or framework, when relevant: none
Activity
0No activity yet.