@aptre/v86 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/v86.js CHANGED
@@ -106,6 +106,7 @@ var FW_CFG_FILE_START = 49152;
106
106
  var FW_CFG_SIGNATURE_QEMU = 1431127377;
107
107
  var WASM_TABLE_SIZE = 900;
108
108
  var WASM_TABLE_OFFSET = 1024;
109
+ var WASM_PAGE_SIZE = 65536;
109
110
  var MIXER_CHANNEL_LEFT = 0;
110
111
  var MIXER_CHANNEL_RIGHT = 1;
111
112
  var MIXER_CHANNEL_BOTH = 2;
@@ -5420,17 +5421,18 @@ var VirtIO = class {
5420
5421
  LOG_VIRTIO
5421
5422
  );
5422
5423
  }
5423
- if (data & ~this.device_status & VIRTIO_STATUS_DRIVER_OK && this.device_status & VIRTIO_STATUS_DEVICE_NEEDS_RESET) {
5424
- this.notify_config_changes();
5425
- }
5426
5424
  if (!this.features_ok) {
5427
5425
  if (false) {
5428
5426
  dbg_log("Removing FEATURES_OK", LOG_VIRTIO);
5429
5427
  }
5430
5428
  data &= ~VIRTIO_STATUS_FEATURES_OK;
5431
5429
  }
5430
+ const prev_status = this.device_status;
5432
5431
  this.device_status = data;
5433
- if (data & ~this.device_status & VIRTIO_STATUS_DRIVER_OK) {
5432
+ if (data & ~prev_status & VIRTIO_STATUS_DRIVER_OK) {
5433
+ if (prev_status & VIRTIO_STATUS_DEVICE_NEEDS_RESET) {
5434
+ this.notify_config_changes();
5435
+ }
5434
5436
  options.on_driver_ok();
5435
5437
  }
5436
5438
  }
