@ai-ecoverse/slicc-kernel 1.91.0 → 1.93.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 +18 -12
- package/dist/driver-worker.js +9 -7
- package/dist/driver-worker.js.map +2 -2
- package/dist/index.js +40 -7
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +851 -199
- package/dist/kernel-worker.js.map +4 -4
- package/dist/meta.js.map +2 -2
- package/dist/node-driver-worker.js +150 -11
- package/dist/node-driver-worker.js.map +4 -4
- package/dist/node-process-worker.js +912 -238
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +939 -208
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +2168 -1506
- package/dist/process-worker.js.map +4 -4
- package/package.json +6 -6
package/dist/node.js
CHANGED
|
@@ -451,6 +451,9 @@ async function present(fs, path, directory) {
|
|
|
451
451
|
}
|
|
452
452
|
async function finalOf(fs, walked, want, options) {
|
|
453
453
|
const perm = walked.perm;
|
|
454
|
+
if (options.follow === false && perm.link !== void 0) {
|
|
455
|
+
return [{ ...perm, mode: 511 }, false];
|
|
456
|
+
}
|
|
454
457
|
if ((want & X_OK) === 0 || options.directory !== void 0) {
|
|
455
458
|
return [perm, options.directory !== false];
|
|
456
459
|
}
|
|
@@ -622,6 +625,18 @@ async function chmodMode(g, path, mode) {
|
|
|
622
625
|
if (!g.dac.enforcing || !perm || g.cred.euid === 0 || inGroup(g.cred, perm.gid)) return mode;
|
|
623
626
|
return mode & ~S_ISGID2;
|
|
624
627
|
}
|
|
628
|
+
function heldChmodMode(g, path, st, mode) {
|
|
629
|
+
const perm = permOf(st);
|
|
630
|
+
if (g.cred.euid !== 0 && perm.uid !== g.cred.euid) deny(g, "fchmod", path, "EPERM");
|
|
631
|
+
if (!g.dac.enforcing || g.cred.euid === 0 || inGroup(g.cred, perm.gid)) return mode;
|
|
632
|
+
return mode & ~S_ISGID2;
|
|
633
|
+
}
|
|
634
|
+
function heldRetimable(g, path, st, now) {
|
|
635
|
+
const perm = permOf(st);
|
|
636
|
+
if (g.cred.euid === 0 || perm.uid === g.cred.euid) return;
|
|
637
|
+
if (!now) deny(g, "futimens", path, "EPERM");
|
|
638
|
+
else if (!permits(g.cred, perm, W_OK, false)) deny(g, "futimens", path);
|
|
639
|
+
}
|
|
625
640
|
async function retimable(g, op, path, now = false) {
|
|
626
641
|
const follow2 = op !== "lutimes";
|
|
627
642
|
if (!now) return void await ownerOnly(g, op, path, follow2);
|
|
@@ -1073,6 +1088,17 @@ var FdTable = class _FdTable {
|
|
|
1073
1088
|
this.status.delete(fd);
|
|
1074
1089
|
void Promise.resolve(previous?.release()).catch(() => void 0);
|
|
1075
1090
|
}
|
|
1091
|
+
async replaceAt(fd, file) {
|
|
1092
|
+
if (fd < 0 || fd >= _FdTable.MAX_FDS) {
|
|
1093
|
+
void Promise.resolve(file.release()).catch(() => void 0);
|
|
1094
|
+
throw new KernelError("EBADF");
|
|
1095
|
+
}
|
|
1096
|
+
const previous = this.fds.get(fd);
|
|
1097
|
+
this.fds.set(fd, file);
|
|
1098
|
+
this.cloexec.delete(fd);
|
|
1099
|
+
this.status.delete(fd);
|
|
1100
|
+
await Promise.resolve(previous?.release()).catch(() => void 0);
|
|
1101
|
+
}
|
|
1076
1102
|
dup(fd, min = 0) {
|
|
1077
1103
|
return this.install(this.get(fd).retain(), min);
|
|
1078
1104
|
}
|
|
@@ -1081,6 +1107,11 @@ var FdTable = class _FdTable {
|
|
|
1081
1107
|
if (oldFd !== newFd) this.installAt(newFd, file.retain());
|
|
1082
1108
|
return newFd;
|
|
1083
1109
|
}
|
|
1110
|
+
async dup2Clipboard(oldFd, newFd) {
|
|
1111
|
+
const file = this.get(oldFd);
|
|
1112
|
+
if (oldFd !== newFd) await this.replaceAt(newFd, file.retain());
|
|
1113
|
+
return newFd;
|
|
1114
|
+
}
|
|
1084
1115
|
close(fd) {
|
|
1085
1116
|
const file = this.get(fd);
|
|
1086
1117
|
this.fds.delete(fd);
|
|
@@ -2020,17 +2051,21 @@ function openWebSocket(Socket, request, withHeaders) {
|
|
|
2020
2051
|
} catch {
|
|
2021
2052
|
}
|
|
2022
2053
|
});
|
|
2023
|
-
socket.addEventListener(
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2054
|
+
socket.addEventListener(
|
|
2055
|
+
"close",
|
|
2056
|
+
(event) => {
|
|
2057
|
+
queue.end({ code: event.code, reason: event.reason, wasClean: event.wasClean === true });
|
|
2058
|
+
if (open) return;
|
|
2059
|
+
fail3(unopened(url, event.code));
|
|
2060
|
+
}
|
|
2061
|
+
);
|
|
2028
2062
|
});
|
|
2029
2063
|
}
|
|
2030
2064
|
|
|
2031
2065
|
// src/transport.ts
|
|
2032
2066
|
var SEND_BUFFER = 1024 * 1024;
|
|
2033
2067
|
var CLOSE_WAIT_MS = 5e3;
|
|
2068
|
+
var DNS_CODES = /* @__PURE__ */ new Set(["ENOTFOUND", "EAI_AGAIN", "EAI_NONAME", "EAI_FAIL", "ENODATA"]);
|
|
2034
2069
|
var NO_WEBSOCKET = "WebSocket not supported by this transport (fetchTransport needs { webSocket: true })";
|
|
2035
2070
|
var MAX_REQUEST_BODY = 64 * 1024 * 1024;
|
|
2036
2071
|
function joinHeaders(headers) {
|
|
@@ -2082,11 +2117,14 @@ function fetchTransport(options = {}) {
|
|
|
2082
2117
|
});
|
|
2083
2118
|
} catch (e) {
|
|
2084
2119
|
if (request.signal.aborted) throw e;
|
|
2120
|
+
const causeCode = e?.cause?.code;
|
|
2121
|
+
const dns = typeof causeCode === "string" && DNS_CODES.has(causeCode);
|
|
2085
2122
|
throw Object.assign(
|
|
2086
2123
|
new Error(
|
|
2087
2124
|
`fetch ${request.url} failed (unreachable, or not allowed by CORS)${options.hint ? `: ${options.hint}` : ""}`
|
|
2088
2125
|
),
|
|
2089
|
-
{ status: 502 }
|
|
2126
|
+
{ status: 502 },
|
|
2127
|
+
dns ? { code: causeCode } : {}
|
|
2090
2128
|
);
|
|
2091
2129
|
}
|
|
2092
2130
|
const headers2 = [];
|
|
@@ -2329,7 +2367,7 @@ function homeOf(home) {
|
|
|
2329
2367
|
if (typeof home !== "string" || !home.startsWith("/") || /[:\r\n]/.test(home)) {
|
|
2330
2368
|
throw fsError("EINVAL", `not a home directory: ${String(home)}`);
|
|
2331
2369
|
}
|
|
2332
|
-
return home;
|
|
2370
|
+
return normalizePath(home);
|
|
2333
2371
|
}
|
|
2334
2372
|
function freeId(users, groups) {
|
|
2335
2373
|
const taken = /* @__PURE__ */ new Set([...users.map((r) => r.uid), ...groups.map((r) => r.gid)]);
|
|
@@ -3220,12 +3258,36 @@ var MemoryMeta = class {
|
|
|
3220
3258
|
};
|
|
3221
3259
|
|
|
3222
3260
|
// src/fs/integrity.ts
|
|
3261
|
+
var SOURCE_SUFFIXES = [
|
|
3262
|
+
".js",
|
|
3263
|
+
".mjs",
|
|
3264
|
+
".cjs",
|
|
3265
|
+
".json",
|
|
3266
|
+
".py",
|
|
3267
|
+
".pyc",
|
|
3268
|
+
".pth",
|
|
3269
|
+
".zip",
|
|
3270
|
+
".so",
|
|
3271
|
+
".wasm",
|
|
3272
|
+
".sh"
|
|
3273
|
+
];
|
|
3274
|
+
var EXEC_OPS = { failed: "exec-integrity", unknown: "exec-unverified" };
|
|
3275
|
+
var READ_OPS = { failed: "read-integrity", unknown: "read-unverified" };
|
|
3223
3276
|
var KERNEL_CALLER = { pid: 0, uid: 0 };
|
|
3224
3277
|
function integrityOptions(options) {
|
|
3225
3278
|
if (options && typeof options.lookup !== "function") {
|
|
3226
3279
|
throw new TypeError("protect.integrity.lookup must be a function");
|
|
3227
3280
|
}
|
|
3228
3281
|
integrityUnknown(options);
|
|
3282
|
+
integritySources(options);
|
|
3283
|
+
}
|
|
3284
|
+
function integritySources(options) {
|
|
3285
|
+
const sources = options?.sources ?? SOURCE_SUFFIXES;
|
|
3286
|
+
const suffix = (s) => typeof s === "string" && /^\.[^/]+$/.test(s);
|
|
3287
|
+
if (!Array.isArray(sources) || !sources.every(suffix)) {
|
|
3288
|
+
throw new TypeError("protect.integrity.sources must be a list of suffixes such as .js");
|
|
3289
|
+
}
|
|
3290
|
+
return [...sources];
|
|
3229
3291
|
}
|
|
3230
3292
|
function integrityUnknown(options) {
|
|
3231
3293
|
const unknown = options?.unknown ?? "allow";
|
|
@@ -3247,16 +3309,21 @@ var Integrity = class {
|
|
|
3247
3309
|
unknown;
|
|
3248
3310
|
report;
|
|
3249
3311
|
unverified = /* @__PURE__ */ new Set();
|
|
3312
|
+
unread = /* @__PURE__ */ new Set();
|
|
3313
|
+
sources;
|
|
3314
|
+
passed = /* @__PURE__ */ new Set();
|
|
3315
|
+
generation = 0;
|
|
3250
3316
|
constructor(prefixes2, root, options, report) {
|
|
3251
3317
|
this.prefixes = prefixes2;
|
|
3252
3318
|
this.root = root;
|
|
3253
3319
|
this.lookup = options.lookup;
|
|
3254
3320
|
this.unknown = integrityUnknown(options);
|
|
3321
|
+
this.sources = integritySources(options);
|
|
3255
3322
|
this.report = report;
|
|
3256
3323
|
}
|
|
3257
|
-
async real(path, asked) {
|
|
3324
|
+
async real(path, asked, known) {
|
|
3258
3325
|
const at = normalizePath(path);
|
|
3259
|
-
const real = await this.root.realpath?.(at, true).catch(() => void 0) ?? at;
|
|
3326
|
+
const real = known ?? await this.root.realpath?.(at, true).catch(() => void 0) ?? at;
|
|
3260
3327
|
if (isProtected(this.prefixes, real)) return { real, name: real };
|
|
3261
3328
|
const names = asked === void 0 ? [] : await namesOf(this.root, asked);
|
|
3262
3329
|
if (real !== at) names.push(...await namesOf(this.root, at));
|
|
@@ -3281,6 +3348,51 @@ var Integrity = class {
|
|
|
3281
3348
|
const read = name2 !== real ? { bytes: await plain(), version: void 0 } : this.root.readVersioned ? await this.root.readVersioned(real) : { bytes: await this.root.readFileBuffer(real), version: void 0 };
|
|
3282
3349
|
const { bytes } = read;
|
|
3283
3350
|
const keyed = read.version === void 0 ? { bytes } : { bytes, key: `${real}\0${read.version}` };
|
|
3351
|
+
await this.verify(name2, bytes, caller, EXEC_OPS);
|
|
3352
|
+
return keyed;
|
|
3353
|
+
}
|
|
3354
|
+
get readsChecked() {
|
|
3355
|
+
return this.sources.length > 0;
|
|
3356
|
+
}
|
|
3357
|
+
async readSource(path, caller, plain) {
|
|
3358
|
+
const found = await this.source(path);
|
|
3359
|
+
return found === void 0 ? plain() : this.checked(found, caller, plain);
|
|
3360
|
+
}
|
|
3361
|
+
async openSource(path, caller, real) {
|
|
3362
|
+
const found = await this.source(path, real);
|
|
3363
|
+
if (found === void 0) return;
|
|
3364
|
+
if (found.name === found.real && this.root.version) {
|
|
3365
|
+
if (this.passed.has(`${found.real}\0${await this.root.version(found.real, true)}`)) return;
|
|
3366
|
+
}
|
|
3367
|
+
await this.checked(found, caller, () => this.root.readFileBuffer(found.real));
|
|
3368
|
+
}
|
|
3369
|
+
async checked(found, caller, plain) {
|
|
3370
|
+
const { real, name: name2 } = found;
|
|
3371
|
+
if (name2 !== real || !this.root.readVersioned) {
|
|
3372
|
+
const bytes2 = await plain();
|
|
3373
|
+
await this.verify(name2, bytes2, caller, READ_OPS);
|
|
3374
|
+
return bytes2;
|
|
3375
|
+
}
|
|
3376
|
+
const { bytes, version } = await this.root.readVersioned(real, true);
|
|
3377
|
+
await this.pass(`${real}\0${version}`, () => this.verify(name2, bytes, caller, READ_OPS));
|
|
3378
|
+
return bytes;
|
|
3379
|
+
}
|
|
3380
|
+
async pass(key, check) {
|
|
3381
|
+
if (this.passed.has(key)) return;
|
|
3382
|
+
const generation = this.generation;
|
|
3383
|
+
await check();
|
|
3384
|
+
if (generation === this.generation) this.passed.add(key);
|
|
3385
|
+
}
|
|
3386
|
+
async source(path, known) {
|
|
3387
|
+
if (this.sources.length === 0) return void 0;
|
|
3388
|
+
const found = await this.real(path, void 0, known);
|
|
3389
|
+
if (found === void 0) return void 0;
|
|
3390
|
+
const suffixed = (p) => this.sources.some((s) => p.endsWith(s));
|
|
3391
|
+
if (suffixed(found.real) || suffixed(found.name) || suffixed(normalizePath(path))) return found;
|
|
3392
|
+
const st = await this.root.stat(found.real).catch(() => void 0);
|
|
3393
|
+
return st?.isFile && ((st.mode ?? 0) & 73) !== 0 ? found : void 0;
|
|
3394
|
+
}
|
|
3395
|
+
async verify(name2, bytes, caller, ops) {
|
|
3284
3396
|
const actual = await sha256(bytes);
|
|
3285
3397
|
const denial = { pid: caller.pid, uid: caller.uid, path: name2, actual };
|
|
3286
3398
|
const tagged = caller.tag !== void 0 ? { tag: caller.tag } : {};
|
|
@@ -3288,27 +3400,33 @@ var Integrity = class {
|
|
|
3288
3400
|
try {
|
|
3289
3401
|
expected = await this.expected(name2);
|
|
3290
3402
|
} catch {
|
|
3291
|
-
this.say({ ...denial, ...tagged, op:
|
|
3403
|
+
this.say({ ...denial, ...tagged, op: ops.failed, errno: "EACCES" });
|
|
3292
3404
|
throw fsError("EACCES", `integrity lookup failed: ${name2}`);
|
|
3293
3405
|
}
|
|
3294
|
-
if (expected ===
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
}
|
|
3300
|
-
return keyed;
|
|
3406
|
+
if (expected === actual) return;
|
|
3407
|
+
if (expected === void 0 && this.unknown === "allow") {
|
|
3408
|
+
const seen = ops === EXEC_OPS ? this.unverified : this.unread;
|
|
3409
|
+
if (!seen.has(name2)) {
|
|
3410
|
+
seen.add(name2);
|
|
3411
|
+
this.say({ ...denial, ...tagged, op: ops.unknown, errno: "ENODATA" });
|
|
3301
3412
|
}
|
|
3302
|
-
|
|
3303
|
-
return keyed;
|
|
3413
|
+
return;
|
|
3304
3414
|
}
|
|
3305
3415
|
const known = expected !== void 0 ? { expected } : {};
|
|
3306
|
-
this.say({ ...denial, ...tagged, ...known, op:
|
|
3416
|
+
this.say({ ...denial, ...tagged, ...known, op: ops.failed, errno: "EACCES" });
|
|
3307
3417
|
throw fsError("EACCES", `integrity check failed: ${name2}`);
|
|
3308
3418
|
}
|
|
3309
3419
|
forget(path) {
|
|
3310
|
-
|
|
3311
|
-
|
|
3420
|
+
this.generation++;
|
|
3421
|
+
for (const seen of [this.unverified, this.unread]) {
|
|
3422
|
+
for (const name2 of [...seen]) {
|
|
3423
|
+
if (path === void 0 || isProtected([path], name2)) seen.delete(name2);
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
for (const key of [...this.passed]) {
|
|
3427
|
+
if (path === void 0 || isProtected([path], key.slice(0, key.indexOf("\0")))) {
|
|
3428
|
+
this.passed.delete(key);
|
|
3429
|
+
}
|
|
3312
3430
|
}
|
|
3313
3431
|
}
|
|
3314
3432
|
async expected(path) {
|
|
@@ -3507,15 +3625,15 @@ var ProtectedFs = class {
|
|
|
3507
3625
|
}
|
|
3508
3626
|
} : {}
|
|
3509
3627
|
};
|
|
3510
|
-
const
|
|
3628
|
+
const view2 = {};
|
|
3511
3629
|
for (let at = fs; at && at !== Object.prototype; at = Object.getPrototypeOf(at)) {
|
|
3512
3630
|
for (const name2 of Object.getOwnPropertyNames(at)) {
|
|
3513
|
-
if (name2 === "constructor" || Object.hasOwn(
|
|
3631
|
+
if (name2 === "constructor" || Object.hasOwn(view2, name2)) continue;
|
|
3514
3632
|
const value = fs[name2];
|
|
3515
|
-
|
|
3633
|
+
view2[name2] = typeof value === "function" ? value.bind(fs) : value;
|
|
3516
3634
|
}
|
|
3517
3635
|
}
|
|
3518
|
-
return Object.assign(
|
|
3636
|
+
return Object.assign(view2, own);
|
|
3519
3637
|
}
|
|
3520
3638
|
};
|
|
3521
3639
|
|
|
@@ -3634,6 +3752,23 @@ var RemoteWatch = class {
|
|
|
3634
3752
|
}
|
|
3635
3753
|
};
|
|
3636
3754
|
|
|
3755
|
+
// src/fs/view.ts
|
|
3756
|
+
function view(target, overrides) {
|
|
3757
|
+
const keys = new Set(Object.keys(overrides));
|
|
3758
|
+
return new Proxy(target, {
|
|
3759
|
+
get(t, prop, receiver) {
|
|
3760
|
+
if (keys.has(prop)) return overrides[prop];
|
|
3761
|
+
return Reflect.get(t, prop, receiver);
|
|
3762
|
+
},
|
|
3763
|
+
set(t, prop, value) {
|
|
3764
|
+
return Reflect.set(t, prop, value);
|
|
3765
|
+
},
|
|
3766
|
+
getPrototypeOf(t) {
|
|
3767
|
+
return Reflect.getPrototypeOf(t);
|
|
3768
|
+
}
|
|
3769
|
+
});
|
|
3770
|
+
}
|
|
3771
|
+
|
|
3637
3772
|
// src/fs/opfs.ts
|
|
3638
3773
|
var S_IFDIR = 16384;
|
|
3639
3774
|
var S_IFREG = 32768;
|
|
@@ -3769,11 +3904,16 @@ var OpfsFs = class {
|
|
|
3769
3904
|
remote;
|
|
3770
3905
|
ranged;
|
|
3771
3906
|
owners = false;
|
|
3907
|
+
rejectLinks = false;
|
|
3772
3908
|
mounts = {};
|
|
3909
|
+
locks;
|
|
3910
|
+
lockPrefix;
|
|
3773
3911
|
constructor(root, meta = new MemoryMeta(), channel, options = {}) {
|
|
3774
3912
|
this.root = root;
|
|
3775
3913
|
this.meta = sidecar(meta);
|
|
3776
3914
|
this.ranged = options.ranged === true;
|
|
3915
|
+
this.locks = options.locks === void 0 ? lockManager() : options.locks ?? void 0;
|
|
3916
|
+
this.lockPrefix = `slicc-excl:${channel ?? ""}:`;
|
|
3777
3917
|
this.channel = channel === void 0 ? void 0 : new BroadcastChannel(channel);
|
|
3778
3918
|
this.remote = channel === void 0 ? void 0 : new RemoteWatch(`${channel}:watch`);
|
|
3779
3919
|
if (this.channel) {
|
|
@@ -3800,12 +3940,13 @@ var OpfsFs = class {
|
|
|
3800
3940
|
return this.remote?.announce(paths) ?? Promise.resolve();
|
|
3801
3941
|
}
|
|
3802
3942
|
committed(target) {
|
|
3943
|
+
this.fileTimes.delete(target);
|
|
3803
3944
|
if (this.remote?.watched()) this.remote.wrote(target);
|
|
3804
3945
|
}
|
|
3805
3946
|
counted = [];
|
|
3806
|
-
async stamp(path) {
|
|
3947
|
+
async stamp(path, cached = false) {
|
|
3807
3948
|
const paths = [path, "/"];
|
|
3808
|
-
const [entry, root] = this.meta.fresh ? await this.meta.fresh(paths) : await this.meta.get(paths);
|
|
3949
|
+
const [entry, root] = this.meta.fresh && !cached ? await this.meta.fresh(paths) : await this.meta.get(paths);
|
|
3809
3950
|
return `${entry?.writes ?? 0}:${root?.epoch ?? 0}`;
|
|
3810
3951
|
}
|
|
3811
3952
|
async epoch() {
|
|
@@ -3904,13 +4045,25 @@ var OpfsFs = class {
|
|
|
3904
4045
|
this.counted = versions ? prefixes2 : [];
|
|
3905
4046
|
}
|
|
3906
4047
|
unguarded() {
|
|
3907
|
-
return
|
|
4048
|
+
return view(this, { guarded: [] });
|
|
4049
|
+
}
|
|
4050
|
+
noLinks() {
|
|
4051
|
+
return view(this, { rejectLinks: true });
|
|
3908
4052
|
}
|
|
3909
4053
|
refuse(path, holding = false) {
|
|
3910
4054
|
if (isProtected(this.guarded, path) || holding && holdsProtected(this.guarded, path)) {
|
|
3911
4055
|
throw fsError("EROFS", path);
|
|
3912
4056
|
}
|
|
3913
4057
|
}
|
|
4058
|
+
async locked(physicalPath, op) {
|
|
4059
|
+
if (!this.locks) return op();
|
|
4060
|
+
const key = `${this.lockPrefix}${physicalPath}`;
|
|
4061
|
+
let result;
|
|
4062
|
+
await this.locks.request(key, async () => {
|
|
4063
|
+
result = await op();
|
|
4064
|
+
});
|
|
4065
|
+
return result;
|
|
4066
|
+
}
|
|
3914
4067
|
async chown(path, change) {
|
|
3915
4068
|
if (!this.owners) return;
|
|
3916
4069
|
if (change.kind === "new") {
|
|
@@ -4042,8 +4195,15 @@ var OpfsFs = class {
|
|
|
4042
4195
|
const link2 = await this.linkAlong(current);
|
|
4043
4196
|
if (!link2) throw err;
|
|
4044
4197
|
if (link2.path === current && !follow2) return { path: current, link: link2 };
|
|
4198
|
+
if (this.rejectLinks) throw fsError("ELOOP", path);
|
|
4045
4199
|
const target = `${link2.link}${current.slice(link2.path.length)}`;
|
|
4046
|
-
|
|
4200
|
+
if (target.split("/").includes("..")) {
|
|
4201
|
+
const resolved2 = await this.physical(parentOf4(link2.path), target, hops + 1, path);
|
|
4202
|
+
current = resolved2.path;
|
|
4203
|
+
hops = resolved2.hops;
|
|
4204
|
+
} else {
|
|
4205
|
+
current = resolvePath(parentOf4(link2.path), target);
|
|
4206
|
+
}
|
|
4047
4207
|
this.crossing(path, current);
|
|
4048
4208
|
}
|
|
4049
4209
|
}
|
|
@@ -4052,7 +4212,8 @@ var OpfsFs = class {
|
|
|
4052
4212
|
async physical(base, path, used, of) {
|
|
4053
4213
|
let done4 = [];
|
|
4054
4214
|
let todo = (path.startsWith("/") ? path : `${base}/${path}`).split("/").filter((p) => p !== "" && p !== ".");
|
|
4055
|
-
|
|
4215
|
+
let hops = used;
|
|
4216
|
+
while (todo.length > 0) {
|
|
4056
4217
|
const part = todo.shift();
|
|
4057
4218
|
if (part === "..") done4.pop();
|
|
4058
4219
|
else if (!todo.includes("..")) done4.push(part);
|
|
@@ -4062,6 +4223,7 @@ var OpfsFs = class {
|
|
|
4062
4223
|
const st = await this.lstat(at);
|
|
4063
4224
|
if (!st.isSymbolicLink && !st.isDirectory) throw fsError("ENOTDIR", at);
|
|
4064
4225
|
if (!st.isSymbolicLink) done4.push(part);
|
|
4226
|
+
else if (this.rejectLinks) throw fsError("ELOOP", of);
|
|
4065
4227
|
else if (++hops > MAX_LINKS5) throw fsError("ELOOP", path);
|
|
4066
4228
|
else {
|
|
4067
4229
|
const link2 = await this.readlink(at);
|
|
@@ -4070,7 +4232,7 @@ var OpfsFs = class {
|
|
|
4070
4232
|
}
|
|
4071
4233
|
}
|
|
4072
4234
|
}
|
|
4073
|
-
return `/${done4.join("/")}
|
|
4235
|
+
return { path: `/${done4.join("/")}`, hops };
|
|
4074
4236
|
}
|
|
4075
4237
|
mode(path, directory, meta) {
|
|
4076
4238
|
const fallback = directory || EXECUTABLE.test(path) ? 493 : 420;
|
|
@@ -4114,6 +4276,11 @@ var OpfsFs = class {
|
|
|
4114
4276
|
return entry.timesAt >= ms;
|
|
4115
4277
|
}
|
|
4116
4278
|
movePending(from, to) {
|
|
4279
|
+
if (to !== void 0) {
|
|
4280
|
+
for (const path of [...this.fileTimes.keys()]) {
|
|
4281
|
+
if (within2(path, to)) this.fileTimes.delete(path);
|
|
4282
|
+
}
|
|
4283
|
+
}
|
|
4117
4284
|
for (const [path, ms] of [...this.fileTimes]) {
|
|
4118
4285
|
if (!within2(path, from)) continue;
|
|
4119
4286
|
this.fileTimes.delete(path);
|
|
@@ -4121,8 +4288,7 @@ var OpfsFs = class {
|
|
|
4121
4288
|
}
|
|
4122
4289
|
}
|
|
4123
4290
|
flushing = {};
|
|
4124
|
-
touch(...dirs) {
|
|
4125
|
-
const now = Date.now();
|
|
4291
|
+
touch(now, ...dirs) {
|
|
4126
4292
|
for (const dir of dirs) {
|
|
4127
4293
|
this.dirTimes.set(dir, now);
|
|
4128
4294
|
this.dirDirty.add(dir);
|
|
@@ -4235,12 +4401,12 @@ var OpfsFs = class {
|
|
|
4235
4401
|
false
|
|
4236
4402
|
);
|
|
4237
4403
|
}
|
|
4238
|
-
async readVersioned(path) {
|
|
4404
|
+
async readVersioned(path, cached = false) {
|
|
4239
4405
|
const handle = await this.existing(path);
|
|
4240
4406
|
return this.retrying(
|
|
4241
4407
|
path,
|
|
4242
4408
|
async () => {
|
|
4243
|
-
const stamp = await this.stamp(path);
|
|
4409
|
+
const stamp = await this.stamp(path, cached);
|
|
4244
4410
|
const file = await handle.getFile();
|
|
4245
4411
|
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
4246
4412
|
return { bytes, version: `${file.lastModified}:${file.size}:${stamp}` };
|
|
@@ -4248,12 +4414,12 @@ var OpfsFs = class {
|
|
|
4248
4414
|
false
|
|
4249
4415
|
);
|
|
4250
4416
|
}
|
|
4251
|
-
async version(path) {
|
|
4417
|
+
async version(path, cached = false) {
|
|
4252
4418
|
const handle = await this.existing(path);
|
|
4253
4419
|
return this.retrying(
|
|
4254
4420
|
path,
|
|
4255
4421
|
async () => {
|
|
4256
|
-
const stamp = await this.stamp(path);
|
|
4422
|
+
const stamp = await this.stamp(path, cached);
|
|
4257
4423
|
const file = await handle.getFile();
|
|
4258
4424
|
return `${file.lastModified}:${file.size}:${stamp}`;
|
|
4259
4425
|
},
|
|
@@ -4338,7 +4504,7 @@ var OpfsFs = class {
|
|
|
4338
4504
|
dir,
|
|
4339
4505
|
(parent) => parent.getFileHandle(name2, { create: true })
|
|
4340
4506
|
);
|
|
4341
|
-
if (!existed) this.touch(parentOf4(target));
|
|
4507
|
+
if (!existed) this.touch(Date.now(), parentOf4(target));
|
|
4342
4508
|
return [handle, existed, target];
|
|
4343
4509
|
} catch (err) {
|
|
4344
4510
|
const code = err?.name === "TypeMismatchError" ? "EISDIR" : null;
|
|
@@ -4393,6 +4559,7 @@ var OpfsFs = class {
|
|
|
4393
4559
|
);
|
|
4394
4560
|
}
|
|
4395
4561
|
async mkdir(path, options = {}) {
|
|
4562
|
+
if (!options.recursive) return this.mkdirExcl(path, options.owner);
|
|
4396
4563
|
let found;
|
|
4397
4564
|
try {
|
|
4398
4565
|
found = await this.locate(path, true);
|
|
@@ -4401,28 +4568,75 @@ var OpfsFs = class {
|
|
|
4401
4568
|
}
|
|
4402
4569
|
if (found) {
|
|
4403
4570
|
const directory = found.handle?.kind === "directory";
|
|
4404
|
-
if (
|
|
4405
|
-
throw fsError(
|
|
4571
|
+
if (directory) return;
|
|
4572
|
+
throw fsError("ENOTDIR", path);
|
|
4406
4573
|
}
|
|
4407
4574
|
const [entry] = await this.meta.get([path]);
|
|
4408
4575
|
if (entry?.link !== void 0) throw fsError("EEXIST", path);
|
|
4409
4576
|
const { owner } = options;
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
await this.mkdir(parentOf4(path), { recursive: true, ...above ? { owner: above } : {} });
|
|
4418
|
-
}
|
|
4577
|
+
const mode = owner?.parents;
|
|
4578
|
+
const above = owner && {
|
|
4579
|
+
uid: owner.uid,
|
|
4580
|
+
gid: owner.gid,
|
|
4581
|
+
...mode !== void 0 ? { mode, parents: mode } : {}
|
|
4582
|
+
};
|
|
4583
|
+
await this.mkdir(parentOf4(path), { recursive: true, ...above ? { owner: above } : {} });
|
|
4419
4584
|
const parent = await this.locate(parentOf4(path), true);
|
|
4420
4585
|
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
4421
4586
|
this.refuse(resolvePath(parent.path, baseOf(path)));
|
|
4422
4587
|
await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
|
|
4588
|
+
const now = Date.now();
|
|
4423
4589
|
const made2 = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
|
|
4424
4590
|
await this.stampNew(made2, owner, true);
|
|
4425
|
-
this.touch(parent.path, made2);
|
|
4591
|
+
this.touch(now, parent.path, made2);
|
|
4592
|
+
}
|
|
4593
|
+
async mkdirExcl(path, owner) {
|
|
4594
|
+
const parent = await this.locate(parentOf4(path), true);
|
|
4595
|
+
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
4596
|
+
const dir = parent.handle;
|
|
4597
|
+
const name2 = baseOf(path);
|
|
4598
|
+
const physical = resolvePath(parent.path, name2);
|
|
4599
|
+
await this.locked(physical, async () => {
|
|
4600
|
+
if (await this.handleExists(dir, name2)) throw fsError("EEXIST", path);
|
|
4601
|
+
const freshMeta = this.meta.fresh ? await this.meta.fresh([path]) : await this.meta.get([path]);
|
|
4602
|
+
if (freshMeta[0]?.link !== void 0) throw fsError("EEXIST", path);
|
|
4603
|
+
this.forget(physical);
|
|
4604
|
+
this.refuse(physical);
|
|
4605
|
+
await dir.getDirectoryHandle(name2, { create: true });
|
|
4606
|
+
const now = Date.now();
|
|
4607
|
+
const made2 = `${parent.path === "/" ? "" : parent.path}/${name2}`;
|
|
4608
|
+
await this.stampNew(made2, owner, true);
|
|
4609
|
+
this.touch(now, parent.path, made2);
|
|
4610
|
+
});
|
|
4611
|
+
}
|
|
4612
|
+
async createExclusive(path, owner) {
|
|
4613
|
+
const parent = await this.locate(parentOf4(path), true);
|
|
4614
|
+
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
4615
|
+
const dir = parent.handle;
|
|
4616
|
+
const name2 = baseOf(path);
|
|
4617
|
+
const physical = resolvePath(parent.path, name2);
|
|
4618
|
+
await this.locked(physical, async () => {
|
|
4619
|
+
if (await this.handleExists(dir, name2)) throw fsError("EEXIST", path);
|
|
4620
|
+
const freshMeta = this.meta.fresh ? await this.meta.fresh([path]) : await this.meta.get([path]);
|
|
4621
|
+
if (freshMeta[0] !== void 0) throw fsError("EEXIST", path);
|
|
4622
|
+
this.forget(physical);
|
|
4623
|
+
this.refuse(physical);
|
|
4624
|
+
await dir.getFileHandle(name2, { create: true });
|
|
4625
|
+
const now = Date.now();
|
|
4626
|
+
const made2 = `${parent.path === "/" ? "" : parent.path}/${name2}`;
|
|
4627
|
+
await this.stampNew(made2, owner, false);
|
|
4628
|
+
this.touch(now, parent.path, made2);
|
|
4629
|
+
});
|
|
4630
|
+
}
|
|
4631
|
+
async handleExists(dir, name2) {
|
|
4632
|
+
try {
|
|
4633
|
+
await dir.getDirectoryHandle(name2);
|
|
4634
|
+
return true;
|
|
4635
|
+
} catch (err) {
|
|
4636
|
+
if (err?.name === "TypeMismatchError") return true;
|
|
4637
|
+
if (err?.name === "NotFoundError") return false;
|
|
4638
|
+
throw err;
|
|
4639
|
+
}
|
|
4426
4640
|
}
|
|
4427
4641
|
async rm(path, options = {}) {
|
|
4428
4642
|
let found;
|
|
@@ -4433,8 +4647,8 @@ var OpfsFs = class {
|
|
|
4433
4647
|
if (options.force && error.code === "ENOENT") return;
|
|
4434
4648
|
throw error;
|
|
4435
4649
|
}
|
|
4436
|
-
this.refuse(found.path, true);
|
|
4437
4650
|
if (options.file && found.handle?.kind === "directory") throw fsError("EISDIR", path);
|
|
4651
|
+
this.refuse(found.path, true);
|
|
4438
4652
|
if (found.handle) {
|
|
4439
4653
|
const [dir, name2] = await this.parent(found.path);
|
|
4440
4654
|
try {
|
|
@@ -4445,22 +4659,23 @@ var OpfsFs = class {
|
|
|
4445
4659
|
} catch (err) {
|
|
4446
4660
|
throw translate(err, path);
|
|
4447
4661
|
}
|
|
4662
|
+
const now = Date.now();
|
|
4448
4663
|
const file = found.handle.kind === "file";
|
|
4449
4664
|
if (!file || this.remote?.watched()) this.changed(found.path);
|
|
4450
4665
|
else this.forget(found.path);
|
|
4451
4666
|
this.movePending(found.path);
|
|
4452
4667
|
if (file && this.meta.absent?.(found.path)) {
|
|
4453
|
-
this.touch(parentOf4(found.path));
|
|
4668
|
+
this.touch(now, parentOf4(found.path));
|
|
4454
4669
|
return;
|
|
4455
4670
|
}
|
|
4456
4671
|
if (file && this.meta.removeFile) {
|
|
4457
4672
|
await this.meta.removeFile(found.path);
|
|
4458
|
-
this.touch(parentOf4(found.path));
|
|
4673
|
+
this.touch(now, parentOf4(found.path));
|
|
4459
4674
|
return;
|
|
4460
4675
|
}
|
|
4461
4676
|
}
|
|
4462
4677
|
await this.meta.remove([found.path]);
|
|
4463
|
-
this.touch(parentOf4(found.path));
|
|
4678
|
+
this.touch(Date.now(), parentOf4(found.path));
|
|
4464
4679
|
}
|
|
4465
4680
|
links() {
|
|
4466
4681
|
return false;
|
|
@@ -4468,11 +4683,14 @@ var OpfsFs = class {
|
|
|
4468
4683
|
async link(from, to, follow2) {
|
|
4469
4684
|
await (follow2 ? this.stat(from) : this.lstat(from));
|
|
4470
4685
|
if (!(await this.stat(parentOf4(to))).isDirectory) throw fsError("ENOTDIR", to);
|
|
4471
|
-
|
|
4686
|
+
const taken = await this.lstat(to).then(
|
|
4472
4687
|
() => true,
|
|
4473
|
-
() =>
|
|
4474
|
-
|
|
4475
|
-
|
|
4688
|
+
(err) => {
|
|
4689
|
+
if (missing2(err)) return false;
|
|
4690
|
+
throw err;
|
|
4691
|
+
}
|
|
4692
|
+
);
|
|
4693
|
+
if (taken) throw fsError("EEXIST", to);
|
|
4476
4694
|
throw fsError("EPERM", `${from}: this file system has no hard links`);
|
|
4477
4695
|
}
|
|
4478
4696
|
async rename(from, to) {
|
|
@@ -4512,7 +4730,7 @@ var OpfsFs = class {
|
|
|
4512
4730
|
if (source.link) {
|
|
4513
4731
|
if (target?.handle) await this.rm(destination);
|
|
4514
4732
|
await this.meta.move(source.path, destination, carry);
|
|
4515
|
-
this.touch(parentOf4(source.path), parent.path);
|
|
4733
|
+
this.touch(Date.now(), parentOf4(source.path), parent.path);
|
|
4516
4734
|
return;
|
|
4517
4735
|
}
|
|
4518
4736
|
const dir = parent.handle;
|
|
@@ -4535,7 +4753,7 @@ var OpfsFs = class {
|
|
|
4535
4753
|
}
|
|
4536
4754
|
if (copied) await this.restamp(destination, timed2);
|
|
4537
4755
|
this.changed(source.path, destination);
|
|
4538
|
-
this.touch(parentOf4(source.path), parent.path);
|
|
4756
|
+
this.touch(Date.now(), parentOf4(source.path), parent.path);
|
|
4539
4757
|
}
|
|
4540
4758
|
async modified(path) {
|
|
4541
4759
|
const handle = await this.handle(path);
|
|
@@ -4605,7 +4823,7 @@ var OpfsFs = class {
|
|
|
4605
4823
|
...fields
|
|
4606
4824
|
}))
|
|
4607
4825
|
);
|
|
4608
|
-
this.touch(parent.path);
|
|
4826
|
+
this.touch(Date.now(), parent.path);
|
|
4609
4827
|
}
|
|
4610
4828
|
async realpath(path, follow2) {
|
|
4611
4829
|
return (await this.locate(path, follow2)).path;
|
|
@@ -5259,15 +5477,15 @@ function frame(opcode, payload) {
|
|
|
5259
5477
|
const n = payload.length;
|
|
5260
5478
|
const size = n < 126 ? 2 : n < 65536 ? 4 : 10;
|
|
5261
5479
|
const out = new Uint8Array(size + n);
|
|
5262
|
-
const
|
|
5480
|
+
const view2 = new DataView(out.buffer);
|
|
5263
5481
|
out[0] = 128 | opcode;
|
|
5264
5482
|
if (n < 126) out[1] = n;
|
|
5265
5483
|
else if (n < 65536) {
|
|
5266
5484
|
out[1] = 126;
|
|
5267
|
-
|
|
5485
|
+
view2.setUint16(2, n);
|
|
5268
5486
|
} else {
|
|
5269
5487
|
out[1] = 127;
|
|
5270
|
-
|
|
5488
|
+
view2.setBigUint64(2, BigInt(n));
|
|
5271
5489
|
}
|
|
5272
5490
|
out.set(payload, size);
|
|
5273
5491
|
return out;
|
|
@@ -5389,9 +5607,9 @@ var WsReader = class {
|
|
|
5389
5607
|
if (short < 126) return short;
|
|
5390
5608
|
const ext = new Uint8Array(short === 126 ? 2 : 8);
|
|
5391
5609
|
await fill(this.incoming, ext, signal);
|
|
5392
|
-
const
|
|
5393
|
-
if (short === 126) return
|
|
5394
|
-
const long =
|
|
5610
|
+
const view2 = new DataView(ext.buffer);
|
|
5611
|
+
if (short === 126) return view2.getUint16(0);
|
|
5612
|
+
const long = view2.getBigUint64(0);
|
|
5395
5613
|
return long > BigInt(this.max) ? this.max + 1 : Number(long);
|
|
5396
5614
|
}
|
|
5397
5615
|
take(opcode, fin, payload) {
|
|
@@ -7571,6 +7789,7 @@ var ClipboardChannel = class {
|
|
|
7571
7789
|
size = 0;
|
|
7572
7790
|
wrote = false;
|
|
7573
7791
|
failed = false;
|
|
7792
|
+
aborted = false;
|
|
7574
7793
|
constructor(broker, access2, stamp) {
|
|
7575
7794
|
this.broker = broker;
|
|
7576
7795
|
this.access = access2;
|
|
@@ -7625,11 +7844,14 @@ var ClipboardChannel = class {
|
|
|
7625
7844
|
poll() {
|
|
7626
7845
|
return READY;
|
|
7627
7846
|
}
|
|
7847
|
+
abort() {
|
|
7848
|
+
this.aborted = true;
|
|
7849
|
+
}
|
|
7628
7850
|
async close() {
|
|
7629
7851
|
this.snapshot = void 0;
|
|
7630
7852
|
this.taken = void 0;
|
|
7631
7853
|
const commits = this.access === "write" || this.access === "both" && this.wrote;
|
|
7632
|
-
if (!commits || this.failed) return;
|
|
7854
|
+
if (!commits || this.failed || this.aborted) return;
|
|
7633
7855
|
const bytes = joined2(this.chunks, this.size);
|
|
7634
7856
|
this.chunks = [];
|
|
7635
7857
|
this.size = 0;
|
|
@@ -7872,6 +8094,15 @@ function parseSize(text) {
|
|
|
7872
8094
|
if (!match) throw errnoError("EINVAL", `not a size: ${text}`);
|
|
7873
8095
|
return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
|
|
7874
8096
|
}
|
|
8097
|
+
function linkedTo(mount, path) {
|
|
8098
|
+
const own = mount.attrs.get(path)?.value;
|
|
8099
|
+
const ino = own?.kind === "directory" ? void 0 : own?.ino;
|
|
8100
|
+
return (attr2) => {
|
|
8101
|
+
if (!attr2 || attr2.kind === "directory") return false;
|
|
8102
|
+
if (ino !== void 0) return attr2.ino === ino;
|
|
8103
|
+
return own?.kind !== "directory" && (attr2.nlink ?? 1) > 1;
|
|
8104
|
+
};
|
|
8105
|
+
}
|
|
7875
8106
|
var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
7876
8107
|
var HeldSet = class extends Set {
|
|
7877
8108
|
idle;
|
|
@@ -8313,10 +8544,13 @@ var MountTable = class {
|
|
|
8313
8544
|
}
|
|
8314
8545
|
forget(mount, path) {
|
|
8315
8546
|
mount.epoch++;
|
|
8316
|
-
|
|
8547
|
+
const linked2 = mount.caps.links ? linkedTo(mount, path) : () => false;
|
|
8548
|
+
for (const [key, cached] of [...mount.attrs])
|
|
8549
|
+
if (within5(key, path) || linked2(cached.value)) mount.attrs.delete(key);
|
|
8317
8550
|
mount.lists.delete(path);
|
|
8318
8551
|
mount.lists.delete(parentOf7(path));
|
|
8319
|
-
for (const key of [...mount.lists
|
|
8552
|
+
for (const [key, cached] of [...mount.lists])
|
|
8553
|
+
if (within5(key, path) || cached.value.some((e) => linked2(e.attr))) mount.lists.delete(key);
|
|
8320
8554
|
}
|
|
8321
8555
|
async getattr(mount, path, rel2, settled = false) {
|
|
8322
8556
|
for (let round = 1; ; round++) {
|
|
@@ -8365,12 +8599,64 @@ function noteExtent(extents, from, to) {
|
|
|
8365
8599
|
last[1] = Math.max(last[1], to);
|
|
8366
8600
|
} else extents.push([from, to]);
|
|
8367
8601
|
}
|
|
8368
|
-
var
|
|
8602
|
+
var RELINKED = [
|
|
8603
|
+
["readFileBuffer", [0]],
|
|
8604
|
+
["writeFile", [0]],
|
|
8605
|
+
["pread", [0]],
|
|
8606
|
+
["pwrite", [0]],
|
|
8607
|
+
["truncate", [0]],
|
|
8608
|
+
["lstat", [0]],
|
|
8609
|
+
["stat", [0]],
|
|
8610
|
+
["readdir", [0]],
|
|
8611
|
+
["readdirStat", [0]],
|
|
8612
|
+
["mkdir", [0]],
|
|
8613
|
+
["rm", [0]],
|
|
8614
|
+
["rename", [0, 1]],
|
|
8615
|
+
["symlink", [1]],
|
|
8616
|
+
["readlink", [0]],
|
|
8617
|
+
["chown", [0]],
|
|
8618
|
+
["chmod", [0]],
|
|
8619
|
+
["utimes", [0]],
|
|
8620
|
+
["lutimes", [0]]
|
|
8621
|
+
];
|
|
8622
|
+
var MountFs = class _MountFs {
|
|
8369
8623
|
table;
|
|
8370
8624
|
base;
|
|
8371
|
-
|
|
8625
|
+
rejectLinks;
|
|
8626
|
+
constructor(table, base, rejectLinks = false) {
|
|
8372
8627
|
this.table = table;
|
|
8373
8628
|
this.base = base;
|
|
8629
|
+
this.rejectLinks = rejectLinks;
|
|
8630
|
+
}
|
|
8631
|
+
static {
|
|
8632
|
+
const proto = _MountFs.prototype;
|
|
8633
|
+
for (const [name2, at] of RELINKED) {
|
|
8634
|
+
const run2 = proto[name2];
|
|
8635
|
+
proto[name2] = async function(...args) {
|
|
8636
|
+
try {
|
|
8637
|
+
return await run2.apply(this, args);
|
|
8638
|
+
} catch (err) {
|
|
8639
|
+
const moved2 = await this.relinked(err, args, at);
|
|
8640
|
+
if (!moved2) throw err;
|
|
8641
|
+
return this[name2](...moved2);
|
|
8642
|
+
}
|
|
8643
|
+
};
|
|
8644
|
+
}
|
|
8645
|
+
}
|
|
8646
|
+
async relinked(err, args, at) {
|
|
8647
|
+
if (err?.code !== "ENOTDIR") return void 0;
|
|
8648
|
+
const moved2 = [...args];
|
|
8649
|
+
let changed = false;
|
|
8650
|
+
for (const i of at) {
|
|
8651
|
+
const path = args[i];
|
|
8652
|
+
if (typeof path !== "string" || !this.at(path)) continue;
|
|
8653
|
+
const named = await this.named(path).catch(() => void 0);
|
|
8654
|
+
if (named === void 0 || named === normalizePath(path)) continue;
|
|
8655
|
+
if (this.rejectLinks) throw errnoError("ELOOP", path);
|
|
8656
|
+
moved2[i] = named;
|
|
8657
|
+
changed = true;
|
|
8658
|
+
}
|
|
8659
|
+
return changed ? moved2 : void 0;
|
|
8374
8660
|
}
|
|
8375
8661
|
at(path) {
|
|
8376
8662
|
return this.table.locate(normalizePath(path));
|
|
@@ -8421,6 +8707,7 @@ var MountFs = class {
|
|
|
8421
8707
|
throw err;
|
|
8422
8708
|
}
|
|
8423
8709
|
if (attr2.kind !== "symlink") return real;
|
|
8710
|
+
if (this.rejectLinks) throw errnoError("ELOOP", path);
|
|
8424
8711
|
if (hops >= MAX_LINKS7) throw errnoError("ELOOP", path);
|
|
8425
8712
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
8426
8713
|
return this.follow(this.base.resolvePath(parentOf7(real), target), hops + 1, create);
|
|
@@ -8825,6 +9112,7 @@ var MountFs = class {
|
|
|
8825
9112
|
}
|
|
8826
9113
|
const { mount, rel: rel2 } = found;
|
|
8827
9114
|
const real = normalizePath(path);
|
|
9115
|
+
if (options.file && rel2 === "/") throw errnoError("EISDIR", real);
|
|
8828
9116
|
if (rel2 === "/" && options.recursive) await this.rmEach(real, mount, rel2, options);
|
|
8829
9117
|
if (rel2 === "/") throw errnoError("EBUSY", `${real} is a mount point`);
|
|
8830
9118
|
this.writable(mount, real);
|
|
@@ -9060,8 +9348,22 @@ var MountFs = class {
|
|
|
9060
9348
|
this.table.forget(found.mount, real);
|
|
9061
9349
|
await found.mount.conn.call({ op: "setattr", path: found.rel, mtime: mtime.getTime() });
|
|
9062
9350
|
}
|
|
9351
|
+
noLinks() {
|
|
9352
|
+
const noLinkBase = this.base.noLinks.call(this.base);
|
|
9353
|
+
return view(this, { rejectLinks: true, base: noLinkBase });
|
|
9354
|
+
}
|
|
9063
9355
|
};
|
|
9064
9356
|
|
|
9357
|
+
// src/fs/dev-removal.ts
|
|
9358
|
+
function inDevTree(path) {
|
|
9359
|
+
return path === "/dev" || path.startsWith("/dev/");
|
|
9360
|
+
}
|
|
9361
|
+
function devRemoval(path, cred) {
|
|
9362
|
+
if (!inDevTree(path)) return void 0;
|
|
9363
|
+
if (cred && cred.euid !== 0) return "EACCES";
|
|
9364
|
+
return path === "/dev" ? "EBUSY" : "EPERM";
|
|
9365
|
+
}
|
|
9366
|
+
|
|
9065
9367
|
// src/realm/sync-fs-token-registry.ts
|
|
9066
9368
|
var registry = /* @__PURE__ */ new Map();
|
|
9067
9369
|
function mintSyncFsToken(entry) {
|
|
@@ -9159,6 +9461,7 @@ async function storageUsage() {
|
|
|
9159
9461
|
if (typeof estimate?.quota !== "number") return null;
|
|
9160
9462
|
return { quota: estimate.quota, usage: estimate.usage ?? 0 };
|
|
9161
9463
|
}
|
|
9464
|
+
var PLAIN_DIR_MODE = 493;
|
|
9162
9465
|
var done = { ok: true, kind: "void" };
|
|
9163
9466
|
function json2(value) {
|
|
9164
9467
|
return { ok: true, kind: "json", json: value };
|
|
@@ -9168,6 +9471,27 @@ function ownerFor2(entry, mode) {
|
|
|
9168
9471
|
if (!cred) return void 0;
|
|
9169
9472
|
return ownerOf(cred, mode === void 0 ? void 0 : mode & ~(entry.umask?.() ?? 18));
|
|
9170
9473
|
}
|
|
9474
|
+
function plainDirMode(entry, mode) {
|
|
9475
|
+
if (entry.cred || mode === void 0) return void 0;
|
|
9476
|
+
const wanted = mode & 4095 & ~(entry.umask?.() ?? 18);
|
|
9477
|
+
return wanted === PLAIN_DIR_MODE ? void 0 : wanted;
|
|
9478
|
+
}
|
|
9479
|
+
function plainParentsMode(entry) {
|
|
9480
|
+
if (entry.cred) return void 0;
|
|
9481
|
+
const wanted = 511 & ~(entry.umask?.() ?? 18) | 192;
|
|
9482
|
+
return wanted === PLAIN_DIR_MODE ? void 0 : wanted;
|
|
9483
|
+
}
|
|
9484
|
+
async function missingParents(fs, path, mode) {
|
|
9485
|
+
const parts2 = path.split("/").filter(Boolean);
|
|
9486
|
+
const dirs = [];
|
|
9487
|
+
for (let i = 1; i < parts2.length; i++) {
|
|
9488
|
+
const p = `/${parts2.slice(0, i).join("/")}`;
|
|
9489
|
+
if (!await fs.exists(p)) dirs.push(p);
|
|
9490
|
+
}
|
|
9491
|
+
return async () => {
|
|
9492
|
+
for (const p of dirs) await fs.chmod(p, mode);
|
|
9493
|
+
};
|
|
9494
|
+
}
|
|
9171
9495
|
function mkdirOptions(entry, mode = 511) {
|
|
9172
9496
|
const owner = ownerFor2(entry, mode);
|
|
9173
9497
|
if (!owner) return { recursive: true };
|
|
@@ -9179,23 +9503,39 @@ async function mkdirExclusive(entry, path, mode) {
|
|
|
9179
9503
|
await entry.fs.mkdir(path, owner ? { owner } : {});
|
|
9180
9504
|
}
|
|
9181
9505
|
async function chown(entry, path, req) {
|
|
9506
|
+
const real = req.handle !== void 0 ? await keptOr(entry, path, req.handle) : path;
|
|
9182
9507
|
const cred = entry.cred?.();
|
|
9183
9508
|
if (!cred) {
|
|
9184
|
-
await (req.op === "chown" ? entry.fs.stat(
|
|
9509
|
+
await (req.op === "chown" ? entry.fs.stat(real) : entry.fs.lstat(real));
|
|
9185
9510
|
return done;
|
|
9186
9511
|
}
|
|
9187
|
-
await chownAs(entry.fs, cred,
|
|
9512
|
+
await chownAs(entry.fs, cred, real, {
|
|
9188
9513
|
...req.uid !== void 0 ? { uid: req.uid } : {},
|
|
9189
9514
|
...req.gid !== void 0 ? { gid: req.gid } : {},
|
|
9190
9515
|
follow: req.op === "chown"
|
|
9191
9516
|
});
|
|
9192
9517
|
return done;
|
|
9193
9518
|
}
|
|
9519
|
+
async function runMkdir(entry, path, req) {
|
|
9520
|
+
const { fs } = entry;
|
|
9521
|
+
const plain = plainDirMode(entry, req.mode);
|
|
9522
|
+
const parentsMode = !req.exclusive ? plainParentsMode(entry) : void 0;
|
|
9523
|
+
const fixParents = parentsMode !== void 0 ? await missingParents(fs, path, parentsMode) : void 0;
|
|
9524
|
+
const fresh2 = plain !== void 0 && !await fs.exists(path);
|
|
9525
|
+
await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
|
|
9526
|
+
await fixParents?.();
|
|
9527
|
+
if (fresh2) await fs.chmod(path, plain);
|
|
9528
|
+
return done;
|
|
9529
|
+
}
|
|
9530
|
+
function readSource(entry, path) {
|
|
9531
|
+
const plain = () => entry.fs.readFileBuffer(path);
|
|
9532
|
+
return entry.source ? entry.source.read(path, plain) : plain();
|
|
9533
|
+
}
|
|
9194
9534
|
async function run(entry, path, req) {
|
|
9195
9535
|
const { fs, cwd } = entry;
|
|
9196
9536
|
switch (req.op) {
|
|
9197
9537
|
case "read":
|
|
9198
|
-
return { ok: true, kind: "bytes", bytes: await
|
|
9538
|
+
return { ok: true, kind: "bytes", bytes: await readSource(entry, path) };
|
|
9199
9539
|
case "write":
|
|
9200
9540
|
await fs.writeFile(path, req.body ?? new Uint8Array(0), ownerFor2(entry, 438));
|
|
9201
9541
|
return done;
|
|
@@ -9210,8 +9550,7 @@ async function run(entry, path, req) {
|
|
|
9210
9550
|
case "readdir-stat":
|
|
9211
9551
|
return json2(await readdirStat(fs, path));
|
|
9212
9552
|
case "mkdir":
|
|
9213
|
-
|
|
9214
|
-
return done;
|
|
9553
|
+
return runMkdir(entry, path, req);
|
|
9215
9554
|
case "rm":
|
|
9216
9555
|
await fs.rm(path, { recursive: true });
|
|
9217
9556
|
return done;
|
|
@@ -9237,7 +9576,7 @@ async function run(entry, path, req) {
|
|
|
9237
9576
|
return done;
|
|
9238
9577
|
case "create":
|
|
9239
9578
|
await createExclusive(fs, path, ownerFor2(entry, req.mode ?? 438));
|
|
9240
|
-
entry.created?.add(path);
|
|
9579
|
+
if (req.opening) entry.created?.add(path);
|
|
9241
9580
|
entry.fresh?.(path);
|
|
9242
9581
|
return done;
|
|
9243
9582
|
case "readlink":
|
|
@@ -9248,10 +9587,10 @@ async function run(entry, path, req) {
|
|
|
9248
9587
|
return done;
|
|
9249
9588
|
}
|
|
9250
9589
|
case "utimes":
|
|
9251
|
-
await fs.utimes(path,
|
|
9590
|
+
await fs.utimes(path, ...await timesOf(entry, path, req));
|
|
9252
9591
|
return done;
|
|
9253
9592
|
case "lutimes":
|
|
9254
|
-
await lutimesOf(fs, path,
|
|
9593
|
+
await lutimesOf(fs, path, ...await timesOf(entry, path, req));
|
|
9255
9594
|
return done;
|
|
9256
9595
|
case "chown":
|
|
9257
9596
|
case "lchown":
|
|
@@ -9264,6 +9603,17 @@ async function run(entry, path, req) {
|
|
|
9264
9603
|
return { ok: false, errno: "EINVAL", message: `sync-fs: unknown op '${req.op}'` };
|
|
9265
9604
|
}
|
|
9266
9605
|
}
|
|
9606
|
+
async function timesOf(entry, path, req) {
|
|
9607
|
+
const given = [new Date(req.atimeMs ?? 0), new Date(req.mtimeMs ?? 0)];
|
|
9608
|
+
const g = req.now === true ? guardOf(entry) : void 0;
|
|
9609
|
+
if (!g || g.cred.euid === 0) return given;
|
|
9610
|
+
const look = req.op === "lutimes" ? entry.fs.lstat : entry.fs.stat;
|
|
9611
|
+
const st = await look.call(entry.fs, path).catch(() => void 0);
|
|
9612
|
+
if (!st || (st.uid ?? 0) === g.cred.euid) return given;
|
|
9613
|
+
const now = /* @__PURE__ */ new Date();
|
|
9614
|
+
const kept = (ms, stored) => ms === stored.getTime() ? stored : now;
|
|
9615
|
+
return [kept(req.atimeMs, st.atime), kept(req.mtimeMs, st.mtime)];
|
|
9616
|
+
}
|
|
9267
9617
|
function guardOf(entry) {
|
|
9268
9618
|
const cred = entry.dac && entry.cred?.();
|
|
9269
9619
|
if (!cred || !entry.dac) return void 0;
|
|
@@ -9273,41 +9623,82 @@ function guardOf(entry) {
|
|
|
9273
9623
|
var FOLLOWING = /* @__PURE__ */ new Set(["exists", "stat", "chown", "statfs"]);
|
|
9274
9624
|
var LOOKS = /* @__PURE__ */ new Set(["exists", "stat", "lstat"]);
|
|
9275
9625
|
var NOT_FOLLOWING = /* @__PURE__ */ new Set(["lstat", "readlink", "lchown"]);
|
|
9626
|
+
async function walkSafe(g, path, follow2) {
|
|
9627
|
+
try {
|
|
9628
|
+
const walked = await walk(g.fs, g.cred, path, follow2, g.links);
|
|
9629
|
+
return walked.path;
|
|
9630
|
+
} catch {
|
|
9631
|
+
return path;
|
|
9632
|
+
}
|
|
9633
|
+
}
|
|
9276
9634
|
async function guard(g, path, req, cwd) {
|
|
9277
9635
|
const { op } = req;
|
|
9278
|
-
if (FOLLOWING.has(op) || NOT_FOLLOWING.has(op))
|
|
9636
|
+
if (FOLLOWING.has(op) || NOT_FOLLOWING.has(op)) {
|
|
9637
|
+
const walked = await need(g, op, path, 0, { follow: FOLLOWING.has(op) });
|
|
9638
|
+
return { path: walked.path };
|
|
9639
|
+
}
|
|
9279
9640
|
const other = () => g.fs.resolvePath(cwd, req.arg2 ?? "");
|
|
9280
9641
|
switch (op) {
|
|
9281
9642
|
case "readdir":
|
|
9282
|
-
case "readdir-stat":
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
return
|
|
9643
|
+
case "readdir-stat": {
|
|
9644
|
+
await listable(g, op, path);
|
|
9645
|
+
const w = await walkSafe(g, path, true);
|
|
9646
|
+
return { path: w };
|
|
9647
|
+
}
|
|
9648
|
+
case "mkdir": {
|
|
9649
|
+
await makeIn(g, op, path);
|
|
9650
|
+
return { path: await walkSafe(g, path, false) };
|
|
9651
|
+
}
|
|
9286
9652
|
case "create":
|
|
9287
9653
|
case "symlink":
|
|
9288
|
-
|
|
9654
|
+
await inParent(g, op, path);
|
|
9655
|
+
return { path: await walkSafe(g, path, op === "create") };
|
|
9289
9656
|
case "unlink":
|
|
9290
|
-
case "rmdir":
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
return
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
|
|
9657
|
+
case "rmdir": {
|
|
9658
|
+
await inParent(g, op, path, true);
|
|
9659
|
+
const w = await walkSafe(g, path, false);
|
|
9660
|
+
return { path: w };
|
|
9661
|
+
}
|
|
9662
|
+
case "rm": {
|
|
9663
|
+
await removable(g, op, path);
|
|
9664
|
+
const w = await walkSafe(g, path, false);
|
|
9665
|
+
return { path: w };
|
|
9666
|
+
}
|
|
9667
|
+
case "rename": {
|
|
9668
|
+
const to = other();
|
|
9669
|
+
await renamable(g, path, to);
|
|
9670
|
+
const fromWalked = await walkSafe(g, path, false);
|
|
9671
|
+
const toWalked = await walkSafe(g, to, false);
|
|
9672
|
+
return { path: fromWalked, arg2: toWalked };
|
|
9673
|
+
}
|
|
9674
|
+
case "link": {
|
|
9675
|
+
const to = other();
|
|
9676
|
+
const follow2 = req.mode === 1;
|
|
9677
|
+
await linkable(g, path, to, follow2);
|
|
9678
|
+
const fromWalked = await walkSafe(g, path, follow2);
|
|
9679
|
+
const toWalked = await walkSafe(g, to, false);
|
|
9680
|
+
return { path: fromWalked, arg2: toWalked };
|
|
9681
|
+
}
|
|
9298
9682
|
case "utimes":
|
|
9299
|
-
case "lutimes":
|
|
9300
|
-
|
|
9683
|
+
case "lutimes": {
|
|
9684
|
+
await retimable(g, op, path, req.now === true);
|
|
9685
|
+
const follow2 = op === "utimes";
|
|
9686
|
+
const w = await walkSafe(g, path, follow2);
|
|
9687
|
+
return { path: w };
|
|
9688
|
+
}
|
|
9301
9689
|
case "read":
|
|
9302
9690
|
case "pread":
|
|
9303
|
-
|
|
9691
|
+
if (req.handle === void 0) await readable(g, op, path);
|
|
9692
|
+
return { path: await walkSafe(g, path, true) };
|
|
9304
9693
|
case "pwrite":
|
|
9305
9694
|
case "truncate":
|
|
9306
|
-
|
|
9695
|
+
if (req.handle === void 0) await fileWritable(g, op, path);
|
|
9696
|
+
return { path: await walkSafe(g, path, true) };
|
|
9307
9697
|
case "write":
|
|
9308
|
-
|
|
9698
|
+
if (req.handle === void 0 && req.mode !== 1) await writable(g, op, path);
|
|
9699
|
+
return { path: await walkSafe(g, path, true) };
|
|
9309
9700
|
default:
|
|
9310
|
-
return
|
|
9701
|
+
return { path: await walkSafe(g, path, true) };
|
|
9311
9702
|
}
|
|
9312
9703
|
}
|
|
9313
9704
|
async function opening(entry, path, mode) {
|
|
@@ -9324,6 +9715,7 @@ async function holdOp(entry, path, req) {
|
|
|
9324
9715
|
if (on && open) {
|
|
9325
9716
|
await opening(entry, path, mode);
|
|
9326
9717
|
const real = await entry.fs.realpath?.(path, true).catch(() => path);
|
|
9718
|
+
await entry.source?.open(path, real);
|
|
9327
9719
|
return json2(entry.hold?.(path, on, open, real) ?? null);
|
|
9328
9720
|
}
|
|
9329
9721
|
entry.hold?.(path, on, open, void 0, req.handle);
|
|
@@ -9376,15 +9768,16 @@ async function mayChange(entry, path, req) {
|
|
|
9376
9768
|
}
|
|
9377
9769
|
async function guarded(entry, path, req) {
|
|
9378
9770
|
const g = guardOf(entry);
|
|
9379
|
-
if (!g) return
|
|
9380
|
-
if (LOOKS.has(req.op) && await entry.holdsKept?.(path)) return
|
|
9771
|
+
if (!g) return {};
|
|
9772
|
+
if (LOOKS.has(req.op) && await entry.holdsKept?.(path)) return {};
|
|
9381
9773
|
try {
|
|
9382
9774
|
await mayChange(entry, path, req);
|
|
9383
|
-
await guard(g, path, req, entry.cwd);
|
|
9384
|
-
return
|
|
9775
|
+
const paths = await guard(g, path, req, entry.cwd);
|
|
9776
|
+
return { paths };
|
|
9385
9777
|
} catch (err) {
|
|
9386
9778
|
const refusal2 = err.code === "EACCES";
|
|
9387
|
-
|
|
9779
|
+
const refused2 = req.op === "exists" && refusal2 ? json2(false) : toErrno(err);
|
|
9780
|
+
return { refused: refused2 };
|
|
9388
9781
|
}
|
|
9389
9782
|
}
|
|
9390
9783
|
async function createExclusive(fs, path, owner) {
|
|
@@ -9410,6 +9803,7 @@ async function ranged(fs, path, req) {
|
|
|
9410
9803
|
else await ops.truncate(path, req.length ?? 0);
|
|
9411
9804
|
return done;
|
|
9412
9805
|
}
|
|
9806
|
+
var CHOWN_OPS = /* @__PURE__ */ new Set(["chown", "lchown"]);
|
|
9413
9807
|
var UNLINKED_OPS = /* @__PURE__ */ new Set(["read", "pread", "pwrite", "truncate"]);
|
|
9414
9808
|
var OWN_OPS = /* @__PURE__ */ new Set(["rm", "unlink", "rmdir", "rename", "link"]);
|
|
9415
9809
|
var TWO_PATHS = /* @__PURE__ */ new Set(["rename", "link"]);
|
|
@@ -9474,7 +9868,7 @@ async function rmPermitted(entry, path, req, refused2) {
|
|
|
9474
9868
|
const st = await entry.fs.lstat(path).catch(() => void 0);
|
|
9475
9869
|
if (!st?.isDirectory) return refused2;
|
|
9476
9870
|
const unlisted = await guarded(entry, path, { ...req, op: "readdir" });
|
|
9477
|
-
if (unlisted) return unlisted;
|
|
9871
|
+
if ("refused" in unlisted) return unlisted.refused;
|
|
9478
9872
|
const names = await entry.fs.readdir(path).catch(() => void 0);
|
|
9479
9873
|
if (!names) return refused2;
|
|
9480
9874
|
let first;
|
|
@@ -9486,31 +9880,64 @@ async function rmPermitted(entry, path, req, refused2) {
|
|
|
9486
9880
|
if (!child.ok) first ??= child;
|
|
9487
9881
|
}
|
|
9488
9882
|
if (first) return first;
|
|
9489
|
-
const
|
|
9490
|
-
|
|
9883
|
+
const g = guardOf(entry);
|
|
9884
|
+
if (g) {
|
|
9885
|
+
try {
|
|
9886
|
+
await inParent(g, "rm", path, true);
|
|
9887
|
+
} catch (err) {
|
|
9888
|
+
return toErrno(err);
|
|
9889
|
+
}
|
|
9890
|
+
}
|
|
9891
|
+
return await run(entry, path, req);
|
|
9892
|
+
}
|
|
9893
|
+
async function byHandle(entry, path, req) {
|
|
9894
|
+
if (req.op === "kept") return json2(entry.keptFor?.(req.handle ?? -1) !== void 0);
|
|
9895
|
+
if (req.op === "stat" && req.handle !== void 0) return keptStat(entry, req);
|
|
9896
|
+
if (CHOWN_OPS.has(req.op) && entry.keptFor?.(req.handle ?? -1)) return chown(entry, path, req);
|
|
9897
|
+
return void 0;
|
|
9491
9898
|
}
|
|
9899
|
+
async function guardAndResolve(entry, path, req) {
|
|
9900
|
+
const result = await guarded(entry, path, req);
|
|
9901
|
+
if ("refused" in result) return result.refused;
|
|
9902
|
+
const { noLinks } = entry;
|
|
9903
|
+
const { paths } = result;
|
|
9904
|
+
if (entry.links === void 0 || !noLinks || !paths) return { path, entry };
|
|
9905
|
+
return {
|
|
9906
|
+
path: paths.path,
|
|
9907
|
+
...paths.arg2 ? { arg2: paths.arg2 } : {},
|
|
9908
|
+
entry: { ...entry, fs: noLinks }
|
|
9909
|
+
};
|
|
9910
|
+
}
|
|
9911
|
+
var REMOVALS = /* @__PURE__ */ new Set(["unlink", "rmdir", "rm"]);
|
|
9492
9912
|
async function dispatchOne(entry, req) {
|
|
9493
9913
|
const { fs, cwd } = entry;
|
|
9494
9914
|
try {
|
|
9495
9915
|
const path = fs.resolvePath(cwd, req.path);
|
|
9496
|
-
|
|
9497
|
-
if (
|
|
9916
|
+
const device = REMOVALS.has(req.op) ? devRemoval(path, entry.cred?.()) : void 0;
|
|
9917
|
+
if (device) return { ok: false, errno: device, message: `${path}: a device entry stays` };
|
|
9918
|
+
const held = await byHandle(entry, path, req);
|
|
9919
|
+
if (held) return held;
|
|
9498
9920
|
if (req.op === "access") return await accessOp(entry, path, req);
|
|
9499
9921
|
if (req.op === "hold") return await holdOp(entry, path, req);
|
|
9500
|
-
const
|
|
9501
|
-
if (
|
|
9502
|
-
|
|
9503
|
-
|
|
9504
|
-
|
|
9505
|
-
|
|
9506
|
-
|
|
9922
|
+
const resolved2 = await guardAndResolve(entry, path, req);
|
|
9923
|
+
if (!("entry" in resolved2)) {
|
|
9924
|
+
if (req.op === "rm" && !resolved2.ok && DENIED.has(resolved2.errno))
|
|
9925
|
+
return await rmPermitted(entry, path, req, resolved2);
|
|
9926
|
+
return resolved2;
|
|
9927
|
+
}
|
|
9928
|
+
const { path: resolvedPath, arg2: resolvedArg2, entry: opEntry } = resolved2;
|
|
9929
|
+
const opReq = resolvedArg2 ? { ...req, arg2: resolvedArg2 } : req;
|
|
9930
|
+
if (req.op === "statfs")
|
|
9931
|
+
return json2(await usageAt(entry, resolvedPath) ?? await storageUsage());
|
|
9932
|
+
if (entry.revoked?.(resolvedPath))
|
|
9933
|
+
return { ok: false, errno: "EIO", message: `${resolvedPath} was unmounted` };
|
|
9507
9934
|
if (req.op === "write" && req.mode === 1 || UNLINKED_OPS.has(req.op)) {
|
|
9508
|
-
const serve = async () => run(
|
|
9509
|
-
return await (entry.gate ? entry.gate(
|
|
9935
|
+
const serve = async () => run(opEntry, await keptOr(opEntry, resolvedPath, req.handle), opReq);
|
|
9936
|
+
return await (entry.gate ? entry.gate(resolvedPath, serve) : serve());
|
|
9510
9937
|
}
|
|
9511
|
-
if (!entry.own || !OWN_OPS.has(req.op)) return await run(
|
|
9512
|
-
const target = TWO_PATHS.has(req.op) ? fs.resolvePath(cwd, req.arg2 ?? "") :
|
|
9513
|
-
return await owned2(
|
|
9938
|
+
if (!entry.own || !OWN_OPS.has(req.op)) return await run(opEntry, resolvedPath, opReq);
|
|
9939
|
+
const target = resolvedArg2 ?? (TWO_PATHS.has(req.op) ? fs.resolvePath(cwd, req.arg2 ?? "") : resolvedPath);
|
|
9940
|
+
return await owned2(opEntry, entry.own, opReq, resolvedPath, target);
|
|
9514
9941
|
} catch (err) {
|
|
9515
9942
|
return toErrno(err);
|
|
9516
9943
|
}
|
|
@@ -9538,6 +9965,15 @@ var SAB_I_KILLED = 13;
|
|
|
9538
9965
|
var SAB_I_ASYNC = 14;
|
|
9539
9966
|
var SAB_I_STOP = 15;
|
|
9540
9967
|
var SAB_I_BLOCKED = 16;
|
|
9968
|
+
var SAB_I_BLOCKED_HI = 17;
|
|
9969
|
+
function loadBlocked(header4) {
|
|
9970
|
+
return [Atomics.load(header4, SAB_I_BLOCKED), Atomics.load(header4, SAB_I_BLOCKED_HI)];
|
|
9971
|
+
}
|
|
9972
|
+
function storeBlocked(header4, [low, high]) {
|
|
9973
|
+
Atomics.store(header4, SAB_I_BLOCKED_HI, high);
|
|
9974
|
+
Atomics.store(header4, SAB_I_BLOCKED, low);
|
|
9975
|
+
}
|
|
9976
|
+
var anyBlocked = (mask) => mask !== void 0 && (mask[0] !== 0 || mask[1] !== 0);
|
|
9541
9977
|
var U32 = 2 ** 32;
|
|
9542
9978
|
function storeU53(header4, lo, hi, value) {
|
|
9543
9979
|
Atomics.store(header4, hi, Math.floor(value / U32));
|
|
@@ -10064,7 +10500,7 @@ function mountTable(mounts = []) {
|
|
|
10064
10500
|
}
|
|
10065
10501
|
|
|
10066
10502
|
// src/version.ts
|
|
10067
|
-
var KERNEL_VERSION = true ? "1.
|
|
10503
|
+
var KERNEL_VERSION = true ? "1.93.0" : "0.0.0-dev";
|
|
10068
10504
|
var OS_RELEASE = "6.0.0-slicc";
|
|
10069
10505
|
function procVersion() {
|
|
10070
10506
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -10764,21 +11200,28 @@ function bodyOf(incoming, head, method, signal) {
|
|
|
10764
11200
|
var KernelLoopback = class {
|
|
10765
11201
|
net;
|
|
10766
11202
|
cred;
|
|
10767
|
-
|
|
11203
|
+
stamp;
|
|
11204
|
+
constructor(net, cred, stamp) {
|
|
10768
11205
|
this.net = net;
|
|
10769
11206
|
this.cred = cred;
|
|
11207
|
+
this.stamp = stamp;
|
|
10770
11208
|
}
|
|
10771
11209
|
port(url) {
|
|
10772
11210
|
return kernelLoopbackPort(url);
|
|
10773
11211
|
}
|
|
10774
|
-
connect(url, port, signal) {
|
|
11212
|
+
async connect(url, port, signal) {
|
|
10775
11213
|
if (url.protocol === "https:" || url.protocol === "wss:") {
|
|
10776
11214
|
throw errno("ENOTSUP", `TLS to the kernel's loopback is not supported: ${url.origin}`);
|
|
10777
11215
|
}
|
|
10778
11216
|
signal.throwIfAborted();
|
|
10779
11217
|
const socket = this.net.socket("inet");
|
|
10780
11218
|
try {
|
|
10781
|
-
void socket.connect(
|
|
11219
|
+
void socket.connect(
|
|
11220
|
+
{ family: "inet", host: "127.0.0.1", port },
|
|
11221
|
+
false,
|
|
11222
|
+
this.cred(),
|
|
11223
|
+
this.stamp ? await this.stamp() : void 0
|
|
11224
|
+
);
|
|
10782
11225
|
} catch (err) {
|
|
10783
11226
|
socket.close();
|
|
10784
11227
|
const refused2 = err instanceof KernelError && err.code === "ECONNREFUSED";
|
|
@@ -10792,7 +11235,7 @@ var KernelLoopback = class {
|
|
|
10792
11235
|
const body = req.body ?? (/^(POST|PUT|PATCH)$/i.test(req.method) ? new Uint8Array(0) : void 0);
|
|
10793
11236
|
const length2 = body ? [["Content-Length", String(body.length)]] : [];
|
|
10794
11237
|
const head = requestHead(req.method, url, req.headers, [...length2, ["Connection", "close"]]);
|
|
10795
|
-
const socket = this.connect(url, port, req.signal);
|
|
11238
|
+
const socket = await this.connect(url, port, req.signal);
|
|
10796
11239
|
try {
|
|
10797
11240
|
await writeAll2(socket, body ? concat([head, body]) : head, req.signal);
|
|
10798
11241
|
const incoming = new Incoming(socket);
|
|
@@ -10813,7 +11256,7 @@ var KernelLoopback = class {
|
|
|
10813
11256
|
}
|
|
10814
11257
|
async websocket(req, port) {
|
|
10815
11258
|
const url = new URL(req.url);
|
|
10816
|
-
const socket = this.connect(url, port, req.signal);
|
|
11259
|
+
const socket = await this.connect(url, port, req.signal);
|
|
10817
11260
|
try {
|
|
10818
11261
|
const key = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(16))));
|
|
10819
11262
|
const extra = [
|
|
@@ -10838,6 +11281,9 @@ var KernelLoopback = class {
|
|
|
10838
11281
|
if (!upgraded || accept2 !== await acceptFor(key)) {
|
|
10839
11282
|
throw errno("EPROTO", "the server did not accept the WebSocket key");
|
|
10840
11283
|
}
|
|
11284
|
+
if (fieldValues(head.headers, "sec-websocket-extensions").length > 0) {
|
|
11285
|
+
throw errno("EPROTO", "the server offered an unsolicited extension");
|
|
11286
|
+
}
|
|
10841
11287
|
const protocol = fieldValues(head.headers, "sec-websocket-protocol")[0] ?? "";
|
|
10842
11288
|
return new LoopbackWebSocket(socket, incoming, protocol);
|
|
10843
11289
|
} catch (err) {
|
|
@@ -10937,7 +11383,7 @@ var LoopbackWebSocket = class {
|
|
|
10937
11383
|
})();
|
|
10938
11384
|
}
|
|
10939
11385
|
async pump(reader, deliver) {
|
|
10940
|
-
let close = { code: 1006, reason: "" };
|
|
11386
|
+
let close = { code: 1006, reason: "", wasClean: false };
|
|
10941
11387
|
try {
|
|
10942
11388
|
for (; ; ) {
|
|
10943
11389
|
await this.space();
|
|
@@ -10947,14 +11393,14 @@ var LoopbackWebSocket = class {
|
|
|
10947
11393
|
else if (event.kind === "binary") deliver(event.bytes);
|
|
10948
11394
|
else if (event.kind === "ping") this.write(clientFrame(OP_PONG, event.payload));
|
|
10949
11395
|
else {
|
|
10950
|
-
close = { code: event.code, reason: event.reason };
|
|
11396
|
+
close = { code: event.code, reason: event.reason, wasClean: true };
|
|
10951
11397
|
if (!this.sentClose) this.sendClose(event.code === 1005 ? 1e3 : event.code, "");
|
|
10952
11398
|
break;
|
|
10953
11399
|
}
|
|
10954
11400
|
}
|
|
10955
11401
|
} catch (err) {
|
|
10956
11402
|
if (err instanceof WsError) this.sendClose(err.code, err.message);
|
|
10957
|
-
close = { code:
|
|
11403
|
+
close = { code: 1006, reason: "", wasClean: false };
|
|
10958
11404
|
} finally {
|
|
10959
11405
|
const late = setTimeout(() => this.stop.abort(), CLOSE_WAIT_MS3);
|
|
10960
11406
|
await this.writes;
|
|
@@ -11744,14 +12190,22 @@ var WsHandles = class {
|
|
|
11744
12190
|
return typeof next2.value === "string" ? { ok: true, kind: "json", json: { text: next2.value } } : { ok: true, kind: "bytes", bytes: next2.value };
|
|
11745
12191
|
}
|
|
11746
12192
|
const closed = await socket.closed;
|
|
11747
|
-
return {
|
|
12193
|
+
return {
|
|
12194
|
+
ok: true,
|
|
12195
|
+
kind: "json",
|
|
12196
|
+
json: { code: closed.code, reason: closed.reason, wasClean: closed.wasClean === true }
|
|
12197
|
+
};
|
|
11748
12198
|
} catch (e) {
|
|
11749
12199
|
return fail("ECONNRESET", message(e));
|
|
11750
12200
|
}
|
|
11751
12201
|
}
|
|
11752
12202
|
async closed(socket) {
|
|
11753
|
-
const
|
|
11754
|
-
return {
|
|
12203
|
+
const closed = await socket.closed;
|
|
12204
|
+
return {
|
|
12205
|
+
ok: true,
|
|
12206
|
+
kind: "json",
|
|
12207
|
+
json: { code: closed.code, reason: closed.reason, wasClean: closed.wasClean === true }
|
|
12208
|
+
};
|
|
11755
12209
|
}
|
|
11756
12210
|
send(socket, req, entry) {
|
|
11757
12211
|
const data = req.text ?? req.body;
|
|
@@ -11791,7 +12245,7 @@ function message2(e) {
|
|
|
11791
12245
|
}
|
|
11792
12246
|
function codeOf2(e) {
|
|
11793
12247
|
const code = e?.code;
|
|
11794
|
-
return typeof code === "string" && /^E[A-Z]+$/.test(code) ? code : "EIO";
|
|
12248
|
+
return typeof code === "string" && /^E[A-Z]+$|^EAI_[A-Z]+$/.test(code) ? code : "EIO";
|
|
11795
12249
|
}
|
|
11796
12250
|
async function* once(bytes) {
|
|
11797
12251
|
yield bytes;
|
|
@@ -11874,7 +12328,8 @@ var HttpHandles = class {
|
|
|
11874
12328
|
if (port !== void 0) return { ok: false, errno: codeOf2(e), message: text };
|
|
11875
12329
|
const status = e.status;
|
|
11876
12330
|
if (text === NO_TRANSPORT || typeof status !== "number" || status === 502) {
|
|
11877
|
-
const
|
|
12331
|
+
const code = codeOf2(e);
|
|
12332
|
+
const errno2 = text === NO_TRANSPORT ? "ENETUNREACH" : code !== "EIO" ? code : "ECONNREFUSED";
|
|
11878
12333
|
return { ok: false, errno: errno2, message: text };
|
|
11879
12334
|
}
|
|
11880
12335
|
entry.body = once(new TextEncoder().encode(text))[Symbol.asyncIterator]();
|
|
@@ -13444,6 +13899,7 @@ function aborted(signal) {
|
|
|
13444
13899
|
signal.addEventListener("abort", () => reject(new KernelError("EINTR")), { once: true });
|
|
13445
13900
|
});
|
|
13446
13901
|
}
|
|
13902
|
+
var PLAIN_FILE_MODE = 420;
|
|
13447
13903
|
var JOB_OPS = /* @__PURE__ */ new Set([
|
|
13448
13904
|
"proc-setpgid",
|
|
13449
13905
|
"proc-getpgid",
|
|
@@ -13466,7 +13922,7 @@ function isPtySyscall(req) {
|
|
|
13466
13922
|
return req.op.startsWith("pty-");
|
|
13467
13923
|
}
|
|
13468
13924
|
function isVfsSyscall(req) {
|
|
13469
|
-
return req.op.startsWith("fd-path-") || req.op === "fd-pread" || req.op === "fd-pwrite" || req.op === "fd-resize" || req.op === "fd-vfs-stat";
|
|
13925
|
+
return req.op.startsWith("fd-path-") || req.op === "fd-pread" || req.op === "fd-pwrite" || req.op === "fd-resize" || req.op === "fd-vfs-stat" || req.op === "fd-chmod" || req.op === "fd-chown" || req.op === "fd-utimes";
|
|
13470
13926
|
}
|
|
13471
13927
|
function isFdSyscall(req) {
|
|
13472
13928
|
return req.op.startsWith("fd-");
|
|
@@ -13486,6 +13942,9 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
13486
13942
|
"fd-pwrite",
|
|
13487
13943
|
"fd-resize",
|
|
13488
13944
|
"fd-vfs-stat",
|
|
13945
|
+
"fd-chmod",
|
|
13946
|
+
"fd-chown",
|
|
13947
|
+
"fd-utimes",
|
|
13489
13948
|
"fd-path-flush",
|
|
13490
13949
|
"fd-path-unlinking",
|
|
13491
13950
|
"fd-path-unlinked",
|
|
@@ -13516,6 +13975,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
13516
13975
|
"fd-tty-owner",
|
|
13517
13976
|
"fd-open-sealed",
|
|
13518
13977
|
"fd-open-clipboard",
|
|
13978
|
+
"fd-clipboard-abort",
|
|
13519
13979
|
"tty-get",
|
|
13520
13980
|
"tty-set",
|
|
13521
13981
|
"tty-winsz",
|
|
@@ -13861,7 +14321,11 @@ var WasmProcess = class {
|
|
|
13861
14321
|
if (!stderr?.write || stderr.clipboard) return;
|
|
13862
14322
|
const line = `${name2}: /dev/clipboard: ${clipboardLine(err.detail)}
|
|
13863
14323
|
`;
|
|
13864
|
-
|
|
14324
|
+
const timeout = new Promise((r) => setTimeout(() => r(0), 1e3));
|
|
14325
|
+
await Promise.race([
|
|
14326
|
+
stderr.write(new TextEncoder().encode(line), AbortSignal.timeout(1e3)).catch(() => 0),
|
|
14327
|
+
timeout
|
|
14328
|
+
]);
|
|
13865
14329
|
}
|
|
13866
14330
|
async write(fd, body, nonblock = false, cancelled) {
|
|
13867
14331
|
const file = this.fds.get(fd).file;
|
|
@@ -13888,30 +14352,39 @@ var WasmProcess = class {
|
|
|
13888
14352
|
await refuseReadonly(this.options.fs, req.path, req.flags, req);
|
|
13889
14353
|
if (this.writesAt(req)) await this.options.fs.mayChange?.(req.path, !req.exclusive);
|
|
13890
14354
|
await this.mayOpen(req);
|
|
14355
|
+
const marked = await this.markedPath(req);
|
|
14356
|
+
await this.sourceOpen(marked === void 0 ? req : { ...req, path: marked });
|
|
14357
|
+
const fs = marked === void 0 ? this.options.fs : this.options.noLinks;
|
|
13891
14358
|
const fd = this.fds.install(heldFile(), 3);
|
|
13892
14359
|
try {
|
|
13893
14360
|
const nodes = this.nodes;
|
|
13894
|
-
const path =
|
|
14361
|
+
const path = await this.openPath(req, marked);
|
|
13895
14362
|
if (inProc(path)) {
|
|
13896
14363
|
this.fds.installAt(fd, await this.procFile(req, path));
|
|
13897
14364
|
return fd;
|
|
13898
14365
|
}
|
|
13899
14366
|
if (this.writesAt(req) && this.options.readonlyAt?.(path)) throw new KernelError("EROFS");
|
|
13900
14367
|
const open = { ...req, path };
|
|
14368
|
+
const plain = this.plainMode(req.mode);
|
|
13901
14369
|
if (req.exclusive) {
|
|
13902
|
-
await nodes.createExclusive(path, this.newOwner());
|
|
13903
|
-
|
|
14370
|
+
await nodes.createExclusive(path, this.newOwner(req.mode));
|
|
14371
|
+
if (plain !== void 0) await fs.chmod?.(path, plain);
|
|
14372
|
+
this.fds.installAt(fd, this.openVfsFile(open, void 0, fs));
|
|
13904
14373
|
} else if (req.create) {
|
|
13905
14374
|
await nodes.onEntry(path, async () => {
|
|
13906
|
-
const
|
|
13907
|
-
|
|
14375
|
+
const marks = this.options.owners || plain !== void 0;
|
|
14376
|
+
const fresh2 = marks && !await present2(fs, path, true);
|
|
14377
|
+
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path), fs));
|
|
13908
14378
|
await this.fds.get(fd).file.stat?.();
|
|
13909
|
-
if (fresh2) await
|
|
14379
|
+
if (fresh2 && plain !== void 0) await fs.chmod?.(path, plain);
|
|
14380
|
+
else if (fresh2) {
|
|
14381
|
+
await fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
|
|
14382
|
+
}
|
|
13910
14383
|
});
|
|
13911
14384
|
} else {
|
|
13912
|
-
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
14385
|
+
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path), fs));
|
|
13913
14386
|
}
|
|
13914
|
-
if (req.existing && !await present2(
|
|
14387
|
+
if (req.existing && !await present2(fs, path, true)) {
|
|
13915
14388
|
throw new KernelError("ENOENT");
|
|
13916
14389
|
}
|
|
13917
14390
|
} catch (err) {
|
|
@@ -13945,6 +14418,27 @@ var WasmProcess = class {
|
|
|
13945
14418
|
writesAt(req) {
|
|
13946
14419
|
return (req.flags & WRITE_ACCESS) !== 0 || req.truncate === true || req.create === true || req.exclusive === true;
|
|
13947
14420
|
}
|
|
14421
|
+
async sourceOpen(req) {
|
|
14422
|
+
const { source } = this.options;
|
|
14423
|
+
const inherited2 = req.orphan || req.contents !== void 0 || req.handle !== void 0;
|
|
14424
|
+
if (!source || inherited2 || req.pin !== void 0 || req.exclusive) return;
|
|
14425
|
+
await source.open(req.path);
|
|
14426
|
+
}
|
|
14427
|
+
async markedPath(req) {
|
|
14428
|
+
const { guard: guard2, noLinks } = this.options;
|
|
14429
|
+
const inherited2 = req.orphan || req.contents !== void 0 || req.handle !== void 0;
|
|
14430
|
+
if (guard2?.links === void 0 || !noLinks || inherited2 || req.pin !== void 0)
|
|
14431
|
+
return void 0;
|
|
14432
|
+
return (await walk(guard2.fs, this.cred, req.path, !req.exclusive, guard2.links)).path;
|
|
14433
|
+
}
|
|
14434
|
+
async openPath(req, marked) {
|
|
14435
|
+
const nodes = this.nodes;
|
|
14436
|
+
if (req.orphan) return req.path;
|
|
14437
|
+
if (req.exclusive) return nodes.entryKey(marked ?? req.path);
|
|
14438
|
+
if (marked !== void 0) return marked;
|
|
14439
|
+
if (req.create) return nodes.targetKey(req.path);
|
|
14440
|
+
return this.keptOr(await nodes.targetKey(req.path), req.handle);
|
|
14441
|
+
}
|
|
13948
14442
|
async mayOpen(req) {
|
|
13949
14443
|
const { guard: guard2 } = this.options;
|
|
13950
14444
|
const inherited2 = req.orphan || req.contents !== void 0 || req.handle !== void 0;
|
|
@@ -14019,9 +14513,14 @@ var WasmProcess = class {
|
|
|
14019
14513
|
if (!tty) throw new KernelError("ENOENT");
|
|
14020
14514
|
return tty;
|
|
14021
14515
|
}
|
|
14022
|
-
|
|
14516
|
+
plainMode(mode) {
|
|
14517
|
+
if (this.options.owners || mode === void 0) return void 0;
|
|
14518
|
+
const wanted = mode & 4095 & ~this.umask;
|
|
14519
|
+
return wanted === PLAIN_FILE_MODE ? void 0 : wanted;
|
|
14520
|
+
}
|
|
14521
|
+
newOwner(mode = 438) {
|
|
14023
14522
|
if (!this.options.owners) return void 0;
|
|
14024
|
-
return { uid: this.cred.euid, gid: this.cred.egid, mode:
|
|
14523
|
+
return { uid: this.cred.euid, gid: this.cred.egid, mode: mode & 4095 & ~this.umask };
|
|
14025
14524
|
}
|
|
14026
14525
|
async openProc(req, path) {
|
|
14027
14526
|
return this.fds.install(await this.procFile(req, path), 3);
|
|
@@ -14039,9 +14538,9 @@ var WasmProcess = class {
|
|
|
14039
14538
|
);
|
|
14040
14539
|
return memoryFile(tree2.read(path), path, req.position);
|
|
14041
14540
|
}
|
|
14042
|
-
openVfsFile(req, id2) {
|
|
14541
|
+
openVfsFile(req, id2, fs = this.options.fs) {
|
|
14043
14542
|
return vfsFile(
|
|
14044
|
-
|
|
14543
|
+
fs,
|
|
14045
14544
|
{
|
|
14046
14545
|
path: req.path,
|
|
14047
14546
|
flags: req.flags,
|
|
@@ -14124,7 +14623,7 @@ var WasmProcess = class {
|
|
|
14124
14623
|
return { ok: true, kind: "void" };
|
|
14125
14624
|
case "fd-renumber":
|
|
14126
14625
|
if (req.from !== req.to) {
|
|
14127
|
-
this.fds.
|
|
14626
|
+
await this.fds.dup2Clipboard(req.from, req.to);
|
|
14128
14627
|
if (!req.keep) await Promise.resolve(this.fds.close(req.from));
|
|
14129
14628
|
}
|
|
14130
14629
|
return { ok: true, kind: "void" };
|
|
@@ -14136,6 +14635,11 @@ var WasmProcess = class {
|
|
|
14136
14635
|
return { ok: true, kind: "json", json: this.openSealed(req.flags) };
|
|
14137
14636
|
case "fd-open-clipboard":
|
|
14138
14637
|
return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
|
|
14638
|
+
case "fd-clipboard-abort": {
|
|
14639
|
+
const file = this.fds.get(req.fd);
|
|
14640
|
+
file.file.clipboard?.abort();
|
|
14641
|
+
return { ok: true, kind: "void" };
|
|
14642
|
+
}
|
|
14139
14643
|
case "fd-tty-owner":
|
|
14140
14644
|
return { ok: true, kind: "json", json: this.terminalByName(req.name).owner ?? null };
|
|
14141
14645
|
case "fd-select": {
|
|
@@ -14188,12 +14692,23 @@ var WasmProcess = class {
|
|
|
14188
14692
|
case "fd-resize":
|
|
14189
14693
|
await file.resize(req.size);
|
|
14190
14694
|
return { ok: true, kind: "void" };
|
|
14191
|
-
case "fd-vfs-stat":
|
|
14192
|
-
|
|
14193
|
-
|
|
14194
|
-
|
|
14195
|
-
|
|
14196
|
-
|
|
14695
|
+
case "fd-vfs-stat": {
|
|
14696
|
+
if (req.identity) {
|
|
14697
|
+
return {
|
|
14698
|
+
ok: true,
|
|
14699
|
+
kind: "json",
|
|
14700
|
+
json: await this.identified(file.stat.bind(file), file.known?.bind(file))
|
|
14701
|
+
};
|
|
14702
|
+
}
|
|
14703
|
+
const vfs = await file.stat();
|
|
14704
|
+
if (!req.full) return { ok: true, kind: "json", json: vfs };
|
|
14705
|
+
const st = await this.heldStat(vfs).catch(() => void 0);
|
|
14706
|
+
return { ok: true, kind: "json", json: { ...vfs, ...st ? { stat: st } : {} } };
|
|
14707
|
+
}
|
|
14708
|
+
case "fd-chmod":
|
|
14709
|
+
case "fd-chown":
|
|
14710
|
+
case "fd-utimes":
|
|
14711
|
+
return this.fdMetadata(await file.stat(), req);
|
|
14197
14712
|
}
|
|
14198
14713
|
}
|
|
14199
14714
|
keptPath;
|
|
@@ -14214,7 +14729,7 @@ var WasmProcess = class {
|
|
|
14214
14729
|
async promote(req) {
|
|
14215
14730
|
if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
|
|
14216
14731
|
if (req.share !== void 0) {
|
|
14217
|
-
this.fds.
|
|
14732
|
+
await this.fds.dup2Clipboard(req.share, req.fd);
|
|
14218
14733
|
return;
|
|
14219
14734
|
}
|
|
14220
14735
|
if (req.path === void 0) throw new KernelError("EINVAL");
|
|
@@ -14243,6 +14758,46 @@ var WasmProcess = class {
|
|
|
14243
14758
|
this.fds.installAt(fd, heldFile(meta));
|
|
14244
14759
|
return fd;
|
|
14245
14760
|
}
|
|
14761
|
+
heldPath(vfs) {
|
|
14762
|
+
if (!vfs.path) throw new KernelError("EBADF");
|
|
14763
|
+
if (vfs.orphan) throw new KernelError("EOPNOTSUPP");
|
|
14764
|
+
return vfs.path;
|
|
14765
|
+
}
|
|
14766
|
+
heldFs() {
|
|
14767
|
+
return this.options.guard?.fs ?? this.options.fs;
|
|
14768
|
+
}
|
|
14769
|
+
async heldStat(vfs) {
|
|
14770
|
+
const stat = this.heldFs().stat;
|
|
14771
|
+
if (!stat) throw new KernelError("ENOSYS");
|
|
14772
|
+
return stat.call(this.heldFs(), this.heldPath(vfs));
|
|
14773
|
+
}
|
|
14774
|
+
async fdMetadata(vfs, req) {
|
|
14775
|
+
const path = this.heldPath(vfs);
|
|
14776
|
+
const fs = this.heldFs();
|
|
14777
|
+
const g = this.guarded();
|
|
14778
|
+
if (req.op === "fd-chown") {
|
|
14779
|
+
const ids = {
|
|
14780
|
+
...req.uid !== void 0 ? { uid: req.uid } : {},
|
|
14781
|
+
...req.gid !== void 0 ? { gid: req.gid } : {}
|
|
14782
|
+
};
|
|
14783
|
+
if (!this.options.owners || !fs.chown || !fs.stat) await this.heldStat(vfs);
|
|
14784
|
+
else await chownAs(fs, this.cred, path, { ...ids, follow: true });
|
|
14785
|
+
return { ok: true, kind: "void" };
|
|
14786
|
+
}
|
|
14787
|
+
const st = await this.heldStat(vfs);
|
|
14788
|
+
if (req.op === "fd-chmod") {
|
|
14789
|
+
if (!fs.chmod) throw new KernelError("ENOSYS");
|
|
14790
|
+
await fs.chmod(path, g ? heldChmodMode(g, path, st, req.mode) : req.mode);
|
|
14791
|
+
return { ok: true, kind: "void" };
|
|
14792
|
+
}
|
|
14793
|
+
if (!fs.utimes) throw new KernelError("ENOSYS");
|
|
14794
|
+
if (g) heldRetimable(g, path, st, req.now === true);
|
|
14795
|
+
const at = (ms, kept) => ms === null ? kept : req.now ? /* @__PURE__ */ new Date() : new Date(ms);
|
|
14796
|
+
const atime = at(req.atimeMs, st.atime);
|
|
14797
|
+
const mtime = at(req.mtimeMs, st.mtime);
|
|
14798
|
+
await fs.utimes(path, atime, mtime);
|
|
14799
|
+
return { ok: true, kind: "void" };
|
|
14800
|
+
}
|
|
14246
14801
|
fdInfo(fd) {
|
|
14247
14802
|
const file = this.fds.get(fd).file;
|
|
14248
14803
|
const flags2 = this.fds.statusFlags(fd);
|
|
@@ -14461,7 +15016,10 @@ var WasmProcess = class {
|
|
|
14461
15016
|
const { file, argv, env, cwd, cred, caller } = answer.grant;
|
|
14462
15017
|
const stdio = [{ fd: 0 }, { fd: 1 }, { fd: 2 }];
|
|
14463
15018
|
return {
|
|
14464
|
-
pid: await this.children.spawn({ file, argv, env, cwd }, stdio, [], {
|
|
15019
|
+
pid: await this.children.spawn({ file, argv, env, cwd, pgid: 0 }, stdio, [], {
|
|
15020
|
+
cred,
|
|
15021
|
+
caller
|
|
15022
|
+
})
|
|
14465
15023
|
};
|
|
14466
15024
|
}
|
|
14467
15025
|
async mountSyscall(req, cancelled) {
|
|
@@ -14761,11 +15319,13 @@ function inherited(opts) {
|
|
|
14761
15319
|
}
|
|
14762
15320
|
function holdingToken(opts, holds, caller) {
|
|
14763
15321
|
return mintSyncFsToken({
|
|
14764
|
-
...opts.owners ? caller : {},
|
|
15322
|
+
...opts.owners ? caller : { umask: caller.umask },
|
|
14765
15323
|
...opts.dac ? { dac: opts.dac, pid: opts.pid, created: caller.created } : {},
|
|
14766
15324
|
...opts.links !== void 0 ? { links: opts.links } : {},
|
|
14767
15325
|
...opts.fresh ? { fresh: (path) => opts.fresh?.(path, caller.umask()) } : {},
|
|
15326
|
+
...opts.source ? { source: opts.source } : {},
|
|
14768
15327
|
fs: opts.fs,
|
|
15328
|
+
noLinks: opts.noLinks,
|
|
14769
15329
|
cwd: opts.cwd,
|
|
14770
15330
|
...opts.statfs ? { statfs: opts.statfs } : {},
|
|
14771
15331
|
hold: (path, on, open, real, handle) => holds.hold(path, on, open, real, handle),
|
|
@@ -14791,6 +15351,8 @@ function processOptions(opts, header4, hooks, created3) {
|
|
|
14791
15351
|
...opts.outputFilter ? { outputFilter: opts.outputFilter } : {},
|
|
14792
15352
|
...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
|
|
14793
15353
|
...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
|
|
15354
|
+
...opts.source ? { source: opts.source } : {},
|
|
15355
|
+
noLinks: opts.noLinks,
|
|
14794
15356
|
...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3, ...linksOf(opts) } } : {},
|
|
14795
15357
|
...opts.identity ? { identity: opts.identity } : {},
|
|
14796
15358
|
...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
|
|
@@ -14846,8 +15408,8 @@ function threadInit(opts, program, sab, cred, thread, main) {
|
|
|
14846
15408
|
function spawnWasmProcess(opts) {
|
|
14847
15409
|
const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
|
|
14848
15410
|
const header4 = new Int32Array(sab, 0, SAB_HEADER_I32);
|
|
14849
|
-
|
|
14850
|
-
|
|
15411
|
+
const blocked = opts.fork?.wasi?.blocked ?? opts.blocked;
|
|
15412
|
+
if (blocked && anyBlocked(blocked) && opts.program.abi === "wasi") storeBlocked(header4, blocked);
|
|
14851
15413
|
const threadSignals = /* @__PURE__ */ new Map();
|
|
14852
15414
|
const created3 = new Created();
|
|
14853
15415
|
const process2 = new WasmProcess(
|
|
@@ -14862,7 +15424,7 @@ function spawnWasmProcess(opts) {
|
|
|
14862
15424
|
...opts.program.abi === "js" ? {
|
|
14863
15425
|
exitNow: (code) => exitNow(code),
|
|
14864
15426
|
exitSignal: (sig) => queueMicrotask(() => finish(128 + sig, sig))
|
|
14865
|
-
} : {}
|
|
15427
|
+
} : { exitSignal: (sig) => terminate(128 + sig, sig) }
|
|
14866
15428
|
},
|
|
14867
15429
|
created3
|
|
14868
15430
|
)
|
|
@@ -14948,7 +15510,7 @@ function spawnWasmProcess(opts) {
|
|
|
14948
15510
|
const tsab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
|
|
14949
15511
|
const tw = opts.createWorker();
|
|
14950
15512
|
const tsignals = threadSignalsOf(tsab);
|
|
14951
|
-
if (thread.blocked)
|
|
15513
|
+
if (thread.blocked) storeBlocked(tsignals.header, thread.blocked);
|
|
14952
15514
|
threadSignals.set(thread.tid, tsignals);
|
|
14953
15515
|
const tdispatch = (req) => dying.serving(
|
|
14954
15516
|
() => isWasmSyscall(req) ? process2.syscall(req, threadCancel(tsignals)) : dispatchSyncFs(req)
|
|
@@ -14976,12 +15538,13 @@ function spawnWasmProcess(opts) {
|
|
|
14976
15538
|
processInit(opts, sab, process2.umask, copyCred(process2.cred), lane?.lane),
|
|
14977
15539
|
transferOf(opts, lane?.lane)
|
|
14978
15540
|
);
|
|
14979
|
-
const
|
|
14980
|
-
if (process2.signal(sig) !== "terminate") return;
|
|
14981
|
-
const code = sig === SIG.KILL ? 137 : 128 + sig;
|
|
15541
|
+
const terminate = (code, sig) => {
|
|
14982
15542
|
if (opts.program.abi !== "wasi" || done4) finish(code, sig);
|
|
14983
15543
|
else dying.start(code, sig, header4, process2, () => finish(code, sig));
|
|
14984
15544
|
};
|
|
15545
|
+
const signal = (sig) => {
|
|
15546
|
+
if (process2.signal(sig) === "terminate") terminate(sig === SIG.KILL ? 137 : 128 + sig, sig);
|
|
15547
|
+
};
|
|
14985
15548
|
const kill = (code = 137) => finish(code, code > 128 && code < 160 ? code - 128 : void 0);
|
|
14986
15549
|
return {
|
|
14987
15550
|
pid: opts.pid,
|
|
@@ -14992,7 +15555,7 @@ function spawnWasmProcess(opts) {
|
|
|
14992
15555
|
onState: (listener) => process2.onState(listener),
|
|
14993
15556
|
memory: () => Atomics.load(header4, SAB_I_MEMORY) * 65536,
|
|
14994
15557
|
ignoredSignals: (fork) => process2.inheritable(fork),
|
|
14995
|
-
blockedSignals: () => opts.program.abi === "wasi" ?
|
|
15558
|
+
blockedSignals: () => opts.program.abi === "wasi" ? loadBlocked(header4) : [0, 0],
|
|
14996
15559
|
signalMasks: () => process2.signalMasks(),
|
|
14997
15560
|
umask: () => process2.umask,
|
|
14998
15561
|
cred: () => copyCred(process2.cred),
|
|
@@ -18201,14 +18764,16 @@ var SUDO_COMMAND = {
|
|
|
18201
18764
|
var SUDO_SOURCE = [
|
|
18202
18765
|
"export default async function sudo(ctx) {",
|
|
18203
18766
|
" const say = (fd, text) => ctx.write(fd, text).catch(() => undefined);",
|
|
18767
|
+
' const RELAYED = ["SIGTERM", "SIGHUP", "SIGINT", "SIGQUIT", "SIGUSR1", "SIGUSR2", "SIGALRM"];',
|
|
18768
|
+
" const NUMBER = { SIGTERM: 15, SIGHUP: 1, SIGINT: 2, SIGQUIT: 3, SIGUSR1: 10, SIGUSR2: 12, SIGALRM: 14 };",
|
|
18204
18769
|
" let pid;",
|
|
18205
18770
|
" let held;",
|
|
18206
18771
|
" const relay = (sig) => {",
|
|
18207
18772
|
" if (pid === undefined) held = sig;",
|
|
18208
|
-
" else return ctx.kill(pid, sig).catch(() => undefined);",
|
|
18773
|
+
" else return ctx.kill(-pid, sig).catch(() => undefined);",
|
|
18209
18774
|
" };",
|
|
18210
|
-
|
|
18211
|
-
" const failed = () => (held === undefined ? 1 : 128 + held);",
|
|
18775
|
+
" for (const sig of RELAYED) await ctx.signals.on(sig, () => relay(sig));",
|
|
18776
|
+
" const failed = () => (held === undefined ? 1 : 128 + NUMBER[held]);",
|
|
18212
18777
|
" let answer;",
|
|
18213
18778
|
" try {",
|
|
18214
18779
|
" answer = await ctx.sudo(ctx.argv.slice(1));",
|
|
@@ -18224,18 +18789,41 @@ var SUDO_SOURCE = [
|
|
|
18224
18789
|
" await say(1, answer.list);",
|
|
18225
18790
|
" return 0;",
|
|
18226
18791
|
" }",
|
|
18227
|
-
' for (const sig of ["SIGINT", "SIGQUIT"]) await ctx.signals.on(sig, () => undefined);',
|
|
18228
18792
|
" if (answer.run !== undefined) {",
|
|
18229
18793
|
" try {",
|
|
18230
|
-
" pid = (await ctx.spawn({ argv: answer.run })).pid;",
|
|
18794
|
+
" pid = (await ctx.spawn({ argv: answer.run, pgid: 0 })).pid;",
|
|
18231
18795
|
" } catch (err) {",
|
|
18232
18796
|
' const why = err && err.code === "ENOENT" ? "command not found" : String(err && err.code);',
|
|
18233
18797
|
' await say(2, "sudo: " + answer.run[0] + ": " + why + "\\n");',
|
|
18234
18798
|
" return failed();",
|
|
18235
18799
|
" }",
|
|
18236
18800
|
" } else pid = answer.pid;",
|
|
18801
|
+
' const stat = () => new TextDecoder().decode(ctx.sync.fs.readFile("/proc/" + ctx.pid + "/stat"));',
|
|
18802
|
+
' const mine = Number(stat().replace(/^.*\\) /, "").split(" ")[2]);',
|
|
18803
|
+
" const foreground = () => ctx.tty.foreground(0).catch(() => undefined);",
|
|
18804
|
+
" const owned = (await foreground()) === mine;",
|
|
18805
|
+
" const handOver = async () => {",
|
|
18806
|
+
" if ((await foreground()) === mine) await ctx.tty.setForeground(0, pid).catch(() => undefined);",
|
|
18807
|
+
" };",
|
|
18808
|
+
" const takeBack = async () => {",
|
|
18809
|
+
" if (owned && (await foreground()) === pid) await ctx.tty.setForeground(0, mine).catch(() => undefined);",
|
|
18810
|
+
" };",
|
|
18811
|
+
" if (owned) await handOver();",
|
|
18237
18812
|
" if (held !== undefined) await relay(held);",
|
|
18238
|
-
"
|
|
18813
|
+
" for (;;) {",
|
|
18814
|
+
" const ended = await ctx.wait(pid, { untraced: true });",
|
|
18815
|
+
" if (!ended.stopped) {",
|
|
18816
|
+
" await takeBack();",
|
|
18817
|
+
" return ended.status;",
|
|
18818
|
+
" }",
|
|
18819
|
+
" const reading = ended.signal === 21 || ended.signal === 22;",
|
|
18820
|
+
" if (!(reading && (await foreground()) === mine)) {",
|
|
18821
|
+
" await takeBack();",
|
|
18822
|
+
' ctx.sync.kill(ctx.pid, ended.signal === 19 ? "SIGTSTP" : ended.signal);',
|
|
18823
|
+
" }",
|
|
18824
|
+
" await handOver();",
|
|
18825
|
+
' await ctx.kill(-pid, "SIGCONT").catch(() => undefined);',
|
|
18826
|
+
" }",
|
|
18239
18827
|
"}",
|
|
18240
18828
|
""
|
|
18241
18829
|
].join("\n");
|
|
@@ -18251,6 +18839,7 @@ var WASM_MAGIC = [0, 97, 115, 109];
|
|
|
18251
18839
|
var SHEBANG_MAX = 256;
|
|
18252
18840
|
var MAX_INTERPRETERS = 4;
|
|
18253
18841
|
var NOT_FOUND = 127;
|
|
18842
|
+
var NOT_EXECUTABLE = 126;
|
|
18254
18843
|
var DECIDE_MS = 100;
|
|
18255
18844
|
var INIT_PID = 1;
|
|
18256
18845
|
var SHARED_DIRS = ["/tmp", "/home"];
|
|
@@ -18400,6 +18989,9 @@ function addTimes(into, pid, t) {
|
|
|
18400
18989
|
var Launcher = class {
|
|
18401
18990
|
fs;
|
|
18402
18991
|
processFs;
|
|
18992
|
+
noLinksFs;
|
|
18993
|
+
mounted;
|
|
18994
|
+
protection;
|
|
18403
18995
|
prefixes;
|
|
18404
18996
|
writerTags;
|
|
18405
18997
|
unguardedRoot;
|
|
@@ -18508,8 +19100,8 @@ var Launcher = class {
|
|
|
18508
19100
|
changed: this.watchers.changed.bind(this.watchers),
|
|
18509
19101
|
owners: this.owners
|
|
18510
19102
|
});
|
|
18511
|
-
|
|
18512
|
-
this.base = this.
|
|
19103
|
+
this.mounted = this.mounts.wrap(options.fs);
|
|
19104
|
+
this.base = this.rooted(this.mounted);
|
|
18513
19105
|
this.createWorker = options.createWorker;
|
|
18514
19106
|
this.createDriverWorker = options.createDriverWorker;
|
|
18515
19107
|
this.media = options.media ?? memoryMedia();
|
|
@@ -18546,12 +19138,7 @@ var Launcher = class {
|
|
|
18546
19138
|
});
|
|
18547
19139
|
enableCdp(this.net, this.cdp);
|
|
18548
19140
|
this.facades.enable(this.net);
|
|
18549
|
-
const fs =
|
|
18550
|
-
this.base,
|
|
18551
|
-
async () => new Set((await this.commands()).keys()),
|
|
18552
|
-
(dir) => this.userCommandDir(dir),
|
|
18553
|
-
this.owners
|
|
18554
|
-
);
|
|
19141
|
+
const fs = this.commandFs(this.base);
|
|
18555
19142
|
this.nodes = new VfsNodes(fs, (path) => this.released(path));
|
|
18556
19143
|
this.nodes.keepsOnDisk = true;
|
|
18557
19144
|
this.openFiles.add(this.nodes);
|
|
@@ -18567,15 +19154,23 @@ var Launcher = class {
|
|
|
18567
19154
|
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
18568
19155
|
}
|
|
18569
19156
|
);
|
|
18570
|
-
this.fs = this.
|
|
19157
|
+
this.fs = this.processChain(fs, this.nodes, this.unlinked);
|
|
18571
19158
|
this.writerTags = writerTags(options.protect);
|
|
18572
19159
|
this.unlinkOwnerOption = options.unlinkOwner;
|
|
18573
19160
|
if (this.writerTags.size > 0 && options.fs.protect && !options.fs.unguarded) {
|
|
18574
19161
|
throw new TypeError("protect.writers needs a root file system with an unguarded view");
|
|
18575
19162
|
}
|
|
19163
|
+
if (!options.fs.noLinks) throw new TypeError("the root file system needs a noLinks view");
|
|
18576
19164
|
this.unguardedRoot = options.fs.unguarded?.() ?? options.fs;
|
|
18577
19165
|
this.prefixes = protectedPrefixes(options.protect?.paths ?? []);
|
|
18578
|
-
this.
|
|
19166
|
+
this.protection = this.prefixes.length > 0 ? new ProtectedFs(this.prefixes) : void 0;
|
|
19167
|
+
this.processFs = this.protection?.view(this.fs) ?? this.fs;
|
|
19168
|
+
const noLinks = this.processChain(
|
|
19169
|
+
this.commandFs(this.rooted(this.noLinksOf(this.mounted))),
|
|
19170
|
+
this.nodes,
|
|
19171
|
+
this.unlinked
|
|
19172
|
+
);
|
|
19173
|
+
this.noLinksFs = this.protection?.view(noLinks) ?? noLinks;
|
|
18579
19174
|
const integrity = options.protect?.integrity;
|
|
18580
19175
|
integrityOptions(integrity);
|
|
18581
19176
|
options.fs.protect?.(this.prefixes, Boolean(integrity));
|
|
@@ -18987,10 +19582,15 @@ var Launcher = class {
|
|
|
18987
19582
|
}
|
|
18988
19583
|
async unrunnable(file, cwd, scope) {
|
|
18989
19584
|
const path = this.fs.resolvePath(cwd, file);
|
|
19585
|
+
if (await this.directory(file, path)) return "EACCES";
|
|
18990
19586
|
const head = await this.base.readFileBuffer(path).catch(() => null);
|
|
18991
19587
|
if (!head || head[0] === 35 && head[1] === 33) return "ENOENT";
|
|
18992
19588
|
return binfmtOf(await this.interpreters(scope), path) ? "ENOENT" : "ENOEXEC";
|
|
18993
19589
|
}
|
|
19590
|
+
async directory(file, path) {
|
|
19591
|
+
if (!file.includes("/") || COMMAND2.test(file)) return false;
|
|
19592
|
+
return (await this.base.stat(path).catch(() => void 0))?.isDirectory === true;
|
|
19593
|
+
}
|
|
18994
19594
|
verifiedFs(fs) {
|
|
18995
19595
|
const read = (path) => this.verified(path, KERNEL_CALLER, fs);
|
|
18996
19596
|
return Object.assign(Object.create(fs), {
|
|
@@ -19130,10 +19730,29 @@ var Launcher = class {
|
|
|
19130
19730
|
return this.jobs.sessionOf(pgid);
|
|
19131
19731
|
}
|
|
19132
19732
|
loopbackFor(pid) {
|
|
19133
|
-
return new KernelLoopback(
|
|
19134
|
-
|
|
19135
|
-
|
|
19733
|
+
return new KernelLoopback(
|
|
19734
|
+
this.net,
|
|
19735
|
+
() => {
|
|
19736
|
+
const cred = this.credOf(pid);
|
|
19737
|
+
return { pid, uid: cred.euid, gid: cred.egid };
|
|
19738
|
+
},
|
|
19739
|
+
() => this.connectorStamp(pid)
|
|
19740
|
+
);
|
|
19741
|
+
}
|
|
19742
|
+
sourceCheck(pid, writer) {
|
|
19743
|
+
const { integrity } = this;
|
|
19744
|
+
if (!integrity?.readsChecked || writer) return {};
|
|
19745
|
+
const caller = () => ({
|
|
19746
|
+
pid,
|
|
19747
|
+
uid: this.credOf(pid).euid,
|
|
19748
|
+
tag: this.tagOf(pid)
|
|
19136
19749
|
});
|
|
19750
|
+
return {
|
|
19751
|
+
source: {
|
|
19752
|
+
read: (path, plain) => integrity.readSource(path, caller(), plain),
|
|
19753
|
+
open: (path, real) => integrity.openSource(path, caller(), real)
|
|
19754
|
+
}
|
|
19755
|
+
};
|
|
19137
19756
|
}
|
|
19138
19757
|
treeScrubs(req) {
|
|
19139
19758
|
const inherited2 = req.ppid !== void 0 ? this.scrubs.get(req.ppid) : void 0;
|
|
@@ -19170,11 +19789,12 @@ var Launcher = class {
|
|
|
19170
19789
|
},
|
|
19171
19790
|
settling: () => this.settling.isSettling(pid),
|
|
19172
19791
|
...req.ignored ? { ignored: req.ignored } : {},
|
|
19173
|
-
...req.blocked ? { blocked: req.blocked } : {},
|
|
19792
|
+
...anyBlocked(req.blocked) ? { blocked: req.blocked } : {},
|
|
19174
19793
|
...req.umask !== void 0 ? { umask: req.umask } : {},
|
|
19175
19794
|
cred: req.cred,
|
|
19176
19795
|
...this.owners && req.program.abi === "wasi" ? { fresh: (path, umask) => this.freshModules.add(path, umask, pid) } : {},
|
|
19177
19796
|
...this.owners ? { owners: true } : {},
|
|
19797
|
+
...this.sourceCheck(pid, stack.writer),
|
|
19178
19798
|
...linksOf2(req),
|
|
19179
19799
|
sealed: this.sealed,
|
|
19180
19800
|
...scrubs ? { outputFilter: (file) => scrubbedAll(file, scrubs) } : {},
|
|
@@ -19187,6 +19807,7 @@ var Launcher = class {
|
|
|
19187
19807
|
cwd: req.cwd,
|
|
19188
19808
|
fds: req.fds,
|
|
19189
19809
|
fs: stack.fs,
|
|
19810
|
+
noLinks: stack.noLinks,
|
|
19190
19811
|
...this.prefixes.length > 0 && !stack.writer ? { readonlyAt: (path) => isProtected(this.prefixes, path) } : {},
|
|
19191
19812
|
createWorker: this.createWorker,
|
|
19192
19813
|
onError: req.report,
|
|
@@ -19264,6 +19885,11 @@ var Launcher = class {
|
|
|
19264
19885
|
return { shownPid: this.jobs.shown(req.ppid), ...ppid !== void 0 ? { ppid } : {} };
|
|
19265
19886
|
}
|
|
19266
19887
|
forget(pid) {
|
|
19888
|
+
const image = this.jobs.list().find((m) => m.pid === pid)?.execParent;
|
|
19889
|
+
const described2 = this.described.get(pid);
|
|
19890
|
+
if (image !== void 0 && described2 && this.described.has(image)) {
|
|
19891
|
+
this.described.set(image, described2);
|
|
19892
|
+
}
|
|
19267
19893
|
this.jobs.remove(pid);
|
|
19268
19894
|
this.described.delete(pid);
|
|
19269
19895
|
const spent = this.spent.get(pid);
|
|
@@ -19379,10 +20005,11 @@ var Launcher = class {
|
|
|
19379
20005
|
const root = member && rootOf(member).pid;
|
|
19380
20006
|
return root !== void 0 && this.running(root) ? root : INIT_PID;
|
|
19381
20007
|
};
|
|
20008
|
+
const superseded = new Set([...members.values()].map((m) => m.execParent));
|
|
19382
20009
|
const listed = [];
|
|
19383
20010
|
for (const member of members.values()) {
|
|
19384
20011
|
const described2 = this.described.get(member.pid);
|
|
19385
|
-
if (member.execed || !described2) continue;
|
|
20012
|
+
if (member.execed && superseded.has(member.pid) || !described2) continue;
|
|
19386
20013
|
const cred = this.credOf(member.pid);
|
|
19387
20014
|
if (!maySee(viewer, cred)) continue;
|
|
19388
20015
|
const root = rootOf(member);
|
|
@@ -19567,7 +20194,7 @@ var Launcher = class {
|
|
|
19567
20194
|
}
|
|
19568
20195
|
signalMasksOf(pid) {
|
|
19569
20196
|
const masks = this.processes.get(pid)?.signalMasks?.();
|
|
19570
|
-
const blocked = this.blockedBy(pid);
|
|
20197
|
+
const [blocked] = this.blockedBy(pid);
|
|
19571
20198
|
const sigBlk = blocked ? { sigBlk: blocked } : {};
|
|
19572
20199
|
return masks ? { sigIgn: masks.ignored, sigCgt: masks.caught, ...sigBlk } : sigBlk;
|
|
19573
20200
|
}
|
|
@@ -19575,14 +20202,14 @@ var Launcher = class {
|
|
|
19575
20202
|
return this.settling.isSettling(pid) || (this.ignoredBy(pid) & sigbit(SIG.TTOU)) !== 0;
|
|
19576
20203
|
}
|
|
19577
20204
|
blockedBy(pid) {
|
|
19578
|
-
return this.processes.get(pid)?.blockedSignals?.() ?? 0;
|
|
20205
|
+
return this.processes.get(pid)?.blockedSignals?.() ?? [0, 0];
|
|
19579
20206
|
}
|
|
19580
20207
|
ignoredBy(pid, fork = false) {
|
|
19581
20208
|
return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
|
|
19582
20209
|
}
|
|
19583
20210
|
kill(pid, sig, sender, from) {
|
|
19584
20211
|
const session = from === void 0 ? void 0 : this.jobs.sidOf(from);
|
|
19585
|
-
const may = (target) => !sender || maySignal(sender, this.credOf(target)) || sig === SIG.CONT && session !== void 0 && this.jobs.sidOf(target) === session || from !== void 0 && this.imageOf(from) === this.imageOf(target) || from !== void 0 && this.granted.get(this.jobs.shown(target)) === from;
|
|
20212
|
+
const may = (target) => !sender || maySignal(sender, this.credOf(target)) || sig === SIG.CONT && session !== void 0 && this.jobs.sidOf(target) === session || from !== void 0 && this.imageOf(from) === this.imageOf(target) || from !== void 0 && this.granted.get(this.jobs.shown(target)) === from || from !== void 0 && this.granted.get(this.jobs.pgidOf(target) ?? 0) === from;
|
|
19586
20213
|
if (pid === -1) {
|
|
19587
20214
|
const sender2 = from === void 0 ? void 0 : this.imageOf(from);
|
|
19588
20215
|
return this.killAll(sig, (member) => this.imageOf(member) !== sender2, may);
|
|
@@ -19945,19 +20572,20 @@ var Launcher = class {
|
|
|
19945
20572
|
}
|
|
19946
20573
|
stackFor(tag) {
|
|
19947
20574
|
if (!this.isWriter(tag)) {
|
|
19948
|
-
return {
|
|
20575
|
+
return {
|
|
20576
|
+
fs: this.processFs,
|
|
20577
|
+
noLinks: this.noLinksFs,
|
|
20578
|
+
nodes: this.nodes,
|
|
20579
|
+
unlinked: this.unlinked,
|
|
20580
|
+
writer: false
|
|
20581
|
+
};
|
|
19949
20582
|
}
|
|
19950
20583
|
this.writers ??= this.writerStack();
|
|
19951
20584
|
return this.writers;
|
|
19952
20585
|
}
|
|
19953
20586
|
writerStack() {
|
|
19954
|
-
const
|
|
19955
|
-
const fs =
|
|
19956
|
-
this.owners ? this.freshModules.wrap(watched) : watched,
|
|
19957
|
-
async () => new Set((await this.commands()).keys()),
|
|
19958
|
-
(dir) => this.userCommandDir(dir),
|
|
19959
|
-
this.owners
|
|
19960
|
-
);
|
|
20587
|
+
const mounted = this.mounts.wrap(this.unguardedRoot);
|
|
20588
|
+
const fs = this.commandFs(this.rooted(mounted));
|
|
19961
20589
|
const nodes = new VfsNodes(fs, (path) => this.released(path), this.nodes.entries);
|
|
19962
20590
|
nodes.keepsOnDisk = true;
|
|
19963
20591
|
this.openFiles.add(nodes);
|
|
@@ -19972,7 +20600,32 @@ var Launcher = class {
|
|
|
19972
20600
|
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
19973
20601
|
}
|
|
19974
20602
|
);
|
|
19975
|
-
|
|
20603
|
+
const noLinks = this.commandFs(this.rooted(this.noLinksOf(mounted)));
|
|
20604
|
+
return {
|
|
20605
|
+
fs: this.processChain(fs, nodes, unlinked),
|
|
20606
|
+
noLinks: this.processChain(noLinks, nodes, unlinked),
|
|
20607
|
+
nodes,
|
|
20608
|
+
unlinked,
|
|
20609
|
+
writer: true
|
|
20610
|
+
};
|
|
20611
|
+
}
|
|
20612
|
+
noLinksOf(mounted) {
|
|
20613
|
+
return mounted.noLinks.call(mounted);
|
|
20614
|
+
}
|
|
20615
|
+
rooted(mounted) {
|
|
20616
|
+
const watched = this.watchers.wrap(mounted);
|
|
20617
|
+
return this.owners ? this.freshModules.wrap(watched) : watched;
|
|
20618
|
+
}
|
|
20619
|
+
commandFs(root) {
|
|
20620
|
+
return withCommandDirs(
|
|
20621
|
+
root,
|
|
20622
|
+
async () => new Set((await this.commands()).keys()),
|
|
20623
|
+
(dir) => this.userCommandDir(dir),
|
|
20624
|
+
this.owners
|
|
20625
|
+
);
|
|
20626
|
+
}
|
|
20627
|
+
processChain(fs, nodes, unlinked) {
|
|
20628
|
+
return unlinked.wrap(keepingOpen(fs, nodes));
|
|
19976
20629
|
}
|
|
19977
20630
|
async starting(argv, dir, account, tag) {
|
|
19978
20631
|
this.resetCatalogs();
|
|
@@ -20054,8 +20707,10 @@ var Launcher = class {
|
|
|
20054
20707
|
const report = (message3) => stderr(encoder7.encode(`${message3}
|
|
20055
20708
|
`));
|
|
20056
20709
|
if (!planned) {
|
|
20057
|
-
|
|
20058
|
-
|
|
20710
|
+
const directory = await this.directory(file, this.fs.resolvePath(cwd, file));
|
|
20711
|
+
report(`${file}: ${directory ? "Is a directory" : "command not found"}`);
|
|
20712
|
+
const status2 = directory ? NOT_EXECUTABLE : NOT_FOUND;
|
|
20713
|
+
return { status: status2, stdout: concat6(out), stderr: concat6(err) };
|
|
20059
20714
|
}
|
|
20060
20715
|
const fds = new FdTable();
|
|
20061
20716
|
fds.installAt(0, options.stdin ? bytesSource(options.stdin) : deviceFile("null"));
|
|
@@ -20078,6 +20733,8 @@ var Launcher = class {
|
|
|
20078
20733
|
};
|
|
20079
20734
|
|
|
20080
20735
|
// src/node/loopback.ts
|
|
20736
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
20737
|
+
import { subscribe } from "node:diagnostics_channel";
|
|
20081
20738
|
import { lookup } from "node:dns/promises";
|
|
20082
20739
|
import { isIP } from "node:net";
|
|
20083
20740
|
var DEVTOOLS_PORTS = [9222, 9223, 9224, 9225, 9226, 9227, 9228, 9229];
|
|
@@ -20137,6 +20794,11 @@ function refusal(url, address, place) {
|
|
|
20137
20794
|
const why = place === "link-local" ? `nodeTransport refuses link-local ${where}` : `nodeTransport refuses the host's loopback ${where} (see nodeTransport({ hostLoopback }))`;
|
|
20138
20795
|
return Object.assign(new Error(why), { status: 403, code: "EACCES" });
|
|
20139
20796
|
}
|
|
20797
|
+
function connectRefusal(address, port, place) {
|
|
20798
|
+
const where = `${address}:${port}`;
|
|
20799
|
+
const why = place === "link-local" ? `nodeTransport refuses link-local ${where}` : `nodeTransport refuses the host's loopback ${where} (see nodeTransport({ hostLoopback }))`;
|
|
20800
|
+
return Object.assign(new Error(why), { status: 403, code: "EACCES" });
|
|
20801
|
+
}
|
|
20140
20802
|
async function addressesOf(host, resolve2) {
|
|
20141
20803
|
const plain = host.replace(/^\[(.*)\]$/, "$1");
|
|
20142
20804
|
if (isIP(plain)) return [plain];
|
|
@@ -20163,7 +20825,66 @@ function guarded2(check, signal) {
|
|
|
20163
20825
|
});
|
|
20164
20826
|
return Promise.race([check, aborted2]).finally(() => signal.removeEventListener("abort", stop));
|
|
20165
20827
|
}
|
|
20828
|
+
var loopbackStore = new AsyncLocalStorage();
|
|
20829
|
+
var subscribed = false;
|
|
20830
|
+
function envProxyEnabled() {
|
|
20831
|
+
if (process.env.NODE_USE_ENV_PROXY === "1") return true;
|
|
20832
|
+
const opts = process.env.NODE_OPTIONS ?? "";
|
|
20833
|
+
const args = process.execArgv ?? [];
|
|
20834
|
+
return args.includes("--use-env-proxy") || opts.includes("--use-env-proxy");
|
|
20835
|
+
}
|
|
20836
|
+
function normaliseProxyUrl(url) {
|
|
20837
|
+
try {
|
|
20838
|
+
const parsed2 = new URL(url);
|
|
20839
|
+
const host = parsed2.hostname.replace(/^\[(.*)\]$/, "$1");
|
|
20840
|
+
const port = parsed2.port || (parsed2.protocol === "https:" ? "443" : "80");
|
|
20841
|
+
return `${host}:${port}`;
|
|
20842
|
+
} catch {
|
|
20843
|
+
return void 0;
|
|
20844
|
+
}
|
|
20845
|
+
}
|
|
20846
|
+
function proxyAddresses() {
|
|
20847
|
+
if (!envProxyEnabled()) return /* @__PURE__ */ new Set();
|
|
20848
|
+
const out = /* @__PURE__ */ new Set();
|
|
20849
|
+
for (const key of ["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"]) {
|
|
20850
|
+
const val = process.env[key];
|
|
20851
|
+
if (val) {
|
|
20852
|
+
const addr = normaliseProxyUrl(val);
|
|
20853
|
+
if (addr) out.add(addr);
|
|
20854
|
+
}
|
|
20855
|
+
}
|
|
20856
|
+
return out;
|
|
20857
|
+
}
|
|
20858
|
+
function ensureSubscribed() {
|
|
20859
|
+
if (subscribed) return;
|
|
20860
|
+
subscribed = true;
|
|
20861
|
+
subscribe("undici:client:connected", (message3) => {
|
|
20862
|
+
const entry = loopbackStore.getStore();
|
|
20863
|
+
if (!entry) return;
|
|
20864
|
+
const { socket, connectParams } = message3;
|
|
20865
|
+
const address = socket.remoteAddress;
|
|
20866
|
+
if (!address) {
|
|
20867
|
+
const err2 = connectRefusal("unknown", 0, "loopback");
|
|
20868
|
+
entry.refused = err2;
|
|
20869
|
+
socket.destroy(err2);
|
|
20870
|
+
return;
|
|
20871
|
+
}
|
|
20872
|
+
const proxies = proxyAddresses();
|
|
20873
|
+
if (proxies.size > 0 && connectParams.hostname) {
|
|
20874
|
+
const cpPort = connectParams.port || (connectParams.protocol === "https:" ? "443" : "80");
|
|
20875
|
+
if (proxies.has(`${connectParams.hostname}:${cpPort}`)) return;
|
|
20876
|
+
}
|
|
20877
|
+
const place = placeOf(address);
|
|
20878
|
+
if (!place) return;
|
|
20879
|
+
const port = socket.remotePort ?? 0;
|
|
20880
|
+
if (place === "loopback" && allows(entry.policy, port)) return;
|
|
20881
|
+
const err = connectRefusal(address, port, place);
|
|
20882
|
+
entry.refused = err;
|
|
20883
|
+
socket.destroy(err);
|
|
20884
|
+
});
|
|
20885
|
+
}
|
|
20166
20886
|
function guardedLoopback(transport, policy, resolve2) {
|
|
20887
|
+
ensureSubscribed();
|
|
20167
20888
|
const open = transport.websocket?.bind(transport);
|
|
20168
20889
|
return {
|
|
20169
20890
|
get traits() {
|
|
@@ -20171,12 +20892,22 @@ function guardedLoopback(transport, policy, resolve2) {
|
|
|
20171
20892
|
},
|
|
20172
20893
|
fetch: async (request, owner) => {
|
|
20173
20894
|
await guarded2(checkDestination(request.url, policy, resolve2), request.signal);
|
|
20174
|
-
|
|
20895
|
+
const entry = { policy };
|
|
20896
|
+
try {
|
|
20897
|
+
return await loopbackStore.run(entry, () => transport.fetch(request, owner));
|
|
20898
|
+
} catch (err) {
|
|
20899
|
+
throw entry.refused ?? err;
|
|
20900
|
+
}
|
|
20175
20901
|
},
|
|
20176
20902
|
...open ? {
|
|
20177
20903
|
websocket: async (request) => {
|
|
20178
20904
|
await guarded2(checkDestination(request.url, policy, resolve2), request.signal);
|
|
20179
|
-
|
|
20905
|
+
const entry = { policy };
|
|
20906
|
+
try {
|
|
20907
|
+
return await loopbackStore.run(entry, () => open(request));
|
|
20908
|
+
} catch (err) {
|
|
20909
|
+
throw entry.refused ?? err;
|
|
20910
|
+
}
|
|
20180
20911
|
}
|
|
20181
20912
|
} : {}
|
|
20182
20913
|
};
|
|
@@ -20228,8 +20959,8 @@ var Chunks = class {
|
|
|
20228
20959
|
const index = Math.floor((at + done4) / CHUNK4);
|
|
20229
20960
|
const from = at + done4 - index * CHUNK4;
|
|
20230
20961
|
const n = Math.min(CHUNK4 - from, bytes.length - done4);
|
|
20231
|
-
const
|
|
20232
|
-
const part =
|
|
20962
|
+
const view2 = bytes.subarray(done4, done4 + n);
|
|
20963
|
+
const part = view2.buffer instanceof ArrayBuffer ? view2 : view2.slice();
|
|
20233
20964
|
const old = this.blobs[index];
|
|
20234
20965
|
if (from === 0 && n >= (old?.size ?? 0)) this.blobs[index] = new Blob([part]);
|
|
20235
20966
|
else {
|