@ai-ecoverse/slicc-kernel 1.92.0 → 1.93.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -12
- package/dist/index.js +40 -7
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +856 -208
- 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 +832 -213
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +944 -217
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +1899 -1292
- 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) {
|
|
@@ -9174,6 +9476,22 @@ function plainDirMode(entry, mode) {
|
|
|
9174
9476
|
const wanted = mode & 4095 & ~(entry.umask?.() ?? 18);
|
|
9175
9477
|
return wanted === PLAIN_DIR_MODE ? void 0 : wanted;
|
|
9176
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
|
+
}
|
|
9177
9495
|
function mkdirOptions(entry, mode = 511) {
|
|
9178
9496
|
const owner = ownerFor2(entry, mode);
|
|
9179
9497
|
if (!owner) return { recursive: true };
|
|
@@ -9185,23 +9503,39 @@ async function mkdirExclusive(entry, path, mode) {
|
|
|
9185
9503
|
await entry.fs.mkdir(path, owner ? { owner } : {});
|
|
9186
9504
|
}
|
|
9187
9505
|
async function chown(entry, path, req) {
|
|
9506
|
+
const real = req.handle !== void 0 ? await keptOr(entry, path, req.handle) : path;
|
|
9188
9507
|
const cred = entry.cred?.();
|
|
9189
9508
|
if (!cred) {
|
|
9190
|
-
await (req.op === "chown" ? entry.fs.stat(
|
|
9509
|
+
await (req.op === "chown" ? entry.fs.stat(real) : entry.fs.lstat(real));
|
|
9191
9510
|
return done;
|
|
9192
9511
|
}
|
|
9193
|
-
await chownAs(entry.fs, cred,
|
|
9512
|
+
await chownAs(entry.fs, cred, real, {
|
|
9194
9513
|
...req.uid !== void 0 ? { uid: req.uid } : {},
|
|
9195
9514
|
...req.gid !== void 0 ? { gid: req.gid } : {},
|
|
9196
9515
|
follow: req.op === "chown"
|
|
9197
9516
|
});
|
|
9198
9517
|
return done;
|
|
9199
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
|
+
}
|
|
9200
9534
|
async function run(entry, path, req) {
|
|
9201
9535
|
const { fs, cwd } = entry;
|
|
9202
9536
|
switch (req.op) {
|
|
9203
9537
|
case "read":
|
|
9204
|
-
return { ok: true, kind: "bytes", bytes: await
|
|
9538
|
+
return { ok: true, kind: "bytes", bytes: await readSource(entry, path) };
|
|
9205
9539
|
case "write":
|
|
9206
9540
|
await fs.writeFile(path, req.body ?? new Uint8Array(0), ownerFor2(entry, 438));
|
|
9207
9541
|
return done;
|
|
@@ -9215,13 +9549,8 @@ async function run(entry, path, req) {
|
|
|
9215
9549
|
return json2(await fs.readdir(path));
|
|
9216
9550
|
case "readdir-stat":
|
|
9217
9551
|
return json2(await readdirStat(fs, path));
|
|
9218
|
-
case "mkdir":
|
|
9219
|
-
|
|
9220
|
-
const fresh2 = plain !== void 0 && !await fs.exists(path);
|
|
9221
|
-
await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
|
|
9222
|
-
if (fresh2) await fs.chmod(path, plain);
|
|
9223
|
-
return done;
|
|
9224
|
-
}
|
|
9552
|
+
case "mkdir":
|
|
9553
|
+
return runMkdir(entry, path, req);
|
|
9225
9554
|
case "rm":
|
|
9226
9555
|
await fs.rm(path, { recursive: true });
|
|
9227
9556
|
return done;
|
|
@@ -9247,7 +9576,7 @@ async function run(entry, path, req) {
|
|
|
9247
9576
|
return done;
|
|
9248
9577
|
case "create":
|
|
9249
9578
|
await createExclusive(fs, path, ownerFor2(entry, req.mode ?? 438));
|
|
9250
|
-
entry.created?.add(path);
|
|
9579
|
+
if (req.opening) entry.created?.add(path);
|
|
9251
9580
|
entry.fresh?.(path);
|
|
9252
9581
|
return done;
|
|
9253
9582
|
case "readlink":
|
|
@@ -9258,10 +9587,10 @@ async function run(entry, path, req) {
|
|
|
9258
9587
|
return done;
|
|
9259
9588
|
}
|
|
9260
9589
|
case "utimes":
|
|
9261
|
-
await fs.utimes(path,
|
|
9590
|
+
await fs.utimes(path, ...await timesOf(entry, path, req));
|
|
9262
9591
|
return done;
|
|
9263
9592
|
case "lutimes":
|
|
9264
|
-
await lutimesOf(fs, path,
|
|
9593
|
+
await lutimesOf(fs, path, ...await timesOf(entry, path, req));
|
|
9265
9594
|
return done;
|
|
9266
9595
|
case "chown":
|
|
9267
9596
|
case "lchown":
|
|
@@ -9274,6 +9603,17 @@ async function run(entry, path, req) {
|
|
|
9274
9603
|
return { ok: false, errno: "EINVAL", message: `sync-fs: unknown op '${req.op}'` };
|
|
9275
9604
|
}
|
|
9276
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
|
+
}
|
|
9277
9617
|
function guardOf(entry) {
|
|
9278
9618
|
const cred = entry.dac && entry.cred?.();
|
|
9279
9619
|
if (!cred || !entry.dac) return void 0;
|
|
@@ -9283,41 +9623,82 @@ function guardOf(entry) {
|
|
|
9283
9623
|
var FOLLOWING = /* @__PURE__ */ new Set(["exists", "stat", "chown", "statfs"]);
|
|
9284
9624
|
var LOOKS = /* @__PURE__ */ new Set(["exists", "stat", "lstat"]);
|
|
9285
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
|
+
}
|
|
9286
9634
|
async function guard(g, path, req, cwd) {
|
|
9287
9635
|
const { op } = req;
|
|
9288
|
-
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
|
+
}
|
|
9289
9640
|
const other = () => g.fs.resolvePath(cwd, req.arg2 ?? "");
|
|
9290
9641
|
switch (op) {
|
|
9291
9642
|
case "readdir":
|
|
9292
|
-
case "readdir-stat":
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
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
|
+
}
|
|
9296
9652
|
case "create":
|
|
9297
9653
|
case "symlink":
|
|
9298
|
-
|
|
9654
|
+
await inParent(g, op, path);
|
|
9655
|
+
return { path: await walkSafe(g, path, op === "create") };
|
|
9299
9656
|
case "unlink":
|
|
9300
|
-
case "rmdir":
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
return
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
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
|
+
}
|
|
9308
9682
|
case "utimes":
|
|
9309
|
-
case "lutimes":
|
|
9310
|
-
|
|
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
|
+
}
|
|
9311
9689
|
case "read":
|
|
9312
9690
|
case "pread":
|
|
9313
|
-
|
|
9691
|
+
if (req.handle === void 0) await readable(g, op, path);
|
|
9692
|
+
return { path: await walkSafe(g, path, true) };
|
|
9314
9693
|
case "pwrite":
|
|
9315
9694
|
case "truncate":
|
|
9316
|
-
|
|
9695
|
+
if (req.handle === void 0) await fileWritable(g, op, path);
|
|
9696
|
+
return { path: await walkSafe(g, path, true) };
|
|
9317
9697
|
case "write":
|
|
9318
|
-
|
|
9698
|
+
if (req.handle === void 0 && req.mode !== 1) await writable(g, op, path);
|
|
9699
|
+
return { path: await walkSafe(g, path, true) };
|
|
9319
9700
|
default:
|
|
9320
|
-
return
|
|
9701
|
+
return { path: await walkSafe(g, path, true) };
|
|
9321
9702
|
}
|
|
9322
9703
|
}
|
|
9323
9704
|
async function opening(entry, path, mode) {
|
|
@@ -9334,6 +9715,7 @@ async function holdOp(entry, path, req) {
|
|
|
9334
9715
|
if (on && open) {
|
|
9335
9716
|
await opening(entry, path, mode);
|
|
9336
9717
|
const real = await entry.fs.realpath?.(path, true).catch(() => path);
|
|
9718
|
+
await entry.source?.open(path, real);
|
|
9337
9719
|
return json2(entry.hold?.(path, on, open, real) ?? null);
|
|
9338
9720
|
}
|
|
9339
9721
|
entry.hold?.(path, on, open, void 0, req.handle);
|
|
@@ -9386,15 +9768,16 @@ async function mayChange(entry, path, req) {
|
|
|
9386
9768
|
}
|
|
9387
9769
|
async function guarded(entry, path, req) {
|
|
9388
9770
|
const g = guardOf(entry);
|
|
9389
|
-
if (!g) return
|
|
9390
|
-
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 {};
|
|
9391
9773
|
try {
|
|
9392
9774
|
await mayChange(entry, path, req);
|
|
9393
|
-
await guard(g, path, req, entry.cwd);
|
|
9394
|
-
return
|
|
9775
|
+
const paths = await guard(g, path, req, entry.cwd);
|
|
9776
|
+
return { paths };
|
|
9395
9777
|
} catch (err) {
|
|
9396
9778
|
const refusal2 = err.code === "EACCES";
|
|
9397
|
-
|
|
9779
|
+
const refused2 = req.op === "exists" && refusal2 ? json2(false) : toErrno(err);
|
|
9780
|
+
return { refused: refused2 };
|
|
9398
9781
|
}
|
|
9399
9782
|
}
|
|
9400
9783
|
async function createExclusive(fs, path, owner) {
|
|
@@ -9420,6 +9803,7 @@ async function ranged(fs, path, req) {
|
|
|
9420
9803
|
else await ops.truncate(path, req.length ?? 0);
|
|
9421
9804
|
return done;
|
|
9422
9805
|
}
|
|
9806
|
+
var CHOWN_OPS = /* @__PURE__ */ new Set(["chown", "lchown"]);
|
|
9423
9807
|
var UNLINKED_OPS = /* @__PURE__ */ new Set(["read", "pread", "pwrite", "truncate"]);
|
|
9424
9808
|
var OWN_OPS = /* @__PURE__ */ new Set(["rm", "unlink", "rmdir", "rename", "link"]);
|
|
9425
9809
|
var TWO_PATHS = /* @__PURE__ */ new Set(["rename", "link"]);
|
|
@@ -9484,7 +9868,7 @@ async function rmPermitted(entry, path, req, refused2) {
|
|
|
9484
9868
|
const st = await entry.fs.lstat(path).catch(() => void 0);
|
|
9485
9869
|
if (!st?.isDirectory) return refused2;
|
|
9486
9870
|
const unlisted = await guarded(entry, path, { ...req, op: "readdir" });
|
|
9487
|
-
if (unlisted) return unlisted;
|
|
9871
|
+
if ("refused" in unlisted) return unlisted.refused;
|
|
9488
9872
|
const names = await entry.fs.readdir(path).catch(() => void 0);
|
|
9489
9873
|
if (!names) return refused2;
|
|
9490
9874
|
let first;
|
|
@@ -9496,31 +9880,64 @@ async function rmPermitted(entry, path, req, refused2) {
|
|
|
9496
9880
|
if (!child.ok) first ??= child;
|
|
9497
9881
|
}
|
|
9498
9882
|
if (first) return first;
|
|
9499
|
-
const
|
|
9500
|
-
|
|
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;
|
|
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
|
+
};
|
|
9501
9910
|
}
|
|
9911
|
+
var REMOVALS = /* @__PURE__ */ new Set(["unlink", "rmdir", "rm"]);
|
|
9502
9912
|
async function dispatchOne(entry, req) {
|
|
9503
9913
|
const { fs, cwd } = entry;
|
|
9504
9914
|
try {
|
|
9505
9915
|
const path = fs.resolvePath(cwd, req.path);
|
|
9506
|
-
|
|
9507
|
-
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;
|
|
9508
9920
|
if (req.op === "access") return await accessOp(entry, path, req);
|
|
9509
9921
|
if (req.op === "hold") return await holdOp(entry, path, req);
|
|
9510
|
-
const
|
|
9511
|
-
if (
|
|
9512
|
-
|
|
9513
|
-
|
|
9514
|
-
|
|
9515
|
-
|
|
9516
|
-
|
|
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` };
|
|
9517
9934
|
if (req.op === "write" && req.mode === 1 || UNLINKED_OPS.has(req.op)) {
|
|
9518
|
-
const serve = async () => run(
|
|
9519
|
-
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());
|
|
9520
9937
|
}
|
|
9521
|
-
if (!entry.own || !OWN_OPS.has(req.op)) return await run(
|
|
9522
|
-
const target = TWO_PATHS.has(req.op) ? fs.resolvePath(cwd, req.arg2 ?? "") :
|
|
9523
|
-
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);
|
|
9524
9941
|
} catch (err) {
|
|
9525
9942
|
return toErrno(err);
|
|
9526
9943
|
}
|
|
@@ -9548,6 +9965,15 @@ var SAB_I_KILLED = 13;
|
|
|
9548
9965
|
var SAB_I_ASYNC = 14;
|
|
9549
9966
|
var SAB_I_STOP = 15;
|
|
9550
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);
|
|
9551
9977
|
var U32 = 2 ** 32;
|
|
9552
9978
|
function storeU53(header4, lo, hi, value) {
|
|
9553
9979
|
Atomics.store(header4, hi, Math.floor(value / U32));
|
|
@@ -10074,7 +10500,7 @@ function mountTable(mounts = []) {
|
|
|
10074
10500
|
}
|
|
10075
10501
|
|
|
10076
10502
|
// src/version.ts
|
|
10077
|
-
var KERNEL_VERSION = true ? "1.
|
|
10503
|
+
var KERNEL_VERSION = true ? "1.93.1" : "0.0.0-dev";
|
|
10078
10504
|
var OS_RELEASE = "6.0.0-slicc";
|
|
10079
10505
|
function procVersion() {
|
|
10080
10506
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -10774,21 +11200,28 @@ function bodyOf(incoming, head, method, signal) {
|
|
|
10774
11200
|
var KernelLoopback = class {
|
|
10775
11201
|
net;
|
|
10776
11202
|
cred;
|
|
10777
|
-
|
|
11203
|
+
stamp;
|
|
11204
|
+
constructor(net, cred, stamp) {
|
|
10778
11205
|
this.net = net;
|
|
10779
11206
|
this.cred = cred;
|
|
11207
|
+
this.stamp = stamp;
|
|
10780
11208
|
}
|
|
10781
11209
|
port(url) {
|
|
10782
11210
|
return kernelLoopbackPort(url);
|
|
10783
11211
|
}
|
|
10784
|
-
connect(url, port, signal) {
|
|
11212
|
+
async connect(url, port, signal) {
|
|
10785
11213
|
if (url.protocol === "https:" || url.protocol === "wss:") {
|
|
10786
11214
|
throw errno("ENOTSUP", `TLS to the kernel's loopback is not supported: ${url.origin}`);
|
|
10787
11215
|
}
|
|
10788
11216
|
signal.throwIfAborted();
|
|
10789
11217
|
const socket = this.net.socket("inet");
|
|
10790
11218
|
try {
|
|
10791
|
-
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
|
+
);
|
|
10792
11225
|
} catch (err) {
|
|
10793
11226
|
socket.close();
|
|
10794
11227
|
const refused2 = err instanceof KernelError && err.code === "ECONNREFUSED";
|
|
@@ -10802,7 +11235,7 @@ var KernelLoopback = class {
|
|
|
10802
11235
|
const body = req.body ?? (/^(POST|PUT|PATCH)$/i.test(req.method) ? new Uint8Array(0) : void 0);
|
|
10803
11236
|
const length2 = body ? [["Content-Length", String(body.length)]] : [];
|
|
10804
11237
|
const head = requestHead(req.method, url, req.headers, [...length2, ["Connection", "close"]]);
|
|
10805
|
-
const socket = this.connect(url, port, req.signal);
|
|
11238
|
+
const socket = await this.connect(url, port, req.signal);
|
|
10806
11239
|
try {
|
|
10807
11240
|
await writeAll2(socket, body ? concat([head, body]) : head, req.signal);
|
|
10808
11241
|
const incoming = new Incoming(socket);
|
|
@@ -10823,7 +11256,7 @@ var KernelLoopback = class {
|
|
|
10823
11256
|
}
|
|
10824
11257
|
async websocket(req, port) {
|
|
10825
11258
|
const url = new URL(req.url);
|
|
10826
|
-
const socket = this.connect(url, port, req.signal);
|
|
11259
|
+
const socket = await this.connect(url, port, req.signal);
|
|
10827
11260
|
try {
|
|
10828
11261
|
const key = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(16))));
|
|
10829
11262
|
const extra = [
|
|
@@ -10848,6 +11281,9 @@ var KernelLoopback = class {
|
|
|
10848
11281
|
if (!upgraded || accept2 !== await acceptFor(key)) {
|
|
10849
11282
|
throw errno("EPROTO", "the server did not accept the WebSocket key");
|
|
10850
11283
|
}
|
|
11284
|
+
if (fieldValues(head.headers, "sec-websocket-extensions").length > 0) {
|
|
11285
|
+
throw errno("EPROTO", "the server offered an unsolicited extension");
|
|
11286
|
+
}
|
|
10851
11287
|
const protocol = fieldValues(head.headers, "sec-websocket-protocol")[0] ?? "";
|
|
10852
11288
|
return new LoopbackWebSocket(socket, incoming, protocol);
|
|
10853
11289
|
} catch (err) {
|
|
@@ -10947,7 +11383,7 @@ var LoopbackWebSocket = class {
|
|
|
10947
11383
|
})();
|
|
10948
11384
|
}
|
|
10949
11385
|
async pump(reader, deliver) {
|
|
10950
|
-
let close = { code: 1006, reason: "" };
|
|
11386
|
+
let close = { code: 1006, reason: "", wasClean: false };
|
|
10951
11387
|
try {
|
|
10952
11388
|
for (; ; ) {
|
|
10953
11389
|
await this.space();
|
|
@@ -10957,14 +11393,14 @@ var LoopbackWebSocket = class {
|
|
|
10957
11393
|
else if (event.kind === "binary") deliver(event.bytes);
|
|
10958
11394
|
else if (event.kind === "ping") this.write(clientFrame(OP_PONG, event.payload));
|
|
10959
11395
|
else {
|
|
10960
|
-
close = { code: event.code, reason: event.reason };
|
|
11396
|
+
close = { code: event.code, reason: event.reason, wasClean: true };
|
|
10961
11397
|
if (!this.sentClose) this.sendClose(event.code === 1005 ? 1e3 : event.code, "");
|
|
10962
11398
|
break;
|
|
10963
11399
|
}
|
|
10964
11400
|
}
|
|
10965
11401
|
} catch (err) {
|
|
10966
11402
|
if (err instanceof WsError) this.sendClose(err.code, err.message);
|
|
10967
|
-
close = { code:
|
|
11403
|
+
close = { code: 1006, reason: "", wasClean: false };
|
|
10968
11404
|
} finally {
|
|
10969
11405
|
const late = setTimeout(() => this.stop.abort(), CLOSE_WAIT_MS3);
|
|
10970
11406
|
await this.writes;
|
|
@@ -11754,14 +12190,22 @@ var WsHandles = class {
|
|
|
11754
12190
|
return typeof next2.value === "string" ? { ok: true, kind: "json", json: { text: next2.value } } : { ok: true, kind: "bytes", bytes: next2.value };
|
|
11755
12191
|
}
|
|
11756
12192
|
const closed = await socket.closed;
|
|
11757
|
-
return {
|
|
12193
|
+
return {
|
|
12194
|
+
ok: true,
|
|
12195
|
+
kind: "json",
|
|
12196
|
+
json: { code: closed.code, reason: closed.reason, wasClean: closed.wasClean === true }
|
|
12197
|
+
};
|
|
11758
12198
|
} catch (e) {
|
|
11759
12199
|
return fail("ECONNRESET", message(e));
|
|
11760
12200
|
}
|
|
11761
12201
|
}
|
|
11762
12202
|
async closed(socket) {
|
|
11763
|
-
const
|
|
11764
|
-
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
|
+
};
|
|
11765
12209
|
}
|
|
11766
12210
|
send(socket, req, entry) {
|
|
11767
12211
|
const data = req.text ?? req.body;
|
|
@@ -11801,7 +12245,7 @@ function message2(e) {
|
|
|
11801
12245
|
}
|
|
11802
12246
|
function codeOf2(e) {
|
|
11803
12247
|
const code = e?.code;
|
|
11804
|
-
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";
|
|
11805
12249
|
}
|
|
11806
12250
|
async function* once(bytes) {
|
|
11807
12251
|
yield bytes;
|
|
@@ -11884,7 +12328,8 @@ var HttpHandles = class {
|
|
|
11884
12328
|
if (port !== void 0) return { ok: false, errno: codeOf2(e), message: text };
|
|
11885
12329
|
const status = e.status;
|
|
11886
12330
|
if (text === NO_TRANSPORT || typeof status !== "number" || status === 502) {
|
|
11887
|
-
const
|
|
12331
|
+
const code = codeOf2(e);
|
|
12332
|
+
const errno2 = text === NO_TRANSPORT ? "ENETUNREACH" : code !== "EIO" ? code : "ECONNREFUSED";
|
|
11888
12333
|
return { ok: false, errno: errno2, message: text };
|
|
11889
12334
|
}
|
|
11890
12335
|
entry.body = once(new TextEncoder().encode(text))[Symbol.asyncIterator]();
|
|
@@ -13477,7 +13922,7 @@ function isPtySyscall(req) {
|
|
|
13477
13922
|
return req.op.startsWith("pty-");
|
|
13478
13923
|
}
|
|
13479
13924
|
function isVfsSyscall(req) {
|
|
13480
|
-
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";
|
|
13481
13926
|
}
|
|
13482
13927
|
function isFdSyscall(req) {
|
|
13483
13928
|
return req.op.startsWith("fd-");
|
|
@@ -13497,6 +13942,9 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
13497
13942
|
"fd-pwrite",
|
|
13498
13943
|
"fd-resize",
|
|
13499
13944
|
"fd-vfs-stat",
|
|
13945
|
+
"fd-chmod",
|
|
13946
|
+
"fd-chown",
|
|
13947
|
+
"fd-utimes",
|
|
13500
13948
|
"fd-path-flush",
|
|
13501
13949
|
"fd-path-unlinking",
|
|
13502
13950
|
"fd-path-unlinked",
|
|
@@ -13527,6 +13975,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
13527
13975
|
"fd-tty-owner",
|
|
13528
13976
|
"fd-open-sealed",
|
|
13529
13977
|
"fd-open-clipboard",
|
|
13978
|
+
"fd-clipboard-abort",
|
|
13530
13979
|
"tty-get",
|
|
13531
13980
|
"tty-set",
|
|
13532
13981
|
"tty-winsz",
|
|
@@ -13872,7 +14321,11 @@ var WasmProcess = class {
|
|
|
13872
14321
|
if (!stderr?.write || stderr.clipboard) return;
|
|
13873
14322
|
const line = `${name2}: /dev/clipboard: ${clipboardLine(err.detail)}
|
|
13874
14323
|
`;
|
|
13875
|
-
|
|
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
|
+
]);
|
|
13876
14329
|
}
|
|
13877
14330
|
async write(fd, body, nonblock = false, cancelled) {
|
|
13878
14331
|
const file = this.fds.get(fd).file;
|
|
@@ -13899,10 +14352,13 @@ var WasmProcess = class {
|
|
|
13899
14352
|
await refuseReadonly(this.options.fs, req.path, req.flags, req);
|
|
13900
14353
|
if (this.writesAt(req)) await this.options.fs.mayChange?.(req.path, !req.exclusive);
|
|
13901
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;
|
|
13902
14358
|
const fd = this.fds.install(heldFile(), 3);
|
|
13903
14359
|
try {
|
|
13904
14360
|
const nodes = this.nodes;
|
|
13905
|
-
const path =
|
|
14361
|
+
const path = await this.openPath(req, marked);
|
|
13906
14362
|
if (inProc(path)) {
|
|
13907
14363
|
this.fds.installAt(fd, await this.procFile(req, path));
|
|
13908
14364
|
return fd;
|
|
@@ -13912,23 +14368,23 @@ var WasmProcess = class {
|
|
|
13912
14368
|
const plain = this.plainMode(req.mode);
|
|
13913
14369
|
if (req.exclusive) {
|
|
13914
14370
|
await nodes.createExclusive(path, this.newOwner(req.mode));
|
|
13915
|
-
if (plain !== void 0) await
|
|
13916
|
-
this.fds.installAt(fd, this.openVfsFile(open));
|
|
14371
|
+
if (plain !== void 0) await fs.chmod?.(path, plain);
|
|
14372
|
+
this.fds.installAt(fd, this.openVfsFile(open, void 0, fs));
|
|
13917
14373
|
} else if (req.create) {
|
|
13918
14374
|
await nodes.onEntry(path, async () => {
|
|
13919
14375
|
const marks = this.options.owners || plain !== void 0;
|
|
13920
|
-
const fresh2 = marks && !await present2(
|
|
13921
|
-
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
14376
|
+
const fresh2 = marks && !await present2(fs, path, true);
|
|
14377
|
+
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path), fs));
|
|
13922
14378
|
await this.fds.get(fd).file.stat?.();
|
|
13923
|
-
if (fresh2 && plain !== void 0) await
|
|
14379
|
+
if (fresh2 && plain !== void 0) await fs.chmod?.(path, plain);
|
|
13924
14380
|
else if (fresh2) {
|
|
13925
|
-
await
|
|
14381
|
+
await fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
|
|
13926
14382
|
}
|
|
13927
14383
|
});
|
|
13928
14384
|
} else {
|
|
13929
|
-
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
14385
|
+
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path), fs));
|
|
13930
14386
|
}
|
|
13931
|
-
if (req.existing && !await present2(
|
|
14387
|
+
if (req.existing && !await present2(fs, path, true)) {
|
|
13932
14388
|
throw new KernelError("ENOENT");
|
|
13933
14389
|
}
|
|
13934
14390
|
} catch (err) {
|
|
@@ -13962,6 +14418,27 @@ var WasmProcess = class {
|
|
|
13962
14418
|
writesAt(req) {
|
|
13963
14419
|
return (req.flags & WRITE_ACCESS) !== 0 || req.truncate === true || req.create === true || req.exclusive === true;
|
|
13964
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
|
+
}
|
|
13965
14442
|
async mayOpen(req) {
|
|
13966
14443
|
const { guard: guard2 } = this.options;
|
|
13967
14444
|
const inherited2 = req.orphan || req.contents !== void 0 || req.handle !== void 0;
|
|
@@ -14061,9 +14538,9 @@ var WasmProcess = class {
|
|
|
14061
14538
|
);
|
|
14062
14539
|
return memoryFile(tree2.read(path), path, req.position);
|
|
14063
14540
|
}
|
|
14064
|
-
openVfsFile(req, id2) {
|
|
14541
|
+
openVfsFile(req, id2, fs = this.options.fs) {
|
|
14065
14542
|
return vfsFile(
|
|
14066
|
-
|
|
14543
|
+
fs,
|
|
14067
14544
|
{
|
|
14068
14545
|
path: req.path,
|
|
14069
14546
|
flags: req.flags,
|
|
@@ -14146,7 +14623,7 @@ var WasmProcess = class {
|
|
|
14146
14623
|
return { ok: true, kind: "void" };
|
|
14147
14624
|
case "fd-renumber":
|
|
14148
14625
|
if (req.from !== req.to) {
|
|
14149
|
-
this.fds.
|
|
14626
|
+
await this.fds.dup2Clipboard(req.from, req.to);
|
|
14150
14627
|
if (!req.keep) await Promise.resolve(this.fds.close(req.from));
|
|
14151
14628
|
}
|
|
14152
14629
|
return { ok: true, kind: "void" };
|
|
@@ -14158,6 +14635,11 @@ var WasmProcess = class {
|
|
|
14158
14635
|
return { ok: true, kind: "json", json: this.openSealed(req.flags) };
|
|
14159
14636
|
case "fd-open-clipboard":
|
|
14160
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
|
+
}
|
|
14161
14643
|
case "fd-tty-owner":
|
|
14162
14644
|
return { ok: true, kind: "json", json: this.terminalByName(req.name).owner ?? null };
|
|
14163
14645
|
case "fd-select": {
|
|
@@ -14210,12 +14692,23 @@ var WasmProcess = class {
|
|
|
14210
14692
|
case "fd-resize":
|
|
14211
14693
|
await file.resize(req.size);
|
|
14212
14694
|
return { ok: true, kind: "void" };
|
|
14213
|
-
case "fd-vfs-stat":
|
|
14214
|
-
|
|
14215
|
-
|
|
14216
|
-
|
|
14217
|
-
|
|
14218
|
-
|
|
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);
|
|
14219
14712
|
}
|
|
14220
14713
|
}
|
|
14221
14714
|
keptPath;
|
|
@@ -14236,7 +14729,7 @@ var WasmProcess = class {
|
|
|
14236
14729
|
async promote(req) {
|
|
14237
14730
|
if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
|
|
14238
14731
|
if (req.share !== void 0) {
|
|
14239
|
-
this.fds.
|
|
14732
|
+
await this.fds.dup2Clipboard(req.share, req.fd);
|
|
14240
14733
|
return;
|
|
14241
14734
|
}
|
|
14242
14735
|
if (req.path === void 0) throw new KernelError("EINVAL");
|
|
@@ -14265,6 +14758,46 @@ var WasmProcess = class {
|
|
|
14265
14758
|
this.fds.installAt(fd, heldFile(meta));
|
|
14266
14759
|
return fd;
|
|
14267
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
|
+
}
|
|
14268
14801
|
fdInfo(fd) {
|
|
14269
14802
|
const file = this.fds.get(fd).file;
|
|
14270
14803
|
const flags2 = this.fds.statusFlags(fd);
|
|
@@ -14483,7 +15016,10 @@ var WasmProcess = class {
|
|
|
14483
15016
|
const { file, argv, env, cwd, cred, caller } = answer.grant;
|
|
14484
15017
|
const stdio = [{ fd: 0 }, { fd: 1 }, { fd: 2 }];
|
|
14485
15018
|
return {
|
|
14486
|
-
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
|
+
})
|
|
14487
15023
|
};
|
|
14488
15024
|
}
|
|
14489
15025
|
async mountSyscall(req, cancelled) {
|
|
@@ -14787,7 +15323,9 @@ function holdingToken(opts, holds, caller) {
|
|
|
14787
15323
|
...opts.dac ? { dac: opts.dac, pid: opts.pid, created: caller.created } : {},
|
|
14788
15324
|
...opts.links !== void 0 ? { links: opts.links } : {},
|
|
14789
15325
|
...opts.fresh ? { fresh: (path) => opts.fresh?.(path, caller.umask()) } : {},
|
|
15326
|
+
...opts.source ? { source: opts.source } : {},
|
|
14790
15327
|
fs: opts.fs,
|
|
15328
|
+
noLinks: opts.noLinks,
|
|
14791
15329
|
cwd: opts.cwd,
|
|
14792
15330
|
...opts.statfs ? { statfs: opts.statfs } : {},
|
|
14793
15331
|
hold: (path, on, open, real, handle) => holds.hold(path, on, open, real, handle),
|
|
@@ -14813,6 +15351,8 @@ function processOptions(opts, header4, hooks, created3) {
|
|
|
14813
15351
|
...opts.outputFilter ? { outputFilter: opts.outputFilter } : {},
|
|
14814
15352
|
...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
|
|
14815
15353
|
...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
|
|
15354
|
+
...opts.source ? { source: opts.source } : {},
|
|
15355
|
+
noLinks: opts.noLinks,
|
|
14816
15356
|
...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3, ...linksOf(opts) } } : {},
|
|
14817
15357
|
...opts.identity ? { identity: opts.identity } : {},
|
|
14818
15358
|
...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
|
|
@@ -14868,8 +15408,8 @@ function threadInit(opts, program, sab, cred, thread, main) {
|
|
|
14868
15408
|
function spawnWasmProcess(opts) {
|
|
14869
15409
|
const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
|
|
14870
15410
|
const header4 = new Int32Array(sab, 0, SAB_HEADER_I32);
|
|
14871
|
-
|
|
14872
|
-
|
|
15411
|
+
const blocked = opts.fork?.wasi?.blocked ?? opts.blocked;
|
|
15412
|
+
if (blocked && anyBlocked(blocked) && opts.program.abi === "wasi") storeBlocked(header4, blocked);
|
|
14873
15413
|
const threadSignals = /* @__PURE__ */ new Map();
|
|
14874
15414
|
const created3 = new Created();
|
|
14875
15415
|
const process2 = new WasmProcess(
|
|
@@ -14884,7 +15424,7 @@ function spawnWasmProcess(opts) {
|
|
|
14884
15424
|
...opts.program.abi === "js" ? {
|
|
14885
15425
|
exitNow: (code) => exitNow(code),
|
|
14886
15426
|
exitSignal: (sig) => queueMicrotask(() => finish(128 + sig, sig))
|
|
14887
|
-
} : {}
|
|
15427
|
+
} : { exitSignal: (sig) => terminate(128 + sig, sig) }
|
|
14888
15428
|
},
|
|
14889
15429
|
created3
|
|
14890
15430
|
)
|
|
@@ -14970,7 +15510,7 @@ function spawnWasmProcess(opts) {
|
|
|
14970
15510
|
const tsab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
|
|
14971
15511
|
const tw = opts.createWorker();
|
|
14972
15512
|
const tsignals = threadSignalsOf(tsab);
|
|
14973
|
-
if (thread.blocked)
|
|
15513
|
+
if (thread.blocked) storeBlocked(tsignals.header, thread.blocked);
|
|
14974
15514
|
threadSignals.set(thread.tid, tsignals);
|
|
14975
15515
|
const tdispatch = (req) => dying.serving(
|
|
14976
15516
|
() => isWasmSyscall(req) ? process2.syscall(req, threadCancel(tsignals)) : dispatchSyncFs(req)
|
|
@@ -14998,12 +15538,13 @@ function spawnWasmProcess(opts) {
|
|
|
14998
15538
|
processInit(opts, sab, process2.umask, copyCred(process2.cred), lane?.lane),
|
|
14999
15539
|
transferOf(opts, lane?.lane)
|
|
15000
15540
|
);
|
|
15001
|
-
const
|
|
15002
|
-
if (process2.signal(sig) !== "terminate") return;
|
|
15003
|
-
const code = sig === SIG.KILL ? 137 : 128 + sig;
|
|
15541
|
+
const terminate = (code, sig) => {
|
|
15004
15542
|
if (opts.program.abi !== "wasi" || done4) finish(code, sig);
|
|
15005
15543
|
else dying.start(code, sig, header4, process2, () => finish(code, sig));
|
|
15006
15544
|
};
|
|
15545
|
+
const signal = (sig) => {
|
|
15546
|
+
if (process2.signal(sig) === "terminate") terminate(sig === SIG.KILL ? 137 : 128 + sig, sig);
|
|
15547
|
+
};
|
|
15007
15548
|
const kill = (code = 137) => finish(code, code > 128 && code < 160 ? code - 128 : void 0);
|
|
15008
15549
|
return {
|
|
15009
15550
|
pid: opts.pid,
|
|
@@ -15014,7 +15555,7 @@ function spawnWasmProcess(opts) {
|
|
|
15014
15555
|
onState: (listener) => process2.onState(listener),
|
|
15015
15556
|
memory: () => Atomics.load(header4, SAB_I_MEMORY) * 65536,
|
|
15016
15557
|
ignoredSignals: (fork) => process2.inheritable(fork),
|
|
15017
|
-
blockedSignals: () => opts.program.abi === "wasi" ?
|
|
15558
|
+
blockedSignals: () => opts.program.abi === "wasi" ? loadBlocked(header4) : [0, 0],
|
|
15018
15559
|
signalMasks: () => process2.signalMasks(),
|
|
15019
15560
|
umask: () => process2.umask,
|
|
15020
15561
|
cred: () => copyCred(process2.cred),
|
|
@@ -18223,14 +18764,16 @@ var SUDO_COMMAND = {
|
|
|
18223
18764
|
var SUDO_SOURCE = [
|
|
18224
18765
|
"export default async function sudo(ctx) {",
|
|
18225
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 };",
|
|
18226
18769
|
" let pid;",
|
|
18227
18770
|
" let held;",
|
|
18228
18771
|
" const relay = (sig) => {",
|
|
18229
18772
|
" if (pid === undefined) held = sig;",
|
|
18230
|
-
" else return ctx.kill(pid, sig).catch(() => undefined);",
|
|
18773
|
+
" else return ctx.kill(-pid, sig).catch(() => undefined);",
|
|
18231
18774
|
" };",
|
|
18232
|
-
|
|
18233
|
-
" 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]);",
|
|
18234
18777
|
" let answer;",
|
|
18235
18778
|
" try {",
|
|
18236
18779
|
" answer = await ctx.sudo(ctx.argv.slice(1));",
|
|
@@ -18246,18 +18789,41 @@ var SUDO_SOURCE = [
|
|
|
18246
18789
|
" await say(1, answer.list);",
|
|
18247
18790
|
" return 0;",
|
|
18248
18791
|
" }",
|
|
18249
|
-
' for (const sig of ["SIGINT", "SIGQUIT"]) await ctx.signals.on(sig, () => undefined);',
|
|
18250
18792
|
" if (answer.run !== undefined) {",
|
|
18251
18793
|
" try {",
|
|
18252
|
-
" pid = (await ctx.spawn({ argv: answer.run })).pid;",
|
|
18794
|
+
" pid = (await ctx.spawn({ argv: answer.run, pgid: 0 })).pid;",
|
|
18253
18795
|
" } catch (err) {",
|
|
18254
18796
|
' const why = err && err.code === "ENOENT" ? "command not found" : String(err && err.code);',
|
|
18255
18797
|
' await say(2, "sudo: " + answer.run[0] + ": " + why + "\\n");',
|
|
18256
18798
|
" return failed();",
|
|
18257
18799
|
" }",
|
|
18258
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();",
|
|
18259
18812
|
" if (held !== undefined) await relay(held);",
|
|
18260
|
-
"
|
|
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
|
+
" }",
|
|
18261
18827
|
"}",
|
|
18262
18828
|
""
|
|
18263
18829
|
].join("\n");
|
|
@@ -18273,6 +18839,7 @@ var WASM_MAGIC = [0, 97, 115, 109];
|
|
|
18273
18839
|
var SHEBANG_MAX = 256;
|
|
18274
18840
|
var MAX_INTERPRETERS = 4;
|
|
18275
18841
|
var NOT_FOUND = 127;
|
|
18842
|
+
var NOT_EXECUTABLE = 126;
|
|
18276
18843
|
var DECIDE_MS = 100;
|
|
18277
18844
|
var INIT_PID = 1;
|
|
18278
18845
|
var SHARED_DIRS = ["/tmp", "/home"];
|
|
@@ -18422,6 +18989,9 @@ function addTimes(into, pid, t) {
|
|
|
18422
18989
|
var Launcher = class {
|
|
18423
18990
|
fs;
|
|
18424
18991
|
processFs;
|
|
18992
|
+
noLinksFs;
|
|
18993
|
+
mounted;
|
|
18994
|
+
protection;
|
|
18425
18995
|
prefixes;
|
|
18426
18996
|
writerTags;
|
|
18427
18997
|
unguardedRoot;
|
|
@@ -18477,6 +19047,9 @@ var Launcher = class {
|
|
|
18477
19047
|
hostname;
|
|
18478
19048
|
watchers = new FsWatchers();
|
|
18479
19049
|
pnpmHome;
|
|
19050
|
+
pnpmChain;
|
|
19051
|
+
pnpmMade;
|
|
19052
|
+
pnpmEpoch = 0;
|
|
18480
19053
|
mounts;
|
|
18481
19054
|
createDriverWorker;
|
|
18482
19055
|
media;
|
|
@@ -18527,11 +19100,14 @@ var Launcher = class {
|
|
|
18527
19100
|
this.mounts = new MountTable({
|
|
18528
19101
|
open: (type, spec) => this.driver(type, spec),
|
|
18529
19102
|
busy: (target) => [...this.openFiles].some((nodes) => nodes.holds(target)) || heldUnder(this.held, target),
|
|
18530
|
-
changed:
|
|
19103
|
+
changed: (...paths) => {
|
|
19104
|
+
if (paths.some((path) => this.pnpmChain.has(path))) this.pnpmStale();
|
|
19105
|
+
this.watchers.changed(...paths);
|
|
19106
|
+
},
|
|
18531
19107
|
owners: this.owners
|
|
18532
19108
|
});
|
|
18533
|
-
|
|
18534
|
-
this.base = this.
|
|
19109
|
+
this.mounted = this.mounts.wrap(options.fs);
|
|
19110
|
+
this.base = this.rooted(this.mounted);
|
|
18535
19111
|
this.createWorker = options.createWorker;
|
|
18536
19112
|
this.createDriverWorker = options.createDriverWorker;
|
|
18537
19113
|
this.media = options.media ?? memoryMedia();
|
|
@@ -18540,6 +19116,7 @@ var Launcher = class {
|
|
|
18540
19116
|
this.fstabRetries = options.fstabRetries ?? FSTAB_RETRIES;
|
|
18541
19117
|
this.processMounts = options.processMounts ?? true;
|
|
18542
19118
|
this.pnpmHome = options.env?.PNPM_HOME ?? SYSTEM_PNPM_HOME;
|
|
19119
|
+
this.pnpmChain = new Set(chain(normalizePath(this.pnpmHome)));
|
|
18543
19120
|
this.modulesDir = options.modules ?? "/node_modules";
|
|
18544
19121
|
this.appended = appendedRoots(options.commands);
|
|
18545
19122
|
this.cdp = new CdpHosts(options.cdp);
|
|
@@ -18568,12 +19145,7 @@ var Launcher = class {
|
|
|
18568
19145
|
});
|
|
18569
19146
|
enableCdp(this.net, this.cdp);
|
|
18570
19147
|
this.facades.enable(this.net);
|
|
18571
|
-
const fs =
|
|
18572
|
-
this.base,
|
|
18573
|
-
async () => new Set((await this.commands()).keys()),
|
|
18574
|
-
(dir) => this.userCommandDir(dir),
|
|
18575
|
-
this.owners
|
|
18576
|
-
);
|
|
19148
|
+
const fs = this.commandFs(this.base);
|
|
18577
19149
|
this.nodes = new VfsNodes(fs, (path) => this.released(path));
|
|
18578
19150
|
this.nodes.keepsOnDisk = true;
|
|
18579
19151
|
this.openFiles.add(this.nodes);
|
|
@@ -18589,15 +19161,23 @@ var Launcher = class {
|
|
|
18589
19161
|
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
18590
19162
|
}
|
|
18591
19163
|
);
|
|
18592
|
-
this.fs = this.
|
|
19164
|
+
this.fs = this.processChain(fs, this.nodes, this.unlinked);
|
|
18593
19165
|
this.writerTags = writerTags(options.protect);
|
|
18594
19166
|
this.unlinkOwnerOption = options.unlinkOwner;
|
|
18595
19167
|
if (this.writerTags.size > 0 && options.fs.protect && !options.fs.unguarded) {
|
|
18596
19168
|
throw new TypeError("protect.writers needs a root file system with an unguarded view");
|
|
18597
19169
|
}
|
|
19170
|
+
if (!options.fs.noLinks) throw new TypeError("the root file system needs a noLinks view");
|
|
18598
19171
|
this.unguardedRoot = options.fs.unguarded?.() ?? options.fs;
|
|
18599
19172
|
this.prefixes = protectedPrefixes(options.protect?.paths ?? []);
|
|
18600
|
-
this.
|
|
19173
|
+
this.protection = this.prefixes.length > 0 ? new ProtectedFs(this.prefixes) : void 0;
|
|
19174
|
+
this.processFs = this.protection?.view(this.fs) ?? this.fs;
|
|
19175
|
+
const noLinks = this.processChain(
|
|
19176
|
+
this.commandFs(this.rooted(this.noLinksOf(this.mounted))),
|
|
19177
|
+
this.nodes,
|
|
19178
|
+
this.unlinked
|
|
19179
|
+
);
|
|
19180
|
+
this.noLinksFs = this.protection?.view(noLinks) ?? noLinks;
|
|
18601
19181
|
const integrity = options.protect?.integrity;
|
|
18602
19182
|
integrityOptions(integrity);
|
|
18603
19183
|
options.fs.protect?.(this.prefixes, Boolean(integrity));
|
|
@@ -19009,10 +19589,15 @@ var Launcher = class {
|
|
|
19009
19589
|
}
|
|
19010
19590
|
async unrunnable(file, cwd, scope) {
|
|
19011
19591
|
const path = this.fs.resolvePath(cwd, file);
|
|
19592
|
+
if (await this.directory(file, path)) return "EACCES";
|
|
19012
19593
|
const head = await this.base.readFileBuffer(path).catch(() => null);
|
|
19013
19594
|
if (!head || head[0] === 35 && head[1] === 33) return "ENOENT";
|
|
19014
19595
|
return binfmtOf(await this.interpreters(scope), path) ? "ENOENT" : "ENOEXEC";
|
|
19015
19596
|
}
|
|
19597
|
+
async directory(file, path) {
|
|
19598
|
+
if (!file.includes("/") || COMMAND2.test(file)) return false;
|
|
19599
|
+
return (await this.base.stat(path).catch(() => void 0))?.isDirectory === true;
|
|
19600
|
+
}
|
|
19016
19601
|
verifiedFs(fs) {
|
|
19017
19602
|
const read = (path) => this.verified(path, KERNEL_CALLER, fs);
|
|
19018
19603
|
return Object.assign(Object.create(fs), {
|
|
@@ -19138,6 +19723,7 @@ var Launcher = class {
|
|
|
19138
19723
|
delete env[TICKET_ENV];
|
|
19139
19724
|
env.HOSTNAME = this.hostname;
|
|
19140
19725
|
let program;
|
|
19726
|
+
await this.pnpmReady();
|
|
19141
19727
|
try {
|
|
19142
19728
|
const caller = { pid: req.ppid ?? 0, uid: req.cred.euid, tag: req.tag };
|
|
19143
19729
|
if (this.integrity) await this.checkedSources(planned, caller);
|
|
@@ -19152,10 +19738,29 @@ var Launcher = class {
|
|
|
19152
19738
|
return this.jobs.sessionOf(pgid);
|
|
19153
19739
|
}
|
|
19154
19740
|
loopbackFor(pid) {
|
|
19155
|
-
return new KernelLoopback(
|
|
19156
|
-
|
|
19157
|
-
|
|
19741
|
+
return new KernelLoopback(
|
|
19742
|
+
this.net,
|
|
19743
|
+
() => {
|
|
19744
|
+
const cred = this.credOf(pid);
|
|
19745
|
+
return { pid, uid: cred.euid, gid: cred.egid };
|
|
19746
|
+
},
|
|
19747
|
+
() => this.connectorStamp(pid)
|
|
19748
|
+
);
|
|
19749
|
+
}
|
|
19750
|
+
sourceCheck(pid, writer) {
|
|
19751
|
+
const { integrity } = this;
|
|
19752
|
+
if (!integrity?.readsChecked || writer) return {};
|
|
19753
|
+
const caller = () => ({
|
|
19754
|
+
pid,
|
|
19755
|
+
uid: this.credOf(pid).euid,
|
|
19756
|
+
tag: this.tagOf(pid)
|
|
19158
19757
|
});
|
|
19758
|
+
return {
|
|
19759
|
+
source: {
|
|
19760
|
+
read: (path, plain) => integrity.readSource(path, caller(), plain),
|
|
19761
|
+
open: (path, real) => integrity.openSource(path, caller(), real)
|
|
19762
|
+
}
|
|
19763
|
+
};
|
|
19159
19764
|
}
|
|
19160
19765
|
treeScrubs(req) {
|
|
19161
19766
|
const inherited2 = req.ppid !== void 0 ? this.scrubs.get(req.ppid) : void 0;
|
|
@@ -19192,11 +19797,12 @@ var Launcher = class {
|
|
|
19192
19797
|
},
|
|
19193
19798
|
settling: () => this.settling.isSettling(pid),
|
|
19194
19799
|
...req.ignored ? { ignored: req.ignored } : {},
|
|
19195
|
-
...req.blocked ? { blocked: req.blocked } : {},
|
|
19800
|
+
...anyBlocked(req.blocked) ? { blocked: req.blocked } : {},
|
|
19196
19801
|
...req.umask !== void 0 ? { umask: req.umask } : {},
|
|
19197
19802
|
cred: req.cred,
|
|
19198
19803
|
...this.owners && req.program.abi === "wasi" ? { fresh: (path, umask) => this.freshModules.add(path, umask, pid) } : {},
|
|
19199
19804
|
...this.owners ? { owners: true } : {},
|
|
19805
|
+
...this.sourceCheck(pid, stack.writer),
|
|
19200
19806
|
...linksOf2(req),
|
|
19201
19807
|
sealed: this.sealed,
|
|
19202
19808
|
...scrubs ? { outputFilter: (file) => scrubbedAll(file, scrubs) } : {},
|
|
@@ -19209,6 +19815,7 @@ var Launcher = class {
|
|
|
19209
19815
|
cwd: req.cwd,
|
|
19210
19816
|
fds: req.fds,
|
|
19211
19817
|
fs: stack.fs,
|
|
19818
|
+
noLinks: stack.noLinks,
|
|
19212
19819
|
...this.prefixes.length > 0 && !stack.writer ? { readonlyAt: (path) => isProtected(this.prefixes, path) } : {},
|
|
19213
19820
|
createWorker: this.createWorker,
|
|
19214
19821
|
onError: req.report,
|
|
@@ -19286,6 +19893,11 @@ var Launcher = class {
|
|
|
19286
19893
|
return { shownPid: this.jobs.shown(req.ppid), ...ppid !== void 0 ? { ppid } : {} };
|
|
19287
19894
|
}
|
|
19288
19895
|
forget(pid) {
|
|
19896
|
+
const image = this.jobs.list().find((m) => m.pid === pid)?.execParent;
|
|
19897
|
+
const described2 = this.described.get(pid);
|
|
19898
|
+
if (image !== void 0 && described2 && this.described.has(image)) {
|
|
19899
|
+
this.described.set(image, described2);
|
|
19900
|
+
}
|
|
19289
19901
|
this.jobs.remove(pid);
|
|
19290
19902
|
this.described.delete(pid);
|
|
19291
19903
|
const spent = this.spent.get(pid);
|
|
@@ -19401,10 +20013,11 @@ var Launcher = class {
|
|
|
19401
20013
|
const root = member && rootOf(member).pid;
|
|
19402
20014
|
return root !== void 0 && this.running(root) ? root : INIT_PID;
|
|
19403
20015
|
};
|
|
20016
|
+
const superseded = new Set([...members.values()].map((m) => m.execParent));
|
|
19404
20017
|
const listed = [];
|
|
19405
20018
|
for (const member of members.values()) {
|
|
19406
20019
|
const described2 = this.described.get(member.pid);
|
|
19407
|
-
if (member.execed || !described2) continue;
|
|
20020
|
+
if (member.execed && superseded.has(member.pid) || !described2) continue;
|
|
19408
20021
|
const cred = this.credOf(member.pid);
|
|
19409
20022
|
if (!maySee(viewer, cred)) continue;
|
|
19410
20023
|
const root = rootOf(member);
|
|
@@ -19589,7 +20202,7 @@ var Launcher = class {
|
|
|
19589
20202
|
}
|
|
19590
20203
|
signalMasksOf(pid) {
|
|
19591
20204
|
const masks = this.processes.get(pid)?.signalMasks?.();
|
|
19592
|
-
const blocked = this.blockedBy(pid);
|
|
20205
|
+
const [blocked] = this.blockedBy(pid);
|
|
19593
20206
|
const sigBlk = blocked ? { sigBlk: blocked } : {};
|
|
19594
20207
|
return masks ? { sigIgn: masks.ignored, sigCgt: masks.caught, ...sigBlk } : sigBlk;
|
|
19595
20208
|
}
|
|
@@ -19597,14 +20210,14 @@ var Launcher = class {
|
|
|
19597
20210
|
return this.settling.isSettling(pid) || (this.ignoredBy(pid) & sigbit(SIG.TTOU)) !== 0;
|
|
19598
20211
|
}
|
|
19599
20212
|
blockedBy(pid) {
|
|
19600
|
-
return this.processes.get(pid)?.blockedSignals?.() ?? 0;
|
|
20213
|
+
return this.processes.get(pid)?.blockedSignals?.() ?? [0, 0];
|
|
19601
20214
|
}
|
|
19602
20215
|
ignoredBy(pid, fork = false) {
|
|
19603
20216
|
return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
|
|
19604
20217
|
}
|
|
19605
20218
|
kill(pid, sig, sender, from) {
|
|
19606
20219
|
const session = from === void 0 ? void 0 : this.jobs.sidOf(from);
|
|
19607
|
-
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;
|
|
20220
|
+
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;
|
|
19608
20221
|
if (pid === -1) {
|
|
19609
20222
|
const sender2 = from === void 0 ? void 0 : this.imageOf(from);
|
|
19610
20223
|
return this.killAll(sig, (member) => this.imageOf(member) !== sender2, may);
|
|
@@ -19755,8 +20368,7 @@ var Launcher = class {
|
|
|
19755
20368
|
this.fs
|
|
19756
20369
|
);
|
|
19757
20370
|
void this.unlinked.sweep(entry.target);
|
|
19758
|
-
|
|
19759
|
-
await this.provisionPnpmHome();
|
|
20371
|
+
this.pnpmStale();
|
|
19760
20372
|
return entry;
|
|
19761
20373
|
}
|
|
19762
20374
|
async ownTmp() {
|
|
@@ -19804,8 +20416,18 @@ var Launcher = class {
|
|
|
19804
20416
|
if (path === "/") return path;
|
|
19805
20417
|
return joinName(await realDir(this.base, parentOf(path)), baseName(path));
|
|
19806
20418
|
}
|
|
19807
|
-
|
|
19808
|
-
|
|
20419
|
+
pnpmReady() {
|
|
20420
|
+
if (this.pnpmMade !== void 0) return this.pnpmMade;
|
|
20421
|
+
const epoch = this.pnpmEpoch;
|
|
20422
|
+
const made2 = this.base.mkdir(this.pnpmHome, { recursive: true }).then(() => this.owners ? this.stampLayout() : void 0).catch(() => {
|
|
20423
|
+
if (this.pnpmEpoch === epoch) this.pnpmMade = void 0;
|
|
20424
|
+
});
|
|
20425
|
+
this.pnpmMade = made2;
|
|
20426
|
+
return made2;
|
|
20427
|
+
}
|
|
20428
|
+
pnpmStale() {
|
|
20429
|
+
this.pnpmEpoch++;
|
|
20430
|
+
this.pnpmMade = void 0;
|
|
19809
20431
|
}
|
|
19810
20432
|
mountedBy(spec, cred) {
|
|
19811
20433
|
if (!this.owners || cred.euid === 0 && cred.egid === 0) return spec;
|
|
@@ -19839,6 +20461,7 @@ var Launcher = class {
|
|
|
19839
20461
|
umount(target, detach = false) {
|
|
19840
20462
|
const at = normalizePath(target);
|
|
19841
20463
|
this.mounts.umount(at, detach);
|
|
20464
|
+
this.pnpmStale();
|
|
19842
20465
|
if (!detach) return;
|
|
19843
20466
|
for (const nodes of this.openFiles) nodes.revoke(at);
|
|
19844
20467
|
for (const holds of this.held) holds.revoke(at);
|
|
@@ -19904,7 +20527,7 @@ var Launcher = class {
|
|
|
19904
20527
|
await this.resolvePrefixes();
|
|
19905
20528
|
for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
|
|
19906
20529
|
if (this.owners) await this.ownTmp();
|
|
19907
|
-
await this.
|
|
20530
|
+
await this.pnpmReady();
|
|
19908
20531
|
await this.users.ensure();
|
|
19909
20532
|
if (this.owners) await this.stampLayout();
|
|
19910
20533
|
if (this.owners) await this.sudoer.provision();
|
|
@@ -19967,19 +20590,20 @@ var Launcher = class {
|
|
|
19967
20590
|
}
|
|
19968
20591
|
stackFor(tag) {
|
|
19969
20592
|
if (!this.isWriter(tag)) {
|
|
19970
|
-
return {
|
|
20593
|
+
return {
|
|
20594
|
+
fs: this.processFs,
|
|
20595
|
+
noLinks: this.noLinksFs,
|
|
20596
|
+
nodes: this.nodes,
|
|
20597
|
+
unlinked: this.unlinked,
|
|
20598
|
+
writer: false
|
|
20599
|
+
};
|
|
19971
20600
|
}
|
|
19972
20601
|
this.writers ??= this.writerStack();
|
|
19973
20602
|
return this.writers;
|
|
19974
20603
|
}
|
|
19975
20604
|
writerStack() {
|
|
19976
|
-
const
|
|
19977
|
-
const fs =
|
|
19978
|
-
this.owners ? this.freshModules.wrap(watched) : watched,
|
|
19979
|
-
async () => new Set((await this.commands()).keys()),
|
|
19980
|
-
(dir) => this.userCommandDir(dir),
|
|
19981
|
-
this.owners
|
|
19982
|
-
);
|
|
20605
|
+
const mounted = this.mounts.wrap(this.unguardedRoot);
|
|
20606
|
+
const fs = this.commandFs(this.rooted(mounted));
|
|
19983
20607
|
const nodes = new VfsNodes(fs, (path) => this.released(path), this.nodes.entries);
|
|
19984
20608
|
nodes.keepsOnDisk = true;
|
|
19985
20609
|
this.openFiles.add(nodes);
|
|
@@ -19994,7 +20618,32 @@ var Launcher = class {
|
|
|
19994
20618
|
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
19995
20619
|
}
|
|
19996
20620
|
);
|
|
19997
|
-
|
|
20621
|
+
const noLinks = this.commandFs(this.rooted(this.noLinksOf(mounted)));
|
|
20622
|
+
return {
|
|
20623
|
+
fs: this.processChain(fs, nodes, unlinked),
|
|
20624
|
+
noLinks: this.processChain(noLinks, nodes, unlinked),
|
|
20625
|
+
nodes,
|
|
20626
|
+
unlinked,
|
|
20627
|
+
writer: true
|
|
20628
|
+
};
|
|
20629
|
+
}
|
|
20630
|
+
noLinksOf(mounted) {
|
|
20631
|
+
return mounted.noLinks.call(mounted);
|
|
20632
|
+
}
|
|
20633
|
+
rooted(mounted) {
|
|
20634
|
+
const watched = this.watchers.wrap(mounted);
|
|
20635
|
+
return this.owners ? this.freshModules.wrap(watched) : watched;
|
|
20636
|
+
}
|
|
20637
|
+
commandFs(root) {
|
|
20638
|
+
return withCommandDirs(
|
|
20639
|
+
root,
|
|
20640
|
+
async () => new Set((await this.commands()).keys()),
|
|
20641
|
+
(dir) => this.userCommandDir(dir),
|
|
20642
|
+
this.owners
|
|
20643
|
+
);
|
|
20644
|
+
}
|
|
20645
|
+
processChain(fs, nodes, unlinked) {
|
|
20646
|
+
return unlinked.wrap(keepingOpen(fs, nodes));
|
|
19998
20647
|
}
|
|
19999
20648
|
async starting(argv, dir, account, tag) {
|
|
20000
20649
|
this.resetCatalogs();
|
|
@@ -20076,8 +20725,10 @@ var Launcher = class {
|
|
|
20076
20725
|
const report = (message3) => stderr(encoder7.encode(`${message3}
|
|
20077
20726
|
`));
|
|
20078
20727
|
if (!planned) {
|
|
20079
|
-
|
|
20080
|
-
|
|
20728
|
+
const directory = await this.directory(file, this.fs.resolvePath(cwd, file));
|
|
20729
|
+
report(`${file}: ${directory ? "Is a directory" : "command not found"}`);
|
|
20730
|
+
const status2 = directory ? NOT_EXECUTABLE : NOT_FOUND;
|
|
20731
|
+
return { status: status2, stdout: concat6(out), stderr: concat6(err) };
|
|
20081
20732
|
}
|
|
20082
20733
|
const fds = new FdTable();
|
|
20083
20734
|
fds.installAt(0, options.stdin ? bytesSource(options.stdin) : deviceFile("null"));
|
|
@@ -20100,6 +20751,8 @@ var Launcher = class {
|
|
|
20100
20751
|
};
|
|
20101
20752
|
|
|
20102
20753
|
// src/node/loopback.ts
|
|
20754
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
20755
|
+
import { subscribe } from "node:diagnostics_channel";
|
|
20103
20756
|
import { lookup } from "node:dns/promises";
|
|
20104
20757
|
import { isIP } from "node:net";
|
|
20105
20758
|
var DEVTOOLS_PORTS = [9222, 9223, 9224, 9225, 9226, 9227, 9228, 9229];
|
|
@@ -20159,6 +20812,11 @@ function refusal(url, address, place) {
|
|
|
20159
20812
|
const why = place === "link-local" ? `nodeTransport refuses link-local ${where}` : `nodeTransport refuses the host's loopback ${where} (see nodeTransport({ hostLoopback }))`;
|
|
20160
20813
|
return Object.assign(new Error(why), { status: 403, code: "EACCES" });
|
|
20161
20814
|
}
|
|
20815
|
+
function connectRefusal(address, port, place) {
|
|
20816
|
+
const where = `${address}:${port}`;
|
|
20817
|
+
const why = place === "link-local" ? `nodeTransport refuses link-local ${where}` : `nodeTransport refuses the host's loopback ${where} (see nodeTransport({ hostLoopback }))`;
|
|
20818
|
+
return Object.assign(new Error(why), { status: 403, code: "EACCES" });
|
|
20819
|
+
}
|
|
20162
20820
|
async function addressesOf(host, resolve2) {
|
|
20163
20821
|
const plain = host.replace(/^\[(.*)\]$/, "$1");
|
|
20164
20822
|
if (isIP(plain)) return [plain];
|
|
@@ -20185,7 +20843,66 @@ function guarded2(check, signal) {
|
|
|
20185
20843
|
});
|
|
20186
20844
|
return Promise.race([check, aborted2]).finally(() => signal.removeEventListener("abort", stop));
|
|
20187
20845
|
}
|
|
20846
|
+
var loopbackStore = new AsyncLocalStorage();
|
|
20847
|
+
var subscribed = false;
|
|
20848
|
+
function envProxyEnabled() {
|
|
20849
|
+
if (process.env.NODE_USE_ENV_PROXY === "1") return true;
|
|
20850
|
+
const opts = process.env.NODE_OPTIONS ?? "";
|
|
20851
|
+
const args = process.execArgv ?? [];
|
|
20852
|
+
return args.includes("--use-env-proxy") || opts.includes("--use-env-proxy");
|
|
20853
|
+
}
|
|
20854
|
+
function normaliseProxyUrl(url) {
|
|
20855
|
+
try {
|
|
20856
|
+
const parsed2 = new URL(url);
|
|
20857
|
+
const host = parsed2.hostname.replace(/^\[(.*)\]$/, "$1");
|
|
20858
|
+
const port = parsed2.port || (parsed2.protocol === "https:" ? "443" : "80");
|
|
20859
|
+
return `${host}:${port}`;
|
|
20860
|
+
} catch {
|
|
20861
|
+
return void 0;
|
|
20862
|
+
}
|
|
20863
|
+
}
|
|
20864
|
+
function proxyAddresses() {
|
|
20865
|
+
if (!envProxyEnabled()) return /* @__PURE__ */ new Set();
|
|
20866
|
+
const out = /* @__PURE__ */ new Set();
|
|
20867
|
+
for (const key of ["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"]) {
|
|
20868
|
+
const val = process.env[key];
|
|
20869
|
+
if (val) {
|
|
20870
|
+
const addr = normaliseProxyUrl(val);
|
|
20871
|
+
if (addr) out.add(addr);
|
|
20872
|
+
}
|
|
20873
|
+
}
|
|
20874
|
+
return out;
|
|
20875
|
+
}
|
|
20876
|
+
function ensureSubscribed() {
|
|
20877
|
+
if (subscribed) return;
|
|
20878
|
+
subscribed = true;
|
|
20879
|
+
subscribe("undici:client:connected", (message3) => {
|
|
20880
|
+
const entry = loopbackStore.getStore();
|
|
20881
|
+
if (!entry) return;
|
|
20882
|
+
const { socket, connectParams } = message3;
|
|
20883
|
+
const address = socket.remoteAddress;
|
|
20884
|
+
if (!address) {
|
|
20885
|
+
const err2 = connectRefusal("unknown", 0, "loopback");
|
|
20886
|
+
entry.refused = err2;
|
|
20887
|
+
socket.destroy(err2);
|
|
20888
|
+
return;
|
|
20889
|
+
}
|
|
20890
|
+
const proxies = proxyAddresses();
|
|
20891
|
+
if (proxies.size > 0 && connectParams.hostname) {
|
|
20892
|
+
const cpPort = connectParams.port || (connectParams.protocol === "https:" ? "443" : "80");
|
|
20893
|
+
if (proxies.has(`${connectParams.hostname}:${cpPort}`)) return;
|
|
20894
|
+
}
|
|
20895
|
+
const place = placeOf(address);
|
|
20896
|
+
if (!place) return;
|
|
20897
|
+
const port = socket.remotePort ?? 0;
|
|
20898
|
+
if (place === "loopback" && allows(entry.policy, port)) return;
|
|
20899
|
+
const err = connectRefusal(address, port, place);
|
|
20900
|
+
entry.refused = err;
|
|
20901
|
+
socket.destroy(err);
|
|
20902
|
+
});
|
|
20903
|
+
}
|
|
20188
20904
|
function guardedLoopback(transport, policy, resolve2) {
|
|
20905
|
+
ensureSubscribed();
|
|
20189
20906
|
const open = transport.websocket?.bind(transport);
|
|
20190
20907
|
return {
|
|
20191
20908
|
get traits() {
|
|
@@ -20193,12 +20910,22 @@ function guardedLoopback(transport, policy, resolve2) {
|
|
|
20193
20910
|
},
|
|
20194
20911
|
fetch: async (request, owner) => {
|
|
20195
20912
|
await guarded2(checkDestination(request.url, policy, resolve2), request.signal);
|
|
20196
|
-
|
|
20913
|
+
const entry = { policy };
|
|
20914
|
+
try {
|
|
20915
|
+
return await loopbackStore.run(entry, () => transport.fetch(request, owner));
|
|
20916
|
+
} catch (err) {
|
|
20917
|
+
throw entry.refused ?? err;
|
|
20918
|
+
}
|
|
20197
20919
|
},
|
|
20198
20920
|
...open ? {
|
|
20199
20921
|
websocket: async (request) => {
|
|
20200
20922
|
await guarded2(checkDestination(request.url, policy, resolve2), request.signal);
|
|
20201
|
-
|
|
20923
|
+
const entry = { policy };
|
|
20924
|
+
try {
|
|
20925
|
+
return await loopbackStore.run(entry, () => open(request));
|
|
20926
|
+
} catch (err) {
|
|
20927
|
+
throw entry.refused ?? err;
|
|
20928
|
+
}
|
|
20202
20929
|
}
|
|
20203
20930
|
} : {}
|
|
20204
20931
|
};
|
|
@@ -20250,8 +20977,8 @@ var Chunks = class {
|
|
|
20250
20977
|
const index = Math.floor((at + done4) / CHUNK4);
|
|
20251
20978
|
const from = at + done4 - index * CHUNK4;
|
|
20252
20979
|
const n = Math.min(CHUNK4 - from, bytes.length - done4);
|
|
20253
|
-
const
|
|
20254
|
-
const part =
|
|
20980
|
+
const view2 = bytes.subarray(done4, done4 + n);
|
|
20981
|
+
const part = view2.buffer instanceof ArrayBuffer ? view2 : view2.slice();
|
|
20255
20982
|
const old = this.blobs[index];
|
|
20256
20983
|
if (from === 0 && n >= (old?.size ?? 0)) this.blobs[index] = new Blob([part]);
|
|
20257
20984
|
else {
|