]>
Commit | Line | Data |
---|---|---|
9f107513 AL |
1 | /* |
2 | * Virtio 9p backend | |
3 | * | |
4 | * Copyright IBM, Corp. 2010 | |
5 | * | |
6 | * Authors: | |
7 | * Anthony Liguori <aliguori@us.ibm.com> | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2. See | |
10 | * the COPYING file in the top-level directory. | |
11 | * | |
12 | */ | |
13 | ||
14 | #include "virtio.h" | |
15 | #include "pc.h" | |
16 | #include "qemu_socket.h" | |
17 | #include "virtio-9p.h" | |
18 | #include "fsdev/qemu-fsdev.h" | |
19 | #include "virtio-9p-debug.h" | |
20 | ||
21 | int dotu = 1; | |
22 | int debug_9p_pdu; | |
23 | ||
a03f7874 AL |
24 | static void v9fs_string_init(V9fsString *str) |
25 | { | |
26 | str->data = NULL; | |
27 | str->size = 0; | |
28 | } | |
29 | ||
30 | static void v9fs_string_free(V9fsString *str) | |
31 | { | |
32 | qemu_free(str->data); | |
33 | str->data = NULL; | |
34 | str->size = 0; | |
35 | } | |
36 | ||
37 | static void v9fs_string_null(V9fsString *str) | |
38 | { | |
39 | v9fs_string_free(str); | |
40 | } | |
41 | ||
42 | static int number_to_string(void *arg, char type) | |
43 | { | |
44 | unsigned int ret = 0; | |
45 | ||
46 | switch (type) { | |
47 | case 'u': { | |
48 | unsigned int num = *(unsigned int *)arg; | |
49 | ||
50 | do { | |
51 | ret++; | |
52 | num = num/10; | |
53 | } while (num); | |
54 | break; | |
55 | } | |
56 | default: | |
57 | printf("Number_to_string: Unknown number format\n"); | |
58 | return -1; | |
59 | } | |
60 | ||
61 | return ret; | |
62 | } | |
63 | ||
64 | static int v9fs_string_alloc_printf(char **strp, const char *fmt, va_list ap) | |
65 | { | |
66 | va_list ap2; | |
67 | char *iter = (char *)fmt; | |
68 | int len = 0; | |
69 | int nr_args = 0; | |
70 | char *arg_char_ptr; | |
71 | unsigned int arg_uint; | |
72 | ||
73 | /* Find the number of %'s that denotes an argument */ | |
74 | for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) { | |
75 | nr_args++; | |
76 | iter++; | |
77 | } | |
78 | ||
79 | len = strlen(fmt) - 2*nr_args; | |
80 | ||
81 | if (!nr_args) { | |
82 | goto alloc_print; | |
83 | } | |
84 | ||
85 | va_copy(ap2, ap); | |
86 | ||
87 | iter = (char *)fmt; | |
88 | ||
89 | /* Now parse the format string */ | |
90 | for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) { | |
91 | iter++; | |
92 | switch (*iter) { | |
93 | case 'u': | |
94 | arg_uint = va_arg(ap2, unsigned int); | |
95 | len += number_to_string((void *)&arg_uint, 'u'); | |
96 | break; | |
97 | case 's': | |
98 | arg_char_ptr = va_arg(ap2, char *); | |
99 | len += strlen(arg_char_ptr); | |
100 | break; | |
101 | case 'c': | |
102 | len += 1; | |
103 | break; | |
104 | default: | |
105 | fprintf(stderr, | |
106 | "v9fs_string_alloc_printf:Incorrect format %c", *iter); | |
107 | return -1; | |
108 | } | |
109 | iter++; | |
110 | } | |
111 | ||
112 | alloc_print: | |
113 | *strp = qemu_malloc((len + 1) * sizeof(**strp)); | |
114 | ||
115 | return vsprintf(*strp, fmt, ap); | |
116 | } | |
117 | ||
118 | static void v9fs_string_sprintf(V9fsString *str, const char *fmt, ...) | |
119 | { | |
120 | va_list ap; | |
121 | int err; | |
122 | ||
123 | v9fs_string_free(str); | |
124 | ||
125 | va_start(ap, fmt); | |
126 | err = v9fs_string_alloc_printf(&str->data, fmt, ap); | |
127 | BUG_ON(err == -1); | |
128 | va_end(ap); | |
129 | ||
130 | str->size = err; | |
131 | } | |
132 | ||
133 | static void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs) | |
134 | { | |
135 | v9fs_string_free(lhs); | |
136 | v9fs_string_sprintf(lhs, "%s", rhs->data); | |
137 | } | |
138 | ||
139 | static size_t v9fs_string_size(V9fsString *str) | |
140 | { | |
141 | return str->size; | |
142 | } | |
143 | ||
9f107513 AL |
144 | static V9fsPDU *alloc_pdu(V9fsState *s) |
145 | { | |
146 | V9fsPDU *pdu = NULL; | |
147 | ||
148 | if (!QLIST_EMPTY(&s->free_list)) { | |
149 | pdu = QLIST_FIRST(&s->free_list); | |
150 | QLIST_REMOVE(pdu, next); | |
151 | } | |
152 | return pdu; | |
153 | } | |
154 | ||
155 | static void free_pdu(V9fsState *s, V9fsPDU *pdu) | |
156 | { | |
157 | if (pdu) { | |
158 | QLIST_INSERT_HEAD(&s->free_list, pdu, next); | |
159 | } | |
160 | } | |
161 | ||
162 | size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count, | |
163 | size_t offset, size_t size, int pack) | |
164 | { | |
165 | int i = 0; | |
166 | size_t copied = 0; | |
167 | ||
168 | for (i = 0; size && i < sg_count; i++) { | |
169 | size_t len; | |
170 | if (offset >= sg[i].iov_len) { | |
171 | /* skip this sg */ | |
172 | offset -= sg[i].iov_len; | |
173 | continue; | |
174 | } else { | |
175 | len = MIN(sg[i].iov_len - offset, size); | |
176 | if (pack) { | |
177 | memcpy(sg[i].iov_base + offset, addr, len); | |
178 | } else { | |
179 | memcpy(addr, sg[i].iov_base + offset, len); | |
180 | } | |
181 | size -= len; | |
182 | copied += len; | |
183 | addr += len; | |
184 | if (size) { | |
185 | offset = 0; | |
186 | continue; | |
187 | } | |
188 | } | |
189 | } | |
190 | ||
191 | return copied; | |
192 | } | |
193 | ||
405a549a AL |
194 | static size_t pdu_unpack(void *dst, V9fsPDU *pdu, size_t offset, size_t size) |
195 | { | |
196 | return pdu_packunpack(dst, pdu->elem.out_sg, pdu->elem.out_num, | |
197 | offset, size, 0); | |
198 | } | |
199 | ||
200 | static size_t pdu_pack(V9fsPDU *pdu, size_t offset, const void *src, | |
201 | size_t size) | |
202 | { | |
203 | return pdu_packunpack((void *)src, pdu->elem.in_sg, pdu->elem.in_num, | |
204 | offset, size, 1); | |
205 | } | |
206 | ||
207 | static int pdu_copy_sg(V9fsPDU *pdu, size_t offset, int rx, struct iovec *sg) | |
208 | { | |
209 | size_t pos = 0; | |
210 | int i, j; | |
211 | struct iovec *src_sg; | |
212 | unsigned int num; | |
213 | ||
214 | if (rx) { | |
215 | src_sg = pdu->elem.in_sg; | |
216 | num = pdu->elem.in_num; | |
217 | } else { | |
218 | src_sg = pdu->elem.out_sg; | |
219 | num = pdu->elem.out_num; | |
220 | } | |
221 | ||
222 | j = 0; | |
223 | for (i = 0; i < num; i++) { | |
224 | if (offset <= pos) { | |
225 | sg[j].iov_base = src_sg[i].iov_base; | |
226 | sg[j].iov_len = src_sg[i].iov_len; | |
227 | j++; | |
228 | } else if (offset < (src_sg[i].iov_len + pos)) { | |
229 | sg[j].iov_base = src_sg[i].iov_base; | |
230 | sg[j].iov_len = src_sg[i].iov_len; | |
231 | sg[j].iov_base += (offset - pos); | |
232 | sg[j].iov_len -= (offset - pos); | |
233 | j++; | |
234 | } | |
235 | pos += src_sg[i].iov_len; | |
236 | } | |
237 | ||
238 | return j; | |
239 | } | |
240 | ||
241 | static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) | |
242 | { | |
243 | size_t old_offset = offset; | |
244 | va_list ap; | |
245 | int i; | |
246 | ||
247 | va_start(ap, fmt); | |
248 | for (i = 0; fmt[i]; i++) { | |
249 | switch (fmt[i]) { | |
250 | case 'b': { | |
251 | uint8_t *valp = va_arg(ap, uint8_t *); | |
252 | offset += pdu_unpack(valp, pdu, offset, sizeof(*valp)); | |
253 | break; | |
254 | } | |
255 | case 'w': { | |
256 | uint16_t val, *valp; | |
257 | valp = va_arg(ap, uint16_t *); | |
258 | val = le16_to_cpupu(valp); | |
259 | offset += pdu_unpack(&val, pdu, offset, sizeof(val)); | |
260 | *valp = val; | |
261 | break; | |
262 | } | |
263 | case 'd': { | |
264 | uint32_t val, *valp; | |
265 | valp = va_arg(ap, uint32_t *); | |
266 | val = le32_to_cpupu(valp); | |
267 | offset += pdu_unpack(&val, pdu, offset, sizeof(val)); | |
268 | *valp = val; | |
269 | break; | |
270 | } | |
271 | case 'q': { | |
272 | uint64_t val, *valp; | |
273 | valp = va_arg(ap, uint64_t *); | |
274 | val = le64_to_cpup(valp); | |
275 | offset += pdu_unpack(&val, pdu, offset, sizeof(val)); | |
276 | *valp = val; | |
277 | break; | |
278 | } | |
279 | case 'v': { | |
280 | struct iovec *iov = va_arg(ap, struct iovec *); | |
281 | int *iovcnt = va_arg(ap, int *); | |
282 | *iovcnt = pdu_copy_sg(pdu, offset, 0, iov); | |
283 | break; | |
284 | } | |
285 | case 's': { | |
286 | V9fsString *str = va_arg(ap, V9fsString *); | |
287 | offset += pdu_unmarshal(pdu, offset, "w", &str->size); | |
288 | /* FIXME: sanity check str->size */ | |
289 | str->data = qemu_malloc(str->size + 1); | |
290 | offset += pdu_unpack(str->data, pdu, offset, str->size); | |
291 | str->data[str->size] = 0; | |
292 | break; | |
293 | } | |
294 | case 'Q': { | |
295 | V9fsQID *qidp = va_arg(ap, V9fsQID *); | |
296 | offset += pdu_unmarshal(pdu, offset, "bdq", | |
297 | &qidp->type, &qidp->version, &qidp->path); | |
298 | break; | |
299 | } | |
300 | case 'S': { | |
301 | V9fsStat *statp = va_arg(ap, V9fsStat *); | |
302 | offset += pdu_unmarshal(pdu, offset, "wwdQdddqsssssddd", | |
303 | &statp->size, &statp->type, &statp->dev, | |
304 | &statp->qid, &statp->mode, &statp->atime, | |
305 | &statp->mtime, &statp->length, | |
306 | &statp->name, &statp->uid, &statp->gid, | |
307 | &statp->muid, &statp->extension, | |
308 | &statp->n_uid, &statp->n_gid, | |
309 | &statp->n_muid); | |
310 | break; | |
311 | } | |
312 | default: | |
313 | break; | |
314 | } | |
315 | } | |
316 | ||
317 | va_end(ap); | |
318 | ||
319 | return offset - old_offset; | |
320 | } | |
321 | ||
322 | static size_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) | |
323 | { | |
324 | size_t old_offset = offset; | |
325 | va_list ap; | |
326 | int i; | |
327 | ||
328 | va_start(ap, fmt); | |
329 | for (i = 0; fmt[i]; i++) { | |
330 | switch (fmt[i]) { | |
331 | case 'b': { | |
332 | uint8_t val = va_arg(ap, int); | |
333 | offset += pdu_pack(pdu, offset, &val, sizeof(val)); | |
334 | break; | |
335 | } | |
336 | case 'w': { | |
337 | uint16_t val; | |
338 | cpu_to_le16w(&val, va_arg(ap, int)); | |
339 | offset += pdu_pack(pdu, offset, &val, sizeof(val)); | |
340 | break; | |
341 | } | |
342 | case 'd': { | |
343 | uint32_t val; | |
344 | cpu_to_le32w(&val, va_arg(ap, uint32_t)); | |
345 | offset += pdu_pack(pdu, offset, &val, sizeof(val)); | |
346 | break; | |
347 | } | |
348 | case 'q': { | |
349 | uint64_t val; | |
350 | cpu_to_le64w(&val, va_arg(ap, uint64_t)); | |
351 | offset += pdu_pack(pdu, offset, &val, sizeof(val)); | |
352 | break; | |
353 | } | |
354 | case 'v': { | |
355 | struct iovec *iov = va_arg(ap, struct iovec *); | |
356 | int *iovcnt = va_arg(ap, int *); | |
357 | *iovcnt = pdu_copy_sg(pdu, offset, 1, iov); | |
358 | break; | |
359 | } | |
360 | case 's': { | |
361 | V9fsString *str = va_arg(ap, V9fsString *); | |
362 | offset += pdu_marshal(pdu, offset, "w", str->size); | |
363 | offset += pdu_pack(pdu, offset, str->data, str->size); | |
364 | break; | |
365 | } | |
366 | case 'Q': { | |
367 | V9fsQID *qidp = va_arg(ap, V9fsQID *); | |
368 | offset += pdu_marshal(pdu, offset, "bdq", | |
369 | qidp->type, qidp->version, qidp->path); | |
370 | break; | |
371 | } | |
372 | case 'S': { | |
373 | V9fsStat *statp = va_arg(ap, V9fsStat *); | |
374 | offset += pdu_marshal(pdu, offset, "wwdQdddqsssssddd", | |
375 | statp->size, statp->type, statp->dev, | |
376 | &statp->qid, statp->mode, statp->atime, | |
377 | statp->mtime, statp->length, &statp->name, | |
378 | &statp->uid, &statp->gid, &statp->muid, | |
379 | &statp->extension, statp->n_uid, | |
380 | statp->n_gid, statp->n_muid); | |
381 | break; | |
382 | } | |
383 | default: | |
384 | break; | |
385 | } | |
386 | } | |
387 | va_end(ap); | |
388 | ||
389 | return offset - old_offset; | |
390 | } | |
391 | ||
392 | static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len) | |
393 | { | |
394 | int8_t id = pdu->id + 1; /* Response */ | |
395 | ||
396 | if (len < 0) { | |
397 | V9fsString str; | |
398 | int err = -len; | |
399 | ||
400 | str.data = strerror(err); | |
401 | str.size = strlen(str.data); | |
402 | ||
403 | len = 7; | |
404 | len += pdu_marshal(pdu, len, "s", &str); | |
405 | if (dotu) { | |
406 | len += pdu_marshal(pdu, len, "d", err); | |
407 | } | |
408 | ||
409 | id = P9_RERROR; | |
410 | } | |
411 | ||
412 | /* fill out the header */ | |
413 | pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag); | |
414 | ||
415 | /* keep these in sync */ | |
416 | pdu->size = len; | |
417 | pdu->id = id; | |
418 | ||
419 | /* push onto queue and notify */ | |
420 | virtqueue_push(s->vq, &pdu->elem, len); | |
421 | ||
422 | /* FIXME: we should batch these completions */ | |
423 | virtio_notify(&s->vdev, s->vq); | |
424 | ||
425 | free_pdu(s, pdu); | |
426 | } | |
427 | ||
428 | static void v9fs_dummy(V9fsState *s, V9fsPDU *pdu) | |
429 | { | |
430 | /* Note: The following have been added to prevent GCC from complaining | |
431 | * They will be removed in the subsequent patches */ | |
432 | (void)pdu_unmarshal; | |
433 | (void) complete_pdu; | |
a03f7874 AL |
434 | (void) v9fs_string_init; |
435 | (void) v9fs_string_free; | |
436 | (void) v9fs_string_null; | |
437 | (void) v9fs_string_sprintf; | |
438 | (void) v9fs_string_copy; | |
439 | (void) v9fs_string_size; | |
440 | ||
405a549a AL |
441 | |
442 | } | |
9f107513 AL |
443 | static void v9fs_version(V9fsState *s, V9fsPDU *pdu) |
444 | { | |
445 | if (debug_9p_pdu) { | |
446 | pprint_pdu(pdu); | |
447 | } | |
448 | } | |
449 | ||
450 | static void v9fs_attach(V9fsState *s, V9fsPDU *pdu) | |
451 | { | |
452 | if (debug_9p_pdu) { | |
453 | pprint_pdu(pdu); | |
454 | } | |
455 | } | |
456 | ||
457 | static void v9fs_stat(V9fsState *s, V9fsPDU *pdu) | |
458 | { | |
459 | if (debug_9p_pdu) { | |
460 | pprint_pdu(pdu); | |
461 | } | |
462 | } | |
463 | ||
464 | static void v9fs_walk(V9fsState *s, V9fsPDU *pdu) | |
465 | { | |
466 | if (debug_9p_pdu) { | |
467 | pprint_pdu(pdu); | |
468 | } | |
469 | } | |
470 | ||
471 | static void v9fs_clunk(V9fsState *s, V9fsPDU *pdu) | |
472 | { | |
473 | if (debug_9p_pdu) { | |
474 | pprint_pdu(pdu); | |
475 | } | |
476 | } | |
477 | ||
478 | static void v9fs_open(V9fsState *s, V9fsPDU *pdu) | |
479 | { if (debug_9p_pdu) { | |
480 | pprint_pdu(pdu); | |
481 | } | |
482 | } | |
483 | ||
484 | static void v9fs_read(V9fsState *s, V9fsPDU *pdu) | |
485 | { | |
486 | if (debug_9p_pdu) { | |
487 | pprint_pdu(pdu); | |
488 | } | |
489 | } | |
490 | ||
491 | static void v9fs_write(V9fsState *s, V9fsPDU *pdu) | |
492 | { | |
493 | if (debug_9p_pdu) { | |
494 | pprint_pdu(pdu); | |
495 | } | |
496 | } | |
497 | ||
498 | static void v9fs_create(V9fsState *s, V9fsPDU *pdu) | |
499 | { | |
500 | if (debug_9p_pdu) { | |
501 | pprint_pdu(pdu); | |
502 | } | |
503 | } | |
504 | ||
505 | static void v9fs_flush(V9fsState *s, V9fsPDU *pdu) | |
506 | { | |
405a549a | 507 | v9fs_dummy(s, pdu); |
9f107513 AL |
508 | if (debug_9p_pdu) { |
509 | pprint_pdu(pdu); | |
510 | } | |
511 | } | |
512 | ||
513 | static void v9fs_remove(V9fsState *s, V9fsPDU *pdu) | |
514 | { | |
515 | if (debug_9p_pdu) { | |
516 | pprint_pdu(pdu); | |
517 | } | |
518 | } | |
519 | ||
520 | static void v9fs_wstat(V9fsState *s, V9fsPDU *pdu) | |
521 | { | |
522 | if (debug_9p_pdu) { | |
523 | pprint_pdu(pdu); | |
524 | } | |
525 | } | |
526 | ||
527 | typedef void (pdu_handler_t)(V9fsState *s, V9fsPDU *pdu); | |
528 | ||
529 | static pdu_handler_t *pdu_handlers[] = { | |
530 | [P9_TVERSION] = v9fs_version, | |
531 | [P9_TATTACH] = v9fs_attach, | |
532 | [P9_TSTAT] = v9fs_stat, | |
533 | [P9_TWALK] = v9fs_walk, | |
534 | [P9_TCLUNK] = v9fs_clunk, | |
535 | [P9_TOPEN] = v9fs_open, | |
536 | [P9_TREAD] = v9fs_read, | |
537 | #if 0 | |
538 | [P9_TAUTH] = v9fs_auth, | |
539 | #endif | |
540 | [P9_TFLUSH] = v9fs_flush, | |
541 | [P9_TCREATE] = v9fs_create, | |
542 | [P9_TWRITE] = v9fs_write, | |
543 | [P9_TWSTAT] = v9fs_wstat, | |
544 | [P9_TREMOVE] = v9fs_remove, | |
545 | }; | |
546 | ||
547 | static void submit_pdu(V9fsState *s, V9fsPDU *pdu) | |
548 | { | |
549 | pdu_handler_t *handler; | |
550 | ||
551 | if (debug_9p_pdu) { | |
552 | pprint_pdu(pdu); | |
553 | } | |
554 | ||
555 | BUG_ON(pdu->id >= ARRAY_SIZE(pdu_handlers)); | |
556 | ||
557 | handler = pdu_handlers[pdu->id]; | |
558 | BUG_ON(handler == NULL); | |
559 | ||
560 | handler(s, pdu); | |
561 | } | |
562 | ||
563 | static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) | |
564 | { | |
565 | V9fsState *s = (V9fsState *)vdev; | |
566 | V9fsPDU *pdu; | |
567 | ssize_t len; | |
568 | ||
569 | while ((pdu = alloc_pdu(s)) && | |
570 | (len = virtqueue_pop(vq, &pdu->elem)) != 0) { | |
571 | uint8_t *ptr; | |
572 | ||
573 | BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0); | |
574 | BUG_ON(pdu->elem.out_sg[0].iov_len < 7); | |
575 | ||
576 | ptr = pdu->elem.out_sg[0].iov_base; | |
577 | ||
578 | memcpy(&pdu->size, ptr, 4); | |
579 | pdu->id = ptr[4]; | |
580 | memcpy(&pdu->tag, ptr + 5, 2); | |
581 | ||
582 | submit_pdu(s, pdu); | |
583 | } | |
584 | ||
585 | free_pdu(s, pdu); | |
586 | } | |
587 | ||
588 | static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features) | |
589 | { | |
590 | features |= 1 << VIRTIO_9P_MOUNT_TAG; | |
591 | return features; | |
592 | } | |
593 | ||
594 | static V9fsState *to_virtio_9p(VirtIODevice *vdev) | |
595 | { | |
596 | return (V9fsState *)vdev; | |
597 | } | |
598 | ||
599 | static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config) | |
600 | { | |
601 | struct virtio_9p_config *cfg; | |
602 | V9fsState *s = to_virtio_9p(vdev); | |
603 | ||
604 | cfg = qemu_mallocz(sizeof(struct virtio_9p_config) + | |
605 | s->tag_len); | |
606 | stw_raw(&cfg->tag_len, s->tag_len); | |
607 | memcpy(cfg->tag, s->tag, s->tag_len); | |
608 | memcpy(config, cfg, s->config_size); | |
609 | qemu_free(cfg); | |
610 | } | |
611 | ||
612 | VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf) | |
613 | { | |
614 | V9fsState *s; | |
615 | int i, len; | |
616 | struct stat stat; | |
617 | FsTypeEntry *fse; | |
618 | ||
619 | ||
620 | s = (V9fsState *)virtio_common_init("virtio-9p", | |
621 | VIRTIO_ID_9P, | |
622 | sizeof(struct virtio_9p_config)+ | |
623 | MAX_TAG_LEN, | |
624 | sizeof(V9fsState)); | |
625 | ||
626 | /* initialize pdu allocator */ | |
627 | QLIST_INIT(&s->free_list); | |
628 | for (i = 0; i < (MAX_REQ - 1); i++) { | |
629 | QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next); | |
630 | } | |
631 | ||
632 | s->vq = virtio_add_queue(&s->vdev, MAX_REQ, handle_9p_output); | |
633 | ||
634 | fse = get_fsdev_fsentry(conf->fsdev_id); | |
635 | ||
636 | if (!fse) { | |
637 | /* We don't have a fsdev identified by fsdev_id */ | |
638 | fprintf(stderr, "Virtio-9p device couldn't find fsdev " | |
639 | "with the id %s\n", conf->fsdev_id); | |
640 | exit(1); | |
641 | } | |
642 | ||
643 | if (!fse->path || !conf->tag) { | |
644 | /* we haven't specified a mount_tag or the path */ | |
645 | fprintf(stderr, "fsdev with id %s needs path " | |
646 | "and Virtio-9p device needs mount_tag arguments\n", | |
647 | conf->fsdev_id); | |
648 | exit(1); | |
649 | } | |
650 | ||
651 | if (lstat(fse->path, &stat)) { | |
652 | fprintf(stderr, "share path %s does not exist\n", fse->path); | |
653 | exit(1); | |
654 | } else if (!S_ISDIR(stat.st_mode)) { | |
655 | fprintf(stderr, "share path %s is not a directory \n", fse->path); | |
656 | exit(1); | |
657 | } | |
658 | ||
659 | s->ctx.fs_root = qemu_strdup(fse->path); | |
660 | len = strlen(conf->tag); | |
661 | if (len > MAX_TAG_LEN) { | |
662 | len = MAX_TAG_LEN; | |
663 | } | |
664 | /* s->tag is non-NULL terminated string */ | |
665 | s->tag = qemu_malloc(len); | |
666 | memcpy(s->tag, conf->tag, len); | |
667 | s->tag_len = len; | |
668 | s->ctx.uid = -1; | |
669 | ||
670 | s->ops = fse->ops; | |
671 | s->vdev.get_features = virtio_9p_get_features; | |
672 | s->config_size = sizeof(struct virtio_9p_config) + | |
673 | s->tag_len; | |
674 | s->vdev.get_config = virtio_9p_get_config; | |
675 | ||
676 | return &s->vdev; | |
677 | } |