@ai-ecoverse/slicc-kernel 1.21.1 → 1.22.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 +8 -6
- package/dist/driver-worker.js +6 -4
- package/dist/driver-worker.js.map +2 -2
- package/dist/driver.js +6 -4
- package/dist/driver.js.map +2 -2
- package/dist/kernel-worker.js +533 -61
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +6 -4
- package/dist/node-driver-worker.js.map +2 -2
- package/dist/node-process-worker.js +387 -33
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +535 -62
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +387 -33
- package/dist/process-worker.js.map +4 -4
- package/package.json +1 -1
package/dist/kernel-worker.js
CHANGED
|
@@ -252,6 +252,11 @@ function versionError(theirs) {
|
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
// src/fs/types.ts
|
|
255
|
+
function rangedOps(fs) {
|
|
256
|
+
const { pread, pwrite, truncate } = fs;
|
|
257
|
+
if (!pread || !pwrite || !truncate) return void 0;
|
|
258
|
+
return { pread: pread.bind(fs), pwrite: pwrite.bind(fs), truncate: truncate.bind(fs) };
|
|
259
|
+
}
|
|
255
260
|
function fsError(code, path) {
|
|
256
261
|
return Object.assign(new Error(`${code}: ${path}`), { code });
|
|
257
262
|
}
|
|
@@ -3654,7 +3659,8 @@ function withCommandDirs(fs, names) {
|
|
|
3654
3659
|
symlink: (target, path) => fs.symlink(target, path),
|
|
3655
3660
|
readlink: (path) => fs.readlink(path),
|
|
3656
3661
|
chmod: (path, mode) => fs.chmod(path, mode),
|
|
3657
|
-
utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime)
|
|
3662
|
+
utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime),
|
|
3663
|
+
...rangedOps(fs) ?? {}
|
|
3658
3664
|
};
|
|
3659
3665
|
}
|
|
3660
3666
|
|
|
@@ -3731,6 +3737,11 @@ var FsWatchers = class {
|
|
|
3731
3737
|
chmod: after(fs.chmod.bind(fs), (path) => [path]),
|
|
3732
3738
|
utimes: after(fs.utimes.bind(fs), (path) => [path])
|
|
3733
3739
|
};
|
|
3740
|
+
const ranged2 = rangedOps(fs);
|
|
3741
|
+
if (ranged2) {
|
|
3742
|
+
overrides.pwrite = after(ranged2.pwrite, (path) => [path]);
|
|
3743
|
+
overrides.truncate = after(ranged2.truncate, (path) => [path]);
|
|
3744
|
+
}
|
|
3734
3745
|
return new Proxy(fs, {
|
|
3735
3746
|
get(target, key, receiver) {
|
|
3736
3747
|
if (Object.hasOwn(overrides, key)) return overrides[key];
|
|
@@ -3925,7 +3936,7 @@ var ChildTable = class {
|
|
|
3925
3936
|
};
|
|
3926
3937
|
|
|
3927
3938
|
// src/mount/protocol.ts
|
|
3928
|
-
var DRIVER_PROTOCOL = [1,
|
|
3939
|
+
var DRIVER_PROTOCOL = [1, 1];
|
|
3929
3940
|
function driverVersionError(theirs) {
|
|
3930
3941
|
const major = Array.isArray(theirs) ? theirs[0] : void 0;
|
|
3931
3942
|
if (major === DRIVER_PROTOCOL[0]) return void 0;
|
|
@@ -4084,7 +4095,8 @@ function statOf(attr, path, mount) {
|
|
|
4084
4095
|
ino: attr.ino ?? inodeOf(path),
|
|
4085
4096
|
dev: mount.dev,
|
|
4086
4097
|
...readonly(mount) ? { readonly: true } : {},
|
|
4087
|
-
...mount.maxFile > 0 ? { maxFile: mount.maxFile } : {}
|
|
4098
|
+
...mount.maxFile > 0 ? { maxFile: mount.maxFile } : {},
|
|
4099
|
+
...mount.caps.ranges ? { ranged: true } : {}
|
|
4088
4100
|
};
|
|
4089
4101
|
}
|
|
4090
4102
|
function concat4(chunks, total) {
|
|
@@ -4097,10 +4109,26 @@ function concat4(chunks, total) {
|
|
|
4097
4109
|
}
|
|
4098
4110
|
return out;
|
|
4099
4111
|
}
|
|
4112
|
+
function resized(bytes, size) {
|
|
4113
|
+
if (size <= bytes.length) return bytes.subarray(0, size);
|
|
4114
|
+
const out = new Uint8Array(size);
|
|
4115
|
+
out.set(bytes);
|
|
4116
|
+
return out;
|
|
4117
|
+
}
|
|
4118
|
+
function handed(bytes, transfer, chunk2) {
|
|
4119
|
+
const whole = bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
|
|
4120
|
+
return transfer && whole && bytes.length <= chunk2 && bytes.buffer instanceof ArrayBuffer;
|
|
4121
|
+
}
|
|
4122
|
+
function spliced(bytes, offset, part) {
|
|
4123
|
+
const out = resized(bytes, Math.max(bytes.length, offset + part.length));
|
|
4124
|
+
out.set(part, offset);
|
|
4125
|
+
return out;
|
|
4126
|
+
}
|
|
4100
4127
|
var MountTable = class {
|
|
4101
4128
|
mounts = /* @__PURE__ */ new Map();
|
|
4102
4129
|
devices = 0;
|
|
4103
4130
|
pending = /* @__PURE__ */ new Set();
|
|
4131
|
+
notes = /* @__PURE__ */ new Map();
|
|
4104
4132
|
deps;
|
|
4105
4133
|
constructor(deps) {
|
|
4106
4134
|
this.deps = deps;
|
|
@@ -4109,7 +4137,7 @@ var MountTable = class {
|
|
|
4109
4137
|
return this.mounts.get(target);
|
|
4110
4138
|
}
|
|
4111
4139
|
list() {
|
|
4112
|
-
|
|
4140
|
+
const mounted = [...this.mounts.values()].map(
|
|
4113
4141
|
({ type, source, target, options, state, error, present }) => ({
|
|
4114
4142
|
type,
|
|
4115
4143
|
source,
|
|
@@ -4119,6 +4147,19 @@ var MountTable = class {
|
|
|
4119
4147
|
...error ? { error } : {}
|
|
4120
4148
|
})
|
|
4121
4149
|
);
|
|
4150
|
+
const noted = [...this.notes.values()].filter((note) => !this.mounts.has(note.target));
|
|
4151
|
+
return [...mounted, ...noted.map((note) => ({ ...note, options: { ...note.options } }))];
|
|
4152
|
+
}
|
|
4153
|
+
note(spec, state, error) {
|
|
4154
|
+
const target = normalizePath(spec.target);
|
|
4155
|
+
this.notes.set(target, {
|
|
4156
|
+
type: spec.type,
|
|
4157
|
+
source: spec.source,
|
|
4158
|
+
target,
|
|
4159
|
+
options: { ...spec.options },
|
|
4160
|
+
state,
|
|
4161
|
+
...error ? { error } : {}
|
|
4162
|
+
});
|
|
4122
4163
|
}
|
|
4123
4164
|
locate(path) {
|
|
4124
4165
|
let best;
|
|
@@ -4195,13 +4236,19 @@ var MountTable = class {
|
|
|
4195
4236
|
};
|
|
4196
4237
|
mounted = mount;
|
|
4197
4238
|
this.mounts.set(target, mount);
|
|
4239
|
+
this.notes.delete(target);
|
|
4198
4240
|
return this.list().find((m) => m.target === target);
|
|
4199
4241
|
}
|
|
4242
|
+
unnote(target) {
|
|
4243
|
+
this.notes.delete(normalizePath(target));
|
|
4244
|
+
}
|
|
4200
4245
|
umount(target, detach = false) {
|
|
4201
4246
|
const at = normalizePath(target);
|
|
4202
4247
|
const mount = this.mounts.get(at);
|
|
4248
|
+
if (!mount && this.notes.delete(at)) return;
|
|
4203
4249
|
if (!mount) throw errnoError("EINVAL", `${at} is not mounted`);
|
|
4204
4250
|
if (!detach && this.deps.busy(at)) throw errnoError("EBUSY", `${at} has open files`);
|
|
4251
|
+
this.notes.delete(at);
|
|
4205
4252
|
this.mounts.delete(at);
|
|
4206
4253
|
mount.conn.close();
|
|
4207
4254
|
mount.dispose();
|
|
@@ -4376,15 +4423,95 @@ var MountFs = class {
|
|
|
4376
4423
|
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
4377
4424
|
try {
|
|
4378
4425
|
for (let offset = 0; offset < bytes.length; offset += chunk2) {
|
|
4379
|
-
|
|
4380
|
-
|
|
4426
|
+
const part = bytes.slice(offset, offset + chunk2);
|
|
4427
|
+
await mount.conn.call({ op: "write", fh, offset, bytes: part }, [part.buffer]);
|
|
4428
|
+
}
|
|
4429
|
+
} finally {
|
|
4430
|
+
await mount.conn.call({ op: "release", fh });
|
|
4431
|
+
this.table.forget(mount, real);
|
|
4432
|
+
}
|
|
4433
|
+
}
|
|
4434
|
+
async pread(path, offset, length2) {
|
|
4435
|
+
const real = await this.follow(path);
|
|
4436
|
+
const found = this.at(real);
|
|
4437
|
+
if (!found) return (await this.base.readFileBuffer(real)).slice(offset, offset + length2);
|
|
4438
|
+
const { mount, rel: rel2 } = found;
|
|
4439
|
+
if ((await this.table.getattr(mount, real, rel2)).kind === "directory") {
|
|
4440
|
+
throw errnoError("EISDIR", real);
|
|
4441
|
+
}
|
|
4442
|
+
const fh = await this.open(mount, rel2, false);
|
|
4443
|
+
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
4444
|
+
const chunks = [];
|
|
4445
|
+
let total = 0;
|
|
4446
|
+
try {
|
|
4447
|
+
while (total < length2) {
|
|
4448
|
+
const bytes = await mount.conn.call({
|
|
4449
|
+
op: "read",
|
|
4381
4450
|
fh,
|
|
4382
|
-
offset,
|
|
4383
|
-
|
|
4451
|
+
offset: offset + total,
|
|
4452
|
+
size: Math.min(chunk2, length2 - total)
|
|
4384
4453
|
});
|
|
4454
|
+
if (bytes.length === 0) break;
|
|
4455
|
+
chunks.push(bytes);
|
|
4456
|
+
total += bytes.length;
|
|
4385
4457
|
}
|
|
4386
4458
|
} finally {
|
|
4387
4459
|
await mount.conn.call({ op: "release", fh });
|
|
4460
|
+
}
|
|
4461
|
+
return concat4(chunks, total);
|
|
4462
|
+
}
|
|
4463
|
+
fits(mount, real, end) {
|
|
4464
|
+
if (mount.maxFile > 0 && end > mount.maxFile) {
|
|
4465
|
+
throw errnoError(
|
|
4466
|
+
"EFBIG",
|
|
4467
|
+
`${real} would be larger than this mount's maxfile (${mount.maxFile})`
|
|
4468
|
+
);
|
|
4469
|
+
}
|
|
4470
|
+
}
|
|
4471
|
+
async pwrite(path, offset, bytes, transfer = false) {
|
|
4472
|
+
const real = await this.follow(path, 0, true);
|
|
4473
|
+
const found = this.at(real);
|
|
4474
|
+
if (!found)
|
|
4475
|
+
return this.base.writeFile(
|
|
4476
|
+
real,
|
|
4477
|
+
spliced(await this.base.readFileBuffer(real), offset, bytes)
|
|
4478
|
+
);
|
|
4479
|
+
const { mount, rel: rel2 } = found;
|
|
4480
|
+
this.writable(mount, real);
|
|
4481
|
+
this.fits(mount, real, offset + bytes.length);
|
|
4482
|
+
this.table.forget(mount, real);
|
|
4483
|
+
const fh = await mount.conn.call({
|
|
4484
|
+
op: "open",
|
|
4485
|
+
path: rel2,
|
|
4486
|
+
write: true,
|
|
4487
|
+
create: true,
|
|
4488
|
+
truncate: false,
|
|
4489
|
+
exclusive: false
|
|
4490
|
+
});
|
|
4491
|
+
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
4492
|
+
try {
|
|
4493
|
+
for (let at = 0; at < bytes.length; at += chunk2) {
|
|
4494
|
+
const part = handed(bytes, transfer, chunk2) ? bytes : bytes.slice(at, at + chunk2);
|
|
4495
|
+
await mount.conn.call({ op: "write", fh, offset: offset + at, bytes: part }, [part.buffer]);
|
|
4496
|
+
}
|
|
4497
|
+
} finally {
|
|
4498
|
+
await mount.conn.call({ op: "release", fh });
|
|
4499
|
+
this.table.forget(mount, real);
|
|
4500
|
+
}
|
|
4501
|
+
}
|
|
4502
|
+
async truncate(path, size) {
|
|
4503
|
+
const real = await this.follow(path);
|
|
4504
|
+
const found = this.at(real);
|
|
4505
|
+
if (!found?.mount.caps.ranges) {
|
|
4506
|
+
return this.writeFile(real, resized(await this.readFileBuffer(real), size));
|
|
4507
|
+
}
|
|
4508
|
+
const { mount, rel: rel2 } = found;
|
|
4509
|
+
this.writable(mount, real);
|
|
4510
|
+
this.fits(mount, real, size);
|
|
4511
|
+
this.table.forget(mount, real);
|
|
4512
|
+
try {
|
|
4513
|
+
await mount.conn.call({ op: "setattr", path: rel2, size });
|
|
4514
|
+
} finally {
|
|
4388
4515
|
this.table.forget(mount, real);
|
|
4389
4516
|
}
|
|
4390
4517
|
}
|
|
@@ -4571,7 +4698,8 @@ function statJson(s) {
|
|
|
4571
4698
|
ino: s.ino,
|
|
4572
4699
|
...s.dev !== void 0 ? { dev: s.dev } : {},
|
|
4573
4700
|
...s.readonly ? { readonly: true } : {},
|
|
4574
|
-
...s.maxFile ? { maxFile: s.maxFile } : {}
|
|
4701
|
+
...s.maxFile ? { maxFile: s.maxFile } : {},
|
|
4702
|
+
...s.ranged ? { ranged: true } : {}
|
|
4575
4703
|
};
|
|
4576
4704
|
}
|
|
4577
4705
|
async function readdirStat(fs, dir) {
|
|
@@ -4636,10 +4764,24 @@ async function run(entry, path, req) {
|
|
|
4636
4764
|
case "utimes":
|
|
4637
4765
|
await fs.utimes(path, new Date(req.atimeMs ?? 0), new Date(req.mtimeMs ?? 0));
|
|
4638
4766
|
return done;
|
|
4767
|
+
case "pread":
|
|
4768
|
+
case "pwrite":
|
|
4769
|
+
case "truncate":
|
|
4770
|
+
return ranged(fs, path, req);
|
|
4639
4771
|
default:
|
|
4640
4772
|
return { ok: false, errno: "EINVAL", message: `sync-fs: unknown op '${req.op}'` };
|
|
4641
4773
|
}
|
|
4642
4774
|
}
|
|
4775
|
+
async function ranged(fs, path, req) {
|
|
4776
|
+
const ops = rangedOps(fs);
|
|
4777
|
+
if (!ops) throw syncError("ENOSYS", path);
|
|
4778
|
+
const offset = req.offset ?? 0;
|
|
4779
|
+
if (req.op === "pread")
|
|
4780
|
+
return { ok: true, kind: "bytes", bytes: await ops.pread(path, offset, req.length ?? 0) };
|
|
4781
|
+
if (req.op === "pwrite") await ops.pwrite(path, offset, req.body ?? new Uint8Array(0), true);
|
|
4782
|
+
else await ops.truncate(path, req.length ?? 0);
|
|
4783
|
+
return done;
|
|
4784
|
+
}
|
|
4643
4785
|
async function dispatchSyncFs(req) {
|
|
4644
4786
|
const entry = resolveSyncFsToken(req.token);
|
|
4645
4787
|
if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
|
|
@@ -5645,6 +5787,226 @@ async function accept(proc, fd, nonblock) {
|
|
|
5645
5787
|
return { fd: proc.fds.install(new OpenFile(socket), 3), peer: socket.peer };
|
|
5646
5788
|
}
|
|
5647
5789
|
|
|
5790
|
+
// src/fs/pages.ts
|
|
5791
|
+
var PAGE = 64 * 1024;
|
|
5792
|
+
var READAHEAD = 1024 * 1024;
|
|
5793
|
+
var CLEAN_LIMIT = 4 * 1024 * 1024;
|
|
5794
|
+
var WRITEBACK = 8 * 1024 * 1024;
|
|
5795
|
+
var RUN = 1024 * 1024;
|
|
5796
|
+
var Pages = class {
|
|
5797
|
+
length;
|
|
5798
|
+
stored;
|
|
5799
|
+
shrink;
|
|
5800
|
+
pages = /* @__PURE__ */ new Map();
|
|
5801
|
+
dirty = /* @__PURE__ */ new Set();
|
|
5802
|
+
free = [];
|
|
5803
|
+
pinned = false;
|
|
5804
|
+
constructor(size) {
|
|
5805
|
+
this.length = size;
|
|
5806
|
+
this.stored = size;
|
|
5807
|
+
}
|
|
5808
|
+
get dirtyBytes() {
|
|
5809
|
+
return this.dirty.size * PAGE;
|
|
5810
|
+
}
|
|
5811
|
+
get changed() {
|
|
5812
|
+
return this.dirty.size > 0 || this.shrink !== void 0 || this.length !== this.stored;
|
|
5813
|
+
}
|
|
5814
|
+
missing(at, length2) {
|
|
5815
|
+
this.evict();
|
|
5816
|
+
const end = Math.min(this.stored, at + length2, this.length);
|
|
5817
|
+
const runs = [];
|
|
5818
|
+
for (let page = Math.floor(at / PAGE); page * PAGE < end; page++) {
|
|
5819
|
+
if (this.pages.has(page)) continue;
|
|
5820
|
+
const last2 = runs[runs.length - 1];
|
|
5821
|
+
if (last2 && last2[0] + last2[1] === page * PAGE) last2[1] += PAGE;
|
|
5822
|
+
else runs.push([page * PAGE, PAGE]);
|
|
5823
|
+
}
|
|
5824
|
+
const last = runs[runs.length - 1];
|
|
5825
|
+
if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
|
|
5826
|
+
return runs.map(([start, n]) => [start, Math.min(n, this.stored - start)]);
|
|
5827
|
+
}
|
|
5828
|
+
partial(at, length2) {
|
|
5829
|
+
if (length2 === 0) return [];
|
|
5830
|
+
this.evict();
|
|
5831
|
+
const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length2 - 1) / PAGE)]);
|
|
5832
|
+
const runs = [];
|
|
5833
|
+
for (const page of edges) {
|
|
5834
|
+
const start = page * PAGE;
|
|
5835
|
+
const covered = at <= start && at + length2 >= start + PAGE;
|
|
5836
|
+
if (covered || this.pages.has(page) || start >= this.stored) continue;
|
|
5837
|
+
runs.push([start, Math.min(PAGE, this.stored - start)]);
|
|
5838
|
+
}
|
|
5839
|
+
return runs;
|
|
5840
|
+
}
|
|
5841
|
+
fill(start, bytes) {
|
|
5842
|
+
for (let at = 0; at < bytes.length; at += PAGE) {
|
|
5843
|
+
const page = (start + at) / PAGE;
|
|
5844
|
+
if (this.pages.has(page)) continue;
|
|
5845
|
+
const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
|
|
5846
|
+
const fresh = this.take(part.length);
|
|
5847
|
+
fresh.set(part);
|
|
5848
|
+
this.pages.set(page, fresh);
|
|
5849
|
+
}
|
|
5850
|
+
}
|
|
5851
|
+
read(at, length2) {
|
|
5852
|
+
const out = new Uint8Array(Math.max(0, Math.min(this.length, at + length2) - at));
|
|
5853
|
+
this.readInto(at, out);
|
|
5854
|
+
return out;
|
|
5855
|
+
}
|
|
5856
|
+
readInto(at, out) {
|
|
5857
|
+
const end = Math.min(this.length, at + out.length);
|
|
5858
|
+
for (let pos = at; pos < end; ) {
|
|
5859
|
+
const page = Math.floor(pos / PAGE);
|
|
5860
|
+
const from = pos - page * PAGE;
|
|
5861
|
+
const n = Math.min(PAGE - from, end - pos);
|
|
5862
|
+
const bytes = this.pages.get(page);
|
|
5863
|
+
if (bytes) out.set(bytes.subarray(from, from + n), pos - at);
|
|
5864
|
+
else out.fill(0, pos - at, pos - at + n);
|
|
5865
|
+
pos += n;
|
|
5866
|
+
}
|
|
5867
|
+
return Math.max(0, end - at);
|
|
5868
|
+
}
|
|
5869
|
+
write(at, bytes) {
|
|
5870
|
+
for (let done4 = 0; done4 < bytes.length; ) {
|
|
5871
|
+
const pos = at + done4;
|
|
5872
|
+
const page = Math.floor(pos / PAGE);
|
|
5873
|
+
const from = pos - page * PAGE;
|
|
5874
|
+
const n = Math.min(PAGE - from, bytes.length - done4);
|
|
5875
|
+
let target = this.pages.get(page);
|
|
5876
|
+
if (!target) {
|
|
5877
|
+
target = this.take(0);
|
|
5878
|
+
this.pages.set(page, target);
|
|
5879
|
+
}
|
|
5880
|
+
target.set(bytes.subarray(done4, done4 + n), from);
|
|
5881
|
+
this.dirty.add(page);
|
|
5882
|
+
done4 += n;
|
|
5883
|
+
}
|
|
5884
|
+
this.length = Math.max(this.length, at + bytes.length);
|
|
5885
|
+
}
|
|
5886
|
+
truncate(size) {
|
|
5887
|
+
if (size < this.stored) {
|
|
5888
|
+
this.stored = size;
|
|
5889
|
+
this.shrink = Math.min(this.shrink ?? size, size);
|
|
5890
|
+
}
|
|
5891
|
+
if (size < this.length) {
|
|
5892
|
+
const edge = Math.floor(size / PAGE);
|
|
5893
|
+
for (const page of [...this.pages.keys()]) {
|
|
5894
|
+
if (page > edge || page === edge && size === edge * PAGE) this.drop(page);
|
|
5895
|
+
}
|
|
5896
|
+
this.pages.get(edge)?.fill(0, size - edge * PAGE);
|
|
5897
|
+
}
|
|
5898
|
+
this.length = size;
|
|
5899
|
+
}
|
|
5900
|
+
plan() {
|
|
5901
|
+
const runs = [];
|
|
5902
|
+
const sorted = [...this.dirty].sort((a, b) => a - b);
|
|
5903
|
+
for (let i = 0; i < sorted.length; ) {
|
|
5904
|
+
let j = i + 1;
|
|
5905
|
+
const first = sorted[i];
|
|
5906
|
+
while (j < sorted.length && sorted[j] === first + (j - i) && sorted[j] * PAGE % RUN !== 0) {
|
|
5907
|
+
j++;
|
|
5908
|
+
}
|
|
5909
|
+
const start = first * PAGE;
|
|
5910
|
+
const end = Math.min(this.length, (first + (j - i)) * PAGE);
|
|
5911
|
+
const bytes = new Uint8Array(end - start);
|
|
5912
|
+
for (let k = i; k < j; k++) {
|
|
5913
|
+
const page = this.pages.get(sorted[k]);
|
|
5914
|
+
bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start)), (k - i) * PAGE);
|
|
5915
|
+
}
|
|
5916
|
+
runs.push({ offset: start, bytes });
|
|
5917
|
+
i = j;
|
|
5918
|
+
}
|
|
5919
|
+
const written = Math.max(this.stored, ...runs.map((r) => r.offset + r.bytes.length));
|
|
5920
|
+
return {
|
|
5921
|
+
...this.shrink !== void 0 ? { shrink: this.shrink } : {},
|
|
5922
|
+
runs,
|
|
5923
|
+
...this.length > written ? { extend: this.length } : {}
|
|
5924
|
+
};
|
|
5925
|
+
}
|
|
5926
|
+
flushed() {
|
|
5927
|
+
this.dirty.clear();
|
|
5928
|
+
this.shrink = void 0;
|
|
5929
|
+
this.stored = this.length;
|
|
5930
|
+
this.evict();
|
|
5931
|
+
}
|
|
5932
|
+
pin() {
|
|
5933
|
+
this.pinned = true;
|
|
5934
|
+
}
|
|
5935
|
+
reset(size) {
|
|
5936
|
+
if (this.pinned) return;
|
|
5937
|
+
for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
|
|
5938
|
+
if (this.changed) return;
|
|
5939
|
+
this.length = size;
|
|
5940
|
+
this.stored = size;
|
|
5941
|
+
}
|
|
5942
|
+
evict() {
|
|
5943
|
+
if (this.pinned) return;
|
|
5944
|
+
let clean = (this.pages.size - this.dirty.size) * PAGE;
|
|
5945
|
+
for (const page of this.pages.keys()) {
|
|
5946
|
+
if (clean <= CLEAN_LIMIT) return;
|
|
5947
|
+
if (this.dirty.has(page)) continue;
|
|
5948
|
+
this.drop(page);
|
|
5949
|
+
clean -= PAGE;
|
|
5950
|
+
}
|
|
5951
|
+
}
|
|
5952
|
+
take(used) {
|
|
5953
|
+
const page = this.free.pop();
|
|
5954
|
+
if (!page) return new Uint8Array(PAGE);
|
|
5955
|
+
page.fill(0, used);
|
|
5956
|
+
return page;
|
|
5957
|
+
}
|
|
5958
|
+
drop(page) {
|
|
5959
|
+
const bytes = this.pages.get(page);
|
|
5960
|
+
this.pages.delete(page);
|
|
5961
|
+
this.dirty.delete(page);
|
|
5962
|
+
if (this.free.length < CLEAN_LIMIT / PAGE) this.free.push(bytes);
|
|
5963
|
+
}
|
|
5964
|
+
};
|
|
5965
|
+
|
|
5966
|
+
// src/fs/ranged.ts
|
|
5967
|
+
var AsyncRangedFile = class {
|
|
5968
|
+
pages;
|
|
5969
|
+
io;
|
|
5970
|
+
pinned = false;
|
|
5971
|
+
path;
|
|
5972
|
+
constructor(io, path, size) {
|
|
5973
|
+
this.io = io;
|
|
5974
|
+
this.path = path;
|
|
5975
|
+
this.pages = new Pages(size);
|
|
5976
|
+
}
|
|
5977
|
+
size() {
|
|
5978
|
+
return this.pages.length;
|
|
5979
|
+
}
|
|
5980
|
+
async fetch(runs) {
|
|
5981
|
+
for (const [start, n] of runs) this.pages.fill(start, await this.io.pread(this.path, start, n));
|
|
5982
|
+
}
|
|
5983
|
+
async read(at, length2) {
|
|
5984
|
+
await this.fetch(this.pages.missing(at, length2));
|
|
5985
|
+
return this.pages.read(at, length2);
|
|
5986
|
+
}
|
|
5987
|
+
async write(at, bytes) {
|
|
5988
|
+
await this.fetch(this.pages.partial(at, bytes.length));
|
|
5989
|
+
this.pages.write(at, bytes);
|
|
5990
|
+
if (!this.pinned && this.pages.dirtyBytes >= WRITEBACK) await this.flush();
|
|
5991
|
+
}
|
|
5992
|
+
truncate(size) {
|
|
5993
|
+
this.pages.truncate(size);
|
|
5994
|
+
}
|
|
5995
|
+
async flush() {
|
|
5996
|
+
if (this.pinned || !this.pages.changed) return;
|
|
5997
|
+
const plan = this.pages.plan();
|
|
5998
|
+
if (plan.shrink !== void 0) await this.io.truncate(this.path, plan.shrink);
|
|
5999
|
+
for (const run2 of plan.runs) await this.io.pwrite(this.path, run2.offset, run2.bytes, true);
|
|
6000
|
+
if (plan.extend !== void 0) await this.io.truncate(this.path, plan.extend);
|
|
6001
|
+
this.pages.flushed();
|
|
6002
|
+
}
|
|
6003
|
+
async pin() {
|
|
6004
|
+
this.pinned = true;
|
|
6005
|
+
this.pages.pin();
|
|
6006
|
+
await this.fetch(this.pages.missing(0, this.pages.length));
|
|
6007
|
+
}
|
|
6008
|
+
};
|
|
6009
|
+
|
|
5648
6010
|
// src/kernel/vfs-file.ts
|
|
5649
6011
|
var O_ACCMODE = 3;
|
|
5650
6012
|
var O_WRONLY = 1;
|
|
@@ -5664,6 +6026,7 @@ function within3(path, root) {
|
|
|
5664
6026
|
}
|
|
5665
6027
|
var VfsNode = class {
|
|
5666
6028
|
data;
|
|
6029
|
+
ranged;
|
|
5667
6030
|
length = 0;
|
|
5668
6031
|
dirty = false;
|
|
5669
6032
|
queue = Promise.resolve();
|
|
@@ -5694,6 +6057,23 @@ var VfsNode = class {
|
|
|
5694
6057
|
clearTimeout(this.writeBack);
|
|
5695
6058
|
this.writeBack = void 0;
|
|
5696
6059
|
this.data = void 0;
|
|
6060
|
+
this.ranged = void 0;
|
|
6061
|
+
}
|
|
6062
|
+
async prepare() {
|
|
6063
|
+
if (this.revoked) throw new KernelError("EIO");
|
|
6064
|
+
if (this.ranged || this.data) return this.ranged;
|
|
6065
|
+
const io = rangedOps(this.fs);
|
|
6066
|
+
const st = io && !this.orphaned ? await this.fs.stat?.(this.path).catch(() => void 0) : void 0;
|
|
6067
|
+
if (io && st?.ranged) {
|
|
6068
|
+
this.ranged = new AsyncRangedFile(io, this.path, st.size ?? 0);
|
|
6069
|
+
return this.ranged;
|
|
6070
|
+
}
|
|
6071
|
+
await this.load();
|
|
6072
|
+
return void 0;
|
|
6073
|
+
}
|
|
6074
|
+
async keep() {
|
|
6075
|
+
const ranged2 = await this.prepare();
|
|
6076
|
+
await ranged2?.pin();
|
|
5697
6077
|
}
|
|
5698
6078
|
async load() {
|
|
5699
6079
|
if (this.revoked) throw new KernelError("EIO");
|
|
@@ -5710,22 +6090,26 @@ var VfsNode = class {
|
|
|
5710
6090
|
return this.data;
|
|
5711
6091
|
}
|
|
5712
6092
|
replace(contents2) {
|
|
6093
|
+
this.ranged = void 0;
|
|
5713
6094
|
this.data = new Uint8Array(contents2);
|
|
5714
6095
|
this.length = this.data.length;
|
|
5715
6096
|
this.missing = false;
|
|
5716
6097
|
this.markDirty();
|
|
5717
6098
|
}
|
|
5718
6099
|
async materialize() {
|
|
5719
|
-
await this.
|
|
6100
|
+
if (await this.prepare()) return;
|
|
5720
6101
|
if (!this.missing || this.orphaned) return;
|
|
5721
6102
|
this.missing = false;
|
|
5722
6103
|
await this.fs.writeFile(this.path, this.data?.slice(0, this.length) ?? new Uint8Array(0));
|
|
6104
|
+
if (!this.dirty) this.data = void 0;
|
|
5723
6105
|
}
|
|
5724
6106
|
async size() {
|
|
5725
|
-
await this.
|
|
5726
|
-
return this.length;
|
|
6107
|
+
const ranged2 = await this.prepare();
|
|
6108
|
+
return ranged2 ? ranged2.size() : this.length;
|
|
5727
6109
|
}
|
|
5728
6110
|
async pread(max, at) {
|
|
6111
|
+
const ranged2 = await this.prepare();
|
|
6112
|
+
if (ranged2) return ranged2.read(at, max);
|
|
5729
6113
|
const bytes = await this.load();
|
|
5730
6114
|
const n = Math.max(0, Math.min(max, this.length - at));
|
|
5731
6115
|
return bytes.slice(at, at + n);
|
|
@@ -5748,12 +6132,18 @@ var VfsNode = class {
|
|
|
5748
6132
|
}
|
|
5749
6133
|
renamedTo(path) {
|
|
5750
6134
|
this.path = path;
|
|
6135
|
+
if (this.ranged) this.ranged.path = path;
|
|
5751
6136
|
this.cap = void 0;
|
|
5752
6137
|
}
|
|
5753
6138
|
async pwrite(bytes, at) {
|
|
5754
6139
|
if (bytes.length === 0) return 0;
|
|
5755
6140
|
await this.fits(at + bytes.length);
|
|
5756
|
-
await this.
|
|
6141
|
+
const ranged2 = await this.prepare();
|
|
6142
|
+
if (ranged2) {
|
|
6143
|
+
await ranged2.write(at, bytes);
|
|
6144
|
+
this.markDirty();
|
|
6145
|
+
return bytes.length;
|
|
6146
|
+
}
|
|
5757
6147
|
const buf = this.ensure(at + bytes.length);
|
|
5758
6148
|
if (at > this.length) buf.fill(0, this.length, at);
|
|
5759
6149
|
buf.set(bytes, at);
|
|
@@ -5763,7 +6153,12 @@ var VfsNode = class {
|
|
|
5763
6153
|
}
|
|
5764
6154
|
async truncate(size) {
|
|
5765
6155
|
await this.fits(size);
|
|
5766
|
-
await this.
|
|
6156
|
+
const ranged2 = await this.prepare();
|
|
6157
|
+
if (ranged2) {
|
|
6158
|
+
ranged2.truncate(size);
|
|
6159
|
+
this.markDirty();
|
|
6160
|
+
return;
|
|
6161
|
+
}
|
|
5767
6162
|
const buf = this.ensure(size);
|
|
5768
6163
|
if (size > this.length) buf.fill(0, this.length, size);
|
|
5769
6164
|
this.length = size;
|
|
@@ -5788,11 +6183,12 @@ var VfsNode = class {
|
|
|
5788
6183
|
}
|
|
5789
6184
|
async flush() {
|
|
5790
6185
|
if (this.revoked && this.dirty) throw new KernelError("EIO");
|
|
5791
|
-
if (!this.dirty || !this.data || this.
|
|
6186
|
+
if (!this.dirty || this.orphaned || !(this.data || this.ranged)) return;
|
|
5792
6187
|
this.dirty = false;
|
|
5793
6188
|
const started = performance.now();
|
|
5794
6189
|
try {
|
|
5795
|
-
|
|
6190
|
+
if (this.ranged) await this.ranged.flush();
|
|
6191
|
+
else await this.fs.writeFile(this.path, this.data.slice(0, this.length));
|
|
5796
6192
|
} catch (err) {
|
|
5797
6193
|
this.dirty = true;
|
|
5798
6194
|
throw err;
|
|
@@ -5839,7 +6235,7 @@ var VfsNodes = class {
|
|
|
5839
6235
|
}
|
|
5840
6236
|
async unlinking(path) {
|
|
5841
6237
|
const node = this.byPath.get(path);
|
|
5842
|
-
if (node) await node.serial(async () => void await Promise.all([node.
|
|
6238
|
+
if (node) await node.serial(async () => void await Promise.all([node.keep(), node.limit()]));
|
|
5843
6239
|
}
|
|
5844
6240
|
revoke(prefix) {
|
|
5845
6241
|
for (const [path, node] of this.byPath) {
|
|
@@ -6988,7 +7384,8 @@ async function dispatch(h, req) {
|
|
|
6988
7384
|
case "setattr":
|
|
6989
7385
|
return h.setattr?.(req.path, {
|
|
6990
7386
|
...req.mode !== void 0 ? { mode: req.mode } : {},
|
|
6991
|
-
...req.mtime !== void 0 ? { mtime: req.mtime } : {}
|
|
7387
|
+
...req.mtime !== void 0 ? { mtime: req.mtime } : {},
|
|
7388
|
+
...req.size !== void 0 ? { size: req.size } : {}
|
|
6992
7389
|
});
|
|
6993
7390
|
case "statfs":
|
|
6994
7391
|
return h.statfs?.() ?? null;
|
|
@@ -6996,7 +7393,7 @@ async function dispatch(h, req) {
|
|
|
6996
7393
|
throw fsError2("ENOSYS", `unknown driver op ${req.op}`);
|
|
6997
7394
|
}
|
|
6998
7395
|
}
|
|
6999
|
-
function serveFilesystem(port, handlers, capabilities = {}) {
|
|
7396
|
+
function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
7000
7397
|
let open = false;
|
|
7001
7398
|
const reply = (message2, transfer = []) => port.postMessage(message2, transfer);
|
|
7002
7399
|
async function hello(message2) {
|
|
@@ -7032,7 +7429,8 @@ function serveFilesystem(port, handlers, capabilities = {}) {
|
|
|
7032
7429
|
dispatch(handlers, data).then(
|
|
7033
7430
|
(result) => {
|
|
7034
7431
|
if (result instanceof Uint8Array) {
|
|
7035
|
-
const
|
|
7432
|
+
const whole = result.byteOffset === 0 && result.byteLength === result.buffer.byteLength;
|
|
7433
|
+
const bytes = options.owned && whole ? result : result.slice();
|
|
7036
7434
|
reply({ id: data.id, result: bytes }, [bytes.buffer]);
|
|
7037
7435
|
} else reply({ id: data.id, result: result ?? null });
|
|
7038
7436
|
},
|
|
@@ -7339,28 +7737,34 @@ function wait(ms, signal) {
|
|
|
7339
7737
|
});
|
|
7340
7738
|
}
|
|
7341
7739
|
async function mountFstab(lines, mount, signal, retries = FSTAB_RETRIES, discard = () => {
|
|
7740
|
+
}, settled = () => {
|
|
7342
7741
|
}) {
|
|
7343
7742
|
return Promise.all(
|
|
7344
|
-
lines.map(async (
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
const entry = await mount(spec);
|
|
7349
|
-
if (!signal.aborted) return { line, target: spec.target, entry };
|
|
7350
|
-
discard(entry);
|
|
7351
|
-
failed = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
|
|
7352
|
-
break;
|
|
7353
|
-
} catch (err) {
|
|
7354
|
-
failed = err;
|
|
7355
|
-
if (FINAL.has(codeOf3(err)) || attempt === retries.length) break;
|
|
7356
|
-
await wait(retries[attempt], signal);
|
|
7357
|
-
}
|
|
7358
|
-
}
|
|
7359
|
-
const code = codeOf3(failed);
|
|
7360
|
-
return { line, target: spec.target, code, error: String(failed?.message ?? code) };
|
|
7743
|
+
lines.map(async (each) => {
|
|
7744
|
+
const result = await mountLine(each, mount, signal, retries, discard);
|
|
7745
|
+
settled(each, result);
|
|
7746
|
+
return result;
|
|
7361
7747
|
})
|
|
7362
7748
|
);
|
|
7363
7749
|
}
|
|
7750
|
+
async function mountLine({ line, spec, invalid }, mount, signal, retries, discard) {
|
|
7751
|
+
let failed = invalid;
|
|
7752
|
+
for (let attempt = 0; !invalid && attempt <= retries.length && !signal.aborted; attempt++) {
|
|
7753
|
+
try {
|
|
7754
|
+
const entry = await mount(spec);
|
|
7755
|
+
if (!signal.aborted) return { line, target: spec.target, entry };
|
|
7756
|
+
discard(entry);
|
|
7757
|
+
failed = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
|
|
7758
|
+
break;
|
|
7759
|
+
} catch (err) {
|
|
7760
|
+
failed = err;
|
|
7761
|
+
if (FINAL.has(codeOf3(err)) || attempt === retries.length) break;
|
|
7762
|
+
await wait(retries[attempt], signal);
|
|
7763
|
+
}
|
|
7764
|
+
}
|
|
7765
|
+
const code = codeOf3(failed);
|
|
7766
|
+
return { line, target: spec.target, code, error: String(failed?.message ?? code) };
|
|
7767
|
+
}
|
|
7364
7768
|
|
|
7365
7769
|
// src/mount/hostfs.ts
|
|
7366
7770
|
var HOSTFS_IO = 16 * 1024 * 1024;
|
|
@@ -7698,6 +8102,58 @@ async function openHostfs(spec, hook, fetch2, timing = {}) {
|
|
|
7698
8102
|
};
|
|
7699
8103
|
}
|
|
7700
8104
|
|
|
8105
|
+
// src/mount/chunked.ts
|
|
8106
|
+
var CHUNK2 = 1024 * 1024;
|
|
8107
|
+
var ChunkedBytes = class {
|
|
8108
|
+
chunks = [];
|
|
8109
|
+
size = 0;
|
|
8110
|
+
read(offset, length2) {
|
|
8111
|
+
const end = Math.min(this.size, offset + length2);
|
|
8112
|
+
if (end <= offset) return new Uint8Array(0);
|
|
8113
|
+
const out = new Uint8Array(end - offset);
|
|
8114
|
+
for (let at = offset; at < end; ) {
|
|
8115
|
+
const index = Math.floor(at / CHUNK2);
|
|
8116
|
+
const from = at - index * CHUNK2;
|
|
8117
|
+
const n = Math.min(CHUNK2 - from, end - at);
|
|
8118
|
+
const chunk2 = this.chunks[index];
|
|
8119
|
+
if (chunk2) out.set(chunk2.subarray(from, Math.min(from + n, chunk2.length)), at - offset);
|
|
8120
|
+
at += n;
|
|
8121
|
+
}
|
|
8122
|
+
return out;
|
|
8123
|
+
}
|
|
8124
|
+
write(offset, bytes, owned = false) {
|
|
8125
|
+
const whole = owned && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
|
|
8126
|
+
for (let done4 = 0; done4 < bytes.length; ) {
|
|
8127
|
+
const at = offset + done4;
|
|
8128
|
+
const index = Math.floor(at / CHUNK2);
|
|
8129
|
+
const from = at - index * CHUNK2;
|
|
8130
|
+
const n = Math.min(CHUNK2 - from, bytes.length - done4);
|
|
8131
|
+
if (whole && n === CHUNK2 && bytes.length === CHUNK2) this.chunks[index] = bytes;
|
|
8132
|
+
else this.room(index, from + n).set(bytes.subarray(done4, done4 + n), from);
|
|
8133
|
+
done4 += n;
|
|
8134
|
+
}
|
|
8135
|
+
this.size = Math.max(this.size, offset + bytes.length);
|
|
8136
|
+
}
|
|
8137
|
+
truncate(size) {
|
|
8138
|
+
if (size < this.size) {
|
|
8139
|
+
const keep = Math.ceil(size / CHUNK2);
|
|
8140
|
+
this.chunks.length = Math.min(this.chunks.length, keep);
|
|
8141
|
+
const tail = this.chunks[keep - 1];
|
|
8142
|
+
const from = size - (keep - 1) * CHUNK2;
|
|
8143
|
+
if (tail && from < tail.length) tail.fill(0, from);
|
|
8144
|
+
}
|
|
8145
|
+
this.size = size;
|
|
8146
|
+
}
|
|
8147
|
+
room(index, need) {
|
|
8148
|
+
const chunk2 = this.chunks[index];
|
|
8149
|
+
if (chunk2 && chunk2.length >= need) return chunk2;
|
|
8150
|
+
const grown = new Uint8Array(Math.min(CHUNK2, Math.max(need, (chunk2?.length ?? 0) * 2)));
|
|
8151
|
+
if (chunk2) grown.set(chunk2);
|
|
8152
|
+
this.chunks[index] = grown;
|
|
8153
|
+
return grown;
|
|
8154
|
+
}
|
|
8155
|
+
};
|
|
8156
|
+
|
|
7701
8157
|
// src/mount/tmpfs.ts
|
|
7702
8158
|
function tmpfs() {
|
|
7703
8159
|
let inodes = 0;
|
|
@@ -7706,7 +8162,7 @@ function tmpfs() {
|
|
|
7706
8162
|
ino: ++inodes,
|
|
7707
8163
|
mode,
|
|
7708
8164
|
mtime: Date.now(),
|
|
7709
|
-
data: new
|
|
8165
|
+
data: new ChunkedBytes(),
|
|
7710
8166
|
target: "",
|
|
7711
8167
|
children: /* @__PURE__ */ new Map()
|
|
7712
8168
|
});
|
|
@@ -7734,7 +8190,7 @@ function tmpfs() {
|
|
|
7734
8190
|
}
|
|
7735
8191
|
const attr = (node) => ({
|
|
7736
8192
|
kind: node.kind,
|
|
7737
|
-
size: node.kind === "file" ? node.data.
|
|
8193
|
+
size: node.kind === "file" ? node.data.size : node.kind === "symlink" ? node.target.length : 4096,
|
|
7738
8194
|
mtime: node.mtime,
|
|
7739
8195
|
mode: node.mode,
|
|
7740
8196
|
ino: node.ino
|
|
@@ -7769,32 +8225,25 @@ function tmpfs() {
|
|
|
7769
8225
|
},
|
|
7770
8226
|
async open(path, flags) {
|
|
7771
8227
|
const node = opened2(path, flags);
|
|
7772
|
-
|
|
7773
|
-
|
|
8228
|
+
if (flags.truncate && node.kind === "file") {
|
|
8229
|
+
node.data.truncate(0);
|
|
8230
|
+
node.mtime = Date.now();
|
|
8231
|
+
}
|
|
8232
|
+
handles.set(++nextFh, { node, write: flags.write });
|
|
7774
8233
|
return nextFh;
|
|
7775
8234
|
},
|
|
7776
8235
|
async read(fh, offset, size) {
|
|
7777
|
-
|
|
7778
|
-
return h.data.slice(offset, Math.min(h.length, offset + size));
|
|
8236
|
+
return handle(fh).node.data.read(offset, size);
|
|
7779
8237
|
},
|
|
7780
8238
|
async write(fh, offset, bytes) {
|
|
7781
8239
|
const h = handle(fh);
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
grown.set(h.data.subarray(0, h.length));
|
|
7786
|
-
h.data = grown;
|
|
7787
|
-
}
|
|
7788
|
-
h.data.set(bytes, offset);
|
|
7789
|
-
h.length = Math.max(h.length, end);
|
|
7790
|
-
h.dirty = true;
|
|
8240
|
+
if (!h.write) throw fsError2("EBADF", `handle ${fh} is read-only`);
|
|
8241
|
+
h.node.data.write(offset, bytes, true);
|
|
8242
|
+
h.node.mtime = Date.now();
|
|
7791
8243
|
},
|
|
7792
8244
|
async release(fh) {
|
|
7793
|
-
|
|
8245
|
+
handle(fh);
|
|
7794
8246
|
handles.delete(fh);
|
|
7795
|
-
if (!h.dirty) return;
|
|
7796
|
-
h.node.data = h.data.slice(0, h.length);
|
|
7797
|
-
h.node.mtime = Date.now();
|
|
7798
8247
|
},
|
|
7799
8248
|
async mkdir(path) {
|
|
7800
8249
|
const { dir, name: name2 } = parent(path);
|
|
@@ -7847,6 +8296,12 @@ function tmpfs() {
|
|
|
7847
8296
|
async setattr(path, change) {
|
|
7848
8297
|
const node = find(path);
|
|
7849
8298
|
if (change.mode !== void 0) node.mode = node.mode & ~4095 | change.mode & 4095;
|
|
8299
|
+
if (change.size !== void 0) {
|
|
8300
|
+
if (node.kind !== "file")
|
|
8301
|
+
throw fsError2(node.kind === "directory" ? "EISDIR" : "EINVAL", path);
|
|
8302
|
+
node.data.truncate(change.size);
|
|
8303
|
+
node.mtime = Date.now();
|
|
8304
|
+
}
|
|
7850
8305
|
if (change.mtime !== void 0) node.mtime = change.mtime;
|
|
7851
8306
|
},
|
|
7852
8307
|
async statfs() {
|
|
@@ -8112,6 +8567,7 @@ var Launcher = class {
|
|
|
8112
8567
|
onMountPending;
|
|
8113
8568
|
removable = /* @__PURE__ */ new Map();
|
|
8114
8569
|
hostfs;
|
|
8570
|
+
fstabRetries;
|
|
8115
8571
|
inserted = /* @__PURE__ */ new Map();
|
|
8116
8572
|
openFiles = /* @__PURE__ */ new Set();
|
|
8117
8573
|
held = /* @__PURE__ */ new Set();
|
|
@@ -8133,6 +8589,7 @@ var Launcher = class {
|
|
|
8133
8589
|
this.media = options.media ?? memoryMedia();
|
|
8134
8590
|
this.onMountPending = options.onMountPending;
|
|
8135
8591
|
this.hostfs = options;
|
|
8592
|
+
this.fstabRetries = options.fstabRetries ?? FSTAB_RETRIES;
|
|
8136
8593
|
this.processMounts = options.processMounts ?? true;
|
|
8137
8594
|
this.pnpmHome = options.env?.PNPM_HOME ?? PNPM_HOME;
|
|
8138
8595
|
this.modulesDir = options.modules ?? "/node_modules";
|
|
@@ -8437,7 +8894,12 @@ var Launcher = class {
|
|
|
8437
8894
|
}
|
|
8438
8895
|
if (type === "tmpfs") {
|
|
8439
8896
|
const { port1, port2 } = new MessageChannel();
|
|
8440
|
-
serveFilesystem(
|
|
8897
|
+
serveFilesystem(
|
|
8898
|
+
port2,
|
|
8899
|
+
tmpfs(),
|
|
8900
|
+
{ symlinks: true, chmod: true, ranges: true, attrTtl: 0 },
|
|
8901
|
+
{ owned: true }
|
|
8902
|
+
);
|
|
8441
8903
|
return {
|
|
8442
8904
|
port: port1,
|
|
8443
8905
|
dispose: () => {
|
|
@@ -8561,12 +9023,22 @@ var Launcher = class {
|
|
|
8561
9023
|
}
|
|
8562
9024
|
async mountFstab() {
|
|
8563
9025
|
const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
|
|
9026
|
+
const lines = parseFstab(text);
|
|
9027
|
+
for (const { spec } of lines) this.mounts.note(spec, "pending");
|
|
8564
9028
|
return mountFstab(
|
|
8565
|
-
|
|
8566
|
-
(spec) => this.mount(spec)
|
|
9029
|
+
lines,
|
|
9030
|
+
(spec) => this.mount(spec).catch((err) => {
|
|
9031
|
+
this.mounts.note(spec, "pending", err?.message ?? String(err));
|
|
9032
|
+
throw err;
|
|
9033
|
+
}),
|
|
8567
9034
|
this.booting.signal,
|
|
8568
|
-
|
|
8569
|
-
(entry) => this.umount(entry.target, true)
|
|
9035
|
+
this.fstabRetries,
|
|
9036
|
+
(entry) => this.umount(entry.target, true),
|
|
9037
|
+
({ spec }, result) => {
|
|
9038
|
+
if (!result.code || result.code === "ECANCELED" || this.booting.signal.aborted) {
|
|
9039
|
+
this.mounts.unnote(spec.target);
|
|
9040
|
+
} else this.mounts.note(spec, "failed", result.error);
|
|
9041
|
+
}
|
|
8570
9042
|
);
|
|
8571
9043
|
}
|
|
8572
9044
|
environment(cwd, extra) {
|