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
75 changes: 32 additions & 43 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -1864,16 +1864,16 @@ function readdir(path, options, callback) {
options = undefined;
}

const h = vfsState.handlers;
if (h !== null && vfsResult(h.readdir(path, options), callback)) return;

callback = makeCallback(callback);
options = getOptions(options);
path = getValidatedPath(path);
if (options.recursive != null) {
validateBoolean(options.recursive, 'options.recursive');
}

const h = vfsState.handlers;
if (h !== null && vfsResult(h.readdir(path, options), callback)) return;

if (options.recursive) {
readdirRecursive(path, options, callback);
return;
Expand Down Expand Up @@ -1910,17 +1910,18 @@ function readdir(path, options, callback) {
* @returns {string | Buffer[] | Dirent[]}
*/
function readdirSync(path, options) {
const h = vfsState.handlers;
if (h !== null) {
const result = h.readdirSync(path, options);
if (result !== undefined) return result;
}
options = getOptions(options);
path = getValidatedPath(path);
if (options.recursive != null) {
validateBoolean(options.recursive, 'options.recursive');
}

const h = vfsState.handlers;
if (h !== null) {
const result = h.readdirSync(path, options);
if (result !== undefined) return result;
}

if (options.recursive) {
return readdirSyncRecursive(path, options);
}
Expand Down Expand Up @@ -2417,7 +2418,7 @@ function fchmod(fd, mode, callback) {
callback = makeCallback(callback);

const h = vfsState.handlers;
if (h !== null && vfsVoid(h.fchmod(fd), callback)) return;
if (h !== null && vfsVoid(h.fchmod(fd, mode), callback)) return;

if (permission.isEnabled()) {
callback(new ERR_ACCESS_DENIED('fchmod API is disabled when Permission Model is enabled.'));
Expand All @@ -2436,19 +2437,18 @@ function fchmod(fd, mode, callback) {
* @returns {void}
*/
function fchmodSync(fd, mode) {
mode = parseFileMode(mode, 'mode');

const h = vfsState.handlers;
if (h !== null) {
const result = h.fchmodSync(fd);
const result = h.fchmodSync(fd, mode);
if (result !== undefined) return;
}

if (permission.isEnabled()) {
throw new ERR_ACCESS_DENIED('fchmod API is disabled when Permission Model is enabled.');
}
binding.fchmod(
fd,
parseFileMode(mode, 'mode'),
);
binding.fchmod(fd, mode);
}

/**
Expand Down Expand Up @@ -2687,18 +2687,15 @@ function chownSync(path, uid, gid) {
function utimes(path, atime, mtime, callback) {
callback = makeCallback(callback);
path = getValidatedPath(path);
atime = toUnixTimestamp(atime);
mtime = toUnixTimestamp(mtime);

const h = vfsState.handlers;
if (h !== null && vfsVoid(h.utimes(path, atime, mtime), callback)) return;

const req = new FSReqCallback();
req.oncomplete = callback;
binding.utimes(
path,
toUnixTimestamp(atime),
toUnixTimestamp(mtime),
req,
);
binding.utimes(path, atime, mtime, req);
}

/**
Expand All @@ -2711,18 +2708,16 @@ function utimes(path, atime, mtime, callback) {
*/
function utimesSync(path, atime, mtime) {
path = getValidatedPath(path);
atime = toUnixTimestamp(atime);
mtime = toUnixTimestamp(mtime);

const h = vfsState.handlers;
if (h !== null) {
const result = h.utimesSync(path, atime, mtime);
if (result !== undefined) return;
}

binding.utimes(
path,
toUnixTimestamp(atime),
toUnixTimestamp(mtime),
);
binding.utimes(path, atime, mtime);
}

/**
Expand All @@ -2740,7 +2735,7 @@ function futimes(fd, atime, mtime, callback) {
callback = makeCallback(callback);

const h = vfsState.handlers;
if (h !== null && vfsVoid(h.futimes(fd), callback)) return;
if (h !== null && vfsVoid(h.futimes(fd, atime, mtime), callback)) return;

if (permission.isEnabled()) {
callback(new ERR_ACCESS_DENIED('futimes API is disabled when Permission Model is enabled.'));
Expand All @@ -2762,21 +2757,20 @@ function futimes(fd, atime, mtime, callback) {
* @returns {void}
*/
function futimesSync(fd, atime, mtime) {
atime = toUnixTimestamp(atime, 'atime');
mtime = toUnixTimestamp(mtime, 'mtime');

const h = vfsState.handlers;
if (h !== null) {
const result = h.futimesSync(fd);
const result = h.futimesSync(fd, atime, mtime);
if (result !== undefined) return;
}

if (permission.isEnabled()) {
throw new ERR_ACCESS_DENIED('futimes API is disabled when Permission Model is enabled.');
}

binding.futimes(
fd,
toUnixTimestamp(atime, 'atime'),
toUnixTimestamp(mtime, 'mtime'),
);
binding.futimes(fd, atime, mtime);
}

/**
Expand All @@ -2791,18 +2785,15 @@ function futimesSync(fd, atime, mtime) {
function lutimes(path, atime, mtime, callback) {
callback = makeCallback(callback);
path = getValidatedPath(path);
atime = toUnixTimestamp(atime);
mtime = toUnixTimestamp(mtime);

const h = vfsState.handlers;
if (h !== null && vfsVoid(h.lutimes(path, atime, mtime), callback)) return;

const req = new FSReqCallback();
req.oncomplete = callback;
binding.lutimes(
path,
toUnixTimestamp(atime),
toUnixTimestamp(mtime),
req,
);
binding.lutimes(path, atime, mtime, req);
}

/**
Expand All @@ -2815,18 +2806,16 @@ function lutimes(path, atime, mtime, callback) {
*/
function lutimesSync(path, atime, mtime) {
path = getValidatedPath(path);
atime = toUnixTimestamp(atime);
mtime = toUnixTimestamp(mtime);

const h = vfsState.handlers;
if (h !== null) {
const result = h.lutimesSync(path, atime, mtime);
if (result !== undefined) return;
}

binding.lutimes(
path,
toUnixTimestamp(atime),
toUnixTimestamp(mtime),
);
binding.lutimes(path, atime, mtime);
}

function writeAll(fd, isUserFd, buffer, offset, length, signal, flush, callback) {
Expand Down
31 changes: 14 additions & 17 deletions lib/internal/fs/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -1668,17 +1668,18 @@ async function readdirRecursiveWithPermissionModel(basePath, options) {
}

async function readdir(path, options) {
const h = vfsState.handlers;
if (h !== null) {
const promise = h.readdir(path, options);
if (promise !== undefined) return await promise;
}
options = getOptions(options);

// Make shallow copy to prevent mutating options from affecting results
options = copyObject(options);

path = getValidatedPath(path);

const h = vfsState.handlers;
if (h !== null) {
const promise = h.readdir(path, options);
if (promise !== undefined) return await promise;
}
if (options.recursive) {
return readdirRecursive(path, options);
}
Expand Down Expand Up @@ -1954,6 +1955,8 @@ async function chown(path, uid, gid) {

async function utimes(path, atime, mtime) {
path = getValidatedPath(path);
atime = toUnixTimestamp(atime);
mtime = toUnixTimestamp(mtime);

const h = vfsState.handlers;
if (h !== null) {
Expand All @@ -1962,12 +1965,7 @@ async function utimes(path, atime, mtime) {
}

return await PromisePrototypeThen(
binding.utimes(
path,
toUnixTimestamp(atime),
toUnixTimestamp(mtime),
kUsePromises,
),
binding.utimes(path, atime, mtime, kUsePromises),
undefined,
handleErrorFromBinding,
);
Expand All @@ -1987,19 +1985,18 @@ async function futimes(handle, atime, mtime) {
}

async function lutimes(path, atime, mtime) {
path = getValidatedPath(path);
atime = toUnixTimestamp(atime);
mtime = toUnixTimestamp(mtime);

const h = vfsState.handlers;
if (h !== null) {
const promise = h.lutimes(path, atime, mtime);
if (promise !== undefined) { await promise; return; }
}

return await PromisePrototypeThen(
binding.lutimes(
getValidatedPath(path),
toUnixTimestamp(atime),
toUnixTimestamp(mtime),
kUsePromises,
),
binding.lutimes(path, atime, mtime, kUsePromises),
undefined,
handleErrorFromBinding,
);
Expand Down
7 changes: 6 additions & 1 deletion lib/internal/vfs/dir.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,15 @@ class VirtualDir {
this.closeSync();
}
}

async [SymbolAsyncDispose]() {
if (!this.#closed) {
this.closeSync();
}
}
}

VirtualDir.prototype[SymbolAsyncIterator] = VirtualDir.prototype.entries;
VirtualDir.prototype[SymbolAsyncDispose] = VirtualDir.prototype.close;

module.exports = {
VirtualDir,
Expand Down
53 changes: 49 additions & 4 deletions lib/internal/vfs/file_handle.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const {
const {
createEBADF,
} = require('internal/vfs/errors');
const { toUnixTimestamp } = require('internal/fs/utils');
const { parseFileMode } = require('internal/validators');

// Private symbols
const kPath = Symbol('kPath');
Expand Down Expand Up @@ -241,10 +243,17 @@ class VirtualFileHandle {
}

/**
* No-op chmod - VFS files don't have real permissions.
* @param {number} mode The new permission bits
*/
chmodSync(mode) {}

/**
* @param {number} mode The new permission bits
* @returns {Promise<void>}
*/
async chmod() {}
async chmod(mode) {
this.chmodSync(mode);
}

/**
* No-op chown - VFS files don't have real ownership.
Expand All @@ -253,10 +262,19 @@ class VirtualFileHandle {
async chown() {}

/**
* No-op utimes - timestamps are handled by the provider.
* @param {Date|number|string} atime The new access time
* @param {Date|number|string} mtime The new modification time
*/
utimesSync(atime, mtime) {}

/**
* @param {Date|number|string} atime The new access time
* @param {Date|number|string} mtime The new modification time
* @returns {Promise<void>}
*/
async utimes() {}
async utimes(atime, mtime) {
this.utimesSync(atime, mtime);
}

/**
* No-op datasync - VFS is in-memory.
Expand Down Expand Up @@ -666,6 +684,33 @@ class MemoryFileHandle extends VirtualFileHandle {
throw new ERR_INVALID_STATE('stats not available');
}

/**
* @param {number} mode The new permission bits
*/
chmodSync(mode) {
this.#checkClosed('fchmod');
mode = parseFileMode(mode, 'mode');
if (this.#entry) {
this.#entry.mode = (this.#entry.mode & ~0o7777) | (mode & 0o7777);
this.#entry.ctime = DateNow();
}
}

/**
* @param {Date|number|string} atime The new access time
* @param {Date|number|string} mtime The new modification time
*/
utimesSync(atime, mtime) {
this.#checkClosed('futimes');
const atimeMs = toUnixTimestamp(atime, 'atime') * 1000;
const mtimeMs = toUnixTimestamp(mtime, 'mtime') * 1000;
if (this.#entry) {
this.#entry.atime = atimeMs;
this.#entry.mtime = mtimeMs;
this.#entry.ctime = DateNow();
}
}

/**
* Gets file stats.
* @param {object} [options] Options
Expand Down
Loading