@ai-ecoverse/slicc-kernel 1.61.1 → 1.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -6
- package/dist/index.js +15 -12
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +616 -260
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +40 -5
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +714 -358
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +40 -5
- package/dist/process-worker.js.map +2 -2
- package/package.json +2 -2
package/dist/node.js
CHANGED
|
@@ -105,13 +105,13 @@ function fsError(code, path) {
|
|
|
105
105
|
return Object.assign(new Error(`${code}: ${path}`), { code });
|
|
106
106
|
}
|
|
107
107
|
function normalizePath(path) {
|
|
108
|
-
const
|
|
108
|
+
const parts2 = [];
|
|
109
109
|
for (const part of path.split("/")) {
|
|
110
110
|
if (part === "" || part === ".") continue;
|
|
111
|
-
if (part === "..")
|
|
112
|
-
else
|
|
111
|
+
if (part === "..") parts2.pop();
|
|
112
|
+
else parts2.push(part);
|
|
113
113
|
}
|
|
114
|
-
return `/${
|
|
114
|
+
return `/${parts2.join("/")}`;
|
|
115
115
|
}
|
|
116
116
|
function resolvePath(base, path) {
|
|
117
117
|
return normalizePath(path.startsWith("/") ? path : `${base}/${path}`);
|
|
@@ -129,6 +129,355 @@ function inodeOf(path) {
|
|
|
129
129
|
return 4294967296 * (2097151 & h2) + (h1 >>> 0) || 1;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
// src/fs/owners.ts
|
|
133
|
+
var ROOT_OWNER = Object.freeze({ uid: 0, gid: 0 });
|
|
134
|
+
var S_ISUID = 2048;
|
|
135
|
+
var S_ISGID = 1024;
|
|
136
|
+
var S_IXGRP = 8;
|
|
137
|
+
function ownOf(entry) {
|
|
138
|
+
if (entry?.uid === void 0 || entry.gid === void 0) return void 0;
|
|
139
|
+
return { uid: entry.uid, gid: entry.gid };
|
|
140
|
+
}
|
|
141
|
+
function handedBy(entry) {
|
|
142
|
+
return entry?.heir ? { uid: entry.heir[0], gid: entry.heir[1] } : ownOf(entry);
|
|
143
|
+
}
|
|
144
|
+
function inherit(entries) {
|
|
145
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
146
|
+
const found = handedBy(entries[i]);
|
|
147
|
+
if (found) return found;
|
|
148
|
+
}
|
|
149
|
+
return { ...ROOT_OWNER };
|
|
150
|
+
}
|
|
151
|
+
function chain(path) {
|
|
152
|
+
const parts2 = path.split("/").filter(Boolean);
|
|
153
|
+
return ["/", ...parts2.map((_, i) => `/${parts2.slice(0, i + 1).join("/")}`)];
|
|
154
|
+
}
|
|
155
|
+
function sameOwner(a, b) {
|
|
156
|
+
return a.uid === b.uid && a.gid === b.gid;
|
|
157
|
+
}
|
|
158
|
+
function ownerOf(cred, mode) {
|
|
159
|
+
return { uid: cred.euid, gid: cred.egid, ...mode !== void 0 ? { mode } : {} };
|
|
160
|
+
}
|
|
161
|
+
function inGroup(cred, gid) {
|
|
162
|
+
return cred.egid === gid || cred.groups.includes(gid);
|
|
163
|
+
}
|
|
164
|
+
function mayChown(cred, current, uid, gid) {
|
|
165
|
+
if (cred.euid === 0) return true;
|
|
166
|
+
const owner = cred.euid === current.uid;
|
|
167
|
+
if (uid !== void 0 && !(owner && uid === current.uid)) return false;
|
|
168
|
+
if (gid !== void 0 && !(owner && (gid === current.gid || inGroup(cred, gid)))) return false;
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
function clearedSetId(mode) {
|
|
172
|
+
const bits2 = S_ISUID | (mode & S_IXGRP ? S_ISGID : 0);
|
|
173
|
+
return mode & 4095 & ~bits2;
|
|
174
|
+
}
|
|
175
|
+
async function chownAs(fs, cred, path, change) {
|
|
176
|
+
const { uid, gid, follow: follow2 } = change;
|
|
177
|
+
const st = follow2 ? await fs.stat(path) : await fs.lstat(path);
|
|
178
|
+
const current = { uid: st.uid ?? 0, gid: st.gid ?? 0 };
|
|
179
|
+
if (!fs.chown || !mayChown(cred, current, uid, gid)) throw fsError("EPERM", path);
|
|
180
|
+
const clear = st.isFile && (uid !== void 0 || gid !== void 0) ? clearedSetId(st.mode) : void 0;
|
|
181
|
+
await fs.chown(path, {
|
|
182
|
+
...uid !== void 0 ? { uid } : {},
|
|
183
|
+
...gid !== void 0 ? { gid } : {},
|
|
184
|
+
follow: follow2,
|
|
185
|
+
...clear !== void 0 && clear !== (st.mode & 4095) ? { mode: clear } : {}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/fs/walk.ts
|
|
190
|
+
var MAX_LINKS = 40;
|
|
191
|
+
function parentOf(path) {
|
|
192
|
+
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
193
|
+
}
|
|
194
|
+
async function mustBeDir(fs, path) {
|
|
195
|
+
if (!fs.lstat) return;
|
|
196
|
+
const st = await fs.lstat(path);
|
|
197
|
+
if (st.isDirectory !== true) throw fsError("ENOTDIR", path);
|
|
198
|
+
}
|
|
199
|
+
async function linkOf(fs, path) {
|
|
200
|
+
return fs.readlink?.(path).catch(() => void 0);
|
|
201
|
+
}
|
|
202
|
+
async function realDir(fs, dir, budget = { hops: 0 }) {
|
|
203
|
+
const queue = dir.split("/").filter(Boolean);
|
|
204
|
+
let at = "/";
|
|
205
|
+
while (queue.length > 0) {
|
|
206
|
+
const part = queue.shift();
|
|
207
|
+
if (part === ".") continue;
|
|
208
|
+
if (part === "..") {
|
|
209
|
+
at = parentOf(at);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
const next2 = at === "/" ? `/${part}` : `${at}/${part}`;
|
|
213
|
+
const link2 = await linkOf(fs, next2);
|
|
214
|
+
if (link2 === void 0) {
|
|
215
|
+
if (queue.find((p) => p !== ".") === "..") await mustBeDir(fs, next2);
|
|
216
|
+
at = next2;
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (budget.hops >= MAX_LINKS) throw fsError("ELOOP", dir);
|
|
220
|
+
budget.hops++;
|
|
221
|
+
queue.unshift(...link2.split("/").filter(Boolean));
|
|
222
|
+
if (link2.startsWith("/")) at = "/";
|
|
223
|
+
}
|
|
224
|
+
return at;
|
|
225
|
+
}
|
|
226
|
+
function joinName(dir, name2) {
|
|
227
|
+
return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
|
|
228
|
+
}
|
|
229
|
+
async function realTarget(fs, path) {
|
|
230
|
+
const budget = { hops: 0 };
|
|
231
|
+
let dir = await realDir(fs, parentOf(path), budget);
|
|
232
|
+
let name2 = path.slice(path.lastIndexOf("/") + 1);
|
|
233
|
+
for (; ; ) {
|
|
234
|
+
const link2 = await linkOf(fs, joinName(dir, name2));
|
|
235
|
+
if (link2 === void 0) break;
|
|
236
|
+
if (budget.hops >= MAX_LINKS) throw fsError("ELOOP", path);
|
|
237
|
+
budget.hops++;
|
|
238
|
+
const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
|
|
239
|
+
dir = await realDir(fs, parentOf(target), budget);
|
|
240
|
+
name2 = target.slice(target.lastIndexOf("/") + 1);
|
|
241
|
+
}
|
|
242
|
+
return joinName(dir, name2);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// src/fs/access.ts
|
|
246
|
+
var R_OK = 4;
|
|
247
|
+
var W_OK = 2;
|
|
248
|
+
var X_OK = 1;
|
|
249
|
+
var MAX_LINKS2 = 40;
|
|
250
|
+
var S_ISVTX = 512;
|
|
251
|
+
function permOf(st, link2) {
|
|
252
|
+
return {
|
|
253
|
+
uid: st.uid ?? 0,
|
|
254
|
+
gid: st.gid ?? 0,
|
|
255
|
+
mode: st.mode & 4095,
|
|
256
|
+
...link2 !== void 0 ? { link: link2 } : {}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function permits(cred, perm, want, directory = true) {
|
|
260
|
+
const mode = perm.mode ?? (directory ? 493 : 420);
|
|
261
|
+
if (cred.euid === 0) return (want & X_OK) === 0 || directory || (mode & 73) !== 0;
|
|
262
|
+
const shift = cred.euid === perm.uid ? 6 : inGroup(cred, perm.gid) ? 3 : 0;
|
|
263
|
+
return (mode >> shift & want) === want;
|
|
264
|
+
}
|
|
265
|
+
function sticky(cred, dir, entry) {
|
|
266
|
+
if (cred.euid === 0 || ((dir.mode ?? 0) & S_ISVTX) === 0) return true;
|
|
267
|
+
return cred.euid === dir.uid || cred.euid === (entry ?? dir).uid;
|
|
268
|
+
}
|
|
269
|
+
async function ancestryOf(fs, path) {
|
|
270
|
+
if (fs.ancestry) return fs.ancestry(path);
|
|
271
|
+
const out = [];
|
|
272
|
+
for (const at of chain(path)) {
|
|
273
|
+
const st = await fs.lstat(at).catch(() => void 0);
|
|
274
|
+
if (!st) break;
|
|
275
|
+
out.push(permOf(st, st.isSymbolicLink ? await fs.readlink(at) : void 0));
|
|
276
|
+
}
|
|
277
|
+
return out;
|
|
278
|
+
}
|
|
279
|
+
var Perms = class {
|
|
280
|
+
known = /* @__PURE__ */ new Map();
|
|
281
|
+
fs;
|
|
282
|
+
constructor(fs) {
|
|
283
|
+
this.fs = fs;
|
|
284
|
+
}
|
|
285
|
+
async learn(path) {
|
|
286
|
+
const names = chain(path);
|
|
287
|
+
const perms = await ancestryOf(this.fs, path);
|
|
288
|
+
for (let i = 0; i < names.length; i++) {
|
|
289
|
+
const perm = perms[i];
|
|
290
|
+
this.known.set(names[i], perm);
|
|
291
|
+
if (perm === void 0 || perm.link !== void 0) break;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
async at(path) {
|
|
295
|
+
if (!this.known.has(path)) await this.learn(path);
|
|
296
|
+
return this.known.get(path);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
function parts(path) {
|
|
300
|
+
return path.split("/").filter((part) => part !== "" && part !== ".");
|
|
301
|
+
}
|
|
302
|
+
async function walk(fs, cred, path, follow2) {
|
|
303
|
+
const perms = new Perms(fs);
|
|
304
|
+
await perms.learn(path);
|
|
305
|
+
const queue = parts(path);
|
|
306
|
+
let at = "/";
|
|
307
|
+
for (let hops = 0; queue.length > 0; ) {
|
|
308
|
+
const part = queue.shift();
|
|
309
|
+
const dir = await perms.at(at);
|
|
310
|
+
if (!dir) return { path: joinName(at, [part, ...queue].join("/")), perm: void 0 };
|
|
311
|
+
if (!permits(cred, dir, X_OK)) return { path, perm: void 0, denied: at };
|
|
312
|
+
if (part === "..") {
|
|
313
|
+
at = parentOf(at);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
const next2 = joinName(at, part);
|
|
317
|
+
const perm = await perms.at(next2);
|
|
318
|
+
const last = queue.length === 0;
|
|
319
|
+
if (perm?.link !== void 0 && (!last || follow2)) {
|
|
320
|
+
if (++hops > MAX_LINKS2) return { path: next2, perm: void 0 };
|
|
321
|
+
queue.unshift(...parts(perm.link));
|
|
322
|
+
if (perm.link.startsWith("/")) at = "/";
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (last) return { path: next2, perm };
|
|
326
|
+
at = next2;
|
|
327
|
+
}
|
|
328
|
+
return { path: at, perm: await perms.at(at) };
|
|
329
|
+
}
|
|
330
|
+
async function present(fs, path, directory) {
|
|
331
|
+
const st = await fs.lstat(path).catch(() => void 0);
|
|
332
|
+
return st !== void 0 && (!directory || st.isDirectory);
|
|
333
|
+
}
|
|
334
|
+
async function access(fs, cred, path, want, options = {}) {
|
|
335
|
+
const walked = await walk(fs, cred, path, options.follow !== false);
|
|
336
|
+
if (walked.denied !== void 0) {
|
|
337
|
+
if (await present(fs, walked.denied, true)) return walked;
|
|
338
|
+
return { path, perm: void 0, missing: true };
|
|
339
|
+
}
|
|
340
|
+
const { perm } = walked;
|
|
341
|
+
if (!perm) return { ...walked, missing: true };
|
|
342
|
+
if (want === 0 || permits(cred, perm, want, options.directory !== false)) return walked;
|
|
343
|
+
if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
|
|
344
|
+
return { ...walked, missing: true };
|
|
345
|
+
}
|
|
346
|
+
function refused(path, errno = "EACCES") {
|
|
347
|
+
return fsError(errno, path);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// src/fs/dac.ts
|
|
351
|
+
var MAX_DENIAL_KEYS = 1e4;
|
|
352
|
+
var S_ISGID2 = 1024;
|
|
353
|
+
var Dac = class {
|
|
354
|
+
mode;
|
|
355
|
+
hook;
|
|
356
|
+
log;
|
|
357
|
+
seen = /* @__PURE__ */ new Set();
|
|
358
|
+
suppressed = false;
|
|
359
|
+
constructor(mode, hook, log = (line) => console.warn(line)) {
|
|
360
|
+
this.mode = mode;
|
|
361
|
+
this.hook = hook;
|
|
362
|
+
this.log = log;
|
|
363
|
+
}
|
|
364
|
+
get enforcing() {
|
|
365
|
+
return this.mode === "enforce";
|
|
366
|
+
}
|
|
367
|
+
denied(denial) {
|
|
368
|
+
try {
|
|
369
|
+
this.hook?.({ ...denial });
|
|
370
|
+
} catch {
|
|
371
|
+
}
|
|
372
|
+
if (this.mode === "report") this.note(denial);
|
|
373
|
+
else throw refused(denial.path, denial.errno);
|
|
374
|
+
}
|
|
375
|
+
note(d) {
|
|
376
|
+
const key = `${d.uid}\0${d.op}\0${d.errno}\0${d.path}`;
|
|
377
|
+
if (this.seen.has(key)) return;
|
|
378
|
+
if (this.seen.size >= MAX_DENIAL_KEYS) {
|
|
379
|
+
if (!this.suppressed) this.log("slicc-kernel users report: more denials suppressed");
|
|
380
|
+
this.suppressed = true;
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
this.seen.add(key);
|
|
384
|
+
this.log(
|
|
385
|
+
`slicc-kernel users report: ${d.errno} ${d.op} ${d.path} (uid ${d.uid}, pid ${d.pid})`
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
function deny(g, op, path, errno = "EACCES") {
|
|
390
|
+
g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno });
|
|
391
|
+
}
|
|
392
|
+
async function need(g, op, path, want, options = {}, shown = path) {
|
|
393
|
+
const walked = await access(g.fs, g.cred, path, want, options);
|
|
394
|
+
if (walked.denied !== void 0) deny(g, op, shown);
|
|
395
|
+
return walked;
|
|
396
|
+
}
|
|
397
|
+
async function stickyEntry(g, op, path, dir) {
|
|
398
|
+
const perm = dir.perm;
|
|
399
|
+
if (!perm || sticky(g.cred, perm, perm)) return;
|
|
400
|
+
const entry = joinName(dir.path, path.slice(path.lastIndexOf("/") + 1));
|
|
401
|
+
if (sticky(g.cred, perm, (await ancestryOf(g.fs, entry)).at(-1))) return;
|
|
402
|
+
if (await present(g.fs, entry, false)) deny(g, op, path, "EPERM");
|
|
403
|
+
}
|
|
404
|
+
async function inParent(g, op, path, old = false) {
|
|
405
|
+
const dir = await need(g, op, parentOf(path), W_OK | X_OK, {}, path);
|
|
406
|
+
if (old && dir.denied === void 0) await stickyEntry(g, op, path, dir);
|
|
407
|
+
}
|
|
408
|
+
async function makeIn(g, op, path) {
|
|
409
|
+
for (let dir = parentOf(path); ; dir = parentOf(dir)) {
|
|
410
|
+
const walked = await access(g.fs, g.cred, dir, W_OK | X_OK);
|
|
411
|
+
if (walked.denied !== void 0) {
|
|
412
|
+
if (!await present(g.fs, path, true)) deny(g, op, path);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
if (!walked.missing || dir === "/") return;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
async function tree(g, op, dir, perm) {
|
|
419
|
+
if (!permits(g.cred, perm, R_OK | W_OK | X_OK)) return deny(g, op, dir);
|
|
420
|
+
for (const [name2, st] of await g.fs.readdirStat(dir)) {
|
|
421
|
+
if (!st) continue;
|
|
422
|
+
const child = joinName(dir, name2);
|
|
423
|
+
if (!sticky(g.cred, perm, permOf(st))) deny(g, op, child, "EPERM");
|
|
424
|
+
if (st.isDirectory) await tree(g, op, child, permOf(st));
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
async function removable(g, op, path) {
|
|
428
|
+
await inParent(g, op, path, true);
|
|
429
|
+
if (g.cred.euid === 0) return;
|
|
430
|
+
const st = await g.fs.lstat(path).catch(() => void 0);
|
|
431
|
+
if (st?.isDirectory) await tree(g, op, path, permOf(st));
|
|
432
|
+
}
|
|
433
|
+
async function renamable(g, from, to) {
|
|
434
|
+
if (from === to) return void await need(g, "rename", from, 0, { follow: false });
|
|
435
|
+
await inParent(g, "rename", from, true);
|
|
436
|
+
await inParent(g, "rename", to, true);
|
|
437
|
+
if (g.cred.euid === 0 || parentOf(from) === parentOf(to)) return;
|
|
438
|
+
const st = await g.fs.lstat(from).catch(() => void 0);
|
|
439
|
+
if (st?.isDirectory && !permits(g.cred, permOf(st), W_OK)) deny(g, "rename", from);
|
|
440
|
+
}
|
|
441
|
+
async function linkable(g, from, to, follow2) {
|
|
442
|
+
const source = await need(g, "link", from, 0, { follow: follow2 });
|
|
443
|
+
const perm = source.perm;
|
|
444
|
+
if (perm && g.cred.euid !== 0 && perm.uid !== g.cred.euid) {
|
|
445
|
+
const safe = permits(g.cred, perm, R_OK | W_OK, false);
|
|
446
|
+
if (!safe && await present(g.fs, source.path, false)) deny(g, "link", from, "EPERM");
|
|
447
|
+
}
|
|
448
|
+
await inParent(g, "link", to);
|
|
449
|
+
}
|
|
450
|
+
async function ownerOnly(g, op, path, follow2) {
|
|
451
|
+
const walked = await need(g, op, path, 0, { follow: follow2 });
|
|
452
|
+
const perm = walked.perm;
|
|
453
|
+
if (!perm || g.cred.euid === 0 || perm.uid === g.cred.euid) return walked;
|
|
454
|
+
if (await present(g.fs, walked.path, false)) deny(g, op, path, "EPERM");
|
|
455
|
+
return walked;
|
|
456
|
+
}
|
|
457
|
+
async function chmodMode(g, path, mode) {
|
|
458
|
+
const perm = (await ownerOnly(g, "chmod", path, true)).perm;
|
|
459
|
+
if (!g.dac.enforcing || !perm || g.cred.euid === 0 || inGroup(g.cred, perm.gid)) return mode;
|
|
460
|
+
return mode & ~S_ISGID2;
|
|
461
|
+
}
|
|
462
|
+
async function retimable(g, op, path) {
|
|
463
|
+
await ownerOnly(g, op, path, op !== "lutimes");
|
|
464
|
+
}
|
|
465
|
+
async function listable(g, op, path) {
|
|
466
|
+
await need(g, op, path, R_OK | X_OK, { directory: true });
|
|
467
|
+
}
|
|
468
|
+
async function searchable(g, op, path, follow2) {
|
|
469
|
+
await need(g, op, path, 0, { follow: follow2 });
|
|
470
|
+
}
|
|
471
|
+
async function readable(g, op, path) {
|
|
472
|
+
await need(g, op, path, R_OK, { directory: false });
|
|
473
|
+
}
|
|
474
|
+
async function writable(g, op, path) {
|
|
475
|
+
const st = await g.fs.stat(path).catch(() => void 0);
|
|
476
|
+
if (st) return void await need(g, op, path, W_OK, { directory: st.isDirectory });
|
|
477
|
+
const target = await need(g, op, path, 0);
|
|
478
|
+
if (target.denied === void 0) await need(g, op, parentOf(target.path), W_OK | X_OK, {}, path);
|
|
479
|
+
}
|
|
480
|
+
|
|
132
481
|
// src/kernel/pipe.ts
|
|
133
482
|
var PIPE_CAPACITY = 64 * 1024;
|
|
134
483
|
var PipeError = class extends Error {
|
|
@@ -372,7 +721,7 @@ function written(device, bytes) {
|
|
|
372
721
|
if (device === "full" && bytes.length > 0) throw new KernelError("ENOSPC");
|
|
373
722
|
return bytes.length;
|
|
374
723
|
}
|
|
375
|
-
function deviceFile(device,
|
|
724
|
+
function deviceFile(device, access2) {
|
|
376
725
|
const read = async (max) => {
|
|
377
726
|
if (device === "null") return new Uint8Array(0);
|
|
378
727
|
const out = new Uint8Array(max);
|
|
@@ -384,10 +733,10 @@ function deviceFile(device, access) {
|
|
|
384
733
|
return out;
|
|
385
734
|
};
|
|
386
735
|
return new OpenFile({
|
|
387
|
-
...
|
|
388
|
-
...
|
|
736
|
+
...access2 !== "write" ? { read } : {},
|
|
737
|
+
...access2 !== "read" ? { write: async (bytes) => written(device, bytes) } : {},
|
|
389
738
|
seek: async () => 0,
|
|
390
|
-
heldMeta: { device, ...
|
|
739
|
+
heldMeta: { device, ...access2 ? { access: access2 } : {} },
|
|
391
740
|
close: () => {
|
|
392
741
|
}
|
|
393
742
|
});
|
|
@@ -1349,8 +1698,8 @@ function openWebSocket(Socket, request, withHeaders) {
|
|
|
1349
1698
|
reject(error);
|
|
1350
1699
|
};
|
|
1351
1700
|
const abort = () => {
|
|
1352
|
-
socket.close(1e3, "aborted");
|
|
1353
1701
|
fail3(signal.reason);
|
|
1702
|
+
socket.close(1e3, "aborted");
|
|
1354
1703
|
};
|
|
1355
1704
|
signal.addEventListener("abort", abort, { once: true });
|
|
1356
1705
|
socket.addEventListener("open", () => {
|
|
@@ -1374,12 +1723,12 @@ function openWebSocket(Socket, request, withHeaders) {
|
|
|
1374
1723
|
});
|
|
1375
1724
|
socket.addEventListener("error", () => {
|
|
1376
1725
|
if (open || settled) return;
|
|
1726
|
+
fail3(unopened(url, 1006));
|
|
1727
|
+
queue.end({ code: 1006, reason: "" });
|
|
1377
1728
|
try {
|
|
1378
1729
|
socket.close();
|
|
1379
1730
|
} catch {
|
|
1380
1731
|
}
|
|
1381
|
-
queue.end({ code: 1006, reason: "" });
|
|
1382
|
-
fail3(unopened(url, 1006));
|
|
1383
1732
|
});
|
|
1384
1733
|
socket.addEventListener("close", (event) => {
|
|
1385
1734
|
queue.end({ code: event.code, reason: event.reason });
|
|
@@ -1824,9 +2173,9 @@ var UserDb = class {
|
|
|
1824
2173
|
if (await this.realDir(pnpm)) await this.fs.chmod(pnpm, scoop ? 488 : 448);
|
|
1825
2174
|
}
|
|
1826
2175
|
async realDir(path) {
|
|
1827
|
-
const
|
|
1828
|
-
for (let n = 1; n <=
|
|
1829
|
-
const at = `/${
|
|
2176
|
+
const parts2 = path.split("/").filter(Boolean);
|
|
2177
|
+
for (let n = 1; n <= parts2.length; n++) {
|
|
2178
|
+
const at = `/${parts2.slice(0, n).join("/")}`;
|
|
1830
2179
|
const st = await this.fs.lstat(at).catch(() => void 0);
|
|
1831
2180
|
if (!st?.isDirectory) return false;
|
|
1832
2181
|
}
|
|
@@ -2054,7 +2403,7 @@ function versionError(theirs) {
|
|
|
2054
2403
|
}
|
|
2055
2404
|
|
|
2056
2405
|
// src/client/serve-client.ts
|
|
2057
|
-
var
|
|
2406
|
+
var MAX_LINKS3 = 40;
|
|
2058
2407
|
function act(session, req, signal) {
|
|
2059
2408
|
if (req.action === "write") session.write(req.bytes);
|
|
2060
2409
|
else if (req.action === "resize") session.resize(req.cols, req.rows);
|
|
@@ -2062,26 +2411,50 @@ function act(session, req, signal) {
|
|
|
2062
2411
|
else session.close();
|
|
2063
2412
|
}
|
|
2064
2413
|
async function realpath(fs, path) {
|
|
2065
|
-
let
|
|
2414
|
+
let parts2 = fs.resolvePath("/", path).split("/").filter(Boolean);
|
|
2066
2415
|
let resolved = "";
|
|
2067
|
-
for (let hops = 0;
|
|
2068
|
-
const [name2, ...rest2] =
|
|
2416
|
+
for (let hops = 0; parts2.length > 0; ) {
|
|
2417
|
+
const [name2, ...rest2] = parts2;
|
|
2069
2418
|
const next2 = `${resolved}/${name2}`;
|
|
2070
2419
|
if (!(await fs.lstat(next2)).isSymbolicLink) {
|
|
2071
2420
|
resolved = next2;
|
|
2072
|
-
|
|
2421
|
+
parts2 = rest2;
|
|
2073
2422
|
continue;
|
|
2074
2423
|
}
|
|
2075
|
-
if (++hops >
|
|
2424
|
+
if (++hops > MAX_LINKS3) throw fsError("ELOOP", path);
|
|
2076
2425
|
const target = await fs.readlink(next2);
|
|
2077
2426
|
const base = target.startsWith("/") ? "/" : resolved || "/";
|
|
2078
|
-
|
|
2427
|
+
parts2 = [...fs.resolvePath(base, target).split("/").filter(Boolean), ...rest2];
|
|
2079
2428
|
resolved = "";
|
|
2080
2429
|
}
|
|
2081
2430
|
return resolved || "/";
|
|
2082
2431
|
}
|
|
2083
|
-
|
|
2432
|
+
var CLIENT_CHECKS = {
|
|
2433
|
+
readFile: (g, at) => readable(g, "read", at(0)),
|
|
2434
|
+
writeFile: (g, at) => writable(g, "write", at(0)),
|
|
2435
|
+
stat: (g, at) => searchable(g, "stat", at(0), true),
|
|
2436
|
+
lstat: (g, at) => searchable(g, "lstat", at(0), false),
|
|
2437
|
+
readdir: (g, at) => listable(g, "readdir", at(0)),
|
|
2438
|
+
mkdir: (g, at) => makeIn(g, "mkdir", at(0)),
|
|
2439
|
+
rm: (g, at) => removable(g, "rm", at(0)),
|
|
2440
|
+
rename: (g, at) => renamable(g, at(0), at(1)),
|
|
2441
|
+
realpath: (g, at) => searchable(g, "realpath", at(0), true),
|
|
2442
|
+
symlink: (g, at) => inParent(g, "symlink", at(1)),
|
|
2443
|
+
readlink: (g, at) => searchable(g, "readlink", at(0), false),
|
|
2444
|
+
exists: (g, at) => searchable(g, "exists", at(0), true)
|
|
2445
|
+
};
|
|
2446
|
+
async function fsCall(fs, method, args, owner, g) {
|
|
2084
2447
|
const path = (i) => fs.resolvePath("/", String(args[i]));
|
|
2448
|
+
if (g) {
|
|
2449
|
+
const denied2 = await Promise.resolve(CLIENT_CHECKS[method](g, path)).then(
|
|
2450
|
+
() => false,
|
|
2451
|
+
(err) => {
|
|
2452
|
+
if (method === "exists") return true;
|
|
2453
|
+
throw err;
|
|
2454
|
+
}
|
|
2455
|
+
);
|
|
2456
|
+
if (denied2) return { result: false };
|
|
2457
|
+
}
|
|
2085
2458
|
switch (method) {
|
|
2086
2459
|
case "readFile": {
|
|
2087
2460
|
const bytes = (await fs.readFileBuffer(path(0))).slice();
|
|
@@ -2144,6 +2517,10 @@ function ownerFor(l, self) {
|
|
|
2144
2517
|
if (!l.owners || !self) return void 0;
|
|
2145
2518
|
return { uid: self.uid, gid: self.gid, umask: self.umask ?? 18 };
|
|
2146
2519
|
}
|
|
2520
|
+
function guardFor(l, self) {
|
|
2521
|
+
if (!l.dac || !self) return void 0;
|
|
2522
|
+
return { fs: l.fs, cred: credOf(self), pid: 0, dac: l.dac };
|
|
2523
|
+
}
|
|
2147
2524
|
async function accountOf(l, user) {
|
|
2148
2525
|
const found = await l.users.lookup(user);
|
|
2149
2526
|
if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
@@ -2212,17 +2589,28 @@ function cdpRegistry(send, gone) {
|
|
|
2212
2589
|
}
|
|
2213
2590
|
};
|
|
2214
2591
|
}
|
|
2215
|
-
function
|
|
2592
|
+
function visibleTo(g, notify) {
|
|
2593
|
+
if (!g?.dac.enforcing) return notify;
|
|
2594
|
+
let queue = Promise.resolve();
|
|
2595
|
+
const seen = async (path) => (await access(g.fs, g.cred, parentOf(path), R_OK | X_OK, { directory: true })).denied === void 0;
|
|
2596
|
+
return (change) => {
|
|
2597
|
+
queue = queue.then(async () => {
|
|
2598
|
+
if (!("paths" in change)) return notify(change);
|
|
2599
|
+
const paths = [];
|
|
2600
|
+
for (const path of change.paths) if (await seen(path)) paths.push(path);
|
|
2601
|
+
if (paths.length > 0) notify({ paths });
|
|
2602
|
+
});
|
|
2603
|
+
};
|
|
2604
|
+
}
|
|
2605
|
+
async function watchOp(l, req, watches, send, g) {
|
|
2216
2606
|
if (req.op === "unwatch") {
|
|
2217
2607
|
watches.get(req.watch)?.();
|
|
2218
2608
|
watches.delete(req.watch);
|
|
2219
2609
|
return { result: true };
|
|
2220
2610
|
}
|
|
2221
|
-
const
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
(change) => send({ watch: req.id, change })
|
|
2225
|
-
);
|
|
2611
|
+
if (g) for (const path of req.paths) await readable(g, "watch", l.fs.resolvePath("/", path));
|
|
2612
|
+
const notify = visibleTo(g, (change) => send({ watch: req.id, change }));
|
|
2613
|
+
const unwatch = l.watchers.watch(req.paths, { recursive: req.recursive }, notify);
|
|
2226
2614
|
watches.set(req.id, unwatch);
|
|
2227
2615
|
return { result: req.id };
|
|
2228
2616
|
}
|
|
@@ -2256,9 +2644,9 @@ function serveClient(port, host) {
|
|
|
2256
2644
|
ended.resolve();
|
|
2257
2645
|
}
|
|
2258
2646
|
async function hello(message3) {
|
|
2259
|
-
const
|
|
2260
|
-
if (
|
|
2261
|
-
send({ hello: { protocol: PROTOCOL, error:
|
|
2647
|
+
const refused2 = versionError(message3.protocol);
|
|
2648
|
+
if (refused2) {
|
|
2649
|
+
send({ hello: { protocol: PROTOCOL, error: refused2 } });
|
|
2262
2650
|
detach();
|
|
2263
2651
|
return;
|
|
2264
2652
|
}
|
|
@@ -2338,10 +2726,10 @@ function serveClient(port, host) {
|
|
|
2338
2726
|
if (!FS_METHODS.includes(req.method)) {
|
|
2339
2727
|
throw new Error(`unknown file system call ${req.method}`);
|
|
2340
2728
|
}
|
|
2341
|
-
return fsCall(l.fs, req.method, req.args, ownerFor(l, self));
|
|
2729
|
+
return fsCall(l.fs, req.method, req.args, ownerFor(l, self), guardFor(l, self));
|
|
2342
2730
|
case "watch":
|
|
2343
2731
|
case "unwatch":
|
|
2344
|
-
return watchOp(l, req, watches, send);
|
|
2732
|
+
return watchOp(l, req, watches, send, guardFor(l, self));
|
|
2345
2733
|
case "mount":
|
|
2346
2734
|
return { result: await l.mount(req.spec) };
|
|
2347
2735
|
case "umount":
|
|
@@ -2399,11 +2787,11 @@ function serveClient(port, host) {
|
|
|
2399
2787
|
}
|
|
2400
2788
|
|
|
2401
2789
|
// src/fs/meta.ts
|
|
2402
|
-
function
|
|
2790
|
+
function parentOf2(path) {
|
|
2403
2791
|
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
2404
2792
|
}
|
|
2405
2793
|
function moved(entry, path) {
|
|
2406
|
-
return entry.link === void 0 ? { ...entry, path } : { ...entry, path, dir:
|
|
2794
|
+
return entry.link === void 0 ? { ...entry, path } : { ...entry, path, dir: parentOf2(path) };
|
|
2407
2795
|
}
|
|
2408
2796
|
function under(path, root) {
|
|
2409
2797
|
return path === root || path.startsWith(`${root}/`);
|
|
@@ -2446,71 +2834,14 @@ var MemoryMeta = class {
|
|
|
2446
2834
|
}
|
|
2447
2835
|
};
|
|
2448
2836
|
|
|
2449
|
-
// src/fs/owners.ts
|
|
2450
|
-
var ROOT_OWNER = Object.freeze({ uid: 0, gid: 0 });
|
|
2451
|
-
var S_ISUID = 2048;
|
|
2452
|
-
var S_ISGID = 1024;
|
|
2453
|
-
var S_IXGRP = 8;
|
|
2454
|
-
function ownOf(entry) {
|
|
2455
|
-
if (entry?.uid === void 0 || entry.gid === void 0) return void 0;
|
|
2456
|
-
return { uid: entry.uid, gid: entry.gid };
|
|
2457
|
-
}
|
|
2458
|
-
function handedBy(entry) {
|
|
2459
|
-
return entry?.heir ? { uid: entry.heir[0], gid: entry.heir[1] } : ownOf(entry);
|
|
2460
|
-
}
|
|
2461
|
-
function inherit(entries) {
|
|
2462
|
-
for (let i = entries.length - 1; i >= 0; i--) {
|
|
2463
|
-
const found = handedBy(entries[i]);
|
|
2464
|
-
if (found) return found;
|
|
2465
|
-
}
|
|
2466
|
-
return { ...ROOT_OWNER };
|
|
2467
|
-
}
|
|
2468
|
-
function chain(path) {
|
|
2469
|
-
const parts = path.split("/").filter(Boolean);
|
|
2470
|
-
return ["/", ...parts.map((_, i) => `/${parts.slice(0, i + 1).join("/")}`)];
|
|
2471
|
-
}
|
|
2472
|
-
function sameOwner(a, b) {
|
|
2473
|
-
return a.uid === b.uid && a.gid === b.gid;
|
|
2474
|
-
}
|
|
2475
|
-
function ownerOf(cred, mode) {
|
|
2476
|
-
return { uid: cred.euid, gid: cred.egid, ...mode !== void 0 ? { mode } : {} };
|
|
2477
|
-
}
|
|
2478
|
-
function inGroup(cred, gid) {
|
|
2479
|
-
return cred.egid === gid || cred.groups.includes(gid);
|
|
2480
|
-
}
|
|
2481
|
-
function mayChown(cred, current, uid, gid) {
|
|
2482
|
-
if (cred.euid === 0) return true;
|
|
2483
|
-
const owner = cred.euid === current.uid;
|
|
2484
|
-
if (uid !== void 0 && !(owner && uid === current.uid)) return false;
|
|
2485
|
-
if (gid !== void 0 && !(owner && (gid === current.gid || inGroup(cred, gid)))) return false;
|
|
2486
|
-
return true;
|
|
2487
|
-
}
|
|
2488
|
-
function clearedSetId(mode) {
|
|
2489
|
-
const bits2 = S_ISUID | (mode & S_IXGRP ? S_ISGID : 0);
|
|
2490
|
-
return mode & 4095 & ~bits2;
|
|
2491
|
-
}
|
|
2492
|
-
async function chownAs(fs, cred, path, change) {
|
|
2493
|
-
const { uid, gid, follow: follow2 } = change;
|
|
2494
|
-
const st = follow2 ? await fs.stat(path) : await fs.lstat(path);
|
|
2495
|
-
const current = { uid: st.uid ?? 0, gid: st.gid ?? 0 };
|
|
2496
|
-
if (!fs.chown || !mayChown(cred, current, uid, gid)) throw fsError("EPERM", path);
|
|
2497
|
-
const clear = st.isFile && (uid !== void 0 || gid !== void 0) ? clearedSetId(st.mode) : void 0;
|
|
2498
|
-
await fs.chown(path, {
|
|
2499
|
-
...uid !== void 0 ? { uid } : {},
|
|
2500
|
-
...gid !== void 0 ? { gid } : {},
|
|
2501
|
-
follow: follow2,
|
|
2502
|
-
...clear !== void 0 && clear !== (st.mode & 4095) ? { mode: clear } : {}
|
|
2503
|
-
});
|
|
2504
|
-
}
|
|
2505
|
-
|
|
2506
2837
|
// src/fs/opfs.ts
|
|
2507
2838
|
var S_IFDIR = 16384;
|
|
2508
2839
|
var S_IFREG = 32768;
|
|
2509
2840
|
var S_IFLNK = 40960;
|
|
2510
2841
|
var PERM_MASK = 4095;
|
|
2511
|
-
var
|
|
2842
|
+
var S_ISGID3 = 1024;
|
|
2512
2843
|
var READ_ATTEMPTS = 3;
|
|
2513
|
-
var
|
|
2844
|
+
var MAX_LINKS4 = 40;
|
|
2514
2845
|
var CHECK_MS = 1e3;
|
|
2515
2846
|
var EXECUTABLE = /\/node_modules\/(?:@[^/]+\/)?[^/]+\/bin\//;
|
|
2516
2847
|
var ERRNO_BY_DOM_ERROR = {
|
|
@@ -2542,10 +2873,10 @@ function translate(err, path, op = "lookup", mapped = true) {
|
|
|
2542
2873
|
var META_OPS = ["get", "update", "move", "remove", "links", "under", "all"];
|
|
2543
2874
|
function sidecar(meta) {
|
|
2544
2875
|
const pathOf = ([first]) => [first, "/"].flat().find((p) => typeof p === "string");
|
|
2545
|
-
const
|
|
2876
|
+
const guarded2 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
|
|
2546
2877
|
throw translate(err, pathOf(args), "sidecar", false);
|
|
2547
2878
|
});
|
|
2548
|
-
return Object.fromEntries(META_OPS.map((op) => [op,
|
|
2879
|
+
return Object.fromEntries(META_OPS.map((op) => [op, guarded2(op)]));
|
|
2549
2880
|
}
|
|
2550
2881
|
function missing(err) {
|
|
2551
2882
|
const code = err?.code;
|
|
@@ -2554,15 +2885,15 @@ function missing(err) {
|
|
|
2554
2885
|
function split(path) {
|
|
2555
2886
|
return path.split("/").filter(Boolean);
|
|
2556
2887
|
}
|
|
2557
|
-
function
|
|
2888
|
+
function parentOf3(path) {
|
|
2558
2889
|
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
2559
2890
|
}
|
|
2560
2891
|
function baseOf(path) {
|
|
2561
2892
|
return path.slice(path.lastIndexOf("/") + 1);
|
|
2562
2893
|
}
|
|
2563
2894
|
function prefixes(path) {
|
|
2564
|
-
const
|
|
2565
|
-
return
|
|
2895
|
+
const parts2 = split(path);
|
|
2896
|
+
return parts2.map((_, i) => `/${parts2.slice(0, i + 1).join("/")}`);
|
|
2566
2897
|
}
|
|
2567
2898
|
function within(path, root) {
|
|
2568
2899
|
return path === root || path.startsWith(`${root}/`);
|
|
@@ -2583,39 +2914,39 @@ async function openSync(handle) {
|
|
|
2583
2914
|
}
|
|
2584
2915
|
}
|
|
2585
2916
|
}
|
|
2586
|
-
function writeAll(
|
|
2917
|
+
function writeAll(access2, at, bytes) {
|
|
2587
2918
|
for (let done4 = 0; done4 < bytes.length; ) {
|
|
2588
|
-
const n =
|
|
2919
|
+
const n = access2.write(bytes.subarray(done4), { at: at + done4 });
|
|
2589
2920
|
if (!(n > 0))
|
|
2590
2921
|
throw Object.assign(new Error("the file system took no more bytes"), { code: "ENOSPC" });
|
|
2591
2922
|
done4 += n;
|
|
2592
2923
|
}
|
|
2593
2924
|
}
|
|
2594
2925
|
async function positioned(handle, op) {
|
|
2595
|
-
const
|
|
2596
|
-
if (
|
|
2926
|
+
const access2 = await openSync(handle);
|
|
2927
|
+
if (access2) {
|
|
2597
2928
|
try {
|
|
2598
2929
|
op({
|
|
2599
|
-
write: (at, bytes) => writeAll(
|
|
2600
|
-
truncate: (n) =>
|
|
2930
|
+
write: (at, bytes) => writeAll(access2, at, bytes),
|
|
2931
|
+
truncate: (n) => access2.truncate(n)
|
|
2601
2932
|
});
|
|
2602
|
-
|
|
2933
|
+
access2.flush();
|
|
2603
2934
|
} finally {
|
|
2604
|
-
|
|
2935
|
+
access2.close();
|
|
2605
2936
|
}
|
|
2606
2937
|
return;
|
|
2607
2938
|
}
|
|
2608
|
-
const
|
|
2939
|
+
const writable2 = await handle.createWritable({ keepExistingData: true });
|
|
2609
2940
|
try {
|
|
2610
2941
|
await op({
|
|
2611
|
-
write: (at, bytes) =>
|
|
2612
|
-
truncate: (n) =>
|
|
2942
|
+
write: (at, bytes) => writable2.write({ type: "write", position: at, data: bytes }),
|
|
2943
|
+
truncate: (n) => writable2.truncate(n)
|
|
2613
2944
|
});
|
|
2614
2945
|
} catch (err) {
|
|
2615
|
-
await
|
|
2946
|
+
await writable2.abort();
|
|
2616
2947
|
throw err;
|
|
2617
2948
|
}
|
|
2618
|
-
await
|
|
2949
|
+
await writable2.close();
|
|
2619
2950
|
}
|
|
2620
2951
|
function isHandle(child) {
|
|
2621
2952
|
return "kind" in child;
|
|
@@ -2650,13 +2981,21 @@ var OpfsFs = class {
|
|
|
2650
2981
|
trackOwners() {
|
|
2651
2982
|
this.owners = true;
|
|
2652
2983
|
}
|
|
2984
|
+
async ancestry(path) {
|
|
2985
|
+
const entries = await this.meta.get(chain(path));
|
|
2986
|
+
return entries.map((entry, i) => ({
|
|
2987
|
+
...ownOf(entry) ?? (i === 0 ? ROOT_OWNER : inherit(entries.slice(0, i))),
|
|
2988
|
+
...entry?.mode !== void 0 ? { mode: entry.mode } : {},
|
|
2989
|
+
...entry?.link !== void 0 ? { link: entry.link } : {}
|
|
2990
|
+
}));
|
|
2991
|
+
}
|
|
2653
2992
|
async handedTo(dir) {
|
|
2654
2993
|
return inherit(await this.meta.get(chain(dir)));
|
|
2655
2994
|
}
|
|
2656
2995
|
async ownerAt(path, entry) {
|
|
2657
2996
|
const own = ownOf(entry);
|
|
2658
2997
|
if (own) return own;
|
|
2659
|
-
return path === "/" ? { ...ROOT_OWNER } : this.handedTo(
|
|
2998
|
+
return path === "/" ? { ...ROOT_OWNER } : this.handedTo(parentOf3(path));
|
|
2660
2999
|
}
|
|
2661
3000
|
async owned(stat, path, entry) {
|
|
2662
3001
|
return this.owners ? { ...stat, ...await this.ownerAt(path, entry) } : stat;
|
|
@@ -2666,11 +3005,11 @@ var OpfsFs = class {
|
|
|
2666
3005
|
const entries = await this.meta.get(chain(path));
|
|
2667
3006
|
const self = entries.pop();
|
|
2668
3007
|
const parent = entries.at(-1);
|
|
2669
|
-
const setgid = ((parent?.mode ?? 0) &
|
|
3008
|
+
const setgid = ((parent?.mode ?? 0) & S_ISGID3) !== 0;
|
|
2670
3009
|
const parentGid = (ownOf(parent) ?? inherit(entries.slice(0, -1))).gid;
|
|
2671
3010
|
const want = { uid: owner.uid, gid: setgid ? parentGid : owner.gid };
|
|
2672
3011
|
let mode = owner.mode === void 0 ? void 0 : owner.mode & PERM_MASK;
|
|
2673
|
-
if (directory && setgid) mode = (mode ?? 493) |
|
|
3012
|
+
if (directory && setgid) mode = (mode ?? 493) | S_ISGID3;
|
|
2674
3013
|
const fields = {};
|
|
2675
3014
|
if (mode !== void 0 && mode !== (this.mode(path, directory, void 0) & PERM_MASK)) {
|
|
2676
3015
|
fields.mode = mode;
|
|
@@ -2686,7 +3025,7 @@ var OpfsFs = class {
|
|
|
2686
3025
|
async chown(path, change) {
|
|
2687
3026
|
if (!this.owners) return;
|
|
2688
3027
|
if (change.kind === "new") {
|
|
2689
|
-
const at = resolvePath((await this.locate(
|
|
3028
|
+
const at = resolvePath((await this.locate(parentOf3(path), true)).path, baseOf(path));
|
|
2690
3029
|
const { uid = 0, gid = 0, mode } = change;
|
|
2691
3030
|
return this.stampNew(at, { uid, gid, ...mode !== void 0 ? { mode } : {} }, false);
|
|
2692
3031
|
}
|
|
@@ -2714,30 +3053,30 @@ var OpfsFs = class {
|
|
|
2714
3053
|
const [stored] = source.link ? [source.link] : await this.meta.get([source.path]);
|
|
2715
3054
|
return ownOf(stored) ? void 0 : this.ownerAt(source.path, stored);
|
|
2716
3055
|
}
|
|
2717
|
-
async walk(
|
|
3056
|
+
async walk(parts2, from, start2) {
|
|
2718
3057
|
let dir = start2;
|
|
2719
|
-
for (let at = from; at <
|
|
2720
|
-
dir = await dir.getDirectoryHandle(
|
|
2721
|
-
this.dirs.set(`/${
|
|
3058
|
+
for (let at = from; at < parts2.length; at++) {
|
|
3059
|
+
dir = await dir.getDirectoryHandle(parts2[at]);
|
|
3060
|
+
this.dirs.set(`/${parts2.slice(0, at + 1).join("/")}`, dir);
|
|
2722
3061
|
this.checked.set(dir, performance.now());
|
|
2723
3062
|
}
|
|
2724
3063
|
return dir;
|
|
2725
3064
|
}
|
|
2726
3065
|
async dir(path) {
|
|
2727
|
-
const
|
|
2728
|
-
let from =
|
|
3066
|
+
const parts2 = split(path);
|
|
3067
|
+
let from = parts2.length;
|
|
2729
3068
|
let cached;
|
|
2730
3069
|
for (; from > 0 && !cached; from--)
|
|
2731
|
-
cached = this.dirs.get(`/${
|
|
3070
|
+
cached = this.dirs.get(`/${parts2.slice(0, from).join("/")}`);
|
|
2732
3071
|
if (cached) from++;
|
|
2733
|
-
const at = `/${
|
|
3072
|
+
const at = `/${parts2.slice(0, from).join("/")}`;
|
|
2734
3073
|
if (cached && !await this.current(at, cached)) {
|
|
2735
3074
|
this.forget(at);
|
|
2736
3075
|
cached = void 0;
|
|
2737
3076
|
from = 0;
|
|
2738
3077
|
}
|
|
2739
3078
|
try {
|
|
2740
|
-
return await this.walk(
|
|
3079
|
+
return await this.walk(parts2, from, cached ?? this.root);
|
|
2741
3080
|
} catch (err) {
|
|
2742
3081
|
throw translate(err, path);
|
|
2743
3082
|
}
|
|
@@ -2767,10 +3106,10 @@ var OpfsFs = class {
|
|
|
2767
3106
|
return kind === "file" ? parent.getFileHandle(name2) : parent.getDirectoryHandle(name2);
|
|
2768
3107
|
}
|
|
2769
3108
|
async parent(path) {
|
|
2770
|
-
const
|
|
2771
|
-
const name2 =
|
|
3109
|
+
const parts2 = split(path);
|
|
3110
|
+
const name2 = parts2.pop();
|
|
2772
3111
|
if (name2 === void 0) throw fsError("EBUSY", path);
|
|
2773
|
-
return [`/${
|
|
3112
|
+
return [`/${parts2.join("/")}`, name2];
|
|
2774
3113
|
}
|
|
2775
3114
|
async handle(path) {
|
|
2776
3115
|
if (split(path).length === 0) return this.root;
|
|
@@ -2797,13 +3136,13 @@ var OpfsFs = class {
|
|
|
2797
3136
|
async linkAlong(path) {
|
|
2798
3137
|
for (const entry of await this.meta.get(prefixes(path))) {
|
|
2799
3138
|
if (entry?.link === void 0) continue;
|
|
2800
|
-
if (!await this.real(entry.path) && await this.real(
|
|
3139
|
+
if (!await this.real(entry.path) && await this.real(parentOf3(entry.path))) return entry;
|
|
2801
3140
|
}
|
|
2802
3141
|
return void 0;
|
|
2803
3142
|
}
|
|
2804
3143
|
async locate(path, follow2) {
|
|
2805
3144
|
let current = path;
|
|
2806
|
-
for (let hops = 0; hops <=
|
|
3145
|
+
for (let hops = 0; hops <= MAX_LINKS4; hops++) {
|
|
2807
3146
|
try {
|
|
2808
3147
|
return { path: current, handle: await this.handle(current) };
|
|
2809
3148
|
} catch (err) {
|
|
@@ -2812,7 +3151,7 @@ var OpfsFs = class {
|
|
|
2812
3151
|
if (!link2) throw err;
|
|
2813
3152
|
if (link2.path === current && !follow2) return { path: current, link: link2 };
|
|
2814
3153
|
const target = `${link2.link}${current.slice(link2.path.length)}`;
|
|
2815
|
-
current = target.split("/").includes("..") ? await this.physical(
|
|
3154
|
+
current = target.split("/").includes("..") ? await this.physical(parentOf3(link2.path), target, hops + 1) : resolvePath(parentOf3(link2.path), target);
|
|
2816
3155
|
}
|
|
2817
3156
|
}
|
|
2818
3157
|
throw fsError("ELOOP", path);
|
|
@@ -2829,7 +3168,7 @@ var OpfsFs = class {
|
|
|
2829
3168
|
const st = await this.lstat(at);
|
|
2830
3169
|
if (!st.isSymbolicLink && !st.isDirectory) throw fsError("ENOTDIR", at);
|
|
2831
3170
|
if (!st.isSymbolicLink) done4.push(part);
|
|
2832
|
-
else if (++hops >
|
|
3171
|
+
else if (++hops > MAX_LINKS4) throw fsError("ELOOP", path);
|
|
2833
3172
|
else {
|
|
2834
3173
|
const link2 = await this.readlink(at);
|
|
2835
3174
|
if (link2.startsWith("/")) done4 = [];
|
|
@@ -2972,19 +3311,19 @@ var OpfsFs = class {
|
|
|
2972
3311
|
}
|
|
2973
3312
|
async writeSession(path, truncate) {
|
|
2974
3313
|
const handle = (await this.fileFor(path))[0];
|
|
2975
|
-
const
|
|
3314
|
+
const writable2 = await this.retrying(
|
|
2976
3315
|
path,
|
|
2977
3316
|
() => handle.createWritable({ keepExistingData: !truncate }),
|
|
2978
3317
|
true
|
|
2979
3318
|
);
|
|
2980
3319
|
return {
|
|
2981
|
-
write: (at, bytes) =>
|
|
3320
|
+
write: (at, bytes) => writable2.write({ type: "write", position: at, data: bytes }).catch((err) => {
|
|
2982
3321
|
throw translate(err, path, "write", false);
|
|
2983
3322
|
}),
|
|
2984
|
-
truncate: (size) =>
|
|
3323
|
+
truncate: (size) => writable2.truncate(size).catch((err) => {
|
|
2985
3324
|
throw translate(err, path, "write", false);
|
|
2986
3325
|
}),
|
|
2987
|
-
close: () =>
|
|
3326
|
+
close: () => writable2.close().catch((err) => {
|
|
2988
3327
|
throw translate(err, path, "commit", false);
|
|
2989
3328
|
})
|
|
2990
3329
|
};
|
|
@@ -2999,8 +3338,8 @@ var OpfsFs = class {
|
|
|
2999
3338
|
if (!missing(err)) throw err;
|
|
3000
3339
|
}
|
|
3001
3340
|
const [entry] = await this.meta.get([path]);
|
|
3002
|
-
const wanted = entry?.link === void 0 ? path : resolvePath(
|
|
3003
|
-
return [resolvePath((await this.locate(
|
|
3341
|
+
const wanted = entry?.link === void 0 ? path : resolvePath(parentOf3(path), entry.link);
|
|
3342
|
+
return [resolvePath((await this.locate(parentOf3(wanted), true)).path, baseOf(wanted)), false];
|
|
3004
3343
|
}
|
|
3005
3344
|
async fileFor(path) {
|
|
3006
3345
|
const [target, existed] = await this.writeTarget(path);
|
|
@@ -3010,7 +3349,7 @@ var OpfsFs = class {
|
|
|
3010
3349
|
dir,
|
|
3011
3350
|
(parent) => parent.getFileHandle(name2, { create: true })
|
|
3012
3351
|
);
|
|
3013
|
-
if (!existed) this.touch(
|
|
3352
|
+
if (!existed) this.touch(parentOf3(target));
|
|
3014
3353
|
return [handle, existed, target];
|
|
3015
3354
|
} catch (err) {
|
|
3016
3355
|
const code = err?.name === "TypeMismatchError" ? "EISDIR" : null;
|
|
@@ -3022,9 +3361,9 @@ var OpfsFs = class {
|
|
|
3022
3361
|
if (!existed) await this.stampNew(target, owner, false);
|
|
3023
3362
|
if (!existed && content.length === 0) return;
|
|
3024
3363
|
try {
|
|
3025
|
-
const
|
|
3026
|
-
await
|
|
3027
|
-
await
|
|
3364
|
+
const writable2 = await file.createWritable();
|
|
3365
|
+
await writable2.write(content);
|
|
3366
|
+
await writable2.close();
|
|
3028
3367
|
} catch (err) {
|
|
3029
3368
|
throw translate(err, path, "commit", false);
|
|
3030
3369
|
}
|
|
@@ -3080,9 +3419,9 @@ var OpfsFs = class {
|
|
|
3080
3419
|
gid: owner.gid,
|
|
3081
3420
|
...mode ? { mode, parents: mode } : {}
|
|
3082
3421
|
};
|
|
3083
|
-
await this.mkdir(
|
|
3422
|
+
await this.mkdir(parentOf3(path), { recursive: true, ...above ? { owner: above } : {} });
|
|
3084
3423
|
}
|
|
3085
|
-
const parent = await this.locate(
|
|
3424
|
+
const parent = await this.locate(parentOf3(path), true);
|
|
3086
3425
|
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
3087
3426
|
await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
|
|
3088
3427
|
const made2 = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
|
|
@@ -3111,7 +3450,7 @@ var OpfsFs = class {
|
|
|
3111
3450
|
this.changed(found.path);
|
|
3112
3451
|
}
|
|
3113
3452
|
await this.meta.remove([found.path]);
|
|
3114
|
-
this.touch(
|
|
3453
|
+
this.touch(parentOf3(found.path));
|
|
3115
3454
|
}
|
|
3116
3455
|
links() {
|
|
3117
3456
|
return false;
|
|
@@ -3123,7 +3462,7 @@ var OpfsFs = class {
|
|
|
3123
3462
|
if (from === to) return;
|
|
3124
3463
|
const source = await this.locate(from, false);
|
|
3125
3464
|
this.forget(source.path);
|
|
3126
|
-
const parent = await this.locate(
|
|
3465
|
+
const parent = await this.locate(parentOf3(to), true);
|
|
3127
3466
|
const destination = resolvePath(parent.path, baseOf(to));
|
|
3128
3467
|
const sourceStat = await this.describe(source);
|
|
3129
3468
|
if (sourceStat.isDirectory && within(destination, source.path)) throw fsError("EINVAL", to);
|
|
@@ -3151,7 +3490,7 @@ var OpfsFs = class {
|
|
|
3151
3490
|
if (source.link) {
|
|
3152
3491
|
if (target?.handle) await this.rm(destination);
|
|
3153
3492
|
await this.meta.move(source.path, destination, carry);
|
|
3154
|
-
this.touch(
|
|
3493
|
+
this.touch(parentOf3(source.path), parent.path);
|
|
3155
3494
|
return;
|
|
3156
3495
|
}
|
|
3157
3496
|
const dir = parent.handle;
|
|
@@ -3173,7 +3512,7 @@ var OpfsFs = class {
|
|
|
3173
3512
|
}
|
|
3174
3513
|
if (copied) await this.restamp(destination, timed2);
|
|
3175
3514
|
this.changed(source.path, destination);
|
|
3176
|
-
this.touch(
|
|
3515
|
+
this.touch(parentOf3(source.path), parent.path);
|
|
3177
3516
|
}
|
|
3178
3517
|
async modified(path) {
|
|
3179
3518
|
const handle = await this.handle(path);
|
|
@@ -3210,16 +3549,16 @@ var OpfsFs = class {
|
|
|
3210
3549
|
}
|
|
3211
3550
|
async copy(handle, dir, name2) {
|
|
3212
3551
|
if (handle.kind === "file") {
|
|
3213
|
-
const
|
|
3214
|
-
await
|
|
3215
|
-
await
|
|
3552
|
+
const writable2 = await (await dir.getFileHandle(name2, { create: true })).createWritable();
|
|
3553
|
+
await writable2.write(await handle.getFile());
|
|
3554
|
+
await writable2.close();
|
|
3216
3555
|
return;
|
|
3217
3556
|
}
|
|
3218
3557
|
const target = await dir.getDirectoryHandle(name2, { create: true });
|
|
3219
3558
|
for await (const child of handle.values()) await this.copy(child, target, child.name);
|
|
3220
3559
|
}
|
|
3221
3560
|
async symlink(target, path, owner) {
|
|
3222
|
-
const parent = await this.locate(
|
|
3561
|
+
const parent = await this.locate(parentOf3(path), true);
|
|
3223
3562
|
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
3224
3563
|
const at = resolvePath(parent.path, baseOf(path));
|
|
3225
3564
|
if (await this.locate(at, false).then(
|
|
@@ -3295,7 +3634,7 @@ var OpfsFs = class {
|
|
|
3295
3634
|
let removed = 0;
|
|
3296
3635
|
for (const entry of await this.meta.all()) {
|
|
3297
3636
|
const real = await this.real(entry.path);
|
|
3298
|
-
const stale = entry.link === void 0 ? !real : real || !await this.real(
|
|
3637
|
+
const stale = entry.link === void 0 ? !real : real || !await this.real(parentOf3(entry.path));
|
|
3299
3638
|
if (!stale) continue;
|
|
3300
3639
|
await this.meta.update(entry.path, () => void 0);
|
|
3301
3640
|
removed++;
|
|
@@ -3366,7 +3705,7 @@ function webLocks() {
|
|
|
3366
3705
|
return globalThis.navigator?.locks;
|
|
3367
3706
|
}
|
|
3368
3707
|
var within2 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
3369
|
-
var
|
|
3708
|
+
var parentOf4 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
3370
3709
|
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
3371
3710
|
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
3372
3711
|
var PathGate = class {
|
|
@@ -3497,7 +3836,7 @@ var UnlinkedKeeper = class {
|
|
|
3497
3836
|
await this.serial(async () => {
|
|
3498
3837
|
await Promise.allSettled([this.fs.rename(file.hidden, path)]);
|
|
3499
3838
|
this.unlinkedName(file.hidden);
|
|
3500
|
-
await this.tidy(
|
|
3839
|
+
await this.tidy(parentOf4(file.hidden));
|
|
3501
3840
|
});
|
|
3502
3841
|
}
|
|
3503
3842
|
done4();
|
|
@@ -3537,7 +3876,7 @@ var UnlinkedKeeper = class {
|
|
|
3537
3876
|
}
|
|
3538
3877
|
await this.fs.rm(hidden).catch(() => void 0);
|
|
3539
3878
|
this.unlinkedName(hidden);
|
|
3540
|
-
await this.tidy(
|
|
3879
|
+
await this.tidy(parentOf4(hidden));
|
|
3541
3880
|
}
|
|
3542
3881
|
async linkedName(hidden) {
|
|
3543
3882
|
if (!mayLink(this.fs, hidden)) return;
|
|
@@ -4547,7 +4886,7 @@ function binfmtOf(binfmts, path) {
|
|
|
4547
4886
|
var S_IFDIR2 = 16384;
|
|
4548
4887
|
var S_IFREG2 = 32768;
|
|
4549
4888
|
var COMMAND = /^\/(?:usr\/)?bin\/([^/]+)$/;
|
|
4550
|
-
var
|
|
4889
|
+
var MAX_LINKS5 = 40;
|
|
4551
4890
|
function missing2(err) {
|
|
4552
4891
|
return err?.code === "ENOENT";
|
|
4553
4892
|
}
|
|
@@ -4567,7 +4906,7 @@ function synthetic(directory, path) {
|
|
|
4567
4906
|
}
|
|
4568
4907
|
async function followLinks(fs, path) {
|
|
4569
4908
|
let current = path;
|
|
4570
|
-
for (let hops = 0; hops <
|
|
4909
|
+
for (let hops = 0; hops < MAX_LINKS5; hops++) {
|
|
4571
4910
|
const st = await fs.lstat(current).catch(() => void 0);
|
|
4572
4911
|
if (!st?.isSymbolicLink) return current;
|
|
4573
4912
|
const parent = current.slice(0, current.lastIndexOf("/")) || "/";
|
|
@@ -4674,6 +5013,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
4674
5013
|
readlink: (path) => fs.readlink(path),
|
|
4675
5014
|
...fs.realpath ? { realpath: fs.realpath.bind(fs) } : {},
|
|
4676
5015
|
...fs.chown ? { chown: fs.chown.bind(fs) } : {},
|
|
5016
|
+
...fs.ancestry ? { ancestry: fs.ancestry.bind(fs) } : {},
|
|
4677
5017
|
chmod: (path, mode) => fs.chmod(path, mode),
|
|
4678
5018
|
utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime),
|
|
4679
5019
|
lutimes: (path, atime, mtime) => lutimesOf(fs, path, atime, mtime),
|
|
@@ -4686,12 +5026,12 @@ var MAX_HOPS = 40;
|
|
|
4686
5026
|
function start(path) {
|
|
4687
5027
|
return { done: [], todo: path.split("/").filter((p) => p !== ""), hops: 0 };
|
|
4688
5028
|
}
|
|
4689
|
-
function next(
|
|
4690
|
-
while (
|
|
4691
|
-
const part =
|
|
5029
|
+
function next(walk2) {
|
|
5030
|
+
while (walk2.todo.length > 0) {
|
|
5031
|
+
const part = walk2.todo.shift();
|
|
4692
5032
|
if (part === ".") continue;
|
|
4693
5033
|
if (part === "..") {
|
|
4694
|
-
|
|
5034
|
+
walk2.done.pop();
|
|
4695
5035
|
continue;
|
|
4696
5036
|
}
|
|
4697
5037
|
return part;
|
|
@@ -4699,42 +5039,42 @@ function next(walk) {
|
|
|
4699
5039
|
return void 0;
|
|
4700
5040
|
}
|
|
4701
5041
|
function lexical(path) {
|
|
4702
|
-
const
|
|
4703
|
-
for (let part = next(
|
|
4704
|
-
return `/${
|
|
5042
|
+
const walk2 = start(path);
|
|
5043
|
+
for (let part = next(walk2); part !== void 0; part = next(walk2)) walk2.done.push(part);
|
|
5044
|
+
return `/${walk2.done.join("/")}`;
|
|
4705
5045
|
}
|
|
4706
|
-
function rest(
|
|
4707
|
-
return lexical(`/${[...
|
|
5046
|
+
function rest(walk2, part) {
|
|
5047
|
+
return lexical(`/${[...walk2.done, part, ...walk2.todo].join("/")}`);
|
|
4708
5048
|
}
|
|
4709
|
-
function follow(
|
|
4710
|
-
if (++
|
|
4711
|
-
if (target.startsWith("/"))
|
|
4712
|
-
|
|
5049
|
+
function follow(walk2, target) {
|
|
5050
|
+
if (++walk2.hops > MAX_HOPS) return false;
|
|
5051
|
+
if (target.startsWith("/")) walk2.done.length = 0;
|
|
5052
|
+
walk2.todo = [...target.split("/").filter((p) => p !== ""), ...walk2.todo];
|
|
4713
5053
|
return true;
|
|
4714
5054
|
}
|
|
4715
5055
|
async function physicalPathAsync(fs, path) {
|
|
4716
|
-
const
|
|
4717
|
-
for (let part = next(
|
|
4718
|
-
const at = `/${[...
|
|
5056
|
+
const walk2 = start(path);
|
|
5057
|
+
for (let part = next(walk2); part !== void 0; part = next(walk2)) {
|
|
5058
|
+
const at = `/${[...walk2.done, part].join("/")}`;
|
|
4719
5059
|
let target;
|
|
4720
5060
|
try {
|
|
4721
5061
|
target = (await fs.lstat(at)).isSymbolicLink ? await fs.readlink(at) : void 0;
|
|
4722
5062
|
} catch {
|
|
4723
|
-
return rest(
|
|
5063
|
+
return rest(walk2, part);
|
|
4724
5064
|
}
|
|
4725
|
-
if (target === void 0)
|
|
4726
|
-
else if (!follow(
|
|
5065
|
+
if (target === void 0) walk2.done.push(part);
|
|
5066
|
+
else if (!follow(walk2, target)) return lexical(path);
|
|
4727
5067
|
}
|
|
4728
|
-
return `/${
|
|
5068
|
+
return `/${walk2.done.join("/")}`;
|
|
4729
5069
|
}
|
|
4730
5070
|
|
|
4731
5071
|
// src/fs/watch.ts
|
|
4732
5072
|
var WATCH_BATCH_LIMIT = 1e3;
|
|
4733
5073
|
var under2 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
|
|
4734
|
-
var
|
|
5074
|
+
var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
4735
5075
|
function touches(watcher, changed) {
|
|
4736
5076
|
return watcher.paths.some(
|
|
4737
|
-
(watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) :
|
|
5077
|
+
(watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) : parentOf5(changed) === watched)
|
|
4738
5078
|
);
|
|
4739
5079
|
}
|
|
4740
5080
|
var FsWatchers = class {
|
|
@@ -4783,9 +5123,9 @@ var FsWatchers = class {
|
|
|
4783
5123
|
mkdir: async (path, options) => {
|
|
4784
5124
|
const created3 = [];
|
|
4785
5125
|
if (options?.recursive) {
|
|
4786
|
-
const
|
|
4787
|
-
for (let at = 1; at <
|
|
4788
|
-
const ancestor = `/${
|
|
5126
|
+
const parts2 = normalizePath(path).split("/").filter(Boolean);
|
|
5127
|
+
for (let at = 1; at < parts2.length; at++) {
|
|
5128
|
+
const ancestor = `/${parts2.slice(0, at).join("/")}`;
|
|
4789
5129
|
if (created3.length > 0 || !await fs.exists(ancestor)) created3.push(ancestor);
|
|
4790
5130
|
}
|
|
4791
5131
|
}
|
|
@@ -5014,62 +5354,6 @@ var ChildTable = class {
|
|
|
5014
5354
|
}
|
|
5015
5355
|
};
|
|
5016
5356
|
|
|
5017
|
-
// src/fs/walk.ts
|
|
5018
|
-
var MAX_LINKS4 = 40;
|
|
5019
|
-
function parentOf5(path) {
|
|
5020
|
-
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
5021
|
-
}
|
|
5022
|
-
async function mustBeDir(fs, path) {
|
|
5023
|
-
if (!fs.lstat) return;
|
|
5024
|
-
const st = await fs.lstat(path);
|
|
5025
|
-
if (st.isDirectory !== true) throw fsError("ENOTDIR", path);
|
|
5026
|
-
}
|
|
5027
|
-
async function linkOf(fs, path) {
|
|
5028
|
-
return fs.readlink?.(path).catch(() => void 0);
|
|
5029
|
-
}
|
|
5030
|
-
async function realDir(fs, dir, budget = { hops: 0 }) {
|
|
5031
|
-
const queue = dir.split("/").filter(Boolean);
|
|
5032
|
-
let at = "/";
|
|
5033
|
-
while (queue.length > 0) {
|
|
5034
|
-
const part = queue.shift();
|
|
5035
|
-
if (part === ".") continue;
|
|
5036
|
-
if (part === "..") {
|
|
5037
|
-
at = parentOf5(at);
|
|
5038
|
-
continue;
|
|
5039
|
-
}
|
|
5040
|
-
const next2 = at === "/" ? `/${part}` : `${at}/${part}`;
|
|
5041
|
-
const link2 = await linkOf(fs, next2);
|
|
5042
|
-
if (link2 === void 0) {
|
|
5043
|
-
if (queue.find((p) => p !== ".") === "..") await mustBeDir(fs, next2);
|
|
5044
|
-
at = next2;
|
|
5045
|
-
continue;
|
|
5046
|
-
}
|
|
5047
|
-
if (budget.hops >= MAX_LINKS4) throw fsError("ELOOP", dir);
|
|
5048
|
-
budget.hops++;
|
|
5049
|
-
queue.unshift(...link2.split("/").filter(Boolean));
|
|
5050
|
-
if (link2.startsWith("/")) at = "/";
|
|
5051
|
-
}
|
|
5052
|
-
return at;
|
|
5053
|
-
}
|
|
5054
|
-
function joinName(dir, name2) {
|
|
5055
|
-
return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
|
|
5056
|
-
}
|
|
5057
|
-
async function realTarget(fs, path) {
|
|
5058
|
-
const budget = { hops: 0 };
|
|
5059
|
-
let dir = await realDir(fs, parentOf5(path), budget);
|
|
5060
|
-
let name2 = path.slice(path.lastIndexOf("/") + 1);
|
|
5061
|
-
for (; ; ) {
|
|
5062
|
-
const link2 = await linkOf(fs, joinName(dir, name2));
|
|
5063
|
-
if (link2 === void 0) break;
|
|
5064
|
-
if (budget.hops >= MAX_LINKS4) throw fsError("ELOOP", path);
|
|
5065
|
-
budget.hops++;
|
|
5066
|
-
const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
|
|
5067
|
-
dir = await realDir(fs, parentOf5(target), budget);
|
|
5068
|
-
name2 = target.slice(target.lastIndexOf("/") + 1);
|
|
5069
|
-
}
|
|
5070
|
-
return joinName(dir, name2);
|
|
5071
|
-
}
|
|
5072
|
-
|
|
5073
5357
|
// src/mount/protocol.ts
|
|
5074
5358
|
var DRIVER_PROTOCOL = [1, 2];
|
|
5075
5359
|
function driverVersionError(theirs) {
|
|
@@ -5132,8 +5416,8 @@ var DriverConnection = class _DriverConnection {
|
|
|
5132
5416
|
} finally {
|
|
5133
5417
|
clearTimeout(timer);
|
|
5134
5418
|
}
|
|
5135
|
-
const
|
|
5136
|
-
if (
|
|
5419
|
+
const refused2 = driverVersionError(hello.protocol);
|
|
5420
|
+
if (refused2) throw errnoError("EPROTO", refused2);
|
|
5137
5421
|
if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
|
|
5138
5422
|
return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options.onFail);
|
|
5139
5423
|
}
|
|
@@ -5173,7 +5457,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
5173
5457
|
};
|
|
5174
5458
|
|
|
5175
5459
|
// src/mount/mount-fs.ts
|
|
5176
|
-
var
|
|
5460
|
+
var S_ISGID4 = 1024;
|
|
5177
5461
|
var ID = /^\d{1,10}$/;
|
|
5178
5462
|
var MAX_ID2 = 4294967294;
|
|
5179
5463
|
function idOption(value) {
|
|
@@ -5186,7 +5470,7 @@ function checkIds(options) {
|
|
|
5186
5470
|
if (!ID.test(value) || Number(value) > MAX_ID2) throw errnoError("EINVAL", `${key}=${value}`);
|
|
5187
5471
|
}
|
|
5188
5472
|
}
|
|
5189
|
-
var
|
|
5473
|
+
var MAX_LINKS6 = 40;
|
|
5190
5474
|
var STALE_RETRIES = 3;
|
|
5191
5475
|
var SETTLE_ROUNDS = 4;
|
|
5192
5476
|
var DEFAULT_IO = 8 * 1024 * 1024;
|
|
@@ -5424,12 +5708,12 @@ var MountTable = class {
|
|
|
5424
5708
|
}
|
|
5425
5709
|
list() {
|
|
5426
5710
|
const mounted = [...this.mounts.values()].map(
|
|
5427
|
-
({ type, source, target, options, state, error, present:
|
|
5711
|
+
({ type, source, target, options, state, error, present: present3 }) => ({
|
|
5428
5712
|
type,
|
|
5429
5713
|
source,
|
|
5430
5714
|
target,
|
|
5431
5715
|
options: { ...options },
|
|
5432
|
-
state: state === "ok" &&
|
|
5716
|
+
state: state === "ok" && present3?.() === false ? "nomedium" : state,
|
|
5433
5717
|
...error ? { error } : {}
|
|
5434
5718
|
})
|
|
5435
5719
|
);
|
|
@@ -5704,7 +5988,7 @@ var MountFs = class {
|
|
|
5704
5988
|
throw err;
|
|
5705
5989
|
}
|
|
5706
5990
|
if (attr2.kind !== "symlink") return real;
|
|
5707
|
-
if (hops >=
|
|
5991
|
+
if (hops >= MAX_LINKS6) throw errnoError("ELOOP", path);
|
|
5708
5992
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
5709
5993
|
return this.follow(this.base.resolvePath(parentOf6(real), target), hops + 1, create);
|
|
5710
5994
|
}
|
|
@@ -5724,9 +6008,9 @@ var MountFs = class {
|
|
|
5724
6008
|
if (!owner || !mount.owners || !mount.caps.owners) return void 0;
|
|
5725
6009
|
const dir = parentOf6(real);
|
|
5726
6010
|
const above = statOf(await this.table.getattr(mount, dir, parentOf6(rel2)), dir, mount);
|
|
5727
|
-
const setgid = (above.mode &
|
|
6011
|
+
const setgid = (above.mode & S_ISGID4) !== 0;
|
|
5728
6012
|
let mode = owner.mode;
|
|
5729
|
-
if (directory && setgid) mode = (mode ?? 493) |
|
|
6013
|
+
if (directory && setgid) mode = (mode ?? 493) | S_ISGID4;
|
|
5730
6014
|
return {
|
|
5731
6015
|
uid: owner.uid,
|
|
5732
6016
|
gid: setgid ? above.gid : owner.gid,
|
|
@@ -6001,14 +6285,14 @@ var MountFs = class {
|
|
|
6001
6285
|
await mount.conn.call({ op: "mkdir", path: rel2, ...made2 });
|
|
6002
6286
|
return;
|
|
6003
6287
|
}
|
|
6004
|
-
const
|
|
6005
|
-
for (let n = 1; n <=
|
|
6006
|
-
const sub = `/${
|
|
6288
|
+
const parts2 = rel2.split("/").filter(Boolean);
|
|
6289
|
+
for (let n = 1; n <= parts2.length; n++) {
|
|
6290
|
+
const sub = `/${parts2.slice(0, n).join("/")}`;
|
|
6007
6291
|
const full = normalizePath(`${mount.target}${sub}`);
|
|
6008
6292
|
const exists = await this.table.getattr(mount, full, sub).catch(() => void 0);
|
|
6009
6293
|
if (exists?.kind === "directory") continue;
|
|
6010
6294
|
if (exists) throw errnoError("ENOTDIR", full);
|
|
6011
|
-
const own = n ===
|
|
6295
|
+
const own = n === parts2.length ? owner : owner && { ...owner, mode: owner.parents };
|
|
6012
6296
|
const made2 = await this.driverOwner({ mount, rel: sub }, full, own, true);
|
|
6013
6297
|
this.table.forget(mount, full);
|
|
6014
6298
|
await mount.conn.call({ op: "mkdir", path: sub, ...made2 });
|
|
@@ -6101,6 +6385,20 @@ var MountFs = class {
|
|
|
6101
6385
|
if (this.at(real)) throw errnoError("ENOSYS", path);
|
|
6102
6386
|
return real;
|
|
6103
6387
|
}
|
|
6388
|
+
async ancestry(path) {
|
|
6389
|
+
const names = chain(normalizePath(path));
|
|
6390
|
+
const first = names.findIndex((name2) => this.at(name2));
|
|
6391
|
+
if (first < 0) return ancestryOf(this.base, path);
|
|
6392
|
+
const out = first === 0 ? [] : await ancestryOf(this.base, names[first - 1]);
|
|
6393
|
+
if (out.length < first || out.some((perm) => perm?.link !== void 0)) return out;
|
|
6394
|
+
for (const name2 of names.slice(first)) {
|
|
6395
|
+
const st = await this.lstat(name2).catch(() => void 0);
|
|
6396
|
+
if (!st) break;
|
|
6397
|
+
out.push(permOf(st, st.isSymbolicLink ? await this.readlink(name2) : void 0));
|
|
6398
|
+
if (st.isSymbolicLink) break;
|
|
6399
|
+
}
|
|
6400
|
+
return out;
|
|
6401
|
+
}
|
|
6104
6402
|
async readlink(path) {
|
|
6105
6403
|
const found = this.at(path);
|
|
6106
6404
|
if (!found) return this.base.readlink(path);
|
|
@@ -6311,9 +6609,11 @@ async function run(entry, path, req) {
|
|
|
6311
6609
|
return done;
|
|
6312
6610
|
case "readlink":
|
|
6313
6611
|
return json2(await fs.readlink(path));
|
|
6314
|
-
case "chmod":
|
|
6315
|
-
|
|
6612
|
+
case "chmod": {
|
|
6613
|
+
const g = guardOf(entry);
|
|
6614
|
+
await fs.chmod(path, g ? await chmodMode(g, path, req.mode ?? 0) : req.mode ?? 0);
|
|
6316
6615
|
return done;
|
|
6616
|
+
}
|
|
6317
6617
|
case "utimes":
|
|
6318
6618
|
await fs.utimes(path, new Date(req.atimeMs ?? 0), new Date(req.mtimeMs ?? 0));
|
|
6319
6619
|
return done;
|
|
@@ -6331,6 +6631,53 @@ async function run(entry, path, req) {
|
|
|
6331
6631
|
return { ok: false, errno: "EINVAL", message: `sync-fs: unknown op '${req.op}'` };
|
|
6332
6632
|
}
|
|
6333
6633
|
}
|
|
6634
|
+
function guardOf(entry) {
|
|
6635
|
+
const cred = entry.dac && entry.cred?.();
|
|
6636
|
+
if (!cred || !entry.dac) return void 0;
|
|
6637
|
+
return { fs: entry.fs, cred, pid: entry.pid ?? 0, dac: entry.dac };
|
|
6638
|
+
}
|
|
6639
|
+
var FOLLOWING = /* @__PURE__ */ new Set(["exists", "stat", "chown", "statfs"]);
|
|
6640
|
+
var NOT_FOLLOWING = /* @__PURE__ */ new Set(["lstat", "readlink", "lchown"]);
|
|
6641
|
+
async function guard(g, path, req, cwd) {
|
|
6642
|
+
const { op } = req;
|
|
6643
|
+
if (FOLLOWING.has(op) || NOT_FOLLOWING.has(op)) return searchable(g, op, path, FOLLOWING.has(op));
|
|
6644
|
+
const other = () => g.fs.resolvePath(cwd, req.arg2 ?? "");
|
|
6645
|
+
switch (op) {
|
|
6646
|
+
case "readdir":
|
|
6647
|
+
case "readdir-stat":
|
|
6648
|
+
return listable(g, op, path);
|
|
6649
|
+
case "mkdir":
|
|
6650
|
+
return makeIn(g, op, path);
|
|
6651
|
+
case "create":
|
|
6652
|
+
case "symlink":
|
|
6653
|
+
return inParent(g, op, path);
|
|
6654
|
+
case "unlink":
|
|
6655
|
+
case "rmdir":
|
|
6656
|
+
return inParent(g, op, path, true);
|
|
6657
|
+
case "rm":
|
|
6658
|
+
return removable(g, op, path);
|
|
6659
|
+
case "rename":
|
|
6660
|
+
return renamable(g, path, other());
|
|
6661
|
+
case "link":
|
|
6662
|
+
return linkable(g, path, other(), req.mode === 1);
|
|
6663
|
+
case "utimes":
|
|
6664
|
+
case "lutimes":
|
|
6665
|
+
return retimable(g, op, path);
|
|
6666
|
+
default:
|
|
6667
|
+
return void 0;
|
|
6668
|
+
}
|
|
6669
|
+
}
|
|
6670
|
+
async function guarded(entry, path, req) {
|
|
6671
|
+
const g = guardOf(entry);
|
|
6672
|
+
if (!g) return void 0;
|
|
6673
|
+
try {
|
|
6674
|
+
await guard(g, path, req, entry.cwd);
|
|
6675
|
+
return void 0;
|
|
6676
|
+
} catch (err) {
|
|
6677
|
+
const refusal = err.code === "EACCES";
|
|
6678
|
+
return req.op === "exists" && refusal ? json2(false) : toErrno(err);
|
|
6679
|
+
}
|
|
6680
|
+
}
|
|
6334
6681
|
async function createExclusive(fs, path, owner) {
|
|
6335
6682
|
if (fs.createExclusive) return fs.createExclusive(path, owner);
|
|
6336
6683
|
if (await fs.lstat(path).then(
|
|
@@ -6383,6 +6730,16 @@ async function owned(entry, own, req, path, target) {
|
|
|
6383
6730
|
}
|
|
6384
6731
|
return own(target, () => run(entry, path, req));
|
|
6385
6732
|
}
|
|
6733
|
+
async function holdOp(entry, path, req) {
|
|
6734
|
+
const mode = req.mode ?? 0;
|
|
6735
|
+
const [on, open] = [(mode & 1) === 1, (mode & 2) === 2];
|
|
6736
|
+
if (on && open) {
|
|
6737
|
+
const real = await entry.fs.realpath?.(path, true).catch(() => path);
|
|
6738
|
+
return json2(entry.hold?.(path, on, open, real) ?? null);
|
|
6739
|
+
}
|
|
6740
|
+
entry.hold?.(path, on, open, void 0, req.handle);
|
|
6741
|
+
return done;
|
|
6742
|
+
}
|
|
6386
6743
|
async function dispatchSyncFs(req) {
|
|
6387
6744
|
const entry = resolveSyncFsToken(req.token);
|
|
6388
6745
|
if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
|
|
@@ -6391,16 +6748,10 @@ async function dispatchSyncFs(req) {
|
|
|
6391
6748
|
const path = fs.resolvePath(cwd, req.path);
|
|
6392
6749
|
if (req.op === "kept") return json2(entry.keptFor?.(req.handle ?? -1) !== void 0);
|
|
6393
6750
|
if (req.op === "stat" && req.handle !== void 0) return await keptStat(entry, req);
|
|
6751
|
+
if (req.op === "hold") return holdOp(entry, path, req);
|
|
6752
|
+
const refused2 = await guarded(entry, path, req);
|
|
6753
|
+
if (refused2) return refused2;
|
|
6394
6754
|
if (req.op === "statfs") return json2(await entry.statfs?.(path) ?? await storageUsage());
|
|
6395
|
-
if (req.op === "hold") {
|
|
6396
|
-
const [on, open] = [((req.mode ?? 0) & 1) === 1, ((req.mode ?? 0) & 2) === 2];
|
|
6397
|
-
if (on && open) {
|
|
6398
|
-
const real = await fs.realpath?.(path, true).catch(() => path);
|
|
6399
|
-
return json2(entry.hold?.(path, on, open, real) ?? null);
|
|
6400
|
-
}
|
|
6401
|
-
entry.hold?.(path, on, open, void 0, req.handle);
|
|
6402
|
-
return done;
|
|
6403
|
-
}
|
|
6404
6755
|
if (entry.revoked?.(path)) return { ok: false, errno: "EIO", message: `${path} was unmounted` };
|
|
6405
6756
|
if (req.op === "write" && req.mode === 1 || UNLINKED_OPS.has(req.op)) {
|
|
6406
6757
|
const serve = async () => run(entry, await keptOr(entry, path, req.handle), req);
|
|
@@ -6601,9 +6952,9 @@ function mountTable(mounts = []) {
|
|
|
6601
6952
|
for (const m of mounts) {
|
|
6602
6953
|
const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
|
|
6603
6954
|
if (m.state !== "ok") options.push(m.state);
|
|
6604
|
-
const
|
|
6955
|
+
const access2 = options.includes("ro") ? [] : ["rw"];
|
|
6605
6956
|
lines2.push(
|
|
6606
|
-
`${field2(m.source)} ${field2(m.target)} ${m.type} ${[...
|
|
6957
|
+
`${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
|
|
6607
6958
|
);
|
|
6608
6959
|
}
|
|
6609
6960
|
return `${lines2.join("\n")}
|
|
@@ -7039,8 +7390,8 @@ var ProcTree = class {
|
|
|
7039
7390
|
alive(pid) {
|
|
7040
7391
|
return this.listing().processes.some((p) => p.pid === pid);
|
|
7041
7392
|
}
|
|
7042
|
-
node(
|
|
7043
|
-
const [top, leaf, ...rest2] =
|
|
7393
|
+
node(parts2) {
|
|
7394
|
+
const [top, leaf, ...rest2] = parts2;
|
|
7044
7395
|
if (top === void 0) {
|
|
7045
7396
|
return {
|
|
7046
7397
|
kind: "dir",
|
|
@@ -7078,9 +7429,9 @@ var ProcTree = class {
|
|
|
7078
7429
|
};
|
|
7079
7430
|
}
|
|
7080
7431
|
parts(path, follow2) {
|
|
7081
|
-
const
|
|
7082
|
-
if (
|
|
7083
|
-
return
|
|
7432
|
+
const parts2 = path.split("/").filter((p) => p !== "").slice(1);
|
|
7433
|
+
if (parts2[0] === "self" && (follow2 || parts2.length > 1)) parts2[0] = String(this.pid);
|
|
7434
|
+
return parts2;
|
|
7084
7435
|
}
|
|
7085
7436
|
find(path, follow2) {
|
|
7086
7437
|
const node = this.node(this.parts(path, follow2));
|
|
@@ -7166,8 +7517,8 @@ function refusalOf(found) {
|
|
|
7166
7517
|
return found === false ? { refused: "ENOTDIR" } : found;
|
|
7167
7518
|
}
|
|
7168
7519
|
function throughLink(target, done4, todo) {
|
|
7169
|
-
const
|
|
7170
|
-
return [target.startsWith("/") ? [] : done4, [...
|
|
7520
|
+
const parts2 = target.split("/").filter((part) => part !== "");
|
|
7521
|
+
return [target.startsWith("/") ? [] : done4, [...parts2, ...todo]];
|
|
7171
7522
|
}
|
|
7172
7523
|
async function procAlias(fs, path, follow2) {
|
|
7173
7524
|
const { readlink, lstat } = fs;
|
|
@@ -7177,14 +7528,14 @@ async function procAlias(fs, path, follow2) {
|
|
|
7177
7528
|
() => false
|
|
7178
7529
|
);
|
|
7179
7530
|
if (!physical) return void 0;
|
|
7180
|
-
const
|
|
7181
|
-
let step =
|
|
7531
|
+
const walk2 = procWalk(path, follow2);
|
|
7532
|
+
let step = walk2.next();
|
|
7182
7533
|
while (!step.done) {
|
|
7183
7534
|
const { at, check } = step.value;
|
|
7184
7535
|
const link2 = await readlink.call(fs, at).catch(() => void 0);
|
|
7185
7536
|
const isDir = (st) => st.isDirectory === true;
|
|
7186
7537
|
const target = link2 ?? (check ? await lstat.call(fs, at).then(isDir, failed) : true);
|
|
7187
|
-
step =
|
|
7538
|
+
step = walk2.next(target);
|
|
7188
7539
|
}
|
|
7189
7540
|
return step.value;
|
|
7190
7541
|
}
|
|
@@ -7778,8 +8129,8 @@ var RealmProxy = class {
|
|
|
7778
8129
|
}
|
|
7779
8130
|
async connect(conn, incoming, req, idle) {
|
|
7780
8131
|
const target = tunnelTarget(req);
|
|
7781
|
-
const
|
|
7782
|
-
if (
|
|
8132
|
+
const refused2 = target.port === 443 ? await this.tunnelReady(target) : void 0;
|
|
8133
|
+
if (refused2) throw refused2;
|
|
7783
8134
|
await conn.write(latin1Bytes("HTTP/1.1 200 Connection Established\r\n\r\n"), this.stop.signal);
|
|
7784
8135
|
try {
|
|
7785
8136
|
const first = await incoming.peek(this.stop.signal);
|
|
@@ -7922,10 +8273,10 @@ var RealmProxy = class {
|
|
|
7922
8273
|
};
|
|
7923
8274
|
|
|
7924
8275
|
// src/kernel/net/x509.ts
|
|
7925
|
-
function concat4(
|
|
7926
|
-
const out = new Uint8Array(
|
|
8276
|
+
function concat4(parts2) {
|
|
8277
|
+
const out = new Uint8Array(parts2.reduce((n, p) => n + p.length, 0));
|
|
7927
8278
|
let at = 0;
|
|
7928
|
-
for (const p of
|
|
8279
|
+
for (const p of parts2) {
|
|
7929
8280
|
out.set(p, at);
|
|
7930
8281
|
at += p.length;
|
|
7931
8282
|
}
|
|
@@ -7941,8 +8292,8 @@ function tlv(tag, ...contents2) {
|
|
|
7941
8292
|
const body = concat4(contents2);
|
|
7942
8293
|
return concat4([Uint8Array.of(tag), length(body.length), body]);
|
|
7943
8294
|
}
|
|
7944
|
-
var seq = (...
|
|
7945
|
-
var set = (...
|
|
8295
|
+
var seq = (...parts2) => tlv(48, ...parts2);
|
|
8296
|
+
var set = (...parts2) => tlv(49, ...parts2);
|
|
7946
8297
|
var octets = (bytes) => tlv(4, bytes);
|
|
7947
8298
|
var bits = (bytes, unused = 0) => tlv(3, Uint8Array.of(unused), bytes);
|
|
7948
8299
|
var TRUE = tlv(1, Uint8Array.of(255));
|
|
@@ -8030,8 +8381,8 @@ function randomSerial() {
|
|
|
8030
8381
|
function ipv4(host) {
|
|
8031
8382
|
const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
|
|
8032
8383
|
if (!m) return void 0;
|
|
8033
|
-
const
|
|
8034
|
-
return
|
|
8384
|
+
const parts2 = m.slice(1).map(Number);
|
|
8385
|
+
return parts2.every((p) => p <= 255) ? Uint8Array.from(parts2) : void 0;
|
|
8035
8386
|
}
|
|
8036
8387
|
function ipv6(host) {
|
|
8037
8388
|
if (!host.includes(":") || !/^[0-9A-Fa-f:.]+$/.test(host)) return void 0;
|
|
@@ -8860,9 +9211,9 @@ var HttpHandles = class {
|
|
|
8860
9211
|
// src/kernel/net/routes.ts
|
|
8861
9212
|
var HOST_ADDRESS = HOST_LOOPBACK_ADDRESS.join(".");
|
|
8862
9213
|
function ipv4Bytes(text) {
|
|
8863
|
-
const
|
|
8864
|
-
if (
|
|
8865
|
-
const bytes =
|
|
9214
|
+
const parts2 = text.split(".");
|
|
9215
|
+
if (parts2.length !== 4 || !parts2.every((p) => /^\d{1,3}$/.test(p))) return void 0;
|
|
9216
|
+
const bytes = parts2.map(Number);
|
|
8866
9217
|
return bytes.every((b) => b <= 255) ? bytes : void 0;
|
|
8867
9218
|
}
|
|
8868
9219
|
function ipv6Groups(text) {
|
|
@@ -8889,9 +9240,9 @@ function addressBits(host) {
|
|
|
8889
9240
|
return v6 ? { v6: true, bits: toBits(v6, 16) } : void 0;
|
|
8890
9241
|
}
|
|
8891
9242
|
function parsePrefix(text) {
|
|
8892
|
-
const
|
|
8893
|
-
if (
|
|
8894
|
-
const [address = "", size] =
|
|
9243
|
+
const parts2 = text.trim().split("/");
|
|
9244
|
+
if (parts2.length > 2) return void 0;
|
|
9245
|
+
const [address = "", size] = parts2;
|
|
8895
9246
|
const parsed2 = addressBits(address);
|
|
8896
9247
|
if (!parsed2) return void 0;
|
|
8897
9248
|
const max = parsed2.v6 ? 128 : 32;
|
|
@@ -9852,10 +10203,10 @@ var VfsNode = class {
|
|
|
9852
10203
|
this.serial(() => this.flush()).catch(() => void 0);
|
|
9853
10204
|
}, delay);
|
|
9854
10205
|
}
|
|
9855
|
-
ensure(
|
|
10206
|
+
ensure(need2) {
|
|
9856
10207
|
const cur = this.data ?? new Uint8Array(0);
|
|
9857
|
-
if (cur.length >=
|
|
9858
|
-
const grown = new Uint8Array(Math.max(
|
|
10208
|
+
if (cur.length >= need2) return cur;
|
|
10209
|
+
const grown = new Uint8Array(Math.max(need2, cur.length * 2, 256));
|
|
9859
10210
|
grown.set(cur.subarray(0, this.length));
|
|
9860
10211
|
this.data = grown;
|
|
9861
10212
|
return grown;
|
|
@@ -9898,14 +10249,14 @@ var VfsNodes = class {
|
|
|
9898
10249
|
return run2;
|
|
9899
10250
|
}
|
|
9900
10251
|
async entryKey(path) {
|
|
9901
|
-
const parent =
|
|
10252
|
+
const parent = parentOf(path);
|
|
9902
10253
|
const dir = await this.real(parent, true, () => realDir(this.fs, parent));
|
|
9903
10254
|
return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
|
|
9904
10255
|
}
|
|
9905
10256
|
targetKey(path) {
|
|
9906
10257
|
return this.real(path, true, () => realTarget(this.fs, path));
|
|
9907
10258
|
}
|
|
9908
|
-
async real(path, follow2,
|
|
10259
|
+
async real(path, follow2, walk2) {
|
|
9909
10260
|
if (this.fs.realpath) {
|
|
9910
10261
|
try {
|
|
9911
10262
|
return await this.fs.realpath(path, follow2);
|
|
@@ -9913,13 +10264,13 @@ var VfsNodes = class {
|
|
|
9913
10264
|
if (errCode(err) === "ELOOP") throw err;
|
|
9914
10265
|
}
|
|
9915
10266
|
}
|
|
9916
|
-
return
|
|
10267
|
+
return walk2();
|
|
9917
10268
|
}
|
|
9918
10269
|
createExclusive(path, owner) {
|
|
9919
10270
|
return this.onEntry(path, async () => {
|
|
9920
10271
|
const node = this.byPath.get(path);
|
|
9921
10272
|
const held = node ? await node.serial(async () => node.holds()) : false;
|
|
9922
|
-
if (held || await
|
|
10273
|
+
if (held || await present2(this.fs, path)) throw fsError("EEXIST", path);
|
|
9923
10274
|
await this.fs.writeFile(path, new Uint8Array(0), owner);
|
|
9924
10275
|
});
|
|
9925
10276
|
}
|
|
@@ -9979,13 +10330,13 @@ var VfsNodes = class {
|
|
|
9979
10330
|
if (id2 !== void 0) this.know(node, id2);
|
|
9980
10331
|
return node;
|
|
9981
10332
|
}
|
|
9982
|
-
open(path, pin,
|
|
10333
|
+
open(path, pin, writable2 = false, id2) {
|
|
9983
10334
|
const node = this.named(path, id2) ?? this.create(path, id2, new VfsNode(this.fs, path, void 0, false, false, pin));
|
|
9984
10335
|
node.opens++;
|
|
9985
|
-
if (
|
|
10336
|
+
if (writable2) node.writers++;
|
|
9986
10337
|
return node;
|
|
9987
10338
|
}
|
|
9988
|
-
adopt(path, contents2, dirty,
|
|
10339
|
+
adopt(path, contents2, dirty, writable2 = false, id2) {
|
|
9989
10340
|
let node = this.named(path, id2);
|
|
9990
10341
|
if (!node) {
|
|
9991
10342
|
node = this.create(path, id2, new VfsNode(this.fs, path, contents2, false, dirty));
|
|
@@ -9993,7 +10344,7 @@ var VfsNodes = class {
|
|
|
9993
10344
|
node.replace(contents2);
|
|
9994
10345
|
}
|
|
9995
10346
|
node.opens++;
|
|
9996
|
-
if (
|
|
10347
|
+
if (writable2) node.writers++;
|
|
9997
10348
|
return node;
|
|
9998
10349
|
}
|
|
9999
10350
|
holds(prefix) {
|
|
@@ -10005,11 +10356,11 @@ var VfsNodes = class {
|
|
|
10005
10356
|
if (node.writers > 0 && within5(path, prefix)) return true;
|
|
10006
10357
|
return false;
|
|
10007
10358
|
}
|
|
10008
|
-
closed(node,
|
|
10359
|
+
closed(node, writable2 = false) {
|
|
10009
10360
|
node.opens--;
|
|
10010
|
-
if (
|
|
10361
|
+
if (writable2) node.writers--;
|
|
10011
10362
|
if (node.opens > 0) {
|
|
10012
|
-
if (
|
|
10363
|
+
if (writable2 && node.writers === 0) this.onClosed?.(node.path);
|
|
10013
10364
|
return;
|
|
10014
10365
|
}
|
|
10015
10366
|
this.forget(node);
|
|
@@ -10086,7 +10437,7 @@ var VfsNodes = class {
|
|
|
10086
10437
|
}
|
|
10087
10438
|
}
|
|
10088
10439
|
};
|
|
10089
|
-
async function
|
|
10440
|
+
async function present2(fs, path, follow2 = false) {
|
|
10090
10441
|
const look = (follow2 ? void 0 : fs.lstat) ?? fs.stat;
|
|
10091
10442
|
try {
|
|
10092
10443
|
await (look ? look.call(fs, path) : fs.readFileBuffer(path));
|
|
@@ -10100,7 +10451,7 @@ function errCode(err) {
|
|
|
10100
10451
|
return err?.code;
|
|
10101
10452
|
}
|
|
10102
10453
|
async function targetDir(fs, path) {
|
|
10103
|
-
return
|
|
10454
|
+
return parentOf(await realTarget(fs, path).catch(() => path));
|
|
10104
10455
|
}
|
|
10105
10456
|
async function refuseReadonly(fs, path, flags, opts = {}) {
|
|
10106
10457
|
const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
|
|
@@ -10150,10 +10501,10 @@ function keepingOpen(fs, nodes) {
|
|
|
10150
10501
|
};
|
|
10151
10502
|
}
|
|
10152
10503
|
function vfsFile(fs, opts, nodes) {
|
|
10153
|
-
const
|
|
10154
|
-
const
|
|
10155
|
-
const
|
|
10156
|
-
const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true,
|
|
10504
|
+
const access2 = opts.flags & O_ACCMODE;
|
|
10505
|
+
const readable2 = access2 !== O_WRONLY;
|
|
10506
|
+
const writable2 = access2 === O_WRONLY || access2 === O_RDWR;
|
|
10507
|
+
const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true, writable2, opts.id) : nodes.open(opts.path, opts.pin, writable2, opts.id);
|
|
10157
10508
|
let openError;
|
|
10158
10509
|
const atOpen = (op) => {
|
|
10159
10510
|
void node.serial(op).catch((err) => {
|
|
@@ -10174,12 +10525,12 @@ function vfsFile(fs, opts, nodes) {
|
|
|
10174
10525
|
return op();
|
|
10175
10526
|
});
|
|
10176
10527
|
return new OpenFile({
|
|
10177
|
-
read:
|
|
10528
|
+
read: readable2 ? (max) => serial(async () => {
|
|
10178
10529
|
const out = await node.pread(max, offset);
|
|
10179
10530
|
offset += out.length;
|
|
10180
10531
|
return out;
|
|
10181
10532
|
}) : void 0,
|
|
10182
|
-
write:
|
|
10533
|
+
write: writable2 ? (bytes) => serial(async () => {
|
|
10183
10534
|
if (opts.flags & O_APPEND) offset = await node.size();
|
|
10184
10535
|
offset += await node.pwrite(bytes, offset);
|
|
10185
10536
|
return bytes.length;
|
|
@@ -10194,15 +10545,15 @@ function vfsFile(fs, opts, nodes) {
|
|
|
10194
10545
|
return offset;
|
|
10195
10546
|
}),
|
|
10196
10547
|
pread: (max, at) => serial(() => {
|
|
10197
|
-
if (!
|
|
10548
|
+
if (!readable2) throw new KernelError("EBADF");
|
|
10198
10549
|
return node.pread(max, at);
|
|
10199
10550
|
}),
|
|
10200
10551
|
pwrite: (bytes, at) => serial(() => {
|
|
10201
|
-
if (!
|
|
10552
|
+
if (!writable2) throw new KernelError("EBADF");
|
|
10202
10553
|
return node.pwrite(bytes, at);
|
|
10203
10554
|
}),
|
|
10204
10555
|
resize: (size) => serial(() => {
|
|
10205
|
-
if (!
|
|
10556
|
+
if (!writable2) throw new KernelError("EBADF");
|
|
10206
10557
|
return node.truncate(size);
|
|
10207
10558
|
}),
|
|
10208
10559
|
stat: () => serial(async () => ({
|
|
@@ -10216,7 +10567,7 @@ function vfsFile(fs, opts, nodes) {
|
|
|
10216
10567
|
takeOpenError();
|
|
10217
10568
|
await node.flush();
|
|
10218
10569
|
} finally {
|
|
10219
|
-
nodes?.closed(node,
|
|
10570
|
+
nodes?.closed(node, writable2);
|
|
10220
10571
|
}
|
|
10221
10572
|
})
|
|
10222
10573
|
});
|
|
@@ -10587,7 +10938,7 @@ var WasmProcess = class {
|
|
|
10587
10938
|
this.fds.installAt(fd, this.openVfsFile(open));
|
|
10588
10939
|
} else if (req.create) {
|
|
10589
10940
|
await nodes.onEntry(path, async () => {
|
|
10590
|
-
const fresh = this.options.owners && !await
|
|
10941
|
+
const fresh = this.options.owners && !await present2(this.options.fs, path, true);
|
|
10591
10942
|
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
10592
10943
|
await this.fds.get(fd).file.stat?.();
|
|
10593
10944
|
if (fresh) await this.options.fs.chown?.(path, { ...this.newOwner(), kind: "new" });
|
|
@@ -10595,7 +10946,7 @@ var WasmProcess = class {
|
|
|
10595
10946
|
} else {
|
|
10596
10947
|
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
10597
10948
|
}
|
|
10598
|
-
if (req.existing && !await
|
|
10949
|
+
if (req.existing && !await present2(this.options.fs, path, true)) {
|
|
10599
10950
|
throw new KernelError("ENOENT");
|
|
10600
10951
|
}
|
|
10601
10952
|
} catch (err) {
|
|
@@ -10615,14 +10966,14 @@ var WasmProcess = class {
|
|
|
10615
10966
|
if (req.flags & WRITE_ACCESS || req.create || req.truncate || req.exclusive) {
|
|
10616
10967
|
throw new KernelError("EROFS");
|
|
10617
10968
|
}
|
|
10618
|
-
const
|
|
10969
|
+
const tree2 = new ProcTree(
|
|
10619
10970
|
{
|
|
10620
10971
|
procList: () => this.options.processes?.() ?? { boot: 0, processes: [] },
|
|
10621
10972
|
mountList: () => this.options.mounts?.() ?? []
|
|
10622
10973
|
},
|
|
10623
10974
|
(await this.options.identity?.())?.pid ?? this.pid
|
|
10624
10975
|
);
|
|
10625
|
-
return memoryFile(
|
|
10976
|
+
return memoryFile(tree2.read(path), path, req.position);
|
|
10626
10977
|
}
|
|
10627
10978
|
openVfsFile(req, id2) {
|
|
10628
10979
|
return vfsFile(
|
|
@@ -10774,7 +11125,7 @@ var WasmProcess = class {
|
|
|
10774
11125
|
const held = handle === void 0 ? void 0 : this.keptHandle?.(handle);
|
|
10775
11126
|
if (held !== void 0) return held;
|
|
10776
11127
|
const kept = this.keptPath?.(path);
|
|
10777
|
-
return kept !== void 0 && !await
|
|
11128
|
+
return kept !== void 0 && !await present2(this.options.fs, path, true) ? kept : path;
|
|
10778
11129
|
}
|
|
10779
11130
|
async promote(req) {
|
|
10780
11131
|
if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
|
|
@@ -11284,6 +11635,7 @@ function inherited(opts) {
|
|
|
11284
11635
|
function holdingToken(opts, holds, caller) {
|
|
11285
11636
|
return mintSyncFsToken({
|
|
11286
11637
|
...opts.owners ? caller : {},
|
|
11638
|
+
...opts.dac ? { dac: opts.dac, pid: opts.pid } : {},
|
|
11287
11639
|
fs: opts.fs,
|
|
11288
11640
|
cwd: opts.cwd,
|
|
11289
11641
|
...opts.statfs ? { statfs: opts.statfs } : {},
|
|
@@ -11913,9 +12265,9 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
|
11913
12265
|
let open = false;
|
|
11914
12266
|
const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
|
|
11915
12267
|
async function hello(message3) {
|
|
11916
|
-
const
|
|
11917
|
-
if (
|
|
11918
|
-
reply({ hello: { protocol: DRIVER_PROTOCOL, error:
|
|
12268
|
+
const refused2 = driverVersionError(message3.protocol);
|
|
12269
|
+
if (refused2) {
|
|
12270
|
+
reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused2, errno: "EPROTO" } });
|
|
11919
12271
|
return;
|
|
11920
12272
|
}
|
|
11921
12273
|
try {
|
|
@@ -12457,7 +12809,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
12457
12809
|
}, againMs);
|
|
12458
12810
|
timer.unref?.();
|
|
12459
12811
|
});
|
|
12460
|
-
async function
|
|
12812
|
+
async function writable2(client, path, flags) {
|
|
12461
12813
|
const { ifMatch: _, ...asked } = flags;
|
|
12462
12814
|
const opening = () => client.call("open", { path: rel(path), ...asked });
|
|
12463
12815
|
try {
|
|
@@ -12488,7 +12840,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
12488
12840
|
async open(path, flags) {
|
|
12489
12841
|
const client = current();
|
|
12490
12842
|
const token = client.token;
|
|
12491
|
-
const { opened: opened2, restore } = await
|
|
12843
|
+
const { opened: opened2, restore } = await writable2(client, path, flags);
|
|
12492
12844
|
if (flags.ifMatch !== void 0 && opened2.attr?.etag !== flags.ifMatch) {
|
|
12493
12845
|
await client.call("release", { fh: opened2.fh }, true);
|
|
12494
12846
|
throw fsError2("ESTALE", `${path} changed on the host while it was being read`);
|
|
@@ -12661,7 +13013,7 @@ var PORT_DEAD_MS = 1e4;
|
|
|
12661
13013
|
var PORT_LINGER_MS = 1e3;
|
|
12662
13014
|
function timed(transport, ms) {
|
|
12663
13015
|
const inflight = /* @__PURE__ */ new Set();
|
|
12664
|
-
const
|
|
13016
|
+
const guard2 = (name2, run2, late, expire) => new Promise((resolve, reject) => {
|
|
12665
13017
|
let expired = false;
|
|
12666
13018
|
const running = run2();
|
|
12667
13019
|
inflight.add(running);
|
|
@@ -12710,15 +13062,15 @@ function timed(transport, ms) {
|
|
|
12710
13062
|
},
|
|
12711
13063
|
retire: (why) => transport.retire?.(why),
|
|
12712
13064
|
...transport.cleanup ? { cleanup } : {},
|
|
12713
|
-
call: (op, body, handle) =>
|
|
13065
|
+
call: (op, body, handle) => guard2(
|
|
12714
13066
|
op,
|
|
12715
13067
|
() => transport.call(op, body, handle),
|
|
12716
13068
|
op === "open" ? release : void 0,
|
|
12717
13069
|
op === "release" ? (running) => void cleanup("release", body ?? {}, running).catch(() => {
|
|
12718
13070
|
}) : void 0
|
|
12719
13071
|
),
|
|
12720
|
-
read: (fh, offset, size, ifMatch) =>
|
|
12721
|
-
write: (fh, offset, bytes) =>
|
|
13072
|
+
read: (fh, offset, size, ifMatch) => guard2("read", () => transport.read(fh, offset, size, ifMatch)),
|
|
13073
|
+
write: (fh, offset, bytes) => guard2("write", () => transport.write(fh, offset, bytes)),
|
|
12722
13074
|
watch: (signal, onEvent, onOpen) => transport.watch(signal, onEvent, onOpen)
|
|
12723
13075
|
};
|
|
12724
13076
|
}
|
|
@@ -13099,10 +13451,10 @@ var ChunkedBytes = class {
|
|
|
13099
13451
|
}
|
|
13100
13452
|
this.size = size;
|
|
13101
13453
|
}
|
|
13102
|
-
room(index,
|
|
13454
|
+
room(index, need2) {
|
|
13103
13455
|
const chunk2 = this.chunks[index];
|
|
13104
|
-
if (chunk2 && chunk2.length >=
|
|
13105
|
-
const grown = new Uint8Array(Math.min(CHUNK3, Math.max(
|
|
13456
|
+
if (chunk2 && chunk2.length >= need2) return chunk2;
|
|
13457
|
+
const grown = new Uint8Array(Math.min(CHUNK3, Math.max(need2, (chunk2?.length ?? 0) * 2)));
|
|
13106
13458
|
if (chunk2) grown.set(chunk2);
|
|
13107
13459
|
this.chunks[index] = grown;
|
|
13108
13460
|
return grown;
|
|
@@ -13149,14 +13501,14 @@ function tmpfs() {
|
|
|
13149
13501
|
const root = make("directory", 16877);
|
|
13150
13502
|
const handles = /* @__PURE__ */ new Map();
|
|
13151
13503
|
let nextFh = 0;
|
|
13152
|
-
const
|
|
13504
|
+
const parts2 = (path) => path.split("/").filter(Boolean);
|
|
13153
13505
|
const place = (dir, name2, node) => {
|
|
13154
13506
|
dir.children.set(name2, node);
|
|
13155
13507
|
dir.mtime = Date.now();
|
|
13156
13508
|
};
|
|
13157
13509
|
function find(path) {
|
|
13158
13510
|
let node = root;
|
|
13159
|
-
for (const name2 of
|
|
13511
|
+
for (const name2 of parts2(path)) {
|
|
13160
13512
|
if (node.kind !== "directory") throw fsError2("ENOTDIR", path);
|
|
13161
13513
|
const next2 = node.children.get(name2);
|
|
13162
13514
|
if (!next2) throw fsError2("ENOENT", path);
|
|
@@ -13165,7 +13517,7 @@ function tmpfs() {
|
|
|
13165
13517
|
return node;
|
|
13166
13518
|
}
|
|
13167
13519
|
function parent(path) {
|
|
13168
|
-
const names =
|
|
13520
|
+
const names = parts2(path);
|
|
13169
13521
|
const name2 = names.pop();
|
|
13170
13522
|
if (name2 === void 0) throw fsError2("EBUSY", "the root of a mount");
|
|
13171
13523
|
const dir = find(`/${names.join("/")}`);
|
|
@@ -13250,8 +13602,8 @@ function tmpfs() {
|
|
|
13250
13602
|
dir.mtime = Date.now();
|
|
13251
13603
|
},
|
|
13252
13604
|
async rename(from, to) {
|
|
13253
|
-
const a = `/${
|
|
13254
|
-
const b = `/${
|
|
13605
|
+
const a = `/${parts2(from).join("/")}`;
|
|
13606
|
+
const b = `/${parts2(to).join("/")}`;
|
|
13255
13607
|
if (b !== a && b.startsWith(`${a}/`)) throw fsError2("EINVAL", `${to} is inside ${from}`);
|
|
13256
13608
|
const source = parent(from);
|
|
13257
13609
|
const node = source.dir.children.get(source.name);
|
|
@@ -13610,11 +13962,13 @@ var Launcher = class {
|
|
|
13610
13962
|
booting = new AbortController();
|
|
13611
13963
|
fstab = Promise.resolve([]);
|
|
13612
13964
|
usersMode;
|
|
13965
|
+
dac;
|
|
13613
13966
|
nextPid = 1e3;
|
|
13614
13967
|
boot = Date.now();
|
|
13615
13968
|
terminals = 0;
|
|
13616
13969
|
constructor(options) {
|
|
13617
13970
|
this.usersMode = usersMode(options.users);
|
|
13971
|
+
this.dac = this.usersMode === "off" ? void 0 : new Dac(this.usersMode, options.onDenial);
|
|
13618
13972
|
if (this.owners) options.fs.trackOwners?.();
|
|
13619
13973
|
this.mounts = new MountTable({
|
|
13620
13974
|
open: (type, spec) => this.driver(type, spec),
|
|
@@ -13945,6 +14299,7 @@ var Launcher = class {
|
|
|
13945
14299
|
...req.umask !== void 0 ? { umask: req.umask } : {},
|
|
13946
14300
|
cred: req.cred,
|
|
13947
14301
|
...this.owners ? { owners: true } : {},
|
|
14302
|
+
...this.dac ? { dac: this.dac } : {},
|
|
13948
14303
|
program: req.program,
|
|
13949
14304
|
argv0: req.argv0,
|
|
13950
14305
|
args: req.args,
|
|
@@ -14908,7 +15263,7 @@ function dialStream(port, onClose) {
|
|
|
14908
15263
|
let closed = false;
|
|
14909
15264
|
let reader;
|
|
14910
15265
|
const acks = [];
|
|
14911
|
-
const
|
|
15266
|
+
const readable2 = new ReadableStream({
|
|
14912
15267
|
start(controller) {
|
|
14913
15268
|
reader = controller;
|
|
14914
15269
|
},
|
|
@@ -14949,7 +15304,7 @@ function dialStream(port, onClose) {
|
|
|
14949
15304
|
else finish(errorWithCode(data.error));
|
|
14950
15305
|
});
|
|
14951
15306
|
port.start?.();
|
|
14952
|
-
const
|
|
15307
|
+
const writable2 = new WritableStream({
|
|
14953
15308
|
write(chunk2) {
|
|
14954
15309
|
if (closed) throw errorWithCode("EPIPE: the connection is closed");
|
|
14955
15310
|
const copy = chunk2.slice();
|
|
@@ -14968,7 +15323,7 @@ function dialStream(port, onClose) {
|
|
|
14968
15323
|
shut();
|
|
14969
15324
|
finish(error);
|
|
14970
15325
|
};
|
|
14971
|
-
return { readable, writable, close, fail: fail3 };
|
|
15326
|
+
return { readable: readable2, writable: writable2, close, fail: fail3 };
|
|
14972
15327
|
}
|
|
14973
15328
|
|
|
14974
15329
|
// src/client/loopback-fetch.ts
|
|
@@ -15452,9 +15807,9 @@ async function greet(channel, lock, timeoutMs) {
|
|
|
15452
15807
|
} finally {
|
|
15453
15808
|
clearTimeout(timer);
|
|
15454
15809
|
}
|
|
15455
|
-
const
|
|
15456
|
-
if (
|
|
15457
|
-
const error = new Error(
|
|
15810
|
+
const refused2 = hello.error ?? versionError(hello.protocol);
|
|
15811
|
+
if (refused2) {
|
|
15812
|
+
const error = new Error(refused2);
|
|
15458
15813
|
channel.end(error);
|
|
15459
15814
|
throw error;
|
|
15460
15815
|
}
|
|
@@ -15604,10 +15959,11 @@ async function createNodeKernel(options = {}) {
|
|
|
15604
15959
|
...hostfsFetchOf(options),
|
|
15605
15960
|
...options.cdp ? { cdp: options.cdp } : {},
|
|
15606
15961
|
...options.hostname !== void 0 ? { hostname: options.hostname } : {},
|
|
15607
|
-
...options.users !== void 0 ? { users: options.users } : {}
|
|
15962
|
+
...options.users !== void 0 ? { users: options.users } : {},
|
|
15963
|
+
...options.onDenial ? { onDenial: options.onDenial } : {}
|
|
15608
15964
|
});
|
|
15609
15965
|
await launcher.prepare();
|
|
15610
|
-
const
|
|
15966
|
+
const guard2 = (work) => new Promise((resolve, reject) => {
|
|
15611
15967
|
if (terminated) return reject(new Error(TERMINATED));
|
|
15612
15968
|
pending.add(reject);
|
|
15613
15969
|
work().then(resolve, reject).finally(() => pending.delete(reject));
|
|
@@ -15619,7 +15975,7 @@ async function createNodeKernel(options = {}) {
|
|
|
15619
15975
|
const { stdin, onStdout, onStderr, ...rest2 } = opts;
|
|
15620
15976
|
const onOut = textOf(onStdout);
|
|
15621
15977
|
const onErr = textOf(onStderr);
|
|
15622
|
-
const result = await
|
|
15978
|
+
const result = await guard2(
|
|
15623
15979
|
() => launcher.run(argv, {
|
|
15624
15980
|
...rest2,
|
|
15625
15981
|
...stdin !== void 0 ? { stdin: typeof stdin === "string" ? new TextEncoder().encode(stdin) : stdin } : {},
|
|
@@ -15637,7 +15993,7 @@ async function createNodeKernel(options = {}) {
|
|
|
15637
15993
|
const { onData, ...rest2 } = opts;
|
|
15638
15994
|
let listener = onData ?? null;
|
|
15639
15995
|
const backlog = [];
|
|
15640
|
-
const session = await
|
|
15996
|
+
const session = await guard2(
|
|
15641
15997
|
() => launcher.openTerminal(argv, {
|
|
15642
15998
|
...rest2,
|
|
15643
15999
|
onData: (bytes) => listener ? listener(bytes) : backlog.push(bytes)
|
|
@@ -15645,7 +16001,7 @@ async function createNodeKernel(options = {}) {
|
|
|
15645
16001
|
);
|
|
15646
16002
|
return {
|
|
15647
16003
|
pid: session.pid,
|
|
15648
|
-
exited:
|
|
16004
|
+
exited: guard2(() => session.exited),
|
|
15649
16005
|
get onData() {
|
|
15650
16006
|
return listener;
|
|
15651
16007
|
},
|
|
@@ -15665,20 +16021,20 @@ async function createNodeKernel(options = {}) {
|
|
|
15665
16021
|
await launcher.fs.writeFile(path, data);
|
|
15666
16022
|
},
|
|
15667
16023
|
readFile: async (path) => launcher.fs.readFileBuffer(path),
|
|
15668
|
-
mount: (spec) =>
|
|
15669
|
-
umount: (target) =>
|
|
16024
|
+
mount: (spec) => guard2(() => launcher.mount(spec)),
|
|
16025
|
+
umount: (target) => guard2(async () => launcher.umount(target)),
|
|
15670
16026
|
mounts: async () => launcher.mounts.list(),
|
|
15671
|
-
setRoutes: (routes) =>
|
|
16027
|
+
setRoutes: (routes) => guard2(async () => launcher.setRoutes(routes)),
|
|
15672
16028
|
users: {
|
|
15673
16029
|
mode: launcher.usersMode,
|
|
15674
|
-
add: (user) =>
|
|
15675
|
-
remove: (name2, options2) =>
|
|
15676
|
-
list: () =>
|
|
16030
|
+
add: (user) => guard2(() => launcher.users.add(user)),
|
|
16031
|
+
remove: (name2, options2) => guard2(() => launcher.users.remove(name2, options2)),
|
|
16032
|
+
list: () => guard2(() => launcher.users.list())
|
|
15677
16033
|
},
|
|
15678
16034
|
groups: {
|
|
15679
|
-
list: () =>
|
|
15680
|
-
addMember: (group, user) =>
|
|
15681
|
-
removeMember: (group, user) =>
|
|
16035
|
+
list: () => guard2(() => launcher.users.groups()),
|
|
16036
|
+
addMember: (group, user) => guard2(() => launcher.users.addMember(group, user)),
|
|
16037
|
+
removeMember: (group, user) => guard2(() => launcher.users.removeMember(group, user))
|
|
15682
16038
|
},
|
|
15683
16039
|
async connect({ user } = {}) {
|
|
15684
16040
|
if (terminated) throw new Error(TERMINATED);
|