@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.
@@ -142,6 +142,7 @@ var V86Starter = (() => {
142
142
  var FW_CFG_SIGNATURE_QEMU = 1431127377;
143
143
  var WASM_TABLE_SIZE = 900;
144
144
  var WASM_TABLE_OFFSET = 1024;
145
+ var WASM_PAGE_SIZE = 65536;
145
146
  var MIXER_CHANNEL_LEFT = 0;
146
147
  var MIXER_CHANNEL_RIGHT = 1;
147
148
  var MIXER_CHANNEL_BOTH = 2;
@@ -5456,17 +5457,18 @@ var V86Starter = (() => {
5456
5457
  LOG_VIRTIO
5457
5458
  );
5458
5459
  }
5459
- if (data & ~this.device_status & VIRTIO_STATUS_DRIVER_OK && this.device_status & VIRTIO_STATUS_DEVICE_NEEDS_RESET) {
5460
- this.notify_config_changes();
5461
- }
5462
5460
  if (!this.features_ok) {
5463
5461
  if (false) {
5464
5462
  dbg_log("Removing FEATURES_OK", LOG_VIRTIO);
5465
5463
  }
5466
5464
  data &= ~VIRTIO_STATUS_FEATURES_OK;
5467
5465
  }
5466
+ const prev_status = this.device_status;
5468
5467
  this.device_status = data;
5469
- if (data & ~this.device_status & VIRTIO_STATUS_DRIVER_OK) {
5468
+ if (data & ~prev_status & VIRTIO_STATUS_DRIVER_OK) {
5469
+ if (prev_status & VIRTIO_STATUS_DEVICE_NEEDS_RESET) {
5470
+ this.notify_config_changes();
5471
+ }
5470
5472
  options.on_driver_ok();
5471
5473
  }
5472
5474
  }
@@ -5703,7 +5705,7 @@ var V86Starter = (() => {
5703
5705
  // Call only within constructor.
5704
5706
  init_capabilities(capabilities) {
5705
5707
  let cap_next = this.pci_space[52] = 64;
5706
- let cap_ptr = cap_next;
5708
+ let cap_ptr;
5707
5709
  for (const cap of capabilities) {
5708
5710
  const cap_len2 = VIRTIO_PCI_CAP_LENGTH + cap.extra.length;
5709
5711
  cap_ptr = cap_next;
@@ -6924,7 +6926,7 @@ var V86Starter = (() => {
6924
6926
  (out_byte) => {
6925
6927
  if ((this.pci_addr[1] & 6) === 2 && (out_byte & 6) === 6) {
6926
6928
  dbg_log("CPU reboot via PCI");
6927
- cpu.reboot_internal();
6929
+ cpu.reboot();
6928
6930
  return;
6929
6931
  }
6930
6932
  this.pci_addr[1] = out_byte;
@@ -7030,6 +7032,7 @@ var V86Starter = (() => {
7030
7032
  0,
7031
7033
  0,
7032
7034
  0,
7035
+ 0,
7033
7036
  PAM0,
7034
7037
  0,
7035
7038
  0,
@@ -7342,7 +7345,11 @@ var V86Starter = (() => {
7342
7345
  dbg_assert(device.pci_space.length >= 64);
7343
7346
  dbg_assert(device_id < this.devices.length);
7344
7347
  const space = new Int32Array(64);
7345
- space.set(new Int32Array(new Uint8Array(device.pci_space).buffer));
7348
+ const pci_bytes = new Uint8Array(device.pci_space);
7349
+ const aligned_len = pci_bytes.length & ~3;
7350
+ if (aligned_len > 0) {
7351
+ space.set(new Int32Array(pci_bytes.buffer, 0, aligned_len >> 2));
7352
+ }
7346
7353
  this.device_spaces[device_id] = space;
7347
7354
  this.devices[device_id] = device;
7348
7355
  const bar_space = space.slice(4, 10);
@@ -7993,7 +8000,7 @@ var V86Starter = (() => {
7993
8000
  break;
7994
8001
  case 254:
7995
8002
  dbg_log("CPU reboot via PS2");
7996
- this.cpu.reboot_internal();
8003
+ this.cpu.reboot();
7997
8004
  break;
7998
8005
  default:
7999
8006
  dbg_log(
@@ -15138,12 +15145,930 @@ var V86Starter = (() => {
15138
15145
  }
15139
15146
  };
15140
15147
 
15148
+ // src/virtio_mem.ts
15149
+ var VIRTIO_MEM_REQ_PLUG = 0;
15150
+ var VIRTIO_MEM_REQ_UNPLUG = 1;
15151
+ var VIRTIO_MEM_REQ_STATE = 3;
15152
+ var VIRTIO_MEM_RESP_ACK = 0;
15153
+ var VIRTIO_MEM_RESP_NACK = 1;
15154
+ var VIRTIO_MEM_RESP_ERROR = 3;
15155
+ var DEFAULT_BLOCK_SIZE = 128 * 1024 * 1024;
15156
+ var VirtioMem = class {
15157
+ bus;
15158
+ cpu;
15159
+ virtio;
15160
+ block_size;
15161
+ region_addr;
15162
+ region_size;
15163
+ usable_region_size;
15164
+ plugged_size;
15165
+ requested_size;
15166
+ constructor(cpu, bus, region_addr, region_size, block_size) {
15167
+ this.bus = bus;
15168
+ this.cpu = cpu;
15169
+ this.block_size = block_size || DEFAULT_BLOCK_SIZE;
15170
+ this.region_addr = region_addr;
15171
+ this.region_size = region_size;
15172
+ this.usable_region_size = region_size;
15173
+ this.plugged_size = 0;
15174
+ this.requested_size = 0;
15175
+ const queues = [{ size_supported: 32, notify_offset: 0 }];
15176
+ this.virtio = new VirtIO(cpu, {
15177
+ name: "virtio-mem",
15178
+ pci_id: 13 << 3,
15179
+ device_id: 4184,
15180
+ subsystem_device_id: 24,
15181
+ common: {
15182
+ initial_port: 59392,
15183
+ queues,
15184
+ features: [VIRTIO_F_VERSION_1],
15185
+ on_driver_ok: () => {
15186
+ dbg_log("virtio-mem setup", LOG_PCI);
15187
+ }
15188
+ },
15189
+ notification: {
15190
+ initial_port: 59648,
15191
+ single_handler: false,
15192
+ handlers: [
15193
+ (queue_id) => {
15194
+ this.handle_request(queue_id);
15195
+ }
15196
+ ]
15197
+ },
15198
+ isr_status: {
15199
+ initial_port: 59136
15200
+ },
15201
+ device_specific: {
15202
+ initial_port: 58880,
15203
+ struct: [
15204
+ // block_size low
15205
+ {
15206
+ bytes: 4,
15207
+ name: "block_size_low",
15208
+ read: () => this.block_size >>> 0,
15209
+ write: () => {
15210
+ }
15211
+ },
15212
+ // block_size high
15213
+ {
15214
+ bytes: 4,
15215
+ name: "block_size_high",
15216
+ read: () => 0,
15217
+ write: () => {
15218
+ }
15219
+ },
15220
+ // node_id (u16 padded to u32)
15221
+ {
15222
+ bytes: 2,
15223
+ name: "node_id",
15224
+ read: () => 0,
15225
+ write: () => {
15226
+ }
15227
+ },
15228
+ // padding (6 bytes as 2+4)
15229
+ {
15230
+ bytes: 2,
15231
+ name: "padding0",
15232
+ read: () => 0,
15233
+ write: () => {
15234
+ }
15235
+ },
15236
+ {
15237
+ bytes: 4,
15238
+ name: "padding1",
15239
+ read: () => 0,
15240
+ write: () => {
15241
+ }
15242
+ },
15243
+ // addr low
15244
+ {
15245
+ bytes: 4,
15246
+ name: "addr_low",
15247
+ read: () => this.region_addr >>> 0,
15248
+ write: () => {
15249
+ }
15250
+ },
15251
+ // addr high
15252
+ {
15253
+ bytes: 4,
15254
+ name: "addr_high",
15255
+ read: () => 0,
15256
+ write: () => {
15257
+ }
15258
+ },
15259
+ // region_size low
15260
+ {
15261
+ bytes: 4,
15262
+ name: "region_size_low",
15263
+ read: () => this.region_size >>> 0,
15264
+ write: () => {
15265
+ }
15266
+ },
15267
+ // region_size high
15268
+ {
15269
+ bytes: 4,
15270
+ name: "region_size_high",
15271
+ read: () => 0,
15272
+ write: () => {
15273
+ }
15274
+ },
15275
+ // usable_region_size low
15276
+ {
15277
+ bytes: 4,
15278
+ name: "usable_region_size_low",
15279
+ read: () => this.usable_region_size >>> 0,
15280
+ write: () => {
15281
+ }
15282
+ },
15283
+ // usable_region_size high
15284
+ {
15285
+ bytes: 4,
15286
+ name: "usable_region_size_high",
15287
+ read: () => 0,
15288
+ write: () => {
15289
+ }
15290
+ },
15291
+ // plugged_size low
15292
+ {
15293
+ bytes: 4,
15294
+ name: "plugged_size_low",
15295
+ read: () => this.plugged_size >>> 0,
15296
+ write: () => {
15297
+ }
15298
+ },
15299
+ // plugged_size high
15300
+ {
15301
+ bytes: 4,
15302
+ name: "plugged_size_high",
15303
+ read: () => 0,
15304
+ write: () => {
15305
+ }
15306
+ },
15307
+ // requested_size low
15308
+ {
15309
+ bytes: 4,
15310
+ name: "requested_size_low",
15311
+ read: () => this.requested_size >>> 0,
15312
+ write: () => {
15313
+ }
15314
+ },
15315
+ // requested_size high
15316
+ {
15317
+ bytes: 4,
15318
+ name: "requested_size_high",
15319
+ read: () => 0,
15320
+ write: () => {
15321
+ }
15322
+ }
15323
+ ]
15324
+ }
15325
+ });
15326
+ }
15327
+ handle_request(queue_id) {
15328
+ const queue = this.virtio.queues[queue_id];
15329
+ while (queue.has_request()) {
15330
+ const bufchain = queue.pop_request();
15331
+ const request = new Uint8Array(bufchain.length_readable);
15332
+ bufchain.get_next_blob(request);
15333
+ const type = request[0] | request[1] << 8;
15334
+ const resp = new Uint8Array(8);
15335
+ let resp_type = VIRTIO_MEM_RESP_ERROR;
15336
+ switch (type) {
15337
+ case VIRTIO_MEM_REQ_PLUG:
15338
+ resp_type = this.handle_plug(request);
15339
+ break;
15340
+ case VIRTIO_MEM_REQ_UNPLUG:
15341
+ resp_type = VIRTIO_MEM_RESP_NACK;
15342
+ break;
15343
+ case VIRTIO_MEM_REQ_STATE:
15344
+ resp_type = this.handle_state(request, resp);
15345
+ break;
15346
+ default:
15347
+ dbg_log("virtio-mem: unknown request type " + type, LOG_PCI);
15348
+ }
15349
+ resp[0] = resp_type & 255;
15350
+ resp[1] = resp_type >> 8 & 255;
15351
+ bufchain.set_next_blob(resp);
15352
+ queue.push_reply(bufchain);
15353
+ }
15354
+ queue.flush_replies();
15355
+ }
15356
+ handle_plug(request) {
15357
+ const addr = (request[8] | request[9] << 8 | request[10] << 16 | request[11] << 24) >>> 0;
15358
+ const nb_blocks = request[16] | request[17] << 8;
15359
+ const size = nb_blocks * this.block_size;
15360
+ const new_plugged = this.plugged_size + size;
15361
+ if (new_plugged > this.usable_region_size) {
15362
+ return VIRTIO_MEM_RESP_NACK;
15363
+ }
15364
+ const new_memory_size = this.cpu.memory_size[0] + size;
15365
+ this.cpu.resize_memory(new_memory_size);
15366
+ this.cpu.full_clear_tlb();
15367
+ this.plugged_size = new_plugged;
15368
+ dbg_log(
15369
+ "virtio-mem: plugged " + nb_blocks + " blocks at 0x" + addr.toString(16) + " (" + (size >> 20) + "MB)",
15370
+ LOG_PCI
15371
+ );
15372
+ return VIRTIO_MEM_RESP_ACK;
15373
+ }
15374
+ handle_state(request, resp) {
15375
+ const addr = (request[8] | request[9] << 8 | request[10] << 16 | request[11] << 24) >>> 0;
15376
+ const nb_blocks = request[16] | request[17] << 8;
15377
+ const offset = addr - this.region_addr;
15378
+ const end = offset + nb_blocks * this.block_size;
15379
+ const all_plugged = end <= this.plugged_size ? 1 : 0;
15380
+ resp[8] = all_plugged & 255;
15381
+ resp[9] = all_plugged >> 8 & 255;
15382
+ return VIRTIO_MEM_RESP_ACK;
15383
+ }
15384
+ set_requested_size(size) {
15385
+ this.requested_size = size;
15386
+ if (this.virtio.device_status & 4) {
15387
+ this.virtio.notify_config_changes();
15388
+ }
15389
+ }
15390
+ get_state() {
15391
+ const state = [];
15392
+ state[0] = this.virtio;
15393
+ state[1] = this.block_size;
15394
+ state[2] = this.region_addr;
15395
+ state[3] = this.region_size;
15396
+ state[4] = this.usable_region_size;
15397
+ state[5] = this.plugged_size;
15398
+ state[6] = this.requested_size;
15399
+ return state;
15400
+ }
15401
+ set_state(state) {
15402
+ this.virtio.set_state(state[0]);
15403
+ this.block_size = state[1];
15404
+ this.region_addr = state[2];
15405
+ this.region_size = state[3];
15406
+ this.usable_region_size = state[4];
15407
+ this.plugged_size = state[5];
15408
+ this.requested_size = state[6];
15409
+ }
15410
+ };
15411
+
15412
+ // src/virtio_v86fs.ts
15413
+ var V86FS_MSG_MOUNT = 0;
15414
+ var V86FS_MSG_LOOKUP = 1;
15415
+ var V86FS_MSG_GETATTR = 2;
15416
+ var V86FS_MSG_READDIR = 3;
15417
+ var V86FS_MSG_OPEN = 4;
15418
+ var V86FS_MSG_CLOSE = 5;
15419
+ var V86FS_MSG_READ = 6;
15420
+ var V86FS_MSG_CREATE = 7;
15421
+ var V86FS_MSG_WRITE = 8;
15422
+ var V86FS_MSG_MKDIR = 9;
15423
+ var V86FS_MSG_SETATTR = 10;
15424
+ var V86FS_MSG_FSYNC = 11;
15425
+ var V86FS_MSG_UNLINK = 12;
15426
+ var V86FS_MSG_RENAME = 13;
15427
+ var V86FS_MSG_SYMLINK = 14;
15428
+ var V86FS_MSG_READLINK = 15;
15429
+ var V86FS_MSG_STATFS = 16;
15430
+ var V86FS_MSG_INVALIDATE = 32;
15431
+ var V86FS_MSG_INVALIDATE_DIR = 33;
15432
+ var V86FS_MSG_MOUNT_R = 128;
15433
+ var V86FS_MSG_LOOKUP_R = 129;
15434
+ var V86FS_MSG_GETATTR_R = 130;
15435
+ var V86FS_MSG_READDIR_R = 131;
15436
+ var V86FS_MSG_OPEN_R = 132;
15437
+ var V86FS_MSG_CLOSE_R = 133;
15438
+ var V86FS_MSG_READ_R = 134;
15439
+ var V86FS_MSG_CREATE_R = 135;
15440
+ var V86FS_MSG_WRITE_R = 136;
15441
+ var V86FS_MSG_MKDIR_R = 137;
15442
+ var V86FS_MSG_SETATTR_R = 138;
15443
+ var V86FS_MSG_FSYNC_R = 139;
15444
+ var V86FS_MSG_UNLINK_R = 140;
15445
+ var V86FS_MSG_RENAME_R = 141;
15446
+ var V86FS_MSG_SYMLINK_R = 142;
15447
+ var V86FS_MSG_READLINK_R = 143;
15448
+ var V86FS_MSG_STATFS_R = 144;
15449
+ var ATTR_MODE = 1;
15450
+ var ATTR_SIZE = 8;
15451
+ var V86FS_STATUS_OK = 0;
15452
+ var V86FS_STATUS_ENOENT = 2;
15453
+ var DT_DIR = 4;
15454
+ var DT_REG = 8;
15455
+ var DT_LNK = 10;
15456
+ var S_IFDIR = 16384;
15457
+ var S_IFREG = 32768;
15458
+ var S_IFLNK = 40960;
15459
+ var textDecoder = new TextDecoder();
15460
+ var textEncoder = new TextEncoder();
15461
+ var FS_ENTRIES = /* @__PURE__ */ new Map([
15462
+ [
15463
+ 1,
15464
+ [
15465
+ {
15466
+ inode_id: 2,
15467
+ name: "hello.txt",
15468
+ mode: S_IFREG | 420,
15469
+ size: 12,
15470
+ dt_type: DT_REG,
15471
+ mtime_sec: 17115e5,
15472
+ mtime_nsec: 0,
15473
+ content: textEncoder.encode("hello world\n")
15474
+ },
15475
+ {
15476
+ inode_id: 3,
15477
+ name: "subdir",
15478
+ mode: S_IFDIR | 493,
15479
+ size: 0,
15480
+ dt_type: DT_DIR,
15481
+ mtime_sec: 17115e5,
15482
+ mtime_nsec: 0
15483
+ }
15484
+ ]
15485
+ ]
15486
+ ]);
15487
+ var INODE_MAP = /* @__PURE__ */ new Map([
15488
+ [
15489
+ 1,
15490
+ {
15491
+ inode_id: 1,
15492
+ name: "",
15493
+ mode: S_IFDIR | 493,
15494
+ size: 0,
15495
+ dt_type: DT_DIR,
15496
+ mtime_sec: 17115e5,
15497
+ mtime_nsec: 0
15498
+ }
15499
+ ]
15500
+ ]);
15501
+ for (const entries of FS_ENTRIES.values()) {
15502
+ for (const e of entries) {
15503
+ INODE_MAP.set(e.inode_id, e);
15504
+ }
15505
+ }
15506
+ var V86FS_HDR_SIZE = 7;
15507
+ function packU32(buf, offset, val) {
15508
+ buf[offset] = val & 255;
15509
+ buf[offset + 1] = val >>> 8 & 255;
15510
+ buf[offset + 2] = val >>> 16 & 255;
15511
+ buf[offset + 3] = val >>> 24 & 255;
15512
+ }
15513
+ function packU64(buf, offset, val) {
15514
+ packU32(buf, offset, val);
15515
+ packU32(buf, offset + 4, 0);
15516
+ }
15517
+ function packU16(buf, offset, val) {
15518
+ buf[offset] = val & 255;
15519
+ buf[offset + 1] = val >>> 8 & 255;
15520
+ }
15521
+ function makeResp(size, type, tag) {
15522
+ const resp = new Uint8Array(size);
15523
+ packU32(resp, 0, size);
15524
+ resp[4] = type;
15525
+ resp[5] = tag & 255;
15526
+ resp[6] = tag >> 8 & 255;
15527
+ return resp;
15528
+ }
15529
+ function readU32(buf, offset) {
15530
+ return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | buf[offset + 3] << 24 >>> 0;
15531
+ }
15532
+ function readU16(buf, offset) {
15533
+ return buf[offset] | buf[offset + 1] << 8;
15534
+ }
15535
+ function readU64(buf, offset) {
15536
+ 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;
15537
+ }
15538
+ var VirtioV86FS = class {
15539
+ bus;
15540
+ virtio;
15541
+ next_handle_id;
15542
+ next_inode_id;
15543
+ open_handles;
15544
+ // handle_id -> inode_id
15545
+ read_count;
15546
+ constructor(cpu, bus) {
15547
+ this.bus = bus;
15548
+ this.next_handle_id = 1;
15549
+ this.next_inode_id = 100;
15550
+ this.open_handles = /* @__PURE__ */ new Map();
15551
+ this.read_count = 0;
15552
+ const queues = [
15553
+ // Queue 0: hipriq - high-priority metadata (LOOKUP, GETATTR)
15554
+ { size_supported: 128, notify_offset: 0 },
15555
+ // Queue 1: requestq - data requests (READ, WRITE, READDIR)
15556
+ { size_supported: 128, notify_offset: 1 },
15557
+ // Queue 2: notifyq - host-to-guest push invalidation
15558
+ { size_supported: 128, notify_offset: 2 }
15559
+ ];
15560
+ this.virtio = new VirtIO(cpu, {
15561
+ name: "virtio-v86fs",
15562
+ pci_id: 14 << 3,
15563
+ device_id: 4223,
15564
+ subsystem_device_id: 63,
15565
+ common: {
15566
+ initial_port: 63488,
15567
+ queues,
15568
+ features: [VIRTIO_F_VERSION_1],
15569
+ on_driver_ok: () => {
15570
+ console.log("v86fs: driver ok");
15571
+ this.bus.send("virtio-v86fs-driver-ok");
15572
+ }
15573
+ },
15574
+ notification: {
15575
+ initial_port: 63744,
15576
+ single_handler: false,
15577
+ handlers: [
15578
+ // Queue 0: hipriq
15579
+ (queue_id) => {
15580
+ this.handle_queue(queue_id);
15581
+ },
15582
+ // Queue 1: requestq
15583
+ (queue_id) => {
15584
+ this.handle_queue(queue_id);
15585
+ },
15586
+ // Queue 2: notifyq
15587
+ (_queue_id) => {
15588
+ dbg_log("v86fs: notifyq notification", LOG_PCI);
15589
+ }
15590
+ ]
15591
+ },
15592
+ isr_status: {
15593
+ initial_port: 63232
15594
+ }
15595
+ });
15596
+ }
15597
+ handle_queue(queue_id) {
15598
+ const queue = this.virtio.queues[queue_id];
15599
+ while (queue.has_request()) {
15600
+ const bufchain = queue.pop_request();
15601
+ const req = new Uint8Array(bufchain.length_readable);
15602
+ bufchain.get_next_blob(req);
15603
+ const resp = this.handle_message(req);
15604
+ if (resp && bufchain.length_writable > 0) {
15605
+ bufchain.set_next_blob(resp);
15606
+ }
15607
+ queue.push_reply(bufchain);
15608
+ }
15609
+ queue.flush_replies();
15610
+ }
15611
+ handle_message(req) {
15612
+ if (req.length < V86FS_HDR_SIZE) {
15613
+ console.warn("v86fs: message too short:", req.length);
15614
+ return null;
15615
+ }
15616
+ const type = req[4];
15617
+ const tag = readU16(req, 5);
15618
+ switch (type) {
15619
+ case V86FS_MSG_MOUNT:
15620
+ return this.handle_mount(req, tag);
15621
+ case V86FS_MSG_LOOKUP:
15622
+ return this.handle_lookup(req, tag);
15623
+ case V86FS_MSG_GETATTR:
15624
+ return this.handle_getattr(req, tag);
15625
+ case V86FS_MSG_READDIR:
15626
+ return this.handle_readdir(req, tag);
15627
+ case V86FS_MSG_OPEN:
15628
+ return this.handle_open(req, tag);
15629
+ case V86FS_MSG_CLOSE:
15630
+ return this.handle_close(req, tag);
15631
+ case V86FS_MSG_READ:
15632
+ return this.handle_read(req, tag);
15633
+ case V86FS_MSG_CREATE:
15634
+ return this.handle_create(req, tag);
15635
+ case V86FS_MSG_WRITE:
15636
+ return this.handle_write(req, tag);
15637
+ case V86FS_MSG_MKDIR:
15638
+ return this.handle_mkdir(req, tag);
15639
+ case V86FS_MSG_SETATTR:
15640
+ return this.handle_setattr(req, tag);
15641
+ case V86FS_MSG_FSYNC:
15642
+ return this.handle_fsync(req, tag);
15643
+ case V86FS_MSG_UNLINK:
15644
+ return this.handle_unlink(req, tag);
15645
+ case V86FS_MSG_RENAME:
15646
+ return this.handle_rename(req, tag);
15647
+ case V86FS_MSG_SYMLINK:
15648
+ return this.handle_symlink(req, tag);
15649
+ case V86FS_MSG_READLINK:
15650
+ return this.handle_readlink(req, tag);
15651
+ case V86FS_MSG_STATFS:
15652
+ return this.handle_statfs(tag);
15653
+ default:
15654
+ console.warn("v86fs: unknown message type:", type);
15655
+ return null;
15656
+ }
15657
+ }
15658
+ handle_mount(req, tag) {
15659
+ const name_len = readU16(req, 7);
15660
+ const name = name_len > 0 ? textDecoder.decode(req.subarray(9, 9 + name_len)) : "";
15661
+ console.log("v86fs: mount:", name || "(default)");
15662
+ this.bus.send("virtio-v86fs-mount", name);
15663
+ const resp = new Uint8Array(23);
15664
+ packU32(resp, 0, 23);
15665
+ resp[4] = V86FS_MSG_MOUNT_R;
15666
+ resp[5] = tag & 255;
15667
+ resp[6] = tag >> 8 & 255;
15668
+ packU32(resp, 7, 0);
15669
+ packU64(resp, 11, 1);
15670
+ packU32(resp, 19, 16877);
15671
+ return resp;
15672
+ }
15673
+ handle_getattr(req, tag) {
15674
+ const inode_id = readU64(req, 7);
15675
+ const entry = INODE_MAP.get(inode_id);
15676
+ const resp = makeResp(35, V86FS_MSG_GETATTR_R, tag);
15677
+ if (!entry) {
15678
+ packU32(resp, 7, V86FS_STATUS_ENOENT);
15679
+ return resp;
15680
+ }
15681
+ packU32(resp, 7, V86FS_STATUS_OK);
15682
+ packU32(resp, 11, entry.mode);
15683
+ packU64(resp, 15, entry.size);
15684
+ packU64(resp, 23, entry.mtime_sec);
15685
+ packU32(resp, 31, entry.mtime_nsec);
15686
+ return resp;
15687
+ }
15688
+ handle_lookup(req, tag) {
15689
+ const parent_id = readU64(req, 7);
15690
+ const name_len = readU16(req, 15);
15691
+ const name = textDecoder.decode(req.subarray(17, 17 + name_len));
15692
+ const entries = FS_ENTRIES.get(parent_id);
15693
+ const entry = entries?.find((e) => e.name === name);
15694
+ const resp = new Uint8Array(31);
15695
+ packU32(resp, 0, 31);
15696
+ resp[4] = V86FS_MSG_LOOKUP_R;
15697
+ resp[5] = tag & 255;
15698
+ resp[6] = tag >> 8 & 255;
15699
+ if (!entry) {
15700
+ packU32(resp, 7, V86FS_STATUS_ENOENT);
15701
+ return resp;
15702
+ }
15703
+ packU32(resp, 7, V86FS_STATUS_OK);
15704
+ packU64(resp, 11, entry.inode_id);
15705
+ packU32(resp, 19, entry.mode);
15706
+ packU64(resp, 23, entry.size);
15707
+ return resp;
15708
+ }
15709
+ handle_readdir(req, tag) {
15710
+ const dir_id = readU64(req, 7);
15711
+ const entries = FS_ENTRIES.get(dir_id) || [];
15712
+ const encodedNames = entries.map((e) => textEncoder.encode(e.name));
15713
+ let size = 7 + 4 + 4;
15714
+ for (const nameBytes of encodedNames) {
15715
+ size += 8 + 1 + 2 + nameBytes.length;
15716
+ }
15717
+ const resp = makeResp(size, V86FS_MSG_READDIR_R, tag);
15718
+ packU32(resp, 7, V86FS_STATUS_OK);
15719
+ packU32(resp, 11, entries.length);
15720
+ let off = 15;
15721
+ for (let i = 0; i < entries.length; i++) {
15722
+ const e = entries[i];
15723
+ const nameBytes = encodedNames[i];
15724
+ packU64(resp, off, e.inode_id);
15725
+ resp[off + 8] = e.dt_type;
15726
+ packU16(resp, off + 9, nameBytes.length);
15727
+ resp.set(nameBytes, off + 11);
15728
+ off += 11 + nameBytes.length;
15729
+ }
15730
+ return resp;
15731
+ }
15732
+ handle_open(req, tag) {
15733
+ const inode_id = readU64(req, 7);
15734
+ const handle_id = this.next_handle_id++;
15735
+ this.open_handles.set(handle_id, inode_id);
15736
+ this.bus.send("virtio-v86fs-open", inode_id);
15737
+ const resp = makeResp(19, V86FS_MSG_OPEN_R, tag);
15738
+ packU32(resp, 7, V86FS_STATUS_OK);
15739
+ packU64(resp, 11, handle_id);
15740
+ return resp;
15741
+ }
15742
+ handle_close(req, tag) {
15743
+ const handle_id = readU64(req, 7);
15744
+ this.open_handles.delete(handle_id);
15745
+ this.bus.send("virtio-v86fs-close", handle_id);
15746
+ const resp = makeResp(11, V86FS_MSG_CLOSE_R, tag);
15747
+ packU32(resp, 7, V86FS_STATUS_OK);
15748
+ return resp;
15749
+ }
15750
+ handle_read(req, tag) {
15751
+ const handle_id = readU64(req, 7);
15752
+ const offset = readU64(req, 15);
15753
+ const size = readU32(req, 23);
15754
+ const inode_id = this.open_handles.get(handle_id) ?? handle_id;
15755
+ const entry = INODE_MAP.get(inode_id);
15756
+ const content = entry?.content;
15757
+ this.read_count++;
15758
+ this.bus.send("virtio-v86fs-read", {
15759
+ handle_id,
15760
+ inode_id,
15761
+ offset,
15762
+ size
15763
+ });
15764
+ if (!content || offset >= content.length) {
15765
+ const resp2 = makeResp(15, V86FS_MSG_READ_R, tag);
15766
+ packU32(resp2, 7, V86FS_STATUS_OK);
15767
+ packU32(resp2, 11, 0);
15768
+ return resp2;
15769
+ }
15770
+ const start = Math.min(offset, content.length);
15771
+ const end = Math.min(start + size, content.length);
15772
+ const data = content.subarray(start, end);
15773
+ const resp = new Uint8Array(15 + data.length);
15774
+ packU32(resp, 0, 15 + data.length);
15775
+ resp[4] = V86FS_MSG_READ_R;
15776
+ resp[5] = tag & 255;
15777
+ resp[6] = tag >> 8 & 255;
15778
+ packU32(resp, 7, V86FS_STATUS_OK);
15779
+ packU32(resp, 11, data.length);
15780
+ resp.set(data, 15);
15781
+ return resp;
15782
+ }
15783
+ handle_create(req, tag) {
15784
+ const parent_id = readU64(req, 7);
15785
+ const name_len = readU16(req, 15);
15786
+ const name = textDecoder.decode(req.subarray(17, 17 + name_len));
15787
+ const mode = readU32(req, 17 + name_len);
15788
+ const inode_id = this.next_inode_id++;
15789
+ const entry = {
15790
+ inode_id,
15791
+ name,
15792
+ mode: mode | S_IFREG,
15793
+ size: 0,
15794
+ dt_type: DT_REG,
15795
+ mtime_sec: Math.floor(Date.now() / 1e3),
15796
+ mtime_nsec: 0,
15797
+ content: new Uint8Array(0)
15798
+ };
15799
+ let children = FS_ENTRIES.get(parent_id);
15800
+ if (!children) {
15801
+ children = [];
15802
+ FS_ENTRIES.set(parent_id, children);
15803
+ }
15804
+ children.push(entry);
15805
+ INODE_MAP.set(inode_id, entry);
15806
+ const resp = makeResp(23, V86FS_MSG_CREATE_R, tag);
15807
+ packU32(resp, 7, V86FS_STATUS_OK);
15808
+ packU64(resp, 11, inode_id);
15809
+ packU32(resp, 19, entry.mode);
15810
+ return resp;
15811
+ }
15812
+ handle_write(req, tag) {
15813
+ const inode_id = readU64(req, 7);
15814
+ const offset = readU64(req, 15);
15815
+ const size = readU32(req, 23);
15816
+ const data = req.subarray(27, 27 + size);
15817
+ const entry = INODE_MAP.get(inode_id);
15818
+ if (entry) {
15819
+ const needed = offset + size;
15820
+ if (!entry.content || entry.content.length < needed) {
15821
+ const newContent = new Uint8Array(needed);
15822
+ if (entry.content) {
15823
+ newContent.set(entry.content);
15824
+ }
15825
+ entry.content = newContent;
15826
+ }
15827
+ entry.content.set(data, offset);
15828
+ if (needed > entry.size) {
15829
+ entry.size = needed;
15830
+ }
15831
+ }
15832
+ const resp = makeResp(15, V86FS_MSG_WRITE_R, tag);
15833
+ packU32(resp, 7, V86FS_STATUS_OK);
15834
+ packU32(resp, 11, size);
15835
+ return resp;
15836
+ }
15837
+ handle_mkdir(req, tag) {
15838
+ const parent_id = readU64(req, 7);
15839
+ const name_len = readU16(req, 15);
15840
+ const name = textDecoder.decode(req.subarray(17, 17 + name_len));
15841
+ const mode = readU32(req, 17 + name_len);
15842
+ const inode_id = this.next_inode_id++;
15843
+ const entry = {
15844
+ inode_id,
15845
+ name,
15846
+ mode: mode | S_IFDIR,
15847
+ size: 0,
15848
+ dt_type: DT_DIR,
15849
+ mtime_sec: Math.floor(Date.now() / 1e3),
15850
+ mtime_nsec: 0
15851
+ };
15852
+ let children = FS_ENTRIES.get(parent_id);
15853
+ if (!children) {
15854
+ children = [];
15855
+ FS_ENTRIES.set(parent_id, children);
15856
+ }
15857
+ children.push(entry);
15858
+ INODE_MAP.set(inode_id, entry);
15859
+ FS_ENTRIES.set(inode_id, []);
15860
+ const resp = makeResp(23, V86FS_MSG_MKDIR_R, tag);
15861
+ packU32(resp, 7, V86FS_STATUS_OK);
15862
+ packU64(resp, 11, inode_id);
15863
+ packU32(resp, 19, entry.mode);
15864
+ return resp;
15865
+ }
15866
+ handle_setattr(req, tag) {
15867
+ const inode_id = readU64(req, 7);
15868
+ const valid = readU32(req, 15);
15869
+ const mode = readU32(req, 19);
15870
+ const size = readU64(req, 23);
15871
+ const entry = INODE_MAP.get(inode_id);
15872
+ if (entry) {
15873
+ if (valid & ATTR_MODE) {
15874
+ entry.mode = entry.mode & 61440 | mode & 4095;
15875
+ }
15876
+ if (valid & ATTR_SIZE) {
15877
+ entry.size = size;
15878
+ if (entry.content) {
15879
+ if (size === 0) {
15880
+ entry.content = new Uint8Array(0);
15881
+ } else if (size < entry.content.length) {
15882
+ entry.content = entry.content.subarray(0, size);
15883
+ }
15884
+ }
15885
+ }
15886
+ }
15887
+ const resp = makeResp(11, V86FS_MSG_SETATTR_R, tag);
15888
+ packU32(resp, 7, V86FS_STATUS_OK);
15889
+ return resp;
15890
+ }
15891
+ handle_fsync(req, tag) {
15892
+ const resp = makeResp(11, V86FS_MSG_FSYNC_R, tag);
15893
+ packU32(resp, 7, V86FS_STATUS_OK);
15894
+ return resp;
15895
+ }
15896
+ handle_unlink(req, tag) {
15897
+ const parent_id = readU64(req, 7);
15898
+ const name_len = readU16(req, 15);
15899
+ const name = textDecoder.decode(req.subarray(17, 17 + name_len));
15900
+ const children = FS_ENTRIES.get(parent_id);
15901
+ let status = V86FS_STATUS_ENOENT;
15902
+ if (children) {
15903
+ const idx = children.findIndex((e) => e.name === name);
15904
+ if (idx >= 0) {
15905
+ const entry = children[idx];
15906
+ INODE_MAP.delete(entry.inode_id);
15907
+ FS_ENTRIES.delete(entry.inode_id);
15908
+ children.splice(idx, 1);
15909
+ status = V86FS_STATUS_OK;
15910
+ }
15911
+ }
15912
+ const resp = makeResp(11, V86FS_MSG_UNLINK_R, tag);
15913
+ packU32(resp, 7, status);
15914
+ return resp;
15915
+ }
15916
+ handle_rename(req, tag) {
15917
+ let off = 7;
15918
+ const old_parent_id = readU64(req, off);
15919
+ off += 8;
15920
+ const old_name_len = readU16(req, off);
15921
+ off += 2;
15922
+ const old_name = textDecoder.decode(
15923
+ req.subarray(off, off + old_name_len)
15924
+ );
15925
+ off += old_name_len;
15926
+ const new_parent_id = readU64(req, off);
15927
+ off += 8;
15928
+ const new_name_len = readU16(req, off);
15929
+ off += 2;
15930
+ const new_name = textDecoder.decode(
15931
+ req.subarray(off, off + new_name_len)
15932
+ );
15933
+ let status = V86FS_STATUS_ENOENT;
15934
+ const old_children = FS_ENTRIES.get(old_parent_id);
15935
+ if (old_children) {
15936
+ const idx = old_children.findIndex((e) => e.name === old_name);
15937
+ if (idx >= 0) {
15938
+ const entry = old_children[idx];
15939
+ old_children.splice(idx, 1);
15940
+ entry.name = new_name;
15941
+ let new_children = FS_ENTRIES.get(new_parent_id);
15942
+ if (!new_children) {
15943
+ new_children = [];
15944
+ FS_ENTRIES.set(new_parent_id, new_children);
15945
+ }
15946
+ const existing = new_children.findIndex(
15947
+ (e) => e.name === new_name
15948
+ );
15949
+ if (existing >= 0) {
15950
+ const old_entry = new_children[existing];
15951
+ INODE_MAP.delete(old_entry.inode_id);
15952
+ new_children.splice(existing, 1);
15953
+ }
15954
+ new_children.push(entry);
15955
+ status = V86FS_STATUS_OK;
15956
+ }
15957
+ }
15958
+ const resp = makeResp(11, V86FS_MSG_RENAME_R, tag);
15959
+ packU32(resp, 7, status);
15960
+ return resp;
15961
+ }
15962
+ handle_symlink(req, tag) {
15963
+ let off = 7;
15964
+ const parent_id = readU64(req, off);
15965
+ off += 8;
15966
+ const name_len = readU16(req, off);
15967
+ off += 2;
15968
+ const name = textDecoder.decode(req.subarray(off, off + name_len));
15969
+ off += name_len;
15970
+ const target_len = readU16(req, off);
15971
+ off += 2;
15972
+ const target = textDecoder.decode(req.subarray(off, off + target_len));
15973
+ const inode_id = this.next_inode_id++;
15974
+ const entry = {
15975
+ inode_id,
15976
+ name,
15977
+ mode: S_IFLNK | 511,
15978
+ size: target.length,
15979
+ dt_type: DT_LNK,
15980
+ mtime_sec: Math.floor(Date.now() / 1e3),
15981
+ mtime_nsec: 0,
15982
+ symlink_target: target
15983
+ };
15984
+ let children = FS_ENTRIES.get(parent_id);
15985
+ if (!children) {
15986
+ children = [];
15987
+ FS_ENTRIES.set(parent_id, children);
15988
+ }
15989
+ children.push(entry);
15990
+ INODE_MAP.set(inode_id, entry);
15991
+ const resp = makeResp(23, V86FS_MSG_SYMLINK_R, tag);
15992
+ packU32(resp, 7, V86FS_STATUS_OK);
15993
+ packU64(resp, 11, inode_id);
15994
+ packU32(resp, 19, entry.mode);
15995
+ return resp;
15996
+ }
15997
+ handle_readlink(req, tag) {
15998
+ const inode_id = readU64(req, 7);
15999
+ const entry = INODE_MAP.get(inode_id);
16000
+ if (!entry || !entry.symlink_target) {
16001
+ const resp2 = makeResp(11, V86FS_MSG_READLINK_R, tag);
16002
+ packU32(resp2, 7, V86FS_STATUS_ENOENT);
16003
+ return resp2;
16004
+ }
16005
+ const target_bytes = textEncoder.encode(entry.symlink_target);
16006
+ const resp_len = 11 + 2 + target_bytes.length;
16007
+ const resp = makeResp(resp_len, V86FS_MSG_READLINK_R, tag);
16008
+ packU32(resp, 7, V86FS_STATUS_OK);
16009
+ packU16(resp, 11, target_bytes.length);
16010
+ resp.set(target_bytes, 13);
16011
+ return resp;
16012
+ }
16013
+ handle_statfs(tag) {
16014
+ const resp = makeResp(55, V86FS_MSG_STATFS_R, tag);
16015
+ packU32(resp, 7, V86FS_STATUS_OK);
16016
+ packU64(resp, 11, 1024 * 1024);
16017
+ packU64(resp, 19, 512 * 1024);
16018
+ packU64(resp, 27, 512 * 1024);
16019
+ packU64(resp, 35, 1024 * 1024);
16020
+ packU64(resp, 43, 512 * 1024);
16021
+ packU32(resp, 51, 4096);
16022
+ return resp;
16023
+ }
16024
+ /** Push an INVALIDATE notification to the guest via notifyq.
16025
+ * Guest kernel will invalidate page cache for the given inode. */
16026
+ invalidate_inode(inode_id) {
16027
+ const queue = this.virtio.queues[2];
16028
+ if (!queue.has_request()) return false;
16029
+ const bufchain = queue.pop_request();
16030
+ const msg = new Uint8Array(15);
16031
+ packU32(msg, 0, 15);
16032
+ msg[4] = V86FS_MSG_INVALIDATE;
16033
+ packU16(msg, 5, 0);
16034
+ packU64(msg, 7, inode_id);
16035
+ bufchain.set_next_blob(msg);
16036
+ queue.push_reply(bufchain);
16037
+ queue.flush_replies();
16038
+ return true;
16039
+ }
16040
+ /** Push an INVALIDATE_DIR notification to the guest via notifyq.
16041
+ * Guest kernel will invalidate dcache for the given directory inode. */
16042
+ invalidate_dir(inode_id) {
16043
+ const queue = this.virtio.queues[2];
16044
+ if (!queue.has_request()) return false;
16045
+ const bufchain = queue.pop_request();
16046
+ const msg = new Uint8Array(15);
16047
+ packU32(msg, 0, 15);
16048
+ msg[4] = V86FS_MSG_INVALIDATE_DIR;
16049
+ packU16(msg, 5, 0);
16050
+ packU64(msg, 7, inode_id);
16051
+ bufchain.set_next_blob(msg);
16052
+ queue.push_reply(bufchain);
16053
+ queue.flush_replies();
16054
+ return true;
16055
+ }
16056
+ get_state() {
16057
+ const state = [];
16058
+ state[0] = this.virtio;
16059
+ return state;
16060
+ }
16061
+ set_state(state) {
16062
+ this.virtio.set_state(state[0]);
16063
+ }
16064
+ };
16065
+
15141
16066
  // lib/filesystem.ts
15142
16067
  var S_IFMT = 61440;
15143
16068
  var S_IFSOCK = 49152;
15144
- var S_IFLNK = 40960;
15145
- var S_IFREG = 32768;
15146
- var S_IFDIR = 16384;
16069
+ var S_IFLNK2 = 40960;
16070
+ var S_IFREG2 = 32768;
16071
+ var S_IFDIR2 = 16384;
15147
16072
  var STATUS_INVALID = -1;
15148
16073
  var STATUS_OK = 0;
15149
16074
  var STATUS_ON_STORAGE = 2;
@@ -15236,11 +16161,11 @@ var V86Starter = (() => {
15236
16161
  get_state() {
15237
16162
  const state = [];
15238
16163
  state[0] = this.mode;
15239
- if ((this.mode & S_IFMT) === S_IFDIR) {
16164
+ if ((this.mode & S_IFMT) === S_IFDIR2) {
15240
16165
  state[1] = [...this.direntries];
15241
- } else if ((this.mode & S_IFMT) === S_IFREG) {
16166
+ } else if ((this.mode & S_IFMT) === S_IFREG2) {
15242
16167
  state[1] = this.sha256sum;
15243
- } else if ((this.mode & S_IFMT) === S_IFLNK) {
16168
+ } else if ((this.mode & S_IFMT) === S_IFLNK2) {
15244
16169
  state[1] = this.symlink;
15245
16170
  } else if ((this.mode & S_IFMT) === S_IFSOCK) {
15246
16171
  state[1] = [this.minor, this.major];
@@ -15263,14 +16188,14 @@ var V86Starter = (() => {
15263
16188
  }
15264
16189
  set_state(state) {
15265
16190
  this.mode = state[0];
15266
- if ((this.mode & S_IFMT) === S_IFDIR) {
16191
+ if ((this.mode & S_IFMT) === S_IFDIR2) {
15267
16192
  this.direntries = /* @__PURE__ */ new Map();
15268
16193
  for (const [name, entry] of state[1]) {
15269
16194
  this.direntries.set(name, entry);
15270
16195
  }
15271
- } else if ((this.mode & S_IFMT) === S_IFREG) {
16196
+ } else if ((this.mode & S_IFMT) === S_IFREG2) {
15272
16197
  this.sha256sum = state[1];
15273
- } else if ((this.mode & S_IFMT) === S_IFLNK) {
16198
+ } else if ((this.mode & S_IFMT) === S_IFLNK2) {
15274
16199
  this.symlink = state[1];
15275
16200
  } else if ((this.mode & S_IFMT) === S_IFSOCK) {
15276
16201
  ;
@@ -15339,7 +16264,7 @@ var V86Starter = (() => {
15339
16264
  state[1] = this.qidcounter.last_qidnumber;
15340
16265
  state[2] = [];
15341
16266
  for (const [id, data] of Object.entries(this.inodedata)) {
15342
- if ((this.inodes[Number(id)].mode & S_IFDIR) === 0) {
16267
+ if ((this.inodes[Number(id)].mode & S_IFDIR2) === 0) {
15343
16268
  state[2].push([id, data]);
15344
16269
  }
15345
16270
  }
@@ -15390,15 +16315,15 @@ var V86Starter = (() => {
15390
16315
  inode.uid = data[JSONFS_IDX_UID];
15391
16316
  inode.gid = data[JSONFS_IDX_GID];
15392
16317
  const ifmt = inode.mode & S_IFMT;
15393
- if (ifmt === S_IFDIR) {
16318
+ if (ifmt === S_IFDIR2) {
15394
16319
  this.PushInode(inode, parentid, name);
15395
16320
  this.LoadDir(this.inodes.length - 1, data[JSONFS_IDX_TARGET]);
15396
- } else if (ifmt === S_IFREG) {
16321
+ } else if (ifmt === S_IFREG2) {
15397
16322
  inode.status = STATUS_ON_STORAGE;
15398
16323
  inode.sha256sum = data[JSONFS_IDX_SHA256];
15399
16324
  dbg_assert(!!inode.sha256sum);
15400
16325
  this.PushInode(inode, parentid, name);
15401
- } else if (ifmt === S_IFLNK) {
16326
+ } else if (ifmt === S_IFLNK2) {
15402
16327
  inode.symlink = data[JSONFS_IDX_TARGET];
15403
16328
  this.PushInode(inode, parentid, name);
15404
16329
  } else if (ifmt === S_IFSOCK) {
@@ -15570,13 +16495,13 @@ var V86Starter = (() => {
15570
16495
  return this.create_forwarder(parent_inode.mount_id, foreign_id);
15571
16496
  }
15572
16497
  const x = this.CreateInode();
15573
- x.mode = 511 | S_IFDIR;
16498
+ x.mode = 511 | S_IFDIR2;
15574
16499
  if (parentid >= 0) {
15575
16500
  x.uid = this.inodes[parentid].uid;
15576
16501
  x.gid = this.inodes[parentid].gid;
15577
- x.mode = this.inodes[parentid].mode & 511 | S_IFDIR;
16502
+ x.mode = this.inodes[parentid].mode & 511 | S_IFDIR2;
15578
16503
  }
15579
- x.qid.type = S_IFDIR >> 8;
16504
+ x.qid.type = S_IFDIR2 >> 8;
15580
16505
  this.PushInode(x, parentid, name);
15581
16506
  this.NotifyListeners(this.inodes.length - 1, "newdir");
15582
16507
  return this.inodes.length - 1;
@@ -15594,8 +16519,8 @@ var V86Starter = (() => {
15594
16519
  const x = this.CreateInode();
15595
16520
  x.uid = this.inodes[parentid].uid;
15596
16521
  x.gid = this.inodes[parentid].gid;
15597
- x.qid.type = S_IFREG >> 8;
15598
- x.mode = this.inodes[parentid].mode & 438 | S_IFREG;
16522
+ x.qid.type = S_IFREG2 >> 8;
16523
+ x.mode = this.inodes[parentid].mode & 438 | S_IFREG2;
15599
16524
  this.PushInode(x, parentid, filename);
15600
16525
  this.NotifyListeners(this.inodes.length - 1, "newfile");
15601
16526
  return this.inodes.length - 1;
@@ -15636,9 +16561,9 @@ var V86Starter = (() => {
15636
16561
  const x = this.CreateInode();
15637
16562
  x.uid = this.inodes[parentid].uid;
15638
16563
  x.gid = this.inodes[parentid].gid;
15639
- x.qid.type = S_IFLNK >> 8;
16564
+ x.qid.type = S_IFLNK2 >> 8;
15640
16565
  x.symlink = symlink;
15641
- x.mode = S_IFLNK;
16566
+ x.mode = S_IFLNK2;
15642
16567
  this.PushInode(x, parentid, filename);
15643
16568
  return this.inodes.length - 1;
15644
16569
  }
@@ -15683,7 +16608,7 @@ var V86Starter = (() => {
15683
16608
  if (this.is_forwarder(inode)) {
15684
16609
  return await this.follow_fs(inode).OpenInode(inode.foreign_id, mode);
15685
16610
  }
15686
- if ((inode.mode & S_IFMT) === S_IFDIR) {
16611
+ if ((inode.mode & S_IFMT) === S_IFDIR2) {
15687
16612
  this.FillDirectory(id);
15688
16613
  }
15689
16614
  }
@@ -16038,7 +16963,7 @@ var V86Starter = (() => {
16038
16963
  let parentid = -1;
16039
16964
  let id = 0;
16040
16965
  let forward_path = null;
16041
- let i = 0;
16966
+ let i;
16042
16967
  for (i = 0; i < n; i++) {
16043
16968
  parentid = id;
16044
16969
  id = this.Search(parentid, walk[i]);
@@ -16104,13 +17029,13 @@ var V86Starter = (() => {
16104
17029
  DeleteNode(path) {
16105
17030
  const ids = this.SearchPath(path);
16106
17031
  if (ids.id === -1) return;
16107
- if ((this.inodes[ids.id].mode & S_IFMT) === S_IFREG) {
17032
+ if ((this.inodes[ids.id].mode & S_IFMT) === S_IFREG2) {
16108
17033
  const ret = this.Unlink(ids.parentid, ids.name);
16109
17034
  dbg_assert(
16110
17035
  ret === 0,
16111
17036
  "Filesystem DeleteNode failed with error code: " + -ret
16112
17037
  );
16113
- } else if ((this.inodes[ids.id].mode & S_IFMT) === S_IFDIR) {
17038
+ } else if ((this.inodes[ids.id].mode & S_IFMT) === S_IFDIR2) {
16114
17039
  this.RecursiveDelete(path);
16115
17040
  const ret = this.Unlink(ids.parentid, ids.name);
16116
17041
  dbg_assert(
@@ -16214,7 +17139,7 @@ var V86Starter = (() => {
16214
17139
  if (this.is_forwarder(inode)) {
16215
17140
  return this.follow_fs(inode).IsDirectory(inode.foreign_id);
16216
17141
  }
16217
- return (inode.mode & S_IFMT) === S_IFDIR;
17142
+ return (inode.mode & S_IFMT) === S_IFDIR2;
16218
17143
  }
16219
17144
  IsEmpty(idx) {
16220
17145
  const inode = this.inodes[idx];
@@ -16744,7 +17669,7 @@ var V86Starter = (() => {
16744
17669
  bufchain.get_next_blob(buffer);
16745
17670
  const state = { offset: 0 };
16746
17671
  const header = Unmarshall(["w", "b", "h"], buffer, state);
16747
- let size = header[0];
17672
+ let size;
16748
17673
  const id = header[1];
16749
17674
  const tag = header[2];
16750
17675
  switch (id) {
@@ -16805,7 +17730,7 @@ var V86Starter = (() => {
16805
17730
  name
16806
17731
  );
16807
17732
  if (ret < 0) {
16808
- let error_message = "";
17733
+ let error_message;
16809
17734
  if (ret === -EPERM)
16810
17735
  error_message = "Operation not permitted";
16811
17736
  else {
@@ -16918,7 +17843,7 @@ var V86Starter = (() => {
16918
17843
  this.fids[fid].inodeid
16919
17844
  );
16920
17845
  const inode = this.fs.GetInode(idx);
16921
- inode.mode = mode | S_IFDIR;
17846
+ inode.mode = mode | S_IFDIR2;
16922
17847
  inode.uid = this.fids[fid].uid;
16923
17848
  inode.gid = gid;
16924
17849
  Marshall(["Q"], [inode.qid], this.replybuffer, 7);
@@ -16949,7 +17874,7 @@ var V86Starter = (() => {
16949
17874
  const inode = this.fs.GetInode(idx);
16950
17875
  inode.uid = this.fids[fid].uid;
16951
17876
  inode.gid = gid;
16952
- inode.mode = mode | S_IFREG;
17877
+ inode.mode = mode | S_IFREG2;
16953
17878
  Marshall(
16954
17879
  ["Q", "w"],
16955
17880
  [inode.qid, this.msize - 24],
@@ -17274,7 +18199,7 @@ var V86Starter = (() => {
17274
18199
  newname
17275
18200
  );
17276
18201
  if (ret < 0) {
17277
- let error_message = "";
18202
+ let error_message;
17278
18203
  if (ret === -ENOENT)
17279
18204
  error_message = "No such file or directory";
17280
18205
  else if (ret === -EPERM)
@@ -17323,7 +18248,7 @@ var V86Starter = (() => {
17323
18248
  }
17324
18249
  const ret = this.fs.Unlink(this.fids[dirfd].inodeid, name);
17325
18250
  if (ret < 0) {
17326
- let error_message = "";
18251
+ let error_message;
17327
18252
  if (ret === -ENOTEMPTY)
17328
18253
  error_message = "Directory not empty";
17329
18254
  else if (ret === -EPERM)
@@ -17975,7 +18900,6 @@ var V86Starter = (() => {
17975
18900
  get_eflags;
17976
18901
  handle_irqs;
17977
18902
  main_loop;
17978
- reboot_internal;
17979
18903
  set_jit_config;
17980
18904
  read8;
17981
18905
  read16;
@@ -18024,10 +18948,10 @@ var V86Starter = (() => {
18024
18948
  this.name = "cpu";
18025
18949
  this.stop_idling = stop_idling;
18026
18950
  this.wm = wm;
18951
+ this.wasm_memory = wm.wasm_memory;
18027
18952
  this.wasm_patch();
18028
18953
  this.create_jit_imports();
18029
- const memory = this.wm.exports["memory"];
18030
- this.wasm_memory = memory;
18954
+ const memory = this.wasm_memory;
18031
18955
  this.memory_size = view(Uint32Array, memory, 812, 1);
18032
18956
  this.mem8 = new Uint8Array(0);
18033
18957
  this.mem32s = new Int32Array(this.mem8.buffer);
@@ -18172,7 +19096,7 @@ var V86Starter = (() => {
18172
19096
  }
18173
19097
  create_jit_imports() {
18174
19098
  const jit_imports = /* @__PURE__ */ Object.create(null);
18175
- jit_imports["m"] = this.wm.exports["memory"];
19099
+ jit_imports["m"] = this.wasm_memory;
18176
19100
  for (const name of Object.keys(this.wm.exports)) {
18177
19101
  if (name.startsWith("_") || name.startsWith("zstd") || name.endsWith("_js")) {
18178
19102
  continue;
@@ -18193,7 +19117,6 @@ var V86Starter = (() => {
18193
19117
  this.get_eflags = get_import("get_eflags");
18194
19118
  this.handle_irqs = get_import("handle_irqs");
18195
19119
  this.main_loop = get_import("main_loop");
18196
- this.reboot_internal = get_import("reboot_internal");
18197
19120
  this.set_jit_config = get_import("set_jit_config");
18198
19121
  this.read8 = get_import("read8");
18199
19122
  this.read16 = get_import("read16");
@@ -18350,6 +19273,8 @@ var V86Starter = (() => {
18350
19273
  state[86] = this.last_result;
18351
19274
  state[87] = this.fpu_status_word;
18352
19275
  state[88] = this.mxcsr;
19276
+ state[89] = this.devices.virtio_mem;
19277
+ state[90] = this.devices.virtio_v86fs;
18353
19278
  return state;
18354
19279
  }
18355
19280
  get_state_pic() {
@@ -18410,6 +19335,25 @@ var V86Starter = (() => {
18410
19335
  IOAPIC_STRUCT_SIZE
18411
19336
  );
18412
19337
  }
19338
+ resize_memory(new_size) {
19339
+ const mem8_offset = this.mem8.byteOffset;
19340
+ const needed_total = mem8_offset + new_size;
19341
+ const current_buffer = this.wasm_memory.buffer.byteLength;
19342
+ if (needed_total > current_buffer) {
19343
+ const grow_pages = Math.ceil(
19344
+ (needed_total - current_buffer) / WASM_PAGE_SIZE
19345
+ );
19346
+ this.wasm_memory.grow(grow_pages);
19347
+ }
19348
+ this.mem8 = view(Uint8Array, this.wasm_memory, mem8_offset, new_size);
19349
+ this.mem32s = view(
19350
+ Int32Array,
19351
+ this.wasm_memory,
19352
+ mem8_offset,
19353
+ new_size >> 2
19354
+ );
19355
+ this.memory_size[0] = new_size;
19356
+ }
18413
19357
  set_state(state) {
18414
19358
  this.memory_size[0] = state[0];
18415
19359
  if (this.mem8.length !== this.memory_size[0]) {
@@ -18509,6 +19453,10 @@ var V86Starter = (() => {
18509
19453
  this.devices.virtio_net.set_state(state[83]);
18510
19454
  if (this.devices.virtio_balloon)
18511
19455
  this.devices.virtio_balloon.set_state(state[84]);
19456
+ if (this.devices.virtio_mem && state[89])
19457
+ this.devices.virtio_mem.set_state(state[89]);
19458
+ if (this.devices.virtio_v86fs && state[90])
19459
+ this.devices.virtio_v86fs.set_state(state[90]);
18512
19460
  this.fw_value = state[62];
18513
19461
  if (state[63]) this.set_state_ioapic(state[63]);
18514
19462
  this.tss_size_32[0] = state[64];
@@ -18919,6 +19867,19 @@ var V86Starter = (() => {
18919
19867
  device_bus
18920
19868
  );
18921
19869
  }
19870
+ if (settings.virtio_mem) {
19871
+ const mem_cfg = settings.virtio_mem;
19872
+ this.devices.virtio_mem = new VirtioMem(
19873
+ this,
19874
+ device_bus,
19875
+ mem_cfg.region_addr,
19876
+ mem_cfg.region_size,
19877
+ mem_cfg.block_size
19878
+ );
19879
+ }
19880
+ if (settings.virtio_v86fs) {
19881
+ this.devices.virtio_v86fs = new VirtioV86FS(this, device_bus);
19882
+ }
18922
19883
  this.devices.sb16 = new SB16(this, device_bus);
18923
19884
  }
18924
19885
  if (settings.multiboot) {
@@ -19144,7 +20105,6 @@ var V86Starter = (() => {
19144
20105
  cpu.write32(multiboot_data + 4, ramdisk_top);
19145
20106
  cpu.write32(multiboot_data + 8, 0);
19146
20107
  cpu.write32(multiboot_data + 12, 0);
19147
- multiboot_data += 16;
19148
20108
  dbg_assert(ramdisk_top < cpu.memory_size[0]);
19149
20109
  cpu.write_blob(new Uint8Array(initrd), ramdisk_address);
19150
20110
  }
@@ -25218,20 +26178,12 @@ ${e.stack || e.message}`
25218
26178
  };
25219
26179
 
25220
26180
  // src/browser/starter.ts
25221
- var FileExistsError = class {
25222
- message;
25223
- constructor(message) {
25224
- this.message = message || "File already exists";
25225
- }
25226
- };
25227
- FileExistsError.prototype = Error.prototype;
25228
- var FileNotFoundError = class {
25229
- message;
26181
+ var import_meta = {};
26182
+ var FileNotFoundError = class extends Error {
25230
26183
  constructor(message) {
25231
- this.message = message || "File not found";
26184
+ super(message || "File not found");
25232
26185
  }
25233
26186
  };
25234
- FileNotFoundError.prototype = Error.prototype;
25235
26187
  var V86 = class {
25236
26188
  cpu_is_running = false;
25237
26189
  cpu_exception_hook = function(_n) {
@@ -25357,9 +26309,10 @@ ${e.stack || e.message}`
25357
26309
  "v86.wasm",
25358
26310
  "v86-fallback.wasm"
25359
26311
  );
25360
- } else if (typeof window === "undefined" && typeof __dirname === "string") {
25361
- v86_bin = __dirname + "/" + v86_bin;
25362
- v86_bin_fallback = __dirname + "/" + v86_bin_fallback;
26312
+ } else if (typeof window === "undefined") {
26313
+ const root = new URL("../../", import_meta.url).pathname;
26314
+ v86_bin = root + "build/" + v86_bin;
26315
+ v86_bin_fallback = root + "build/" + v86_bin_fallback;
25363
26316
  } else {
25364
26317
  v86_bin = "build/" + v86_bin;
25365
26318
  v86_bin_fallback = "build/" + v86_bin_fallback;
@@ -25402,7 +26355,8 @@ ${e.stack || e.message}`
25402
26355
  exports["rust_init"]();
25403
26356
  const emulator = this.v86 = new v86(this.emulator_bus, {
25404
26357
  exports,
25405
- wasm_table
26358
+ wasm_table,
26359
+ wasm_memory
25406
26360
  });
25407
26361
  cpu = emulator.cpu;
25408
26362
  this.continue_init(emulator, options);
@@ -25444,7 +26398,9 @@ ${e.stack || e.message}`
25444
26398
  settings.mac_address_translation = options.mac_address_translation;
25445
26399
  settings.cpuid_level = options.cpuid_level;
25446
26400
  settings.virtio_balloon = options.virtio_balloon;
26401
+ settings.virtio_mem = options.virtio_mem;
25447
26402
  settings.virtio_console = !!options.virtio_console;
26403
+ settings.virtio_v86fs = !!options.virtio_v86fs;
25448
26404
  const relay_url = options.network_relay_url || options.net_device && options.net_device.relay_url;
25449
26405
  if (relay_url) {
25450
26406
  if (relay_url === "fetch") {