New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@zenfs/core

Package Overview
Dependencies
Maintainers
0
Versions
169
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@zenfs/core - npm Package Compare versions

Comparing version 0.18.0 to 0.18.1

8

dist/backends/overlay.d.ts

@@ -91,7 +91,5 @@ import type { File } from '../file.js';

}
declare const OverlayFS_base: import("../mixins/shared.js").Mixin<typeof UnmutexedOverlayFS, {
lock(path: string, syscall: string): Promise<import("../mixins/mutexed.js").MutexLock>;
lockSync(path: string): import("../mixins/mutexed.js").MutexLock;
isLocked(path: string): boolean;
}>;
declare const OverlayFS_base: {
new (): import("../mixins/mutexed.js").__MutexedFS<UnmutexedOverlayFS>;
} & (new (args_0: OverlayOptions) => import("../mixins/mutexed.js").__MutexedFS<UnmutexedOverlayFS>);
/**

@@ -98,0 +96,0 @@ * OverlayFS makes a read-only filesystem writable by storing writes on a second,

@@ -379,3 +379,4 @@ import { dirname } from '../emulation/path.js';

createParentDirectoriesSync(path) {
let parent = dirname(path), toCreate = [];
let parent = dirname(path);
const toCreate = [];
while (!this.writable.existsSync(parent)) {

@@ -385,9 +386,9 @@ toCreate.push(parent);

}
toCreate = toCreate.reverse();
for (const p of toCreate) {
this.writable.mkdirSync(p, this.statSync(p).mode);
for (const path of toCreate.reverse()) {
this.writable.mkdirSync(path, this.statSync(path).mode);
}
}
async createParentDirectories(path) {
let parent = dirname(path), toCreate = [];
let parent = dirname(path);
const toCreate = [];
while (!(await this.writable.exists(parent))) {

@@ -397,6 +398,5 @@ toCreate.push(parent);

}
toCreate = toCreate.reverse();
for (const p of toCreate) {
const stats = await this.stat(p);
await this.writable.mkdir(p, stats.mode);
for (const path of toCreate.reverse()) {
const stats = await this.stat(path);
await this.writable.mkdir(path, stats.mode);
}

@@ -403,0 +403,0 @@ }

@@ -20,5 +20,4 @@ /// <reference types="node" resolution-mode="require"/>

export declare class PortFile extends File {
readonly fs: PortFS;
fs: PortFS;
readonly fd: number;
readonly path: string;
position: number;

@@ -25,0 +24,0 @@ constructor(fs: PortFS, fd: number, path: string, position: number);

@@ -11,6 +11,5 @@ import { resolveMountConfig } from '../../config.js';

constructor(fs, fd, path, position) {
super();
super(fs, path);
this.fs = fs;
this.fd = fd;
this.path = path;
this.position = position;

@@ -27,3 +26,3 @@ }

_throwNoSync(syscall) {
throw new ErrnoError(Errno.ENOTSUP, 'Syncrohnous operations not support on PortFile', this.path, syscall);
throw new ErrnoError(Errno.ENOTSUP, 'Synchronous operations not supported on PortFile', this.path, syscall);
}

@@ -30,0 +29,0 @@ async stat() {

@@ -57,3 +57,2 @@ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {

import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
import { credentials } from '../credentials.js';
import { ReadStream, WriteStream } from './streams.js';

@@ -217,3 +216,3 @@ import { FSWatcher, emitChange } from './watchers.js';

const stats = await this.file.stat();
if (!stats.hasAccess(constants.R_OK, credentials)) {
if (!stats.hasAccess(constants.R_OK)) {
throw ErrnoError.With('EACCES', this.file.path, 'stat');

@@ -362,3 +361,3 @@ }

const dst = resolveMount(newPath);
if (!(await stat(dirname(oldPath))).hasAccess(constants.W_OK, credentials)) {
if (!(await stat(dirname(oldPath))).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', oldPath, 'rename');

@@ -402,3 +401,3 @@ }

const stats = await fs.stat(resolved);
if (!stats.hasAccess(constants.R_OK, credentials)) {
if (!stats.hasAccess(constants.R_OK)) {
throw ErrnoError.With('EACCES', path, 'stat');

@@ -456,3 +455,3 @@ }

try {
if (!(await fs.stat(resolved)).hasAccess(constants.W_OK, credentials)) {
if (!(await fs.stat(resolved)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', resolved, 'unlink');

@@ -484,3 +483,3 @@ }

const parentStats = await fs.stat(dirname(resolved));
if (!parentStats.hasAccess(constants.W_OK, credentials)) {
if (!parentStats.hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(path), '_open');

@@ -493,3 +492,3 @@ }

}
if (!stats.hasAccess(flagToMode(flag), credentials)) {
if (!stats.hasAccess(flagToMode(flag))) {
throw ErrnoError.With('EACCES', path, '_open');

@@ -620,3 +619,3 @@ }

try {
if (!(await fs.stat(resolved)).hasAccess(constants.W_OK, credentials)) {
if (!(await fs.stat(resolved)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', resolved, 'rmdir');

@@ -641,3 +640,3 @@ }

if (!options?.recursive) {
if (!(await fs.stat(dirname(resolved))).hasAccess(constants.W_OK, credentials)) {
if (!(await fs.stat(dirname(resolved))).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(resolved), 'mkdir');

@@ -655,3 +654,3 @@ }

for (const dir of dirs) {
if (!(await fs.stat(dirname(dir))).hasAccess(constants.W_OK, credentials)) {
if (!(await fs.stat(dirname(dir))).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(dir), 'mkdir');

@@ -671,3 +670,3 @@ }

path = normalizePath(path);
if (!(await stat(path)).hasAccess(constants.R_OK, credentials)) {
if (!(await stat(path)).hasAccess(constants.R_OK)) {
throw ErrnoError.With('EACCES', path, 'readdir');

@@ -709,7 +708,7 @@ }

targetPath = normalizePath(targetPath);
if (!(await stat(dirname(targetPath))).hasAccess(constants.R_OK, credentials)) {
if (!(await stat(dirname(targetPath))).hasAccess(constants.R_OK)) {
throw ErrnoError.With('EACCES', dirname(targetPath), 'link');
}
linkPath = normalizePath(linkPath);
if (!(await stat(dirname(linkPath))).hasAccess(constants.W_OK, credentials)) {
if (!(await stat(dirname(linkPath))).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(linkPath), 'link');

@@ -723,3 +722,3 @@ }

try {
if (!(await fs.stat(path)).hasAccess(constants.W_OK, credentials)) {
if (!(await fs.stat(path)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', path, 'link');

@@ -954,3 +953,3 @@ }

const stats = await stat(path);
if (!stats.hasAccess(mode, credentials)) {
if (!stats.hasAccess(mode)) {
throw new ErrnoError(Errno.EACCES);

@@ -957,0 +956,0 @@ }

@@ -55,3 +55,2 @@ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {

import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
import { credentials } from '../credentials.js';
import { emitChange } from './watchers.js';

@@ -68,3 +67,3 @@ /**

const newMount = resolveMount(newPath);
if (!statSync(dirname(oldPath)).hasAccess(constants.W_OK, credentials)) {
if (!statSync(dirname(oldPath)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', oldPath, 'rename');

@@ -110,3 +109,3 @@ }

const stats = fs.statSync(resolved);
if (!stats.hasAccess(constants.R_OK, credentials)) {
if (!stats.hasAccess(constants.R_OK)) {
throw ErrnoError.With('EACCES', path, 'stat');

@@ -165,3 +164,3 @@ }

try {
if (!fs.statSync(resolved).hasAccess(constants.W_OK, credentials)) {
if (!fs.statSync(resolved).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', resolved, 'unlink');

@@ -188,3 +187,3 @@ }

const parentStats = fs.statSync(dirname(resolved));
if (!parentStats.hasAccess(constants.W_OK, credentials)) {
if (!parentStats.hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(path), '_open');

@@ -198,3 +197,3 @@ }

const stats = fs.statSync(resolved);
if (!stats.hasAccess(mode, credentials) || !stats.hasAccess(flagToMode(flag), credentials)) {
if (!stats.hasAccess(mode) || !stats.hasAccess(flagToMode(flag))) {
throw ErrnoError.With('EACCES', path, '_open');

@@ -449,3 +448,3 @@ }

try {
if (!fs.statSync(resolved).hasAccess(constants.W_OK, credentials)) {
if (!fs.statSync(resolved).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', resolved, 'rmdir');

@@ -470,3 +469,3 @@ }

if (!options?.recursive) {
if (!fs.statSync(dirname(resolved)).hasAccess(constants.W_OK, credentials)) {
if (!fs.statSync(dirname(resolved)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(resolved), 'mkdir');

@@ -482,3 +481,3 @@ }

for (const dir of dirs) {
if (!fs.statSync(dirname(dir)).hasAccess(constants.W_OK, credentials)) {
if (!fs.statSync(dirname(dir)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(dir), 'mkdir');

@@ -500,3 +499,3 @@ }

let entries;
if (!statSync(path).hasAccess(constants.R_OK, credentials)) {
if (!statSync(path).hasAccess(constants.R_OK)) {
throw ErrnoError.With('EACCES', path, 'readdir');

@@ -540,7 +539,7 @@ }

targetPath = normalizePath(targetPath);
if (!statSync(dirname(targetPath)).hasAccess(constants.R_OK, credentials)) {
if (!statSync(dirname(targetPath)).hasAccess(constants.R_OK)) {
throw ErrnoError.With('EACCES', dirname(targetPath), 'link');
}
linkPath = normalizePath(linkPath);
if (!statSync(dirname(linkPath)).hasAccess(constants.W_OK, credentials)) {
if (!statSync(dirname(linkPath)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(linkPath), 'link');

@@ -554,3 +553,3 @@ }

try {
if (!fs.statSync(path).hasAccess(constants.W_OK, credentials)) {
if (!fs.statSync(path).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', path, 'link');

@@ -687,3 +686,3 @@ }

const stats = statSync(path);
if (!stats.hasAccess(mode, credentials)) {
if (!stats.hasAccess(mode)) {
throw new ErrnoError(Errno.EACCES);

@@ -690,0 +689,0 @@ }

@@ -40,10 +40,25 @@ /// <reference types="node" resolution-mode="require"/>

/**
* Get the current file position.
* @internal
* The file system that created the file
*/
abstract position: number;
fs: FileSystem;
/**
* The path to the file
*/
abstract readonly path: string;
readonly path: string;
constructor(
/**
* @internal
* The file system that created the file
*/
fs: FileSystem,
/**
* The path to the file
*/
path: string);
/**
* Get the current file position.
*/
abstract position: number;
/**
* Asynchronous `stat`.

@@ -187,8 +202,5 @@ */

