@ai-ecoverse/slicc-kernel 1.61.2 → 1.62.0
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 +19 -5
- package/dist/index.js +12 -9
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +616 -260
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +14 -4
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +711 -355
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +14 -4
- 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,6 +8305,16 @@ 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
|
+
}
|
|
7961
8318
|
async function dispatchSyncFs(req) {
|
|
7962
8319
|
const entry = resolveSyncFsToken(req.token);
|
|
7963
8320
|
if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
|
|
@@ -7966,16 +8323,10 @@ async function dispatchSyncFs(req) {
|
|
|
7966
8323
|
const path = fs.resolvePath(cwd, req.path);
|
|
7967
8324
|
if (req.op === "kept") return json2(entry.keptFor?.(req.handle ?? -1) !== void 0);
|
|
7968
8325
|
if (req.op === "stat" && req.handle !== void 0) return await keptStat(entry, req);
|
|
8326
|
+
if (req.op === "hold") return holdOp(entry, path, req);
|
|
8327
|
+
const refused2 = await guarded(entry, path, req);
|
|
8328
|
+
if (refused2) return refused2;
|
|
7969
8329
|
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
8330
|
if (entry.revoked?.(path)) return { ok: false, errno: "EIO", message: `${path} was unmounted` };
|
|
7980
8331
|
if (req.op === "write" && req.mode === 1 || UNLINKED_OPS.has(req.op)) {
|
|
7981
8332
|
const serve = async () => run(entry, await keptOr(entry, path, req.handle), req);
|
|
@@ -8149,9 +8500,9 @@ function mountTable(mounts = []) {
|
|
|
8149
8500
|
for (const m of mounts) {
|
|
8150
8501
|
const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
|
|
8151
8502
|
if (m.state !== "ok") options.push(m.state);
|
|
8152
|
-
const
|
|
8503
|
+
const access2 = options.includes("ro") ? [] : ["rw"];
|
|
8153
8504
|
lines2.push(
|
|
8154
|
-
`${field2(m.source)} ${field2(m.target)} ${m.type} ${[...
|
|
8505
|
+
`${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
|
|
8155
8506
|
);
|
|
8156
8507
|
}
|
|
8157
8508
|
return `${lines2.join("\n")}
|
|
@@ -8587,8 +8938,8 @@ var ProcTree = class {
|
|
|
8587
8938
|
alive(pid) {
|
|
8588
8939
|
return this.listing().processes.some((p) => p.pid === pid);
|
|
8589
8940
|
}
|
|
8590
|
-
node(
|
|
8591
|
-
const [top, leaf, ...rest2] =
|
|
8941
|
+
node(parts2) {
|
|
8942
|
+
const [top, leaf, ...rest2] = parts2;
|
|
8592
8943
|
if (top === void 0) {
|
|
8593
8944
|
return {
|
|
8594
8945
|
kind: "dir",
|
|
@@ -8626,9 +8977,9 @@ var ProcTree = class {
|
|
|
8626
8977
|
};
|
|
8627
8978
|
}
|
|
8628
8979
|
parts(path, follow2) {
|
|
8629
|
-
const
|
|
8630
|
-
if (
|
|
8631
|
-
return
|
|
8980
|
+
const parts2 = path.split("/").filter((p) => p !== "").slice(1);
|
|
8981
|
+
if (parts2[0] === "self" && (follow2 || parts2.length > 1)) parts2[0] = String(this.pid);
|
|
8982
|
+
return parts2;
|
|
8632
8983
|
}
|
|
8633
8984
|
find(path, follow2) {
|
|
8634
8985
|
const node = this.node(this.parts(path, follow2));
|
|
@@ -8714,8 +9065,8 @@ function refusalOf(found) {
|
|
|
8714
9065
|
return found === false ? { refused: "ENOTDIR" } : found;
|
|
8715
9066
|
}
|
|
8716
9067
|
function throughLink(target, done4, todo) {
|
|
8717
|
-
const
|
|
8718
|
-
return [target.startsWith("/") ? [] : done4, [...
|
|
9068
|
+
const parts2 = target.split("/").filter((part) => part !== "");
|
|
9069
|
+
return [target.startsWith("/") ? [] : done4, [...parts2, ...todo]];
|
|
8719
9070
|
}
|
|
8720
9071
|
async function procAlias(fs, path, follow2) {
|
|
8721
9072
|
const { readlink, lstat } = fs;
|
|
@@ -8725,14 +9076,14 @@ async function procAlias(fs, path, follow2) {
|
|
|
8725
9076
|
() => false
|
|
8726
9077
|
);
|
|
8727
9078
|
if (!physical) return void 0;
|
|
8728
|
-
const
|
|
8729
|
-
let step =
|
|
9079
|
+
const walk2 = procWalk(path, follow2);
|
|
9080
|
+
let step = walk2.next();
|
|
8730
9081
|
while (!step.done) {
|
|
8731
9082
|
const { at, check } = step.value;
|
|
8732
9083
|
const link2 = await readlink.call(fs, at).catch(() => void 0);
|
|
8733
9084
|
const isDir = (st) => st.isDirectory === true;
|
|
8734
9085
|
const target = link2 ?? (check ? await lstat.call(fs, at).then(isDir, failed) : true);
|
|
8735
|
-
step =
|
|
9086
|
+
step = walk2.next(target);
|
|
8736
9087
|
}
|
|
8737
9088
|
return step.value;
|
|
8738
9089
|
}
|
|
@@ -10079,10 +10430,10 @@ var VfsNode = class {
|
|
|
10079
10430
|
this.serial(() => this.flush()).catch(() => void 0);
|
|
10080
10431
|
}, delay);
|
|
10081
10432
|
}
|
|
10082
|
-
ensure(
|
|
10433
|
+
ensure(need2) {
|
|
10083
10434
|
const cur = this.data ?? new Uint8Array(0);
|
|
10084
|
-
if (cur.length >=
|
|
10085
|
-
const grown = new Uint8Array(Math.max(
|
|
10435
|
+
if (cur.length >= need2) return cur;
|
|
10436
|
+
const grown = new Uint8Array(Math.max(need2, cur.length * 2, 256));
|
|
10086
10437
|
grown.set(cur.subarray(0, this.length));
|
|
10087
10438
|
this.data = grown;
|
|
10088
10439
|
return grown;
|
|
@@ -10125,14 +10476,14 @@ var VfsNodes = class {
|
|
|
10125
10476
|
return run2;
|
|
10126
10477
|
}
|
|
10127
10478
|
async entryKey(path) {
|
|
10128
|
-
const parent =
|
|
10479
|
+
const parent = parentOf3(path);
|
|
10129
10480
|
const dir = await this.real(parent, true, () => realDir(this.fs, parent));
|
|
10130
10481
|
return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
|
|
10131
10482
|
}
|
|
10132
10483
|
targetKey(path) {
|
|
10133
10484
|
return this.real(path, true, () => realTarget(this.fs, path));
|
|
10134
10485
|
}
|
|
10135
|
-
async real(path, follow2,
|
|
10486
|
+
async real(path, follow2, walk2) {
|
|
10136
10487
|
if (this.fs.realpath) {
|
|
10137
10488
|
try {
|
|
10138
10489
|
return await this.fs.realpath(path, follow2);
|
|
@@ -10140,13 +10491,13 @@ var VfsNodes = class {
|
|
|
10140
10491
|
if (errCode(err) === "ELOOP") throw err;
|
|
10141
10492
|
}
|
|
10142
10493
|
}
|
|
10143
|
-
return
|
|
10494
|
+
return walk2();
|
|
10144
10495
|
}
|
|
10145
10496
|
createExclusive(path, owner) {
|
|
10146
10497
|
return this.onEntry(path, async () => {
|
|
10147
10498
|
const node = this.byPath.get(path);
|
|
10148
10499
|
const held = node ? await node.serial(async () => node.holds()) : false;
|
|
10149
|
-
if (held || await
|
|
10500
|
+
if (held || await present2(this.fs, path)) throw fsError("EEXIST", path);
|
|
10150
10501
|
await this.fs.writeFile(path, new Uint8Array(0), owner);
|
|
10151
10502
|
});
|
|
10152
10503
|
}
|
|
@@ -10206,13 +10557,13 @@ var VfsNodes = class {
|
|
|
10206
10557
|
if (id2 !== void 0) this.know(node, id2);
|
|
10207
10558
|
return node;
|
|
10208
10559
|
}
|
|
10209
|
-
open(path, pin,
|
|
10560
|
+
open(path, pin, writable2 = false, id2) {
|
|
10210
10561
|
const node = this.named(path, id2) ?? this.create(path, id2, new VfsNode(this.fs, path, void 0, false, false, pin));
|
|
10211
10562
|
node.opens++;
|
|
10212
|
-
if (
|
|
10563
|
+
if (writable2) node.writers++;
|
|
10213
10564
|
return node;
|
|
10214
10565
|
}
|
|
10215
|
-
adopt(path, contents2, dirty,
|
|
10566
|
+
adopt(path, contents2, dirty, writable2 = false, id2) {
|
|
10216
10567
|
let node = this.named(path, id2);
|
|
10217
10568
|
if (!node) {
|
|
10218
10569
|
node = this.create(path, id2, new VfsNode(this.fs, path, contents2, false, dirty));
|
|
@@ -10220,7 +10571,7 @@ var VfsNodes = class {
|
|
|
10220
10571
|
node.replace(contents2);
|
|
10221
10572
|
}
|
|
10222
10573
|
node.opens++;
|
|
10223
|
-
if (
|
|
10574
|
+
if (writable2) node.writers++;
|
|
10224
10575
|
return node;
|
|
10225
10576
|
}
|
|
10226
10577
|
holds(prefix) {
|
|
@@ -10232,11 +10583,11 @@ var VfsNodes = class {
|
|
|
10232
10583
|
if (node.writers > 0 && within5(path, prefix)) return true;
|
|
10233
10584
|
return false;
|
|
10234
10585
|
}
|
|
10235
|
-
closed(node,
|
|
10586
|
+
closed(node, writable2 = false) {
|
|
10236
10587
|
node.opens--;
|
|
10237
|
-
if (
|
|
10588
|
+
if (writable2) node.writers--;
|
|
10238
10589
|
if (node.opens > 0) {
|
|
10239
|
-
if (
|
|
10590
|
+
if (writable2 && node.writers === 0) this.onClosed?.(node.path);
|
|
10240
10591
|
return;
|
|
10241
10592
|
}
|
|
10242
10593
|
this.forget(node);
|
|
@@ -10313,7 +10664,7 @@ var VfsNodes = class {
|
|
|
10313
10664
|
}
|
|
10314
10665
|
}
|
|
10315
10666
|
};
|
|
10316
|
-
async function
|
|
10667
|
+
async function present2(fs, path, follow2 = false) {
|
|
10317
10668
|
const look = (follow2 ? void 0 : fs.lstat) ?? fs.stat;
|
|
10318
10669
|
try {
|
|
10319
10670
|
await (look ? look.call(fs, path) : fs.readFileBuffer(path));
|
|
@@ -10327,7 +10678,7 @@ function errCode(err) {
|
|
|
10327
10678
|
return err?.code;
|
|
10328
10679
|
}
|
|
10329
10680
|
async function targetDir(fs, path) {
|
|
10330
|
-
return
|
|
10681
|
+
return parentOf3(await realTarget(fs, path).catch(() => path));
|
|
10331
10682
|
}
|
|
10332
10683
|
async function refuseReadonly(fs, path, flags, opts = {}) {
|
|
10333
10684
|
const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
|
|
@@ -10377,10 +10728,10 @@ function keepingOpen(fs, nodes) {
|
|
|
10377
10728
|
};
|
|
10378
10729
|
}
|
|
10379
10730
|
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,
|
|
10731
|
+
const access2 = opts.flags & O_ACCMODE;
|
|
10732
|
+
const readable2 = access2 !== O_WRONLY;
|
|
10733
|
+
const writable2 = access2 === O_WRONLY || access2 === O_RDWR;
|
|
10734
|
+
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
10735
|
let openError;
|
|
10385
10736
|
const atOpen = (op) => {
|
|
10386
10737
|
void node.serial(op).catch((err) => {
|
|
@@ -10401,12 +10752,12 @@ function vfsFile(fs, opts, nodes) {
|
|
|
10401
10752
|
return op();
|
|
10402
10753
|
});
|
|
10403
10754
|
return new OpenFile({
|
|
10404
|
-
read:
|
|
10755
|
+
read: readable2 ? (max) => serial(async () => {
|
|
10405
10756
|
const out = await node.pread(max, offset);
|
|
10406
10757
|
offset += out.length;
|
|
10407
10758
|
return out;
|
|
10408
10759
|
}) : void 0,
|
|
10409
|
-
write:
|
|
10760
|
+
write: writable2 ? (bytes) => serial(async () => {
|
|
10410
10761
|
if (opts.flags & O_APPEND) offset = await node.size();
|
|
10411
10762
|
offset += await node.pwrite(bytes, offset);
|
|
10412
10763
|
return bytes.length;
|
|
@@ -10421,15 +10772,15 @@ function vfsFile(fs, opts, nodes) {
|
|
|
10421
10772
|
return offset;
|
|
10422
10773
|
}),
|
|
10423
10774
|
pread: (max, at) => serial(() => {
|
|
10424
|
-
if (!
|
|
10775
|
+
if (!readable2) throw new KernelError("EBADF");
|
|
10425
10776
|
return node.pread(max, at);
|
|
10426
10777
|
}),
|
|
10427
10778
|
pwrite: (bytes, at) => serial(() => {
|
|
10428
|
-
if (!
|
|
10779
|
+
if (!writable2) throw new KernelError("EBADF");
|
|
10429
10780
|
return node.pwrite(bytes, at);
|
|
10430
10781
|
}),
|
|
10431
10782
|
resize: (size) => serial(() => {
|
|
10432
|
-
if (!
|
|
10783
|
+
if (!writable2) throw new KernelError("EBADF");
|
|
10433
10784
|
return node.truncate(size);
|
|
10434
10785
|
}),
|
|
10435
10786
|
stat: () => serial(async () => ({
|
|
@@ -10443,7 +10794,7 @@ function vfsFile(fs, opts, nodes) {
|
|
|
10443
10794
|
takeOpenError();
|
|
10444
10795
|
await node.flush();
|
|
10445
10796
|
} finally {
|
|
10446
|
-
nodes?.closed(node,
|
|
10797
|
+
nodes?.closed(node, writable2);
|
|
10447
10798
|
}
|
|
10448
10799
|
})
|
|
10449
10800
|
});
|
|
@@ -10814,7 +11165,7 @@ var WasmProcess = class {
|
|
|
10814
11165
|
this.fds.installAt(fd, this.openVfsFile(open));
|
|
10815
11166
|
} else if (req.create) {
|
|
10816
11167
|
await nodes.onEntry(path, async () => {
|
|
10817
|
-
const fresh = this.options.owners && !await
|
|
11168
|
+
const fresh = this.options.owners && !await present2(this.options.fs, path, true);
|
|
10818
11169
|
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
10819
11170
|
await this.fds.get(fd).file.stat?.();
|
|
10820
11171
|
if (fresh) await this.options.fs.chown?.(path, { ...this.newOwner(), kind: "new" });
|
|
@@ -10822,7 +11173,7 @@ var WasmProcess = class {
|
|
|
10822
11173
|
} else {
|
|
10823
11174
|
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
10824
11175
|
}
|
|
10825
|
-
if (req.existing && !await
|
|
11176
|
+
if (req.existing && !await present2(this.options.fs, path, true)) {
|
|
10826
11177
|
throw new KernelError("ENOENT");
|
|
10827
11178
|
}
|
|
10828
11179
|
} catch (err) {
|
|
@@ -10842,14 +11193,14 @@ var WasmProcess = class {
|
|
|
10842
11193
|
if (req.flags & WRITE_ACCESS || req.create || req.truncate || req.exclusive) {
|
|
10843
11194
|
throw new KernelError("EROFS");
|
|
10844
11195
|
}
|
|
10845
|
-
const
|
|
11196
|
+
const tree2 = new ProcTree(
|
|
10846
11197
|
{
|
|
10847
11198
|
procList: () => this.options.processes?.() ?? { boot: 0, processes: [] },
|
|
10848
11199
|
mountList: () => this.options.mounts?.() ?? []
|
|
10849
11200
|
},
|
|
10850
11201
|
(await this.options.identity?.())?.pid ?? this.pid
|
|
10851
11202
|
);
|
|
10852
|
-
return memoryFile(
|
|
11203
|
+
return memoryFile(tree2.read(path), path, req.position);
|
|
10853
11204
|
}
|
|
10854
11205
|
openVfsFile(req, id2) {
|
|
10855
11206
|
return vfsFile(
|
|
@@ -11001,7 +11352,7 @@ var WasmProcess = class {
|
|
|
11001
11352
|
const held = handle === void 0 ? void 0 : this.keptHandle?.(handle);
|
|
11002
11353
|
if (held !== void 0) return held;
|
|
11003
11354
|
const kept = this.keptPath?.(path);
|
|
11004
|
-
return kept !== void 0 && !await
|
|
11355
|
+
return kept !== void 0 && !await present2(this.options.fs, path, true) ? kept : path;
|
|
11005
11356
|
}
|
|
11006
11357
|
async promote(req) {
|
|
11007
11358
|
if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
|
|
@@ -11511,6 +11862,7 @@ function inherited(opts) {
|
|
|
11511
11862
|
function holdingToken(opts, holds, caller) {
|
|
11512
11863
|
return mintSyncFsToken({
|
|
11513
11864
|
...opts.owners ? caller : {},
|
|
11865
|
+
...opts.dac ? { dac: opts.dac, pid: opts.pid } : {},
|
|
11514
11866
|
fs: opts.fs,
|
|
11515
11867
|
cwd: opts.cwd,
|
|
11516
11868
|
...opts.statfs ? { statfs: opts.statfs } : {},
|
|
@@ -12140,9 +12492,9 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
|
12140
12492
|
let open = false;
|
|
12141
12493
|
const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
|
|
12142
12494
|
async function hello(message3) {
|
|
12143
|
-
const
|
|
12144
|
-
if (
|
|
12145
|
-
reply({ hello: { protocol: DRIVER_PROTOCOL, error:
|
|
12495
|
+
const refused2 = driverVersionError(message3.protocol);
|
|
12496
|
+
if (refused2) {
|
|
12497
|
+
reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused2, errno: "EPROTO" } });
|
|
12146
12498
|
return;
|
|
12147
12499
|
}
|
|
12148
12500
|
try {
|
|
@@ -12684,7 +13036,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
12684
13036
|
}, againMs);
|
|
12685
13037
|
timer.unref?.();
|
|
12686
13038
|
});
|
|
12687
|
-
async function
|
|
13039
|
+
async function writable2(client, path, flags) {
|
|
12688
13040
|
const { ifMatch: _, ...asked } = flags;
|
|
12689
13041
|
const opening = () => client.call("open", { path: rel(path), ...asked });
|
|
12690
13042
|
try {
|
|
@@ -12715,7 +13067,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
12715
13067
|
async open(path, flags) {
|
|
12716
13068
|
const client = current();
|
|
12717
13069
|
const token = client.token;
|
|
12718
|
-
const { opened: opened2, restore } = await
|
|
13070
|
+
const { opened: opened2, restore } = await writable2(client, path, flags);
|
|
12719
13071
|
if (flags.ifMatch !== void 0 && opened2.attr?.etag !== flags.ifMatch) {
|
|
12720
13072
|
await client.call("release", { fh: opened2.fh }, true);
|
|
12721
13073
|
throw fsError2("ESTALE", `${path} changed on the host while it was being read`);
|
|
@@ -12888,7 +13240,7 @@ var PORT_DEAD_MS = 1e4;
|
|
|
12888
13240
|
var PORT_LINGER_MS = 1e3;
|
|
12889
13241
|
function timed(transport, ms) {
|
|
12890
13242
|
const inflight = /* @__PURE__ */ new Set();
|
|
12891
|
-
const
|
|
13243
|
+
const guard2 = (name2, run2, late, expire) => new Promise((resolve, reject) => {
|
|
12892
13244
|
let expired = false;
|
|
12893
13245
|
const running = run2();
|
|
12894
13246
|
inflight.add(running);
|
|
@@ -12937,15 +13289,15 @@ function timed(transport, ms) {
|
|
|
12937
13289
|
},
|
|
12938
13290
|
retire: (why) => transport.retire?.(why),
|
|
12939
13291
|
...transport.cleanup ? { cleanup } : {},
|
|
12940
|
-
call: (op, body, handle) =>
|
|
13292
|
+
call: (op, body, handle) => guard2(
|
|
12941
13293
|
op,
|
|
12942
13294
|
() => transport.call(op, body, handle),
|
|
12943
13295
|
op === "open" ? release : void 0,
|
|
12944
13296
|
op === "release" ? (running) => void cleanup("release", body ?? {}, running).catch(() => {
|
|
12945
13297
|
}) : void 0
|
|
12946
13298
|
),
|
|
12947
|
-
read: (fh, offset, size, ifMatch) =>
|
|
12948
|
-
write: (fh, offset, bytes) =>
|
|
13299
|
+
read: (fh, offset, size, ifMatch) => guard2("read", () => transport.read(fh, offset, size, ifMatch)),
|
|
13300
|
+
write: (fh, offset, bytes) => guard2("write", () => transport.write(fh, offset, bytes)),
|
|
12949
13301
|
watch: (signal, onEvent, onOpen) => transport.watch(signal, onEvent, onOpen)
|
|
12950
13302
|
};
|
|
12951
13303
|
}
|
|
@@ -13223,10 +13575,10 @@ var ChunkedBytes = class {
|
|
|
13223
13575
|
}
|
|
13224
13576
|
this.size = size;
|
|
13225
13577
|
}
|
|
13226
|
-
room(index,
|
|
13578
|
+
room(index, need2) {
|
|
13227
13579
|
const chunk2 = this.chunks[index];
|
|
13228
|
-
if (chunk2 && chunk2.length >=
|
|
13229
|
-
const grown = new Uint8Array(Math.min(CHUNK3, Math.max(
|
|
13580
|
+
if (chunk2 && chunk2.length >= need2) return chunk2;
|
|
13581
|
+
const grown = new Uint8Array(Math.min(CHUNK3, Math.max(need2, (chunk2?.length ?? 0) * 2)));
|
|
13230
13582
|
if (chunk2) grown.set(chunk2);
|
|
13231
13583
|
this.chunks[index] = grown;
|
|
13232
13584
|
return grown;
|
|
@@ -13273,14 +13625,14 @@ function tmpfs() {
|
|
|
13273
13625
|
const root = make("directory", 16877);
|
|
13274
13626
|
const handles = /* @__PURE__ */ new Map();
|
|
13275
13627
|
let nextFh = 0;
|
|
13276
|
-
const
|
|
13628
|
+
const parts2 = (path) => path.split("/").filter(Boolean);
|
|
13277
13629
|
const place = (dir, name2, node) => {
|
|
13278
13630
|
dir.children.set(name2, node);
|
|
13279
13631
|
dir.mtime = Date.now();
|
|
13280
13632
|
};
|
|
13281
13633
|
function find(path) {
|
|
13282
13634
|
let node = root;
|
|
13283
|
-
for (const name2 of
|
|
13635
|
+
for (const name2 of parts2(path)) {
|
|
13284
13636
|
if (node.kind !== "directory") throw fsError2("ENOTDIR", path);
|
|
13285
13637
|
const next2 = node.children.get(name2);
|
|
13286
13638
|
if (!next2) throw fsError2("ENOENT", path);
|
|
@@ -13289,7 +13641,7 @@ function tmpfs() {
|
|
|
13289
13641
|
return node;
|
|
13290
13642
|
}
|
|
13291
13643
|
function parent(path) {
|
|
13292
|
-
const names =
|
|
13644
|
+
const names = parts2(path);
|
|
13293
13645
|
const name2 = names.pop();
|
|
13294
13646
|
if (name2 === void 0) throw fsError2("EBUSY", "the root of a mount");
|
|
13295
13647
|
const dir = find(`/${names.join("/")}`);
|
|
@@ -13374,8 +13726,8 @@ function tmpfs() {
|
|
|
13374
13726
|
dir.mtime = Date.now();
|
|
13375
13727
|
},
|
|
13376
13728
|
async rename(from, to) {
|
|
13377
|
-
const a = `/${
|
|
13378
|
-
const b = `/${
|
|
13729
|
+
const a = `/${parts2(from).join("/")}`;
|
|
13730
|
+
const b = `/${parts2(to).join("/")}`;
|
|
13379
13731
|
if (b !== a && b.startsWith(`${a}/`)) throw fsError2("EINVAL", `${to} is inside ${from}`);
|
|
13380
13732
|
const source = parent(from);
|
|
13381
13733
|
const node = source.dir.children.get(source.name);
|
|
@@ -13734,11 +14086,13 @@ var Launcher = class {
|
|
|
13734
14086
|
booting = new AbortController();
|
|
13735
14087
|
fstab = Promise.resolve([]);
|
|
13736
14088
|
usersMode;
|
|
14089
|
+
dac;
|
|
13737
14090
|
nextPid = 1e3;
|
|
13738
14091
|
boot = Date.now();
|
|
13739
14092
|
terminals = 0;
|
|
13740
14093
|
constructor(options) {
|
|
13741
14094
|
this.usersMode = usersMode(options.users);
|
|
14095
|
+
this.dac = this.usersMode === "off" ? void 0 : new Dac(this.usersMode, options.onDenial);
|
|
13742
14096
|
if (this.owners) options.fs.trackOwners?.();
|
|
13743
14097
|
this.mounts = new MountTable({
|
|
13744
14098
|
open: (type, spec) => this.driver(type, spec),
|
|
@@ -14069,6 +14423,7 @@ var Launcher = class {
|
|
|
14069
14423
|
...req.umask !== void 0 ? { umask: req.umask } : {},
|
|
14070
14424
|
cred: req.cred,
|
|
14071
14425
|
...this.owners ? { owners: true } : {},
|
|
14426
|
+
...this.dac ? { dac: this.dac } : {},
|
|
14072
14427
|
program: req.program,
|
|
14073
14428
|
argv0: req.argv0,
|
|
14074
14429
|
args: req.args,
|
|
@@ -14670,10 +15025,11 @@ function usersOp(l, req) {
|
|
|
14670
15025
|
if (req.op === "groups-remove-member") return l.users.removeMember(req.group, req.user);
|
|
14671
15026
|
return l.users.list();
|
|
14672
15027
|
}
|
|
14673
|
-
function hostOptions(req) {
|
|
15028
|
+
function hostOptions(req, port) {
|
|
14674
15029
|
return {
|
|
14675
15030
|
...req.hostname !== void 0 ? { hostname: req.hostname } : {},
|
|
14676
|
-
...req.users !== void 0 ? { users: req.users } : {}
|
|
15031
|
+
...req.users !== void 0 ? { users: req.users } : {},
|
|
15032
|
+
...req.denials ? { onDenial: (denial) => port.postMessage({ denial }) } : {}
|
|
14677
15033
|
};
|
|
14678
15034
|
}
|
|
14679
15035
|
function serveKernel(port, deps) {
|
|
@@ -14750,7 +15106,7 @@ function serveKernel(port, deps) {
|
|
|
14750
15106
|
...deps.createDriverWorker ? { createDriverWorker: deps.createDriverWorker } : {},
|
|
14751
15107
|
...req.modules ? { modules: req.modules } : {},
|
|
14752
15108
|
...req.env ? { env: req.env } : {},
|
|
14753
|
-
...hostOptions(req),
|
|
15109
|
+
...hostOptions(req, port),
|
|
14754
15110
|
...net,
|
|
14755
15111
|
...media ? { media } : {},
|
|
14756
15112
|
onMountPending: (medium) => port.postMessage({ medium }),
|