@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/backends/backend.js
CHANGED
|
@@ -55,9 +55,7 @@ export function checkOptions(backend, options) {
|
|
|
55
55
|
: typeof value === type || value?.constructor?.name === type;
|
|
56
56
|
if (Array.isArray(opt.type) ? opt.type.some(v => isType(v)) : isType(opt.type))
|
|
57
57
|
continue;
|
|
58
|
-
// The type of the value as a string
|
|
59
58
|
const type = typeof value == 'object' && 'constructor' in value ? value.constructor.name : typeof value;
|
|
60
|
-
// The expected type (as a string)
|
|
61
59
|
const name = (type) => (typeof type == 'function' ? (type.name != 'type' ? type.name : type.toString()) : type);
|
|
62
60
|
const expected = Array.isArray(opt.type) ? `one of ${opt.type.map(name).join(', ')}` : name(opt.type);
|
|
63
61
|
throw err(withErrno('EINVAL', `Incorrect type for "${optName}": ${type} (expected ${expected})`));
|
package/dist/backends/cow.d.ts
CHANGED
|
@@ -162,14 +162,14 @@ declare const _CopyOnWrite: {
|
|
|
162
162
|
};
|
|
163
163
|
type _CopyOnWrite = typeof _CopyOnWrite;
|
|
164
164
|
/**
|
|
165
|
-
*
|
|
165
|
+
* Makes a read-only filesystem writable by storing writes on a second, writable file system.
|
|
166
166
|
* Deletes are persisted via metadata stored on the writable file system.
|
|
167
167
|
* @category Backends and Configuration
|
|
168
168
|
*/
|
|
169
169
|
export interface CopyOnWrite extends _CopyOnWrite {
|
|
170
170
|
}
|
|
171
171
|
/**
|
|
172
|
-
*
|
|
172
|
+
* Makes a read-only filesystem writable by storing writes on a second, writable file system.
|
|
173
173
|
* Deletes are persisted via metadata stored on the writable file system.
|
|
174
174
|
* @category Backends and Configuration
|
|
175
175
|
* @internal
|
package/dist/backends/cow.js
CHANGED
|
@@ -141,7 +141,7 @@ export class CopyOnWriteFS extends FileSystem {
|
|
|
141
141
|
}
|
|
142
142
|
/* Note the layer's inode is not mutated, since it may be a persistent object.
|
|
143
143
|
`Inode` fields are accessors, so a copy must be explicit rather than a spread. */
|
|
144
|
-
return
|
|
144
|
+
return Object.assign(pick(inode, _inode_fields), { attributes: inode.attributes, ino });
|
|
145
145
|
}
|
|
146
146
|
/**
|
|
147
147
|
* @todo Consider trying to track information on the writable as well
|
|
@@ -252,25 +252,19 @@ export class CopyOnWriteFS extends FileSystem {
|
|
|
252
252
|
async unlink(path) {
|
|
253
253
|
if (!(await this.exists(path)))
|
|
254
254
|
throw withErrno('ENOENT');
|
|
255
|
-
if (await this.writable.exists(path))
|
|
255
|
+
if (await this.writable.exists(path))
|
|
256
256
|
await this.writable.unlink(path);
|
|
257
|
-
|
|
258
|
-
// if it still exists add to the delete log
|
|
259
|
-
if (await this.exists(path)) {
|
|
257
|
+
if (await this.exists(path))
|
|
260
258
|
this.journal.add('delete', path);
|
|
261
|
-
}
|
|
262
259
|
this.inos.delete(path);
|
|
263
260
|
}
|
|
264
261
|
unlinkSync(path) {
|
|
265
262
|
if (!this.existsSync(path))
|
|
266
263
|
throw withErrno('ENOENT');
|
|
267
|
-
if (this.writable.existsSync(path))
|
|
264
|
+
if (this.writable.existsSync(path))
|
|
268
265
|
this.writable.unlinkSync(path);
|
|
269
|
-
|
|
270
|
-
// if it still exists add to the delete log
|
|
271
|
-
if (this.existsSync(path)) {
|
|
266
|
+
if (this.existsSync(path))
|
|
272
267
|
this.journal.add('delete', path);
|
|
273
|
-
}
|
|
274
268
|
this.inos.delete(path);
|
|
275
269
|
}
|
|
276
270
|
async rmdir(path) {
|
|
@@ -283,7 +277,6 @@ export class CopyOnWriteFS extends FileSystem {
|
|
|
283
277
|
this.inos.delete(path);
|
|
284
278
|
return;
|
|
285
279
|
}
|
|
286
|
-
// Check if directory is empty.
|
|
287
280
|
if ((await this.readdir(path)).length)
|
|
288
281
|
throw withErrno('ENOTEMPTY');
|
|
289
282
|
this.journal.add('delete', path);
|
|
@@ -299,7 +292,6 @@ export class CopyOnWriteFS extends FileSystem {
|
|
|
299
292
|
this.inos.delete(path);
|
|
300
293
|
return;
|
|
301
294
|
}
|
|
302
|
-
// Check if directory is empty.
|
|
303
295
|
if (this.readdirSync(path).length)
|
|
304
296
|
throw withErrno('ENOTEMPTY');
|
|
305
297
|
this.journal.add('delete', path);
|
|
@@ -488,7 +480,7 @@ const _CopyOnWrite = {
|
|
|
488
480
|
},
|
|
489
481
|
};
|
|
490
482
|
/**
|
|
491
|
-
*
|
|
483
|
+
* Makes a read-only filesystem writable by storing writes on a second, writable file system.
|
|
492
484
|
* Deletes are persisted via metadata stored on the writable file system.
|
|
493
485
|
* @category Backends and Configuration
|
|
494
486
|
* @internal
|
|
@@ -16,21 +16,9 @@ export declare class PassthroughFS extends FileSystem {
|
|
|
16
16
|
constructor(nodeFS: NodeFS, prefix: string);
|
|
17
17
|
usage(): UsageInfo;
|
|
18
18
|
path(path: string): string;
|
|
19
|
-
/**
|
|
20
|
-
* Rename a file or directory.
|
|
21
|
-
*/
|
|
22
19
|
rename(oldPath: string, newPath: string): Promise<void>;
|
|
23
|
-
/**
|
|
24
|
-
* Rename a file or directory synchronously.
|
|
25
|
-
*/
|
|
26
20
|
renameSync(oldPath: string, newPath: string): void;
|
|
27
|
-
/**
|
|
28
|
-
* Get file statistics.
|
|
29
|
-
*/
|
|
30
21
|
stat(path: string): Promise<InodeLike>;
|
|
31
|
-
/**
|
|
32
|
-
* Get file statistics synchronously.
|
|
33
|
-
*/
|
|
34
22
|
statSync(path: string): InodeLike;
|
|
35
23
|
/**
|
|
36
24
|
* @privateRemarks
|
|
@@ -42,61 +30,19 @@ export declare class PassthroughFS extends FileSystem {
|
|
|
42
30
|
* Timestamps should be updated by the underlying file system.
|
|
43
31
|
*/
|
|
44
32
|
touchSync(path: string, metadata: InodeLike): void;
|
|
45
|
-
/**
|
|
46
|
-
* Unlink (delete) a file.
|
|
47
|
-
*/
|
|
48
33
|
unlink(path: string): Promise<void>;
|
|
49
|
-
/**
|
|
50
|
-
* Unlink (delete) a file synchronously.
|
|
51
|
-
*/
|
|
52
34
|
unlinkSync(path: string): void;
|
|
53
|
-
/**
|
|
54
|
-
* Create a directory.
|
|
55
|
-
*/
|
|
56
35
|
mkdir(path: string, options: CreationOptions): Promise<InodeLike>;
|
|
57
|
-
/**
|
|
58
|
-
* Create a directory synchronously.
|
|
59
|
-
*/
|
|
60
36
|
mkdirSync(path: string, options: CreationOptions): InodeLike;
|
|
61
|
-
/**
|
|
62
|
-
* Read the contents of a directory.
|
|
63
|
-
*/
|
|
64
37
|
readdir(path: string): Promise<string[]>;
|
|
65
|
-
/**
|
|
66
|
-
* Read the contents of a directory synchronously.
|
|
67
|
-
*/
|
|
68
38
|
readdirSync(path: string): string[];
|
|
69
|
-
/**
|
|
70
|
-
* Create a file.
|
|
71
|
-
*/
|
|
72
39
|
createFile(path: string, options: CreationOptions): Promise<InodeLike>;
|
|
73
|
-
/**
|
|
74
|
-
* Create a file synchronously.
|
|
75
|
-
*/
|
|
76
40
|
createFileSync(path: string, options: CreationOptions): InodeLike;
|
|
77
|
-
/**
|
|
78
|
-
* Remove a directory.
|
|
79
|
-
*/
|
|
80
41
|
rmdir(path: string): Promise<void>;
|
|
81
|
-
/**
|
|
82
|
-
* Remove a directory synchronously.
|
|
83
|
-
*/
|
|
84
42
|
rmdirSync(path: string): void;
|
|
85
|
-
/**
|
|
86
|
-
* Synchronize data to the file system.
|
|
87
|
-
*/
|
|
88
43
|
sync(): Promise<void>;
|
|
89
|
-
/**
|
|
90
|
-
* Synchronize data to the file system synchronously.
|
|
91
|
-
*/
|
|
92
44
|
syncSync(): void;
|
|
93
|
-
/**
|
|
94
|
-
* Create a hard link.
|
|
95
|
-
*/
|
|
96
45
|
link(target: string, link: string): Promise<void>;
|
|
97
|
-
/**
|
|
98
|
-
* Create a hard link synchronously.
|
|
99
|
-
*/
|
|
100
46
|
linkSync(target: string, link: string): void;
|
|
101
47
|
read(path: string, buffer: Uint8Array, offset: number, end: number): Promise<void>;
|
|
102
48
|
readSync(path: string, buffer: Uint8Array, offset: number, end: number): void;
|
|
@@ -72,27 +72,15 @@ export class PassthroughFS extends FileSystem {
|
|
|
72
72
|
path(path) {
|
|
73
73
|
return this.prefix + path;
|
|
74
74
|
}
|
|
75
|
-
/**
|
|
76
|
-
* Rename a file or directory.
|
|
77
|
-
*/
|
|
78
75
|
async rename(oldPath, newPath) {
|
|
79
76
|
await this.nodeFS.promises.rename(this.path(oldPath), this.path(newPath));
|
|
80
77
|
}
|
|
81
|
-
/**
|
|
82
|
-
* Rename a file or directory synchronously.
|
|
83
|
-
*/
|
|
84
78
|
renameSync(oldPath, newPath) {
|
|
85
79
|
this.nodeFS.renameSync(this.path(oldPath), this.path(newPath));
|
|
86
80
|
}
|
|
87
|
-
/**
|
|
88
|
-
* Get file statistics.
|
|
89
|
-
*/
|
|
90
81
|
async stat(path) {
|
|
91
82
|
return await this.nodeFS.promises.stat(this.path(path));
|
|
92
83
|
}
|
|
93
|
-
/**
|
|
94
|
-
* Get file statistics synchronously.
|
|
95
|
-
*/
|
|
96
84
|
statSync(path) {
|
|
97
85
|
return this.nodeFS.statSync(this.path(path));
|
|
98
86
|
}
|
|
@@ -135,47 +123,26 @@ export class PassthroughFS extends FileSystem {
|
|
|
135
123
|
err('Failed to chown passthrough file: ' + error.message);
|
|
136
124
|
}
|
|
137
125
|
}
|
|
138
|
-
/**
|
|
139
|
-
* Unlink (delete) a file.
|
|
140
|
-
*/
|
|
141
126
|
async unlink(path) {
|
|
142
127
|
await this.nodeFS.promises.unlink(this.path(path));
|
|
143
128
|
}
|
|
144
|
-
/**
|
|
145
|
-
* Unlink (delete) a file synchronously.
|
|
146
|
-
*/
|
|
147
129
|
unlinkSync(path) {
|
|
148
130
|
this.nodeFS.unlinkSync(this.path(path));
|
|
149
131
|
}
|
|
150
|
-
/**
|
|
151
|
-
* Create a directory.
|
|
152
|
-
*/
|
|
153
132
|
async mkdir(path, options) {
|
|
154
133
|
await this.nodeFS.promises.mkdir(this.path(path), options);
|
|
155
134
|
return await this.nodeFS.promises.stat(this.path(path));
|
|
156
135
|
}
|
|
157
|
-
/**
|
|
158
|
-
* Create a directory synchronously.
|
|
159
|
-
*/
|
|
160
136
|
mkdirSync(path, options) {
|
|
161
137
|
this.nodeFS.mkdirSync(this.path(path), options);
|
|
162
138
|
return this.nodeFS.statSync(this.path(path));
|
|
163
139
|
}
|
|
164
|
-
/**
|
|
165
|
-
* Read the contents of a directory.
|
|
166
|
-
*/
|
|
167
140
|
async readdir(path) {
|
|
168
141
|
return await this.nodeFS.promises.readdir(this.path(path));
|
|
169
142
|
}
|
|
170
|
-
/**
|
|
171
|
-
* Read the contents of a directory synchronously.
|
|
172
|
-
*/
|
|
173
143
|
readdirSync(path) {
|
|
174
144
|
return this.nodeFS.readdirSync(this.path(path));
|
|
175
145
|
}
|
|
176
|
-
/**
|
|
177
|
-
* Create a file.
|
|
178
|
-
*/
|
|
179
146
|
async createFile(path, options) {
|
|
180
147
|
if (isDirectory(options)) {
|
|
181
148
|
await this.nodeFS.promises.mkdir(this.path(path), { mode: options.mode });
|
|
@@ -198,9 +165,6 @@ export class PassthroughFS extends FileSystem {
|
|
|
198
165
|
}
|
|
199
166
|
return await this.nodeFS.promises.stat(this.path(path));
|
|
200
167
|
}
|
|
201
|
-
/**
|
|
202
|
-
* Create a file synchronously.
|
|
203
|
-
*/
|
|
204
168
|
createFileSync(path, options) {
|
|
205
169
|
if (isDirectory(options)) {
|
|
206
170
|
this.nodeFS.mkdirSync(this.path(path), { mode: options.mode });
|
|
@@ -211,39 +175,21 @@ export class PassthroughFS extends FileSystem {
|
|
|
211
175
|
}
|
|
212
176
|
return this.nodeFS.statSync(this.path(path));
|
|
213
177
|
}
|
|
214
|
-
/**
|
|
215
|
-
* Remove a directory.
|
|
216
|
-
*/
|
|
217
178
|
async rmdir(path) {
|
|
218
179
|
await this.nodeFS.promises.rmdir(this.path(path));
|
|
219
180
|
}
|
|
220
|
-
/**
|
|
221
|
-
* Remove a directory synchronously.
|
|
222
|
-
*/
|
|
223
181
|
rmdirSync(path) {
|
|
224
182
|
this.nodeFS.rmdirSync(this.path(path));
|
|
225
183
|
}
|
|
226
|
-
/**
|
|
227
|
-
* Synchronize data to the file system.
|
|
228
|
-
*/
|
|
229
184
|
async sync() {
|
|
230
185
|
warn('Sync on passthrough is unnecessary');
|
|
231
186
|
}
|
|
232
|
-
/**
|
|
233
|
-
* Synchronize data to the file system synchronously.
|
|
234
|
-
*/
|
|
235
187
|
syncSync() {
|
|
236
188
|
warn('Sync on passthrough is unnecessary');
|
|
237
189
|
}
|
|
238
|
-
/**
|
|
239
|
-
* Create a hard link.
|
|
240
|
-
*/
|
|
241
190
|
async link(target, link) {
|
|
242
191
|
await this.nodeFS.promises.link(this.path(target), this.path(link));
|
|
243
192
|
}
|
|
244
|
-
/**
|
|
245
|
-
* Create a hard link synchronously.
|
|
246
|
-
*/
|
|
247
193
|
linkSync(target, link) {
|
|
248
194
|
this.nodeFS.linkSync(this.path(target), this.path(link));
|
|
249
195
|
}
|
package/dist/backends/port.d.ts
CHANGED
|
@@ -33,13 +33,9 @@ export declare class PortFS<T extends RPC.Channel = RPC.Channel> extends PortFS_
|
|
|
33
33
|
readonly channel: T;
|
|
34
34
|
readonly timeout: number;
|
|
35
35
|
readonly port: RPC.Port<T>;
|
|
36
|
-
/**
|
|
37
|
-
* @hidden
|
|
38
|
-
*/
|
|
36
|
+
/** @hidden */
|
|
39
37
|
_sync: import("./index.js").StoreFS<import("./memory.js").InMemoryStore>;
|
|
40
|
-
/**
|
|
41
|
-
* Constructs a new PortFS instance that connects with the FS running on `options.port`.
|
|
42
|
-
*/
|
|
38
|
+
/** Constructs a new PortFS instance that connects with the FS running on `options.port`. */
|
|
43
39
|
constructor(channel: T, timeout?: number);
|
|
44
40
|
protected rpc<const T extends RPC.Method>(method: T, ...args: Parameters<RPC.Methods[T]>): Promise<Awaited<ReturnType<RPC.Methods[T]>>>;
|
|
45
41
|
ready(): Promise<void>;
|
package/dist/backends/port.js
CHANGED
|
@@ -19,13 +19,9 @@ export class PortFS extends Async(FileSystem) {
|
|
|
19
19
|
channel;
|
|
20
20
|
timeout;
|
|
21
21
|
port;
|
|
22
|
-
/**
|
|
23
|
-
* @hidden
|
|
24
|
-
*/
|
|
22
|
+
/** @hidden */
|
|
25
23
|
_sync = InMemory.create({ label: 'tmpfs:port' });
|
|
26
|
-
/**
|
|
27
|
-
* Constructs a new PortFS instance that connects with the FS running on `options.port`.
|
|
28
|
-
*/
|
|
24
|
+
/** Constructs a new PortFS instance that connects with the FS running on `options.port`. */
|
|
29
25
|
constructor(channel, timeout = 250) {
|
|
30
26
|
super(0x706f7274, 'portfs');
|
|
31
27
|
this.channel = channel;
|
|
@@ -34,11 +30,7 @@ export class PortFS extends Async(FileSystem) {
|
|
|
34
30
|
RPC.attach(this.port, RPC.handleResponse);
|
|
35
31
|
}
|
|
36
32
|
rpc(method, ...args) {
|
|
37
|
-
return RPC.request({ method, args }, {
|
|
38
|
-
port: this.port,
|
|
39
|
-
timeout: this.timeout,
|
|
40
|
-
fs: this,
|
|
41
|
-
});
|
|
33
|
+
return RPC.request({ method, args }, { port: this.port, timeout: this.timeout, fs: this });
|
|
42
34
|
}
|
|
43
35
|
async ready() {
|
|
44
36
|
await this.rpc('ready');
|
|
@@ -42,9 +42,6 @@ export declare class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
42
42
|
ready(): Promise<void>;
|
|
43
43
|
readySync(): void;
|
|
44
44
|
constructor(store: T);
|
|
45
|
-
/**
|
|
46
|
-
* @experimental
|
|
47
|
-
*/
|
|
48
45
|
usage(): UsageInfo;
|
|
49
46
|
/**
|
|
50
47
|
* Load an index into the StoreFS.
|
|
@@ -77,13 +74,7 @@ export declare class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
77
74
|
mkdirSync(path: string, options: CreationOptions): Inode;
|
|
78
75
|
readdir(path: string): Promise<string[]>;
|
|
79
76
|
readdirSync(path: string): string[];
|
|
80
|
-
/**
|
|
81
|
-
* Updated the inode and data node at `path`
|
|
82
|
-
*/
|
|
83
77
|
sync(): Promise<void>;
|
|
84
|
-
/**
|
|
85
|
-
* Updated the inode and data node at `path`
|
|
86
|
-
*/
|
|
87
78
|
syncSync(): void;
|
|
88
79
|
link(target: string, link: string): Promise<void>;
|
|
89
80
|
linkSync(target: string, link: string): void;
|
|
@@ -93,9 +93,7 @@ export class StoreFS extends FileSystem {
|
|
|
93
93
|
* Add a inode/path pair
|
|
94
94
|
*/
|
|
95
95
|
_add(ino, path) {
|
|
96
|
-
|
|
97
|
-
this._paths.set(ino, new Set());
|
|
98
|
-
this._paths.get(ino).add(path);
|
|
96
|
+
this._paths.getOrInsertComputed(ino, () => new Set()).add(path);
|
|
99
97
|
this._ids.set(path, ino);
|
|
100
98
|
}
|
|
101
99
|
/**
|
|
@@ -162,9 +160,6 @@ export class StoreFS extends FileSystem {
|
|
|
162
160
|
this.label = store.label;
|
|
163
161
|
debug(this.name + ': supports features: ' + this.store.flags?.join(', '));
|
|
164
162
|
}
|
|
165
|
-
/**
|
|
166
|
-
* @experimental
|
|
167
|
-
*/
|
|
168
163
|
usage() {
|
|
169
164
|
return (this.store.usage?.() || {
|
|
170
165
|
totalSpace: 0,
|
|
@@ -525,13 +520,7 @@ export class StoreFS extends FileSystem {
|
|
|
525
520
|
__disposeResources(env_12);
|
|
526
521
|
}
|
|
527
522
|
}
|
|
528
|
-
/**
|
|
529
|
-
* Updated the inode and data node at `path`
|
|
530
|
-
*/
|
|
531
523
|
async sync() { }
|
|
532
|
-
/**
|
|
533
|
-
* Updated the inode and data node at `path`
|
|
534
|
-
*/
|
|
535
524
|
syncSync() { }
|
|
536
525
|
async link(target, link) {
|
|
537
526
|
const env_13 = { stack: [], error: void 0, hasError: false };
|
|
@@ -758,16 +747,13 @@ export class StoreFS extends FileSystem {
|
|
|
758
747
|
crit('Store contains an invalid root inode. Refusing to populate tables');
|
|
759
748
|
return;
|
|
760
749
|
}
|
|
761
|
-
// Keep track of directories we have already traversed to avoid loops
|
|
762
750
|
const visitedDirectories = new Set();
|
|
763
751
|
let i = 0;
|
|
764
|
-
// Start BFS from root
|
|
765
752
|
const queue = [['/', rootIno]];
|
|
766
753
|
while (queue.length > 0) {
|
|
767
754
|
i++;
|
|
768
755
|
const [path, ino] = queue.shift();
|
|
769
756
|
this._add(ino, path);
|
|
770
|
-
// Get the inode data from the store
|
|
771
757
|
const inodeData = await tx.get(ino);
|
|
772
758
|
if (!inodeData) {
|
|
773
759
|
warn('Store is missing data for inode: ' + ino);
|
|
@@ -777,14 +763,10 @@ export class StoreFS extends FileSystem {
|
|
|
777
763
|
warn(`Invalid inode size for ino ${ino}: ${inodeData.length}`);
|
|
778
764
|
continue;
|
|
779
765
|
}
|
|
780
|
-
// Parse the raw data into our Inode object
|
|
781
766
|
const inode = new Inode(inodeData);
|
|
782
|
-
|
|
783
|
-
if (!isDirectory(inode) || visitedDirectories.has(ino)) {
|
|
767
|
+
if (!isDirectory(inode) || visitedDirectories.has(ino))
|
|
784
768
|
continue;
|
|
785
|
-
}
|
|
786
769
|
visitedDirectories.add(ino);
|
|
787
|
-
// Grab the directory listing from the store
|
|
788
770
|
const dirData = await tx.get(inode.data);
|
|
789
771
|
if (!dirData) {
|
|
790
772
|
warn('Store is missing directory data: ' + inode.data);
|
|
@@ -963,11 +945,9 @@ export class StoreFS extends FileSystem {
|
|
|
963
945
|
const { dir: parentPath, base: fname } = parse(path);
|
|
964
946
|
const parent = await this.findInode(tx, parentPath);
|
|
965
947
|
const listing = decodeDirListing((await tx.get(parent.data)) ?? _throw(withErrno('ENOENT')));
|
|
966
|
-
// Check if file already exists.
|
|
967
948
|
if (listing[fname])
|
|
968
949
|
throw withErrno('EEXIST');
|
|
969
950
|
const id = this.allocNew(path);
|
|
970
|
-
// Commit data.
|
|
971
951
|
const inode = new Inode({
|
|
972
952
|
...options,
|
|
973
953
|
ino: id,
|
|
@@ -977,7 +957,6 @@ export class StoreFS extends FileSystem {
|
|
|
977
957
|
});
|
|
978
958
|
await tx.set(inode.ino, inode);
|
|
979
959
|
await tx.set(inode.data, data);
|
|
980
|
-
// Update and commit parent directory listing.
|
|
981
960
|
listing[fname] = inode.ino;
|
|
982
961
|
await tx.set(parent.data, encodeDirListing(listing));
|
|
983
962
|
await tx.commit();
|
|
@@ -1025,7 +1004,6 @@ export class StoreFS extends FileSystem {
|
|
|
1025
1004
|
size: data.byteLength,
|
|
1026
1005
|
nlink: 1,
|
|
1027
1006
|
});
|
|
1028
|
-
// Update and commit parent directory listing.
|
|
1029
1007
|
tx.setSync(inode.ino, inode);
|
|
1030
1008
|
tx.setSync(inode.data, data);
|
|
1031
1009
|
listing[fname] = inode.ino;
|
|
@@ -162,7 +162,7 @@ export declare class WrappedTransaction<T extends Store = Store> {
|
|
|
162
162
|
data?: Uint8Array;
|
|
163
163
|
offset: number;
|
|
164
164
|
}[]>;
|
|
165
|
-
/**
|
|
165
|
+
/**
|
|
166
166
|
* List of keys modified in this transaction, if any.
|
|
167
167
|
*/
|
|
168
168
|
protected modifiedKeys: Set<number>;
|
|
@@ -109,7 +109,7 @@ export class WrappedTransaction {
|
|
|
109
109
|
* Allows us to roll back commits.
|
|
110
110
|
*/
|
|
111
111
|
originalData = new Map();
|
|
112
|
-
/**
|
|
112
|
+
/**
|
|
113
113
|
* List of keys modified in this transaction, if any.
|
|
114
114
|
*/
|
|
115
115
|
modifiedKeys = new Set();
|
|
@@ -154,11 +154,9 @@ export class WrappedTransaction {
|
|
|
154
154
|
async abort() {
|
|
155
155
|
if (this.done)
|
|
156
156
|
return;
|
|
157
|
-
// Rollback old values.
|
|
158
157
|
for (const [id, entries] of this.originalData) {
|
|
159
158
|
if (!this.modifiedKeys.has(id))
|
|
160
159
|
continue;
|
|
161
|
-
// Key didn't exist.
|
|
162
160
|
if (entries.some(ent => !ent.data)) {
|
|
163
161
|
await this.raw.remove(id);
|
|
164
162
|
this.fs._remove(id);
|
|
@@ -173,11 +171,9 @@ export class WrappedTransaction {
|
|
|
173
171
|
abortSync() {
|
|
174
172
|
if (this.done)
|
|
175
173
|
return;
|
|
176
|
-
// Rollback old values.
|
|
177
174
|
for (const [id, entries] of this.originalData) {
|
|
178
175
|
if (!this.modifiedKeys.has(id))
|
|
179
176
|
continue;
|
|
180
|
-
// Key didn't exist.
|
|
181
177
|
if (entries.some(ent => !ent.data)) {
|
|
182
178
|
this.raw.removeSync(id);
|
|
183
179
|
this.fs._remove(id);
|
|
@@ -206,9 +202,7 @@ export class WrappedTransaction {
|
|
|
206
202
|
* on during the transaction.
|
|
207
203
|
*/
|
|
208
204
|
stash(id, data, offset = 0) {
|
|
209
|
-
|
|
210
|
-
this.originalData.set(id, []);
|
|
211
|
-
this.originalData.get(id).push({ data, offset });
|
|
205
|
+
this.originalData.getOrInsertComputed(id, () => []).push({ data, offset });
|
|
212
206
|
}
|
|
213
207
|
/**
|
|
214
208
|
* Marks an id as modified, and stashes its value if it has not been stashed already.
|
package/dist/config.d.ts
CHANGED
|
@@ -72,6 +72,12 @@ export interface Configuration<T extends ConfigMounts> extends SharedConfig {
|
|
|
72
72
|
* @default false
|
|
73
73
|
*/
|
|
74
74
|
disableAccessChecks: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* If true, path resolution always walks each directory in a path, checking search permission on each.
|
|
77
|
+
* This is more correct but can be much slower.
|
|
78
|
+
* @default false
|
|
79
|
+
*/
|
|
80
|
+
resolveFullWalk: boolean;
|
|
75
81
|
/**
|
|
76
82
|
* If true, files will only sync to the file system when closed.
|
|
77
83
|
* This overrides `disableUpdateOnRead`
|
|
@@ -79,6 +85,7 @@ export interface Configuration<T extends ConfigMounts> extends SharedConfig {
|
|
|
79
85
|
* This can increase performance.
|
|
80
86
|
* @experimental
|
|
81
87
|
* @default false
|
|
88
|
+
* @deprecated Replaced by the `Sync` inode flag in v2.0.0
|
|
82
89
|
*/
|
|
83
90
|
onlySyncOnClose: boolean;
|
|
84
91
|
/**
|
package/dist/config.js
CHANGED
|
@@ -6,7 +6,7 @@ import { DeviceFS } from './internal/devices.js';
|
|
|
6
6
|
import { FileSystem } from './internal/filesystem.js';
|
|
7
7
|
import { exists, mkdir, stat } from './node/promises.js';
|
|
8
8
|
import { existsSync, mkdirSync, statSync } from './node/sync.js';
|
|
9
|
-
import {
|
|
9
|
+
import { _setVFSConfig } from './vfs/config.js';
|
|
10
10
|
import { mount, umount } from './vfs/shared.js';
|
|
11
11
|
/**
|
|
12
12
|
* Update the configuration of a file system.
|
|
@@ -209,7 +209,10 @@ export async function configure(configuration) {
|
|
|
209
209
|
uid: configuration.uid || 0,
|
|
210
210
|
gid: configuration.gid || 0,
|
|
211
211
|
}));
|
|
212
|
-
|
|
212
|
+
_setVFSConfig({
|
|
213
|
+
checkAccess: !configuration.disableAccessChecks,
|
|
214
|
+
resolveFullWalk: !!configuration.resolveFullWalk,
|
|
215
|
+
});
|
|
213
216
|
if (configuration.log)
|
|
214
217
|
log.configure(configuration.log);
|
|
215
218
|
if (configuration.mounts) {
|
|
@@ -257,7 +260,10 @@ export function configureSync(configuration) {
|
|
|
257
260
|
uid: configuration.uid || 0,
|
|
258
261
|
gid: configuration.gid || 0,
|
|
259
262
|
}));
|
|
260
|
-
|
|
263
|
+
_setVFSConfig({
|
|
264
|
+
checkAccess: !configuration.disableAccessChecks,
|
|
265
|
+
resolveFullWalk: !!configuration.resolveFullWalk,
|
|
266
|
+
});
|
|
261
267
|
if (configuration.log)
|
|
262
268
|
log.configure(configuration.log);
|
|
263
269
|
if (configuration.mounts) {
|
|
@@ -20,8 +20,7 @@ export interface FSContext {
|
|
|
20
20
|
/** The unique ID of the context */
|
|
21
21
|
readonly id: number;
|
|
22
22
|
/**
|
|
23
|
-
* The absolute root path of the context
|
|
24
|
-
*
|
|
23
|
+
* The absolute root path of the context.
|
|
25
24
|
* Note the parent's root is not considered
|
|
26
25
|
*/
|
|
27
26
|
root: string;
|
package/dist/internal/error.d.ts
CHANGED
|
@@ -25,3 +25,8 @@ export declare function wrap<const FS, const Prop extends keyof FS & string>(fs:
|
|
|
25
25
|
* Wraps an `fs` so that thrown errors aren't empty
|
|
26
26
|
*/
|
|
27
27
|
export declare function withExceptionContext<const FS extends FileSystem>(fs: FS, context: ExceptionExtra): FS;
|
|
28
|
+
/**
|
|
29
|
+
* @internal
|
|
30
|
+
* Returns the file system wrapped by {@link withExceptionContext}, or `fs` if it is not wrapped
|
|
31
|
+
*/
|
|
32
|
+
export declare function withoutExceptionContext<const FS extends FileSystem>(fs: FS): FS;
|
package/dist/internal/error.js
CHANGED
|
@@ -26,6 +26,7 @@ export function wrap(fs, prop, path, dest) {
|
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
+
const kTarget = Symbol('withExceptionContext:target');
|
|
29
30
|
/**
|
|
30
31
|
* @internal
|
|
31
32
|
* Wraps an `fs` so that thrown errors aren't empty
|
|
@@ -33,6 +34,8 @@ export function wrap(fs, prop, path, dest) {
|
|
|
33
34
|
export function withExceptionContext(fs, context) {
|
|
34
35
|
return new Proxy(fs, {
|
|
35
36
|
get(target, prop) {
|
|
37
|
+
if (prop === kTarget)
|
|
38
|
+
return target;
|
|
36
39
|
const value = Reflect.get(target, prop);
|
|
37
40
|
if (typeof value != 'function')
|
|
38
41
|
return value;
|
|
@@ -58,3 +61,10 @@ export function withExceptionContext(fs, context) {
|
|
|
58
61
|
},
|
|
59
62
|
});
|
|
60
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* @internal
|
|
66
|
+
* Returns the file system wrapped by {@link withExceptionContext}, or `fs` if it is not wrapped
|
|
67
|
+
*/
|
|
68
|
+
export function withoutExceptionContext(fs) {
|
|
69
|
+
return fs[kTarget] ?? fs;
|
|
70
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
2
|
-
/* Note: this file is named file_index.ts because Typescript has special behavior regarding index.ts which can't be disabled. */
|
|
3
2
|
import { withErrno } from 'kerium';
|
|
4
3
|
import { sizeof } from 'memium';
|
|
5
4
|
import { isJSON, randomInt } from 'utilium';
|
|
@@ -40,8 +40,8 @@ export declare abstract class IndexFS extends FileSystem {
|
|
|
40
40
|
protected _mkdirSync?(path: string, options: CreationOptions): void;
|
|
41
41
|
mkdir(path: string, options: CreationOptions): Promise<Inode>;
|
|
42
42
|
mkdirSync(path: string, options: CreationOptions): Inode;
|
|
43
|
-
link(
|
|
44
|
-
linkSync(
|
|
43
|
+
link(): Promise<void>;
|
|
44
|
+
linkSync(): void;
|
|
45
45
|
readdir(path: string): Promise<string[]>;
|
|
46
46
|
readdirSync(path: string): string[];
|
|
47
47
|
sync(): Promise<void>;
|
|
@@ -200,10 +200,10 @@ export class IndexFS extends FileSystem {
|
|
|
200
200
|
this._mkdirSync?.(path, options);
|
|
201
201
|
return inode;
|
|
202
202
|
}
|
|
203
|
-
link(
|
|
203
|
+
link() {
|
|
204
204
|
throw withErrno('ENOSYS');
|
|
205
205
|
}
|
|
206
|
-
linkSync(
|
|
206
|
+
linkSync() {
|
|
207
207
|
throw withErrno('ENOSYS');
|
|
208
208
|
}
|
|
209
209
|
async readdir(path) {
|