Skip to content

Commit 59fff92

Browse files
aduh95BethGriggs
authored andcommitted
fs: make open and close stream override optional when unused
When using `createReadStream` or `createWriteStream` with a specific file descriptor or `FileHandle` instead of a path, `open` method is not used, there is no point in forcing users to provide it. When using `createReadStream` or `createWriteStream` with `autoClose` set to false, `close` method is not used, there is no point in forcing users to provide it. PR-URL: #40013 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent b7dc651 commit 59fff92

2 files changed

Lines changed: 77 additions & 59 deletions

File tree

‎doc/api/fs.md‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1887,6 +1887,12 @@ behavior is similar to `cp dir1/ dir2/`.
18871887
<!-- YAML
18881888
added: v0.1.31
18891889
changes:
1890+
- version: REPLACEME
1891+
pr-url: https://github.com/nodejs/node/pull/40013
1892+
description: The `fs` option does not need `open` method if an `fd` was provided.
1893+
- version: REPLACEME
1894+
pr-url: https://github.com/nodejs/node/pull/40013
1895+
description: The `fs` option does not need `close` method if `autoClose` is `false`.
18901896
- version:
18911897
- v15.4.0
18921898
pr-url: https://github.com/nodejs/node/pull/35922
@@ -1962,7 +1968,9 @@ destroyed, like most `Readable` streams. Set the `emitClose` option to
19621968
19631969
By providing the `fs` option, it is possible to override the corresponding `fs`
19641970
implementations for `open`, `read`, and `close`. When providing the `fs` option,
1965-
overrides for `open`, `read`, and `close` are required.
1971+
an override for `read` is required. If no `fd` is provided, an override for
1972+
`open` is also required. If `autoClose` is `true`, an override for `close` is
1973+
also required.
19661974
19671975
```mjs
19681976
import { createReadStream } from 'fs';
@@ -2004,6 +2012,12 @@ If `options` is a string, then it specifies the encoding.
20042012
<!-- YAML
20052013
added: v0.1.31
20062014
changes:
2015+
- version: REPLACEME
2016+
pr-url: https://github.com/nodejs/node/pull/40013
2017+
description: The `fs` option does not need `open` method if an `fd` was provided.
2018+
- version: REPLACEME
2019+
pr-url: https://github.com/nodejs/node/pull/40013
2020+
description: The `fs` option does not need `close` method if `autoClose` is `false`.
20072021
- version:
20082022
- v15.4.0
20092023
pr-url: https://github.com/nodejs/node/pull/35922
@@ -2067,8 +2081,10 @@ destroyed, like most `Writable` streams. Set the `emitClose` option to
20672081
By providing the `fs` option it is possible to override the corresponding `fs`
20682082
implementations for `open`, `write`, `writev` and `close`. Overriding `write()`
20692083
without `writev()` can reduce performance as some optimizations (`_writev()`)
2070-
will be disabled. When providing the `fs` option, overrides for `open`,
2071-
`close`, and at least one of `write` and `writev` are required.
2084+
will be disabled. When providing the `fs` option, overrides for at least one of
2085+
`write` and `writev` are required. If no `fd` option is supplied, an override
2086+
for `open` is also required. If `autoClose` is `true`, an override for `close`
2087+
is also required.
20722088
20732089
Like {fs.ReadStream}, if `fd` is specified, {fs.WriteStream} will ignore the
20742090
`path` argument and will use the specified file descriptor. This means that no

‎lib/internal/fs/streams.js‎

Lines changed: 58 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -120,29 +120,29 @@ function close(stream, err, cb) {
120120
}
121121

122122
functionimportFd(stream,options){
123-
stream.fd=null;
124-
if(options.fd!=null){
125-
if(typeofoptions.fd==='number'){
126-
// When fd is a raw descriptor, we must keep our fingers crossed
127-
// that the descriptor won't get closed, or worse, replaced with
128-
// another one
129-
// https://github.com/nodejs/node/issues/35862
130-
stream.fd=options.fd;
131-
}elseif(typeofoptions.fd==='object'&&
132-
options.fdinstanceofFileHandle){
133-
// When fd is a FileHandle we can listen for 'close' events
134-
if(options.fs)
135-
// FileHandle is not supported with custom fs operations
136-
thrownewERR_METHOD_NOT_IMPLEMENTED('FileHandle with fs');
137-
stream[kHandle]=options.fd;
138-
stream.fd=options.fd.fd;
139-
stream[kFs]=FileHandleOperations(stream[kHandle]);
140-
stream[kHandle][kRef]();
141-
options.fd.on('close',FunctionPrototypeBind(stream.close,stream));
142-
}else
143-
throwERR_INVALID_ARG_TYPE('options.fd',
144-
['number','FileHandle'],options.fd);
123+
if(typeofoptions.fd==='number'){
124+
// When fd is a raw descriptor, we must keep our fingers crossed
125+
// that the descriptor won't get closed, or worse, replaced with
126+
// another one
127+
// https://github.com/nodejs/node/issues/35862
128+
stream[kFs]=options.fs||fs;
129+
returnoptions.fd;
130+
}elseif(typeofoptions.fd==='object'&&
131+
options.fdinstanceofFileHandle){
132+
// When fd is a FileHandle we can listen for 'close' events
133+
if(options.fs){
134+
// FileHandle is not supported with custom fs operations
135+
thrownewERR_METHOD_NOT_IMPLEMENTED('FileHandle with fs');
136+
}
137+
stream[kHandle]=options.fd;
138+
stream[kFs]=FileHandleOperations(stream[kHandle]);
139+
stream[kHandle][kRef]();
140+
options.fd.on('close',FunctionPrototypeBind(stream.close,stream));
141+
returnoptions.fd.fd;
145142
}
143+
144+
throwERR_INVALID_ARG_TYPE('options.fd',
145+
['number','FileHandle'],options.fd);
146146
}
147147

148148
functionReadStream(path,options){
@@ -158,21 +158,29 @@ function ReadStream(path, options) {
158158
options.autoDestroy=false;
159159
}
160160

161-
this[kFs]=options.fs||fs;
161+
if(options.fd==null){
162+
this.fd=null;
163+
this[kFs]=options.fs||fs;
164+
validateFunction(this[kFs].open,'options.fs.open');
162165

163-
validateFunction(this[kFs].open,'options.fs.open');
164-
validateFunction(this[kFs].read,'options.fs.read');
165-
validateFunction(this[kFs].close,'options.fs.close');
166+
// Path will be ignored when fd is specified, so it can be falsy
167+
this.path=toPathIfFileURL(path);
168+
this.flags=options.flags===undefined ? 'r' : options.flags;
169+
this.mode=options.mode===undefined ? 0o666 : options.mode;
170+
171+
validatePath(this.path);
172+
}else{
173+
this.fd=getValidatedFd(importFd(this,options));
174+
}
166175

167176
options.autoDestroy=options.autoClose===undefined ?
168177
true : options.autoClose;
169178

170-
// Path will be ignored when fd is specified, so it can be falsy
171-
this.path=toPathIfFileURL(path);
172-
this.flags=options.flags===undefined ? 'r' : options.flags;
173-
this.mode=options.mode===undefined ? 0o666 : options.mode;
179+
validateFunction(this[kFs].read,'options.fs.read');
174180

175-
importFd(this,options);
181+
if(options.autoDestroy){
182+
validateFunction(this[kFs].close,'options.fs.close');
183+
}
176184

177185
this.start=options.start;
178186
this.end=options.end;
@@ -187,12 +195,6 @@ function ReadStream(path, options) {
187195
this.pos=this.start;
188196
}
189197

190-
// If fd has been set, validate, otherwise validate path.
191-
if(this.fd!=null){
192-
this.fd=getValidatedFd(this.fd);
193-
}else{
194-
validatePath(this.path);
195-
}
196198

197199
if(this.end===undefined){
198200
this.end=Infinity;
@@ -310,9 +312,23 @@ function WriteStream(path, options) {
310312
// Only buffers are supported.
311313
options.decodeStrings=true;
312314

313-
this[kFs]=options.fs||fs;
315+
if(options.fd==null){
316+
this.fd=null;
317+
this[kFs]=options.fs||fs;
318+
validateFunction(this[kFs].open,'options.fs.open');
319+
320+
// Path will be ignored when fd is specified, so it can be falsy
321+
this.path=toPathIfFileURL(path);
322+
this.flags=options.flags===undefined ? 'w' : options.flags;
323+
this.mode=options.mode===undefined ? 0o666 : options.mode;
324+
325+
validatePath(this.path);
326+
}else{
327+
this.fd=getValidatedFd(importFd(this,options));
328+
}
314329

315-
validateFunction(this[kFs].open,'options.fs.open');
330+
options.autoDestroy=options.autoClose===undefined ?
331+
true : options.autoClose;
316332

317333
if(!this[kFs].write&&!this[kFs].writev){
318334
thrownewERR_INVALID_ARG_TYPE('options.fs.write','function',
@@ -327,7 +343,9 @@ function WriteStream(path, options) {
327343
validateFunction(this[kFs].writev,'options.fs.writev');
328344
}
329345

330-
validateFunction(this[kFs].close,'options.fs.close');
346+
if(options.autoDestroy){
347+
validateFunction(this[kFs].close,'options.fs.close');
348+
}
331349

332350
// It's enough to override either, in which case only one will be used.
333351
if(!this[kFs].write){
@@ -337,28 +355,12 @@ function WriteStream(path, options) {
337355
this._writev=null;
338356
}
339357

340-
options.autoDestroy=options.autoClose===undefined ?
341-
true : options.autoClose;
342-
343-
// Path will be ignored when fd is specified, so it can be falsy
344-
this.path=toPathIfFileURL(path);
345-
this.flags=options.flags===undefined ? 'w' : options.flags;
346-
this.mode=options.mode===undefined ? 0o666 : options.mode;
347-
348-
importFd(this,options);
349-
350358
this.start=options.start;
351359
this.pos=undefined;
352360
this.bytesWritten=0;
353361
this.closed=false;
354362
this[kIsPerformingIO]=false;
355363

356-
// If fd has been set, validate, otherwise validate path.
357-
if(this.fd!=null){
358-
this.fd=getValidatedFd(this.fd);
359-
}else{
360-
validatePath(this.path);
361-
}
362364

363365
if(this.start!==undefined){
364366
validateInteger(this.start,'start',0);

0 commit comments

Comments
 (0)