]>
git.proxmox.com Git - qemu.git/blob - hw/virtio-9p.c
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 int v9fs_do_lstat(V9fsState
*s
, V9fsString
*path
, struct stat
*stbuf
)
26 return s
->ops
->lstat(&s
->ctx
, path
->data
, stbuf
);
29 static int v9fs_do_setuid(V9fsState
*s
, uid_t uid
)
31 return s
->ops
->setuid(&s
->ctx
, uid
);
34 static ssize_t
v9fs_do_readlink(V9fsState
*s
, V9fsString
*path
, V9fsString
*buf
)
38 buf
->data
= qemu_malloc(1024);
40 len
= s
->ops
->readlink(&s
->ctx
, path
->data
, buf
->data
, 1024 - 1);
49 static int v9fs_do_close(V9fsState
*s
, int fd
)
51 return s
->ops
->close(&s
->ctx
, fd
);
54 static int v9fs_do_closedir(V9fsState
*s
, DIR *dir
)
56 return s
->ops
->closedir(&s
->ctx
, dir
);
59 static void v9fs_string_init(V9fsString
*str
)
65 static void v9fs_string_free(V9fsString
*str
)
72 static void v9fs_string_null(V9fsString
*str
)
74 v9fs_string_free(str
);
77 static int number_to_string(void *arg
, char type
)
83 unsigned int num
= *(unsigned int *)arg
;
92 printf("Number_to_string: Unknown number format\n");
99 static int v9fs_string_alloc_printf(char **strp
, const char *fmt
, va_list ap
)
102 char *iter
= (char *)fmt
;
106 unsigned int arg_uint
;
108 /* Find the number of %'s that denotes an argument */
109 for (iter
= strstr(iter
, "%"); iter
; iter
= strstr(iter
, "%")) {
114 len
= strlen(fmt
) - 2*nr_args
;
124 /* Now parse the format string */
125 for (iter
= strstr(iter
, "%"); iter
; iter
= strstr(iter
, "%")) {
129 arg_uint
= va_arg(ap2
, unsigned int);
130 len
+= number_to_string((void *)&arg_uint
, 'u');
133 arg_char_ptr
= va_arg(ap2
, char *);
134 len
+= strlen(arg_char_ptr
);
141 "v9fs_string_alloc_printf:Incorrect format %c", *iter
);
148 *strp
= qemu_malloc((len
+ 1) * sizeof(**strp
));
150 return vsprintf(*strp
, fmt
, ap
);
153 static void v9fs_string_sprintf(V9fsString
*str
, const char *fmt
, ...)
158 v9fs_string_free(str
);
161 err
= v9fs_string_alloc_printf(&str
->data
, fmt
, ap
);
168 static void v9fs_string_copy(V9fsString
*lhs
, V9fsString
*rhs
)
170 v9fs_string_free(lhs
);
171 v9fs_string_sprintf(lhs
, "%s", rhs
->data
);
174 static size_t v9fs_string_size(V9fsString
*str
)
179 static V9fsPDU
*alloc_pdu(V9fsState
*s
)
183 if (!QLIST_EMPTY(&s
->free_list
)) {
184 pdu
= QLIST_FIRST(&s
->free_list
);
185 QLIST_REMOVE(pdu
, next
);
190 static void free_pdu(V9fsState
*s
, V9fsPDU
*pdu
)
193 QLIST_INSERT_HEAD(&s
->free_list
, pdu
, next
);
197 size_t pdu_packunpack(void *addr
, struct iovec
*sg
, int sg_count
,
198 size_t offset
, size_t size
, int pack
)
203 for (i
= 0; size
&& i
< sg_count
; i
++) {
205 if (offset
>= sg
[i
].iov_len
) {
207 offset
-= sg
[i
].iov_len
;
210 len
= MIN(sg
[i
].iov_len
- offset
, size
);
212 memcpy(sg
[i
].iov_base
+ offset
, addr
, len
);
214 memcpy(addr
, sg
[i
].iov_base
+ offset
, len
);
229 static size_t pdu_unpack(void *dst
, V9fsPDU
*pdu
, size_t offset
, size_t size
)
231 return pdu_packunpack(dst
, pdu
->elem
.out_sg
, pdu
->elem
.out_num
,
235 static size_t pdu_pack(V9fsPDU
*pdu
, size_t offset
, const void *src
,
238 return pdu_packunpack((void *)src
, pdu
->elem
.in_sg
, pdu
->elem
.in_num
,
242 static int pdu_copy_sg(V9fsPDU
*pdu
, size_t offset
, int rx
, struct iovec
*sg
)
246 struct iovec
*src_sg
;
250 src_sg
= pdu
->elem
.in_sg
;
251 num
= pdu
->elem
.in_num
;
253 src_sg
= pdu
->elem
.out_sg
;
254 num
= pdu
->elem
.out_num
;
258 for (i
= 0; i
< num
; i
++) {
260 sg
[j
].iov_base
= src_sg
[i
].iov_base
;
261 sg
[j
].iov_len
= src_sg
[i
].iov_len
;
263 } else if (offset
< (src_sg
[i
].iov_len
+ pos
)) {
264 sg
[j
].iov_base
= src_sg
[i
].iov_base
;
265 sg
[j
].iov_len
= src_sg
[i
].iov_len
;
266 sg
[j
].iov_base
+= (offset
- pos
);
267 sg
[j
].iov_len
-= (offset
- pos
);
270 pos
+= src_sg
[i
].iov_len
;
276 static size_t pdu_unmarshal(V9fsPDU
*pdu
, size_t offset
, const char *fmt
, ...)
278 size_t old_offset
= offset
;
283 for (i
= 0; fmt
[i
]; i
++) {
286 uint8_t *valp
= va_arg(ap
, uint8_t *);
287 offset
+= pdu_unpack(valp
, pdu
, offset
, sizeof(*valp
));
292 valp
= va_arg(ap
, uint16_t *);
293 val
= le16_to_cpupu(valp
);
294 offset
+= pdu_unpack(&val
, pdu
, offset
, sizeof(val
));
300 valp
= va_arg(ap
, uint32_t *);
301 val
= le32_to_cpupu(valp
);
302 offset
+= pdu_unpack(&val
, pdu
, offset
, sizeof(val
));
308 valp
= va_arg(ap
, uint64_t *);
309 val
= le64_to_cpup(valp
);
310 offset
+= pdu_unpack(&val
, pdu
, offset
, sizeof(val
));
315 struct iovec
*iov
= va_arg(ap
, struct iovec
*);
316 int *iovcnt
= va_arg(ap
, int *);
317 *iovcnt
= pdu_copy_sg(pdu
, offset
, 0, iov
);
321 V9fsString
*str
= va_arg(ap
, V9fsString
*);
322 offset
+= pdu_unmarshal(pdu
, offset
, "w", &str
->size
);
323 /* FIXME: sanity check str->size */
324 str
->data
= qemu_malloc(str
->size
+ 1);
325 offset
+= pdu_unpack(str
->data
, pdu
, offset
, str
->size
);
326 str
->data
[str
->size
] = 0;
330 V9fsQID
*qidp
= va_arg(ap
, V9fsQID
*);
331 offset
+= pdu_unmarshal(pdu
, offset
, "bdq",
332 &qidp
->type
, &qidp
->version
, &qidp
->path
);
336 V9fsStat
*statp
= va_arg(ap
, V9fsStat
*);
337 offset
+= pdu_unmarshal(pdu
, offset
, "wwdQdddqsssssddd",
338 &statp
->size
, &statp
->type
, &statp
->dev
,
339 &statp
->qid
, &statp
->mode
, &statp
->atime
,
340 &statp
->mtime
, &statp
->length
,
341 &statp
->name
, &statp
->uid
, &statp
->gid
,
342 &statp
->muid
, &statp
->extension
,
343 &statp
->n_uid
, &statp
->n_gid
,
354 return offset
- old_offset
;
357 static size_t pdu_marshal(V9fsPDU
*pdu
, size_t offset
, const char *fmt
, ...)
359 size_t old_offset
= offset
;
364 for (i
= 0; fmt
[i
]; i
++) {
367 uint8_t val
= va_arg(ap
, int);
368 offset
+= pdu_pack(pdu
, offset
, &val
, sizeof(val
));
373 cpu_to_le16w(&val
, va_arg(ap
, int));
374 offset
+= pdu_pack(pdu
, offset
, &val
, sizeof(val
));
379 cpu_to_le32w(&val
, va_arg(ap
, uint32_t));
380 offset
+= pdu_pack(pdu
, offset
, &val
, sizeof(val
));
385 cpu_to_le64w(&val
, va_arg(ap
, uint64_t));
386 offset
+= pdu_pack(pdu
, offset
, &val
, sizeof(val
));
390 struct iovec
*iov
= va_arg(ap
, struct iovec
*);
391 int *iovcnt
= va_arg(ap
, int *);
392 *iovcnt
= pdu_copy_sg(pdu
, offset
, 1, iov
);
396 V9fsString
*str
= va_arg(ap
, V9fsString
*);
397 offset
+= pdu_marshal(pdu
, offset
, "w", str
->size
);
398 offset
+= pdu_pack(pdu
, offset
, str
->data
, str
->size
);
402 V9fsQID
*qidp
= va_arg(ap
, V9fsQID
*);
403 offset
+= pdu_marshal(pdu
, offset
, "bdq",
404 qidp
->type
, qidp
->version
, qidp
->path
);
408 V9fsStat
*statp
= va_arg(ap
, V9fsStat
*);
409 offset
+= pdu_marshal(pdu
, offset
, "wwdQdddqsssssddd",
410 statp
->size
, statp
->type
, statp
->dev
,
411 &statp
->qid
, statp
->mode
, statp
->atime
,
412 statp
->mtime
, statp
->length
, &statp
->name
,
413 &statp
->uid
, &statp
->gid
, &statp
->muid
,
414 &statp
->extension
, statp
->n_uid
,
415 statp
->n_gid
, statp
->n_muid
);
424 return offset
- old_offset
;
427 static void complete_pdu(V9fsState
*s
, V9fsPDU
*pdu
, ssize_t len
)
429 int8_t id
= pdu
->id
+ 1; /* Response */
435 str
.data
= strerror(err
);
436 str
.size
= strlen(str
.data
);
439 len
+= pdu_marshal(pdu
, len
, "s", &str
);
441 len
+= pdu_marshal(pdu
, len
, "d", err
);
447 /* fill out the header */
448 pdu_marshal(pdu
, 0, "dbw", (int32_t)len
, id
, pdu
->tag
);
450 /* keep these in sync */
454 /* push onto queue and notify */
455 virtqueue_push(s
->vq
, &pdu
->elem
, len
);
457 /* FIXME: we should batch these completions */
458 virtio_notify(&s
->vdev
, s
->vq
);
463 static void v9fs_dummy(V9fsState
*s
, V9fsPDU
*pdu
)
465 /* Note: The following have been added to prevent GCC from complaining
466 * They will be removed in the subsequent patches */
469 (void) v9fs_string_init
;
470 (void) v9fs_string_free
;
471 (void) v9fs_string_null
;
472 (void) v9fs_string_sprintf
;
473 (void) v9fs_string_copy
;
474 (void) v9fs_string_size
;
475 (void) v9fs_do_lstat
;
476 (void) v9fs_do_setuid
;
477 (void) v9fs_do_readlink
;
478 (void) v9fs_do_close
;
479 (void) v9fs_do_closedir
;
482 static void v9fs_version(V9fsState
*s
, V9fsPDU
*pdu
)
489 static void v9fs_attach(V9fsState
*s
, V9fsPDU
*pdu
)
496 static void v9fs_stat(V9fsState
*s
, V9fsPDU
*pdu
)
503 static void v9fs_walk(V9fsState
*s
, V9fsPDU
*pdu
)
510 static void v9fs_clunk(V9fsState
*s
, V9fsPDU
*pdu
)
517 static void v9fs_open(V9fsState
*s
, V9fsPDU
*pdu
)
518 { if (debug_9p_pdu
) {
523 static void v9fs_read(V9fsState
*s
, V9fsPDU
*pdu
)
530 static void v9fs_write(V9fsState
*s
, V9fsPDU
*pdu
)
537 static void v9fs_create(V9fsState
*s
, V9fsPDU
*pdu
)
544 static void v9fs_flush(V9fsState
*s
, V9fsPDU
*pdu
)
552 static void v9fs_remove(V9fsState
*s
, V9fsPDU
*pdu
)
559 static void v9fs_wstat(V9fsState
*s
, V9fsPDU
*pdu
)
566 typedef void (pdu_handler_t
)(V9fsState
*s
, V9fsPDU
*pdu
);
568 static pdu_handler_t
*pdu_handlers
[] = {
569 [P9_TVERSION
] = v9fs_version
,
570 [P9_TATTACH
] = v9fs_attach
,
571 [P9_TSTAT
] = v9fs_stat
,
572 [P9_TWALK
] = v9fs_walk
,
573 [P9_TCLUNK
] = v9fs_clunk
,
574 [P9_TOPEN
] = v9fs_open
,
575 [P9_TREAD
] = v9fs_read
,
577 [P9_TAUTH
] = v9fs_auth
,
579 [P9_TFLUSH
] = v9fs_flush
,
580 [P9_TCREATE
] = v9fs_create
,
581 [P9_TWRITE
] = v9fs_write
,
582 [P9_TWSTAT
] = v9fs_wstat
,
583 [P9_TREMOVE
] = v9fs_remove
,
586 static void submit_pdu(V9fsState
*s
, V9fsPDU
*pdu
)
588 pdu_handler_t
*handler
;
594 BUG_ON(pdu
->id
>= ARRAY_SIZE(pdu_handlers
));
596 handler
= pdu_handlers
[pdu
->id
];
597 BUG_ON(handler
== NULL
);
602 static void handle_9p_output(VirtIODevice
*vdev
, VirtQueue
*vq
)
604 V9fsState
*s
= (V9fsState
*)vdev
;
608 while ((pdu
= alloc_pdu(s
)) &&
609 (len
= virtqueue_pop(vq
, &pdu
->elem
)) != 0) {
612 BUG_ON(pdu
->elem
.out_num
== 0 || pdu
->elem
.in_num
== 0);
613 BUG_ON(pdu
->elem
.out_sg
[0].iov_len
< 7);
615 ptr
= pdu
->elem
.out_sg
[0].iov_base
;
617 memcpy(&pdu
->size
, ptr
, 4);
619 memcpy(&pdu
->tag
, ptr
+ 5, 2);
627 static uint32_t virtio_9p_get_features(VirtIODevice
*vdev
, uint32_t features
)
629 features
|= 1 << VIRTIO_9P_MOUNT_TAG
;
633 static V9fsState
*to_virtio_9p(VirtIODevice
*vdev
)
635 return (V9fsState
*)vdev
;
638 static void virtio_9p_get_config(VirtIODevice
*vdev
, uint8_t *config
)
640 struct virtio_9p_config
*cfg
;
641 V9fsState
*s
= to_virtio_9p(vdev
);
643 cfg
= qemu_mallocz(sizeof(struct virtio_9p_config
) +
645 stw_raw(&cfg
->tag_len
, s
->tag_len
);
646 memcpy(cfg
->tag
, s
->tag
, s
->tag_len
);
647 memcpy(config
, cfg
, s
->config_size
);
651 VirtIODevice
*virtio_9p_init(DeviceState
*dev
, V9fsConf
*conf
)
659 s
= (V9fsState
*)virtio_common_init("virtio-9p",
661 sizeof(struct virtio_9p_config
)+
665 /* initialize pdu allocator */
666 QLIST_INIT(&s
->free_list
);
667 for (i
= 0; i
< (MAX_REQ
- 1); i
++) {
668 QLIST_INSERT_HEAD(&s
->free_list
, &s
->pdus
[i
], next
);
671 s
->vq
= virtio_add_queue(&s
->vdev
, MAX_REQ
, handle_9p_output
);
673 fse
= get_fsdev_fsentry(conf
->fsdev_id
);
676 /* We don't have a fsdev identified by fsdev_id */
677 fprintf(stderr
, "Virtio-9p device couldn't find fsdev "
678 "with the id %s\n", conf
->fsdev_id
);
682 if (!fse
->path
|| !conf
->tag
) {
683 /* we haven't specified a mount_tag or the path */
684 fprintf(stderr
, "fsdev with id %s needs path "
685 "and Virtio-9p device needs mount_tag arguments\n",
690 if (lstat(fse
->path
, &stat
)) {
691 fprintf(stderr
, "share path %s does not exist\n", fse
->path
);
693 } else if (!S_ISDIR(stat
.st_mode
)) {
694 fprintf(stderr
, "share path %s is not a directory \n", fse
->path
);
698 s
->ctx
.fs_root
= qemu_strdup(fse
->path
);
699 len
= strlen(conf
->tag
);
700 if (len
> MAX_TAG_LEN
) {
703 /* s->tag is non-NULL terminated string */
704 s
->tag
= qemu_malloc(len
);
705 memcpy(s
->tag
, conf
->tag
, len
);
710 s
->vdev
.get_features
= virtio_9p_get_features
;
711 s
->config_size
= sizeof(struct virtio_9p_config
) +
713 s
->vdev
.get_config
= virtio_9p_get_config
;