* The file system that created the file.
* @internal
*/
protected fs: FS;
/**
* Path to the file
*/
readonly path: string;
fs: FS;
readonly flag: string;

@@ -224,9 +236,6 @@ readonly stats: Stats;

* The file system that created the file.
* @internal
*/
fs: FS,
fs: FS, path: string, flag: string, stats: Stats, _buffer?: Uint8Array);
/**
* Path to the file
*/
path: string, flag: string, stats: Stats, _buffer?: Uint8Array);
/**
* Get the underlying buffer for this file. Mutating not recommended and will mess up dirty tracking.

@@ -233,0 +242,0 @@ */

@@ -108,2 +108,15 @@ import { O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_SYNC, O_TRUNC, O_WRONLY, S_IFMT } from './emulation/constants.js';

export class File {
constructor(
/**
* @internal
* The file system that created the file
*/
fs,
/**
* The path to the file
*/
path) {
this.fs = fs;
this.path = path;
}
[Symbol.asyncDispose]() {

@@ -153,11 +166,7 @@ return this.close();

* The file system that created the file.
* @internal
*/
fs,
/**
* Path to the file
*/
path, flag, stats, _buffer = new Uint8Array(new ArrayBuffer(0, fs.metadata().noResizableBuffers ? {} : { maxByteLength: size_max }))) {
super();
fs, path, flag, stats, _buffer = new Uint8Array(new ArrayBuffer(0, fs.metadata().noResizableBuffers ? {} : { maxByteLength: size_max }))) {
super(fs, path);
this.fs = fs;
this.path = path;
this.flag = flag;

@@ -164,0 +173,0 @@ this.stats = stats;

@@ -1,6 +0,7 @@

import type { FileSystem } from '../filesystem.js';
import type { File } from '../file.js';
import type { FileSystem, FileSystemMetadata } from '../filesystem.js';
import '../polyfills.js';
import type { Mixin } from './shared.js';
import type { Stats } from '../stats.js';
import type { Concrete } from '../utils.js';
export declare class MutexLock {
readonly path: string;
protected readonly previous?: MutexLock | undefined;

@@ -10,3 +11,3 @@ protected current: PromiseWithResolvers<void>;

get isLocked(): boolean;
constructor(path: string, previous?: MutexLock | undefined);
constructor(previous?: MutexLock | undefined);
done(): Promise<void>;

@@ -17,2 +18,60 @@ unlock(): void;

/**
* @hidden
*/
export declare class __MutexedFS<T extends FileSystem> implements FileSystem {
/**
* @internal
*/
_fs: T;
ready(): Promise<void>;
metadata(): FileSystemMetadata;
/**
* The current locks
*/
private currentLock?;
/**
* Adds a lock for a path
*/
protected addLock(): MutexLock;
/**
* Locks `path` asynchronously.
* If the path is currently locked, waits for it to be unlocked.
* @internal
*/
lock(path: string, syscall: string): Promise<MutexLock>;
/**
* Locks `path` asynchronously.
* If the path is currently locked, an error will be thrown
* @internal
*/
lockSync(path: string, syscall: string): MutexLock;
/**
* Whether `path` is locked
* @internal
*/
get isLocked(): boolean;
rename(oldPath: string, newPath: string): Promise<void>;
renameSync(oldPath: string, newPath: string): void;
stat(path: string): Promise<Stats>;
statSync(path: string): Stats;
openFile(path: string, flag: string): Promise<File>;
openFileSync(path: string, flag: string): File;
createFile(path: string, flag: string, mode: number): Promise<File>;
createFileSync(path: string, flag: string, mode: number): File;
unlink(path: string): Promise<void>;
unlinkSync(path: string): void;
rmdir(path: string): Promise<void>;
rmdirSync(path: string): void;
mkdir(path: string, mode: number): Promise<void>;
mkdirSync(path: string, mode: number): void;
readdir(path: string): Promise<string[]>;
readdirSync(path: string): string[];
exists(path: string): Promise<boolean>;
existsSync(path: string): boolean;
link(srcpath: string, dstpath: string): Promise<void>;
linkSync(srcpath: string, dstpath: string): void;
sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
}
/**
* This serializes access to an underlying async filesystem.

@@ -26,3 +85,8 @@ * For example, on an OverlayFS instance with an async lower

*
* Note: `@ts-expect-error 2513` is needed because `FS` is not properly detected as being concrete
* Note:
* Instead of extending the passed class, `MutexedFS` stores it internally.
* This is to avoid a deadlock caused when a mathod calls another one
* The problem is discussed extensivly in [#78](https://github.com/zen-fs/core/issues/78)
* Instead of extending `FileSystem`,
* `MutexedFS` implements it in order to make sure all of the methods are passed through
*

@@ -32,6 +96,4 @@ * @todo Change `using _` to `using void` pending https://github.com/tc39/proposal-discard-binding

*/
export declare function Mutexed<T extends new (...args: any[]) => FileSystem>(FS: T): Mixin<T, {
lock(path: string, syscall: string): Promise<MutexLock>;
lockSync(path: string): MutexLock;
isLocked(path: string): boolean;
}>;
export declare function Mutexed<const T extends Concrete<typeof FileSystem>>(FS: T): typeof __MutexedFS<InstanceType<T>> & {
new (...args: ConstructorParameters<T>): __MutexedFS<InstanceType<T>>;
};

@@ -52,4 +52,3 @@ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {

}
constructor(path, previous) {
this.path = path;
constructor(previous) {
this.previous = previous;

@@ -72,404 +71,401 @@ this.current = Promise.withResolvers();

/**
* This serializes access to an underlying async filesystem.
* For example, on an OverlayFS instance with an async lower
* directory operations like rename and rmdir may involve multiple
* requests involving both the upper and lower filesystems -- they
* are not executed in a single atomic step. OverlayFS uses this
* to avoid having to reason about the correctness of
* multiple requests interleaving.
*
* Note: `@ts-expect-error 2513` is needed because `FS` is not properly detected as being concrete
*
* @todo Change `using _` to `using void` pending https://github.com/tc39/proposal-discard-binding
* @internal
* @hidden
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function Mutexed(FS) {
class MutexedFS extends FS {
constructor() {
super(...arguments);
/**
* The current locks
*/
this.locks = new Map();
/* eslint-enable @typescript-eslint/no-unused-vars */
export class __MutexedFS {
async ready() {
return await this._fs.ready();
}
metadata() {
return this._fs.metadata();
}
/**
* Adds a lock for a path
*/
addLock() {
const lock = new MutexLock(this.currentLock);
this.currentLock = lock;
return lock;
}
/**
* Locks `path` asynchronously.
* If the path is currently locked, waits for it to be unlocked.
* @internal
*/
async lock(path, syscall) {
const previous = this.currentLock;
const lock = this.addLock();
const stack = new Error().stack;
setTimeout(() => {
if (lock.isLocked) {
const error = ErrnoError.With('EDEADLK', path, syscall);
error.stack += stack?.slice('Error'.length);
throw error;
}
}, 5000);
await previous?.done();
return lock;
}
/**
* Locks `path` asynchronously.
* If the path is currently locked, an error will be thrown
* @internal
*/
lockSync(path, syscall) {
if (this.currentLock) {
throw ErrnoError.With('EBUSY', path, syscall);
}
/**
* Adds a lock for a path
*/
addLock(path) {
const previous = this.locks.get(path);
const lock = new MutexLock(path, previous?.isLocked ? previous : undefined);
this.locks.set(path, lock);
return lock;
return this.addLock();
}
/**
* Whether `path` is locked
* @internal
*/
get isLocked() {
return !!this.currentLock?.isLocked;
}
/* eslint-disable @typescript-eslint/no-unused-vars */
async rename(oldPath, newPath) {
const env_1 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_1, await this.lock(oldPath, 'rename'), false);
await this._fs.rename(oldPath, newPath);
}
/**
* Locks `path` asynchronously.
* If the path is currently locked, waits for it to be unlocked.
* @internal
*/
async lock(path, syscall) {
const previous = this.locks.get(path);
const lock = this.addLock(path);
const stack = new Error().stack;
setTimeout(() => {
if (lock.isLocked) {
const error = ErrnoError.With('EDEADLK', path, syscall);
error.stack += stack?.slice('Error'.length);
throw error;
}
}, 5000);
await previous?.done();
return lock;
catch (e_1) {
env_1.error = e_1;
env_1.hasError = true;
}
/**
* Locks `path` asynchronously.
* If the path is currently locked, an error will be thrown
* @internal
*/
lockSync(path) {
if (this.locks.has(path)) {
// Non-null assertion: we already checked locks has path
throw ErrnoError.With('EBUSY', path, 'lock');
}
return this.addLock(path);
finally {
__disposeResources(env_1);
}
/**
* Whether `path` is locked
* @internal
*/
isLocked(path) {
return !!this.locks.get(path)?.isLocked;
}
renameSync(oldPath, newPath) {
const env_2 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_2, this.lockSync(oldPath, 'rename'), false);
return this._fs.renameSync(oldPath, newPath);
}
/* eslint-disable @typescript-eslint/no-unused-vars */
async rename(oldPath, newPath) {
const env_1 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_1, await this.lock(oldPath, 'rename'), false);
// @ts-expect-error 2513
await super.rename(oldPath, newPath);
}
catch (e_1) {
env_1.error = e_1;
env_1.hasError = true;
}
finally {
__disposeResources(env_1);
}
catch (e_2) {
env_2.error = e_2;
env_2.hasError = true;
}
renameSync(oldPath, newPath) {
const env_2 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_2, this.lockSync(oldPath), false);
// @ts-expect-error 2513
return super.renameSync(oldPath, newPath);
}
catch (e_2) {
env_2.error = e_2;
env_2.hasError = true;
}
finally {
__disposeResources(env_2);
}
finally {
__disposeResources(env_2);
}
async stat(path) {
const env_3 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_3, await this.lock(path, 'stat'), false);
// @ts-expect-error 2513
return await super.stat(path);
}
catch (e_3) {
env_3.error = e_3;
env_3.hasError = true;
}
finally {
__disposeResources(env_3);
}
}
async stat(path) {
const env_3 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_3, await this.lock(path, 'stat'), false);
return await this._fs.stat(path);
}
statSync(path) {
const env_4 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_4, this.lockSync(path), false);
// @ts-expect-error 2513
return super.statSync(path);
}
catch (e_4) {
env_4.error = e_4;
env_4.hasError = true;
}
finally {
__disposeResources(env_4);
}
catch (e_3) {
env_3.error = e_3;
env_3.hasError = true;
}
async openFile(path, flag) {
const env_5 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_5, await this.lock(path, 'openFile'), false);
// @ts-expect-error 2513
return await super.openFile(path, flag);
}
catch (e_5) {
env_5.error = e_5;
env_5.hasError = true;
}
finally {
__disposeResources(env_5);
}
finally {
__disposeResources(env_3);
}
openFileSync(path, flag) {
const env_6 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_6, this.lockSync(path), false);
// @ts-expect-error 2513
return super.openFileSync(path, flag);
}
catch (e_6) {
env_6.error = e_6;
env_6.hasError = true;
}
finally {
__disposeResources(env_6);
}
}
statSync(path) {
const env_4 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_4, this.lockSync(path, 'stat'), false);
return this._fs.statSync(path);
}
async createFile(path, flag, mode) {
const env_7 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_7, await this.lock(path, 'createFile'), false);
// @ts-expect-error 2513
return await super.createFile(path, flag, mode);
}
catch (e_7) {
env_7.error = e_7;
env_7.hasError = true;
}
finally {
__disposeResources(env_7);
}
catch (e_4) {
env_4.error = e_4;
env_4.hasError = true;
}
createFileSync(path, flag, mode) {
const env_8 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_8, this.lockSync(path), false);
// @ts-expect-error 2513
return super.createFileSync(path, flag, mode);
}
catch (e_8) {
env_8.error = e_8;
env_8.hasError = true;
}
finally {
__disposeResources(env_8);
}
finally {
__disposeResources(env_4);
}
async unlink(path) {
const env_9 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_9, await this.lock(path, 'unlink'), false);
// @ts-expect-error 2513
await super.unlink(path);
}
catch (e_9) {
env_9.error = e_9;
env_9.hasError = true;
}
finally {
__disposeResources(env_9);
}
}
async openFile(path, flag) {
const env_5 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_5, await this.lock(path, 'openFile'), false);
const file = await this._fs.openFile(path, flag);
file.fs = this;
return file;
}
unlinkSync(path) {
const env_10 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_10, this.lockSync(path), false);
// @ts-expect-error 2513
return super.unlinkSync(path);
}
catch (e_10) {
env_10.error = e_10;
env_10.hasError = true;
}
finally {
__disposeResources(env_10);
}
catch (e_5) {
env_5.error = e_5;
env_5.hasError = true;
}
async rmdir(path) {
const env_11 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_11, await this.lock(path, 'rmdir'), false);
// @ts-expect-error 2513
await super.rmdir(path);
}
catch (e_11) {
env_11.error = e_11;
env_11.hasError = true;
}
finally {
__disposeResources(env_11);
}
finally {
__disposeResources(env_5);
}
rmdirSync(path) {
const env_12 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_12, this.lockSync(path), false);
// @ts-expect-error 2513
return super.rmdirSync(path);
}
catch (e_12) {
env_12.error = e_12;
env_12.hasError = true;
}
finally {
__disposeResources(env_12);
}
}
openFileSync(path, flag) {
const env_6 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_6, this.lockSync(path, 'openFile'), false);
const file = this._fs.openFileSync(path, flag);
file.fs = this;
return file;
}
async mkdir(path, mode) {
const env_13 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_13, await this.lock(path, 'mkdir'), false);
// @ts-expect-error 2513
await super.mkdir(path, mode);
}
catch (e_13) {
env_13.error = e_13;
env_13.hasError = true;
}
finally {
__disposeResources(env_13);
}
catch (e_6) {
env_6.error = e_6;
env_6.hasError = true;
}
mkdirSync(path, mode) {
const env_14 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_14, this.lockSync(path), false);
// @ts-expect-error 2513
return super.mkdirSync(path, mode);
}
catch (e_14) {
env_14.error = e_14;
env_14.hasError = true;
}
finally {
__disposeResources(env_14);
}
finally {
__disposeResources(env_6);
}
async readdir(path) {
const env_15 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_15, await this.lock(path, 'readdir'), false);
// @ts-expect-error 2513
return await super.readdir(path);
}
catch (e_15) {
env_15.error = e_15;
env_15.hasError = true;
}
finally {
__disposeResources(env_15);
}
}
async createFile(path, flag, mode) {
const env_7 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_7, await this.lock(path, 'createFile'), false);
const file = await this._fs.createFile(path, flag, mode);
file.fs = this;
return file;
}
readdirSync(path) {
const env_16 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_16, this.lockSync(path), false);
// @ts-expect-error 2513
return super.readdirSync(path);
}
catch (e_16) {
env_16.error = e_16;
env_16.hasError = true;
}
finally {
__disposeResources(env_16);
}
catch (e_7) {
env_7.error = e_7;
env_7.hasError = true;
}
async exists(path) {
const env_17 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_17, await this.lock(path, 'exists'), false);
return await super.exists(path);
}
catch (e_17) {
env_17.error = e_17;
env_17.hasError = true;
}
finally {
__disposeResources(env_17);
}
finally {
__disposeResources(env_7);
}
existsSync(path) {
const env_18 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_18, this.lockSync(path), false);
return super.existsSync(path);
}
catch (e_18) {
env_18.error = e_18;
env_18.hasError = true;
}
finally {
__disposeResources(env_18);
}
}
createFileSync(path, flag, mode) {
const env_8 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_8, this.lockSync(path, 'createFile'), false);
const file = this._fs.createFileSync(path, flag, mode);
file.fs = this;
return file;
}
async link(srcpath, dstpath) {
const env_19 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_19, await this.lock(srcpath, 'link'), false);
// @ts-expect-error 2513
await super.link(srcpath, dstpath);
}
catch (e_19) {
env_19.error = e_19;
env_19.hasError = true;
}
finally {
__disposeResources(env_19);
}
catch (e_8) {
env_8.error = e_8;
env_8.hasError = true;
}
linkSync(srcpath, dstpath) {
const env_20 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_20, this.lockSync(srcpath), false);
// @ts-expect-error 2513
return super.linkSync(srcpath, dstpath);
}
catch (e_20) {
env_20.error = e_20;
env_20.hasError = true;
}
finally {
__disposeResources(env_20);
}
finally {
__disposeResources(env_8);
}
async sync(path, data, stats) {
const env_21 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_21, await this.lock(path, 'sync'), false);
// @ts-expect-error 2513
await super.sync(path, data, stats);
}
catch (e_21) {
env_21.error = e_21;
env_21.hasError = true;
}
finally {
__disposeResources(env_21);
}
}
async unlink(path) {
const env_9 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_9, await this.lock(path, 'unlink'), false);
await this._fs.unlink(path);
}
syncSync(path, data, stats) {
const env_22 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_22, this.lockSync(path), false);
// @ts-expect-error 2513
return super.syncSync(path, data, stats);
}
catch (e_22) {
env_22.error = e_22;
env_22.hasError = true;
}
finally {
__disposeResources(env_22);
}
catch (e_9) {
env_9.error = e_9;
env_9.hasError = true;
}
finally {
__disposeResources(env_9);
}
}
unlinkSync(path) {
const env_10 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_10, this.lockSync(path, 'unlink'), false);
return this._fs.unlinkSync(path);
}
catch (e_10) {
env_10.error = e_10;
env_10.hasError = true;
}
finally {
__disposeResources(env_10);
}
}
async rmdir(path) {
const env_11 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_11, await this.lock(path, 'rmdir'), false);
await this._fs.rmdir(path);
}
catch (e_11) {
env_11.error = e_11;
env_11.hasError = true;
}
finally {
__disposeResources(env_11);
}
}
rmdirSync(path) {
const env_12 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_12, this.lockSync(path, 'rmdir'), false);
return this._fs.rmdirSync(path);
}
catch (e_12) {
env_12.error = e_12;
env_12.hasError = true;
}
finally {
__disposeResources(env_12);
}
}
async mkdir(path, mode) {
const env_13 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_13, await this.lock(path, 'mkdir'), false);
await this._fs.mkdir(path, mode);
}
catch (e_13) {
env_13.error = e_13;
env_13.hasError = true;
}
finally {
__disposeResources(env_13);
}
}
mkdirSync(path, mode) {
const env_14 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_14, this.lockSync(path, 'mkdir'), false);
return this._fs.mkdirSync(path, mode);
}
catch (e_14) {
env_14.error = e_14;
env_14.hasError = true;
}
finally {
__disposeResources(env_14);
}
}
async readdir(path) {
const env_15 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_15, await this.lock(path, 'readdir'), false);
return await this._fs.readdir(path);
}
catch (e_15) {
env_15.error = e_15;
env_15.hasError = true;
}
finally {
__disposeResources(env_15);
}
}
readdirSync(path) {
const env_16 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_16, this.lockSync(path, 'readdir'), false);
return this._fs.readdirSync(path);
}
catch (e_16) {
env_16.error = e_16;
env_16.hasError = true;
}
finally {
__disposeResources(env_16);
}
}
async exists(path) {
const env_17 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_17, await this.lock(path, 'exists'), false);
return await this._fs.exists(path);
}
catch (e_17) {
env_17.error = e_17;
env_17.hasError = true;
}
finally {
__disposeResources(env_17);
}
}
existsSync(path) {
const env_18 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_18, this.lockSync(path, 'exists'), false);
return this._fs.existsSync(path);
}
catch (e_18) {
env_18.error = e_18;
env_18.hasError = true;
}
finally {
__disposeResources(env_18);
}
}
async link(srcpath, dstpath) {
const env_19 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_19, await this.lock(srcpath, 'link'), false);
await this._fs.link(srcpath, dstpath);
}
catch (e_19) {
env_19.error = e_19;
env_19.hasError = true;
}
finally {
__disposeResources(env_19);
}
}
linkSync(srcpath, dstpath) {
const env_20 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_20, this.lockSync(srcpath, 'link'), false);
return this._fs.linkSync(srcpath, dstpath);
}
catch (e_20) {
env_20.error = e_20;
env_20.hasError = true;
}
finally {
__disposeResources(env_20);
}
}
async sync(path, data, stats) {
const env_21 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_21, await this.lock(path, 'sync'), false);
await this._fs.sync(path, data, stats);
}
catch (e_21) {
env_21.error = e_21;
env_21.hasError = true;
}
finally {
__disposeResources(env_21);
}
}
syncSync(path, data, stats) {
const env_22 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_22, this.lockSync(path, 'sync'), false);
return this._fs.syncSync(path, data, stats);
}
catch (e_22) {
env_22.error = e_22;
env_22.hasError = true;
}
finally {
__disposeResources(env_22);
}
}
}
/**
* This serializes access to an underlying async filesystem.
* For example, on an OverlayFS instance with an async lower
* directory operations like rename and rmdir may involve multiple
* requests involving both the upper and lower filesystems -- they
* are not executed in a single atomic step. OverlayFS uses this
* to avoid having to reason about the correctness of
* multiple requests interleaving.
*
* Note:
* Instead of extending the passed class, `MutexedFS` stores it internally.
* This is to avoid a deadlock caused when a mathod calls another one
* The problem is discussed extensivly in [#78](https://github.com/zen-fs/core/issues/78)
* Instead of extending `FileSystem`,
* `MutexedFS` implements it in order to make sure all of the methods are passed through
*
* @todo Change `using _` to `using void` pending https://github.com/tc39/proposal-discard-binding
* @internal
*/
export function Mutexed(FS) {
class MutexedFS extends __MutexedFS {
constructor(...args) {
super();
this._fs = new FS(...args);
}
}
return MutexedFS;
}

