@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.browser.js +1016 -60
- package/dist/v86.browser.js.map +4 -4
- package/dist/v86.js +1015 -60
- package/dist/v86.js.map +4 -4
- package/lib/9p.ts +4 -4
- package/lib/filesystem.ts +1 -1
- package/package.json +7 -7
- package/src/browser/starter.ts +11 -21
- package/src/const.ts +2 -0
- package/src/cpu.ts +48 -8
- package/src/pci.ts +8 -3
- package/src/ps2.ts +2 -2
- package/src/virtio.ts +10 -18
- package/src/virtio_mem.ts +349 -0
- package/src/virtio_v86fs.ts +873 -0
- package/src/rust/gen/analyzer.rs +0 -3807
- package/src/rust/gen/analyzer0f.rs +0 -3992
- package/src/rust/gen/interpreter.rs +0 -4447
- package/src/rust/gen/interpreter0f.rs +0 -5404
- package/src/rust/gen/jit.rs +0 -5080
- package/src/rust/gen/jit0f.rs +0 -5547
|
@@ -0,0 +1,873 @@
|
|
|
1
|
+
// VirtioV86FS: custom virtio device for v86fs guest filesystem
|
|
2
|
+
// PCI device ID 0x107F (virtio type 63, last slot in modern range)
|
|
3
|
+
// Three virtqueues: hipriq (metadata), requestq (data), notifyq (push invalidation)
|
|
4
|
+
|
|
5
|
+
import { LOG_PCI } from './const.js'
|
|
6
|
+
import { dbg_log } from './log.js'
|
|
7
|
+
import { VirtIO, VIRTIO_F_VERSION_1 } from './virtio.js'
|
|
8
|
+
import { BusConnector } from './bus.js'
|
|
9
|
+
|
|
10
|
+
interface VirtioV86FSCPU {
|
|
11
|
+
io: {
|
|
12
|
+
register_read(
|
|
13
|
+
port: number,
|
|
14
|
+
device: object,
|
|
15
|
+
r8?: ((port: number) => number) | undefined,
|
|
16
|
+
r16?: ((port: number) => number) | undefined,
|
|
17
|
+
r32?: ((port: number) => number) | undefined,
|
|
18
|
+
): void
|
|
19
|
+
register_write(
|
|
20
|
+
port: number,
|
|
21
|
+
device: object,
|
|
22
|
+
w8?: ((port: number) => void) | undefined,
|
|
23
|
+
w16?: ((port: number) => void) | undefined,
|
|
24
|
+
w32?: ((port: number) => void) | undefined,
|
|
25
|
+
): void
|
|
26
|
+
}
|
|
27
|
+
devices: {
|
|
28
|
+
pci: {
|
|
29
|
+
register_device(device: VirtIO): void
|
|
30
|
+
raise_irq(pci_id: number): void
|
|
31
|
+
lower_irq(pci_id: number): void
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
read16(addr: number): number
|
|
35
|
+
read32s(addr: number): number
|
|
36
|
+
write16(addr: number, value: number): void
|
|
37
|
+
write32(addr: number, value: number): void
|
|
38
|
+
read_blob(addr: number, length: number): Uint8Array
|
|
39
|
+
write_blob(blob: Uint8Array, addr: number): void
|
|
40
|
+
zero_memory(addr: number, length: number): void
|
|
41
|
+
memory_size: Int32Array
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Protocol message types
|
|
45
|
+
const V86FS_MSG_MOUNT = 0x00
|
|
46
|
+
const V86FS_MSG_LOOKUP = 0x01
|
|
47
|
+
const V86FS_MSG_GETATTR = 0x02
|
|
48
|
+
const V86FS_MSG_READDIR = 0x03
|
|
49
|
+
const V86FS_MSG_OPEN = 0x04
|
|
50
|
+
const V86FS_MSG_CLOSE = 0x05
|
|
51
|
+
const V86FS_MSG_READ = 0x06
|
|
52
|
+
const V86FS_MSG_CREATE = 0x07
|
|
53
|
+
const V86FS_MSG_WRITE = 0x08
|
|
54
|
+
const V86FS_MSG_MKDIR = 0x09
|
|
55
|
+
const V86FS_MSG_SETATTR = 0x0a
|
|
56
|
+
const V86FS_MSG_FSYNC = 0x0b
|
|
57
|
+
const V86FS_MSG_UNLINK = 0x0c
|
|
58
|
+
const V86FS_MSG_RENAME = 0x0d
|
|
59
|
+
const V86FS_MSG_SYMLINK = 0x0e
|
|
60
|
+
const V86FS_MSG_READLINK = 0x0f
|
|
61
|
+
const V86FS_MSG_STATFS = 0x10
|
|
62
|
+
const V86FS_MSG_INVALIDATE = 0x20
|
|
63
|
+
const V86FS_MSG_INVALIDATE_DIR = 0x21
|
|
64
|
+
|
|
65
|
+
// Response types
|
|
66
|
+
const V86FS_MSG_MOUNT_R = 0x80
|
|
67
|
+
const V86FS_MSG_LOOKUP_R = 0x81
|
|
68
|
+
const V86FS_MSG_GETATTR_R = 0x82
|
|
69
|
+
const V86FS_MSG_READDIR_R = 0x83
|
|
70
|
+
const V86FS_MSG_OPEN_R = 0x84
|
|
71
|
+
const V86FS_MSG_CLOSE_R = 0x85
|
|
72
|
+
const V86FS_MSG_READ_R = 0x86
|
|
73
|
+
const V86FS_MSG_CREATE_R = 0x87
|
|
74
|
+
const V86FS_MSG_WRITE_R = 0x88
|
|
75
|
+
const V86FS_MSG_MKDIR_R = 0x89
|
|
76
|
+
const V86FS_MSG_SETATTR_R = 0x8a
|
|
77
|
+
const V86FS_MSG_FSYNC_R = 0x8b
|
|
78
|
+
const V86FS_MSG_UNLINK_R = 0x8c
|
|
79
|
+
const V86FS_MSG_RENAME_R = 0x8d
|
|
80
|
+
const V86FS_MSG_SYMLINK_R = 0x8e
|
|
81
|
+
const V86FS_MSG_READLINK_R = 0x8f
|
|
82
|
+
const V86FS_MSG_STATFS_R = 0x90
|
|
83
|
+
const _V86FS_MSG_ERROR_R = 0xff
|
|
84
|
+
|
|
85
|
+
// ATTR_* valid mask bits (matching Linux)
|
|
86
|
+
const ATTR_MODE = 1
|
|
87
|
+
const ATTR_SIZE = 8
|
|
88
|
+
|
|
89
|
+
// Status codes
|
|
90
|
+
const V86FS_STATUS_OK = 0
|
|
91
|
+
const V86FS_STATUS_ENOENT = 2
|
|
92
|
+
|
|
93
|
+
// DT_* types (matching Linux dirent.h)
|
|
94
|
+
const DT_DIR = 4
|
|
95
|
+
const DT_REG = 8
|
|
96
|
+
const DT_LNK = 10
|
|
97
|
+
|
|
98
|
+
// S_IF* mode bits
|
|
99
|
+
const S_IFDIR = 0o040000
|
|
100
|
+
const S_IFREG = 0o100000
|
|
101
|
+
const S_IFLNK = 0o120000
|
|
102
|
+
|
|
103
|
+
const textDecoder = new TextDecoder()
|
|
104
|
+
const textEncoder = new TextEncoder()
|
|
105
|
+
|
|
106
|
+
interface FsEntry {
|
|
107
|
+
inode_id: number
|
|
108
|
+
name: string
|
|
109
|
+
mode: number
|
|
110
|
+
size: number
|
|
111
|
+
dt_type: number
|
|
112
|
+
mtime_sec: number
|
|
113
|
+
mtime_nsec: number
|
|
114
|
+
content?: Uint8Array
|
|
115
|
+
symlink_target?: string
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Hardcoded test filesystem: root dir (inode 1) with two entries
|
|
119
|
+
export const FS_ENTRIES: Map<number, FsEntry[]> = new Map([
|
|
120
|
+
[
|
|
121
|
+
1,
|
|
122
|
+
[
|
|
123
|
+
{
|
|
124
|
+
inode_id: 2,
|
|
125
|
+
name: 'hello.txt',
|
|
126
|
+
mode: S_IFREG | 0o644,
|
|
127
|
+
size: 12,
|
|
128
|
+
dt_type: DT_REG,
|
|
129
|
+
mtime_sec: 1711500000,
|
|
130
|
+
mtime_nsec: 0,
|
|
131
|
+
content: textEncoder.encode('hello world\n'),
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
inode_id: 3,
|
|
135
|
+
name: 'subdir',
|
|
136
|
+
mode: S_IFDIR | 0o755,
|
|
137
|
+
size: 0,
|
|
138
|
+
dt_type: DT_DIR,
|
|
139
|
+
mtime_sec: 1711500000,
|
|
140
|
+
mtime_nsec: 0,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
],
|
|
144
|
+
])
|
|
145
|
+
|
|
146
|
+
// Inode lookup map built from FS_ENTRIES (includes root dir)
|
|
147
|
+
export const INODE_MAP: Map<number, FsEntry> = new Map([
|
|
148
|
+
[
|
|
149
|
+
1,
|
|
150
|
+
{
|
|
151
|
+
inode_id: 1,
|
|
152
|
+
name: '',
|
|
153
|
+
mode: S_IFDIR | 0o755,
|
|
154
|
+
size: 0,
|
|
155
|
+
dt_type: DT_DIR,
|
|
156
|
+
mtime_sec: 1711500000,
|
|
157
|
+
mtime_nsec: 0,
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
])
|
|
161
|
+
for (const entries of FS_ENTRIES.values()) {
|
|
162
|
+
for (const e of entries) {
|
|
163
|
+
INODE_MAP.set(e.inode_id, e)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Header size: 4B length + 1B type + 2B tag
|
|
168
|
+
const V86FS_HDR_SIZE = 7
|
|
169
|
+
|
|
170
|
+
const _VIRTIO_V86FS_QUEUE_HIPRIQ = 0
|
|
171
|
+
const _VIRTIO_V86FS_QUEUE_REQUESTQ = 1
|
|
172
|
+
const _VIRTIO_V86FS_QUEUE_NOTIFYQ = 2
|
|
173
|
+
|
|
174
|
+
function packU32(buf: Uint8Array, offset: number, val: number): void {
|
|
175
|
+
buf[offset] = val & 0xff
|
|
176
|
+
buf[offset + 1] = (val >>> 8) & 0xff
|
|
177
|
+
buf[offset + 2] = (val >>> 16) & 0xff
|
|
178
|
+
buf[offset + 3] = (val >>> 24) & 0xff
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function packU64(buf: Uint8Array, offset: number, val: number): void {
|
|
182
|
+
packU32(buf, offset, val)
|
|
183
|
+
packU32(buf, offset + 4, 0)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function packU16(buf: Uint8Array, offset: number, val: number): void {
|
|
187
|
+
buf[offset] = val & 0xff
|
|
188
|
+
buf[offset + 1] = (val >>> 8) & 0xff
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function makeResp(size: number, type: number, tag: number): Uint8Array {
|
|
192
|
+
const resp = new Uint8Array(size)
|
|
193
|
+
packU32(resp, 0, size)
|
|
194
|
+
resp[4] = type
|
|
195
|
+
resp[5] = tag & 0xff
|
|
196
|
+
resp[6] = (tag >> 8) & 0xff
|
|
197
|
+
return resp
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function readU32(buf: Uint8Array, offset: number): number {
|
|
201
|
+
return (
|
|
202
|
+
buf[offset] |
|
|
203
|
+
(buf[offset + 1] << 8) |
|
|
204
|
+
(buf[offset + 2] << 16) |
|
|
205
|
+
((buf[offset + 3] << 24) >>> 0)
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function readU16(buf: Uint8Array, offset: number): number {
|
|
210
|
+
return buf[offset] | (buf[offset + 1] << 8)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function readU64(buf: Uint8Array, offset: number): number {
|
|
214
|
+
return (
|
|
215
|
+
buf[offset] |
|
|
216
|
+
(buf[offset + 1] << 8) |
|
|
217
|
+
(buf[offset + 2] << 16) |
|
|
218
|
+
(((buf[offset + 3] << 24) >>> 0) +
|
|
219
|
+
(buf[offset + 4] |
|
|
220
|
+
(buf[offset + 5] << 8) |
|
|
221
|
+
(buf[offset + 6] << 16) |
|
|
222
|
+
((buf[offset + 7] << 24) >>> 0)) *
|
|
223
|
+
0x100000000)
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export class VirtioV86FS {
|
|
228
|
+
bus: BusConnector
|
|
229
|
+
virtio: VirtIO
|
|
230
|
+
next_handle_id: number
|
|
231
|
+
next_inode_id: number
|
|
232
|
+
open_handles: Map<number, number> // handle_id -> inode_id
|
|
233
|
+
read_count: number
|
|
234
|
+
|
|
235
|
+
constructor(cpu: VirtioV86FSCPU, bus: BusConnector) {
|
|
236
|
+
this.bus = bus
|
|
237
|
+
this.next_handle_id = 1
|
|
238
|
+
this.next_inode_id = 100
|
|
239
|
+
this.open_handles = new Map()
|
|
240
|
+
this.read_count = 0
|
|
241
|
+
|
|
242
|
+
const queues = [
|
|
243
|
+
// Queue 0: hipriq - high-priority metadata (LOOKUP, GETATTR)
|
|
244
|
+
{ size_supported: 128, notify_offset: 0 },
|
|
245
|
+
// Queue 1: requestq - data requests (READ, WRITE, READDIR)
|
|
246
|
+
{ size_supported: 128, notify_offset: 1 },
|
|
247
|
+
// Queue 2: notifyq - host-to-guest push invalidation
|
|
248
|
+
{ size_supported: 128, notify_offset: 2 },
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
this.virtio = new VirtIO(cpu, {
|
|
252
|
+
name: 'virtio-v86fs',
|
|
253
|
+
pci_id: 0x0e << 3,
|
|
254
|
+
device_id: 0x107f,
|
|
255
|
+
subsystem_device_id: 63,
|
|
256
|
+
common: {
|
|
257
|
+
initial_port: 0xf800,
|
|
258
|
+
queues: queues,
|
|
259
|
+
features: [VIRTIO_F_VERSION_1],
|
|
260
|
+
on_driver_ok: () => {
|
|
261
|
+
console.log('v86fs: driver ok')
|
|
262
|
+
this.bus.send('virtio-v86fs-driver-ok')
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
notification: {
|
|
266
|
+
initial_port: 0xf900,
|
|
267
|
+
single_handler: false,
|
|
268
|
+
handlers: [
|
|
269
|
+
// Queue 0: hipriq
|
|
270
|
+
(queue_id: number) => {
|
|
271
|
+
this.handle_queue(queue_id)
|
|
272
|
+
},
|
|
273
|
+
// Queue 1: requestq
|
|
274
|
+
(queue_id: number) => {
|
|
275
|
+
this.handle_queue(queue_id)
|
|
276
|
+
},
|
|
277
|
+
// Queue 2: notifyq
|
|
278
|
+
(_queue_id: number) => {
|
|
279
|
+
dbg_log('v86fs: notifyq notification', LOG_PCI)
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
},
|
|
283
|
+
isr_status: {
|
|
284
|
+
initial_port: 0xf700,
|
|
285
|
+
},
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
handle_queue(queue_id: number): void {
|
|
290
|
+
const queue = this.virtio.queues[queue_id]
|
|
291
|
+
while (queue.has_request()) {
|
|
292
|
+
const bufchain = queue.pop_request()
|
|
293
|
+
const req = new Uint8Array(bufchain.length_readable)
|
|
294
|
+
bufchain.get_next_blob(req)
|
|
295
|
+
|
|
296
|
+
const resp = this.handle_message(req)
|
|
297
|
+
if (resp && bufchain.length_writable > 0) {
|
|
298
|
+
bufchain.set_next_blob(resp)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
queue.push_reply(bufchain)
|
|
302
|
+
}
|
|
303
|
+
queue.flush_replies()
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
handle_message(req: Uint8Array): Uint8Array | null {
|
|
307
|
+
if (req.length < V86FS_HDR_SIZE) {
|
|
308
|
+
console.warn('v86fs: message too short:', req.length)
|
|
309
|
+
return null
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const type = req[4]
|
|
313
|
+
const tag = readU16(req, 5)
|
|
314
|
+
|
|
315
|
+
switch (type) {
|
|
316
|
+
case V86FS_MSG_MOUNT:
|
|
317
|
+
return this.handle_mount(req, tag)
|
|
318
|
+
case V86FS_MSG_LOOKUP:
|
|
319
|
+
return this.handle_lookup(req, tag)
|
|
320
|
+
case V86FS_MSG_GETATTR:
|
|
321
|
+
return this.handle_getattr(req, tag)
|
|
322
|
+
case V86FS_MSG_READDIR:
|
|
323
|
+
return this.handle_readdir(req, tag)
|
|
324
|
+
case V86FS_MSG_OPEN:
|
|
325
|
+
return this.handle_open(req, tag)
|
|
326
|
+
case V86FS_MSG_CLOSE:
|
|
327
|
+
return this.handle_close(req, tag)
|
|
328
|
+
case V86FS_MSG_READ:
|
|
329
|
+
return this.handle_read(req, tag)
|
|
330
|
+
case V86FS_MSG_CREATE:
|
|
331
|
+
return this.handle_create(req, tag)
|
|
332
|
+
case V86FS_MSG_WRITE:
|
|
333
|
+
return this.handle_write(req, tag)
|
|
334
|
+
case V86FS_MSG_MKDIR:
|
|
335
|
+
return this.handle_mkdir(req, tag)
|
|
336
|
+
case V86FS_MSG_SETATTR:
|
|
337
|
+
return this.handle_setattr(req, tag)
|
|
338
|
+
case V86FS_MSG_FSYNC:
|
|
339
|
+
return this.handle_fsync(req, tag)
|
|
340
|
+
case V86FS_MSG_UNLINK:
|
|
341
|
+
return this.handle_unlink(req, tag)
|
|
342
|
+
case V86FS_MSG_RENAME:
|
|
343
|
+
return this.handle_rename(req, tag)
|
|
344
|
+
case V86FS_MSG_SYMLINK:
|
|
345
|
+
return this.handle_symlink(req, tag)
|
|
346
|
+
case V86FS_MSG_READLINK:
|
|
347
|
+
return this.handle_readlink(req, tag)
|
|
348
|
+
case V86FS_MSG_STATFS:
|
|
349
|
+
return this.handle_statfs(tag)
|
|
350
|
+
default:
|
|
351
|
+
console.warn('v86fs: unknown message type:', type)
|
|
352
|
+
return null
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
handle_mount(req: Uint8Array, tag: number): Uint8Array {
|
|
357
|
+
// Parse MOUNT: [7B hdr] [2B name_len] [name...]
|
|
358
|
+
const name_len = readU16(req, 7)
|
|
359
|
+
const name =
|
|
360
|
+
name_len > 0
|
|
361
|
+
? textDecoder.decode(req.subarray(9, 9 + name_len))
|
|
362
|
+
: ''
|
|
363
|
+
|
|
364
|
+
console.log('v86fs: mount:', name || '(default)')
|
|
365
|
+
|
|
366
|
+
// Emit bus event so host adapter can resolve the name
|
|
367
|
+
this.bus.send('virtio-v86fs-mount', name)
|
|
368
|
+
|
|
369
|
+
// Build MOUNT_R: [7B hdr] [4B status=0] [8B root_id=1] [4B mode=0x41ED]
|
|
370
|
+
// 0x41ED = S_IFDIR | 0755
|
|
371
|
+
const resp = new Uint8Array(23)
|
|
372
|
+
packU32(resp, 0, 23) // length
|
|
373
|
+
resp[4] = V86FS_MSG_MOUNT_R // type
|
|
374
|
+
resp[5] = tag & 0xff // tag low
|
|
375
|
+
resp[6] = (tag >> 8) & 0xff // tag high
|
|
376
|
+
packU32(resp, 7, 0) // status = 0 (success)
|
|
377
|
+
packU64(resp, 11, 1) // root_inode_id = 1
|
|
378
|
+
packU32(resp, 19, 0x41ed) // mode = S_IFDIR | 0755
|
|
379
|
+
|
|
380
|
+
return resp
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
handle_getattr(req: Uint8Array, tag: number): Uint8Array {
|
|
384
|
+
// Parse GETATTR: [7B hdr] [8B inode_id]
|
|
385
|
+
const inode_id = readU64(req, 7)
|
|
386
|
+
const entry = INODE_MAP.get(inode_id)
|
|
387
|
+
|
|
388
|
+
// GETATTR_R: [7B hdr] [4B status] [4B mode] [8B size] [8B mtime_sec] [4B mtime_nsec]
|
|
389
|
+
const resp = makeResp(35, V86FS_MSG_GETATTR_R, tag)
|
|
390
|
+
|
|
391
|
+
if (!entry) {
|
|
392
|
+
packU32(resp, 7, V86FS_STATUS_ENOENT)
|
|
393
|
+
return resp
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
packU32(resp, 7, V86FS_STATUS_OK)
|
|
397
|
+
packU32(resp, 11, entry.mode)
|
|
398
|
+
packU64(resp, 15, entry.size)
|
|
399
|
+
packU64(resp, 23, entry.mtime_sec)
|
|
400
|
+
packU32(resp, 31, entry.mtime_nsec)
|
|
401
|
+
return resp
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
handle_lookup(req: Uint8Array, tag: number): Uint8Array {
|
|
405
|
+
// Parse LOOKUP: [7B hdr] [8B parent_id] [2B name_len] [name...]
|
|
406
|
+
const parent_id = readU64(req, 7)
|
|
407
|
+
const name_len = readU16(req, 15)
|
|
408
|
+
const name = textDecoder.decode(req.subarray(17, 17 + name_len))
|
|
409
|
+
|
|
410
|
+
const entries = FS_ENTRIES.get(parent_id)
|
|
411
|
+
const entry = entries?.find((e) => e.name === name)
|
|
412
|
+
|
|
413
|
+
// LOOKUP_R: [7B hdr] [4B status] [8B inode_id] [4B mode] [8B size]
|
|
414
|
+
const resp = new Uint8Array(31)
|
|
415
|
+
packU32(resp, 0, 31) // length
|
|
416
|
+
resp[4] = V86FS_MSG_LOOKUP_R
|
|
417
|
+
resp[5] = tag & 0xff
|
|
418
|
+
resp[6] = (tag >> 8) & 0xff
|
|
419
|
+
|
|
420
|
+
if (!entry) {
|
|
421
|
+
packU32(resp, 7, V86FS_STATUS_ENOENT)
|
|
422
|
+
return resp
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
packU32(resp, 7, V86FS_STATUS_OK)
|
|
426
|
+
packU64(resp, 11, entry.inode_id)
|
|
427
|
+
packU32(resp, 19, entry.mode)
|
|
428
|
+
packU64(resp, 23, entry.size)
|
|
429
|
+
return resp
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
handle_readdir(req: Uint8Array, tag: number): Uint8Array {
|
|
433
|
+
// Parse READDIR: [7B hdr] [8B dir_id]
|
|
434
|
+
const dir_id = readU64(req, 7)
|
|
435
|
+
const entries = FS_ENTRIES.get(dir_id) || []
|
|
436
|
+
|
|
437
|
+
// Pre-encode names to avoid double encoding
|
|
438
|
+
const encodedNames = entries.map((e) => textEncoder.encode(e.name))
|
|
439
|
+
|
|
440
|
+
// READDIR_R: [7B hdr] [4B status] [4B count] [entries...]
|
|
441
|
+
// Each entry: [8B inode_id] [1B type] [2B name_len] [name...]
|
|
442
|
+
let size = 7 + 4 + 4
|
|
443
|
+
for (const nameBytes of encodedNames) {
|
|
444
|
+
size += 8 + 1 + 2 + nameBytes.length
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const resp = makeResp(size, V86FS_MSG_READDIR_R, tag)
|
|
448
|
+
packU32(resp, 7, V86FS_STATUS_OK)
|
|
449
|
+
packU32(resp, 11, entries.length)
|
|
450
|
+
|
|
451
|
+
let off = 15
|
|
452
|
+
for (let i = 0; i < entries.length; i++) {
|
|
453
|
+
const e = entries[i]
|
|
454
|
+
const nameBytes = encodedNames[i]
|
|
455
|
+
packU64(resp, off, e.inode_id)
|
|
456
|
+
resp[off + 8] = e.dt_type
|
|
457
|
+
packU16(resp, off + 9, nameBytes.length)
|
|
458
|
+
resp.set(nameBytes, off + 11)
|
|
459
|
+
off += 11 + nameBytes.length
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return resp
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
handle_open(req: Uint8Array, tag: number): Uint8Array {
|
|
466
|
+
// Parse OPEN: [7B hdr] [8B inode_id] [4B flags]
|
|
467
|
+
const inode_id = readU64(req, 7)
|
|
468
|
+
const handle_id = this.next_handle_id++
|
|
469
|
+
this.open_handles.set(handle_id, inode_id)
|
|
470
|
+
|
|
471
|
+
this.bus.send('virtio-v86fs-open', inode_id)
|
|
472
|
+
|
|
473
|
+
// OPEN_R: [7B hdr] [4B status] [8B handle_id]
|
|
474
|
+
const resp = makeResp(19, V86FS_MSG_OPEN_R, tag)
|
|
475
|
+
packU32(resp, 7, V86FS_STATUS_OK)
|
|
476
|
+
packU64(resp, 11, handle_id)
|
|
477
|
+
return resp
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
handle_close(req: Uint8Array, tag: number): Uint8Array {
|
|
481
|
+
// Parse CLOSE: [7B hdr] [8B handle_id]
|
|
482
|
+
const handle_id = readU64(req, 7)
|
|
483
|
+
this.open_handles.delete(handle_id)
|
|
484
|
+
|
|
485
|
+
this.bus.send('virtio-v86fs-close', handle_id)
|
|
486
|
+
|
|
487
|
+
// CLOSE_R: [7B hdr] [4B status]
|
|
488
|
+
const resp = makeResp(11, V86FS_MSG_CLOSE_R, tag)
|
|
489
|
+
packU32(resp, 7, V86FS_STATUS_OK)
|
|
490
|
+
return resp
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
handle_read(req: Uint8Array, tag: number): Uint8Array {
|
|
494
|
+
// Parse READ: [7B hdr] [8B handle_id] [8B offset] [4B size]
|
|
495
|
+
const handle_id = readU64(req, 7)
|
|
496
|
+
const offset = readU64(req, 15)
|
|
497
|
+
const size = readU32(req, 23)
|
|
498
|
+
|
|
499
|
+
const inode_id = this.open_handles.get(handle_id) ?? handle_id
|
|
500
|
+
const entry = INODE_MAP.get(inode_id)
|
|
501
|
+
const content = entry?.content
|
|
502
|
+
|
|
503
|
+
this.read_count++
|
|
504
|
+
this.bus.send('virtio-v86fs-read', {
|
|
505
|
+
handle_id,
|
|
506
|
+
inode_id,
|
|
507
|
+
offset,
|
|
508
|
+
size,
|
|
509
|
+
})
|
|
510
|
+
|
|
511
|
+
if (!content || offset >= content.length) {
|
|
512
|
+
// EOF or no content
|
|
513
|
+
const resp = makeResp(15, V86FS_MSG_READ_R, tag)
|
|
514
|
+
packU32(resp, 7, V86FS_STATUS_OK)
|
|
515
|
+
packU32(resp, 11, 0) // 0 bytes read
|
|
516
|
+
return resp
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const start = Math.min(offset, content.length)
|
|
520
|
+
const end = Math.min(start + size, content.length)
|
|
521
|
+
const data = content.subarray(start, end)
|
|
522
|
+
|
|
523
|
+
// READ_R: [7B hdr] [4B status] [4B bytes_read] [data...]
|
|
524
|
+
const resp = new Uint8Array(15 + data.length)
|
|
525
|
+
packU32(resp, 0, 15 + data.length)
|
|
526
|
+
resp[4] = V86FS_MSG_READ_R
|
|
527
|
+
resp[5] = tag & 0xff
|
|
528
|
+
resp[6] = (tag >> 8) & 0xff
|
|
529
|
+
packU32(resp, 7, V86FS_STATUS_OK)
|
|
530
|
+
packU32(resp, 11, data.length)
|
|
531
|
+
resp.set(data, 15)
|
|
532
|
+
return resp
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
handle_create(req: Uint8Array, tag: number): Uint8Array {
|
|
536
|
+
// Parse CREATE: [7B hdr] [8B parent_id] [2B name_len] [name...] [4B mode]
|
|
537
|
+
const parent_id = readU64(req, 7)
|
|
538
|
+
const name_len = readU16(req, 15)
|
|
539
|
+
const name = textDecoder.decode(req.subarray(17, 17 + name_len))
|
|
540
|
+
const mode = readU32(req, 17 + name_len)
|
|
541
|
+
|
|
542
|
+
const inode_id = this.next_inode_id++
|
|
543
|
+
const entry: FsEntry = {
|
|
544
|
+
inode_id,
|
|
545
|
+
name,
|
|
546
|
+
mode: mode | S_IFREG,
|
|
547
|
+
size: 0,
|
|
548
|
+
dt_type: DT_REG,
|
|
549
|
+
mtime_sec: Math.floor(Date.now() / 1000),
|
|
550
|
+
mtime_nsec: 0,
|
|
551
|
+
content: new Uint8Array(0),
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Add to parent's entry list
|
|
555
|
+
let children = FS_ENTRIES.get(parent_id)
|
|
556
|
+
if (!children) {
|
|
557
|
+
children = []
|
|
558
|
+
FS_ENTRIES.set(parent_id, children)
|
|
559
|
+
}
|
|
560
|
+
children.push(entry)
|
|
561
|
+
INODE_MAP.set(inode_id, entry)
|
|
562
|
+
|
|
563
|
+
// CREATE_R: [7B hdr] [4B status] [8B inode_id] [4B mode]
|
|
564
|
+
const resp = makeResp(23, V86FS_MSG_CREATE_R, tag)
|
|
565
|
+
packU32(resp, 7, V86FS_STATUS_OK)
|
|
566
|
+
packU64(resp, 11, inode_id)
|
|
567
|
+
packU32(resp, 19, entry.mode)
|
|
568
|
+
return resp
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
handle_write(req: Uint8Array, tag: number): Uint8Array {
|
|
572
|
+
// Parse WRITE: [7B hdr] [8B inode_id] [8B offset] [4B size] [data...]
|
|
573
|
+
const inode_id = readU64(req, 7)
|
|
574
|
+
const offset = readU64(req, 15)
|
|
575
|
+
const size = readU32(req, 23)
|
|
576
|
+
const data = req.subarray(27, 27 + size)
|
|
577
|
+
|
|
578
|
+
const entry = INODE_MAP.get(inode_id)
|
|
579
|
+
if (entry) {
|
|
580
|
+
// Grow content buffer if needed
|
|
581
|
+
const needed = offset + size
|
|
582
|
+
if (!entry.content || entry.content.length < needed) {
|
|
583
|
+
const newContent = new Uint8Array(needed)
|
|
584
|
+
if (entry.content) {
|
|
585
|
+
newContent.set(entry.content)
|
|
586
|
+
}
|
|
587
|
+
entry.content = newContent
|
|
588
|
+
}
|
|
589
|
+
entry.content.set(data, offset)
|
|
590
|
+
if (needed > entry.size) {
|
|
591
|
+
entry.size = needed
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// WRITE_R: [7B hdr] [4B status] [4B bytes_written]
|
|
596
|
+
const resp = makeResp(15, V86FS_MSG_WRITE_R, tag)
|
|
597
|
+
packU32(resp, 7, V86FS_STATUS_OK)
|
|
598
|
+
packU32(resp, 11, size)
|
|
599
|
+
return resp
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
handle_mkdir(req: Uint8Array, tag: number): Uint8Array {
|
|
603
|
+
// Parse MKDIR: [7B hdr] [8B parent_id] [2B name_len] [name...] [4B mode]
|
|
604
|
+
const parent_id = readU64(req, 7)
|
|
605
|
+
const name_len = readU16(req, 15)
|
|
606
|
+
const name = textDecoder.decode(req.subarray(17, 17 + name_len))
|
|
607
|
+
const mode = readU32(req, 17 + name_len)
|
|
608
|
+
|
|
609
|
+
const inode_id = this.next_inode_id++
|
|
610
|
+
const entry: FsEntry = {
|
|
611
|
+
inode_id,
|
|
612
|
+
name,
|
|
613
|
+
mode: mode | S_IFDIR,
|
|
614
|
+
size: 0,
|
|
615
|
+
dt_type: DT_DIR,
|
|
616
|
+
mtime_sec: Math.floor(Date.now() / 1000),
|
|
617
|
+
mtime_nsec: 0,
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
let children = FS_ENTRIES.get(parent_id)
|
|
621
|
+
if (!children) {
|
|
622
|
+
children = []
|
|
623
|
+
FS_ENTRIES.set(parent_id, children)
|
|
624
|
+
}
|
|
625
|
+
children.push(entry)
|
|
626
|
+
INODE_MAP.set(inode_id, entry)
|
|
627
|
+
FS_ENTRIES.set(inode_id, []) // empty dir
|
|
628
|
+
|
|
629
|
+
// MKDIR_R: [7B hdr] [4B status] [8B inode_id] [4B mode]
|
|
630
|
+
const resp = makeResp(23, V86FS_MSG_MKDIR_R, tag)
|
|
631
|
+
packU32(resp, 7, V86FS_STATUS_OK)
|
|
632
|
+
packU64(resp, 11, inode_id)
|
|
633
|
+
packU32(resp, 19, entry.mode)
|
|
634
|
+
return resp
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
handle_setattr(req: Uint8Array, tag: number): Uint8Array {
|
|
638
|
+
// Parse SETATTR: [7B hdr] [8B inode_id] [4B valid] [4B mode] [8B size]
|
|
639
|
+
const inode_id = readU64(req, 7)
|
|
640
|
+
const valid = readU32(req, 15)
|
|
641
|
+
const mode = readU32(req, 19)
|
|
642
|
+
const size = readU64(req, 23)
|
|
643
|
+
|
|
644
|
+
const entry = INODE_MAP.get(inode_id)
|
|
645
|
+
if (entry) {
|
|
646
|
+
if (valid & ATTR_MODE) {
|
|
647
|
+
entry.mode = (entry.mode & 0o170000) | (mode & 0o7777)
|
|
648
|
+
}
|
|
649
|
+
if (valid & ATTR_SIZE) {
|
|
650
|
+
entry.size = size
|
|
651
|
+
if (entry.content) {
|
|
652
|
+
if (size === 0) {
|
|
653
|
+
entry.content = new Uint8Array(0)
|
|
654
|
+
} else if (size < entry.content.length) {
|
|
655
|
+
entry.content = entry.content.subarray(0, size)
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// SETATTR_R: [7B hdr] [4B status]
|
|
662
|
+
const resp = makeResp(11, V86FS_MSG_SETATTR_R, tag)
|
|
663
|
+
packU32(resp, 7, V86FS_STATUS_OK)
|
|
664
|
+
return resp
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
handle_fsync(req: Uint8Array, tag: number): Uint8Array {
|
|
668
|
+
// FSYNC is a no-op for the in-memory test FS
|
|
669
|
+
const resp = makeResp(11, V86FS_MSG_FSYNC_R, tag)
|
|
670
|
+
packU32(resp, 7, V86FS_STATUS_OK)
|
|
671
|
+
return resp
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
handle_unlink(req: Uint8Array, tag: number): Uint8Array {
|
|
675
|
+
// Parse UNLINK: [7B hdr] [8B parent_id] [2B name_len] [name...]
|
|
676
|
+
const parent_id = readU64(req, 7)
|
|
677
|
+
const name_len = readU16(req, 15)
|
|
678
|
+
const name = textDecoder.decode(req.subarray(17, 17 + name_len))
|
|
679
|
+
|
|
680
|
+
const children = FS_ENTRIES.get(parent_id)
|
|
681
|
+
let status = V86FS_STATUS_ENOENT
|
|
682
|
+
if (children) {
|
|
683
|
+
const idx = children.findIndex((e) => e.name === name)
|
|
684
|
+
if (idx >= 0) {
|
|
685
|
+
const entry = children[idx]
|
|
686
|
+
INODE_MAP.delete(entry.inode_id)
|
|
687
|
+
FS_ENTRIES.delete(entry.inode_id)
|
|
688
|
+
children.splice(idx, 1)
|
|
689
|
+
status = V86FS_STATUS_OK
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const resp = makeResp(11, V86FS_MSG_UNLINK_R, tag)
|
|
694
|
+
packU32(resp, 7, status)
|
|
695
|
+
return resp
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
handle_rename(req: Uint8Array, tag: number): Uint8Array {
|
|
699
|
+
// Parse RENAME: [7B hdr] [8B old_parent_id] [2B old_name_len] [old_name...]
|
|
700
|
+
// [8B new_parent_id] [2B new_name_len] [new_name...]
|
|
701
|
+
let off = 7
|
|
702
|
+
const old_parent_id = readU64(req, off)
|
|
703
|
+
off += 8
|
|
704
|
+
const old_name_len = readU16(req, off)
|
|
705
|
+
off += 2
|
|
706
|
+
const old_name = textDecoder.decode(
|
|
707
|
+
req.subarray(off, off + old_name_len),
|
|
708
|
+
)
|
|
709
|
+
off += old_name_len
|
|
710
|
+
const new_parent_id = readU64(req, off)
|
|
711
|
+
off += 8
|
|
712
|
+
const new_name_len = readU16(req, off)
|
|
713
|
+
off += 2
|
|
714
|
+
const new_name = textDecoder.decode(
|
|
715
|
+
req.subarray(off, off + new_name_len),
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
let status = V86FS_STATUS_ENOENT
|
|
719
|
+
const old_children = FS_ENTRIES.get(old_parent_id)
|
|
720
|
+
if (old_children) {
|
|
721
|
+
const idx = old_children.findIndex((e) => e.name === old_name)
|
|
722
|
+
if (idx >= 0) {
|
|
723
|
+
const entry = old_children[idx]
|
|
724
|
+
old_children.splice(idx, 1)
|
|
725
|
+
entry.name = new_name
|
|
726
|
+
let new_children = FS_ENTRIES.get(new_parent_id)
|
|
727
|
+
if (!new_children) {
|
|
728
|
+
new_children = []
|
|
729
|
+
FS_ENTRIES.set(new_parent_id, new_children)
|
|
730
|
+
}
|
|
731
|
+
// Remove any existing entry with new_name in target dir
|
|
732
|
+
const existing = new_children.findIndex(
|
|
733
|
+
(e) => e.name === new_name,
|
|
734
|
+
)
|
|
735
|
+
if (existing >= 0) {
|
|
736
|
+
const old_entry = new_children[existing]
|
|
737
|
+
INODE_MAP.delete(old_entry.inode_id)
|
|
738
|
+
new_children.splice(existing, 1)
|
|
739
|
+
}
|
|
740
|
+
new_children.push(entry)
|
|
741
|
+
status = V86FS_STATUS_OK
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const resp = makeResp(11, V86FS_MSG_RENAME_R, tag)
|
|
746
|
+
packU32(resp, 7, status)
|
|
747
|
+
return resp
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
handle_symlink(req: Uint8Array, tag: number): Uint8Array {
|
|
751
|
+
// Parse SYMLINK: [7B hdr] [8B parent_id] [2B name_len] [name...]
|
|
752
|
+
// [2B target_len] [target...]
|
|
753
|
+
let off = 7
|
|
754
|
+
const parent_id = readU64(req, off)
|
|
755
|
+
off += 8
|
|
756
|
+
const name_len = readU16(req, off)
|
|
757
|
+
off += 2
|
|
758
|
+
const name = textDecoder.decode(req.subarray(off, off + name_len))
|
|
759
|
+
off += name_len
|
|
760
|
+
const target_len = readU16(req, off)
|
|
761
|
+
off += 2
|
|
762
|
+
const target = textDecoder.decode(req.subarray(off, off + target_len))
|
|
763
|
+
|
|
764
|
+
const inode_id = this.next_inode_id++
|
|
765
|
+
const entry: FsEntry = {
|
|
766
|
+
inode_id,
|
|
767
|
+
name,
|
|
768
|
+
mode: S_IFLNK | 0o777,
|
|
769
|
+
size: target.length,
|
|
770
|
+
dt_type: DT_LNK,
|
|
771
|
+
mtime_sec: Math.floor(Date.now() / 1000),
|
|
772
|
+
mtime_nsec: 0,
|
|
773
|
+
symlink_target: target,
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
let children = FS_ENTRIES.get(parent_id)
|
|
777
|
+
if (!children) {
|
|
778
|
+
children = []
|
|
779
|
+
FS_ENTRIES.set(parent_id, children)
|
|
780
|
+
}
|
|
781
|
+
children.push(entry)
|
|
782
|
+
INODE_MAP.set(inode_id, entry)
|
|
783
|
+
|
|
784
|
+
// SYMLINK_R: [7B hdr] [4B status] [8B inode_id] [4B mode]
|
|
785
|
+
const resp = makeResp(23, V86FS_MSG_SYMLINK_R, tag)
|
|
786
|
+
packU32(resp, 7, V86FS_STATUS_OK)
|
|
787
|
+
packU64(resp, 11, inode_id)
|
|
788
|
+
packU32(resp, 19, entry.mode)
|
|
789
|
+
return resp
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
handle_readlink(req: Uint8Array, tag: number): Uint8Array {
|
|
793
|
+
// Parse READLINK: [7B hdr] [8B inode_id]
|
|
794
|
+
const inode_id = readU64(req, 7)
|
|
795
|
+
const entry = INODE_MAP.get(inode_id)
|
|
796
|
+
|
|
797
|
+
if (!entry || !entry.symlink_target) {
|
|
798
|
+
const resp = makeResp(11, V86FS_MSG_READLINK_R, tag)
|
|
799
|
+
packU32(resp, 7, V86FS_STATUS_ENOENT)
|
|
800
|
+
return resp
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
const target_bytes = textEncoder.encode(entry.symlink_target)
|
|
804
|
+
const resp_len = 11 + 2 + target_bytes.length
|
|
805
|
+
const resp = makeResp(resp_len, V86FS_MSG_READLINK_R, tag)
|
|
806
|
+
packU32(resp, 7, V86FS_STATUS_OK)
|
|
807
|
+
packU16(resp, 11, target_bytes.length)
|
|
808
|
+
resp.set(target_bytes, 13)
|
|
809
|
+
return resp
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
handle_statfs(tag: number): Uint8Array {
|
|
813
|
+
// STATFS_R: [7B hdr] [4B status] [8B blocks] [8B bfree] [8B bavail]
|
|
814
|
+
// [8B files] [8B ffree] [4B bsize] [4B namelen]
|
|
815
|
+
const resp = makeResp(55, V86FS_MSG_STATFS_R, tag)
|
|
816
|
+
packU32(resp, 7, V86FS_STATUS_OK)
|
|
817
|
+
packU64(resp, 11, 1024 * 1024) // blocks
|
|
818
|
+
packU64(resp, 19, 512 * 1024) // bfree
|
|
819
|
+
packU64(resp, 27, 512 * 1024) // bavail
|
|
820
|
+
packU64(resp, 35, 1024 * 1024) // files
|
|
821
|
+
packU64(resp, 43, 512 * 1024) // ffree
|
|
822
|
+
packU32(resp, 51, 4096) // bsize
|
|
823
|
+
return resp
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/** Push an INVALIDATE notification to the guest via notifyq.
|
|
827
|
+
* Guest kernel will invalidate page cache for the given inode. */
|
|
828
|
+
invalidate_inode(inode_id: number): boolean {
|
|
829
|
+
const queue = this.virtio.queues[2] // notifyq
|
|
830
|
+
if (!queue.has_request()) return false
|
|
831
|
+
|
|
832
|
+
const bufchain = queue.pop_request()
|
|
833
|
+
// INVALIDATE: [7B hdr] [8B inode_id]
|
|
834
|
+
const msg = new Uint8Array(15)
|
|
835
|
+
packU32(msg, 0, 15)
|
|
836
|
+
msg[4] = V86FS_MSG_INVALIDATE
|
|
837
|
+
packU16(msg, 5, 0)
|
|
838
|
+
packU64(msg, 7, inode_id)
|
|
839
|
+
bufchain.set_next_blob(msg)
|
|
840
|
+
queue.push_reply(bufchain)
|
|
841
|
+
queue.flush_replies()
|
|
842
|
+
return true
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/** Push an INVALIDATE_DIR notification to the guest via notifyq.
|
|
846
|
+
* Guest kernel will invalidate dcache for the given directory inode. */
|
|
847
|
+
invalidate_dir(inode_id: number): boolean {
|
|
848
|
+
const queue = this.virtio.queues[2] // notifyq
|
|
849
|
+
if (!queue.has_request()) return false
|
|
850
|
+
|
|
851
|
+
const bufchain = queue.pop_request()
|
|
852
|
+
// INVALIDATE_DIR: [7B hdr] [8B inode_id]
|
|
853
|
+
const msg = new Uint8Array(15)
|
|
854
|
+
packU32(msg, 0, 15)
|
|
855
|
+
msg[4] = V86FS_MSG_INVALIDATE_DIR
|
|
856
|
+
packU16(msg, 5, 0)
|
|
857
|
+
packU64(msg, 7, inode_id)
|
|
858
|
+
bufchain.set_next_blob(msg)
|
|
859
|
+
queue.push_reply(bufchain)
|
|
860
|
+
queue.flush_replies()
|
|
861
|
+
return true
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
get_state(): any[] {
|
|
865
|
+
const state: any[] = []
|
|
866
|
+
state[0] = this.virtio
|
|
867
|
+
return state
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
set_state(state: any[]): void {
|
|
871
|
+
this.virtio.set_state(state[0])
|
|
872
|
+
}
|
|
873
|
+
}
|