Skip to content

Commit ed82387

Browse files
author
Mert Can Altin
committed
fs: enable chunked reading for large files in readFileHandle
1 parent 03ec900 commit ed82387

File tree

2 files changed

+36
-40
lines changed

2 files changed

+36
-40
lines changed

lib/internal/fs/promises.js

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ const {
3939
aggregateTwoErrors,
4040
codes: {
4141
ERR_ACCESS_DENIED,
42-
ERR_FS_FILE_TOO_LARGE,
4342
ERR_INVALID_ARG_VALUE,
4443
ERR_INVALID_STATE,
4544
ERR_METHOD_NOT_IMPLEMENTED,
@@ -49,7 +48,6 @@ const { isArrayBufferView } = require('internal/util/types');
4948

5049
const {
5150
constants: {
52-
kIoMaxLength,
5351
kMaxUserId,
5452
kReadFileBufferLength,
5553
kReadFileUnknownBufferLength,
@@ -526,17 +524,14 @@ async function readFileHandle(filehandle, options) {
526524

527525
let size = 0;
528526
let length = 0;
529-
if ((statFields[1/* mode */] & S_IFMT) === S_IFREG) {
530-
size = statFields[8/* size */];
527+
if ((statFields[1 /* mode */] & S_IFMT) === S_IFREG) {
528+
size = statFields[8 /* size */];
531529
length = encoding ? MathMin(size, kReadFileBufferLength) : size;
532530
}
533531
if (length === 0) {
534532
length = kReadFileUnknownBufferLength;
535533
}
536534

537-
if (size > kIoMaxLength)
538-
throw new ERR_FS_FILE_TOO_LARGE(size);
539-
540535
let totalRead = 0;
541536
const noSize = size === 0;
542537
let buffer = Buffer.allocUnsafeSlow(length);
@@ -559,9 +554,7 @@ async function readFileHandle(filehandle, options) {
559554
)) ?? 0;
560555
totalRead += bytesRead;
561556

562-
if (bytesRead === 0 ||
563-
totalRead === size ||
564-
(bytesRead !== buffer.length && !chunkedRead && !noSize)) {
557+
if (bytesRead === 0 || totalRead === size || (bytesRead !== buffer.length && !chunkedRead && !noSize)) {
565558
const singleRead = bytesRead === totalRead;
566559

567560
const bytesToCheck = chunkedRead ? totalRead : bytesRead;
@@ -584,9 +577,9 @@ async function readFileHandle(filehandle, options) {
584577
result += decoder.end(buffer);
585578
return result;
586579
}
587-
const readBuffer = bytesRead !== buffer.length ?
588-
buffer.subarray(0, bytesRead) :
589-
buffer;
580+
581+
const readBuffer = bytesRead !== buffer.length ? buffer.subarray(0, bytesRead) : buffer;
582+
590583
if (encoding) {
591584
result += decoder.write(readBuffer);
592585
} else if (size !== 0) {
@@ -595,6 +588,8 @@ async function readFileHandle(filehandle, options) {
595588
buffers ??= [];
596589
// Unknown file size requires chunks.
597590
ArrayPrototypePush(buffers, readBuffer);
591+
592+
// Create a new buffer (continue for large files)
598593
buffer = Buffer.allocUnsafeSlow(kReadFileUnknownBufferLength);
599594
}
600595
}

test/parallel/test-fs-promises-file-handle-readFile.js

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ const common = require('../common');
55
// The following tests validate base functionality for the fs.promises
66
// FileHandle.readFile method.
77

8-
const fs = require('fs');
8+
const fs = require('node:fs');
99
const {
1010
open,
1111
readFile,
1212
writeFile,
13-
truncate,
1413
} = fs.promises;
15-
const path = require('path');
14+
const path = require('node:path');
15+
const os = require('node:os');
1616
const tmpdir = require('../common/tmpdir');
1717
const tick = require('../common/tick');
18-
const assert = require('assert');
18+
const assert = require('node:assert');
1919
const tmpDir = tmpdir.path;
2020

2121
tmpdir.refresh();
@@ -35,6 +35,29 @@ async function validateReadFile() {
3535
await fileHandle.close();
3636
}
3737

38+
async function validateLargeFileSupport() {
39+
const LARGE_FILE_SIZE = 3 * 1024 * 1024 * 1024; // 3 GiB
40+
const FILE_PATH = path.join(os.tmpdir(), 'large-virtual-file.bin');
41+
42+
function createVirtualLargeFile() {
43+
return Buffer.alloc(LARGE_FILE_SIZE, 'A');
44+
}
45+
46+
const virtualFile = createVirtualLargeFile();
47+
48+
await writeFile(FILE_PATH, virtualFile);
49+
50+
const buffer = await readFile(FILE_PATH);
51+
52+
assert.strictEqual(
53+
buffer.length,
54+
LARGE_FILE_SIZE,
55+
`Expected file size to be ${LARGE_FILE_SIZE}, but got ${buffer.length}`
56+
);
57+
58+
await fs.promises.unlink(FILE_PATH);
59+
}
60+
3861
async function validateReadFileProc() {
3962
// Test to make sure reading a file under the /proc directory works. Adapted
4063
// from test-fs-read-file-sync-hostname.js.
@@ -100,32 +123,10 @@ async function doReadAndCancel() {
100123

101124
await fileHandle.close();
102125
}
103-
104-
// Validate file size is within range for reading
105-
{
106-
// Variable taken from https://github.com/nodejs/node/blob/1377163f3351/lib/internal/fs/promises.js#L5
107-
const kIoMaxLength = 2 ** 31 - 1;
108-
109-
if (!tmpdir.hasEnoughSpace(kIoMaxLength)) {
110-
// truncate() will fail with ENOSPC if there is not enough space.
111-
common.printSkipMessage(`Not enough space in ${tmpDir}`);
112-
} else {
113-
const newFile = path.resolve(tmpDir, 'dogs-running3.txt');
114-
await writeFile(newFile, Buffer.from('0'));
115-
await truncate(newFile, kIoMaxLength + 1);
116-
117-
const fileHandle = await open(newFile, 'r');
118-
119-
await assert.rejects(fileHandle.readFile(), {
120-
name: 'RangeError',
121-
code: 'ERR_FS_FILE_TOO_LARGE'
122-
});
123-
await fileHandle.close();
124-
}
125-
}
126126
}
127127

128128
validateReadFile()
129+
.then(validateLargeFileSupport)
129130
.then(validateReadFileProc)
130131
.then(doReadAndCancel)
131132
.then(common.mustCall());

0 commit comments

Comments
 (0)