@zenfs/core 2.7.5 → 2.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backends/backend.js +0 -2
- package/dist/backends/cow.d.ts +2 -2
- package/dist/backends/cow.js +6 -14
- package/dist/backends/passthrough.d.ts +0 -54
- package/dist/backends/passthrough.js +0 -54
- package/dist/backends/port.d.ts +2 -6
- package/dist/backends/port.js +3 -11
- package/dist/backends/store/fs.d.ts +0 -9
- package/dist/backends/store/fs.js +2 -24
- package/dist/backends/store/store.d.ts +1 -1
- package/dist/backends/store/store.js +2 -8
- package/dist/config.d.ts +7 -0
- package/dist/config.js +9 -3
- package/dist/internal/contexts.d.ts +1 -2
- package/dist/internal/credentials.d.ts +0 -3
- package/dist/internal/error.d.ts +5 -0
- package/dist/internal/error.js +10 -0
- package/dist/internal/file_index.js +0 -1
- package/dist/internal/index_fs.d.ts +2 -2
- package/dist/internal/index_fs.js +2 -2
- package/dist/internal/inode.d.ts +46 -43
- package/dist/internal/inode.js +75 -10
- package/dist/internal/rpc.d.ts +16 -7
- package/dist/internal/rpc.js +2 -11
- package/dist/mixins/async.js +22 -24
- package/dist/mixins/mutexed.d.ts +6 -41
- package/dist/mixins/mutexed.js +36 -109
- package/dist/mixins/sync.js +0 -2
- package/dist/node/async.js +2 -12
- package/dist/node/index.d.ts +1 -1
- package/dist/node/promises.d.ts +1 -6
- package/dist/node/promises.js +18 -22
- package/dist/node/stats.d.ts +25 -99
- package/dist/node/stats.js +22 -56
- package/dist/node/streams.d.ts +1 -37
- package/dist/node/sync.d.ts +1 -6
- package/dist/node/sync.js +16 -20
- package/dist/polyfills.js +25 -0
- package/dist/utils.d.ts +0 -4
- package/dist/vfs/acl.d.ts +1 -3
- package/dist/vfs/acl.js +1 -3
- package/dist/vfs/async.js +77 -69
- package/dist/vfs/config.d.ts +10 -1
- package/dist/vfs/config.js +5 -2
- package/dist/vfs/dir.d.ts +3 -0
- package/dist/vfs/dir.js +4 -1
- package/dist/vfs/file.d.ts +1 -8
- package/dist/vfs/file.js +11 -7
- package/dist/vfs/ioctl.js +2 -2
- package/dist/vfs/shared.d.ts +2 -0
- package/dist/vfs/shared.js +3 -4
- package/dist/vfs/sync.js +77 -61
- package/dist/vfs/vcache.d.ts +14 -14
- package/dist/vfs/vcache.js +65 -58
- package/dist/vfs/watchers.d.ts +1 -0
- package/dist/vfs/watchers.js +2 -4
- package/dist/vfs/xattr.js +18 -17
- package/package.json +2 -4
- package/scripts/make-index.js +1 -1
- package/tests/common/async.test.ts +35 -0
- package/tests/common/config.test.ts +1 -1
- package/tests/common/context.test.ts +1 -1
- package/tests/common/file_index.test.ts +1 -1
- package/tests/common/mounts.test.ts +12 -0
- package/tests/common/vcache.test.ts +13 -0
- package/tests/fs/concurrency.test.ts +3 -3
- package/tests/fs/links.test.ts +76 -0
- package/tests/fs/permissions.test.ts +232 -0
- package/tests/fs/stat.test.ts +1 -1
- package/tests/fs/streams.test.ts +1 -1
- package/tests/fs/xattr.test.ts +10 -0
- package/tests/common/mutex.test.ts +0 -61
package/dist/internal/inode.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ExceptionExtra } from 'kerium';
|
|
1
2
|
import { BufferView } from 'utilium/buffer';
|
|
2
3
|
import { Stats } from '../node/stats.js';
|
|
3
4
|
import { type V_Context } from './contexts.js';
|
|
@@ -32,61 +33,39 @@ export interface InodeFields {
|
|
|
32
33
|
data?: number;
|
|
33
34
|
flags?: number;
|
|
34
35
|
version?: number;
|
|
36
|
+
/** Extended attributes */
|
|
37
|
+
attributes?: Attributes;
|
|
35
38
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
* @internal
|
|
39
|
-
*/
|
|
40
|
-
export interface InodeLike<T extends number | bigint = number> extends InodeFields {
|
|
41
|
-
/**
|
|
42
|
-
* Size of the item in bytes.
|
|
43
|
-
* For directories/symlinks, this is normally the size of the struct that represents the item.
|
|
44
|
-
*/
|
|
39
|
+
export interface StatsLike<T extends number | bigint = number | bigint> {
|
|
40
|
+
/** Size of the item in bytes. For directories/symlinks, this may be the size of the struct that represents the item. */
|
|
45
41
|
size: T;
|
|
46
|
-
/**
|
|
47
|
-
* Unix-style file mode (e.g. 0o644) that includes the item type
|
|
48
|
-
*/
|
|
42
|
+
/** Unix-style file mode (e.g. 0o644) that includes the item type */
|
|
49
43
|
mode: T;
|
|
50
|
-
/**
|
|
51
|
-
* Time of last access, since epoch
|
|
52
|
-
*/
|
|
44
|
+
/** Time of last access, since epoch */
|
|
53
45
|
atimeMs: T;
|
|
54
|
-
/**
|
|
55
|
-
* Time of last modification, since epoch
|
|
56
|
-
*/
|
|
46
|
+
/** Time of last modification, since epoch */
|
|
57
47
|
mtimeMs: T;
|
|
58
|
-
/**
|
|
59
|
-
* Time of last time file status was changed, since epoch
|
|
60
|
-
*/
|
|
48
|
+
/** Time of last time file status was changed, since epoch */
|
|
61
49
|
ctimeMs: T;
|
|
62
|
-
/**
|
|
63
|
-
* Time of file creation, since epoch
|
|
64
|
-
*/
|
|
50
|
+
/** Time of file creation, since epoch */
|
|
65
51
|
birthtimeMs: T;
|
|
66
|
-
/**
|
|
67
|
-
* The id of the user that owns the file
|
|
68
|
-
*/
|
|
52
|
+
/** The id of the user that owns the file */
|
|
69
53
|
uid: T;
|
|
70
|
-
/**
|
|
71
|
-
* The id of the group that owns the file
|
|
72
|
-
*/
|
|
54
|
+
/** The id of the group that owns the file */
|
|
73
55
|
gid: T;
|
|
74
|
-
/**
|
|
75
|
-
* Inode number
|
|
76
|
-
*/
|
|
56
|
+
/** Inode number */
|
|
77
57
|
ino: T;
|
|
78
|
-
/**
|
|
79
|
-
* Number of hard links
|
|
80
|
-
*/
|
|
58
|
+
/** Number of hard links */
|
|
81
59
|
nlink: T;
|
|
82
|
-
/**
|
|
83
|
-
* Device ID for special files
|
|
84
|
-
*/
|
|
60
|
+
/** Device ID for special files */
|
|
85
61
|
rdev: T;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* @category Internals
|
|
65
|
+
* @internal
|
|
66
|
+
* @todo [breaking] switch fully to using bigints
|
|
67
|
+
*/
|
|
68
|
+
export interface InodeLike<T extends number | bigint = number> extends InodeFields, StatsLike<T> {
|
|
90
69
|
}
|
|
91
70
|
/**
|
|
92
71
|
* @internal @hidden
|
|
@@ -256,10 +235,34 @@ export declare function isFIFO(metadata: {
|
|
|
256
235
|
* @internal
|
|
257
236
|
*/
|
|
258
237
|
export declare function hasAccess($: V_Context, inode: Pick<InodeLike, 'mode' | 'uid' | 'gid'>, access: number): boolean;
|
|
238
|
+
/**
|
|
239
|
+
* Throws if a given user may not search the directory `dir`
|
|
240
|
+
* @internal
|
|
241
|
+
*/
|
|
242
|
+
export declare function assertSearchable($: V_Context, dir: Pick<InodeLike, 'mode' | 'uid' | 'gid'>, extra?: ExceptionExtra): void;
|
|
243
|
+
/**
|
|
244
|
+
* Throws if a given user may not remove `entry` from the directory `parent`
|
|
245
|
+
* @internal
|
|
246
|
+
*/
|
|
247
|
+
export declare function assertRemovable($: V_Context, parent: Pick<InodeLike, 'mode' | 'uid' | 'gid'>, entry: Pick<InodeLike, 'uid'>, extra?: ExceptionExtra): void;
|
|
248
|
+
/**
|
|
249
|
+
* Throws if a given user is neither root nor the owner of `inode`
|
|
250
|
+
* @internal
|
|
251
|
+
*/
|
|
252
|
+
export declare function assertOwner($: V_Context, inode: Pick<InodeLike, 'uid'>, extra?: ExceptionExtra): void;
|
|
253
|
+
/**
|
|
254
|
+
* Throws if a given user may not change the owner of `inode` to `uid` and `gid`, where `-1` leaves an ID unchanged
|
|
255
|
+
* @internal
|
|
256
|
+
*/
|
|
257
|
+
export declare function assertCanChown($: V_Context, inode: Pick<InodeLike, 'uid' | 'gid'>, uid: number, gid: number, extra?: ExceptionExtra): void;
|
|
259
258
|
/**
|
|
260
259
|
* @hidden @internal
|
|
261
260
|
*
|
|
262
261
|
* @todo https://github.com/nodejs/node/pull/58836 support -1 for `*chown`
|
|
263
262
|
*/
|
|
264
263
|
export declare function _chown(stats: Partial<InodeLike>, uid: number, gid: number): boolean;
|
|
264
|
+
/**
|
|
265
|
+
* @hidden @internal
|
|
266
|
+
*/
|
|
267
|
+
export declare function _chmod($: V_Context, inode: Pick<InodeLike, 'mode' | 'gid'>, mode: number): void;
|
|
265
268
|
export {};
|
package/dist/internal/inode.js
CHANGED
|
@@ -33,7 +33,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
|
|
|
33
33
|
return useValue ? value : void 0;
|
|
34
34
|
};
|
|
35
35
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
36
|
-
import { withErrno } from 'kerium';
|
|
36
|
+
import { UV, withErrno } from 'kerium';
|
|
37
37
|
import { crit, warn } from 'kerium/log';
|
|
38
38
|
import { sizeof } from 'memium';
|
|
39
39
|
import { $from, field, struct, types as t } from 'memium/decorators';
|
|
@@ -41,6 +41,7 @@ import { decodeUTF8, encodeUTF8, pick } from 'utilium';
|
|
|
41
41
|
import { BufferView } from 'utilium/buffer';
|
|
42
42
|
import * as c from '../constants.js';
|
|
43
43
|
import { Stats } from '../node/stats.js';
|
|
44
|
+
import { checkAccess } from '../vfs/config.js';
|
|
44
45
|
import { contextOf } from './contexts.js';
|
|
45
46
|
/**
|
|
46
47
|
* Root inode
|
|
@@ -654,10 +655,10 @@ export function hasAccess($, inode, access) {
|
|
|
654
655
|
const { credentials } = contextOf($);
|
|
655
656
|
if (isSymbolicLink(inode))
|
|
656
657
|
return true;
|
|
657
|
-
if (credentials.euid === 0
|
|
658
|
+
if (credentials.euid === 0)
|
|
658
659
|
return !(access & c.X_OK) || isDirectory(inode) || !!(inode.mode & (c.S_IXUSR | c.S_IXGRP | c.S_IXOTH));
|
|
659
660
|
let perm = 0;
|
|
660
|
-
if (credentials.
|
|
661
|
+
if (credentials.euid === inode.uid) {
|
|
661
662
|
if (inode.mode & c.S_IRUSR)
|
|
662
663
|
perm |= c.R_OK;
|
|
663
664
|
if (inode.mode & c.S_IWUSR)
|
|
@@ -665,7 +666,7 @@ export function hasAccess($, inode, access) {
|
|
|
665
666
|
if (inode.mode & c.S_IXUSR)
|
|
666
667
|
perm |= c.X_OK;
|
|
667
668
|
}
|
|
668
|
-
if (credentials.
|
|
669
|
+
else if (credentials.egid === inode.gid || credentials.groups.includes(Number(inode.gid))) {
|
|
669
670
|
if (inode.mode & c.S_IRGRP)
|
|
670
671
|
perm |= c.R_OK;
|
|
671
672
|
if (inode.mode & c.S_IWGRP)
|
|
@@ -673,14 +674,64 @@ export function hasAccess($, inode, access) {
|
|
|
673
674
|
if (inode.mode & c.S_IXGRP)
|
|
674
675
|
perm |= c.X_OK;
|
|
675
676
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
677
|
+
else {
|
|
678
|
+
if (inode.mode & c.S_IROTH)
|
|
679
|
+
perm |= c.R_OK;
|
|
680
|
+
if (inode.mode & c.S_IWOTH)
|
|
681
|
+
perm |= c.W_OK;
|
|
682
|
+
if (inode.mode & c.S_IXOTH)
|
|
683
|
+
perm |= c.X_OK;
|
|
684
|
+
}
|
|
682
685
|
return (perm & access) === access;
|
|
683
686
|
}
|
|
687
|
+
/**
|
|
688
|
+
* Throws if a given user may not search the directory `dir`
|
|
689
|
+
* @internal
|
|
690
|
+
*/
|
|
691
|
+
export function assertSearchable($, dir, extra) {
|
|
692
|
+
if (checkAccess && !hasAccess($, dir, c.X_OK))
|
|
693
|
+
throw UV('EACCES', extra);
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Throws if a given user may not remove `entry` from the directory `parent`
|
|
697
|
+
* @internal
|
|
698
|
+
*/
|
|
699
|
+
export function assertRemovable($, parent, entry, extra) {
|
|
700
|
+
if (!checkAccess)
|
|
701
|
+
return;
|
|
702
|
+
if (!hasAccess($, parent, c.W_OK | c.X_OK))
|
|
703
|
+
throw UV('EACCES', extra);
|
|
704
|
+
const { euid } = contextOf($).credentials;
|
|
705
|
+
if (parent.mode & c.S_ISVTX && euid !== 0 && euid !== entry.uid && euid !== parent.uid)
|
|
706
|
+
throw UV('EPERM', extra);
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Throws if a given user is neither root nor the owner of `inode`
|
|
710
|
+
* @internal
|
|
711
|
+
*/
|
|
712
|
+
export function assertOwner($, inode, extra) {
|
|
713
|
+
if (!checkAccess)
|
|
714
|
+
return;
|
|
715
|
+
const { euid } = contextOf($).credentials;
|
|
716
|
+
if (euid !== 0 && euid !== inode.uid)
|
|
717
|
+
throw UV('EPERM', extra);
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Throws if a given user may not change the owner of `inode` to `uid` and `gid`, where `-1` leaves an ID unchanged
|
|
721
|
+
* @internal
|
|
722
|
+
*/
|
|
723
|
+
export function assertCanChown($, inode, uid, gid, extra) {
|
|
724
|
+
if (!checkAccess)
|
|
725
|
+
return;
|
|
726
|
+
const { euid, egid, groups } = contextOf($).credentials;
|
|
727
|
+
if (euid === 0)
|
|
728
|
+
return;
|
|
729
|
+
const isOwner = euid === inode.uid;
|
|
730
|
+
if (uid !== -1 && !(isOwner && uid === inode.uid))
|
|
731
|
+
throw UV('EPERM', extra);
|
|
732
|
+
if (gid !== -1 && !(isOwner && (gid === inode.gid || gid === egid || groups.includes(gid))))
|
|
733
|
+
throw UV('EPERM', extra);
|
|
734
|
+
}
|
|
684
735
|
/**
|
|
685
736
|
* @hidden @internal
|
|
686
737
|
*
|
|
@@ -696,5 +747,19 @@ export function _chown(stats, uid, gid) {
|
|
|
696
747
|
stats.gid = gid;
|
|
697
748
|
else
|
|
698
749
|
valid = false;
|
|
750
|
+
if (stats.mode !== undefined && !isDirectory({ mode: stats.mode })) {
|
|
751
|
+
stats.mode &= ~c.S_ISUID;
|
|
752
|
+
if (stats.mode & c.S_IXGRP)
|
|
753
|
+
stats.mode &= ~c.S_ISGID;
|
|
754
|
+
}
|
|
699
755
|
return valid;
|
|
700
756
|
}
|
|
757
|
+
/**
|
|
758
|
+
* @hidden @internal
|
|
759
|
+
*/
|
|
760
|
+
export function _chmod($, inode, mode) {
|
|
761
|
+
const { euid, egid, groups } = contextOf($).credentials;
|
|
762
|
+
if (checkAccess && euid !== 0 && egid !== inode.gid && !groups.includes(inode.gid))
|
|
763
|
+
mode &= ~c.S_ISGID;
|
|
764
|
+
inode.mode = (inode.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
|
|
765
|
+
}
|
package/dist/internal/rpc.d.ts
CHANGED
|
@@ -100,15 +100,24 @@ export interface Message {
|
|
|
100
100
|
method: Method;
|
|
101
101
|
stack: string;
|
|
102
102
|
}
|
|
103
|
-
export interface
|
|
103
|
+
export interface RequestInit<TMethod extends Method = Method> {
|
|
104
104
|
method: TMethod;
|
|
105
105
|
args: Parameters<Methods[TMethod]>;
|
|
106
106
|
}
|
|
107
|
-
export
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
export type Request<TMethod extends Method = Method> = {
|
|
108
|
+
[K in Method]: Message & RequestInit<K>;
|
|
109
|
+
}[TMethod];
|
|
110
|
+
export type Response<TMethod extends Method = Method> = {
|
|
111
|
+
[K in Method]: Message & {
|
|
112
|
+
error?: WithOptional<ExceptionJSON, 'code' | 'errno'>;
|
|
113
|
+
method: K;
|
|
114
|
+
/**
|
|
115
|
+
* This is undefined if an error occurs, and we check it at runtime.
|
|
116
|
+
* We don't do the type stuff because Typescript gets confused
|
|
117
|
+
*/
|
|
118
|
+
value: ReturnType<Methods[K]>;
|
|
119
|
+
};
|
|
120
|
+
}[TMethod];
|
|
112
121
|
/**
|
|
113
122
|
* Encode a RPC message as a string using JSON.
|
|
114
123
|
* This is only done when structured cloning is not available.
|
|
@@ -130,7 +139,7 @@ export interface Executor extends PromiseWithResolvers<any> {
|
|
|
130
139
|
fs: PortFS;
|
|
131
140
|
timeout: ReturnType<typeof setTimeout>;
|
|
132
141
|
}
|
|
133
|
-
export declare function request<const
|
|
142
|
+
export declare function request<const Init extends RequestInit, TValue>(request: Init, { port, timeout: ms, fs }: Partial<Options> & {
|
|
134
143
|
fs: PortFS;
|
|
135
144
|
}): Promise<TValue>;
|
|
136
145
|
export declare function handleResponse<const TMethod extends Method>(response: Response<TMethod>): void;
|
package/dist/internal/rpc.js
CHANGED
|
@@ -160,11 +160,6 @@ export function request(request, { port, timeout: ms = 1000, fs }) {
|
|
|
160
160
|
port.send({ ...request, _zenfs: true, id, stack });
|
|
161
161
|
return promise;
|
|
162
162
|
}
|
|
163
|
-
// Why Typescript, WHY does the type need to be asserted even when the method is explicitly checked?
|
|
164
|
-
function __requestMethod(req) { }
|
|
165
|
-
function __responseMethod(res, ...t) {
|
|
166
|
-
return t.includes(res.method);
|
|
167
|
-
}
|
|
168
163
|
export function handleResponse(response) {
|
|
169
164
|
if (!isMessage(response))
|
|
170
165
|
return;
|
|
@@ -182,7 +177,7 @@ export function handleResponse(response) {
|
|
|
182
177
|
return;
|
|
183
178
|
}
|
|
184
179
|
disposeExecutors(response.id);
|
|
185
|
-
resolve(
|
|
180
|
+
resolve(['stat', 'createFile', 'mkdir'].includes(response.method) ? new Inode(response.value) : response.value);
|
|
186
181
|
return;
|
|
187
182
|
}
|
|
188
183
|
export function attach(port, handler) {
|
|
@@ -214,11 +209,9 @@ export async function handleRequest(port, fs, request) {
|
|
|
214
209
|
if (!isMessage(request))
|
|
215
210
|
return;
|
|
216
211
|
let value, error;
|
|
217
|
-
const transferList = [];
|
|
218
212
|
try {
|
|
219
213
|
switch (request.method) {
|
|
220
214
|
case 'read': {
|
|
221
|
-
__requestMethod(request);
|
|
222
215
|
const [path, buffer, start, end] = request.args;
|
|
223
216
|
await fs.read(path, buffer, start, end);
|
|
224
217
|
value = buffer;
|
|
@@ -227,7 +220,6 @@ export async function handleRequest(port, fs, request) {
|
|
|
227
220
|
case 'stat':
|
|
228
221
|
case 'createFile':
|
|
229
222
|
case 'mkdir': {
|
|
230
|
-
__requestMethod(request);
|
|
231
223
|
// @ts-expect-error 2556
|
|
232
224
|
const md = await fs[request.method](...request.args);
|
|
233
225
|
const inode = md instanceof Inode ? md : new Inode(md);
|
|
@@ -235,7 +227,6 @@ export async function handleRequest(port, fs, request) {
|
|
|
235
227
|
break;
|
|
236
228
|
}
|
|
237
229
|
case 'touch': {
|
|
238
|
-
__requestMethod(request);
|
|
239
230
|
const [path, metadata] = request.args;
|
|
240
231
|
await fs.touch(path, new Inode(metadata));
|
|
241
232
|
value = undefined;
|
|
@@ -249,5 +240,5 @@ export async function handleRequest(port, fs, request) {
|
|
|
249
240
|
catch (e) {
|
|
250
241
|
error = e instanceof Exception ? e.toJSON() : pick(e, 'message', 'stack');
|
|
251
242
|
}
|
|
252
|
-
port.send({ _zenfs: true, ...pick(request, 'id', 'method', 'stack'), error, value }
|
|
243
|
+
port.send({ _zenfs: true, ...pick(request, 'id', 'method', 'stack'), error, value });
|
|
253
244
|
}
|
package/dist/mixins/async.js
CHANGED
|
@@ -57,6 +57,12 @@ import { StoreFS } from '../backends/store/fs.js';
|
|
|
57
57
|
import { isDirectory } from '../internal/inode.js';
|
|
58
58
|
import { join } from '../path.js';
|
|
59
59
|
import { _asyncFSKeys } from './shared.js';
|
|
60
|
+
/**
|
|
61
|
+
* Whether the stack of `error` has a patched method or `key`'s sync method below the innermost patched method.
|
|
62
|
+
*/
|
|
63
|
+
function isInLoop(key, error = new Error()) {
|
|
64
|
+
return new RegExp(`__patched__[^]*(__patched__|\\b${key}Sync\\b)`).test(error.stack ?? '');
|
|
65
|
+
}
|
|
60
66
|
/**
|
|
61
67
|
* Async() implements synchronous methods on an asynchronous file system
|
|
62
68
|
*
|
|
@@ -82,6 +88,8 @@ export function Async(FS) {
|
|
|
82
88
|
return this.sync();
|
|
83
89
|
}
|
|
84
90
|
_promise = Promise.resolve();
|
|
91
|
+
/** The async methods before patching, which do not update the sync cache */
|
|
92
|
+
_unpatched = {};
|
|
85
93
|
_async(thunk) {
|
|
86
94
|
this._promise = this._promise.finally(() => thunk());
|
|
87
95
|
}
|
|
@@ -132,7 +140,7 @@ export function Async(FS) {
|
|
|
132
140
|
renameSync(oldPath, newPath) {
|
|
133
141
|
this.checkSync();
|
|
134
142
|
this._sync.renameSync(oldPath, newPath);
|
|
135
|
-
this._async(() => this.rename(oldPath, newPath));
|
|
143
|
+
this._async(() => this._unpatched.rename(oldPath, newPath));
|
|
136
144
|
}
|
|
137
145
|
statSync(path) {
|
|
138
146
|
this.checkSync();
|
|
@@ -141,28 +149,28 @@ export function Async(FS) {
|
|
|
141
149
|
touchSync(path, metadata) {
|
|
142
150
|
this.checkSync();
|
|
143
151
|
this._sync.touchSync(path, metadata);
|
|
144
|
-
this._async(() => this.touch(path, metadata));
|
|
152
|
+
this._async(() => this._unpatched.touch(path, metadata));
|
|
145
153
|
}
|
|
146
154
|
createFileSync(path, options) {
|
|
147
155
|
this.checkSync();
|
|
148
156
|
const result = this._sync.createFileSync(path, options);
|
|
149
|
-
this._async(() => this.createFile(path, options));
|
|
157
|
+
this._async(() => this._unpatched.createFile(path, options));
|
|
150
158
|
return result;
|
|
151
159
|
}
|
|
152
160
|
unlinkSync(path) {
|
|
153
161
|
this.checkSync();
|
|
154
162
|
this._sync.unlinkSync(path);
|
|
155
|
-
this._async(() => this.unlink(path));
|
|
163
|
+
this._async(() => this._unpatched.unlink(path));
|
|
156
164
|
}
|
|
157
165
|
rmdirSync(path) {
|
|
158
166
|
this.checkSync();
|
|
159
167
|
this._sync.rmdirSync(path);
|
|
160
|
-
this._async(() => this.rmdir(path));
|
|
168
|
+
this._async(() => this._unpatched.rmdir(path));
|
|
161
169
|
}
|
|
162
170
|
mkdirSync(path, options) {
|
|
163
171
|
this.checkSync();
|
|
164
172
|
const result = this._sync.mkdirSync(path, options);
|
|
165
|
-
this._async(() => this.mkdir(path, options));
|
|
173
|
+
this._async(() => this._unpatched.mkdir(path, options));
|
|
166
174
|
return result;
|
|
167
175
|
}
|
|
168
176
|
readdirSync(path) {
|
|
@@ -172,7 +180,7 @@ export function Async(FS) {
|
|
|
172
180
|
linkSync(srcpath, dstpath) {
|
|
173
181
|
this.checkSync();
|
|
174
182
|
this._sync.linkSync(srcpath, dstpath);
|
|
175
|
-
this._async(() => this.link(srcpath, dstpath));
|
|
183
|
+
this._async(() => this._unpatched.link(srcpath, dstpath));
|
|
176
184
|
}
|
|
177
185
|
async sync() {
|
|
178
186
|
if (!this.attributes.has('no_async_preload') && this._sync)
|
|
@@ -194,7 +202,7 @@ export function Async(FS) {
|
|
|
194
202
|
writeSync(path, buffer, offset) {
|
|
195
203
|
this.checkSync();
|
|
196
204
|
this._sync.writeSync(path, buffer, offset);
|
|
197
|
-
this._async(() => this.write(path, buffer, offset));
|
|
205
|
+
this._async(() => this._unpatched.write(path, buffer, offset));
|
|
198
206
|
}
|
|
199
207
|
ioctlSync(context, command, ...args) {
|
|
200
208
|
this.checkSync();
|
|
@@ -261,22 +269,9 @@ export function Async(FS) {
|
|
|
261
269
|
for (const key of toPatch) {
|
|
262
270
|
// TS does not narrow the union based on the key
|
|
263
271
|
const originalMethod = this[key].bind(this);
|
|
264
|
-
function
|
|
265
|
-
if (!error) {
|
|
266
|
-
error = new Error();
|
|
267
|
-
Error.captureStackTrace(error, isInLoop);
|
|
268
|
-
}
|
|
269
|
-
if (!error.stack)
|
|
270
|
-
return false;
|
|
271
|
-
const stack = error.stack.split('\n').slice(depth).join('\n');
|
|
272
|
-
// From the async queue
|
|
273
|
-
return (stack.includes(`at <computed> [as ${key}]`)
|
|
274
|
-
|| stack.includes(`at async <computed> [as ${key}]`)
|
|
275
|
-
|| stack.includes(`${key}Sync `));
|
|
276
|
-
}
|
|
277
|
-
this[key] = async (...args) => {
|
|
272
|
+
const fn = async function __patched__(...args) {
|
|
278
273
|
const result = await originalMethod(...args);
|
|
279
|
-
if (isInLoop(
|
|
274
|
+
if (isInLoop(key))
|
|
280
275
|
return result;
|
|
281
276
|
if (!this._isInitialized) {
|
|
282
277
|
this._skippedCacheUpdates++;
|
|
@@ -287,13 +282,16 @@ export function Async(FS) {
|
|
|
287
282
|
this._sync?.[`${key}Sync`]?.(...args);
|
|
288
283
|
}
|
|
289
284
|
catch (e) {
|
|
290
|
-
if (isInLoop(
|
|
285
|
+
if (isInLoop(key, e))
|
|
291
286
|
return result;
|
|
292
287
|
e.message += ' (Out of sync!)';
|
|
293
288
|
throw err(e);
|
|
294
289
|
}
|
|
295
290
|
return result;
|
|
296
291
|
};
|
|
292
|
+
Object.defineProperty(fn, 'name', { get: () => key });
|
|
293
|
+
this._unpatched[key] = originalMethod;
|
|
294
|
+
this[key] = fn;
|
|
297
295
|
}
|
|
298
296
|
debug(`Async: patched ${toPatch.length} methods`);
|
|
299
297
|
}
|
package/dist/mixins/mutexed.d.ts
CHANGED
|
@@ -1,28 +1,17 @@
|
|
|
1
|
+
import { RwLockable } from 'kerium/locks';
|
|
1
2
|
import type { UUID } from 'node:crypto';
|
|
2
3
|
import type { Concrete } from 'utilium';
|
|
3
4
|
import type { CreationOptions, FileSystem, StreamOptions, UsageInfo } from '../internal/filesystem.js';
|
|
4
5
|
import type { InodeLike } from '../internal/inode.js';
|
|
5
6
|
import type { IoctlContext } from '../internal/ioctl.js';
|
|
6
7
|
import '../polyfills.js';
|
|
7
|
-
/**
|
|
8
|
-
* @category Internals
|
|
9
|
-
* @internal
|
|
10
|
-
*/
|
|
11
|
-
export declare class MutexLock {
|
|
12
|
-
protected readonly previous?: MutexLock | undefined;
|
|
13
|
-
protected current: PromiseWithResolvers<void>;
|
|
14
|
-
protected _isLocked: boolean;
|
|
15
|
-
get isLocked(): boolean;
|
|
16
|
-
constructor(previous?: MutexLock | undefined);
|
|
17
|
-
done(): Promise<void>;
|
|
18
|
-
unlock(): void;
|
|
19
|
-
[Symbol.dispose](): void;
|
|
20
|
-
}
|
|
21
8
|
/**
|
|
22
9
|
* @hidden
|
|
23
10
|
* @category Internals
|
|
11
|
+
* @deprecated This is no longer needed and will be removed
|
|
12
|
+
* @todo [breaking] Remove
|
|
24
13
|
*/
|
|
25
|
-
export declare class _MutexedFS<T extends FileSystem> implements FileSystem {
|
|
14
|
+
export declare class _MutexedFS<T extends FileSystem> extends RwLockable implements FileSystem {
|
|
26
15
|
/**
|
|
27
16
|
* @internal
|
|
28
17
|
*/
|
|
@@ -38,31 +27,6 @@ export declare class _MutexedFS<T extends FileSystem> implements FileSystem {
|
|
|
38
27
|
ready(): Promise<void>;
|
|
39
28
|
readySync(): void;
|
|
40
29
|
usage(): UsageInfo;
|
|
41
|
-
/**
|
|
42
|
-
* The current locks
|
|
43
|
-
*/
|
|
44
|
-
private currentLock?;
|
|
45
|
-
/**
|
|
46
|
-
* Adds a lock for a path
|
|
47
|
-
*/
|
|
48
|
-
protected addLock(): MutexLock;
|
|
49
|
-
/**
|
|
50
|
-
* Locks `path` asynchronously.
|
|
51
|
-
* If the path is currently locked, waits for it to be unlocked.
|
|
52
|
-
* @internal
|
|
53
|
-
*/
|
|
54
|
-
lock(timeout?: number): Promise<MutexLock>;
|
|
55
|
-
/**
|
|
56
|
-
* Locks `path` asynchronously.
|
|
57
|
-
* If the path is currently locked, an error will be thrown
|
|
58
|
-
* @internal
|
|
59
|
-
*/
|
|
60
|
-
lockSync(): MutexLock;
|
|
61
|
-
/**
|
|
62
|
-
* Whether `path` is locked
|
|
63
|
-
* @internal
|
|
64
|
-
*/
|
|
65
|
-
get isLocked(): boolean;
|
|
66
30
|
rename(oldPath: string, newPath: string): Promise<void>;
|
|
67
31
|
renameSync(oldPath: string, newPath: string): void;
|
|
68
32
|
stat(path: string): Promise<InodeLike>;
|
|
@@ -110,9 +74,10 @@ export declare class _MutexedFS<T extends FileSystem> implements FileSystem {
|
|
|
110
74
|
* Instead of extending `FileSystem`,
|
|
111
75
|
* `MutexedFS` implements it in order to make sure all of the methods are passed through
|
|
112
76
|
*
|
|
113
|
-
* @todo Change `using _` to `using void` pending https://github.com/tc39/proposal-discard-binding
|
|
114
77
|
* @category Internals
|
|
115
78
|
* @internal
|
|
79
|
+
* @deprecated This is no longer needed and will be removed
|
|
80
|
+
* @todo [breaking] Remove
|
|
116
81
|
*/
|
|
117
82
|
export declare function Mutexed<const T extends Concrete<typeof FileSystem>>(FS: T): typeof _MutexedFS<InstanceType<T>> & {
|
|
118
83
|
new (...args: ConstructorParameters<T>): _MutexedFS<InstanceType<T>>;
|