@ai-ecoverse/slicc-kernel 1.61.2 → 1.62.1
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 +20 -6
- package/dist/index.js +12 -9
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +691 -263
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +72 -19
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +786 -358
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +72 -19
- package/dist/process-worker.js.map +2 -2
- package/package.json +2 -2
package/dist/kernel-worker.js
CHANGED
|
@@ -73,7 +73,7 @@ var IndexedDbMeta = class {
|
|
|
73
73
|
this.generation++;
|
|
74
74
|
for (const path of paths) this.cache.delete(path);
|
|
75
75
|
for (const key of [...this.cache.keys()]) {
|
|
76
|
-
if (trees.some((
|
|
76
|
+
if (trees.some((tree2) => under(key, tree2))) this.cache.delete(key);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
changed(invalidation2) {
|
|
@@ -195,13 +195,13 @@ function fsError(code, path) {
|
|
|
195
195
|
return Object.assign(new Error(`${code}: ${path}`), { code });
|
|
196
196
|
}
|
|
197
197
|
function normalizePath(path) {
|
|
198
|
-
const
|
|
198
|
+
const parts2 = [];
|
|
199
199
|
for (const part of path.split("/")) {
|
|
200
200
|
if (part === "" || part === ".") continue;
|
|
201
|
-
if (part === "..")
|
|
202
|
-
else
|
|
201
|
+
if (part === "..") parts2.pop();
|
|
202
|
+
else parts2.push(part);
|
|
203
203
|
}
|
|
204
|
-
return `/${
|
|
204
|
+
return `/${parts2.join("/")}`;
|
|
205
205
|
}
|
|
206
206
|
function resolvePath(base, path) {
|
|
207
207
|
return normalizePath(path.startsWith("/") ? path : `${base}/${path}`);
|
|
@@ -239,8 +239,8 @@ function inherit(entries) {
|
|
|
239
239
|
return { ...ROOT_OWNER };
|
|
240
240
|
}
|
|
241
241
|
function chain(path) {
|
|
242
|
-
const
|
|
243
|
-
return ["/", ...
|
|
242
|
+
const parts2 = path.split("/").filter(Boolean);
|
|
243
|
+
return ["/", ...parts2.map((_, i) => `/${parts2.slice(0, i + 1).join("/")}`)];
|
|
244
244
|
}
|
|
245
245
|
function sameOwner(a, b) {
|
|
246
246
|
return a.uid === b.uid && a.gid === b.gid;
|
|
@@ -315,10 +315,10 @@ function translate(err, path, op = "lookup", mapped = true) {
|
|
|
315
315
|
var META_OPS = ["get", "update", "move", "remove", "links", "under", "all"];
|
|
316
316
|
function sidecar(meta) {
|
|
317
317
|
const pathOf = ([first]) => [first, "/"].flat().find((p) => typeof p === "string");
|
|
318
|
-
const
|
|
318
|
+
const guarded2 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
|
|
319
319
|
throw translate(err, pathOf(args), "sidecar", false);
|
|
320
320
|
});
|
|
321
|
-
return Object.fromEntries(META_OPS.map((op) => [op,
|
|
321
|
+
return Object.fromEntries(META_OPS.map((op) => [op, guarded2(op)]));
|
|
322
322
|
}
|
|
323
323
|
function missing(err) {
|
|
324
324
|
const code = err?.code;
|
|
@@ -334,8 +334,8 @@ function baseOf(path) {
|
|
|
334
334
|
return path.slice(path.lastIndexOf("/") + 1);
|
|
335
335
|
}
|
|
336
336
|
function prefixes(path) {
|
|
337
|
-
const
|
|
338
|
-
return
|
|
337
|
+
const parts2 = split(path);
|
|
338
|
+
return parts2.map((_, i) => `/${parts2.slice(0, i + 1).join("/")}`);
|
|
339
339
|
}
|
|
340
340
|
function within(path, root) {
|
|
341
341
|
return path === root || path.startsWith(`${root}/`);
|
|
@@ -356,39 +356,39 @@ async function openSync(handle) {
|
|
|
356
356
|
}
|
|
357
357
|
}
|
|
358
358
|
}
|
|
359
|
-
function writeAll(
|
|
359
|
+
function writeAll(access2, at, bytes) {
|
|
360
360
|
for (let done4 = 0; done4 < bytes.length; ) {
|
|
361
|
-
const n =
|
|
361
|
+
const n = access2.write(bytes.subarray(done4), { at: at + done4 });
|
|
362
362
|
if (!(n > 0))
|
|
363
363
|
throw Object.assign(new Error("the file system took no more bytes"), { code: "ENOSPC" });
|
|
364
364
|
done4 += n;
|
|
365
365
|
}
|
|
366
366
|
}
|
|
367
367
|
async function positioned(handle, op) {
|
|
368
|
-
const
|
|
369
|
-
if (
|
|
368
|
+
const access2 = await openSync(handle);
|
|
369
|
+
if (access2) {
|
|
370
370
|
try {
|
|
371
371
|
op({
|
|
372
|
-
write: (at, bytes) => writeAll(
|
|
373
|
-
truncate: (n) =>
|
|
372
|
+
write: (at, bytes) => writeAll(access2, at, bytes),
|
|
373
|
+
truncate: (n) => access2.truncate(n)
|
|
374
374
|
});
|
|
375
|
-
|
|
375
|
+
access2.flush();
|
|
376
376
|
} finally {
|
|
377
|
-
|
|
377
|
+
access2.close();
|
|
378
378
|
}
|
|
379
379
|
return;
|
|
380
380
|
}
|
|
381
|
-
const
|
|
381
|
+
const writable2 = await handle.createWritable({ keepExistingData: true });
|
|
382
382
|
try {
|
|
383
383
|
await op({
|
|
384
|
-
write: (at, bytes) =>
|
|
385
|
-
truncate: (n) =>
|
|
384
|
+
write: (at, bytes) => writable2.write({ type: "write", position: at, data: bytes }),
|
|
385
|
+
truncate: (n) => writable2.truncate(n)
|
|
386
386
|
});
|
|
387
387
|
} catch (err) {
|
|
388
|
-
await
|
|
388
|
+
await writable2.abort();
|
|
389
389
|
throw err;
|
|
390
390
|
}
|
|
391
|
-
await
|
|
391
|
+
await writable2.close();
|
|
392
392
|
}
|
|
393
393
|
function syncAccessHandles(scope = globalThis) {
|
|
394
394
|
const handle = scope.FileSystemFileHandle;
|
|
@@ -427,6 +427,14 @@ var OpfsFs = class {
|
|
|
427
427
|
trackOwners() {
|
|
428
428
|
this.owners = true;
|
|
429
429
|
}
|
|
430
|
+
async ancestry(path) {
|
|
431
|
+
const entries = await this.meta.get(chain(path));
|
|
432
|
+
return entries.map((entry, i) => ({
|
|
433
|
+
...ownOf(entry) ?? (i === 0 ? ROOT_OWNER : inherit(entries.slice(0, i))),
|
|
434
|
+
...entry?.mode !== void 0 ? { mode: entry.mode } : {},
|
|
435
|
+
...entry?.link !== void 0 ? { link: entry.link } : {}
|
|
436
|
+
}));
|
|
437
|
+
}
|
|
430
438
|
async handedTo(dir) {
|
|
431
439
|
return inherit(await this.meta.get(chain(dir)));
|
|
432
440
|
}
|
|
@@ -491,30 +499,30 @@ var OpfsFs = class {
|
|
|
491
499
|
const [stored] = source.link ? [source.link] : await this.meta.get([source.path]);
|
|
492
500
|
return ownOf(stored) ? void 0 : this.ownerAt(source.path, stored);
|
|
493
501
|
}
|
|
494
|
-
async walk(
|
|
502
|
+
async walk(parts2, from, start2) {
|
|
495
503
|
let dir = start2;
|
|
496
|
-
for (let at = from; at <
|
|
497
|
-
dir = await dir.getDirectoryHandle(
|
|
498
|
-
this.dirs.set(`/${
|
|
504
|
+
for (let at = from; at < parts2.length; at++) {
|
|
505
|
+
dir = await dir.getDirectoryHandle(parts2[at]);
|
|
506
|
+
this.dirs.set(`/${parts2.slice(0, at + 1).join("/")}`, dir);
|
|
499
507
|
this.checked.set(dir, performance.now());
|
|
500
508
|
}
|
|
501
509
|
return dir;
|
|
502
510
|
}
|
|
503
511
|
async dir(path) {
|
|
504
|
-
const
|
|
505
|
-
let from =
|
|
512
|
+
const parts2 = split(path);
|
|
513
|
+
let from = parts2.length;
|
|
506
514
|
let cached;
|
|
507
515
|
for (; from > 0 && !cached; from--)
|
|
508
|
-
cached = this.dirs.get(`/${
|
|
516
|
+
cached = this.dirs.get(`/${parts2.slice(0, from).join("/")}`);
|
|
509
517
|
if (cached) from++;
|
|
510
|
-
const at = `/${
|
|
518
|
+
const at = `/${parts2.slice(0, from).join("/")}`;
|
|
511
519
|
if (cached && !await this.current(at, cached)) {
|
|
512
520
|
this.forget(at);
|
|
513
521
|
cached = void 0;
|
|
514
522
|
from = 0;
|
|
515
523
|
}
|
|
516
524
|
try {
|
|
517
|
-
return await this.walk(
|
|
525
|
+
return await this.walk(parts2, from, cached ?? this.root);
|
|
518
526
|
} catch (err) {
|
|
519
527
|
throw translate(err, path);
|
|
520
528
|
}
|
|
@@ -544,10 +552,10 @@ var OpfsFs = class {
|
|
|
544
552
|
return kind === "file" ? parent.getFileHandle(name2) : parent.getDirectoryHandle(name2);
|
|
545
553
|
}
|
|
546
554
|
async parent(path) {
|
|
547
|
-
const
|
|
548
|
-
const name2 =
|
|
555
|
+
const parts2 = split(path);
|
|
556
|
+
const name2 = parts2.pop();
|
|
549
557
|
if (name2 === void 0) throw fsError("EBUSY", path);
|
|
550
|
-
return [`/${
|
|
558
|
+
return [`/${parts2.join("/")}`, name2];
|
|
551
559
|
}
|
|
552
560
|
async handle(path) {
|
|
553
561
|
if (split(path).length === 0) return this.root;
|
|
@@ -749,19 +757,19 @@ var OpfsFs = class {
|
|
|
749
757
|
}
|
|
750
758
|
async writeSession(path, truncate) {
|
|
751
759
|
const handle = (await this.fileFor(path))[0];
|
|
752
|
-
const
|
|
760
|
+
const writable2 = await this.retrying(
|
|
753
761
|
path,
|
|
754
762
|
() => handle.createWritable({ keepExistingData: !truncate }),
|
|
755
763
|
true
|
|
756
764
|
);
|
|
757
765
|
return {
|
|
758
|
-
write: (at, bytes) =>
|
|
766
|
+
write: (at, bytes) => writable2.write({ type: "write", position: at, data: bytes }).catch((err) => {
|
|
759
767
|
throw translate(err, path, "write", false);
|
|
760
768
|
}),
|
|
761
|
-
truncate: (size) =>
|
|
769
|
+
truncate: (size) => writable2.truncate(size).catch((err) => {
|
|
762
770
|
throw translate(err, path, "write", false);
|
|
763
771
|
}),
|
|
764
|
-
close: () =>
|
|
772
|
+
close: () => writable2.close().catch((err) => {
|
|
765
773
|
throw translate(err, path, "commit", false);
|
|
766
774
|
})
|
|
767
775
|
};
|
|
@@ -799,9 +807,9 @@ var OpfsFs = class {
|
|
|
799
807
|
if (!existed) await this.stampNew(target, owner, false);
|
|
800
808
|
if (!existed && content.length === 0) return;
|
|
801
809
|
try {
|
|
802
|
-
const
|
|
803
|
-
await
|
|
804
|
-
await
|
|
810
|
+
const writable2 = await file.createWritable();
|
|
811
|
+
await writable2.write(content);
|
|
812
|
+
await writable2.close();
|
|
805
813
|
} catch (err) {
|
|
806
814
|
throw translate(err, path, "commit", false);
|
|
807
815
|
}
|
|
@@ -987,9 +995,9 @@ var OpfsFs = class {
|
|
|
987
995
|
}
|
|
988
996
|
async copy(handle, dir, name2) {
|
|
989
997
|
if (handle.kind === "file") {
|
|
990
|
-
const
|
|
991
|
-
await
|
|
992
|
-
await
|
|
998
|
+
const writable2 = await (await dir.getFileHandle(name2, { create: true })).createWritable();
|
|
999
|
+
await writable2.write(await handle.getFile());
|
|
1000
|
+
await writable2.close();
|
|
993
1001
|
return;
|
|
994
1002
|
}
|
|
995
1003
|
const target = await dir.getDirectoryHandle(name2, { create: true });
|
|
@@ -1241,6 +1249,298 @@ function versionError(theirs) {
|
|
|
1241
1249
|
return `slicc-kernel client protocol ${named} is not supported: this side speaks ${PROTOCOL.join(".")}`;
|
|
1242
1250
|
}
|
|
1243
1251
|
|
|
1252
|
+
// src/fs/walk.ts
|
|
1253
|
+
var MAX_LINKS2 = 40;
|
|
1254
|
+
function parentOf3(path) {
|
|
1255
|
+
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
1256
|
+
}
|
|
1257
|
+
async function mustBeDir(fs, path) {
|
|
1258
|
+
if (!fs.lstat) return;
|
|
1259
|
+
const st = await fs.lstat(path);
|
|
1260
|
+
if (st.isDirectory !== true) throw fsError("ENOTDIR", path);
|
|
1261
|
+
}
|
|
1262
|
+
async function linkOf(fs, path) {
|
|
1263
|
+
return fs.readlink?.(path).catch(() => void 0);
|
|
1264
|
+
}
|
|
1265
|
+
async function realDir(fs, dir, budget = { hops: 0 }) {
|
|
1266
|
+
const queue = dir.split("/").filter(Boolean);
|
|
1267
|
+
let at = "/";
|
|
1268
|
+
while (queue.length > 0) {
|
|
1269
|
+
const part = queue.shift();
|
|
1270
|
+
if (part === ".") continue;
|
|
1271
|
+
if (part === "..") {
|
|
1272
|
+
at = parentOf3(at);
|
|
1273
|
+
continue;
|
|
1274
|
+
}
|
|
1275
|
+
const next2 = at === "/" ? `/${part}` : `${at}/${part}`;
|
|
1276
|
+
const link2 = await linkOf(fs, next2);
|
|
1277
|
+
if (link2 === void 0) {
|
|
1278
|
+
if (queue.find((p) => p !== ".") === "..") await mustBeDir(fs, next2);
|
|
1279
|
+
at = next2;
|
|
1280
|
+
continue;
|
|
1281
|
+
}
|
|
1282
|
+
if (budget.hops >= MAX_LINKS2) throw fsError("ELOOP", dir);
|
|
1283
|
+
budget.hops++;
|
|
1284
|
+
queue.unshift(...link2.split("/").filter(Boolean));
|
|
1285
|
+
if (link2.startsWith("/")) at = "/";
|
|
1286
|
+
}
|
|
1287
|
+
return at;
|
|
1288
|
+
}
|
|
1289
|
+
function joinName(dir, name2) {
|
|
1290
|
+
return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
|
|
1291
|
+
}
|
|
1292
|
+
async function realTarget(fs, path) {
|
|
1293
|
+
const budget = { hops: 0 };
|
|
1294
|
+
let dir = await realDir(fs, parentOf3(path), budget);
|
|
1295
|
+
let name2 = path.slice(path.lastIndexOf("/") + 1);
|
|
1296
|
+
for (; ; ) {
|
|
1297
|
+
const link2 = await linkOf(fs, joinName(dir, name2));
|
|
1298
|
+
if (link2 === void 0) break;
|
|
1299
|
+
if (budget.hops >= MAX_LINKS2) throw fsError("ELOOP", path);
|
|
1300
|
+
budget.hops++;
|
|
1301
|
+
const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
|
|
1302
|
+
dir = await realDir(fs, parentOf3(target), budget);
|
|
1303
|
+
name2 = target.slice(target.lastIndexOf("/") + 1);
|
|
1304
|
+
}
|
|
1305
|
+
return joinName(dir, name2);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// src/fs/access.ts
|
|
1309
|
+
var R_OK = 4;
|
|
1310
|
+
var W_OK = 2;
|
|
1311
|
+
var X_OK = 1;
|
|
1312
|
+
var MAX_LINKS3 = 40;
|
|
1313
|
+
var S_ISVTX = 512;
|
|
1314
|
+
function permOf(st, link2) {
|
|
1315
|
+
return {
|
|
1316
|
+
uid: st.uid ?? 0,
|
|
1317
|
+
gid: st.gid ?? 0,
|
|
1318
|
+
mode: st.mode & 4095,
|
|
1319
|
+
...link2 !== void 0 ? { link: link2 } : {}
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
function permits(cred, perm, want, directory = true) {
|
|
1323
|
+
const mode = perm.mode ?? (directory ? 493 : 420);
|
|
1324
|
+
if (cred.euid === 0) return (want & X_OK) === 0 || directory || (mode & 73) !== 0;
|
|
1325
|
+
const shift = cred.euid === perm.uid ? 6 : inGroup(cred, perm.gid) ? 3 : 0;
|
|
1326
|
+
return (mode >> shift & want) === want;
|
|
1327
|
+
}
|
|
1328
|
+
function sticky(cred, dir, entry) {
|
|
1329
|
+
if (cred.euid === 0 || ((dir.mode ?? 0) & S_ISVTX) === 0) return true;
|
|
1330
|
+
return cred.euid === dir.uid || cred.euid === (entry ?? dir).uid;
|
|
1331
|
+
}
|
|
1332
|
+
async function ancestryOf(fs, path) {
|
|
1333
|
+
if (fs.ancestry) return fs.ancestry(path);
|
|
1334
|
+
const out = [];
|
|
1335
|
+
for (const at of chain(path)) {
|
|
1336
|
+
const st = await fs.lstat(at).catch(() => void 0);
|
|
1337
|
+
if (!st) break;
|
|
1338
|
+
out.push(permOf(st, st.isSymbolicLink ? await fs.readlink(at) : void 0));
|
|
1339
|
+
}
|
|
1340
|
+
return out;
|
|
1341
|
+
}
|
|
1342
|
+
var Perms = class {
|
|
1343
|
+
known = /* @__PURE__ */ new Map();
|
|
1344
|
+
fs;
|
|
1345
|
+
constructor(fs) {
|
|
1346
|
+
this.fs = fs;
|
|
1347
|
+
}
|
|
1348
|
+
async learn(path) {
|
|
1349
|
+
const names = chain(path);
|
|
1350
|
+
const perms = await ancestryOf(this.fs, path);
|
|
1351
|
+
for (let i = 0; i < names.length; i++) {
|
|
1352
|
+
const perm = perms[i];
|
|
1353
|
+
this.known.set(names[i], perm);
|
|
1354
|
+
if (perm === void 0 || perm.link !== void 0) break;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
async at(path) {
|
|
1358
|
+
if (!this.known.has(path)) await this.learn(path);
|
|
1359
|
+
return this.known.get(path);
|
|
1360
|
+
}
|
|
1361
|
+
};
|
|
1362
|
+
function parts(path) {
|
|
1363
|
+
return path.split("/").filter((part) => part !== "" && part !== ".");
|
|
1364
|
+
}
|
|
1365
|
+
async function walk(fs, cred, path, follow2) {
|
|
1366
|
+
const perms = new Perms(fs);
|
|
1367
|
+
await perms.learn(path);
|
|
1368
|
+
const queue = parts(path);
|
|
1369
|
+
let at = "/";
|
|
1370
|
+
for (let hops = 0; queue.length > 0; ) {
|
|
1371
|
+
const part = queue.shift();
|
|
1372
|
+
const dir = await perms.at(at);
|
|
1373
|
+
if (!dir) return { path: joinName(at, [part, ...queue].join("/")), perm: void 0 };
|
|
1374
|
+
if (!permits(cred, dir, X_OK)) return { path, perm: void 0, denied: at };
|
|
1375
|
+
if (part === "..") {
|
|
1376
|
+
at = parentOf3(at);
|
|
1377
|
+
continue;
|
|
1378
|
+
}
|
|
1379
|
+
const next2 = joinName(at, part);
|
|
1380
|
+
const perm = await perms.at(next2);
|
|
1381
|
+
const last = queue.length === 0;
|
|
1382
|
+
if (perm?.link !== void 0 && (!last || follow2)) {
|
|
1383
|
+
if (++hops > MAX_LINKS3) return { path: next2, perm: void 0 };
|
|
1384
|
+
queue.unshift(...parts(perm.link));
|
|
1385
|
+
if (perm.link.startsWith("/")) at = "/";
|
|
1386
|
+
continue;
|
|
1387
|
+
}
|
|
1388
|
+
if (last) return { path: next2, perm };
|
|
1389
|
+
at = next2;
|
|
1390
|
+
}
|
|
1391
|
+
return { path: at, perm: await perms.at(at) };
|
|
1392
|
+
}
|
|
1393
|
+
async function present(fs, path, directory) {
|
|
1394
|
+
const st = await fs.lstat(path).catch(() => void 0);
|
|
1395
|
+
return st !== void 0 && (!directory || st.isDirectory);
|
|
1396
|
+
}
|
|
1397
|
+
async function access(fs, cred, path, want, options = {}) {
|
|
1398
|
+
const walked = await walk(fs, cred, path, options.follow !== false);
|
|
1399
|
+
if (walked.denied !== void 0) {
|
|
1400
|
+
if (await present(fs, walked.denied, true)) return walked;
|
|
1401
|
+
return { path, perm: void 0, missing: true };
|
|
1402
|
+
}
|
|
1403
|
+
const { perm } = walked;
|
|
1404
|
+
if (!perm) return { ...walked, missing: true };
|
|
1405
|
+
if (want === 0 || permits(cred, perm, want, options.directory !== false)) return walked;
|
|
1406
|
+
if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
|
|
1407
|
+
return { ...walked, missing: true };
|
|
1408
|
+
}
|
|
1409
|
+
function refused(path, errno = "EACCES") {
|
|
1410
|
+
return fsError(errno, path);
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
// src/fs/dac.ts
|
|
1414
|
+
var MAX_DENIAL_KEYS = 1e4;
|
|
1415
|
+
var S_ISGID3 = 1024;
|
|
1416
|
+
var Dac = class {
|
|
1417
|
+
mode;
|
|
1418
|
+
hook;
|
|
1419
|
+
log;
|
|
1420
|
+
seen = /* @__PURE__ */ new Set();
|
|
1421
|
+
suppressed = false;
|
|
1422
|
+
constructor(mode, hook, log = (line) => console.warn(line)) {
|
|
1423
|
+
this.mode = mode;
|
|
1424
|
+
this.hook = hook;
|
|
1425
|
+
this.log = log;
|
|
1426
|
+
}
|
|
1427
|
+
get enforcing() {
|
|
1428
|
+
return this.mode === "enforce";
|
|
1429
|
+
}
|
|
1430
|
+
denied(denial) {
|
|
1431
|
+
try {
|
|
1432
|
+
this.hook?.({ ...denial });
|
|
1433
|
+
} catch {
|
|
1434
|
+
}
|
|
1435
|
+
if (this.mode === "report") this.note(denial);
|
|
1436
|
+
else throw refused(denial.path, denial.errno);
|
|
1437
|
+
}
|
|
1438
|
+
note(d) {
|
|
1439
|
+
const key = `${d.uid}\0${d.op}\0${d.errno}\0${d.path}`;
|
|
1440
|
+
if (this.seen.has(key)) return;
|
|
1441
|
+
if (this.seen.size >= MAX_DENIAL_KEYS) {
|
|
1442
|
+
if (!this.suppressed) this.log("slicc-kernel users report: more denials suppressed");
|
|
1443
|
+
this.suppressed = true;
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
this.seen.add(key);
|
|
1447
|
+
this.log(
|
|
1448
|
+
`slicc-kernel users report: ${d.errno} ${d.op} ${d.path} (uid ${d.uid}, pid ${d.pid})`
|
|
1449
|
+
);
|
|
1450
|
+
}
|
|
1451
|
+
};
|
|
1452
|
+
function deny(g, op, path, errno = "EACCES") {
|
|
1453
|
+
g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno });
|
|
1454
|
+
}
|
|
1455
|
+
async function need(g, op, path, want, options = {}, shown = path) {
|
|
1456
|
+
const walked = await access(g.fs, g.cred, path, want, options);
|
|
1457
|
+
if (walked.denied !== void 0) deny(g, op, shown);
|
|
1458
|
+
return walked;
|
|
1459
|
+
}
|
|
1460
|
+
async function stickyEntry(g, op, path, dir) {
|
|
1461
|
+
const perm = dir.perm;
|
|
1462
|
+
if (!perm || sticky(g.cred, perm, perm)) return;
|
|
1463
|
+
const entry = joinName(dir.path, path.slice(path.lastIndexOf("/") + 1));
|
|
1464
|
+
if (sticky(g.cred, perm, (await ancestryOf(g.fs, entry)).at(-1))) return;
|
|
1465
|
+
if (await present(g.fs, entry, false)) deny(g, op, path, "EPERM");
|
|
1466
|
+
}
|
|
1467
|
+
async function inParent(g, op, path, old = false) {
|
|
1468
|
+
const dir = await need(g, op, parentOf3(path), W_OK | X_OK, {}, path);
|
|
1469
|
+
if (old && dir.denied === void 0) await stickyEntry(g, op, path, dir);
|
|
1470
|
+
}
|
|
1471
|
+
async function makeIn(g, op, path) {
|
|
1472
|
+
for (let dir = parentOf3(path); ; dir = parentOf3(dir)) {
|
|
1473
|
+
const walked = await access(g.fs, g.cred, dir, W_OK | X_OK);
|
|
1474
|
+
if (walked.denied !== void 0) {
|
|
1475
|
+
if (!await present(g.fs, path, true)) deny(g, op, path);
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1478
|
+
if (!walked.missing || dir === "/") return;
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
async function tree(g, op, dir, perm) {
|
|
1482
|
+
if (!permits(g.cred, perm, R_OK | W_OK | X_OK)) return deny(g, op, dir);
|
|
1483
|
+
for (const [name2, st] of await g.fs.readdirStat(dir)) {
|
|
1484
|
+
if (!st) continue;
|
|
1485
|
+
const child = joinName(dir, name2);
|
|
1486
|
+
if (!sticky(g.cred, perm, permOf(st))) deny(g, op, child, "EPERM");
|
|
1487
|
+
if (st.isDirectory) await tree(g, op, child, permOf(st));
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
async function removable(g, op, path) {
|
|
1491
|
+
await inParent(g, op, path, true);
|
|
1492
|
+
if (g.cred.euid === 0) return;
|
|
1493
|
+
const st = await g.fs.lstat(path).catch(() => void 0);
|
|
1494
|
+
if (st?.isDirectory) await tree(g, op, path, permOf(st));
|
|
1495
|
+
}
|
|
1496
|
+
async function renamable(g, from, to) {
|
|
1497
|
+
if (from === to) return void await need(g, "rename", from, 0, { follow: false });
|
|
1498
|
+
await inParent(g, "rename", from, true);
|
|
1499
|
+
await inParent(g, "rename", to, true);
|
|
1500
|
+
if (g.cred.euid === 0 || parentOf3(from) === parentOf3(to)) return;
|
|
1501
|
+
const st = await g.fs.lstat(from).catch(() => void 0);
|
|
1502
|
+
if (st?.isDirectory && !permits(g.cred, permOf(st), W_OK)) deny(g, "rename", from);
|
|
1503
|
+
}
|
|
1504
|
+
async function linkable(g, from, to, follow2) {
|
|
1505
|
+
const source = await need(g, "link", from, 0, { follow: follow2 });
|
|
1506
|
+
const perm = source.perm;
|
|
1507
|
+
if (perm && g.cred.euid !== 0 && perm.uid !== g.cred.euid) {
|
|
1508
|
+
const safe = permits(g.cred, perm, R_OK | W_OK, false);
|
|
1509
|
+
if (!safe && await present(g.fs, source.path, false)) deny(g, "link", from, "EPERM");
|
|
1510
|
+
}
|
|
1511
|
+
await inParent(g, "link", to);
|
|
1512
|
+
}
|
|
1513
|
+
async function ownerOnly(g, op, path, follow2) {
|
|
1514
|
+
const walked = await need(g, op, path, 0, { follow: follow2 });
|
|
1515
|
+
const perm = walked.perm;
|
|
1516
|
+
if (!perm || g.cred.euid === 0 || perm.uid === g.cred.euid) return walked;
|
|
1517
|
+
if (await present(g.fs, walked.path, false)) deny(g, op, path, "EPERM");
|
|
1518
|
+
return walked;
|
|
1519
|
+
}
|
|
1520
|
+
async function chmodMode(g, path, mode) {
|
|
1521
|
+
const perm = (await ownerOnly(g, "chmod", path, true)).perm;
|
|
1522
|
+
if (!g.dac.enforcing || !perm || g.cred.euid === 0 || inGroup(g.cred, perm.gid)) return mode;
|
|
1523
|
+
return mode & ~S_ISGID3;
|
|
1524
|
+
}
|
|
1525
|
+
async function retimable(g, op, path) {
|
|
1526
|
+
await ownerOnly(g, op, path, op !== "lutimes");
|
|
1527
|
+
}
|
|
1528
|
+
async function listable(g, op, path) {
|
|
1529
|
+
await need(g, op, path, R_OK | X_OK, { directory: true });
|
|
1530
|
+
}
|
|
1531
|
+
async function searchable(g, op, path, follow2) {
|
|
1532
|
+
await need(g, op, path, 0, { follow: follow2 });
|
|
1533
|
+
}
|
|
1534
|
+
async function readable(g, op, path) {
|
|
1535
|
+
await need(g, op, path, R_OK, { directory: false });
|
|
1536
|
+
}
|
|
1537
|
+
async function writable(g, op, path) {
|
|
1538
|
+
const st = await g.fs.stat(path).catch(() => void 0);
|
|
1539
|
+
if (st) return void await need(g, op, path, W_OK, { directory: st.isDirectory });
|
|
1540
|
+
const target = await need(g, op, path, 0);
|
|
1541
|
+
if (target.denied === void 0) await need(g, op, parentOf3(target.path), W_OK | X_OK, {}, path);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1244
1544
|
// src/kernel/pipe.ts
|
|
1245
1545
|
var PIPE_CAPACITY = 64 * 1024;
|
|
1246
1546
|
var PipeError = class extends Error {
|
|
@@ -1484,7 +1784,7 @@ function written(device, bytes) {
|
|
|
1484
1784
|
if (device === "full" && bytes.length > 0) throw new KernelError("ENOSPC");
|
|
1485
1785
|
return bytes.length;
|
|
1486
1786
|
}
|
|
1487
|
-
function deviceFile(device,
|
|
1787
|
+
function deviceFile(device, access2) {
|
|
1488
1788
|
const read = async (max) => {
|
|
1489
1789
|
if (device === "null") return new Uint8Array(0);
|
|
1490
1790
|
const out = new Uint8Array(max);
|
|
@@ -1496,10 +1796,10 @@ function deviceFile(device, access) {
|
|
|
1496
1796
|
return out;
|
|
1497
1797
|
};
|
|
1498
1798
|
return new OpenFile({
|
|
1499
|
-
...
|
|
1500
|
-
...
|
|
1799
|
+
...access2 !== "write" ? { read } : {},
|
|
1800
|
+
...access2 !== "read" ? { write: async (bytes) => written(device, bytes) } : {},
|
|
1501
1801
|
seek: async () => 0,
|
|
1502
|
-
heldMeta: { device, ...
|
|
1802
|
+
heldMeta: { device, ...access2 ? { access: access2 } : {} },
|
|
1503
1803
|
close: () => {
|
|
1504
1804
|
}
|
|
1505
1805
|
});
|
|
@@ -2751,9 +3051,9 @@ var UserDb = class {
|
|
|
2751
3051
|
if (await this.realDir(pnpm)) await this.fs.chmod(pnpm, scoop ? 488 : 448);
|
|
2752
3052
|
}
|
|
2753
3053
|
async realDir(path) {
|
|
2754
|
-
const
|
|
2755
|
-
for (let n = 1; n <=
|
|
2756
|
-
const at = `/${
|
|
3054
|
+
const parts2 = path.split("/").filter(Boolean);
|
|
3055
|
+
for (let n = 1; n <= parts2.length; n++) {
|
|
3056
|
+
const at = `/${parts2.slice(0, n).join("/")}`;
|
|
2757
3057
|
const st = await this.fs.lstat(at).catch(() => void 0);
|
|
2758
3058
|
if (!st?.isDirectory) return false;
|
|
2759
3059
|
}
|
|
@@ -2954,7 +3254,7 @@ var UserDb = class {
|
|
|
2954
3254
|
};
|
|
2955
3255
|
|
|
2956
3256
|
// src/client/serve-client.ts
|
|
2957
|
-
var
|
|
3257
|
+
var MAX_LINKS4 = 40;
|
|
2958
3258
|
function act(session, req, signal) {
|
|
2959
3259
|
if (req.action === "write") session.write(req.bytes);
|
|
2960
3260
|
else if (req.action === "resize") session.resize(req.cols, req.rows);
|
|
@@ -2962,26 +3262,50 @@ function act(session, req, signal) {
|
|
|
2962
3262
|
else session.close();
|
|
2963
3263
|
}
|
|
2964
3264
|
async function realpath(fs, path) {
|
|
2965
|
-
let
|
|
3265
|
+
let parts2 = fs.resolvePath("/", path).split("/").filter(Boolean);
|
|
2966
3266
|
let resolved = "";
|
|
2967
|
-
for (let hops = 0;
|
|
2968
|
-
const [name2, ...rest2] =
|
|
3267
|
+
for (let hops = 0; parts2.length > 0; ) {
|
|
3268
|
+
const [name2, ...rest2] = parts2;
|
|
2969
3269
|
const next2 = `${resolved}/${name2}`;
|
|
2970
3270
|
if (!(await fs.lstat(next2)).isSymbolicLink) {
|
|
2971
3271
|
resolved = next2;
|
|
2972
|
-
|
|
3272
|
+
parts2 = rest2;
|
|
2973
3273
|
continue;
|
|
2974
3274
|
}
|
|
2975
|
-
if (++hops >
|
|
3275
|
+
if (++hops > MAX_LINKS4) throw fsError("ELOOP", path);
|
|
2976
3276
|
const target = await fs.readlink(next2);
|
|
2977
3277
|
const base = target.startsWith("/") ? "/" : resolved || "/";
|
|
2978
|
-
|
|
3278
|
+
parts2 = [...fs.resolvePath(base, target).split("/").filter(Boolean), ...rest2];
|
|
2979
3279
|
resolved = "";
|
|
2980
3280
|
}
|
|
2981
3281
|
return resolved || "/";
|
|
2982
3282
|
}
|
|
2983
|
-
|
|
3283
|
+
var CLIENT_CHECKS = {
|
|
3284
|
+
readFile: (g, at) => readable(g, "read", at(0)),
|
|
3285
|
+
writeFile: (g, at) => writable(g, "write", at(0)),
|
|
3286
|
+
stat: (g, at) => searchable(g, "stat", at(0), true),
|
|
3287
|
+
lstat: (g, at) => searchable(g, "lstat", at(0), false),
|
|
3288
|
+
readdir: (g, at) => listable(g, "readdir", at(0)),
|
|
3289
|
+
mkdir: (g, at) => makeIn(g, "mkdir", at(0)),
|
|
3290
|
+
rm: (g, at) => removable(g, "rm", at(0)),
|
|
3291
|
+
rename: (g, at) => renamable(g, at(0), at(1)),
|
|
3292
|
+
realpath: (g, at) => searchable(g, "realpath", at(0), true),
|
|
3293
|
+
symlink: (g, at) => inParent(g, "symlink", at(1)),
|
|
3294
|
+
readlink: (g, at) => searchable(g, "readlink", at(0), false),
|
|
3295
|
+
exists: (g, at) => searchable(g, "exists", at(0), true)
|
|
3296
|
+
};
|
|
3297
|
+
async function fsCall(fs, method, args, owner, g) {
|
|
2984
3298
|
const path = (i) => fs.resolvePath("/", String(args[i]));
|
|
3299
|
+
if (g) {
|
|
3300
|
+
const denied2 = await Promise.resolve(CLIENT_CHECKS[method](g, path)).then(
|
|
3301
|
+
() => false,
|
|
3302
|
+
(err) => {
|
|
3303
|
+
if (method === "exists") return true;
|
|
3304
|
+
throw err;
|
|
3305
|
+
}
|
|
3306
|
+
);
|
|
3307
|
+
if (denied2) return { result: false };
|
|
3308
|
+
}
|
|
2985
3309
|
switch (method) {
|
|
2986
3310
|
case "readFile": {
|
|
2987
3311
|
const bytes = (await fs.readFileBuffer(path(0))).slice();
|
|
@@ -3044,6 +3368,10 @@ function ownerFor(l, self) {
|
|
|
3044
3368
|
if (!l.owners || !self) return void 0;
|
|
3045
3369
|
return { uid: self.uid, gid: self.gid, umask: self.umask ?? 18 };
|
|
3046
3370
|
}
|
|
3371
|
+
function guardFor(l, self) {
|
|
3372
|
+
if (!l.dac || !self) return void 0;
|
|
3373
|
+
return { fs: l.fs, cred: credOf(self), pid: 0, dac: l.dac };
|
|
3374
|
+
}
|
|
3047
3375
|
async function accountOf(l, user) {
|
|
3048
3376
|
const found = await l.users.lookup(user);
|
|
3049
3377
|
if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
@@ -3112,17 +3440,28 @@ function cdpRegistry(send, gone) {
|
|
|
3112
3440
|
}
|
|
3113
3441
|
};
|
|
3114
3442
|
}
|
|
3115
|
-
function
|
|
3443
|
+
function visibleTo(g, notify) {
|
|
3444
|
+
if (!g?.dac.enforcing) return notify;
|
|
3445
|
+
let queue = Promise.resolve();
|
|
3446
|
+
const seen = async (path) => (await access(g.fs, g.cred, parentOf3(path), R_OK | X_OK, { directory: true })).denied === void 0;
|
|
3447
|
+
return (change) => {
|
|
3448
|
+
queue = queue.then(async () => {
|
|
3449
|
+
if (!("paths" in change)) return notify(change);
|
|
3450
|
+
const paths = [];
|
|
3451
|
+
for (const path of change.paths) if (await seen(path)) paths.push(path);
|
|
3452
|
+
if (paths.length > 0) notify({ paths });
|
|
3453
|
+
});
|
|
3454
|
+
};
|
|
3455
|
+
}
|
|
3456
|
+
async function watchOp(l, req, watches, send, g) {
|
|
3116
3457
|
if (req.op === "unwatch") {
|
|
3117
3458
|
watches.get(req.watch)?.();
|
|
3118
3459
|
watches.delete(req.watch);
|
|
3119
3460
|
return { result: true };
|
|
3120
3461
|
}
|
|
3121
|
-
const
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
(change) => send({ watch: req.id, change })
|
|
3125
|
-
);
|
|
3462
|
+
if (g) for (const path of req.paths) await readable(g, "watch", l.fs.resolvePath("/", path));
|
|
3463
|
+
const notify = visibleTo(g, (change) => send({ watch: req.id, change }));
|
|
3464
|
+
const unwatch = l.watchers.watch(req.paths, { recursive: req.recursive }, notify);
|
|
3126
3465
|
watches.set(req.id, unwatch);
|
|
3127
3466
|
return { result: req.id };
|
|
3128
3467
|
}
|
|
@@ -3156,9 +3495,9 @@ function serveClient(port, host) {
|
|
|
3156
3495
|
ended.resolve();
|
|
3157
3496
|
}
|
|
3158
3497
|
async function hello(message3) {
|
|
3159
|
-
const
|
|
3160
|
-
if (
|
|
3161
|
-
send({ hello: { protocol: PROTOCOL, error:
|
|
3498
|
+
const refused2 = versionError(message3.protocol);
|
|
3499
|
+
if (refused2) {
|
|
3500
|
+
send({ hello: { protocol: PROTOCOL, error: refused2 } });
|
|
3162
3501
|
detach();
|
|
3163
3502
|
return;
|
|
3164
3503
|
}
|
|
@@ -3238,10 +3577,10 @@ function serveClient(port, host) {
|
|
|
3238
3577
|
if (!FS_METHODS.includes(req.method)) {
|
|
3239
3578
|
throw new Error(`unknown file system call ${req.method}`);
|
|
3240
3579
|
}
|
|
3241
|
-
return fsCall(l.fs, req.method, req.args, ownerFor(l, self));
|
|
3580
|
+
return fsCall(l.fs, req.method, req.args, ownerFor(l, self), guardFor(l, self));
|
|
3242
3581
|
case "watch":
|
|
3243
3582
|
case "unwatch":
|
|
3244
|
-
return watchOp(l, req, watches, send);
|
|
3583
|
+
return watchOp(l, req, watches, send, guardFor(l, self));
|
|
3245
3584
|
case "mount":
|
|
3246
3585
|
return { result: await l.mount(req.spec) };
|
|
3247
3586
|
case "umount":
|
|
@@ -4184,8 +4523,8 @@ var RealmProxy = class {
|
|
|
4184
4523
|
}
|
|
4185
4524
|
async connect(conn, incoming, req, idle) {
|
|
4186
4525
|
const target = tunnelTarget(req);
|
|
4187
|
-
const
|
|
4188
|
-
if (
|
|
4526
|
+
const refused2 = target.port === 443 ? await this.tunnelReady(target) : void 0;
|
|
4527
|
+
if (refused2) throw refused2;
|
|
4189
4528
|
await conn.write(latin1Bytes("HTTP/1.1 200 Connection Established\r\n\r\n"), this.stop.signal);
|
|
4190
4529
|
try {
|
|
4191
4530
|
const first = await incoming.peek(this.stop.signal);
|
|
@@ -4328,10 +4667,10 @@ var RealmProxy = class {
|
|
|
4328
4667
|
};
|
|
4329
4668
|
|
|
4330
4669
|
// src/kernel/net/x509.ts
|
|
4331
|
-
function concat2(
|
|
4332
|
-
const out = new Uint8Array(
|
|
4670
|
+
function concat2(parts2) {
|
|
4671
|
+
const out = new Uint8Array(parts2.reduce((n, p) => n + p.length, 0));
|
|
4333
4672
|
let at = 0;
|
|
4334
|
-
for (const p of
|
|
4673
|
+
for (const p of parts2) {
|
|
4335
4674
|
out.set(p, at);
|
|
4336
4675
|
at += p.length;
|
|
4337
4676
|
}
|
|
@@ -4347,8 +4686,8 @@ function tlv(tag, ...contents2) {
|
|
|
4347
4686
|
const body = concat2(contents2);
|
|
4348
4687
|
return concat2([Uint8Array.of(tag), length(body.length), body]);
|
|
4349
4688
|
}
|
|
4350
|
-
var seq = (...
|
|
4351
|
-
var set = (...
|
|
4689
|
+
var seq = (...parts2) => tlv(48, ...parts2);
|
|
4690
|
+
var set = (...parts2) => tlv(49, ...parts2);
|
|
4352
4691
|
var octets = (bytes) => tlv(4, bytes);
|
|
4353
4692
|
var bits = (bytes, unused = 0) => tlv(3, Uint8Array.of(unused), bytes);
|
|
4354
4693
|
var TRUE = tlv(1, Uint8Array.of(255));
|
|
@@ -4436,8 +4775,8 @@ function randomSerial() {
|
|
|
4436
4775
|
function ipv4(host) {
|
|
4437
4776
|
const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
|
|
4438
4777
|
if (!m) return void 0;
|
|
4439
|
-
const
|
|
4440
|
-
return
|
|
4778
|
+
const parts2 = m.slice(1).map(Number);
|
|
4779
|
+
return parts2.every((p) => p <= 255) ? Uint8Array.from(parts2) : void 0;
|
|
4441
4780
|
}
|
|
4442
4781
|
function ipv6(host) {
|
|
4443
4782
|
if (!host.includes(":") || !/^[0-9A-Fa-f:.]+$/.test(host)) return void 0;
|
|
@@ -5211,9 +5550,9 @@ var RemoteTransport = class {
|
|
|
5211
5550
|
// src/kernel/net/routes.ts
|
|
5212
5551
|
var HOST_ADDRESS = HOST_LOOPBACK_ADDRESS.join(".");
|
|
5213
5552
|
function ipv4Bytes(text) {
|
|
5214
|
-
const
|
|
5215
|
-
if (
|
|
5216
|
-
const bytes =
|
|
5553
|
+
const parts2 = text.split(".");
|
|
5554
|
+
if (parts2.length !== 4 || !parts2.every((p) => /^\d{1,3}$/.test(p))) return void 0;
|
|
5555
|
+
const bytes = parts2.map(Number);
|
|
5217
5556
|
return bytes.every((b) => b <= 255) ? bytes : void 0;
|
|
5218
5557
|
}
|
|
5219
5558
|
function ipv6Groups(text) {
|
|
@@ -5240,9 +5579,9 @@ function addressBits(host) {
|
|
|
5240
5579
|
return v6 ? { v6: true, bits: toBits(v6, 16) } : void 0;
|
|
5241
5580
|
}
|
|
5242
5581
|
function parsePrefix(text) {
|
|
5243
|
-
const
|
|
5244
|
-
if (
|
|
5245
|
-
const [address = "", size] =
|
|
5582
|
+
const parts2 = text.trim().split("/");
|
|
5583
|
+
if (parts2.length > 2) return void 0;
|
|
5584
|
+
const [address = "", size] = parts2;
|
|
5246
5585
|
const parsed2 = addressBits(address);
|
|
5247
5586
|
if (!parsed2) return void 0;
|
|
5248
5587
|
const max = parsed2.v6 ? 128 : 32;
|
|
@@ -5903,7 +6242,7 @@ function binfmtOf(binfmts, path) {
|
|
|
5903
6242
|
var S_IFDIR2 = 16384;
|
|
5904
6243
|
var S_IFREG2 = 32768;
|
|
5905
6244
|
var COMMAND = /^\/(?:usr\/)?bin\/([^/]+)$/;
|
|
5906
|
-
var
|
|
6245
|
+
var MAX_LINKS5 = 40;
|
|
5907
6246
|
function missing2(err) {
|
|
5908
6247
|
return err?.code === "ENOENT";
|
|
5909
6248
|
}
|
|
@@ -5923,7 +6262,7 @@ function synthetic(directory, path) {
|
|
|
5923
6262
|
}
|
|
5924
6263
|
async function followLinks(fs, path) {
|
|
5925
6264
|
let current = path;
|
|
5926
|
-
for (let hops = 0; hops <
|
|
6265
|
+
for (let hops = 0; hops < MAX_LINKS5; hops++) {
|
|
5927
6266
|
const st = await fs.lstat(current).catch(() => void 0);
|
|
5928
6267
|
if (!st?.isSymbolicLink) return current;
|
|
5929
6268
|
const parent = current.slice(0, current.lastIndexOf("/")) || "/";
|
|
@@ -6030,6 +6369,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
6030
6369
|
readlink: (path) => fs.readlink(path),
|
|
6031
6370
|
...fs.realpath ? { realpath: fs.realpath.bind(fs) } : {},
|
|
6032
6371
|
...fs.chown ? { chown: fs.chown.bind(fs) } : {},
|
|
6372
|
+
...fs.ancestry ? { ancestry: fs.ancestry.bind(fs) } : {},
|
|
6033
6373
|
chmod: (path, mode) => fs.chmod(path, mode),
|
|
6034
6374
|
utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime),
|
|
6035
6375
|
lutimes: (path, atime, mtime) => lutimesOf(fs, path, atime, mtime),
|
|
@@ -6042,12 +6382,12 @@ var MAX_HOPS = 40;
|
|
|
6042
6382
|
function start(path) {
|
|
6043
6383
|
return { done: [], todo: path.split("/").filter((p) => p !== ""), hops: 0 };
|
|
6044
6384
|
}
|
|
6045
|
-
function next(
|
|
6046
|
-
while (
|
|
6047
|
-
const part =
|
|
6385
|
+
function next(walk2) {
|
|
6386
|
+
while (walk2.todo.length > 0) {
|
|
6387
|
+
const part = walk2.todo.shift();
|
|
6048
6388
|
if (part === ".") continue;
|
|
6049
6389
|
if (part === "..") {
|
|
6050
|
-
|
|
6390
|
+
walk2.done.pop();
|
|
6051
6391
|
continue;
|
|
6052
6392
|
}
|
|
6053
6393
|
return part;
|
|
@@ -6055,33 +6395,33 @@ function next(walk) {
|
|
|
6055
6395
|
return void 0;
|
|
6056
6396
|
}
|
|
6057
6397
|
function lexical(path) {
|
|
6058
|
-
const
|
|
6059
|
-
for (let part = next(
|
|
6060
|
-
return `/${
|
|
6398
|
+
const walk2 = start(path);
|
|
6399
|
+
for (let part = next(walk2); part !== void 0; part = next(walk2)) walk2.done.push(part);
|
|
6400
|
+
return `/${walk2.done.join("/")}`;
|
|
6061
6401
|
}
|
|
6062
|
-
function rest(
|
|
6063
|
-
return lexical(`/${[...
|
|
6402
|
+
function rest(walk2, part) {
|
|
6403
|
+
return lexical(`/${[...walk2.done, part, ...walk2.todo].join("/")}`);
|
|
6064
6404
|
}
|
|
6065
|
-
function follow(
|
|
6066
|
-
if (++
|
|
6067
|
-
if (target.startsWith("/"))
|
|
6068
|
-
|
|
6405
|
+
function follow(walk2, target) {
|
|
6406
|
+
if (++walk2.hops > MAX_HOPS) return false;
|
|
6407
|
+
if (target.startsWith("/")) walk2.done.length = 0;
|
|
6408
|
+
walk2.todo = [...target.split("/").filter((p) => p !== ""), ...walk2.todo];
|
|
6069
6409
|
return true;
|
|
6070
6410
|
}
|
|
6071
6411
|
async function physicalPathAsync(fs, path) {
|
|
6072
|
-
const
|
|
6073
|
-
for (let part = next(
|
|
6074
|
-
const at = `/${[...
|
|
6412
|
+
const walk2 = start(path);
|
|
6413
|
+
for (let part = next(walk2); part !== void 0; part = next(walk2)) {
|
|
6414
|
+
const at = `/${[...walk2.done, part].join("/")}`;
|
|
6075
6415
|
let target;
|
|
6076
6416
|
try {
|
|
6077
6417
|
target = (await fs.lstat(at)).isSymbolicLink ? await fs.readlink(at) : void 0;
|
|
6078
6418
|
} catch {
|
|
6079
|
-
return rest(
|
|
6419
|
+
return rest(walk2, part);
|
|
6080
6420
|
}
|
|
6081
|
-
if (target === void 0)
|
|
6082
|
-
else if (!follow(
|
|
6421
|
+
if (target === void 0) walk2.done.push(part);
|
|
6422
|
+
else if (!follow(walk2, target)) return lexical(path);
|
|
6083
6423
|
}
|
|
6084
|
-
return `/${
|
|
6424
|
+
return `/${walk2.done.join("/")}`;
|
|
6085
6425
|
}
|
|
6086
6426
|
|
|
6087
6427
|
// src/fs/unlinked.ts
|
|
@@ -6090,7 +6430,7 @@ function webLocks() {
|
|
|
6090
6430
|
return globalThis.navigator?.locks;
|
|
6091
6431
|
}
|
|
6092
6432
|
var within3 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
6093
|
-
var
|
|
6433
|
+
var parentOf4 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
6094
6434
|
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
6095
6435
|
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
6096
6436
|
var PathGate = class {
|
|
@@ -6221,7 +6561,7 @@ var UnlinkedKeeper = class {
|
|
|
6221
6561
|
await this.serial(async () => {
|
|
6222
6562
|
await Promise.allSettled([this.fs.rename(file.hidden, path)]);
|
|
6223
6563
|
this.unlinkedName(file.hidden);
|
|
6224
|
-
await this.tidy(
|
|
6564
|
+
await this.tidy(parentOf4(file.hidden));
|
|
6225
6565
|
});
|
|
6226
6566
|
}
|
|
6227
6567
|
done4();
|
|
@@ -6261,7 +6601,7 @@ var UnlinkedKeeper = class {
|
|
|
6261
6601
|
}
|
|
6262
6602
|
await this.fs.rm(hidden).catch(() => void 0);
|
|
6263
6603
|
this.unlinkedName(hidden);
|
|
6264
|
-
await this.tidy(
|
|
6604
|
+
await this.tidy(parentOf4(hidden));
|
|
6265
6605
|
}
|
|
6266
6606
|
async linkedName(hidden) {
|
|
6267
6607
|
if (!mayLink(this.fs, hidden)) return;
|
|
@@ -6306,10 +6646,10 @@ var UnlinkedKeeper = class {
|
|
|
6306
6646
|
// src/fs/watch.ts
|
|
6307
6647
|
var WATCH_BATCH_LIMIT = 1e3;
|
|
6308
6648
|
var under2 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
|
|
6309
|
-
var
|
|
6649
|
+
var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
6310
6650
|
function touches(watcher, changed) {
|
|
6311
6651
|
return watcher.paths.some(
|
|
6312
|
-
(watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) :
|
|
6652
|
+
(watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) : parentOf5(changed) === watched)
|
|
6313
6653
|
);
|
|
6314
6654
|
}
|
|
6315
6655
|
var FsWatchers = class {
|
|
@@ -6358,9 +6698,9 @@ var FsWatchers = class {
|
|
|
6358
6698
|
mkdir: async (path, options) => {
|
|
6359
6699
|
const created3 = [];
|
|
6360
6700
|
if (options?.recursive) {
|
|
6361
|
-
const
|
|
6362
|
-
for (let at = 1; at <
|
|
6363
|
-
const ancestor = `/${
|
|
6701
|
+
const parts2 = normalizePath(path).split("/").filter(Boolean);
|
|
6702
|
+
for (let at = 1; at < parts2.length; at++) {
|
|
6703
|
+
const ancestor = `/${parts2.slice(0, at).join("/")}`;
|
|
6364
6704
|
if (created3.length > 0 || !await fs.exists(ancestor)) created3.push(ancestor);
|
|
6365
6705
|
}
|
|
6366
6706
|
}
|
|
@@ -6589,62 +6929,6 @@ var ChildTable = class {
|
|
|
6589
6929
|
}
|
|
6590
6930
|
};
|
|
6591
6931
|
|
|
6592
|
-
// src/fs/walk.ts
|
|
6593
|
-
var MAX_LINKS4 = 40;
|
|
6594
|
-
function parentOf5(path) {
|
|
6595
|
-
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
6596
|
-
}
|
|
6597
|
-
async function mustBeDir(fs, path) {
|
|
6598
|
-
if (!fs.lstat) return;
|
|
6599
|
-
const st = await fs.lstat(path);
|
|
6600
|
-
if (st.isDirectory !== true) throw fsError("ENOTDIR", path);
|
|
6601
|
-
}
|
|
6602
|
-
async function linkOf(fs, path) {
|
|
6603
|
-
return fs.readlink?.(path).catch(() => void 0);
|
|
6604
|
-
}
|
|
6605
|
-
async function realDir(fs, dir, budget = { hops: 0 }) {
|
|
6606
|
-
const queue = dir.split("/").filter(Boolean);
|
|
6607
|
-
let at = "/";
|
|
6608
|
-
while (queue.length > 0) {
|
|
6609
|
-
const part = queue.shift();
|
|
6610
|
-
if (part === ".") continue;
|
|
6611
|
-
if (part === "..") {
|
|
6612
|
-
at = parentOf5(at);
|
|
6613
|
-
continue;
|
|
6614
|
-
}
|
|
6615
|
-
const next2 = at === "/" ? `/${part}` : `${at}/${part}`;
|
|
6616
|
-
const link2 = await linkOf(fs, next2);
|
|
6617
|
-
if (link2 === void 0) {
|
|
6618
|
-
if (queue.find((p) => p !== ".") === "..") await mustBeDir(fs, next2);
|
|
6619
|
-
at = next2;
|
|
6620
|
-
continue;
|
|
6621
|
-
}
|
|
6622
|
-
if (budget.hops >= MAX_LINKS4) throw fsError("ELOOP", dir);
|
|
6623
|
-
budget.hops++;
|
|
6624
|
-
queue.unshift(...link2.split("/").filter(Boolean));
|
|
6625
|
-
if (link2.startsWith("/")) at = "/";
|
|
6626
|
-
}
|
|
6627
|
-
return at;
|
|
6628
|
-
}
|
|
6629
|
-
function joinName(dir, name2) {
|
|
6630
|
-
return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
|
|
6631
|
-
}
|
|
6632
|
-
async function realTarget(fs, path) {
|
|
6633
|
-
const budget = { hops: 0 };
|
|
6634
|
-
let dir = await realDir(fs, parentOf5(path), budget);
|
|
6635
|
-
let name2 = path.slice(path.lastIndexOf("/") + 1);
|
|
6636
|
-
for (; ; ) {
|
|
6637
|
-
const link2 = await linkOf(fs, joinName(dir, name2));
|
|
6638
|
-
if (link2 === void 0) break;
|
|
6639
|
-
if (budget.hops >= MAX_LINKS4) throw fsError("ELOOP", path);
|
|
6640
|
-
budget.hops++;
|
|
6641
|
-
const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
|
|
6642
|
-
dir = await realDir(fs, parentOf5(target), budget);
|
|
6643
|
-
name2 = target.slice(target.lastIndexOf("/") + 1);
|
|
6644
|
-
}
|
|
6645
|
-
return joinName(dir, name2);
|
|
6646
|
-
}
|
|
6647
|
-
|
|
6648
6932
|
// src/mount/protocol.ts
|
|
6649
6933
|
var DRIVER_PROTOCOL = [1, 2];
|
|
6650
6934
|
function driverVersionError(theirs) {
|
|
@@ -6707,8 +6991,8 @@ var DriverConnection = class _DriverConnection {
|
|
|
6707
6991
|
} finally {
|
|
6708
6992
|
clearTimeout(timer);
|
|
6709
6993
|
}
|
|
6710
|
-
const
|
|
6711
|
-
if (
|
|
6994
|
+
const refused2 = driverVersionError(hello.protocol);
|
|
6995
|
+
if (refused2) throw errnoError("EPROTO", refused2);
|
|
6712
6996
|
if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
|
|
6713
6997
|
return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options.onFail);
|
|
6714
6998
|
}
|
|
@@ -6748,7 +7032,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
6748
7032
|
};
|
|
6749
7033
|
|
|
6750
7034
|
// src/mount/mount-fs.ts
|
|
6751
|
-
var
|
|
7035
|
+
var S_ISGID4 = 1024;
|
|
6752
7036
|
var ID = /^\d{1,10}$/;
|
|
6753
7037
|
var MAX_ID2 = 4294967294;
|
|
6754
7038
|
function idOption(value) {
|
|
@@ -6761,7 +7045,7 @@ function checkIds(options) {
|
|
|
6761
7045
|
if (!ID.test(value) || Number(value) > MAX_ID2) throw errnoError("EINVAL", `${key}=${value}`);
|
|
6762
7046
|
}
|
|
6763
7047
|
}
|
|
6764
|
-
var
|
|
7048
|
+
var MAX_LINKS6 = 40;
|
|
6765
7049
|
var STALE_RETRIES = 3;
|
|
6766
7050
|
var SETTLE_ROUNDS = 4;
|
|
6767
7051
|
var DEFAULT_IO = 8 * 1024 * 1024;
|
|
@@ -6999,12 +7283,12 @@ var MountTable = class {
|
|
|
6999
7283
|
}
|
|
7000
7284
|
list() {
|
|
7001
7285
|
const mounted = [...this.mounts.values()].map(
|
|
7002
|
-
({ type, source, target, options, state, error, present:
|
|
7286
|
+
({ type, source, target, options, state, error, present: present3 }) => ({
|
|
7003
7287
|
type,
|
|
7004
7288
|
source,
|
|
7005
7289
|
target,
|
|
7006
7290
|
options: { ...options },
|
|
7007
|
-
state: state === "ok" &&
|
|
7291
|
+
state: state === "ok" && present3?.() === false ? "nomedium" : state,
|
|
7008
7292
|
...error ? { error } : {}
|
|
7009
7293
|
})
|
|
7010
7294
|
);
|
|
@@ -7279,7 +7563,7 @@ var MountFs = class {
|
|
|
7279
7563
|
throw err;
|
|
7280
7564
|
}
|
|
7281
7565
|
if (attr2.kind !== "symlink") return real;
|
|
7282
|
-
if (hops >=
|
|
7566
|
+
if (hops >= MAX_LINKS6) throw errnoError("ELOOP", path);
|
|
7283
7567
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
7284
7568
|
return this.follow(this.base.resolvePath(parentOf6(real), target), hops + 1, create);
|
|
7285
7569
|
}
|
|
@@ -7299,9 +7583,9 @@ var MountFs = class {
|
|
|
7299
7583
|
if (!owner || !mount.owners || !mount.caps.owners) return void 0;
|
|
7300
7584
|
const dir = parentOf6(real);
|
|
7301
7585
|
const above = statOf(await this.table.getattr(mount, dir, parentOf6(rel2)), dir, mount);
|
|
7302
|
-
const setgid = (above.mode &
|
|
7586
|
+
const setgid = (above.mode & S_ISGID4) !== 0;
|
|
7303
7587
|
let mode = owner.mode;
|
|
7304
|
-
if (directory && setgid) mode = (mode ?? 493) |
|
|
7588
|
+
if (directory && setgid) mode = (mode ?? 493) | S_ISGID4;
|
|
7305
7589
|
return {
|
|
7306
7590
|
uid: owner.uid,
|
|
7307
7591
|
gid: setgid ? above.gid : owner.gid,
|
|
@@ -7576,14 +7860,14 @@ var MountFs = class {
|
|
|
7576
7860
|
await mount.conn.call({ op: "mkdir", path: rel2, ...made2 });
|
|
7577
7861
|
return;
|
|
7578
7862
|
}
|
|
7579
|
-
const
|
|
7580
|
-
for (let n = 1; n <=
|
|
7581
|
-
const sub = `/${
|
|
7863
|
+
const parts2 = rel2.split("/").filter(Boolean);
|
|
7864
|
+
for (let n = 1; n <= parts2.length; n++) {
|
|
7865
|
+
const sub = `/${parts2.slice(0, n).join("/")}`;
|
|
7582
7866
|
const full = normalizePath(`${mount.target}${sub}`);
|
|
7583
7867
|
const exists = await this.table.getattr(mount, full, sub).catch(() => void 0);
|
|
7584
7868
|
if (exists?.kind === "directory") continue;
|
|
7585
7869
|
if (exists) throw errnoError("ENOTDIR", full);
|
|
7586
|
-
const own = n ===
|
|
7870
|
+
const own = n === parts2.length ? owner : owner && { ...owner, mode: owner.parents };
|
|
7587
7871
|
const made2 = await this.driverOwner({ mount, rel: sub }, full, own, true);
|
|
7588
7872
|
this.table.forget(mount, full);
|
|
7589
7873
|
await mount.conn.call({ op: "mkdir", path: sub, ...made2 });
|
|
@@ -7676,6 +7960,20 @@ var MountFs = class {
|
|
|
7676
7960
|
if (this.at(real)) throw errnoError("ENOSYS", path);
|
|
7677
7961
|
return real;
|
|
7678
7962
|
}
|
|
7963
|
+
async ancestry(path) {
|
|
7964
|
+
const names = chain(normalizePath(path));
|
|
7965
|
+
const first = names.findIndex((name2) => this.at(name2));
|
|
7966
|
+
if (first < 0) return ancestryOf(this.base, path);
|
|
7967
|
+
const out = first === 0 ? [] : await ancestryOf(this.base, names[first - 1]);
|
|
7968
|
+
if (out.length < first || out.some((perm) => perm?.link !== void 0)) return out;
|
|
7969
|
+
for (const name2 of names.slice(first)) {
|
|
7970
|
+
const st = await this.lstat(name2).catch(() => void 0);
|
|
7971
|
+
if (!st) break;
|
|
7972
|
+
out.push(permOf(st, st.isSymbolicLink ? await this.readlink(name2) : void 0));
|
|
7973
|
+
if (st.isSymbolicLink) break;
|
|
7974
|
+
}
|
|
7975
|
+
return out;
|
|
7976
|
+
}
|
|
7679
7977
|
async readlink(path) {
|
|
7680
7978
|
const found = this.at(path);
|
|
7681
7979
|
if (!found) return this.base.readlink(path);
|
|
@@ -7886,9 +8184,11 @@ async function run(entry, path, req) {
|
|
|
7886
8184
|
return done;
|
|
7887
8185
|
case "readlink":
|
|
7888
8186
|
return json2(await fs.readlink(path));
|
|
7889
|
-
case "chmod":
|
|
7890
|
-
|
|
8187
|
+
case "chmod": {
|
|
8188
|
+
const g = guardOf(entry);
|
|
8189
|
+
await fs.chmod(path, g ? await chmodMode(g, path, req.mode ?? 0) : req.mode ?? 0);
|
|
7891
8190
|
return done;
|
|
8191
|
+
}
|
|
7892
8192
|
case "utimes":
|
|
7893
8193
|
await fs.utimes(path, new Date(req.atimeMs ?? 0), new Date(req.mtimeMs ?? 0));
|
|
7894
8194
|
return done;
|
|
@@ -7906,6 +8206,53 @@ async function run(entry, path, req) {
|
|
|
7906
8206
|
return { ok: false, errno: "EINVAL", message: `sync-fs: unknown op '${req.op}'` };
|
|
7907
8207
|
}
|
|
7908
8208
|
}
|
|
8209
|
+
function guardOf(entry) {
|
|
8210
|
+
const cred = entry.dac && entry.cred?.();
|
|
8211
|
+
if (!cred || !entry.dac) return void 0;
|
|
8212
|
+
return { fs: entry.fs, cred, pid: entry.pid ?? 0, dac: entry.dac };
|
|
8213
|
+
}
|
|
8214
|
+
var FOLLOWING = /* @__PURE__ */ new Set(["exists", "stat", "chown", "statfs"]);
|
|
8215
|
+
var NOT_FOLLOWING = /* @__PURE__ */ new Set(["lstat", "readlink", "lchown"]);
|
|
8216
|
+
async function guard(g, path, req, cwd) {
|
|
8217
|
+
const { op } = req;
|
|
8218
|
+
if (FOLLOWING.has(op) || NOT_FOLLOWING.has(op)) return searchable(g, op, path, FOLLOWING.has(op));
|
|
8219
|
+
const other = () => g.fs.resolvePath(cwd, req.arg2 ?? "");
|
|
8220
|
+
switch (op) {
|
|
8221
|
+
case "readdir":
|
|
8222
|
+
case "readdir-stat":
|
|
8223
|
+
return listable(g, op, path);
|
|
8224
|
+
case "mkdir":
|
|
8225
|
+
return makeIn(g, op, path);
|
|
8226
|
+
case "create":
|
|
8227
|
+
case "symlink":
|
|
8228
|
+
return inParent(g, op, path);
|
|
8229
|
+
case "unlink":
|
|
8230
|
+
case "rmdir":
|
|
8231
|
+
return inParent(g, op, path, true);
|
|
8232
|
+
case "rm":
|
|
8233
|
+
return removable(g, op, path);
|
|
8234
|
+
case "rename":
|
|
8235
|
+
return renamable(g, path, other());
|
|
8236
|
+
case "link":
|
|
8237
|
+
return linkable(g, path, other(), req.mode === 1);
|
|
8238
|
+
case "utimes":
|
|
8239
|
+
case "lutimes":
|
|
8240
|
+
return retimable(g, op, path);
|
|
8241
|
+
default:
|
|
8242
|
+
return void 0;
|
|
8243
|
+
}
|
|
8244
|
+
}
|
|
8245
|
+
async function guarded(entry, path, req) {
|
|
8246
|
+
const g = guardOf(entry);
|
|
8247
|
+
if (!g) return void 0;
|
|
8248
|
+
try {
|
|
8249
|
+
await guard(g, path, req, entry.cwd);
|
|
8250
|
+
return void 0;
|
|
8251
|
+
} catch (err) {
|
|
8252
|
+
const refusal = err.code === "EACCES";
|
|
8253
|
+
return req.op === "exists" && refusal ? json2(false) : toErrno(err);
|
|
8254
|
+
}
|
|
8255
|
+
}
|
|
7909
8256
|
async function createExclusive(fs, path, owner) {
|
|
7910
8257
|
if (fs.createExclusive) return fs.createExclusive(path, owner);
|
|
7911
8258
|
if (await fs.lstat(path).then(
|
|
@@ -7958,24 +8305,56 @@ async function owned(entry, own, req, path, target) {
|
|
|
7958
8305
|
}
|
|
7959
8306
|
return own(target, () => run(entry, path, req));
|
|
7960
8307
|
}
|
|
8308
|
+
async function holdOp(entry, path, req) {
|
|
8309
|
+
const mode = req.mode ?? 0;
|
|
8310
|
+
const [on, open] = [(mode & 1) === 1, (mode & 2) === 2];
|
|
8311
|
+
if (on && open) {
|
|
8312
|
+
const real = await entry.fs.realpath?.(path, true).catch(() => path);
|
|
8313
|
+
return json2(entry.hold?.(path, on, open, real) ?? null);
|
|
8314
|
+
}
|
|
8315
|
+
entry.hold?.(path, on, open, void 0, req.handle);
|
|
8316
|
+
return done;
|
|
8317
|
+
}
|
|
8318
|
+
var IDENTITY_OPS = /* @__PURE__ */ new Set([
|
|
8319
|
+
"write",
|
|
8320
|
+
"pwrite",
|
|
8321
|
+
"truncate",
|
|
8322
|
+
"chmod",
|
|
8323
|
+
"utimes",
|
|
8324
|
+
"chown",
|
|
8325
|
+
"lchown"
|
|
8326
|
+
]);
|
|
7961
8327
|
async function dispatchSyncFs(req) {
|
|
7962
8328
|
const entry = resolveSyncFsToken(req.token);
|
|
7963
8329
|
if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
|
|
8330
|
+
if (!req.identity || !IDENTITY_OPS.has(req.op)) return dispatchOne(entry, req);
|
|
8331
|
+
const { fs, cwd } = entry;
|
|
8332
|
+
const path = fs.resolvePath(cwd, req.path);
|
|
8333
|
+
const identified = async (held) => {
|
|
8334
|
+
const own = held ? { ...entry, fs: { ...fs, writeFile: held.writeFile } } : entry;
|
|
8335
|
+
const result = await dispatchOne(own, req);
|
|
8336
|
+
if (!result.ok) return result;
|
|
8337
|
+
if (held && !held.direct) return json2(null);
|
|
8338
|
+
const look = req.op === "lchown" ? fs.lstat : fs.stat;
|
|
8339
|
+
const st = await look.call(fs, path).catch(() => void 0);
|
|
8340
|
+
return json2(st ? statJson(st) : null);
|
|
8341
|
+
};
|
|
8342
|
+
try {
|
|
8343
|
+
return await (fs.exclusive ? fs.exclusive(path, identified, req.op !== "lchown") : identified());
|
|
8344
|
+
} catch (err) {
|
|
8345
|
+
return toErrno(err);
|
|
8346
|
+
}
|
|
8347
|
+
}
|
|
8348
|
+
async function dispatchOne(entry, req) {
|
|
7964
8349
|
const { fs, cwd } = entry;
|
|
7965
8350
|
try {
|
|
7966
8351
|
const path = fs.resolvePath(cwd, req.path);
|
|
7967
8352
|
if (req.op === "kept") return json2(entry.keptFor?.(req.handle ?? -1) !== void 0);
|
|
7968
8353
|
if (req.op === "stat" && req.handle !== void 0) return await keptStat(entry, req);
|
|
8354
|
+
if (req.op === "hold") return holdOp(entry, path, req);
|
|
8355
|
+
const refused2 = await guarded(entry, path, req);
|
|
8356
|
+
if (refused2) return refused2;
|
|
7969
8357
|
if (req.op === "statfs") return json2(await entry.statfs?.(path) ?? await storageUsage());
|
|
7970
|
-
if (req.op === "hold") {
|
|
7971
|
-
const [on, open] = [((req.mode ?? 0) & 1) === 1, ((req.mode ?? 0) & 2) === 2];
|
|
7972
|
-
if (on && open) {
|
|
7973
|
-
const real = await fs.realpath?.(path, true).catch(() => path);
|
|
7974
|
-
return json2(entry.hold?.(path, on, open, real) ?? null);
|
|
7975
|
-
}
|
|
7976
|
-
entry.hold?.(path, on, open, void 0, req.handle);
|
|
7977
|
-
return done;
|
|
7978
|
-
}
|
|
7979
8358
|
if (entry.revoked?.(path)) return { ok: false, errno: "EIO", message: `${path} was unmounted` };
|
|
7980
8359
|
if (req.op === "write" && req.mode === 1 || UNLINKED_OPS.has(req.op)) {
|
|
7981
8360
|
const serve = async () => run(entry, await keptOr(entry, path, req.handle), req);
|
|
@@ -8149,9 +8528,9 @@ function mountTable(mounts = []) {
|
|
|
8149
8528
|
for (const m of mounts) {
|
|
8150
8529
|
const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
|
|
8151
8530
|
if (m.state !== "ok") options.push(m.state);
|
|
8152
|
-
const
|
|
8531
|
+
const access2 = options.includes("ro") ? [] : ["rw"];
|
|
8153
8532
|
lines2.push(
|
|
8154
|
-
`${field2(m.source)} ${field2(m.target)} ${m.type} ${[...
|
|
8533
|
+
`${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
|
|
8155
8534
|
);
|
|
8156
8535
|
}
|
|
8157
8536
|
return `${lines2.join("\n")}
|
|
@@ -8587,8 +8966,8 @@ var ProcTree = class {
|
|
|
8587
8966
|
alive(pid) {
|
|
8588
8967
|
return this.listing().processes.some((p) => p.pid === pid);
|
|
8589
8968
|
}
|
|
8590
|
-
node(
|
|
8591
|
-
const [top, leaf, ...rest2] =
|
|
8969
|
+
node(parts2) {
|
|
8970
|
+
const [top, leaf, ...rest2] = parts2;
|
|
8592
8971
|
if (top === void 0) {
|
|
8593
8972
|
return {
|
|
8594
8973
|
kind: "dir",
|
|
@@ -8626,9 +9005,9 @@ var ProcTree = class {
|
|
|
8626
9005
|
};
|
|
8627
9006
|
}
|
|
8628
9007
|
parts(path, follow2) {
|
|
8629
|
-
const
|
|
8630
|
-
if (
|
|
8631
|
-
return
|
|
9008
|
+
const parts2 = path.split("/").filter((p) => p !== "").slice(1);
|
|
9009
|
+
if (parts2[0] === "self" && (follow2 || parts2.length > 1)) parts2[0] = String(this.pid);
|
|
9010
|
+
return parts2;
|
|
8632
9011
|
}
|
|
8633
9012
|
find(path, follow2) {
|
|
8634
9013
|
const node = this.node(this.parts(path, follow2));
|
|
@@ -8714,8 +9093,8 @@ function refusalOf(found) {
|
|
|
8714
9093
|
return found === false ? { refused: "ENOTDIR" } : found;
|
|
8715
9094
|
}
|
|
8716
9095
|
function throughLink(target, done4, todo) {
|
|
8717
|
-
const
|
|
8718
|
-
return [target.startsWith("/") ? [] : done4, [...
|
|
9096
|
+
const parts2 = target.split("/").filter((part) => part !== "");
|
|
9097
|
+
return [target.startsWith("/") ? [] : done4, [...parts2, ...todo]];
|
|
8719
9098
|
}
|
|
8720
9099
|
async function procAlias(fs, path, follow2) {
|
|
8721
9100
|
const { readlink, lstat } = fs;
|
|
@@ -8725,14 +9104,14 @@ async function procAlias(fs, path, follow2) {
|
|
|
8725
9104
|
() => false
|
|
8726
9105
|
);
|
|
8727
9106
|
if (!physical) return void 0;
|
|
8728
|
-
const
|
|
8729
|
-
let step =
|
|
9107
|
+
const walk2 = procWalk(path, follow2);
|
|
9108
|
+
let step = walk2.next();
|
|
8730
9109
|
while (!step.done) {
|
|
8731
9110
|
const { at, check } = step.value;
|
|
8732
9111
|
const link2 = await readlink.call(fs, at).catch(() => void 0);
|
|
8733
9112
|
const isDir = (st) => st.isDirectory === true;
|
|
8734
9113
|
const target = link2 ?? (check ? await lstat.call(fs, at).then(isDir, failed) : true);
|
|
8735
|
-
step =
|
|
9114
|
+
step = walk2.next(target);
|
|
8736
9115
|
}
|
|
8737
9116
|
return step.value;
|
|
8738
9117
|
}
|
|
@@ -9966,7 +10345,9 @@ var VfsNode = class {
|
|
|
9966
10345
|
this.ranged = new AsyncRangedFile(io, this.path, this.pin.size, this.pin.version);
|
|
9967
10346
|
return this.ranged;
|
|
9968
10347
|
}
|
|
9969
|
-
const
|
|
10348
|
+
const at = this.path;
|
|
10349
|
+
const st = io && !this.orphaned ? await this.fs.stat?.(at).catch(() => void 0) : void 0;
|
|
10350
|
+
this.note(at, st);
|
|
9970
10351
|
if (io && st?.ranged) {
|
|
9971
10352
|
this.ranged = new AsyncRangedFile(io, this.path, st.size ?? 0, st.version);
|
|
9972
10353
|
return this.ranged;
|
|
@@ -10026,12 +10407,23 @@ var VfsNode = class {
|
|
|
10026
10407
|
const stat = this.fs.stat?.bind(this.fs);
|
|
10027
10408
|
if (!stat || this.orphaned) return Promise.resolve(void 0);
|
|
10028
10409
|
const parent = this.path.slice(0, this.path.lastIndexOf("/")) || "/";
|
|
10029
|
-
|
|
10410
|
+
const at = this.path;
|
|
10411
|
+
this.cap ??= stat(at).then((st) => {
|
|
10412
|
+
this.note(at, st);
|
|
10413
|
+
return st;
|
|
10414
|
+
}).catch(() => stat(parent)).then(
|
|
10030
10415
|
(st) => st.maxFile,
|
|
10031
10416
|
() => void 0
|
|
10032
10417
|
);
|
|
10033
10418
|
return this.cap;
|
|
10034
10419
|
}
|
|
10420
|
+
seen;
|
|
10421
|
+
note(path, st) {
|
|
10422
|
+
if (st?.ino !== void 0) this.seen = { path, ino: st.ino };
|
|
10423
|
+
}
|
|
10424
|
+
known() {
|
|
10425
|
+
return this.seen?.path === this.path && !this.orphaned ? this.seen.ino : void 0;
|
|
10426
|
+
}
|
|
10035
10427
|
async fits(end) {
|
|
10036
10428
|
const max = await this.limit();
|
|
10037
10429
|
if (max !== void 0 && end > max) throw new KernelError("EFBIG");
|
|
@@ -10079,10 +10471,10 @@ var VfsNode = class {
|
|
|
10079
10471
|
this.serial(() => this.flush()).catch(() => void 0);
|
|
10080
10472
|
}, delay);
|
|
10081
10473
|
}
|
|
10082
|
-
ensure(
|
|
10474
|
+
ensure(need2) {
|
|
10083
10475
|
const cur = this.data ?? new Uint8Array(0);
|
|
10084
|
-
if (cur.length >=
|
|
10085
|
-
const grown = new Uint8Array(Math.max(
|
|
10476
|
+
if (cur.length >= need2) return cur;
|
|
10477
|
+
const grown = new Uint8Array(Math.max(need2, cur.length * 2, 256));
|
|
10086
10478
|
grown.set(cur.subarray(0, this.length));
|
|
10087
10479
|
this.data = grown;
|
|
10088
10480
|
return grown;
|
|
@@ -10125,14 +10517,14 @@ var VfsNodes = class {
|
|
|
10125
10517
|
return run2;
|
|
10126
10518
|
}
|
|
10127
10519
|
async entryKey(path) {
|
|
10128
|
-
const parent =
|
|
10520
|
+
const parent = parentOf3(path);
|
|
10129
10521
|
const dir = await this.real(parent, true, () => realDir(this.fs, parent));
|
|
10130
10522
|
return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
|
|
10131
10523
|
}
|
|
10132
10524
|
targetKey(path) {
|
|
10133
10525
|
return this.real(path, true, () => realTarget(this.fs, path));
|
|
10134
10526
|
}
|
|
10135
|
-
async real(path, follow2,
|
|
10527
|
+
async real(path, follow2, walk2) {
|
|
10136
10528
|
if (this.fs.realpath) {
|
|
10137
10529
|
try {
|
|
10138
10530
|
return await this.fs.realpath(path, follow2);
|
|
@@ -10140,13 +10532,13 @@ var VfsNodes = class {
|
|
|
10140
10532
|
if (errCode(err) === "ELOOP") throw err;
|
|
10141
10533
|
}
|
|
10142
10534
|
}
|
|
10143
|
-
return
|
|
10535
|
+
return walk2();
|
|
10144
10536
|
}
|
|
10145
10537
|
createExclusive(path, owner) {
|
|
10146
10538
|
return this.onEntry(path, async () => {
|
|
10147
10539
|
const node = this.byPath.get(path);
|
|
10148
10540
|
const held = node ? await node.serial(async () => node.holds()) : false;
|
|
10149
|
-
if (held || await
|
|
10541
|
+
if (held || await present2(this.fs, path)) throw fsError("EEXIST", path);
|
|
10150
10542
|
await this.fs.writeFile(path, new Uint8Array(0), owner);
|
|
10151
10543
|
});
|
|
10152
10544
|
}
|
|
@@ -10206,13 +10598,13 @@ var VfsNodes = class {
|
|
|
10206
10598
|
if (id2 !== void 0) this.know(node, id2);
|
|
10207
10599
|
return node;
|
|
10208
10600
|
}
|
|
10209
|
-
open(path, pin,
|
|
10601
|
+
open(path, pin, writable2 = false, id2) {
|
|
10210
10602
|
const node = this.named(path, id2) ?? this.create(path, id2, new VfsNode(this.fs, path, void 0, false, false, pin));
|
|
10211
10603
|
node.opens++;
|
|
10212
|
-
if (
|
|
10604
|
+
if (writable2) node.writers++;
|
|
10213
10605
|
return node;
|
|
10214
10606
|
}
|
|
10215
|
-
adopt(path, contents2, dirty,
|
|
10607
|
+
adopt(path, contents2, dirty, writable2 = false, id2) {
|
|
10216
10608
|
let node = this.named(path, id2);
|
|
10217
10609
|
if (!node) {
|
|
10218
10610
|
node = this.create(path, id2, new VfsNode(this.fs, path, contents2, false, dirty));
|
|
@@ -10220,7 +10612,7 @@ var VfsNodes = class {
|
|
|
10220
10612
|
node.replace(contents2);
|
|
10221
10613
|
}
|
|
10222
10614
|
node.opens++;
|
|
10223
|
-
if (
|
|
10615
|
+
if (writable2) node.writers++;
|
|
10224
10616
|
return node;
|
|
10225
10617
|
}
|
|
10226
10618
|
holds(prefix) {
|
|
@@ -10232,11 +10624,11 @@ var VfsNodes = class {
|
|
|
10232
10624
|
if (node.writers > 0 && within5(path, prefix)) return true;
|
|
10233
10625
|
return false;
|
|
10234
10626
|
}
|
|
10235
|
-
closed(node,
|
|
10627
|
+
closed(node, writable2 = false) {
|
|
10236
10628
|
node.opens--;
|
|
10237
|
-
if (
|
|
10629
|
+
if (writable2) node.writers--;
|
|
10238
10630
|
if (node.opens > 0) {
|
|
10239
|
-
if (
|
|
10631
|
+
if (writable2 && node.writers === 0) this.onClosed?.(node.path);
|
|
10240
10632
|
return;
|
|
10241
10633
|
}
|
|
10242
10634
|
this.forget(node);
|
|
@@ -10313,7 +10705,7 @@ var VfsNodes = class {
|
|
|
10313
10705
|
}
|
|
10314
10706
|
}
|
|
10315
10707
|
};
|
|
10316
|
-
async function
|
|
10708
|
+
async function present2(fs, path, follow2 = false) {
|
|
10317
10709
|
const look = (follow2 ? void 0 : fs.lstat) ?? fs.stat;
|
|
10318
10710
|
try {
|
|
10319
10711
|
await (look ? look.call(fs, path) : fs.readFileBuffer(path));
|
|
@@ -10327,7 +10719,7 @@ function errCode(err) {
|
|
|
10327
10719
|
return err?.code;
|
|
10328
10720
|
}
|
|
10329
10721
|
async function targetDir(fs, path) {
|
|
10330
|
-
return
|
|
10722
|
+
return parentOf3(await realTarget(fs, path).catch(() => path));
|
|
10331
10723
|
}
|
|
10332
10724
|
async function refuseReadonly(fs, path, flags, opts = {}) {
|
|
10333
10725
|
const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
|
|
@@ -10345,6 +10737,18 @@ async function refuseReadonly(fs, path, flags, opts = {}) {
|
|
|
10345
10737
|
function keepingOpen(fs, nodes) {
|
|
10346
10738
|
return {
|
|
10347
10739
|
...fs,
|
|
10740
|
+
async exclusive(path, run2, follow2 = true) {
|
|
10741
|
+
const at = fs.resolvePath("/", path);
|
|
10742
|
+
const key = follow2 ? nodes.targetKey(at) : nodes.entryKey(at);
|
|
10743
|
+
const locked = await key.catch(() => at);
|
|
10744
|
+
return nodes.onEntry(
|
|
10745
|
+
locked,
|
|
10746
|
+
() => run2({
|
|
10747
|
+
writeFile: (p, content, owner) => fs.writeFile(p, content, owner),
|
|
10748
|
+
direct: locked === at
|
|
10749
|
+
})
|
|
10750
|
+
);
|
|
10751
|
+
},
|
|
10348
10752
|
async rm(path, options) {
|
|
10349
10753
|
const at = await nodes.entryKey(fs.resolvePath("/", path));
|
|
10350
10754
|
await nodes.unlinking(at);
|
|
@@ -10377,10 +10781,10 @@ function keepingOpen(fs, nodes) {
|
|
|
10377
10781
|
};
|
|
10378
10782
|
}
|
|
10379
10783
|
function vfsFile(fs, opts, nodes) {
|
|
10380
|
-
const
|
|
10381
|
-
const
|
|
10382
|
-
const
|
|
10383
|
-
const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true,
|
|
10784
|
+
const access2 = opts.flags & O_ACCMODE;
|
|
10785
|
+
const readable2 = access2 !== O_WRONLY;
|
|
10786
|
+
const writable2 = access2 === O_WRONLY || access2 === O_RDWR;
|
|
10787
|
+
const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true, writable2, opts.id) : nodes.open(opts.path, opts.pin, writable2, opts.id);
|
|
10384
10788
|
let openError;
|
|
10385
10789
|
const atOpen = (op) => {
|
|
10386
10790
|
void node.serial(op).catch((err) => {
|
|
@@ -10401,12 +10805,12 @@ function vfsFile(fs, opts, nodes) {
|
|
|
10401
10805
|
return op();
|
|
10402
10806
|
});
|
|
10403
10807
|
return new OpenFile({
|
|
10404
|
-
read:
|
|
10808
|
+
read: readable2 ? (max) => serial(async () => {
|
|
10405
10809
|
const out = await node.pread(max, offset);
|
|
10406
10810
|
offset += out.length;
|
|
10407
10811
|
return out;
|
|
10408
10812
|
}) : void 0,
|
|
10409
|
-
write:
|
|
10813
|
+
write: writable2 ? (bytes) => serial(async () => {
|
|
10410
10814
|
if (opts.flags & O_APPEND) offset = await node.size();
|
|
10411
10815
|
offset += await node.pwrite(bytes, offset);
|
|
10412
10816
|
return bytes.length;
|
|
@@ -10421,15 +10825,15 @@ function vfsFile(fs, opts, nodes) {
|
|
|
10421
10825
|
return offset;
|
|
10422
10826
|
}),
|
|
10423
10827
|
pread: (max, at) => serial(() => {
|
|
10424
|
-
if (!
|
|
10828
|
+
if (!readable2) throw new KernelError("EBADF");
|
|
10425
10829
|
return node.pread(max, at);
|
|
10426
10830
|
}),
|
|
10427
10831
|
pwrite: (bytes, at) => serial(() => {
|
|
10428
|
-
if (!
|
|
10832
|
+
if (!writable2) throw new KernelError("EBADF");
|
|
10429
10833
|
return node.pwrite(bytes, at);
|
|
10430
10834
|
}),
|
|
10431
10835
|
resize: (size) => serial(() => {
|
|
10432
|
-
if (!
|
|
10836
|
+
if (!writable2) throw new KernelError("EBADF");
|
|
10433
10837
|
return node.truncate(size);
|
|
10434
10838
|
}),
|
|
10435
10839
|
stat: () => serial(async () => ({
|
|
@@ -10437,13 +10841,14 @@ function vfsFile(fs, opts, nodes) {
|
|
|
10437
10841
|
size: await node.size(),
|
|
10438
10842
|
...node.orphaned ? { orphan: true } : {}
|
|
10439
10843
|
})),
|
|
10844
|
+
known: () => node.known(),
|
|
10440
10845
|
flush: () => serial(() => node.flush()),
|
|
10441
10846
|
close: () => node.serial(async () => {
|
|
10442
10847
|
try {
|
|
10443
10848
|
takeOpenError();
|
|
10444
10849
|
await node.flush();
|
|
10445
10850
|
} finally {
|
|
10446
|
-
nodes?.closed(node,
|
|
10851
|
+
nodes?.closed(node, writable2);
|
|
10447
10852
|
}
|
|
10448
10853
|
})
|
|
10449
10854
|
});
|
|
@@ -10814,7 +11219,7 @@ var WasmProcess = class {
|
|
|
10814
11219
|
this.fds.installAt(fd, this.openVfsFile(open));
|
|
10815
11220
|
} else if (req.create) {
|
|
10816
11221
|
await nodes.onEntry(path, async () => {
|
|
10817
|
-
const fresh = this.options.owners && !await
|
|
11222
|
+
const fresh = this.options.owners && !await present2(this.options.fs, path, true);
|
|
10818
11223
|
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
10819
11224
|
await this.fds.get(fd).file.stat?.();
|
|
10820
11225
|
if (fresh) await this.options.fs.chown?.(path, { ...this.newOwner(), kind: "new" });
|
|
@@ -10822,7 +11227,7 @@ var WasmProcess = class {
|
|
|
10822
11227
|
} else {
|
|
10823
11228
|
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
10824
11229
|
}
|
|
10825
|
-
if (req.existing && !await
|
|
11230
|
+
if (req.existing && !await present2(this.options.fs, path, true)) {
|
|
10826
11231
|
throw new KernelError("ENOENT");
|
|
10827
11232
|
}
|
|
10828
11233
|
} catch (err) {
|
|
@@ -10831,6 +11236,20 @@ var WasmProcess = class {
|
|
|
10831
11236
|
}
|
|
10832
11237
|
return fd;
|
|
10833
11238
|
}
|
|
11239
|
+
async identified(stat, known) {
|
|
11240
|
+
const first = await stat();
|
|
11241
|
+
if (first.orphan) return first;
|
|
11242
|
+
const ino = known?.();
|
|
11243
|
+
if (ino !== void 0) return { ...first, ino };
|
|
11244
|
+
const { fs } = this.options;
|
|
11245
|
+
const look = async () => {
|
|
11246
|
+
const now = await stat();
|
|
11247
|
+
if (now.path !== first.path || now.orphan) return now;
|
|
11248
|
+
const st = await fs.stat?.(now.path).catch(() => void 0);
|
|
11249
|
+
return st?.ino === void 0 ? now : { ...now, ino: st.ino };
|
|
11250
|
+
};
|
|
11251
|
+
return fs.exclusive ? fs.exclusive(first.path, look) : look();
|
|
11252
|
+
}
|
|
10834
11253
|
newOwner() {
|
|
10835
11254
|
if (!this.options.owners) return void 0;
|
|
10836
11255
|
return { uid: this.cred.euid, gid: this.cred.egid, mode: 438 & ~this.umask };
|
|
@@ -10842,14 +11261,14 @@ var WasmProcess = class {
|
|
|
10842
11261
|
if (req.flags & WRITE_ACCESS || req.create || req.truncate || req.exclusive) {
|
|
10843
11262
|
throw new KernelError("EROFS");
|
|
10844
11263
|
}
|
|
10845
|
-
const
|
|
11264
|
+
const tree2 = new ProcTree(
|
|
10846
11265
|
{
|
|
10847
11266
|
procList: () => this.options.processes?.() ?? { boot: 0, processes: [] },
|
|
10848
11267
|
mountList: () => this.options.mounts?.() ?? []
|
|
10849
11268
|
},
|
|
10850
11269
|
(await this.options.identity?.())?.pid ?? this.pid
|
|
10851
11270
|
);
|
|
10852
|
-
return memoryFile(
|
|
11271
|
+
return memoryFile(tree2.read(path), path, req.position);
|
|
10853
11272
|
}
|
|
10854
11273
|
openVfsFile(req, id2) {
|
|
10855
11274
|
return vfsFile(
|
|
@@ -10992,7 +11411,11 @@ var WasmProcess = class {
|
|
|
10992
11411
|
await file.resize(req.size);
|
|
10993
11412
|
return { ok: true, kind: "void" };
|
|
10994
11413
|
case "fd-vfs-stat":
|
|
10995
|
-
return {
|
|
11414
|
+
return {
|
|
11415
|
+
ok: true,
|
|
11416
|
+
kind: "json",
|
|
11417
|
+
json: req.identity ? await this.identified(file.stat.bind(file), file.known?.bind(file)) : await file.stat()
|
|
11418
|
+
};
|
|
10996
11419
|
}
|
|
10997
11420
|
}
|
|
10998
11421
|
keptPath;
|
|
@@ -11001,7 +11424,7 @@ var WasmProcess = class {
|
|
|
11001
11424
|
const held = handle === void 0 ? void 0 : this.keptHandle?.(handle);
|
|
11002
11425
|
if (held !== void 0) return held;
|
|
11003
11426
|
const kept = this.keptPath?.(path);
|
|
11004
|
-
return kept !== void 0 && !await
|
|
11427
|
+
return kept !== void 0 && !await present2(this.options.fs, path, true) ? kept : path;
|
|
11005
11428
|
}
|
|
11006
11429
|
async promote(req) {
|
|
11007
11430
|
if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
|
|
@@ -11511,6 +11934,7 @@ function inherited(opts) {
|
|
|
11511
11934
|
function holdingToken(opts, holds, caller) {
|
|
11512
11935
|
return mintSyncFsToken({
|
|
11513
11936
|
...opts.owners ? caller : {},
|
|
11937
|
+
...opts.dac ? { dac: opts.dac, pid: opts.pid } : {},
|
|
11514
11938
|
fs: opts.fs,
|
|
11515
11939
|
cwd: opts.cwd,
|
|
11516
11940
|
...opts.statfs ? { statfs: opts.statfs } : {},
|
|
@@ -12140,9 +12564,9 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
|
12140
12564
|
let open = false;
|
|
12141
12565
|
const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
|
|
12142
12566
|
async function hello(message3) {
|
|
12143
|
-
const
|
|
12144
|
-
if (
|
|
12145
|
-
reply({ hello: { protocol: DRIVER_PROTOCOL, error:
|
|
12567
|
+
const refused2 = driverVersionError(message3.protocol);
|
|
12568
|
+
if (refused2) {
|
|
12569
|
+
reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused2, errno: "EPROTO" } });
|
|
12146
12570
|
return;
|
|
12147
12571
|
}
|
|
12148
12572
|
try {
|
|
@@ -12684,7 +13108,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
12684
13108
|
}, againMs);
|
|
12685
13109
|
timer.unref?.();
|
|
12686
13110
|
});
|
|
12687
|
-
async function
|
|
13111
|
+
async function writable2(client, path, flags) {
|
|
12688
13112
|
const { ifMatch: _, ...asked } = flags;
|
|
12689
13113
|
const opening = () => client.call("open", { path: rel(path), ...asked });
|
|
12690
13114
|
try {
|
|
@@ -12715,7 +13139,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
12715
13139
|
async open(path, flags) {
|
|
12716
13140
|
const client = current();
|
|
12717
13141
|
const token = client.token;
|
|
12718
|
-
const { opened: opened2, restore } = await
|
|
13142
|
+
const { opened: opened2, restore } = await writable2(client, path, flags);
|
|
12719
13143
|
if (flags.ifMatch !== void 0 && opened2.attr?.etag !== flags.ifMatch) {
|
|
12720
13144
|
await client.call("release", { fh: opened2.fh }, true);
|
|
12721
13145
|
throw fsError2("ESTALE", `${path} changed on the host while it was being read`);
|
|
@@ -12888,7 +13312,7 @@ var PORT_DEAD_MS = 1e4;
|
|
|
12888
13312
|
var PORT_LINGER_MS = 1e3;
|
|
12889
13313
|
function timed(transport, ms) {
|
|
12890
13314
|
const inflight = /* @__PURE__ */ new Set();
|
|
12891
|
-
const
|
|
13315
|
+
const guard2 = (name2, run2, late, expire) => new Promise((resolve, reject) => {
|
|
12892
13316
|
let expired = false;
|
|
12893
13317
|
const running = run2();
|
|
12894
13318
|
inflight.add(running);
|
|
@@ -12937,15 +13361,15 @@ function timed(transport, ms) {
|
|
|
12937
13361
|
},
|
|
12938
13362
|
retire: (why) => transport.retire?.(why),
|
|
12939
13363
|
...transport.cleanup ? { cleanup } : {},
|
|
12940
|
-
call: (op, body, handle) =>
|
|
13364
|
+
call: (op, body, handle) => guard2(
|
|
12941
13365
|
op,
|
|
12942
13366
|
() => transport.call(op, body, handle),
|
|
12943
13367
|
op === "open" ? release : void 0,
|
|
12944
13368
|
op === "release" ? (running) => void cleanup("release", body ?? {}, running).catch(() => {
|
|
12945
13369
|
}) : void 0
|
|
12946
13370
|
),
|
|
12947
|
-
read: (fh, offset, size, ifMatch) =>
|
|
12948
|
-
write: (fh, offset, bytes) =>
|
|
13371
|
+
read: (fh, offset, size, ifMatch) => guard2("read", () => transport.read(fh, offset, size, ifMatch)),
|
|
13372
|
+
write: (fh, offset, bytes) => guard2("write", () => transport.write(fh, offset, bytes)),
|
|
12949
13373
|
watch: (signal, onEvent, onOpen) => transport.watch(signal, onEvent, onOpen)
|
|
12950
13374
|
};
|
|
12951
13375
|
}
|
|
@@ -13223,10 +13647,10 @@ var ChunkedBytes = class {
|
|
|
13223
13647
|
}
|
|
13224
13648
|
this.size = size;
|
|
13225
13649
|
}
|
|
13226
|
-
room(index,
|
|
13650
|
+
room(index, need2) {
|
|
13227
13651
|
const chunk2 = this.chunks[index];
|
|
13228
|
-
if (chunk2 && chunk2.length >=
|
|
13229
|
-
const grown = new Uint8Array(Math.min(CHUNK3, Math.max(
|
|
13652
|
+
if (chunk2 && chunk2.length >= need2) return chunk2;
|
|
13653
|
+
const grown = new Uint8Array(Math.min(CHUNK3, Math.max(need2, (chunk2?.length ?? 0) * 2)));
|
|
13230
13654
|
if (chunk2) grown.set(chunk2);
|
|
13231
13655
|
this.chunks[index] = grown;
|
|
13232
13656
|
return grown;
|
|
@@ -13273,14 +13697,14 @@ function tmpfs() {
|
|
|
13273
13697
|
const root = make("directory", 16877);
|
|
13274
13698
|
const handles = /* @__PURE__ */ new Map();
|
|
13275
13699
|
let nextFh = 0;
|
|
13276
|
-
const
|
|
13700
|
+
const parts2 = (path) => path.split("/").filter(Boolean);
|
|
13277
13701
|
const place = (dir, name2, node) => {
|
|
13278
13702
|
dir.children.set(name2, node);
|
|
13279
13703
|
dir.mtime = Date.now();
|
|
13280
13704
|
};
|
|
13281
13705
|
function find(path) {
|
|
13282
13706
|
let node = root;
|
|
13283
|
-
for (const name2 of
|
|
13707
|
+
for (const name2 of parts2(path)) {
|
|
13284
13708
|
if (node.kind !== "directory") throw fsError2("ENOTDIR", path);
|
|
13285
13709
|
const next2 = node.children.get(name2);
|
|
13286
13710
|
if (!next2) throw fsError2("ENOENT", path);
|
|
@@ -13289,7 +13713,7 @@ function tmpfs() {
|
|
|
13289
13713
|
return node;
|
|
13290
13714
|
}
|
|
13291
13715
|
function parent(path) {
|
|
13292
|
-
const names =
|
|
13716
|
+
const names = parts2(path);
|
|
13293
13717
|
const name2 = names.pop();
|
|
13294
13718
|
if (name2 === void 0) throw fsError2("EBUSY", "the root of a mount");
|
|
13295
13719
|
const dir = find(`/${names.join("/")}`);
|
|
@@ -13374,8 +13798,8 @@ function tmpfs() {
|
|
|
13374
13798
|
dir.mtime = Date.now();
|
|
13375
13799
|
},
|
|
13376
13800
|
async rename(from, to) {
|
|
13377
|
-
const a = `/${
|
|
13378
|
-
const b = `/${
|
|
13801
|
+
const a = `/${parts2(from).join("/")}`;
|
|
13802
|
+
const b = `/${parts2(to).join("/")}`;
|
|
13379
13803
|
if (b !== a && b.startsWith(`${a}/`)) throw fsError2("EINVAL", `${to} is inside ${from}`);
|
|
13380
13804
|
const source = parent(from);
|
|
13381
13805
|
const node = source.dir.children.get(source.name);
|
|
@@ -13734,11 +14158,13 @@ var Launcher = class {
|
|
|
13734
14158
|
booting = new AbortController();
|
|
13735
14159
|
fstab = Promise.resolve([]);
|
|
13736
14160
|
usersMode;
|
|
14161
|
+
dac;
|
|
13737
14162
|
nextPid = 1e3;
|
|
13738
14163
|
boot = Date.now();
|
|
13739
14164
|
terminals = 0;
|
|
13740
14165
|
constructor(options) {
|
|
13741
14166
|
this.usersMode = usersMode(options.users);
|
|
14167
|
+
this.dac = this.usersMode === "off" ? void 0 : new Dac(this.usersMode, options.onDenial);
|
|
13742
14168
|
if (this.owners) options.fs.trackOwners?.();
|
|
13743
14169
|
this.mounts = new MountTable({
|
|
13744
14170
|
open: (type, spec) => this.driver(type, spec),
|
|
@@ -14069,6 +14495,7 @@ var Launcher = class {
|
|
|
14069
14495
|
...req.umask !== void 0 ? { umask: req.umask } : {},
|
|
14070
14496
|
cred: req.cred,
|
|
14071
14497
|
...this.owners ? { owners: true } : {},
|
|
14498
|
+
...this.dac ? { dac: this.dac } : {},
|
|
14072
14499
|
program: req.program,
|
|
14073
14500
|
argv0: req.argv0,
|
|
14074
14501
|
args: req.args,
|
|
@@ -14670,10 +15097,11 @@ function usersOp(l, req) {
|
|
|
14670
15097
|
if (req.op === "groups-remove-member") return l.users.removeMember(req.group, req.user);
|
|
14671
15098
|
return l.users.list();
|
|
14672
15099
|
}
|
|
14673
|
-
function hostOptions(req) {
|
|
15100
|
+
function hostOptions(req, port) {
|
|
14674
15101
|
return {
|
|
14675
15102
|
...req.hostname !== void 0 ? { hostname: req.hostname } : {},
|
|
14676
|
-
...req.users !== void 0 ? { users: req.users } : {}
|
|
15103
|
+
...req.users !== void 0 ? { users: req.users } : {},
|
|
15104
|
+
...req.denials ? { onDenial: (denial) => port.postMessage({ denial }) } : {}
|
|
14677
15105
|
};
|
|
14678
15106
|
}
|
|
14679
15107
|
function serveKernel(port, deps) {
|
|
@@ -14750,7 +15178,7 @@ function serveKernel(port, deps) {
|
|
|
14750
15178
|
...deps.createDriverWorker ? { createDriverWorker: deps.createDriverWorker } : {},
|
|
14751
15179
|
...req.modules ? { modules: req.modules } : {},
|
|
14752
15180
|
...req.env ? { env: req.env } : {},
|
|
14753
|
-
...hostOptions(req),
|
|
15181
|
+
...hostOptions(req, port),
|
|
14754
15182
|
...net,
|
|
14755
15183
|
...media ? { media } : {},
|
|
14756
15184
|
onMountPending: (medium) => port.postMessage({ medium }),
|