@@ -13,1 +13,2 @@ import type { ExtractProperties } from 'utilium';

export type _AsyncFSMethods = ExtractProperties<FileSystem, (...args: any[]) => Promise<unknown>>;
export type ConcreteFS = ExtractProperties<FileSystem, any>;

@@ -0,1 +1,2 @@

/* eslint-disable @typescript-eslint/no-explicit-any */
/*

@@ -2,0 +3,0 @@ Code shared by various mixins

/// <reference types="node" resolution-mode="require"/>
import type * as Node from 'fs';
import type { Credentials } from './credentials.js';
import { type Credentials } from './credentials.js';
import { S_IFDIR, S_IFLNK, S_IFREG } from './emulation/constants.js';

@@ -149,7 +149,6 @@ /**

* @param mode The requested access, combination of W_OK, R_OK, and X_OK
* @param cred The requesting credentials
* @returns True if the request has access, false if the request does not
* @internal
*/
hasAccess(mode: number, cred: Credentials): boolean;
hasAccess(mode: number): boolean;
/**

@@ -156,0 +155,0 @@ * Convert the current stats object into a credentials object

@@ -0,1 +1,2 @@

import { credentials } from './credentials.js';
import { S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRWXG, S_IRWXO, S_IRWXU } from './emulation/constants.js';

@@ -118,8 +119,7 @@ import { size_max } from './inode.js';

* @param mode The requested access, combination of W_OK, R_OK, and X_OK
* @param cred The requesting credentials
* @returns True if the request has access, false if the request does not
* @internal
*/
hasAccess(mode, cred) {
if (cred.euid === 0 || cred.egid === 0) {
hasAccess(mode) {
if (credentials.euid === 0 || credentials.egid === 0) {
//Running as root

@@ -129,3 +129,3 @@ return true;

// Mask for
const adjusted = (cred.uid == this.uid ? S_IRWXU : 0) | (cred.gid == this.gid ? S_IRWXG : 0) | S_IRWXO;
const adjusted = (credentials.uid == this.uid ? S_IRWXU : 0) | (credentials.gid == this.gid ? S_IRWXG : 0) | S_IRWXO;
return (mode & this.mode & adjusted) == mode;

@@ -132,0 +132,0 @@ }

/// <reference types="node" resolution-mode="require"/>
/// <reference types="node" resolution-mode="require"/>
import type * as fs from 'node:fs';
import type { OptionalTuple } from 'utilium';
import type { ClassLike, OptionalTuple } from 'utilium';
import { type AbsolutePath } from './emulation/path.js';

@@ -84,1 +84,2 @@ import { ErrnoError } from './error.js';

};
export type Concrete<T extends ClassLike> = Pick<T, keyof T> & (new (...args: any[]) => InstanceType<T>);
{
"name": "@zenfs/core",
"version": "0.18.0",
"version": "0.18.1",
"description": "A filesystem, anywhere",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

@@ -20,3 +20,3 @@ # ZenFS

As an added bonus, all ZenFS backends support syncrohnous operations. All of the backends included with the core are cross-platform.
As an added bonus, all ZenFS backends support synchronous operations. All of the backends included with the core are cross-platform.

@@ -23,0 +23,0 @@ For more information, see the [docs](https://zen-fs.github.io/core).

@@ -435,4 +435,4 @@ import { dirname } from '../emulation/path.js';

private createParentDirectoriesSync(path: string): void {
let parent = dirname(path),
toCreate: string[] = [];
let parent = dirname(path);
const toCreate: string[] = [];
while (!this.writable.existsSync(parent)) {

@@ -442,6 +442,5 @@ toCreate.push(parent);

}
toCreate = toCreate.reverse();
for (const p of toCreate) {
this.writable.mkdirSync(p, this.statSync(p).mode);
for (const path of toCreate.reverse()) {
this.writable.mkdirSync(path, this.statSync(path).mode);
}

@@ -451,4 +450,4 @@ }

private async createParentDirectories(path: string): Promise<void> {
let parent = dirname(path),
toCreate: string[] = [];
let parent = dirname(path);
const toCreate: string[] = [];
while (!(await this.writable.exists(parent))) {

@@ -458,7 +457,6 @@ toCreate.push(parent);

}
toCreate = toCreate.reverse();
for (const p of toCreate) {
const stats = await this.stat(p);
await this.writable.mkdir(p, stats.mode);
for (const path of toCreate.reverse()) {
const stats = await this.stat(path);
await this.writable.mkdir(path, stats.mode);
}

@@ -465,0 +463,0 @@ }

@@ -25,8 +25,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */

constructor(
public readonly fs: PortFS,
public fs: PortFS,
public readonly fd: number,
public readonly path: string,
path: string,
public position: number
) {
super();
super(fs, path);
}

@@ -47,3 +47,3 @@

protected _throwNoSync(syscall: string): never {
throw new ErrnoError(Errno.ENOTSUP, 'Syncrohnous operations not support on PortFile', this.path, syscall);
throw new ErrnoError(Errno.ENOTSUP, 'Synchronous operations not supported on PortFile', this.path, syscall);
}

@@ -50,0 +50,0 @@

@@ -21,3 +21,2 @@ /* eslint-disable @typescript-eslint/no-redundant-type-constituents */

import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
import { credentials } from '../credentials.js';
import { ReadStream, WriteStream } from './streams.js';

@@ -224,3 +223,3 @@ import { FSWatcher, emitChange } from './watchers.js';

const stats = await this.file.stat();
if (!stats.hasAccess(constants.R_OK, credentials)) {
if (!stats.hasAccess(constants.R_OK)) {
throw ErrnoError.With('EACCES', this.file.path, 'stat');

@@ -401,3 +400,3 @@ }

const dst = resolveMount(newPath);
if (!(await stat(dirname(oldPath))).hasAccess(constants.W_OK, credentials)) {
if (!(await stat(dirname(oldPath))).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', oldPath, 'rename');

@@ -450,3 +449,3 @@ }

const stats = await fs.stat(resolved);
if (!stats.hasAccess(constants.R_OK, credentials)) {
if (!stats.hasAccess(constants.R_OK)) {
throw ErrnoError.With('EACCES', path, 'stat');

@@ -503,3 +502,3 @@ }

try {
if (!(await fs.stat(resolved)).hasAccess(constants.W_OK, credentials)) {
if (!(await fs.stat(resolved)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', resolved, 'unlink');

@@ -535,3 +534,3 @@ }

const parentStats: Stats = await fs.stat(dirname(resolved));
if (!parentStats.hasAccess(constants.W_OK, credentials)) {
if (!parentStats.hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(path), '_open');

@@ -545,3 +544,3 @@ }

if (!stats.hasAccess(flagToMode(flag), credentials)) {
if (!stats.hasAccess(flagToMode(flag))) {
throw ErrnoError.With('EACCES', path, '_open');

@@ -673,3 +672,3 @@ }

try {
if (!(await fs.stat(resolved)).hasAccess(constants.W_OK, credentials)) {
if (!(await fs.stat(resolved)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', resolved, 'rmdir');

@@ -705,3 +704,3 @@ }

if (!options?.recursive) {
if (!(await fs.stat(dirname(resolved))).hasAccess(constants.W_OK, credentials)) {
if (!(await fs.stat(dirname(resolved))).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(resolved), 'mkdir');

@@ -720,3 +719,3 @@ }

for (const dir of dirs) {
if (!(await fs.stat(dirname(dir))).hasAccess(constants.W_OK, credentials)) {
if (!(await fs.stat(dirname(dir))).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(dir), 'mkdir');

@@ -751,3 +750,3 @@ }

path = normalizePath(path);
if (!(await stat(path)).hasAccess(constants.R_OK, credentials)) {
if (!(await stat(path)).hasAccess(constants.R_OK)) {
throw ErrnoError.With('EACCES', path, 'readdir');

@@ -790,7 +789,7 @@ }

targetPath = normalizePath(targetPath);
if (!(await stat(dirname(targetPath))).hasAccess(constants.R_OK, credentials)) {
if (!(await stat(dirname(targetPath))).hasAccess(constants.R_OK)) {
throw ErrnoError.With('EACCES', dirname(targetPath), 'link');
}
linkPath = normalizePath(linkPath);
if (!(await stat(dirname(linkPath))).hasAccess(constants.W_OK, credentials)) {
if (!(await stat(dirname(linkPath))).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(linkPath), 'link');

@@ -805,3 +804,3 @@ }

try {
if (!(await fs.stat(path)).hasAccess(constants.W_OK, credentials)) {
if (!(await fs.stat(path)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', path, 'link');

@@ -985,3 +984,3 @@ }

const stats = await stat(path);
if (!stats.hasAccess(mode, credentials)) {
if (!stats.hasAccess(mode)) {
throw new ErrnoError(Errno.EACCES);

@@ -988,0 +987,0 @@ }

@@ -13,3 +13,2 @@ import { Buffer } from 'buffer';

import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
import { credentials } from '../credentials.js';
import { emitChange } from './watchers.js';

@@ -27,3 +26,3 @@

const newMount = resolveMount(newPath);
if (!statSync(dirname(oldPath)).hasAccess(constants.W_OK, credentials)) {
if (!statSync(dirname(oldPath)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', oldPath, 'rename');

@@ -78,3 +77,3 @@ }

const stats = fs.statSync(resolved);
if (!stats.hasAccess(constants.R_OK, credentials)) {
if (!stats.hasAccess(constants.R_OK)) {
throw ErrnoError.With('EACCES', path, 'stat');

@@ -132,3 +131,3 @@ }

try {
if (!fs.statSync(resolved).hasAccess(constants.W_OK, credentials)) {
if (!fs.statSync(resolved).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', resolved, 'unlink');

@@ -158,3 +157,3 @@ }

const parentStats: Stats = fs.statSync(dirname(resolved));
if (!parentStats.hasAccess(constants.W_OK, credentials)) {
if (!parentStats.hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(path), '_open');

@@ -170,3 +169,3 @@ }

if (!stats.hasAccess(mode, credentials) || !stats.hasAccess(flagToMode(flag), credentials)) {
if (!stats.hasAccess(mode) || !stats.hasAccess(flagToMode(flag))) {
throw ErrnoError.With('EACCES', path, '_open');

@@ -475,3 +474,3 @@ }

try {
if (!fs.statSync(resolved).hasAccess(constants.W_OK, credentials)) {
if (!fs.statSync(resolved).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', resolved, 'rmdir');

@@ -506,3 +505,3 @@ }

if (!options?.recursive) {
if (!fs.statSync(dirname(resolved)).hasAccess(constants.W_OK, credentials)) {
if (!fs.statSync(dirname(resolved)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(resolved), 'mkdir');

@@ -519,3 +518,3 @@ }

for (const dir of dirs) {
if (!fs.statSync(dirname(dir)).hasAccess(constants.W_OK, credentials)) {
if (!fs.statSync(dirname(dir)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(dir), 'mkdir');

@@ -548,3 +547,3 @@ }

let entries: string[];
if (!statSync(path).hasAccess(constants.R_OK, credentials)) {
if (!statSync(path).hasAccess(constants.R_OK)) {
throw ErrnoError.With('EACCES', path, 'readdir');

@@ -591,7 +590,7 @@ }

targetPath = normalizePath(targetPath);
if (!statSync(dirname(targetPath)).hasAccess(constants.R_OK, credentials)) {
if (!statSync(dirname(targetPath)).hasAccess(constants.R_OK)) {
throw ErrnoError.With('EACCES', dirname(targetPath), 'link');
}
linkPath = normalizePath(linkPath);
if (!statSync(dirname(linkPath)).hasAccess(constants.W_OK, credentials)) {
if (!statSync(dirname(linkPath)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', dirname(linkPath), 'link');

@@ -606,3 +605,3 @@ }

try {
if (!fs.statSync(path).hasAccess(constants.W_OK, credentials)) {
if (!fs.statSync(path).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', path, 'link');

@@ -768,3 +767,3 @@ }

const stats = statSync(path);
if (!stats.hasAccess(mode, credentials)) {
if (!stats.hasAccess(mode)) {
throw new ErrnoError(Errno.EACCES);

@@ -771,0 +770,0 @@ }

@@ -149,2 +149,14 @@ import type { FileReadResult } from 'node:fs/promises';

export abstract class File {
constructor(
/**
* @internal
* The file system that created the file
*/
public fs: FileSystem,
/**
* The path to the file
*/
public readonly path: string
) {}
/**

@@ -156,7 +168,2 @@ * Get the current file position.

/**
* The path to the file
*/
public abstract readonly path: string;
/**
* Asynchronous `stat`.

@@ -360,8 +367,6 @@ */

* The file system that created the file.
* @internal
*/
protected fs: FS,
/**
* Path to the file
*/
public readonly path: string,
public fs: FS,
path: string,
public readonly flag: string,

@@ -371,3 +376,3 @@ public readonly stats: Stats,

) {
super();
super(fs, path);

@@ -374,0 +379,0 @@ /*

import { ErrnoError } from '../error.js';
import type { File } from '../file.js';
import type { FileSystem } from '../filesystem.js';
import type { FileSystem, FileSystemMetadata } from '../filesystem.js';
import '../polyfills.js';
import type { Stats } from '../stats.js';
import type { Mixin } from './shared.js';
import type { Concrete } from '../utils.js';

@@ -16,6 +16,3 @@ export class MutexLock {

public constructor(
public readonly path: string,
protected readonly previous?: MutexLock
) {}
public constructor(protected readonly previous?: MutexLock) {}

@@ -38,217 +35,225 @@ public async done(): Promise<void> {

/**
* This serializes access to an underlying async filesystem.
* For example, on an OverlayFS instance with an async lower
* directory operations like rename and rmdir may involve multiple
* requests involving both the upper and lower filesystems -- they
* are not executed in a single atomic step. OverlayFS uses this
* to avoid having to reason about the correctness of
* multiple requests interleaving.
*
* Note: `@ts-expect-error 2513` is needed because `FS` is not properly detected as being concrete
*
* @todo Change `using _` to `using void` pending https://github.com/tc39/proposal-discard-binding
* @internal
* @hidden
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function Mutexed<T extends new (...args: any[]) => FileSystem>(
FS: T
): Mixin<
T,
{
lock(path: string, syscall: string): Promise<MutexLock>;
lockSync(path: string): MutexLock;
isLocked(path: string): boolean;
export class __MutexedFS<T extends FileSystem> implements FileSystem {
/**
* @internal
*/
public _fs!: T;
public async ready(): Promise<void> {
return await this._fs.ready();
}
> {
class MutexedFS extends FS {
/**
* The current locks
*/
private locks: Map<string, MutexLock> = new Map();
/**
* Adds a lock for a path
*/
protected addLock(path: string): MutexLock {
const previous = this.locks.get(path);
const lock = new MutexLock(path, previous?.isLocked ? previous : undefined);
this.locks.set(path, lock);
return lock;
}
public metadata(): FileSystemMetadata {
return this._fs.metadata();
}
/**
* Locks `path` asynchronously.
* If the path is currently locked, waits for it to be unlocked.
* @internal
*/
public async lock(path: string, syscall: string): Promise<MutexLock> {
const previous = this.locks.get(path);
const lock = this.addLock(path);
const stack = new Error().stack;
setTimeout(() => {
if (lock.isLocked) {
const error = ErrnoError.With('EDEADLK', path, syscall);
error.stack += stack?.slice('Error'.length);
throw error;
}
}, 5000);
await previous?.done();
return lock;
}
/**
* The current locks
*/
private currentLock?: MutexLock;
/**
* Locks `path` asynchronously.
* If the path is currently locked, an error will be thrown
* @internal
*/
public lockSync(path: string): MutexLock {
if (this.locks.has(path)) {
// Non-null assertion: we already checked locks has path
throw ErrnoError.With('EBUSY', path, 'lock');
/**
* Adds a lock for a path
*/
protected addLock(): MutexLock {
const lock = new MutexLock(this.currentLock);
this.currentLock = lock;
return lock;
}
/**
* Locks `path` asynchronously.
* If the path is currently locked, waits for it to be unlocked.
* @internal
*/
public async lock(path: string, syscall: string): Promise<MutexLock> {
const previous = this.currentLock;
const lock = this.addLock();
const stack = new Error().stack;
setTimeout(() => {
if (lock.isLocked) {
const error = ErrnoError.With('EDEADLK', path, syscall);
error.stack += stack?.slice('Error'.length);
throw error;
}
}, 5000);
await previous?.done();
return lock;
}
return this.addLock(path);
/**
* Locks `path` asynchronously.
* If the path is currently locked, an error will be thrown
* @internal
*/
public lockSync(path: string, syscall: string): MutexLock {
if (this.currentLock) {
throw ErrnoError.With('EBUSY', path, syscall);
}
/**
* Whether `path` is locked
* @internal
*/
public isLocked(path: string): boolean {
return !!this.locks.get(path)?.isLocked;
}
return this.addLock();
}
/* eslint-disable @typescript-eslint/no-unused-vars */
public async rename(oldPath: string, newPath: string): Promise<void> {
using _ = await this.lock(oldPath, 'rename');
// @ts-expect-error 2513
await super.rename(oldPath, newPath);
}
/**
* Whether `path` is locked
* @internal
*/
public get isLocked(): boolean {
return !!this.currentLock?.isLocked;
}
public renameSync(oldPath: string, newPath: string): void {
using _ = this.lockSync(oldPath);
// @ts-expect-error 2513
return super.renameSync(oldPath, newPath);
}
/* eslint-disable @typescript-eslint/no-unused-vars */
public async rename(oldPath: string, newPath: string): Promise<void> {
using _ = await this.lock(oldPath, 'rename');
await this._fs.rename(oldPath, newPath);
}
public async stat(path: string): Promise<Stats> {
using _ = await this.lock(path, 'stat');
// @ts-expect-error 2513
return await super.stat(path);
}
public renameSync(oldPath: string, newPath: string): void {
using _ = this.lockSync(oldPath, 'rename');
return this._fs.renameSync(oldPath, newPath);
}
public statSync(path: string): Stats {
using _ = this.lockSync(path);
// @ts-expect-error 2513
return super.statSync(path);
}
public async stat(path: string): Promise<Stats> {
using _ = await this.lock(path, 'stat');
return await this._fs.stat(path);
}
public async openFile(path: string, flag: string): Promise<File> {
using _ = await this.lock(path, 'openFile');
// @ts-expect-error 2513
return await super.openFile(path, flag);
}
public statSync(path: string): Stats {
using _ = this.lockSync(path, 'stat');
return this._fs.statSync(path);
}
public openFileSync(path: string, flag: string): File {
using _ = this.lockSync(path);
// @ts-expect-error 2513
return super.openFileSync(path, flag);
}
public async openFile(path: string, flag: string): Promise<File> {
using _ = await this.lock(path, 'openFile');
const file = await this._fs.openFile(path, flag);
file.fs = this;
return file;
}
public async createFile(path: string, flag: string, mode: number): Promise<File> {
using _ = await this.lock(path, 'createFile');
// @ts-expect-error 2513
return await super.createFile(path, flag, mode);
}
public openFileSync(path: string, flag: string): File {
using _ = this.lockSync(path, 'openFile');
const file = this._fs.openFileSync(path, flag);
file.fs = this;
return file;
}
public createFileSync(path: string, flag: string, mode: number): File {
using _ = this.lockSync(path);
// @ts-expect-error 2513
return super.createFileSync(path, flag, mode);
}
public async createFile(path: string, flag: string, mode: number): Promise<File> {
using _ = await this.lock(path, 'createFile');
const file = await this._fs.createFile(path, flag, mode);
file.fs = this;
return file;
}
public async unlink(path: string): Promise<void> {
using _ = await this.lock(path, 'unlink');
// @ts-expect-error 2513
await super.unlink(path);
}
public createFileSync(path: string, flag: string, mode: number): File {
using _ = this.lockSync(path, 'createFile');
const file = this._fs.createFileSync(path, flag, mode);
file.fs = this;
return file;
}
public unlinkSync(path: string): void {
using _ = this.lockSync(path);
// @ts-expect-error 2513
return super.unlinkSync(path);
}
public async unlink(path: string): Promise<void> {
using _ = await this.lock(path, 'unlink');
await this._fs.unlink(path);
}
public async rmdir(path: string): Promise<void> {
using _ = await this.lock(path, 'rmdir');
// @ts-expect-error 2513
await super.rmdir(path);
}
public unlinkSync(path: string): void {
using _ = this.lockSync(path, 'unlink');
return this._fs.unlinkSync(path);
}
public rmdirSync(path: string): void {
using _ = this.lockSync(path);
// @ts-expect-error 2513
return super.rmdirSync(path);
}
public async rmdir(path: string): Promise<void> {
using _ = await this.lock(path, 'rmdir');
await this._fs.rmdir(path);
}
public async mkdir(path: string, mode: number): Promise<void> {
using _ = await this.lock(path, 'mkdir');
// @ts-expect-error 2513
await super.mkdir(path, mode);
}
public rmdirSync(path: string): void {
using _ = this.lockSync(path, 'rmdir');
return this._fs.rmdirSync(path);
}
public mkdirSync(path: string, mode: number): void {
using _ = this.lockSync(path);
// @ts-expect-error 2513
return super.mkdirSync(path, mode);
}
public async mkdir(path: string, mode: number): Promise<void> {
using _ = await this.lock(path, 'mkdir');
await this._fs.mkdir(path, mode);
}
public async readdir(path: string): Promise<string[]> {
using _ = await this.lock(path, 'readdir');
// @ts-expect-error 2513
return await super.readdir(path);
}
public mkdirSync(path: string, mode: number): void {
using _ = this.lockSync(path, 'mkdir');
return this._fs.mkdirSync(path, mode);
}
public readdirSync(path: string): string[] {
using _ = this.lockSync(path);
// @ts-expect-error 2513
return super.readdirSync(path);
}
public async readdir(path: string): Promise<string[]> {
using _ = await this.lock(path, 'readdir');
return await this._fs.readdir(path);
}
public async exists(path: string): Promise<boolean> {
using _ = await this.lock(path, 'exists');
return await super.exists(path);
}
public readdirSync(path: string): string[] {
using _ = this.lockSync(path, 'readdir');
return this._fs.readdirSync(path);
}
public existsSync(path: string): boolean {
using _ = this.lockSync(path);
return super.existsSync(path);
}
public async exists(path: string): Promise<boolean> {
using _ = await this.lock(path, 'exists');
return await this._fs.exists(path);
}
public async link(srcpath: string, dstpath: string): Promise<void> {
using _ = await this.lock(srcpath, 'link');
// @ts-expect-error 2513
await super.link(srcpath, dstpath);
}
public existsSync(path: string): boolean {
using _ = this.lockSync(path, 'exists');
return this._fs.existsSync(path);
}
public linkSync(srcpath: string, dstpath: string): void {
using _ = this.lockSync(srcpath);
// @ts-expect-error 2513
return super.linkSync(srcpath, dstpath);
}
public async link(srcpath: string, dstpath: string): Promise<void> {
using _ = await this.lock(srcpath, 'link');
await this._fs.link(srcpath, dstpath);
}
public async sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> {
using _ = await this.lock(path, 'sync');
// @ts-expect-error 2513
await super.sync(path, data, stats);
}
public linkSync(srcpath: string, dstpath: string): void {
using _ = this.lockSync(srcpath, 'link');
return this._fs.linkSync(srcpath, dstpath);
}
public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void {
using _ = this.lockSync(path);
// @ts-expect-error 2513
return super.syncSync(path, data, stats);
public async sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> {
using _ = await this.lock(path, 'sync');
await this._fs.sync(path, data, stats);
}
public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void {
using _ = this.lockSync(path, 'sync');
return this._fs.syncSync(path, data, stats);
}
/* eslint-enable @typescript-eslint/no-unused-vars */
}
/**
* This serializes access to an underlying async filesystem.
* For example, on an OverlayFS instance with an async lower
* directory operations like rename and rmdir may involve multiple
* requests involving both the upper and lower filesystems -- they
* are not executed in a single atomic step. OverlayFS uses this
* to avoid having to reason about the correctness of
* multiple requests interleaving.
*
* Note:
* Instead of extending the passed class, `MutexedFS` stores it internally.
* This is to avoid a deadlock caused when a mathod calls another one
* The problem is discussed extensivly in [#78](https://github.com/zen-fs/core/issues/78)
* Instead of extending `FileSystem`,
* `MutexedFS` implements it in order to make sure all of the methods are passed through
*
* @todo Change `using _` to `using void` pending https://github.com/tc39/proposal-discard-binding
* @internal
*/
export function Mutexed<const T extends Concrete<typeof FileSystem>>(
FS: T
): typeof __MutexedFS<InstanceType<T>> & {
new (...args: ConstructorParameters<T>): __MutexedFS<InstanceType<T>>;
} {
class MutexedFS extends __MutexedFS<InstanceType<T>> {
public constructor(...args: ConstructorParameters<T>) {
super();
this._fs = new FS(...args) as InstanceType<T>;
}
/* eslint-enable @typescript-eslint/no-unused-vars */
}
return MutexedFS;
}

@@ -0,1 +1,2 @@

/* eslint-disable @typescript-eslint/no-explicit-any */
/*

@@ -12,3 +13,2 @@ Code shared by various mixins

*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type Mixin<TBase extends typeof FileSystem, TMixin> = (abstract new (...args: any[]) => TMixin) & TBase;

@@ -20,3 +20,4 @@

*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type _AsyncFSMethods = ExtractProperties<FileSystem, (...args: any[]) => Promise<unknown>>;
export type ConcreteFS = ExtractProperties<FileSystem, any>;
import type * as Node from 'fs';
import type { Credentials } from './credentials.js';
import { credentials, type Credentials } from './credentials.js';
import { S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRWXG, S_IRWXO, S_IRWXU } from './emulation/constants.js';

@@ -236,8 +236,7 @@ import { size_max } from './inode.js';

* @param mode The requested access, combination of W_OK, R_OK, and X_OK
* @param cred The requesting credentials
* @returns True if the request has access, false if the request does not
* @internal
*/
public hasAccess(mode: number, cred: Credentials): boolean {
if (cred.euid === 0 || cred.egid === 0) {
public hasAccess(mode: number): boolean {
if (credentials.euid === 0 || credentials.egid === 0) {
//Running as root

@@ -248,3 +247,3 @@ return true;

// Mask for
const adjusted = (cred.uid == this.uid ? S_IRWXU : 0) | (cred.gid == this.gid ? S_IRWXG : 0) | S_IRWXO;
const adjusted = (credentials.uid == this.uid ? S_IRWXU : 0) | (credentials.gid == this.gid ? S_IRWXG : 0) | S_IRWXO;
return (mode & this.mode & adjusted) == mode;

@@ -251,0 +250,0 @@ }

/* eslint-disable @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-return */
import type * as fs from 'node:fs';
import type { OptionalTuple } from 'utilium';
import type { ClassLike, OptionalTuple } from 'utilium';
import { dirname, resolve, type AbsolutePath } from './emulation/path.js';

@@ -265,1 +265,3 @@ import { Errno, ErrnoError } from './error.js';

}
export type Concrete<T extends ClassLike> = Pick<T, keyof T> & (new (...args: any[]) => InstanceType<T>);

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc