4 * Copyright IBM, Corp. 2010
7 * Anthony Liguori <aliguori@us.ibm.com>
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
16 #include "qemu_socket.h"
17 #include "virtio-9p.h"
18 #include "fsdev/qemu-fsdev.h"
19 #include "virtio-9p-debug.h"
24 static void v9fs_string_init(V9fsString
*str
)
30 static void v9fs_string_free(V9fsString
*str
)
37 static void v9fs_string_null(V9fsString
*str
)
39 v9fs_string_free(str
);
42 static int number_to_string(void *arg
, char type
)
48 unsigned int num
= *(unsigned int *)arg
;
57 printf("Number_to_string: Unknown number format\n");
64 static int v9fs_string_alloc_printf(char **strp
, const char *fmt
, va_list ap
)
67 char *iter
= (char *)fmt
;
71 unsigned int arg_uint
;
73 /* Find the number of %'s that denotes an argument */
74 for (iter
= strstr(iter
, "%"); iter
; iter
= strstr(iter
, "%")) {
79 len
= strlen(fmt
) - 2*nr_args
;
89 /* Now parse the format string */
90 for (iter
= strstr(iter
, "%"); iter
; iter
= strstr(iter
, "%")) {
94 arg_uint
= va_arg(ap2
, unsigned int);
95 len
+= number_to_string((void *)&arg_uint
, 'u');
98 arg_char_ptr
= va_arg(ap2
, char *);
99 len
+= strlen(arg_char_ptr
);
106 "v9fs_string_alloc_printf:Incorrect format %c", *iter
);
113 *strp
= qemu_malloc((len
+ 1) * sizeof(**strp
));
115 return vsprintf(*strp
, fmt
, ap
);
118 static void v9fs_string_sprintf(V9fsString
*str
, const char *fmt
, ...)
123 v9fs_string_free(str
);
126 err
= v9fs_string_alloc_printf(&str
->data
, fmt
, ap
);
133 static void v9fs_string_copy(V9fsString
*lhs
, V9fsString
*rhs
)
135 v9fs_string_free(lhs
);
136 v9fs_string_sprintf(lhs
, "%s", rhs
->data
);
139 static size_t v9fs_string_size(V9fsString
*str
)
144 static V9fsPDU
*alloc_pdu(V9fsState
*s
)
148 if (!QLIST_EMPTY(&s
->free_list
)) {
149 pdu
= QLIST_FIRST(&s
->free_list
);
150 QLIST_REMOVE(pdu
, next
);
155 static void free_pdu(V9fsState
*s
, V9fsPDU
*pdu
)
158 QLIST_INSERT_HEAD(&s
->free_list
, pdu
, next
);
162 size_t pdu_packunpack(void *addr
, struct iovec
*sg
, int sg_count
,
163 size_t offset
, size_t size
, int pack
)
168 for (i
= 0; size
&& i
< sg_count
; i
++) {
170 if (offset
>= sg
[i
].iov_len
) {
172 offset
-= sg
[i
].iov_len
;
175 len
= MIN(sg
[i
].iov_len
- offset
, size
);
177 memcpy(sg
[i
].iov_base
+ offset
, addr
, len
);
179 memcpy(addr
, sg
[i
].iov_base
+ offset
, len
);
194 static size_t pdu_unpack(void *dst
, V9fsPDU
*pdu
, size_t offset
, size_t size
)
196 return pdu_packunpack(dst
, pdu
->elem
.out_sg
, pdu
->elem
.out_num
,
200 static size_t pdu_pack(V9fsPDU
*pdu
, size_t offset
, const void *src
,
203 return pdu_packunpack((void *)src
, pdu
->elem
.in_sg
, pdu
->elem
.in_num
,
207 static int pdu_copy_sg(V9fsPDU
*pdu
, size_t offset
, int rx
, struct iovec
*sg
)
211 struct iovec
*src_sg
;
215 src_sg
= pdu
->elem
.in_sg
;
216 num
= pdu
->elem
.in_num
;
218 src_sg
= pdu
->elem
.out_sg
;
219 num
= pdu
->elem
.out_num
;
223 for (i
= 0; i
< num
; i
++) {
225 sg
[j
].iov_base
= src_sg
[i
].iov_base
;
226 sg
[j
].iov_len
= src_sg
[i
].iov_len
;
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
);
235 pos
+= src_sg
[i
].iov_len
;
241 static size_t pdu_unmarshal(V9fsPDU
*pdu
, size_t offset
, const char *fmt
, ...)
243 size_t old_offset
= offset
;
248 for (i
= 0; fmt
[i
]; i
++) {
251 uint8_t *valp
= va_arg(ap
, uint8_t *);
252 offset
+= pdu_unpack(valp
, pdu
, offset
, sizeof(*valp
));
257 valp
= va_arg(ap
, uint16_t *);
258 val
= le16_to_cpupu(valp
);
259 offset
+= pdu_unpack(&val
, pdu
, offset
, sizeof(val
));
265 valp
= va_arg(ap
, uint32_t *);
266 val
= le32_to_cpupu(valp
);
267 offset
+= pdu_unpack(&val
, pdu
, offset
, sizeof(val
));
273 valp
= va_arg(ap
, uint64_t *);
274 val
= le64_to_cpup(valp
);
275 offset
+= pdu_unpack(&val
, pdu
, offset
, sizeof(val
));
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
);
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;
295 V9fsQID
*qidp
= va_arg(ap
, V9fsQID
*);
296 offset
+= pdu_unmarshal(pdu
, offset
, "bdq",
297 &qidp
->type
, &qidp
->version
, &qidp
->path
);
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
,
319 return offset
- old_offset
;
322 static size_t pdu_marshal(V9fsPDU
*pdu
, size_t offset
, const char *fmt
, ...)
324 size_t old_offset
= offset
;
329 for (i
= 0; fmt
[i
]; i
++) {
332 uint8_t val
= va_arg(ap
, int);
333 offset
+= pdu_pack(pdu
, offset
, &val
, sizeof(val
));
338 cpu_to_le16w(&val
, va_arg(ap
, int));
339 offset
+= pdu_pack(pdu
, offset
, &val
, sizeof(val
));
344 cpu_to_le32w(&val
, va_arg(ap
, uint32_t));
345 offset
+= pdu_pack(pdu
, offset
, &val
, sizeof(val
));
350 cpu_to_le64w(&val
, va_arg(ap
, uint64_t));
351 offset
+= pdu_pack(pdu
, offset
, &val
, sizeof(val
));
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
);
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
);
367 V9fsQID
*qidp
= va_arg(ap
, V9fsQID
*);
368 offset
+= pdu_marshal(pdu
, offset
, "bdq",
369 qidp
->type
, qidp
->version
, qidp
->path
);
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
);
389 return offset
- old_offset
;
392 static void complete_pdu(V9fsState
*s
, V9fsPDU
*pdu
, ssize_t len
)
394 int8_t id
= pdu
->id
+ 1; /* Response */
400 str
.data
= strerror(err
);
401 str
.size
= strlen(str
.data
);
404 len
+= pdu_marshal(pdu
, len
, "s", &str
);
406 len
+= pdu_marshal(pdu
, len
, "d", err
);
412 /* fill out the header */
413 pdu_marshal(pdu
, 0, "dbw", (int32_t)len
, id
, pdu
->tag
);
415 /* keep these in sync */
419 /* push onto queue and notify */
420 virtqueue_push(s
->vq
, &pdu
->elem
, len
);
422 /* FIXME: we should batch these completions */
423 virtio_notify(&s
->vdev
, s
->vq
);
428 static void v9fs_dummy(V9fsState
*s
, V9fsPDU
*pdu
)
430 /* Note: The following have been added to prevent GCC from complaining
431 * They will be removed in the subsequent patches */
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
;
443 static void v9fs_version(V9fsState
*s
, V9fsPDU
*pdu
)
450 static void v9fs_attach(V9fsState
*s
, V9fsPDU
*pdu
)
457 static void v9fs_stat(V9fsState
*s
, V9fsPDU
*pdu
)
464 static void v9fs_walk(V9fsState
*s
, V9fsPDU
*pdu
)
471 static void v9fs_clunk(V9fsState
*s
, V9fsPDU
*pdu
)
478 static void v9fs_open(V9fsState
*s
, V9fsPDU
*pdu
)
479 { if (debug_9p_pdu
) {
484 static void v9fs_read(V9fsState
*s
, V9fsPDU
*pdu
)
491 static void v9fs_write(V9fsState
*s
, V9fsPDU
*pdu
)
498 static void v9fs_create(V9fsState
*s
, V9fsPDU
*pdu
)
505 static void v9fs_flush(V9fsState
*s
, V9fsPDU
*pdu
)
513 static void v9fs_remove(V9fsState
*s
, V9fsPDU
*pdu
)
520 static void v9fs_wstat(V9fsState
*s
, V9fsPDU
*pdu
)
527 typedef void (pdu_handler_t
)(V9fsState
*s
, V9fsPDU
*pdu
);
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
,
538 [P9_TAUTH
] = v9fs_auth
,
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
,
547 static void submit_pdu(V9fsState
*s
, V9fsPDU
*pdu
)
549 pdu_handler_t
*handler
;
555 BUG_ON(pdu
->id
>= ARRAY_SIZE(pdu_handlers
));
557 handler
= pdu_handlers
[pdu
->id
];
558 BUG_ON(handler
== NULL
);
563 static void handle_9p_output(VirtIODevice
*vdev
, VirtQueue
*vq
)
565 V9fsState
*s
= (V9fsState
*)vdev
;
569 while ((pdu
= alloc_pdu(s
)) &&
570 (len
= virtqueue_pop(vq
, &pdu
->elem
)) != 0) {
573 BUG_ON(pdu
->elem
.out_num
== 0 || pdu
->elem
.in_num
== 0);
574 BUG_ON(pdu
->elem
.out_sg
[0].iov_len
< 7);
576 ptr
= pdu
->elem
.out_sg
[0].iov_base
;
578 memcpy(&pdu
->size
, ptr
, 4);
580 memcpy(&pdu
->tag
, ptr
+ 5, 2);
588 static uint32_t virtio_9p_get_features(VirtIODevice
*vdev
, uint32_t features
)
590 features
|= 1 << VIRTIO_9P_MOUNT_TAG
;
594 static V9fsState
*to_virtio_9p(VirtIODevice
*vdev
)
596 return (V9fsState
*)vdev
;
599 static void virtio_9p_get_config(VirtIODevice
*vdev
, uint8_t *config
)
601 struct virtio_9p_config
*cfg
;
602 V9fsState
*s
= to_virtio_9p(vdev
);
604 cfg
= qemu_mallocz(sizeof(struct virtio_9p_config
) +
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
);
612 VirtIODevice
*virtio_9p_init(DeviceState
*dev
, V9fsConf
*conf
)
620 s
= (V9fsState
*)virtio_common_init("virtio-9p",
622 sizeof(struct virtio_9p_config
)+
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
);
632 s
->vq
= virtio_add_queue(&s
->vdev
, MAX_REQ
, handle_9p_output
);
634 fse
= get_fsdev_fsentry(conf
->fsdev_id
);
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
);
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",
651 if (lstat(fse
->path
, &stat
)) {
652 fprintf(stderr
, "share path %s does not exist\n", fse
->path
);
654 } else if (!S_ISDIR(stat
.st_mode
)) {
655 fprintf(stderr
, "share path %s is not a directory \n", fse
->path
);
659 s
->ctx
.fs_root
= qemu_strdup(fse
->path
);
660 len
= strlen(conf
->tag
);
661 if (len
> MAX_TAG_LEN
) {
664 /* s->tag is non-NULL terminated string */
665 s
->tag
= qemu_malloc(len
);
666 memcpy(s
->tag
, conf
->tag
, len
);
671 s
->vdev
.get_features
= virtio_9p_get_features
;
672 s
->config_size
= sizeof(struct virtio_9p_config
) +
674 s
->vdev
.get_config
= virtio_9p_get_config
;