Skip to content
File

Blob: src/pyodide/internal/metadatafs.ts

typescript90 lines
1// Copyright (c) 2026 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5import { default as MetadataReader } from 'pyodide-internal:runtime-generated/metadata';
6import { createReadonlyFS } from 'pyodide-internal:readOnlyFS';
7import { PythonWorkersInternalError } from 'pyodide-internal:util';
8 
9function createTree(paths: string[]): MetadataDirInfo {
10 const tree: MetadataFSInfo = new Map();
11 paths.forEach((elt: string, idx: number) => {
12 let subTree: MetadataFSInfo = tree;
13 const parts = elt.split('/');
14 const name = parts.pop()!;
15 for (const part of parts) {
16 if (typeof subTree === 'number') {
17 throw new PythonWorkersInternalError(
18 'expected subtree to not be a number'
19 );
20 }
21 let next: MetadataFSInfo | undefined = subTree.get(part);
22 if (!next) {
23 next = new Map();
24 subTree.set(part, next);
25 }
26 subTree = next;
27 }
28 if (typeof subTree === 'number') {
29 throw new PythonWorkersInternalError(
30 'expected subtree to not be a number'
31 );
32 }
33 subTree.set(name, idx);
34 });
35 return tree;
36}
37 
38export function createMetadataFS(Module: Module): object {
39 // TODO: Make this type more specific
40 const TIMESTAMP = Date.now();
41 const names = MetadataReader.getNames();
42 const sizes = MetadataReader.getSizes();
43 const rootInfo = createTree(names);
44 const FSOps: FSOps<MetadataFSInfo> = {
45 getNodeMode(_parent, _name, info) {
46 return {
47 permissions: 0o555, // read and execute but not write
48 isDir: typeof info !== 'number',
49 };
50 },
51 setNodeAttributes(node, info, isDir) {
52 node.modtime = TIMESTAMP;
53 node.usedBytes = 0;
54 if (info === undefined) {
55 info = rootInfo;
56 }
57 if (isDir) {
58 node.tree = info as MetadataDirInfo;
59 } else {
60 node.index = info as number;
61 node.usedBytes = sizes[info as number]!;
62 }
63 },
64 readdir(node) {
65 if (node.tree == undefined) {
66 throw new PythonWorkersInternalError(
67 'cannot read directory, tree is undefined'
68 );
69 }
70 return Array.from(node.tree.keys());
71 },
72 lookup(parent, name) {
73 // Parent is not a directory so we always raise ENOENT (44)
74 if (parent.tree == undefined) {
75 throw new Module.FS.ErrnoError(44);
76 }
77 const res = parent.tree.get(name);
78 if (res === undefined) {
79 throw new Module.FS.ErrnoError(44);
80 }
81 return res;
82 },
83 read(stream, position, buffer) {
84 return MetadataReader.read(stream.node.index!, position, buffer);
85 },
86 };
87 
88 return createReadonlyFS(FSOps, Module);
89}