fs: abort in-flight stat operations · nodejs/node@6673eba · GitHub
Skip to content

Commit 6673eba

Browse files
mertcanaltinaduh95
authored andcommitted
fs: abort in-flight stat operations
Signed-off-by: Mert Can Altin <mertgold60@gmail.com> PR-URL: #63143 Refs: #57775 Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 74234ee commit 6673eba

4 files changed

Lines changed: 200 additions & 34 deletions

File tree

doc/api/fs.md

Lines changed: 24 additions & 0 deletions

lib/fs.js

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ const {
139139
const {
140140
isInt32,
141141
parseFileMode,
142+
validateAbortSignal,
142143
validateBoolean,
143144
validateBuffer,
144145
validateEncoding,
@@ -383,6 +384,25 @@ function checkAborted(signal, callback) {
383384
return false;
384385
}
385386

387+
function bindSignalToReq(req, signal, callback) {
388+
if (!signal) {
389+
req.oncomplete = callback;
390+
return;
391+
}
392+
let aborted = false;
393+
const onAbort = () => {
394+
aborted = true;
395+
callback(new AbortError(undefined, { cause: signal.reason }));
396+
};
397+
kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation;
398+
signal.addEventListener('abort', onAbort, { __proto__: null, [kResistStopPropagation]: true });
399+
req.oncomplete = function(err, result) {
400+
signal.removeEventListener('abort', onAbort);
401+
if (aborted) return;
402+
callback(err, result);
403+
};
404+
}
405+
386406
/**
387407
* Asynchronously reads the entire contents of a file.
388408
* @param {string | Buffer | URL | number} path
@@ -1954,7 +1974,7 @@ function readdirSync(path, options) {
19541974
* Invokes the callback with the `fs.Stats`
19551975
* for the file descriptor.
19561976
* @param {number} fd
1957-
* @param {{ bigint?: boolean; }} [options]
1977+
* @param {{ bigint?: boolean, signal?: AbortSignal }} [options]
19581978
* @param {(
19591979
* err?: Error,
19601980
* stats?: Stats
@@ -1965,23 +1985,28 @@ function fstat(fd, options = { __proto__: null, bigint: false }, callback) {
19651985
if (typeof options === 'function') {
19661986
callback = options;
19671987
options = kEmptyObject;
1988+
} else if (options === null || typeof options !== 'object') {
1989+
options = kEmptyObject;
19681990
}
19691991

19701992
const h = vfsState.handlers;
19711993
if (h !== null && vfsResult(h.fstat(fd, options), callback)) return;
19721994

19731995
callback = makeStatsCallback(callback);
19741996

1997+
if (options.signal !== undefined) validateAbortSignal(options.signal, 'options.signal');
1998+
if (checkAborted(options.signal, callback)) return;
1999+
19752000
const req = new FSReqCallback(options.bigint);
1976-
req.oncomplete = callback;
2001+
bindSignalToReq(req, options.signal, callback);
19772002
binding.fstat(fd, options.bigint, req);
19782003
}
19792004

19802005
/**
19812006
* Retrieves the `fs.Stats` for the symbolic link
19822007
* referred to by the `path`.
19832008
* @param {string | Buffer | URL} path
1984-
* @param {{ bigint?: boolean; }} [options]
2009+
* @param {{ bigint?: boolean, signal?: AbortSignal }} [options]
19852010
* @param {(
19862011
* err?: Error,
19872012
* stats?: Stats
@@ -1992,6 +2017,10 @@ function lstat(path, options = { __proto__: null, bigint: false }, callback) {
19922017
if (typeof options === 'function') {
19932018
callback = options;
19942019
options = kEmptyObject;
2020+
} else if (options === null || typeof options !== 'object') {
2021+
options = kEmptyObject;
2022+
} else {
2023+
options = getOptions(options, { bigint: false });
19952024
}
19962025

19972026
const h = vfsState.handlers;
@@ -2005,8 +2034,11 @@ function lstat(path, options = { __proto__: null, bigint: false }, callback) {
20052034
return;
20062035
}
20072036

2037+
if (options.signal !== undefined) validateAbortSignal(options.signal, 'options.signal');
2038+
if (checkAborted(options.signal, callback)) return;
2039+
20082040
const req = new FSReqCallback(options.bigint);
2009-
req.oncomplete = callback;
2041+
bindSignalToReq(req, options.signal, callback);
20102042
binding.lstat(path, options.bigint, req);
20112043
}
20122044

@@ -2036,11 +2068,12 @@ function stat(path, options = { __proto__: null, bigint: false, throwIfNoEntry:
20362068
callback = makeStatsCallback(callback);
20372069
path = getValidatedPath(path);
20382070

2071+
if (options.signal !== undefined) validateAbortSignal(options.signal, 'options.signal');
20392072
if (checkAborted(options.signal, callback)) return;
20402073

20412074
const req = new FSReqCallback(options.bigint);
2042-
req.oncomplete = callback;
2043-
binding.stat(getValidatedPath(path), options.bigint, req, options.throwIfNoEntry);
2075+
bindSignalToReq(req, options.signal, callback);
2076+
binding.stat(path, options.bigint, req, options.throwIfNoEntry);
20442077
}
20452078

20462079
function statfs(path, options = { __proto__: null, bigint: false }, callback) {

lib/internal/fs/promises.js

Lines changed: 50 additions & 12 deletions

0 commit comments

Comments
 (0)