Skip to content

Commit a653f23

Browse files
addaleaxMylesBorins
authored andcommitted
fs: fix createReadStream(…, {end: n}) for non-seekable fds
82bdf8f fixed an issue by silently modifying the `start` option for the case when only `end` is passed, in order to perform reads from a specified range in the file. However, that approach does not work for non-seekable files, since a numeric `start` option means that positioned reads will be used to read data from the file. This patch fixes that, and instead ends reading after a specified size by adjusting the read buffer size. This way we avoid re-introducing the bug that 82bdf8f fixed, and align behaviour with the native file stream mechanism introduced in #18936 as well. Backport-PR-URL: #19410 PR-URL: #19329 Fixes: #19240 Refs: #18121 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Chen Gang <gangc.cxy@foxmail.com>
1 parent 18be476 commit a653f23

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

lib/fs.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -1950,8 +1950,7 @@ function ReadStream(path, options) {
19501950
this.flags = options.flags === undefined ? 'r' : options.flags;
19511951
this.mode = options.mode === undefined ? 0o666 : options.mode;
19521952

1953-
this.start = typeof this.fd !== 'number' && options.start === undefined ?
1954-
0 : options.start;
1953+
this.start = options.start;
19551954
this.end = options.end;
19561955
this.autoClose = options.autoClose === undefined ? true : options.autoClose;
19571956
this.pos = undefined;
@@ -1974,6 +1973,12 @@ function ReadStream(path, options) {
19741973
this.pos = this.start;
19751974
}
19761975

1976+
// Backwards compatibility: Make sure `end` is a number regardless of `start`.
1977+
// TODO(addaleax): Make the above typecheck not depend on `start` instead.
1978+
// (That is a semver-major change).
1979+
if (typeof this.end !== 'number')
1980+
this.end = Infinity;
1981+
19771982
if (typeof this.fd !== 'number')
19781983
this.open();
19791984

@@ -2028,6 +2033,8 @@ ReadStream.prototype._read = function(n) {
20282033

20292034
if (this.pos !== undefined)
20302035
toRead = Math.min(this.end - this.pos + 1, toRead);
2036+
else
2037+
toRead = Math.min(this.end - this.bytesRead + 1, toRead);
20312038

20322039
// already read everything we were supposed to read!
20332040
// treat as EOF.

test/parallel/test-fs-read-stream.js

+26
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'use strict';
2323
const common = require('../common');
2424

25+
const child_process = require('child_process');
2526
const assert = require('assert');
2627
const fs = require('fs');
2728
const fixtures = require('../common/fixtures');
@@ -171,6 +172,31 @@ assert.throws(function() {
171172
}));
172173
}
173174

175+
if (!common.isWindows) {
176+
// Verify that end works when start is not specified, and we do not try to
177+
// use positioned reads. This makes sure that this keeps working for
178+
// non-seekable file descriptors.
179+
common.refreshTmpDir();
180+
const filename = `${common.tmpDir}/foo.pipe`;
181+
const mkfifoResult = child_process.spawnSync('mkfifo', [filename]);
182+
if (!mkfifoResult.error) {
183+
child_process.exec(`echo "xyz foobar" > '${filename}'`);
184+
const stream = new fs.createReadStream(filename, { end: 1 });
185+
stream.data = '';
186+
187+
stream.on('data', function(chunk) {
188+
stream.data += chunk;
189+
});
190+
191+
stream.on('end', common.mustCall(function() {
192+
assert.strictEqual('xy', stream.data);
193+
fs.unlinkSync(filename);
194+
}));
195+
} else {
196+
common.printSkipMessage('mkfifo not available');
197+
}
198+
}
199+
174200
{
175201
// pause and then resume immediately.
176202
const pauseRes = fs.createReadStream(rangeFile);

0 commit comments

Comments
 (0)