@zenfs/core 1.0.8 → 1.0.10
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/overlay.d.ts +4 -4
- package/dist/backends/overlay.js +119 -50
- package/dist/mixins/mutexed.d.ts +3 -3
- package/dist/mixins/mutexed.js +3 -3
- package/package.json +1 -1
- package/src/backends/overlay.ts +41 -34
- package/src/mixins/mutexed.ts +5 -5
|
@@ -79,8 +79,8 @@ export declare class UnmutexedOverlayFS extends FileSystem {
|
|
|
79
79
|
* - Ensures p is on writable before proceeding. Throws an error if it doesn't exist.
|
|
80
80
|
* - Calls f to perform operation on writable.
|
|
81
81
|
*/
|
|
82
|
-
private
|
|
83
|
-
private
|
|
82
|
+
private copyForWriteSync;
|
|
83
|
+
private copyForWrite;
|
|
84
84
|
/**
|
|
85
85
|
* Copy from readable to writable storage.
|
|
86
86
|
* PRECONDITION: File does not exist on writable storage.
|
|
@@ -89,8 +89,8 @@ export declare class UnmutexedOverlayFS extends FileSystem {
|
|
|
89
89
|
private copyToWritable;
|
|
90
90
|
}
|
|
91
91
|
declare const OverlayFS_base: {
|
|
92
|
-
new (): import("../mixins/mutexed.js").
|
|
93
|
-
} & (new (args_0: OverlayOptions) => import("../mixins/mutexed.js").
|
|
92
|
+
new (): import("../mixins/mutexed.js")._MutexedFS<UnmutexedOverlayFS>;
|
|
93
|
+
} & (new (args_0: OverlayOptions) => import("../mixins/mutexed.js")._MutexedFS<UnmutexedOverlayFS>);
|
|
94
94
|
/**
|
|
95
95
|
* OverlayFS makes a read-only filesystem writable by storing writes on a second,
|
|
96
96
|
* writable file system. Deletes are persisted via metadata stored on the writable
|
package/dist/backends/overlay.js
CHANGED
|
@@ -1,3 +1,48 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
}
|
|
13
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
14
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
15
|
+
}
|
|
16
|
+
else if (async) {
|
|
17
|
+
env.stack.push({ async: true });
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
};
|
|
21
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
22
|
+
return function (env) {
|
|
23
|
+
function fail(e) {
|
|
24
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
25
|
+
env.hasError = true;
|
|
26
|
+
}
|
|
27
|
+
function next() {
|
|
28
|
+
while (env.stack.length) {
|
|
29
|
+
var rec = env.stack.pop();
|
|
30
|
+
try {
|
|
31
|
+
var result = rec.dispose && rec.dispose.call(rec.value);
|
|
32
|
+
if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
fail(e);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (env.hasError) throw env.error;
|
|
39
|
+
}
|
|
40
|
+
return next();
|
|
41
|
+
};
|
|
42
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
43
|
+
var e = new Error(message);
|
|
44
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
45
|
+
});
|
|
1
46
|
import { dirname } from '../emulation/path.js';
|
|
2
47
|
import { Errno, ErrnoError } from '../error.js';
|
|
3
48
|
import { PreloadFile, parseFlag } from '../file.js';
|
|
@@ -47,14 +92,14 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
47
92
|
};
|
|
48
93
|
}
|
|
49
94
|
async sync(path, data, stats) {
|
|
50
|
-
await this.
|
|
95
|
+
await this.copyForWrite(path);
|
|
51
96
|
if (!(await this.writable.exists(path))) {
|
|
52
97
|
await this.writable.createFile(path, 'w', 0o644);
|
|
53
98
|
}
|
|
54
99
|
await this.writable.sync(path, data, stats);
|
|
55
100
|
}
|
|
56
101
|
syncSync(path, data, stats) {
|
|
57
|
-
this.
|
|
102
|
+
this.copyForWriteSync(path);
|
|
58
103
|
this.writable.syncSync(path, data, stats);
|
|
59
104
|
}
|
|
60
105
|
/**
|
|
@@ -92,6 +137,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
92
137
|
this.checkInitialized();
|
|
93
138
|
this.checkPath(oldPath);
|
|
94
139
|
this.checkPath(newPath);
|
|
140
|
+
await this.copyForWrite(oldPath);
|
|
95
141
|
try {
|
|
96
142
|
await this.writable.rename(oldPath, newPath);
|
|
97
143
|
}
|
|
@@ -105,6 +151,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
105
151
|
this.checkInitialized();
|
|
106
152
|
this.checkPath(oldPath);
|
|
107
153
|
this.checkPath(newPath);
|
|
154
|
+
this.copyForWriteSync(oldPath);
|
|
108
155
|
try {
|
|
109
156
|
this.writable.renameSync(oldPath, newPath);
|
|
110
157
|
}
|
|
@@ -124,7 +171,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
124
171
|
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
125
172
|
}
|
|
126
173
|
const oldStat = new Stats(await this.readable.stat(path));
|
|
127
|
-
// Make the oldStat's mode writable.
|
|
174
|
+
// Make the oldStat's mode writable.
|
|
128
175
|
oldStat.mode |= 0o222;
|
|
129
176
|
return oldStat;
|
|
130
177
|
}
|
|
@@ -139,7 +186,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
139
186
|
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
140
187
|
}
|
|
141
188
|
const oldStat = new Stats(this.readable.statSync(path));
|
|
142
|
-
// Make the oldStat's mode writable.
|
|
189
|
+
// Make the oldStat's mode writable.
|
|
143
190
|
oldStat.mode |= 0o222;
|
|
144
191
|
return oldStat;
|
|
145
192
|
}
|
|
@@ -150,7 +197,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
150
197
|
}
|
|
151
198
|
// Create an OverlayFile.
|
|
152
199
|
const file = await this.readable.openFile(path, parseFlag('r'));
|
|
153
|
-
const stats =
|
|
200
|
+
const stats = await file.stat();
|
|
154
201
|
const { buffer } = await file.read(new Uint8Array(stats.size));
|
|
155
202
|
return new PreloadFile(this, path, flag, stats, buffer);
|
|
156
203
|
}
|
|
@@ -160,7 +207,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
160
207
|
}
|
|
161
208
|
// Create an OverlayFile.
|
|
162
209
|
const file = this.readable.openFileSync(path, parseFlag('r'));
|
|
163
|
-
const stats =
|
|
210
|
+
const stats = file.statSync();
|
|
164
211
|
const data = new Uint8Array(stats.size);
|
|
165
212
|
file.readSync(data);
|
|
166
213
|
return new PreloadFile(this, path, flag, stats, data);
|
|
@@ -177,10 +224,12 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
177
224
|
}
|
|
178
225
|
async link(srcpath, dstpath) {
|
|
179
226
|
this.checkInitialized();
|
|
227
|
+
await this.copyForWrite(srcpath);
|
|
180
228
|
await this.writable.link(srcpath, dstpath);
|
|
181
229
|
}
|
|
182
230
|
linkSync(srcpath, dstpath) {
|
|
183
231
|
this.checkInitialized();
|
|
232
|
+
this.copyForWriteSync(srcpath);
|
|
184
233
|
this.writable.linkSync(srcpath, dstpath);
|
|
185
234
|
}
|
|
186
235
|
async unlink(path) {
|
|
@@ -219,15 +268,14 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
219
268
|
if (await this.writable.exists(path)) {
|
|
220
269
|
await this.writable.rmdir(path);
|
|
221
270
|
}
|
|
222
|
-
if (await this.exists(path)) {
|
|
223
|
-
|
|
224
|
-
if ((await this.readdir(path)).length > 0) {
|
|
225
|
-
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
|
|
226
|
-
}
|
|
227
|
-
else {
|
|
228
|
-
await this.deletePath(path);
|
|
229
|
-
}
|
|
271
|
+
if (!(await this.exists(path))) {
|
|
272
|
+
return;
|
|
230
273
|
}
|
|
274
|
+
// Check if directory is empty.
|
|
275
|
+
if ((await this.readdir(path)).length) {
|
|
276
|
+
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
|
|
277
|
+
}
|
|
278
|
+
await this.deletePath(path);
|
|
231
279
|
}
|
|
232
280
|
rmdirSync(path) {
|
|
233
281
|
this.checkInitialized();
|
|
@@ -237,15 +285,14 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
237
285
|
if (this.writable.existsSync(path)) {
|
|
238
286
|
this.writable.rmdirSync(path);
|
|
239
287
|
}
|
|
240
|
-
if (this.existsSync(path)) {
|
|
241
|
-
|
|
242
|
-
if (this.readdirSync(path).length > 0) {
|
|
243
|
-
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
void this.deletePath(path);
|
|
247
|
-
}
|
|
288
|
+
if (!this.existsSync(path)) {
|
|
289
|
+
return;
|
|
248
290
|
}
|
|
291
|
+
// Check if directory is empty.
|
|
292
|
+
if (this.readdirSync(path).length) {
|
|
293
|
+
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
|
|
294
|
+
}
|
|
295
|
+
void this.deletePath(path);
|
|
249
296
|
}
|
|
250
297
|
async mkdir(path, mode) {
|
|
251
298
|
this.checkInitialized();
|
|
@@ -403,19 +450,23 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
403
450
|
* - Ensures p is on writable before proceeding. Throws an error if it doesn't exist.
|
|
404
451
|
* - Calls f to perform operation on writable.
|
|
405
452
|
*/
|
|
406
|
-
|
|
453
|
+
copyForWriteSync(path) {
|
|
407
454
|
if (!this.existsSync(path)) {
|
|
408
|
-
throw ErrnoError.With('ENOENT', path, '
|
|
455
|
+
throw ErrnoError.With('ENOENT', path, 'copyForWrite');
|
|
456
|
+
}
|
|
457
|
+
if (!this.writable.existsSync(dirname(path))) {
|
|
458
|
+
this.createParentDirectoriesSync(path);
|
|
409
459
|
}
|
|
410
460
|
if (!this.writable.existsSync(path)) {
|
|
411
|
-
// File is on readable storage. Copy to writable storage before
|
|
412
|
-
// changing its mode.
|
|
413
461
|
this.copyToWritableSync(path);
|
|
414
462
|
}
|
|
415
463
|
}
|
|
416
|
-
async
|
|
464
|
+
async copyForWrite(path) {
|
|
417
465
|
if (!(await this.exists(path))) {
|
|
418
|
-
throw ErrnoError.With('ENOENT', path, '
|
|
466
|
+
throw ErrnoError.With('ENOENT', path, 'copyForWrite');
|
|
467
|
+
}
|
|
468
|
+
if (!(await this.writable.exists(dirname(path)))) {
|
|
469
|
+
await this.createParentDirectories(path);
|
|
419
470
|
}
|
|
420
471
|
if (!(await this.writable.exists(path))) {
|
|
421
472
|
return this.copyToWritable(path);
|
|
@@ -426,32 +477,50 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
426
477
|
* PRECONDITION: File does not exist on writable storage.
|
|
427
478
|
*/
|
|
428
479
|
copyToWritableSync(path) {
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
this.
|
|
432
|
-
|
|
480
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
481
|
+
try {
|
|
482
|
+
const stats = this.statSync(path);
|
|
483
|
+
if (stats.isDirectory()) {
|
|
484
|
+
this.writable.mkdirSync(path, stats.mode);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const data = new Uint8Array(stats.size);
|
|
488
|
+
const readable = __addDisposableResource(env_1, this.readable.openFileSync(path, 'r'), false);
|
|
489
|
+
readable.readSync(data);
|
|
490
|
+
const writable = __addDisposableResource(env_1, this.writable.createFileSync(path, 'w', stats.mode | 0o222), false);
|
|
491
|
+
writable.writeSync(data);
|
|
492
|
+
}
|
|
493
|
+
catch (e_1) {
|
|
494
|
+
env_1.error = e_1;
|
|
495
|
+
env_1.hasError = true;
|
|
496
|
+
}
|
|
497
|
+
finally {
|
|
498
|
+
__disposeResources(env_1);
|
|
433
499
|
}
|
|
434
|
-
const data = new Uint8Array(stats.size);
|
|
435
|
-
const readable = this.readable.openFileSync(path, parseFlag('r'));
|
|
436
|
-
readable.readSync(data);
|
|
437
|
-
readable.closeSync();
|
|
438
|
-
const writable = this.writable.openFileSync(path, parseFlag('w'));
|
|
439
|
-
writable.writeSync(data);
|
|
440
|
-
writable.closeSync();
|
|
441
500
|
}
|
|
442
501
|
async copyToWritable(path) {
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
await this.
|
|
446
|
-
|
|
502
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
503
|
+
try {
|
|
504
|
+
const stats = await this.stat(path);
|
|
505
|
+
if (stats.isDirectory()) {
|
|
506
|
+
await this.writable.mkdir(path, stats.mode);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
const data = new Uint8Array(stats.size);
|
|
510
|
+
const readable = __addDisposableResource(env_2, await this.readable.openFile(path, 'r'), true);
|
|
511
|
+
await readable.read(data);
|
|
512
|
+
const writable = __addDisposableResource(env_2, await this.writable.createFile(path, 'w', stats.mode | 0o222), true);
|
|
513
|
+
await writable.write(data);
|
|
514
|
+
}
|
|
515
|
+
catch (e_2) {
|
|
516
|
+
env_2.error = e_2;
|
|
517
|
+
env_2.hasError = true;
|
|
518
|
+
}
|
|
519
|
+
finally {
|
|
520
|
+
const result_1 = __disposeResources(env_2);
|
|
521
|
+
if (result_1)
|
|
522
|
+
await result_1;
|
|
447
523
|
}
|
|
448
|
-
const data = new Uint8Array(stats.size);
|
|
449
|
-
const readable = await this.readable.openFile(path, parseFlag('r'));
|
|
450
|
-
await readable.read(data);
|
|
451
|
-
await readable.close();
|
|
452
|
-
const writable = await this.writable.openFile(path, parseFlag('w'));
|
|
453
|
-
await writable.write(data);
|
|
454
|
-
await writable.close();
|
|
455
524
|
}
|
|
456
525
|
}
|
|
457
526
|
/**
|
package/dist/mixins/mutexed.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export declare class MutexLock {
|
|
|
16
16
|
/**
|
|
17
17
|
* @hidden
|
|
18
18
|
*/
|
|
19
|
-
export declare class
|
|
19
|
+
export declare class _MutexedFS<T extends FileSystem> implements FileSystem {
|
|
20
20
|
/**
|
|
21
21
|
* @internal
|
|
22
22
|
*/
|
|
@@ -90,6 +90,6 @@ export declare class __MutexedFS<T extends FileSystem> implements FileSystem {
|
|
|
90
90
|
* @todo Change `using _` to `using void` pending https://github.com/tc39/proposal-discard-binding
|
|
91
91
|
* @internal
|
|
92
92
|
*/
|
|
93
|
-
export declare function Mutexed<const T extends Concrete<typeof FileSystem>>(FS: T): typeof
|
|
94
|
-
new (...args: ConstructorParameters<T>):
|
|
93
|
+
export declare function Mutexed<const T extends Concrete<typeof FileSystem>>(FS: T): typeof _MutexedFS<InstanceType<T>> & {
|
|
94
|
+
new (...args: ConstructorParameters<T>): _MutexedFS<InstanceType<T>>;
|
|
95
95
|
};
|
package/dist/mixins/mutexed.js
CHANGED
|
@@ -69,7 +69,7 @@ export class MutexLock {
|
|
|
69
69
|
/**
|
|
70
70
|
* @hidden
|
|
71
71
|
*/
|
|
72
|
-
export class
|
|
72
|
+
export class _MutexedFS {
|
|
73
73
|
async ready() {
|
|
74
74
|
return await this._fs.ready();
|
|
75
75
|
}
|
|
@@ -109,7 +109,7 @@ export class __MutexedFS {
|
|
|
109
109
|
* @internal
|
|
110
110
|
*/
|
|
111
111
|
lockSync(path, syscall) {
|
|
112
|
-
if (this.currentLock) {
|
|
112
|
+
if (this.currentLock?.isLocked) {
|
|
113
113
|
throw ErrnoError.With('EBUSY', path, syscall);
|
|
114
114
|
}
|
|
115
115
|
return this.addLock();
|
|
@@ -459,7 +459,7 @@ export class __MutexedFS {
|
|
|
459
459
|
* @internal
|
|
460
460
|
*/
|
|
461
461
|
export function Mutexed(FS) {
|
|
462
|
-
class MutexedFS extends
|
|
462
|
+
class MutexedFS extends _MutexedFS {
|
|
463
463
|
constructor(...args) {
|
|
464
464
|
super();
|
|
465
465
|
this._fs = new FS(...args);
|
package/package.json
CHANGED
package/src/backends/overlay.ts
CHANGED
|
@@ -75,7 +75,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
public async sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> {
|
|
78
|
-
await this.
|
|
78
|
+
await this.copyForWrite(path);
|
|
79
79
|
if (!(await this.writable.exists(path))) {
|
|
80
80
|
await this.writable.createFile(path, 'w', 0o644);
|
|
81
81
|
}
|
|
@@ -83,7 +83,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void {
|
|
86
|
-
this.
|
|
86
|
+
this.copyForWriteSync(path);
|
|
87
87
|
this.writable.syncSync(path, data, stats);
|
|
88
88
|
}
|
|
89
89
|
|
|
@@ -126,6 +126,8 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
126
126
|
this.checkPath(oldPath);
|
|
127
127
|
this.checkPath(newPath);
|
|
128
128
|
|
|
129
|
+
await this.copyForWrite(oldPath);
|
|
130
|
+
|
|
129
131
|
try {
|
|
130
132
|
await this.writable.rename(oldPath, newPath);
|
|
131
133
|
} catch (e) {
|
|
@@ -140,6 +142,8 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
140
142
|
this.checkPath(oldPath);
|
|
141
143
|
this.checkPath(newPath);
|
|
142
144
|
|
|
145
|
+
this.copyForWriteSync(oldPath);
|
|
146
|
+
|
|
143
147
|
try {
|
|
144
148
|
this.writable.renameSync(oldPath, newPath);
|
|
145
149
|
} catch (e) {
|
|
@@ -158,7 +162,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
158
162
|
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
159
163
|
}
|
|
160
164
|
const oldStat = new Stats(await this.readable.stat(path));
|
|
161
|
-
// Make the oldStat's mode writable.
|
|
165
|
+
// Make the oldStat's mode writable.
|
|
162
166
|
oldStat.mode |= 0o222;
|
|
163
167
|
return oldStat;
|
|
164
168
|
}
|
|
@@ -173,7 +177,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
173
177
|
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
174
178
|
}
|
|
175
179
|
const oldStat = new Stats(this.readable.statSync(path));
|
|
176
|
-
// Make the oldStat's mode writable.
|
|
180
|
+
// Make the oldStat's mode writable.
|
|
177
181
|
oldStat.mode |= 0o222;
|
|
178
182
|
return oldStat;
|
|
179
183
|
}
|
|
@@ -185,7 +189,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
185
189
|
}
|
|
186
190
|
// Create an OverlayFile.
|
|
187
191
|
const file = await this.readable.openFile(path, parseFlag('r'));
|
|
188
|
-
const stats =
|
|
192
|
+
const stats = await file.stat();
|
|
189
193
|
const { buffer } = await file.read(new Uint8Array(stats.size));
|
|
190
194
|
return new PreloadFile(this, path, flag, stats, buffer);
|
|
191
195
|
}
|
|
@@ -196,7 +200,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
196
200
|
}
|
|
197
201
|
// Create an OverlayFile.
|
|
198
202
|
const file = this.readable.openFileSync(path, parseFlag('r'));
|
|
199
|
-
const stats =
|
|
203
|
+
const stats = file.statSync();
|
|
200
204
|
const data = new Uint8Array(stats.size);
|
|
201
205
|
file.readSync(data);
|
|
202
206
|
return new PreloadFile(this, path, flag, stats, data);
|
|
@@ -216,11 +220,13 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
216
220
|
|
|
217
221
|
public async link(srcpath: string, dstpath: string): Promise<void> {
|
|
218
222
|
this.checkInitialized();
|
|
223
|
+
await this.copyForWrite(srcpath);
|
|
219
224
|
await this.writable.link(srcpath, dstpath);
|
|
220
225
|
}
|
|
221
226
|
|
|
222
227
|
public linkSync(srcpath: string, dstpath: string): void {
|
|
223
228
|
this.checkInitialized();
|
|
229
|
+
this.copyForWriteSync(srcpath);
|
|
224
230
|
this.writable.linkSync(srcpath, dstpath);
|
|
225
231
|
}
|
|
226
232
|
|
|
@@ -266,14 +272,14 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
266
272
|
if (await this.writable.exists(path)) {
|
|
267
273
|
await this.writable.rmdir(path);
|
|
268
274
|
}
|
|
269
|
-
if (await this.exists(path)) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
275
|
+
if (!(await this.exists(path))) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
// Check if directory is empty.
|
|
279
|
+
if ((await this.readdir(path)).length) {
|
|
280
|
+
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
|
|
276
281
|
}
|
|
282
|
+
await this.deletePath(path);
|
|
277
283
|
}
|
|
278
284
|
|
|
279
285
|
public rmdirSync(path: string): void {
|
|
@@ -284,14 +290,14 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
284
290
|
if (this.writable.existsSync(path)) {
|
|
285
291
|
this.writable.rmdirSync(path);
|
|
286
292
|
}
|
|
287
|
-
if (this.existsSync(path)) {
|
|
288
|
-
|
|
289
|
-
if (this.readdirSync(path).length > 0) {
|
|
290
|
-
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
|
|
291
|
-
} else {
|
|
292
|
-
void this.deletePath(path);
|
|
293
|
-
}
|
|
293
|
+
if (!this.existsSync(path)) {
|
|
294
|
+
return;
|
|
294
295
|
}
|
|
296
|
+
// Check if directory is empty.
|
|
297
|
+
if (this.readdirSync(path).length) {
|
|
298
|
+
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
|
|
299
|
+
}
|
|
300
|
+
void this.deletePath(path);
|
|
295
301
|
}
|
|
296
302
|
|
|
297
303
|
public async mkdir(path: string, mode: number): Promise<void> {
|
|
@@ -463,20 +469,25 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
463
469
|
* - Ensures p is on writable before proceeding. Throws an error if it doesn't exist.
|
|
464
470
|
* - Calls f to perform operation on writable.
|
|
465
471
|
*/
|
|
466
|
-
private
|
|
472
|
+
private copyForWriteSync(path: string): void {
|
|
467
473
|
if (!this.existsSync(path)) {
|
|
468
|
-
throw ErrnoError.With('ENOENT', path, '
|
|
474
|
+
throw ErrnoError.With('ENOENT', path, 'copyForWrite');
|
|
475
|
+
}
|
|
476
|
+
if (!this.writable.existsSync(dirname(path))) {
|
|
477
|
+
this.createParentDirectoriesSync(path);
|
|
469
478
|
}
|
|
470
479
|
if (!this.writable.existsSync(path)) {
|
|
471
|
-
// File is on readable storage. Copy to writable storage before
|
|
472
|
-
// changing its mode.
|
|
473
480
|
this.copyToWritableSync(path);
|
|
474
481
|
}
|
|
475
482
|
}
|
|
476
483
|
|
|
477
|
-
private async
|
|
484
|
+
private async copyForWrite(path: string): Promise<void> {
|
|
478
485
|
if (!(await this.exists(path))) {
|
|
479
|
-
throw ErrnoError.With('ENOENT', path, '
|
|
486
|
+
throw ErrnoError.With('ENOENT', path, 'copyForWrite');
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (!(await this.writable.exists(dirname(path)))) {
|
|
490
|
+
await this.createParentDirectories(path);
|
|
480
491
|
}
|
|
481
492
|
|
|
482
493
|
if (!(await this.writable.exists(path))) {
|
|
@@ -496,12 +507,10 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
496
507
|
}
|
|
497
508
|
|
|
498
509
|
const data = new Uint8Array(stats.size);
|
|
499
|
-
|
|
510
|
+
using readable = this.readable.openFileSync(path, 'r');
|
|
500
511
|
readable.readSync(data);
|
|
501
|
-
|
|
502
|
-
const writable = this.writable.openFileSync(path, parseFlag('w'));
|
|
512
|
+
using writable = this.writable.createFileSync(path, 'w', stats.mode | 0o222);
|
|
503
513
|
writable.writeSync(data);
|
|
504
|
-
writable.closeSync();
|
|
505
514
|
}
|
|
506
515
|
|
|
507
516
|
private async copyToWritable(path: string): Promise<void> {
|
|
@@ -512,12 +521,10 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
512
521
|
}
|
|
513
522
|
|
|
514
523
|
const data = new Uint8Array(stats.size);
|
|
515
|
-
|
|
524
|
+
await using readable = await this.readable.openFile(path, 'r');
|
|
516
525
|
await readable.read(data);
|
|
517
|
-
await
|
|
518
|
-
const writable = await this.writable.openFile(path, parseFlag('w'));
|
|
526
|
+
await using writable = await this.writable.createFile(path, 'w', stats.mode | 0o222);
|
|
519
527
|
await writable.write(data);
|
|
520
|
-
await writable.close();
|
|
521
528
|
}
|
|
522
529
|
}
|
|
523
530
|
|
package/src/mixins/mutexed.ts
CHANGED
|
@@ -33,7 +33,7 @@ export class MutexLock {
|
|
|
33
33
|
/**
|
|
34
34
|
* @hidden
|
|
35
35
|
*/
|
|
36
|
-
export class
|
|
36
|
+
export class _MutexedFS<T extends FileSystem> implements FileSystem {
|
|
37
37
|
/**
|
|
38
38
|
* @internal
|
|
39
39
|
*/
|
|
@@ -87,7 +87,7 @@ export class __MutexedFS<T extends FileSystem> implements FileSystem {
|
|
|
87
87
|
* @internal
|
|
88
88
|
*/
|
|
89
89
|
public lockSync(path: string, syscall: string): MutexLock {
|
|
90
|
-
if (this.currentLock) {
|
|
90
|
+
if (this.currentLock?.isLocked) {
|
|
91
91
|
throw ErrnoError.With('EBUSY', path, syscall);
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -244,10 +244,10 @@ export class __MutexedFS<T extends FileSystem> implements FileSystem {
|
|
|
244
244
|
*/
|
|
245
245
|
export function Mutexed<const T extends Concrete<typeof FileSystem>>(
|
|
246
246
|
FS: T
|
|
247
|
-
): typeof
|
|
248
|
-
new (...args: ConstructorParameters<T>):
|
|
247
|
+
): typeof _MutexedFS<InstanceType<T>> & {
|
|
248
|
+
new (...args: ConstructorParameters<T>): _MutexedFS<InstanceType<T>>;
|
|
249
249
|
} {
|
|
250
|
-
class MutexedFS extends
|
|
250
|
+
class MutexedFS extends _MutexedFS<InstanceType<T>> {
|
|
251
251
|
public constructor(...args: ConstructorParameters<T>) {
|
|
252
252
|
super();
|
|
253
253
|
this._fs = new FS(...args) as InstanceType<T>;
|