@ai-ecoverse/slicc-kernel 1.61.2 → 1.62.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -6
- package/dist/index.js +12 -9
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +691 -263
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +72 -19
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +786 -358
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +72 -19
- 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
|
});
|
|
@@ -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,24 +6730,56 @@ 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
|
+
}
|
|
6743
|
+
var IDENTITY_OPS = /* @__PURE__ */ new Set([
|
|
6744
|
+
"write",
|
|
6745
|
+
"pwrite",
|
|
6746
|
+
"truncate",
|
|
6747
|
+
"chmod",
|
|
6748
|
+
"utimes",
|
|
6749
|
+
"chown",
|
|
6750
|
+
"lchown"
|
|
6751
|
+
]);
|
|
6386
6752
|
async function dispatchSyncFs(req) {
|
|
6387
6753
|
const entry = resolveSyncFsToken(req.token);
|
|
6388
6754
|
if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
|
|
6755
|
+
if (!req.identity || !IDENTITY_OPS.has(req.op)) return dispatchOne(entry, req);
|
|
6756
|
+
const { fs, cwd } = entry;
|
|
6757
|
+
const path = fs.resolvePath(cwd, req.path);
|
|
6758
|
+
const identified = async (held) => {
|
|
6759
|
+
const own = held ? { ...entry, fs: { ...fs, writeFile: held.writeFile } } : entry;
|
|
6760
|
+
const result = await dispatchOne(own, req);
|
|
6761
|
+
if (!result.ok) return result;
|
|
6762
|
+
if (held && !held.direct) return json2(null);
|
|
6763
|
+
const look = req.op === "lchown" ? fs.lstat : fs.stat;
|
|
6764
|
+
const st = await look.call(fs, path).catch(() => void 0);
|
|
6765
|
+
return json2(st ? statJson(st) : null);
|
|
6766
|
+
};
|
|
6767
|
+
try {
|
|
6768
|
+
return await (fs.exclusive ? fs.exclusive(path, identified, req.op !== "lchown") : identified());
|
|
6769
|
+
} catch (err) {
|
|
6770
|
+
return toErrno(err);
|
|
6771
|
+
}
|
|
6772
|
+
}
|
|
6773
|
+
async function dispatchOne(entry, req) {
|
|
6389
6774
|
const { fs, cwd } = entry;
|
|
6390
6775
|
try {
|
|
6391
6776
|
const path = fs.resolvePath(cwd, req.path);
|
|
6392
6777
|
if (req.op === "kept") return json2(entry.keptFor?.(req.handle ?? -1) !== void 0);
|
|
6393
6778
|
if (req.op === "stat" && req.handle !== void 0) return await keptStat(entry, req);
|
|
6779
|
+
if (req.op === "hold") return holdOp(entry, path, req);
|
|
6780
|
+
const refused2 = await guarded(entry, path, req);
|
|
6781
|
+
if (refused2) return refused2;
|
|
6394
6782
|
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
6783
|
if (entry.revoked?.(path)) return { ok: false, errno: "EIO", message: `${path} was unmounted` };
|
|
6405
6784
|
if (req.op === "write" && req.mode === 1 || UNLINKED_OPS.has(req.op)) {
|
|
6406
6785
|
const serve = async () => run(entry, await keptOr(entry, path, req.handle), req);
|
|
@@ -6601,9 +6980,9 @@ function mountTable(mounts = []) {
|
|
|
6601
6980
|
for (const m of mounts) {
|
|
6602
6981
|
const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
|
|
6603
6982
|
if (m.state !== "ok") options.push(m.state);
|
|
6604
|
-
const
|
|
6983
|
+
const access2 = options.includes("ro") ? [] : ["rw"];
|
|
6605
6984
|
lines2.push(
|
|
6606
|
-
`${field2(m.source)} ${field2(m.target)} ${m.type} ${[...
|
|
6985
|
+
`${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
|
|
6607
6986
|
);
|
|
6608
6987
|
}
|
|
6609
6988
|
return `${lines2.join("\n")}
|
|
@@ -7039,8 +7418,8 @@ var ProcTree = class {
|
|
|
7039
7418
|
alive(pid) {
|
|
7040
7419
|
return this.listing().processes.some((p) => p.pid === pid);
|
|
7041
7420
|
}
|
|
7042
|
-
node(
|
|
7043
|
-
const [top, leaf, ...rest2] =
|
|
7421
|
+
node(parts2) {
|
|
7422
|
+
const [top, leaf, ...rest2] = parts2;
|
|
7044
7423
|
if (top === void 0) {
|
|
7045
7424
|
return {
|
|
7046
7425
|
kind: "dir",
|
|
@@ -7078,9 +7457,9 @@ var ProcTree = class {
|
|
|
7078
7457
|
};
|
|
7079
7458
|
}
|
|
7080
7459
|
parts(path, follow2) {
|
|
7081
|
-
const
|
|
7082
|
-
if (
|
|
7083
|
-
return
|
|
7460
|
+
const parts2 = path.split("/").filter((p) => p !== "").slice(1);
|
|
7461
|
+
if (parts2[0] === "self" && (follow2 || parts2.length > 1)) parts2[0] = String(this.pid);
|
|
7462
|
+
return parts2;
|
|
7084
7463
|
}
|
|
7085
7464
|
find(path, follow2) {
|
|
7086
7465
|
const node = this.node(this.parts(path, follow2));
|
|
@@ -7166,8 +7545,8 @@ function refusalOf(found) {
|
|
|
7166
7545
|
return found === false ? { refused: "ENOTDIR" } : found;
|
|
7167
7546
|
}
|
|
7168
7547
|
function throughLink(target, done4, todo) {
|
|
7169
|
-
const
|
|
7170
|
-
return [target.startsWith("/") ? [] : done4, [...
|
|
7548
|
+
const parts2 = target.split("/").filter((part) => part !== "");
|
|
7549
|
+
return [target.startsWith("/") ? [] : done4, [...parts2, ...todo]];
|
|
7171
7550
|
}
|
|
7172
7551
|
async function procAlias(fs, path, follow2) {
|
|
7173
7552
|
const { readlink, lstat } = fs;
|
|
@@ -7177,14 +7556,14 @@ async function procAlias(fs, path, follow2) {
|
|
|
7177
7556
|
() => false
|
|
7178
7557
|
);
|
|
7179
7558
|
if (!physical) return void 0;
|
|
7180
|
-
const
|
|
7181
|
-
let step =
|
|
7559
|
+
const walk2 = procWalk(path, follow2);
|
|
7560
|
+
let step = walk2.next();
|
|
7182
7561
|
while (!step.done) {
|
|
7183
7562
|
const { at, check } = step.value;
|
|
7184
7563
|
const link2 = await readlink.call(fs, at).catch(() => void 0);
|
|
7185
7564
|
const isDir = (st) => st.isDirectory === true;
|
|
7186
7565
|
const target = link2 ?? (check ? await lstat.call(fs, at).then(isDir, failed) : true);
|
|
7187
|
-
step =
|
|
7566
|
+
step = walk2.next(target);
|
|
7188
7567
|
}
|
|
7189
7568
|
return step.value;
|
|
7190
7569
|
}
|
|
@@ -7778,8 +8157,8 @@ var RealmProxy = class {
|
|
|
7778
8157
|
}
|
|
7779
8158
|
async connect(conn, incoming, req, idle) {
|
|
7780
8159
|
const target = tunnelTarget(req);
|
|
7781
|
-
const
|
|
7782
|
-
if (
|
|
8160
|
+
const refused2 = target.port === 443 ? await this.tunnelReady(target) : void 0;
|
|
8161
|
+
if (refused2) throw refused2;
|
|
7783
8162
|
await conn.write(latin1Bytes("HTTP/1.1 200 Connection Established\r\n\r\n"), this.stop.signal);
|
|
7784
8163
|
try {
|
|
7785
8164
|
const first = await incoming.peek(this.stop.signal);
|
|
@@ -7922,10 +8301,10 @@ var RealmProxy = class {
|
|
|
7922
8301
|
};
|
|
7923
8302
|
|
|
7924
8303
|
// src/kernel/net/x509.ts
|
|
7925
|
-
function concat4(
|
|
7926
|
-
const out = new Uint8Array(
|
|
8304
|
+
function concat4(parts2) {
|
|
8305
|
+
const out = new Uint8Array(parts2.reduce((n, p) => n + p.length, 0));
|
|
7927
8306
|
let at = 0;
|
|
7928
|
-
for (const p of
|
|
8307
|
+
for (const p of parts2) {
|
|
7929
8308
|
out.set(p, at);
|
|
7930
8309
|
at += p.length;
|
|
7931
8310
|
}
|
|
@@ -7941,8 +8320,8 @@ function tlv(tag, ...contents2) {
|
|
|
7941
8320
|
const body = concat4(contents2);
|
|
7942
8321
|
return concat4([Uint8Array.of(tag), length(body.length), body]);
|
|
7943
8322
|
}
|
|
7944
|
-
var seq = (...
|
|
7945
|
-
var set = (...
|
|
8323
|
+
var seq = (...parts2) => tlv(48, ...parts2);
|
|
8324
|
+
var set = (...parts2) => tlv(49, ...parts2);
|
|
7946
8325
|
var octets = (bytes) => tlv(4, bytes);
|
|
7947
8326
|
var bits = (bytes, unused = 0) => tlv(3, Uint8Array.of(unused), bytes);
|
|
7948
8327
|
var TRUE = tlv(1, Uint8Array.of(255));
|
|
@@ -8030,8 +8409,8 @@ function randomSerial() {
|
|
|
8030
8409
|
function ipv4(host) {
|
|
8031
8410
|
const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
|
|
8032
8411
|
if (!m) return void 0;
|
|
8033
|
-
const
|
|
8034
|
-
return
|
|
8412
|
+
const parts2 = m.slice(1).map(Number);
|
|
8413
|
+
return parts2.every((p) => p <= 255) ? Uint8Array.from(parts2) : void 0;
|
|
8035
8414
|
}
|
|
8036
8415
|
function ipv6(host) {
|
|
8037
8416
|
if (!host.includes(":") || !/^[0-9A-Fa-f:.]+$/.test(host)) return void 0;
|
|
@@ -8860,9 +9239,9 @@ var HttpHandles = class {
|
|
|
8860
9239
|
// src/kernel/net/routes.ts
|
|
8861
9240
|
var HOST_ADDRESS = HOST_LOOPBACK_ADDRESS.join(".");
|
|
8862
9241
|
function ipv4Bytes(text) {
|
|
8863
|
-
const
|
|
8864
|
-
if (
|
|
8865
|
-
const bytes =
|
|
9242
|
+
const parts2 = text.split(".");
|
|
9243
|
+
if (parts2.length !== 4 || !parts2.every((p) => /^\d{1,3}$/.test(p))) return void 0;
|
|
9244
|
+
const bytes = parts2.map(Number);
|
|
8866
9245
|
return bytes.every((b) => b <= 255) ? bytes : void 0;
|
|
8867
9246
|
}
|
|
8868
9247
|
function ipv6Groups(text) {
|
|
@@ -8889,9 +9268,9 @@ function addressBits(host) {
|
|
|
8889
9268
|
return v6 ? { v6: true, bits: toBits(v6, 16) } : void 0;
|
|
8890
9269
|
}
|
|
8891
9270
|
function parsePrefix(text) {
|
|
8892
|
-
const
|
|
8893
|
-
if (
|
|
8894
|
-
const [address = "", size] =
|
|
9271
|
+
const parts2 = text.trim().split("/");
|
|
9272
|
+
if (parts2.length > 2) return void 0;
|
|
9273
|
+
const [address = "", size] = parts2;
|
|
8895
9274
|
const parsed2 = addressBits(address);
|
|
8896
9275
|
if (!parsed2) return void 0;
|
|
8897
9276
|
const max = parsed2.v6 ? 128 : 32;
|
|
@@ -9739,7 +10118,9 @@ var VfsNode = class {
|
|
|
9739
10118
|
this.ranged = new AsyncRangedFile(io, this.path, this.pin.size, this.pin.version);
|
|
9740
10119
|
return this.ranged;
|
|
9741
10120
|
}
|
|
9742
|
-
const
|
|
10121
|
+
const at = this.path;
|
|
10122
|
+
const st = io && !this.orphaned ? await this.fs.stat?.(at).catch(() => void 0) : void 0;
|
|
10123
|
+
this.note(at, st);
|
|
9743
10124
|
if (io && st?.ranged) {
|
|
9744
10125
|
this.ranged = new AsyncRangedFile(io, this.path, st.size ?? 0, st.version);
|
|
9745
10126
|
return this.ranged;
|
|
@@ -9799,12 +10180,23 @@ var VfsNode = class {
|
|
|
9799
10180
|
const stat = this.fs.stat?.bind(this.fs);
|
|
9800
10181
|
if (!stat || this.orphaned) return Promise.resolve(void 0);
|
|
9801
10182
|
const parent = this.path.slice(0, this.path.lastIndexOf("/")) || "/";
|
|
9802
|
-
|
|
10183
|
+
const at = this.path;
|
|
10184
|
+
this.cap ??= stat(at).then((st) => {
|
|
10185
|
+
this.note(at, st);
|
|
10186
|
+
return st;
|
|
10187
|
+
}).catch(() => stat(parent)).then(
|
|
9803
10188
|
(st) => st.maxFile,
|
|
9804
10189
|
() => void 0
|
|
9805
10190
|
);
|
|
9806
10191
|
return this.cap;
|
|
9807
10192
|
}
|
|
10193
|
+
seen;
|
|
10194
|
+
note(path, st) {
|
|
10195
|
+
if (st?.ino !== void 0) this.seen = { path, ino: st.ino };
|
|
10196
|
+
}
|
|
10197
|
+
known() {
|
|
10198
|
+
return this.seen?.path === this.path && !this.orphaned ? this.seen.ino : void 0;
|
|
10199
|
+
}
|
|
9808
10200
|
async fits(end) {
|
|
9809
10201
|
const max = await this.limit();
|
|
9810
10202
|
if (max !== void 0 && end > max) throw new KernelError("EFBIG");
|
|
@@ -9852,10 +10244,10 @@ var VfsNode = class {
|
|
|
9852
10244
|
this.serial(() => this.flush()).catch(() => void 0);
|
|
9853
10245
|
}, delay);
|
|
9854
10246
|
}
|
|
9855
|
-
ensure(
|
|
10247
|
+
ensure(need2) {
|
|
9856
10248
|
const cur = this.data ?? new Uint8Array(0);
|
|
9857
|
-
if (cur.length >=
|
|
9858
|
-
const grown = new Uint8Array(Math.max(
|
|
10249
|
+
if (cur.length >= need2) return cur;
|
|
10250
|
+
const grown = new Uint8Array(Math.max(need2, cur.length * 2, 256));
|
|
9859
10251
|
grown.set(cur.subarray(0, this.length));
|
|
9860
10252
|
this.data = grown;
|
|
9861
10253
|
return grown;
|
|
@@ -9898,14 +10290,14 @@ var VfsNodes = class {
|
|
|
9898
10290
|
return run2;
|
|
9899
10291
|
}
|
|
9900
10292
|
async entryKey(path) {
|
|
9901
|
-
const parent =
|
|
10293
|
+
const parent = parentOf(path);
|
|
9902
10294
|
const dir = await this.real(parent, true, () => realDir(this.fs, parent));
|
|
9903
10295
|
return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
|
|
9904
10296
|
}
|
|
9905
10297
|
targetKey(path) {
|
|
9906
10298
|
return this.real(path, true, () => realTarget(this.fs, path));
|
|
9907
10299
|
}
|
|
9908
|
-
async real(path, follow2,
|
|
10300
|
+
async real(path, follow2, walk2) {
|
|
9909
10301
|
if (this.fs.realpath) {
|
|
9910
10302
|
try {
|
|
9911
10303
|
return await this.fs.realpath(path, follow2);
|
|
@@ -9913,13 +10305,13 @@ var VfsNodes = class {
|
|
|
9913
10305
|
if (errCode(err) === "ELOOP") throw err;
|
|
9914
10306
|
}
|
|
9915
10307
|
}
|
|
9916
|
-
return
|
|
10308
|
+
return walk2();
|
|
9917
10309
|
}
|
|
9918
10310
|
createExclusive(path, owner) {
|
|
9919
10311
|
return this.onEntry(path, async () => {
|
|
9920
10312
|
const node = this.byPath.get(path);
|
|
9921
10313
|
const held = node ? await node.serial(async () => node.holds()) : false;
|
|
9922
|
-
if (held || await
|
|
10314
|
+
if (held || await present2(this.fs, path)) throw fsError("EEXIST", path);
|
|
9923
10315
|
await this.fs.writeFile(path, new Uint8Array(0), owner);
|
|
9924
10316
|
});
|
|
9925
10317
|
}
|
|
@@ -9979,13 +10371,13 @@ var VfsNodes = class {
|
|
|
9979
10371
|
if (id2 !== void 0) this.know(node, id2);
|
|
9980
10372
|
return node;
|
|
9981
10373
|
}
|
|
9982
|
-
open(path, pin,
|
|
10374
|
+
open(path, pin, writable2 = false, id2) {
|
|
9983
10375
|
const node = this.named(path, id2) ?? this.create(path, id2, new VfsNode(this.fs, path, void 0, false, false, pin));
|
|
9984
10376
|
node.opens++;
|
|
9985
|
-
if (
|
|
10377
|
+
if (writable2) node.writers++;
|
|
9986
10378
|
return node;
|
|
9987
10379
|
}
|
|
9988
|
-
adopt(path, contents2, dirty,
|
|
10380
|
+
adopt(path, contents2, dirty, writable2 = false, id2) {
|
|
9989
10381
|
let node = this.named(path, id2);
|
|
9990
10382
|
if (!node) {
|
|
9991
10383
|
node = this.create(path, id2, new VfsNode(this.fs, path, contents2, false, dirty));
|
|
@@ -9993,7 +10385,7 @@ var VfsNodes = class {
|
|
|
9993
10385
|
node.replace(contents2);
|
|
9994
10386
|
}
|
|
9995
10387
|
node.opens++;
|
|
9996
|
-
if (
|
|
10388
|
+
if (writable2) node.writers++;
|
|
9997
10389
|
return node;
|
|
9998
10390
|
}
|
|
9999
10391
|
holds(prefix) {
|
|
@@ -10005,11 +10397,11 @@ var VfsNodes = class {
|
|
|
10005
10397
|
if (node.writers > 0 && within5(path, prefix)) return true;
|
|
10006
10398
|
return false;
|
|
10007
10399
|
}
|
|
10008
|
-
closed(node,
|
|
10400
|
+
closed(node, writable2 = false) {
|
|
10009
10401
|
node.opens--;
|
|
10010
|
-
if (
|
|
10402
|
+
if (writable2) node.writers--;
|
|
10011
10403
|
if (node.opens > 0) {
|
|
10012
|
-
if (
|
|
10404
|
+
if (writable2 && node.writers === 0) this.onClosed?.(node.path);
|
|
10013
10405
|
return;
|
|
10014
10406
|
}
|
|
10015
10407
|
this.forget(node);
|
|
@@ -10086,7 +10478,7 @@ var VfsNodes = class {
|
|
|
10086
10478
|
}
|
|
10087
10479
|
}
|
|
10088
10480
|
};
|
|
10089
|
-
async function
|
|
10481
|
+
async function present2(fs, path, follow2 = false) {
|
|
10090
10482
|
const look = (follow2 ? void 0 : fs.lstat) ?? fs.stat;
|
|
10091
10483
|
try {
|
|
10092
10484
|
await (look ? look.call(fs, path) : fs.readFileBuffer(path));
|
|
@@ -10100,7 +10492,7 @@ function errCode(err) {
|
|
|
10100
10492
|
return err?.code;
|
|
10101
10493
|
}
|
|
10102
10494
|
async function targetDir(fs, path) {
|
|
10103
|
-
return
|
|
10495
|
+
return parentOf(await realTarget(fs, path).catch(() => path));
|
|
10104
10496
|
}
|
|
10105
10497
|
async function refuseReadonly(fs, path, flags, opts = {}) {
|
|
10106
10498
|
const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
|
|
@@ -10118,6 +10510,18 @@ async function refuseReadonly(fs, path, flags, opts = {}) {
|
|
|
10118
10510
|
function keepingOpen(fs, nodes) {
|
|
10119
10511
|
return {
|
|
10120
10512
|
...fs,
|
|
10513
|
+
async exclusive(path, run2, follow2 = true) {
|
|
10514
|
+
const at = fs.resolvePath("/", path);
|
|
10515
|
+
const key = follow2 ? nodes.targetKey(at) : nodes.entryKey(at);
|
|
10516
|
+
const locked = await key.catch(() => at);
|
|
10517
|
+
return nodes.onEntry(
|
|
10518
|
+
locked,
|
|
10519
|
+
() => run2({
|
|
10520
|
+
writeFile: (p, content, owner) => fs.writeFile(p, content, owner),
|
|
10521
|
+
direct: locked === at
|
|
10522
|
+
})
|
|
10523
|
+
);
|
|
10524
|
+
},
|
|
10121
10525
|
async rm(path, options) {
|
|
10122
10526
|
const at = await nodes.entryKey(fs.resolvePath("/", path));
|
|
10123
10527
|
await nodes.unlinking(at);
|
|
@@ -10150,10 +10554,10 @@ function keepingOpen(fs, nodes) {
|
|
|
10150
10554
|
};
|
|
10151
10555
|
}
|
|
10152
10556
|
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,
|
|
10557
|
+
const access2 = opts.flags & O_ACCMODE;
|
|
10558
|
+
const readable2 = access2 !== O_WRONLY;
|
|
10559
|
+
const writable2 = access2 === O_WRONLY || access2 === O_RDWR;
|
|
10560
|
+
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
10561
|
let openError;
|
|
10158
10562
|
const atOpen = (op) => {
|
|
10159
10563
|
void node.serial(op).catch((err) => {
|
|
@@ -10174,12 +10578,12 @@ function vfsFile(fs, opts, nodes) {
|
|
|
10174
10578
|
return op();
|
|
10175
10579
|
});
|
|
10176
10580
|
return new OpenFile({
|
|
10177
|
-
read:
|
|
10581
|
+
read: readable2 ? (max) => serial(async () => {
|
|
10178
10582
|
const out = await node.pread(max, offset);
|
|
10179
10583
|
offset += out.length;
|
|
10180
10584
|
return out;
|
|
10181
10585
|
}) : void 0,
|
|
10182
|
-
write:
|
|
10586
|
+
write: writable2 ? (bytes) => serial(async () => {
|
|
10183
10587
|
if (opts.flags & O_APPEND) offset = await node.size();
|
|
10184
10588
|
offset += await node.pwrite(bytes, offset);
|
|
10185
10589
|
return bytes.length;
|
|
@@ -10194,15 +10598,15 @@ function vfsFile(fs, opts, nodes) {
|
|
|
10194
10598
|
return offset;
|
|
10195
10599
|
}),
|
|
10196
10600
|
pread: (max, at) => serial(() => {
|
|
10197
|
-
if (!
|
|
10601
|
+
if (!readable2) throw new KernelError("EBADF");
|
|
10198
10602
|
return node.pread(max, at);
|
|
10199
10603
|
}),
|
|
10200
10604
|
pwrite: (bytes, at) => serial(() => {
|
|
10201
|
-
if (!
|
|
10605
|
+
if (!writable2) throw new KernelError("EBADF");
|
|
10202
10606
|
return node.pwrite(bytes, at);
|
|
10203
10607
|
}),
|
|
10204
10608
|
resize: (size) => serial(() => {
|
|
10205
|
-
if (!
|
|
10609
|
+
if (!writable2) throw new KernelError("EBADF");
|
|
10206
10610
|
return node.truncate(size);
|
|
10207
10611
|
}),
|
|
10208
10612
|
stat: () => serial(async () => ({
|
|
@@ -10210,13 +10614,14 @@ function vfsFile(fs, opts, nodes) {
|
|
|
10210
10614
|
size: await node.size(),
|
|
10211
10615
|
...node.orphaned ? { orphan: true } : {}
|
|
10212
10616
|
})),
|
|
10617
|
+
known: () => node.known(),
|
|
10213
10618
|
flush: () => serial(() => node.flush()),
|
|
10214
10619
|
close: () => node.serial(async () => {
|
|
10215
10620
|
try {
|
|
10216
10621
|
takeOpenError();
|
|
10217
10622
|
await node.flush();
|
|
10218
10623
|
} finally {
|
|
10219
|
-
nodes?.closed(node,
|
|
10624
|
+
nodes?.closed(node, writable2);
|
|
10220
10625
|
}
|
|
10221
10626
|
})
|
|
10222
10627
|
});
|
|
@@ -10587,7 +10992,7 @@ var WasmProcess = class {
|
|
|
10587
10992
|
this.fds.installAt(fd, this.openVfsFile(open));
|
|
10588
10993
|
} else if (req.create) {
|
|
10589
10994
|
await nodes.onEntry(path, async () => {
|
|
10590
|
-
const fresh = this.options.owners && !await
|
|
10995
|
+
const fresh = this.options.owners && !await present2(this.options.fs, path, true);
|
|
10591
10996
|
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
10592
10997
|
await this.fds.get(fd).file.stat?.();
|
|
10593
10998
|
if (fresh) await this.options.fs.chown?.(path, { ...this.newOwner(), kind: "new" });
|
|
@@ -10595,7 +11000,7 @@ var WasmProcess = class {
|
|
|
10595
11000
|
} else {
|
|
10596
11001
|
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
10597
11002
|
}
|
|
10598
|
-
if (req.existing && !await
|
|
11003
|
+
if (req.existing && !await present2(this.options.fs, path, true)) {
|
|
10599
11004
|
throw new KernelError("ENOENT");
|
|
10600
11005
|
}
|
|
10601
11006
|
} catch (err) {
|
|
@@ -10604,6 +11009,20 @@ var WasmProcess = class {
|
|
|
10604
11009
|
}
|
|
10605
11010
|
return fd;
|
|
10606
11011
|
}
|
|
11012
|
+
async identified(stat, known) {
|
|
11013
|
+
const first = await stat();
|
|
11014
|
+
if (first.orphan) return first;
|
|
11015
|
+
const ino = known?.();
|
|
11016
|
+
if (ino !== void 0) return { ...first, ino };
|
|
11017
|
+
const { fs } = this.options;
|
|
11018
|
+
const look = async () => {
|
|
11019
|
+
const now = await stat();
|
|
11020
|
+
if (now.path !== first.path || now.orphan) return now;
|
|
11021
|
+
const st = await fs.stat?.(now.path).catch(() => void 0);
|
|
11022
|
+
return st?.ino === void 0 ? now : { ...now, ino: st.ino };
|
|
11023
|
+
};
|
|
11024
|
+
return fs.exclusive ? fs.exclusive(first.path, look) : look();
|
|
11025
|
+
}
|
|
10607
11026
|
newOwner() {
|
|
10608
11027
|
if (!this.options.owners) return void 0;
|
|
10609
11028
|
return { uid: this.cred.euid, gid: this.cred.egid, mode: 438 & ~this.umask };
|
|
@@ -10615,14 +11034,14 @@ var WasmProcess = class {
|
|
|
10615
11034
|
if (req.flags & WRITE_ACCESS || req.create || req.truncate || req.exclusive) {
|
|
10616
11035
|
throw new KernelError("EROFS");
|
|
10617
11036
|
}
|
|
10618
|
-
const
|
|
11037
|
+
const tree2 = new ProcTree(
|
|
10619
11038
|
{
|
|
10620
11039
|
procList: () => this.options.processes?.() ?? { boot: 0, processes: [] },
|
|
10621
11040
|
mountList: () => this.options.mounts?.() ?? []
|
|
10622
11041
|
},
|
|
10623
11042
|
(await this.options.identity?.())?.pid ?? this.pid
|
|
10624
11043
|
);
|
|
10625
|
-
return memoryFile(
|
|
11044
|
+
return memoryFile(tree2.read(path), path, req.position);
|
|
10626
11045
|
}
|
|
10627
11046
|
openVfsFile(req, id2) {
|
|
10628
11047
|
return vfsFile(
|
|
@@ -10765,7 +11184,11 @@ var WasmProcess = class {
|
|
|
10765
11184
|
await file.resize(req.size);
|
|
10766
11185
|
return { ok: true, kind: "void" };
|
|
10767
11186
|
case "fd-vfs-stat":
|
|
10768
|
-
return {
|
|
11187
|
+
return {
|
|
11188
|
+
ok: true,
|
|
11189
|
+
kind: "json",
|
|
11190
|
+
json: req.identity ? await this.identified(file.stat.bind(file), file.known?.bind(file)) : await file.stat()
|
|
11191
|
+
};
|
|
10769
11192
|
}
|
|
10770
11193
|
}
|
|
10771
11194
|
keptPath;
|
|
@@ -10774,7 +11197,7 @@ var WasmProcess = class {
|
|
|
10774
11197
|
const held = handle === void 0 ? void 0 : this.keptHandle?.(handle);
|
|
10775
11198
|
if (held !== void 0) return held;
|
|
10776
11199
|
const kept = this.keptPath?.(path);
|
|
10777
|
-
return kept !== void 0 && !await
|
|
11200
|
+
return kept !== void 0 && !await present2(this.options.fs, path, true) ? kept : path;
|
|
10778
11201
|
}
|
|
10779
11202
|
async promote(req) {
|
|
10780
11203
|
if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
|
|
@@ -11284,6 +11707,7 @@ function inherited(opts) {
|
|
|
11284
11707
|
function holdingToken(opts, holds, caller) {
|
|
11285
11708
|
return mintSyncFsToken({
|
|
11286
11709
|
...opts.owners ? caller : {},
|
|
11710
|
+
...opts.dac ? { dac: opts.dac, pid: opts.pid } : {},
|
|
11287
11711
|
fs: opts.fs,
|
|
11288
11712
|
cwd: opts.cwd,
|
|
11289
11713
|
...opts.statfs ? { statfs: opts.statfs } : {},
|
|
@@ -11913,9 +12337,9 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
|
11913
12337
|
let open = false;
|
|
11914
12338
|
const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
|
|
11915
12339
|
async function hello(message3) {
|
|
11916
|
-
const
|
|
11917
|
-
if (
|
|
11918
|
-
reply({ hello: { protocol: DRIVER_PROTOCOL, error:
|
|
12340
|
+
const refused2 = driverVersionError(message3.protocol);
|
|
12341
|
+
if (refused2) {
|
|
12342
|
+
reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused2, errno: "EPROTO" } });
|
|
11919
12343
|
return;
|
|
11920
12344
|
}
|
|
11921
12345
|
try {
|
|
@@ -12457,7 +12881,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
12457
12881
|
}, againMs);
|
|
12458
12882
|
timer.unref?.();
|
|
12459
12883
|
});
|
|
12460
|
-
async function
|
|
12884
|
+
async function writable2(client, path, flags) {
|
|
12461
12885
|
const { ifMatch: _, ...asked } = flags;
|
|
12462
12886
|
const opening = () => client.call("open", { path: rel(path), ...asked });
|
|
12463
12887
|
try {
|
|
@@ -12488,7 +12912,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
12488
12912
|
async open(path, flags) {
|
|
12489
12913
|
const client = current();
|
|
12490
12914
|
const token = client.token;
|
|
12491
|
-
const { opened: opened2, restore } = await
|
|
12915
|
+
const { opened: opened2, restore } = await writable2(client, path, flags);
|
|
12492
12916
|
if (flags.ifMatch !== void 0 && opened2.attr?.etag !== flags.ifMatch) {
|
|
12493
12917
|
await client.call("release", { fh: opened2.fh }, true);
|
|
12494
12918
|
throw fsError2("ESTALE", `${path} changed on the host while it was being read`);
|
|
@@ -12661,7 +13085,7 @@ var PORT_DEAD_MS = 1e4;
|
|
|
12661
13085
|
var PORT_LINGER_MS = 1e3;
|
|
12662
13086
|
function timed(transport, ms) {
|
|
12663
13087
|
const inflight = /* @__PURE__ */ new Set();
|
|
12664
|
-
const
|
|
13088
|
+
const guard2 = (name2, run2, late, expire) => new Promise((resolve, reject) => {
|
|
12665
13089
|
let expired = false;
|
|
12666
13090
|
const running = run2();
|
|
12667
13091
|
inflight.add(running);
|
|
@@ -12710,15 +13134,15 @@ function timed(transport, ms) {
|
|
|
12710
13134
|
},
|
|
12711
13135
|
retire: (why) => transport.retire?.(why),
|
|
12712
13136
|
...transport.cleanup ? { cleanup } : {},
|
|
12713
|
-
call: (op, body, handle) =>
|
|
13137
|
+
call: (op, body, handle) => guard2(
|
|
12714
13138
|
op,
|
|
12715
13139
|
() => transport.call(op, body, handle),
|
|
12716
13140
|
op === "open" ? release : void 0,
|
|
12717
13141
|
op === "release" ? (running) => void cleanup("release", body ?? {}, running).catch(() => {
|
|
12718
13142
|
}) : void 0
|
|
12719
13143
|
),
|
|
12720
|
-
read: (fh, offset, size, ifMatch) =>
|
|
12721
|
-
write: (fh, offset, bytes) =>
|
|
13144
|
+
read: (fh, offset, size, ifMatch) => guard2("read", () => transport.read(fh, offset, size, ifMatch)),
|
|
13145
|
+
write: (fh, offset, bytes) => guard2("write", () => transport.write(fh, offset, bytes)),
|
|
12722
13146
|
watch: (signal, onEvent, onOpen) => transport.watch(signal, onEvent, onOpen)
|
|
12723
13147
|
};
|
|
12724
13148
|
}
|
|
@@ -13099,10 +13523,10 @@ var ChunkedBytes = class {
|
|
|
13099
13523
|
}
|
|
13100
13524
|
this.size = size;
|
|
13101
13525
|
}
|
|
13102
|
-
room(index,
|
|
13526
|
+
room(index, need2) {
|
|
13103
13527
|
const chunk2 = this.chunks[index];
|
|
13104
|
-
if (chunk2 && chunk2.length >=
|
|
13105
|
-
const grown = new Uint8Array(Math.min(CHUNK3, Math.max(
|
|
13528
|
+
if (chunk2 && chunk2.length >= need2) return chunk2;
|
|
13529
|
+
const grown = new Uint8Array(Math.min(CHUNK3, Math.max(need2, (chunk2?.length ?? 0) * 2)));
|
|
13106
13530
|
if (chunk2) grown.set(chunk2);
|
|
13107
13531
|
this.chunks[index] = grown;
|
|
13108
13532
|
return grown;
|
|
@@ -13149,14 +13573,14 @@ function tmpfs() {
|
|
|
13149
13573
|
const root = make("directory", 16877);
|
|
13150
13574
|
const handles = /* @__PURE__ */ new Map();
|
|
13151
13575
|
let nextFh = 0;
|
|
13152
|
-
const
|
|
13576
|
+
const parts2 = (path) => path.split("/").filter(Boolean);
|
|
13153
13577
|
const place = (dir, name2, node) => {
|
|
13154
13578
|
dir.children.set(name2, node);
|
|
13155
13579
|
dir.mtime = Date.now();
|
|
13156
13580
|
};
|
|
13157
13581
|
function find(path) {
|
|
13158
13582
|
let node = root;
|
|
13159
|
-
for (const name2 of
|
|
13583
|
+
for (const name2 of parts2(path)) {
|
|
13160
13584
|
if (node.kind !== "directory") throw fsError2("ENOTDIR", path);
|
|
13161
13585
|
const next2 = node.children.get(name2);
|
|
13162
13586
|
if (!next2) throw fsError2("ENOENT", path);
|
|
@@ -13165,7 +13589,7 @@ function tmpfs() {
|
|
|
13165
13589
|
return node;
|
|
13166
13590
|
}
|
|
13167
13591
|
function parent(path) {
|
|
13168
|
-
const names =
|
|
13592
|
+
const names = parts2(path);
|
|
13169
13593
|
const name2 = names.pop();
|
|
13170
13594
|
if (name2 === void 0) throw fsError2("EBUSY", "the root of a mount");
|
|
13171
13595
|
const dir = find(`/${names.join("/")}`);
|
|
@@ -13250,8 +13674,8 @@ function tmpfs() {
|
|
|
13250
13674
|
dir.mtime = Date.now();
|
|
13251
13675
|
},
|
|
13252
13676
|
async rename(from, to) {
|
|
13253
|
-
const a = `/${
|
|
13254
|
-
const b = `/${
|
|
13677
|
+
const a = `/${parts2(from).join("/")}`;
|
|
13678
|
+
const b = `/${parts2(to).join("/")}`;
|
|
13255
13679
|
if (b !== a && b.startsWith(`${a}/`)) throw fsError2("EINVAL", `${to} is inside ${from}`);
|
|
13256
13680
|
const source = parent(from);
|
|
13257
13681
|
const node = source.dir.children.get(source.name);
|
|
@@ -13610,11 +14034,13 @@ var Launcher = class {
|
|
|
13610
14034
|
booting = new AbortController();
|
|
13611
14035
|
fstab = Promise.resolve([]);
|
|
13612
14036
|
usersMode;
|
|
14037
|
+
dac;
|
|
13613
14038
|
nextPid = 1e3;
|
|
13614
14039
|
boot = Date.now();
|
|
13615
14040
|
terminals = 0;
|
|
13616
14041
|
constructor(options) {
|
|
13617
14042
|
this.usersMode = usersMode(options.users);
|
|
14043
|
+
this.dac = this.usersMode === "off" ? void 0 : new Dac(this.usersMode, options.onDenial);
|
|
13618
14044
|
if (this.owners) options.fs.trackOwners?.();
|
|
13619
14045
|
this.mounts = new MountTable({
|
|
13620
14046
|
open: (type, spec) => this.driver(type, spec),
|
|
@@ -13945,6 +14371,7 @@ var Launcher = class {
|
|
|
13945
14371
|
...req.umask !== void 0 ? { umask: req.umask } : {},
|
|
13946
14372
|
cred: req.cred,
|
|
13947
14373
|
...this.owners ? { owners: true } : {},
|
|
14374
|
+
...this.dac ? { dac: this.dac } : {},
|
|
13948
14375
|
program: req.program,
|
|
13949
14376
|
argv0: req.argv0,
|
|
13950
14377
|
args: req.args,
|
|
@@ -14908,7 +15335,7 @@ function dialStream(port, onClose) {
|
|
|
14908
15335
|
let closed = false;
|
|
14909
15336
|
let reader;
|
|
14910
15337
|
const acks = [];
|
|
14911
|
-
const
|
|
15338
|
+
const readable2 = new ReadableStream({
|
|
14912
15339
|
start(controller) {
|
|
14913
15340
|
reader = controller;
|
|
14914
15341
|
},
|
|
@@ -14949,7 +15376,7 @@ function dialStream(port, onClose) {
|
|
|
14949
15376
|
else finish(errorWithCode(data.error));
|
|
14950
15377
|
});
|
|
14951
15378
|
port.start?.();
|
|
14952
|
-
const
|
|
15379
|
+
const writable2 = new WritableStream({
|
|
14953
15380
|
write(chunk2) {
|
|
14954
15381
|
if (closed) throw errorWithCode("EPIPE: the connection is closed");
|
|
14955
15382
|
const copy = chunk2.slice();
|
|
@@ -14968,7 +15395,7 @@ function dialStream(port, onClose) {
|
|
|
14968
15395
|
shut();
|
|
14969
15396
|
finish(error);
|
|
14970
15397
|
};
|
|
14971
|
-
return { readable, writable, close, fail: fail3 };
|
|
15398
|
+
return { readable: readable2, writable: writable2, close, fail: fail3 };
|
|
14972
15399
|
}
|
|
14973
15400
|
|
|
14974
15401
|
// src/client/loopback-fetch.ts
|
|
@@ -15452,9 +15879,9 @@ async function greet(channel, lock, timeoutMs) {
|
|
|
15452
15879
|
} finally {
|
|
15453
15880
|
clearTimeout(timer);
|
|
15454
15881
|
}
|
|
15455
|
-
const
|
|
15456
|
-
if (
|
|
15457
|
-
const error = new Error(
|
|
15882
|
+
const refused2 = hello.error ?? versionError(hello.protocol);
|
|
15883
|
+
if (refused2) {
|
|
15884
|
+
const error = new Error(refused2);
|
|
15458
15885
|
channel.end(error);
|
|
15459
15886
|
throw error;
|
|
15460
15887
|
}
|
|
@@ -15604,10 +16031,11 @@ async function createNodeKernel(options = {}) {
|
|
|
15604
16031
|
...hostfsFetchOf(options),
|
|
15605
16032
|
...options.cdp ? { cdp: options.cdp } : {},
|
|
15606
16033
|
...options.hostname !== void 0 ? { hostname: options.hostname } : {},
|
|
15607
|
-
...options.users !== void 0 ? { users: options.users } : {}
|
|
16034
|
+
...options.users !== void 0 ? { users: options.users } : {},
|
|
16035
|
+
...options.onDenial ? { onDenial: options.onDenial } : {}
|
|
15608
16036
|
});
|
|
15609
16037
|
await launcher.prepare();
|
|
15610
|
-
const
|
|
16038
|
+
const guard2 = (work) => new Promise((resolve, reject) => {
|
|
15611
16039
|
if (terminated) return reject(new Error(TERMINATED));
|
|
15612
16040
|
pending.add(reject);
|
|
15613
16041
|
work().then(resolve, reject).finally(() => pending.delete(reject));
|
|
@@ -15619,7 +16047,7 @@ async function createNodeKernel(options = {}) {
|
|
|
15619
16047
|
const { stdin, onStdout, onStderr, ...rest2 } = opts;
|
|
15620
16048
|
const onOut = textOf(onStdout);
|
|
15621
16049
|
const onErr = textOf(onStderr);
|
|
15622
|
-
const result = await
|
|
16050
|
+
const result = await guard2(
|
|
15623
16051
|
() => launcher.run(argv, {
|
|
15624
16052
|
...rest2,
|
|
15625
16053
|
...stdin !== void 0 ? { stdin: typeof stdin === "string" ? new TextEncoder().encode(stdin) : stdin } : {},
|
|
@@ -15637,7 +16065,7 @@ async function createNodeKernel(options = {}) {
|
|
|
15637
16065
|
const { onData, ...rest2 } = opts;
|
|
15638
16066
|
let listener = onData ?? null;
|
|
15639
16067
|
const backlog = [];
|
|
15640
|
-
const session = await
|
|
16068
|
+
const session = await guard2(
|
|
15641
16069
|
() => launcher.openTerminal(argv, {
|
|
15642
16070
|
...rest2,
|
|
15643
16071
|
onData: (bytes) => listener ? listener(bytes) : backlog.push(bytes)
|
|
@@ -15645,7 +16073,7 @@ async function createNodeKernel(options = {}) {
|
|
|
15645
16073
|
);
|
|
15646
16074
|
return {
|
|
15647
16075
|
pid: session.pid,
|
|
15648
|
-
exited:
|
|
16076
|
+
exited: guard2(() => session.exited),
|
|
15649
16077
|
get onData() {
|
|
15650
16078
|
return listener;
|
|
15651
16079
|
},
|
|
@@ -15665,20 +16093,20 @@ async function createNodeKernel(options = {}) {
|
|
|
15665
16093
|
await launcher.fs.writeFile(path, data);
|
|
15666
16094
|
},
|
|
15667
16095
|
readFile: async (path) => launcher.fs.readFileBuffer(path),
|
|
15668
|
-
mount: (spec) =>
|
|
15669
|
-
umount: (target) =>
|
|
16096
|
+
mount: (spec) => guard2(() => launcher.mount(spec)),
|
|
16097
|
+
umount: (target) => guard2(async () => launcher.umount(target)),
|
|
15670
16098
|
mounts: async () => launcher.mounts.list(),
|
|
15671
|
-
setRoutes: (routes) =>
|
|
16099
|
+
setRoutes: (routes) => guard2(async () => launcher.setRoutes(routes)),
|
|
15672
16100
|
users: {
|
|
15673
16101
|
mode: launcher.usersMode,
|
|
15674
|
-
add: (user) =>
|
|
15675
|
-
remove: (name2, options2) =>
|
|
15676
|
-
list: () =>
|
|
16102
|
+
add: (user) => guard2(() => launcher.users.add(user)),
|
|
16103
|
+
remove: (name2, options2) => guard2(() => launcher.users.remove(name2, options2)),
|
|
16104
|
+
list: () => guard2(() => launcher.users.list())
|
|
15677
16105
|
},
|
|
15678
16106
|
groups: {
|
|
15679
|
-
list: () =>
|
|
15680
|
-
addMember: (group, user) =>
|
|
15681
|
-
removeMember: (group, user) =>
|
|
16107
|
+
list: () => guard2(() => launcher.users.groups()),
|
|
16108
|
+
addMember: (group, user) => guard2(() => launcher.users.addMember(group, user)),
|
|
16109
|
+
removeMember: (group, user) => guard2(() => launcher.users.removeMember(group, user))
|
|
15682
16110
|
},
|
|
15683
16111
|
async connect({ user } = {}) {
|
|
15684
16112
|
if (terminated) throw new Error(TERMINATED);
|