@poe-platform/safe-fs 0.1.629 → 0.1.631
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/bridge/stats.js +1 -1
- package/dist/safe-fs/contracts/descriptor.d.ts +31 -0
- package/dist/safe-fs/contracts/descriptor.js +1 -0
- package/dist/safe-fs/contracts/errors.d.ts +1 -1
- package/dist/safe-fs/contracts/errors.js +1 -1
- package/dist/safe-fs/contracts/filesystem.d.ts +14 -1
- package/dist/safe-fs/core.d.ts +3 -0
- package/dist/safe-fs/core.js +2 -0
- package/dist/safe-fs/fs/capabilities.d.ts +1 -1
- package/dist/safe-fs/fs/capabilities.js +5 -3
- package/dist/safe-fs/fs/descriptor.d.ts +21 -0
- package/dist/safe-fs/fs/descriptor.js +244 -0
- package/dist/safe-fs/fs/devices/index.d.ts +5 -1
- package/dist/safe-fs/fs/devices/index.js +53 -4
- package/dist/safe-fs/fs/memory/index.d.ts +8 -1
- package/dist/safe-fs/fs/memory/index.js +125 -8
- package/dist/safe-fs/fs/memory/limits.d.ts +1 -0
- package/dist/safe-fs/fs/memory/limits.js +7 -1
- package/dist/safe-fs/fs/mount/index.d.ts +5 -1
- package/dist/safe-fs/fs/mount/index.js +69 -6
- package/dist/safe-fs/fs/overlay/index.d.ts +2 -0
- package/dist/safe-fs/fs/overlay/index.js +9 -1
- package/dist/safe-fs/fs/quota/index.js +5 -1
- package/dist/safe-fs/fs/readonly/index.d.ts +2 -0
- package/dist/safe-fs/fs/readonly/index.js +35 -2
- package/dist/safe-fs/fs/real/index.d.ts +3 -0
- package/dist/safe-fs/fs/real/index.js +73 -3
- package/dist/safe-fs/fs/s3/filesystem.d.ts +3 -0
- package/dist/safe-fs/fs/s3/filesystem.js +5 -0
- package/dist/safe-fs/fs/scoped.js +68 -17
- package/dist/safe-fs/fs/webdav/webdav.d.ts +3 -0
- package/dist/safe-fs/fs/webdav/webdav.js +5 -0
- package/dist/safe-fs/index.d.ts +3 -0
- package/dist/safe-fs/index.js +2 -0
- package/dist/safe-fs/python/emscripten.d.ts +14 -0
- package/dist/safe-fs/python/emscripten.js +354 -0
- package/dist/safe-fs/python/filesystem.d.ts +51 -0
- package/dist/safe-fs/python/filesystem.js +216 -0
- package/dist/safe-fs/python/flags.d.ts +3 -0
- package/dist/safe-fs/python/flags.js +14 -0
- package/dist/safe-fs/python/index.d.ts +7 -0
- package/dist/safe-fs/python/index.js +4 -0
- package/dist/safe-fs/python/request.d.ts +2 -0
- package/dist/safe-fs/python/request.js +61 -0
- package/dist/safe-fs/python/stat.d.ts +8 -0
- package/dist/safe-fs/python/stat.js +31 -0
- package/dist/safe-fs/xml.d.ts +3 -0
- package/dist/safe-fs/xml.js +41 -19
- package/package.json +5 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AppendFileOptions, CopyFileOptions, DirectoryEntry, EntryComparison, FileReadHandle, FileResizeHandle, FileStat, FileSystem, FsOptions, RenameOptions, MkdirOptions, ReadDirectoryOptions, ReadFileOptions, ReadStreamOptions, RemoveOptions, OpenReadFileOptions, OpenResizeFileOptions, WriteFileOptions, ConditionalWriteFileOptions, ConditionalRemoveFileOptions, CreateStagedFileOptions, FileStaging, PublishStagedFileOptions, PrepareDirectoryOptions, StagedFileContent } from "../../contracts/filesystem.js";
|
|
1
|
+
import type { AppendFileOptions, CopyFileOptions, DirectoryEntry, EntryComparison, FileReadHandle, FileResizeHandle, FileStat, FileSystem, FsOptions, RenameOptions, MkdirOptions, ReadDirectoryOptions, ReadFileOptions, ReadStreamOptions, RemoveOptions, FileDescriptor, OpenFileOptions, OpenReadFileOptions, OpenResizeFileOptions, WriteFileOptions, ConditionalWriteFileOptions, ConditionalRemoveFileOptions, ConditionalRemoveEntryOptions, CreateStagedFileOptions, FileStaging, PublishStagedFileOptions, PrepareDirectoryOptions, StagedFileContent } from "../../contracts/filesystem.js";
|
|
2
2
|
import type { ByteSource } from "../../contracts/io.js";
|
|
3
3
|
import { type MemoryFileSystemOptions } from "./limits.js";
|
|
4
4
|
export { defaultMemoryFileSystemLimits, type MemoryFileSystemLimits, type MemoryFileSystemOptions } from "./limits.js";
|
|
@@ -9,6 +9,7 @@ export declare class MemoryFileSystem implements FileSystem {
|
|
|
9
9
|
readdir: true;
|
|
10
10
|
realpath: true;
|
|
11
11
|
access: true;
|
|
12
|
+
open: true;
|
|
12
13
|
write: true;
|
|
13
14
|
append: true;
|
|
14
15
|
exclusiveCreate: true;
|
|
@@ -35,6 +36,7 @@ export declare class MemoryFileSystem implements FileSystem {
|
|
|
35
36
|
atomicRename: true;
|
|
36
37
|
atomicFileStaging: true;
|
|
37
38
|
atomicFileMutation: true;
|
|
39
|
+
atomicEntryRemoval: true;
|
|
38
40
|
atomicDirectoryMetadata: true;
|
|
39
41
|
streamingRead: true;
|
|
40
42
|
retainedRead: true;
|
|
@@ -46,6 +48,7 @@ export declare class MemoryFileSystem implements FileSystem {
|
|
|
46
48
|
private nextInode;
|
|
47
49
|
private readonly ledger;
|
|
48
50
|
private readonly root;
|
|
51
|
+
private totalBytes;
|
|
49
52
|
constructor(options?: MemoryFileSystemOptions);
|
|
50
53
|
compareEntry(path: string, peer: FileSystem, peerPath: string, options?: FsOptions): Promise<EntryComparison>;
|
|
51
54
|
canonicalizeMissingTarget(path: string, options?: FsOptions): string | undefined;
|
|
@@ -55,6 +58,7 @@ export declare class MemoryFileSystem implements FileSystem {
|
|
|
55
58
|
private releaseNode;
|
|
56
59
|
private releaseReference;
|
|
57
60
|
private replaceData;
|
|
61
|
+
private admitSize;
|
|
58
62
|
private fail;
|
|
59
63
|
private validatePath;
|
|
60
64
|
private permission;
|
|
@@ -68,6 +72,7 @@ export declare class MemoryFileSystem implements FileSystem {
|
|
|
68
72
|
private snapshot;
|
|
69
73
|
private bytes;
|
|
70
74
|
private allocate;
|
|
75
|
+
open(path: string, options: OpenFileOptions): Promise<FileDescriptor>;
|
|
71
76
|
private prepareWrite;
|
|
72
77
|
private openWrite;
|
|
73
78
|
private writeData;
|
|
@@ -80,6 +85,7 @@ export declare class MemoryFileSystem implements FileSystem {
|
|
|
80
85
|
removeStagedFile(staging: FileStaging, options?: FsOptions): Promise<void>;
|
|
81
86
|
prepareDirectory(path: string, options: PrepareDirectoryOptions): Promise<FileStat>;
|
|
82
87
|
writeFileConditional(path: string, data: Uint8Array, options: ConditionalWriteFileOptions): Promise<FileStat>;
|
|
88
|
+
removeEntryConditional(path: string, options: ConditionalRemoveEntryOptions): Promise<void>;
|
|
83
89
|
removeFileConditional(path: string, options: ConditionalRemoveFileOptions): Promise<void>;
|
|
84
90
|
writeFile(path: string, data: Uint8Array, options?: WriteFileOptions): Promise<void>;
|
|
85
91
|
appendFile(path: string, data: Uint8Array, options?: AppendFileOptions): Promise<void>;
|
|
@@ -89,6 +95,7 @@ export declare class MemoryFileSystem implements FileSystem {
|
|
|
89
95
|
mkdir(path: string, options?: MkdirOptions): Promise<void>;
|
|
90
96
|
rmdir(path: string, options?: FsOptions): Promise<void>;
|
|
91
97
|
rm(path: string, options?: RemoveOptions): Promise<void>;
|
|
98
|
+
unlink(path: string, options?: FsOptions): Promise<void>;
|
|
92
99
|
rename(source: string, destination: string, options?: RenameOptions): Promise<void>;
|
|
93
100
|
copyFile(source: string, destination: string, options?: CopyFileOptions): Promise<void>;
|
|
94
101
|
realpath(path: string, options?: FsOptions): Promise<string>;
|
|
@@ -3,11 +3,13 @@ import { assertCallbackAuthorityAllowed, compareEntries, registerEntryAuthority
|
|
|
3
3
|
import { getOwnedS3Entry } from "../s3/registry.js";
|
|
4
4
|
import { getOwnedWebDavEntry } from "../webdav/resource-id.js";
|
|
5
5
|
import { admitDirectoryEntries, directoryEntryLimit } from "../directory-admission.js";
|
|
6
|
+
import { openFileDescriptor } from "../descriptor.js";
|
|
6
7
|
import { resolveMissingTarget } from "./missing-target.js";
|
|
7
8
|
import { MemoryAllocation, MemoryLedger } from "./ledger.js";
|
|
8
9
|
import { normalizeMemoryFileSystemLimits } from "./limits.js";
|
|
9
10
|
export { defaultMemoryFileSystemLimits } from "./limits.js";
|
|
10
11
|
const typeModes = { file: 0o100000, directory: 0o040000, symlink: 0o120000 };
|
|
12
|
+
const preferredIoBlockSize = 64 * 1024;
|
|
11
13
|
const ext4HtreeEof64 = (1n << 63n) - 1n;
|
|
12
14
|
const ownedStats = new WeakMap();
|
|
13
15
|
const ownedStores = new WeakMap();
|
|
@@ -55,7 +57,7 @@ const compareOwnedMemory = async (own, peer, options) => {
|
|
|
55
57
|
export class MemoryFileSystem {
|
|
56
58
|
capabilities = ((filesystem) => {
|
|
57
59
|
return Object.freeze({
|
|
58
|
-
read: true, stat: true, readdir: true, realpath: true, access: true,
|
|
60
|
+
read: true, stat: true, readdir: true, realpath: true, access: true, open: true,
|
|
59
61
|
write: true, append: true, exclusiveCreate: true, explicitDirectories: true, implicitDirectories: false,
|
|
60
62
|
mkdir: true, recursiveMkdir: true, remove: true, removeDirectory: true, recursiveRemove: true,
|
|
61
63
|
rename: true, atomicRenameNoReplace: true, copy: true, exclusiveCopy: true, readlink: true, truncate: true,
|
|
@@ -66,7 +68,7 @@ export class MemoryFileSystem {
|
|
|
66
68
|
permissions: true,
|
|
67
69
|
timestamps: true,
|
|
68
70
|
atomicRename: true,
|
|
69
|
-
atomicFileStaging: true, atomicFileMutation: true,
|
|
71
|
+
atomicFileStaging: true, atomicFileMutation: true, atomicEntryRemoval: true,
|
|
70
72
|
atomicDirectoryMetadata: true,
|
|
71
73
|
streamingRead: true,
|
|
72
74
|
retainedRead: true,
|
|
@@ -79,6 +81,7 @@ export class MemoryFileSystem {
|
|
|
79
81
|
nextInode = 1;
|
|
80
82
|
ledger;
|
|
81
83
|
root;
|
|
84
|
+
totalBytes = 0;
|
|
82
85
|
constructor(options = {}) {
|
|
83
86
|
this.ledger = new MemoryLedger(normalizeMemoryFileSystemLimits(options));
|
|
84
87
|
this.ledger.reserve(0, 1, "mkdir", "/");
|
|
@@ -129,7 +132,11 @@ export class MemoryFileSystem {
|
|
|
129
132
|
this.ledger.reserve(bytes, 2, syscall, path);
|
|
130
133
|
try {
|
|
131
134
|
const node = create();
|
|
135
|
+
if (node.type === "file")
|
|
136
|
+
this.admitSize(undefined, node.data.byteLength, syscall, path);
|
|
132
137
|
parent.entries.set(name, node);
|
|
138
|
+
if (node.type === "file")
|
|
139
|
+
this.totalBytes += node.data.byteLength;
|
|
133
140
|
this.changed(parent);
|
|
134
141
|
return node;
|
|
135
142
|
}
|
|
@@ -142,8 +149,10 @@ export class MemoryFileSystem {
|
|
|
142
149
|
if (node.nlink !== 0 || node.references !== 0)
|
|
143
150
|
return;
|
|
144
151
|
this.ledger.release(node.type === "symlink" ? node.target.length * 2 : 0, 1);
|
|
145
|
-
if (node.type === "file")
|
|
152
|
+
if (node.type === "file") {
|
|
153
|
+
this.totalBytes -= node.data.byteLength;
|
|
146
154
|
node.allocation.release();
|
|
155
|
+
}
|
|
147
156
|
}
|
|
148
157
|
releaseReference(node, path) {
|
|
149
158
|
node.references--;
|
|
@@ -152,10 +161,17 @@ export class MemoryFileSystem {
|
|
|
152
161
|
}
|
|
153
162
|
replaceData(node, allocation, length = allocation.data.byteLength) {
|
|
154
163
|
const previous = node.allocation;
|
|
164
|
+
this.totalBytes += length - node.data.byteLength;
|
|
155
165
|
node.data = allocation.data.subarray(0, length);
|
|
156
166
|
node.allocation = allocation;
|
|
157
167
|
previous.release();
|
|
158
168
|
}
|
|
169
|
+
admitSize(node, length, syscall, path) {
|
|
170
|
+
this.ledger.fileSize(length, syscall, path);
|
|
171
|
+
if (length - (node?.data.byteLength ?? 0) > (this.ledger.limits.maxBytes ?? Number.MAX_SAFE_INTEGER) - this.totalBytes) {
|
|
172
|
+
this.fail("ENOSPC", syscall, path);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
159
175
|
fail(code, syscall, path, dest) {
|
|
160
176
|
throw new FsError(code, { syscall, path, ...(dest === undefined ? {} : { dest }) });
|
|
161
177
|
}
|
|
@@ -262,6 +278,7 @@ export class MemoryFileSystem {
|
|
|
262
278
|
snapshot(node) {
|
|
263
279
|
return {
|
|
264
280
|
type: node.type,
|
|
281
|
+
ioBlockSize: preferredIoBlockSize,
|
|
265
282
|
...(Number.isSafeInteger(node.revision) ? { revision: node.revision } : {}),
|
|
266
283
|
preferredIoBlockSize: 4096,
|
|
267
284
|
size: node.type === "file" ? node.data.byteLength
|
|
@@ -295,6 +312,72 @@ export class MemoryFileSystem {
|
|
|
295
312
|
throw new FsError("EFBIG", { syscall, path, cause });
|
|
296
313
|
}
|
|
297
314
|
}
|
|
315
|
+
open(path, options) {
|
|
316
|
+
return openFileDescriptor(path, options, {
|
|
317
|
+
positionedRead: true, positionedWrite: true, truncate: true, synchronization: "volatile", position: true,
|
|
318
|
+
}, async (admitted) => {
|
|
319
|
+
const location = this.resolve(path, "open", {
|
|
320
|
+
followFinal: admitted.creation !== "exclusive", allowMissing: admitted.creation !== "never",
|
|
321
|
+
});
|
|
322
|
+
let node = location.node;
|
|
323
|
+
if (node) {
|
|
324
|
+
if (admitted.creation === "exclusive")
|
|
325
|
+
this.fail("EEXIST", "open", path);
|
|
326
|
+
if (node.type !== "file")
|
|
327
|
+
this.fail("EISDIR", "open", path);
|
|
328
|
+
this.permission(node, admitted.access === "read" ? 4 : admitted.access === "write" ? 2 : 6, "open", path);
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
this.permission(location.parent, 3, "open", path);
|
|
332
|
+
}
|
|
333
|
+
this.ledger.reserve(path.length * 2, 1, "open", path);
|
|
334
|
+
try {
|
|
335
|
+
node ??= this.openWrite(path, {}, "open", { location, mode: admitted.mode, append: false });
|
|
336
|
+
if (admitted.truncate)
|
|
337
|
+
this.resizeNode(node, 0, "open", path);
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
this.ledger.release(path.length * 2, 1);
|
|
341
|
+
throw error;
|
|
342
|
+
}
|
|
343
|
+
node.references++;
|
|
344
|
+
const resource = { node, position: 0 };
|
|
345
|
+
return {
|
|
346
|
+
resource,
|
|
347
|
+
getPosition: async (retained) => retained.position,
|
|
348
|
+
stat: async (retained) => this.snapshot(retained.node),
|
|
349
|
+
read: async (retained, buffer, position) => {
|
|
350
|
+
const inode = retained.node;
|
|
351
|
+
const start = position ?? retained.position;
|
|
352
|
+
const count = Math.min(buffer.byteLength, Math.max(0, inode.data.byteLength - start));
|
|
353
|
+
buffer.set(inode.data.subarray(start, start + count));
|
|
354
|
+
if (position === null)
|
|
355
|
+
retained.position += count;
|
|
356
|
+
inode.atimeMs = Date.now();
|
|
357
|
+
return count;
|
|
358
|
+
},
|
|
359
|
+
write: async (retained, buffer, position) => {
|
|
360
|
+
const inode = retained.node;
|
|
361
|
+
const start = admitted.append ? inode.data.byteLength : position ?? retained.position;
|
|
362
|
+
const end = start + buffer.byteLength;
|
|
363
|
+
this.writeAt(inode, buffer, start, "write", path);
|
|
364
|
+
if (position === null)
|
|
365
|
+
retained.position = end;
|
|
366
|
+
return buffer.byteLength;
|
|
367
|
+
},
|
|
368
|
+
truncate: async (retained, length) => {
|
|
369
|
+
const inode = retained.node;
|
|
370
|
+
this.resizeNode(inode, length, "ftruncate", path);
|
|
371
|
+
},
|
|
372
|
+
sync: async () => { },
|
|
373
|
+
close: async (retained) => {
|
|
374
|
+
const inode = retained.node;
|
|
375
|
+
retained.node = undefined;
|
|
376
|
+
this.releaseReference(inode, path);
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
});
|
|
380
|
+
}
|
|
298
381
|
prepareWrite(path, options, syscall) {
|
|
299
382
|
const flag = options.flag ?? "w";
|
|
300
383
|
if (!["w", "wx", "a", "ax"].includes(flag))
|
|
@@ -329,7 +412,7 @@ export class MemoryFileSystem {
|
|
|
329
412
|
const target = this.prepareWrite(path, options, syscall);
|
|
330
413
|
const current = target.location.node;
|
|
331
414
|
const length = (target.append ? current?.data.byteLength ?? 0 : 0) + data.byteLength;
|
|
332
|
-
this.
|
|
415
|
+
this.admitSize(current, length, syscall, path);
|
|
333
416
|
const growth = target.append && length > (current?.allocation.data.byteLength ?? 0) ? length : 0;
|
|
334
417
|
this.ledger.check(data.byteLength + growth + (current ? 0 : target.location.name.length * 2), current ? 0 : 2, syscall, path);
|
|
335
418
|
const copied = this.bytes(data, syscall, path);
|
|
@@ -373,6 +456,7 @@ export class MemoryFileSystem {
|
|
|
373
456
|
const end = position + data.byteLength;
|
|
374
457
|
this.ledger.fileSize(end, syscall, path);
|
|
375
458
|
const length = Math.max(node.data.byteLength, end);
|
|
459
|
+
this.admitSize(node, length, syscall, path);
|
|
376
460
|
let allocation = node.allocation;
|
|
377
461
|
if (length > allocation.data.byteLength) {
|
|
378
462
|
this.ledger.check(length, 0, syscall, path);
|
|
@@ -394,8 +478,10 @@ export class MemoryFileSystem {
|
|
|
394
478
|
}
|
|
395
479
|
if (allocation !== node.allocation)
|
|
396
480
|
this.replaceData(node, allocation, length);
|
|
397
|
-
else
|
|
481
|
+
else {
|
|
482
|
+
this.totalBytes += length - node.data.byteLength;
|
|
398
483
|
node.data = allocation.data.subarray(0, length);
|
|
484
|
+
}
|
|
399
485
|
this.changed(node);
|
|
400
486
|
}
|
|
401
487
|
async readFile(path, options = {}) {
|
|
@@ -460,6 +546,8 @@ export class MemoryFileSystem {
|
|
|
460
546
|
this.fail("EINVAL", "createStagedFile", directoryPath);
|
|
461
547
|
if (content.type === "file" && !(content.data instanceof Uint8Array))
|
|
462
548
|
throw new TypeError("Staged files require Uint8Array data");
|
|
549
|
+
if (content.type === "file")
|
|
550
|
+
this.admitSize(undefined, content.data.byteLength, "createStagedFile", directoryPath);
|
|
463
551
|
if (content.type === "symlink")
|
|
464
552
|
this.validatePath(content.target, "createStagedFile");
|
|
465
553
|
const retained = (location.name.length + name.length) * 2 + (content.type === "symlink" ? content.target.length * 2 : 0);
|
|
@@ -480,6 +568,8 @@ export class MemoryFileSystem {
|
|
|
480
568
|
file.mtimeMs = options.mtimeMs;
|
|
481
569
|
directory.entries.set(name, file);
|
|
482
570
|
location.parent.entries.set(location.name, directory);
|
|
571
|
+
if (file.type === "file")
|
|
572
|
+
this.totalBytes += file.data.byteLength;
|
|
483
573
|
}
|
|
484
574
|
catch (error) {
|
|
485
575
|
allocation?.release();
|
|
@@ -566,6 +656,29 @@ export class MemoryFileSystem {
|
|
|
566
656
|
this.writeData(path, data, { ...(options.mode === undefined ? {} : { mode: options.mode }), flag: options.append ? "a" : "w" }, "writeFileConditional");
|
|
567
657
|
return Object.freeze(this.snapshot(location.parent.entries.get(location.name)));
|
|
568
658
|
}
|
|
659
|
+
async removeEntryConditional(path, options) {
|
|
660
|
+
options.signal?.throwIfAborted();
|
|
661
|
+
const location = this.entry(path, "removeEntryConditional", true);
|
|
662
|
+
this.expectEntry(location.parent, options.parent, path, false);
|
|
663
|
+
this.expectEntry(location.node, options.expected, path);
|
|
664
|
+
const node = location.node;
|
|
665
|
+
if (this.terminalDot(path))
|
|
666
|
+
this.fail("EINVAL", "removeEntryConditional", path);
|
|
667
|
+
if (node === this.root)
|
|
668
|
+
this.fail("EBUSY", "removeEntryConditional", path);
|
|
669
|
+
if (node.type !== "file" && node.type !== "symlink" && node.type !== "directory")
|
|
670
|
+
this.fail("EINVAL", "removeEntryConditional", path);
|
|
671
|
+
this.permission(location.parent, 3, "removeEntryConditional", path);
|
|
672
|
+
if (node.type === "directory" && node.entries.size > 0)
|
|
673
|
+
this.fail("ENOTEMPTY", "removeEntryConditional", path);
|
|
674
|
+
location.parent.entries.delete(location.name);
|
|
675
|
+
this.ledger.release(location.name.length * 2, 1);
|
|
676
|
+
node.nlink--;
|
|
677
|
+
node.ctimeMs = Date.now();
|
|
678
|
+
node.revision = Math.min(Number.MAX_SAFE_INTEGER + 1, node.revision + 1);
|
|
679
|
+
this.releaseNode(node);
|
|
680
|
+
this.changed(location.parent);
|
|
681
|
+
}
|
|
569
682
|
async removeFileConditional(path, options) {
|
|
570
683
|
options.signal?.throwIfAborted();
|
|
571
684
|
const location = this.entry(path, "removeFileConditional", true);
|
|
@@ -692,6 +805,10 @@ export class MemoryFileSystem {
|
|
|
692
805
|
}
|
|
693
806
|
this.changed(location.parent);
|
|
694
807
|
}
|
|
808
|
+
unlink(path, options = {}) {
|
|
809
|
+
// Do not allow an untyped recursive option to widen final-entry removal.
|
|
810
|
+
return this.rm(path, { ...(options.signal === undefined ? {} : { signal: options.signal }) });
|
|
811
|
+
}
|
|
695
812
|
async rename(source, destination, options = {}) {
|
|
696
813
|
options.signal?.throwIfAborted();
|
|
697
814
|
try {
|
|
@@ -762,7 +879,6 @@ export class MemoryFileSystem {
|
|
|
762
879
|
this.fail("EINVAL", "copyFile", source, destination);
|
|
763
880
|
const node = this.writeData(destination, origin.data, { mode: origin.mode & 0o7777, flag: options.exclusive ? "wx" : "w" }, "copyFile");
|
|
764
881
|
node.mode = origin.mode;
|
|
765
|
-
this.changed(node);
|
|
766
882
|
origin.atimeMs = Date.now();
|
|
767
883
|
}
|
|
768
884
|
catch (error) {
|
|
@@ -847,6 +963,7 @@ export class MemoryFileSystem {
|
|
|
847
963
|
this.resizeNode(node, length, "truncate", path);
|
|
848
964
|
}
|
|
849
965
|
resizeNode(node, length, syscall, path) {
|
|
966
|
+
this.admitSize(node, length, syscall, path);
|
|
850
967
|
const data = this.allocate(length, syscall, path);
|
|
851
968
|
try {
|
|
852
969
|
data.data.set(node.data.subarray(0, length));
|
|
@@ -987,7 +1104,7 @@ export class MemoryFileSystem {
|
|
|
987
1104
|
async *readStream(path, options = {}) {
|
|
988
1105
|
options.signal?.throwIfAborted();
|
|
989
1106
|
const start = options.start ?? 0;
|
|
990
|
-
const chunkSize = options.chunkSize ??
|
|
1107
|
+
const chunkSize = options.chunkSize ?? preferredIoBlockSize;
|
|
991
1108
|
this.integer(start, "readStream", path);
|
|
992
1109
|
this.integer(chunkSize, "readStream", path);
|
|
993
1110
|
if (chunkSize === 0)
|
|
@@ -1060,7 +1177,7 @@ function stockDescriptorWrite(filesystem) {
|
|
|
1060
1177
|
return false;
|
|
1061
1178
|
for (const name of ["writeStream", "writeFile", "appendFile", "access", "stat", "lstat", "realpath",
|
|
1062
1179
|
"openWrite", "prepareWrite", "writeData", "addNode", "replaceData", "releaseReference", "releaseNode",
|
|
1063
|
-
"resolve", "permission", "validatePath", "mode", "bytes", "allocate", "changed", "metadata", "integer", "writeAt", "fail"]) {
|
|
1180
|
+
"resolve", "permission", "validatePath", "mode", "bytes", "allocate", "admitSize", "changed", "metadata", "integer", "writeAt", "fail"]) {
|
|
1064
1181
|
const descriptor = Object.getOwnPropertyDescriptor(filesystem, name)
|
|
1065
1182
|
?? Object.getOwnPropertyDescriptor(MemoryFileSystem.prototype, name);
|
|
1066
1183
|
if (!descriptor || !("value" in descriptor) || descriptor.value !== memoryImplementation[name]?.value)
|
|
@@ -6,8 +6,14 @@ export const defaultMemoryFileSystemLimits = Object.freeze({
|
|
|
6
6
|
});
|
|
7
7
|
export function normalizeMemoryFileSystemLimits(options) {
|
|
8
8
|
const keys = ["maxFileBytes", "maxRetainedBytes", "maxMetadataUnits"];
|
|
9
|
-
const record = readConfigRecord(options, "memory option", keys);
|
|
9
|
+
const record = readConfigRecord(options, "memory option", [...keys, "maxBytes"]);
|
|
10
10
|
const limits = { ...defaultMemoryFileSystemLimits };
|
|
11
|
+
if (Object.hasOwn(record, "maxBytes") && record.maxBytes !== undefined) {
|
|
12
|
+
if (typeof record.maxBytes !== "number" || !Number.isSafeInteger(record.maxBytes) || record.maxBytes < 0) {
|
|
13
|
+
throw new RangeError("maxBytes must be a nonnegative safe integer");
|
|
14
|
+
}
|
|
15
|
+
limits.maxBytes = record.maxBytes;
|
|
16
|
+
}
|
|
11
17
|
for (const key of keys) {
|
|
12
18
|
const value = Object.hasOwn(record, key) ? record[key] : limits[key];
|
|
13
19
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < (key === "maxMetadataUnits" ? 1 : 0)) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { AppendFileOptions, CapabilityQueryOptions, CopyFileOptions, DirectoryEntry, FileReadHandle, FileResizeHandle, FileResizeOperation, FileResizeOptions, FileStat, FileSystem, OpenReadFileOptions, OpenResizeFileOptions, FileSystemCapabilities, FsOptions, RenameOptions, MkdirOptions, ReadDirectoryOptions, ReadFileOptions, ReadStreamOptions, RemoveOptions, WriteFileOptions, ConditionalWriteFileOptions, ConditionalRemoveFileOptions, CreateStagedFileOptions, FileStaging, PublishStagedFileOptions, PrepareDirectoryOptions, StagedFileContent } from "../../contracts/filesystem.js";
|
|
1
|
+
import type { AppendFileOptions, CapabilityQueryOptions, CopyFileOptions, DirectoryEntry, FileReadHandle, FileResizeHandle, FileResizeOperation, FileResizeOptions, FileStat, FileSystem, OpenReadFileOptions, OpenResizeFileOptions, FileSystemCapabilities, FsOptions, RenameOptions, MkdirOptions, ReadDirectoryOptions, ReadFileOptions, ReadStreamOptions, RemoveOptions, WriteFileOptions, ConditionalWriteFileOptions, ConditionalRemoveFileOptions, ConditionalRemoveEntryOptions, CreateStagedFileOptions, FileStaging, PublishStagedFileOptions, PrepareDirectoryOptions, StagedFileContent } from "../../contracts/filesystem.js";
|
|
2
2
|
import type { ByteSource } from "../../contracts/io.js";
|
|
3
|
+
import type { FileDescriptor, OpenFileOptions } from "../../contracts/descriptor.js";
|
|
3
4
|
export interface MountFileSystemOptions {
|
|
4
5
|
readonly root: FileSystem;
|
|
5
6
|
readonly mounts?: Readonly<Record<string, FileSystem>>;
|
|
@@ -23,6 +24,7 @@ export declare class MountFileSystem implements FileSystem {
|
|
|
23
24
|
private entryPath;
|
|
24
25
|
private optional;
|
|
25
26
|
readFile(path: string, options?: ReadFileOptions): Promise<Uint8Array>;
|
|
27
|
+
open(path: string, options: OpenFileOptions): Promise<FileDescriptor>;
|
|
26
28
|
writeFile(path: string, data: Uint8Array, options?: WriteFileOptions): Promise<void>;
|
|
27
29
|
appendFile(path: string, data: Uint8Array, options?: AppendFileOptions): Promise<void>;
|
|
28
30
|
stat(path: string, options?: FsOptions): Promise<FileStat>;
|
|
@@ -30,9 +32,11 @@ export declare class MountFileSystem implements FileSystem {
|
|
|
30
32
|
readdir(path: string, options?: ReadDirectoryOptions): Promise<DirectoryEntry[]>;
|
|
31
33
|
mkdir(path: string, options?: MkdirOptions): Promise<void>;
|
|
32
34
|
rmdir(path: string, options?: FsOptions): Promise<void>;
|
|
35
|
+
unlink(path: string, options?: FsOptions): Promise<void>;
|
|
33
36
|
rm(path: string, options?: RemoveOptions): Promise<void>;
|
|
34
37
|
private localStaging;
|
|
35
38
|
writeFileConditional(path: string, data: Uint8Array, options: ConditionalWriteFileOptions): Promise<FileStat>;
|
|
39
|
+
removeEntryConditional(path: string, options: ConditionalRemoveEntryOptions): Promise<void>;
|
|
36
40
|
removeFileConditional(path: string, options: ConditionalRemoveFileOptions): Promise<void>;
|
|
37
41
|
createStagedFile(path: string, name: string, content: StagedFileContent, options: CreateStagedFileOptions): Promise<FileStaging>;
|
|
38
42
|
publishStagedFile(staging: FileStaging, destination: string, options: PublishStagedFileOptions): Promise<void>;
|
|
@@ -4,6 +4,7 @@ import { finishCleanup } from "../../contracts/cleanup.js";
|
|
|
4
4
|
import { requireOwnedMutation, ownedMutationCapabilities, openRetainedReadFile, openRetainedResizeFile, readOnlyCapabilities, retainedReadCapabilities, retainedResizeCapabilities } from "../capabilities.js";
|
|
5
5
|
import { admitDirectoryEntries, directoryEntryLimit } from "../directory-admission.js";
|
|
6
6
|
import { normalizePath, validatePath } from "../../contracts/virtual-path.js";
|
|
7
|
+
import { forwardFileDescriptor, openFileDescriptor } from "../descriptor.js";
|
|
7
8
|
import { pathNamespace } from "../path-namespace.js";
|
|
8
9
|
import { compareIdentity } from "./identity.js";
|
|
9
10
|
import { compareEntries, registerEntryAuthority, registerEntryView } from "./comparison.js";
|
|
@@ -21,16 +22,19 @@ function fail(code) {
|
|
|
21
22
|
throw new FsError(code);
|
|
22
23
|
}
|
|
23
24
|
function snapshotStat(stat) {
|
|
24
|
-
const { type, size, allocatedBytes, preferredIoBlockSize, mode, mtimeMs, atimeMs, ctimeMs, birthtimeMs, revision, identityScope, ino, dev, nlink, uid, gid } = stat;
|
|
25
|
+
const { type, size, allocatedBytes, ioBlockSize, preferredIoBlockSize, mode, mtimeMs, atimeMs, ctimeMs, birthtimeMs, revision, identityScope, ino, dev, rdevMajor, rdevMinor, nlink, uid, gid } = stat;
|
|
25
26
|
return {
|
|
26
27
|
type, size, mode, mtimeMs, atimeMs, ctimeMs,
|
|
27
28
|
...(revision === undefined ? {} : { revision }),
|
|
28
29
|
...(allocatedBytes === undefined ? {} : { allocatedBytes }),
|
|
30
|
+
...(ioBlockSize === undefined ? {} : { ioBlockSize }),
|
|
29
31
|
...(preferredIoBlockSize === undefined ? {} : { preferredIoBlockSize }),
|
|
30
32
|
...(birthtimeMs === undefined ? {} : { birthtimeMs }),
|
|
31
33
|
...(identityScope === undefined ? {} : { identityScope }),
|
|
32
34
|
...(ino === undefined ? {} : { ino }),
|
|
33
35
|
...(dev === undefined ? {} : { dev }),
|
|
36
|
+
...(rdevMajor === undefined ? {} : { rdevMajor }),
|
|
37
|
+
...(rdevMinor === undefined ? {} : { rdevMinor }),
|
|
34
38
|
...(nlink === undefined ? {} : { nlink }),
|
|
35
39
|
...(uid === undefined ? {} : { uid }),
|
|
36
40
|
...(gid === undefined ? {} : { gid }),
|
|
@@ -91,13 +95,14 @@ export class MountFileSystem {
|
|
|
91
95
|
: mounts.every(({ backend }) => backend.capabilities.append === false) ? false : undefined;
|
|
92
96
|
const common = (capability) => {
|
|
93
97
|
const optional = {
|
|
94
|
-
|
|
98
|
+
open: ["open"],
|
|
99
|
+
atomicEntryRemoval: ["removeEntryConditional"], atomicFileMutation: ["writeFileConditional", "removeFileConditional"], atomicFileStaging: ["createStagedFile", "publishStagedFile", "removeStagedFile"], atomicDirectoryMetadata: ["prepareDirectory"],
|
|
95
100
|
symlinks: ["symlink", "readlink"], hardlinks: ["link"], permissions: ["chmod"], timestamps: ["utimes"], readlink: ["readlink"],
|
|
96
101
|
descriptorWriteStream: ["writeStream"], retainedResize: ["openResizeFile"], atomicResize: ["resizeFile"],
|
|
97
102
|
};
|
|
98
103
|
const values = mounts.map(({ backend }) => {
|
|
99
104
|
if (backend.capabilities.readOnly === true
|
|
100
|
-
&& !["read", "stat", "readdir", "realpath", "access", "readlink", "explicitDirectories", "implicitDirectories"].includes(capability))
|
|
105
|
+
&& !["open", "read", "stat", "readdir", "realpath", "access", "readlink", "explicitDirectories", "implicitDirectories"].includes(capability))
|
|
101
106
|
return false;
|
|
102
107
|
const declared = backend.capabilities[capability];
|
|
103
108
|
if (capability === "descriptorWriteStream" && backend.capabilities.streamingWrite === false)
|
|
@@ -109,7 +114,7 @@ export class MountFileSystem {
|
|
|
109
114
|
return values.every(value => value === true) ? true : values.every(value => value === false) ? false : undefined;
|
|
110
115
|
};
|
|
111
116
|
const semantics = Object.fromEntries([
|
|
112
|
-
"atomicFileMutation", "atomicFileStaging", "atomicDirectoryMetadata", "read", "stat", "readdir", "realpath", "access",
|
|
117
|
+
"atomicEntryRemoval", "atomicFileMutation", "atomicFileStaging", "atomicDirectoryMetadata", "read", "stat", "readdir", "realpath", "access", "open",
|
|
113
118
|
"write", "append", "exclusiveCreate", "explicitDirectories", "implicitDirectories", "mkdir", "recursiveMkdir",
|
|
114
119
|
"remove", "removeDirectory", "recursiveRemove", "rename", "atomicRenameNoReplace", "copy", "exclusiveCopy", "readlink", "truncate",
|
|
115
120
|
"streamingAppend", "randomAccessWrite", "descriptorWriteStream", "retainedResize", "atomicResize", "symlinks", "hardlinks", "permissions", "timestamps",
|
|
@@ -141,8 +146,9 @@ export class MountFileSystem {
|
|
|
141
146
|
? { ...observed, descriptorWriteStream: false } : observed;
|
|
142
147
|
const resize = declared.atomicResize === true && (typeof location.mount.backend.resizeFile !== "function" || declared.readOnly === true)
|
|
143
148
|
? { ...declared, atomicResize: false } : declared;
|
|
144
|
-
const
|
|
145
|
-
|
|
149
|
+
const withOpen = { ...resize, ...(typeof location.mount.backend.open === "function" ? {} : { open: false }) };
|
|
150
|
+
const capabilities = location.synthetic ? { ...withOpen, open: false, retainedRead: false }
|
|
151
|
+
: retainedResizeCapabilities(location.mount.backend, retainedReadCapabilities(location.mount.backend, withOpen));
|
|
146
152
|
if (location.synthetic)
|
|
147
153
|
return readOnlyCapabilities(capabilities);
|
|
148
154
|
if (this.mounts.length === 1 || capabilities.readOnly === true)
|
|
@@ -413,6 +419,38 @@ export class MountFileSystem {
|
|
|
413
419
|
return location.mount.backend.readFile(location.local, options);
|
|
414
420
|
});
|
|
415
421
|
}
|
|
422
|
+
open(path, options) {
|
|
423
|
+
return openFileDescriptor(globalPath(path), options, {
|
|
424
|
+
positionedRead: true, positionedWrite: true, truncate: true, synchronization: "storage",
|
|
425
|
+
}, async (admitted) => {
|
|
426
|
+
try {
|
|
427
|
+
const location = await this.resolve(path, admitted, {
|
|
428
|
+
allowMissing: admitted.creation !== "never", followFinal: admitted.creation !== "exclusive",
|
|
429
|
+
});
|
|
430
|
+
if (location.synthetic)
|
|
431
|
+
fail("EISDIR");
|
|
432
|
+
if (admitted.access !== "read" || admitted.creation !== "never" || admitted.truncate || admitted.append)
|
|
433
|
+
this.mutable(location);
|
|
434
|
+
const backend = location.mount.backend;
|
|
435
|
+
const capabilities = await backend.capabilitiesFor?.(location.local, admitted) ?? backend.capabilities;
|
|
436
|
+
if (!backend.open || capabilities.open === false)
|
|
437
|
+
fail("ENOTSUP");
|
|
438
|
+
admitted.signal?.throwIfAborted();
|
|
439
|
+
const descriptor = await backend.open(location.local, admitted);
|
|
440
|
+
try {
|
|
441
|
+
return forwardFileDescriptor(descriptor, (syscall, forwarded, action) => this.operation(syscall, path, forwarded, action), descriptor.capabilities, snapshotStat);
|
|
442
|
+
}
|
|
443
|
+
catch (error) {
|
|
444
|
+
await finishCleanup(() => descriptor.close(), true);
|
|
445
|
+
throw error;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
catch (error) {
|
|
449
|
+
admitted.signal?.throwIfAborted();
|
|
450
|
+
throw this.error(error, "open", path, admitted);
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
}
|
|
416
454
|
writeFile(path, data, options = {}) {
|
|
417
455
|
return this.operation("writeFile", path, options, async () => {
|
|
418
456
|
const location = await this.resolve(path, options, { allowMissing: true, followFinal: !options.flag?.endsWith("x") });
|
|
@@ -499,6 +537,18 @@ export class MountFileSystem {
|
|
|
499
537
|
await backend.rmdir(location.local, options);
|
|
500
538
|
});
|
|
501
539
|
}
|
|
540
|
+
unlink(path, options = {}) {
|
|
541
|
+
return this.operation("unlink", path, options, async () => {
|
|
542
|
+
const location = await this.resolve(path, options, { followFinal: false, entry: true });
|
|
543
|
+
this.mutable(location);
|
|
544
|
+
this.entryPath(path);
|
|
545
|
+
const backend = location.mount.backend;
|
|
546
|
+
if (!backend.unlink)
|
|
547
|
+
fail("ENOTSUP");
|
|
548
|
+
options.signal?.throwIfAborted();
|
|
549
|
+
await backend.unlink(location.local, options);
|
|
550
|
+
});
|
|
551
|
+
}
|
|
502
552
|
rm(path, options = {}) {
|
|
503
553
|
return this.operation("rm", path, options, async () => {
|
|
504
554
|
let location;
|
|
@@ -544,6 +594,19 @@ export class MountFileSystem {
|
|
|
544
594
|
return snapshotStat(await backend.writeFileConditional(location.local, data, options));
|
|
545
595
|
}, undefined, true);
|
|
546
596
|
}
|
|
597
|
+
removeEntryConditional(path, options) {
|
|
598
|
+
return this.operation("removeEntryConditional", path, options, async () => {
|
|
599
|
+
const location = await this.resolve(path, options, { followFinal: false, entry: true, allowMissing: true });
|
|
600
|
+
if (this.protected(location.path))
|
|
601
|
+
fail("EBUSY");
|
|
602
|
+
this.mutable(location);
|
|
603
|
+
const backend = location.mount.backend;
|
|
604
|
+
await requireOwnedMutation(backend, location.local, "atomicEntryRemoval", options);
|
|
605
|
+
if (!backend.removeEntryConditional)
|
|
606
|
+
fail("ENOTSUP");
|
|
607
|
+
await backend.removeEntryConditional(location.local, options);
|
|
608
|
+
});
|
|
609
|
+
}
|
|
547
610
|
removeFileConditional(path, options) {
|
|
548
611
|
return this.operation("removeFileConditional", path, options, async () => {
|
|
549
612
|
const location = await this.resolve(path, options, { followFinal: false, entry: true, allowMissing: true });
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ByteSource } from "../../contracts/io.js";
|
|
2
|
+
import type { FileDescriptor, OpenFileOptions } from "../../contracts/descriptor.js";
|
|
2
3
|
import type { AppendFileOptions, CapabilityQueryOptions, CopyFileOptions, DirectoryEntry, OpenReadFileOptions, FileStat, FileSystem, FileSystemCapabilities, FsOptions, RenameOptions, MkdirOptions, ReadDirectoryOptions, ReadFileOptions, ReadStreamOptions, RemoveOptions, WriteFileOptions } from "../../contracts/filesystem.js";
|
|
3
4
|
export interface OverlayFileSystemOptions {
|
|
4
5
|
readonly upper: FileSystem;
|
|
@@ -17,6 +18,7 @@ export declare class OverlayFileSystem implements FileSystem {
|
|
|
17
18
|
private readonly linkOrigins;
|
|
18
19
|
private queue;
|
|
19
20
|
constructor(options: OverlayFileSystemOptions);
|
|
21
|
+
open(path: string, options: OpenFileOptions): Promise<FileDescriptor>;
|
|
20
22
|
private run;
|
|
21
23
|
capabilitiesFor(path: string, options?: CapabilityQueryOptions): Promise<FileSystemCapabilities>;
|
|
22
24
|
openReadFile(path: string, options?: OpenReadFileOptions): Promise<import("../../contracts/filesystem.js").FileReadHandle>;
|
|
@@ -8,16 +8,19 @@ import { compareIdentity } from "../mount/identity.js";
|
|
|
8
8
|
import { compareEntries, registerEntryView } from "../mount/comparison.js";
|
|
9
9
|
import { admitDirectoryEntries, directoryEntryLimit } from "../directory-admission.js";
|
|
10
10
|
function snapshotStat(stat) {
|
|
11
|
-
const { type, size, allocatedBytes, preferredIoBlockSize, mode, mtimeMs, atimeMs, ctimeMs, birthtimeMs, revision, identityScope, ino, dev, nlink, uid, gid } = stat;
|
|
11
|
+
const { type, size, allocatedBytes, ioBlockSize, preferredIoBlockSize, mode, mtimeMs, atimeMs, ctimeMs, birthtimeMs, revision, identityScope, ino, dev, rdevMajor, rdevMinor, nlink, uid, gid } = stat;
|
|
12
12
|
return {
|
|
13
13
|
type, size, mode, mtimeMs, atimeMs, ctimeMs,
|
|
14
14
|
...(allocatedBytes === undefined ? {} : { allocatedBytes }),
|
|
15
|
+
...(ioBlockSize === undefined ? {} : { ioBlockSize }),
|
|
15
16
|
...(preferredIoBlockSize === undefined ? {} : { preferredIoBlockSize }),
|
|
16
17
|
...(birthtimeMs === undefined ? {} : { birthtimeMs }),
|
|
17
18
|
...(revision === undefined ? {} : { revision }),
|
|
18
19
|
...(identityScope === undefined ? {} : { identityScope }),
|
|
19
20
|
...(ino === undefined ? {} : { ino }),
|
|
20
21
|
...(dev === undefined ? {} : { dev }),
|
|
22
|
+
...(rdevMajor === undefined ? {} : { rdevMajor }),
|
|
23
|
+
...(rdevMinor === undefined ? {} : { rdevMinor }),
|
|
21
24
|
...(nlink === undefined ? {} : { nlink }),
|
|
22
25
|
...(uid === undefined ? {} : { uid }),
|
|
23
26
|
...(gid === undefined ? {} : { gid }),
|
|
@@ -114,6 +117,7 @@ export class OverlayFileSystem {
|
|
|
114
117
|
].filter(([, value]) => value !== undefined));
|
|
115
118
|
this.capabilities = Object.freeze({
|
|
116
119
|
...semantics,
|
|
120
|
+
open: false,
|
|
117
121
|
atomicFileMutation: false, atomicFileStaging: false, atomicDirectoryMetadata: false,
|
|
118
122
|
implicitDirectories: false,
|
|
119
123
|
readlink: upper.readlink === true && this.#lower.capabilities.readlink === true ? true
|
|
@@ -135,6 +139,10 @@ export class OverlayFileSystem {
|
|
|
135
139
|
});
|
|
136
140
|
Object.defineProperty(this, "capabilities", { writable: false, configurable: false });
|
|
137
141
|
}
|
|
142
|
+
async open(path, options) {
|
|
143
|
+
options.signal?.throwIfAborted();
|
|
144
|
+
throw new FsError("ENOTSUP", { syscall: "open", path });
|
|
145
|
+
}
|
|
138
146
|
async run(options, operation, cleanup = true) {
|
|
139
147
|
options.signal?.throwIfAborted();
|
|
140
148
|
const previous = this.queue;
|
|
@@ -183,6 +183,10 @@ export function withFileSystemQuota(fs, options) {
|
|
|
183
183
|
throw new FileSystemQuotaError(options.maxBytes);
|
|
184
184
|
};
|
|
185
185
|
const mutations = {
|
|
186
|
+
async open(path, openOptions) {
|
|
187
|
+
openOptions.signal?.throwIfAborted();
|
|
188
|
+
throw new FsError("ENOTSUP", { syscall: "open", path });
|
|
189
|
+
},
|
|
186
190
|
openResizeFile(path, resizeOptions = {}) {
|
|
187
191
|
return mutate(async () => {
|
|
188
192
|
let retained;
|
|
@@ -316,7 +320,7 @@ export function withFileSystemQuota(fs, options) {
|
|
|
316
320
|
// capabilities and methods without violating invariants on own properties.
|
|
317
321
|
return new Proxy(Object.create(fs), {
|
|
318
322
|
get(_target, property) {
|
|
319
|
-
if (property === "writeFileConditional" || property === "removeFileConditional" || property === "resizeFile" || property === "canonicalizeMissingTarget" || property === "createStagedFile" || property === "publishStagedFile" || property === "removeStagedFile" || property === "prepareDirectory")
|
|
323
|
+
if (property === "removeEntryConditional" || property === "unlink" || property === "writeFileConditional" || property === "removeFileConditional" || property === "resizeFile" || property === "canonicalizeMissingTarget" || property === "createStagedFile" || property === "publishStagedFile" || property === "removeStagedFile" || property === "prepareDirectory")
|
|
320
324
|
return undefined;
|
|
321
325
|
if (property === "capabilities")
|
|
322
326
|
return quotaCapabilities(retainedResizeCapabilities(fs, retainedReadCapabilities(fs)));
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { ByteSource } from "../../contracts/io.js";
|
|
2
2
|
import type { AppendFileOptions, CapabilityQueryOptions, CopyFileOptions, DirectoryEntry, FileResizeHandle, FileStat, OpenReadFileOptions, OpenResizeFileOptions, FileSystem, FileSystemCapabilities, FsOptions, RenameOptions, MkdirOptions, ReadDirectoryOptions, ReadFileOptions, ReadStreamOptions, RemoveOptions, WriteFileOptions } from "../../contracts/filesystem.js";
|
|
3
|
+
import type { FileDescriptor, OpenFileOptions } from "../../contracts/descriptor.js";
|
|
3
4
|
export declare class ReadOnlyFileSystem implements FileSystem {
|
|
4
5
|
#private;
|
|
5
6
|
constructor(filesystem: FileSystem);
|
|
6
7
|
get capabilities(): FileSystemCapabilities;
|
|
7
8
|
capabilitiesFor(path: string, options?: CapabilityQueryOptions): Promise<FileSystemCapabilities>;
|
|
9
|
+
open(path: string, options: OpenFileOptions): Promise<FileDescriptor>;
|
|
8
10
|
openReadFile(path: string, options?: OpenReadFileOptions): Promise<import("../../contracts/filesystem.js").FileReadHandle>;
|
|
9
11
|
readFile(path: string, options?: ReadFileOptions): Promise<Uint8Array>;
|
|
10
12
|
stat(path: string, options?: FsOptions): Promise<FileStat>;
|