@ai-ecoverse/slicc-kernel 1.69.0 → 1.71.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 +22 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +4 -4
- package/dist/kernel-worker.js +343 -80
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +5 -1
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +333 -70
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +5 -1
- package/dist/process-worker.js.map +2 -2
- package/package.json +1 -1
package/dist/node.js
CHANGED
|
@@ -2490,22 +2490,22 @@ function act(session, req, signal) {
|
|
|
2490
2490
|
}
|
|
2491
2491
|
async function realpath(fs, path) {
|
|
2492
2492
|
let parts2 = fs.resolvePath("/", path).split("/").filter(Boolean);
|
|
2493
|
-
let
|
|
2493
|
+
let resolved2 = "";
|
|
2494
2494
|
for (let hops = 0; parts2.length > 0; ) {
|
|
2495
2495
|
const [name2, ...rest2] = parts2;
|
|
2496
|
-
const next2 = `${
|
|
2496
|
+
const next2 = `${resolved2}/${name2}`;
|
|
2497
2497
|
if (!(await fs.lstat(next2)).isSymbolicLink) {
|
|
2498
|
-
|
|
2498
|
+
resolved2 = next2;
|
|
2499
2499
|
parts2 = rest2;
|
|
2500
2500
|
continue;
|
|
2501
2501
|
}
|
|
2502
2502
|
if (++hops > MAX_LINKS3) throw fsError("ELOOP", path);
|
|
2503
2503
|
const target = await fs.readlink(next2);
|
|
2504
|
-
const base = target.startsWith("/") ? "/" :
|
|
2504
|
+
const base = target.startsWith("/") ? "/" : resolved2 || "/";
|
|
2505
2505
|
parts2 = [...fs.resolvePath(base, target).split("/").filter(Boolean), ...rest2];
|
|
2506
|
-
|
|
2506
|
+
resolved2 = "";
|
|
2507
2507
|
}
|
|
2508
|
-
return
|
|
2508
|
+
return resolved2 || "/";
|
|
2509
2509
|
}
|
|
2510
2510
|
var CLIENT_CHECKS = {
|
|
2511
2511
|
readFile: (g, at) => readable(g, "read", at(0)),
|
|
@@ -2810,7 +2810,7 @@ function serveClient(port, host) {
|
|
|
2810
2810
|
if (!FS_METHODS.includes(req.method)) {
|
|
2811
2811
|
throw new Error(`unknown file system call ${req.method}`);
|
|
2812
2812
|
}
|
|
2813
|
-
return fsCall(l.
|
|
2813
|
+
return fsCall(l.processFs, req.method, req.args, ownerFor(l, self), guardFor(l, self));
|
|
2814
2814
|
case "watch":
|
|
2815
2815
|
case "unwatch":
|
|
2816
2816
|
return watchOp(l, req, watches, send, guardFor(l, self));
|
|
@@ -2997,6 +2997,183 @@ var MemoryMeta = class {
|
|
|
2997
2997
|
}
|
|
2998
2998
|
};
|
|
2999
2999
|
|
|
3000
|
+
// src/fs/protect.ts
|
|
3001
|
+
var PROTECT_WRITER = "slicc-kernel: protect.writer needs the process tag (#405), which this kernel does not have yet";
|
|
3002
|
+
function protectedPrefixes(paths) {
|
|
3003
|
+
return [...new Set(paths.map((p) => normalizePath(p)))];
|
|
3004
|
+
}
|
|
3005
|
+
function within(path, prefix) {
|
|
3006
|
+
return prefix === "/" || path === prefix || path.startsWith(`${prefix}/`);
|
|
3007
|
+
}
|
|
3008
|
+
function isProtected(prefixes2, path) {
|
|
3009
|
+
return prefixes2.some((prefix) => within(path, prefix));
|
|
3010
|
+
}
|
|
3011
|
+
function holdsProtected(prefixes2, path) {
|
|
3012
|
+
return prefixes2.some((prefix) => within(prefix, path));
|
|
3013
|
+
}
|
|
3014
|
+
var MAX_LINKS4 = 40;
|
|
3015
|
+
function partsOf(path) {
|
|
3016
|
+
return path.split("/").filter((p) => p !== "" && p !== ".");
|
|
3017
|
+
}
|
|
3018
|
+
function missing(err) {
|
|
3019
|
+
const code = err?.code;
|
|
3020
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
3021
|
+
}
|
|
3022
|
+
async function resolved(fs, path, follow2) {
|
|
3023
|
+
let done4 = [];
|
|
3024
|
+
let todo = partsOf(path);
|
|
3025
|
+
for (let hops = 0; todo.length > 0; ) {
|
|
3026
|
+
const part = todo.shift();
|
|
3027
|
+
if (part === "..") {
|
|
3028
|
+
done4.pop();
|
|
3029
|
+
continue;
|
|
3030
|
+
}
|
|
3031
|
+
if (todo.length === 0 && !follow2) {
|
|
3032
|
+
done4.push(part);
|
|
3033
|
+
break;
|
|
3034
|
+
}
|
|
3035
|
+
const at = `/${[...done4, part].join("/")}`;
|
|
3036
|
+
const st = await fs.lstat(at).catch((err) => {
|
|
3037
|
+
if (missing(err)) return void 0;
|
|
3038
|
+
throw err;
|
|
3039
|
+
});
|
|
3040
|
+
if (!st) {
|
|
3041
|
+
return normalizePath([...done4, part, ...todo].join("/"));
|
|
3042
|
+
}
|
|
3043
|
+
if (!st.isSymbolicLink) {
|
|
3044
|
+
done4.push(part);
|
|
3045
|
+
continue;
|
|
3046
|
+
}
|
|
3047
|
+
if (++hops > MAX_LINKS4) throw fsError("ELOOP", path);
|
|
3048
|
+
const link2 = await fs.readlink(at);
|
|
3049
|
+
if (link2.startsWith("/")) done4 = [];
|
|
3050
|
+
todo = [...partsOf(link2), ...todo];
|
|
3051
|
+
}
|
|
3052
|
+
return `/${done4.join("/")}`;
|
|
3053
|
+
}
|
|
3054
|
+
var ProtectedFs = class {
|
|
3055
|
+
prefixes;
|
|
3056
|
+
constructor(prefixes2) {
|
|
3057
|
+
this.prefixes = prefixes2;
|
|
3058
|
+
}
|
|
3059
|
+
async check(fs, path, follow2, whole = false) {
|
|
3060
|
+
const lexical2 = normalizePath(path);
|
|
3061
|
+
const real = await resolved(fs, lexical2, follow2);
|
|
3062
|
+
for (const p of /* @__PURE__ */ new Set([lexical2, real])) {
|
|
3063
|
+
if (isProtected(this.prefixes, p) || whole && holdsProtected(this.prefixes, p)) {
|
|
3064
|
+
throw fsError("EROFS", path);
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
view(fs) {
|
|
3069
|
+
const check = (path, follow2, whole = false) => this.check(fs, path, follow2, whole);
|
|
3070
|
+
const marked = (path, st) => !st.readonly && isProtected(this.prefixes, normalizePath(path)) ? { ...st, readonly: true } : st;
|
|
3071
|
+
const own = {
|
|
3072
|
+
async writeFile(...args) {
|
|
3073
|
+
await check(args[0], true);
|
|
3074
|
+
return fs.writeFile(...args);
|
|
3075
|
+
},
|
|
3076
|
+
async mkdir(...args) {
|
|
3077
|
+
await check(args[0], false);
|
|
3078
|
+
return fs.mkdir(...args);
|
|
3079
|
+
},
|
|
3080
|
+
async rm(...args) {
|
|
3081
|
+
await check(args[0], false, true);
|
|
3082
|
+
return fs.rm(...args);
|
|
3083
|
+
},
|
|
3084
|
+
async rename(from, to) {
|
|
3085
|
+
await check(from, false, true);
|
|
3086
|
+
await check(to, false, true);
|
|
3087
|
+
return fs.rename(from, to);
|
|
3088
|
+
},
|
|
3089
|
+
async link(from, to, follow2) {
|
|
3090
|
+
await check(from, follow2 === true);
|
|
3091
|
+
await check(to, false);
|
|
3092
|
+
return fs.link(from, to, follow2);
|
|
3093
|
+
},
|
|
3094
|
+
async symlink(...args) {
|
|
3095
|
+
await check(args[1], false);
|
|
3096
|
+
return fs.symlink(...args);
|
|
3097
|
+
},
|
|
3098
|
+
async chmod(path, mode) {
|
|
3099
|
+
await check(path, true);
|
|
3100
|
+
return fs.chmod(path, mode);
|
|
3101
|
+
},
|
|
3102
|
+
async utimes(path, atime, mtime) {
|
|
3103
|
+
await check(path, true);
|
|
3104
|
+
return fs.utimes(path, atime, mtime);
|
|
3105
|
+
},
|
|
3106
|
+
async stat(path) {
|
|
3107
|
+
return marked(path, await fs.stat(path));
|
|
3108
|
+
},
|
|
3109
|
+
async lstat(path) {
|
|
3110
|
+
return marked(path, await fs.lstat(path));
|
|
3111
|
+
},
|
|
3112
|
+
async readdirStat(path) {
|
|
3113
|
+
const dir = normalizePath(path);
|
|
3114
|
+
return (await fs.readdirStat(path)).map(([name2, st]) => [
|
|
3115
|
+
name2,
|
|
3116
|
+
st && marked(`${dir}/${name2}`, st)
|
|
3117
|
+
]);
|
|
3118
|
+
},
|
|
3119
|
+
...fs.createExclusive ? {
|
|
3120
|
+
async createExclusive(...args) {
|
|
3121
|
+
await check(args[0], true);
|
|
3122
|
+
return fs.createExclusive?.(...args);
|
|
3123
|
+
}
|
|
3124
|
+
} : {},
|
|
3125
|
+
...fs.chown ? {
|
|
3126
|
+
async chown(...args) {
|
|
3127
|
+
await check(args[0], args[1].follow !== false);
|
|
3128
|
+
return fs.chown?.(...args);
|
|
3129
|
+
}
|
|
3130
|
+
} : {},
|
|
3131
|
+
...fs.lutimes ? {
|
|
3132
|
+
async lutimes(path, atime, mtime) {
|
|
3133
|
+
await check(path, false);
|
|
3134
|
+
return fs.lutimes?.(path, atime, mtime);
|
|
3135
|
+
}
|
|
3136
|
+
} : {},
|
|
3137
|
+
...fs.pwrite ? {
|
|
3138
|
+
async pwrite(...args) {
|
|
3139
|
+
await check(args[0], true);
|
|
3140
|
+
return fs.pwrite?.(...args);
|
|
3141
|
+
}
|
|
3142
|
+
} : {},
|
|
3143
|
+
...fs.truncate ? {
|
|
3144
|
+
async truncate(path, size) {
|
|
3145
|
+
await check(path, true);
|
|
3146
|
+
return fs.truncate?.(path, size);
|
|
3147
|
+
}
|
|
3148
|
+
} : {},
|
|
3149
|
+
...fs.exclusive ? {
|
|
3150
|
+
async exclusive(path, run2, follow2) {
|
|
3151
|
+
return fs.exclusive(
|
|
3152
|
+
path,
|
|
3153
|
+
(held) => run2({
|
|
3154
|
+
...held,
|
|
3155
|
+
writeFile: async (...args) => {
|
|
3156
|
+
await check(args[0], true);
|
|
3157
|
+
return held.writeFile(...args);
|
|
3158
|
+
}
|
|
3159
|
+
}),
|
|
3160
|
+
follow2
|
|
3161
|
+
);
|
|
3162
|
+
}
|
|
3163
|
+
} : {}
|
|
3164
|
+
};
|
|
3165
|
+
const view = {};
|
|
3166
|
+
for (let at = fs; at && at !== Object.prototype; at = Object.getPrototypeOf(at)) {
|
|
3167
|
+
for (const name2 of Object.getOwnPropertyNames(at)) {
|
|
3168
|
+
if (name2 === "constructor" || Object.hasOwn(view, name2)) continue;
|
|
3169
|
+
const value = fs[name2];
|
|
3170
|
+
view[name2] = typeof value === "function" ? value.bind(fs) : value;
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
return Object.assign(view, own);
|
|
3174
|
+
}
|
|
3175
|
+
};
|
|
3176
|
+
|
|
3000
3177
|
// src/fs/opfs.ts
|
|
3001
3178
|
var S_IFDIR = 16384;
|
|
3002
3179
|
var S_IFREG = 32768;
|
|
@@ -3004,7 +3181,7 @@ var S_IFLNK = 40960;
|
|
|
3004
3181
|
var PERM_MASK = 4095;
|
|
3005
3182
|
var S_ISGID3 = 1024;
|
|
3006
3183
|
var READ_ATTEMPTS = 3;
|
|
3007
|
-
var
|
|
3184
|
+
var MAX_LINKS5 = 40;
|
|
3008
3185
|
var CHECK_MS = 1e3;
|
|
3009
3186
|
var EXECUTABLE = /\/node_modules\/(?:@[^/]+\/)?[^/]+\/bin\//;
|
|
3010
3187
|
var ERRNO_BY_DOM_ERROR = {
|
|
@@ -3041,7 +3218,7 @@ function sidecar(meta) {
|
|
|
3041
3218
|
});
|
|
3042
3219
|
return Object.fromEntries(META_OPS.map((op) => [op, guarded2(op)]));
|
|
3043
3220
|
}
|
|
3044
|
-
function
|
|
3221
|
+
function missing2(err) {
|
|
3045
3222
|
const code = err?.code;
|
|
3046
3223
|
return code === "ENOENT" || code === "ENOTDIR";
|
|
3047
3224
|
}
|
|
@@ -3058,7 +3235,7 @@ function prefixes(path) {
|
|
|
3058
3235
|
const parts2 = split(path);
|
|
3059
3236
|
return parts2.map((_, i) => `/${parts2.slice(0, i + 1).join("/")}`);
|
|
3060
3237
|
}
|
|
3061
|
-
function
|
|
3238
|
+
function within2(path, root) {
|
|
3062
3239
|
return path === root || path.startsWith(`${root}/`);
|
|
3063
3240
|
}
|
|
3064
3241
|
var LOCKED = /* @__PURE__ */ new Set(["NotReadableError", "NoModificationAllowedError", "InvalidStateError"]);
|
|
@@ -3193,14 +3370,25 @@ var OpfsFs = class {
|
|
|
3193
3370
|
const fields = await this.newFields(path, owner, directory);
|
|
3194
3371
|
if (fields) await this.meta.update(path, (entry) => ({ ...entry, path, ...fields }));
|
|
3195
3372
|
}
|
|
3373
|
+
guarded = [];
|
|
3374
|
+
protect(prefixes2) {
|
|
3375
|
+
this.guarded = prefixes2;
|
|
3376
|
+
}
|
|
3377
|
+
refuse(path, holding = false) {
|
|
3378
|
+
if (isProtected(this.guarded, path) || holding && holdsProtected(this.guarded, path)) {
|
|
3379
|
+
throw fsError("EROFS", path);
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3196
3382
|
async chown(path, change) {
|
|
3197
3383
|
if (!this.owners) return;
|
|
3198
3384
|
if (change.kind === "new") {
|
|
3199
3385
|
const at = resolvePath((await this.locate(parentOf4(path), true)).path, baseOf(path));
|
|
3386
|
+
this.refuse(at);
|
|
3200
3387
|
const { uid = 0, gid = 0, mode } = change;
|
|
3201
3388
|
return this.stampNew(at, { uid, gid, ...mode !== void 0 ? { mode } : {} }, false);
|
|
3202
3389
|
}
|
|
3203
3390
|
const found = await this.locate(path, change.follow !== false);
|
|
3391
|
+
this.refuse(found.path);
|
|
3204
3392
|
const [stored] = found.link ? [found.link] : await this.meta.get([found.path]);
|
|
3205
3393
|
const current = await this.ownerAt(found.path, stored);
|
|
3206
3394
|
const next2 = { uid: change.uid ?? current.uid, gid: change.gid ?? current.gid };
|
|
@@ -3313,11 +3501,11 @@ var OpfsFs = class {
|
|
|
3313
3501
|
}
|
|
3314
3502
|
async locate(path, follow2) {
|
|
3315
3503
|
let current = path;
|
|
3316
|
-
for (let hops = 0; hops <=
|
|
3504
|
+
for (let hops = 0; hops <= MAX_LINKS5; hops++) {
|
|
3317
3505
|
try {
|
|
3318
3506
|
return { path: current, handle: await this.handle(current) };
|
|
3319
3507
|
} catch (err) {
|
|
3320
|
-
if (!
|
|
3508
|
+
if (!missing2(err)) throw err;
|
|
3321
3509
|
const link2 = await this.linkAlong(current);
|
|
3322
3510
|
if (!link2) throw err;
|
|
3323
3511
|
if (link2.path === current && !follow2) return { path: current, link: link2 };
|
|
@@ -3339,7 +3527,7 @@ var OpfsFs = class {
|
|
|
3339
3527
|
const st = await this.lstat(at);
|
|
3340
3528
|
if (!st.isSymbolicLink && !st.isDirectory) throw fsError("ENOTDIR", at);
|
|
3341
3529
|
if (!st.isSymbolicLink) done4.push(part);
|
|
3342
|
-
else if (++hops >
|
|
3530
|
+
else if (++hops > MAX_LINKS5) throw fsError("ELOOP", path);
|
|
3343
3531
|
else {
|
|
3344
3532
|
const link2 = await this.readlink(at);
|
|
3345
3533
|
if (link2.startsWith("/")) done4 = [];
|
|
@@ -3436,7 +3624,7 @@ var OpfsFs = class {
|
|
|
3436
3624
|
return this.locate(path, true).then(
|
|
3437
3625
|
() => true,
|
|
3438
3626
|
(err) => {
|
|
3439
|
-
if (
|
|
3627
|
+
if (missing2(err)) return false;
|
|
3440
3628
|
throw err;
|
|
3441
3629
|
}
|
|
3442
3630
|
);
|
|
@@ -3454,8 +3642,10 @@ var OpfsFs = class {
|
|
|
3454
3642
|
}
|
|
3455
3643
|
}
|
|
3456
3644
|
}
|
|
3457
|
-
async existing(path) {
|
|
3458
|
-
const
|
|
3645
|
+
async existing(path, writing = false) {
|
|
3646
|
+
const found = await this.locate(path, true);
|
|
3647
|
+
if (writing) this.refuse(found.path);
|
|
3648
|
+
const handle = found.handle;
|
|
3459
3649
|
if (handle.kind === "directory") throw fsError("EISDIR", path);
|
|
3460
3650
|
return handle;
|
|
3461
3651
|
}
|
|
@@ -3480,7 +3670,7 @@ var OpfsFs = class {
|
|
|
3480
3670
|
await this.retrying(path, () => positioned(handle, (io) => io.write(offset, bytes)), true);
|
|
3481
3671
|
}
|
|
3482
3672
|
async truncate(path, size) {
|
|
3483
|
-
const handle = await this.existing(path);
|
|
3673
|
+
const handle = await this.existing(path, true);
|
|
3484
3674
|
await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
|
|
3485
3675
|
}
|
|
3486
3676
|
async writeSession(path, truncate) {
|
|
@@ -3506,10 +3696,15 @@ var OpfsFs = class {
|
|
|
3506
3696
|
return new TextDecoder().decode(await this.readFileBuffer(path));
|
|
3507
3697
|
}
|
|
3508
3698
|
async writeTarget(path) {
|
|
3699
|
+
const [target, existed] = await this.locateWrite(path);
|
|
3700
|
+
this.refuse(target);
|
|
3701
|
+
return [target, existed];
|
|
3702
|
+
}
|
|
3703
|
+
async locateWrite(path) {
|
|
3509
3704
|
try {
|
|
3510
3705
|
return [(await this.locate(path, true)).path, true];
|
|
3511
3706
|
} catch (err) {
|
|
3512
|
-
if (!
|
|
3707
|
+
if (!missing2(err)) throw err;
|
|
3513
3708
|
}
|
|
3514
3709
|
const [entry] = await this.meta.get([path]);
|
|
3515
3710
|
const wanted = entry?.link === void 0 ? path : resolvePath(parentOf4(path), entry.link);
|
|
@@ -3578,7 +3773,7 @@ var OpfsFs = class {
|
|
|
3578
3773
|
try {
|
|
3579
3774
|
found = await this.locate(path, true);
|
|
3580
3775
|
} catch (err) {
|
|
3581
|
-
if (!
|
|
3776
|
+
if (!missing2(err)) throw err;
|
|
3582
3777
|
}
|
|
3583
3778
|
if (found) {
|
|
3584
3779
|
const directory = found.handle?.kind === "directory";
|
|
@@ -3599,6 +3794,7 @@ var OpfsFs = class {
|
|
|
3599
3794
|
}
|
|
3600
3795
|
const parent = await this.locate(parentOf4(path), true);
|
|
3601
3796
|
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
3797
|
+
this.refuse(resolvePath(parent.path, baseOf(path)));
|
|
3602
3798
|
await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
|
|
3603
3799
|
const made2 = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
|
|
3604
3800
|
await this.stampNew(made2, owner, true);
|
|
@@ -3613,6 +3809,7 @@ var OpfsFs = class {
|
|
|
3613
3809
|
if (options.force && error.code === "ENOENT") return;
|
|
3614
3810
|
throw error;
|
|
3615
3811
|
}
|
|
3812
|
+
this.refuse(found.path, true);
|
|
3616
3813
|
if (found.handle) {
|
|
3617
3814
|
const [dir, name2] = await this.parent(found.path);
|
|
3618
3815
|
try {
|
|
@@ -3637,13 +3834,15 @@ var OpfsFs = class {
|
|
|
3637
3834
|
async rename(from, to) {
|
|
3638
3835
|
if (from === to) return;
|
|
3639
3836
|
const source = await this.locate(from, false);
|
|
3837
|
+
this.refuse(source.path, true);
|
|
3640
3838
|
this.forget(source.path);
|
|
3641
3839
|
const parent = await this.locate(parentOf4(to), true);
|
|
3642
3840
|
const destination = resolvePath(parent.path, baseOf(to));
|
|
3841
|
+
this.refuse(destination, true);
|
|
3643
3842
|
const sourceStat = await this.describe(source);
|
|
3644
|
-
if (sourceStat.isDirectory &&
|
|
3843
|
+
if (sourceStat.isDirectory && within2(destination, source.path)) throw fsError("EINVAL", to);
|
|
3645
3844
|
const target = await this.locate(destination, false).catch((err) => {
|
|
3646
|
-
if (
|
|
3845
|
+
if (missing2(err)) return null;
|
|
3647
3846
|
throw err;
|
|
3648
3847
|
});
|
|
3649
3848
|
const targetStat = target && await this.describe(target);
|
|
@@ -3737,9 +3936,10 @@ var OpfsFs = class {
|
|
|
3737
3936
|
const parent = await this.locate(parentOf4(path), true);
|
|
3738
3937
|
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
3739
3938
|
const at = resolvePath(parent.path, baseOf(path));
|
|
3939
|
+
this.refuse(at);
|
|
3740
3940
|
if (await this.locate(at, false).then(
|
|
3741
3941
|
() => true,
|
|
3742
|
-
(err) => !
|
|
3942
|
+
(err) => !missing2(err)
|
|
3743
3943
|
)) {
|
|
3744
3944
|
throw fsError("EEXIST", path);
|
|
3745
3945
|
}
|
|
@@ -3766,6 +3966,7 @@ var OpfsFs = class {
|
|
|
3766
3966
|
}
|
|
3767
3967
|
async chmod(path, mode) {
|
|
3768
3968
|
const found = await this.locate(path, true);
|
|
3969
|
+
this.refuse(found.path);
|
|
3769
3970
|
const ctimeMs = Date.now();
|
|
3770
3971
|
await this.meta.update(found.path, (entry) => ({
|
|
3771
3972
|
...entry,
|
|
@@ -3781,6 +3982,7 @@ var OpfsFs = class {
|
|
|
3781
3982
|
await this.setTimes(await this.locate(path, false), atime, mtime);
|
|
3782
3983
|
}
|
|
3783
3984
|
async setTimes(found, atime, mtime) {
|
|
3985
|
+
this.refuse(found.path);
|
|
3784
3986
|
const { link: link2 } = found;
|
|
3785
3987
|
if (link2) {
|
|
3786
3988
|
const ctimeMs2 = Date.now();
|
|
@@ -3880,7 +4082,7 @@ function pidOwner(proc) {
|
|
|
3880
4082
|
function webLocks() {
|
|
3881
4083
|
return globalThis.navigator?.locks;
|
|
3882
4084
|
}
|
|
3883
|
-
var
|
|
4085
|
+
var within3 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
3884
4086
|
var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
3885
4087
|
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
3886
4088
|
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
@@ -3980,7 +4182,7 @@ var UnlinkedKeeper = class {
|
|
|
3980
4182
|
for (const holder of this.holders) {
|
|
3981
4183
|
if (holder.owns(target)) continue;
|
|
3982
4184
|
for (const path of holder.held()) {
|
|
3983
|
-
if (!
|
|
4185
|
+
if (!within3(path, target) || holder.isKept(path)) continue;
|
|
3984
4186
|
held.set(path, [...held.get(path) ?? [], holder]);
|
|
3985
4187
|
}
|
|
3986
4188
|
}
|
|
@@ -5068,8 +5270,8 @@ function binfmtOf(binfmts, path) {
|
|
|
5068
5270
|
var S_IFDIR2 = 16384;
|
|
5069
5271
|
var S_IFREG2 = 32768;
|
|
5070
5272
|
var COMMAND = /^\/(?:usr\/)?bin\/([^/]+)$/;
|
|
5071
|
-
var
|
|
5072
|
-
function
|
|
5273
|
+
var MAX_LINKS6 = 40;
|
|
5274
|
+
function missing3(err) {
|
|
5073
5275
|
return err?.code === "ENOENT";
|
|
5074
5276
|
}
|
|
5075
5277
|
function synthetic(directory, path) {
|
|
@@ -5088,7 +5290,7 @@ function synthetic(directory, path) {
|
|
|
5088
5290
|
}
|
|
5089
5291
|
async function followLinks(fs, path) {
|
|
5090
5292
|
let current = path;
|
|
5091
|
-
for (let hops = 0; hops <
|
|
5293
|
+
for (let hops = 0; hops < MAX_LINKS6; hops++) {
|
|
5092
5294
|
const st = await fs.lstat(current).catch(() => void 0);
|
|
5093
5295
|
if (!st?.isSymbolicLink) return current;
|
|
5094
5296
|
const parent = current.slice(0, current.lastIndexOf("/")) || "/";
|
|
@@ -5135,7 +5337,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
5135
5337
|
try {
|
|
5136
5338
|
return await real(path);
|
|
5137
5339
|
} catch (err) {
|
|
5138
|
-
const found =
|
|
5340
|
+
const found = missing3(err) ? await linked(path) : void 0;
|
|
5139
5341
|
if (found) return found;
|
|
5140
5342
|
throw err;
|
|
5141
5343
|
}
|
|
@@ -5147,7 +5349,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
5147
5349
|
try {
|
|
5148
5350
|
return await fs.readFileBuffer(path);
|
|
5149
5351
|
} catch (err) {
|
|
5150
|
-
if (
|
|
5352
|
+
if (missing3(err) && (await linked(path))?.isFile) return new Uint8Array(0);
|
|
5151
5353
|
throw err;
|
|
5152
5354
|
}
|
|
5153
5355
|
},
|
|
@@ -5163,7 +5365,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
5163
5365
|
const extra = await children2(path);
|
|
5164
5366
|
if (!extra) return fs.readdir(path);
|
|
5165
5367
|
const real = await fs.readdir(path).catch((err) => {
|
|
5166
|
-
if (
|
|
5368
|
+
if (missing3(err)) return [];
|
|
5167
5369
|
throw err;
|
|
5168
5370
|
});
|
|
5169
5371
|
return [.../* @__PURE__ */ new Set([...real, ...extra])].sort();
|
|
@@ -5172,7 +5374,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
5172
5374
|
const extra = await children2(path);
|
|
5173
5375
|
if (!extra) return fs.readdirStat(path);
|
|
5174
5376
|
const real = await fs.readdirStat(path).catch((err) => {
|
|
5175
|
-
if (
|
|
5377
|
+
if (missing3(err)) return [];
|
|
5176
5378
|
throw err;
|
|
5177
5379
|
});
|
|
5178
5380
|
const seen = new Set(real.map(([name2]) => name2));
|
|
@@ -5387,6 +5589,18 @@ function capturing(chunks2, max = Number.POSITIVE_INFINITY, overflow) {
|
|
|
5387
5589
|
size += bytes.length;
|
|
5388
5590
|
};
|
|
5389
5591
|
}
|
|
5592
|
+
function captureFile(onData, control) {
|
|
5593
|
+
if (!control) return sinkFile(onData);
|
|
5594
|
+
control.opened = true;
|
|
5595
|
+
return new OpenFile({
|
|
5596
|
+
write: async (bytes) => {
|
|
5597
|
+
if (control.shut) throw new KernelError("EPIPE");
|
|
5598
|
+
onData(bytes.slice());
|
|
5599
|
+
return bytes.length;
|
|
5600
|
+
},
|
|
5601
|
+
close: () => control.ended()
|
|
5602
|
+
});
|
|
5603
|
+
}
|
|
5390
5604
|
function concat2(chunks2) {
|
|
5391
5605
|
const out = new Uint8Array(chunks2.reduce((n, c) => n + c.length, 0));
|
|
5392
5606
|
let offset = 0;
|
|
@@ -5421,7 +5635,12 @@ var ChildTable = class {
|
|
|
5421
5635
|
const fds = new FdTable();
|
|
5422
5636
|
const captured = /* @__PURE__ */ new Map();
|
|
5423
5637
|
try {
|
|
5424
|
-
for (const [n, slot2] of stdio.entries())
|
|
5638
|
+
for (const [n, slot2] of stdio.entries()) {
|
|
5639
|
+
fds.installAt(
|
|
5640
|
+
n,
|
|
5641
|
+
"sameAs" in slot2 ? fds.get(slot2.sameAs).retain() : this.openSlot(slot2, n, captured)
|
|
5642
|
+
);
|
|
5643
|
+
}
|
|
5425
5644
|
for (const slot2 of inherit2) {
|
|
5426
5645
|
if (slot2.fd <= 2) continue;
|
|
5427
5646
|
if ("device" in slot2) {
|
|
@@ -5478,7 +5697,7 @@ var ChildTable = class {
|
|
|
5478
5697
|
if ("capture" in slot2) {
|
|
5479
5698
|
const chunks2 = [];
|
|
5480
5699
|
captured.set(n, chunks2);
|
|
5481
|
-
return
|
|
5700
|
+
return captureFile(capturing(chunks2, slot2.max, slot2.overflow), slot2.control);
|
|
5482
5701
|
}
|
|
5483
5702
|
return deviceFile("null");
|
|
5484
5703
|
}
|
|
@@ -5671,7 +5890,7 @@ function checkIds(options) {
|
|
|
5671
5890
|
if (!ID.test(value) || Number(value) > MAX_ID2) throw errnoError("EINVAL", `${key}=${value}`);
|
|
5672
5891
|
}
|
|
5673
5892
|
}
|
|
5674
|
-
var
|
|
5893
|
+
var MAX_LINKS7 = 40;
|
|
5675
5894
|
var STALE_RETRIES = 3;
|
|
5676
5895
|
var SETTLE_ROUNDS = 4;
|
|
5677
5896
|
var DEFAULT_IO = 8 * 1024 * 1024;
|
|
@@ -5681,7 +5900,7 @@ function parseSize(text) {
|
|
|
5681
5900
|
if (!match) throw errnoError("EINVAL", `not a size: ${text}`);
|
|
5682
5901
|
return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
|
|
5683
5902
|
}
|
|
5684
|
-
var
|
|
5903
|
+
var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
5685
5904
|
var HeldSet = class extends Set {
|
|
5686
5905
|
idle;
|
|
5687
5906
|
constructor(idle) {
|
|
@@ -5700,7 +5919,7 @@ var HeldSet = class extends Set {
|
|
|
5700
5919
|
}
|
|
5701
5920
|
};
|
|
5702
5921
|
function heldUnder(held, target) {
|
|
5703
|
-
for (const paths of held) for (const path of paths.keys()) if (
|
|
5922
|
+
for (const paths of held) for (const path of paths.keys()) if (within4(path, target)) return true;
|
|
5704
5923
|
return false;
|
|
5705
5924
|
}
|
|
5706
5925
|
var HeldPaths = class extends Map {
|
|
@@ -5811,24 +6030,24 @@ var HeldPaths = class extends Map {
|
|
|
5811
6030
|
return this.owning === path;
|
|
5812
6031
|
}
|
|
5813
6032
|
revoke(prefix) {
|
|
5814
|
-
for (const path of this.keys()) if (
|
|
6033
|
+
for (const path of this.keys()) if (within4(path, prefix)) this.revoked.add(path);
|
|
5815
6034
|
}
|
|
5816
6035
|
renamed(from, to) {
|
|
5817
6036
|
for (const [path, count] of [...this]) {
|
|
5818
|
-
if (!
|
|
6037
|
+
if (!within4(path, from)) continue;
|
|
5819
6038
|
const moved2 = to + path.slice(from.length);
|
|
5820
6039
|
this.delete(path);
|
|
5821
6040
|
this.set(moved2, (this.get(moved2) ?? 0) + count);
|
|
5822
6041
|
if (this.revoked.delete(path)) this.revoked.add(moved2);
|
|
5823
6042
|
}
|
|
5824
6043
|
for (const [path, count] of [...this.opens]) {
|
|
5825
|
-
if (!
|
|
6044
|
+
if (!within4(path, from)) continue;
|
|
5826
6045
|
const moved2 = to + path.slice(from.length);
|
|
5827
6046
|
this.opens.delete(path);
|
|
5828
6047
|
this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count);
|
|
5829
6048
|
}
|
|
5830
6049
|
for (const [id2, key] of this.handles) {
|
|
5831
|
-
if (
|
|
6050
|
+
if (within4(key, from)) this.handles.set(id2, to + key.slice(from.length));
|
|
5832
6051
|
}
|
|
5833
6052
|
}
|
|
5834
6053
|
isRevoked(path) {
|
|
@@ -5935,7 +6154,7 @@ var MountTable = class {
|
|
|
5935
6154
|
locate(path) {
|
|
5936
6155
|
let best;
|
|
5937
6156
|
for (const mount of this.mounts.values()) {
|
|
5938
|
-
if (
|
|
6157
|
+
if (within4(path, mount.target) && (!best || mount.target.length > best.target.length))
|
|
5939
6158
|
best = mount;
|
|
5940
6159
|
}
|
|
5941
6160
|
if (!best) return void 0;
|
|
@@ -6078,7 +6297,7 @@ var MountTable = class {
|
|
|
6078
6297
|
async commitUnder(prefix) {
|
|
6079
6298
|
const at = normalizePath(prefix);
|
|
6080
6299
|
await Promise.all(
|
|
6081
|
-
[...this.sessions.keys()].filter((p) =>
|
|
6300
|
+
[...this.sessions.keys()].filter((p) => within4(p, at)).map((p) => this.commit(p))
|
|
6082
6301
|
);
|
|
6083
6302
|
}
|
|
6084
6303
|
async commitOverlapping(real, offset, length2) {
|
|
@@ -6113,10 +6332,10 @@ var MountTable = class {
|
|
|
6113
6332
|
}
|
|
6114
6333
|
forget(mount, path) {
|
|
6115
6334
|
mount.epoch++;
|
|
6116
|
-
for (const key of [...mount.attrs.keys()]) if (
|
|
6335
|
+
for (const key of [...mount.attrs.keys()]) if (within4(key, path)) mount.attrs.delete(key);
|
|
6117
6336
|
mount.lists.delete(path);
|
|
6118
6337
|
mount.lists.delete(parentOf7(path));
|
|
6119
|
-
for (const key of [...mount.lists.keys()]) if (
|
|
6338
|
+
for (const key of [...mount.lists.keys()]) if (within4(key, path)) mount.lists.delete(key);
|
|
6120
6339
|
}
|
|
6121
6340
|
async getattr(mount, path, rel2, settled = false) {
|
|
6122
6341
|
for (let round = 1; ; round++) {
|
|
@@ -6189,7 +6408,7 @@ var MountFs = class {
|
|
|
6189
6408
|
throw err;
|
|
6190
6409
|
}
|
|
6191
6410
|
if (attr2.kind !== "symlink") return real;
|
|
6192
|
-
if (hops >=
|
|
6411
|
+
if (hops >= MAX_LINKS7) throw errnoError("ELOOP", path);
|
|
6193
6412
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
6194
6413
|
return this.follow(this.base.resolvePath(parentOf7(real), target), hops + 1, create);
|
|
6195
6414
|
}
|
|
@@ -8834,11 +9053,11 @@ function ipv6(host) {
|
|
|
8834
9053
|
const back = halves.length === 2 ? groups(halves[1]) : [];
|
|
8835
9054
|
if (![...head, ...back].every((g) => /^[0-9A-Fa-f]{1,4}$/.test(g))) return void 0;
|
|
8836
9055
|
const words2 = [...head, ...back].map((g) => Number.parseInt(g, 16));
|
|
8837
|
-
const
|
|
8838
|
-
if (halves.length === 2 ?
|
|
9056
|
+
const missing4 = 8 - words2.length - tail.length;
|
|
9057
|
+
if (halves.length === 2 ? missing4 < 1 : missing4 !== 0) return void 0;
|
|
8839
9058
|
const all = [
|
|
8840
9059
|
...head.map((g) => Number.parseInt(g, 16)),
|
|
8841
|
-
...new Array(halves.length === 2 ?
|
|
9060
|
+
...new Array(halves.length === 2 ? missing4 : 0).fill(0),
|
|
8842
9061
|
...back.map((g) => Number.parseInt(g, 16)),
|
|
8843
9062
|
...tail
|
|
8844
9063
|
];
|
|
@@ -9682,7 +9901,7 @@ function parsePrefix(text) {
|
|
|
9682
9901
|
if (length2 < 0 || length2 > max) return void 0;
|
|
9683
9902
|
return { ...parsed2, length: length2 };
|
|
9684
9903
|
}
|
|
9685
|
-
function
|
|
9904
|
+
function within5(prefix, addr) {
|
|
9686
9905
|
if (prefix.v6 !== addr.v6) return false;
|
|
9687
9906
|
const shift = BigInt((prefix.v6 ? 128 : 32) - prefix.length);
|
|
9688
9907
|
return addr.bits >> shift === prefix.bits >> shift;
|
|
@@ -9722,9 +9941,9 @@ var Routes = class {
|
|
|
9722
9941
|
if (!addr) return "unreachable";
|
|
9723
9942
|
if (addr.v6 && addr.bits === 1n) return "loopback";
|
|
9724
9943
|
if (!addr.v6 && addr.bits >> 24n === 127n) return "loopback";
|
|
9725
|
-
if (RESERVED.some((p) =>
|
|
9944
|
+
if (RESERVED.some((p) => within5(p, addr))) return "reserved";
|
|
9726
9945
|
if (addr.v6 && !this.ipv6) return "unreachable";
|
|
9727
|
-
if (this.exit || this.table.some((p) =>
|
|
9946
|
+
if (this.exit || this.table.some((p) => within5(p, addr))) return "uplink";
|
|
9728
9947
|
return "unreachable";
|
|
9729
9948
|
}
|
|
9730
9949
|
};
|
|
@@ -10333,9 +10552,11 @@ function capLimit(maxBuffer) {
|
|
|
10333
10552
|
return Math.min(asked, RUN_CAPTURE_CAP);
|
|
10334
10553
|
}
|
|
10335
10554
|
function slot(how, n, extra) {
|
|
10555
|
+
if (how === "stdout") return { sameAs: 1 };
|
|
10556
|
+
if (typeof how === "number") return { fd: how };
|
|
10336
10557
|
if (how === "inherit") return { fd: n };
|
|
10337
10558
|
if (how === "ignore") return { none: true };
|
|
10338
|
-
return extra;
|
|
10559
|
+
return extra();
|
|
10339
10560
|
}
|
|
10340
10561
|
function frame2(outcome, out, err) {
|
|
10341
10562
|
const head = new TextEncoder().encode(JSON.stringify(outcome));
|
|
@@ -10365,12 +10586,20 @@ async function runChild(req, deps) {
|
|
|
10365
10586
|
if (failure !== void 0) return;
|
|
10366
10587
|
failure = code;
|
|
10367
10588
|
if (pid !== 0) kill();
|
|
10589
|
+
decided.resolve();
|
|
10590
|
+
};
|
|
10591
|
+
const controls = [];
|
|
10592
|
+
const capture = () => {
|
|
10593
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
10594
|
+
const control = { shut: false, ended: resolve, eof: promise };
|
|
10595
|
+
controls.push(control);
|
|
10596
|
+
return { capture: true, max, overflow: () => stop("ENOBUFS"), control };
|
|
10368
10597
|
};
|
|
10369
|
-
const
|
|
10598
|
+
const decided = Promise.withResolvers();
|
|
10370
10599
|
const stdio = [
|
|
10371
|
-
slot(req.stdin, 0, { input: req.input ?? new Uint8Array(0) }),
|
|
10372
|
-
slot(req.stdout, 1, capture
|
|
10373
|
-
slot(req.stderr, 2, capture
|
|
10600
|
+
slot(req.stdin, 0, () => ({ input: req.input ?? new Uint8Array(0) })),
|
|
10601
|
+
slot(req.stdout, 1, capture),
|
|
10602
|
+
slot(req.stderr, 2, capture)
|
|
10374
10603
|
];
|
|
10375
10604
|
try {
|
|
10376
10605
|
pid = await deps.children.spawn(
|
|
@@ -10387,8 +10616,11 @@ async function runChild(req, deps) {
|
|
|
10387
10616
|
let status;
|
|
10388
10617
|
try {
|
|
10389
10618
|
status = await reaped(deps, pid);
|
|
10619
|
+
const open = controls.filter((c) => c.opened);
|
|
10620
|
+
await Promise.race([Promise.all(open.map((c) => c.eof)), decided.promise]);
|
|
10390
10621
|
} finally {
|
|
10391
10622
|
clearTimeout(timer);
|
|
10623
|
+
for (const control of controls) control.shut = true;
|
|
10392
10624
|
}
|
|
10393
10625
|
const out = deps.children.captured(pid, 1);
|
|
10394
10626
|
const err = deps.children.captured(pid, 2);
|
|
@@ -10558,7 +10790,7 @@ function isMissing(err) {
|
|
|
10558
10790
|
if (err?.code === "ENOENT") return true;
|
|
10559
10791
|
return err instanceof Error && err.message.startsWith("ENOENT");
|
|
10560
10792
|
}
|
|
10561
|
-
function
|
|
10793
|
+
function within6(path, root) {
|
|
10562
10794
|
return path === root || path.startsWith(root.endsWith("/") ? root : `${root}/`);
|
|
10563
10795
|
}
|
|
10564
10796
|
var VfsNode = class {
|
|
@@ -10879,12 +11111,12 @@ var VfsNodes = class {
|
|
|
10879
11111
|
return node;
|
|
10880
11112
|
}
|
|
10881
11113
|
holds(prefix) {
|
|
10882
|
-
for (const path of this.byPath.keys()) if (
|
|
11114
|
+
for (const path of this.byPath.keys()) if (within6(path, prefix)) return true;
|
|
10883
11115
|
return false;
|
|
10884
11116
|
}
|
|
10885
11117
|
writes(prefix) {
|
|
10886
11118
|
for (const [path, node] of this.byPath)
|
|
10887
|
-
if (node.writers > 0 &&
|
|
11119
|
+
if (node.writers > 0 && within6(path, prefix)) return true;
|
|
10888
11120
|
return false;
|
|
10889
11121
|
}
|
|
10890
11122
|
closed(node, writable2 = false) {
|
|
@@ -10928,11 +11160,11 @@ var VfsNodes = class {
|
|
|
10928
11160
|
this.kept.delete(node);
|
|
10929
11161
|
}
|
|
10930
11162
|
async flush(path) {
|
|
10931
|
-
for (const [p, node] of this.byPath) if (
|
|
11163
|
+
for (const [p, node] of this.byPath) if (within6(p, path)) await node.serial(() => node.flush());
|
|
10932
11164
|
}
|
|
10933
11165
|
async unlinking(path) {
|
|
10934
11166
|
for (const [held, node] of [...this.byPath]) {
|
|
10935
|
-
if (!
|
|
11167
|
+
if (!within6(held, path)) continue;
|
|
10936
11168
|
await node.serial(async () => {
|
|
10937
11169
|
await Promise.all([this.keepsOnDisk || node.keep(), node.limit()]);
|
|
10938
11170
|
});
|
|
@@ -10940,14 +11172,14 @@ var VfsNodes = class {
|
|
|
10940
11172
|
}
|
|
10941
11173
|
revoke(prefix) {
|
|
10942
11174
|
for (const [path, node] of [...this.byPath]) {
|
|
10943
|
-
if (!
|
|
11175
|
+
if (!within6(path, prefix)) continue;
|
|
10944
11176
|
node.revoke();
|
|
10945
11177
|
this.forget(node);
|
|
10946
11178
|
}
|
|
10947
11179
|
}
|
|
10948
11180
|
unlinked(path) {
|
|
10949
11181
|
for (const [held, node] of [...this.byPath]) {
|
|
10950
|
-
if (!
|
|
11182
|
+
if (!within6(held, path) || this.byPath.get(held) !== node) continue;
|
|
10951
11183
|
if (!this.dropName(held, node)) this.orphan(node);
|
|
10952
11184
|
}
|
|
10953
11185
|
}
|
|
@@ -10955,8 +11187,8 @@ var VfsNodes = class {
|
|
|
10955
11187
|
if (from === to) return;
|
|
10956
11188
|
const moved2 = [];
|
|
10957
11189
|
for (const [p, node] of [...this.byPath]) {
|
|
10958
|
-
if (
|
|
10959
|
-
else if (
|
|
11190
|
+
if (within6(p, from)) moved2.push([p, node]);
|
|
11191
|
+
else if (within6(p, to) && this.byPath.get(p) === node && !this.dropName(p, node)) {
|
|
10960
11192
|
this.orphan(node);
|
|
10961
11193
|
}
|
|
10962
11194
|
}
|
|
@@ -11502,6 +11734,7 @@ var WasmProcess = class {
|
|
|
11502
11734
|
this.fds.installAt(fd, await this.procFile(req, path));
|
|
11503
11735
|
return fd;
|
|
11504
11736
|
}
|
|
11737
|
+
if (this.writesAt(req) && this.options.readonlyAt?.(path)) throw new KernelError("EROFS");
|
|
11505
11738
|
const open = { ...req, path };
|
|
11506
11739
|
if (req.exclusive) {
|
|
11507
11740
|
await nodes.createExclusive(path, this.newOwner());
|
|
@@ -11547,6 +11780,9 @@ var WasmProcess = class {
|
|
|
11547
11780
|
};
|
|
11548
11781
|
return fs.exclusive ? fs.exclusive(first.path, look) : look();
|
|
11549
11782
|
}
|
|
11783
|
+
writesAt(req) {
|
|
11784
|
+
return (req.flags & WRITE_ACCESS) !== 0 || req.truncate === true || req.create === true || req.exclusive === true;
|
|
11785
|
+
}
|
|
11550
11786
|
async mayOpen(req) {
|
|
11551
11787
|
const { guard: guard2 } = this.options;
|
|
11552
11788
|
const inherited2 = req.orphan || req.contents !== void 0 || req.handle !== void 0;
|
|
@@ -12328,6 +12564,7 @@ function processOptions(opts, header4, hooks, created3) {
|
|
|
12328
12564
|
...inherited(opts),
|
|
12329
12565
|
...opts.owners ? { owners: true } : {},
|
|
12330
12566
|
...opts.sealed ? { sealed: opts.sealed } : {},
|
|
12567
|
+
...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
|
|
12331
12568
|
...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
|
|
12332
12569
|
...opts.identity ? { identity: opts.identity } : {},
|
|
12333
12570
|
...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
|
|
@@ -15157,13 +15394,13 @@ function interpreter(head) {
|
|
|
15157
15394
|
function expandDefaults(defaults, env) {
|
|
15158
15395
|
const out = {};
|
|
15159
15396
|
for (const [key, value] of Object.entries(defaults ?? {})) {
|
|
15160
|
-
let
|
|
15397
|
+
let missing4 = false;
|
|
15161
15398
|
const expanded = value.replace(ENV_REFERENCE, (_, name2) => {
|
|
15162
15399
|
const set2 = Object.hasOwn(env, name2) ? env[name2] : void 0;
|
|
15163
|
-
if (set2 === void 0)
|
|
15400
|
+
if (set2 === void 0) missing4 = true;
|
|
15164
15401
|
return set2 ?? "";
|
|
15165
15402
|
});
|
|
15166
|
-
if (!
|
|
15403
|
+
if (!missing4) out[key] = expanded;
|
|
15167
15404
|
}
|
|
15168
15405
|
return out;
|
|
15169
15406
|
}
|
|
@@ -15210,6 +15447,8 @@ function concat5(chunks2) {
|
|
|
15210
15447
|
}
|
|
15211
15448
|
var Launcher = class {
|
|
15212
15449
|
fs;
|
|
15450
|
+
processFs;
|
|
15451
|
+
prefixes;
|
|
15213
15452
|
base;
|
|
15214
15453
|
createWorker;
|
|
15215
15454
|
modulesDir;
|
|
@@ -15336,6 +15575,10 @@ var Launcher = class {
|
|
|
15336
15575
|
}
|
|
15337
15576
|
);
|
|
15338
15577
|
this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
|
|
15578
|
+
if (options.protect?.writer) throw new Error(PROTECT_WRITER);
|
|
15579
|
+
this.prefixes = protectedPrefixes(options.protect?.paths ?? []);
|
|
15580
|
+
this.processFs = this.prefixes.length > 0 ? new ProtectedFs(this.prefixes).view(this.fs) : this.fs;
|
|
15581
|
+
options.fs.protect?.(this.prefixes);
|
|
15339
15582
|
this.users = new UserDb(this.fs, this.owners);
|
|
15340
15583
|
this.sudoer = new Sudo({
|
|
15341
15584
|
fs: this.fs,
|
|
@@ -15686,7 +15929,8 @@ var Launcher = class {
|
|
|
15686
15929
|
env: req.env,
|
|
15687
15930
|
cwd: req.cwd,
|
|
15688
15931
|
fds: req.fds,
|
|
15689
|
-
fs: this.
|
|
15932
|
+
fs: this.processFs,
|
|
15933
|
+
...this.prefixes.length > 0 ? { readonlyAt: (path) => isProtected(this.prefixes, path) } : {},
|
|
15690
15934
|
createWorker: this.createWorker,
|
|
15691
15935
|
onError: req.report,
|
|
15692
15936
|
spawner: this.spawner(pid, req.report),
|
|
@@ -15971,6 +16215,18 @@ var Launcher = class {
|
|
|
15971
16215
|
const keyed = () => this.jobs.fromKeyboard(send);
|
|
15972
16216
|
this.settling.deliver(sig, pgid, keyed, tty.reading ? void 0 : foreground);
|
|
15973
16217
|
}
|
|
16218
|
+
async resolvePrefixes() {
|
|
16219
|
+
for (const prefix of [...this.prefixes]) {
|
|
16220
|
+
const real = await this.base.realpath?.(prefix, true).catch(() => void 0);
|
|
16221
|
+
if (real !== void 0 && !this.prefixes.includes(real)) this.prefixes.push(real);
|
|
16222
|
+
}
|
|
16223
|
+
}
|
|
16224
|
+
guardMount(target) {
|
|
16225
|
+
const at = normalizePath(target);
|
|
16226
|
+
if (isProtected(this.prefixes, at) || holdsProtected(this.prefixes, at)) {
|
|
16227
|
+
throw fsError("EROFS", at);
|
|
16228
|
+
}
|
|
16229
|
+
}
|
|
15974
16230
|
umaskOf(pid) {
|
|
15975
16231
|
return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
|
|
15976
16232
|
}
|
|
@@ -16105,6 +16361,7 @@ var Launcher = class {
|
|
|
16105
16361
|
};
|
|
16106
16362
|
}
|
|
16107
16363
|
async mount(spec) {
|
|
16364
|
+
this.guardMount(spec.target);
|
|
16108
16365
|
const fsa = spec.type === "fsa" && !spec.source.startsWith("fsa:");
|
|
16109
16366
|
const entry = await this.mounts.mount(
|
|
16110
16367
|
fsa ? { ...spec, source: `fsa:${crypto.randomUUID()}` } : spec,
|
|
@@ -16123,6 +16380,7 @@ var Launcher = class {
|
|
|
16123
16380
|
[`/${UNLINKED_DIR}`, 448]
|
|
16124
16381
|
];
|
|
16125
16382
|
for (const [path, mode] of layout) {
|
|
16383
|
+
if (isProtected(this.prefixes, normalizePath(path))) continue;
|
|
16126
16384
|
const real = await this.realLayoutPath(path);
|
|
16127
16385
|
const fresh2 = real !== void 0 && !await this.base.hasOwner?.(real);
|
|
16128
16386
|
if (fresh2) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
|
|
@@ -16185,6 +16443,7 @@ var Launcher = class {
|
|
|
16185
16443
|
}
|
|
16186
16444
|
async processMount(pid, call, signal) {
|
|
16187
16445
|
const spec = mountCall(call);
|
|
16446
|
+
this.guardMount(spec.target);
|
|
16188
16447
|
await this.permit({ op: "mount", pid, ...spec, options: { ...spec.options } });
|
|
16189
16448
|
await this.mountsOwn(
|
|
16190
16449
|
this.credOf(pid),
|
|
@@ -16201,6 +16460,7 @@ var Launcher = class {
|
|
|
16201
16460
|
}
|
|
16202
16461
|
async umountAs(cred, target) {
|
|
16203
16462
|
const at = normalizePath(target);
|
|
16463
|
+
this.guardMount(at);
|
|
16204
16464
|
await this.mountsOwn(cred, at, async () => Number(this.mounts.mounted(at)?.options.uid));
|
|
16205
16465
|
this.umount(at);
|
|
16206
16466
|
}
|
|
@@ -16214,6 +16474,7 @@ var Launcher = class {
|
|
|
16214
16474
|
async processUmount(pid, target, flags2) {
|
|
16215
16475
|
const call = umountCall(target, flags2);
|
|
16216
16476
|
let mounted;
|
|
16477
|
+
this.guardMount(call.target);
|
|
16217
16478
|
for (; ; ) {
|
|
16218
16479
|
mounted = this.mounts.mounted(call.target);
|
|
16219
16480
|
await this.permit({
|
|
@@ -16228,6 +16489,7 @@ var Launcher = class {
|
|
|
16228
16489
|
this.umount(call.target, call.detach);
|
|
16229
16490
|
}
|
|
16230
16491
|
async prepare() {
|
|
16492
|
+
await this.resolvePrefixes();
|
|
16231
16493
|
for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
|
|
16232
16494
|
if (this.owners) await this.base.chmod("/tmp", 1023);
|
|
16233
16495
|
await this.provisionPnpmHome();
|
|
@@ -16284,7 +16546,7 @@ var Launcher = class {
|
|
|
16284
16546
|
const start2 = dir ?? "/";
|
|
16285
16547
|
const cwd = await physicalPathAsync(this.base, start2.startsWith("/") ? start2 : `/${start2}`);
|
|
16286
16548
|
const [file = ""] = argv;
|
|
16287
|
-
await this.base.mkdir(cwd, { recursive: true });
|
|
16549
|
+
if (!await this.base.exists(cwd)) await this.processFs.mkdir(cwd, { recursive: true });
|
|
16288
16550
|
const planned = await this.plan(file, argv, cwd, scope);
|
|
16289
16551
|
await this.mayExec(credOf(account), { file, cwd, pid: 0 }, planned);
|
|
16290
16552
|
return { cwd, file, planned };
|
|
@@ -17524,6 +17786,7 @@ async function createNodeKernel(options = {}) {
|
|
|
17524
17786
|
...options.hostname !== void 0 ? { hostname: options.hostname } : {},
|
|
17525
17787
|
...options.users !== void 0 ? { users: options.users } : {},
|
|
17526
17788
|
...options.onDenial ? { onDenial: options.onDenial } : {},
|
|
17789
|
+
...options.protect ? { protect: options.protect } : {},
|
|
17527
17790
|
...options.sealed ? { sealed: options.sealed } : {},
|
|
17528
17791
|
...options.approve ? { approve: options.approve } : {}
|
|
17529
17792
|
});
|