Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions doc/api/fs.md
Original file line number Diff line number Diff line change
Expand Up @@ -624,15 +624,17 @@ Read from a file and write to an array of {ArrayBufferView}s
<!-- YAML
added: v10.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/57775
description: Now accepts an additional `signal` property to allow aborting the operation.
- version: v10.5.0
pr-url: https://github.com/nodejs/node/pull/20220
description: Accepts an additional `options` object to specify whether
the numeric values returned should be bigint.
description: Accepts an additional `options` object to specify whether the numeric values returned should be bigint.
-->

* `options` {Object}
* `bigint` {boolean} Whether the numeric values in the returned
{fs.Stats} object should be `bigint`. **Default:** `false`.
* `bigint` {boolean} Whether the numeric values in the returned {fs.Stats} object should be `bigint`. **Default:** `false`.
* `signal` {AbortSignal} An AbortSignal to cancel the operation. **Default:** `undefined`.
* Returns: {Promise} Fulfills with an {fs.Stats} for the file.

#### `filehandle.sync()`
Expand Down
10 changes: 9 additions & 1 deletion lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -1619,7 +1619,7 @@ function lstat(path, options = { bigint: false }, callback) {
/**
* Asynchronously gets the stats of a file.
* @param {string | Buffer | URL} path
* @param {{ bigint?: boolean; }} [options]
* @param {{ bigint?: boolean, signal?: AbortSignal }} [options]
* @param {(
* err?: Error,
* stats?: Stats
Expand All @@ -1630,8 +1630,16 @@ function stat(path, options = { bigint: false }, callback) {
if (typeof options === 'function') {
callback = options;
options = kEmptyObject;
} else if (options === null || typeof options !== 'object') {
options = kEmptyObject;
} else {
options = getOptions(options, { bigint: false });
}

callback = makeStatsCallback(callback);
path = getValidatedPath(path);

if (checkAborted(options.signal, callback)) return;

const req = new FSReqCallback(options.bigint);
req.oncomplete = callback;
Expand Down
34 changes: 34 additions & 0 deletions test/parallel/test-fs-stat-abort-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';

require('../common');
const test = require('node:test');
const assert = require('node:assert');
const fs = require('node:fs');
const tmpdir = require('../common/tmpdir');

test('fs.stat should throw AbortError when called with an already aborted AbortSignal', async () => {
// This test verifies that fs.stat immediately throws an AbortError if the provided AbortSignal
// has already been canceled. This approach is used because trying to abort an fs.stat call in flight
// is unreliable given that file system operations tend to complete very quickly on many platforms.
tmpdir.refresh();

const filePath = tmpdir.resolve('temp.txt');
fs.writeFileSync(filePath, 'Test');

// Create an already aborted AbortSignal.
const signal = AbortSignal.abort();

const { promise, resolve, reject } = Promise.withResolvers();
fs.stat(filePath, { signal }, (err, stats) => {
if (err) {
return reject(err);
}
resolve(stats);
});

// Assert that the promise is rejected with an AbortError.
await assert.rejects(promise, { name: 'AbortError' });

fs.unlinkSync(filePath);
tmpdir.refresh();
});
Loading