From 3612229d4433a2f9913f3363830999ca7c7a3c7e Mon Sep 17 00:00:00 2001 From: Nitzan Uziely Date: Mon, 10 May 2021 01:45:52 +0300 Subject: [PATCH] fs: fix async iterator partial writes fix an issue where chunks might be partially written when using writeFile with an async iterator. PR-URL: https://github.com/nodejs/node/pull/38615 Reviewed-By: Antoine du Hamel Reviewed-By: Rich Trott Reviewed-By: James M Snell --- lib/internal/fs/promises.js | 14 ++++++++++---- test/parallel/test-fs-promises-writefile.js | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js index 7a575e69491e38..0253a29c7d5c57 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -296,10 +296,16 @@ async function writeFileHandle(filehandle, data, signal, encoding) { if (isCustomIterable(data)) { for await (const buf of data) { checkAborted(signal); - await write( - filehandle, buf, undefined, - isArrayBufferView(buf) ? buf.byteLength : encoding); - checkAborted(signal); + const toWrite = + isArrayBufferView(buf) ? buf : Buffer.from(buf, encoding || 'utf8'); + let remaining = toWrite.byteLength; + while (remaining > 0) { + const writeSize = MathMin(kWriteFileMaxChunkSize, remaining); + const { bytesWritten } = await write( + filehandle, toWrite, toWrite.byteLength - remaining, writeSize); + remaining -= bytesWritten; + checkAborted(signal); + } } return; } diff --git a/test/parallel/test-fs-promises-writefile.js b/test/parallel/test-fs-promises-writefile.js index a3a5937e9b0724..494a9015773d13 100644 --- a/test/parallel/test-fs-promises-writefile.js +++ b/test/parallel/test-fs-promises-writefile.js @@ -25,6 +25,14 @@ const iterable = { yield 'c'; } }; + +const veryLargeBuffer = { + expected: 'dogs running'.repeat(512 * 1024), + *[Symbol.iterator]() { + yield Buffer.from('dogs running'.repeat(512 * 1024), 'utf8'); + } +}; + function iterableWith(value) { return { *[Symbol.iterator]() { @@ -106,6 +114,12 @@ async function doWriteAsyncIterable() { assert.deepStrictEqual(data, asyncIterable.expected); } +async function doWriteAsyncLargeIterable() { + await fsPromises.writeFile(dest, veryLargeBuffer); + const data = fs.readFileSync(dest, 'utf-8'); + assert.deepStrictEqual(data, veryLargeBuffer.expected); +} + async function doWriteInvalidValues() { await Promise.all( [42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) => @@ -158,5 +172,6 @@ async function doReadWithEncoding() { await doWriteIterableWithEncoding(); await doWriteBufferIterable(); await doWriteAsyncIterable(); + await doWriteAsyncLargeIterable(); await doWriteInvalidValues(); })().then(common.mustCall());