Skip to content

Commit

Permalink
fs: fix createReadStream(…, {end: n}) for non-seekable fds
Browse files Browse the repository at this point in the history
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 nodejs#18936 as well.

PR-URL: nodejs#19329
Fixes: nodejs#19240
Refs: nodejs#18121
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Chen Gang <[email protected]>
  • Loading branch information
addaleax committed Mar 17, 2018
1 parent ce751b1 commit 571f29f
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 2 deletions.
11 changes: 9 additions & 2 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -1950,8 +1950,7 @@ function ReadStream(path, options) {
this.flags = options.flags === undefined ? 'r' : options.flags;
this.mode = options.mode === undefined ? 0o666 : options.mode;

this.start = typeof this.fd !== 'number' && options.start === undefined ?
0 : options.start;
this.start = options.start;
this.end = options.end;
this.autoClose = options.autoClose === undefined ? true : options.autoClose;
this.pos = undefined;
Expand All @@ -1974,6 +1973,12 @@ function ReadStream(path, options) {
this.pos = this.start;
}

// Backwards compatibility: Make sure `end` is a number regardless of `start`.
// TODO(addaleax): Make the above typecheck not depend on `start` instead.
// (That is a semver-major change).
if (typeof this.end !== 'number')
this.end = Infinity;

if (typeof this.fd !== 'number')
this.open();

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

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

// already read everything we were supposed to read!
// treat as EOF.
Expand Down
27 changes: 27 additions & 0 deletions test/parallel/test-fs-read-stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@

'use strict';
const common = require('../common');
const tmpdir = require('../common/tmpdir');

const child_process = require('child_process');
const assert = require('assert');
const fs = require('fs');
const fixtures = require('../common/fixtures');
Expand Down Expand Up @@ -171,6 +173,31 @@ assert.throws(function() {
}));
}

if (!common.isWindows) {
// Verify that end works when start is not specified, and we do not try to
// use positioned reads. This makes sure that this keeps working for
// non-seekable file descriptors.
tmpdir.refresh();
const filename = `${tmpdir.path}/foo.pipe`;
const mkfifoResult = child_process.spawnSync('mkfifo', [filename]);
if (!mkfifoResult.error) {
child_process.exec(`echo "xyz foobar" > '${filename}'`);
const stream = new fs.createReadStream(filename, { end: 1 });
stream.data = '';

stream.on('data', function(chunk) {
stream.data += chunk;
});

stream.on('end', common.mustCall(function() {
assert.strictEqual('xy', stream.data);
fs.unlinkSync(filename);
}));
} else {
common.printSkipMessage('mkfifo not available');
}
}

{
// pause and then resume immediately.
const pauseRes = fs.createReadStream(rangeFile);
Expand Down

0 comments on commit 571f29f

Please sign in to comment.