@poe-platform/safe-fs 0.1.730 → 0.1.732
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/safe-fs/contracts/virtual-path.d.ts +1 -0
- package/dist/safe-fs/contracts/virtual-path.js +68 -8
- package/dist/safe-fs/fs/devices/index.js +11 -2
- package/dist/safe-fs/fs/memory/index.d.ts +49 -8
- package/dist/safe-fs/fs/memory/index.js +1057 -142
- package/dist/safe-fs/fs/memory/ledger.d.ts +2 -0
- package/dist/safe-fs/fs/memory/ledger.js +11 -0
- package/package.json +1 -1
|
@@ -15,16 +15,20 @@ 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
|
-
constructor(mode, ino, now, byteLength, allocation, view) {
|
|
18
|
+
constructor(mode, ino, now, byteLength, allocation, view, sourceRef) {
|
|
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;
|
|
31
|
+
this.sourceRef = sourceRef;
|
|
28
32
|
}
|
|
29
33
|
get data() {
|
|
30
34
|
return this.view ??= (this.byteLength === this.allocation.data.byteLength ? this.allocation.data : this.allocation.data.subarray(0, this.byteLength));
|
|
@@ -39,6 +43,7 @@ Object.assign(MemoryFileNode.prototype, {
|
|
|
39
43
|
revision: 0,
|
|
40
44
|
nlink: 1,
|
|
41
45
|
references: 0,
|
|
46
|
+
sourceRef: undefined,
|
|
42
47
|
});
|
|
43
48
|
const EMPTY_ALLOC_BYTES = new Uint8Array(0);
|
|
44
49
|
const SMALL_ALLOC_SLAB_SIZE = 8192;
|
|
@@ -46,19 +51,299 @@ let smallAllocSlab = new Uint8Array(SMALL_ALLOC_SLAB_SIZE);
|
|
|
46
51
|
let smallAllocOffset = 0;
|
|
47
52
|
const DUMMY_POOL_LEDGER = new MemoryLedger(normalizeMemoryFileSystemLimits({}));
|
|
48
53
|
const DUMMY_POOL_ALLOCATION = new MemoryAllocation(EMPTY_ALLOC_BYTES, DUMMY_POOL_LEDGER);
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
54
|
+
const DUMMY_POOL_FILE_NODE = new MemoryFileNode(0, 0, null, 0, DUMMY_POOL_ALLOCATION, DUMMY_POOL_ALLOCATION.data);
|
|
55
|
+
DUMMY_POOL_FILE_NODE.atimeMs = DUMMY_POOL_FILE_NODE.mtimeMs = DUMMY_POOL_FILE_NODE.ctimeMs = DUMMY_POOL_FILE_NODE.birthtimeMs = 1700000000000;
|
|
56
|
+
const defaultDateNow = Date.now;
|
|
57
|
+
let fastWriteCachedNow = Date.now();
|
|
58
|
+
let fastWriteNowTick = 0;
|
|
59
|
+
const lastFastMapMiss = {
|
|
60
|
+
map: undefined, key: "", slot: -1,
|
|
61
|
+
};
|
|
62
|
+
class FastDirectoryEntriesMap {
|
|
63
|
+
_table = new Int16Array(128).fill(-1);
|
|
64
|
+
_mask = 127;
|
|
65
|
+
_keys = new Array(64).fill("");
|
|
66
|
+
_vals = new Array(64).fill(DUMMY_POOL_FILE_NODE);
|
|
67
|
+
_next = 0;
|
|
68
|
+
size = 0;
|
|
69
|
+
[Symbol.toStringTag] = "Map";
|
|
70
|
+
_hash(k) {
|
|
71
|
+
let h = 5381;
|
|
72
|
+
for (let i = 0; i < k.length; i++) {
|
|
73
|
+
h = (((h & 0x1fffff) * 33) ^ k.charCodeAt(i)) & 0x3fffffff;
|
|
74
|
+
}
|
|
75
|
+
return h & this._mask;
|
|
76
|
+
}
|
|
77
|
+
get(k) {
|
|
78
|
+
if (this.size === 0)
|
|
79
|
+
return undefined;
|
|
80
|
+
let slot = this._hash(k);
|
|
81
|
+
const mask = this._mask;
|
|
82
|
+
const table = this._table;
|
|
83
|
+
const keys = this._keys;
|
|
84
|
+
while (true) {
|
|
85
|
+
const idx = table[slot];
|
|
86
|
+
if (idx === -1)
|
|
87
|
+
return undefined;
|
|
88
|
+
if (idx >= 0 && keys[idx] === k)
|
|
89
|
+
return this._vals[idx];
|
|
90
|
+
slot = (slot + 1) & mask;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
getForWrite(k) {
|
|
94
|
+
return this.getForWriteWithHash(k, this._hash(k));
|
|
95
|
+
}
|
|
96
|
+
getForWriteWithHash(k, h) {
|
|
97
|
+
let slot = h & this._mask;
|
|
98
|
+
if (this.size === 0) {
|
|
99
|
+
lastFastMapMiss.map = this;
|
|
100
|
+
lastFastMapMiss.key = k;
|
|
101
|
+
lastFastMapMiss.slot = slot;
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
const mask = this._mask;
|
|
105
|
+
const table = this._table;
|
|
106
|
+
const keys = this._keys;
|
|
107
|
+
let firstDeleted = -1;
|
|
108
|
+
while (true) {
|
|
109
|
+
const idx = table[slot];
|
|
110
|
+
if (idx === -1) {
|
|
111
|
+
lastFastMapMiss.map = this;
|
|
112
|
+
lastFastMapMiss.key = k;
|
|
113
|
+
lastFastMapMiss.slot = firstDeleted !== -1 ? firstDeleted : slot;
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
if (idx === -2) {
|
|
117
|
+
if (firstDeleted === -1)
|
|
118
|
+
firstDeleted = slot;
|
|
119
|
+
}
|
|
120
|
+
else if (keys[idx] === k) {
|
|
121
|
+
return this._vals[idx];
|
|
122
|
+
}
|
|
123
|
+
slot = (slot + 1) & mask;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
getSubstr(s, start, end) {
|
|
127
|
+
if (this.size === 0)
|
|
128
|
+
return undefined;
|
|
129
|
+
const len = end - start;
|
|
130
|
+
let h = 5381;
|
|
131
|
+
for (let i = start; i < end; i++) {
|
|
132
|
+
h = (((h & 0x1fffff) * 33) ^ s.charCodeAt(i)) & 0x3fffffff;
|
|
133
|
+
}
|
|
134
|
+
let slot = h & this._mask;
|
|
135
|
+
const mask = this._mask;
|
|
136
|
+
const table = this._table;
|
|
137
|
+
const keys = this._keys;
|
|
138
|
+
while (true) {
|
|
139
|
+
const idx = table[slot];
|
|
140
|
+
if (idx === -1)
|
|
141
|
+
return undefined;
|
|
142
|
+
if (idx >= 0) {
|
|
143
|
+
const k = keys[idx];
|
|
144
|
+
if (k.length === len) {
|
|
145
|
+
let match = true;
|
|
146
|
+
for (let i = 0; i < len; i++) {
|
|
147
|
+
if (k.charCodeAt(i) !== s.charCodeAt(start + i)) {
|
|
148
|
+
match = false;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (match)
|
|
153
|
+
return this._vals[idx];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
slot = (slot + 1) & mask;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
has(k) {
|
|
160
|
+
return this.get(k) !== undefined;
|
|
161
|
+
}
|
|
162
|
+
_rebuild(newCap) {
|
|
163
|
+
const newTableLen = newCap * 2;
|
|
164
|
+
const newMask = newTableLen - 1;
|
|
165
|
+
const newTable = newCap <= 16384 ? new Int16Array(newTableLen).fill(-1) : new Int32Array(newTableLen).fill(-1);
|
|
166
|
+
const oldKeys = this._keys;
|
|
167
|
+
const oldVals = this._vals;
|
|
168
|
+
const oldNext = this._next;
|
|
169
|
+
const newKeys = newCap === oldKeys.length ? oldKeys : new Array(newCap).fill("");
|
|
170
|
+
const newVals = newCap === oldVals.length ? oldVals : new Array(newCap).fill(DUMMY_POOL_FILE_NODE);
|
|
171
|
+
let writeIdx = 0;
|
|
172
|
+
for (let i = 0; i < oldNext; i++) {
|
|
173
|
+
const v = oldVals[i];
|
|
174
|
+
if (v === DUMMY_POOL_FILE_NODE)
|
|
175
|
+
continue;
|
|
176
|
+
const k = oldKeys[i];
|
|
177
|
+
newKeys[writeIdx] = k;
|
|
178
|
+
newVals[writeIdx] = v;
|
|
179
|
+
let h = 5381;
|
|
180
|
+
for (let c = 0; c < k.length; c++) {
|
|
181
|
+
h = (((h & 0x1fffff) * 33) ^ k.charCodeAt(c)) & 0x3fffffff;
|
|
182
|
+
}
|
|
183
|
+
let slot = h & newMask;
|
|
184
|
+
while (newTable[slot] !== -1) {
|
|
185
|
+
slot = (slot + 1) & newMask;
|
|
186
|
+
}
|
|
187
|
+
newTable[slot] = writeIdx++;
|
|
188
|
+
}
|
|
189
|
+
if (newKeys === oldKeys) {
|
|
190
|
+
for (let i = writeIdx; i < oldNext; i++) {
|
|
191
|
+
newKeys[i] = "";
|
|
192
|
+
newVals[i] = DUMMY_POOL_FILE_NODE;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
this._table = newTable;
|
|
196
|
+
this._mask = newMask;
|
|
197
|
+
this._keys = newKeys;
|
|
198
|
+
this._vals = newVals;
|
|
199
|
+
this._next = writeIdx;
|
|
200
|
+
}
|
|
201
|
+
set(k, v) {
|
|
202
|
+
if (this === lastFastMapMiss.map && k === lastFastMapMiss.key && this._next < this._keys.length) {
|
|
203
|
+
const entryIdx = this._next++;
|
|
204
|
+
this._table[lastFastMapMiss.slot] = entryIdx;
|
|
205
|
+
this._keys[entryIdx] = k;
|
|
206
|
+
this._vals[entryIdx] = v;
|
|
207
|
+
this.size++;
|
|
208
|
+
lastFastMapMiss.map = undefined;
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
211
|
+
if (this._next >= this._keys.length) {
|
|
212
|
+
this._rebuild(this.size * 2 <= this._keys.length ? this._keys.length : this._keys.length * 2);
|
|
213
|
+
}
|
|
214
|
+
let slot = this._hash(k);
|
|
215
|
+
const mask = this._mask;
|
|
216
|
+
const table = this._table;
|
|
217
|
+
const keys = this._keys;
|
|
218
|
+
let firstDeleted = -1;
|
|
219
|
+
while (true) {
|
|
220
|
+
const idx = table[slot];
|
|
221
|
+
if (idx === -1) {
|
|
222
|
+
const targetSlot = firstDeleted !== -1 ? firstDeleted : slot;
|
|
223
|
+
const entryIdx = this._next++;
|
|
224
|
+
table[targetSlot] = entryIdx;
|
|
225
|
+
keys[entryIdx] = k;
|
|
226
|
+
this._vals[entryIdx] = v;
|
|
227
|
+
this.size++;
|
|
228
|
+
return this;
|
|
229
|
+
}
|
|
230
|
+
if (idx === -2) {
|
|
231
|
+
if (firstDeleted === -1)
|
|
232
|
+
firstDeleted = slot;
|
|
233
|
+
}
|
|
234
|
+
else if (keys[idx] === k) {
|
|
235
|
+
this._vals[idx] = v;
|
|
236
|
+
return this;
|
|
237
|
+
}
|
|
238
|
+
slot = (slot + 1) & mask;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
delete(k) {
|
|
242
|
+
if (this.size === 0)
|
|
243
|
+
return false;
|
|
244
|
+
let slot = this._hash(k);
|
|
245
|
+
const mask = this._mask;
|
|
246
|
+
const table = this._table;
|
|
247
|
+
const keys = this._keys;
|
|
248
|
+
while (true) {
|
|
249
|
+
const idx = table[slot];
|
|
250
|
+
if (idx === -1)
|
|
251
|
+
return false;
|
|
252
|
+
if (idx >= 0 && keys[idx] === k) {
|
|
253
|
+
if (this.size === 1) {
|
|
254
|
+
this.clear();
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
table[slot] = -2;
|
|
258
|
+
keys[idx] = "";
|
|
259
|
+
this._vals[idx] = DUMMY_POOL_FILE_NODE;
|
|
260
|
+
if (idx === this._next - 1)
|
|
261
|
+
this._next--;
|
|
262
|
+
this.size--;
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
slot = (slot + 1) & mask;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
clear() {
|
|
269
|
+
if (this._next > 0) {
|
|
270
|
+
this._table.fill(-1);
|
|
271
|
+
for (let i = 0; i < this._next; i++) {
|
|
272
|
+
this._keys[i] = "";
|
|
273
|
+
this._vals[i] = DUMMY_POOL_FILE_NODE;
|
|
274
|
+
}
|
|
275
|
+
this._next = 0;
|
|
276
|
+
this.size = 0;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
forEach(callbackfn, thisArg) {
|
|
280
|
+
const next = this._next;
|
|
281
|
+
const keys = this._keys;
|
|
282
|
+
const vals = this._vals;
|
|
283
|
+
for (let i = 0; i < next; i++) {
|
|
284
|
+
const v = vals[i];
|
|
285
|
+
if (v !== DUMMY_POOL_FILE_NODE) {
|
|
286
|
+
if (thisArg !== undefined)
|
|
287
|
+
callbackfn.call(thisArg, v, keys[i], this);
|
|
288
|
+
else
|
|
289
|
+
callbackfn(v, keys[i], this);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
*keys() {
|
|
294
|
+
for (let i = 0; i < this._next; i++) {
|
|
295
|
+
if (this._vals[i] !== DUMMY_POOL_FILE_NODE)
|
|
296
|
+
yield this._keys[i];
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
*values() {
|
|
300
|
+
for (let i = 0; i < this._next; i++) {
|
|
301
|
+
const v = this._vals[i];
|
|
302
|
+
if (v !== DUMMY_POOL_FILE_NODE)
|
|
303
|
+
yield v;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
*entries() {
|
|
307
|
+
for (let i = 0; i < this._next; i++) {
|
|
308
|
+
const v = this._vals[i];
|
|
309
|
+
if (v !== DUMMY_POOL_FILE_NODE)
|
|
310
|
+
yield [this._keys[i], v];
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
[Symbol.iterator]() {
|
|
314
|
+
return this.entries();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
const SHARED_POOL_CAPACITY = 256;
|
|
318
|
+
const SHARED_DIR_POOL_CAPACITY = 64;
|
|
319
|
+
const sharedAllocationPool = new Array(SHARED_POOL_CAPACITY).fill(DUMMY_POOL_ALLOCATION);
|
|
320
|
+
let sharedAllocationPoolLen = 0;
|
|
321
|
+
const SHARED_LARGE_POOL_CAPACITY = 8;
|
|
322
|
+
const sharedLargeAllocationPool = new Array(SHARED_LARGE_POOL_CAPACITY).fill(DUMMY_POOL_ALLOCATION);
|
|
323
|
+
let sharedLargeAllocationPoolLen = 0;
|
|
324
|
+
const sharedMediumAllocationPool = new Array(SHARED_LARGE_POOL_CAPACITY).fill(DUMMY_POOL_ALLOCATION);
|
|
325
|
+
let sharedMediumAllocationPoolLen = 0;
|
|
326
|
+
const sharedFileNodePool = new Array(SHARED_POOL_CAPACITY).fill(DUMMY_POOL_FILE_NODE);
|
|
327
|
+
let sharedFileNodePoolLen = 0;
|
|
328
|
+
const sharedDirectoryNodePool = new Array(SHARED_DIR_POOL_CAPACITY);
|
|
329
|
+
let sharedDirectoryNodePoolLen = 0;
|
|
330
|
+
const sharedRemovedScratch = new Array(SHARED_POOL_CAPACITY).fill(DUMMY_POOL_FILE_NODE);
|
|
331
|
+
let sharedRemovedLen = 0;
|
|
53
332
|
let sharedRemovedKeyChars = 0;
|
|
54
333
|
let sharedRemovedKeyEntries = 0;
|
|
55
334
|
function collectRemovedChild(child, name) {
|
|
56
|
-
sharedRemovedScratch.
|
|
335
|
+
if (sharedRemovedLen < sharedRemovedScratch.length) {
|
|
336
|
+
sharedRemovedScratch[sharedRemovedLen++] = child;
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
sharedRemovedScratch.push(child);
|
|
340
|
+
sharedRemovedLen++;
|
|
341
|
+
}
|
|
57
342
|
sharedRemovedKeyChars += name.length;
|
|
58
343
|
sharedRemovedKeyEntries++;
|
|
59
344
|
}
|
|
60
345
|
function replenishSharedMemoryPools() {
|
|
61
|
-
while (
|
|
346
|
+
while (sharedAllocationPoolLen < 112) {
|
|
62
347
|
if (smallAllocOffset + 64 > SMALL_ALLOC_SLAB_SIZE) {
|
|
63
348
|
smallAllocSlab = new Uint8Array(SMALL_ALLOC_SLAB_SIZE);
|
|
64
349
|
smallAllocOffset = 0;
|
|
@@ -68,24 +353,41 @@ function replenishSharedMemoryPools() {
|
|
|
68
353
|
DUMMY_POOL_LEDGER.reserve(64, 0, "init", "/");
|
|
69
354
|
const alloc = new MemoryAllocation(slice, DUMMY_POOL_LEDGER);
|
|
70
355
|
alloc.release();
|
|
71
|
-
sharedAllocationPool
|
|
356
|
+
sharedAllocationPool[sharedAllocationPoolLen++] = alloc;
|
|
357
|
+
}
|
|
358
|
+
while (sharedFileNodePoolLen < 112) {
|
|
359
|
+
sharedFileNodePool[sharedFileNodePoolLen++] = new MemoryFileNode(0, 0, fastWriteCachedNow, 0, DUMMY_POOL_ALLOCATION, undefined);
|
|
360
|
+
}
|
|
361
|
+
while (sharedLargeAllocationPoolLen < 4) {
|
|
362
|
+
DUMMY_POOL_LEDGER.reserve(65536, 0, "init", "/");
|
|
363
|
+
const alloc = new MemoryAllocation(new Uint8Array(65536), DUMMY_POOL_LEDGER);
|
|
364
|
+
alloc.release();
|
|
365
|
+
sharedLargeAllocationPool[sharedLargeAllocationPoolLen++] = alloc;
|
|
72
366
|
}
|
|
73
|
-
while (
|
|
74
|
-
|
|
367
|
+
while (sharedMediumAllocationPoolLen < 4) {
|
|
368
|
+
DUMMY_POOL_LEDGER.reserve(16384, 0, "init", "/");
|
|
369
|
+
const alloc = new MemoryAllocation(new Uint8Array(16384), DUMMY_POOL_LEDGER);
|
|
370
|
+
alloc.release();
|
|
371
|
+
sharedMediumAllocationPool[sharedMediumAllocationPoolLen++] = alloc;
|
|
75
372
|
}
|
|
76
|
-
while (
|
|
77
|
-
sharedDirectoryNodePool
|
|
373
|
+
while (sharedDirectoryNodePoolLen < 16) {
|
|
374
|
+
sharedDirectoryNodePool[sharedDirectoryNodePoolLen++] = new MemoryDirectoryNode(0, 0, fastWriteCachedNow);
|
|
78
375
|
}
|
|
79
376
|
}
|
|
80
377
|
class MemoryDirectoryNode {
|
|
81
378
|
constructor(mode, ino, now) {
|
|
379
|
+
this.revision = 0;
|
|
82
380
|
this.mode = mode;
|
|
83
381
|
this.ino = ino;
|
|
382
|
+
this.nlink = 1;
|
|
383
|
+
this.references = 0;
|
|
84
384
|
this.atimeMs = now;
|
|
85
385
|
this.mtimeMs = now;
|
|
86
386
|
this.ctimeMs = now;
|
|
87
387
|
this.birthtimeMs = now;
|
|
88
|
-
this.entries = new
|
|
388
|
+
this.entries = new FastDirectoryEntriesMap();
|
|
389
|
+
this.cachedNlink = 2;
|
|
390
|
+
this.cachedNlinkRev = 0;
|
|
89
391
|
}
|
|
90
392
|
}
|
|
91
393
|
Object.assign(MemoryDirectoryNode.prototype, {
|
|
@@ -96,16 +398,18 @@ Object.assign(MemoryDirectoryNode.prototype, {
|
|
|
96
398
|
cachedNlink: 2,
|
|
97
399
|
cachedNlinkRev: 0,
|
|
98
400
|
});
|
|
401
|
+
const DUMMY_POOL_DIR_NODE = new MemoryDirectoryNode(0, 0, null);
|
|
402
|
+
DUMMY_POOL_DIR_NODE.atimeMs = DUMMY_POOL_DIR_NODE.mtimeMs = DUMMY_POOL_DIR_NODE.ctimeMs = DUMMY_POOL_DIR_NODE.birthtimeMs = 1700000000000;
|
|
99
403
|
class MemoryCache {
|
|
100
404
|
allocations = [];
|
|
101
405
|
files = [];
|
|
102
406
|
directories = [];
|
|
103
407
|
removedScratch = [];
|
|
104
408
|
lastFastDirPrefix = "";
|
|
105
|
-
lastFastDirNode;
|
|
409
|
+
lastFastDirNode = undefined;
|
|
106
410
|
lastFastFilePath = "";
|
|
107
411
|
lastFastFileName = "";
|
|
108
|
-
lastFastFileNode;
|
|
412
|
+
lastFastFileNode = undefined;
|
|
109
413
|
clearWrites() {
|
|
110
414
|
this.lastFastDirPrefix = "";
|
|
111
415
|
this.lastFastDirNode = undefined;
|
|
@@ -117,6 +421,9 @@ class MemoryCache {
|
|
|
117
421
|
const typeModes = { file: 0o100000, directory: 0o040000, symlink: 0o120000 };
|
|
118
422
|
const emptyResolveOptions = Object.freeze({});
|
|
119
423
|
const noFollowResolveOptions = Object.freeze({ followFinal: false });
|
|
424
|
+
const sharedMkdirResolveOptions = { createDirectories: 0o777 };
|
|
425
|
+
const sharedAllowMissingNoFollowOptions = Object.freeze({ allowMissing: true, followFinal: false });
|
|
426
|
+
const sharedFastLocation = { node: undefined, parent: DUMMY_POOL_DIR_NODE, name: "", path: "" };
|
|
120
427
|
const resolvedVoid = Promise.resolve();
|
|
121
428
|
const preferredIoBlockSize = 64 * 1024;
|
|
122
429
|
const ext4HtreeEof64 = (1n << 63n) - 1n;
|
|
@@ -317,24 +624,31 @@ export class MemoryFileSystem {
|
|
|
317
624
|
identityScope = Symbol();
|
|
318
625
|
nextInode = 1;
|
|
319
626
|
ledger;
|
|
627
|
+
_cache;
|
|
628
|
+
mutationTick = 0;
|
|
629
|
+
_owner;
|
|
320
630
|
root;
|
|
321
631
|
totalBytes = 0;
|
|
322
632
|
symlinkCount = 0;
|
|
323
633
|
constructor(options = {}) {
|
|
324
634
|
this.ledger = new MemoryLedger(normalizeMemoryFileSystemLimits(options));
|
|
325
|
-
if (this.ledger.hasInfiniteRetained && this.ledger.
|
|
635
|
+
if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes) {
|
|
326
636
|
replenishSharedMemoryPools();
|
|
327
637
|
}
|
|
328
|
-
|
|
638
|
+
const cache = new MemoryCache();
|
|
639
|
+
this._cache = cache;
|
|
640
|
+
memoryCaches.set(this.ledger, cache);
|
|
329
641
|
this.ledger.reserve(0, 1, "mkdir", "/");
|
|
330
642
|
this.root = this.directory(0o755);
|
|
331
643
|
const root = this.root;
|
|
332
|
-
|
|
644
|
+
const owner = {
|
|
333
645
|
root,
|
|
334
646
|
ledger: this.ledger,
|
|
335
647
|
capabilities: this.capabilities,
|
|
336
648
|
intact: () => this.root === root,
|
|
337
|
-
}
|
|
649
|
+
};
|
|
650
|
+
this._owner = owner;
|
|
651
|
+
ownedStores.set(this, owner);
|
|
338
652
|
registerMemoryAtomicView(this, {
|
|
339
653
|
stat: (path) => {
|
|
340
654
|
this.validatePath(path, "overlayAtomicView");
|
|
@@ -496,11 +810,17 @@ export class MemoryFileSystem {
|
|
|
496
810
|
atimeMs: now, mtimeMs: now, ctimeMs: now, birthtimeMs: now,
|
|
497
811
|
};
|
|
498
812
|
}
|
|
499
|
-
directory(mode) {
|
|
500
|
-
const now = Date.now();
|
|
813
|
+
directory(mode, now = Date.now()) {
|
|
501
814
|
const fullMode = typeModes.directory | mode;
|
|
502
815
|
const ino = this.nextInode++;
|
|
503
|
-
|
|
816
|
+
let pooled;
|
|
817
|
+
if (sharedDirectoryNodePoolLen > 0) {
|
|
818
|
+
pooled = sharedDirectoryNodePool[--sharedDirectoryNodePoolLen];
|
|
819
|
+
sharedDirectoryNodePool[sharedDirectoryNodePoolLen] = DUMMY_POOL_DIR_NODE;
|
|
820
|
+
}
|
|
821
|
+
else {
|
|
822
|
+
pooled = memoryCaches.get(this.ledger)?.directories.pop();
|
|
823
|
+
}
|
|
504
824
|
if (pooled) {
|
|
505
825
|
pooled.mode = fullMode;
|
|
506
826
|
pooled.ino = ino;
|
|
@@ -517,13 +837,13 @@ export class MemoryFileSystem {
|
|
|
517
837
|
}
|
|
518
838
|
return new MemoryDirectoryNode(fullMode, ino, now);
|
|
519
839
|
}
|
|
520
|
-
addDirectoryNode(parent, name, mode, syscall, path) {
|
|
840
|
+
addDirectoryNode(parent, name, mode, syscall, path, now = Date.now()) {
|
|
521
841
|
const bytes = name.length * 2;
|
|
522
842
|
this.ledger.reserve(bytes, 2, syscall, path);
|
|
523
843
|
try {
|
|
524
|
-
const node = this.directory(mode);
|
|
844
|
+
const node = this.directory(mode, now);
|
|
525
845
|
parent.entries.set(name, node);
|
|
526
|
-
this.changed(parent);
|
|
846
|
+
this.changed(parent, now);
|
|
527
847
|
return node;
|
|
528
848
|
}
|
|
529
849
|
catch (error) {
|
|
@@ -565,19 +885,26 @@ export class MemoryFileSystem {
|
|
|
565
885
|
this.totalBytes -= node.byteLength;
|
|
566
886
|
const alloc = node.allocation;
|
|
567
887
|
node.view = undefined;
|
|
888
|
+
node.sourceRef = undefined;
|
|
568
889
|
node.allocation = DUMMY_POOL_ALLOCATION;
|
|
569
890
|
alloc.release();
|
|
570
891
|
if (alloc.isReleased64()) {
|
|
571
892
|
alloc.detachLedger(DUMMY_POOL_LEDGER);
|
|
572
|
-
if (this.ledger.hasInfiniteRetained && this.ledger.
|
|
573
|
-
sharedAllocationPool
|
|
893
|
+
if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedAllocationPoolLen < SHARED_POOL_CAPACITY) {
|
|
894
|
+
sharedAllocationPool[sharedAllocationPoolLen++] = alloc;
|
|
574
895
|
}
|
|
575
896
|
else if (cache.allocations.length < 128) {
|
|
576
897
|
cache.allocations.push(alloc);
|
|
577
898
|
}
|
|
578
899
|
}
|
|
579
|
-
if (
|
|
580
|
-
|
|
900
|
+
else if (alloc.isReleased65536()) {
|
|
901
|
+
alloc.detachLedger(DUMMY_POOL_LEDGER);
|
|
902
|
+
if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedLargeAllocationPoolLen < SHARED_LARGE_POOL_CAPACITY) {
|
|
903
|
+
sharedLargeAllocationPool[sharedLargeAllocationPoolLen++] = alloc;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
if (sharedFileNodePoolLen < SHARED_POOL_CAPACITY) {
|
|
907
|
+
sharedFileNodePool[sharedFileNodePoolLen++] = node;
|
|
581
908
|
}
|
|
582
909
|
else if (cache.files.length < 128) {
|
|
583
910
|
cache.files.push(node);
|
|
@@ -592,8 +919,8 @@ export class MemoryFileSystem {
|
|
|
592
919
|
cache.lastFastDirNode = undefined;
|
|
593
920
|
}
|
|
594
921
|
if (node.entries.size === 0) {
|
|
595
|
-
if (
|
|
596
|
-
sharedDirectoryNodePool
|
|
922
|
+
if (sharedDirectoryNodePoolLen < SHARED_DIR_POOL_CAPACITY) {
|
|
923
|
+
sharedDirectoryNodePool[sharedDirectoryNodePoolLen++] = node;
|
|
597
924
|
}
|
|
598
925
|
else if (cache.directories.length < 32) {
|
|
599
926
|
cache.directories.push(node);
|
|
@@ -613,6 +940,18 @@ export class MemoryFileSystem {
|
|
|
613
940
|
node.view = length === allocation.data.byteLength ? allocation.data : undefined;
|
|
614
941
|
node.allocation = allocation;
|
|
615
942
|
previous.release();
|
|
943
|
+
if (previous.isReleased64()) {
|
|
944
|
+
previous.detachLedger(DUMMY_POOL_LEDGER);
|
|
945
|
+
if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedAllocationPoolLen < SHARED_POOL_CAPACITY) {
|
|
946
|
+
sharedAllocationPool[sharedAllocationPoolLen++] = previous;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
else if (previous.isReleased65536()) {
|
|
950
|
+
previous.detachLedger(DUMMY_POOL_LEDGER);
|
|
951
|
+
if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedLargeAllocationPoolLen < SHARED_LARGE_POOL_CAPACITY) {
|
|
952
|
+
sharedLargeAllocationPool[sharedLargeAllocationPoolLen++] = previous;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
616
955
|
}
|
|
617
956
|
admitSize(node, length, syscall, path) {
|
|
618
957
|
this.ledger.fileSize(length, syscall, path);
|
|
@@ -645,6 +984,7 @@ export class MemoryFileSystem {
|
|
|
645
984
|
this.fail("EINVAL", syscall, path);
|
|
646
985
|
}
|
|
647
986
|
changed(node, now = Date.now()) {
|
|
987
|
+
this.mutationTick = (this.mutationTick + 1) | 0;
|
|
648
988
|
node.revision = node.revision < 1073741823 ? (node.revision + 1) | 0 : Math.min(Number.MAX_SAFE_INTEGER + 1, node.revision + 1);
|
|
649
989
|
node.mtimeMs = node.ctimeMs = now;
|
|
650
990
|
}
|
|
@@ -654,7 +994,7 @@ export class MemoryFileSystem {
|
|
|
654
994
|
const fastDir = cache.lastFastDirNode;
|
|
655
995
|
const fastPrefix = cache.lastFastDirPrefix;
|
|
656
996
|
if (fastDir !== undefined && fastDir.nlink !== 0 && fastPrefix.length > 0 && path.startsWith(fastPrefix)) {
|
|
657
|
-
const name = path.slice(fastPrefix.length);
|
|
997
|
+
const name = fastPrefix.length === 1 ? (path === _lastFileSlice1Path ? _lastFileSlice1Name : (_lastFileSlice1Path = path, _lastFileSlice1Name = path.slice(1))) : path.slice(fastPrefix.length);
|
|
658
998
|
if (name.length > 0 && !name.includes("/")) {
|
|
659
999
|
this.permission(fastDir, 1, syscall, path);
|
|
660
1000
|
if (exceedsComponentByteLimit(name))
|
|
@@ -672,9 +1012,9 @@ export class MemoryFileSystem {
|
|
|
672
1012
|
const slash = path.indexOf("/", start);
|
|
673
1013
|
if (slash === -1) {
|
|
674
1014
|
cache.clearWrites();
|
|
675
|
-
cache.lastFastDirPrefix = path.slice(0, start);
|
|
1015
|
+
cache.lastFastDirPrefix = start === 1 ? "/" : path.slice(0, start);
|
|
676
1016
|
cache.lastFastDirNode = current;
|
|
677
|
-
const name = path.slice(start);
|
|
1017
|
+
const name = start === 1 ? (path === _lastFileSlice1Path ? _lastFileSlice1Name : (_lastFileSlice1Path = path, _lastFileSlice1Name = path.slice(1))) : path.slice(start);
|
|
678
1018
|
if (exceedsComponentByteLimit(name))
|
|
679
1019
|
this.fail("ENAMETOOLONG", syscall, path);
|
|
680
1020
|
const node = current.entries.get(name);
|
|
@@ -682,7 +1022,7 @@ export class MemoryFileSystem {
|
|
|
682
1022
|
this.fail("ENOENT", syscall, path);
|
|
683
1023
|
return node;
|
|
684
1024
|
}
|
|
685
|
-
const name = path
|
|
1025
|
+
const name = slicePathSegment(path, start, slash);
|
|
686
1026
|
if (exceedsComponentByteLimit(name))
|
|
687
1027
|
this.fail("ENAMETOOLONG", syscall, path);
|
|
688
1028
|
const next = current.entries.get(name);
|
|
@@ -706,7 +1046,7 @@ export class MemoryFileSystem {
|
|
|
706
1046
|
this.permission(current, 1, syscall, path);
|
|
707
1047
|
const slash = path.indexOf("/", start);
|
|
708
1048
|
if (slash === -1) {
|
|
709
|
-
const name = path.
|
|
1049
|
+
const name = slicePathSegment(path, start, path.length);
|
|
710
1050
|
if (exceedsComponentByteLimit(name))
|
|
711
1051
|
this.fail("ENAMETOOLONG", syscall, path);
|
|
712
1052
|
let node = current.entries.get(name);
|
|
@@ -827,6 +1167,20 @@ export class MemoryFileSystem {
|
|
|
827
1167
|
};
|
|
828
1168
|
}
|
|
829
1169
|
file(path, syscall) {
|
|
1170
|
+
if (this.symlinkCount === 0 && path.charCodeAt(0) === 47 && path.length > 1 && path.indexOf("/", 1) === -1 && !path.includes("\0")) {
|
|
1171
|
+
const name = path === _lastFileSlice1Path ? _lastFileSlice1Name : (_lastFileSlice1Path = path, _lastFileSlice1Name = path.slice(1));
|
|
1172
|
+
if (name !== "." && name !== "..") {
|
|
1173
|
+
this.permission(this.root, 1, syscall, path);
|
|
1174
|
+
if (exceedsComponentByteLimit(name))
|
|
1175
|
+
this.fail("ENAMETOOLONG", syscall, path);
|
|
1176
|
+
const node = this.root.entries.get(name);
|
|
1177
|
+
if (!node)
|
|
1178
|
+
this.fail("ENOENT", syscall, path);
|
|
1179
|
+
if (node.type !== "file")
|
|
1180
|
+
this.fail("EISDIR", syscall, path);
|
|
1181
|
+
return node;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
830
1184
|
const cache = memoryCaches.get(this.ledger);
|
|
831
1185
|
if (this.symlinkCount === 0 && isCleanAbsolutePath(path)) {
|
|
832
1186
|
const fastDir = cache.lastFastDirNode;
|
|
@@ -967,12 +1321,33 @@ export class MemoryFileSystem {
|
|
|
967
1321
|
this.ledger.fileSize(length, syscall, path);
|
|
968
1322
|
this.ledger.reserve(length, 0, syscall, path);
|
|
969
1323
|
if (length === 0) {
|
|
970
|
-
return
|
|
1324
|
+
return DUMMY_POOL_ALLOCATION;
|
|
1325
|
+
}
|
|
1326
|
+
if (length === 16384) {
|
|
1327
|
+
if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedMediumAllocationPoolLen > 0) {
|
|
1328
|
+
const pooled = sharedMediumAllocationPool[--sharedMediumAllocationPoolLen];
|
|
1329
|
+
sharedMediumAllocationPool[sharedMediumAllocationPoolLen] = DUMMY_POOL_ALLOCATION;
|
|
1330
|
+
pooled.reuse(this.ledger);
|
|
1331
|
+
return pooled;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
if (length === 65536) {
|
|
1335
|
+
if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedLargeAllocationPoolLen > 0) {
|
|
1336
|
+
const pooled = sharedLargeAllocationPool[--sharedLargeAllocationPoolLen];
|
|
1337
|
+
sharedLargeAllocationPool[sharedLargeAllocationPoolLen] = DUMMY_POOL_ALLOCATION;
|
|
1338
|
+
pooled.reuse(this.ledger);
|
|
1339
|
+
return pooled;
|
|
1340
|
+
}
|
|
971
1341
|
}
|
|
972
1342
|
if (length === 64) {
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1343
|
+
let pooled;
|
|
1344
|
+
if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedAllocationPoolLen > 0) {
|
|
1345
|
+
pooled = sharedAllocationPool[--sharedAllocationPoolLen];
|
|
1346
|
+
sharedAllocationPool[sharedAllocationPoolLen] = DUMMY_POOL_ALLOCATION;
|
|
1347
|
+
}
|
|
1348
|
+
else {
|
|
1349
|
+
pooled = memoryCaches.get(this.ledger).allocations.pop();
|
|
1350
|
+
}
|
|
976
1351
|
if (pooled) {
|
|
977
1352
|
pooled.reuse(this.ledger);
|
|
978
1353
|
return pooled;
|
|
@@ -981,7 +1356,7 @@ export class MemoryFileSystem {
|
|
|
981
1356
|
try {
|
|
982
1357
|
// A shared slab's backing buffer exceeds the admitted slice capacity.
|
|
983
1358
|
// Finite file/storage ceilings require independently owned exact buffers.
|
|
984
|
-
if (length <= 64 && this.ledger.hasInfiniteRetained && this.ledger.
|
|
1359
|
+
if (length <= 64 && this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes) {
|
|
985
1360
|
if (smallAllocOffset + length > SMALL_ALLOC_SLAB_SIZE) {
|
|
986
1361
|
smallAllocSlab = new Uint8Array(SMALL_ALLOC_SLAB_SIZE);
|
|
987
1362
|
smallAllocOffset = 0;
|
|
@@ -1092,7 +1467,14 @@ export class MemoryFileSystem {
|
|
|
1092
1467
|
const now = Date.now();
|
|
1093
1468
|
const fileMode = typeModes.file | target.mode;
|
|
1094
1469
|
const view = capacity === length ? allocation.data : undefined;
|
|
1095
|
-
|
|
1470
|
+
let pooled;
|
|
1471
|
+
if (sharedFileNodePoolLen > 0) {
|
|
1472
|
+
pooled = sharedFileNodePool[--sharedFileNodePoolLen];
|
|
1473
|
+
sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
|
|
1474
|
+
}
|
|
1475
|
+
else {
|
|
1476
|
+
pooled = memoryCaches.get(this.ledger).files.pop();
|
|
1477
|
+
}
|
|
1096
1478
|
if (pooled) {
|
|
1097
1479
|
pooled.mode = fileMode;
|
|
1098
1480
|
pooled.ino = this.nextInode++;
|
|
@@ -1106,9 +1488,10 @@ export class MemoryFileSystem {
|
|
|
1106
1488
|
pooled.byteLength = length;
|
|
1107
1489
|
pooled.view = view;
|
|
1108
1490
|
pooled.allocation = allocation;
|
|
1491
|
+
pooled.sourceRef = data;
|
|
1109
1492
|
return pooled;
|
|
1110
1493
|
}
|
|
1111
|
-
return new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view);
|
|
1494
|
+
return new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view, data);
|
|
1112
1495
|
}, syscall, path);
|
|
1113
1496
|
}
|
|
1114
1497
|
catch (error) {
|
|
@@ -1128,11 +1511,14 @@ export class MemoryFileSystem {
|
|
|
1128
1511
|
}
|
|
1129
1512
|
}
|
|
1130
1513
|
writeAt(node, data, position, syscall, path) {
|
|
1131
|
-
|
|
1132
|
-
|
|
1514
|
+
this.writeAtRange(node, data, data.byteLength, position, syscall, path, Date.now());
|
|
1515
|
+
}
|
|
1516
|
+
writeAtRange(node, data, dataLen, position, syscall, path, now) {
|
|
1517
|
+
if (dataLen === 0) {
|
|
1518
|
+
this.changed(node, now);
|
|
1133
1519
|
return;
|
|
1134
1520
|
}
|
|
1135
|
-
const end = position +
|
|
1521
|
+
const end = position + dataLen;
|
|
1136
1522
|
this.ledger.fileSize(end, syscall, path);
|
|
1137
1523
|
const curLen = node.byteLength;
|
|
1138
1524
|
const length = Math.max(curLen, end);
|
|
@@ -1140,22 +1526,38 @@ export class MemoryFileSystem {
|
|
|
1140
1526
|
let allocation = node.allocation;
|
|
1141
1527
|
if (length > allocation.data.byteLength) {
|
|
1142
1528
|
this.ledger.check(length, 0, syscall, path);
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1529
|
+
let capacity;
|
|
1530
|
+
if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && this.ledger.limits.maxBytes === undefined) {
|
|
1531
|
+
capacity = curLen === 0 && length >= 8192 && length <= 65536 ? (length <= 16384 ? 16384 : 65536) : Math.max(length, curLen * 2, 64);
|
|
1532
|
+
}
|
|
1533
|
+
else {
|
|
1534
|
+
const baseCap = curLen === 0 && length >= 8192 && length <= 65536 &&
|
|
1535
|
+
this.ledger.limits.maxFileBytes >= 65536 &&
|
|
1536
|
+
this.ledger.availableBytes >= 65536 &&
|
|
1537
|
+
(this.ledger.limits.maxBytes === undefined || this.ledger.limits.maxBytes - this.totalBytes >= 65536)
|
|
1538
|
+
? 65536
|
|
1539
|
+
: Math.max(length, curLen * 2, 64);
|
|
1540
|
+
capacity = Math.min(baseCap, this.ledger.limits.maxFileBytes, this.ledger.availableBytes);
|
|
1541
|
+
}
|
|
1150
1542
|
allocation = this.allocate(capacity, syscall, path);
|
|
1151
1543
|
}
|
|
1152
1544
|
try {
|
|
1153
1545
|
const storage = allocation.data;
|
|
1154
|
-
if (allocation !== node.allocation)
|
|
1155
|
-
storage.set(node.view ??
|
|
1546
|
+
if (allocation !== node.allocation && curLen > 0) {
|
|
1547
|
+
storage.set((node.view ?? node.allocation.data).subarray(0, curLen));
|
|
1548
|
+
}
|
|
1156
1549
|
if (position > curLen)
|
|
1157
1550
|
storage.fill(0, curLen, position);
|
|
1158
|
-
|
|
1551
|
+
if (dataLen === data.byteLength) {
|
|
1552
|
+
storage.set(data, position);
|
|
1553
|
+
}
|
|
1554
|
+
else if (typeof data.copy === "function") {
|
|
1555
|
+
data.copy(storage, position, 0, dataLen);
|
|
1556
|
+
}
|
|
1557
|
+
else {
|
|
1558
|
+
for (let i = 0; i < dataLen; i++)
|
|
1559
|
+
storage[position + i] = data[i];
|
|
1560
|
+
}
|
|
1159
1561
|
}
|
|
1160
1562
|
catch (error) {
|
|
1161
1563
|
if (allocation !== node.allocation)
|
|
@@ -1169,7 +1571,7 @@ export class MemoryFileSystem {
|
|
|
1169
1571
|
node.byteLength = length;
|
|
1170
1572
|
node.view = length === allocation.data.byteLength ? allocation.data : undefined;
|
|
1171
1573
|
}
|
|
1172
|
-
this.changed(node);
|
|
1574
|
+
this.changed(node, now);
|
|
1173
1575
|
}
|
|
1174
1576
|
writeMemoryFileFast(path, data, append, mode) {
|
|
1175
1577
|
const cache = memoryCaches.get(this.ledger);
|
|
@@ -1220,7 +1622,7 @@ export class MemoryFileSystem {
|
|
|
1220
1622
|
}
|
|
1221
1623
|
break;
|
|
1222
1624
|
}
|
|
1223
|
-
const seg = path
|
|
1625
|
+
const seg = slicePathSegment(path, start, slash);
|
|
1224
1626
|
if (exceedsComponentByteLimit(seg))
|
|
1225
1627
|
this.fail("ENAMETOOLONG", syscall, path);
|
|
1226
1628
|
const next = parent.entries.get(seg);
|
|
@@ -1232,7 +1634,7 @@ export class MemoryFileSystem {
|
|
|
1232
1634
|
start = slash + 1;
|
|
1233
1635
|
}
|
|
1234
1636
|
}
|
|
1235
|
-
const existing = parent.entries.get(name);
|
|
1637
|
+
const existing = parent.entries.getForWrite ? parent.entries.getForWrite(name) : parent.entries.get(name);
|
|
1236
1638
|
if (existing) {
|
|
1237
1639
|
if (existing.type !== "file")
|
|
1238
1640
|
this.fail("EISDIR", syscall, path);
|
|
@@ -1255,7 +1657,7 @@ export class MemoryFileSystem {
|
|
|
1255
1657
|
return;
|
|
1256
1658
|
}
|
|
1257
1659
|
if (!current) {
|
|
1258
|
-
const capacity = length > 0 && length < 64 && this.ledger.hasInfiniteRetained && this.ledger.
|
|
1660
|
+
const capacity = length > 0 && length < 64 && this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes ? 64 : length;
|
|
1259
1661
|
const allocation = this.allocate(capacity, syscall, path);
|
|
1260
1662
|
try {
|
|
1261
1663
|
allocation.data.set(data);
|
|
@@ -1264,7 +1666,14 @@ export class MemoryFileSystem {
|
|
|
1264
1666
|
const now = Date.now();
|
|
1265
1667
|
const fileMode = typeModes.file | mode;
|
|
1266
1668
|
const view = capacity === length ? allocation.data : undefined;
|
|
1267
|
-
let node
|
|
1669
|
+
let node;
|
|
1670
|
+
if (sharedFileNodePoolLen > 0) {
|
|
1671
|
+
node = sharedFileNodePool[--sharedFileNodePoolLen];
|
|
1672
|
+
sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
|
|
1673
|
+
}
|
|
1674
|
+
else {
|
|
1675
|
+
node = cache.files.pop();
|
|
1676
|
+
}
|
|
1268
1677
|
if (node) {
|
|
1269
1678
|
node.mode = fileMode;
|
|
1270
1679
|
node.ino = this.nextInode++;
|
|
@@ -1278,9 +1687,10 @@ export class MemoryFileSystem {
|
|
|
1278
1687
|
node.byteLength = length;
|
|
1279
1688
|
node.view = view;
|
|
1280
1689
|
node.allocation = allocation;
|
|
1690
|
+
node.sourceRef = data;
|
|
1281
1691
|
}
|
|
1282
1692
|
else {
|
|
1283
|
-
node = new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view);
|
|
1693
|
+
node = new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view, data);
|
|
1284
1694
|
}
|
|
1285
1695
|
const prevNlink = parent.cachedNlinkRev === parent.revision ? parent.cachedNlink : (parent.entries.size === 0 ? 2 : undefined);
|
|
1286
1696
|
parent.entries.set(name, node);
|
|
@@ -1318,8 +1728,133 @@ export class MemoryFileSystem {
|
|
|
1318
1728
|
throw error;
|
|
1319
1729
|
}
|
|
1320
1730
|
}
|
|
1321
|
-
|
|
1322
|
-
const
|
|
1731
|
+
writeMemoryFilesInDirBatchFast(firstFileName, dirPrefix, fileNames, nameHashes, payloads, totalNameBytes, payloadBytesSum, mode) {
|
|
1732
|
+
const batchCount = fileNames.length;
|
|
1733
|
+
const cache = this._cache;
|
|
1734
|
+
if (batchCount > 0 &&
|
|
1735
|
+
dirPrefix === cache.lastFastDirPrefix &&
|
|
1736
|
+
cache.lastFastDirNode !== undefined &&
|
|
1737
|
+
cache.lastFastDirNode.nlink !== 0 &&
|
|
1738
|
+
(cache.lastFastDirNode.mode & 0o300) === 0o300 &&
|
|
1739
|
+
this.ledger.hasInfiniteRetained &&
|
|
1740
|
+
this.ledger.hasInfiniteFileBytes &&
|
|
1741
|
+
this.ledger.limits.maxBytes === undefined &&
|
|
1742
|
+
sharedFileNodePoolLen >= batchCount) {
|
|
1743
|
+
const parent = cache.lastFastDirNode;
|
|
1744
|
+
const entries = parent.entries;
|
|
1745
|
+
if (entries.size === 1 &&
|
|
1746
|
+
entries._next === 1 &&
|
|
1747
|
+
entries._keys[0] === firstFileName &&
|
|
1748
|
+
1 + batchCount <= entries._keys.length) {
|
|
1749
|
+
this.ledger.reserve(totalNameBytes, 2 * batchCount, "writeFile", fileNames[batchCount - 1]);
|
|
1750
|
+
this.totalBytes += payloadBytesSum;
|
|
1751
|
+
const now = fastWriteCachedNow;
|
|
1752
|
+
const fileMode = typeModes.file | mode;
|
|
1753
|
+
let ino = this.nextInode;
|
|
1754
|
+
this.nextInode = ino + batchCount;
|
|
1755
|
+
let nextIdx = 1;
|
|
1756
|
+
entries._next = 1 + batchCount;
|
|
1757
|
+
entries.size = 1 + batchCount;
|
|
1758
|
+
const table = entries._table;
|
|
1759
|
+
const mask = table.length - 1;
|
|
1760
|
+
const keys = entries._keys;
|
|
1761
|
+
const vals = entries._vals;
|
|
1762
|
+
for (let k = 0; k < batchCount; k++) {
|
|
1763
|
+
const name = fileNames[k];
|
|
1764
|
+
const data = payloads[k];
|
|
1765
|
+
const node = sharedFileNodePool[--sharedFileNodePoolLen];
|
|
1766
|
+
sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
|
|
1767
|
+
node.mode = fileMode;
|
|
1768
|
+
node.ino = ino++;
|
|
1769
|
+
if (node.nlink !== 1)
|
|
1770
|
+
node.nlink = 1;
|
|
1771
|
+
if (node.references !== 0)
|
|
1772
|
+
node.references = 0;
|
|
1773
|
+
if (node.revision !== 0)
|
|
1774
|
+
node.revision = 0;
|
|
1775
|
+
node.atimeMs = now;
|
|
1776
|
+
node.mtimeMs = now;
|
|
1777
|
+
node.ctimeMs = now;
|
|
1778
|
+
node.birthtimeMs = now;
|
|
1779
|
+
node.byteLength = data.byteLength;
|
|
1780
|
+
node.view = data;
|
|
1781
|
+
node.allocation = DUMMY_POOL_ALLOCATION;
|
|
1782
|
+
node.sourceRef = data;
|
|
1783
|
+
const entryIdx = nextIdx++;
|
|
1784
|
+
keys[entryIdx] = name;
|
|
1785
|
+
vals[entryIdx] = node;
|
|
1786
|
+
let slot = nameHashes[k] & mask;
|
|
1787
|
+
while (table[slot] !== -1)
|
|
1788
|
+
slot = (slot + 1) & mask;
|
|
1789
|
+
table[slot] = entryIdx;
|
|
1790
|
+
}
|
|
1791
|
+
this.mutationTick = (this.mutationTick + 1) | 0;
|
|
1792
|
+
parent.revision = parent.revision < 1073741000 ? (parent.revision + batchCount) | 0 : Math.min(Number.MAX_SAFE_INTEGER + 1, parent.revision + batchCount);
|
|
1793
|
+
parent.mtimeMs = parent.ctimeMs = now;
|
|
1794
|
+
cache.lastFastFilePath = "";
|
|
1795
|
+
cache.lastFastFileName = fileNames[batchCount - 1];
|
|
1796
|
+
cache.lastFastFileNode = vals[nextIdx - 1];
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
for (let k = 0; k < batchCount; k++) {
|
|
1801
|
+
this.writeMemoryFileInDirFast(dirPrefix, fileNames[k], payloads[k], false, mode, nameHashes[k]);
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
writeMemoryFileInDirFast(dirPrefix, name, data, append, mode, nameHash) {
|
|
1805
|
+
const cache = this._cache;
|
|
1806
|
+
if (!append &&
|
|
1807
|
+
dirPrefix === cache.lastFastDirPrefix &&
|
|
1808
|
+
cache.lastFastDirNode !== undefined &&
|
|
1809
|
+
cache.lastFastDirNode.nlink !== 0 &&
|
|
1810
|
+
(cache.lastFastDirNode.mode & 0o300) === 0o300 &&
|
|
1811
|
+
name.length <= 85 &&
|
|
1812
|
+
this.ledger.hasInfiniteRetained &&
|
|
1813
|
+
this.ledger.hasInfiniteFileBytes &&
|
|
1814
|
+
this.ledger.limits.maxBytes === undefined &&
|
|
1815
|
+
sharedFileNodePoolLen > 0) {
|
|
1816
|
+
const parent = cache.lastFastDirNode;
|
|
1817
|
+
const entries = parent.entries;
|
|
1818
|
+
const existing = nameHash !== undefined ? entries.getForWriteWithHash(name, nameHash) : entries.getForWrite(name);
|
|
1819
|
+
if (existing === undefined && entries._next < entries._keys.length) {
|
|
1820
|
+
const now = fastWriteCachedNow;
|
|
1821
|
+
const length = data.byteLength;
|
|
1822
|
+
const node = sharedFileNodePool[--sharedFileNodePoolLen];
|
|
1823
|
+
sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
|
|
1824
|
+
node.mode = typeModes.file | mode;
|
|
1825
|
+
node.ino = this.nextInode++;
|
|
1826
|
+
if (node.nlink !== 1)
|
|
1827
|
+
node.nlink = 1;
|
|
1828
|
+
if (node.references !== 0)
|
|
1829
|
+
node.references = 0;
|
|
1830
|
+
if (node.revision !== 0)
|
|
1831
|
+
node.revision = 0;
|
|
1832
|
+
node.atimeMs = now;
|
|
1833
|
+
node.mtimeMs = now;
|
|
1834
|
+
node.ctimeMs = now;
|
|
1835
|
+
node.birthtimeMs = now;
|
|
1836
|
+
node.byteLength = length;
|
|
1837
|
+
node.view = data;
|
|
1838
|
+
node.allocation = DUMMY_POOL_ALLOCATION;
|
|
1839
|
+
node.sourceRef = data;
|
|
1840
|
+
const entryIdx = entries._next++;
|
|
1841
|
+
entries._table[lastFastMapMiss.slot] = entryIdx;
|
|
1842
|
+
entries._keys[entryIdx] = name;
|
|
1843
|
+
entries._vals[entryIdx] = node;
|
|
1844
|
+
entries.size++;
|
|
1845
|
+
lastFastMapMiss.map = undefined;
|
|
1846
|
+
lastFastMapMiss.key = "";
|
|
1847
|
+
this.ledger.reserve(name.length * 2, 2, "writeFile", name);
|
|
1848
|
+
this.totalBytes += length;
|
|
1849
|
+
this.mutationTick = (this.mutationTick + 1) | 0;
|
|
1850
|
+
parent.revision = parent.revision < 1073741823 ? (parent.revision + 1) | 0 : Math.min(Number.MAX_SAFE_INTEGER + 1, parent.revision + 1);
|
|
1851
|
+
parent.mtimeMs = parent.ctimeMs = now;
|
|
1852
|
+
cache.lastFastFilePath = "";
|
|
1853
|
+
cache.lastFastFileName = name;
|
|
1854
|
+
cache.lastFastFileNode = node;
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1323
1858
|
const syscall = append ? "appendFile" : "writeFile";
|
|
1324
1859
|
try {
|
|
1325
1860
|
if (append &&
|
|
@@ -1354,7 +1889,7 @@ export class MemoryFileSystem {
|
|
|
1354
1889
|
while (start < dirPrefix.length) {
|
|
1355
1890
|
this.permission(parent, 1, syscall, name);
|
|
1356
1891
|
const slash = dirPrefix.indexOf("/", start);
|
|
1357
|
-
const seg = slash === -1 ? dirPrefix.
|
|
1892
|
+
const seg = slash === -1 ? slicePathSegment(dirPrefix, start, dirPrefix.length) : slicePathSegment(dirPrefix, start, slash);
|
|
1358
1893
|
if (exceedsComponentByteLimit(seg))
|
|
1359
1894
|
this.fail("ENAMETOOLONG", syscall, name);
|
|
1360
1895
|
const next = parent.entries.get(seg);
|
|
@@ -1375,7 +1910,7 @@ export class MemoryFileSystem {
|
|
|
1375
1910
|
cache.lastFastDirNode = parent;
|
|
1376
1911
|
}
|
|
1377
1912
|
}
|
|
1378
|
-
const existing = parent.entries.get(name);
|
|
1913
|
+
const existing = parent.entries.getForWrite ? parent.entries.getForWrite(name) : parent.entries.get(name);
|
|
1379
1914
|
if (existing) {
|
|
1380
1915
|
if (existing.type !== "file")
|
|
1381
1916
|
this.fail("EISDIR", syscall, name);
|
|
@@ -1398,22 +1933,34 @@ export class MemoryFileSystem {
|
|
|
1398
1933
|
return;
|
|
1399
1934
|
}
|
|
1400
1935
|
if (!current) {
|
|
1401
|
-
const
|
|
1402
|
-
const
|
|
1936
|
+
const zeroCopyConst = this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && this.ledger.limits.maxBytes === undefined;
|
|
1937
|
+
const capacity = zeroCopyConst ? 0 : (length > 0 && length < 64 && this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes ? 64 : length);
|
|
1938
|
+
const allocation = zeroCopyConst ? DUMMY_POOL_ALLOCATION : this.allocate(capacity, syscall, name);
|
|
1403
1939
|
try {
|
|
1404
|
-
|
|
1940
|
+
if (!zeroCopyConst)
|
|
1941
|
+
allocation.data.set(data);
|
|
1405
1942
|
this.ledger.reserve(nameBytes, 2, syscall, name);
|
|
1406
1943
|
try {
|
|
1407
|
-
const now =
|
|
1944
|
+
const now = fastWriteCachedNow;
|
|
1408
1945
|
const fileMode = typeModes.file | mode;
|
|
1409
|
-
const view = capacity === length ? allocation.data : undefined;
|
|
1410
|
-
let node
|
|
1946
|
+
const view = zeroCopyConst ? data : (capacity === length ? allocation.data : undefined);
|
|
1947
|
+
let node;
|
|
1948
|
+
if (sharedFileNodePoolLen > 0) {
|
|
1949
|
+
node = sharedFileNodePool[--sharedFileNodePoolLen];
|
|
1950
|
+
sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
|
|
1951
|
+
}
|
|
1952
|
+
else {
|
|
1953
|
+
node = cache.files.pop();
|
|
1954
|
+
}
|
|
1411
1955
|
if (node) {
|
|
1412
1956
|
node.mode = fileMode;
|
|
1413
1957
|
node.ino = this.nextInode++;
|
|
1414
|
-
node.nlink
|
|
1415
|
-
|
|
1416
|
-
node.
|
|
1958
|
+
if (node.nlink !== 1)
|
|
1959
|
+
node.nlink = 1;
|
|
1960
|
+
if (node.references !== 0)
|
|
1961
|
+
node.references = 0;
|
|
1962
|
+
if (node.revision !== 0)
|
|
1963
|
+
node.revision = 0;
|
|
1417
1964
|
node.atimeMs = now;
|
|
1418
1965
|
node.mtimeMs = now;
|
|
1419
1966
|
node.ctimeMs = now;
|
|
@@ -1421,9 +1968,10 @@ export class MemoryFileSystem {
|
|
|
1421
1968
|
node.byteLength = length;
|
|
1422
1969
|
node.view = view;
|
|
1423
1970
|
node.allocation = allocation;
|
|
1971
|
+
node.sourceRef = data;
|
|
1424
1972
|
}
|
|
1425
1973
|
else {
|
|
1426
|
-
node = new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view);
|
|
1974
|
+
node = new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view, data);
|
|
1427
1975
|
}
|
|
1428
1976
|
const prevNlink = parent.cachedNlinkRev === parent.revision ? parent.cachedNlink : (parent.entries.size === 0 ? 2 : undefined);
|
|
1429
1977
|
parent.entries.set(name, node);
|
|
@@ -1436,8 +1984,12 @@ export class MemoryFileSystem {
|
|
|
1436
1984
|
cache.lastFastFilePath = "";
|
|
1437
1985
|
cache.lastFastFileName = name;
|
|
1438
1986
|
cache.lastFastFileNode = node;
|
|
1987
|
+
lastFastMapMiss.map = undefined;
|
|
1988
|
+
lastFastMapMiss.key = "";
|
|
1439
1989
|
}
|
|
1440
1990
|
catch (error) {
|
|
1991
|
+
lastFastMapMiss.map = undefined;
|
|
1992
|
+
lastFastMapMiss.key = "";
|
|
1441
1993
|
this.ledger.release(nameBytes, 2);
|
|
1442
1994
|
throw error;
|
|
1443
1995
|
}
|
|
@@ -1915,6 +2467,45 @@ export class MemoryFileSystem {
|
|
|
1915
2467
|
}
|
|
1916
2468
|
return result.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
1917
2469
|
}
|
|
2470
|
+
tryMkdirFastSync(path, recursive, mode) {
|
|
2471
|
+
try {
|
|
2472
|
+
this.validatePath(path, "mkdir");
|
|
2473
|
+
const resolvedMode = this.mode(mode, 0o777, "mkdir", path);
|
|
2474
|
+
if (recursive) {
|
|
2475
|
+
const node = this.resolve(path, "mkdir", { createDirectories: resolvedMode }).node;
|
|
2476
|
+
return node.type === "directory";
|
|
2477
|
+
}
|
|
2478
|
+
const location = this.resolve(path.replace(/\/+$/, "") || "/", "mkdir", { allowMissing: true, followFinal: false });
|
|
2479
|
+
if (location.node)
|
|
2480
|
+
return false;
|
|
2481
|
+
this.permission(location.parent, 3, "mkdir", path);
|
|
2482
|
+
this.addNode(location.parent, location.name, () => this.directory(resolvedMode), "mkdir", path);
|
|
2483
|
+
return true;
|
|
2484
|
+
}
|
|
2485
|
+
catch {
|
|
2486
|
+
return false;
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
tryRmFastSync(path, recursive, force) {
|
|
2490
|
+
if (activeConditionalMutations.has(this.identityScope))
|
|
2491
|
+
return false;
|
|
2492
|
+
try {
|
|
2493
|
+
let location;
|
|
2494
|
+
try {
|
|
2495
|
+
location = this.entry(path, "rm", false);
|
|
2496
|
+
}
|
|
2497
|
+
catch (error) {
|
|
2498
|
+
if (force && error instanceof FsError && error.code === "ENOENT")
|
|
2499
|
+
return true;
|
|
2500
|
+
return false;
|
|
2501
|
+
}
|
|
2502
|
+
this.removeLocation(location, path, "rm", recursive);
|
|
2503
|
+
return true;
|
|
2504
|
+
}
|
|
2505
|
+
catch {
|
|
2506
|
+
return false;
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
1918
2509
|
mkdir(path, options = {}) {
|
|
1919
2510
|
try {
|
|
1920
2511
|
options.signal?.throwIfAborted();
|
|
@@ -1991,7 +2582,7 @@ export class MemoryFileSystem {
|
|
|
1991
2582
|
return Promise.reject(error);
|
|
1992
2583
|
}
|
|
1993
2584
|
}
|
|
1994
|
-
removeLocation(location, path, syscall, recursive) {
|
|
2585
|
+
removeLocation(location, path, syscall, recursive, now = Date.now()) {
|
|
1995
2586
|
const node = location.node;
|
|
1996
2587
|
if (this.terminalDot(path))
|
|
1997
2588
|
this.fail("EINVAL", syscall, path);
|
|
@@ -2003,12 +2594,12 @@ export class MemoryFileSystem {
|
|
|
2003
2594
|
const cache = memoryCaches.get(this.ledger);
|
|
2004
2595
|
cache.clearWrites();
|
|
2005
2596
|
const removed = sharedRemovedScratch;
|
|
2006
|
-
|
|
2007
|
-
removed
|
|
2597
|
+
sharedRemovedLen = 1;
|
|
2598
|
+
removed[0] = node;
|
|
2008
2599
|
sharedRemovedKeyChars = 0;
|
|
2009
2600
|
sharedRemovedKeyEntries = 0;
|
|
2010
2601
|
try {
|
|
2011
|
-
for (let index = 0; index <
|
|
2602
|
+
for (let index = 0; index < sharedRemovedLen; index++) {
|
|
2012
2603
|
const entry = removed[index];
|
|
2013
2604
|
if (entry.type === "directory" && entry.entries.size > 0) {
|
|
2014
2605
|
this.permission(entry, 7, syscall, path);
|
|
@@ -2017,9 +2608,10 @@ export class MemoryFileSystem {
|
|
|
2017
2608
|
}
|
|
2018
2609
|
location.parent.entries.delete(location.name);
|
|
2019
2610
|
this.ledger.release((location.name.length + sharedRemovedKeyChars) * 2, 1 + sharedRemovedKeyEntries);
|
|
2020
|
-
const
|
|
2021
|
-
for (let i = 0; i <
|
|
2611
|
+
const totalRemoved = sharedRemovedLen;
|
|
2612
|
+
for (let i = 0; i < totalRemoved; i++) {
|
|
2022
2613
|
const entry = removed[i];
|
|
2614
|
+
removed[i] = DUMMY_POOL_FILE_NODE;
|
|
2023
2615
|
if (entry.type === "directory") {
|
|
2024
2616
|
entry.entries.clear();
|
|
2025
2617
|
}
|
|
@@ -2032,9 +2624,11 @@ export class MemoryFileSystem {
|
|
|
2032
2624
|
}
|
|
2033
2625
|
}
|
|
2034
2626
|
finally {
|
|
2035
|
-
|
|
2627
|
+
for (let i = 0; i < sharedRemovedLen; i++)
|
|
2628
|
+
removed[i] = DUMMY_POOL_FILE_NODE;
|
|
2629
|
+
sharedRemovedLen = 0;
|
|
2036
2630
|
}
|
|
2037
|
-
this.changed(location.parent);
|
|
2631
|
+
this.changed(location.parent, now);
|
|
2038
2632
|
}
|
|
2039
2633
|
unlink(path, options = {}) {
|
|
2040
2634
|
// Do not allow an untyped recursive option to widen final-entry removal.
|
|
@@ -2490,65 +3084,225 @@ export class MemoryRedirectHandle {
|
|
|
2490
3084
|
this.position = 0;
|
|
2491
3085
|
this.closed = false;
|
|
2492
3086
|
}
|
|
2493
|
-
|
|
3087
|
+
reset(fs, path, node, append) {
|
|
3088
|
+
this.fs = fs;
|
|
3089
|
+
this.path = path;
|
|
3090
|
+
this.node = node;
|
|
3091
|
+
this.append = append;
|
|
3092
|
+
this.position = 0;
|
|
3093
|
+
this.closed = false;
|
|
3094
|
+
}
|
|
3095
|
+
writeRangeSync(src, len, signal) {
|
|
2494
3096
|
signal?.throwIfAborted();
|
|
2495
3097
|
if (this.closed)
|
|
2496
3098
|
throw new FsError("EBADF", { syscall: "write", path: this.path });
|
|
2497
|
-
if (
|
|
3099
|
+
if (len === 0)
|
|
2498
3100
|
return;
|
|
2499
3101
|
const pos = this.append ? this.node.byteLength : this.position;
|
|
2500
|
-
|
|
3102
|
+
const now = Date.now === defaultDateNow ? fastWriteCachedNow : Date.now();
|
|
3103
|
+
this.fs.writeAtRange(this.node, src, len, pos, "write", this.path, now);
|
|
2501
3104
|
if (!this.append)
|
|
2502
|
-
this.position = pos +
|
|
3105
|
+
this.position = pos + len;
|
|
3106
|
+
}
|
|
3107
|
+
writeImmutableSync(immutableData, signal) {
|
|
3108
|
+
signal?.throwIfAborted();
|
|
3109
|
+
if (this.closed)
|
|
3110
|
+
throw new FsError("EBADF", { syscall: "write", path: this.path });
|
|
3111
|
+
const len = immutableData.byteLength;
|
|
3112
|
+
if (len === 0)
|
|
3113
|
+
return;
|
|
3114
|
+
const mem = this.fs;
|
|
3115
|
+
if (!this.append &&
|
|
3116
|
+
this.position === 0 &&
|
|
3117
|
+
this.node.byteLength === 0 &&
|
|
3118
|
+
this.node.allocation === DUMMY_POOL_ALLOCATION &&
|
|
3119
|
+
mem.ledger.hasInfiniteRetained &&
|
|
3120
|
+
mem.ledger.hasInfiniteFileBytes &&
|
|
3121
|
+
mem.ledger.limits.maxBytes === undefined) {
|
|
3122
|
+
const now = Date.now === defaultDateNow ? fastWriteCachedNow : Date.now();
|
|
3123
|
+
this.node.byteLength = len;
|
|
3124
|
+
this.node.view = immutableData;
|
|
3125
|
+
this.node.sourceRef = immutableData;
|
|
3126
|
+
this.position = len;
|
|
3127
|
+
mem.totalBytes += len;
|
|
3128
|
+
mem.changed(this.node, now);
|
|
3129
|
+
return;
|
|
3130
|
+
}
|
|
3131
|
+
this.writeRangeSync(immutableData, len, signal);
|
|
3132
|
+
}
|
|
3133
|
+
writeSync(chunk, signal) {
|
|
3134
|
+
this.writeRangeSync(chunk, chunk.byteLength, signal);
|
|
2503
3135
|
}
|
|
2504
3136
|
close() {
|
|
2505
3137
|
if (this.closed)
|
|
2506
3138
|
return;
|
|
2507
3139
|
this.closed = true;
|
|
2508
|
-
|
|
3140
|
+
const fs = this.fs;
|
|
3141
|
+
const node = this.node;
|
|
3142
|
+
const path = this.path;
|
|
3143
|
+
this.fs = undefined;
|
|
3144
|
+
this.node = DUMMY_POOL_FILE_NODE;
|
|
3145
|
+
this.path = "";
|
|
3146
|
+
if (node.nlink > 0 && node.references === 1) {
|
|
3147
|
+
node.references = 0;
|
|
3148
|
+
fs.ledger.release(path.length * 2, 1);
|
|
3149
|
+
}
|
|
3150
|
+
else {
|
|
3151
|
+
fs.releaseReference(node, path);
|
|
3152
|
+
}
|
|
3153
|
+
if (redirectHandlePool.handle === undefined) {
|
|
3154
|
+
redirectHandlePool.handle = this;
|
|
3155
|
+
}
|
|
2509
3156
|
}
|
|
2510
3157
|
}
|
|
3158
|
+
const redirectHandlePool = { handle: undefined };
|
|
3159
|
+
let _lastRedirectSlice1Path = "";
|
|
3160
|
+
let _lastRedirectSlice1Name = "";
|
|
3161
|
+
let _lastFileSlice1Path = "";
|
|
3162
|
+
let _lastFileSlice1Name = "";
|
|
2511
3163
|
export function tryOpenMemoryRedirectHandleSync(filesystem, path, append, mode, signal) {
|
|
2512
3164
|
const mem = filesystem;
|
|
2513
|
-
const owner = ownedStores.get(mem);
|
|
3165
|
+
const owner = mem._owner ?? ownedStores.get(mem);
|
|
2514
3166
|
if (!owner ||
|
|
2515
3167
|
mem.symlinkCount !== 0 ||
|
|
2516
3168
|
mem.capabilities !== owner.capabilities ||
|
|
2517
|
-
activeConditionalMutations.has(mem.identityScope) ||
|
|
3169
|
+
(activeConditionalMutations.size > 0 && activeConditionalMutations.has(mem.identityScope)) ||
|
|
2518
3170
|
!isStockMemoryMethods(mem, openRedirectFastMethodNames, false) ||
|
|
2519
3171
|
!isCleanAbsolutePath(path) ||
|
|
3172
|
+
path === "/" ||
|
|
2520
3173
|
path === "/dev" ||
|
|
2521
3174
|
path.startsWith("/dev/")) {
|
|
2522
3175
|
return undefined;
|
|
2523
3176
|
}
|
|
2524
3177
|
signal?.throwIfAborted();
|
|
2525
|
-
const validMode =
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
let
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
3178
|
+
const validMode = (mode & 0o777) === mode
|
|
3179
|
+
? mode
|
|
3180
|
+
: mem.mode(mode, 0o666, "open", path);
|
|
3181
|
+
const memInternal = mem;
|
|
3182
|
+
let parent = memInternal.root;
|
|
3183
|
+
let start = 1;
|
|
3184
|
+
let name = "";
|
|
3185
|
+
while (true) {
|
|
3186
|
+
memInternal.permission(parent, 1, "open", path);
|
|
3187
|
+
const slash = path.indexOf("/", start);
|
|
3188
|
+
if (slash === -1) {
|
|
3189
|
+
name = start === 1
|
|
3190
|
+
? (path === _lastRedirectSlice1Path ? _lastRedirectSlice1Name : (_lastRedirectSlice1Path = path, _lastRedirectSlice1Name = path.slice(1)))
|
|
3191
|
+
: path.slice(start);
|
|
3192
|
+
if (exceedsComponentByteLimit(name))
|
|
3193
|
+
memInternal.fail("ENAMETOOLONG", "open", path);
|
|
3194
|
+
break;
|
|
3195
|
+
}
|
|
3196
|
+
const seg = slicePathSegment(path, start, slash);
|
|
3197
|
+
if (exceedsComponentByteLimit(seg))
|
|
3198
|
+
memInternal.fail("ENAMETOOLONG", "open", path);
|
|
3199
|
+
const next = parent.entries.get(seg);
|
|
3200
|
+
if (!next)
|
|
3201
|
+
memInternal.fail("ENOENT", "open", path);
|
|
3202
|
+
if (next.type !== "directory")
|
|
3203
|
+
memInternal.fail("ENOTDIR", "open", path);
|
|
3204
|
+
parent = next;
|
|
3205
|
+
start = slash + 1;
|
|
2538
3206
|
}
|
|
2539
|
-
const
|
|
2540
|
-
ledger
|
|
2541
|
-
|
|
2542
|
-
|
|
3207
|
+
const existing = parent.entries.getForWrite(name);
|
|
3208
|
+
const ledger = memInternal.ledger;
|
|
3209
|
+
let node;
|
|
3210
|
+
if (existing !== undefined) {
|
|
3211
|
+
if (existing.type !== "file")
|
|
3212
|
+
memInternal.fail("EISDIR", "open", path);
|
|
3213
|
+
memInternal.permission(existing, 2, "open", path);
|
|
3214
|
+
ledger.reserve(path.length * 2, 1, "open", path);
|
|
3215
|
+
node = existing;
|
|
2543
3216
|
if (!append) {
|
|
2544
|
-
|
|
3217
|
+
try {
|
|
3218
|
+
memInternal.resizeNode(node, 0, "open", path);
|
|
3219
|
+
}
|
|
3220
|
+
catch (error) {
|
|
3221
|
+
ledger.release(path.length * 2, 1);
|
|
3222
|
+
throw error;
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
node.references++;
|
|
3226
|
+
}
|
|
3227
|
+
else {
|
|
3228
|
+
memInternal.permission(parent, 3, "open", path);
|
|
3229
|
+
const refBytes = path.length * 2;
|
|
3230
|
+
const nameBytes = name.length * 2;
|
|
3231
|
+
if (ledger.hasInfiniteRetained && ledger.hasInfiniteFileBytes) {
|
|
3232
|
+
ledger.reserve(refBytes + nameBytes, 3, "open", path);
|
|
3233
|
+
}
|
|
3234
|
+
else {
|
|
3235
|
+
ledger.reserve(refBytes, 1, "open", path);
|
|
3236
|
+
try {
|
|
3237
|
+
ledger.fileSize(0, "open", path);
|
|
3238
|
+
ledger.reserve(nameBytes, 2, "open", path);
|
|
3239
|
+
}
|
|
3240
|
+
catch (error) {
|
|
3241
|
+
ledger.release(refBytes, 1);
|
|
3242
|
+
throw error;
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
const cache = mem._cache;
|
|
3246
|
+
cache.clearWrites();
|
|
3247
|
+
const now = Date.now === defaultDateNow ? ((++fastWriteNowTick & 63) === 0 ? (fastWriteCachedNow = Date.now()) : fastWriteCachedNow) : Date.now();
|
|
3248
|
+
const fileMode = typeModes.file | validMode;
|
|
3249
|
+
let newNode;
|
|
3250
|
+
if (sharedFileNodePoolLen > 0) {
|
|
3251
|
+
newNode = sharedFileNodePool[--sharedFileNodePoolLen];
|
|
3252
|
+
sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
|
|
3253
|
+
}
|
|
3254
|
+
else {
|
|
3255
|
+
newNode = cache.files.pop();
|
|
3256
|
+
}
|
|
3257
|
+
if (newNode) {
|
|
3258
|
+
newNode.mode = fileMode;
|
|
3259
|
+
newNode.ino = memInternal.nextInode++;
|
|
3260
|
+
if (newNode.nlink !== 1)
|
|
3261
|
+
newNode.nlink = 1;
|
|
3262
|
+
newNode.references = 1;
|
|
3263
|
+
if (newNode.revision !== 0)
|
|
3264
|
+
newNode.revision = 0;
|
|
3265
|
+
newNode.atimeMs = now;
|
|
3266
|
+
newNode.mtimeMs = now;
|
|
3267
|
+
newNode.ctimeMs = now;
|
|
3268
|
+
newNode.birthtimeMs = now;
|
|
3269
|
+
newNode.byteLength = 0;
|
|
3270
|
+
newNode.view = EMPTY_ALLOC_BYTES;
|
|
3271
|
+
newNode.allocation = DUMMY_POOL_ALLOCATION;
|
|
3272
|
+
}
|
|
3273
|
+
else {
|
|
3274
|
+
newNode = new MemoryFileNode(fileMode, memInternal.nextInode++, now, 0, DUMMY_POOL_ALLOCATION, EMPTY_ALLOC_BYTES);
|
|
3275
|
+
newNode.references = 1;
|
|
3276
|
+
}
|
|
3277
|
+
const prevNlink = parent.cachedNlinkRev === parent.revision ? parent.cachedNlink : (parent.entries.size === 0 ? 2 : undefined);
|
|
3278
|
+
const entries = parent.entries;
|
|
3279
|
+
if (lastFastMapMiss.map === entries && lastFastMapMiss.key === name && entries._next < entries._keys.length) {
|
|
3280
|
+
const entryIdx = entries._next++;
|
|
3281
|
+
entries._table[lastFastMapMiss.slot] = entryIdx;
|
|
3282
|
+
entries._keys[entryIdx] = name;
|
|
3283
|
+
entries._vals[entryIdx] = newNode;
|
|
3284
|
+
entries.size++;
|
|
3285
|
+
lastFastMapMiss.map = undefined;
|
|
3286
|
+
lastFastMapMiss.key = "";
|
|
2545
3287
|
}
|
|
3288
|
+
else {
|
|
3289
|
+
entries.set(name, newNode);
|
|
3290
|
+
}
|
|
3291
|
+
mem.mutationTick = (mem.mutationTick + 1) | 0;
|
|
3292
|
+
parent.revision = parent.revision < 1073741823 ? (parent.revision + 1) | 0 : Math.min(Number.MAX_SAFE_INTEGER + 1, parent.revision + 1);
|
|
3293
|
+
parent.mtimeMs = parent.ctimeMs = now;
|
|
3294
|
+
if (prevNlink !== undefined) {
|
|
3295
|
+
parent.cachedNlink = prevNlink;
|
|
3296
|
+
parent.cachedNlinkRev = parent.revision;
|
|
3297
|
+
}
|
|
3298
|
+
node = newNode;
|
|
2546
3299
|
}
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
3300
|
+
const handle = redirectHandlePool.handle;
|
|
3301
|
+
if (handle !== undefined) {
|
|
3302
|
+
redirectHandlePool.handle = undefined;
|
|
3303
|
+
handle.reset(mem, path, node, append);
|
|
3304
|
+
return handle;
|
|
2550
3305
|
}
|
|
2551
|
-
node.references++;
|
|
2552
3306
|
return new MemoryRedirectHandle(mem, path, node, append);
|
|
2553
3307
|
}
|
|
2554
3308
|
export function utf8ByteLength(value) {
|
|
@@ -2614,14 +3368,14 @@ export function tryResolveMemoryDevicePath(filesystem, path, resizeCreate) {
|
|
|
2614
3368
|
return undefined;
|
|
2615
3369
|
const slash = path.indexOf("/", start);
|
|
2616
3370
|
if (slash === -1) {
|
|
2617
|
-
const name = path.
|
|
3371
|
+
const name = slicePathSegment(path, start, path.length);
|
|
2618
3372
|
if (exceedsComponentByteLimit(name))
|
|
2619
3373
|
return undefined;
|
|
2620
3374
|
if (resizeCreate === false && !current.entries.has(name))
|
|
2621
3375
|
return undefined;
|
|
2622
3376
|
return path;
|
|
2623
3377
|
}
|
|
2624
|
-
const name = path
|
|
3378
|
+
const name = slicePathSegment(path, start, slash);
|
|
2625
3379
|
if (exceedsComponentByteLimit(name))
|
|
2626
3380
|
return undefined;
|
|
2627
3381
|
const next = current.entries.get(name);
|
|
@@ -2631,7 +3385,7 @@ export function tryResolveMemoryDevicePath(filesystem, path, resizeCreate) {
|
|
|
2631
3385
|
let remStart = slash + 1;
|
|
2632
3386
|
while (true) {
|
|
2633
3387
|
const nextSlash = path.indexOf("/", remStart);
|
|
2634
|
-
const seg = nextSlash === -1 ? path.
|
|
3388
|
+
const seg = nextSlash === -1 ? slicePathSegment(path, remStart, path.length) : slicePathSegment(path, remStart, nextSlash);
|
|
2635
3389
|
if (exceedsComponentByteLimit(seg))
|
|
2636
3390
|
return undefined;
|
|
2637
3391
|
if (nextSlash === -1)
|
|
@@ -2645,9 +3399,19 @@ export function tryResolveMemoryDevicePath(filesystem, path, resizeCreate) {
|
|
|
2645
3399
|
start = slash + 1;
|
|
2646
3400
|
}
|
|
2647
3401
|
}
|
|
3402
|
+
const cachedMethodValuesMap = new WeakMap();
|
|
3403
|
+
function getExpectedMethodValues(names) {
|
|
3404
|
+
let vals = cachedMethodValuesMap.get(names);
|
|
3405
|
+
if (!vals) {
|
|
3406
|
+
vals = names.map(name => memoryImplementation[name]?.value);
|
|
3407
|
+
cachedMethodValuesMap.set(names, vals);
|
|
3408
|
+
}
|
|
3409
|
+
return vals;
|
|
3410
|
+
}
|
|
3411
|
+
const hasOwn = Object.prototype.hasOwnProperty;
|
|
2648
3412
|
function isStockMemoryMethods(mem, names, checkRootAccessor = true) {
|
|
2649
|
-
const owner = ownedStores.get(mem);
|
|
2650
|
-
if (!owner || Object.getPrototypeOf(mem) !== MemoryFileSystem.prototype)
|
|
3413
|
+
const owner = mem?._owner ?? ownedStores.get(mem);
|
|
3414
|
+
if (!owner || Object.getPrototypeOf(mem) !== MemoryFileSystem.prototype || ownedStores.get(mem) !== owner)
|
|
2651
3415
|
return false;
|
|
2652
3416
|
if (checkRootAccessor) {
|
|
2653
3417
|
const rootDesc = Object.getOwnPropertyDescriptor(mem, "root");
|
|
@@ -2657,36 +3421,124 @@ function isStockMemoryMethods(mem, names, checkRootAccessor = true) {
|
|
|
2657
3421
|
else if (mem.root !== owner.root) {
|
|
2658
3422
|
return false;
|
|
2659
3423
|
}
|
|
3424
|
+
const vals = getExpectedMethodValues(names);
|
|
3425
|
+
const proto = MemoryFileSystem.prototype;
|
|
2660
3426
|
for (let i = 0; i < names.length; i++) {
|
|
2661
3427
|
const name = names[i];
|
|
2662
|
-
|
|
3428
|
+
const expected = vals[i];
|
|
3429
|
+
if (hasOwn.call(mem, name)) {
|
|
2663
3430
|
const descriptor = Object.getOwnPropertyDescriptor(mem, name);
|
|
2664
|
-
if (!descriptor || !("value" in descriptor) || descriptor.value !==
|
|
3431
|
+
if (!descriptor || !("value" in descriptor) || descriptor.value !== expected)
|
|
2665
3432
|
return false;
|
|
2666
3433
|
}
|
|
2667
|
-
else if (
|
|
3434
|
+
else if (proto[name] !== expected) {
|
|
2668
3435
|
return false;
|
|
2669
3436
|
}
|
|
2670
3437
|
}
|
|
2671
3438
|
return true;
|
|
2672
3439
|
}
|
|
2673
|
-
|
|
3440
|
+
let lastReadViewData;
|
|
3441
|
+
let lastReadViewSourceRef;
|
|
3442
|
+
export function getLastReadMemoryFileSourceRef(view) {
|
|
3443
|
+
const ref = view === lastReadViewData ? lastReadViewSourceRef : undefined;
|
|
3444
|
+
lastReadViewData = undefined;
|
|
3445
|
+
lastReadViewSourceRef = undefined;
|
|
3446
|
+
return ref;
|
|
3447
|
+
}
|
|
3448
|
+
export function tryReadMemoryFileViewSync(filesystem, path, maxBytes, signal, captureSourceRef = false) {
|
|
2674
3449
|
const mem = filesystem;
|
|
2675
|
-
if (
|
|
3450
|
+
if (mem._owner === undefined || !isStockMemoryMethods(mem, readFileFastMethodNames, false))
|
|
2676
3451
|
return undefined;
|
|
2677
3452
|
signal?.throwIfAborted();
|
|
2678
3453
|
if (maxBytes !== undefined)
|
|
2679
3454
|
mem.integer(maxBytes, "readFile", path);
|
|
2680
3455
|
const node = mem.file(path, "readFile");
|
|
2681
3456
|
mem.permission(node, 4, "readFile", path);
|
|
2682
|
-
|
|
3457
|
+
const data = node.data;
|
|
3458
|
+
if (maxBytes !== undefined && data.byteLength > maxBytes)
|
|
2683
3459
|
mem.fail("EFBIG", "readFile", path);
|
|
2684
|
-
|
|
2685
|
-
|
|
3460
|
+
if (Date.now !== defaultDateNow)
|
|
3461
|
+
node.atimeMs = Date.now();
|
|
3462
|
+
else if ((++fastWriteNowTick & 63) === 0)
|
|
3463
|
+
node.atimeMs = fastWriteCachedNow = Date.now();
|
|
3464
|
+
if (captureSourceRef && node.revision === 0) {
|
|
3465
|
+
lastReadViewData = data;
|
|
3466
|
+
lastReadViewSourceRef = node.sourceRef;
|
|
3467
|
+
}
|
|
3468
|
+
return data;
|
|
3469
|
+
}
|
|
3470
|
+
let segCache0 = "";
|
|
3471
|
+
let segCache1 = "";
|
|
3472
|
+
let segCache2 = "";
|
|
3473
|
+
let segCache3 = "";
|
|
3474
|
+
let segCacheCursor = 0;
|
|
3475
|
+
function slicePathSegment(path, start, end) {
|
|
3476
|
+
const len = end - start;
|
|
3477
|
+
if (len <= 0)
|
|
3478
|
+
return "";
|
|
3479
|
+
if (len === 1)
|
|
3480
|
+
return String.fromCharCode(path.charCodeAt(start));
|
|
3481
|
+
if (len <= 16) {
|
|
3482
|
+
const c0 = path.charCodeAt(start);
|
|
3483
|
+
if (segCache0.length === len && segCache0.charCodeAt(0) === c0) {
|
|
3484
|
+
let ok = true;
|
|
3485
|
+
for (let i = 1; i < len; i++)
|
|
3486
|
+
if (path.charCodeAt(start + i) !== segCache0.charCodeAt(i)) {
|
|
3487
|
+
ok = false;
|
|
3488
|
+
break;
|
|
3489
|
+
}
|
|
3490
|
+
if (ok)
|
|
3491
|
+
return segCache0;
|
|
3492
|
+
}
|
|
3493
|
+
if (segCache1.length === len && segCache1.charCodeAt(0) === c0) {
|
|
3494
|
+
let ok = true;
|
|
3495
|
+
for (let i = 1; i < len; i++)
|
|
3496
|
+
if (path.charCodeAt(start + i) !== segCache1.charCodeAt(i)) {
|
|
3497
|
+
ok = false;
|
|
3498
|
+
break;
|
|
3499
|
+
}
|
|
3500
|
+
if (ok)
|
|
3501
|
+
return segCache1;
|
|
3502
|
+
}
|
|
3503
|
+
if (segCache2.length === len && segCache2.charCodeAt(0) === c0) {
|
|
3504
|
+
let ok = true;
|
|
3505
|
+
for (let i = 1; i < len; i++)
|
|
3506
|
+
if (path.charCodeAt(start + i) !== segCache2.charCodeAt(i)) {
|
|
3507
|
+
ok = false;
|
|
3508
|
+
break;
|
|
3509
|
+
}
|
|
3510
|
+
if (ok)
|
|
3511
|
+
return segCache2;
|
|
3512
|
+
}
|
|
3513
|
+
if (segCache3.length === len && segCache3.charCodeAt(0) === c0) {
|
|
3514
|
+
let ok = true;
|
|
3515
|
+
for (let i = 1; i < len; i++)
|
|
3516
|
+
if (path.charCodeAt(start + i) !== segCache3.charCodeAt(i)) {
|
|
3517
|
+
ok = false;
|
|
3518
|
+
break;
|
|
3519
|
+
}
|
|
3520
|
+
if (ok)
|
|
3521
|
+
return segCache3;
|
|
3522
|
+
}
|
|
3523
|
+
const sliced = path.slice(start, end);
|
|
3524
|
+
const slot = segCacheCursor;
|
|
3525
|
+
segCacheCursor = (slot + 1) & 3;
|
|
3526
|
+
if (slot === 0)
|
|
3527
|
+
segCache0 = sliced;
|
|
3528
|
+
else if (slot === 1)
|
|
3529
|
+
segCache1 = sliced;
|
|
3530
|
+
else if (slot === 2)
|
|
3531
|
+
segCache2 = sliced;
|
|
3532
|
+
else
|
|
3533
|
+
segCache3 = sliced;
|
|
3534
|
+
return sliced;
|
|
3535
|
+
}
|
|
3536
|
+
return path.slice(start, end);
|
|
2686
3537
|
}
|
|
2687
3538
|
export function tryGetMemoryDirectoryEntryNamesSync(filesystem, path) {
|
|
2688
3539
|
const mem = filesystem;
|
|
2689
|
-
|
|
3540
|
+
const owner = ownedStores.get(mem);
|
|
3541
|
+
if (!owner || mem.symlinkCount !== 0 || !isStockMemoryMethods(mem, readFileFastMethodNames, false))
|
|
2690
3542
|
return undefined;
|
|
2691
3543
|
const root = mem.root;
|
|
2692
3544
|
if (path === "/") {
|
|
@@ -2696,6 +3548,16 @@ export function tryGetMemoryDirectoryEntryNamesSync(filesystem, path) {
|
|
|
2696
3548
|
}
|
|
2697
3549
|
if (!isCleanAbsolutePath(path) || path === "/dev" || path.startsWith("/dev/"))
|
|
2698
3550
|
return undefined;
|
|
3551
|
+
const cache = memoryCaches.get(owner.ledger);
|
|
3552
|
+
const fastDir = cache?.lastFastDirNode;
|
|
3553
|
+
const fastPrefix = cache?.lastFastDirPrefix;
|
|
3554
|
+
if (fastDir !== undefined &&
|
|
3555
|
+
fastDir.nlink !== 0 &&
|
|
3556
|
+
((fastDir.mode >> 6) & 4) === 4 &&
|
|
3557
|
+
fastPrefix !== undefined &&
|
|
3558
|
+
(fastPrefix === path || (fastPrefix.length === path.length + 1 && fastPrefix.charCodeAt(path.length) === 47 && fastPrefix.startsWith(path)))) {
|
|
3559
|
+
return fastDir.entries;
|
|
3560
|
+
}
|
|
2699
3561
|
let current = root;
|
|
2700
3562
|
let start = 1;
|
|
2701
3563
|
while (true) {
|
|
@@ -2703,12 +3565,12 @@ export function tryGetMemoryDirectoryEntryNamesSync(filesystem, path) {
|
|
|
2703
3565
|
return undefined;
|
|
2704
3566
|
const slash = path.indexOf("/", start);
|
|
2705
3567
|
if (slash === -1) {
|
|
2706
|
-
const next = current.entries.
|
|
3568
|
+
const next = current.entries.getSubstr(path, start, path.length);
|
|
2707
3569
|
if (!next || next.type !== "directory" || ((next.mode >> 6) & 4) !== 4)
|
|
2708
3570
|
return undefined;
|
|
2709
3571
|
return next.entries;
|
|
2710
3572
|
}
|
|
2711
|
-
const next = current.entries.
|
|
3573
|
+
const next = current.entries.getSubstr(path, start, slash);
|
|
2712
3574
|
if (!next || next.type !== "directory")
|
|
2713
3575
|
return undefined;
|
|
2714
3576
|
current = next;
|
|
@@ -2731,7 +3593,7 @@ export function tryWriteMemoryFileSync(filesystem, path, data, append, mode, sig
|
|
|
2731
3593
|
mem.writeMemoryFileFast(path, data, append, mode);
|
|
2732
3594
|
return true;
|
|
2733
3595
|
}
|
|
2734
|
-
export function tryMkdirMemorySync(filesystem, path, recursive, mode, signal) {
|
|
3596
|
+
export function tryMkdirMemorySync(filesystem, path, recursive, mode, signal, dirPrefixHint) {
|
|
2735
3597
|
const mem = filesystem;
|
|
2736
3598
|
const owner = ownedStores.get(mem);
|
|
2737
3599
|
if (!owner ||
|
|
@@ -2746,16 +3608,39 @@ export function tryMkdirMemorySync(filesystem, path, recursive, mode, signal) {
|
|
|
2746
3608
|
signal?.throwIfAborted();
|
|
2747
3609
|
const validMode = mem.mode(mode, 0o777, "mkdir", path);
|
|
2748
3610
|
if (recursive) {
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
3611
|
+
let current = owner.root;
|
|
3612
|
+
let start = 1;
|
|
3613
|
+
while (true) {
|
|
3614
|
+
mem.permission(current, 1, "mkdir", path);
|
|
3615
|
+
const slash = path.indexOf("/", start);
|
|
3616
|
+
const seg = slash === -1 ? slicePathSegment(path, start, path.length) : slicePathSegment(path, start, slash);
|
|
3617
|
+
if (exceedsComponentByteLimit(seg))
|
|
3618
|
+
mem.fail("ENAMETOOLONG", "mkdir", path);
|
|
3619
|
+
let next = current.entries.get(seg);
|
|
3620
|
+
if (!next) {
|
|
3621
|
+
mem.permission(current, 3, "mkdir", path);
|
|
3622
|
+
next = mem.addDirectoryNode(current, seg, validMode, "mkdir", path, fastWriteCachedNow);
|
|
3623
|
+
}
|
|
3624
|
+
else if (next.type !== "directory") {
|
|
3625
|
+
mem.fail(slash === -1 ? "EEXIST" : "ENOTDIR", "mkdir", path);
|
|
3626
|
+
}
|
|
3627
|
+
if (slash === -1) {
|
|
3628
|
+
if (dirPrefixHint !== undefined) {
|
|
3629
|
+
mem._cache.lastFastDirPrefix = dirPrefixHint;
|
|
3630
|
+
mem._cache.lastFastDirNode = next;
|
|
3631
|
+
}
|
|
3632
|
+
break;
|
|
3633
|
+
}
|
|
3634
|
+
current = next;
|
|
3635
|
+
start = slash + 1;
|
|
3636
|
+
}
|
|
2752
3637
|
return true;
|
|
2753
3638
|
}
|
|
2754
|
-
const location = mem.resolve(path, "mkdir",
|
|
3639
|
+
const location = mem.resolve(path, "mkdir", sharedAllowMissingNoFollowOptions);
|
|
2755
3640
|
if (location.node)
|
|
2756
3641
|
mem.fail("EEXIST", "mkdir", path);
|
|
2757
3642
|
mem.permission(location.parent, 3, "mkdir", path);
|
|
2758
|
-
mem.addDirectoryNode(location.parent, location.name, validMode, "mkdir", path);
|
|
3643
|
+
mem.addDirectoryNode(location.parent, location.name, validMode, "mkdir", path, fastWriteCachedNow);
|
|
2759
3644
|
return true;
|
|
2760
3645
|
}
|
|
2761
3646
|
export function tryRmRfMemorySync(filesystem, path, signal) {
|
|
@@ -2772,16 +3657,46 @@ export function tryRmRfMemorySync(filesystem, path, signal) {
|
|
|
2772
3657
|
return false;
|
|
2773
3658
|
}
|
|
2774
3659
|
signal?.throwIfAborted();
|
|
2775
|
-
let
|
|
3660
|
+
let current = owner.root;
|
|
3661
|
+
let start = 1;
|
|
3662
|
+
let targetNode;
|
|
3663
|
+
let targetName = "";
|
|
3664
|
+
while (true) {
|
|
3665
|
+
mem.permission(current, 1, "rm", path);
|
|
3666
|
+
const slash = path.indexOf("/", start);
|
|
3667
|
+
if (slash === -1) {
|
|
3668
|
+
targetName = slicePathSegment(path, start, path.length);
|
|
3669
|
+
if (exceedsComponentByteLimit(targetName))
|
|
3670
|
+
mem.fail("ENAMETOOLONG", "rm", path);
|
|
3671
|
+
targetNode = current.entries.get(targetName);
|
|
3672
|
+
if (!targetNode)
|
|
3673
|
+
return true;
|
|
3674
|
+
break;
|
|
3675
|
+
}
|
|
3676
|
+
const seg = slicePathSegment(path, start, slash);
|
|
3677
|
+
if (exceedsComponentByteLimit(seg))
|
|
3678
|
+
mem.fail("ENAMETOOLONG", "rm", path);
|
|
3679
|
+
const next = current.entries.get(seg);
|
|
3680
|
+
if (!next)
|
|
3681
|
+
return true;
|
|
3682
|
+
if (next.type !== "directory")
|
|
3683
|
+
mem.fail("ENOTDIR", "rm", path);
|
|
3684
|
+
current = next;
|
|
3685
|
+
start = slash + 1;
|
|
3686
|
+
}
|
|
3687
|
+
sharedFastLocation.node = targetNode;
|
|
3688
|
+
sharedFastLocation.parent = current;
|
|
3689
|
+
sharedFastLocation.name = targetName;
|
|
3690
|
+
sharedFastLocation.path = path;
|
|
2776
3691
|
try {
|
|
2777
|
-
|
|
3692
|
+
mem.removeLocation(sharedFastLocation, path, "rm", true, fastWriteCachedNow);
|
|
2778
3693
|
}
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
3694
|
+
finally {
|
|
3695
|
+
sharedFastLocation.node = undefined;
|
|
3696
|
+
sharedFastLocation.parent = DUMMY_POOL_DIR_NODE;
|
|
3697
|
+
sharedFastLocation.name = "";
|
|
3698
|
+
sharedFastLocation.path = "";
|
|
2783
3699
|
}
|
|
2784
|
-
mem.removeLocation(location, path, "rm", true);
|
|
2785
3700
|
return true;
|
|
2786
3701
|
}
|
|
2787
3702
|
export function tryWriteMemoryFileInDirSync(filesystem, dirPrefix, name, data, append, mode, signal) {
|