@aptre/v86 0.5.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/LICENSE +22 -0
- package/LICENSE.MIT +22 -0
- package/Readme.md +237 -0
- package/dist/v86.browser.js +26666 -0
- package/dist/v86.browser.js.map +7 -0
- package/dist/v86.js +26632 -0
- package/dist/v86.js.map +7 -0
- package/gen/generate_analyzer.ts +512 -0
- package/gen/generate_interpreter.ts +522 -0
- package/gen/generate_jit.ts +624 -0
- package/gen/rust_ast.ts +107 -0
- package/gen/util.ts +35 -0
- package/gen/x86_table.ts +1836 -0
- package/lib/9p.ts +1547 -0
- package/lib/filesystem.ts +1879 -0
- package/lib/marshall.ts +168 -0
- package/lib/softfloat/softfloat.c +32501 -0
- package/lib/zstd/zstddeclib.c +13520 -0
- package/package.json +75 -0
- package/src/acpi.ts +267 -0
- package/src/browser/dummy_screen.ts +106 -0
- package/src/browser/fake_network.ts +1771 -0
- package/src/browser/fetch_network.ts +361 -0
- package/src/browser/filestorage.ts +124 -0
- package/src/browser/inbrowser_network.ts +57 -0
- package/src/browser/keyboard.ts +564 -0
- package/src/browser/main.ts +3415 -0
- package/src/browser/mouse.ts +255 -0
- package/src/browser/network.ts +142 -0
- package/src/browser/print_stats.ts +336 -0
- package/src/browser/screen.ts +978 -0
- package/src/browser/serial.ts +316 -0
- package/src/browser/speaker.ts +1223 -0
- package/src/browser/starter.ts +1688 -0
- package/src/browser/wisp_network.ts +332 -0
- package/src/browser/worker_bus.ts +64 -0
- package/src/buffer.ts +652 -0
- package/src/bus.ts +78 -0
- package/src/const.ts +128 -0
- package/src/cpu.ts +2891 -0
- package/src/dma.ts +474 -0
- package/src/elf.ts +251 -0
- package/src/floppy.ts +1778 -0
- package/src/ide.ts +3455 -0
- package/src/io.ts +504 -0
- package/src/iso9660.ts +317 -0
- package/src/kernel.ts +250 -0
- package/src/lib.ts +645 -0
- package/src/log.ts +149 -0
- package/src/main.ts +199 -0
- package/src/ne2k.ts +1589 -0
- package/src/pci.ts +815 -0
- package/src/pit.ts +406 -0
- package/src/ps2.ts +820 -0
- package/src/rtc.ts +537 -0
- package/src/rust/analysis.rs +101 -0
- package/src/rust/codegen.rs +2660 -0
- package/src/rust/config.rs +3 -0
- package/src/rust/control_flow.rs +425 -0
- package/src/rust/cpu/apic.rs +658 -0
- package/src/rust/cpu/arith.rs +1207 -0
- package/src/rust/cpu/call_indirect.rs +2 -0
- package/src/rust/cpu/cpu.rs +4501 -0
- package/src/rust/cpu/fpu.rs +923 -0
- package/src/rust/cpu/global_pointers.rs +112 -0
- package/src/rust/cpu/instructions.rs +2486 -0
- package/src/rust/cpu/instructions_0f.rs +5261 -0
- package/src/rust/cpu/ioapic.rs +316 -0
- package/src/rust/cpu/memory.rs +351 -0
- package/src/rust/cpu/misc_instr.rs +613 -0
- package/src/rust/cpu/mod.rs +16 -0
- package/src/rust/cpu/modrm.rs +133 -0
- package/src/rust/cpu/pic.rs +402 -0
- package/src/rust/cpu/sse_instr.rs +361 -0
- package/src/rust/cpu/string.rs +701 -0
- package/src/rust/cpu/vga.rs +175 -0
- package/src/rust/cpu_context.rs +69 -0
- package/src/rust/dbg.rs +98 -0
- package/src/rust/gen/analyzer.rs +3807 -0
- package/src/rust/gen/analyzer0f.rs +3992 -0
- package/src/rust/gen/interpreter.rs +4447 -0
- package/src/rust/gen/interpreter0f.rs +5404 -0
- package/src/rust/gen/jit.rs +5080 -0
- package/src/rust/gen/jit0f.rs +5547 -0
- package/src/rust/gen/mod.rs +14 -0
- package/src/rust/jit.rs +2443 -0
- package/src/rust/jit_instructions.rs +7881 -0
- package/src/rust/js_api.rs +6 -0
- package/src/rust/leb.rs +46 -0
- package/src/rust/lib.rs +29 -0
- package/src/rust/modrm.rs +330 -0
- package/src/rust/opstats.rs +249 -0
- package/src/rust/page.rs +15 -0
- package/src/rust/paging.rs +25 -0
- package/src/rust/prefix.rs +15 -0
- package/src/rust/profiler.rs +155 -0
- package/src/rust/regs.rs +38 -0
- package/src/rust/softfloat.rs +286 -0
- package/src/rust/state_flags.rs +27 -0
- package/src/rust/wasmgen/mod.rs +2 -0
- package/src/rust/wasmgen/wasm_builder.rs +1047 -0
- package/src/rust/wasmgen/wasm_opcodes.rs +221 -0
- package/src/rust/zstd.rs +105 -0
- package/src/sb16.ts +1928 -0
- package/src/state.ts +359 -0
- package/src/uart.ts +472 -0
- package/src/vga.ts +2791 -0
- package/src/virtio.ts +1756 -0
- package/src/virtio_balloon.ts +273 -0
- package/src/virtio_console.ts +372 -0
- package/src/virtio_net.ts +326 -0
package/lib/9p.ts
ADDED
|
@@ -0,0 +1,1547 @@
|
|
|
1
|
+
// -------------------------------------------------
|
|
2
|
+
// --------------------- 9P ------------------------
|
|
3
|
+
// -------------------------------------------------
|
|
4
|
+
// Implementation of the 9p filesystem device following the
|
|
5
|
+
// 9P2000.L protocol ( https://code.google.com/p/diod/wiki/protocol )
|
|
6
|
+
|
|
7
|
+
import { LOG_9P } from './../src/const.js'
|
|
8
|
+
import {
|
|
9
|
+
VirtIO,
|
|
10
|
+
VirtQueue,
|
|
11
|
+
VirtQueueBufferChain,
|
|
12
|
+
VIRTIO_F_VERSION_1,
|
|
13
|
+
VIRTIO_F_RING_EVENT_IDX,
|
|
14
|
+
VIRTIO_F_RING_INDIRECT_DESC,
|
|
15
|
+
} from '../src/virtio.js'
|
|
16
|
+
import { S_IFREG, S_IFDIR, STATUS_UNLINKED } from './filesystem.js'
|
|
17
|
+
import * as marshall from '../lib/marshall.js'
|
|
18
|
+
import { dbg_log, dbg_assert } from '../src/log.js'
|
|
19
|
+
import { h } from '../src/lib.js'
|
|
20
|
+
|
|
21
|
+
import type { CPU } from '../src/cpu.js'
|
|
22
|
+
import type { BusConnector } from '../src/bus.js'
|
|
23
|
+
import type { FS } from './filesystem.js'
|
|
24
|
+
|
|
25
|
+
// More accurate filenames in 9p debug messages at the cost of performance.
|
|
26
|
+
const TRACK_FILENAMES = false
|
|
27
|
+
|
|
28
|
+
// Feature bit (bit position) for mount tag.
|
|
29
|
+
const VIRTIO_9P_F_MOUNT_TAG = 0
|
|
30
|
+
// Assumed max tag length in bytes.
|
|
31
|
+
const VIRTIO_9P_MAX_TAGLEN = 254
|
|
32
|
+
|
|
33
|
+
const MAX_REPLYBUFFER_SIZE = 16 * 1024 * 1024
|
|
34
|
+
|
|
35
|
+
export const EPERM = 1 /* Operation not permitted */
|
|
36
|
+
export const ENOENT = 2 /* No such file or directory */
|
|
37
|
+
export const EEXIST = 17 /* File exists */
|
|
38
|
+
export const EINVAL = 22 /* Invalid argument */
|
|
39
|
+
export const EOPNOTSUPP = 95 /* Operation is not supported */
|
|
40
|
+
export const ENOTEMPTY = 39 /* Directory not empty */
|
|
41
|
+
export const EPROTO = 71 /* Protocol error */
|
|
42
|
+
|
|
43
|
+
const P9_SETATTR_MODE = 0x00000001
|
|
44
|
+
const P9_SETATTR_UID = 0x00000002
|
|
45
|
+
const P9_SETATTR_GID = 0x00000004
|
|
46
|
+
const P9_SETATTR_SIZE = 0x00000008
|
|
47
|
+
const P9_SETATTR_ATIME = 0x00000010
|
|
48
|
+
const P9_SETATTR_MTIME = 0x00000020
|
|
49
|
+
const P9_SETATTR_CTIME = 0x00000040
|
|
50
|
+
const P9_SETATTR_ATIME_SET = 0x00000080
|
|
51
|
+
const P9_SETATTR_MTIME_SET = 0x00000100
|
|
52
|
+
|
|
53
|
+
const _P9_STAT_MODE_DIR = 0x80000000
|
|
54
|
+
const _P9_STAT_MODE_APPEND = 0x40000000
|
|
55
|
+
const _P9_STAT_MODE_EXCL = 0x20000000
|
|
56
|
+
const _P9_STAT_MODE_MOUNT = 0x10000000
|
|
57
|
+
const _P9_STAT_MODE_AUTH = 0x08000000
|
|
58
|
+
const _P9_STAT_MODE_TMP = 0x04000000
|
|
59
|
+
const _P9_STAT_MODE_SYMLINK = 0x02000000
|
|
60
|
+
const _P9_STAT_MODE_LINK = 0x01000000
|
|
61
|
+
const _P9_STAT_MODE_DEVICE = 0x00800000
|
|
62
|
+
const _P9_STAT_MODE_NAMED_PIPE = 0x00200000
|
|
63
|
+
const _P9_STAT_MODE_SOCKET = 0x00100000
|
|
64
|
+
const _P9_STAT_MODE_SETUID = 0x00080000
|
|
65
|
+
const _P9_STAT_MODE_SETGID = 0x00040000
|
|
66
|
+
const _P9_STAT_MODE_SETVTX = 0x00010000
|
|
67
|
+
|
|
68
|
+
export const P9_LOCK_TYPE_RDLCK = 0
|
|
69
|
+
export const P9_LOCK_TYPE_WRLCK = 1
|
|
70
|
+
export const P9_LOCK_TYPE_UNLCK = 2
|
|
71
|
+
const P9_LOCK_TYPES = ['shared', 'exclusive', 'unlock']
|
|
72
|
+
|
|
73
|
+
const _P9_LOCK_FLAGS_BLOCK = 1
|
|
74
|
+
const _P9_LOCK_FLAGS_RECLAIM = 2
|
|
75
|
+
|
|
76
|
+
export const P9_LOCK_SUCCESS = 0
|
|
77
|
+
export const P9_LOCK_BLOCKED = 1
|
|
78
|
+
export const P9_LOCK_ERROR = 2
|
|
79
|
+
export const P9_LOCK_GRACE = 3
|
|
80
|
+
|
|
81
|
+
const FID_NONE = -1
|
|
82
|
+
const FID_INODE = 1
|
|
83
|
+
const FID_XATTR = 2
|
|
84
|
+
|
|
85
|
+
interface Fid {
|
|
86
|
+
inodeid: number
|
|
87
|
+
type: number
|
|
88
|
+
uid: number
|
|
89
|
+
dbg_name: string
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
type P9Handler = (
|
|
93
|
+
reqbuf: Uint8Array,
|
|
94
|
+
reply: (replybuf: Uint8Array) => void,
|
|
95
|
+
) => void
|
|
96
|
+
|
|
97
|
+
function range(size: number): number[] {
|
|
98
|
+
return Array.from(Array(size).keys())
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function init_virtio(
|
|
102
|
+
cpu: CPU,
|
|
103
|
+
configspace_taglen: number,
|
|
104
|
+
configspace_tagname: number[],
|
|
105
|
+
receive: (bufchain: VirtQueueBufferChain) => void,
|
|
106
|
+
): VirtIO {
|
|
107
|
+
const virtio = new VirtIO(cpu, {
|
|
108
|
+
name: 'virtio-9p',
|
|
109
|
+
pci_id: 0x06 << 3,
|
|
110
|
+
device_id: 0x1049,
|
|
111
|
+
subsystem_device_id: 9,
|
|
112
|
+
common: {
|
|
113
|
+
initial_port: 0xa800,
|
|
114
|
+
queues: [
|
|
115
|
+
{
|
|
116
|
+
size_supported: 32,
|
|
117
|
+
notify_offset: 0,
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
features: [
|
|
121
|
+
VIRTIO_9P_F_MOUNT_TAG,
|
|
122
|
+
VIRTIO_F_VERSION_1,
|
|
123
|
+
VIRTIO_F_RING_EVENT_IDX,
|
|
124
|
+
VIRTIO_F_RING_INDIRECT_DESC,
|
|
125
|
+
],
|
|
126
|
+
on_driver_ok: () => {},
|
|
127
|
+
},
|
|
128
|
+
notification: {
|
|
129
|
+
initial_port: 0xa900,
|
|
130
|
+
single_handler: false,
|
|
131
|
+
handlers: [
|
|
132
|
+
(queue_id: number) => {
|
|
133
|
+
if (queue_id !== 0) {
|
|
134
|
+
dbg_assert(
|
|
135
|
+
false,
|
|
136
|
+
'Virtio9P Notified for non-existent queue: ' +
|
|
137
|
+
queue_id +
|
|
138
|
+
' (expected queue_id of 0)',
|
|
139
|
+
)
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
const virtqueue = virtio.queues[0]
|
|
143
|
+
while (virtqueue.has_request()) {
|
|
144
|
+
const bufchain = virtqueue.pop_request()
|
|
145
|
+
receive(bufchain)
|
|
146
|
+
}
|
|
147
|
+
virtqueue.notify_me_after(0)
|
|
148
|
+
// Don't flush replies here: async replies are not completed yet.
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
},
|
|
152
|
+
isr_status: {
|
|
153
|
+
initial_port: 0xa700,
|
|
154
|
+
},
|
|
155
|
+
device_specific: {
|
|
156
|
+
initial_port: 0xa600,
|
|
157
|
+
struct: [
|
|
158
|
+
{
|
|
159
|
+
bytes: 2,
|
|
160
|
+
name: 'mount tag length',
|
|
161
|
+
read: () => configspace_taglen,
|
|
162
|
+
write: (_data: number) => {
|
|
163
|
+
/* read only */
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
].concat(
|
|
167
|
+
range(VIRTIO_9P_MAX_TAGLEN).map((index) => ({
|
|
168
|
+
bytes: 1,
|
|
169
|
+
name: 'mount tag name ' + index,
|
|
170
|
+
// Note: configspace_tagname may have changed after set_state
|
|
171
|
+
read: () => configspace_tagname[index] || 0,
|
|
172
|
+
write: (_data: number) => {
|
|
173
|
+
/* read only */
|
|
174
|
+
},
|
|
175
|
+
})),
|
|
176
|
+
),
|
|
177
|
+
},
|
|
178
|
+
})
|
|
179
|
+
return virtio
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export class Virtio9p {
|
|
183
|
+
fs: FS
|
|
184
|
+
bus: BusConnector
|
|
185
|
+
configspace_tagname: number[]
|
|
186
|
+
configspace_taglen: number
|
|
187
|
+
virtio: VirtIO
|
|
188
|
+
virtqueue: VirtQueue
|
|
189
|
+
VERSION: string
|
|
190
|
+
BLOCKSIZE: number
|
|
191
|
+
msize: number
|
|
192
|
+
replybuffer: Uint8Array
|
|
193
|
+
replybuffersize: number
|
|
194
|
+
fids: Fid[]
|
|
195
|
+
|
|
196
|
+
constructor(filesystem: FS, cpu: CPU, bus: BusConnector) {
|
|
197
|
+
this.fs = filesystem
|
|
198
|
+
this.bus = bus
|
|
199
|
+
|
|
200
|
+
this.configspace_tagname = [0x68, 0x6f, 0x73, 0x74, 0x39, 0x70] // "host9p" string
|
|
201
|
+
this.configspace_taglen = this.configspace_tagname.length // num bytes
|
|
202
|
+
|
|
203
|
+
this.virtio = init_virtio(
|
|
204
|
+
cpu,
|
|
205
|
+
this.configspace_taglen,
|
|
206
|
+
this.configspace_tagname,
|
|
207
|
+
this.ReceiveRequest.bind(this),
|
|
208
|
+
)
|
|
209
|
+
this.virtqueue = this.virtio.queues[0]
|
|
210
|
+
|
|
211
|
+
this.VERSION = '9P2000.L'
|
|
212
|
+
this.BLOCKSIZE = 8192 // Let's define one page.
|
|
213
|
+
this.msize = 8192 // maximum message size
|
|
214
|
+
this.replybuffer = new Uint8Array(this.msize * 2) // Twice the msize to stay on the safe site
|
|
215
|
+
this.replybuffersize = 0
|
|
216
|
+
this.fids = []
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
get_state(): any[] {
|
|
220
|
+
const state: any[] = []
|
|
221
|
+
|
|
222
|
+
state[0] = this.configspace_tagname
|
|
223
|
+
state[1] = this.configspace_taglen
|
|
224
|
+
state[2] = this.virtio
|
|
225
|
+
state[3] = this.VERSION
|
|
226
|
+
state[4] = this.BLOCKSIZE
|
|
227
|
+
state[5] = this.msize
|
|
228
|
+
state[6] = this.replybuffer
|
|
229
|
+
state[7] = this.replybuffersize
|
|
230
|
+
state[8] = this.fids.map(function (f) {
|
|
231
|
+
return [f.inodeid, f.type, f.uid, f.dbg_name]
|
|
232
|
+
})
|
|
233
|
+
state[9] = this.fs
|
|
234
|
+
|
|
235
|
+
return state
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
set_state(state: any[]): void {
|
|
239
|
+
this.configspace_tagname = state[0]
|
|
240
|
+
this.configspace_taglen = state[1]
|
|
241
|
+
this.virtio.set_state(state[2])
|
|
242
|
+
this.virtqueue = this.virtio.queues[0]
|
|
243
|
+
this.VERSION = state[3]
|
|
244
|
+
this.BLOCKSIZE = state[4]
|
|
245
|
+
this.msize = state[5]
|
|
246
|
+
this.replybuffer = state[6]
|
|
247
|
+
this.replybuffersize = state[7]
|
|
248
|
+
|
|
249
|
+
this.fids = state[8].map(function (f: any[]) {
|
|
250
|
+
return { inodeid: f[0], type: f[1], uid: f[2], dbg_name: f[3] }
|
|
251
|
+
})
|
|
252
|
+
this.fs.set_state(state[9])
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Note: dbg_name is only used for debugging messages and may not be the same as the filename,
|
|
256
|
+
// since it is not synchronised with renames done outside of 9p. Hard-links, linking and unlinking
|
|
257
|
+
// operations also mean that having a single filename no longer makes sense.
|
|
258
|
+
// Set TRACK_FILENAMES = true to sync dbg_name during 9p renames.
|
|
259
|
+
Createfid(
|
|
260
|
+
inodeid: number,
|
|
261
|
+
type: number,
|
|
262
|
+
uid: number,
|
|
263
|
+
dbg_name: string,
|
|
264
|
+
): Fid {
|
|
265
|
+
return { inodeid, type, uid, dbg_name }
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
update_dbg_name(idx: number, newname: string): void {
|
|
269
|
+
for (const fid of this.fids) {
|
|
270
|
+
if (fid.inodeid === idx) fid.dbg_name = newname
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
reset(): void {
|
|
275
|
+
this.fids = []
|
|
276
|
+
this.virtio.reset()
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
BuildReply(id: number, tag: number, payloadsize: number): void {
|
|
280
|
+
dbg_assert(payloadsize >= 0, '9P: Negative payload size')
|
|
281
|
+
marshall.Marshall(
|
|
282
|
+
['w', 'b', 'h'],
|
|
283
|
+
[payloadsize + 7, id + 1, tag],
|
|
284
|
+
this.replybuffer,
|
|
285
|
+
0,
|
|
286
|
+
)
|
|
287
|
+
if (payloadsize + 7 >= this.replybuffer.length) {
|
|
288
|
+
dbg_log('Error in 9p: payloadsize exceeds maximum length', LOG_9P)
|
|
289
|
+
}
|
|
290
|
+
this.replybuffersize = payloadsize + 7
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
SendError(tag: number, errormsg: string, errorcode: number): void {
|
|
294
|
+
const size = marshall.Marshall(['w'], [errorcode], this.replybuffer, 7)
|
|
295
|
+
this.BuildReply(6, tag, size)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
SendReply(bufchain: VirtQueueBufferChain): void {
|
|
299
|
+
dbg_assert(this.replybuffersize >= 0, '9P: Negative replybuffersize')
|
|
300
|
+
bufchain.set_next_blob(
|
|
301
|
+
this.replybuffer.subarray(0, this.replybuffersize),
|
|
302
|
+
)
|
|
303
|
+
this.virtqueue.push_reply(bufchain)
|
|
304
|
+
this.virtqueue.flush_replies()
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async ReceiveRequest(bufchain: VirtQueueBufferChain): Promise<void> {
|
|
308
|
+
// TODO: split into header + data blobs to avoid unnecessary copying.
|
|
309
|
+
const buffer = new Uint8Array(bufchain.length_readable)
|
|
310
|
+
bufchain.get_next_blob(buffer)
|
|
311
|
+
|
|
312
|
+
const state = { offset: 0 }
|
|
313
|
+
const header = marshall.Unmarshall(['w', 'b', 'h'], buffer, state)
|
|
314
|
+
let size = header[0]
|
|
315
|
+
const id = header[1]
|
|
316
|
+
const tag = header[2]
|
|
317
|
+
|
|
318
|
+
switch (id) {
|
|
319
|
+
case 8: {
|
|
320
|
+
// statfs
|
|
321
|
+
size = this.fs.GetTotalSize() // size used by all files
|
|
322
|
+
const space = this.fs.GetSpace()
|
|
323
|
+
|
|
324
|
+
const req: any[] = []
|
|
325
|
+
req[0] = 0x01021997
|
|
326
|
+
req[1] = this.BLOCKSIZE // optimal transfer block size
|
|
327
|
+
req[2] = Math.floor(space / req[1]) // free blocks
|
|
328
|
+
req[3] = req[2] - Math.floor(size / req[1]) // free blocks in fs
|
|
329
|
+
req[4] = req[2] - Math.floor(size / req[1]) // free blocks avail to non-superuser
|
|
330
|
+
req[5] = this.fs.CountUsedInodes() // total number of inodes
|
|
331
|
+
req[6] = this.fs.CountFreeInodes()
|
|
332
|
+
req[7] = 0 // file system id?
|
|
333
|
+
req[8] = 256 // maximum length of filenames
|
|
334
|
+
|
|
335
|
+
size = marshall.Marshall(
|
|
336
|
+
['w', 'w', 'd', 'd', 'd', 'd', 'd', 'd', 'w'],
|
|
337
|
+
req,
|
|
338
|
+
this.replybuffer,
|
|
339
|
+
7,
|
|
340
|
+
)
|
|
341
|
+
this.BuildReply(id, tag, size)
|
|
342
|
+
this.SendReply(bufchain)
|
|
343
|
+
break
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
case 112: // topen
|
|
347
|
+
case 12: {
|
|
348
|
+
// tlopen
|
|
349
|
+
let req = marshall.Unmarshall(['w', 'w'], buffer, state)
|
|
350
|
+
const fid = req[0]
|
|
351
|
+
const mode = req[1]
|
|
352
|
+
dbg_log('[open] fid=' + fid + ', mode=' + mode, LOG_9P)
|
|
353
|
+
const idx = this.fids[fid].inodeid
|
|
354
|
+
const inode = this.fs.GetInode(idx)
|
|
355
|
+
dbg_log(
|
|
356
|
+
'file open ' + this.fids[fid].dbg_name + ' tag:' + tag,
|
|
357
|
+
LOG_9P,
|
|
358
|
+
)
|
|
359
|
+
await this.fs.OpenInode(idx, mode)
|
|
360
|
+
|
|
361
|
+
req = []
|
|
362
|
+
req[0] = inode.qid
|
|
363
|
+
req[1] = this.msize - 24
|
|
364
|
+
marshall.Marshall(['Q', 'w'], req, this.replybuffer, 7)
|
|
365
|
+
this.BuildReply(id, tag, 13 + 4)
|
|
366
|
+
this.SendReply(bufchain)
|
|
367
|
+
break
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
case 70: {
|
|
371
|
+
// link
|
|
372
|
+
const req = marshall.Unmarshall(['w', 'w', 's'], buffer, state)
|
|
373
|
+
const dfid = req[0]
|
|
374
|
+
const fid = req[1]
|
|
375
|
+
const name = req[2]
|
|
376
|
+
dbg_log('[link] dfid=' + dfid + ', name=' + name, LOG_9P)
|
|
377
|
+
|
|
378
|
+
const ret = this.fs.Link(
|
|
379
|
+
this.fids[dfid].inodeid,
|
|
380
|
+
this.fids[fid].inodeid,
|
|
381
|
+
name,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
if (ret < 0) {
|
|
385
|
+
let error_message = ''
|
|
386
|
+
if (ret === -EPERM)
|
|
387
|
+
error_message = 'Operation not permitted'
|
|
388
|
+
else {
|
|
389
|
+
error_message = 'Unknown error: ' + -ret
|
|
390
|
+
dbg_assert(
|
|
391
|
+
false,
|
|
392
|
+
'[link]: Unexpected error code: ' + -ret,
|
|
393
|
+
)
|
|
394
|
+
}
|
|
395
|
+
this.SendError(tag, error_message, -ret)
|
|
396
|
+
this.SendReply(bufchain)
|
|
397
|
+
break
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
this.BuildReply(id, tag, 0)
|
|
401
|
+
this.SendReply(bufchain)
|
|
402
|
+
break
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
case 16: {
|
|
406
|
+
// symlink
|
|
407
|
+
const req = marshall.Unmarshall(
|
|
408
|
+
['w', 's', 's', 'w'],
|
|
409
|
+
buffer,
|
|
410
|
+
state,
|
|
411
|
+
)
|
|
412
|
+
const fid = req[0]
|
|
413
|
+
const name = req[1]
|
|
414
|
+
const symgt = req[2]
|
|
415
|
+
const gid = req[3]
|
|
416
|
+
dbg_log(
|
|
417
|
+
'[symlink] fid=' +
|
|
418
|
+
fid +
|
|
419
|
+
', name=' +
|
|
420
|
+
name +
|
|
421
|
+
', symgt=' +
|
|
422
|
+
symgt +
|
|
423
|
+
', gid=' +
|
|
424
|
+
gid,
|
|
425
|
+
LOG_9P,
|
|
426
|
+
)
|
|
427
|
+
const idx = this.fs.CreateSymlink(
|
|
428
|
+
name,
|
|
429
|
+
this.fids[fid].inodeid,
|
|
430
|
+
symgt,
|
|
431
|
+
)
|
|
432
|
+
const inode = this.fs.GetInode(idx)
|
|
433
|
+
inode.uid = this.fids[fid].uid
|
|
434
|
+
inode.gid = gid
|
|
435
|
+
marshall.Marshall(['Q'], [inode.qid], this.replybuffer, 7)
|
|
436
|
+
this.BuildReply(id, tag, 13)
|
|
437
|
+
this.SendReply(bufchain)
|
|
438
|
+
break
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
case 18: {
|
|
442
|
+
// mknod
|
|
443
|
+
const req = marshall.Unmarshall(
|
|
444
|
+
['w', 's', 'w', 'w', 'w', 'w'],
|
|
445
|
+
buffer,
|
|
446
|
+
state,
|
|
447
|
+
)
|
|
448
|
+
const fid = req[0]
|
|
449
|
+
const name = req[1]
|
|
450
|
+
const mode = req[2]
|
|
451
|
+
const major = req[3]
|
|
452
|
+
const minor = req[4]
|
|
453
|
+
const gid = req[5]
|
|
454
|
+
dbg_log(
|
|
455
|
+
'[mknod] fid=' +
|
|
456
|
+
fid +
|
|
457
|
+
', name=' +
|
|
458
|
+
name +
|
|
459
|
+
', major=' +
|
|
460
|
+
major +
|
|
461
|
+
', minor=' +
|
|
462
|
+
minor +
|
|
463
|
+
'',
|
|
464
|
+
LOG_9P,
|
|
465
|
+
)
|
|
466
|
+
const idx = this.fs.CreateNode(
|
|
467
|
+
name,
|
|
468
|
+
this.fids[fid].inodeid,
|
|
469
|
+
major,
|
|
470
|
+
minor,
|
|
471
|
+
)
|
|
472
|
+
const inode = this.fs.GetInode(idx)
|
|
473
|
+
inode.mode = mode
|
|
474
|
+
//inode.mode = mode | S_IFCHR; // XXX: fails "Mknod - fifo" test
|
|
475
|
+
inode.uid = this.fids[fid].uid
|
|
476
|
+
inode.gid = gid
|
|
477
|
+
marshall.Marshall(['Q'], [inode.qid], this.replybuffer, 7)
|
|
478
|
+
this.BuildReply(id, tag, 13)
|
|
479
|
+
this.SendReply(bufchain)
|
|
480
|
+
break
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
case 22: {
|
|
484
|
+
// TREADLINK
|
|
485
|
+
const req = marshall.Unmarshall(['w'], buffer, state)
|
|
486
|
+
const fid = req[0]
|
|
487
|
+
const inode = this.fs.GetInode(this.fids[fid].inodeid)
|
|
488
|
+
dbg_log(
|
|
489
|
+
'[readlink] fid=' +
|
|
490
|
+
fid +
|
|
491
|
+
' name=' +
|
|
492
|
+
this.fids[fid].dbg_name +
|
|
493
|
+
' target=' +
|
|
494
|
+
inode.symlink,
|
|
495
|
+
LOG_9P,
|
|
496
|
+
)
|
|
497
|
+
size = marshall.Marshall(
|
|
498
|
+
['s'],
|
|
499
|
+
[inode.symlink],
|
|
500
|
+
this.replybuffer,
|
|
501
|
+
7,
|
|
502
|
+
)
|
|
503
|
+
this.BuildReply(id, tag, size)
|
|
504
|
+
this.SendReply(bufchain)
|
|
505
|
+
break
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
case 72: {
|
|
509
|
+
// tmkdir
|
|
510
|
+
const req = marshall.Unmarshall(
|
|
511
|
+
['w', 's', 'w', 'w'],
|
|
512
|
+
buffer,
|
|
513
|
+
state,
|
|
514
|
+
)
|
|
515
|
+
const fid = req[0]
|
|
516
|
+
const name = req[1]
|
|
517
|
+
const mode = req[2]
|
|
518
|
+
const gid = req[3]
|
|
519
|
+
dbg_log(
|
|
520
|
+
'[mkdir] fid=' +
|
|
521
|
+
fid +
|
|
522
|
+
', name=' +
|
|
523
|
+
name +
|
|
524
|
+
', mode=' +
|
|
525
|
+
mode +
|
|
526
|
+
', gid=' +
|
|
527
|
+
gid,
|
|
528
|
+
LOG_9P,
|
|
529
|
+
)
|
|
530
|
+
const idx = this.fs.CreateDirectory(
|
|
531
|
+
name,
|
|
532
|
+
this.fids[fid].inodeid,
|
|
533
|
+
)
|
|
534
|
+
const inode = this.fs.GetInode(idx)
|
|
535
|
+
inode.mode = mode | S_IFDIR
|
|
536
|
+
inode.uid = this.fids[fid].uid
|
|
537
|
+
inode.gid = gid
|
|
538
|
+
marshall.Marshall(['Q'], [inode.qid], this.replybuffer, 7)
|
|
539
|
+
this.BuildReply(id, tag, 13)
|
|
540
|
+
this.SendReply(bufchain)
|
|
541
|
+
break
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
case 14: {
|
|
545
|
+
// tlcreate
|
|
546
|
+
const req = marshall.Unmarshall(
|
|
547
|
+
['w', 's', 'w', 'w', 'w'],
|
|
548
|
+
buffer,
|
|
549
|
+
state,
|
|
550
|
+
)
|
|
551
|
+
const fid = req[0]
|
|
552
|
+
const name = req[1]
|
|
553
|
+
const flags = req[2]
|
|
554
|
+
const mode = req[3]
|
|
555
|
+
const gid = req[4]
|
|
556
|
+
this.bus.send('9p-create', [name, this.fids[fid].inodeid])
|
|
557
|
+
dbg_log(
|
|
558
|
+
'[create] fid=' +
|
|
559
|
+
fid +
|
|
560
|
+
', name=' +
|
|
561
|
+
name +
|
|
562
|
+
', flags=' +
|
|
563
|
+
flags +
|
|
564
|
+
', mode=' +
|
|
565
|
+
mode +
|
|
566
|
+
', gid=' +
|
|
567
|
+
gid,
|
|
568
|
+
LOG_9P,
|
|
569
|
+
)
|
|
570
|
+
const idx = this.fs.CreateFile(name, this.fids[fid].inodeid)
|
|
571
|
+
this.fids[fid].inodeid = idx
|
|
572
|
+
this.fids[fid].type = FID_INODE
|
|
573
|
+
this.fids[fid].dbg_name = name
|
|
574
|
+
const inode = this.fs.GetInode(idx)
|
|
575
|
+
inode.uid = this.fids[fid].uid
|
|
576
|
+
inode.gid = gid
|
|
577
|
+
inode.mode = mode | S_IFREG
|
|
578
|
+
marshall.Marshall(
|
|
579
|
+
['Q', 'w'],
|
|
580
|
+
[inode.qid, this.msize - 24],
|
|
581
|
+
this.replybuffer,
|
|
582
|
+
7,
|
|
583
|
+
)
|
|
584
|
+
this.BuildReply(id, tag, 13 + 4)
|
|
585
|
+
this.SendReply(bufchain)
|
|
586
|
+
break
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
case 52: {
|
|
590
|
+
// lock
|
|
591
|
+
const req = marshall.Unmarshall(
|
|
592
|
+
['w', 'b', 'w', 'd', 'd', 'w', 's'],
|
|
593
|
+
buffer,
|
|
594
|
+
state,
|
|
595
|
+
)
|
|
596
|
+
const fid = req[0]
|
|
597
|
+
const flags = req[2]
|
|
598
|
+
const lock_length = req[4] === 0 ? Infinity : req[4]
|
|
599
|
+
const lock_request = this.fs.DescribeLock(
|
|
600
|
+
req[1],
|
|
601
|
+
req[3],
|
|
602
|
+
lock_length,
|
|
603
|
+
req[5],
|
|
604
|
+
req[6],
|
|
605
|
+
)
|
|
606
|
+
dbg_log(
|
|
607
|
+
'[lock] fid=' +
|
|
608
|
+
fid +
|
|
609
|
+
', type=' +
|
|
610
|
+
P9_LOCK_TYPES[lock_request.type] +
|
|
611
|
+
', start=' +
|
|
612
|
+
lock_request.start +
|
|
613
|
+
', length=' +
|
|
614
|
+
lock_request.length +
|
|
615
|
+
', proc_id=' +
|
|
616
|
+
lock_request.proc_id,
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
const ret = this.fs.Lock(
|
|
620
|
+
this.fids[fid].inodeid,
|
|
621
|
+
lock_request,
|
|
622
|
+
flags,
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
marshall.Marshall(['b'], [ret], this.replybuffer, 7)
|
|
626
|
+
this.BuildReply(id, tag, 1)
|
|
627
|
+
this.SendReply(bufchain)
|
|
628
|
+
break
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
case 54: {
|
|
632
|
+
// getlock
|
|
633
|
+
const req = marshall.Unmarshall(
|
|
634
|
+
['w', 'b', 'd', 'd', 'w', 's'],
|
|
635
|
+
buffer,
|
|
636
|
+
state,
|
|
637
|
+
)
|
|
638
|
+
const fid = req[0]
|
|
639
|
+
const lock_length = req[3] === 0 ? Infinity : req[3]
|
|
640
|
+
const lock_request = this.fs.DescribeLock(
|
|
641
|
+
req[1],
|
|
642
|
+
req[2],
|
|
643
|
+
lock_length,
|
|
644
|
+
req[4],
|
|
645
|
+
req[5],
|
|
646
|
+
)
|
|
647
|
+
dbg_log(
|
|
648
|
+
'[getlock] fid=' +
|
|
649
|
+
fid +
|
|
650
|
+
', type=' +
|
|
651
|
+
P9_LOCK_TYPES[lock_request.type] +
|
|
652
|
+
', start=' +
|
|
653
|
+
lock_request.start +
|
|
654
|
+
', length=' +
|
|
655
|
+
lock_request.length +
|
|
656
|
+
', proc_id=' +
|
|
657
|
+
lock_request.proc_id,
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
let ret_lock = this.fs.GetLock(
|
|
661
|
+
this.fids[fid].inodeid,
|
|
662
|
+
lock_request,
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
if (!ret_lock) {
|
|
666
|
+
ret_lock = lock_request
|
|
667
|
+
ret_lock.type = P9_LOCK_TYPE_UNLCK
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const ret_length =
|
|
671
|
+
ret_lock.length === Infinity ? 0 : ret_lock.length
|
|
672
|
+
|
|
673
|
+
size = marshall.Marshall(
|
|
674
|
+
['b', 'd', 'd', 'w', 's'],
|
|
675
|
+
[
|
|
676
|
+
ret_lock.type,
|
|
677
|
+
ret_lock.start,
|
|
678
|
+
ret_length,
|
|
679
|
+
ret_lock.proc_id,
|
|
680
|
+
ret_lock.client_id,
|
|
681
|
+
],
|
|
682
|
+
this.replybuffer,
|
|
683
|
+
7,
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
this.BuildReply(id, tag, size)
|
|
687
|
+
this.SendReply(bufchain)
|
|
688
|
+
break
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
case 24: {
|
|
692
|
+
// getattr
|
|
693
|
+
const req = marshall.Unmarshall(['w', 'd'], buffer, state)
|
|
694
|
+
const fid = req[0]
|
|
695
|
+
const inode = this.fs.GetInode(this.fids[fid].inodeid)
|
|
696
|
+
dbg_log(
|
|
697
|
+
'[getattr]: fid=' +
|
|
698
|
+
fid +
|
|
699
|
+
' name=' +
|
|
700
|
+
this.fids[fid].dbg_name +
|
|
701
|
+
' request mask=' +
|
|
702
|
+
req[1],
|
|
703
|
+
LOG_9P,
|
|
704
|
+
)
|
|
705
|
+
if (!inode || inode.status === STATUS_UNLINKED) {
|
|
706
|
+
dbg_log('getattr: unlinked', LOG_9P)
|
|
707
|
+
this.SendError(tag, 'No such file or directory', ENOENT)
|
|
708
|
+
this.SendReply(bufchain)
|
|
709
|
+
break
|
|
710
|
+
}
|
|
711
|
+
req[0] = req[1] // request mask
|
|
712
|
+
req[1] = inode.qid
|
|
713
|
+
|
|
714
|
+
req[2] = inode.mode
|
|
715
|
+
req[3] = inode.uid // user id
|
|
716
|
+
req[4] = inode.gid // group id
|
|
717
|
+
|
|
718
|
+
req[5] = inode.nlinks // number of hard links
|
|
719
|
+
req[6] = (inode.major << 8) | inode.minor // device id low
|
|
720
|
+
req[7] = inode.size // size low
|
|
721
|
+
req[8] = this.BLOCKSIZE
|
|
722
|
+
req[9] = Math.floor(inode.size / 512 + 1) // blk size low
|
|
723
|
+
req[10] = inode.atime // atime
|
|
724
|
+
req[11] = 0x0
|
|
725
|
+
req[12] = inode.mtime // mtime
|
|
726
|
+
req[13] = 0x0
|
|
727
|
+
req[14] = inode.ctime // ctime
|
|
728
|
+
req[15] = 0x0
|
|
729
|
+
req[16] = 0x0 // btime
|
|
730
|
+
req[17] = 0x0
|
|
731
|
+
req[18] = 0x0 // st_gen
|
|
732
|
+
req[19] = 0x0 // data_version
|
|
733
|
+
marshall.Marshall(
|
|
734
|
+
[
|
|
735
|
+
'd',
|
|
736
|
+
'Q',
|
|
737
|
+
'w',
|
|
738
|
+
'w',
|
|
739
|
+
'w',
|
|
740
|
+
'd',
|
|
741
|
+
'd',
|
|
742
|
+
'd',
|
|
743
|
+
'd',
|
|
744
|
+
'd',
|
|
745
|
+
'd',
|
|
746
|
+
'd', // atime
|
|
747
|
+
'd',
|
|
748
|
+
'd', // mtime
|
|
749
|
+
'd',
|
|
750
|
+
'd', // ctime
|
|
751
|
+
'd',
|
|
752
|
+
'd', // btime
|
|
753
|
+
'd',
|
|
754
|
+
'd',
|
|
755
|
+
],
|
|
756
|
+
req,
|
|
757
|
+
this.replybuffer,
|
|
758
|
+
7,
|
|
759
|
+
)
|
|
760
|
+
this.BuildReply(id, tag, 8 + 13 + 4 + 4 + 4 + 8 * 15)
|
|
761
|
+
this.SendReply(bufchain)
|
|
762
|
+
break
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
case 26: {
|
|
766
|
+
// setattr
|
|
767
|
+
const req = marshall.Unmarshall(
|
|
768
|
+
[
|
|
769
|
+
'w',
|
|
770
|
+
'w',
|
|
771
|
+
'w', // mode
|
|
772
|
+
'w',
|
|
773
|
+
'w', // uid, gid
|
|
774
|
+
'd', // size
|
|
775
|
+
'd',
|
|
776
|
+
'd', // atime
|
|
777
|
+
'd',
|
|
778
|
+
'd', // mtime
|
|
779
|
+
],
|
|
780
|
+
buffer,
|
|
781
|
+
state,
|
|
782
|
+
)
|
|
783
|
+
const fid = req[0]
|
|
784
|
+
const inode = this.fs.GetInode(this.fids[fid].inodeid)
|
|
785
|
+
dbg_log(
|
|
786
|
+
'[setattr]: fid=' +
|
|
787
|
+
fid +
|
|
788
|
+
' request mask=' +
|
|
789
|
+
req[1] +
|
|
790
|
+
' name=' +
|
|
791
|
+
this.fids[fid].dbg_name,
|
|
792
|
+
LOG_9P,
|
|
793
|
+
)
|
|
794
|
+
if (req[1] & P9_SETATTR_MODE) {
|
|
795
|
+
// XXX: check mode (S_IFREG or S_IFDIR or similar should be set)
|
|
796
|
+
inode.mode = req[2]
|
|
797
|
+
}
|
|
798
|
+
if (req[1] & P9_SETATTR_UID) {
|
|
799
|
+
inode.uid = req[3]
|
|
800
|
+
}
|
|
801
|
+
if (req[1] & P9_SETATTR_GID) {
|
|
802
|
+
inode.gid = req[4]
|
|
803
|
+
}
|
|
804
|
+
if (req[1] & P9_SETATTR_ATIME) {
|
|
805
|
+
inode.atime = Math.floor(new Date().getTime() / 1000)
|
|
806
|
+
}
|
|
807
|
+
if (req[1] & P9_SETATTR_MTIME) {
|
|
808
|
+
inode.mtime = Math.floor(new Date().getTime() / 1000)
|
|
809
|
+
}
|
|
810
|
+
if (req[1] & P9_SETATTR_CTIME) {
|
|
811
|
+
inode.ctime = Math.floor(new Date().getTime() / 1000)
|
|
812
|
+
}
|
|
813
|
+
if (req[1] & P9_SETATTR_ATIME_SET) {
|
|
814
|
+
inode.atime = req[6]
|
|
815
|
+
}
|
|
816
|
+
if (req[1] & P9_SETATTR_MTIME_SET) {
|
|
817
|
+
inode.mtime = req[8]
|
|
818
|
+
}
|
|
819
|
+
if (req[1] & P9_SETATTR_SIZE) {
|
|
820
|
+
await this.fs.ChangeSize(this.fids[fid].inodeid, req[5])
|
|
821
|
+
}
|
|
822
|
+
this.BuildReply(id, tag, 0)
|
|
823
|
+
this.SendReply(bufchain)
|
|
824
|
+
break
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
case 50: {
|
|
828
|
+
// fsync
|
|
829
|
+
const req = marshall.Unmarshall(['w', 'd'], buffer, state)
|
|
830
|
+
const _fid = req[0]
|
|
831
|
+
this.BuildReply(id, tag, 0)
|
|
832
|
+
this.SendReply(bufchain)
|
|
833
|
+
break
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
case 40: // TREADDIR
|
|
837
|
+
case 116: {
|
|
838
|
+
// read
|
|
839
|
+
const req = marshall.Unmarshall(['w', 'd', 'w'], buffer, state)
|
|
840
|
+
const fid = req[0]
|
|
841
|
+
const offset = req[1]
|
|
842
|
+
let count = req[2]
|
|
843
|
+
const inode = this.fs.GetInode(this.fids[fid].inodeid)
|
|
844
|
+
if (id === 40)
|
|
845
|
+
dbg_log(
|
|
846
|
+
'[treaddir]: fid=' +
|
|
847
|
+
fid +
|
|
848
|
+
' offset=' +
|
|
849
|
+
offset +
|
|
850
|
+
' count=' +
|
|
851
|
+
count,
|
|
852
|
+
LOG_9P,
|
|
853
|
+
)
|
|
854
|
+
if (id === 116)
|
|
855
|
+
dbg_log(
|
|
856
|
+
'[read]: fid=' +
|
|
857
|
+
fid +
|
|
858
|
+
' (' +
|
|
859
|
+
this.fids[fid].dbg_name +
|
|
860
|
+
') offset=' +
|
|
861
|
+
offset +
|
|
862
|
+
' count=' +
|
|
863
|
+
count +
|
|
864
|
+
' fidtype=' +
|
|
865
|
+
this.fids[fid].type,
|
|
866
|
+
LOG_9P,
|
|
867
|
+
)
|
|
868
|
+
if (!inode || inode.status === STATUS_UNLINKED) {
|
|
869
|
+
dbg_log('read/treaddir: unlinked', LOG_9P)
|
|
870
|
+
this.SendError(tag, 'No such file or directory', ENOENT)
|
|
871
|
+
this.SendReply(bufchain)
|
|
872
|
+
break
|
|
873
|
+
}
|
|
874
|
+
if (this.fids[fid].type === FID_XATTR) {
|
|
875
|
+
if (inode.caps!.length < offset + count)
|
|
876
|
+
count = inode.caps!.length - offset
|
|
877
|
+
for (let i = 0; i < count; i++)
|
|
878
|
+
this.replybuffer[7 + 4 + i] = inode.caps![offset + i]
|
|
879
|
+
marshall.Marshall(['w'], [count], this.replybuffer, 7)
|
|
880
|
+
this.BuildReply(id, tag, 4 + count)
|
|
881
|
+
this.SendReply(bufchain)
|
|
882
|
+
} else {
|
|
883
|
+
await this.fs.OpenInode(this.fids[fid].inodeid, undefined)
|
|
884
|
+
const inodeid = this.fids[fid].inodeid
|
|
885
|
+
|
|
886
|
+
count = Math.min(count, this.replybuffer.length - (7 + 4))
|
|
887
|
+
|
|
888
|
+
if (inode.size < offset + count) count = inode.size - offset
|
|
889
|
+
else if (id === 40) {
|
|
890
|
+
// for directories, return whole number of dir-entries.
|
|
891
|
+
count =
|
|
892
|
+
this.fs.RoundToDirentry(inodeid, offset + count) -
|
|
893
|
+
offset
|
|
894
|
+
}
|
|
895
|
+
if (offset > inode.size) {
|
|
896
|
+
// offset can be greater than available - should return count of zero.
|
|
897
|
+
// See http://ericvh.github.io/9p-rfc/rfc9p2000.html#anchor30
|
|
898
|
+
count = 0
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
this.bus.send('9p-read-start', [this.fids[fid].dbg_name])
|
|
902
|
+
|
|
903
|
+
const data = await this.fs.Read(inodeid, offset, count)
|
|
904
|
+
|
|
905
|
+
this.bus.send('9p-read-end', [
|
|
906
|
+
this.fids[fid].dbg_name,
|
|
907
|
+
count,
|
|
908
|
+
])
|
|
909
|
+
|
|
910
|
+
if (data) {
|
|
911
|
+
this.replybuffer.set(data, 7 + 4)
|
|
912
|
+
}
|
|
913
|
+
marshall.Marshall(['w'], [count], this.replybuffer, 7)
|
|
914
|
+
this.BuildReply(id, tag, 4 + count)
|
|
915
|
+
this.SendReply(bufchain)
|
|
916
|
+
}
|
|
917
|
+
break
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
case 118: {
|
|
921
|
+
// write
|
|
922
|
+
const req = marshall.Unmarshall(['w', 'd', 'w'], buffer, state)
|
|
923
|
+
const fid = req[0]
|
|
924
|
+
const offset = req[1]
|
|
925
|
+
const count = req[2]
|
|
926
|
+
|
|
927
|
+
const filename = this.fids[fid].dbg_name
|
|
928
|
+
|
|
929
|
+
dbg_log(
|
|
930
|
+
'[write]: fid=' +
|
|
931
|
+
fid +
|
|
932
|
+
' (' +
|
|
933
|
+
filename +
|
|
934
|
+
') offset=' +
|
|
935
|
+
offset +
|
|
936
|
+
' count=' +
|
|
937
|
+
count +
|
|
938
|
+
' fidtype=' +
|
|
939
|
+
this.fids[fid].type,
|
|
940
|
+
LOG_9P,
|
|
941
|
+
)
|
|
942
|
+
if (this.fids[fid].type === FID_XATTR) {
|
|
943
|
+
// XXX: xattr not supported yet. Ignore write.
|
|
944
|
+
this.SendError(tag, 'Setxattr not supported', EOPNOTSUPP)
|
|
945
|
+
this.SendReply(bufchain)
|
|
946
|
+
break
|
|
947
|
+
} else {
|
|
948
|
+
// XXX: Size of the subarray is unchecked
|
|
949
|
+
await this.fs.Write(
|
|
950
|
+
this.fids[fid].inodeid,
|
|
951
|
+
offset,
|
|
952
|
+
count,
|
|
953
|
+
buffer.subarray(state.offset),
|
|
954
|
+
)
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
this.bus.send('9p-write-end', [filename, count])
|
|
958
|
+
|
|
959
|
+
marshall.Marshall(['w'], [count], this.replybuffer, 7)
|
|
960
|
+
this.BuildReply(id, tag, 4)
|
|
961
|
+
this.SendReply(bufchain)
|
|
962
|
+
break
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
case 74: {
|
|
966
|
+
// RENAMEAT
|
|
967
|
+
const req = marshall.Unmarshall(
|
|
968
|
+
['w', 's', 'w', 's'],
|
|
969
|
+
buffer,
|
|
970
|
+
state,
|
|
971
|
+
)
|
|
972
|
+
const olddirfid = req[0]
|
|
973
|
+
const oldname = req[1]
|
|
974
|
+
const newdirfid = req[2]
|
|
975
|
+
const newname = req[3]
|
|
976
|
+
dbg_log(
|
|
977
|
+
'[renameat]: oldname=' + oldname + ' newname=' + newname,
|
|
978
|
+
LOG_9P,
|
|
979
|
+
)
|
|
980
|
+
const ret = await this.fs.Rename(
|
|
981
|
+
this.fids[olddirfid].inodeid,
|
|
982
|
+
oldname,
|
|
983
|
+
this.fids[newdirfid].inodeid,
|
|
984
|
+
newname,
|
|
985
|
+
)
|
|
986
|
+
if (ret < 0) {
|
|
987
|
+
let error_message = ''
|
|
988
|
+
if (ret === -ENOENT)
|
|
989
|
+
error_message = 'No such file or directory'
|
|
990
|
+
else if (ret === -EPERM)
|
|
991
|
+
error_message = 'Operation not permitted'
|
|
992
|
+
else if (ret === -ENOTEMPTY)
|
|
993
|
+
error_message = 'Directory not empty'
|
|
994
|
+
else {
|
|
995
|
+
error_message = 'Unknown error: ' + -ret
|
|
996
|
+
dbg_assert(
|
|
997
|
+
false,
|
|
998
|
+
'[renameat]: Unexpected error code: ' + -ret,
|
|
999
|
+
)
|
|
1000
|
+
}
|
|
1001
|
+
this.SendError(tag, error_message, -ret)
|
|
1002
|
+
this.SendReply(bufchain)
|
|
1003
|
+
break
|
|
1004
|
+
}
|
|
1005
|
+
if (TRACK_FILENAMES) {
|
|
1006
|
+
const newidx = this.fs.Search(
|
|
1007
|
+
this.fids[newdirfid].inodeid,
|
|
1008
|
+
newname,
|
|
1009
|
+
)
|
|
1010
|
+
this.update_dbg_name(newidx, newname)
|
|
1011
|
+
}
|
|
1012
|
+
this.BuildReply(id, tag, 0)
|
|
1013
|
+
this.SendReply(bufchain)
|
|
1014
|
+
break
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
case 76: {
|
|
1018
|
+
// TUNLINKAT
|
|
1019
|
+
const req = marshall.Unmarshall(['w', 's', 'w'], buffer, state)
|
|
1020
|
+
const dirfd = req[0]
|
|
1021
|
+
const name = req[1]
|
|
1022
|
+
const flags = req[2]
|
|
1023
|
+
dbg_log(
|
|
1024
|
+
'[unlink]: dirfd=' +
|
|
1025
|
+
dirfd +
|
|
1026
|
+
' name=' +
|
|
1027
|
+
name +
|
|
1028
|
+
' flags=' +
|
|
1029
|
+
flags,
|
|
1030
|
+
LOG_9P,
|
|
1031
|
+
)
|
|
1032
|
+
const fid_search = this.fs.Search(
|
|
1033
|
+
this.fids[dirfd].inodeid,
|
|
1034
|
+
name,
|
|
1035
|
+
)
|
|
1036
|
+
if (fid_search === -1) {
|
|
1037
|
+
this.SendError(tag, 'No such file or directory', ENOENT)
|
|
1038
|
+
this.SendReply(bufchain)
|
|
1039
|
+
break
|
|
1040
|
+
}
|
|
1041
|
+
const ret = this.fs.Unlink(this.fids[dirfd].inodeid, name)
|
|
1042
|
+
if (ret < 0) {
|
|
1043
|
+
let error_message = ''
|
|
1044
|
+
if (ret === -ENOTEMPTY)
|
|
1045
|
+
error_message = 'Directory not empty'
|
|
1046
|
+
else if (ret === -EPERM)
|
|
1047
|
+
error_message = 'Operation not permitted'
|
|
1048
|
+
else {
|
|
1049
|
+
error_message = 'Unknown error: ' + -ret
|
|
1050
|
+
dbg_assert(
|
|
1051
|
+
false,
|
|
1052
|
+
'[unlink]: Unexpected error code: ' + -ret,
|
|
1053
|
+
)
|
|
1054
|
+
}
|
|
1055
|
+
this.SendError(tag, error_message, -ret)
|
|
1056
|
+
this.SendReply(bufchain)
|
|
1057
|
+
break
|
|
1058
|
+
}
|
|
1059
|
+
this.BuildReply(id, tag, 0)
|
|
1060
|
+
this.SendReply(bufchain)
|
|
1061
|
+
break
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
case 100: {
|
|
1065
|
+
// version
|
|
1066
|
+
const version = marshall.Unmarshall(['w', 's'], buffer, state)
|
|
1067
|
+
dbg_log(
|
|
1068
|
+
'[version]: msize=' + version[0] + ' version=' + version[1],
|
|
1069
|
+
LOG_9P,
|
|
1070
|
+
)
|
|
1071
|
+
if (this.msize !== version[0]) {
|
|
1072
|
+
this.msize = version[0]
|
|
1073
|
+
this.replybuffer = new Uint8Array(
|
|
1074
|
+
Math.min(MAX_REPLYBUFFER_SIZE, this.msize * 2),
|
|
1075
|
+
)
|
|
1076
|
+
}
|
|
1077
|
+
size = marshall.Marshall(
|
|
1078
|
+
['w', 's'],
|
|
1079
|
+
[this.msize, this.VERSION],
|
|
1080
|
+
this.replybuffer,
|
|
1081
|
+
7,
|
|
1082
|
+
)
|
|
1083
|
+
this.BuildReply(id, tag, size)
|
|
1084
|
+
this.SendReply(bufchain)
|
|
1085
|
+
break
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
case 104: {
|
|
1089
|
+
// attach
|
|
1090
|
+
// return root directorie's QID
|
|
1091
|
+
const req = marshall.Unmarshall(
|
|
1092
|
+
['w', 'w', 's', 's', 'w'],
|
|
1093
|
+
buffer,
|
|
1094
|
+
state,
|
|
1095
|
+
)
|
|
1096
|
+
const fid = req[0]
|
|
1097
|
+
const uid = req[4]
|
|
1098
|
+
dbg_log(
|
|
1099
|
+
'[attach]: fid=' +
|
|
1100
|
+
fid +
|
|
1101
|
+
' afid=' +
|
|
1102
|
+
h(req[1]) +
|
|
1103
|
+
' uname=' +
|
|
1104
|
+
req[2] +
|
|
1105
|
+
' aname=' +
|
|
1106
|
+
req[3],
|
|
1107
|
+
LOG_9P,
|
|
1108
|
+
)
|
|
1109
|
+
this.fids[fid] = this.Createfid(0, FID_INODE, uid, '')
|
|
1110
|
+
const inode = this.fs.GetInode(this.fids[fid].inodeid)
|
|
1111
|
+
marshall.Marshall(['Q'], [inode.qid], this.replybuffer, 7)
|
|
1112
|
+
this.BuildReply(id, tag, 13)
|
|
1113
|
+
this.SendReply(bufchain)
|
|
1114
|
+
this.bus.send('9p-attach')
|
|
1115
|
+
break
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
case 108: {
|
|
1119
|
+
// tflush
|
|
1120
|
+
const req = marshall.Unmarshall(['h'], buffer, state)
|
|
1121
|
+
const _oldtag = req[0]
|
|
1122
|
+
dbg_log('[flush] ' + tag, LOG_9P)
|
|
1123
|
+
this.BuildReply(id, tag, 0)
|
|
1124
|
+
this.SendReply(bufchain)
|
|
1125
|
+
break
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
case 110: {
|
|
1129
|
+
// walk
|
|
1130
|
+
const req = marshall.Unmarshall(['w', 'w', 'h'], buffer, state)
|
|
1131
|
+
const fid = req[0]
|
|
1132
|
+
const nwfid = req[1]
|
|
1133
|
+
const nwname = req[2]
|
|
1134
|
+
dbg_log(
|
|
1135
|
+
'[walk]: fid=' +
|
|
1136
|
+
req[0] +
|
|
1137
|
+
' nwfid=' +
|
|
1138
|
+
req[1] +
|
|
1139
|
+
' nwname=' +
|
|
1140
|
+
nwname,
|
|
1141
|
+
LOG_9P,
|
|
1142
|
+
)
|
|
1143
|
+
if (nwname === 0) {
|
|
1144
|
+
this.fids[nwfid] = this.Createfid(
|
|
1145
|
+
this.fids[fid].inodeid,
|
|
1146
|
+
FID_INODE,
|
|
1147
|
+
this.fids[fid].uid,
|
|
1148
|
+
this.fids[fid].dbg_name,
|
|
1149
|
+
)
|
|
1150
|
+
marshall.Marshall(['h'], [0], this.replybuffer, 7)
|
|
1151
|
+
this.BuildReply(id, tag, 2)
|
|
1152
|
+
this.SendReply(bufchain)
|
|
1153
|
+
break
|
|
1154
|
+
}
|
|
1155
|
+
const wnames: string[] = []
|
|
1156
|
+
for (let i = 0; i < nwname; i++) {
|
|
1157
|
+
wnames.push('s')
|
|
1158
|
+
}
|
|
1159
|
+
const walk = marshall.Unmarshall(
|
|
1160
|
+
wnames as marshall.MarshallTypeCode[],
|
|
1161
|
+
buffer,
|
|
1162
|
+
state,
|
|
1163
|
+
)
|
|
1164
|
+
let idx = this.fids[fid].inodeid
|
|
1165
|
+
let offset = 7 + 2
|
|
1166
|
+
let nwidx = 0
|
|
1167
|
+
dbg_log(
|
|
1168
|
+
'walk in dir ' +
|
|
1169
|
+
this.fids[fid].dbg_name +
|
|
1170
|
+
' to: ' +
|
|
1171
|
+
walk.toString(),
|
|
1172
|
+
LOG_9P,
|
|
1173
|
+
)
|
|
1174
|
+
for (let i = 0; i < nwname; i++) {
|
|
1175
|
+
idx = this.fs.Search(idx, walk[i])
|
|
1176
|
+
|
|
1177
|
+
if (idx === -1) {
|
|
1178
|
+
dbg_log('Could not find: ' + walk[i], LOG_9P)
|
|
1179
|
+
break
|
|
1180
|
+
}
|
|
1181
|
+
offset += marshall.Marshall(
|
|
1182
|
+
['Q'],
|
|
1183
|
+
[this.fs.GetInode(idx).qid],
|
|
1184
|
+
this.replybuffer,
|
|
1185
|
+
offset,
|
|
1186
|
+
)
|
|
1187
|
+
nwidx++
|
|
1188
|
+
this.fids[nwfid] = this.Createfid(
|
|
1189
|
+
idx,
|
|
1190
|
+
FID_INODE,
|
|
1191
|
+
this.fids[fid].uid,
|
|
1192
|
+
walk[i],
|
|
1193
|
+
)
|
|
1194
|
+
}
|
|
1195
|
+
marshall.Marshall(['h'], [nwidx], this.replybuffer, 7)
|
|
1196
|
+
this.BuildReply(id, tag, offset - 7)
|
|
1197
|
+
this.SendReply(bufchain)
|
|
1198
|
+
break
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
case 120: {
|
|
1202
|
+
// clunk
|
|
1203
|
+
const req = marshall.Unmarshall(['w'], buffer, state)
|
|
1204
|
+
dbg_log('[clunk]: fid=' + req[0], LOG_9P)
|
|
1205
|
+
if (this.fids[req[0]] && this.fids[req[0]].inodeid >= 0) {
|
|
1206
|
+
await this.fs.CloseInode(this.fids[req[0]].inodeid)
|
|
1207
|
+
this.fids[req[0]].inodeid = -1
|
|
1208
|
+
this.fids[req[0]].type = FID_NONE
|
|
1209
|
+
}
|
|
1210
|
+
this.BuildReply(id, tag, 0)
|
|
1211
|
+
this.SendReply(bufchain)
|
|
1212
|
+
break
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
case 32: {
|
|
1216
|
+
// txattrcreate
|
|
1217
|
+
const req = marshall.Unmarshall(
|
|
1218
|
+
['w', 's', 'd', 'w'],
|
|
1219
|
+
buffer,
|
|
1220
|
+
state,
|
|
1221
|
+
)
|
|
1222
|
+
const fid = req[0]
|
|
1223
|
+
const name = req[1]
|
|
1224
|
+
const attr_size = req[2]
|
|
1225
|
+
const flags = req[3]
|
|
1226
|
+
dbg_log(
|
|
1227
|
+
'[txattrcreate]: fid=' +
|
|
1228
|
+
fid +
|
|
1229
|
+
' name=' +
|
|
1230
|
+
name +
|
|
1231
|
+
' attr_size=' +
|
|
1232
|
+
attr_size +
|
|
1233
|
+
' flags=' +
|
|
1234
|
+
flags,
|
|
1235
|
+
LOG_9P,
|
|
1236
|
+
)
|
|
1237
|
+
|
|
1238
|
+
// XXX: xattr not supported yet. E.g. checks corresponding to the flags needed.
|
|
1239
|
+
this.fids[fid].type = FID_XATTR
|
|
1240
|
+
|
|
1241
|
+
this.BuildReply(id, tag, 0)
|
|
1242
|
+
this.SendReply(bufchain)
|
|
1243
|
+
break
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
case 30: {
|
|
1247
|
+
// xattrwalk
|
|
1248
|
+
const req = marshall.Unmarshall(['w', 'w', 's'], buffer, state)
|
|
1249
|
+
const _fid = req[0]
|
|
1250
|
+
const _newfid = req[1]
|
|
1251
|
+
const _name = req[2]
|
|
1252
|
+
dbg_log(
|
|
1253
|
+
'[xattrwalk]: fid=' +
|
|
1254
|
+
req[0] +
|
|
1255
|
+
' newfid=' +
|
|
1256
|
+
req[1] +
|
|
1257
|
+
' name=' +
|
|
1258
|
+
req[2],
|
|
1259
|
+
LOG_9P,
|
|
1260
|
+
)
|
|
1261
|
+
|
|
1262
|
+
// Workaround for Linux restarts writes until full blocksize
|
|
1263
|
+
this.SendError(tag, 'Setxattr not supported', EOPNOTSUPP)
|
|
1264
|
+
this.SendReply(bufchain)
|
|
1265
|
+
break
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
default:
|
|
1269
|
+
dbg_log(
|
|
1270
|
+
'Error in Virtio9p: Unknown id ' + id + ' received',
|
|
1271
|
+
LOG_9P,
|
|
1272
|
+
)
|
|
1273
|
+
dbg_assert(false)
|
|
1274
|
+
break
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
export class Virtio9pHandler {
|
|
1280
|
+
handle_fn: P9Handler
|
|
1281
|
+
tag_bufchain: Map<number, VirtQueueBufferChain>
|
|
1282
|
+
configspace_tagname: number[]
|
|
1283
|
+
configspace_taglen: number
|
|
1284
|
+
virtio: VirtIO
|
|
1285
|
+
virtqueue: VirtQueue
|
|
1286
|
+
|
|
1287
|
+
constructor(handle_fn: P9Handler, cpu: CPU) {
|
|
1288
|
+
this.handle_fn = handle_fn
|
|
1289
|
+
this.tag_bufchain = new Map()
|
|
1290
|
+
|
|
1291
|
+
this.configspace_tagname = [0x68, 0x6f, 0x73, 0x74, 0x39, 0x70] // "host9p" string
|
|
1292
|
+
this.configspace_taglen = this.configspace_tagname.length // num bytes
|
|
1293
|
+
|
|
1294
|
+
this.virtio = init_virtio(
|
|
1295
|
+
cpu,
|
|
1296
|
+
this.configspace_taglen,
|
|
1297
|
+
this.configspace_tagname,
|
|
1298
|
+
async (bufchain: VirtQueueBufferChain) => {
|
|
1299
|
+
// TODO: split into header + data blobs to avoid unnecessary copying.
|
|
1300
|
+
const reqbuf = new Uint8Array(bufchain.length_readable)
|
|
1301
|
+
bufchain.get_next_blob(reqbuf)
|
|
1302
|
+
|
|
1303
|
+
const reqheader = marshall.Unmarshall(['w', 'b', 'h'], reqbuf, {
|
|
1304
|
+
offset: 0,
|
|
1305
|
+
})
|
|
1306
|
+
const reqtag = reqheader[2]
|
|
1307
|
+
|
|
1308
|
+
this.tag_bufchain.set(reqtag, bufchain)
|
|
1309
|
+
this.handle_fn(reqbuf, (replybuf: Uint8Array) => {
|
|
1310
|
+
const replyheader = marshall.Unmarshall(
|
|
1311
|
+
['w', 'b', 'h'],
|
|
1312
|
+
replybuf,
|
|
1313
|
+
{ offset: 0 },
|
|
1314
|
+
)
|
|
1315
|
+
const replytag = replyheader[2]
|
|
1316
|
+
|
|
1317
|
+
const bufchain = this.tag_bufchain.get(replytag)
|
|
1318
|
+
if (!bufchain) {
|
|
1319
|
+
console.error('No bufchain found for tag: ' + replytag)
|
|
1320
|
+
return
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
bufchain.set_next_blob(replybuf)
|
|
1324
|
+
this.virtqueue.push_reply(bufchain)
|
|
1325
|
+
this.virtqueue.flush_replies()
|
|
1326
|
+
|
|
1327
|
+
this.tag_bufchain.delete(replytag)
|
|
1328
|
+
})
|
|
1329
|
+
},
|
|
1330
|
+
)
|
|
1331
|
+
this.virtqueue = this.virtio.queues[0]
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
get_state(): any[] {
|
|
1335
|
+
const state: any[] = []
|
|
1336
|
+
|
|
1337
|
+
state[0] = this.configspace_tagname
|
|
1338
|
+
state[1] = this.configspace_taglen
|
|
1339
|
+
state[2] = this.virtio
|
|
1340
|
+
state[3] = this.tag_bufchain
|
|
1341
|
+
|
|
1342
|
+
return state
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
set_state(state: any[]): void {
|
|
1346
|
+
this.configspace_tagname = state[0]
|
|
1347
|
+
this.configspace_taglen = state[1]
|
|
1348
|
+
this.virtio.set_state(state[2])
|
|
1349
|
+
this.virtqueue = this.virtio.queues[0]
|
|
1350
|
+
this.tag_bufchain = state[3]
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
reset(): void {
|
|
1354
|
+
this.virtio.reset()
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
export class Virtio9pProxy {
|
|
1359
|
+
socket: WebSocket | undefined
|
|
1360
|
+
cpu: CPU
|
|
1361
|
+
send_queue: Uint8Array[]
|
|
1362
|
+
url: string
|
|
1363
|
+
reconnect_interval: number
|
|
1364
|
+
last_connect_attempt: number
|
|
1365
|
+
send_queue_limit: number
|
|
1366
|
+
destroyed: boolean
|
|
1367
|
+
tag_bufchain: Map<number, VirtQueueBufferChain>
|
|
1368
|
+
configspace_tagname: number[]
|
|
1369
|
+
configspace_taglen: number
|
|
1370
|
+
virtio: VirtIO
|
|
1371
|
+
virtqueue: VirtQueue
|
|
1372
|
+
|
|
1373
|
+
constructor(url: string, cpu: CPU) {
|
|
1374
|
+
this.socket = undefined
|
|
1375
|
+
this.cpu = cpu
|
|
1376
|
+
|
|
1377
|
+
// TODO: circular buffer?
|
|
1378
|
+
this.send_queue = []
|
|
1379
|
+
this.url = url
|
|
1380
|
+
|
|
1381
|
+
this.reconnect_interval = 10000
|
|
1382
|
+
this.last_connect_attempt = Date.now() - this.reconnect_interval
|
|
1383
|
+
this.send_queue_limit = 64
|
|
1384
|
+
this.destroyed = false
|
|
1385
|
+
|
|
1386
|
+
this.tag_bufchain = new Map()
|
|
1387
|
+
|
|
1388
|
+
this.configspace_tagname = [0x68, 0x6f, 0x73, 0x74, 0x39, 0x70] // "host9p" string
|
|
1389
|
+
this.configspace_taglen = this.configspace_tagname.length // num bytes
|
|
1390
|
+
|
|
1391
|
+
this.virtio = init_virtio(
|
|
1392
|
+
cpu,
|
|
1393
|
+
this.configspace_taglen,
|
|
1394
|
+
this.configspace_tagname,
|
|
1395
|
+
async (bufchain: VirtQueueBufferChain) => {
|
|
1396
|
+
// TODO: split into header + data blobs to avoid unnecessary copying.
|
|
1397
|
+
const reqbuf = new Uint8Array(bufchain.length_readable)
|
|
1398
|
+
bufchain.get_next_blob(reqbuf)
|
|
1399
|
+
|
|
1400
|
+
const reqheader = marshall.Unmarshall(['w', 'b', 'h'], reqbuf, {
|
|
1401
|
+
offset: 0,
|
|
1402
|
+
})
|
|
1403
|
+
const reqtag = reqheader[2]
|
|
1404
|
+
|
|
1405
|
+
this.tag_bufchain.set(reqtag, bufchain)
|
|
1406
|
+
this.send(reqbuf)
|
|
1407
|
+
},
|
|
1408
|
+
)
|
|
1409
|
+
this.virtqueue = this.virtio.queues[0]
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
get_state(): any[] {
|
|
1413
|
+
const state: any[] = []
|
|
1414
|
+
|
|
1415
|
+
state[0] = this.configspace_tagname
|
|
1416
|
+
state[1] = this.configspace_taglen
|
|
1417
|
+
state[2] = this.virtio
|
|
1418
|
+
state[3] = this.tag_bufchain
|
|
1419
|
+
|
|
1420
|
+
return state
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
set_state(state: any[]): void {
|
|
1424
|
+
this.configspace_tagname = state[0]
|
|
1425
|
+
this.configspace_taglen = state[1]
|
|
1426
|
+
this.virtio.set_state(state[2])
|
|
1427
|
+
this.virtqueue = this.virtio.queues[0]
|
|
1428
|
+
this.tag_bufchain = state[3]
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
reset(): void {
|
|
1432
|
+
this.virtio.reset()
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
handle_message(e: MessageEvent): void {
|
|
1436
|
+
const replybuf = new Uint8Array(e.data)
|
|
1437
|
+
const replyheader = marshall.Unmarshall(['w', 'b', 'h'], replybuf, {
|
|
1438
|
+
offset: 0,
|
|
1439
|
+
})
|
|
1440
|
+
const replytag = replyheader[2]
|
|
1441
|
+
|
|
1442
|
+
const bufchain = this.tag_bufchain.get(replytag)
|
|
1443
|
+
if (!bufchain) {
|
|
1444
|
+
console.error(
|
|
1445
|
+
'Virtio9pProxy: No bufchain found for tag: ' + replytag,
|
|
1446
|
+
)
|
|
1447
|
+
return
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
bufchain.set_next_blob(replybuf)
|
|
1451
|
+
this.virtqueue.push_reply(bufchain)
|
|
1452
|
+
this.virtqueue.flush_replies()
|
|
1453
|
+
|
|
1454
|
+
this.tag_bufchain.delete(replytag)
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
handle_close(_e: CloseEvent): void {
|
|
1458
|
+
if (!this.destroyed) {
|
|
1459
|
+
this.connect()
|
|
1460
|
+
setTimeout(this.connect.bind(this), this.reconnect_interval)
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
handle_open(_e: Event): void {
|
|
1465
|
+
for (let i = 0; i < this.send_queue.length; i++) {
|
|
1466
|
+
this.send(this.send_queue[i])
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
this.send_queue = []
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
handle_error(_e: Event): void {
|
|
1473
|
+
//console.log("onerror", e);
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
destroy(): void {
|
|
1477
|
+
this.destroyed = true
|
|
1478
|
+
if (this.socket) {
|
|
1479
|
+
this.socket.close()
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
connect(): void {
|
|
1484
|
+
if (typeof WebSocket === 'undefined') {
|
|
1485
|
+
return
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
if (this.socket) {
|
|
1489
|
+
const state = this.socket.readyState
|
|
1490
|
+
|
|
1491
|
+
if (state === 0 || state === 1) {
|
|
1492
|
+
// already or almost there
|
|
1493
|
+
return
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
const now = Date.now()
|
|
1498
|
+
|
|
1499
|
+
if (this.last_connect_attempt + this.reconnect_interval > now) {
|
|
1500
|
+
return
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
this.last_connect_attempt = Date.now()
|
|
1504
|
+
|
|
1505
|
+
try {
|
|
1506
|
+
this.socket = new WebSocket(this.url)
|
|
1507
|
+
} catch (e) {
|
|
1508
|
+
console.error(e)
|
|
1509
|
+
return
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
this.socket.binaryType = 'arraybuffer'
|
|
1513
|
+
|
|
1514
|
+
this.socket.onopen = this.handle_open.bind(this)
|
|
1515
|
+
this.socket.onmessage = this.handle_message.bind(this)
|
|
1516
|
+
this.socket.onclose = this.handle_close.bind(this)
|
|
1517
|
+
this.socket.onerror = this.handle_error.bind(this)
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
send(data: Uint8Array): void {
|
|
1521
|
+
if (!this.socket || this.socket.readyState !== 1) {
|
|
1522
|
+
this.send_queue.push(data)
|
|
1523
|
+
|
|
1524
|
+
if (this.send_queue.length > 2 * this.send_queue_limit) {
|
|
1525
|
+
this.send_queue = this.send_queue.slice(-this.send_queue_limit)
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
this.connect()
|
|
1529
|
+
} else {
|
|
1530
|
+
// Copy into a plain ArrayBuffer for WebSocket.send() compatibility
|
|
1531
|
+
const buf = new ArrayBuffer(data.byteLength)
|
|
1532
|
+
new Uint8Array(buf).set(data)
|
|
1533
|
+
this.socket.send(buf)
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
change_proxy(url: string): void {
|
|
1538
|
+
this.url = url
|
|
1539
|
+
|
|
1540
|
+
if (this.socket) {
|
|
1541
|
+
this.socket.onclose = function () {}
|
|
1542
|
+
this.socket.onerror = function () {}
|
|
1543
|
+
this.socket.close()
|
|
1544
|
+
this.socket = undefined
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
}
|