Summary
The process option added in #1248 is stored on the Superblock instance and used for
path resolution, but Node.canRead(), Node.canWrite(), and Node.canExecute() still use
the global process.getuid/process.getgid captured at module load time.
This means fs.accessSync() with X_OK, R_OK, or W_OK ignores the custom process
object for permission checks.
Reproduction
const { memfs } = require('memfs');
const { fs } = memfs({ '/exec.sh': '#!/bin/sh' }, {
process: {
getuid: () => 1000,
getgid: () => 1000,
cwd: () => '/',
platform: 'linux',
emitWarning: () => {},
env: {},
},
});
fs.chmodSync('/exec.sh', 0o100); // user-execute only
fs.chownSync('/exec.sh', 1000, 1000);
// Expected: no error (uid 1000 owns the file and has execute permission)
// Actual: throws EACCES
fs.accessSync('/exec.sh', 1); // X_OK
Root cause
In @jsonjoy.com/fs-core/lib/Node.js:
const getuid = () => process_1.default.getuid?.() ?? 0;
const getgid = () => process_1.default.getgid?.() ?? 0;
// ...
canExecute(uid = getuid(), gid = getgid()) { ... }
canRead(uid = getuid(), gid = getgid()) { ... }
canWrite(uid = getuid(), gid = getgid()) { ... }
These use the global process default, not the instance-level this.process from the
Superblock.
Meanwhile Superblock.js correctly uses this.process.getuid?.() for its own path
resolution checks (lines ~350, ~378), so there's an inconsistency.
Expected behavior
accessSync/access should use the custom process object passed via memfs() options
or Volume.fromJSON() for all permission checks, including canRead, canWrite, and
canExecute on Node.
Versions
- memfs: 4.57.1
- @jsonjoy.com/fs-core: (bundled with memfs 4.57.1)
Summary
The
processoption added in #1248 is stored on theSuperblockinstance and used forpath resolution, but
Node.canRead(),Node.canWrite(), andNode.canExecute()still usethe global
process.getuid/process.getgidcaptured at module load time.This means
fs.accessSync()withX_OK,R_OK, orW_OKignores the custom processobject for permission checks.
Reproduction
Root cause
In
@jsonjoy.com/fs-core/lib/Node.js:These use the global
processdefault, not the instance-levelthis.processfrom theSuperblock.
Meanwhile
Superblock.jscorrectly usesthis.process.getuid?.()for its own pathresolution checks (lines ~350, ~378), so there's an inconsistency.
Expected behavior
accessSync/accessshould use the customprocessobject passed viamemfs()optionsor
Volume.fromJSON()for all permission checks, includingcanRead,canWrite, andcanExecuteonNode.Versions