@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.
- package/README.md +9 -1
- package/dist/safe-fs/contracts/filesystem.d.ts +8 -0
- package/dist/safe-fs/core.d.ts +1 -1
- package/dist/safe-fs/core.js +1 -1
- package/dist/safe-fs/fs/capabilities.js +4 -2
- package/dist/safe-fs/fs/memory/index.d.ts +7 -4
- package/dist/safe-fs/fs/memory/index.js +641 -111
- package/dist/safe-fs/fs/memory/ledger.d.ts +3 -2
- package/dist/safe-fs/fs/memory/ledger.js +11 -1
- package/dist/safe-fs/fs/mount/index.js +1 -1
- package/dist/safe-fs/fs/real/index.js +28 -9
- package/dist/safe-fs/fs/scoped.d.ts +1 -0
- package/dist/safe-fs/fs/scoped.js +34 -3
- package/dist/safe-fs/node/native-seek-unavailable.d.ts +1 -0
- package/dist/safe-fs/node/native-seek-unavailable.js +5 -0
- package/dist/safe-fs/platform/browser.d.ts +8 -0
- package/dist/safe-fs/platform/browser.js +1 -0
- package/dist/safe-fs/platform/node.d.ts +8 -0
- package/dist/safe-fs/platform/node.js +1 -0
- package/package.json +6 -2
|
@@ -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
|
|
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
|
-
|
|
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()
|
|
516
|
-
|
|
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
|
|
531
|
-
|
|
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
|
|
1186
|
+
return DUMMY_POOL_ALLOCATION;
|
|
902
1187
|
}
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1060
|
-
|
|
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 +
|
|
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.
|
|
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
|
-
|
|
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
|
|
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 =
|
|
1651
|
+
const now = fastWriteCachedNow;
|
|
1336
1652
|
const fileMode = typeModes.file | mode;
|
|
1337
|
-
const view = allocation.data;
|
|
1338
|
-
let node
|
|
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
|
|
1343
|
-
|
|
1344
|
-
node.
|
|
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,
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
1894
|
-
|
|
1895
|
-
removed
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
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
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
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
|
-
|
|
1925
|
-
|
|
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
|
-
|
|
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 (
|
|
2762
|
+
if (len === 0)
|
|
2386
2763
|
return;
|
|
2387
2764
|
const pos = this.append ? this.node.byteLength : this.position;
|
|
2388
|
-
|
|
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 +
|
|
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
|
-
|
|
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 =
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
let
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
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
|
|
2428
|
-
ledger
|
|
2429
|
-
|
|
2430
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
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",
|
|
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
|
|
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
|
-
|
|
3195
|
+
mem.removeLocation(sharedFastLocation, path, "rm", true, fastWriteCachedNow);
|
|
2666
3196
|
}
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
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) {
|