@ai-ecoverse/slicc-kernel 1.65.0 → 1.65.2
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 +1 -1
- package/dist/kernel-worker.js +141 -62
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node.js +124 -43
- package/dist/node.js.map +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -392,7 +392,7 @@ The proxy answers `200` with `Content-Type: application/vnd.slicc.raw-fetch`: a
|
|
|
392
392
|
|
|
393
393
|
## Filesystem
|
|
394
394
|
|
|
395
|
-
`/` is the OPFS root. Each process mounts the top-level directories that exist when it starts (plus `/usr` and `/bin`), so files in them are shared by all processes and visible through the OPFS API as soon as the process that wrote them has closed them; `run` resolves only after that. `createKernel` creates `/tmp` and `/home` in OPFS, so they are shared too. Files directly in `/`, and directories created there after a process started, are OPFS as well, the same for Emscripten and WASI programs; only `/dev` and `/proc` live in each process's memory. A program's `mkdir` (WASI `path_create_directory`, Emscripten's `mkdir`) is exclusive, as on Linux: it fails with `EEXIST` when the name exists, and of several processes or threads making one name at once exactly one succeeds, so a directory works as a lock. Under a missing directory it fails with `ENOENT`, as on Linux (`mkdir -p` and `os.makedirs` make the parents in the program); a JS program's `ctx.fs.mkdir` makes parents itself. The kernel makes the system `PNPM_HOME` (`/usr/local/share/pnpm`, or the embedder's) when it starts, since pnpm makes only its `bin` directory there. An OPFS or IndexedDB failure that has no POSIX counterpart is `EIO`; its message names the DOM error and the operation (`lookup`, `read`, `write`, `commit` or `sidecar`), and the kernel worker logs the first of each kind to its console. File contents are buffered per open file and written back on close, `fsync` and exit; metadata operations (`mkdir`, `rename`, `rm`, …) go straight to OPFS. The kernel worker is the only writer.
|
|
395
|
+
`/` is the OPFS root. Each process mounts the top-level directories that exist when it starts (plus `/usr` and `/bin`), so files in them are shared by all processes and visible through the OPFS API as soon as the process that wrote them has closed them; `run` resolves only after that. `createKernel` creates `/tmp` and `/home` in OPFS, so they are shared too. Files directly in `/`, and directories created there after a process started, are OPFS as well, the same for Emscripten and WASI programs; only `/dev` and `/proc` live in each process's memory. A program's `mkdir` (WASI `path_create_directory`, Emscripten's `mkdir`) is exclusive, as on Linux: it fails with `EEXIST` when the name exists, and of several processes or threads making one name at once exactly one succeeds, so a directory works as a lock. Under a missing directory it fails with `ENOENT`, as on Linux (`mkdir -p` and `os.makedirs` make the parents in the program); a JS program's `ctx.fs.mkdir` makes parents itself. The kernel makes the system `PNPM_HOME` (`/usr/local/share/pnpm`, or the embedder's) when it starts, since pnpm makes only its `bin` directory there. A full origin storage (`QuotaExceededError`, which also comes early in a browser's private or off-the-record contexts, whatever `navigator.storage.estimate()` says) is `ENOSPC`, never retried. An OPFS or IndexedDB failure that has no POSIX counterpart is `EIO`; its message names the DOM error and the operation (`lookup`, `read`, `write`, `commit` or `sidecar`), and the kernel worker logs the first of each kind to its console. File contents are buffered per open file and written back on close, `fsync` and exit; metadata operations (`mkdir`, `rename`, `rm`, …) go straight to OPFS. The kernel worker is the only writer.
|
|
396
396
|
|
|
397
397
|
Directories are renamed with `FileSystemHandle.move()` where available, else by copy and delete.
|
|
398
398
|
|
package/dist/kernel-worker.js
CHANGED
|
@@ -1,18 +1,99 @@
|
|
|
1
|
+
// src/fs/path-map.ts
|
|
2
|
+
function parentOf(path) {
|
|
3
|
+
if (path === "/" || path === "") return void 0;
|
|
4
|
+
const at = path.lastIndexOf("/");
|
|
5
|
+
return at <= 0 ? "/" : path.slice(0, at);
|
|
6
|
+
}
|
|
7
|
+
var PathMap = class {
|
|
8
|
+
values = /* @__PURE__ */ new Map();
|
|
9
|
+
children = /* @__PURE__ */ new Map();
|
|
10
|
+
get size() {
|
|
11
|
+
return this.values.size;
|
|
12
|
+
}
|
|
13
|
+
has(path) {
|
|
14
|
+
return this.values.has(path);
|
|
15
|
+
}
|
|
16
|
+
get(path) {
|
|
17
|
+
return this.values.get(path);
|
|
18
|
+
}
|
|
19
|
+
keys() {
|
|
20
|
+
return this.values.keys();
|
|
21
|
+
}
|
|
22
|
+
set(path, value) {
|
|
23
|
+
if (!this.values.has(path)) this.link(path);
|
|
24
|
+
this.values.set(path, value);
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
delete(path) {
|
|
28
|
+
if (!this.values.delete(path)) return false;
|
|
29
|
+
this.prune(path);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
subtree(root) {
|
|
33
|
+
const out = [];
|
|
34
|
+
const stack = [root];
|
|
35
|
+
for (let path = stack.pop(); path !== void 0; path = stack.pop()) {
|
|
36
|
+
if (this.values.has(path)) out.push(path);
|
|
37
|
+
const below = this.children.get(path);
|
|
38
|
+
if (below) for (const child of below) stack.push(child);
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
entries() {
|
|
43
|
+
return this.values.entries();
|
|
44
|
+
}
|
|
45
|
+
deleteTree(root) {
|
|
46
|
+
const stack = [root];
|
|
47
|
+
for (let path = stack.pop(); path !== void 0; path = stack.pop()) {
|
|
48
|
+
this.values.delete(path);
|
|
49
|
+
const below = this.children.get(path);
|
|
50
|
+
if (below) for (const child of below) stack.push(child);
|
|
51
|
+
this.children.delete(path);
|
|
52
|
+
}
|
|
53
|
+
this.prune(root);
|
|
54
|
+
}
|
|
55
|
+
clear() {
|
|
56
|
+
this.values.clear();
|
|
57
|
+
this.children.clear();
|
|
58
|
+
}
|
|
59
|
+
link(path) {
|
|
60
|
+
for (let child = path, parent = parentOf(path); parent !== void 0; ) {
|
|
61
|
+
const set2 = this.children.get(parent);
|
|
62
|
+
if (set2?.has(child)) return;
|
|
63
|
+
if (set2) set2.add(child);
|
|
64
|
+
else this.children.set(parent, /* @__PURE__ */ new Set([child]));
|
|
65
|
+
child = parent;
|
|
66
|
+
parent = parentOf(parent);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
prune(path) {
|
|
70
|
+
for (let child = path, parent = parentOf(path); parent !== void 0; ) {
|
|
71
|
+
if (this.values.has(child) || this.children.has(child)) return;
|
|
72
|
+
const set2 = this.children.get(parent);
|
|
73
|
+
set2?.delete(child);
|
|
74
|
+
if (set2 && set2.size > 0) return;
|
|
75
|
+
this.children.delete(parent);
|
|
76
|
+
child = parent;
|
|
77
|
+
parent = parentOf(parent);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
1
82
|
// src/fs/meta.ts
|
|
2
83
|
var META_DB = "slicc-kernel";
|
|
3
84
|
var META_VERSION = 1;
|
|
4
85
|
var META_STORE = "entries";
|
|
5
|
-
function
|
|
86
|
+
function parentOf2(path) {
|
|
6
87
|
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
7
88
|
}
|
|
8
89
|
function moved(entry, path) {
|
|
9
|
-
return entry.link === void 0 ? { ...entry, path } : { ...entry, path, dir:
|
|
90
|
+
return entry.link === void 0 ? { ...entry, path } : { ...entry, path, dir: parentOf2(path) };
|
|
10
91
|
}
|
|
11
92
|
function under(path, root) {
|
|
12
93
|
return path === root || path.startsWith(`${root}/`);
|
|
13
94
|
}
|
|
14
95
|
var MemoryMeta = class {
|
|
15
|
-
entries =
|
|
96
|
+
entries = new PathMap();
|
|
16
97
|
async get(paths) {
|
|
17
98
|
return paths.map((path) => this.entries.get(path));
|
|
18
99
|
}
|
|
@@ -25,7 +106,7 @@ var MemoryMeta = class {
|
|
|
25
106
|
}
|
|
26
107
|
async move(from, to, root) {
|
|
27
108
|
await this.remove([to]);
|
|
28
|
-
const carried =
|
|
109
|
+
const carried = this.entries.subtree(from).map((path) => this.entries.get(path));
|
|
29
110
|
for (const entry of carried) this.entries.delete(entry.path);
|
|
30
111
|
for (const entry of carried) {
|
|
31
112
|
const path = to + entry.path.slice(from.length);
|
|
@@ -34,18 +115,16 @@ var MemoryMeta = class {
|
|
|
34
115
|
this.entries.set(to, moved(root(carried.find((entry) => entry.path === from)), to));
|
|
35
116
|
}
|
|
36
117
|
async remove(paths) {
|
|
37
|
-
for (const
|
|
38
|
-
if (paths.some((path) => under(key, path))) this.entries.delete(key);
|
|
39
|
-
}
|
|
118
|
+
for (const path of paths) this.entries.deleteTree(path);
|
|
40
119
|
}
|
|
41
120
|
async links(dir) {
|
|
42
|
-
return [...this.entries.
|
|
121
|
+
return [...this.entries.entries()].map(([, e]) => e).filter((entry) => entry.dir === dir);
|
|
43
122
|
}
|
|
44
123
|
async under(path) {
|
|
45
|
-
return [...this.entries.
|
|
124
|
+
return [...this.entries.entries()].map(([, e]) => e).filter((entry) => under(entry.path, path));
|
|
46
125
|
}
|
|
47
126
|
async all() {
|
|
48
|
-
return [...this.entries.
|
|
127
|
+
return [...this.entries.entries()].map(([, e]) => e);
|
|
49
128
|
}
|
|
50
129
|
};
|
|
51
130
|
function descendants(path) {
|
|
@@ -59,7 +138,7 @@ function settle(transaction, value) {
|
|
|
59
138
|
}
|
|
60
139
|
var IndexedDbMeta = class {
|
|
61
140
|
db;
|
|
62
|
-
cache =
|
|
141
|
+
cache = new PathMap();
|
|
63
142
|
channel;
|
|
64
143
|
generation = 0;
|
|
65
144
|
constructor(db) {
|
|
@@ -72,9 +151,7 @@ var IndexedDbMeta = class {
|
|
|
72
151
|
drop({ paths = [], trees = [] }) {
|
|
73
152
|
this.generation++;
|
|
74
153
|
for (const path of paths) this.cache.delete(path);
|
|
75
|
-
for (const
|
|
76
|
-
if (trees.some((tree2) => under(key, tree2))) this.cache.delete(key);
|
|
77
|
-
}
|
|
154
|
+
for (const tree2 of trees) this.cache.deleteTree(tree2);
|
|
78
155
|
}
|
|
79
156
|
changed(invalidation2) {
|
|
80
157
|
this.drop(invalidation2);
|
|
@@ -299,15 +376,15 @@ function translate(err, path, op = "lookup", mapped = true) {
|
|
|
299
376
|
if (typeof err?.code === "string") return err;
|
|
300
377
|
const raw = err?.name;
|
|
301
378
|
const name2 = typeof raw === "string" ? raw : "Error";
|
|
302
|
-
const code = mapped && ERRNO_BY_DOM_ERROR[name2] || "EIO";
|
|
379
|
+
const code = name2 === "QuotaExceededError" ? "ENOSPC" : mapped && ERRNO_BY_DOM_ERROR[name2] || "EIO";
|
|
303
380
|
const error = Object.assign(fsError(code, path), { domName: name2, op, cause: err });
|
|
304
|
-
if (code !== "EIO") return error;
|
|
381
|
+
if (code !== "EIO" && code !== "ENOSPC") return error;
|
|
305
382
|
const detail = err?.message;
|
|
306
383
|
error.message += ` (OPFS ${op}: ${name2}${typeof detail === "string" && detail ? `: ${detail}` : ""})`;
|
|
307
384
|
if (!reported.has(`${name2}/${op}`)) {
|
|
308
385
|
reported.add(`${name2}/${op}`);
|
|
309
386
|
console.warn(
|
|
310
|
-
`slicc-kernel: ${error.message}, reported as
|
|
387
|
+
`slicc-kernel: ${error.message}, reported as ${code}; logged once per error and operation`
|
|
311
388
|
);
|
|
312
389
|
}
|
|
313
390
|
return error;
|
|
@@ -327,7 +404,7 @@ function missing(err) {
|
|
|
327
404
|
function split(path) {
|
|
328
405
|
return path.split("/").filter(Boolean);
|
|
329
406
|
}
|
|
330
|
-
function
|
|
407
|
+
function parentOf3(path) {
|
|
331
408
|
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
332
409
|
}
|
|
333
410
|
function baseOf(path) {
|
|
@@ -400,7 +477,7 @@ function isHandle(child) {
|
|
|
400
477
|
var OpfsFs = class {
|
|
401
478
|
root;
|
|
402
479
|
meta;
|
|
403
|
-
dirs =
|
|
480
|
+
dirs = new PathMap();
|
|
404
481
|
checked = /* @__PURE__ */ new WeakMap();
|
|
405
482
|
channel;
|
|
406
483
|
ranged;
|
|
@@ -449,7 +526,7 @@ var OpfsFs = class {
|
|
|
449
526
|
async ownerAt(path, entry) {
|
|
450
527
|
const own = ownOf(entry);
|
|
451
528
|
if (own) return own;
|
|
452
|
-
return path === "/" ? { ...ROOT_OWNER } : this.handedTo(
|
|
529
|
+
return path === "/" ? { ...ROOT_OWNER } : this.handedTo(parentOf3(path));
|
|
453
530
|
}
|
|
454
531
|
async owned(stat, path, entry) {
|
|
455
532
|
return this.owners ? { ...stat, ...await this.ownerAt(path, entry) } : stat;
|
|
@@ -479,7 +556,7 @@ var OpfsFs = class {
|
|
|
479
556
|
async chown(path, change) {
|
|
480
557
|
if (!this.owners) return;
|
|
481
558
|
if (change.kind === "new") {
|
|
482
|
-
const at = resolvePath((await this.locate(
|
|
559
|
+
const at = resolvePath((await this.locate(parentOf3(path), true)).path, baseOf(path));
|
|
483
560
|
const { uid = 0, gid = 0, mode } = change;
|
|
484
561
|
return this.stampNew(at, { uid, gid, ...mode !== void 0 ? { mode } : {} }, false);
|
|
485
562
|
}
|
|
@@ -543,7 +620,7 @@ var OpfsFs = class {
|
|
|
543
620
|
return true;
|
|
544
621
|
}
|
|
545
622
|
forget(path) {
|
|
546
|
-
|
|
623
|
+
this.dirs.deleteTree(path);
|
|
547
624
|
}
|
|
548
625
|
async inDir(path, op) {
|
|
549
626
|
const dir = await this.dir(path);
|
|
@@ -590,7 +667,7 @@ var OpfsFs = class {
|
|
|
590
667
|
async linkAlong(path) {
|
|
591
668
|
for (const entry of await this.meta.get(prefixes(path))) {
|
|
592
669
|
if (entry?.link === void 0) continue;
|
|
593
|
-
if (!await this.real(entry.path) && await this.real(
|
|
670
|
+
if (!await this.real(entry.path) && await this.real(parentOf3(entry.path))) return entry;
|
|
594
671
|
}
|
|
595
672
|
return void 0;
|
|
596
673
|
}
|
|
@@ -605,7 +682,7 @@ var OpfsFs = class {
|
|
|
605
682
|
if (!link2) throw err;
|
|
606
683
|
if (link2.path === current && !follow2) return { path: current, link: link2 };
|
|
607
684
|
const target = `${link2.link}${current.slice(link2.path.length)}`;
|
|
608
|
-
current = target.split("/").includes("..") ? await this.physical(
|
|
685
|
+
current = target.split("/").includes("..") ? await this.physical(parentOf3(link2.path), target, hops + 1) : resolvePath(parentOf3(link2.path), target);
|
|
609
686
|
}
|
|
610
687
|
}
|
|
611
688
|
throw fsError("ELOOP", path);
|
|
@@ -795,8 +872,8 @@ var OpfsFs = class {
|
|
|
795
872
|
if (!missing(err)) throw err;
|
|
796
873
|
}
|
|
797
874
|
const [entry] = await this.meta.get([path]);
|
|
798
|
-
const wanted = entry?.link === void 0 ? path : resolvePath(
|
|
799
|
-
return [resolvePath((await this.locate(
|
|
875
|
+
const wanted = entry?.link === void 0 ? path : resolvePath(parentOf3(path), entry.link);
|
|
876
|
+
return [resolvePath((await this.locate(parentOf3(wanted), true)).path, baseOf(wanted)), false];
|
|
800
877
|
}
|
|
801
878
|
async fileFor(path) {
|
|
802
879
|
const [target, existed] = await this.writeTarget(path);
|
|
@@ -806,7 +883,7 @@ var OpfsFs = class {
|
|
|
806
883
|
dir,
|
|
807
884
|
(parent) => parent.getFileHandle(name2, { create: true })
|
|
808
885
|
);
|
|
809
|
-
if (!existed) this.touch(
|
|
886
|
+
if (!existed) this.touch(parentOf3(target));
|
|
810
887
|
return [handle, existed, target];
|
|
811
888
|
} catch (err) {
|
|
812
889
|
const code = err?.name === "TypeMismatchError" ? "EISDIR" : null;
|
|
@@ -817,11 +894,13 @@ var OpfsFs = class {
|
|
|
817
894
|
const [file, existed, target] = await this.fileFor(path);
|
|
818
895
|
if (!existed) await this.stampNew(target, owner, false);
|
|
819
896
|
if (!existed && content.length === 0) return;
|
|
897
|
+
let writable2;
|
|
820
898
|
try {
|
|
821
|
-
|
|
899
|
+
writable2 = await file.createWritable();
|
|
822
900
|
await writable2.write(content);
|
|
823
901
|
await writable2.close();
|
|
824
902
|
} catch (err) {
|
|
903
|
+
await writable2?.abort().catch(() => void 0);
|
|
825
904
|
throw translate(err, path, "commit", false);
|
|
826
905
|
}
|
|
827
906
|
}
|
|
@@ -876,9 +955,9 @@ var OpfsFs = class {
|
|
|
876
955
|
gid: owner.gid,
|
|
877
956
|
...mode ? { mode, parents: mode } : {}
|
|
878
957
|
};
|
|
879
|
-
await this.mkdir(
|
|
958
|
+
await this.mkdir(parentOf3(path), { recursive: true, ...above ? { owner: above } : {} });
|
|
880
959
|
}
|
|
881
|
-
const parent = await this.locate(
|
|
960
|
+
const parent = await this.locate(parentOf3(path), true);
|
|
882
961
|
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
883
962
|
await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
|
|
884
963
|
const made2 = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
|
|
@@ -907,7 +986,7 @@ var OpfsFs = class {
|
|
|
907
986
|
this.changed(found.path);
|
|
908
987
|
}
|
|
909
988
|
await this.meta.remove([found.path]);
|
|
910
|
-
this.touch(
|
|
989
|
+
this.touch(parentOf3(found.path));
|
|
911
990
|
}
|
|
912
991
|
links() {
|
|
913
992
|
return false;
|
|
@@ -919,7 +998,7 @@ var OpfsFs = class {
|
|
|
919
998
|
if (from === to) return;
|
|
920
999
|
const source = await this.locate(from, false);
|
|
921
1000
|
this.forget(source.path);
|
|
922
|
-
const parent = await this.locate(
|
|
1001
|
+
const parent = await this.locate(parentOf3(to), true);
|
|
923
1002
|
const destination = resolvePath(parent.path, baseOf(to));
|
|
924
1003
|
const sourceStat = await this.describe(source);
|
|
925
1004
|
if (sourceStat.isDirectory && within(destination, source.path)) throw fsError("EINVAL", to);
|
|
@@ -947,7 +1026,7 @@ var OpfsFs = class {
|
|
|
947
1026
|
if (source.link) {
|
|
948
1027
|
if (target?.handle) await this.rm(destination);
|
|
949
1028
|
await this.meta.move(source.path, destination, carry);
|
|
950
|
-
this.touch(
|
|
1029
|
+
this.touch(parentOf3(source.path), parent.path);
|
|
951
1030
|
return;
|
|
952
1031
|
}
|
|
953
1032
|
const dir = parent.handle;
|
|
@@ -969,7 +1048,7 @@ var OpfsFs = class {
|
|
|
969
1048
|
}
|
|
970
1049
|
if (copied) await this.restamp(destination, timed2);
|
|
971
1050
|
this.changed(source.path, destination);
|
|
972
|
-
this.touch(
|
|
1051
|
+
this.touch(parentOf3(source.path), parent.path);
|
|
973
1052
|
}
|
|
974
1053
|
async modified(path) {
|
|
975
1054
|
const handle = await this.handle(path);
|
|
@@ -1015,7 +1094,7 @@ var OpfsFs = class {
|
|
|
1015
1094
|
for await (const child of handle.values()) await this.copy(child, target, child.name);
|
|
1016
1095
|
}
|
|
1017
1096
|
async symlink(target, path, owner) {
|
|
1018
|
-
const parent = await this.locate(
|
|
1097
|
+
const parent = await this.locate(parentOf3(path), true);
|
|
1019
1098
|
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
1020
1099
|
const at = resolvePath(parent.path, baseOf(path));
|
|
1021
1100
|
if (await this.locate(at, false).then(
|
|
@@ -1091,7 +1170,7 @@ var OpfsFs = class {
|
|
|
1091
1170
|
let removed = 0;
|
|
1092
1171
|
for (const entry of await this.meta.all()) {
|
|
1093
1172
|
const real = await this.real(entry.path);
|
|
1094
|
-
const stale = entry.link === void 0 ? !real : real || !await this.real(
|
|
1173
|
+
const stale = entry.link === void 0 ? !real : real || !await this.real(parentOf3(entry.path));
|
|
1095
1174
|
if (!stale) continue;
|
|
1096
1175
|
await this.meta.update(entry.path, () => void 0);
|
|
1097
1176
|
removed++;
|
|
@@ -1262,7 +1341,7 @@ function versionError(theirs) {
|
|
|
1262
1341
|
|
|
1263
1342
|
// src/fs/walk.ts
|
|
1264
1343
|
var MAX_LINKS2 = 40;
|
|
1265
|
-
function
|
|
1344
|
+
function parentOf4(path) {
|
|
1266
1345
|
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
1267
1346
|
}
|
|
1268
1347
|
async function mustBeDir(fs, path) {
|
|
@@ -1280,7 +1359,7 @@ async function realDir(fs, dir, budget = { hops: 0 }) {
|
|
|
1280
1359
|
const part = queue.shift();
|
|
1281
1360
|
if (part === ".") continue;
|
|
1282
1361
|
if (part === "..") {
|
|
1283
|
-
at =
|
|
1362
|
+
at = parentOf4(at);
|
|
1284
1363
|
continue;
|
|
1285
1364
|
}
|
|
1286
1365
|
const next2 = at === "/" ? `/${part}` : `${at}/${part}`;
|
|
@@ -1302,7 +1381,7 @@ function joinName(dir, name2) {
|
|
|
1302
1381
|
}
|
|
1303
1382
|
async function realTarget(fs, path) {
|
|
1304
1383
|
const budget = { hops: 0 };
|
|
1305
|
-
let dir = await realDir(fs,
|
|
1384
|
+
let dir = await realDir(fs, parentOf4(path), budget);
|
|
1306
1385
|
let name2 = path.slice(path.lastIndexOf("/") + 1);
|
|
1307
1386
|
for (; ; ) {
|
|
1308
1387
|
const link2 = await linkOf(fs, joinName(dir, name2));
|
|
@@ -1310,7 +1389,7 @@ async function realTarget(fs, path) {
|
|
|
1310
1389
|
if (budget.hops >= MAX_LINKS2) throw fsError("ELOOP", path);
|
|
1311
1390
|
budget.hops++;
|
|
1312
1391
|
const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
|
|
1313
|
-
dir = await realDir(fs,
|
|
1392
|
+
dir = await realDir(fs, parentOf4(target), budget);
|
|
1314
1393
|
name2 = target.slice(target.lastIndexOf("/") + 1);
|
|
1315
1394
|
}
|
|
1316
1395
|
return joinName(dir, name2);
|
|
@@ -1384,7 +1463,7 @@ async function walk(fs, cred, path, follow2) {
|
|
|
1384
1463
|
if (!dir) return { path: joinName(at, [part, ...queue].join("/")), perm: void 0 };
|
|
1385
1464
|
if (!permits(cred, dir, X_OK)) return { path, perm: void 0, denied: at };
|
|
1386
1465
|
if (part === "..") {
|
|
1387
|
-
at =
|
|
1466
|
+
at = parentOf4(at);
|
|
1388
1467
|
continue;
|
|
1389
1468
|
}
|
|
1390
1469
|
const next2 = joinName(at, part);
|
|
@@ -1506,17 +1585,17 @@ async function stickyEntry(g, op, path, dir) {
|
|
|
1506
1585
|
}
|
|
1507
1586
|
async function inParent(g, op, path, old = false) {
|
|
1508
1587
|
if (old === "replaced") {
|
|
1509
|
-
const dir2 = await need(g, op,
|
|
1588
|
+
const dir2 = await need(g, op, parentOf4(path), W_OK | X_OK, DIR, path);
|
|
1510
1589
|
return dir2.denied === void 0 ? stickyEntry(g, op, path, dir2) : void 0;
|
|
1511
1590
|
}
|
|
1512
|
-
if (!old) return void await need(g, op,
|
|
1513
|
-
const dir = await access(g.fs, g.cred,
|
|
1591
|
+
if (!old) return void await need(g, op, parentOf4(path), W_OK | X_OK, DIR, path);
|
|
1592
|
+
const dir = await access(g.fs, g.cred, parentOf4(path), W_OK | X_OK, DIR);
|
|
1514
1593
|
if (dir.denied === void 0) return stickyEntry(g, op, path, dir);
|
|
1515
1594
|
const lookup = dir.denied === dir.path && dir.perm && permits(g.cred, dir.perm, X_OK);
|
|
1516
1595
|
if (!lookup || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
|
|
1517
1596
|
}
|
|
1518
1597
|
async function makeIn(g, op, path) {
|
|
1519
|
-
for (let dir =
|
|
1598
|
+
for (let dir = parentOf4(path); ; dir = parentOf4(dir)) {
|
|
1520
1599
|
const walked = await access(g.fs, g.cred, dir, W_OK | X_OK, DIR);
|
|
1521
1600
|
if (walked.denied !== void 0) {
|
|
1522
1601
|
if (!await present(g.fs, path, true)) deny(g, op, path);
|
|
@@ -1544,7 +1623,7 @@ async function renamable(g, from, to) {
|
|
|
1544
1623
|
if (from === to) return void await need(g, "rename", from, 0, { follow: false });
|
|
1545
1624
|
await inParent(g, "rename", from, true);
|
|
1546
1625
|
await inParent(g, "rename", to, "replaced");
|
|
1547
|
-
if (g.cred.euid === 0 ||
|
|
1626
|
+
if (g.cred.euid === 0 || parentOf4(from) === parentOf4(to)) return;
|
|
1548
1627
|
const st = await g.fs.lstat(from).catch(() => void 0);
|
|
1549
1628
|
if (st?.isDirectory && !permits(g.cred, permOf(st), W_OK)) deny(g, "rename", from);
|
|
1550
1629
|
}
|
|
@@ -1598,7 +1677,7 @@ async function writable(g, op, path) {
|
|
|
1598
1677
|
}
|
|
1599
1678
|
async function creatable(g, op, path) {
|
|
1600
1679
|
const target = await need(g, op, path, 0);
|
|
1601
|
-
if (target.denied === void 0) await need(g, op,
|
|
1680
|
+
if (target.denied === void 0) await need(g, op, parentOf4(target.path), W_OK | X_OK, DIR, path);
|
|
1602
1681
|
}
|
|
1603
1682
|
async function openable(g, op, path, want, create) {
|
|
1604
1683
|
const st = create ? await g.fs.stat(path).catch(() => void 0) : void 0;
|
|
@@ -3516,7 +3595,7 @@ function cdpRegistry(send, gone) {
|
|
|
3516
3595
|
function visibleTo(g, notify) {
|
|
3517
3596
|
if (!g?.dac.enforcing) return notify;
|
|
3518
3597
|
let queue = Promise.resolve();
|
|
3519
|
-
const seen = async (path) => (await access(g.fs, g.cred,
|
|
3598
|
+
const seen = async (path) => (await access(g.fs, g.cred, parentOf4(path), R_OK | X_OK, { directory: true })).denied === void 0;
|
|
3520
3599
|
return (change) => {
|
|
3521
3600
|
queue = queue.then(async () => {
|
|
3522
3601
|
if (!("paths" in change)) return notify(change);
|
|
@@ -6504,7 +6583,7 @@ function webLocks() {
|
|
|
6504
6583
|
return globalThis.navigator?.locks;
|
|
6505
6584
|
}
|
|
6506
6585
|
var within3 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
6507
|
-
var
|
|
6586
|
+
var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
6508
6587
|
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
6509
6588
|
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
6510
6589
|
var PathGate = class {
|
|
@@ -6637,7 +6716,7 @@ var UnlinkedKeeper = class {
|
|
|
6637
6716
|
await this.serial(async () => {
|
|
6638
6717
|
await Promise.allSettled([this.fs.rename(file.hidden, path)]);
|
|
6639
6718
|
this.unlinkedName(file.hidden);
|
|
6640
|
-
await this.tidy(
|
|
6719
|
+
await this.tidy(parentOf5(file.hidden));
|
|
6641
6720
|
});
|
|
6642
6721
|
}
|
|
6643
6722
|
done4();
|
|
@@ -6678,7 +6757,7 @@ var UnlinkedKeeper = class {
|
|
|
6678
6757
|
}
|
|
6679
6758
|
await this.fs.rm(hidden).catch(() => void 0);
|
|
6680
6759
|
this.unlinkedName(hidden);
|
|
6681
|
-
await this.tidy(
|
|
6760
|
+
await this.tidy(parentOf5(hidden));
|
|
6682
6761
|
}
|
|
6683
6762
|
async linkedName(hidden) {
|
|
6684
6763
|
if (!mayLink(this.fs, hidden)) return;
|
|
@@ -6723,10 +6802,10 @@ var UnlinkedKeeper = class {
|
|
|
6723
6802
|
// src/fs/watch.ts
|
|
6724
6803
|
var WATCH_BATCH_LIMIT = 1e3;
|
|
6725
6804
|
var under2 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
|
|
6726
|
-
var
|
|
6805
|
+
var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
6727
6806
|
function touches(watcher, changed) {
|
|
6728
6807
|
return watcher.paths.some(
|
|
6729
|
-
(watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) :
|
|
6808
|
+
(watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) : parentOf6(changed) === watched)
|
|
6730
6809
|
);
|
|
6731
6810
|
}
|
|
6732
6811
|
var FsWatchers = class {
|
|
@@ -7303,7 +7382,7 @@ var HeldPaths = class extends Map {
|
|
|
7303
7382
|
return this.revoked.has(path);
|
|
7304
7383
|
}
|
|
7305
7384
|
};
|
|
7306
|
-
var
|
|
7385
|
+
var parentOf7 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
7307
7386
|
var readonly = (mount) => mount.caps.readonly === true || mount.options.ro !== void 0;
|
|
7308
7387
|
function inodeless(mount, attr2) {
|
|
7309
7388
|
if (attr2.ino !== void 0) return false;
|
|
@@ -7583,7 +7662,7 @@ var MountTable = class {
|
|
|
7583
7662
|
mount.epoch++;
|
|
7584
7663
|
for (const key of [...mount.attrs.keys()]) if (within4(key, path)) mount.attrs.delete(key);
|
|
7585
7664
|
mount.lists.delete(path);
|
|
7586
|
-
mount.lists.delete(
|
|
7665
|
+
mount.lists.delete(parentOf7(path));
|
|
7587
7666
|
for (const key of [...mount.lists.keys()]) if (within4(key, path)) mount.lists.delete(key);
|
|
7588
7667
|
}
|
|
7589
7668
|
async getattr(mount, path, rel2, settled = false) {
|
|
@@ -7659,7 +7738,7 @@ var MountFs = class {
|
|
|
7659
7738
|
if (attr2.kind !== "symlink") return real;
|
|
7660
7739
|
if (hops >= MAX_LINKS6) throw errnoError("ELOOP", path);
|
|
7661
7740
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
7662
|
-
return this.follow(this.base.resolvePath(
|
|
7741
|
+
return this.follow(this.base.resolvePath(parentOf7(real), target), hops + 1, create);
|
|
7663
7742
|
}
|
|
7664
7743
|
async open(mount, rel2, write, owner) {
|
|
7665
7744
|
return await mount.conn.call({
|
|
@@ -7675,8 +7754,8 @@ var MountFs = class {
|
|
|
7675
7754
|
async driverOwner(found, real, owner, directory) {
|
|
7676
7755
|
const { mount, rel: rel2 } = found;
|
|
7677
7756
|
if (!owner || !mount.owners || !mount.caps.owners) return void 0;
|
|
7678
|
-
const dir =
|
|
7679
|
-
const above = statOf(await this.table.getattr(mount, dir,
|
|
7757
|
+
const dir = parentOf7(real);
|
|
7758
|
+
const above = statOf(await this.table.getattr(mount, dir, parentOf7(rel2)), dir, mount);
|
|
7680
7759
|
const setgid = (above.mode & S_ISGID4) !== 0;
|
|
7681
7760
|
let mode = owner.mode;
|
|
7682
7761
|
if (directory && setgid) mode = (mode ?? 493) | S_ISGID4;
|
|
@@ -8015,7 +8094,7 @@ var MountFs = class {
|
|
|
8015
8094
|
}
|
|
8016
8095
|
async named(path) {
|
|
8017
8096
|
const at = normalizePath(path);
|
|
8018
|
-
return joinName(await realDir(this,
|
|
8097
|
+
return joinName(await realDir(this, parentOf7(at)), at.slice(at.lastIndexOf("/") + 1));
|
|
8019
8098
|
}
|
|
8020
8099
|
async link(source, target, follow2 = false) {
|
|
8021
8100
|
const from = follow2 ? await realTarget(this, normalizePath(source)) : await this.named(source);
|
|
@@ -10753,7 +10832,7 @@ var VfsNodes = class {
|
|
|
10753
10832
|
return run2;
|
|
10754
10833
|
}
|
|
10755
10834
|
async entryKey(path) {
|
|
10756
|
-
const parent =
|
|
10835
|
+
const parent = parentOf4(path);
|
|
10757
10836
|
const dir = await this.real(parent, true, () => realDir(this.fs, parent));
|
|
10758
10837
|
return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
|
|
10759
10838
|
}
|
|
@@ -10955,7 +11034,7 @@ function errCode(err) {
|
|
|
10955
11034
|
return err?.code;
|
|
10956
11035
|
}
|
|
10957
11036
|
async function targetDir(fs, path) {
|
|
10958
|
-
return
|
|
11037
|
+
return parentOf4(await realTarget(fs, path).catch(() => path));
|
|
10959
11038
|
}
|
|
10960
11039
|
async function refuseReadonly(fs, path, flags, opts = {}) {
|
|
10961
11040
|
const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
|
|
@@ -15167,7 +15246,7 @@ var Launcher = class {
|
|
|
15167
15246
|
async realLayoutPath(path) {
|
|
15168
15247
|
if (!await this.base.exists(path)) return void 0;
|
|
15169
15248
|
if (path === "/") return path;
|
|
15170
|
-
return joinName(await realDir(this.base,
|
|
15249
|
+
return joinName(await realDir(this.base, parentOf4(path)), baseName(path));
|
|
15171
15250
|
}
|
|
15172
15251
|
async provisionPnpmHome() {
|
|
15173
15252
|
await this.base.mkdir(this.pnpmHome, { recursive: true }).catch(() => void 0);
|