From 583f21f8b9982d60c451e812af2d9dfe19d19d3f Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Tue, 3 Jan 2017 17:28:44 +0100 Subject: [PATCH 01/13] 9pfs: move pdus to V9fsState pdus are initialized and used in 9pfs common code. Move the array from V9fsVirtioState to V9fsState. Signed-off-by: Stefano Stabellini Reviewed-by: Greg Kurz Signed-off-by: Greg Kurz --- hw/9pfs/9p.c | 7 +++---- hw/9pfs/9p.h | 1 + hw/9pfs/virtio-9p.h | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index faebd91f5f..6bd364f6a0 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -3440,7 +3440,6 @@ void pdu_submit(V9fsPDU *pdu) /* Returns 0 on success, 1 on failure. */ int v9fs_device_realize_common(V9fsState *s, Error **errp) { - V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); int i, len; struct stat stat; FsDriverEntry *fse; @@ -3451,9 +3450,9 @@ int v9fs_device_realize_common(V9fsState *s, Error **errp) QLIST_INIT(&s->free_list); QLIST_INIT(&s->active_list); for (i = 0; i < (MAX_REQ - 1); i++) { - QLIST_INSERT_HEAD(&s->free_list, &v->pdus[i], next); - v->pdus[i].s = s; - v->pdus[i].idx = i; + QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next); + s->pdus[i].s = s; + s->pdus[i].idx = i; } v9fs_path_init(&path); diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 3976b7fe3d..07cee01aba 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -229,6 +229,7 @@ typedef struct V9fsState char *tag; enum p9_proto_version proto_version; int32_t msize; + V9fsPDU pdus[MAX_REQ]; /* * lock ensuring atomic path update * on rename. diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h index 25c47c7cb6..52c4b9df51 100644 --- a/hw/9pfs/virtio-9p.h +++ b/hw/9pfs/virtio-9p.h @@ -10,7 +10,6 @@ typedef struct V9fsVirtioState VirtIODevice parent_obj; VirtQueue *vq; size_t config_size; - V9fsPDU pdus[MAX_REQ]; VirtQueueElement *elems[MAX_REQ]; V9fsState state; } V9fsVirtioState; From ea83441cc44143b5b68a400ae4292a3eef14a675 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Tue, 3 Jan 2017 17:28:44 +0100 Subject: [PATCH 02/13] 9pfs: introduce transport specific callbacks Don't call virtio functions from 9pfs generic code, use generic function callbacks instead. Signed-off-by: Stefano Stabellini Reviewed-by: Greg Kurz Signed-off-by: Greg Kurz --- hw/9pfs/9p.c | 8 ++++---- hw/9pfs/9p.h | 19 +++++++++++++++++++ hw/9pfs/virtio-9p-device.c | 24 +++++++++++++++++------- hw/9pfs/virtio-9p.h | 9 --------- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 6bd364f6a0..3f5647aa56 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -47,7 +47,7 @@ ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) va_list ap; va_start(ap, fmt); - ret = virtio_pdu_vmarshal(pdu, offset, fmt, ap); + ret = pdu->s->transport->pdu_vmarshal(pdu, offset, fmt, ap); va_end(ap); return ret; @@ -59,7 +59,7 @@ ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) va_list ap; va_start(ap, fmt); - ret = virtio_pdu_vunmarshal(pdu, offset, fmt, ap); + ret = pdu->s->transport->pdu_vunmarshal(pdu, offset, fmt, ap); va_end(ap); return ret; @@ -67,7 +67,7 @@ ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) static void pdu_push_and_notify(V9fsPDU *pdu) { - virtio_9p_push_and_notify(pdu); + pdu->s->transport->push_and_notify(pdu); } static int omode_to_uflags(int8_t mode) @@ -1751,7 +1751,7 @@ static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu, struct iovec *iov; unsigned int niov; - virtio_init_iov_from_pdu(pdu, &iov, &niov, is_write); + pdu->s->transport->init_iov_from_pdu(pdu, &iov, &niov, is_write); qemu_iovec_init_external(&elem, iov, niov); qemu_iovec_init(qiov, niov); diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 07cee01aba..c8c9aa8909 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -230,6 +230,7 @@ typedef struct V9fsState enum p9_proto_version proto_version; int32_t msize; V9fsPDU pdus[MAX_REQ]; + const struct V9fsTransport *transport; /* * lock ensuring atomic path update * on rename. @@ -343,4 +344,22 @@ void pdu_free(V9fsPDU *pdu); void pdu_submit(V9fsPDU *pdu); void v9fs_reset(V9fsState *s); +struct V9fsTransport { + ssize_t (*pdu_vmarshal)(V9fsPDU *pdu, size_t offset, const char *fmt, + va_list ap); + ssize_t (*pdu_vunmarshal)(V9fsPDU *pdu, size_t offset, const char *fmt, + va_list ap); + void (*init_iov_from_pdu)(V9fsPDU *pdu, struct iovec **piov, + unsigned int *pniov, bool is_write); + void (*push_and_notify)(V9fsPDU *pdu); +}; + +static inline int v9fs_register_transport(V9fsState *s, + const struct V9fsTransport *t) +{ + assert(!s->transport); + s->transport = t; + return 0; +} + #endif diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 1782e4a227..273425b9d1 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -20,7 +20,9 @@ #include "hw/virtio/virtio-access.h" #include "qemu/iov.h" -void virtio_9p_push_and_notify(V9fsPDU *pdu) +static const struct V9fsTransport virtio_9p_transport; + +static void virtio_9p_push_and_notify(V9fsPDU *pdu) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); @@ -126,6 +128,7 @@ static void virtio_9p_device_realize(DeviceState *dev, Error **errp) v->config_size = sizeof(struct virtio_9p_config) + strlen(s->fsconf.tag); virtio_init(vdev, "virtio-9p", VIRTIO_ID_9P, v->config_size); v->vq = virtio_add_queue(vdev, MAX_REQ, handle_9p_output); + v9fs_register_transport(s, &virtio_9p_transport); out: return; @@ -148,8 +151,8 @@ static void virtio_9p_reset(VirtIODevice *vdev) v9fs_reset(&v->state); } -ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset, - const char *fmt, va_list ap) +static ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset, + const char *fmt, va_list ap) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); @@ -158,8 +161,8 @@ ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset, return v9fs_iov_vmarshal(elem->in_sg, elem->in_num, offset, 1, fmt, ap); } -ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, - const char *fmt, va_list ap) +static ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, + const char *fmt, va_list ap) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); @@ -168,8 +171,8 @@ ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, return v9fs_iov_vunmarshal(elem->out_sg, elem->out_num, offset, 1, fmt, ap); } -void virtio_init_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov, bool is_write) +static void virtio_init_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, + unsigned int *pniov, bool is_write) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); @@ -184,6 +187,13 @@ void virtio_init_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, } } +static const struct V9fsTransport virtio_9p_transport = { + .pdu_vmarshal = virtio_pdu_vmarshal, + .pdu_vunmarshal = virtio_pdu_vunmarshal, + .init_iov_from_pdu = virtio_init_iov_from_pdu, + .push_and_notify = virtio_9p_push_and_notify, +}; + /* virtio-9p device */ static const VMStateDescription vmstate_virtio_9p = { diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h index 52c4b9df51..e763da2c02 100644 --- a/hw/9pfs/virtio-9p.h +++ b/hw/9pfs/virtio-9p.h @@ -14,15 +14,6 @@ typedef struct V9fsVirtioState V9fsState state; } V9fsVirtioState; -void virtio_9p_push_and_notify(V9fsPDU *pdu); - -ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset, - const char *fmt, va_list ap); -ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, - const char *fmt, va_list ap); -void virtio_init_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov, bool is_write); - #define TYPE_VIRTIO_9P "virtio-9p-device" #define VIRTIO_9P(obj) \ OBJECT_CHECK(V9fsVirtioState, (obj), TYPE_VIRTIO_9P) From bcb8998fac16a7f26ac9cc3637e17701cfe69e5b Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Tue, 3 Jan 2017 17:28:44 +0100 Subject: [PATCH 03/13] 9pfs: call v9fs_init_qiov_from_pdu before v9fs_pack v9fs_xattr_read should not access VirtQueueElement elems directly. Move v9fs_init_qiov_from_pdu up in the file and call v9fs_init_qiov_from_pdu before v9fs_pack. Use v9fs_pack on the new iovec. Signed-off-by: Stefano Stabellini Reviewed-by: Greg Kurz Signed-off-by: Greg Kurz --- hw/9pfs/9p.c | 59 ++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 3f5647aa56..f9acd63259 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1633,14 +1633,39 @@ out_nofid: pdu_complete(pdu, err); } +/* + * Create a QEMUIOVector for a sub-region of PDU iovecs + * + * @qiov: uninitialized QEMUIOVector + * @skip: number of bytes to skip from beginning of PDU + * @size: number of bytes to include + * @is_write: true - write, false - read + * + * The resulting QEMUIOVector has heap-allocated iovecs and must be cleaned up + * with qemu_iovec_destroy(). + */ +static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu, + size_t skip, size_t size, + bool is_write) +{ + QEMUIOVector elem; + struct iovec *iov; + unsigned int niov; + + pdu->s->transport->init_iov_from_pdu(pdu, &iov, &niov, is_write); + + qemu_iovec_init_external(&elem, iov, niov); + qemu_iovec_init(qiov, niov); + qemu_iovec_concat(qiov, &elem, skip, size); +} + static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, uint64_t off, uint32_t max_count) { ssize_t err; size_t offset = 7; uint64_t read_count; - V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); - VirtQueueElement *elem = v->elems[pdu->idx]; + QEMUIOVector qiov_full; if (fidp->fs.xattr.len < off) { read_count = 0; @@ -1656,9 +1681,11 @@ static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, } offset += err; - err = v9fs_pack(elem->in_sg, elem->in_num, offset, + v9fs_init_qiov_from_pdu(&qiov_full, pdu, 0, read_count, false); + err = v9fs_pack(qiov_full.iov, qiov_full.niov, offset, ((char *)fidp->fs.xattr.value) + off, read_count); + qemu_iovec_destroy(&qiov_full); if (err < 0) { return err; } @@ -1732,32 +1759,6 @@ static int coroutine_fn v9fs_do_readdir_with_stat(V9fsPDU *pdu, return count; } -/* - * Create a QEMUIOVector for a sub-region of PDU iovecs - * - * @qiov: uninitialized QEMUIOVector - * @skip: number of bytes to skip from beginning of PDU - * @size: number of bytes to include - * @is_write: true - write, false - read - * - * The resulting QEMUIOVector has heap-allocated iovecs and must be cleaned up - * with qemu_iovec_destroy(). - */ -static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu, - size_t skip, size_t size, - bool is_write) -{ - QEMUIOVector elem; - struct iovec *iov; - unsigned int niov; - - pdu->s->transport->init_iov_from_pdu(pdu, &iov, &niov, is_write); - - qemu_iovec_init_external(&elem, iov, niov); - qemu_iovec_init(qiov, niov); - qemu_iovec_concat(qiov, &elem, skip, size); -} - static void coroutine_fn v9fs_read(void *opaque) { int32_t fid; From 88da0b03019ecadc3faaddf81a97c7ac83448280 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Tue, 3 Jan 2017 17:28:44 +0100 Subject: [PATCH 04/13] 9pfs: introduce init_out/in_iov_from_pdu Not all 9pfs transports share memory between request and response. For those who don't, it is necessary to know how much memory is required in the response. Split the existing init_iov_from_pdu function in two: init_out_iov_from_pdu (for writes) and init_in_iov_from_pdu (for reads). init_in_iov_from_pdu takes an additional size parameter to specify the memory required for the response message. Signed-off-by: Stefano Stabellini Reviewed-by: Greg Kurz Signed-off-by: Greg Kurz --- hw/9pfs/9p.c | 6 +++++- hw/9pfs/9p.h | 6 ++++-- hw/9pfs/virtio-9p-device.c | 28 ++++++++++++++++++---------- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index f9acd63259..97c2926aae 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1652,7 +1652,11 @@ static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu, struct iovec *iov; unsigned int niov; - pdu->s->transport->init_iov_from_pdu(pdu, &iov, &niov, is_write); + if (is_write) { + pdu->s->transport->init_out_iov_from_pdu(pdu, &iov, &niov); + } else { + pdu->s->transport->init_in_iov_from_pdu(pdu, &iov, &niov, size); + } qemu_iovec_init_external(&elem, iov, niov); qemu_iovec_init(qiov, niov); diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index c8c9aa8909..4c4feafa2e 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -349,8 +349,10 @@ struct V9fsTransport { va_list ap); ssize_t (*pdu_vunmarshal)(V9fsPDU *pdu, size_t offset, const char *fmt, va_list ap); - void (*init_iov_from_pdu)(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov, bool is_write); + void (*init_in_iov_from_pdu)(V9fsPDU *pdu, struct iovec **piov, + unsigned int *pniov, size_t size); + void (*init_out_iov_from_pdu)(V9fsPDU *pdu, struct iovec **piov, + unsigned int *pniov); void (*push_and_notify)(V9fsPDU *pdu); }; diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 273425b9d1..27a4a32f5c 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -171,26 +171,34 @@ static ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, return v9fs_iov_vunmarshal(elem->out_sg, elem->out_num, offset, 1, fmt, ap); } -static void virtio_init_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov, bool is_write) +/* The size parameter is used by other transports. Do not drop it. */ +static void virtio_init_in_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, + unsigned int *pniov, size_t size) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); VirtQueueElement *elem = v->elems[pdu->idx]; - if (is_write) { - *piov = elem->out_sg; - *pniov = elem->out_num; - } else { - *piov = elem->in_sg; - *pniov = elem->in_num; - } + *piov = elem->in_sg; + *pniov = elem->in_num; +} + +static void virtio_init_out_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, + unsigned int *pniov) +{ + V9fsState *s = pdu->s; + V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); + VirtQueueElement *elem = v->elems[pdu->idx]; + + *piov = elem->out_sg; + *pniov = elem->out_num; } static const struct V9fsTransport virtio_9p_transport = { .pdu_vmarshal = virtio_pdu_vmarshal, .pdu_vunmarshal = virtio_pdu_vunmarshal, - .init_iov_from_pdu = virtio_init_iov_from_pdu, + .init_in_iov_from_pdu = virtio_init_in_iov_from_pdu, + .init_out_iov_from_pdu = virtio_init_out_iov_from_pdu, .push_and_notify = virtio_9p_push_and_notify, }; From f2b58c43758efc61e2a49b899f5e58848489d0dc Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 3 Jan 2017 17:28:44 +0100 Subject: [PATCH 05/13] 9pfs: fix crash when fsdev is missing If the user passes -device virtio-9p without the corresponding -fsdev, QEMU dereferences a NULL pointer and crashes. This is a 2.8 regression introduced by commit 702dbcc274e2c. Signed-off-by: Greg Kurz Reviewed-by: Li Qiang --- hw/9pfs/9p.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 97c2926aae..fa58877570 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -3525,7 +3525,7 @@ int v9fs_device_realize_common(V9fsState *s, Error **errp) rc = 0; out: if (rc) { - if (s->ops->cleanup && s->ctx.private) { + if (s->ops && s->ops->cleanup && s->ctx.private) { s->ops->cleanup(&s->ctx); } g_free(s->tag); From d5ebc8272b513adfdd6a8309d2c491e6bdc836b8 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 3 Jan 2017 17:28:44 +0100 Subject: [PATCH 06/13] tests: virtio-9p: rename PCI configuration test Signed-off-by: Greg Kurz --- tests/virtio-9p-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c index 9c4f6cb406..f458297f9a 100644 --- a/tests/virtio-9p-test.c +++ b/tests/virtio-9p-test.c @@ -92,7 +92,7 @@ static void qvirtio_9p_pci_free(QVirtIO9P *v9p) g_free(v9p); } -static void pci_basic_config(void) +static void pci_config(void) { QVirtIO9P *v9p; size_t tag_len; @@ -121,7 +121,7 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); qtest_add_func("/virtio/9p/pci/nop", pci_nop); - qtest_add_func("/virtio/9p/pci/basic/configuration", pci_basic_config); + qtest_add_func("/virtio/9p/pci/config", pci_config); return g_test_run(); } From 1211d81b17e74df5ebc26884778b59b4e3ea064f Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 3 Jan 2017 17:28:44 +0100 Subject: [PATCH 07/13] tests: virtio-9p: code refactoring This moves the test_share static and the QOSState into the QVirtIO9P structure, and put PCI related code in functions with a _pci_ name. This will avoid code duplication in future tests, and allow to add support for non-PCI platforms. Signed-off-by: Greg Kurz --- tests/virtio-9p-test.c | 113 ++++++++++++++++++++--------------------- 1 file changed, 56 insertions(+), 57 deletions(-) diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c index f458297f9a..7273160863 100644 --- a/tests/virtio-9p-test.c +++ b/tests/virtio-9p-test.c @@ -18,59 +18,49 @@ #include "standard-headers/linux/virtio_pci.h" static const char mount_tag[] = "qtest"; -static char *test_share; - - -static QOSState *qvirtio_9p_start(void) -{ - const char *arch = qtest_get_arch(); - const char *cmd = "-fsdev local,id=fsdev0,security_model=none,path=%s " - "-device virtio-9p-pci,fsdev=fsdev0,mount_tag=%s"; - - test_share = g_strdup("/tmp/qtest.XXXXXX"); - g_assert_nonnull(mkdtemp(test_share)); - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - return qtest_pc_boot(cmd, test_share, mount_tag); - } - if (strcmp(arch, "ppc64") == 0) { - return qtest_spapr_boot(cmd, test_share, mount_tag); - } - - g_printerr("virtio-9p tests are only available on x86 or ppc64\n"); - exit(EXIT_FAILURE); -} - -static void qvirtio_9p_stop(QOSState *qs) -{ - qtest_shutdown(qs); - rmdir(test_share); - g_free(test_share); -} - -static void pci_nop(void) -{ - QOSState *qs; - - qs = qvirtio_9p_start(); - qvirtio_9p_stop(qs); -} typedef struct { QVirtioDevice *dev; QOSState *qs; QVirtQueue *vq; + char *test_share; } QVirtIO9P; -static QVirtIO9P *qvirtio_9p_pci_init(QOSState *qs) +static QVirtIO9P *qvirtio_9p_start(const char *driver) { - QVirtIO9P *v9p; - QVirtioPCIDevice *dev; + const char *arch = qtest_get_arch(); + const char *cmd = "-fsdev local,id=fsdev0,security_model=none,path=%s " + "-device %s,fsdev=fsdev0,mount_tag=%s"; + QVirtIO9P *v9p = g_new0(QVirtIO9P, 1); - v9p = g_new0(QVirtIO9P, 1); + v9p->test_share = g_strdup("/tmp/qtest.XXXXXX"); + g_assert_nonnull(mkdtemp(v9p->test_share)); - v9p->qs = qs; - dev = qvirtio_pci_device_find(v9p->qs->pcibus, VIRTIO_ID_9P); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + v9p->qs = qtest_pc_boot(cmd, v9p->test_share, driver, mount_tag); + } else if (strcmp(arch, "ppc64") == 0) { + v9p->qs = qtest_spapr_boot(cmd, v9p->test_share, driver, mount_tag); + } else { + g_printerr("virtio-9p tests are only available on x86 or ppc64\n"); + exit(EXIT_FAILURE); + } + + return v9p; +} + +static void qvirtio_9p_stop(QVirtIO9P *v9p) +{ + qtest_shutdown(v9p->qs); + rmdir(v9p->test_share); + g_free(v9p->test_share); + g_free(v9p); +} + +static QVirtIO9P *qvirtio_9p_pci_start(void) +{ + QVirtIO9P *v9p = qvirtio_9p_start("virtio-9p-pci"); + QVirtioPCIDevice *dev = qvirtio_pci_device_find(v9p->qs->pcibus, + VIRTIO_ID_9P); g_assert_nonnull(dev); g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_9P); v9p->dev = (QVirtioDevice *) dev; @@ -84,26 +74,20 @@ static QVirtIO9P *qvirtio_9p_pci_init(QOSState *qs) return v9p; } -static void qvirtio_9p_pci_free(QVirtIO9P *v9p) +static void qvirtio_9p_pci_stop(QVirtIO9P *v9p) { qvirtqueue_cleanup(v9p->dev->bus, v9p->vq, v9p->qs->alloc); qvirtio_pci_device_disable(container_of(v9p->dev, QVirtioPCIDevice, vdev)); g_free(v9p->dev); - g_free(v9p); + qvirtio_9p_stop(v9p); } -static void pci_config(void) +static void pci_config(QVirtIO9P *v9p) { - QVirtIO9P *v9p; - size_t tag_len; + size_t tag_len = qvirtio_config_readw(v9p->dev, 0); char *tag; int i; - QOSState *qs; - qs = qvirtio_9p_start(); - v9p = qvirtio_9p_pci_init(qs); - - tag_len = qvirtio_config_readw(v9p->dev, 0); g_assert_cmpint(tag_len, ==, strlen(mount_tag)); tag = g_malloc(tag_len); @@ -112,16 +96,31 @@ static void pci_config(void) } g_assert_cmpmem(tag, tag_len, mount_tag, tag_len); g_free(tag); +} - qvirtio_9p_pci_free(v9p); - qvirtio_9p_stop(qs); +typedef void (*v9fs_test_fn)(QVirtIO9P *v9p); + +static void v9fs_run_pci_test(gconstpointer data) +{ + v9fs_test_fn fn = data; + QVirtIO9P *v9p = qvirtio_9p_pci_start(); + + if (fn) { + fn(v9p); + } + qvirtio_9p_pci_stop(v9p); +} + +static void v9fs_qtest_pci_add(const char *path, v9fs_test_fn fn) +{ + qtest_add_data_func(path, fn, v9fs_run_pci_test); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - qtest_add_func("/virtio/9p/pci/nop", pci_nop); - qtest_add_func("/virtio/9p/pci/config", pci_config); + v9fs_qtest_pci_add("/virtio/9p/pci/nop", NULL); + v9fs_qtest_pci_add("/virtio/9p/pci/config", pci_config); return g_test_run(); } From baecbde6d75076c6631a58475866b2330391b819 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 3 Jan 2017 17:28:44 +0100 Subject: [PATCH 08/13] 9pfs: fix P9_NOTAG and P9_NOFID macros The u16 and u32 types don't exist in QEMU common headers. It never broke build because these two macros aren't use by the current code, but this is about to change with the future addition of functional tests for 9P. Also, these should have enclosing parenthesis to be usable in any syntactical situation. As suggested by Eric Blake, let's use UINT16_MAX and UINT32_MAX to address both issues. Signed-off-by: Greg Kurz --- hw/9pfs/9p.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 4c4feafa2e..b7e836251e 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -99,8 +99,8 @@ enum p9_proto_version { V9FS_PROTO_2000L = 0x02, }; -#define P9_NOTAG (u16)(~0) -#define P9_NOFID (u32)(~0) +#define P9_NOTAG UINT16_MAX +#define P9_NOFID UINT32_MAX #define P9_MAXWELEM 16 #define FID_REFERENCED 0x1 From 6cc9906b4c4a659841485ac483f608dea31dfa63 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 3 Jan 2017 17:28:44 +0100 Subject: [PATCH 09/13] tests: virtio-9p: add version operation test This patch lays the foundations to be able to test 9P operations and provides a test for the version operation as a first example. A 9P request is composed of a T-message sent by the client (guest) to the server (QEMU), and a R-message sent by the server back to the client. The following general calls are available to implement requests for any 9P operations: v9fs_req_init(): allocates the request structure and the guest memory for the T-message v9fs_req_send(): allocates the guest memory for the R-message and sends the T-message to QEMU v9fs_req_recv(): waits for QEMU to answer and does some sanity checks on the returned R-message header v9fs_req_free(): releases the guest memory and the request structure Helpers are provided, to be used by each specific 9P operation to copy data to/from the guest memory. The version operation is used to negotiate the 9P protocol version to be used and the maximum buffer size for exchanged data. It is necessarily the first message of a 9P session. For simplicity, the maximum buffer size is hardcoded to 4k, which should be enough for functional tests. The test simply advertises the "9P2000.L" version to QEMU and expects QEMU to answer it is supported. References: http://man.cat-v.org/plan_9/5/intro http://man.cat-v.org/plan_9/5/version Signed-off-by: Greg Kurz --- tests/virtio-9p-test.c | 222 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c index 7273160863..b3ee956cbc 100644 --- a/tests/virtio-9p-test.c +++ b/tests/virtio-9p-test.c @@ -16,6 +16,7 @@ #include "libqos/virtio-pci.h" #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/virtio_pci.h" +#include "hw/9pfs/9p.h" static const char mount_tag[] = "qtest"; @@ -98,6 +99,226 @@ static void pci_config(QVirtIO9P *v9p) g_free(tag); } +#define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */ + +typedef struct { + QVirtIO9P *v9p; + uint16_t tag; + uint64_t t_msg; + uint32_t t_size; + uint64_t r_msg; + /* No r_size, it is hardcoded to P9_MAX_SIZE */ + size_t t_off; + size_t r_off; +} P9Req; + +static void v9fs_memwrite(P9Req *req, const void *addr, size_t len) +{ + memwrite(req->t_msg + req->t_off, addr, len); + req->t_off += len; +} + +static void v9fs_memskip(P9Req *req, size_t len) +{ + req->r_off += len; +} + +static void v9fs_memrewind(P9Req *req, size_t len) +{ + req->r_off -= len; +} + +static void v9fs_memread(P9Req *req, void *addr, size_t len) +{ + memread(req->r_msg + req->r_off, addr, len); + req->r_off += len; +} + +static void v9fs_uint16_write(P9Req *req, uint16_t val) +{ + uint16_t le_val = cpu_to_le16(val); + + v9fs_memwrite(req, &le_val, 2); +} + +static void v9fs_uint16_read(P9Req *req, uint16_t *val) +{ + v9fs_memread(req, val, 2); + le16_to_cpus(val); +} + +static void v9fs_uint32_write(P9Req *req, uint32_t val) +{ + uint32_t le_val = cpu_to_le32(val); + + v9fs_memwrite(req, &le_val, 4); +} + +static void v9fs_uint32_read(P9Req *req, uint32_t *val) +{ + v9fs_memread(req, val, 4); + le32_to_cpus(val); +} + +/* len[2] string[len] */ +static uint16_t v9fs_string_size(const char *string) +{ + size_t len = strlen(string); + + g_assert_cmpint(len, <=, UINT16_MAX); + + return 2 + len; +} + +static void v9fs_string_write(P9Req *req, const char *string) +{ + int len = strlen(string); + + g_assert_cmpint(len, <=, UINT16_MAX); + + v9fs_uint16_write(req, (uint16_t) len); + v9fs_memwrite(req, string, len); +} + +static void v9fs_string_read(P9Req *req, uint16_t *len, char **string) +{ + uint16_t local_len; + + v9fs_uint16_read(req, &local_len); + if (len) { + *len = local_len; + } + if (string) { + *string = g_malloc(local_len); + v9fs_memread(req, *string, local_len); + } else { + v9fs_memskip(req, local_len); + } +} + + typedef struct { + uint32_t size; + uint8_t id; + uint16_t tag; +} QEMU_PACKED P9Hdr; + +static P9Req *v9fs_req_init(QVirtIO9P *v9p, uint32_t size, uint8_t id, + uint16_t tag) +{ + P9Req *req = g_new0(P9Req, 1); + uint32_t t_size = 7 + size; /* 9P header has well-known size of 7 bytes */ + P9Hdr hdr = { + .size = cpu_to_le32(t_size), + .id = id, + .tag = cpu_to_le16(tag) + }; + + g_assert_cmpint(t_size, <=, P9_MAX_SIZE); + + req->v9p = v9p; + req->t_size = t_size; + req->t_msg = guest_alloc(v9p->qs->alloc, req->t_size); + v9fs_memwrite(req, &hdr, 7); + req->tag = tag; + return req; +} + +static void v9fs_req_send(P9Req *req) +{ + QVirtIO9P *v9p = req->v9p; + uint32_t free_head; + + req->r_msg = guest_alloc(v9p->qs->alloc, P9_MAX_SIZE); + free_head = qvirtqueue_add(v9p->vq, req->t_msg, req->t_size, false, true); + qvirtqueue_add(v9p->vq, req->r_msg, P9_MAX_SIZE, true, false); + qvirtqueue_kick(v9p->dev, v9p->vq, free_head); + req->t_off = 0; +} + +static void v9fs_req_recv(P9Req *req, uint8_t id) +{ + QVirtIO9P *v9p = req->v9p; + P9Hdr hdr; + int i; + + for (i = 0; i < 10; i++) { + qvirtio_wait_queue_isr(v9p->dev, v9p->vq, 1000 * 1000); + + v9fs_memread(req, &hdr, 7); + le32_to_cpus(&hdr.size); + le16_to_cpus(&hdr.tag); + if (hdr.size >= 7) { + break; + } + v9fs_memrewind(req, 7); + } + + g_assert_cmpint(hdr.size, >=, 7); + g_assert_cmpint(hdr.size, <=, P9_MAX_SIZE); + g_assert_cmpint(hdr.tag, ==, req->tag); + + if (hdr.id != id && hdr.id == P9_RLERROR) { + uint32_t err; + v9fs_uint32_read(req, &err); + g_printerr("Received Rlerror (%d) instead of Response %d\n", err, id); + g_assert_not_reached(); + } + g_assert_cmpint(hdr.id, ==, id); +} + +static void v9fs_req_free(P9Req *req) +{ + QVirtIO9P *v9p = req->v9p; + + guest_free(v9p->qs->alloc, req->t_msg); + guest_free(v9p->qs->alloc, req->r_msg); + g_free(req); +} + +/* size[4] Tversion tag[2] msize[4] version[s] */ +static P9Req *v9fs_tversion(QVirtIO9P *v9p, uint32_t msize, const char *version) +{ + P9Req *req = v9fs_req_init(v9p, 4 + v9fs_string_size(version), P9_TVERSION, + P9_NOTAG); + + v9fs_uint32_write(req, msize); + v9fs_string_write(req, version); + v9fs_req_send(req); + return req; +} + +/* size[4] Rversion tag[2] msize[4] version[s] */ +static void v9fs_rversion(P9Req *req, uint16_t *len, char **version) +{ + uint32_t msize; + + v9fs_req_recv(req, P9_RVERSION); + v9fs_uint32_read(req, &msize); + + g_assert_cmpint(msize, ==, P9_MAX_SIZE); + + if (len || version) { + v9fs_string_read(req, len, version); + } + + v9fs_req_free(req); +} + +static void fs_version(QVirtIO9P *v9p) +{ + const char *version = "9P2000.L"; + uint16_t server_len; + char *server_version; + P9Req *req; + + req = v9fs_tversion(v9p, P9_MAX_SIZE, version); + v9fs_rversion(req, &server_len, &server_version); + + g_assert_cmpmem(server_version, server_len, version, strlen(version)); + + g_free(server_version); +} + typedef void (*v9fs_test_fn)(QVirtIO9P *v9p); static void v9fs_run_pci_test(gconstpointer data) @@ -121,6 +342,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); v9fs_qtest_pci_add("/virtio/9p/pci/nop", NULL); v9fs_qtest_pci_add("/virtio/9p/pci/config", pci_config); + v9fs_qtest_pci_add("/virtio/9p/pci/fs/version/basic", fs_version); return g_test_run(); } From 5c3df1f0967e2166fff9c026ff1f616a89cab391 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 3 Jan 2017 17:28:44 +0100 Subject: [PATCH 10/13] tests: virtio-9p: add attach operation test The attach operation is used to establish a connection between the client and the server. After this, the client is able to access the underlying filesystem and do I/O. This test simply ensures the operation succeeds without error. Reference: http://man.cat-v.org/plan_9/5/attach Signed-off-by: Greg Kurz --- tests/virtio-9p-test.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c index b3ee956cbc..37a256ef4a 100644 --- a/tests/virtio-9p-test.c +++ b/tests/virtio-9p-test.c @@ -25,6 +25,7 @@ typedef struct { QOSState *qs; QVirtQueue *vq; char *test_share; + uint16_t p9_req_tag; } QVirtIO9P; static QVirtIO9P *qvirtio_9p_start(const char *driver) @@ -304,6 +305,35 @@ static void v9fs_rversion(P9Req *req, uint16_t *len, char **version) v9fs_req_free(req); } +/* size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] */ +static P9Req *v9fs_tattach(QVirtIO9P *v9p, uint32_t fid, uint32_t n_uname) +{ + const char *uname = ""; /* ignored by QEMU */ + const char *aname = ""; /* ignored by QEMU */ + P9Req *req = v9fs_req_init(v9p, 4 + 4 + 2 + 2 + 4, P9_TATTACH, + ++(v9p->p9_req_tag)); + + v9fs_uint32_write(req, fid); + v9fs_uint32_write(req, P9_NOFID); + v9fs_string_write(req, uname); + v9fs_string_write(req, aname); + v9fs_uint32_write(req, n_uname); + v9fs_req_send(req); + return req; +} + +typedef char v9fs_qid[13]; + +/* size[4] Rattach tag[2] qid[13] */ +static void v9fs_rattach(P9Req *req, v9fs_qid *qid) +{ + v9fs_req_recv(req, P9_RATTACH); + if (qid) { + v9fs_memread(req, qid, 13); + } + v9fs_req_free(req); +} + static void fs_version(QVirtIO9P *v9p) { const char *version = "9P2000.L"; @@ -319,6 +349,15 @@ static void fs_version(QVirtIO9P *v9p) g_free(server_version); } +static void fs_attach(QVirtIO9P *v9p) +{ + P9Req *req; + + fs_version(v9p); + req = v9fs_tattach(v9p, 0, getuid()); + v9fs_rattach(req, NULL); +} + typedef void (*v9fs_test_fn)(QVirtIO9P *v9p); static void v9fs_run_pci_test(gconstpointer data) @@ -343,6 +382,7 @@ int main(int argc, char **argv) v9fs_qtest_pci_add("/virtio/9p/pci/nop", NULL); v9fs_qtest_pci_add("/virtio/9p/pci/config", pci_config); v9fs_qtest_pci_add("/virtio/9p/pci/fs/version/basic", fs_version); + v9fs_qtest_pci_add("/virtio/9p/pci/fs/attach/basic", fs_attach); return g_test_run(); } From 04b88c84a14b55d31b9e8aec1fe86ab43e111662 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 3 Jan 2017 17:28:44 +0100 Subject: [PATCH 11/13] tests: virtio-9p: add walk operation test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The walk operation is used to traverse the directory tree and to associate paths to fids. A single walk can be used to traverse up to P9_MAXWELEM path elements at the same time. The test creates a path with P9_MAXWELEM elements on the backend (à la 'mkdir -p') and issues a walk operation. The walk is expected to succeed without error. Reference: http://man.cat-v.org/plan_9/5/walk Signed-off-by: Greg Kurz --- tests/virtio-9p-test.c | 70 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c index 37a256ef4a..2deb7fbf92 100644 --- a/tests/virtio-9p-test.c +++ b/tests/virtio-9p-test.c @@ -334,6 +334,45 @@ static void v9fs_rattach(P9Req *req, v9fs_qid *qid) v9fs_req_free(req); } +/* size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) */ +static P9Req *v9fs_twalk(QVirtIO9P *v9p, uint32_t fid, uint32_t newfid, + uint16_t nwname, char *const wnames[]) +{ + P9Req *req; + int i; + uint32_t size = 4 + 4 + 2; + + for (i = 0; i < nwname; i++) { + size += v9fs_string_size(wnames[i]); + } + req = v9fs_req_init(v9p, size, P9_TWALK, ++(v9p->p9_req_tag)); + v9fs_uint32_write(req, fid); + v9fs_uint32_write(req, newfid); + v9fs_uint16_write(req, nwname); + for (i = 0; i < nwname; i++) { + v9fs_string_write(req, wnames[i]); + } + v9fs_req_send(req); + return req; +} + +/* size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) */ +static void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid) +{ + uint16_t local_nwqid; + + v9fs_req_recv(req, P9_RWALK); + v9fs_uint16_read(req, &local_nwqid); + if (nwqid) { + *nwqid = local_nwqid; + } + if (wqid) { + *wqid = g_malloc(local_nwqid * 13); + v9fs_memread(req, *wqid, local_nwqid * 13); + } + v9fs_req_free(req); +} + static void fs_version(QVirtIO9P *v9p) { const char *version = "9P2000.L"; @@ -358,6 +397,36 @@ static void fs_attach(QVirtIO9P *v9p) v9fs_rattach(req, NULL); } +static void fs_walk(QVirtIO9P *v9p) +{ + char *wnames[P9_MAXWELEM], *paths[P9_MAXWELEM]; + char *last_path = v9p->test_share; + uint16_t nwqid; + v9fs_qid *wqid; + int i; + P9Req *req; + + for (i = 0; i < P9_MAXWELEM; i++) { + wnames[i] = g_strdup_printf("%s%d", __func__, i); + last_path = paths[i] = g_strdup_printf("%s/%s", last_path, wnames[i]); + g_assert(!mkdir(paths[i], 0700)); + } + + fs_attach(v9p); + req = v9fs_twalk(v9p, 0, 1, P9_MAXWELEM, wnames); + v9fs_rwalk(req, &nwqid, &wqid); + + g_assert_cmpint(nwqid, ==, P9_MAXWELEM); + + for (i = 0; i < P9_MAXWELEM; i++) { + rmdir(paths[P9_MAXWELEM - i - 1]); + g_free(paths[P9_MAXWELEM - i - 1]); + g_free(wnames[i]); + } + + g_free(wqid); +} + typedef void (*v9fs_test_fn)(QVirtIO9P *v9p); static void v9fs_run_pci_test(gconstpointer data) @@ -383,6 +452,7 @@ int main(int argc, char **argv) v9fs_qtest_pci_add("/virtio/9p/pci/config", pci_config); v9fs_qtest_pci_add("/virtio/9p/pci/fs/version/basic", fs_version); v9fs_qtest_pci_add("/virtio/9p/pci/fs/attach/basic", fs_attach); + v9fs_qtest_pci_add("/virtio/9p/pci/fs/walk/basic", fs_walk); return g_test_run(); } From ba0d10378c5b040d20adc3340f74640b47528d4b Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 3 Jan 2017 17:28:44 +0100 Subject: [PATCH 12/13] tests: virtio-9p: no slash in path elements during walk The walk operation is expected to fail and to return ENOENT. Signed-off-by: Greg Kurz --- tests/virtio-9p-test.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c index 2deb7fbf92..83cb23b3af 100644 --- a/tests/virtio-9p-test.c +++ b/tests/virtio-9p-test.c @@ -276,6 +276,14 @@ static void v9fs_req_free(P9Req *req) g_free(req); } +/* size[4] Rlerror tag[2] ecode[4] */ +static void v9fs_rlerror(P9Req *req, uint32_t *err) +{ + v9fs_req_recv(req, P9_RLERROR); + v9fs_uint32_read(req, err); + v9fs_req_free(req); +} + /* size[4] Tversion tag[2] msize[4] version[s] */ static P9Req *v9fs_tversion(QVirtIO9P *v9p, uint32_t msize, const char *version) { @@ -427,6 +435,21 @@ static void fs_walk(QVirtIO9P *v9p) g_free(wqid); } +static void fs_walk_no_slash(QVirtIO9P *v9p) +{ + char *const wnames[] = { g_strdup(" /") }; + P9Req *req; + uint32_t err; + + fs_attach(v9p); + req = v9fs_twalk(v9p, 0, 1, 1, wnames); + v9fs_rlerror(req, &err); + + g_assert_cmpint(err, ==, ENOENT); + + g_free(wnames[0]); +} + typedef void (*v9fs_test_fn)(QVirtIO9P *v9p); static void v9fs_run_pci_test(gconstpointer data) @@ -453,6 +476,7 @@ int main(int argc, char **argv) v9fs_qtest_pci_add("/virtio/9p/pci/fs/version/basic", fs_version); v9fs_qtest_pci_add("/virtio/9p/pci/fs/attach/basic", fs_attach); v9fs_qtest_pci_add("/virtio/9p/pci/fs/walk/basic", fs_walk); + v9fs_qtest_pci_add("/virtio/9p/pci/fs/walk/no_slash", fs_walk_no_slash); return g_test_run(); } From a37c07022b44e4fc29238deca7588406d081e6c2 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 3 Jan 2017 17:28:44 +0100 Subject: [PATCH 13/13] tests: virtio-9p: ".." cannot be used to walk out of the shared directory According to the 9P spec at http://man.cat-v.org/plan_9/5/intro, the parent directory of the root directory of a server's tree is itself. This test hence checks that the qid of the root directory as returned by attach is the same as the qid of ".." when walking from the root directory. Signed-off-by: Greg Kurz --- tests/virtio-9p-test.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c index 83cb23b3af..060407b20e 100644 --- a/tests/virtio-9p-test.c +++ b/tests/virtio-9p-test.c @@ -450,6 +450,25 @@ static void fs_walk_no_slash(QVirtIO9P *v9p) g_free(wnames[0]); } +static void fs_walk_dotdot(QVirtIO9P *v9p) +{ + char *const wnames[] = { g_strdup("..") }; + v9fs_qid root_qid, *wqid; + P9Req *req; + + fs_version(v9p); + req = v9fs_tattach(v9p, 0, getuid()); + v9fs_rattach(req, &root_qid); + + req = v9fs_twalk(v9p, 0, 1, 1, wnames); + v9fs_rwalk(req, NULL, &wqid); /* We now we'll get one qid */ + + g_assert_cmpmem(&root_qid, 13, wqid[0], 13); + + g_free(wqid); + g_free(wnames[0]); +} + typedef void (*v9fs_test_fn)(QVirtIO9P *v9p); static void v9fs_run_pci_test(gconstpointer data) @@ -477,6 +496,8 @@ int main(int argc, char **argv) v9fs_qtest_pci_add("/virtio/9p/pci/fs/attach/basic", fs_attach); v9fs_qtest_pci_add("/virtio/9p/pci/fs/walk/basic", fs_walk); v9fs_qtest_pci_add("/virtio/9p/pci/fs/walk/no_slash", fs_walk_no_slash); + v9fs_qtest_pci_add("/virtio/9p/pci/fs/walk/dotdot_from_root", + fs_walk_dotdot); return g_test_run(); }