cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

virtio-9p-test.c (40503B)


      1/*
      2 * QTest testcase for VirtIO 9P
      3 *
      4 * Copyright (c) 2014 SUSE LINUX Products GmbH
      5 *
      6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
      7 * See the COPYING file in the top-level directory.
      8 */
      9
     10/*
     11 * Not so fast! You might want to read the 9p developer docs first:
     12 * https://wiki.qemu.org/Documentation/9p
     13 */
     14
     15#include "qemu/osdep.h"
     16#include "libqtest-single.h"
     17#include "qemu/module.h"
     18#include "hw/9pfs/9p.h"
     19#include "hw/9pfs/9p-synth.h"
     20#include "libqos/virtio-9p.h"
     21#include "libqos/qgraph.h"
     22
     23#define QVIRTIO_9P_TIMEOUT_US (10 * 1000 * 1000)
     24static QGuestAllocator *alloc;
     25
     26/*
     27 * Used to auto generate new fids. Start with arbitrary high value to avoid
     28 * collision with hard coded fids in basic test code.
     29 */
     30static uint32_t fid_generator = 1000;
     31
     32static uint32_t genfid(void)
     33{
     34    return fid_generator++;
     35}
     36
     37/**
     38 * Splits the @a in string by @a delim into individual (non empty) strings
     39 * and outputs them to @a out. The output array @a out is NULL terminated.
     40 *
     41 * Output array @a out must be freed by calling split_free().
     42 *
     43 * @returns number of individual elements in output array @a out (without the
     44 *          final NULL terminating element)
     45 */
     46static int split(const char *in, const char *delim, char ***out)
     47{
     48    int n = 0, i = 0;
     49    char *tmp, *p;
     50
     51    tmp = g_strdup(in);
     52    for (p = strtok(tmp, delim); p != NULL; p = strtok(NULL, delim)) {
     53        if (strlen(p) > 0) {
     54            ++n;
     55        }
     56    }
     57    g_free(tmp);
     58
     59    *out = g_new0(char *, n + 1); /* last element NULL delimiter */
     60
     61    tmp = g_strdup(in);
     62    for (p = strtok(tmp, delim); p != NULL; p = strtok(NULL, delim)) {
     63        if (strlen(p) > 0) {
     64            (*out)[i++] = g_strdup(p);
     65        }
     66    }
     67    g_free(tmp);
     68
     69    return n;
     70}
     71
     72static void split_free(char ***out)
     73{
     74    int i;
     75    for (i = 0; (*out)[i]; ++i) {
     76        g_free((*out)[i]);
     77    }
     78    g_free(*out);
     79    *out = NULL;
     80}
     81
     82static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
     83{
     84    QVirtio9P *v9p = obj;
     85    alloc = t_alloc;
     86    size_t tag_len = qvirtio_config_readw(v9p->vdev, 0);
     87    char *tag;
     88    int i;
     89
     90    g_assert_cmpint(tag_len, ==, strlen(MOUNT_TAG));
     91
     92    tag = g_malloc(tag_len);
     93    for (i = 0; i < tag_len; i++) {
     94        tag[i] = qvirtio_config_readb(v9p->vdev, i + 2);
     95    }
     96    g_assert_cmpmem(tag, tag_len, MOUNT_TAG, tag_len);
     97    g_free(tag);
     98}
     99
    100#define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */
    101
    102typedef struct {
    103    QTestState *qts;
    104    QVirtio9P *v9p;
    105    uint16_t tag;
    106    uint64_t t_msg;
    107    uint32_t t_size;
    108    uint64_t r_msg;
    109    /* No r_size, it is hardcoded to P9_MAX_SIZE */
    110    size_t t_off;
    111    size_t r_off;
    112    uint32_t free_head;
    113} P9Req;
    114
    115static void v9fs_memwrite(P9Req *req, const void *addr, size_t len)
    116{
    117    qtest_memwrite(req->qts, req->t_msg + req->t_off, addr, len);
    118    req->t_off += len;
    119}
    120
    121static void v9fs_memskip(P9Req *req, size_t len)
    122{
    123    req->r_off += len;
    124}
    125
    126static void v9fs_memread(P9Req *req, void *addr, size_t len)
    127{
    128    qtest_memread(req->qts, req->r_msg + req->r_off, addr, len);
    129    req->r_off += len;
    130}
    131
    132static void v9fs_uint8_read(P9Req *req, uint8_t *val)
    133{
    134    v9fs_memread(req, val, 1);
    135}
    136
    137static void v9fs_uint16_write(P9Req *req, uint16_t val)
    138{
    139    uint16_t le_val = cpu_to_le16(val);
    140
    141    v9fs_memwrite(req, &le_val, 2);
    142}
    143
    144static void v9fs_uint16_read(P9Req *req, uint16_t *val)
    145{
    146    v9fs_memread(req, val, 2);
    147    le16_to_cpus(val);
    148}
    149
    150static void v9fs_uint32_write(P9Req *req, uint32_t val)
    151{
    152    uint32_t le_val = cpu_to_le32(val);
    153
    154    v9fs_memwrite(req, &le_val, 4);
    155}
    156
    157static void v9fs_uint64_write(P9Req *req, uint64_t val)
    158{
    159    uint64_t le_val = cpu_to_le64(val);
    160
    161    v9fs_memwrite(req, &le_val, 8);
    162}
    163
    164static void v9fs_uint32_read(P9Req *req, uint32_t *val)
    165{
    166    v9fs_memread(req, val, 4);
    167    le32_to_cpus(val);
    168}
    169
    170static void v9fs_uint64_read(P9Req *req, uint64_t *val)
    171{
    172    v9fs_memread(req, val, 8);
    173    le64_to_cpus(val);
    174}
    175
    176/* len[2] string[len] */
    177static uint16_t v9fs_string_size(const char *string)
    178{
    179    size_t len = strlen(string);
    180
    181    g_assert_cmpint(len, <=, UINT16_MAX - 2);
    182
    183    return 2 + len;
    184}
    185
    186static void v9fs_string_write(P9Req *req, const char *string)
    187{
    188    int len = strlen(string);
    189
    190    g_assert_cmpint(len, <=, UINT16_MAX);
    191
    192    v9fs_uint16_write(req, (uint16_t) len);
    193    v9fs_memwrite(req, string, len);
    194}
    195
    196static void v9fs_string_read(P9Req *req, uint16_t *len, char **string)
    197{
    198    uint16_t local_len;
    199
    200    v9fs_uint16_read(req, &local_len);
    201    if (len) {
    202        *len = local_len;
    203    }
    204    if (string) {
    205        *string = g_malloc(local_len + 1);
    206        v9fs_memread(req, *string, local_len);
    207        (*string)[local_len] = 0;
    208    } else {
    209        v9fs_memskip(req, local_len);
    210    }
    211}
    212
    213 typedef struct {
    214    uint32_t size;
    215    uint8_t id;
    216    uint16_t tag;
    217} QEMU_PACKED P9Hdr;
    218
    219static P9Req *v9fs_req_init(QVirtio9P *v9p, uint32_t size, uint8_t id,
    220                            uint16_t tag)
    221{
    222    P9Req *req = g_new0(P9Req, 1);
    223    uint32_t total_size = 7; /* 9P header has well-known size of 7 bytes */
    224    P9Hdr hdr = {
    225        .id = id,
    226        .tag = cpu_to_le16(tag)
    227    };
    228
    229    g_assert_cmpint(total_size, <=, UINT32_MAX - size);
    230    total_size += size;
    231    hdr.size = cpu_to_le32(total_size);
    232
    233    g_assert_cmpint(total_size, <=, P9_MAX_SIZE);
    234
    235    req->qts = global_qtest;
    236    req->v9p = v9p;
    237    req->t_size = total_size;
    238    req->t_msg = guest_alloc(alloc, req->t_size);
    239    v9fs_memwrite(req, &hdr, 7);
    240    req->tag = tag;
    241    return req;
    242}
    243
    244static void v9fs_req_send(P9Req *req)
    245{
    246    QVirtio9P *v9p = req->v9p;
    247
    248    req->r_msg = guest_alloc(alloc, P9_MAX_SIZE);
    249    req->free_head = qvirtqueue_add(req->qts, v9p->vq, req->t_msg, req->t_size,
    250                                    false, true);
    251    qvirtqueue_add(req->qts, v9p->vq, req->r_msg, P9_MAX_SIZE, true, false);
    252    qvirtqueue_kick(req->qts, v9p->vdev, v9p->vq, req->free_head);
    253    req->t_off = 0;
    254}
    255
    256static const char *rmessage_name(uint8_t id)
    257{
    258    return
    259        id == P9_RLERROR ? "RLERROR" :
    260        id == P9_RVERSION ? "RVERSION" :
    261        id == P9_RATTACH ? "RATTACH" :
    262        id == P9_RWALK ? "RWALK" :
    263        id == P9_RLOPEN ? "RLOPEN" :
    264        id == P9_RWRITE ? "RWRITE" :
    265        id == P9_RMKDIR ? "RMKDIR" :
    266        id == P9_RLCREATE ? "RLCREATE" :
    267        id == P9_RSYMLINK ? "RSYMLINK" :
    268        id == P9_RLINK ? "RLINK" :
    269        id == P9_RUNLINKAT ? "RUNLINKAT" :
    270        id == P9_RFLUSH ? "RFLUSH" :
    271        id == P9_RREADDIR ? "READDIR" :
    272        "<unknown>";
    273}
    274
    275static void v9fs_req_wait_for_reply(P9Req *req, uint32_t *len)
    276{
    277    QVirtio9P *v9p = req->v9p;
    278
    279    qvirtio_wait_used_elem(req->qts, v9p->vdev, v9p->vq, req->free_head, len,
    280                           QVIRTIO_9P_TIMEOUT_US);
    281}
    282
    283static void v9fs_req_recv(P9Req *req, uint8_t id)
    284{
    285    P9Hdr hdr;
    286
    287    v9fs_memread(req, &hdr, 7);
    288    hdr.size = ldl_le_p(&hdr.size);
    289    hdr.tag = lduw_le_p(&hdr.tag);
    290
    291    g_assert_cmpint(hdr.size, >=, 7);
    292    g_assert_cmpint(hdr.size, <=, P9_MAX_SIZE);
    293    g_assert_cmpint(hdr.tag, ==, req->tag);
    294
    295    if (hdr.id != id) {
    296        g_printerr("Received response %d (%s) instead of %d (%s)\n",
    297                   hdr.id, rmessage_name(hdr.id), id, rmessage_name(id));
    298
    299        if (hdr.id == P9_RLERROR) {
    300            uint32_t err;
    301            v9fs_uint32_read(req, &err);
    302            g_printerr("Rlerror has errno %d (%s)\n", err, strerror(err));
    303        }
    304    }
    305    g_assert_cmpint(hdr.id, ==, id);
    306}
    307
    308static void v9fs_req_free(P9Req *req)
    309{
    310    guest_free(alloc, req->t_msg);
    311    guest_free(alloc, req->r_msg);
    312    g_free(req);
    313}
    314
    315/* size[4] Rlerror tag[2] ecode[4] */
    316static void v9fs_rlerror(P9Req *req, uint32_t *err)
    317{
    318    v9fs_req_recv(req, P9_RLERROR);
    319    v9fs_uint32_read(req, err);
    320    v9fs_req_free(req);
    321}
    322
    323/* size[4] Tversion tag[2] msize[4] version[s] */
    324static P9Req *v9fs_tversion(QVirtio9P *v9p, uint32_t msize, const char *version,
    325                            uint16_t tag)
    326{
    327    P9Req *req;
    328    uint32_t body_size = 4;
    329    uint16_t string_size = v9fs_string_size(version);
    330
    331    g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
    332    body_size += string_size;
    333    req = v9fs_req_init(v9p, body_size, P9_TVERSION, tag);
    334
    335    v9fs_uint32_write(req, msize);
    336    v9fs_string_write(req, version);
    337    v9fs_req_send(req);
    338    return req;
    339}
    340
    341/* size[4] Rversion tag[2] msize[4] version[s] */
    342static void v9fs_rversion(P9Req *req, uint16_t *len, char **version)
    343{
    344    uint32_t msize;
    345
    346    v9fs_req_recv(req, P9_RVERSION);
    347    v9fs_uint32_read(req, &msize);
    348
    349    g_assert_cmpint(msize, ==, P9_MAX_SIZE);
    350
    351    if (len || version) {
    352        v9fs_string_read(req, len, version);
    353    }
    354
    355    v9fs_req_free(req);
    356}
    357
    358/* size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] */
    359static P9Req *v9fs_tattach(QVirtio9P *v9p, uint32_t fid, uint32_t n_uname,
    360                           uint16_t tag)
    361{
    362    const char *uname = ""; /* ignored by QEMU */
    363    const char *aname = ""; /* ignored by QEMU */
    364    P9Req *req = v9fs_req_init(v9p, 4 + 4 + 2 + 2 + 4, P9_TATTACH, tag);
    365
    366    v9fs_uint32_write(req, fid);
    367    v9fs_uint32_write(req, P9_NOFID);
    368    v9fs_string_write(req, uname);
    369    v9fs_string_write(req, aname);
    370    v9fs_uint32_write(req, n_uname);
    371    v9fs_req_send(req);
    372    return req;
    373}
    374
    375typedef char v9fs_qid[13];
    376
    377/* size[4] Rattach tag[2] qid[13] */
    378static void v9fs_rattach(P9Req *req, v9fs_qid *qid)
    379{
    380    v9fs_req_recv(req, P9_RATTACH);
    381    if (qid) {
    382        v9fs_memread(req, qid, 13);
    383    }
    384    v9fs_req_free(req);
    385}
    386
    387/* size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) */
    388static P9Req *v9fs_twalk(QVirtio9P *v9p, uint32_t fid, uint32_t newfid,
    389                         uint16_t nwname, char *const wnames[], uint16_t tag)
    390{
    391    P9Req *req;
    392    int i;
    393    uint32_t body_size = 4 + 4 + 2;
    394
    395    for (i = 0; i < nwname; i++) {
    396        uint16_t wname_size = v9fs_string_size(wnames[i]);
    397
    398        g_assert_cmpint(body_size, <=, UINT32_MAX - wname_size);
    399        body_size += wname_size;
    400    }
    401    req = v9fs_req_init(v9p,  body_size, P9_TWALK, tag);
    402    v9fs_uint32_write(req, fid);
    403    v9fs_uint32_write(req, newfid);
    404    v9fs_uint16_write(req, nwname);
    405    for (i = 0; i < nwname; i++) {
    406        v9fs_string_write(req, wnames[i]);
    407    }
    408    v9fs_req_send(req);
    409    return req;
    410}
    411
    412/* size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) */
    413static void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid)
    414{
    415    uint16_t local_nwqid;
    416
    417    v9fs_req_recv(req, P9_RWALK);
    418    v9fs_uint16_read(req, &local_nwqid);
    419    if (nwqid) {
    420        *nwqid = local_nwqid;
    421    }
    422    if (wqid) {
    423        *wqid = g_malloc(local_nwqid * 13);
    424        v9fs_memread(req, *wqid, local_nwqid * 13);
    425    }
    426    v9fs_req_free(req);
    427}
    428
    429/* size[4] Treaddir tag[2] fid[4] offset[8] count[4] */
    430static P9Req *v9fs_treaddir(QVirtio9P *v9p, uint32_t fid, uint64_t offset,
    431                            uint32_t count, uint16_t tag)
    432{
    433    P9Req *req;
    434
    435    req = v9fs_req_init(v9p, 4 + 8 + 4, P9_TREADDIR, tag);
    436    v9fs_uint32_write(req, fid);
    437    v9fs_uint64_write(req, offset);
    438    v9fs_uint32_write(req, count);
    439    v9fs_req_send(req);
    440    return req;
    441}
    442
    443struct V9fsDirent {
    444    v9fs_qid qid;
    445    uint64_t offset;
    446    uint8_t type;
    447    char *name;
    448    struct V9fsDirent *next;
    449};
    450
    451/* size[4] Rreaddir tag[2] count[4] data[count] */
    452static void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries,
    453                          struct V9fsDirent **entries)
    454{
    455    uint32_t local_count;
    456    struct V9fsDirent *e = NULL;
    457    uint16_t slen;
    458    uint32_t n = 0;
    459
    460    v9fs_req_recv(req, P9_RREADDIR);
    461    v9fs_uint32_read(req, &local_count);
    462
    463    if (count) {
    464        *count = local_count;
    465    }
    466
    467    for (int32_t togo = (int32_t)local_count;
    468         togo >= 13 + 8 + 1 + 2;
    469         togo -= 13 + 8 + 1 + 2 + slen, ++n)
    470    {
    471        if (!e) {
    472            e = g_malloc(sizeof(struct V9fsDirent));
    473            if (entries) {
    474                *entries = e;
    475            }
    476        } else {
    477            e = e->next = g_malloc(sizeof(struct V9fsDirent));
    478        }
    479        e->next = NULL;
    480        /* qid[13] offset[8] type[1] name[s] */
    481        v9fs_memread(req, &e->qid, 13);
    482        v9fs_uint64_read(req, &e->offset);
    483        v9fs_uint8_read(req, &e->type);
    484        v9fs_string_read(req, &slen, &e->name);
    485    }
    486
    487    if (nentries) {
    488        *nentries = n;
    489    }
    490
    491    v9fs_req_free(req);
    492}
    493
    494static void v9fs_free_dirents(struct V9fsDirent *e)
    495{
    496    struct V9fsDirent *next = NULL;
    497
    498    for (; e; e = next) {
    499        next = e->next;
    500        g_free(e->name);
    501        g_free(e);
    502    }
    503}
    504
    505/* size[4] Tlopen tag[2] fid[4] flags[4] */
    506static P9Req *v9fs_tlopen(QVirtio9P *v9p, uint32_t fid, uint32_t flags,
    507                          uint16_t tag)
    508{
    509    P9Req *req;
    510
    511    req = v9fs_req_init(v9p,  4 + 4, P9_TLOPEN, tag);
    512    v9fs_uint32_write(req, fid);
    513    v9fs_uint32_write(req, flags);
    514    v9fs_req_send(req);
    515    return req;
    516}
    517
    518/* size[4] Rlopen tag[2] qid[13] iounit[4] */
    519static void v9fs_rlopen(P9Req *req, v9fs_qid *qid, uint32_t *iounit)
    520{
    521    v9fs_req_recv(req, P9_RLOPEN);
    522    if (qid) {
    523        v9fs_memread(req, qid, 13);
    524    } else {
    525        v9fs_memskip(req, 13);
    526    }
    527    if (iounit) {
    528        v9fs_uint32_read(req, iounit);
    529    }
    530    v9fs_req_free(req);
    531}
    532
    533/* size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] */
    534static P9Req *v9fs_twrite(QVirtio9P *v9p, uint32_t fid, uint64_t offset,
    535                          uint32_t count, const void *data, uint16_t tag)
    536{
    537    P9Req *req;
    538    uint32_t body_size = 4 + 8 + 4;
    539
    540    g_assert_cmpint(body_size, <=, UINT32_MAX - count);
    541    body_size += count;
    542    req = v9fs_req_init(v9p,  body_size, P9_TWRITE, tag);
    543    v9fs_uint32_write(req, fid);
    544    v9fs_uint64_write(req, offset);
    545    v9fs_uint32_write(req, count);
    546    v9fs_memwrite(req, data, count);
    547    v9fs_req_send(req);
    548    return req;
    549}
    550
    551/* size[4] Rwrite tag[2] count[4] */
    552static void v9fs_rwrite(P9Req *req, uint32_t *count)
    553{
    554    v9fs_req_recv(req, P9_RWRITE);
    555    if (count) {
    556        v9fs_uint32_read(req, count);
    557    }
    558    v9fs_req_free(req);
    559}
    560
    561/* size[4] Tflush tag[2] oldtag[2] */
    562static P9Req *v9fs_tflush(QVirtio9P *v9p, uint16_t oldtag, uint16_t tag)
    563{
    564    P9Req *req;
    565
    566    req = v9fs_req_init(v9p,  2, P9_TFLUSH, tag);
    567    v9fs_uint32_write(req, oldtag);
    568    v9fs_req_send(req);
    569    return req;
    570}
    571
    572/* size[4] Rflush tag[2] */
    573static void v9fs_rflush(P9Req *req)
    574{
    575    v9fs_req_recv(req, P9_RFLUSH);
    576    v9fs_req_free(req);
    577}
    578
    579static void do_version(QVirtio9P *v9p)
    580{
    581    const char *version = "9P2000.L";
    582    uint16_t server_len;
    583    char *server_version;
    584    P9Req *req;
    585
    586    req = v9fs_tversion(v9p, P9_MAX_SIZE, version, P9_NOTAG);
    587    v9fs_req_wait_for_reply(req, NULL);
    588    v9fs_rversion(req, &server_len, &server_version);
    589
    590    g_assert_cmpmem(server_version, server_len, version, strlen(version));
    591
    592    g_free(server_version);
    593}
    594
    595/* utility function: walk to requested dir and return fid for that dir */
    596static uint32_t do_walk(QVirtio9P *v9p, const char *path)
    597{
    598    char **wnames;
    599    P9Req *req;
    600    const uint32_t fid = genfid();
    601
    602    int nwnames = split(path, "/", &wnames);
    603
    604    req = v9fs_twalk(v9p, 0, fid, nwnames, wnames, 0);
    605    v9fs_req_wait_for_reply(req, NULL);
    606    v9fs_rwalk(req, NULL, NULL);
    607
    608    split_free(&wnames);
    609    return fid;
    610}
    611
    612static void fs_version(void *obj, void *data, QGuestAllocator *t_alloc)
    613{
    614    alloc = t_alloc;
    615    do_version(obj);
    616}
    617
    618static void do_attach(QVirtio9P *v9p)
    619{
    620    P9Req *req;
    621
    622    do_version(v9p);
    623    req = v9fs_tattach(v9p, 0, getuid(), 0);
    624    v9fs_req_wait_for_reply(req, NULL);
    625    v9fs_rattach(req, NULL);
    626}
    627
    628static void fs_attach(void *obj, void *data, QGuestAllocator *t_alloc)
    629{
    630    alloc = t_alloc;
    631    do_attach(obj);
    632}
    633
    634static void fs_walk(void *obj, void *data, QGuestAllocator *t_alloc)
    635{
    636    QVirtio9P *v9p = obj;
    637    alloc = t_alloc;
    638    char *wnames[P9_MAXWELEM];
    639    uint16_t nwqid;
    640    v9fs_qid *wqid;
    641    int i;
    642    P9Req *req;
    643
    644    for (i = 0; i < P9_MAXWELEM; i++) {
    645        wnames[i] = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE, i);
    646    }
    647
    648    do_attach(v9p);
    649    req = v9fs_twalk(v9p, 0, 1, P9_MAXWELEM, wnames, 0);
    650    v9fs_req_wait_for_reply(req, NULL);
    651    v9fs_rwalk(req, &nwqid, &wqid);
    652
    653    g_assert_cmpint(nwqid, ==, P9_MAXWELEM);
    654
    655    for (i = 0; i < P9_MAXWELEM; i++) {
    656        g_free(wnames[i]);
    657    }
    658
    659    g_free(wqid);
    660}
    661
    662static bool fs_dirents_contain_name(struct V9fsDirent *e, const char* name)
    663{
    664    for (; e; e = e->next) {
    665        if (!strcmp(e->name, name)) {
    666            return true;
    667        }
    668    }
    669    return false;
    670}
    671
    672/* size[4] Tmkdir tag[2] dfid[4] name[s] mode[4] gid[4] */
    673static P9Req *v9fs_tmkdir(QVirtio9P *v9p, uint32_t dfid, const char *name,
    674                          uint32_t mode, uint32_t gid, uint16_t tag)
    675{
    676    P9Req *req;
    677
    678    uint32_t body_size = 4 + 4 + 4;
    679    uint16_t string_size = v9fs_string_size(name);
    680
    681    g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
    682    body_size += string_size;
    683
    684    req = v9fs_req_init(v9p, body_size, P9_TMKDIR, tag);
    685    v9fs_uint32_write(req, dfid);
    686    v9fs_string_write(req, name);
    687    v9fs_uint32_write(req, mode);
    688    v9fs_uint32_write(req, gid);
    689    v9fs_req_send(req);
    690    return req;
    691}
    692
    693/* size[4] Rmkdir tag[2] qid[13] */
    694static void v9fs_rmkdir(P9Req *req, v9fs_qid *qid)
    695{
    696    v9fs_req_recv(req, P9_RMKDIR);
    697    if (qid) {
    698        v9fs_memread(req, qid, 13);
    699    } else {
    700        v9fs_memskip(req, 13);
    701    }
    702    v9fs_req_free(req);
    703}
    704
    705/* size[4] Tlcreate tag[2] fid[4] name[s] flags[4] mode[4] gid[4] */
    706static P9Req *v9fs_tlcreate(QVirtio9P *v9p, uint32_t fid, const char *name,
    707                            uint32_t flags, uint32_t mode, uint32_t gid,
    708                            uint16_t tag)
    709{
    710    P9Req *req;
    711
    712    uint32_t body_size = 4 + 4 + 4 + 4;
    713    uint16_t string_size = v9fs_string_size(name);
    714
    715    g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
    716    body_size += string_size;
    717
    718    req = v9fs_req_init(v9p, body_size, P9_TLCREATE, tag);
    719    v9fs_uint32_write(req, fid);
    720    v9fs_string_write(req, name);
    721    v9fs_uint32_write(req, flags);
    722    v9fs_uint32_write(req, mode);
    723    v9fs_uint32_write(req, gid);
    724    v9fs_req_send(req);
    725    return req;
    726}
    727
    728/* size[4] Rlcreate tag[2] qid[13] iounit[4] */
    729static void v9fs_rlcreate(P9Req *req, v9fs_qid *qid, uint32_t *iounit)
    730{
    731    v9fs_req_recv(req, P9_RLCREATE);
    732    if (qid) {
    733        v9fs_memread(req, qid, 13);
    734    } else {
    735        v9fs_memskip(req, 13);
    736    }
    737    if (iounit) {
    738        v9fs_uint32_read(req, iounit);
    739    }
    740    v9fs_req_free(req);
    741}
    742
    743/* size[4] Tsymlink tag[2] fid[4] name[s] symtgt[s] gid[4] */
    744static P9Req *v9fs_tsymlink(QVirtio9P *v9p, uint32_t fid, const char *name,
    745                            const char *symtgt, uint32_t gid, uint16_t tag)
    746{
    747    P9Req *req;
    748
    749    uint32_t body_size = 4 + 4;
    750    uint16_t string_size = v9fs_string_size(name) + v9fs_string_size(symtgt);
    751
    752    g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
    753    body_size += string_size;
    754
    755    req = v9fs_req_init(v9p, body_size, P9_TSYMLINK, tag);
    756    v9fs_uint32_write(req, fid);
    757    v9fs_string_write(req, name);
    758    v9fs_string_write(req, symtgt);
    759    v9fs_uint32_write(req, gid);
    760    v9fs_req_send(req);
    761    return req;
    762}
    763
    764/* size[4] Rsymlink tag[2] qid[13] */
    765static void v9fs_rsymlink(P9Req *req, v9fs_qid *qid)
    766{
    767    v9fs_req_recv(req, P9_RSYMLINK);
    768    if (qid) {
    769        v9fs_memread(req, qid, 13);
    770    } else {
    771        v9fs_memskip(req, 13);
    772    }
    773    v9fs_req_free(req);
    774}
    775
    776/* size[4] Tlink tag[2] dfid[4] fid[4] name[s] */
    777static P9Req *v9fs_tlink(QVirtio9P *v9p, uint32_t dfid, uint32_t fid,
    778                         const char *name, uint16_t tag)
    779{
    780    P9Req *req;
    781
    782    uint32_t body_size = 4 + 4;
    783    uint16_t string_size = v9fs_string_size(name);
    784
    785    g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
    786    body_size += string_size;
    787
    788    req = v9fs_req_init(v9p, body_size, P9_TLINK, tag);
    789    v9fs_uint32_write(req, dfid);
    790    v9fs_uint32_write(req, fid);
    791    v9fs_string_write(req, name);
    792    v9fs_req_send(req);
    793    return req;
    794}
    795
    796/* size[4] Rlink tag[2] */
    797static void v9fs_rlink(P9Req *req)
    798{
    799    v9fs_req_recv(req, P9_RLINK);
    800    v9fs_req_free(req);
    801}
    802
    803/* size[4] Tunlinkat tag[2] dirfd[4] name[s] flags[4] */
    804static P9Req *v9fs_tunlinkat(QVirtio9P *v9p, uint32_t dirfd, const char *name,
    805                             uint32_t flags, uint16_t tag)
    806{
    807    P9Req *req;
    808
    809    uint32_t body_size = 4 + 4;
    810    uint16_t string_size = v9fs_string_size(name);
    811
    812    g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
    813    body_size += string_size;
    814
    815    req = v9fs_req_init(v9p, body_size, P9_TUNLINKAT, tag);
    816    v9fs_uint32_write(req, dirfd);
    817    v9fs_string_write(req, name);
    818    v9fs_uint32_write(req, flags);
    819    v9fs_req_send(req);
    820    return req;
    821}
    822
    823/* size[4] Runlinkat tag[2] */
    824static void v9fs_runlinkat(P9Req *req)
    825{
    826    v9fs_req_recv(req, P9_RUNLINKAT);
    827    v9fs_req_free(req);
    828}
    829
    830/* basic readdir test where reply fits into a single response message */
    831static void fs_readdir(void *obj, void *data, QGuestAllocator *t_alloc)
    832{
    833    QVirtio9P *v9p = obj;
    834    alloc = t_alloc;
    835    char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR) };
    836    uint16_t nqid;
    837    v9fs_qid qid;
    838    uint32_t count, nentries;
    839    struct V9fsDirent *entries = NULL;
    840    P9Req *req;
    841
    842    do_attach(v9p);
    843    req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
    844    v9fs_req_wait_for_reply(req, NULL);
    845    v9fs_rwalk(req, &nqid, NULL);
    846    g_assert_cmpint(nqid, ==, 1);
    847
    848    req = v9fs_tlopen(v9p, 1, O_DIRECTORY, 0);
    849    v9fs_req_wait_for_reply(req, NULL);
    850    v9fs_rlopen(req, &qid, NULL);
    851
    852    /*
    853     * submit count = msize - 11, because 11 is the header size of Rreaddir
    854     */
    855    req = v9fs_treaddir(v9p, 1, 0, P9_MAX_SIZE - 11, 0);
    856    v9fs_req_wait_for_reply(req, NULL);
    857    v9fs_rreaddir(req, &count, &nentries, &entries);
    858
    859    /*
    860     * Assuming msize (P9_MAX_SIZE) is large enough so we can retrieve all
    861     * dir entries with only one readdir request.
    862     */
    863    g_assert_cmpint(
    864        nentries, ==,
    865        QTEST_V9FS_SYNTH_READDIR_NFILES + 2 /* "." and ".." */
    866    );
    867
    868    /*
    869     * Check all file names exist in returned entries, ignore their order
    870     * though.
    871     */
    872    g_assert_cmpint(fs_dirents_contain_name(entries, "."), ==, true);
    873    g_assert_cmpint(fs_dirents_contain_name(entries, ".."), ==, true);
    874    for (int i = 0; i < QTEST_V9FS_SYNTH_READDIR_NFILES; ++i) {
    875        char *name = g_strdup_printf(QTEST_V9FS_SYNTH_READDIR_FILE, i);
    876        g_assert_cmpint(fs_dirents_contain_name(entries, name), ==, true);
    877        g_free(name);
    878    }
    879
    880    v9fs_free_dirents(entries);
    881    g_free(wnames[0]);
    882}
    883
    884/* readdir test where overall request is split over several messages */
    885static void do_readdir_split(QVirtio9P *v9p, uint32_t count)
    886{
    887    char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR) };
    888    uint16_t nqid;
    889    v9fs_qid qid;
    890    uint32_t nentries, npartialentries;
    891    struct V9fsDirent *entries, *tail, *partialentries;
    892    P9Req *req;
    893    int fid;
    894    uint64_t offset;
    895
    896    do_attach(v9p);
    897
    898    fid = 1;
    899    offset = 0;
    900    entries = NULL;
    901    nentries = 0;
    902    tail = NULL;
    903
    904    req = v9fs_twalk(v9p, 0, fid, 1, wnames, 0);
    905    v9fs_req_wait_for_reply(req, NULL);
    906    v9fs_rwalk(req, &nqid, NULL);
    907    g_assert_cmpint(nqid, ==, 1);
    908
    909    req = v9fs_tlopen(v9p, fid, O_DIRECTORY, 0);
    910    v9fs_req_wait_for_reply(req, NULL);
    911    v9fs_rlopen(req, &qid, NULL);
    912
    913    /*
    914     * send as many Treaddir requests as required to get all directory
    915     * entries
    916     */
    917    while (true) {
    918        npartialentries = 0;
    919        partialentries = NULL;
    920
    921        req = v9fs_treaddir(v9p, fid, offset, count, 0);
    922        v9fs_req_wait_for_reply(req, NULL);
    923        v9fs_rreaddir(req, &count, &npartialentries, &partialentries);
    924        if (npartialentries > 0 && partialentries) {
    925            if (!entries) {
    926                entries = partialentries;
    927                nentries = npartialentries;
    928                tail = partialentries;
    929            } else {
    930                tail->next = partialentries;
    931                nentries += npartialentries;
    932            }
    933            while (tail->next) {
    934                tail = tail->next;
    935            }
    936            offset = tail->offset;
    937        } else {
    938            break;
    939        }
    940    }
    941
    942    g_assert_cmpint(
    943        nentries, ==,
    944        QTEST_V9FS_SYNTH_READDIR_NFILES + 2 /* "." and ".." */
    945    );
    946
    947    /*
    948     * Check all file names exist in returned entries, ignore their order
    949     * though.
    950     */
    951    g_assert_cmpint(fs_dirents_contain_name(entries, "."), ==, true);
    952    g_assert_cmpint(fs_dirents_contain_name(entries, ".."), ==, true);
    953    for (int i = 0; i < QTEST_V9FS_SYNTH_READDIR_NFILES; ++i) {
    954        char *name = g_strdup_printf(QTEST_V9FS_SYNTH_READDIR_FILE, i);
    955        g_assert_cmpint(fs_dirents_contain_name(entries, name), ==, true);
    956        g_free(name);
    957    }
    958
    959    v9fs_free_dirents(entries);
    960
    961    g_free(wnames[0]);
    962}
    963
    964static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc)
    965{
    966    QVirtio9P *v9p = obj;
    967    alloc = t_alloc;
    968    char *const wnames[] = { g_strdup(" /") };
    969    P9Req *req;
    970    uint32_t err;
    971
    972    do_attach(v9p);
    973    req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
    974    v9fs_req_wait_for_reply(req, NULL);
    975    v9fs_rlerror(req, &err);
    976
    977    g_assert_cmpint(err, ==, ENOENT);
    978
    979    g_free(wnames[0]);
    980}
    981
    982static void fs_walk_dotdot(void *obj, void *data, QGuestAllocator *t_alloc)
    983{
    984    QVirtio9P *v9p = obj;
    985    alloc = t_alloc;
    986    char *const wnames[] = { g_strdup("..") };
    987    v9fs_qid root_qid, *wqid;
    988    P9Req *req;
    989
    990    do_version(v9p);
    991    req = v9fs_tattach(v9p, 0, getuid(), 0);
    992    v9fs_req_wait_for_reply(req, NULL);
    993    v9fs_rattach(req, &root_qid);
    994
    995    req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
    996    v9fs_req_wait_for_reply(req, NULL);
    997    v9fs_rwalk(req, NULL, &wqid); /* We now we'll get one qid */
    998
    999    g_assert_cmpmem(&root_qid, 13, wqid[0], 13);
   1000
   1001    g_free(wqid);
   1002    g_free(wnames[0]);
   1003}
   1004
   1005static void fs_lopen(void *obj, void *data, QGuestAllocator *t_alloc)
   1006{
   1007    QVirtio9P *v9p = obj;
   1008    alloc = t_alloc;
   1009    char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_LOPEN_FILE) };
   1010    P9Req *req;
   1011
   1012    do_attach(v9p);
   1013    req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
   1014    v9fs_req_wait_for_reply(req, NULL);
   1015    v9fs_rwalk(req, NULL, NULL);
   1016
   1017    req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
   1018    v9fs_req_wait_for_reply(req, NULL);
   1019    v9fs_rlopen(req, NULL, NULL);
   1020
   1021    g_free(wnames[0]);
   1022}
   1023
   1024static void fs_write(void *obj, void *data, QGuestAllocator *t_alloc)
   1025{
   1026    QVirtio9P *v9p = obj;
   1027    alloc = t_alloc;
   1028    static const uint32_t write_count = P9_MAX_SIZE / 2;
   1029    char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_WRITE_FILE) };
   1030    char *buf = g_malloc0(write_count);
   1031    uint32_t count;
   1032    P9Req *req;
   1033
   1034    do_attach(v9p);
   1035    req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
   1036    v9fs_req_wait_for_reply(req, NULL);
   1037    v9fs_rwalk(req, NULL, NULL);
   1038
   1039    req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
   1040    v9fs_req_wait_for_reply(req, NULL);
   1041    v9fs_rlopen(req, NULL, NULL);
   1042
   1043    req = v9fs_twrite(v9p, 1, 0, write_count, buf, 0);
   1044    v9fs_req_wait_for_reply(req, NULL);
   1045    v9fs_rwrite(req, &count);
   1046    g_assert_cmpint(count, ==, write_count);
   1047
   1048    g_free(buf);
   1049    g_free(wnames[0]);
   1050}
   1051
   1052static void fs_flush_success(void *obj, void *data, QGuestAllocator *t_alloc)
   1053{
   1054    QVirtio9P *v9p = obj;
   1055    alloc = t_alloc;
   1056    char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) };
   1057    P9Req *req, *flush_req;
   1058    uint32_t reply_len;
   1059    uint8_t should_block;
   1060
   1061    do_attach(v9p);
   1062    req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
   1063    v9fs_req_wait_for_reply(req, NULL);
   1064    v9fs_rwalk(req, NULL, NULL);
   1065
   1066    req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
   1067    v9fs_req_wait_for_reply(req, NULL);
   1068    v9fs_rlopen(req, NULL, NULL);
   1069
   1070    /* This will cause the 9p server to try to write data to the backend,
   1071     * until the write request gets cancelled.
   1072     */
   1073    should_block = 1;
   1074    req = v9fs_twrite(v9p, 1, 0, sizeof(should_block), &should_block, 0);
   1075
   1076    flush_req = v9fs_tflush(v9p, req->tag, 1);
   1077
   1078    /* The write request is supposed to be flushed: the server should just
   1079     * mark the write request as used and reply to the flush request.
   1080     */
   1081    v9fs_req_wait_for_reply(req, &reply_len);
   1082    g_assert_cmpint(reply_len, ==, 0);
   1083    v9fs_req_free(req);
   1084    v9fs_rflush(flush_req);
   1085
   1086    g_free(wnames[0]);
   1087}
   1088
   1089static void fs_flush_ignored(void *obj, void *data, QGuestAllocator *t_alloc)
   1090{
   1091    QVirtio9P *v9p = obj;
   1092    alloc = t_alloc;
   1093    char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) };
   1094    P9Req *req, *flush_req;
   1095    uint32_t count;
   1096    uint8_t should_block;
   1097
   1098    do_attach(v9p);
   1099    req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
   1100    v9fs_req_wait_for_reply(req, NULL);
   1101    v9fs_rwalk(req, NULL, NULL);
   1102
   1103    req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
   1104    v9fs_req_wait_for_reply(req, NULL);
   1105    v9fs_rlopen(req, NULL, NULL);
   1106
   1107    /* This will cause the write request to complete right away, before it
   1108     * could be actually cancelled.
   1109     */
   1110    should_block = 0;
   1111    req = v9fs_twrite(v9p, 1, 0, sizeof(should_block), &should_block, 0);
   1112
   1113    flush_req = v9fs_tflush(v9p, req->tag, 1);
   1114
   1115    /* The write request is supposed to complete. The server should
   1116     * reply to the write request and the flush request.
   1117     */
   1118    v9fs_req_wait_for_reply(req, NULL);
   1119    v9fs_rwrite(req, &count);
   1120    g_assert_cmpint(count, ==, sizeof(should_block));
   1121    v9fs_rflush(flush_req);
   1122
   1123    g_free(wnames[0]);
   1124}
   1125
   1126static void do_mkdir(QVirtio9P *v9p, const char *path, const char *cname)
   1127{
   1128    char *const name = g_strdup(cname);
   1129    uint32_t fid;
   1130    P9Req *req;
   1131
   1132    fid = do_walk(v9p, path);
   1133
   1134    req = v9fs_tmkdir(v9p, fid, name, 0750, 0, 0);
   1135    v9fs_req_wait_for_reply(req, NULL);
   1136    v9fs_rmkdir(req, NULL);
   1137
   1138    g_free(name);
   1139}
   1140
   1141/* create a regular file with Tlcreate and return file's fid */
   1142static uint32_t do_lcreate(QVirtio9P *v9p, const char *path,
   1143                           const char *cname)
   1144{
   1145    char *const name = g_strdup(cname);
   1146    uint32_t fid;
   1147    P9Req *req;
   1148
   1149    fid = do_walk(v9p, path);
   1150
   1151    req = v9fs_tlcreate(v9p, fid, name, 0, 0750, 0, 0);
   1152    v9fs_req_wait_for_reply(req, NULL);
   1153    v9fs_rlcreate(req, NULL, NULL);
   1154
   1155    g_free(name);
   1156    return fid;
   1157}
   1158
   1159/* create symlink named @a clink in directory @a path pointing to @a to */
   1160static void do_symlink(QVirtio9P *v9p, const char *path, const char *clink,
   1161                       const char *to)
   1162{
   1163    char *const name = g_strdup(clink);
   1164    char *const dst = g_strdup(to);
   1165    uint32_t fid;
   1166    P9Req *req;
   1167
   1168    fid = do_walk(v9p, path);
   1169
   1170    req = v9fs_tsymlink(v9p, fid, name, dst, 0, 0);
   1171    v9fs_req_wait_for_reply(req, NULL);
   1172    v9fs_rsymlink(req, NULL);
   1173
   1174    g_free(dst);
   1175    g_free(name);
   1176}
   1177
   1178/* create a hard link named @a clink in directory @a path pointing to @a to */
   1179static void do_hardlink(QVirtio9P *v9p, const char *path, const char *clink,
   1180                        const char *to)
   1181{
   1182    uint32_t dfid, fid;
   1183    P9Req *req;
   1184
   1185    dfid = do_walk(v9p, path);
   1186    fid = do_walk(v9p, to);
   1187
   1188    req = v9fs_tlink(v9p, dfid, fid, clink, 0);
   1189    v9fs_req_wait_for_reply(req, NULL);
   1190    v9fs_rlink(req);
   1191}
   1192
   1193static void do_unlinkat(QVirtio9P *v9p, const char *atpath, const char *rpath,
   1194                        uint32_t flags)
   1195{
   1196    char *const name = g_strdup(rpath);
   1197    uint32_t fid;
   1198    P9Req *req;
   1199
   1200    fid = do_walk(v9p, atpath);
   1201
   1202    req = v9fs_tunlinkat(v9p, fid, name, flags, 0);
   1203    v9fs_req_wait_for_reply(req, NULL);
   1204    v9fs_runlinkat(req);
   1205
   1206    g_free(name);
   1207}
   1208
   1209static void fs_readdir_split_128(void *obj, void *data,
   1210                                 QGuestAllocator *t_alloc)
   1211{
   1212    alloc = t_alloc;
   1213    do_readdir_split(obj, 128);
   1214}
   1215
   1216static void fs_readdir_split_256(void *obj, void *data,
   1217                                 QGuestAllocator *t_alloc)
   1218{
   1219    alloc = t_alloc;
   1220    do_readdir_split(obj, 256);
   1221}
   1222
   1223static void fs_readdir_split_512(void *obj, void *data,
   1224                                 QGuestAllocator *t_alloc)
   1225{
   1226    alloc = t_alloc;
   1227    do_readdir_split(obj, 512);
   1228}
   1229
   1230
   1231/* tests using the 9pfs 'local' fs driver */
   1232
   1233static void fs_create_dir(void *obj, void *data, QGuestAllocator *t_alloc)
   1234{
   1235    QVirtio9P *v9p = obj;
   1236    alloc = t_alloc;
   1237    struct stat st;
   1238    char *root_path = virtio_9p_test_path("");
   1239    char *new_dir = virtio_9p_test_path("01");
   1240
   1241    g_assert(root_path != NULL);
   1242
   1243    do_attach(v9p);
   1244    do_mkdir(v9p, "/", "01");
   1245
   1246    /* check if created directory really exists now ... */
   1247    g_assert(stat(new_dir, &st) == 0);
   1248    /* ... and is actually a directory */
   1249    g_assert((st.st_mode & S_IFMT) == S_IFDIR);
   1250
   1251    g_free(new_dir);
   1252    g_free(root_path);
   1253}
   1254
   1255static void fs_unlinkat_dir(void *obj, void *data, QGuestAllocator *t_alloc)
   1256{
   1257    QVirtio9P *v9p = obj;
   1258    alloc = t_alloc;
   1259    struct stat st;
   1260    char *root_path = virtio_9p_test_path("");
   1261    char *new_dir = virtio_9p_test_path("02");
   1262
   1263    g_assert(root_path != NULL);
   1264
   1265    do_attach(v9p);
   1266    do_mkdir(v9p, "/", "02");
   1267
   1268    /* check if created directory really exists now ... */
   1269    g_assert(stat(new_dir, &st) == 0);
   1270    /* ... and is actually a directory */
   1271    g_assert((st.st_mode & S_IFMT) == S_IFDIR);
   1272
   1273    do_unlinkat(v9p, "/", "02", AT_REMOVEDIR);
   1274    /* directory should be gone now */
   1275    g_assert(stat(new_dir, &st) != 0);
   1276
   1277    g_free(new_dir);
   1278    g_free(root_path);
   1279}
   1280
   1281static void fs_create_file(void *obj, void *data, QGuestAllocator *t_alloc)
   1282{
   1283    QVirtio9P *v9p = obj;
   1284    alloc = t_alloc;
   1285    struct stat st;
   1286    char *new_file = virtio_9p_test_path("03/1st_file");
   1287
   1288    do_attach(v9p);
   1289    do_mkdir(v9p, "/", "03");
   1290    do_lcreate(v9p, "03", "1st_file");
   1291
   1292    /* check if created file exists now ... */
   1293    g_assert(stat(new_file, &st) == 0);
   1294    /* ... and is a regular file */
   1295    g_assert((st.st_mode & S_IFMT) == S_IFREG);
   1296
   1297    g_free(new_file);
   1298}
   1299
   1300static void fs_unlinkat_file(void *obj, void *data, QGuestAllocator *t_alloc)
   1301{
   1302    QVirtio9P *v9p = obj;
   1303    alloc = t_alloc;
   1304    struct stat st;
   1305    char *new_file = virtio_9p_test_path("04/doa_file");
   1306
   1307    do_attach(v9p);
   1308    do_mkdir(v9p, "/", "04");
   1309    do_lcreate(v9p, "04", "doa_file");
   1310
   1311    /* check if created file exists now ... */
   1312    g_assert(stat(new_file, &st) == 0);
   1313    /* ... and is a regular file */
   1314    g_assert((st.st_mode & S_IFMT) == S_IFREG);
   1315
   1316    do_unlinkat(v9p, "04", "doa_file", 0);
   1317    /* file should be gone now */
   1318    g_assert(stat(new_file, &st) != 0);
   1319
   1320    g_free(new_file);
   1321}
   1322
   1323static void fs_symlink_file(void *obj, void *data, QGuestAllocator *t_alloc)
   1324{
   1325    QVirtio9P *v9p = obj;
   1326    alloc = t_alloc;
   1327    struct stat st;
   1328    char *real_file = virtio_9p_test_path("05/real_file");
   1329    char *symlink_file = virtio_9p_test_path("05/symlink_file");
   1330
   1331    do_attach(v9p);
   1332    do_mkdir(v9p, "/", "05");
   1333    do_lcreate(v9p, "05", "real_file");
   1334    g_assert(stat(real_file, &st) == 0);
   1335    g_assert((st.st_mode & S_IFMT) == S_IFREG);
   1336
   1337    do_symlink(v9p, "05", "symlink_file", "real_file");
   1338
   1339    /* check if created link exists now */
   1340    g_assert(stat(symlink_file, &st) == 0);
   1341
   1342    g_free(symlink_file);
   1343    g_free(real_file);
   1344}
   1345
   1346static void fs_unlinkat_symlink(void *obj, void *data,
   1347                                QGuestAllocator *t_alloc)
   1348{
   1349    QVirtio9P *v9p = obj;
   1350    alloc = t_alloc;
   1351    struct stat st;
   1352    char *real_file = virtio_9p_test_path("06/real_file");
   1353    char *symlink_file = virtio_9p_test_path("06/symlink_file");
   1354
   1355    do_attach(v9p);
   1356    do_mkdir(v9p, "/", "06");
   1357    do_lcreate(v9p, "06", "real_file");
   1358    g_assert(stat(real_file, &st) == 0);
   1359    g_assert((st.st_mode & S_IFMT) == S_IFREG);
   1360
   1361    do_symlink(v9p, "06", "symlink_file", "real_file");
   1362    g_assert(stat(symlink_file, &st) == 0);
   1363
   1364    do_unlinkat(v9p, "06", "symlink_file", 0);
   1365    /* symlink should be gone now */
   1366    g_assert(stat(symlink_file, &st) != 0);
   1367
   1368    g_free(symlink_file);
   1369    g_free(real_file);
   1370}
   1371
   1372static void fs_hardlink_file(void *obj, void *data, QGuestAllocator *t_alloc)
   1373{
   1374    QVirtio9P *v9p = obj;
   1375    alloc = t_alloc;
   1376    struct stat st_real, st_link;
   1377    char *real_file = virtio_9p_test_path("07/real_file");
   1378    char *hardlink_file = virtio_9p_test_path("07/hardlink_file");
   1379
   1380    do_attach(v9p);
   1381    do_mkdir(v9p, "/", "07");
   1382    do_lcreate(v9p, "07", "real_file");
   1383    g_assert(stat(real_file, &st_real) == 0);
   1384    g_assert((st_real.st_mode & S_IFMT) == S_IFREG);
   1385
   1386    do_hardlink(v9p, "07", "hardlink_file", "07/real_file");
   1387
   1388    /* check if link exists now ... */
   1389    g_assert(stat(hardlink_file, &st_link) == 0);
   1390    /* ... and it's a hard link, right? */
   1391    g_assert((st_link.st_mode & S_IFMT) == S_IFREG);
   1392    g_assert(st_link.st_dev == st_real.st_dev);
   1393    g_assert(st_link.st_ino == st_real.st_ino);
   1394
   1395    g_free(hardlink_file);
   1396    g_free(real_file);
   1397}
   1398
   1399static void fs_unlinkat_hardlink(void *obj, void *data,
   1400                                 QGuestAllocator *t_alloc)
   1401{
   1402    QVirtio9P *v9p = obj;
   1403    alloc = t_alloc;
   1404    struct stat st_real, st_link;
   1405    char *real_file = virtio_9p_test_path("08/real_file");
   1406    char *hardlink_file = virtio_9p_test_path("08/hardlink_file");
   1407
   1408    do_attach(v9p);
   1409    do_mkdir(v9p, "/", "08");
   1410    do_lcreate(v9p, "08", "real_file");
   1411    g_assert(stat(real_file, &st_real) == 0);
   1412    g_assert((st_real.st_mode & S_IFMT) == S_IFREG);
   1413
   1414    do_hardlink(v9p, "08", "hardlink_file", "08/real_file");
   1415    g_assert(stat(hardlink_file, &st_link) == 0);
   1416
   1417    do_unlinkat(v9p, "08", "hardlink_file", 0);
   1418    /* symlink should be gone now */
   1419    g_assert(stat(hardlink_file, &st_link) != 0);
   1420    /* and old file should still exist */
   1421    g_assert(stat(real_file, &st_real) == 0);
   1422
   1423    g_free(hardlink_file);
   1424    g_free(real_file);
   1425}
   1426
   1427static void *assign_9p_local_driver(GString *cmd_line, void *arg)
   1428{
   1429    virtio_9p_assign_local_driver(cmd_line, "security_model=mapped-xattr");
   1430    return arg;
   1431}
   1432
   1433static void register_virtio_9p_test(void)
   1434{
   1435
   1436    QOSGraphTestOptions opts = {
   1437    };
   1438
   1439    /* 9pfs test cases using the 'synth' filesystem driver */
   1440    qos_add_test("synth/config", "virtio-9p", pci_config, &opts);
   1441    qos_add_test("synth/version/basic", "virtio-9p", fs_version,  &opts);
   1442    qos_add_test("synth/attach/basic", "virtio-9p", fs_attach,  &opts);
   1443    qos_add_test("synth/walk/basic", "virtio-9p", fs_walk,  &opts);
   1444    qos_add_test("synth/walk/no_slash", "virtio-9p", fs_walk_no_slash,
   1445                  &opts);
   1446    qos_add_test("synth/walk/dotdot_from_root", "virtio-9p",
   1447                 fs_walk_dotdot,  &opts);
   1448    qos_add_test("synth/lopen/basic", "virtio-9p", fs_lopen,  &opts);
   1449    qos_add_test("synth/write/basic", "virtio-9p", fs_write,  &opts);
   1450    qos_add_test("synth/flush/success", "virtio-9p", fs_flush_success,
   1451                  &opts);
   1452    qos_add_test("synth/flush/ignored", "virtio-9p", fs_flush_ignored,
   1453                  &opts);
   1454    qos_add_test("synth/readdir/basic", "virtio-9p", fs_readdir,  &opts);
   1455    qos_add_test("synth/readdir/split_512", "virtio-9p",
   1456                 fs_readdir_split_512,  &opts);
   1457    qos_add_test("synth/readdir/split_256", "virtio-9p",
   1458                 fs_readdir_split_256,  &opts);
   1459    qos_add_test("synth/readdir/split_128", "virtio-9p",
   1460                 fs_readdir_split_128,  &opts);
   1461
   1462
   1463    /* 9pfs test cases using the 'local' filesystem driver */
   1464
   1465    /*
   1466     * XXX: Until we are sure that these tests can run everywhere,
   1467     * keep them as "slow" so that they aren't run with "make check".
   1468     */
   1469    if (!g_test_slow()) {
   1470        return;
   1471    }
   1472
   1473    opts.before = assign_9p_local_driver;
   1474    qos_add_test("local/config", "virtio-9p", pci_config,  &opts);
   1475    qos_add_test("local/create_dir", "virtio-9p", fs_create_dir, &opts);
   1476    qos_add_test("local/unlinkat_dir", "virtio-9p", fs_unlinkat_dir, &opts);
   1477    qos_add_test("local/create_file", "virtio-9p", fs_create_file, &opts);
   1478    qos_add_test("local/unlinkat_file", "virtio-9p", fs_unlinkat_file, &opts);
   1479    qos_add_test("local/symlink_file", "virtio-9p", fs_symlink_file, &opts);
   1480    qos_add_test("local/unlinkat_symlink", "virtio-9p", fs_unlinkat_symlink,
   1481                 &opts);
   1482    qos_add_test("local/hardlink_file", "virtio-9p", fs_hardlink_file, &opts);
   1483    qos_add_test("local/unlinkat_hardlink", "virtio-9p", fs_unlinkat_hardlink,
   1484                 &opts);
   1485}
   1486
   1487libqos_init(register_virtio_9p_test);
   1488
   1489static void __attribute__((constructor)) construct_9p_test(void)
   1490{
   1491    /* make sure test dir for the 'local' tests exists */
   1492    virtio_9p_create_local_test_dir();
   1493}
   1494
   1495static void __attribute__((destructor)) destruct_9p_test(void)
   1496{
   1497    /* remove previously created test dir when test suite completed */
   1498    virtio_9p_remove_local_test_dir();
   1499}