Skip to content

Commit 377fe93

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: #19411 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 1196355 commit 377fe93

File tree

2 files changed

+43
-7
lines changed

2 files changed

+43
-7
lines changed

lib/fs.js

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

1922-
this.start = typeof this.fd !== 'number' && options.start === undefined ?
1923-
0 : options.start;
1922+
this.start = options.start;
19241923
this.end = options.end;
19251924
this.autoClose = options.autoClose === undefined ? true : options.autoClose;
19261925
this.pos = undefined;
@@ -1943,6 +1942,12 @@ function ReadStream(path, options) {
19431942
this.pos = this.start;
19441943
}
19451944

1945+
// Backwards compatibility: Make sure `end` is a number regardless of `start`.
1946+
// TODO(addaleax): Make the above typecheck not depend on `start` instead.
1947+
// (That is a semver-major change).
1948+
if (typeof this.end !== 'number')
1949+
this.end = Infinity;
1950+
19461951
if (typeof this.fd !== 'number')
19471952
this.open();
19481953

@@ -1996,6 +2001,8 @@ ReadStream.prototype._read = function(n) {
19962001

19972002
if (this.pos !== undefined)
19982003
toRead = Math.min(this.end - this.pos + 1, toRead);
2004+
else
2005+
toRead = Math.min(this.end - this.bytesRead + 1, toRead);
19992006

20002007
// already read everything we were supposed to read!
20012008
// treat as EOF.

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

+34-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22
const common = require('../common');
3+
4+
const child_process = require('child_process');
35
const assert = require('assert');
46

57
const fixtures = require('../common/fixtures');
@@ -146,11 +148,6 @@ stream.on('end', function() {
146148
}));
147149
}
148150

149-
// pause and then resume immediately.
150-
const pauseRes = fs.createReadStream(rangeFile);
151-
pauseRes.pause();
152-
pauseRes.resume();
153-
154151
let file7 = fs.createReadStream(rangeFile, {autoClose: false });
155152
file7.on('data', () => {});
156153
file7.on('end', function() {
@@ -173,6 +170,38 @@ function file7Next() {
173170
});
174171
}
175172

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

0 commit comments

Comments
 (0)