Skip to content

Commit b8cf57c

Browse files
committed
vfs: split followup tests into named test files
Replace the monolithic test-vfs-followups.js with individual test files: - test-vfs-overlapping-mounts.js - test-vfs-promises-open.js - test-vfs-stream-properties.js - test-vfs-dir-disposal.js - test-vfs-stats-ino-dev.js - test-vfs-append-write.js - test-vfs-cross-device.js - test-vfs-readdir-symlink-recursive.js - test-vfs-package-json-cache.js - test-vfs-readfile-async.js
1 parent 7346ef3 commit b8cf57c

18 files changed

+309
-288
lines changed

lib/internal/fs/promises.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ async function copyFile(src, dest, mode) {
643643
async function open(path, flags, mode) {
644644
const h = vfsState.handlers;
645645
if (h !== null) {
646-
const result = h.open(path, flags, mode);
646+
const result = h.promisesOpen(path, flags, mode);
647647
if (result !== undefined) return result;
648648
}
649649
path = getValidatedPath(path);

lib/internal/vfs/file_system.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ const kVirtualCwd = Symbol('kVirtualCwd');
4646
const kVirtualCwdEnabled = Symbol('kVirtualCwdEnabled');
4747
const kOriginalChdir = Symbol('kOriginalChdir');
4848
const kOriginalCwd = Symbol('kOriginalCwd');
49-
const kAllowWithPermissionModel = Symbol('kAllowWithPermissionModel');
5049

5150
// Lazy-loaded VFS setup
5251
let registerVFS;
@@ -109,7 +108,6 @@ class VirtualFileSystem {
109108
this[kVirtualCwd] = null; // Set when chdir() is called
110109
this[kOriginalChdir] = null; // Saved process.chdir
111110
this[kOriginalCwd] = null; // Saved process.cwd
112-
this[kAllowWithPermissionModel] = options.allowWithPermissionModel === true;
113111
}
114112

115113
/**
@@ -162,14 +160,6 @@ class VirtualFileSystem {
162160
return this[kVirtualCwdEnabled];
163161
}
164162

165-
/**
166-
* Returns true if this VFS allows being used with the permission model.
167-
* @returns {boolean}
168-
*/
169-
get allowWithPermissionModel() {
170-
return this[kAllowWithPermissionModel];
171-
}
172-
173163
// ==================== Virtual Working Directory ====================
174164

175165
/**

lib/internal/vfs/providers/memory.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,7 @@ class MemoryProvider extends VirtualProvider {
985985
}
986986

987987
if (listener) {
988-
watcher.addListener(listener);
988+
watcher.addListener('change', listener);
989989
}
990990

991991
return watcher;
@@ -1005,7 +1005,7 @@ class MemoryProvider extends VirtualProvider {
10051005
}
10061006

10071007
if (listener) {
1008-
watcher.removeListener(listener);
1008+
watcher.removeListener('change', listener);
10091009
} else {
10101010
// Remove all listeners
10111011
watcher.removeAllListeners('change');

lib/internal/vfs/setup.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const { createENOENT, createEXDEV } = require('internal/vfs/errors');
3030
const { getVirtualFd, closeVirtualFd } = require('internal/vfs/fd');
3131
const { vfsState, setVfsHandlers } = require('internal/fs/utils');
3232
const permission = require('internal/process/permission');
33+
const { getOptionValue } = require('internal/options');
3334

3435
/**
3536
* Converts a path argument (string or URL) to a string path.
@@ -72,10 +73,10 @@ let vfsHandlerObj;
7273
* @param {VirtualFileSystem} vfs The VFS instance to register
7374
*/
7475
function registerVFS(vfs) {
75-
if (permission.isEnabled() && !vfs.allowWithPermissionModel) {
76+
if (permission.isEnabled() && !getOptionValue('--allow-fs-vfs')) {
7677
throw new ERR_INVALID_STATE(
7778
'VFS cannot be used when the permission model is enabled. ' +
78-
'Set allowWithPermissionModel: true to override.',
79+
'Use --allow-fs-vfs to allow it.',
7980
);
8081
}
8182
if (ArrayPrototypeIndexOf(activeVFSList, vfs) === -1) {
@@ -85,8 +86,11 @@ function registerVFS(vfs) {
8586
for (let i = 0; i < activeVFSList.length; i++) {
8687
const existingMount = activeVFSList[i].mountPoint;
8788
if (existingMount == null) continue;
88-
if (StringPrototypeStartsWith(newMount, existingMount) ||
89-
StringPrototypeStartsWith(existingMount, newMount)) {
89+
const newPrefix = newMount === '/' ? '/' : newMount + '/';
90+
const existingPrefix = existingMount === '/' ? '/' : existingMount + '/';
91+
if (newMount === existingMount ||
92+
StringPrototypeStartsWith(newMount, existingPrefix) ||
93+
StringPrototypeStartsWith(existingMount, newPrefix)) {
9094
throw new ERR_INVALID_STATE(
9195
`VFS mount '${newMount}' overlaps with existing mount '${existingMount}'`,
9296
);
@@ -884,6 +888,9 @@ function createVfsHandlers() {
884888
chmod: (path, mode) => vfsOp(path, (vfs, n) => vfs.promises.chmod(n, mode).then(() => true)),
885889
utimes: (path, atime, mtime) => vfsOp(path, (vfs, n) => vfs.promises.utimes(n, atime, mtime).then(() => true)),
886890
open(path, flags, mode) {
891+
return vfsOp(path, (vfs, n) => PromiseResolve(vfs.openSync(n, flags, mode)));
892+
},
893+
promisesOpen(path, flags, mode) {
887894
const pathStr = toPathStr(path);
888895
if (pathStr !== null) {
889896
const r = findVFSForPath(pathStr);

src/node_options.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,12 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
663663
kAllowedInEnvvar,
664664
false,
665665
OptionNamespaces::kPermissionNamespace);
666+
AddOption("--allow-fs-vfs",
667+
"allow use of virtual filesystem when any permissions are set",
668+
&EnvironmentOptions::allow_fs_vfs,
669+
kAllowedInEnvvar,
670+
false,
671+
OptionNamespaces::kPermissionNamespace);
666672
AddOption("--allow-child-process",
667673
"allow use of child process when any permissions are set",
668674
&EnvironmentOptions::allow_child_process,

src/node_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ class EnvironmentOptions : public Options {
142142
std::vector<std::string> allow_fs_read;
143143
std::vector<std::string> allow_fs_write;
144144
bool allow_addons = false;
145+
bool allow_fs_vfs = false;
145146
bool allow_inspector = false;
146147
bool allow_child_process = false;
147148
bool allow_net = false;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict';
2+
3+
// writeSync in append mode must append, not overwrite.
4+
5+
require('../common');
6+
const assert = require('assert');
7+
const vfs = require('node:vfs');
8+
9+
const myVfs = vfs.create();
10+
myVfs.writeFileSync('/append.txt', 'init');
11+
12+
const fd = myVfs.openSync('/append.txt', 'a');
13+
const buf = Buffer.from(' more');
14+
myVfs.writeSync(fd, buf, 0, buf.length);
15+
myVfs.closeSync(fd);
16+
17+
const content = myVfs.readFileSync('/append.txt', 'utf8');
18+
assert.strictEqual(content, 'init more');
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
// Cross-VFS rename, copyFile, and link must throw EXDEV.
4+
5+
require('../common');
6+
const assert = require('assert');
7+
const fs = require('fs');
8+
const vfs = require('node:vfs');
9+
10+
const vfs1 = vfs.create();
11+
vfs1.writeFileSync('/src.txt', 'data');
12+
vfs1.mount('/xdev_a');
13+
14+
const vfs2 = vfs.create();
15+
vfs2.mkdirSync('/dest');
16+
vfs2.mount('/xdev_b');
17+
18+
assert.throws(() => fs.renameSync('/xdev_a/src.txt', '/xdev_b/dest/src.txt'), {
19+
code: 'EXDEV',
20+
});
21+
22+
assert.throws(() => fs.copyFileSync('/xdev_a/src.txt', '/xdev_b/dest/src.txt'), {
23+
code: 'EXDEV',
24+
});
25+
26+
assert.throws(() => fs.linkSync('/xdev_a/src.txt', '/xdev_b/dest/link.txt'), {
27+
code: 'EXDEV',
28+
});
29+
30+
vfs2.unmount();
31+
vfs1.unmount();
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict';
2+
3+
// VirtualDir must support Symbol.dispose and Symbol.asyncDispose.
4+
5+
const common = require('../common');
6+
const assert = require('assert');
7+
const fs = require('fs');
8+
const vfs = require('node:vfs');
9+
10+
const myVfs = vfs.create();
11+
myVfs.mkdirSync('/dir');
12+
myVfs.writeFileSync('/dir/a.txt', 'a');
13+
myVfs.mount('/mnt_dirdispose');
14+
15+
// Symbol.dispose via closeSync
16+
{
17+
const dir = fs.opendirSync('/mnt_dirdispose/dir');
18+
dir[Symbol.dispose]();
19+
assert.throws(() => dir.closeSync(), { code: 'ERR_DIR_CLOSED' });
20+
}
21+
22+
// Symbol.asyncDispose via close
23+
{
24+
const dir = fs.opendirSync('/mnt_dirdispose/dir');
25+
dir[Symbol.asyncDispose]().then(common.mustCall(() => {
26+
assert.throws(() => dir.closeSync(), { code: 'ERR_DIR_CLOSED' });
27+
}));
28+
}
29+
30+
myVfs.unmount();

0 commit comments

Comments
 (0)