diff --git a/packages/runtime/src/scr_fs_stream.c b/packages/runtime/src/scr_fs_stream.c index 2ac88b706..dde56ddb0 100644 --- a/packages/runtime/src/scr_fs_stream.c +++ b/packages/runtime/src/scr_fs_stream.c @@ -557,23 +557,19 @@ static ScrStream *scr_fss_new(const ScrDyn *path, const ScrDyn *opts_in, bool re scr_dyn_release(eo); } if (!s) goto fail; + bool unknown_encoding = false; if (!readable) { /* setDefaultEncoding(options.encoding) after the Writable constructor: * 'buffer' is not an encoding there; utf8 is the default */ ScrDyn *enc = scr_fss_get(o, "encoding"); - bool buffer = enc && enc->kind == SCR_DYN_STR && enc->v.str->len == 6 && + unknown_encoding = enc && enc->kind == SCR_DYN_STR && enc->v.str->len == 6 && memcmp(enc->v.str->data, "buffer", 6) == 0; - bool other = enc && enc->kind == SCR_DYN_STR && enc->v.str->len > 0 && !buffer && + bool other = enc && enc->kind == SCR_DYN_STR && enc->v.str->len > 0 && !unknown_encoding && !(enc->v.str->len == 4 && memcmp(enc->v.str->data, "utf8", 4) == 0) && !(enc->v.str->len == 5 && memcmp(enc->v.str->data, "utf-8", 5) == 0); if (enc) scr_dyn_release(enc); - if (buffer) { - static const char m[] = "Unknown encoding: buffer"; - scr_throw_error_msg_code(SCR_ERR_TYPE, m, sizeof m - 1, "ERR_UNKNOWN_ENCODING"); - } else if (other) { + if (other) { scr_fss_unsupported("the WriteStream option 'encoding' is not supported yet (utf8, the default, is the write-side string encoding)"); - } - if (buffer || other) { scr_stream_release(s); s = NULL; goto fail; @@ -592,6 +588,15 @@ static ScrStream *scr_fss_new(const ScrDyn *path, const ScrDyn *opts_in, bool re } scr_stream_set_destroy(s, scr_fss_hook((void *)&scr_fss_destroy), &scr_fss_destroy); scr_stream_construct(s, scr_fss_hook((void *)&scr_fss_construct), &scr_fss_construct); + if (unknown_encoding) { + /* Node's constructor has already scheduled _construct when + * setDefaultEncoding throws, so the unreachable stream still opens + * (and with the default 'w' flags truncates) its file. */ + static const char m[] = "Unknown encoding: buffer"; + scr_throw_error_msg_code(SCR_ERR_TYPE, m, sizeof m - 1, "ERR_UNKNOWN_ENCODING"); + scr_stream_release(s); + s = NULL; + } fail: if (f) scr_fss_free(f); if (owner) owner_ops->release(owner); diff --git a/tests/corpus/5547-fs-write-stream.cjs b/tests/corpus/5547-fs-write-stream.cjs index 6bacc19c1..9198ed221 100644 --- a/tests/corpus/5547-fs-write-stream.cjs +++ b/tests/corpus/5547-fs-write-stream.cjs @@ -114,7 +114,9 @@ function check(label, fn) { check("start negative", () => fs.createWriteStream(file, { start: -1 })); check("path null", () => fs.createWriteStream(null)); check("fd string", () => fs.createWriteStream(file, { fd: "3" })); - check("encoding buffer", () => fs.createWriteStream(file, { encoding: "buffer" })); + // Node's constructor throws here after scheduling the open, so the + // stream still opens (and truncates) its file later: keep it off `file`. + check("encoding buffer", () => fs.createWriteStream(path.join(dir, "unknown-encoding.txt"), { encoding: "buffer" })); check("hwm string", () => fs.createWriteStream(file, { highWaterMark: "4" })); { const log = []; @@ -134,5 +136,6 @@ function check(label, fn) { await new Promise((resolve) => ws.on("close", () => resolve(report("pipe", [], ws.bytesWritten, ws.pending)))); console.log("piped", fs.statSync(file).size); } + console.log("unknown-encoding stream opened its file", fs.existsSync(path.join(dir, "unknown-encoding.txt"))); fs.rmSync(dir, { recursive: true }); })();