@zenfs/core 0.16.3 → 0.17.0
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.d.ts +3 -4
- package/dist/backends/fetch.d.ts +8 -3
- package/dist/backends/fetch.js +3 -2
- package/dist/backends/{index/fs.d.ts → file_index.d.ts} +49 -10
- package/dist/backends/{index/fs.js → file_index.js} +84 -5
- package/dist/backends/memory.d.ts +6 -1
- package/dist/backends/memory.js +2 -1
- package/dist/backends/overlay.d.ts +16 -16
- package/dist/backends/overlay.js +59 -82
- package/dist/backends/port/fs.d.ts +6 -2
- package/dist/backends/port/fs.js +4 -2
- package/dist/backends/store/fs.js +484 -304
- package/dist/backends/store/simple.js +5 -1
- package/dist/backends/store/store.d.ts +4 -1
- package/dist/backends/store/store.js +9 -5
- package/dist/browser.min.js +4 -4
- package/dist/browser.min.js.map +4 -4
- package/dist/config.d.ts +3 -3
- package/dist/emulation/async.d.ts +0 -3
- package/dist/emulation/async.js +6 -2
- package/dist/emulation/dir.d.ts +4 -0
- package/dist/emulation/dir.js +8 -6
- package/dist/emulation/promises.d.ts +1 -3
- package/dist/emulation/promises.js +26 -3
- package/dist/emulation/sync.js +1 -2
- package/dist/emulation/watchers.d.ts +9 -4
- package/dist/emulation/watchers.js +7 -0
- package/dist/file.d.ts +17 -1
- package/dist/file.js +86 -1
- package/dist/filesystem.d.ts +0 -63
- package/dist/filesystem.js +0 -311
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/mixins/async.d.ts +39 -0
- package/dist/mixins/async.js +216 -0
- package/dist/mixins/mutexed.d.ts +33 -0
- package/dist/mixins/mutexed.js +465 -0
- package/dist/mixins/readonly.d.ts +25 -0
- package/dist/mixins/readonly.js +57 -0
- package/dist/mixins/shared.d.ts +12 -0
- package/dist/mixins/shared.js +4 -0
- package/dist/mixins/sync.d.ts +6 -0
- package/dist/mixins/sync.js +43 -0
- package/package.json +1 -1
- package/src/backends/backend.ts +3 -4
- package/src/backends/fetch.ts +7 -3
- package/src/backends/{index/fs.ts → file_index.ts} +106 -8
- package/src/backends/memory.ts +5 -1
- package/src/backends/overlay.ts +64 -90
- package/src/backends/port/fs.ts +7 -2
- package/src/backends/{index/readme.md → readme.md} +1 -1
- package/src/backends/store/fs.ts +97 -155
- package/src/backends/store/simple.ts +5 -1
- package/src/backends/store/store.ts +10 -5
- package/src/config.ts +3 -1
- package/src/emulation/async.ts +15 -6
- package/src/emulation/dir.ts +19 -16
- package/src/emulation/promises.ts +30 -8
- package/src/emulation/sync.ts +2 -3
- package/src/emulation/watchers.ts +10 -4
- package/src/file.ts +94 -1
- package/src/filesystem.ts +3 -366
- package/src/index.ts +1 -2
- package/src/mixins/async.ts +211 -0
- package/src/mixins/mutexed.ts +245 -0
- package/src/mixins/readonly.ts +97 -0
- package/src/mixins/shared.ts +20 -0
- package/src/mixins/sync.ts +59 -0
- package/dist/backends/index/index.d.ts +0 -43
- package/dist/backends/index/index.js +0 -83
- package/dist/backends/locked.d.ts +0 -92
- package/dist/backends/locked.js +0 -487
- package/src/backends/index/index.ts +0 -104
- package/src/backends/locked.ts +0 -264
package/src/backends/store/fs.ts
CHANGED
|
@@ -65,8 +65,8 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
65
65
|
* @todo Make rename compatible with the cache.
|
|
66
66
|
*/
|
|
67
67
|
public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
await using tx = this.store.transaction();
|
|
69
|
+
const oldParent = dirname(oldPath),
|
|
70
70
|
oldName = basename(oldPath),
|
|
71
71
|
newParent = dirname(newPath),
|
|
72
72
|
newName = basename(newPath),
|
|
@@ -108,13 +108,8 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
108
108
|
// If it's a file, delete it.
|
|
109
109
|
const newNameNode = await this.getINode(tx, newDirList[newName], newPath);
|
|
110
110
|
if (newNameNode.toStats().isFile()) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
await tx.remove(newDirList[newName]);
|
|
114
|
-
} catch (e) {
|
|
115
|
-
await tx.abort();
|
|
116
|
-
throw e;
|
|
117
|
-
}
|
|
111
|
+
await tx.remove(newNameNode.ino);
|
|
112
|
+
await tx.remove(newDirList[newName]);
|
|
118
113
|
} else {
|
|
119
114
|
// If it's a directory, throw a permissions error.
|
|
120
115
|
throw ErrnoError.With('EPERM', newPath, 'rename');
|
|
@@ -122,20 +117,14 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
122
117
|
}
|
|
123
118
|
newDirList[newName] = nodeId;
|
|
124
119
|
// Commit the two changed directory listings.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
await tx.set(newDirNode.ino, encodeDirListing(newDirList));
|
|
128
|
-
} catch (e) {
|
|
129
|
-
await tx.abort();
|
|
130
|
-
throw e;
|
|
131
|
-
}
|
|
132
|
-
|
|
120
|
+
await tx.set(oldDirNode.ino, encodeDirListing(oldDirList));
|
|
121
|
+
await tx.set(newDirNode.ino, encodeDirListing(newDirList));
|
|
133
122
|
await tx.commit();
|
|
134
123
|
}
|
|
135
124
|
|
|
136
125
|
public renameSync(oldPath: string, newPath: string, cred: Cred): void {
|
|
137
|
-
|
|
138
|
-
|
|
126
|
+
using tx = this.store.transaction();
|
|
127
|
+
const oldParent = dirname(oldPath),
|
|
139
128
|
oldName = basename(oldPath),
|
|
140
129
|
newParent = dirname(newPath),
|
|
141
130
|
newName = basename(newPath),
|
|
@@ -177,13 +166,8 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
177
166
|
// If it's a file, delete it.
|
|
178
167
|
const newNameNode = this.getINodeSync(tx, newDirList[newName], newPath);
|
|
179
168
|
if (newNameNode.toStats().isFile()) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
tx.removeSync(newDirList[newName]);
|
|
183
|
-
} catch (e) {
|
|
184
|
-
tx.abortSync();
|
|
185
|
-
throw e;
|
|
186
|
-
}
|
|
169
|
+
tx.removeSync(newNameNode.ino);
|
|
170
|
+
tx.removeSync(newDirList[newName]);
|
|
187
171
|
} else {
|
|
188
172
|
// If it's a directory, throw a permissions error.
|
|
189
173
|
throw ErrnoError.With('EPERM', newPath, 'rename');
|
|
@@ -192,19 +176,13 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
192
176
|
newDirList[newName] = ino;
|
|
193
177
|
|
|
194
178
|
// Commit the two changed directory listings.
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
tx.setSync(newDirNode.ino, encodeDirListing(newDirList));
|
|
198
|
-
} catch (e) {
|
|
199
|
-
tx.abortSync();
|
|
200
|
-
throw e;
|
|
201
|
-
}
|
|
202
|
-
|
|
179
|
+
tx.setSync(oldDirNode.ino, encodeDirListing(oldDirList));
|
|
180
|
+
tx.setSync(newDirNode.ino, encodeDirListing(newDirList));
|
|
203
181
|
tx.commitSync();
|
|
204
182
|
}
|
|
205
183
|
|
|
206
184
|
public async stat(path: string, cred: Cred): Promise<Stats> {
|
|
207
|
-
|
|
185
|
+
await using tx = this.store.transaction();
|
|
208
186
|
const inode = await this.findINode(tx, path);
|
|
209
187
|
if (!inode) {
|
|
210
188
|
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
@@ -217,8 +195,9 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
217
195
|
}
|
|
218
196
|
|
|
219
197
|
public statSync(path: string, cred: Cred): Stats {
|
|
198
|
+
using tx = this.store.transaction();
|
|
220
199
|
// Get the inode to the item, convert it into a Stats object.
|
|
221
|
-
const stats = this.findINodeSync(
|
|
200
|
+
const stats = this.findINodeSync(tx, path).toStats();
|
|
222
201
|
if (!stats.hasAccess(R_OK, cred)) {
|
|
223
202
|
throw ErrnoError.With('EACCES', path, 'stat');
|
|
224
203
|
}
|
|
@@ -226,9 +205,8 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
226
205
|
}
|
|
227
206
|
|
|
228
207
|
public async createFile(path: string, flag: string, mode: number, cred: Cred): Promise<PreloadFile<this>> {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
return new PreloadFile(this, path, flag, file.toStats(), data);
|
|
208
|
+
const node = await this.commitNew(path, S_IFREG, mode, cred, new Uint8Array(0));
|
|
209
|
+
return new PreloadFile(this, path, flag, node.toStats(), new Uint8Array(0));
|
|
232
210
|
}
|
|
233
211
|
|
|
234
212
|
public createFileSync(path: string, flag: string, mode: number, cred: Cred): PreloadFile<this> {
|
|
@@ -237,8 +215,8 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
237
215
|
}
|
|
238
216
|
|
|
239
217
|
public async openFile(path: string, flag: string, cred: Cred): Promise<PreloadFile<this>> {
|
|
240
|
-
|
|
241
|
-
|
|
218
|
+
await using tx = this.store.transaction();
|
|
219
|
+
const node = await this.findINode(tx, path),
|
|
242
220
|
data = await tx.get(node.ino);
|
|
243
221
|
if (!node.toStats().hasAccess(flagToMode(flag), cred)) {
|
|
244
222
|
throw ErrnoError.With('EACCES', path, 'openFile');
|
|
@@ -250,8 +228,8 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
250
228
|
}
|
|
251
229
|
|
|
252
230
|
public openFileSync(path: string, flag: string, cred: Cred): PreloadFile<this> {
|
|
253
|
-
|
|
254
|
-
|
|
231
|
+
using tx = this.store.transaction();
|
|
232
|
+
const node = this.findINodeSync(tx, path),
|
|
255
233
|
data = tx.getSync(node.ino);
|
|
256
234
|
if (!node.toStats().hasAccess(flagToMode(flag), cred)) {
|
|
257
235
|
throw ErrnoError.With('EACCES', path, 'openFile');
|
|
@@ -289,9 +267,7 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
289
267
|
}
|
|
290
268
|
|
|
291
269
|
public async mkdir(path: string, mode: number, cred: Cred): Promise<void> {
|
|
292
|
-
|
|
293
|
-
data = encode('{}');
|
|
294
|
-
await this.commitNew(tx, path, S_IFDIR, mode, cred, data);
|
|
270
|
+
await this.commitNew(path, S_IFDIR, mode, cred, encode('{}'));
|
|
295
271
|
}
|
|
296
272
|
|
|
297
273
|
public mkdirSync(path: string, mode: number, cred: Cred): void {
|
|
@@ -299,7 +275,7 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
299
275
|
}
|
|
300
276
|
|
|
301
277
|
public async readdir(path: string, cred: Cred): Promise<string[]> {
|
|
302
|
-
|
|
278
|
+
await using tx = this.store.transaction();
|
|
303
279
|
const node = await this.findINode(tx, path);
|
|
304
280
|
if (!node.toStats().hasAccess(R_OK, cred)) {
|
|
305
281
|
throw ErrnoError.With('EACCES', path, 'readdur');
|
|
@@ -308,7 +284,7 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
308
284
|
}
|
|
309
285
|
|
|
310
286
|
public readdirSync(path: string, cred: Cred): string[] {
|
|
311
|
-
|
|
287
|
+
using tx = this.store.transaction();
|
|
312
288
|
const node = this.findINodeSync(tx, path);
|
|
313
289
|
if (!node.toStats().hasAccess(R_OK, cred)) {
|
|
314
290
|
throw ErrnoError.With('EACCES', path, 'readdir');
|
|
@@ -321,23 +297,19 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
321
297
|
* @todo Ensure mtime updates properly, and use that to determine if a data update is required.
|
|
322
298
|
*/
|
|
323
299
|
public async sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
300
|
+
await using tx = this.store.transaction();
|
|
301
|
+
// We use _findInode because we actually need the INode id.
|
|
302
|
+
const fileInodeId = await this._findINode(tx, dirname(path), basename(path)),
|
|
327
303
|
fileInode = await this.getINode(tx, fileInodeId, path),
|
|
328
304
|
inodeChanged = fileInode.update(stats);
|
|
329
305
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
await tx.set(fileInodeId, fileInode.data);
|
|
336
|
-
}
|
|
337
|
-
} catch (e) {
|
|
338
|
-
await tx.abort();
|
|
339
|
-
throw e;
|
|
306
|
+
// Sync data.
|
|
307
|
+
await tx.set(fileInode.ino, data);
|
|
308
|
+
// Sync metadata.
|
|
309
|
+
if (inodeChanged) {
|
|
310
|
+
await tx.set(fileInodeId, fileInode.data);
|
|
340
311
|
}
|
|
312
|
+
|
|
341
313
|
await tx.commit();
|
|
342
314
|
}
|
|
343
315
|
|
|
@@ -346,29 +318,25 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
346
318
|
* @todo Ensure mtime updates properly, and use that to determine if a data update is required.
|
|
347
319
|
*/
|
|
348
320
|
public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
321
|
+
using tx = this.store.transaction();
|
|
322
|
+
// We use _findInode because we actually need the INode id.
|
|
323
|
+
const fileInodeId = this._findINodeSync(tx, dirname(path), basename(path)),
|
|
352
324
|
fileInode = this.getINodeSync(tx, fileInodeId, path),
|
|
353
325
|
inodeChanged = fileInode.update(stats);
|
|
354
326
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
tx.setSync(fileInodeId, fileInode.data);
|
|
361
|
-
}
|
|
362
|
-
} catch (e) {
|
|
363
|
-
tx.abortSync();
|
|
364
|
-
throw e;
|
|
327
|
+
// Sync data.
|
|
328
|
+
tx.setSync(fileInode.ino, data);
|
|
329
|
+
// Sync metadata.
|
|
330
|
+
if (inodeChanged) {
|
|
331
|
+
tx.setSync(fileInodeId, fileInode.data);
|
|
365
332
|
}
|
|
333
|
+
|
|
366
334
|
tx.commitSync();
|
|
367
335
|
}
|
|
368
336
|
|
|
369
337
|
public async link(existing: string, newpath: string, cred: Cred): Promise<void> {
|
|
370
|
-
|
|
371
|
-
|
|
338
|
+
await using tx = this.store.transaction();
|
|
339
|
+
const existingDir: string = dirname(existing),
|
|
372
340
|
existingDirNode = await this.findINode(tx, existingDir);
|
|
373
341
|
|
|
374
342
|
if (!existingDirNode.toStats().hasAccess(R_OK, cred)) {
|
|
@@ -392,19 +360,15 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
392
360
|
|
|
393
361
|
node.nlink++;
|
|
394
362
|
newListing[basename(newpath)] = ino;
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
} catch (e) {
|
|
399
|
-
tx.abortSync();
|
|
400
|
-
throw e;
|
|
401
|
-
}
|
|
363
|
+
|
|
364
|
+
tx.setSync(ino, node.data);
|
|
365
|
+
tx.setSync(newDirNode.ino, encodeDirListing(newListing));
|
|
402
366
|
tx.commitSync();
|
|
403
367
|
}
|
|
404
368
|
|
|
405
369
|
public linkSync(existing: string, newpath: string, cred: Cred): void {
|
|
406
|
-
|
|
407
|
-
|
|
370
|
+
using tx = this.store.transaction();
|
|
371
|
+
const existingDir: string = dirname(existing),
|
|
408
372
|
existingDirNode = this.findINodeSync(tx, existingDir);
|
|
409
373
|
|
|
410
374
|
if (!existingDirNode.toStats().hasAccess(R_OK, cred)) {
|
|
@@ -427,13 +391,9 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
427
391
|
}
|
|
428
392
|
node.nlink++;
|
|
429
393
|
newListing[basename(newpath)] = ino;
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
} catch (e) {
|
|
434
|
-
tx.abortSync();
|
|
435
|
-
throw e;
|
|
436
|
-
}
|
|
394
|
+
|
|
395
|
+
tx.setSync(ino, node.data);
|
|
396
|
+
tx.setSync(newDirNode.ino, encodeDirListing(newListing));
|
|
437
397
|
tx.commitSync();
|
|
438
398
|
}
|
|
439
399
|
|
|
@@ -441,7 +401,7 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
441
401
|
* Checks if the root directory exists. Creates it if it doesn't.
|
|
442
402
|
*/
|
|
443
403
|
public async checkRoot(): Promise<void> {
|
|
444
|
-
|
|
404
|
+
await using tx = this.store.transaction();
|
|
445
405
|
if (await tx.get(rootIno)) {
|
|
446
406
|
return;
|
|
447
407
|
}
|
|
@@ -458,7 +418,7 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
458
418
|
* Checks if the root directory exists. Creates it if it doesn't.
|
|
459
419
|
*/
|
|
460
420
|
public checkRootSync(): void {
|
|
461
|
-
|
|
421
|
+
using tx = this.store.transaction();
|
|
462
422
|
if (tx.getSync(rootIno)) {
|
|
463
423
|
return;
|
|
464
424
|
}
|
|
@@ -656,7 +616,8 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
656
616
|
* @param cred The UID/GID to create the file with
|
|
657
617
|
* @param data The data to store at the file's data node.
|
|
658
618
|
*/
|
|
659
|
-
private async commitNew(
|
|
619
|
+
private async commitNew(path: string, type: FileType, mode: number, cred: Cred, data: Uint8Array): Promise<Inode> {
|
|
620
|
+
await using tx = this.store.transaction();
|
|
660
621
|
const parentPath = dirname(path),
|
|
661
622
|
parent = await this.findINode(tx, parentPath);
|
|
662
623
|
|
|
@@ -682,25 +643,20 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
682
643
|
await tx.abort();
|
|
683
644
|
throw ErrnoError.With('EEXIST', path, 'commitNewFile');
|
|
684
645
|
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
return inode;
|
|
700
|
-
} catch (e) {
|
|
701
|
-
await tx.abort();
|
|
702
|
-
throw e;
|
|
703
|
-
}
|
|
646
|
+
|
|
647
|
+
// Commit data.
|
|
648
|
+
const inode = new Inode();
|
|
649
|
+
inode.ino = await this.addNew(tx, data, path);
|
|
650
|
+
inode.mode = mode | type;
|
|
651
|
+
inode.uid = cred.uid;
|
|
652
|
+
inode.gid = cred.gid;
|
|
653
|
+
inode.size = data.length;
|
|
654
|
+
|
|
655
|
+
// Update and commit parent directory listing.
|
|
656
|
+
listing[fname] = await this.addNew(tx, inode.data, path);
|
|
657
|
+
await tx.set(parent.ino, encodeDirListing(listing));
|
|
658
|
+
await tx.commit();
|
|
659
|
+
return inode;
|
|
704
660
|
}
|
|
705
661
|
|
|
706
662
|
/**
|
|
@@ -713,8 +669,8 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
713
669
|
* @return The Inode for the new file.
|
|
714
670
|
*/
|
|
715
671
|
protected commitNewSync(path: string, type: FileType, mode: number, cred: Cred, data: Uint8Array = new Uint8Array()): Inode {
|
|
716
|
-
|
|
717
|
-
|
|
672
|
+
using tx = this.store.transaction();
|
|
673
|
+
const parentPath = dirname(path),
|
|
718
674
|
parent = this.findINodeSync(tx, parentPath);
|
|
719
675
|
|
|
720
676
|
//Check that the creater has correct access
|
|
@@ -739,21 +695,16 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
739
695
|
throw ErrnoError.With('EEXIST', path, 'commitNewFile');
|
|
740
696
|
}
|
|
741
697
|
|
|
698
|
+
// Commit data.
|
|
742
699
|
const node = new Inode();
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
listing[fname] = this.addNewSync(tx, node.data, path);
|
|
752
|
-
tx.setSync(parent.ino, encodeDirListing(listing));
|
|
753
|
-
} catch (e) {
|
|
754
|
-
tx.abortSync();
|
|
755
|
-
throw e;
|
|
756
|
-
}
|
|
700
|
+
node.ino = this.addNewSync(tx, data, path);
|
|
701
|
+
node.size = data.length;
|
|
702
|
+
node.mode = mode | type;
|
|
703
|
+
node.uid = cred.uid;
|
|
704
|
+
node.gid = cred.gid;
|
|
705
|
+
// Update and commit parent directory listing.
|
|
706
|
+
listing[fname] = this.addNewSync(tx, node.data, path);
|
|
707
|
+
tx.setSync(parent.ino, encodeDirListing(listing));
|
|
757
708
|
tx.commitSync();
|
|
758
709
|
return node;
|
|
759
710
|
}
|
|
@@ -765,8 +716,8 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
765
716
|
* @todo Update mtime.
|
|
766
717
|
*/
|
|
767
718
|
private async remove(path: string, isDir: boolean, cred: Cred): Promise<void> {
|
|
768
|
-
|
|
769
|
-
|
|
719
|
+
await using tx = this.store.transaction();
|
|
720
|
+
const parent: string = dirname(path),
|
|
770
721
|
parentNode = await this.findINode(tx, parent),
|
|
771
722
|
listing = await this.getDirListing(tx, parentNode, parent),
|
|
772
723
|
fileName: string = basename(path);
|
|
@@ -795,17 +746,12 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
795
746
|
throw ErrnoError.With('ENOTDIR', path, 'removeEntry');
|
|
796
747
|
}
|
|
797
748
|
|
|
798
|
-
|
|
799
|
-
await tx.set(parentNode.ino, encodeDirListing(listing));
|
|
749
|
+
await tx.set(parentNode.ino, encodeDirListing(listing));
|
|
800
750
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
}
|
|
806
|
-
} catch (e) {
|
|
807
|
-
await tx.abort();
|
|
808
|
-
throw e;
|
|
751
|
+
if (--fileNode.nlink < 1) {
|
|
752
|
+
// remove file
|
|
753
|
+
await tx.remove(fileNode.ino);
|
|
754
|
+
await tx.remove(fileIno);
|
|
809
755
|
}
|
|
810
756
|
|
|
811
757
|
// Success.
|
|
@@ -819,8 +765,8 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
819
765
|
* @todo Update mtime.
|
|
820
766
|
*/
|
|
821
767
|
protected removeSync(path: string, isDir: boolean, cred: Cred): void {
|
|
822
|
-
|
|
823
|
-
|
|
768
|
+
using tx = this.store.transaction();
|
|
769
|
+
const parent: string = dirname(path),
|
|
824
770
|
parentNode = this.findINodeSync(tx, parent),
|
|
825
771
|
listing = this.getDirListingSync(tx, parentNode, parent),
|
|
826
772
|
fileName: string = basename(path),
|
|
@@ -848,19 +794,15 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
848
794
|
throw ErrnoError.With('ENOTDIR', path, 'removeEntry');
|
|
849
795
|
}
|
|
850
796
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
tx.setSync(parentNode.ino, encodeDirListing(listing));
|
|
797
|
+
// Update directory listing.
|
|
798
|
+
tx.setSync(parentNode.ino, encodeDirListing(listing));
|
|
854
799
|
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
}
|
|
860
|
-
} catch (e) {
|
|
861
|
-
tx.abortSync();
|
|
862
|
-
throw e;
|
|
800
|
+
if (--fileNode.nlink < 1) {
|
|
801
|
+
// remove file
|
|
802
|
+
tx.removeSync(fileNode.ino);
|
|
803
|
+
tx.removeSync(fileIno);
|
|
863
804
|
}
|
|
805
|
+
|
|
864
806
|
// Success.
|
|
865
807
|
tx.commitSync();
|
|
866
808
|
}
|
|
@@ -103,10 +103,13 @@ export class SimpleTransaction extends SyncTransaction<SimpleSyncStore> {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
public commitSync(): void {
|
|
106
|
-
|
|
106
|
+
this.done = true;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
public abortSync(): void {
|
|
110
|
+
if (!this.done) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
110
113
|
// Rollback old values.
|
|
111
114
|
for (const key of this.modifiedKeys) {
|
|
112
115
|
const value = this.originalData.get(key);
|
|
@@ -118,6 +121,7 @@ export class SimpleTransaction extends SyncTransaction<SimpleSyncStore> {
|
|
|
118
121
|
this.store.set(key, value);
|
|
119
122
|
}
|
|
120
123
|
}
|
|
124
|
+
this.done = true;
|
|
121
125
|
}
|
|
122
126
|
|
|
123
127
|
/**
|
|
@@ -38,7 +38,10 @@ export interface Store {
|
|
|
38
38
|
export abstract class Transaction<T extends Store = Store> {
|
|
39
39
|
constructor(protected store: T) {}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Whether the transaction was commited or aborted
|
|
43
|
+
*/
|
|
44
|
+
protected done: boolean = false;
|
|
42
45
|
|
|
43
46
|
/**
|
|
44
47
|
* Retrieves the data at the given key.
|
|
@@ -92,11 +95,11 @@ export abstract class Transaction<T extends Store = Store> {
|
|
|
92
95
|
public abstract commit(): Promise<void>;
|
|
93
96
|
|
|
94
97
|
public async [Symbol.asyncDispose]() {
|
|
95
|
-
if (this.
|
|
98
|
+
if (this.done) {
|
|
96
99
|
return;
|
|
97
100
|
}
|
|
98
101
|
|
|
99
|
-
await this.
|
|
102
|
+
await this.abort();
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
/**
|
|
@@ -105,11 +108,11 @@ export abstract class Transaction<T extends Store = Store> {
|
|
|
105
108
|
public abstract commitSync(): void;
|
|
106
109
|
|
|
107
110
|
public [Symbol.dispose]() {
|
|
108
|
-
if (this.
|
|
111
|
+
if (this.done) {
|
|
109
112
|
return;
|
|
110
113
|
}
|
|
111
114
|
|
|
112
|
-
this.
|
|
115
|
+
this.abortSync();
|
|
113
116
|
}
|
|
114
117
|
|
|
115
118
|
/**
|
|
@@ -127,6 +130,7 @@ export abstract class Transaction<T extends Store = Store> {
|
|
|
127
130
|
* Transaction that implements asynchronous operations with synchronous ones
|
|
128
131
|
*/
|
|
129
132
|
export abstract class SyncTransaction<T extends Store = Store> extends Transaction<T> {
|
|
133
|
+
/* eslint-disable @typescript-eslint/require-await */
|
|
130
134
|
public async get(ino: Ino): Promise<Uint8Array> {
|
|
131
135
|
return this.getSync(ino);
|
|
132
136
|
}
|
|
@@ -142,6 +146,7 @@ export abstract class SyncTransaction<T extends Store = Store> extends Transacti
|
|
|
142
146
|
public async abort(): Promise<void> {
|
|
143
147
|
return this.abortSync();
|
|
144
148
|
}
|
|
149
|
+
/* eslint-enable @typescript-eslint/require-await */
|
|
145
150
|
}
|
|
146
151
|
|
|
147
152
|
/**
|
package/src/config.ts
CHANGED
|
@@ -64,7 +64,9 @@ export async function resolveMountConfig<T extends Backend>(config: MountConfigu
|
|
|
64
64
|
return mount;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
export
|
|
67
|
+
export interface ConfigMounts {
|
|
68
|
+
[K: AbsolutePath]: Backend;
|
|
69
|
+
}
|
|
68
70
|
|
|
69
71
|
/**
|
|
70
72
|
* Configuration
|
package/src/emulation/async.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { type Dir } from './dir.js';
|
|
|
10
10
|
import * as promises from './promises.js';
|
|
11
11
|
import { fd2file } from './shared.js';
|
|
12
12
|
import { ReadStream, WriteStream } from './streams.js';
|
|
13
|
+
import { FSWatcher } from './watchers.js';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Asynchronous rename. No arguments other than a possible exception are given
|
|
@@ -675,7 +676,11 @@ access satisfies Omit<typeof fs.access, '__promisify__'>;
|
|
|
675
676
|
*/
|
|
676
677
|
export function watchFile(path: fs.PathLike, listener: (curr: Stats, prev: Stats) => void): void;
|
|
677
678
|
export function watchFile(path: fs.PathLike, options: { persistent?: boolean; interval?: number }, listener: (curr: Stats, prev: Stats) => void): void;
|
|
678
|
-
export function watchFile(
|
|
679
|
+
export function watchFile(
|
|
680
|
+
path: fs.PathLike,
|
|
681
|
+
optsListener: { persistent?: boolean; interval?: number } | ((curr: Stats, prev: Stats) => void),
|
|
682
|
+
listener: (curr: Stats, prev: Stats) => void = nop
|
|
683
|
+
): void {
|
|
679
684
|
throw ErrnoError.With('ENOSYS', path.toString(), 'watchFile');
|
|
680
685
|
}
|
|
681
686
|
watchFile satisfies Omit<typeof fs.watchFile, '__promisify__'>;
|
|
@@ -688,13 +693,17 @@ export function unwatchFile(path: fs.PathLike, listener: (curr: Stats, prev: Sta
|
|
|
688
693
|
}
|
|
689
694
|
unwatchFile satisfies Omit<typeof fs.unwatchFile, '__promisify__'>;
|
|
690
695
|
|
|
691
|
-
/**
|
|
692
|
-
* @todo Implement
|
|
693
|
-
*/
|
|
694
696
|
export function watch(path: fs.PathLike, listener?: (event: string, filename: string) => any): fs.FSWatcher;
|
|
695
697
|
export function watch(path: fs.PathLike, options: { persistent?: boolean }, listener?: (event: string, filename: string) => any): fs.FSWatcher;
|
|
696
|
-
export function watch(
|
|
697
|
-
|
|
698
|
+
export function watch(
|
|
699
|
+
path: fs.PathLike,
|
|
700
|
+
options?: fs.WatchOptions | ((event: string, filename: string) => any),
|
|
701
|
+
listener?: (event: string, filename: string) => any
|
|
702
|
+
): fs.FSWatcher {
|
|
703
|
+
const watcher = new FSWatcher<string>(typeof options == 'object' ? options : {});
|
|
704
|
+
listener = typeof options == 'function' ? options : listener;
|
|
705
|
+
watcher.on('change', listener || nop);
|
|
706
|
+
return watcher;
|
|
698
707
|
}
|
|
699
708
|
watch satisfies Omit<typeof fs.watch, '__promisify__'>;
|
|
700
709
|
|
package/src/emulation/dir.ts
CHANGED
|
@@ -57,15 +57,22 @@ export class Dir implements _Dir {
|
|
|
57
57
|
|
|
58
58
|
protected _entries: Dirent[] = [];
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
/**
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
63
|
+
public async _loadEntries() {
|
|
64
|
+
this._entries ??= await readdir(this.path, { withFileTypes: true });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public constructor(public readonly path: string) {}
|
|
61
68
|
|
|
62
69
|
/**
|
|
63
70
|
* Asynchronously close the directory's underlying resource handle.
|
|
64
71
|
* Subsequent reads will result in errors.
|
|
65
72
|
*/
|
|
66
|
-
close(): Promise<void>;
|
|
67
|
-
close(cb: Callback): void;
|
|
68
|
-
close(cb?: Callback): void | Promise<void> {
|
|
73
|
+
public close(): Promise<void>;
|
|
74
|
+
public close(cb: Callback): void;
|
|
75
|
+
public close(cb?: Callback): void | Promise<void> {
|
|
69
76
|
this.closed = true;
|
|
70
77
|
if (!cb) {
|
|
71
78
|
return Promise.resolve();
|
|
@@ -77,14 +84,12 @@ export class Dir implements _Dir {
|
|
|
77
84
|
* Synchronously close the directory's underlying resource handle.
|
|
78
85
|
* Subsequent reads will result in errors.
|
|
79
86
|
*/
|
|
80
|
-
closeSync(): void {
|
|
87
|
+
public closeSync(): void {
|
|
81
88
|
this.closed = true;
|
|
82
89
|
}
|
|
83
90
|
|
|
84
91
|
protected async _read(): Promise<Dirent | null> {
|
|
85
|
-
|
|
86
|
-
this._entries = await readdir(this.path, { withFileTypes: true });
|
|
87
|
-
}
|
|
92
|
+
await this._loadEntries();
|
|
88
93
|
if (!this._entries.length) {
|
|
89
94
|
return null;
|
|
90
95
|
}
|
|
@@ -96,9 +101,9 @@ export class Dir implements _Dir {
|
|
|
96
101
|
* After the read is completed, a value is returned that will be resolved with an `Dirent`, or `null` if there are no more directory entries to read.
|
|
97
102
|
* Directory entries returned by this function are in no particular order as provided by the operating system's underlying directory mechanisms.
|
|
98
103
|
*/
|
|
99
|
-
read(): Promise<Dirent | null>;
|
|
100
|
-
read(cb: Callback<[Dirent | null]>): void;
|
|
101
|
-
read(cb?: Callback<[Dirent | null]>): void | Promise<Dirent | null> {
|
|
104
|
+
public read(): Promise<Dirent | null>;
|
|
105
|
+
public read(cb: Callback<[Dirent | null]>): void;
|
|
106
|
+
public read(cb?: Callback<[Dirent | null]>): void | Promise<Dirent | null> {
|
|
102
107
|
if (!cb) {
|
|
103
108
|
return this._read();
|
|
104
109
|
}
|
|
@@ -111,10 +116,8 @@ export class Dir implements _Dir {
|
|
|
111
116
|
* If there are no more directory entries to read, null will be returned.
|
|
112
117
|
* Directory entries returned by this function are in no particular order as provided by the operating system's underlying directory mechanisms.
|
|
113
118
|
*/
|
|
114
|
-
readSync(): Dirent | null {
|
|
115
|
-
|
|
116
|
-
this._entries = readdirSync(this.path, { withFileTypes: true });
|
|
117
|
-
}
|
|
119
|
+
public readSync(): Dirent | null {
|
|
120
|
+
this._entries ??= readdirSync(this.path, { withFileTypes: true });
|
|
118
121
|
if (!this._entries.length) {
|
|
119
122
|
return null;
|
|
120
123
|
}
|
|
@@ -124,7 +127,7 @@ export class Dir implements _Dir {
|
|
|
124
127
|
/**
|
|
125
128
|
* Asynchronously iterates over the directory via `readdir(3)` until all entries have been read.
|
|
126
129
|
*/
|
|
127
|
-
[Symbol.asyncIterator](): AsyncIterableIterator<Dirent> {
|
|
130
|
+
public [Symbol.asyncIterator](): AsyncIterableIterator<Dirent> {
|
|
128
131
|
const _this = this;
|
|
129
132
|
|
|
130
133
|
return {
|