qemu/tests/qtest/virtio-9p-test.c
<<
>>
Prefs
   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}
1500