#603

Output redirection that fails with EROFS or EACCES still rejects bash.exec() instead of failing the command

Openasafyishopened 14d ago

just-bash version

3.6.0

Reproduction

echo before; echo x > /ro/f.txt; echo "after rc=$?"
echo x > /ro/f.txt || echo handled
for i in 1 2; do echo $i > /ro/f.txt; echo "loop $i"; done; echo end

How 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=1
bash: /ro/f.txt: Read-only file system
handled
bash: /ro/f.txt: Read-only file system
loop 1
bash: /ro/f.txt: Read-only file system
loop 2
end

Actual behavior

  • The first two scripts reject exec() with EROFS: read-only file system, open '/ro/f.txt'. The output from before is lost, and || echo handled never runs.
  • The loop prints only end and exits 0. Neither loop line 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

0

No activity yet.