@@ -5667,7 +5669,7 @@ var VirtIO = class {
5667
5669
  // Call only within constructor.
5668
5670
  init_capabilities(capabilities) {
5669
5671
  let cap_next = this.pci_space[52] = 64;
5670
- let cap_ptr = cap_next;
5672
+ let cap_ptr;
5671
5673
  for (const cap of capabilities) {
5672
5674
  const cap_len2 = VIRTIO_PCI_CAP_LENGTH + cap.extra.length;
5673
5675
  cap_ptr = cap_next;
@@ -6888,7 +6890,7 @@ var PCI = class {
6888
6890
  (out_byte) => {
6889
6891
  if ((this.pci_addr[1] & 6) === 2 && (out_byte & 6) === 6) {
6890
6892
  dbg_log("CPU reboot via PCI");
6891
- cpu.reboot_internal();
6893
+ cpu.reboot();
6892
6894
  return;
6893
6895
  }
6894
6896
  this.pci_addr[1] = out_byte;
@@ -6994,6 +6996,7 @@ var PCI = class {
6994
6996
  0,
6995
6997
  0,
6996
6998
  0,
6999
+ 0,
6997
7000
  PAM0,
6998
7001
  0,
6999
7002
  0,
@@ -7306,7 +7309,11 @@ var PCI = class {
7306
7309
  dbg_assert(device.pci_space.length >= 64);
7307
7310
  dbg_assert(device_id < this.devices.length);
7308
7311
  const space = new Int32Array(64);
7309
- space.set(new Int32Array(new Uint8Array(device.pci_space).buffer));
7312
+ const pci_bytes = new Uint8Array(device.pci_space);
7313
+ const aligned_len = pci_bytes.length & ~3;
7314
+ if (aligned_len > 0) {
7315
+ space.set(new Int32Array(pci_bytes.buffer, 0, aligned_len >> 2));
7316
+ }
7310
7317
  this.device_spaces[device_id] = space;
7311
7318
  this.devices[device_id] = device;
7312
7319
  const bar_space = space.slice(4, 10);
@@ -7957,7 +7964,7 @@ var PS2 = class {
7957
7964
  break;
7958
7965
  case 254:
7959
7966
  dbg_log("CPU reboot via PS2");
7960
- this.cpu.reboot_internal();
7967
+ this.cpu.reboot();
7961
7968
  break;
7962
7969
  default:
7963
7970
  dbg_log(
@@ -15102,12 +15109,930 @@ var VirtioBalloon = class {
15102
15109
  }
15103
15110
  };
15104
15111
 
15112
+ // src/virtio_mem.ts
15113
+ var VIRTIO_MEM_REQ_PLUG = 0;
15114
+ var VIRTIO_MEM_REQ_UNPLUG = 1;
15115
+ var VIRTIO_MEM_REQ_STATE = 3;
15116
+ var VIRTIO_MEM_RESP_ACK = 0;
15117
+ var VIRTIO_MEM_RESP_NACK = 1;
15118
+ var VIRTIO_MEM_RESP_ERROR = 3;
15119
+ var DEFAULT_BLOCK_SIZE = 128 * 1024 * 1024;
15120
+ var VirtioMem = class {
15121
+ bus;
15122
+ cpu;
15123
+ virtio;
15124
+ block_size;
15125
+ region_addr;
15126
+ region_size;
15127
+ usable_region_size;
15128
+ plugged_size;
15129
+ requested_size;
15130
+ constructor(cpu, bus, region_addr, region_size, block_size) {
15131
+ this.bus = bus;
15132
+ this.cpu = cpu;
15133
+ this.block_size = block_size || DEFAULT_BLOCK_SIZE;
15134
+ this.region_addr = region_addr;
15135
+ this.region_size = region_size;
15136
+ this.usable_region_size = region_size;
15137
+ this.plugged_size = 0;
15138
+ this.requested_size = 0;
15139
+ const queues = [{ size_supported: 32, notify_offset: 0 }];
15140
+ this.virtio = new VirtIO(cpu, {
15141
+ name: "virtio-mem",
15142
+ pci_id: 13 << 3,
15143
+ device_id: 4184,
15144
+ subsystem_device_id: 24,
15145
+ common: {
15146
+ initial_port: 59392,
15147
+ queues,
15148
+ features: [VIRTIO_F_VERSION_1],
15149
+ on_driver_ok: () => {
15150
+ dbg_log("virtio-mem setup", LOG_PCI);
15151
+ }
15152
+ },
15153
+ notification: {
15154
+ initial_port: 59648,
15155
+ single_handler: false,
15156
+ handlers: [
15157
+ (queue_id) => {
15158
+ this.handle_request(queue_id);
15159
+ }
15160
+ ]
15161
+ },
15162
+ isr_status: {
15163
+ initial_port: 59136
15164
+ },
15165
+ device_specific: {
15166
+ initial_port: 58880,
15167
+ struct: [
15168
+ // block_size low
15169
+ {
15170
+ bytes: 4,
15171
+ name: "block_size_low",
15172
+ read: () => this.block_size >>> 0,
15173
+ write: () => {
15174
+ }
15175
+ },
15176
+ // block_size high
15177
+ {
15178
+ bytes: 4,
15179
+ name: "block_size_high",
15180
+ read: () => 0,
15181
+ write: () => {
15182
+ }
15183
+ },
15184
+ // node_id (u16 padded to u32)
15185
+ {
15186
+ bytes: 2,
15187
+ name: "node_id",
15188
+ read: () => 0,
15189
+ write: () => {
15190
+ }
15191
+ },
15192
+ // padding (6 bytes as 2+4)
15193
+ {
15194
+ bytes: 2,
15195
+ name: "padding0",
15196
+ read: () => 0,
15197
+ write: () => {
15198
+ }
15199
+ },
15200
+ {
15201
+ bytes: 4,
15202
+ name: "padding1",
15203
+ read: () => 0,
15204
+ write: () => {
15205
+ }
15206
+ },
15207
+ // addr low
15208
+ {
15209
+ bytes: 4,
15210
+ name: "addr_low",
15211
+ read: () => this.region_addr >>> 0,
15212
+ write: () => {
15213
+ }
15214
+ },
15215
+ // addr high
15216
+ {
15217
+ bytes: 4,
15218
+ name: "addr_high",
15219
+ read: () => 0,
15220
+ write: () => {
15221
+ }
15222
+ },
15223
+ // region_size low
15224
+ {
15225
+ bytes: 4,
15226
+ name: "region_size_low",
15227
+ read: () => this.region_size >>> 0,
15228
+ write: () => {
15229
+ }
15230
+ },
15231
+ // region_size high
15232
+ {
15233
+ bytes: 4,
15234
+ name: "region_size_high",
15235
+ read: () => 0,
15236
+ write: () => {
15237
+ }
15238
+ },
15239
+ // usable_region_size low
15240
+ {
15241
+ bytes: 4,
15242
+ name: "usable_region_size_low",
15243
+ read: () => this.usable_region_size >>> 0,
15244
+ write: () => {
15245
+ }
15246
+ },
15247
+ // usable_region_size high
15248
+ {
15249
+ bytes: 4,
15250
+ name: "usable_region_size_high",
15251
+ read: () => 0,
15252
+ write: () => {
15253
+ }
15254
+ },
15255
+ // plugged_size low
15256
+ {
15257
+ bytes: 4,
15258
+ name: "plugged_size_low",
15259
+ read: () => this.plugged_size >>> 0,
15260
+ write: () => {
15261
+ }
15262
+ },
15263
+ // plugged_size high
15264
+ {
15265
+ bytes: 4,
15266
+ name: "plugged_size_high",
15267
+ read: () => 0,
15268
+ write: () => {
15269
+ }
15270
+ },
15271
+ // requested_size low
15272
+ {
15273
+ bytes: 4,
15274
+ name: "requested_size_low",
15275
+ read: () => this.requested_size >>> 0,
15276
+ write: () => {
15277
+ }
15278
+ },
15279
+ // requested_size high
15280
+ {
15281
+ bytes: 4,
15282
+ name: "requested_size_high",
15283
+ read: () => 0,
15284
+ write: () => {
15285
+ }
15286
+ }
15287
+ ]
15288
+ }
15289
+ });
15290
+ }
15291
+ handle_request(queue_id) {
15292
+ const queue = this.virtio.queues[queue_id];
15293
+ while (queue.has_request()) {
15294
+ const bufchain = queue.pop_request();
15295
+ const request = new Uint8Array(bufchain.length_readable);
15296
+ bufchain.get_next_blob(request);
15297
+ const type = request[0] | request[1] << 8;
15298
+ const resp = new Uint8Array(8);
15299
+ let resp_type = VIRTIO_MEM_RESP_ERROR;
15300
+ switch (type) {
15301
+ case VIRTIO_MEM_REQ_PLUG:
15302
+ resp_type = this.handle_plug(request);
15303
+ break;
15304
+ case VIRTIO_MEM_REQ_UNPLUG:
15305
+ resp_type = VIRTIO_MEM_RESP_NACK;
15306
+ break;
15307
+ case VIRTIO_MEM_REQ_STATE:
15308
+ resp_type = this.handle_state(request, resp);
15309
+ break;
15310
+ default:
15311
+ dbg_log("virtio-mem: unknown request type " + type, LOG_PCI);
15312
+ }
15313
+ resp[0] = resp_type & 255;
15314
+ resp[1] = resp_type >> 8 & 255;
15315
+ bufchain.set_next_blob(resp);
15316
+ queue.push_reply(bufchain);
15317
+ }
15318
+ queue.flush_replies();
15319
+ }
15320
+ handle_plug(request) {
15321
+ const addr = (request[8] | request[9] << 8 | request[10] << 16 | request[11] << 24) >>> 0;
15322
+ const nb_blocks = request[16] | request[17] << 8;
15323
+ const size = nb_blocks * this.block_size;
15324
+ const new_plugged = this.plugged_size + size;
15325
+ if (new_plugged > this.usable_region_size) {
15326
+ return VIRTIO_MEM_RESP_NACK;
15327
+ }
15328
+ const new_memory_size = this.cpu.memory_size[0] + size;
15329
+ this.cpu.resize_memory(new_memory_size);
15330
+ this.cpu.full_clear_tlb();
15331
+ this.plugged_size = new_plugged;
15332
+ dbg_log(
15333
+ "virtio-mem: plugged " + nb_blocks + " blocks at 0x" + addr.toString(16) + " (" + (size >> 20) + "MB)",
15334
+ LOG_PCI
15335
+ );
15336
+ return VIRTIO_MEM_RESP_ACK;
15337
+ }
15338
+ handle_state(request, resp) {
15339
+ const addr = (request[8] | request[9] << 8 | request[10] << 16 | request[11] << 24) >>> 0;
15340
+ const nb_blocks = request[16] | request[17] << 8;
15341
+ const offset = addr - this.region_addr;
15342
+ const end = offset + nb_blocks * this.block_size;
15343
+ const all_plugged = end <= this.plugged_size ? 1 : 0;
15344
+ resp[8] = all_plugged & 255;
15345
+ resp[9] = all_plugged >> 8 & 255;
15346
+ return VIRTIO_MEM_RESP_ACK;
15347
+ }
15348
+ set_requested_size(size) {
15349
+ this.requested_size = size;
15350
+ if (this.virtio.device_status & 4) {
15351
+ this.virtio.notify_config_changes();
15352
+ }
15353
+ }
15354
+ get_state() {
15355
+ const state = [];
15356
+ state[0] = this.virtio;
15357
+ state[1] = this.block_size;
15358
+ state[2] = this.region_addr;
15359
+ state[3] = this.region_size;
15360
+ state[4] = this.usable_region_size;
15361
+ state[5] = this.plugged_size;
15362
+ state[6] = this.requested_size;
15363
+ return state;
15364
+ }
15365
+ set_state(state) {
15366
+ this.virtio.set_state(state[0]);
15367
+ this.block_size = state[1];
15368
+ this.region_addr = state[2];
15369
+ this.region_size = state[3];
15370
+ this.usable_region_size = state[4];
15371
+ this.plugged_size = state[5];
15372
+ this.requested_size = state[6];
15373
+ }
15374
+ };
15375
+
15376
+ // src/virtio_v86fs.ts
15377
+ var V86FS_MSG_MOUNT = 0;
15378
+ var V86FS_MSG_LOOKUP = 1;
15379
+ var V86FS_MSG_GETATTR = 2;
15380
+ var V86FS_MSG_READDIR = 3;
15381
+ var V86FS_MSG_OPEN = 4;
15382
+ var V86FS_MSG_CLOSE = 5;
15383
+ var V86FS_MSG_READ = 6;
15384
+ var V86FS_MSG_CREATE = 7;
15385
+ var V86FS_MSG_WRITE = 8;
15386
+ var V86FS_MSG_MKDIR = 9;
15387
+ var V86FS_MSG_SETATTR = 10;
15388
+ var V86FS_MSG_FSYNC = 11;
15389
+ var V86FS_MSG_UNLINK = 12;
15390
+ var V86FS_MSG_RENAME = 13;
15391
+ var V86FS_MSG_SYMLINK = 14;
15392
+ var V86FS_MSG_READLINK = 15;
15393
+ var V86FS_MSG_STATFS = 16;
15394
+ var V86FS_MSG_INVALIDATE = 32;
15395
+ var V86FS_MSG_INVALIDATE_DIR = 33;
15396
+ var V86FS_MSG_MOUNT_R = 128;
15397
+ var V86FS_MSG_LOOKUP_R = 129;
15398
+ var V86FS_MSG_GETATTR_R = 130;
15399
+ var V86FS_MSG_READDIR_R = 131;
15400
+ var V86FS_MSG_OPEN_R = 132;
15401
+ var V86FS_MSG_CLOSE_R = 133;
15402
+ var V86FS_MSG_READ_R = 134;
15403
+ var V86FS_MSG_CREATE_R = 135;
15404
+ var V86FS_MSG_WRITE_R = 136;
15405
+ var V86FS_MSG_MKDIR_R = 137;
15406
+ var V86FS_MSG_SETATTR_R = 138;
15407
+ var V86FS_MSG_FSYNC_R = 139;
15408
+ var V86FS_MSG_UNLINK_R = 140;
15409
+ var V86FS_MSG_RENAME_R = 141;
15410
+ var V86FS_MSG_SYMLINK_R = 142;
15411
+ var V86FS_MSG_READLINK_R = 143;
15412
+ var V86FS_MSG_STATFS_R = 144;
15413
+ var ATTR_MODE = 1;
15414
+ var ATTR_SIZE = 8;
15415
+ var V86FS_STATUS_OK = 0;
15416
+ var V86FS_STATUS_ENOENT = 2;
15417
+ var DT_DIR = 4;
15418
+ var DT_REG = 8;
15419
+ var DT_LNK = 10;
15420
+ var S_IFDIR = 16384;
15421
+ var S_IFREG = 32768;
15422
+ var S_IFLNK = 40960;
15423
+ var textDecoder = new TextDecoder();
15424
+ var textEncoder = new TextEncoder();
15425
+ var FS_ENTRIES = /* @__PURE__ */ new Map([
15426
+ [
15427
+ 1,
15428
+ [
15429
+ {
15430
+ inode_id: 2,
15431
+ name: "hello.txt",
15432
+ mode: S_IFREG | 420,
15433
+ size: 12,
15434
+ dt_type: DT_REG,
15435
+ mtime_sec: 17115e5,
15436
+ mtime_nsec: 0,
15437
+ content: textEncoder.encode("hello world\n")
15438
+ },
15439
+ {
15440
+ inode_id: 3,
15441
+ name: "subdir",
15442
+ mode: S_IFDIR | 493,
15443
+ size: 0,
15444
+ dt_type: DT_DIR,
15445
+ mtime_sec: 17115e5,
15446
+ mtime_nsec: 0
15447
+ }
15448
+ ]
15449
+ ]
15450
+ ]);
15451
+ var INODE_MAP = /* @__PURE__ */ new Map([
15452
+ [
15453
+ 1,
15454
+ {
15455
+ inode_id: 1,
15456
+ name: "",
15457
+ mode: S_IFDIR | 493,
15458
+ size: 0,
15459
+ dt_type: DT_DIR,
15460
+ mtime_sec: 17115e5,
15461
+ mtime_nsec: 0
15462
+ }
15463
+ ]
15464
+ ]);
15465
+ for (const entries of FS_ENTRIES.values()) {
15466
+ for (const e of entries) {
15467
+ INODE_MAP.set(e.inode_id, e);
15468
+ }
15469
+ }
15470
+ var V86FS_HDR_SIZE = 7;
15471
+ function packU32(buf, offset, val) {
15472
+ buf[offset] = val & 255;
15473
+ buf[offset + 1] = val >>> 8 & 255;
15474
+ buf[offset + 2] = val >>> 16 & 255;
15475
+ buf[offset + 3] = val >>> 24 & 255;
15476
+ }
15477
+ function packU64(buf, offset, val) {
15478
+ packU32(buf, offset, val);
15479
+ packU32(buf, offset + 4, 0);
15480
+ }
15481
+ function packU16(buf, offset, val) {
15482
+ buf[offset] = val & 255;
15483
+ buf[offset + 1] = val >>> 8 & 255;
15484
+ }
15485
+ function makeResp(size, type, tag) {
15486
+ const resp = new Uint8Array(size);
15487
+ packU32(resp, 0, size);
15488
+ resp[4] = type;
15489
+ resp[5] = tag & 255;
15490
+ resp[6] = tag >> 8 & 255;
15491
+ return resp;
15492
+ }
15493
+ function readU32(buf, offset) {
15494
+ return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | buf[offset + 3] << 24 >>> 0;
15495
+ }
15496
+ function readU16(buf, offset) {
15497
+ return buf[offset] | buf[offset + 1] << 8;
15498
+ }
15499
+ function readU64(buf, offset) {
15500
+ return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | (buf[offset + 3] << 24 >>> 0) + (buf[offset + 4] | buf[offset + 5] << 8 | buf[offset + 6] << 16 | buf[offset + 7] << 24 >>> 0) * 4294967296;
15501
+ }
15502
+ var VirtioV86FS = class {
15503
+ bus;
15504
+ virtio;
15505
+ next_handle_id;
15506
+ next_inode_id;
15507
+ open_handles;
15508
+ // handle_id -> inode_id
15509
+ read_count;
15510
+ constructor(cpu, bus) {
15511
+ this.bus = bus;
15512
+ this.next_handle_id = 1;
15513
+ this.next_inode_id = 100;
15514
+ this.open_handles = /* @__PURE__ */ new Map();
15515
+ this.read_count = 0;
15516
+ const queues = [
15517
+ // Queue 0: hipriq - high-priority metadata (LOOKUP, GETATTR)
15518
+ { size_supported: 128, notify_offset: 0 },
15519
+ // Queue 1: requestq - data requests (READ, WRITE, READDIR)
15520
+ { size_supported: 128, notify_offset: 1 },
15521
+ // Queue 2: notifyq - host-to-guest push invalidation
15522
+ { size_supported: 128, notify_offset: 2 }
15523
+ ];
15524
+ this.virtio = new VirtIO(cpu, {
15525
+ name: "virtio-v86fs",
15526
+ pci_id: 14 << 3,
15527
+ device_id: 4223,
15528
+ subsystem_device_id: 63,
15529
+ common: {
15530
+ initial_port: 63488,
15531
+ queues,
15532
+ features: [VIRTIO_F_VERSION_1],
15533
+ on_driver_ok: () => {
15534
+ console.log("v86fs: driver ok");
15535
+ this.bus.send("virtio-v86fs-driver-ok");
15536
+ }
15537
+ },
15538
+ notification: {
15539
+ initial_port: 63744,
15540
+ single_handler: false,
15541
+ handlers: [
15542
+ // Queue 0: hipriq
15543
+ (queue_id) => {
15544
+ this.handle_queue(queue_id);
15545
+ },
15546
+ // Queue 1: requestq
15547
+ (queue_id) => {
15548
+ this.handle_queue(queue_id);
15549
+ },
15550
+ // Queue 2: notifyq
15551
+ (_queue_id) => {
15552
+ dbg_log("v86fs: notifyq notification", LOG_PCI);
15553
+ }
15554
+ ]
15555
+ },
15556
+ isr_status: {
15557
+ initial_port: 63232
15558
+ }
15559
+ });
15560
+ }
15561
+ handle_queue(queue_id) {
15562
+ const queue = this.virtio.queues[queue_id];
15563
+ while (queue.has_request()) {
15564
+ const bufchain = queue.pop_request();
15565
+ const req = new Uint8Array(bufchain.length_readable);
15566
+ bufchain.get_next_blob(req);
15567
+ const resp = this.handle_message(req);
15568
+ if (resp && bufchain.length_writable > 0) {
15569
+ bufchain.set_next_blob(resp);
15570
+ }
15571
+ queue.push_reply(bufchain);
15572
+ }
15573
+ queue.flush_replies();
15574
+ }
15575
+ handle_message(req) {
15576
+ if (req.length < V86FS_HDR_SIZE) {
15577
+ console.warn("v86fs: message too short:", req.length);
15578
+ return null;
15579
+ }
15580
+ const type = req[4];
15581
+ const tag = readU16(req, 5);
15582
+ switch (type) {
15583
+ case V86FS_MSG_MOUNT:
15584
+ return this.handle_mount(req, tag);
15585
+ case V86FS_MSG_LOOKUP:
15586
+ return this.handle_lookup(req, tag);
15587
+ case V86FS_MSG_GETATTR:
15588
+ return this.handle_getattr(req, tag);
15589
+ case V86FS_MSG_READDIR:
15590
+ return this.handle_readdir(req, tag);
15591
+ case V86FS_MSG_OPEN:
15592
+ return this.handle_open(req, tag);
15593
+ case V86FS_MSG_CLOSE:
15594
+ return this.handle_close(req, tag);
15595
+ case V86FS_MSG_READ:
15596
+ return this.handle_read(req, tag);
15597
+ case V86FS_MSG_CREATE:
15598
+ return this.handle_create(req, tag);
15599
+ case V86FS_MSG_WRITE:
15600
+ return this.handle_write(req, tag);
15601
+ case V86FS_MSG_MKDIR:
15602
+ return this.handle_mkdir(req, tag);
15603
+ case V86FS_MSG_SETATTR:
15604
+ return this.handle_setattr(req, tag);
15605
+ case V86FS_MSG_FSYNC:
15606
+ return this.handle_fsync(req, tag);
15607
+ case V86FS_MSG_UNLINK:
15608
+ return this.handle_unlink(req, tag);
15609
+ case V86FS_MSG_RENAME:
15610
+ return this.handle_rename(req, tag);
15611
+ case V86FS_MSG_SYMLINK:
15612
+ return this.handle_symlink(req, tag);
15613
+ case V86FS_MSG_READLINK:
15614
+ return this.handle_readlink(req, tag);
15615
+ case V86FS_MSG_STATFS:
15616
+ return this.handle_statfs(tag);
15617
+ default:
15618
+ console.warn("v86fs: unknown message type:", type);
15619
+ return null;
15620
+ }
15621
+ }
15622
+ handle_mount(req, tag) {
15623
+ const name_len = readU16(req, 7);
15624
+ const name = name_len > 0 ? textDecoder.decode(req.subarray(9, 9 + name_len)) : "";
15625
+ console.log("v86fs: mount:", name || "(default)");
15626
+ this.bus.send("virtio-v86fs-mount", name);
15627
+ const resp = new Uint8Array(23);
15628
+ packU32(resp, 0, 23);
15629
+ resp[4] = V86FS_MSG_MOUNT_R;
15630
+ resp[5] = tag & 255;
15631
+ resp[6] = tag >> 8 & 255;
15632
+ packU32(resp, 7, 0);
15633
+ packU64(resp, 11, 1);
15634
+ packU32(resp, 19, 16877);
15635
+ return resp;
15636
+ }
15637
+ handle_getattr(req, tag) {
15638
+ const inode_id = readU64(req, 7);
15639
+ const entry = INODE_MAP.get(inode_id);
15640
+ const resp = makeResp(35, V86FS_MSG_GETATTR_R, tag);
15641
+ if (!entry) {
15642
+ packU32(resp, 7, V86FS_STATUS_ENOENT);
15643
+ return resp;
15644
+ }
15645
+ packU32(resp, 7, V86FS_STATUS_OK);
15646
+ packU32(resp, 11, entry.mode);
15647
+ packU64(resp, 15, entry.size);
15648
+ packU64(resp, 23, entry.mtime_sec);
15649
+ packU32(resp, 31, entry.mtime_nsec);
15650
+ return resp;
15651
+ }
15652
+ handle_lookup(req, tag) {
15653
+ const parent_id = readU64(req, 7);
15654
+ const name_len = readU16(req, 15);
15655
+ const name = textDecoder.decode(req.subarray(17, 17 + name_len));
15656
+ const entries = FS_ENTRIES.get(parent_id);
15657
+ const entry = entries?.find((e) => e.name === name);
15658
+ const resp = new Uint8Array(31);
15659
+ packU32(resp, 0, 31);
15660
+ resp[4] = V86FS_MSG_LOOKUP_R;
15661
+ resp[5] = tag & 255;
15662
+ resp[6] = tag >> 8 & 255;
15663
+ if (!entry) {
15664
+ packU32(resp, 7, V86FS_STATUS_ENOENT);
15665
+ return resp;
15666
+ }
15667
+ packU32(resp, 7, V86FS_STATUS_OK);
15668
+ packU64(resp, 11, entry.inode_id);
15669
+ packU32(resp, 19, entry.mode);
15670
+ packU64(resp, 23, entry.size);
15671
+ return resp;
15672
+ }
15673
+ handle_readdir(req, tag) {
15674
+ const dir_id = readU64(req, 7);
15675
+ const entries = FS_ENTRIES.get(dir_id) || [];
15676
+ const encodedNames = entries.map((e) => textEncoder.encode(e.name));
15677
+ let size = 7 + 4 + 4;
15678
+ for (const nameBytes of encodedNames) {
15679
+ size += 8 + 1 + 2 + nameBytes.length;
15680
+ }
15681
+ const resp = makeResp(size, V86FS_MSG_READDIR_R, tag);
15682
+ packU32(resp, 7, V86FS_STATUS_OK);
15683
+ packU32(resp, 11, entries.length);
15684
+ let off = 15;
15685
+ for (let i = 0; i < entries.length; i++) {
15686
+ const e = entries[i];
15687
+ const nameBytes = encodedNames[i];
15688
+ packU64(resp, off, e.inode_id);
15689
+ resp[off + 8] = e.dt_type;
15690
+ packU16(resp, off + 9, nameBytes.length);
15691
+ resp.set(nameBytes, off + 11);
15692
+ off += 11 + nameBytes.length;
15693
+ }
15694
+ return resp;
15695
+ }
15696
+ handle_open(req, tag) {
15697
+ const inode_id = readU64(req, 7);
15698
+ const handle_id = this.next_handle_id++;
15699
+ this.open_handles.set(handle_id, inode_id);
15700
+ this.bus.send("virtio-v86fs-open", inode_id);
15701
+ const resp = makeResp(19, V86FS_MSG_OPEN_R, tag);
15702
+ packU32(resp, 7, V86FS_STATUS_OK);
15703
+ packU64(resp, 11, handle_id);
15704
+ return resp;
15705
+ }
15706
+ handle_close(req, tag) {
15707
+ const handle_id = readU64(req, 7);
15708
+ this.open_handles.delete(handle_id);
15709
+ this.bus.send("virtio-v86fs-close", handle_id);
15710
+ const resp = makeResp(11, V86FS_MSG_CLOSE_R, tag);
15711
+ packU32(resp, 7, V86FS_STATUS_OK);
15712
+ return resp;
15713
+ }
15714
+ handle_read(req, tag) {
15715
+ const handle_id = readU64(req, 7);
15716
+ const offset = readU64(req, 15);
15717
+ const size = readU32(req, 23);
15718
+ const inode_id = this.open_handles.get(handle_id) ?? handle_id;
15719
+ const entry = INODE_MAP.get(inode_id);
15720
+ const content = entry?.content;
15721
+ this.read_count++;
15722
+ this.bus.send("virtio-v86fs-read", {
15723
+ handle_id,
15724
+ inode_id,
15725
+ offset,
15726
+ size
15727
+ });
15728
+ if (!content || offset >= content.length) {
15729
+ const resp2 = makeResp(15, V86FS_MSG_READ_R, tag);
15730
+ packU32(resp2, 7, V86FS_STATUS_OK);
15731
+ packU32(resp2, 11, 0);
15732
+ return resp2;
15733
+ }
15734
+ const start = Math.min(offset, content.length);
15735
+ const end = Math.min(start + size, content.length);
15736
+ const data = content.subarray(start, end);
15737
+ const resp = new Uint8Array(15 + data.length);
15738
+ packU32(resp, 0, 15 + data.length);
15739
+ resp[4] = V86FS_MSG_READ_R;
15740
+ resp[5] = tag & 255;
15741
+ resp[6] = tag >> 8 & 255;
15742
+ packU32(resp, 7, V86FS_STATUS_OK);
15743
+ packU32(resp, 11, data.length);
15744
+ resp.set(data, 15);
15745
+ return resp;
15746
+ }
15747
+ handle_create(req, tag) {
15748
+ const parent_id = readU64(req, 7);
15749
+ const name_len = readU16(req, 15);
15750
+ const name = textDecoder.decode(req.subarray(17, 17 + name_len));
15751
+ const mode = readU32(req, 17 + name_len);
15752
+ const inode_id = this.next_inode_id++;
15753
+ const entry = {
15754
+ inode_id,
15755
+ name,
15756
+ mode: mode | S_IFREG,
15757
+ size: 0,
15758
+ dt_type: DT_REG,
15759
+ mtime_sec: Math.floor(Date.now() / 1e3),
15760
+ mtime_nsec: 0,
15761
+ content: new Uint8Array(0)
15762
+ };
15763
+ let children = FS_ENTRIES.get(parent_id);
15764
+ if (!children) {
15765
+ children = [];
15766
+ FS_ENTRIES.set(parent_id, children);
15767
+ }
15768
+ children.push(entry);
15769
+ INODE_MAP.set(inode_id, entry);
15770
+ const resp = makeResp(23, V86FS_MSG_CREATE_R, tag);
15771
+ packU32(resp, 7, V86FS_STATUS_OK);
15772
+ packU64(resp, 11, inode_id);
15773
+ packU32(resp, 19, entry.mode);
15774
+ return resp;
15775
+ }
15776
+ handle_write(req, tag) {
15777
+ const inode_id = readU64(req, 7);
15778
+ const offset = readU64(req, 15);
15779
+ const size = readU32(req, 23);
15780
+ const data = req.subarray(27, 27 + size);
15781
+ const entry = INODE_MAP.get(inode_id);
15782
+ if (entry) {
15783
+ const needed = offset + size;
15784
+ if (!entry.content || entry.content.length < needed) {
15785
+ const newContent = new Uint8Array(needed);
15786
+ if (entry.content) {
15787
+ newContent.set(entry.content);
15788
+ }
15789
+ entry.content = newContent;
15790
+ }
15791
+ entry.content.set(data, offset);
15792
+ if (needed > entry.size) {
15793
+ entry.size = needed;
15794
+ }
15795
+ }
15796
+ const resp = makeResp(15, V86FS_MSG_WRITE_R, tag);
15797
+ packU32(resp, 7, V86FS_STATUS_OK);
15798
+ packU32(resp, 11, size);
15799
+ return resp;
15800
+ }
15801
+ handle_mkdir(req, tag) {
15802
+ const parent_id = readU64(req, 7);
15803
+ const name_len = readU16(req, 15);
15804
+ const name = textDecoder.decode(req.subarray(17, 17 + name_len));
15805
+ const mode = readU32(req, 17 + name_len);
15806
+ const inode_id = this.next_inode_id++;
15807
+ const entry = {
15808
+ inode_id,
15809
+ name,
15810
+ mode: mode | S_IFDIR,
15811
+ size: 0,
15812
+ dt_type: DT_DIR,
15813
+ mtime_sec: Math.floor(Date.now() / 1e3),
15814
+ mtime_nsec: 0
15815
+ };
15816
+ let children = FS_ENTRIES.get(parent_id);
15817
+ if (!children) {
15818
+ children = [];
15819
+ FS_ENTRIES.set(parent_id, children);
15820
+ }
15821
+ children.push(entry);
15822
+ INODE_MAP.set(inode_id, entry);
15823
+ FS_ENTRIES.set(inode_id, []);
15824
+ const resp = makeResp(23, V86FS_MSG_MKDIR_R, tag);
15825
+ packU32(resp, 7, V86FS_STATUS_OK);
15826
+ packU64(resp, 11, inode_id);
15827
+ packU32(resp, 19, entry.mode);
15828
+ return resp;
15829
+ }
15830
+ handle_setattr(req, tag) {
15831
+ const inode_id = readU64(req, 7);
15832
+ const valid = readU32(req, 15);
15833
+ const mode = readU32(req, 19);
15834
+ const size = readU64(req, 23);
15835
+ const entry = INODE_MAP.get(inode_id);
15836
+ if (entry) {
15837
+ if (valid & ATTR_MODE) {
15838
+ entry.mode = entry.mode & 61440 | mode & 4095;
15839
+ }
15840
+ if (valid & ATTR_SIZE) {
15841
+ entry.size = size;
15842
+ if (entry.content) {
15843
+ if (size === 0) {
15844
+ entry.content = new Uint8Array(0);
15845
+ } else if (size < entry.content.length) {
15846
+ entry.content = entry.content.subarray(0, size);
15847
+ }
15848
+ }
15849
+ }
15850
+ }
15851
+ const resp = makeResp(11, V86FS_MSG_SETATTR_R, tag);
15852
+ packU32(resp, 7, V86FS_STATUS_OK);
15853
+ return resp;
15854
+ }
15855
+ handle_fsync(req, tag) {
15856
+ const resp = makeResp(11, V86FS_MSG_FSYNC_R, tag);
15857
+ packU32(resp, 7, V86FS_STATUS_OK);
15858
+ return resp;
15859
+ }
15860
+ handle_unlink(req, tag) {
15861
+ const parent_id = readU64(req, 7);
15862
+ const name_len = readU16(req, 15);
15863
+ const name = textDecoder.decode(req.subarray(17, 17 + name_len));
15864
+ const children = FS_ENTRIES.get(parent_id);
15865
+ let status = V86FS_STATUS_ENOENT;
15866
+ if (children) {
15867
+ const idx = children.findIndex((e) => e.name === name);
15868
+ if (idx >= 0) {
15869
+ const entry = children[idx];
15870
+ INODE_MAP.delete(entry.inode_id);
15871
+ FS_ENTRIES.delete(entry.inode_id);
15872
+ children.splice(idx, 1);
15873
+ status = V86FS_STATUS_OK;
15874
+ }
15875
+ }
15876
+ const resp = makeResp(11, V86FS_MSG_UNLINK_R, tag);
15877
+ packU32(resp, 7, status);
15878
+ return resp;
15879
+ }
15880
+ handle_rename(req, tag) {
15881
+ let off = 7;
15882
+ const old_parent_id = readU64(req, off);
15883
+ off += 8;
15884
+ const old_name_len = readU16(req, off);
15885
+ off += 2;
15886
+ const old_name = textDecoder.decode(
15887
+ req.subarray(off, off + old_name_len)
15888
+ );
15889
+ off += old_name_len;
15890
+ const new_parent_id = readU64(req, off);
15891
+ off += 8;
15892
+ const new_name_len = readU16(req, off);
15893
+ off += 2;
15894
+ const new_name = textDecoder.decode(
15895
+ req.subarray(off, off + new_name_len)
15896
+ );
15897
+ let status = V86FS_STATUS_ENOENT;
15898
+ const old_children = FS_ENTRIES.get(old_parent_id);
15899
+ if (old_children) {
15900
+ const idx = old_children.findIndex((e) => e.name === old_name);
15901
+ if (idx >= 0) {
15902
+ const entry = old_children[idx];
15903
+ old_children.splice(idx, 1);
15904
+ entry.name = new_name;
15905
+ let new_children = FS_ENTRIES.get(new_parent_id);
15906
+ if (!new_children) {
15907
+ new_children = [];
15908
+ FS_ENTRIES.set(new_parent_id, new_children);
15909
+ }
15910
+ const existing = new_children.findIndex(
15911
+ (e) => e.name === new_name
15912
+ );
15913
+ if (existing >= 0) {
15914
+ const old_entry = new_children[existing];
15915
+ INODE_MAP.delete(old_entry.inode_id);
15916
+ new_children.splice(existing, 1);
15917
+ }
15918
+ new_children.push(entry);
15919
+ status = V86FS_STATUS_OK;
15920
+ }
15921
+ }
15922
+ const resp = makeResp(11, V86FS_MSG_RENAME_R, tag);
15923
+ packU32(resp, 7, status);
15924
+ return resp;
15925
+ }
15926
+ handle_symlink(req, tag) {
15927
+ let off = 7;
15928
+ const parent_id = readU64(req, off);
15929
+ off += 8;
15930
+ const name_len = readU16(req, off);
15931
+ off += 2;
15932
+ const name = textDecoder.decode(req.subarray(off, off + name_len));
15933
+ off += name_len;
15934
+ const target_len = readU16(req, off);
15935
+ off += 2;
15936
+ const target = textDecoder.decode(req.subarray(off, off + target_len));
15937
+ const inode_id = this.next_inode_id++;
15938
+ const entry = {
15939
+ inode_id,
15940
+ name,
15941
+ mode: S_IFLNK | 511,
15942
+ size: target.length,
15943
+ dt_type: DT_LNK,
15944
+ mtime_sec: Math.floor(Date.now() / 1e3),
15945
+ mtime_nsec: 0,
15946
+ symlink_target: target
15947
+ };
15948
+ let children = FS_ENTRIES.get(parent_id);
15949
+ if (!children) {
15950
+ children = [];
15951
+ FS_ENTRIES.set(parent_id, children);
15952
+ }
15953
+ children.push(entry);
15954
+ INODE_MAP.set(inode_id, entry);
15955
+ const resp = makeResp(23, V86FS_MSG_SYMLINK_R, tag);
15956
+ packU32(resp, 7, V86FS_STATUS_OK);
15957
+ packU64(resp, 11, inode_id);
15958
+ packU32(resp, 19, entry.mode);
15959
+ return resp;
15960
+ }
15961
+ handle_readlink(req, tag) {
15962
+ const inode_id = readU64(req, 7);
15963
+ const entry = INODE_MAP.get(inode_id);
15964
+ if (!entry || !entry.symlink_target) {
15965
+ const resp2 = makeResp(11, V86FS_MSG_READLINK_R, tag);
15966
+ packU32(resp2, 7, V86FS_STATUS_ENOENT);
15967
+ return resp2;
15968
+ }
15969
+ const target_bytes = textEncoder.encode(entry.symlink_target);
15970
+ const resp_len = 11 + 2 + target_bytes.length;
15971
+ const resp = makeResp(resp_len, V86FS_MSG_READLINK_R, tag);
15972
+ packU32(resp, 7, V86FS_STATUS_OK);
15973
+ packU16(resp, 11, target_bytes.length);
15974
+ resp.set(target_bytes, 13);
15975
+ return resp;
15976
+ }
15977
+ handle_statfs(tag) {
15978
+ const resp = makeResp(55, V86FS_MSG_STATFS_R, tag);
15979
+ packU32(resp, 7, V86FS_STATUS_OK);
15980
+ packU64(resp, 11, 1024 * 1024);
15981
+ packU64(resp, 19, 512 * 1024);
15982
+ packU64(resp, 27, 512 * 1024);
15983
+ packU64(resp, 35, 1024 * 1024);
15984
+ packU64(resp, 43, 512 * 1024);
15985
+ packU32(resp, 51, 4096);
15986
+ return resp;
15987
+ }
15988
+ /** Push an INVALIDATE notification to the guest via notifyq.
15989
+ * Guest kernel will invalidate page cache for the given inode. */
15990
+ invalidate_inode(inode_id) {
15991
+ const queue = this.virtio.queues[2];
15992
+ if (!queue.has_request()) return false;
15993
+ const bufchain = queue.pop_request();
15994
+ const msg = new Uint8Array(15);
15995
+ packU32(msg, 0, 15);
15996
+ msg[4] = V86FS_MSG_INVALIDATE;
15997
+ packU16(msg, 5, 0);
15998
+ packU64(msg, 7, inode_id);
15999
+ bufchain.set_next_blob(msg);
16000
+ queue.push_reply(bufchain);
16001
+ queue.flush_replies();
16002
+ return true;
16003
+ }
16004
+ /** Push an INVALIDATE_DIR notification to the guest via notifyq.
16005
+ * Guest kernel will invalidate dcache for the given directory inode. */
16006
+ invalidate_dir(inode_id) {
16007
+ const queue = this.virtio.queues[2];
16008
+ if (!queue.has_request()) return false;
16009
+ const bufchain = queue.pop_request();
16010
+ const msg = new Uint8Array(15);
16011
+ packU32(msg, 0, 15);
16012
+ msg[4] = V86FS_MSG_INVALIDATE_DIR;
16013
+ packU16(msg, 5, 0);
16014
+ packU64(msg, 7, inode_id);
16015
+ bufchain.set_next_blob(msg);
16016
+ queue.push_reply(bufchain);
16017
+ queue.flush_replies();
16018
+ return true;
16019
+ }
16020
+ get_state() {
16021
+ const state = [];
16022
+ state[0] = this.virtio;
16023
+ return state;
16024
+ }
16025
+ set_state(state) {
16026
+ this.virtio.set_state(state[0]);
16027
+ }
16028
+ };
16029
+
15105
16030
  // lib/filesystem.ts
15106
16031
  var S_IFMT = 61440;
15107
16032
  var S_IFSOCK = 49152;
15108
- var S_IFLNK = 40960;
15109
- var S_IFREG = 32768;
15110
- var S_IFDIR = 16384;
16033
+ var S_IFLNK2 = 40960;
16034
+ var S_IFREG2 = 32768;
16035
+ var S_IFDIR2 = 16384;
15111
16036
  var STATUS_INVALID = -1;
15112
16037
  var STATUS_OK = 0;
15113
16038
  var STATUS_ON_STORAGE = 2;
@@ -15200,11 +16125,11 @@ var Inode = class {
15200
16125
  get_state() {
15201
16126
  const state = [];
15202
16127
  state[0] = this.mode;
15203
- if ((this.mode & S_IFMT) === S_IFDIR) {
16128
+ if ((this.mode & S_IFMT) === S_IFDIR2) {
15204
16129
  state[1] = [...this.direntries];
15205
- } else if ((this.mode & S_IFMT) === S_IFREG) {
16130
+ } else if ((this.mode & S_IFMT) === S_IFREG2) {
15206
16131
  state[1] = this.sha256sum;
15207
- } else if ((this.mode & S_IFMT) === S_IFLNK) {
16132
+ } else if ((this.mode & S_IFMT) === S_IFLNK2) {
15208
16133
  state[1] = this.symlink;
15209
16134
  } else if ((this.mode & S_IFMT) === S_IFSOCK) {
15210
16135
  state[1] = [this.minor, this.major];
@@ -15227,14 +16152,14 @@ var Inode = class {
15227
16152
  }
15228
16153
  set_state(state) {
15229
16154
  this.mode = state[0];
15230
- if ((this.mode & S_IFMT) === S_IFDIR) {
16155
+ if ((this.mode & S_IFMT) === S_IFDIR2) {
15231
16156
  this.direntries = /* @__PURE__ */ new Map();
15232
16157
  for (const [name, entry] of state[1]) {
15233
16158
  this.direntries.set(name, entry);
15234
16159
  }
15235
- } else if ((this.mode & S_IFMT) === S_IFREG) {
16160
+ } else if ((this.mode & S_IFMT) === S_IFREG2) {
15236
16161
  this.sha256sum = state[1];
15237
- } else if ((this.mode & S_IFMT) === S_IFLNK) {
16162
+ } else if ((this.mode & S_IFMT) === S_IFLNK2) {
15238
16163
  this.symlink = state[1];
15239
16164
  } else if ((this.mode & S_IFMT) === S_IFSOCK) {
15240
16165
  ;
@@ -15303,7 +16228,7 @@ var FS = class {
15303
16228
  state[1] = this.qidcounter.last_qidnumber;
15304
16229
  state[2] = [];
15305
16230
  for (const [id, data] of Object.entries(this.inodedata)) {
15306
- if ((this.inodes[Number(id)].mode & S_IFDIR) === 0) {
16231
+ if ((this.inodes[Number(id)].mode & S_IFDIR2) === 0) {
15307
16232
  state[2].push([id, data]);
15308
16233
  }
15309
16234
  }
@@ -15354,15 +16279,15 @@ var FS = class {
15354
16279
  inode.uid = data[JSONFS_IDX_UID];
15355
16280
  inode.gid = data[JSONFS_IDX_GID];
15356
16281
  const ifmt = inode.mode & S_IFMT;
15357
- if (ifmt === S_IFDIR) {
16282
+ if (ifmt === S_IFDIR2) {
15358
16283
  this.PushInode(inode, parentid, name);
15359
16284
  this.LoadDir(this.inodes.length - 1, data[JSONFS_IDX_TARGET]);
15360
- } else if (ifmt === S_IFREG) {
16285
+ } else if (ifmt === S_IFREG2) {
15361
16286
  inode.status = STATUS_ON_STORAGE;
15362
16287
  inode.sha256sum = data[JSONFS_IDX_SHA256];
15363
16288
  dbg_assert(!!inode.sha256sum);
15364
16289
  this.PushInode(inode, parentid, name);
15365
- } else if (ifmt === S_IFLNK) {
16290
+ } else if (ifmt === S_IFLNK2) {
15366
16291
  inode.symlink = data[JSONFS_IDX_TARGET];
15367
16292
  this.PushInode(inode, parentid, name);
15368
16293
  } else if (ifmt === S_IFSOCK) {
@@ -15534,13 +16459,13 @@ var FS = class {
15534
16459
  return this.create_forwarder(parent_inode.mount_id, foreign_id);
15535
16460
  }
15536
16461
  const x = this.CreateInode();
15537
- x.mode = 511 | S_IFDIR;
16462
+ x.mode = 511 | S_IFDIR2;
15538
16463
  if (parentid >= 0) {
15539
16464
  x.uid = this.inodes[parentid].uid;
15540
16465
  x.gid = this.inodes[parentid].gid;
15541
- x.mode = this.inodes[parentid].mode & 511 | S_IFDIR;
16466
+ x.mode = this.inodes[parentid].mode & 511 | S_IFDIR2;
15542
16467
  }
15543
- x.qid.type = S_IFDIR >> 8;
16468
+ x.qid.type = S_IFDIR2 >> 8;
15544
16469
  this.PushInode(x, parentid, name);
15545
16470
  this.NotifyListeners(this.inodes.length - 1, "newdir");
15546
16471
  return this.inodes.length - 1;
@@ -15558,8 +16483,8 @@ var FS = class {
15558
16483
  const x = this.CreateInode();
15559
16484
  x.uid = this.inodes[parentid].uid;
15560
16485
  x.gid = this.inodes[parentid].gid;
15561
- x.qid.type = S_IFREG >> 8;
15562
- x.mode = this.inodes[parentid].mode & 438 | S_IFREG;
16486
+ x.qid.type = S_IFREG2 >> 8;
16487
+ x.mode = this.inodes[parentid].mode & 438 | S_IFREG2;
15563
16488
  this.PushInode(x, parentid, filename);
15564
16489
  this.NotifyListeners(this.inodes.length - 1, "newfile");
15565
16490
  return this.inodes.length - 1;
@@ -15600,9 +16525,9 @@ var FS = class {
15600
16525
  const x = this.CreateInode();
15601
16526
  x.uid = this.inodes[parentid].uid;
15602
16527
  x.gid = this.inodes[parentid].gid;
15603
- x.qid.type = S_IFLNK >> 8;
16528
+ x.qid.type = S_IFLNK2 >> 8;
15604
16529
  x.symlink = symlink;
15605
- x.mode = S_IFLNK;
16530
+ x.mode = S_IFLNK2;
15606
16531
  this.PushInode(x, parentid, filename);
15607
16532
  return this.inodes.length - 1;
15608
16533
  }
@@ -15647,7 +16572,7 @@ var FS = class {
15647
16572
  if (this.is_forwarder(inode)) {
15648
16573
  return await this.follow_fs(inode).OpenInode(inode.foreign_id, mode);
15649
16574
  }
15650
- if ((inode.mode & S_IFMT) === S_IFDIR) {
16575
+ if ((inode.mode & S_IFMT) === S_IFDIR2) {
15651
16576
  this.FillDirectory(id);
15652
16577
  }
15653
16578
  }
@@ -16002,7 +16927,7 @@ var FS = class {
16002
16927
  let parentid = -1;
16003
16928
  let id = 0;
16004
16929
  let forward_path = null;
16005
- let i = 0;
16930
+ let i;
16006
16931
  for (i = 0; i < n; i++) {
16007
16932
  parentid = id;
16008
16933
  id = this.Search(parentid, walk[i]);
@@ -16068,13 +16993,13 @@ var FS = class {
16068
16993
  DeleteNode(path) {
16069
16994
  const ids = this.SearchPath(path);
16070
16995
  if (ids.id === -1) return;
16071
- if ((this.inodes[ids.id].mode & S_IFMT) === S_IFREG) {
16996
+ if ((this.inodes[ids.id].mode & S_IFMT) === S_IFREG2) {
16072
16997
  const ret = this.Unlink(ids.parentid, ids.name);
16073
16998
  dbg_assert(
16074
16999
  ret === 0,
16075
17000
  "Filesystem DeleteNode failed with error code: " + -ret
16076
17001
  );
16077
- } else if ((this.inodes[ids.id].mode & S_IFMT) === S_IFDIR) {
17002
+ } else if ((this.inodes[ids.id].mode & S_IFMT) === S_IFDIR2) {
16078
17003
  this.RecursiveDelete(path);
16079
17004
  const ret = this.Unlink(ids.parentid, ids.name);
16080
17005
  dbg_assert(
@@ -16178,7 +17103,7 @@ var FS = class {
16178
17103
  if (this.is_forwarder(inode)) {
16179
17104
  return this.follow_fs(inode).IsDirectory(inode.foreign_id);
16180
17105
  }
16181
- return (inode.mode & S_IFMT) === S_IFDIR;
17106
+ return (inode.mode & S_IFMT) === S_IFDIR2;
16182
17107
  }
16183
17108
  IsEmpty(idx) {
16184
17109
  const inode = this.inodes[idx];
@@ -16708,7 +17633,7 @@ var Virtio9p = class {
16708
17633
  bufchain.get_next_blob(buffer);
16709
17634
  const state = { offset: 0 };
16710
17635
  const header = Unmarshall(["w", "b", "h"], buffer, state);
16711
- let size = header[0];
17636
+ let size;
16712
17637
  const id = header[1];
16713
17638
  const tag = header[2];
16714
17639
  switch (id) {
@@ -16769,7 +17694,7 @@ var Virtio9p = class {
16769
17694
  name
16770
17695
  );
16771
17696
  if (ret < 0) {
16772
- let error_message = "";
17697
+ let error_message;
16773
17698
  if (ret === -EPERM)
16774
17699
  error_message = "Operation not permitted";
16775
17700
  else {
@@ -16882,7 +17807,7 @@ var Virtio9p = class {
16882
17807
  this.fids[fid].inodeid
16883
17808
  );
16884
17809
  const inode = this.fs.GetInode(idx);
16885
- inode.mode = mode | S_IFDIR;
17810
+ inode.mode = mode | S_IFDIR2;
16886
17811
  inode.uid = this.fids[fid].uid;
16887
17812
  inode.gid = gid;
16888
17813
  Marshall(["Q"], [inode.qid], this.replybuffer, 7);
@@ -16913,7 +17838,7 @@ var Virtio9p = class {
16913
17838
  const inode = this.fs.GetInode(idx);
16914
17839
  inode.uid = this.fids[fid].uid;
16915
17840
  inode.gid = gid;
16916
- inode.mode = mode | S_IFREG;
17841
+ inode.mode = mode | S_IFREG2;
16917
17842
  Marshall(
16918
17843
  ["Q", "w"],
16919
17844
  [inode.qid, this.msize - 24],
@@ -17238,7 +18163,7 @@ var Virtio9p = class {
17238
18163
  newname
17239
18164
  );
17240
18165
  if (ret < 0) {
17241
- let error_message = "";
18166
+ let error_message;
17242
18167
  if (ret === -ENOENT)
17243
18168
  error_message = "No such file or directory";
17244
18169
  else if (ret === -EPERM)
@@ -17287,7 +18212,7 @@ var Virtio9p = class {
17287
18212
  }
17288
18213
  const ret = this.fs.Unlink(this.fids[dirfd].inodeid, name);
17289
18214
  if (ret < 0) {
17290
- let error_message = "";
18215
+ let error_message;
17291
18216
  if (ret === -ENOTEMPTY)
17292
18217
  error_message = "Directory not empty";
17293
18218
  else if (ret === -EPERM)
@@ -17939,7 +18864,6 @@ var CPU = class {
17939
18864
  get_eflags;
17940
18865
  handle_irqs;
17941
18866
  main_loop;
17942
- reboot_internal;
17943
18867
  set_jit_config;
17944
18868
  read8;
17945
18869
  read16;
@@ -17988,10 +18912,10 @@ var CPU = class {
17988
18912
  this.name = "cpu";
17989
18913
  this.stop_idling = stop_idling;
17990
18914
  this.wm = wm;
18915
+ this.wasm_memory = wm.wasm_memory;
17991
18916
  this.wasm_patch();
17992
18917
  this.create_jit_imports();
17993
- const memory = this.wm.exports["memory"];
17994
- this.wasm_memory = memory;
18918
+ const memory = this.wasm_memory;
17995
18919
  this.memory_size = view(Uint32Array, memory, 812, 1);
17996
18920
  this.mem8 = new Uint8Array(0);
17997
18921
  this.mem32s = new Int32Array(this.mem8.buffer);
@@ -18136,7 +19060,7 @@ var CPU = class {
18136
19060
  }
18137
19061
  create_jit_imports() {
18138
19062
  const jit_imports = /* @__PURE__ */ Object.create(null);
18139
- jit_imports["m"] = this.wm.exports["memory"];
19063
+ jit_imports["m"] = this.wasm_memory;
18140
19064
  for (const name of Object.keys(this.wm.exports)) {
18141
19065
  if (name.startsWith("_") || name.startsWith("zstd") || name.endsWith("_js")) {
18142
19066
  continue;
@@ -18157,7 +19081,6 @@ var CPU = class {
18157
19081
  this.get_eflags = get_import("get_eflags");
18158
19082
  this.handle_irqs = get_import("handle_irqs");
18159
19083
  this.main_loop = get_import("main_loop");
18160
- this.reboot_internal = get_import("reboot_internal");
18161
19084
  this.set_jit_config = get_import("set_jit_config");
18162
19085
  this.read8 = get_import("read8");
18163
19086
  this.read16 = get_import("read16");
@@ -18314,6 +19237,8 @@ var CPU = class {
18314
19237
  state[86] = this.last_result;
18315
19238
  state[87] = this.fpu_status_word;
18316
19239
  state[88] = this.mxcsr;
19240
+ state[89] = this.devices.virtio_mem;
19241
+ state[90] = this.devices.virtio_v86fs;
18317
19242
  return state;
18318
19243
  }
18319
19244
  get_state_pic() {
@@ -18374,6 +19299,25 @@ var CPU = class {
18374
19299
  IOAPIC_STRUCT_SIZE
18375
19300
  );
18376
19301
  }
19302
+ resize_memory(new_size) {
19303
+ const mem8_offset = this.mem8.byteOffset;
19304
+ const needed_total = mem8_offset + new_size;
19305
+ const current_buffer = this.wasm_memory.buffer.byteLength;
19306
+ if (needed_total > current_buffer) {
19307
+ const grow_pages = Math.ceil(
19308
+ (needed_total - current_buffer) / WASM_PAGE_SIZE
19309
+ );
19310
+ this.wasm_memory.grow(grow_pages);
19311
+ }
19312
+ this.mem8 = view(Uint8Array, this.wasm_memory, mem8_offset, new_size);
19313
+ this.mem32s = view(
19314
+ Int32Array,
19315
+ this.wasm_memory,
19316
+ mem8_offset,
19317
+ new_size >> 2
19318
+ );
19319
+ this.memory_size[0] = new_size;
19320
+ }
18377
19321
  set_state(state) {
18378
19322
  this.memory_size[0] = state[0];
18379
19323
  if (this.mem8.length !== this.memory_size[0]) {
@@ -18473,6 +19417,10 @@ var CPU = class {
18473
19417
  this.devices.virtio_net.set_state(state[83]);
18474
19418
  if (this.devices.virtio_balloon)
18475
19419
  this.devices.virtio_balloon.set_state(state[84]);
19420
+ if (this.devices.virtio_mem && state[89])
19421
+ this.devices.virtio_mem.set_state(state[89]);
19422
+ if (this.devices.virtio_v86fs && state[90])
19423
+ this.devices.virtio_v86fs.set_state(state[90]);
18476
19424
  this.fw_value = state[62];
18477
19425
  if (state[63]) this.set_state_ioapic(state[63]);
18478
19426
  this.tss_size_32[0] = state[64];
@@ -18883,6 +19831,19 @@ var CPU = class {
18883
19831
  device_bus
18884
19832
  );
18885
19833
  }
19834
+ if (settings.virtio_mem) {
19835
+ const mem_cfg = settings.virtio_mem;
19836
+ this.devices.virtio_mem = new VirtioMem(
19837
+ this,
19838
+ device_bus,
19839
+ mem_cfg.region_addr,
19840
+ mem_cfg.region_size,
19841
+ mem_cfg.block_size
19842
+ );
19843
+ }
19844
+ if (settings.virtio_v86fs) {
19845
+ this.devices.virtio_v86fs = new VirtioV86FS(this, device_bus);
19846
+ }
18886
19847
  this.devices.sb16 = new SB16(this, device_bus);
18887
19848
  }
18888
19849
  if (settings.multiboot) {
@@ -19108,7 +20069,6 @@ var CPU = class {
19108
20069
  cpu.write32(multiboot_data + 4, ramdisk_top);
19109
20070
  cpu.write32(multiboot_data + 8, 0);
19110
20071
  cpu.write32(multiboot_data + 12, 0);
19111
- multiboot_data += 16;
19112
20072
  dbg_assert(ramdisk_top < cpu.memory_size[0]);
19113
20073
  cpu.write_blob(new Uint8Array(initrd), ramdisk_address);
19114
20074
  }
@@ -25182,20 +26142,11 @@ var ServerFileStorageWrapper = class {
25182
26142
  };
25183
26143
 
25184
26144
  // src/browser/starter.ts
25185
- var FileExistsError = class {
25186
- message;
25187
- constructor(message) {
25188
- this.message = message || "File already exists";
25189
- }
25190
- };
25191
- FileExistsError.prototype = Error.prototype;
25192
- var FileNotFoundError = class {
25193
- message;
26145
+ var FileNotFoundError = class extends Error {
25194
26146
  constructor(message) {
25195
- this.message = message || "File not found";
26147
+ super(message || "File not found");
25196
26148
  }
25197
26149
  };
25198
- FileNotFoundError.prototype = Error.prototype;
25199
26150
  var V86 = class {
25200
26151
  cpu_is_running = false;
25201
26152
  cpu_exception_hook = function(_n) {
@@ -25321,9 +26272,10 @@ var V86 = class {
25321
26272
  "v86.wasm",
25322
26273
  "v86-fallback.wasm"
25323
26274
  );
25324
- } else if (typeof window === "undefined" && typeof __dirname === "string") {
25325
- v86_bin = __dirname + "/" + v86_bin;
25326
- v86_bin_fallback = __dirname + "/" + v86_bin_fallback;
26275
+ } else if (typeof window === "undefined") {
26276
+ const root = new URL("../../", import.meta.url).pathname;
26277
+ v86_bin = root + "build/" + v86_bin;
26278
+ v86_bin_fallback = root + "build/" + v86_bin_fallback;
25327
26279
  } else {
25328
26280
  v86_bin = "build/" + v86_bin;
25329
26281
  v86_bin_fallback = "build/" + v86_bin_fallback;
@@ -25366,7 +26318,8 @@ var V86 = class {
25366
26318
  exports["rust_init"]();
25367
26319
  const emulator = this.v86 = new v86(this.emulator_bus, {
25368
26320
  exports,
25369
- wasm_table
26321
+ wasm_table,
26322
+ wasm_memory
25370
26323
  });
25371
26324
  cpu = emulator.cpu;
25372
26325
  this.continue_init(emulator, options);
@@ -25408,7 +26361,9 @@ var V86 = class {
25408
26361
  settings.mac_address_translation = options.mac_address_translation;
25409
26362
  settings.cpuid_level = options.cpuid_level;
25410
26363
  settings.virtio_balloon = options.virtio_balloon;
26364
+ settings.virtio_mem = options.virtio_mem;
25411
26365
  settings.virtio_console = !!options.virtio_console;
26366
+ settings.virtio_v86fs = !!options.virtio_v86fs;
25412
26367
  const relay_url = options.network_relay_url || options.net_device && options.net_device.relay_url;
25413
26368
  if (relay_url) {
25414
26369
  if (relay_url === "fetch") {