@poe-platform/safe-fs 0.1.728 → 0.1.729
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/README.md +7 -7
- package/dist/safe-fs/contracts/io.js +140 -114
- package/dist/safe-fs/fs/memory/index.d.ts +36 -0
- package/dist/safe-fs/fs/memory/index.js +563 -127
- package/dist/safe-fs/fs/memory/ledger.d.ts +6 -1
- package/dist/safe-fs/fs/memory/ledger.js +31 -3
- package/dist/safe-fs/fs/overlay/index.js +5 -3
- package/dist/safe-fs/fs/quota/index.js +4 -4
- package/dist/safe-fs/fs/s3/filesystem.js +10 -10
- package/dist/safe-fs/fs/webdav/webdav.js +7 -5
- package/package.json +1 -1
|
@@ -15,19 +15,6 @@ import { MemoryAllocation, MemoryLedger } from "./ledger.js";
|
|
|
15
15
|
import { normalizeMemoryFileSystemLimits } from "./limits.js";
|
|
16
16
|
export { defaultMemoryFileSystemLimits } from "./limits.js";
|
|
17
17
|
class MemoryFileNode {
|
|
18
|
-
type = "file";
|
|
19
|
-
revision = 0;
|
|
20
|
-
mode;
|
|
21
|
-
ino;
|
|
22
|
-
nlink = 1;
|
|
23
|
-
atimeMs;
|
|
24
|
-
mtimeMs;
|
|
25
|
-
ctimeMs;
|
|
26
|
-
birthtimeMs;
|
|
27
|
-
references = 0;
|
|
28
|
-
byteLength;
|
|
29
|
-
view;
|
|
30
|
-
allocation;
|
|
31
18
|
constructor(mode, ino, now, byteLength, allocation, view) {
|
|
32
19
|
this.mode = mode;
|
|
33
20
|
this.ino = ino;
|
|
@@ -47,17 +34,50 @@ class MemoryFileNode {
|
|
|
47
34
|
this.byteLength = value.byteLength;
|
|
48
35
|
}
|
|
49
36
|
}
|
|
37
|
+
Object.assign(MemoryFileNode.prototype, {
|
|
38
|
+
type: "file",
|
|
39
|
+
revision: 0,
|
|
40
|
+
nlink: 1,
|
|
41
|
+
references: 0,
|
|
42
|
+
});
|
|
43
|
+
const EMPTY_ALLOC_BYTES = new Uint8Array(0);
|
|
44
|
+
const SMALL_ALLOC_SLAB_SIZE = 8192;
|
|
45
|
+
let smallAllocSlab = new Uint8Array(SMALL_ALLOC_SLAB_SIZE);
|
|
46
|
+
let smallAllocOffset = 0;
|
|
47
|
+
class MemoryDirectoryNode {
|
|
48
|
+
constructor(mode, ino, now) {
|
|
49
|
+
this.mode = mode;
|
|
50
|
+
this.ino = ino;
|
|
51
|
+
this.atimeMs = now;
|
|
52
|
+
this.mtimeMs = now;
|
|
53
|
+
this.ctimeMs = now;
|
|
54
|
+
this.birthtimeMs = now;
|
|
55
|
+
this.entries = new Map();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
Object.assign(MemoryDirectoryNode.prototype, {
|
|
59
|
+
type: "directory",
|
|
60
|
+
revision: 0,
|
|
61
|
+
nlink: 1,
|
|
62
|
+
references: 0,
|
|
63
|
+
cachedNlink: 2,
|
|
64
|
+
cachedNlinkRev: 0,
|
|
65
|
+
});
|
|
50
66
|
class MemoryCache {
|
|
51
67
|
allocations = [];
|
|
52
68
|
files = [];
|
|
69
|
+
directories = [];
|
|
70
|
+
removedScratch = [];
|
|
53
71
|
lastFastDirPrefix = "";
|
|
54
72
|
lastFastDirNode;
|
|
55
73
|
lastFastFilePath = "";
|
|
74
|
+
lastFastFileName = "";
|
|
56
75
|
lastFastFileNode;
|
|
57
76
|
clearWrites() {
|
|
58
77
|
this.lastFastDirPrefix = "";
|
|
59
78
|
this.lastFastDirNode = undefined;
|
|
60
79
|
this.lastFastFilePath = "";
|
|
80
|
+
this.lastFastFileName = "";
|
|
61
81
|
this.lastFastFileNode = undefined;
|
|
62
82
|
}
|
|
63
83
|
}
|
|
@@ -70,6 +90,14 @@ const ext4HtreeEof64 = (1n << 63n) - 1n;
|
|
|
70
90
|
const extractionStreamGuard = Symbol("extractionStreamGuard");
|
|
71
91
|
const ownedStats = new WeakMap();
|
|
72
92
|
const ownedStores = new WeakMap();
|
|
93
|
+
// Ensure V8 uses Tagged representation for timestamp fields so all 4 fields share one HeapNumber pointer.
|
|
94
|
+
{
|
|
95
|
+
const dummyAlloc = new MemoryAllocation(new Uint8Array(0), new MemoryLedger(normalizeMemoryFileSystemLimits({})));
|
|
96
|
+
const dummyFile = new MemoryFileNode(0, 0, null, 0, dummyAlloc, dummyAlloc.data);
|
|
97
|
+
dummyFile.atimeMs = dummyFile.mtimeMs = dummyFile.ctimeMs = dummyFile.birthtimeMs = 1700000000000;
|
|
98
|
+
const dummyDir = new MemoryDirectoryNode(0, 0, null);
|
|
99
|
+
dummyDir.atimeMs = dummyDir.mtimeMs = dummyDir.ctimeMs = dummyDir.birthtimeMs = 1700000000000;
|
|
100
|
+
}
|
|
73
101
|
// Forwarded receivers share the ledger, so mutations invalidate the owner's cache.
|
|
74
102
|
const memoryCaches = new WeakMap();
|
|
75
103
|
const registeredAuthorities = new WeakSet();
|
|
@@ -127,6 +155,107 @@ export async function bindConditionalMutation(identityScope, binding, action) {
|
|
|
127
155
|
}
|
|
128
156
|
}
|
|
129
157
|
}
|
|
158
|
+
const STREAM_DONE_RESULT = Object.freeze({ done: true, value: undefined });
|
|
159
|
+
const STREAM_RESOLVED_DONE = Promise.resolve(STREAM_DONE_RESULT);
|
|
160
|
+
class MemoryReadStream {
|
|
161
|
+
constructor(fs, path, options) {
|
|
162
|
+
this.fs = fs;
|
|
163
|
+
this.path = path;
|
|
164
|
+
this.options = options;
|
|
165
|
+
this.abortSignal = options.signal;
|
|
166
|
+
this.initialized = false;
|
|
167
|
+
this.finished = false;
|
|
168
|
+
this.offset = 0;
|
|
169
|
+
this.end = 0;
|
|
170
|
+
this.chunkSize = preferredIoBlockSize;
|
|
171
|
+
this.node = undefined;
|
|
172
|
+
this.allocation = undefined;
|
|
173
|
+
this.data = undefined;
|
|
174
|
+
}
|
|
175
|
+
[Symbol.asyncIterator]() {
|
|
176
|
+
if (this.initialized || this.finished) {
|
|
177
|
+
return new MemoryReadStream(this.fs, this.path, this.options);
|
|
178
|
+
}
|
|
179
|
+
return this;
|
|
180
|
+
}
|
|
181
|
+
_release() {
|
|
182
|
+
if (this.finished)
|
|
183
|
+
return;
|
|
184
|
+
this.finished = true;
|
|
185
|
+
if (this.allocation) {
|
|
186
|
+
this.allocation.release();
|
|
187
|
+
this.allocation = undefined;
|
|
188
|
+
}
|
|
189
|
+
if (this.node) {
|
|
190
|
+
const releasedNode = this.node;
|
|
191
|
+
this.node = undefined;
|
|
192
|
+
this.fs.releaseReference(releasedNode, this.path);
|
|
193
|
+
}
|
|
194
|
+
this.data = undefined;
|
|
195
|
+
}
|
|
196
|
+
tryNextSync() {
|
|
197
|
+
if (this.finished)
|
|
198
|
+
return STREAM_DONE_RESULT;
|
|
199
|
+
try {
|
|
200
|
+
const options = this.options;
|
|
201
|
+
options.signal?.throwIfAborted();
|
|
202
|
+
if (!this.initialized) {
|
|
203
|
+
this.initialized = true;
|
|
204
|
+
const fs = this.fs;
|
|
205
|
+
const path = this.path;
|
|
206
|
+
const start = options.start ?? 0;
|
|
207
|
+
const chunkSize = options.chunkSize ?? preferredIoBlockSize;
|
|
208
|
+
this.chunkSize = chunkSize;
|
|
209
|
+
fs.integer(start, "readStream", path);
|
|
210
|
+
fs.integer(chunkSize, "readStream", path);
|
|
211
|
+
if (chunkSize === 0)
|
|
212
|
+
fs.fail("EINVAL", "readStream", path);
|
|
213
|
+
if (options.endExclusive !== undefined) {
|
|
214
|
+
fs.integer(options.endExclusive, "readStream", path);
|
|
215
|
+
if (options.endExclusive < start)
|
|
216
|
+
fs.fail("EINVAL", "readStream", path);
|
|
217
|
+
}
|
|
218
|
+
const target = fs.file(path, "readStream");
|
|
219
|
+
fs.permission(target, 4, "readStream", path);
|
|
220
|
+
fs.ledger.reserve(path.length * 2, 1, "readStream", path);
|
|
221
|
+
target.references++;
|
|
222
|
+
this.node = target;
|
|
223
|
+
this.allocation = target.allocation;
|
|
224
|
+
target.allocation.retain();
|
|
225
|
+
const data = target.data;
|
|
226
|
+
this.data = data;
|
|
227
|
+
this.offset = start;
|
|
228
|
+
this.end = Math.min(options.endExclusive ?? data.byteLength, data.byteLength);
|
|
229
|
+
target.atimeMs = Date.now();
|
|
230
|
+
}
|
|
231
|
+
if (this.offset < this.end) {
|
|
232
|
+
const nextEnd = Math.min(this.offset + this.chunkSize, this.end);
|
|
233
|
+
const chunk = this.data.slice(this.offset, nextEnd);
|
|
234
|
+
this.offset = nextEnd;
|
|
235
|
+
return { done: false, value: chunk };
|
|
236
|
+
}
|
|
237
|
+
this._release();
|
|
238
|
+
return STREAM_DONE_RESULT;
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
this._release();
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
next() {
|
|
246
|
+
try {
|
|
247
|
+
const res = this.tryNextSync();
|
|
248
|
+
return res.done ? STREAM_RESOLVED_DONE : Promise.resolve(res);
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
return Promise.reject(error);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return() {
|
|
255
|
+
this._release();
|
|
256
|
+
return STREAM_RESOLVED_DONE;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
130
259
|
export class MemoryFileSystem {
|
|
131
260
|
capabilitiesFor;
|
|
132
261
|
capabilities = ((filesystem) => {
|
|
@@ -250,8 +379,8 @@ export class MemoryFileSystem {
|
|
|
250
379
|
}
|
|
251
380
|
retained.set("/", this.root);
|
|
252
381
|
}
|
|
253
|
-
const capabilities =
|
|
254
|
-
const allowed = new Set(["mkdir", "rm", "rmdir", "rename", "symlink", "link", "chmod", "utimes", "writeFile", "appendFile", "writeStream", "writeFileConditional", "removeFileConditional"]);
|
|
382
|
+
const capabilities = this.capabilities;
|
|
383
|
+
const allowed = new Set(["mkdir", "rm", "rmdir", "rename", "symlink", "link", "chmod", "utimes", "writeFile", "appendFile", "writeStream", "writeFileConditional", "removeFileConditional", "prepareDirectory", "createStagedFile", "publishStagedFile", "removeStagedFile"]);
|
|
255
384
|
const reads = new Set(["access", "capabilitiesFor", "compareEntry", "lstat", "stat", "readFile", "readStream", "readdir", "readlink", "realpath"]);
|
|
256
385
|
const check = (path, followFinal) => {
|
|
257
386
|
if (!roots.some(root => root === "/" || path === root || path.startsWith(`${root}/`)))
|
|
@@ -287,7 +416,7 @@ export class MemoryFileSystem {
|
|
|
287
416
|
throw new FsError("ENOTSUP", { syscall: String(property) });
|
|
288
417
|
// Stock mutations resolve and commit synchronously. Streamed writes
|
|
289
418
|
// repeat the boundary and file-binding check at each backend mutation.
|
|
290
|
-
const paths = property === "symlink" ? [args[1]] : property === "link" || property === "rename" ? args.slice(0, 2) : [args[0]];
|
|
419
|
+
const paths = property === "publishStagedFile" ? [args[1], args[0].directory.path] : property === "removeStagedFile" ? [args[0].directory.path] : property === "symlink" ? [args[1]] : property === "link" || property === "rename" ? args.slice(0, 2) : [args[0]];
|
|
291
420
|
for (const path of paths) {
|
|
292
421
|
if (typeof path !== "string")
|
|
293
422
|
throw new FsError("EINVAL");
|
|
@@ -314,7 +443,39 @@ export class MemoryFileSystem {
|
|
|
314
443
|
};
|
|
315
444
|
}
|
|
316
445
|
directory(mode) {
|
|
317
|
-
|
|
446
|
+
const now = Date.now();
|
|
447
|
+
const fullMode = typeModes.directory | mode;
|
|
448
|
+
const ino = this.nextInode++;
|
|
449
|
+
const pooled = memoryCaches.get(this.ledger)?.directories.pop();
|
|
450
|
+
if (pooled) {
|
|
451
|
+
pooled.mode = fullMode;
|
|
452
|
+
pooled.ino = ino;
|
|
453
|
+
pooled.nlink = 1;
|
|
454
|
+
pooled.references = 0;
|
|
455
|
+
pooled.revision = 0;
|
|
456
|
+
pooled.atimeMs = now;
|
|
457
|
+
pooled.mtimeMs = now;
|
|
458
|
+
pooled.ctimeMs = now;
|
|
459
|
+
pooled.birthtimeMs = now;
|
|
460
|
+
pooled.cachedNlink = 2;
|
|
461
|
+
pooled.cachedNlinkRev = 0;
|
|
462
|
+
return pooled;
|
|
463
|
+
}
|
|
464
|
+
return new MemoryDirectoryNode(fullMode, ino, now);
|
|
465
|
+
}
|
|
466
|
+
addDirectoryNode(parent, name, mode, syscall, path) {
|
|
467
|
+
const bytes = name.length * 2;
|
|
468
|
+
this.ledger.reserve(bytes, 2, syscall, path);
|
|
469
|
+
try {
|
|
470
|
+
const node = this.directory(mode);
|
|
471
|
+
parent.entries.set(name, node);
|
|
472
|
+
this.changed(parent);
|
|
473
|
+
return node;
|
|
474
|
+
}
|
|
475
|
+
catch (error) {
|
|
476
|
+
this.ledger.release(bytes, 2);
|
|
477
|
+
throw error;
|
|
478
|
+
}
|
|
318
479
|
}
|
|
319
480
|
addNode(parent, name, create, syscall, path, retainedBytes = 0) {
|
|
320
481
|
const bytes = name.length * 2 + retainedBytes;
|
|
@@ -344,6 +505,7 @@ export class MemoryFileSystem {
|
|
|
344
505
|
if (node.type === "file") {
|
|
345
506
|
if (cache.lastFastFileNode === node) {
|
|
346
507
|
cache.lastFastFilePath = "";
|
|
508
|
+
cache.lastFastFileName = "";
|
|
347
509
|
cache.lastFastFileNode = undefined;
|
|
348
510
|
}
|
|
349
511
|
this.totalBytes -= node.byteLength;
|
|
@@ -360,9 +522,14 @@ export class MemoryFileSystem {
|
|
|
360
522
|
else if (node.type === "symlink") {
|
|
361
523
|
this.symlinkCount--;
|
|
362
524
|
}
|
|
363
|
-
else
|
|
364
|
-
cache.
|
|
365
|
-
|
|
525
|
+
else {
|
|
526
|
+
if (cache.lastFastDirNode === node) {
|
|
527
|
+
cache.lastFastDirPrefix = "";
|
|
528
|
+
cache.lastFastDirNode = undefined;
|
|
529
|
+
}
|
|
530
|
+
if (node.entries.size === 0 && cache.directories.length < 32) {
|
|
531
|
+
cache.directories.push(node);
|
|
532
|
+
}
|
|
366
533
|
}
|
|
367
534
|
}
|
|
368
535
|
releaseReference(node, path) {
|
|
@@ -408,9 +575,9 @@ export class MemoryFileSystem {
|
|
|
408
575
|
if (!Number.isSafeInteger(value) || value < 0)
|
|
409
576
|
this.fail("EINVAL", syscall, path);
|
|
410
577
|
}
|
|
411
|
-
changed(node) {
|
|
578
|
+
changed(node, now = Date.now()) {
|
|
412
579
|
node.revision = node.revision < 1073741823 ? (node.revision + 1) | 0 : Math.min(Number.MAX_SAFE_INTEGER + 1, node.revision + 1);
|
|
413
|
-
node.mtimeMs = node.ctimeMs =
|
|
580
|
+
node.mtimeMs = node.ctimeMs = now;
|
|
414
581
|
}
|
|
415
582
|
resolveNode(path, syscall, followFinal = true) {
|
|
416
583
|
const cache = memoryCaches.get(this.ledger);
|
|
@@ -462,7 +629,7 @@ export class MemoryFileSystem {
|
|
|
462
629
|
}
|
|
463
630
|
resolve(path, syscall, options = emptyResolveOptions) {
|
|
464
631
|
this.validatePath(path, syscall);
|
|
465
|
-
if (this.symlinkCount === 0 && options.
|
|
632
|
+
if (this.symlinkCount === 0 && options.resizeCreate === undefined
|
|
466
633
|
&& options.resolutionSteps === undefined && isCleanAbsolutePath(path)) {
|
|
467
634
|
let current = this.root;
|
|
468
635
|
let start = 1;
|
|
@@ -473,7 +640,11 @@ export class MemoryFileSystem {
|
|
|
473
640
|
const name = path.slice(start);
|
|
474
641
|
if (exceedsComponentByteLimit(name))
|
|
475
642
|
this.fail("ENAMETOOLONG", syscall, path);
|
|
476
|
-
|
|
643
|
+
let node = current.entries.get(name);
|
|
644
|
+
if (!node && options.createDirectories !== undefined) {
|
|
645
|
+
this.permission(current, 3, syscall, path);
|
|
646
|
+
node = this.addDirectoryNode(current, name, options.createDirectories, syscall, path);
|
|
647
|
+
}
|
|
477
648
|
if (!node && !options.allowMissing)
|
|
478
649
|
this.fail("ENOENT", syscall, path);
|
|
479
650
|
return { node, parent: current, name, path };
|
|
@@ -481,7 +652,11 @@ export class MemoryFileSystem {
|
|
|
481
652
|
const name = path.slice(start, slash);
|
|
482
653
|
if (exceedsComponentByteLimit(name))
|
|
483
654
|
this.fail("ENAMETOOLONG", syscall, path);
|
|
484
|
-
|
|
655
|
+
let next = current.entries.get(name);
|
|
656
|
+
if (!next && options.createDirectories !== undefined) {
|
|
657
|
+
this.permission(current, 3, syscall, path);
|
|
658
|
+
next = this.addDirectoryNode(current, name, options.createDirectories, syscall, path);
|
|
659
|
+
}
|
|
485
660
|
if (!next)
|
|
486
661
|
this.fail("ENOENT", syscall, path);
|
|
487
662
|
if (next.type !== "directory")
|
|
@@ -645,6 +820,12 @@ export class MemoryFileSystem {
|
|
|
645
820
|
return location;
|
|
646
821
|
}
|
|
647
822
|
terminalDot(path) {
|
|
823
|
+
const len = path.length;
|
|
824
|
+
if (len === 0)
|
|
825
|
+
return false;
|
|
826
|
+
const last = path.charCodeAt(len - 1);
|
|
827
|
+
if (last !== 46 && last !== 47)
|
|
828
|
+
return false;
|
|
648
829
|
return /(?:^|\/)\.{1,2}\/*$/.test(path);
|
|
649
830
|
}
|
|
650
831
|
directoryNlink(node) {
|
|
@@ -716,6 +897,9 @@ export class MemoryFileSystem {
|
|
|
716
897
|
allocate(length, syscall, path) {
|
|
717
898
|
this.ledger.fileSize(length, syscall, path);
|
|
718
899
|
this.ledger.reserve(length, 0, syscall, path);
|
|
900
|
+
if (length === 0) {
|
|
901
|
+
return new MemoryAllocation(EMPTY_ALLOC_BYTES, this.ledger);
|
|
902
|
+
}
|
|
719
903
|
const allocations = memoryCaches.get(this.ledger).allocations;
|
|
720
904
|
if (length === 64 && allocations.length > 0) {
|
|
721
905
|
const pooled = allocations.pop();
|
|
@@ -723,6 +907,17 @@ export class MemoryFileSystem {
|
|
|
723
907
|
return pooled;
|
|
724
908
|
}
|
|
725
909
|
try {
|
|
910
|
+
// A shared slab's backing buffer exceeds the admitted slice capacity.
|
|
911
|
+
// Finite file/storage ceilings require independently owned exact buffers.
|
|
912
|
+
if (length <= 64 && this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity) {
|
|
913
|
+
if (smallAllocOffset + length > SMALL_ALLOC_SLAB_SIZE) {
|
|
914
|
+
smallAllocSlab = new Uint8Array(SMALL_ALLOC_SLAB_SIZE);
|
|
915
|
+
smallAllocOffset = 0;
|
|
916
|
+
}
|
|
917
|
+
const slice = smallAllocSlab.subarray(smallAllocOffset, smallAllocOffset + length);
|
|
918
|
+
smallAllocOffset += length;
|
|
919
|
+
return new MemoryAllocation(slice, this.ledger);
|
|
920
|
+
}
|
|
726
921
|
return new MemoryAllocation(new Uint8Array(length), this.ledger);
|
|
727
922
|
}
|
|
728
923
|
catch (cause) {
|
|
@@ -873,7 +1068,13 @@ export class MemoryFileSystem {
|
|
|
873
1068
|
let allocation = node.allocation;
|
|
874
1069
|
if (length > allocation.data.byteLength) {
|
|
875
1070
|
this.ledger.check(length, 0, syscall, path);
|
|
876
|
-
const
|
|
1071
|
+
const baseCap = curLen === 0 && length >= 8192 && length <= 16384 &&
|
|
1072
|
+
this.ledger.limits.maxFileBytes >= 65536 &&
|
|
1073
|
+
this.ledger.availableBytes >= 65536 &&
|
|
1074
|
+
(this.ledger.limits.maxBytes === undefined || this.ledger.limits.maxBytes - this.totalBytes >= 65536)
|
|
1075
|
+
? 65536
|
|
1076
|
+
: Math.max(length, curLen * 2, 64);
|
|
1077
|
+
const capacity = Math.min(baseCap, this.ledger.limits.maxFileBytes, this.ledger.availableBytes);
|
|
877
1078
|
allocation = this.allocate(capacity, syscall, path);
|
|
878
1079
|
}
|
|
879
1080
|
try {
|
|
@@ -977,13 +1178,12 @@ export class MemoryFileSystem {
|
|
|
977
1178
|
if (current && append) {
|
|
978
1179
|
this.writeAt(current, data, current.byteLength, syscall, path);
|
|
979
1180
|
cache.lastFastFilePath = path;
|
|
1181
|
+
cache.lastFastFileName = name;
|
|
980
1182
|
cache.lastFastFileNode = current;
|
|
981
1183
|
return;
|
|
982
1184
|
}
|
|
983
1185
|
if (!current) {
|
|
984
|
-
const capacity = length
|
|
985
|
-
? Math.min(Math.max(length, 64), this.ledger.limits.maxFileBytes, this.ledger.availableBytes - nameBytes)
|
|
986
|
-
: length;
|
|
1186
|
+
const capacity = length;
|
|
987
1187
|
const allocation = this.allocate(capacity, syscall, path);
|
|
988
1188
|
try {
|
|
989
1189
|
allocation.data.set(data);
|
|
@@ -991,7 +1191,7 @@ export class MemoryFileSystem {
|
|
|
991
1191
|
try {
|
|
992
1192
|
const now = Date.now();
|
|
993
1193
|
const fileMode = typeModes.file | mode;
|
|
994
|
-
const view =
|
|
1194
|
+
const view = allocation.data;
|
|
995
1195
|
let node = cache.files.pop();
|
|
996
1196
|
if (node) {
|
|
997
1197
|
node.mode = fileMode;
|
|
@@ -1010,16 +1210,16 @@ export class MemoryFileSystem {
|
|
|
1010
1210
|
else {
|
|
1011
1211
|
node = new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view);
|
|
1012
1212
|
}
|
|
1013
|
-
this.admitSize(undefined, length, syscall, path);
|
|
1014
1213
|
const prevNlink = parent.cachedNlinkRev === parent.revision ? parent.cachedNlink : (parent.entries.size === 0 ? 2 : undefined);
|
|
1015
1214
|
parent.entries.set(name, node);
|
|
1016
1215
|
this.totalBytes += length;
|
|
1017
|
-
this.changed(parent);
|
|
1216
|
+
this.changed(parent, now);
|
|
1018
1217
|
if (prevNlink !== undefined) {
|
|
1019
1218
|
parent.cachedNlink = prevNlink;
|
|
1020
1219
|
parent.cachedNlinkRev = parent.revision;
|
|
1021
1220
|
}
|
|
1022
1221
|
cache.lastFastFilePath = path;
|
|
1222
|
+
cache.lastFastFileName = name;
|
|
1023
1223
|
cache.lastFastFileNode = node;
|
|
1024
1224
|
}
|
|
1025
1225
|
catch (error) {
|
|
@@ -1037,12 +1237,169 @@ export class MemoryFileSystem {
|
|
|
1037
1237
|
try {
|
|
1038
1238
|
this.replaceData(current, copied);
|
|
1039
1239
|
this.changed(current);
|
|
1240
|
+
cache.lastFastFilePath = path;
|
|
1241
|
+
cache.lastFastFileName = name;
|
|
1242
|
+
cache.lastFastFileNode = current;
|
|
1040
1243
|
}
|
|
1041
1244
|
catch (error) {
|
|
1042
1245
|
copied.release();
|
|
1043
1246
|
throw error;
|
|
1044
1247
|
}
|
|
1045
1248
|
}
|
|
1249
|
+
writeMemoryFileInDirFast(dirPrefix, name, data, append, mode) {
|
|
1250
|
+
const cache = memoryCaches.get(this.ledger);
|
|
1251
|
+
const syscall = append ? "appendFile" : "writeFile";
|
|
1252
|
+
try {
|
|
1253
|
+
if (append &&
|
|
1254
|
+
dirPrefix === cache.lastFastDirPrefix &&
|
|
1255
|
+
name === cache.lastFastFileName &&
|
|
1256
|
+
cache.lastFastFileNode !== undefined &&
|
|
1257
|
+
cache.lastFastFileNode.nlink !== 0 &&
|
|
1258
|
+
cache.lastFastDirNode !== undefined &&
|
|
1259
|
+
cache.lastFastDirNode.nlink !== 0 &&
|
|
1260
|
+
(cache.lastFastDirNode.mode & 1) !== 0 &&
|
|
1261
|
+
(cache.lastFastFileNode.mode & 2) !== 0) {
|
|
1262
|
+
const current = cache.lastFastFileNode;
|
|
1263
|
+
const length = current.byteLength + data.byteLength;
|
|
1264
|
+
this.admitSize(current, length, syscall, name);
|
|
1265
|
+
const growth = length > current.allocation.data.byteLength ? length : 0;
|
|
1266
|
+
this.ledger.check(data.byteLength + growth, 0, syscall, name);
|
|
1267
|
+
this.writeAt(current, data, current.byteLength, syscall, name);
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
let parent = this.root;
|
|
1271
|
+
const cachedDir = cache.lastFastDirNode;
|
|
1272
|
+
if (cachedDir !== undefined &&
|
|
1273
|
+
cachedDir.nlink !== 0 &&
|
|
1274
|
+
(cachedDir.mode & 1) !== 0 &&
|
|
1275
|
+
dirPrefix === cache.lastFastDirPrefix) {
|
|
1276
|
+
parent = cachedDir;
|
|
1277
|
+
if (exceedsComponentByteLimit(name))
|
|
1278
|
+
this.fail("ENAMETOOLONG", syscall, name);
|
|
1279
|
+
}
|
|
1280
|
+
else {
|
|
1281
|
+
let start = 1;
|
|
1282
|
+
while (start < dirPrefix.length) {
|
|
1283
|
+
this.permission(parent, 1, syscall, name);
|
|
1284
|
+
const slash = dirPrefix.indexOf("/", start);
|
|
1285
|
+
const seg = slash === -1 ? dirPrefix.slice(start) : dirPrefix.slice(start, slash);
|
|
1286
|
+
if (exceedsComponentByteLimit(seg))
|
|
1287
|
+
this.fail("ENAMETOOLONG", syscall, name);
|
|
1288
|
+
const next = parent.entries.get(seg);
|
|
1289
|
+
if (!next)
|
|
1290
|
+
this.fail("ENOENT", syscall, name);
|
|
1291
|
+
if (next.type !== "directory")
|
|
1292
|
+
this.fail("ENOTDIR", syscall, name);
|
|
1293
|
+
parent = next;
|
|
1294
|
+
if (slash === -1)
|
|
1295
|
+
break;
|
|
1296
|
+
start = slash + 1;
|
|
1297
|
+
}
|
|
1298
|
+
this.permission(parent, 1, syscall, name);
|
|
1299
|
+
if (exceedsComponentByteLimit(name))
|
|
1300
|
+
this.fail("ENAMETOOLONG", syscall, name);
|
|
1301
|
+
if (dirPrefix.length > 1 && (parent.mode & 1) !== 0) {
|
|
1302
|
+
cache.lastFastDirPrefix = dirPrefix;
|
|
1303
|
+
cache.lastFastDirNode = parent;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
const existing = parent.entries.get(name);
|
|
1307
|
+
if (existing) {
|
|
1308
|
+
if (existing.type !== "file")
|
|
1309
|
+
this.fail("EISDIR", syscall, name);
|
|
1310
|
+
this.permission(existing, 2, syscall, name);
|
|
1311
|
+
}
|
|
1312
|
+
else {
|
|
1313
|
+
this.permission(parent, 3, syscall, name);
|
|
1314
|
+
}
|
|
1315
|
+
const current = existing;
|
|
1316
|
+
const length = (append ? current?.byteLength ?? 0 : 0) + data.byteLength;
|
|
1317
|
+
this.admitSize(current, length, syscall, name);
|
|
1318
|
+
const growth = append && length > (current?.allocation.data.byteLength ?? 0) ? length : 0;
|
|
1319
|
+
const nameBytes = name.length * 2;
|
|
1320
|
+
this.ledger.check(data.byteLength + growth + (current ? 0 : nameBytes), current ? 0 : 2, syscall, name);
|
|
1321
|
+
if (current && append) {
|
|
1322
|
+
this.writeAt(current, data, current.byteLength, syscall, name);
|
|
1323
|
+
cache.lastFastFilePath = "";
|
|
1324
|
+
cache.lastFastFileName = name;
|
|
1325
|
+
cache.lastFastFileNode = current;
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
if (!current) {
|
|
1329
|
+
const capacity = length;
|
|
1330
|
+
const allocation = this.allocate(capacity, syscall, name);
|
|
1331
|
+
try {
|
|
1332
|
+
allocation.data.set(data);
|
|
1333
|
+
this.ledger.reserve(nameBytes, 2, syscall, name);
|
|
1334
|
+
try {
|
|
1335
|
+
const now = Date.now();
|
|
1336
|
+
const fileMode = typeModes.file | mode;
|
|
1337
|
+
const view = allocation.data;
|
|
1338
|
+
let node = cache.files.pop();
|
|
1339
|
+
if (node) {
|
|
1340
|
+
node.mode = fileMode;
|
|
1341
|
+
node.ino = this.nextInode++;
|
|
1342
|
+
node.nlink = 1;
|
|
1343
|
+
node.references = 0;
|
|
1344
|
+
node.revision = 0;
|
|
1345
|
+
node.atimeMs = now;
|
|
1346
|
+
node.mtimeMs = now;
|
|
1347
|
+
node.ctimeMs = now;
|
|
1348
|
+
node.birthtimeMs = now;
|
|
1349
|
+
node.byteLength = length;
|
|
1350
|
+
node.view = view;
|
|
1351
|
+
node.allocation = allocation;
|
|
1352
|
+
}
|
|
1353
|
+
else {
|
|
1354
|
+
node = new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view);
|
|
1355
|
+
}
|
|
1356
|
+
const prevNlink = parent.cachedNlinkRev === parent.revision ? parent.cachedNlink : (parent.entries.size === 0 ? 2 : undefined);
|
|
1357
|
+
parent.entries.set(name, node);
|
|
1358
|
+
this.totalBytes += length;
|
|
1359
|
+
this.changed(parent, now);
|
|
1360
|
+
if (prevNlink !== undefined) {
|
|
1361
|
+
parent.cachedNlink = prevNlink;
|
|
1362
|
+
parent.cachedNlinkRev = parent.revision;
|
|
1363
|
+
}
|
|
1364
|
+
cache.lastFastFilePath = "";
|
|
1365
|
+
cache.lastFastFileName = name;
|
|
1366
|
+
cache.lastFastFileNode = node;
|
|
1367
|
+
}
|
|
1368
|
+
catch (error) {
|
|
1369
|
+
this.ledger.release(nameBytes, 2);
|
|
1370
|
+
throw error;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
catch (error) {
|
|
1374
|
+
allocation.release();
|
|
1375
|
+
throw error;
|
|
1376
|
+
}
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
const copied = this.bytes(data, syscall, name);
|
|
1380
|
+
try {
|
|
1381
|
+
this.replaceData(current, copied);
|
|
1382
|
+
this.changed(current);
|
|
1383
|
+
cache.lastFastFilePath = "";
|
|
1384
|
+
cache.lastFastFileName = name;
|
|
1385
|
+
cache.lastFastFileNode = current;
|
|
1386
|
+
}
|
|
1387
|
+
catch (error) {
|
|
1388
|
+
copied.release();
|
|
1389
|
+
throw error;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
catch (error) {
|
|
1393
|
+
if (error instanceof FsError && error.path === name) {
|
|
1394
|
+
throw new FsError(error.code, {
|
|
1395
|
+
syscall: error.syscall ?? syscall,
|
|
1396
|
+
path: dirPrefix + name,
|
|
1397
|
+
...(error.dest === undefined ? {} : { dest: error.dest }),
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
throw error;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1046
1403
|
async readFile(path, options = {}) {
|
|
1047
1404
|
options.signal?.throwIfAborted();
|
|
1048
1405
|
if (options.maxBytes !== undefined)
|
|
@@ -1143,6 +1500,8 @@ export class MemoryFileSystem {
|
|
|
1143
1500
|
location.parent.entries.set(location.name, directory);
|
|
1144
1501
|
if (file.type === "file")
|
|
1145
1502
|
this.totalBytes += file.data.byteLength;
|
|
1503
|
+
else
|
|
1504
|
+
this.symlinkCount++;
|
|
1146
1505
|
}
|
|
1147
1506
|
catch (error) {
|
|
1148
1507
|
allocation?.release();
|
|
@@ -1529,32 +1888,40 @@ export class MemoryFileSystem {
|
|
|
1529
1888
|
this.permission(location.parent, 3, syscall, path);
|
|
1530
1889
|
if (node.type === "directory" && !recursive)
|
|
1531
1890
|
this.fail("EISDIR", syscall, path);
|
|
1532
|
-
memoryCaches.get(this.ledger)
|
|
1533
|
-
|
|
1891
|
+
const cache = memoryCaches.get(this.ledger);
|
|
1892
|
+
cache.clearWrites();
|
|
1893
|
+
const removed = cache.removedScratch;
|
|
1894
|
+
removed.length = 0;
|
|
1895
|
+
removed.push(node);
|
|
1534
1896
|
let keyChars = 0;
|
|
1535
1897
|
let keyEntries = 0;
|
|
1536
1898
|
for (let index = 0; index < removed.length; index++) {
|
|
1537
1899
|
const entry = removed[index];
|
|
1538
1900
|
if (entry.type === "directory" && entry.entries.size > 0) {
|
|
1539
1901
|
this.permission(entry, 7, syscall, path);
|
|
1540
|
-
entry.entries
|
|
1902
|
+
for (const [name, child] of entry.entries) {
|
|
1541
1903
|
removed.push(child);
|
|
1542
1904
|
keyChars += name.length;
|
|
1543
1905
|
keyEntries++;
|
|
1544
|
-
}
|
|
1906
|
+
}
|
|
1545
1907
|
}
|
|
1546
1908
|
}
|
|
1547
1909
|
location.parent.entries.delete(location.name);
|
|
1548
1910
|
this.ledger.release((location.name.length + keyChars) * 2, 1 + keyEntries);
|
|
1549
|
-
|
|
1911
|
+
const now = Date.now();
|
|
1912
|
+
for (let i = 0; i < removed.length; i++) {
|
|
1913
|
+
const entry = removed[i];
|
|
1550
1914
|
if (entry.type === "directory") {
|
|
1551
1915
|
entry.entries.clear();
|
|
1552
1916
|
}
|
|
1553
1917
|
entry.nlink--;
|
|
1554
|
-
entry.
|
|
1555
|
-
|
|
1918
|
+
if (entry.nlink !== 0 || entry.references !== 0) {
|
|
1919
|
+
entry.ctimeMs = now;
|
|
1920
|
+
entry.revision = Math.min(Number.MAX_SAFE_INTEGER + 1, entry.revision + 1);
|
|
1921
|
+
}
|
|
1556
1922
|
this.releaseNode(entry);
|
|
1557
1923
|
}
|
|
1924
|
+
removed.length = 0;
|
|
1558
1925
|
this.changed(location.parent);
|
|
1559
1926
|
}
|
|
1560
1927
|
unlink(path, options = {}) {
|
|
@@ -1882,94 +2249,7 @@ export class MemoryFileSystem {
|
|
|
1882
2249
|
};
|
|
1883
2250
|
}
|
|
1884
2251
|
readStream(path, options = {}) {
|
|
1885
|
-
return
|
|
1886
|
-
[Symbol.asyncIterator]: () => {
|
|
1887
|
-
let initialized = false;
|
|
1888
|
-
let finished = false;
|
|
1889
|
-
let offset = 0;
|
|
1890
|
-
let end = 0;
|
|
1891
|
-
let chunkSize = preferredIoBlockSize;
|
|
1892
|
-
let node;
|
|
1893
|
-
let allocation;
|
|
1894
|
-
let data;
|
|
1895
|
-
const release = () => {
|
|
1896
|
-
if (finished)
|
|
1897
|
-
return;
|
|
1898
|
-
finished = true;
|
|
1899
|
-
if (allocation) {
|
|
1900
|
-
allocation.release();
|
|
1901
|
-
allocation = undefined;
|
|
1902
|
-
}
|
|
1903
|
-
if (node) {
|
|
1904
|
-
const releasedNode = node;
|
|
1905
|
-
node = undefined;
|
|
1906
|
-
this.releaseReference(releasedNode, path);
|
|
1907
|
-
}
|
|
1908
|
-
data = undefined;
|
|
1909
|
-
};
|
|
1910
|
-
const step = () => {
|
|
1911
|
-
if (finished)
|
|
1912
|
-
return { done: true, value: undefined };
|
|
1913
|
-
try {
|
|
1914
|
-
options.signal?.throwIfAborted();
|
|
1915
|
-
if (!initialized) {
|
|
1916
|
-
initialized = true;
|
|
1917
|
-
const start = options.start ?? 0;
|
|
1918
|
-
chunkSize = options.chunkSize ?? preferredIoBlockSize;
|
|
1919
|
-
this.integer(start, "readStream", path);
|
|
1920
|
-
this.integer(chunkSize, "readStream", path);
|
|
1921
|
-
if (chunkSize === 0)
|
|
1922
|
-
this.fail("EINVAL", "readStream", path);
|
|
1923
|
-
if (options.endExclusive !== undefined) {
|
|
1924
|
-
this.integer(options.endExclusive, "readStream", path);
|
|
1925
|
-
if (options.endExclusive < start)
|
|
1926
|
-
this.fail("EINVAL", "readStream", path);
|
|
1927
|
-
}
|
|
1928
|
-
const target = this.file(path, "readStream");
|
|
1929
|
-
this.permission(target, 4, "readStream", path);
|
|
1930
|
-
this.ledger.reserve(path.length * 2, 1, "readStream", path);
|
|
1931
|
-
target.references++;
|
|
1932
|
-
node = target;
|
|
1933
|
-
allocation = target.allocation;
|
|
1934
|
-
allocation.retain();
|
|
1935
|
-
data = target.data;
|
|
1936
|
-
offset = start;
|
|
1937
|
-
end = Math.min(options.endExclusive ?? data.byteLength, data.byteLength);
|
|
1938
|
-
target.atimeMs = Date.now();
|
|
1939
|
-
}
|
|
1940
|
-
if (offset < end) {
|
|
1941
|
-
const nextEnd = Math.min(offset + chunkSize, end);
|
|
1942
|
-
const chunk = data.slice(offset, nextEnd);
|
|
1943
|
-
offset = nextEnd;
|
|
1944
|
-
return { done: false, value: chunk };
|
|
1945
|
-
}
|
|
1946
|
-
release();
|
|
1947
|
-
return { done: true, value: undefined };
|
|
1948
|
-
}
|
|
1949
|
-
catch (error) {
|
|
1950
|
-
release();
|
|
1951
|
-
throw error;
|
|
1952
|
-
}
|
|
1953
|
-
};
|
|
1954
|
-
return {
|
|
1955
|
-
abortSignal: options.signal,
|
|
1956
|
-
[Symbol.asyncIterator]() { return this; },
|
|
1957
|
-
tryNextSync: step,
|
|
1958
|
-
next() {
|
|
1959
|
-
try {
|
|
1960
|
-
return Promise.resolve(step());
|
|
1961
|
-
}
|
|
1962
|
-
catch (error) {
|
|
1963
|
-
return Promise.reject(error);
|
|
1964
|
-
}
|
|
1965
|
-
},
|
|
1966
|
-
return() {
|
|
1967
|
-
release();
|
|
1968
|
-
return Promise.resolve({ done: true, value: undefined });
|
|
1969
|
-
},
|
|
1970
|
-
};
|
|
1971
|
-
},
|
|
1972
|
-
};
|
|
2252
|
+
return new MemoryReadStream(this, path, options);
|
|
1973
2253
|
}
|
|
1974
2254
|
async writeStream(path, source, options = {}) {
|
|
1975
2255
|
options.signal?.throwIfAborted();
|
|
@@ -2075,6 +2355,90 @@ const writeFileFastMethodNames = [
|
|
|
2075
2355
|
"addNode", "replaceData", "resolve", "permission", "validatePath", "mode",
|
|
2076
2356
|
"bytes", "allocate", "admitSize", "changed", "metadata", "fail",
|
|
2077
2357
|
];
|
|
2358
|
+
const mkdirFastMethodNames = [
|
|
2359
|
+
"mkdir", "lstat", "stat", "resolve", "permission", "validatePath", "mode",
|
|
2360
|
+
"addNode", "directory", "changed", "fail",
|
|
2361
|
+
];
|
|
2362
|
+
const rmFastMethodNames = [
|
|
2363
|
+
"rm", "lstat", "stat", "readdir", "entry", "resolve", "removeLocation",
|
|
2364
|
+
"permission", "validatePath", "terminalDot", "releaseNode", "changed", "fail",
|
|
2365
|
+
];
|
|
2366
|
+
const openRedirectFastMethodNames = [
|
|
2367
|
+
"open", "writeFile", "appendFile", "rename", "writeData", "prepareWrite", "openWrite",
|
|
2368
|
+
"resizeNode", "writeAt", "addNode", "replaceData", "resolve", "permission",
|
|
2369
|
+
"validatePath", "mode", "bytes", "allocate", "admitSize", "changed", "metadata",
|
|
2370
|
+
"releaseReference", "releaseNode", "fail",
|
|
2371
|
+
];
|
|
2372
|
+
export class MemoryRedirectHandle {
|
|
2373
|
+
constructor(fs, path, node, append) {
|
|
2374
|
+
this.fs = fs;
|
|
2375
|
+
this.path = path;
|
|
2376
|
+
this.node = node;
|
|
2377
|
+
this.append = append;
|
|
2378
|
+
this.position = 0;
|
|
2379
|
+
this.closed = false;
|
|
2380
|
+
}
|
|
2381
|
+
writeSync(chunk, signal) {
|
|
2382
|
+
signal?.throwIfAborted();
|
|
2383
|
+
if (this.closed)
|
|
2384
|
+
throw new FsError("EBADF", { syscall: "write", path: this.path });
|
|
2385
|
+
if (chunk.byteLength === 0)
|
|
2386
|
+
return;
|
|
2387
|
+
const pos = this.append ? this.node.byteLength : this.position;
|
|
2388
|
+
this.fs.writeAt(this.node, chunk, pos, "write", this.path);
|
|
2389
|
+
if (!this.append)
|
|
2390
|
+
this.position = pos + chunk.byteLength;
|
|
2391
|
+
}
|
|
2392
|
+
close() {
|
|
2393
|
+
if (this.closed)
|
|
2394
|
+
return;
|
|
2395
|
+
this.closed = true;
|
|
2396
|
+
this.fs.releaseReference(this.node, this.path);
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
export function tryOpenMemoryRedirectHandleSync(filesystem, path, append, mode, signal) {
|
|
2400
|
+
const mem = filesystem;
|
|
2401
|
+
const owner = ownedStores.get(mem);
|
|
2402
|
+
if (!owner ||
|
|
2403
|
+
mem.symlinkCount !== 0 ||
|
|
2404
|
+
mem.capabilities !== owner.capabilities ||
|
|
2405
|
+
activeConditionalMutations.has(mem.identityScope) ||
|
|
2406
|
+
!isStockMemoryMethods(mem, openRedirectFastMethodNames, false) ||
|
|
2407
|
+
!isCleanAbsolutePath(path) ||
|
|
2408
|
+
path === "/dev" ||
|
|
2409
|
+
path.startsWith("/dev/")) {
|
|
2410
|
+
return undefined;
|
|
2411
|
+
}
|
|
2412
|
+
signal?.throwIfAborted();
|
|
2413
|
+
const validMode = mem.mode(mode, 0o666, "open", path);
|
|
2414
|
+
const location = mem.resolve(path, "open", {
|
|
2415
|
+
followFinal: true,
|
|
2416
|
+
allowMissing: true,
|
|
2417
|
+
});
|
|
2418
|
+
let node = location.node;
|
|
2419
|
+
if (node) {
|
|
2420
|
+
if (node.type !== "file")
|
|
2421
|
+
mem.fail("EISDIR", "open", path);
|
|
2422
|
+
mem.permission(node, 2, "open", path);
|
|
2423
|
+
}
|
|
2424
|
+
else {
|
|
2425
|
+
mem.permission(location.parent, 3, "open", path);
|
|
2426
|
+
}
|
|
2427
|
+
const ledger = mem.ledger;
|
|
2428
|
+
ledger.reserve(path.length * 2, 1, "open", path);
|
|
2429
|
+
try {
|
|
2430
|
+
node ??= mem.openWrite(path, {}, "open", { location, mode: validMode, append: false });
|
|
2431
|
+
if (!append) {
|
|
2432
|
+
mem.resizeNode(node, 0, "open", path);
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
catch (error) {
|
|
2436
|
+
ledger.release(path.length * 2, 1);
|
|
2437
|
+
throw error;
|
|
2438
|
+
}
|
|
2439
|
+
node.references++;
|
|
2440
|
+
return new MemoryRedirectHandle(mem, path, node, append);
|
|
2441
|
+
}
|
|
2078
2442
|
export function utf8ByteLength(value) {
|
|
2079
2443
|
let bytes = 0;
|
|
2080
2444
|
for (let i = 0; i < value.length; i++) {
|
|
@@ -2255,3 +2619,75 @@ export function tryWriteMemoryFileSync(filesystem, path, data, append, mode, sig
|
|
|
2255
2619
|
mem.writeMemoryFileFast(path, data, append, mode);
|
|
2256
2620
|
return true;
|
|
2257
2621
|
}
|
|
2622
|
+
export function tryMkdirMemorySync(filesystem, path, recursive, mode, signal) {
|
|
2623
|
+
const mem = filesystem;
|
|
2624
|
+
const owner = ownedStores.get(mem);
|
|
2625
|
+
if (!owner ||
|
|
2626
|
+
mem.symlinkCount !== 0 ||
|
|
2627
|
+
mem.capabilities !== owner.capabilities ||
|
|
2628
|
+
!isStockMemoryMethods(mem, mkdirFastMethodNames, false) ||
|
|
2629
|
+
!isCleanAbsolutePath(path) ||
|
|
2630
|
+
path === "/dev" ||
|
|
2631
|
+
path.startsWith("/dev/")) {
|
|
2632
|
+
return false;
|
|
2633
|
+
}
|
|
2634
|
+
signal?.throwIfAborted();
|
|
2635
|
+
const validMode = mem.mode(mode, 0o777, "mkdir", path);
|
|
2636
|
+
if (recursive) {
|
|
2637
|
+
const node = mem.resolve(path, "mkdir", { createDirectories: validMode }).node;
|
|
2638
|
+
if (node.type !== "directory")
|
|
2639
|
+
mem.fail("EEXIST", "mkdir", path);
|
|
2640
|
+
return true;
|
|
2641
|
+
}
|
|
2642
|
+
const location = mem.resolve(path, "mkdir", { allowMissing: true, followFinal: false });
|
|
2643
|
+
if (location.node)
|
|
2644
|
+
mem.fail("EEXIST", "mkdir", path);
|
|
2645
|
+
mem.permission(location.parent, 3, "mkdir", path);
|
|
2646
|
+
mem.addDirectoryNode(location.parent, location.name, validMode, "mkdir", path);
|
|
2647
|
+
return true;
|
|
2648
|
+
}
|
|
2649
|
+
export function tryRmRfMemorySync(filesystem, path, signal) {
|
|
2650
|
+
const mem = filesystem;
|
|
2651
|
+
const owner = ownedStores.get(mem);
|
|
2652
|
+
if (!owner ||
|
|
2653
|
+
mem.symlinkCount !== 0 ||
|
|
2654
|
+
mem.capabilities !== owner.capabilities ||
|
|
2655
|
+
activeConditionalMutations.has(mem.identityScope) ||
|
|
2656
|
+
!isStockMemoryMethods(mem, rmFastMethodNames, false) ||
|
|
2657
|
+
!isCleanAbsolutePath(path) ||
|
|
2658
|
+
path === "/dev" ||
|
|
2659
|
+
path.startsWith("/dev/")) {
|
|
2660
|
+
return false;
|
|
2661
|
+
}
|
|
2662
|
+
signal?.throwIfAborted();
|
|
2663
|
+
let location;
|
|
2664
|
+
try {
|
|
2665
|
+
location = mem.resolve(path, "rm", noFollowResolveOptions);
|
|
2666
|
+
}
|
|
2667
|
+
catch (error) {
|
|
2668
|
+
if (error instanceof FsError && error.code === "ENOENT")
|
|
2669
|
+
return true;
|
|
2670
|
+
throw error;
|
|
2671
|
+
}
|
|
2672
|
+
mem.removeLocation(location, path, "rm", true);
|
|
2673
|
+
return true;
|
|
2674
|
+
}
|
|
2675
|
+
export function tryWriteMemoryFileInDirSync(filesystem, dirPrefix, name, data, append, mode, signal) {
|
|
2676
|
+
const mem = filesystem;
|
|
2677
|
+
const owner = ownedStores.get(mem);
|
|
2678
|
+
if (!owner ||
|
|
2679
|
+
mem.symlinkCount !== 0 ||
|
|
2680
|
+
mem.capabilities !== owner.capabilities ||
|
|
2681
|
+
!isStockMemoryMethods(mem, writeFileFastMethodNames, false) ||
|
|
2682
|
+
name.length === 0 ||
|
|
2683
|
+
name === "." ||
|
|
2684
|
+
name === ".." ||
|
|
2685
|
+
name.indexOf("/") !== -1 ||
|
|
2686
|
+
name.indexOf("\0") !== -1 ||
|
|
2687
|
+
dirPrefix.length + name.length > 65536) {
|
|
2688
|
+
return false;
|
|
2689
|
+
}
|
|
2690
|
+
signal?.throwIfAborted();
|
|
2691
|
+
mem.writeMemoryFileInDirFast(dirPrefix, name, data, append, mode);
|
|
2692
|
+
return true;
|
|
2693
|
+
}
|