Skip to content

Commit 2e37618

Browse files
committed
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. 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 893432a commit 2e37618

File tree

2 files changed

+36
-2
lines changed

2 files changed

+36
-2
lines changed

lib/fs.js

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

1970-
this.start = typeof this.fd !== 'number' && options.start === undefined ?
1971-
0 : options.start;
1970+
this.start = options.start;
19721971
this.end = options.end;
19731972
this.autoClose = options.autoClose === undefined ? true : options.autoClose;
19741973
this.pos = undefined;
@@ -1993,6 +1992,12 @@ function ReadStream(path, options) {
19931992
this.pos = this.start;
19941993
}
19951994

1995+
// Backwards compatibility: Make sure `end` is a number regardless of `start`.
1996+
// TODO(addaleax): Make the above typecheck not depend on `start` instead.
1997+
// (That is a semver-major change).
1998+
if (typeof this.end !== 'number')
1999+
this.end = Infinity;
2000+
19962001
if (typeof this.fd !== 'number')
19972002
this.open();
19982003

@@ -2047,6 +2052,8 @@ ReadStream.prototype._read = function(n) {
20472052

20482053
if (this.pos !== undefined)
20492054
toRead = Math.min(this.end - this.pos + 1, toRead);
2055+
else
2056+
toRead = Math.min(this.end - this.bytesRead + 1, toRead);
20502057

20512058
// already read everything we were supposed to read!
20522059
// treat as EOF.

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

+27
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121

2222
'use strict';
2323
const common = require('../common');
24+
const tmpdir = require('../common/tmpdir');
2425

26+
const child_process = require('child_process');
2527
const assert = require('assert');
2628
const fs = require('fs');
2729
const fixtures = require('../common/fixtures');
@@ -178,6 +180,31 @@ common.expectsError(
178180
}));
179181
}
180182

183+
if (!common.isWindows) {
184+
// Verify that end works when start is not specified, and we do not try to
185+
// use positioned reads. This makes sure that this keeps working for
186+
// non-seekable file descriptors.
187+
tmpdir.refresh();
188+
const filename = `${tmpdir.path}/foo.pipe`;
189+
const mkfifoResult = child_process.spawnSync('mkfifo', [filename]);
190+
if (!mkfifoResult.error) {
191+
child_process.exec(`echo "xyz foobar" > '${filename}'`);
192+
const stream = new fs.createReadStream(filename, { end: 1 });
193+
stream.data = '';
194+
195+
stream.on('data', function(chunk) {
196+
stream.data += chunk;
197+
});
198+
199+
stream.on('end', common.mustCall(function() {
200+
assert.strictEqual('xy', stream.data);
201+
fs.unlinkSync(filename);
202+
}));
203+
} else {
204+
common.printSkipMessage('mkfifo not available');
205+
}
206+
}
207+
181208
{
182209
// pause and then resume immediately.
183210
const pauseRes = fs.createReadStream(rangeFile);

0 commit comments

Comments
 (0)