@poe-platform/safe-fs 0.1.729 → 0.1.731

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.
@@ -16,12 +16,15 @@ import { normalizeMemoryFileSystemLimits } from "./limits.js";
16
16
  export { defaultMemoryFileSystemLimits } from "./limits.js";
17
17
  class MemoryFileNode {
18
18
  constructor(mode, ino, now, byteLength, allocation, view) {
19
+ this.revision = 0;
19
20
  this.mode = mode;
20
21
  this.ino = ino;
22
+ this.nlink = 1;
21
23
  this.atimeMs = now;
22
24
  this.mtimeMs = now;
23
25
  this.ctimeMs = now;
24
26
  this.birthtimeMs = now;
27
+ this.references = 0;
25
28
  this.byteLength = byteLength;
26
29
  this.allocation = allocation;
27
30
  this.view = view;
@@ -44,15 +47,250 @@ const EMPTY_ALLOC_BYTES = new Uint8Array(0);
44
47
  const SMALL_ALLOC_SLAB_SIZE = 8192;
45
48
  let smallAllocSlab = new Uint8Array(SMALL_ALLOC_SLAB_SIZE);
46
49
  let smallAllocOffset = 0;
50
+ const DUMMY_POOL_LEDGER = new MemoryLedger(normalizeMemoryFileSystemLimits({}));
51
+ const DUMMY_POOL_ALLOCATION = new MemoryAllocation(EMPTY_ALLOC_BYTES, DUMMY_POOL_LEDGER);
52
+ const DUMMY_POOL_FILE_NODE = new MemoryFileNode(0, 0, null, 0, DUMMY_POOL_ALLOCATION, DUMMY_POOL_ALLOCATION.data);
53
+ DUMMY_POOL_FILE_NODE.atimeMs = DUMMY_POOL_FILE_NODE.mtimeMs = DUMMY_POOL_FILE_NODE.ctimeMs = DUMMY_POOL_FILE_NODE.birthtimeMs = 1700000000000;
54
+ const defaultDateNow = Date.now;
55
+ let fastWriteCachedNow = Date.now();
56
+ let fastWriteNowTick = 0;
57
+ class FastDirectoryEntriesMap {
58
+ _table = new Int16Array(128).fill(-1);
59
+ _mask = 127;
60
+ _keys = new Array(64).fill("");
61
+ _vals = new Array(64).fill(DUMMY_POOL_FILE_NODE);
62
+ _next = 0;
63
+ size = 0;
64
+ [Symbol.toStringTag] = "Map";
65
+ _hash(k) {
66
+ let h = 5381;
67
+ for (let i = 0; i < k.length; i++) {
68
+ h = (((h & 0x1fffff) * 33) ^ k.charCodeAt(i)) & 0x3fffffff;
69
+ }
70
+ return h & this._mask;
71
+ }
72
+ get(k) {
73
+ if (this.size === 0)
74
+ return undefined;
75
+ let slot = this._hash(k);
76
+ const mask = this._mask;
77
+ const table = this._table;
78
+ const keys = this._keys;
79
+ while (true) {
80
+ const idx = table[slot];
81
+ if (idx === -1)
82
+ return undefined;
83
+ if (idx >= 0 && keys[idx] === k)
84
+ return this._vals[idx];
85
+ slot = (slot + 1) & mask;
86
+ }
87
+ }
88
+ has(k) {
89
+ return this.get(k) !== undefined;
90
+ }
91
+ _rebuild(newCap) {
92
+ const newTableLen = newCap * 2;
93
+ const newMask = newTableLen - 1;
94
+ const newTable = newCap <= 16384 ? new Int16Array(newTableLen).fill(-1) : new Int32Array(newTableLen).fill(-1);
95
+ const oldKeys = this._keys;
96
+ const oldVals = this._vals;
97
+ const oldNext = this._next;
98
+ const newKeys = newCap === oldKeys.length ? oldKeys : new Array(newCap).fill("");
99
+ const newVals = newCap === oldVals.length ? oldVals : new Array(newCap).fill(DUMMY_POOL_FILE_NODE);
100
+ let writeIdx = 0;
101
+ for (let i = 0; i < oldNext; i++) {
102
+ const v = oldVals[i];
103
+ if (v === DUMMY_POOL_FILE_NODE)
104
+ continue;
105
+ const k = oldKeys[i];
106
+ newKeys[writeIdx] = k;
107
+ newVals[writeIdx] = v;
108
+ let h = 5381;
109
+ for (let c = 0; c < k.length; c++) {
110
+ h = (((h & 0x1fffff) * 33) ^ k.charCodeAt(c)) & 0x3fffffff;
111
+ }
112
+ let slot = h & newMask;
113
+ while (newTable[slot] !== -1) {
114
+ slot = (slot + 1) & newMask;
115
+ }
116
+ newTable[slot] = writeIdx++;
117
+ }
118
+ if (newKeys === oldKeys) {
119
+ for (let i = writeIdx; i < oldNext; i++) {
120
+ newKeys[i] = "";
121
+ newVals[i] = DUMMY_POOL_FILE_NODE;
122
+ }
123
+ }
124
+ this._table = newTable;
125
+ this._mask = newMask;
126
+ this._keys = newKeys;
127
+ this._vals = newVals;
128
+ this._next = writeIdx;
129
+ }
130
+ set(k, v) {
131
+ if (this._next >= this._keys.length) {
132
+ this._rebuild(this.size * 2 <= this._keys.length ? this._keys.length : this._keys.length * 2);
133
+ }
134
+ let slot = this._hash(k);
135
+ const mask = this._mask;
136
+ const table = this._table;
137
+ const keys = this._keys;
138
+ let firstDeleted = -1;
139
+ while (true) {
140
+ const idx = table[slot];
141
+ if (idx === -1) {
142
+ const targetSlot = firstDeleted !== -1 ? firstDeleted : slot;
143
+ const entryIdx = this._next++;
144
+ table[targetSlot] = entryIdx;
145
+ keys[entryIdx] = k;
146
+ this._vals[entryIdx] = v;
147
+ this.size++;
148
+ return this;
149
+ }
150
+ if (idx === -2) {
151
+ if (firstDeleted === -1)
152
+ firstDeleted = slot;
153
+ }
154
+ else if (keys[idx] === k) {
155
+ this._vals[idx] = v;
156
+ return this;
157
+ }
158
+ slot = (slot + 1) & mask;
159
+ }
160
+ }
161
+ delete(k) {
162
+ if (this.size === 0)
163
+ return false;
164
+ let slot = this._hash(k);
165
+ const mask = this._mask;
166
+ const table = this._table;
167
+ const keys = this._keys;
168
+ while (true) {
169
+ const idx = table[slot];
170
+ if (idx === -1)
171
+ return false;
172
+ if (idx >= 0 && keys[idx] === k) {
173
+ if (this.size === 1) {
174
+ this.clear();
175
+ return true;
176
+ }
177
+ table[slot] = -2;
178
+ keys[idx] = "";
179
+ this._vals[idx] = DUMMY_POOL_FILE_NODE;
180
+ if (idx === this._next - 1)
181
+ this._next--;
182
+ this.size--;
183
+ return true;
184
+ }
185
+ slot = (slot + 1) & mask;
186
+ }
187
+ }
188
+ clear() {
189
+ if (this._next > 0) {
190
+ this._table.fill(-1);
191
+ for (let i = 0; i < this._next; i++) {
192
+ this._keys[i] = "";
193
+ this._vals[i] = DUMMY_POOL_FILE_NODE;
194
+ }
195
+ this._next = 0;
196
+ this.size = 0;
197
+ }
198
+ }
199
+ forEach(callbackfn, thisArg) {
200
+ const next = this._next;
201
+ const keys = this._keys;
202
+ const vals = this._vals;
203
+ for (let i = 0; i < next; i++) {
204
+ const v = vals[i];
205
+ if (v !== DUMMY_POOL_FILE_NODE) {
206
+ if (thisArg !== undefined)
207
+ callbackfn.call(thisArg, v, keys[i], this);
208
+ else
209
+ callbackfn(v, keys[i], this);
210
+ }
211
+ }
212
+ }
213
+ *keys() {
214
+ for (let i = 0; i < this._next; i++) {
215
+ if (this._vals[i] !== DUMMY_POOL_FILE_NODE)
216
+ yield this._keys[i];
217
+ }
218
+ }
219
+ *values() {
220
+ for (let i = 0; i < this._next; i++) {
221
+ const v = this._vals[i];
222
+ if (v !== DUMMY_POOL_FILE_NODE)
223
+ yield v;
224
+ }
225
+ }
226
+ *entries() {
227
+ for (let i = 0; i < this._next; i++) {
228
+ const v = this._vals[i];
229
+ if (v !== DUMMY_POOL_FILE_NODE)
230
+ yield [this._keys[i], v];
231
+ }
232
+ }
233
+ [Symbol.iterator]() {
234
+ return this.entries();
235
+ }
236
+ }
237
+ const SHARED_POOL_CAPACITY = 256;
238
+ const SHARED_DIR_POOL_CAPACITY = 64;
239
+ const sharedAllocationPool = new Array(SHARED_POOL_CAPACITY).fill(DUMMY_POOL_ALLOCATION);
240
+ let sharedAllocationPoolLen = 0;
241
+ const sharedFileNodePool = new Array(SHARED_POOL_CAPACITY).fill(DUMMY_POOL_FILE_NODE);
242
+ let sharedFileNodePoolLen = 0;
243
+ const sharedDirectoryNodePool = new Array(SHARED_DIR_POOL_CAPACITY);
244
+ let sharedDirectoryNodePoolLen = 0;
245
+ const sharedRemovedScratch = new Array(SHARED_POOL_CAPACITY).fill(DUMMY_POOL_FILE_NODE);
246
+ let sharedRemovedLen = 0;
247
+ let sharedRemovedKeyChars = 0;
248
+ let sharedRemovedKeyEntries = 0;
249
+ function collectRemovedChild(child, name) {
250
+ if (sharedRemovedLen < sharedRemovedScratch.length) {
251
+ sharedRemovedScratch[sharedRemovedLen++] = child;
252
+ }
253
+ else {
254
+ sharedRemovedScratch.push(child);
255
+ sharedRemovedLen++;
256
+ }
257
+ sharedRemovedKeyChars += name.length;
258
+ sharedRemovedKeyEntries++;
259
+ }
260
+ function replenishSharedMemoryPools() {
261
+ while (sharedAllocationPoolLen < 112) {
262
+ if (smallAllocOffset + 64 > SMALL_ALLOC_SLAB_SIZE) {
263
+ smallAllocSlab = new Uint8Array(SMALL_ALLOC_SLAB_SIZE);
264
+ smallAllocOffset = 0;
265
+ }
266
+ const slice = smallAllocSlab.subarray(smallAllocOffset, smallAllocOffset + 64);
267
+ smallAllocOffset += 64;
268
+ DUMMY_POOL_LEDGER.reserve(64, 0, "init", "/");
269
+ const alloc = new MemoryAllocation(slice, DUMMY_POOL_LEDGER);
270
+ alloc.release();
271
+ sharedAllocationPool[sharedAllocationPoolLen++] = alloc;
272
+ }
273
+ while (sharedFileNodePoolLen < 112) {
274
+ sharedFileNodePool[sharedFileNodePoolLen++] = new MemoryFileNode(0, 0, fastWriteCachedNow, 0, DUMMY_POOL_ALLOCATION, undefined);
275
+ }
276
+ while (sharedDirectoryNodePoolLen < 16) {
277
+ sharedDirectoryNodePool[sharedDirectoryNodePoolLen++] = new MemoryDirectoryNode(0, 0, fastWriteCachedNow);
278
+ }
279
+ }
47
280
  class MemoryDirectoryNode {
48
281
  constructor(mode, ino, now) {
282
+ this.revision = 0;
49
283
  this.mode = mode;
50
284
  this.ino = ino;
285
+ this.nlink = 1;
286
+ this.references = 0;
51
287
  this.atimeMs = now;
52
288
  this.mtimeMs = now;
53
289
  this.ctimeMs = now;
54
290
  this.birthtimeMs = now;
55
- this.entries = new Map();
291
+ this.entries = new FastDirectoryEntriesMap();
292
+ this.cachedNlink = 2;
293
+ this.cachedNlinkRev = 0;
56
294
  }
57
295
  }
58
296
  Object.assign(MemoryDirectoryNode.prototype, {
@@ -63,6 +301,8 @@ Object.assign(MemoryDirectoryNode.prototype, {
63
301
  cachedNlink: 2,
64
302
  cachedNlinkRev: 0,
65
303
  });
304
+ const DUMMY_POOL_DIR_NODE = new MemoryDirectoryNode(0, 0, null);
305
+ DUMMY_POOL_DIR_NODE.atimeMs = DUMMY_POOL_DIR_NODE.mtimeMs = DUMMY_POOL_DIR_NODE.ctimeMs = DUMMY_POOL_DIR_NODE.birthtimeMs = 1700000000000;
66
306
  class MemoryCache {
67
307
  allocations = [];
68
308
  files = [];
@@ -84,6 +324,9 @@ class MemoryCache {
84
324
  const typeModes = { file: 0o100000, directory: 0o040000, symlink: 0o120000 };
85
325
  const emptyResolveOptions = Object.freeze({});
86
326
  const noFollowResolveOptions = Object.freeze({ followFinal: false });
327
+ const sharedMkdirResolveOptions = { createDirectories: 0o777 };
328
+ const sharedAllowMissingNoFollowOptions = Object.freeze({ allowMissing: true, followFinal: false });
329
+ const sharedFastLocation = { node: undefined, parent: DUMMY_POOL_DIR_NODE, name: "", path: "" };
87
330
  const resolvedVoid = Promise.resolve();
88
331
  const preferredIoBlockSize = 64 * 1024;
89
332
  const ext4HtreeEof64 = (1n << 63n) - 1n;
@@ -271,7 +514,7 @@ export class MemoryFileSystem {
271
514
  permissions: true,
272
515
  timestamps: true,
273
516
  atomicRename: true,
274
- atomicFileStaging: true, retainedStagingCleanup: true, atomicStagingAncestry: true, atomicFileMutation: true, atomicEntryRemoval: true, atomicEntryRemovalReceipt: true, atomicTreeRemoval: true,
517
+ atomicFileStaging: true, retainedStagingCleanup: true, retainedStagingWrite: true, atomicStagingAncestry: true, atomicFileMutation: true, atomicEntryRemoval: true, atomicEntryRemovalReceipt: true, atomicTreeRemoval: true,
275
518
  synchronousDirectoryValidation: true, synchronousStagingResolution: true, guardedStagingPublication: true,
276
519
  atomicDirectoryMetadata: true,
277
520
  streamingRead: true,
@@ -289,6 +532,9 @@ export class MemoryFileSystem {
289
532
  symlinkCount = 0;
290
533
  constructor(options = {}) {
291
534
  this.ledger = new MemoryLedger(normalizeMemoryFileSystemLimits(options));
535
+ if (this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity) {
536
+ replenishSharedMemoryPools();
537
+ }
292
538
  memoryCaches.set(this.ledger, new MemoryCache());
293
539
  this.ledger.reserve(0, 1, "mkdir", "/");
294
540
  this.root = this.directory(0o755);
@@ -380,7 +626,7 @@ export class MemoryFileSystem {
380
626
  retained.set("/", this.root);
381
627
  }
382
628
  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"]);
629
+ const allowed = new Set(["mkdir", "rm", "rmdir", "rename", "symlink", "link", "chmod", "utimes", "writeFile", "appendFile", "writeStream", "writeFileConditional", "removeFileConditional", "removeEntryConditional", "prepareDirectory", "createStagedFile", "publishStagedFile", "removeStagedFile"]);
384
630
  const reads = new Set(["access", "capabilitiesFor", "compareEntry", "lstat", "stat", "readFile", "readStream", "readdir", "readlink", "realpath"]);
385
631
  const check = (path, followFinal) => {
386
632
  if (!roots.some(root => root === "/" || path === root || path.startsWith(`${root}/`)))
@@ -430,6 +676,24 @@ export class MemoryFileSystem {
430
676
  this.fail("EPERM", "writeStream", path);
431
677
  } };
432
678
  }
679
+ if (property === "createStagedFile")
680
+ return Reflect.apply(value, target, args).then(staging => {
681
+ const writer = staging.writer;
682
+ if (!writer)
683
+ return staging;
684
+ return Object.freeze({ ...staging, writer: Object.freeze({
685
+ write: async (bytes, controls = {}) => {
686
+ controls.signal?.throwIfAborted();
687
+ check(staging.file.path, true);
688
+ await writer.write(bytes, controls);
689
+ },
690
+ finish: async (controls = {}) => {
691
+ controls.signal?.throwIfAborted();
692
+ check(staging.file.path, true);
693
+ return writer.finish(controls);
694
+ },
695
+ }) });
696
+ });
433
697
  return Reflect.apply(value, target, args);
434
698
  };
435
699
  },
@@ -442,11 +706,17 @@ export class MemoryFileSystem {
442
706
  atimeMs: now, mtimeMs: now, ctimeMs: now, birthtimeMs: now,
443
707
  };
444
708
  }
445
- directory(mode) {
446
- const now = Date.now();
709
+ directory(mode, now = Date.now()) {
447
710
  const fullMode = typeModes.directory | mode;
448
711
  const ino = this.nextInode++;
449
- const pooled = memoryCaches.get(this.ledger)?.directories.pop();
712
+ let pooled;
713
+ if (sharedDirectoryNodePoolLen > 0) {
714
+ pooled = sharedDirectoryNodePool[--sharedDirectoryNodePoolLen];
715
+ sharedDirectoryNodePool[sharedDirectoryNodePoolLen] = DUMMY_POOL_DIR_NODE;
716
+ }
717
+ else {
718
+ pooled = memoryCaches.get(this.ledger)?.directories.pop();
719
+ }
450
720
  if (pooled) {
451
721
  pooled.mode = fullMode;
452
722
  pooled.ino = ino;
@@ -463,13 +733,13 @@ export class MemoryFileSystem {
463
733
  }
464
734
  return new MemoryDirectoryNode(fullMode, ino, now);
465
735
  }
466
- addDirectoryNode(parent, name, mode, syscall, path) {
736
+ addDirectoryNode(parent, name, mode, syscall, path, now = Date.now()) {
467
737
  const bytes = name.length * 2;
468
738
  this.ledger.reserve(bytes, 2, syscall, path);
469
739
  try {
470
- const node = this.directory(mode);
740
+ const node = this.directory(mode, now);
471
741
  parent.entries.set(name, node);
472
- this.changed(parent);
742
+ this.changed(parent, now);
473
743
  return node;
474
744
  }
475
745
  catch (error) {
@@ -511,11 +781,21 @@ export class MemoryFileSystem {
511
781
  this.totalBytes -= node.byteLength;
512
782
  const alloc = node.allocation;
513
783
  node.view = undefined;
784
+ node.allocation = DUMMY_POOL_ALLOCATION;
514
785
  alloc.release();
515
- if (alloc.isReleased64() && cache.allocations.length < 128) {
516
- cache.allocations.push(alloc);
786
+ if (alloc.isReleased64()) {
787
+ alloc.detachLedger(DUMMY_POOL_LEDGER);
788
+ if (this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity && sharedAllocationPoolLen < SHARED_POOL_CAPACITY) {
789
+ sharedAllocationPool[sharedAllocationPoolLen++] = alloc;
790
+ }
791
+ else if (cache.allocations.length < 128) {
792
+ cache.allocations.push(alloc);
793
+ }
794
+ }
795
+ if (sharedFileNodePoolLen < SHARED_POOL_CAPACITY) {
796
+ sharedFileNodePool[sharedFileNodePoolLen++] = node;
517
797
  }
518
- if (cache.files.length < 128) {
798
+ else if (cache.files.length < 128) {
519
799
  cache.files.push(node);
520
800
  }
521
801
  }
@@ -527,8 +807,13 @@ export class MemoryFileSystem {
527
807
  cache.lastFastDirPrefix = "";
528
808
  cache.lastFastDirNode = undefined;
529
809
  }
530
- if (node.entries.size === 0 && cache.directories.length < 32) {
531
- cache.directories.push(node);
810
+ if (node.entries.size === 0) {
811
+ if (sharedDirectoryNodePoolLen < SHARED_DIR_POOL_CAPACITY) {
812
+ sharedDirectoryNodePool[sharedDirectoryNodePoolLen++] = node;
813
+ }
814
+ else if (cache.directories.length < 32) {
815
+ cache.directories.push(node);
816
+ }
532
817
  }
533
818
  }
534
819
  }
@@ -898,13 +1183,21 @@ export class MemoryFileSystem {
898
1183
  this.ledger.fileSize(length, syscall, path);
899
1184
  this.ledger.reserve(length, 0, syscall, path);
900
1185
  if (length === 0) {
901
- return new MemoryAllocation(EMPTY_ALLOC_BYTES, this.ledger);
1186
+ return DUMMY_POOL_ALLOCATION;
902
1187
  }
903
- const allocations = memoryCaches.get(this.ledger).allocations;
904
- if (length === 64 && allocations.length > 0) {
905
- const pooled = allocations.pop();
906
- pooled.reuse();
907
- return pooled;
1188
+ if (length === 64) {
1189
+ let pooled;
1190
+ if (this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity && sharedAllocationPoolLen > 0) {
1191
+ pooled = sharedAllocationPool[--sharedAllocationPoolLen];
1192
+ sharedAllocationPool[sharedAllocationPoolLen] = DUMMY_POOL_ALLOCATION;
1193
+ }
1194
+ else {
1195
+ pooled = memoryCaches.get(this.ledger).allocations.pop();
1196
+ }
1197
+ if (pooled) {
1198
+ pooled.reuse(this.ledger);
1199
+ return pooled;
1200
+ }
908
1201
  }
909
1202
  try {
910
1203
  // A shared slab's backing buffer exceeds the admitted slice capacity.
@@ -1020,7 +1313,14 @@ export class MemoryFileSystem {
1020
1313
  const now = Date.now();
1021
1314
  const fileMode = typeModes.file | target.mode;
1022
1315
  const view = capacity === length ? allocation.data : undefined;
1023
- const pooled = memoryCaches.get(this.ledger).files.pop();
1316
+ let pooled;
1317
+ if (sharedFileNodePoolLen > 0) {
1318
+ pooled = sharedFileNodePool[--sharedFileNodePoolLen];
1319
+ sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
1320
+ }
1321
+ else {
1322
+ pooled = memoryCaches.get(this.ledger).files.pop();
1323
+ }
1024
1324
  if (pooled) {
1025
1325
  pooled.mode = fileMode;
1026
1326
  pooled.ino = this.nextInode++;
@@ -1056,11 +1356,14 @@ export class MemoryFileSystem {
1056
1356
  }
1057
1357
  }
1058
1358
  writeAt(node, data, position, syscall, path) {
1059
- if (data.byteLength === 0) {
1060
- this.changed(node);
1359
+ this.writeAtRange(node, data, data.byteLength, position, syscall, path, Date.now());
1360
+ }
1361
+ writeAtRange(node, data, dataLen, position, syscall, path, now) {
1362
+ if (dataLen === 0) {
1363
+ this.changed(node, now);
1061
1364
  return;
1062
1365
  }
1063
- const end = position + data.byteLength;
1366
+ const end = position + dataLen;
1064
1367
  this.ledger.fileSize(end, syscall, path);
1065
1368
  const curLen = node.byteLength;
1066
1369
  const length = Math.max(curLen, end);
@@ -1079,11 +1382,17 @@ export class MemoryFileSystem {
1079
1382
  }
1080
1383
  try {
1081
1384
  const storage = allocation.data;
1082
- if (allocation !== node.allocation)
1083
- storage.set(node.view ?? (curLen === node.allocation.data.byteLength ? node.allocation.data : node.allocation.data.subarray(0, curLen)));
1385
+ if (allocation !== node.allocation && curLen > 0) {
1386
+ storage.set(node.allocation.data.subarray(0, curLen));
1387
+ }
1084
1388
  if (position > curLen)
1085
1389
  storage.fill(0, curLen, position);
1086
- storage.set(data, position);
1390
+ if (dataLen === data.byteLength) {
1391
+ storage.set(data, position);
1392
+ }
1393
+ else {
1394
+ storage.set(data.subarray(0, dataLen), position);
1395
+ }
1087
1396
  }
1088
1397
  catch (error) {
1089
1398
  if (allocation !== node.allocation)
@@ -1097,7 +1406,7 @@ export class MemoryFileSystem {
1097
1406
  node.byteLength = length;
1098
1407
  node.view = length === allocation.data.byteLength ? allocation.data : undefined;
1099
1408
  }
1100
- this.changed(node);
1409
+ this.changed(node, now);
1101
1410
  }
1102
1411
  writeMemoryFileFast(path, data, append, mode) {
1103
1412
  const cache = memoryCaches.get(this.ledger);
@@ -1183,7 +1492,7 @@ export class MemoryFileSystem {
1183
1492
  return;
1184
1493
  }
1185
1494
  if (!current) {
1186
- const capacity = length;
1495
+ const capacity = length > 0 && length < 64 && this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity ? 64 : length;
1187
1496
  const allocation = this.allocate(capacity, syscall, path);
1188
1497
  try {
1189
1498
  allocation.data.set(data);
@@ -1191,8 +1500,15 @@ export class MemoryFileSystem {
1191
1500
  try {
1192
1501
  const now = Date.now();
1193
1502
  const fileMode = typeModes.file | mode;
1194
- const view = allocation.data;
1195
- let node = cache.files.pop();
1503
+ const view = capacity === length ? allocation.data : undefined;
1504
+ let node;
1505
+ if (sharedFileNodePoolLen > 0) {
1506
+ node = sharedFileNodePool[--sharedFileNodePoolLen];
1507
+ sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
1508
+ }
1509
+ else {
1510
+ node = cache.files.pop();
1511
+ }
1196
1512
  if (node) {
1197
1513
  node.mode = fileMode;
1198
1514
  node.ino = this.nextInode++;
@@ -1326,22 +1642,32 @@ export class MemoryFileSystem {
1326
1642
  return;
1327
1643
  }
1328
1644
  if (!current) {
1329
- const capacity = length;
1645
+ const capacity = length > 0 && length < 64 && this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity ? 64 : length;
1330
1646
  const allocation = this.allocate(capacity, syscall, name);
1331
1647
  try {
1332
1648
  allocation.data.set(data);
1333
1649
  this.ledger.reserve(nameBytes, 2, syscall, name);
1334
1650
  try {
1335
- const now = Date.now();
1651
+ const now = fastWriteCachedNow;
1336
1652
  const fileMode = typeModes.file | mode;
1337
- const view = allocation.data;
1338
- let node = cache.files.pop();
1653
+ const view = capacity === length ? allocation.data : undefined;
1654
+ let node;
1655
+ if (sharedFileNodePoolLen > 0) {
1656
+ node = sharedFileNodePool[--sharedFileNodePoolLen];
1657
+ sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
1658
+ }
1659
+ else {
1660
+ node = cache.files.pop();
1661
+ }
1339
1662
  if (node) {
1340
1663
  node.mode = fileMode;
1341
1664
  node.ino = this.nextInode++;
1342
- node.nlink = 1;
1343
- node.references = 0;
1344
- node.revision = 0;
1665
+ if (node.nlink !== 1)
1666
+ node.nlink = 1;
1667
+ if (node.references !== 0)
1668
+ node.references = 0;
1669
+ if (node.revision !== 0)
1670
+ node.revision = 0;
1345
1671
  node.atimeMs = now;
1346
1672
  node.mtimeMs = now;
1347
1673
  node.ctimeMs = now;
@@ -1517,13 +1843,16 @@ export class MemoryFileSystem {
1517
1843
  location.parent.references++;
1518
1844
  directory.references++;
1519
1845
  file.references++;
1520
- return Object.freeze({ ...staging, cleanup: this.retainStagingCleanup(staging, location.parent, directory, file, location.name, name) });
1846
+ return Object.freeze({ ...staging, ...this.retainStagingCleanup(staging, location.parent, directory, file, location.name, name) });
1521
1847
  }
1522
1848
  retainStagingCleanup(staging, parent, directory, file, directoryName, fileName) {
1523
1849
  // Clear this state on release, so keeping the closed handle does not keep
1524
1850
  // detached nodes alive outside the Memory ledger.
1525
1851
  let retained = { parent, directory, file };
1526
- return createStagingCleanup(staging.directory.path, options => {
1852
+ let expected = staging.file.stat;
1853
+ let writing = true;
1854
+ const receipt = () => ({ ...staging, file: { ...staging.file, stat: expected } });
1855
+ const ownedCleanup = createStagingCleanup(staging.directory.path, options => {
1527
1856
  options.signal?.throwIfAborted();
1528
1857
  const state = retained;
1529
1858
  if (state.parent.nlink === 0 || state.parent.entries.get(directoryName) !== state.directory)
@@ -1535,7 +1864,7 @@ export class MemoryFileSystem {
1535
1864
  const child = state.directory.entries.get(fileName);
1536
1865
  if (child && child !== state.file)
1537
1866
  this.fail("EAGAIN", "removeStagedFile", staging.file.path);
1538
- this.removeStagingLocations(staging, {
1867
+ this.removeStagingLocations(receipt(), {
1539
1868
  node: state.directory, parent: state.parent, name: directoryName, path: staging.directory.path,
1540
1869
  }, { node: child, parent: state.directory, name: fileName, path: staging.file.path });
1541
1870
  }, () => {
@@ -1545,6 +1874,43 @@ export class MemoryFileSystem {
1545
1874
  this.releaseReference(state.directory, staging.directory.path);
1546
1875
  this.releaseReference(state.parent, staging.parent.path);
1547
1876
  });
1877
+ const cleanup = Object.freeze({
1878
+ remove: (options) => { writing = false; return ownedCleanup.remove(options); },
1879
+ close: () => { writing = false; return ownedCleanup.close(); },
1880
+ });
1881
+ if (file.type !== "file")
1882
+ return { cleanup };
1883
+ const inspect = (options) => {
1884
+ options.signal?.throwIfAborted();
1885
+ if (!retained || !writing)
1886
+ this.fail("EBADF", "stagedWrite", staging.file.path);
1887
+ const location = this.stagingLocations(receipt());
1888
+ if (location.file.node !== retained.file)
1889
+ this.fail("EAGAIN", "stagedWrite", staging.file.path);
1890
+ this.expectEntry(retained.file, expected, staging.file.path);
1891
+ this.permission(retained.parent, 3, "stagedWrite", staging.parent.path);
1892
+ this.permission(retained.directory, 3, "stagedWrite", staging.directory.path);
1893
+ return retained.file;
1894
+ };
1895
+ return { cleanup, writer: Object.freeze({
1896
+ write: async (bytes, options = {}) => {
1897
+ const node = inspect(options);
1898
+ if (!(bytes instanceof Uint8Array))
1899
+ throw new TypeError("Staged writes require Uint8Array data");
1900
+ this.writeAt(node, bytes, node.byteLength, "stagedWrite", staging.file.path);
1901
+ expected = Object.freeze(this.snapshot(node));
1902
+ },
1903
+ finish: async (options = {}) => {
1904
+ const node = inspect(options);
1905
+ this.changed(node);
1906
+ // changed updates modification time; archive creation metadata is final.
1907
+ node.atimeMs = staging.file.stat.atimeMs;
1908
+ node.mtimeMs = staging.file.stat.mtimeMs;
1909
+ writing = false;
1910
+ expected = Object.freeze(this.snapshot(node));
1911
+ return expected;
1912
+ },
1913
+ }) };
1548
1914
  }
1549
1915
  async prepareDirectoryAncestry(ancestors, options = {}) {
1550
1916
  const entries = snapshotDirectoryAncestry(ancestors);
@@ -1879,7 +2245,7 @@ export class MemoryFileSystem {
1879
2245
  return Promise.reject(error);
1880
2246
  }
1881
2247
  }
1882
- removeLocation(location, path, syscall, recursive) {
2248
+ removeLocation(location, path, syscall, recursive, now = Date.now()) {
1883
2249
  const node = location.node;
1884
2250
  if (this.terminalDot(path))
1885
2251
  this.fail("EINVAL", syscall, path);
@@ -1890,39 +2256,42 @@ export class MemoryFileSystem {
1890
2256
  this.fail("EISDIR", syscall, path);
1891
2257
  const cache = memoryCaches.get(this.ledger);
1892
2258
  cache.clearWrites();
1893
- const removed = cache.removedScratch;
1894
- removed.length = 0;
1895
- removed.push(node);
1896
- let keyChars = 0;
1897
- let keyEntries = 0;
1898
- for (let index = 0; index < removed.length; index++) {
1899
- const entry = removed[index];
1900
- if (entry.type === "directory" && entry.entries.size > 0) {
1901
- this.permission(entry, 7, syscall, path);
1902
- for (const [name, child] of entry.entries) {
1903
- removed.push(child);
1904
- keyChars += name.length;
1905
- keyEntries++;
2259
+ const removed = sharedRemovedScratch;
2260
+ sharedRemovedLen = 1;
2261
+ removed[0] = node;
2262
+ sharedRemovedKeyChars = 0;
2263
+ sharedRemovedKeyEntries = 0;
2264
+ try {
2265
+ for (let index = 0; index < sharedRemovedLen; index++) {
2266
+ const entry = removed[index];
2267
+ if (entry.type === "directory" && entry.entries.size > 0) {
2268
+ this.permission(entry, 7, syscall, path);
2269
+ entry.entries.forEach(collectRemovedChild);
1906
2270
  }
1907
2271
  }
1908
- }
1909
- location.parent.entries.delete(location.name);
1910
- this.ledger.release((location.name.length + keyChars) * 2, 1 + keyEntries);
1911
- const now = Date.now();
1912
- for (let i = 0; i < removed.length; i++) {
1913
- const entry = removed[i];
1914
- if (entry.type === "directory") {
1915
- entry.entries.clear();
1916
- }
1917
- entry.nlink--;
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);
2272
+ location.parent.entries.delete(location.name);
2273
+ this.ledger.release((location.name.length + sharedRemovedKeyChars) * 2, 1 + sharedRemovedKeyEntries);
2274
+ const totalRemoved = sharedRemovedLen;
2275
+ for (let i = 0; i < totalRemoved; i++) {
2276
+ const entry = removed[i];
2277
+ removed[i] = DUMMY_POOL_FILE_NODE;
2278
+ if (entry.type === "directory") {
2279
+ entry.entries.clear();
2280
+ }
2281
+ entry.nlink--;
2282
+ if (entry.nlink !== 0 || entry.references !== 0) {
2283
+ entry.ctimeMs = now;
2284
+ entry.revision = Math.min(Number.MAX_SAFE_INTEGER + 1, entry.revision + 1);
2285
+ }
2286
+ this.releaseNode(entry);
1921
2287
  }
1922
- this.releaseNode(entry);
1923
2288
  }
1924
- removed.length = 0;
1925
- this.changed(location.parent);
2289
+ finally {
2290
+ for (let i = 0; i < sharedRemovedLen; i++)
2291
+ removed[i] = DUMMY_POOL_FILE_NODE;
2292
+ sharedRemovedLen = 0;
2293
+ }
2294
+ this.changed(location.parent, now);
1926
2295
  }
1927
2296
  unlink(path, options = {}) {
1928
2297
  // Do not allow an untyped recursive option to widen final-entry removal.
@@ -2378,24 +2747,46 @@ export class MemoryRedirectHandle {
2378
2747
  this.position = 0;
2379
2748
  this.closed = false;
2380
2749
  }
2381
- writeSync(chunk, signal) {
2750
+ reset(fs, path, node, append) {
2751
+ this.fs = fs;
2752
+ this.path = path;
2753
+ this.node = node;
2754
+ this.append = append;
2755
+ this.position = 0;
2756
+ this.closed = false;
2757
+ }
2758
+ writeRangeSync(src, len, signal) {
2382
2759
  signal?.throwIfAborted();
2383
2760
  if (this.closed)
2384
2761
  throw new FsError("EBADF", { syscall: "write", path: this.path });
2385
- if (chunk.byteLength === 0)
2762
+ if (len === 0)
2386
2763
  return;
2387
2764
  const pos = this.append ? this.node.byteLength : this.position;
2388
- this.fs.writeAt(this.node, chunk, pos, "write", this.path);
2765
+ const now = Date.now === defaultDateNow ? fastWriteCachedNow : Date.now();
2766
+ this.fs.writeAtRange(this.node, src, len, pos, "write", this.path, now);
2389
2767
  if (!this.append)
2390
- this.position = pos + chunk.byteLength;
2768
+ this.position = pos + len;
2769
+ }
2770
+ writeSync(chunk, signal) {
2771
+ this.writeRangeSync(chunk, chunk.byteLength, signal);
2391
2772
  }
2392
2773
  close() {
2393
2774
  if (this.closed)
2394
2775
  return;
2395
2776
  this.closed = true;
2396
- this.fs.releaseReference(this.node, this.path);
2777
+ const fs = this.fs;
2778
+ const node = this.node;
2779
+ const path = this.path;
2780
+ this.fs = undefined;
2781
+ this.node = DUMMY_POOL_FILE_NODE;
2782
+ this.path = "";
2783
+ fs.releaseReference(node, path);
2784
+ if (redirectHandlePool.handle === undefined) {
2785
+ redirectHandlePool.handle = this;
2786
+ }
2397
2787
  }
2398
2788
  }
2789
+ const redirectHandlePool = { handle: undefined };
2399
2790
  export function tryOpenMemoryRedirectHandleSync(filesystem, path, append, mode, signal) {
2400
2791
  const mem = filesystem;
2401
2792
  const owner = ownedStores.get(mem);
@@ -2405,38 +2796,118 @@ export function tryOpenMemoryRedirectHandleSync(filesystem, path, append, mode,
2405
2796
  activeConditionalMutations.has(mem.identityScope) ||
2406
2797
  !isStockMemoryMethods(mem, openRedirectFastMethodNames, false) ||
2407
2798
  !isCleanAbsolutePath(path) ||
2799
+ path === "/" ||
2408
2800
  path === "/dev" ||
2409
2801
  path.startsWith("/dev/")) {
2410
2802
  return undefined;
2411
2803
  }
2412
2804
  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);
2805
+ const validMode = (mode & 0o777) === mode
2806
+ ? mode
2807
+ : mem.mode(mode, 0o666, "open", path);
2808
+ const memInternal = mem;
2809
+ let parent = memInternal.root;
2810
+ let start = 1;
2811
+ let name = "";
2812
+ while (true) {
2813
+ memInternal.permission(parent, 1, "open", path);
2814
+ const slash = path.indexOf("/", start);
2815
+ if (slash === -1) {
2816
+ name = path.slice(start);
2817
+ if (exceedsComponentByteLimit(name))
2818
+ memInternal.fail("ENAMETOOLONG", "open", path);
2819
+ break;
2820
+ }
2821
+ const seg = path.slice(start, slash);
2822
+ if (exceedsComponentByteLimit(seg))
2823
+ memInternal.fail("ENAMETOOLONG", "open", path);
2824
+ const next = parent.entries.get(seg);
2825
+ if (!next)
2826
+ memInternal.fail("ENOENT", "open", path);
2827
+ if (next.type !== "directory")
2828
+ memInternal.fail("ENOTDIR", "open", path);
2829
+ parent = next;
2830
+ start = slash + 1;
2426
2831
  }
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 });
2832
+ const existing = parent.entries.get(name);
2833
+ const ledger = memInternal.ledger;
2834
+ let node;
2835
+ if (existing !== undefined) {
2836
+ if (existing.type !== "file")
2837
+ memInternal.fail("EISDIR", "open", path);
2838
+ memInternal.permission(existing, 2, "open", path);
2839
+ ledger.reserve(path.length * 2, 1, "open", path);
2840
+ node = existing;
2431
2841
  if (!append) {
2432
- mem.resizeNode(node, 0, "open", path);
2842
+ try {
2843
+ memInternal.resizeNode(node, 0, "open", path);
2844
+ }
2845
+ catch (error) {
2846
+ ledger.release(path.length * 2, 1);
2847
+ throw error;
2848
+ }
2849
+ }
2850
+ node.references++;
2851
+ }
2852
+ else {
2853
+ memInternal.permission(parent, 3, "open", path);
2854
+ const refBytes = path.length * 2;
2855
+ const nameBytes = name.length * 2;
2856
+ ledger.reserve(refBytes, 1, "open", path);
2857
+ try {
2858
+ ledger.fileSize(0, "open", path);
2859
+ ledger.reserve(nameBytes, 2, "open", path);
2860
+ }
2861
+ catch (error) {
2862
+ ledger.release(refBytes, 1);
2863
+ throw error;
2864
+ }
2865
+ const cache = memoryCaches.get(ledger);
2866
+ cache.clearWrites();
2867
+ const now = Date.now === defaultDateNow ? ((++fastWriteNowTick & 63) === 0 ? (fastWriteCachedNow = Date.now()) : fastWriteCachedNow) : Date.now();
2868
+ const fileMode = typeModes.file | validMode;
2869
+ let newNode;
2870
+ if (sharedFileNodePoolLen > 0) {
2871
+ newNode = sharedFileNodePool[--sharedFileNodePoolLen];
2872
+ sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
2873
+ }
2874
+ else {
2875
+ newNode = cache.files.pop();
2876
+ }
2877
+ if (newNode) {
2878
+ newNode.mode = fileMode;
2879
+ newNode.ino = memInternal.nextInode++;
2880
+ newNode.nlink = 1;
2881
+ newNode.references = 1;
2882
+ newNode.revision = 0;
2883
+ newNode.atimeMs = now;
2884
+ newNode.mtimeMs = now;
2885
+ newNode.ctimeMs = now;
2886
+ newNode.birthtimeMs = now;
2887
+ newNode.byteLength = 0;
2888
+ newNode.view = EMPTY_ALLOC_BYTES;
2889
+ newNode.allocation = DUMMY_POOL_ALLOCATION;
2890
+ }
2891
+ else {
2892
+ newNode = new MemoryFileNode(fileMode, memInternal.nextInode++, now, 0, DUMMY_POOL_ALLOCATION, EMPTY_ALLOC_BYTES);
2893
+ newNode.references = 1;
2894
+ }
2895
+ const prevNlink = parent.cachedNlinkRev === parent.revision ? parent.cachedNlink : (parent.entries.size === 0 ? 2 : undefined);
2896
+ parent.entries.set(name, newNode);
2897
+ parent.revision = parent.revision < 1073741823 ? (parent.revision + 1) | 0 : Math.min(Number.MAX_SAFE_INTEGER + 1, parent.revision + 1);
2898
+ parent.mtimeMs = parent.ctimeMs = now;
2899
+ if (prevNlink !== undefined) {
2900
+ parent.cachedNlink = prevNlink;
2901
+ parent.cachedNlinkRev = parent.revision;
2433
2902
  }
2903
+ node = newNode;
2434
2904
  }
2435
- catch (error) {
2436
- ledger.release(path.length * 2, 1);
2437
- throw error;
2905
+ const handle = redirectHandlePool.handle;
2906
+ if (handle !== undefined) {
2907
+ redirectHandlePool.handle = undefined;
2908
+ handle.reset(mem, path, node, append);
2909
+ return handle;
2438
2910
  }
2439
- node.references++;
2440
2911
  return new MemoryRedirectHandle(mem, path, node, append);
2441
2912
  }
2442
2913
  export function utf8ByteLength(value) {
@@ -2574,7 +3045,8 @@ export function tryReadMemoryFileViewSync(filesystem, path, maxBytes, signal) {
2574
3045
  }
2575
3046
  export function tryGetMemoryDirectoryEntryNamesSync(filesystem, path) {
2576
3047
  const mem = filesystem;
2577
- if (!ownedStores.has(mem) || mem.symlinkCount !== 0 || !isStockMemoryMethods(mem, readFileFastMethodNames, false))
3048
+ const owner = ownedStores.get(mem);
3049
+ if (!owner || mem.symlinkCount !== 0 || !isStockMemoryMethods(mem, readFileFastMethodNames, false))
2578
3050
  return undefined;
2579
3051
  const root = mem.root;
2580
3052
  if (path === "/") {
@@ -2584,6 +3056,16 @@ export function tryGetMemoryDirectoryEntryNamesSync(filesystem, path) {
2584
3056
  }
2585
3057
  if (!isCleanAbsolutePath(path) || path === "/dev" || path.startsWith("/dev/"))
2586
3058
  return undefined;
3059
+ const cache = memoryCaches.get(owner.ledger);
3060
+ const fastDir = cache?.lastFastDirNode;
3061
+ const fastPrefix = cache?.lastFastDirPrefix;
3062
+ if (fastDir !== undefined &&
3063
+ fastDir.nlink !== 0 &&
3064
+ ((fastDir.mode >> 6) & 4) === 4 &&
3065
+ fastPrefix !== undefined &&
3066
+ (fastPrefix === path || (fastPrefix.length === path.length + 1 && fastPrefix.charCodeAt(path.length) === 47 && fastPrefix.startsWith(path)))) {
3067
+ return fastDir.entries;
3068
+ }
2587
3069
  let current = root;
2588
3070
  let start = 1;
2589
3071
  while (true) {
@@ -2634,16 +3116,34 @@ export function tryMkdirMemorySync(filesystem, path, recursive, mode, signal) {
2634
3116
  signal?.throwIfAborted();
2635
3117
  const validMode = mem.mode(mode, 0o777, "mkdir", path);
2636
3118
  if (recursive) {
2637
- const node = mem.resolve(path, "mkdir", { createDirectories: validMode }).node;
2638
- if (node.type !== "directory")
2639
- mem.fail("EEXIST", "mkdir", path);
3119
+ let current = owner.root;
3120
+ let start = 1;
3121
+ while (true) {
3122
+ mem.permission(current, 1, "mkdir", path);
3123
+ const slash = path.indexOf("/", start);
3124
+ const seg = slash === -1 ? path.slice(start) : path.slice(start, slash);
3125
+ if (exceedsComponentByteLimit(seg))
3126
+ mem.fail("ENAMETOOLONG", "mkdir", path);
3127
+ let next = current.entries.get(seg);
3128
+ if (!next) {
3129
+ mem.permission(current, 3, "mkdir", path);
3130
+ next = mem.addDirectoryNode(current, seg, validMode, "mkdir", path, fastWriteCachedNow);
3131
+ }
3132
+ else if (next.type !== "directory") {
3133
+ mem.fail(slash === -1 ? "EEXIST" : "ENOTDIR", "mkdir", path);
3134
+ }
3135
+ if (slash === -1)
3136
+ break;
3137
+ current = next;
3138
+ start = slash + 1;
3139
+ }
2640
3140
  return true;
2641
3141
  }
2642
- const location = mem.resolve(path, "mkdir", { allowMissing: true, followFinal: false });
3142
+ const location = mem.resolve(path, "mkdir", sharedAllowMissingNoFollowOptions);
2643
3143
  if (location.node)
2644
3144
  mem.fail("EEXIST", "mkdir", path);
2645
3145
  mem.permission(location.parent, 3, "mkdir", path);
2646
- mem.addDirectoryNode(location.parent, location.name, validMode, "mkdir", path);
3146
+ mem.addDirectoryNode(location.parent, location.name, validMode, "mkdir", path, fastWriteCachedNow);
2647
3147
  return true;
2648
3148
  }
2649
3149
  export function tryRmRfMemorySync(filesystem, path, signal) {
@@ -2660,16 +3160,46 @@ export function tryRmRfMemorySync(filesystem, path, signal) {
2660
3160
  return false;
2661
3161
  }
2662
3162
  signal?.throwIfAborted();
2663
- let location;
3163
+ let current = owner.root;
3164
+ let start = 1;
3165
+ let targetNode;
3166
+ let targetName = "";
3167
+ while (true) {
3168
+ mem.permission(current, 1, "rm", path);
3169
+ const slash = path.indexOf("/", start);
3170
+ if (slash === -1) {
3171
+ targetName = path.slice(start);
3172
+ if (exceedsComponentByteLimit(targetName))
3173
+ mem.fail("ENAMETOOLONG", "rm", path);
3174
+ targetNode = current.entries.get(targetName);
3175
+ if (!targetNode)
3176
+ return true;
3177
+ break;
3178
+ }
3179
+ const seg = path.slice(start, slash);
3180
+ if (exceedsComponentByteLimit(seg))
3181
+ mem.fail("ENAMETOOLONG", "rm", path);
3182
+ const next = current.entries.get(seg);
3183
+ if (!next)
3184
+ return true;
3185
+ if (next.type !== "directory")
3186
+ mem.fail("ENOTDIR", "rm", path);
3187
+ current = next;
3188
+ start = slash + 1;
3189
+ }
3190
+ sharedFastLocation.node = targetNode;
3191
+ sharedFastLocation.parent = current;
3192
+ sharedFastLocation.name = targetName;
3193
+ sharedFastLocation.path = path;
2664
3194
  try {
2665
- location = mem.resolve(path, "rm", noFollowResolveOptions);
3195
+ mem.removeLocation(sharedFastLocation, path, "rm", true, fastWriteCachedNow);
2666
3196
  }
2667
- catch (error) {
2668
- if (error instanceof FsError && error.code === "ENOENT")
2669
- return true;
2670
- throw error;
3197
+ finally {
3198
+ sharedFastLocation.node = undefined;
3199
+ sharedFastLocation.parent = DUMMY_POOL_DIR_NODE;
3200
+ sharedFastLocation.name = "";
3201
+ sharedFastLocation.path = "";
2671
3202
  }
2672
- mem.removeLocation(location, path, "rm", true);
2673
3203
  return true;
2674
3204
  }
2675
3205
  export function tryWriteMemoryFileInDirSync(filesystem, dirPrefix, name, data, append, mode, signal) {