qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

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


      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 "qemu/module.h"
     17 #include "libqos/virtio-9p-client.h"
     18 
     19 #define twalk(...) v9fs_twalk((TWalkOpt) __VA_ARGS__)
     20 #define tversion(...) v9fs_tversion((TVersionOpt) __VA_ARGS__)
     21 #define tattach(...) v9fs_tattach((TAttachOpt) __VA_ARGS__)
     22 #define tgetattr(...) v9fs_tgetattr((TGetAttrOpt) __VA_ARGS__)
     23 #define treaddir(...) v9fs_treaddir((TReadDirOpt) __VA_ARGS__)
     24 #define tlopen(...) v9fs_tlopen((TLOpenOpt) __VA_ARGS__)
     25 #define twrite(...) v9fs_twrite((TWriteOpt) __VA_ARGS__)
     26 #define tflush(...) v9fs_tflush((TFlushOpt) __VA_ARGS__)
     27 #define tmkdir(...) v9fs_tmkdir((TMkdirOpt) __VA_ARGS__)
     28 #define tlcreate(...) v9fs_tlcreate((TlcreateOpt) __VA_ARGS__)
     29 #define tsymlink(...) v9fs_tsymlink((TsymlinkOpt) __VA_ARGS__)
     30 #define tlink(...) v9fs_tlink((TlinkOpt) __VA_ARGS__)
     31 #define tunlinkat(...) v9fs_tunlinkat((TunlinkatOpt) __VA_ARGS__)
     32 
     33 static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
     34 {
     35     QVirtio9P *v9p = obj;
     36     v9fs_set_allocator(t_alloc);
     37     size_t tag_len = qvirtio_config_readw(v9p->vdev, 0);
     38     g_autofree char *tag = NULL;
     39     int i;
     40 
     41     g_assert_cmpint(tag_len, ==, strlen(MOUNT_TAG));
     42 
     43     tag = g_malloc(tag_len);
     44     for (i = 0; i < tag_len; i++) {
     45         tag[i] = qvirtio_config_readb(v9p->vdev, i + 2);
     46     }
     47     g_assert_cmpmem(tag, tag_len, MOUNT_TAG, tag_len);
     48 }
     49 
     50 static inline bool is_same_qid(v9fs_qid a, v9fs_qid b)
     51 {
     52     /* don't compare QID version for checking for file ID equalness */
     53     return a[0] == b[0] && memcmp(&a[5], &b[5], 8) == 0;
     54 }
     55 
     56 static void fs_version(void *obj, void *data, QGuestAllocator *t_alloc)
     57 {
     58     v9fs_set_allocator(t_alloc);
     59     tversion({ .client = obj });
     60 }
     61 
     62 static void fs_attach(void *obj, void *data, QGuestAllocator *t_alloc)
     63 {
     64     v9fs_set_allocator(t_alloc);
     65     tattach({ .client = obj });
     66 }
     67 
     68 static void fs_walk(void *obj, void *data, QGuestAllocator *t_alloc)
     69 {
     70     QVirtio9P *v9p = obj;
     71     v9fs_set_allocator(t_alloc);
     72     char *wnames[P9_MAXWELEM];
     73     uint16_t nwqid;
     74     g_autofree v9fs_qid *wqid = NULL;
     75     int i;
     76 
     77     for (i = 0; i < P9_MAXWELEM; i++) {
     78         wnames[i] = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE, i);
     79     }
     80 
     81     tattach({ .client = v9p });
     82     twalk({
     83         .client = v9p, .fid = 0, .newfid = 1,
     84         .nwname = P9_MAXWELEM, .wnames = wnames,
     85         .rwalk = { .nwqid = &nwqid, .wqid = &wqid }
     86     });
     87 
     88     g_assert_cmpint(nwqid, ==, P9_MAXWELEM);
     89 
     90     for (i = 0; i < P9_MAXWELEM; i++) {
     91         g_free(wnames[i]);
     92     }
     93 }
     94 
     95 static bool fs_dirents_contain_name(struct V9fsDirent *e, const char* name)
     96 {
     97     for (; e; e = e->next) {
     98         if (!strcmp(e->name, name)) {
     99             return true;
    100         }
    101     }
    102     return false;
    103 }
    104 
    105 /* basic readdir test where reply fits into a single response message */
    106 static void fs_readdir(void *obj, void *data, QGuestAllocator *t_alloc)
    107 {
    108     QVirtio9P *v9p = obj;
    109     v9fs_set_allocator(t_alloc);
    110     char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR) };
    111     uint16_t nqid;
    112     v9fs_qid qid;
    113     uint32_t count, nentries;
    114     struct V9fsDirent *entries = NULL;
    115 
    116     tattach({ .client = v9p });
    117     twalk({
    118         .client = v9p, .fid = 0, .newfid = 1,
    119         .nwname = 1, .wnames = wnames, .rwalk.nwqid = &nqid
    120     });
    121     g_assert_cmpint(nqid, ==, 1);
    122 
    123     tlopen({
    124         .client = v9p, .fid = 1, .flags = O_DIRECTORY, .rlopen.qid = &qid
    125     });
    126 
    127     /*
    128      * submit count = msize - 11, because 11 is the header size of Rreaddir
    129      */
    130     treaddir({
    131         .client = v9p, .fid = 1, .offset = 0, .count = P9_MAX_SIZE - 11,
    132         .rreaddir = {
    133             .count = &count, .nentries = &nentries, .entries = &entries
    134         }
    135     });
    136 
    137     /*
    138      * Assuming msize (P9_MAX_SIZE) is large enough so we can retrieve all
    139      * dir entries with only one readdir request.
    140      */
    141     g_assert_cmpint(
    142         nentries, ==,
    143         QTEST_V9FS_SYNTH_READDIR_NFILES + 2 /* "." and ".." */
    144     );
    145 
    146     /*
    147      * Check all file names exist in returned entries, ignore their order
    148      * though.
    149      */
    150     g_assert_cmpint(fs_dirents_contain_name(entries, "."), ==, true);
    151     g_assert_cmpint(fs_dirents_contain_name(entries, ".."), ==, true);
    152     for (int i = 0; i < QTEST_V9FS_SYNTH_READDIR_NFILES; ++i) {
    153         g_autofree char *name =
    154             g_strdup_printf(QTEST_V9FS_SYNTH_READDIR_FILE, i);
    155         g_assert_cmpint(fs_dirents_contain_name(entries, name), ==, true);
    156     }
    157 
    158     v9fs_free_dirents(entries);
    159     g_free(wnames[0]);
    160 }
    161 
    162 /* readdir test where overall request is split over several messages */
    163 static void do_readdir_split(QVirtio9P *v9p, uint32_t count)
    164 {
    165     char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR) };
    166     uint16_t nqid;
    167     v9fs_qid qid;
    168     uint32_t nentries, npartialentries;
    169     struct V9fsDirent *entries, *tail, *partialentries;
    170     int fid;
    171     uint64_t offset;
    172 
    173     tattach({ .client = v9p });
    174 
    175     fid = 1;
    176     offset = 0;
    177     entries = NULL;
    178     nentries = 0;
    179     tail = NULL;
    180 
    181     twalk({
    182         .client = v9p, .fid = 0, .newfid = fid,
    183         .nwname = 1, .wnames = wnames, .rwalk.nwqid = &nqid
    184     });
    185     g_assert_cmpint(nqid, ==, 1);
    186 
    187     tlopen({
    188         .client = v9p, .fid = fid, .flags = O_DIRECTORY, .rlopen.qid = &qid
    189     });
    190 
    191     /*
    192      * send as many Treaddir requests as required to get all directory
    193      * entries
    194      */
    195     while (true) {
    196         npartialentries = 0;
    197         partialentries = NULL;
    198 
    199         treaddir({
    200             .client = v9p, .fid = fid, .offset = offset, .count = count,
    201             .rreaddir = {
    202                 .count = &count, .nentries = &npartialentries,
    203                 .entries = &partialentries
    204             }
    205         });
    206         if (npartialentries > 0 && partialentries) {
    207             if (!entries) {
    208                 entries = partialentries;
    209                 nentries = npartialentries;
    210                 tail = partialentries;
    211             } else {
    212                 tail->next = partialentries;
    213                 nentries += npartialentries;
    214             }
    215             while (tail->next) {
    216                 tail = tail->next;
    217             }
    218             offset = tail->offset;
    219         } else {
    220             break;
    221         }
    222     }
    223 
    224     g_assert_cmpint(
    225         nentries, ==,
    226         QTEST_V9FS_SYNTH_READDIR_NFILES + 2 /* "." and ".." */
    227     );
    228 
    229     /*
    230      * Check all file names exist in returned entries, ignore their order
    231      * though.
    232      */
    233     g_assert_cmpint(fs_dirents_contain_name(entries, "."), ==, true);
    234     g_assert_cmpint(fs_dirents_contain_name(entries, ".."), ==, true);
    235     for (int i = 0; i < QTEST_V9FS_SYNTH_READDIR_NFILES; ++i) {
    236         char *name = g_strdup_printf(QTEST_V9FS_SYNTH_READDIR_FILE, i);
    237         g_assert_cmpint(fs_dirents_contain_name(entries, name), ==, true);
    238         g_free(name);
    239     }
    240 
    241     v9fs_free_dirents(entries);
    242 
    243     g_free(wnames[0]);
    244 }
    245 
    246 static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc)
    247 {
    248     QVirtio9P *v9p = obj;
    249     v9fs_set_allocator(t_alloc);
    250     char *wnames[] = { g_strdup(" /") };
    251 
    252     tattach({ .client = v9p });
    253     twalk({
    254         .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames,
    255         .expectErr = ENOENT
    256     });
    257 
    258     g_free(wnames[0]);
    259 }
    260 
    261 static void fs_walk_nonexistent(void *obj, void *data, QGuestAllocator *t_alloc)
    262 {
    263     QVirtio9P *v9p = obj;
    264     v9fs_set_allocator(t_alloc);
    265 
    266     tattach({ .client = v9p });
    267     /*
    268      * The 9p2000 protocol spec says: "If the first element cannot be walked
    269      * for any reason, Rerror is returned."
    270      */
    271     twalk({ .client = v9p, .path = "non-existent", .expectErr = ENOENT });
    272 }
    273 
    274 static void fs_walk_2nd_nonexistent(void *obj, void *data,
    275                                     QGuestAllocator *t_alloc)
    276 {
    277     QVirtio9P *v9p = obj;
    278     v9fs_set_allocator(t_alloc);
    279     v9fs_qid root_qid;
    280     uint16_t nwqid;
    281     uint32_t fid;
    282     g_autofree v9fs_qid *wqid = NULL;
    283     g_autofree char *path = g_strdup_printf(
    284         QTEST_V9FS_SYNTH_WALK_FILE "/non-existent", 0
    285     );
    286 
    287     tattach({ .client = v9p, .rattach.qid = &root_qid });
    288     fid = twalk({
    289         .client = v9p, .path = path,
    290         .rwalk = { .nwqid = &nwqid, .wqid = &wqid }
    291     }).newfid;
    292     /*
    293      * The 9p2000 protocol spec says: "nwqid is therefore either nwname or the
    294      * index of the first elementwise walk that failed."
    295      */
    296     assert(nwqid == 1);
    297 
    298     /* returned QID wqid[0] is file ID of 1st subdir */
    299     g_assert(wqid && wqid[0] && !is_same_qid(root_qid, wqid[0]));
    300 
    301     /* expect fid being unaffected by walk above */
    302     tgetattr({
    303         .client = v9p, .fid = fid, .request_mask = P9_GETATTR_BASIC,
    304         .expectErr = ENOENT
    305     });
    306 }
    307 
    308 static void fs_walk_none(void *obj, void *data, QGuestAllocator *t_alloc)
    309 {
    310     QVirtio9P *v9p = obj;
    311     v9fs_set_allocator(t_alloc);
    312     v9fs_qid root_qid;
    313     g_autofree v9fs_qid *wqid = NULL;
    314     struct v9fs_attr attr;
    315 
    316     tversion({ .client = v9p });
    317     tattach({
    318         .client = v9p, .fid = 0, .n_uname = getuid(),
    319         .rattach.qid = &root_qid
    320     });
    321 
    322     twalk({
    323         .client = v9p, .fid = 0, .newfid = 1, .nwname = 0, .wnames = NULL,
    324         .rwalk.wqid = &wqid
    325     });
    326 
    327     /* special case: no QID is returned if nwname=0 was sent */
    328     g_assert(wqid == NULL);
    329 
    330     tgetattr({
    331         .client = v9p, .fid = 1, .request_mask = P9_GETATTR_BASIC,
    332         .rgetattr.attr = &attr
    333     });
    334 
    335     g_assert(is_same_qid(root_qid, attr.qid));
    336 }
    337 
    338 static void fs_walk_dotdot(void *obj, void *data, QGuestAllocator *t_alloc)
    339 {
    340     QVirtio9P *v9p = obj;
    341     v9fs_set_allocator(t_alloc);
    342     char *wnames[] = { g_strdup("..") };
    343     v9fs_qid root_qid;
    344     g_autofree v9fs_qid *wqid = NULL;
    345 
    346     tversion({ .client = v9p });
    347     tattach({
    348         .client = v9p, .fid = 0, .n_uname = getuid(),
    349         .rattach.qid = &root_qid
    350     });
    351 
    352     twalk({
    353         .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames,
    354         .rwalk.wqid = &wqid /* We now we'll get one qid */
    355     });
    356 
    357     g_assert_cmpmem(&root_qid, 13, wqid[0], 13);
    358 
    359     g_free(wnames[0]);
    360 }
    361 
    362 static void fs_lopen(void *obj, void *data, QGuestAllocator *t_alloc)
    363 {
    364     QVirtio9P *v9p = obj;
    365     v9fs_set_allocator(t_alloc);
    366     char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_LOPEN_FILE) };
    367 
    368     tattach({ .client = v9p });
    369     twalk({
    370         .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames
    371     });
    372 
    373     tlopen({ .client = v9p, .fid = 1, .flags = O_WRONLY });
    374 
    375     g_free(wnames[0]);
    376 }
    377 
    378 static void fs_write(void *obj, void *data, QGuestAllocator *t_alloc)
    379 {
    380     QVirtio9P *v9p = obj;
    381     v9fs_set_allocator(t_alloc);
    382     static const uint32_t write_count = P9_MAX_SIZE / 2;
    383     char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_WRITE_FILE) };
    384     g_autofree char *buf = g_malloc0(write_count);
    385     uint32_t count;
    386 
    387     tattach({ .client = v9p });
    388     twalk({
    389         .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames
    390     });
    391 
    392     tlopen({ .client = v9p, .fid = 1, .flags = O_WRONLY });
    393 
    394     count = twrite({
    395         .client = v9p, .fid = 1, .offset = 0, .count = write_count,
    396         .data = buf
    397     }).count;
    398     g_assert_cmpint(count, ==, write_count);
    399 
    400     g_free(wnames[0]);
    401 }
    402 
    403 static void fs_flush_success(void *obj, void *data, QGuestAllocator *t_alloc)
    404 {
    405     QVirtio9P *v9p = obj;
    406     v9fs_set_allocator(t_alloc);
    407     char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) };
    408     P9Req *req, *flush_req;
    409     uint32_t reply_len;
    410     uint8_t should_block;
    411 
    412     tattach({ .client = v9p });
    413     twalk({
    414         .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames
    415     });
    416 
    417     tlopen({ .client = v9p, .fid = 1, .flags = O_WRONLY });
    418 
    419     /* This will cause the 9p server to try to write data to the backend,
    420      * until the write request gets cancelled.
    421      */
    422     should_block = 1;
    423     req = twrite({
    424         .client = v9p, .fid = 1, .offset = 0,
    425         .count = sizeof(should_block), .data = &should_block,
    426         .requestOnly = true
    427     }).req;
    428 
    429     flush_req = tflush({
    430         .client = v9p, .oldtag = req->tag, .tag = 1, .requestOnly = true
    431     }).req;
    432 
    433     /* The write request is supposed to be flushed: the server should just
    434      * mark the write request as used and reply to the flush request.
    435      */
    436     v9fs_req_wait_for_reply(req, &reply_len);
    437     g_assert_cmpint(reply_len, ==, 0);
    438     v9fs_req_free(req);
    439     v9fs_rflush(flush_req);
    440 
    441     g_free(wnames[0]);
    442 }
    443 
    444 static void fs_flush_ignored(void *obj, void *data, QGuestAllocator *t_alloc)
    445 {
    446     QVirtio9P *v9p = obj;
    447     v9fs_set_allocator(t_alloc);
    448     char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) };
    449     P9Req *req, *flush_req;
    450     uint32_t count;
    451     uint8_t should_block;
    452 
    453     tattach({ .client = v9p });
    454     twalk({
    455         .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames
    456     });
    457 
    458     tlopen({ .client = v9p, .fid = 1, .flags = O_WRONLY });
    459 
    460     /* This will cause the write request to complete right away, before it
    461      * could be actually cancelled.
    462      */
    463     should_block = 0;
    464     req = twrite({
    465         .client = v9p, .fid = 1, .offset = 0,
    466         .count = sizeof(should_block), .data = &should_block,
    467         .requestOnly = true
    468     }).req;
    469 
    470     flush_req = tflush({
    471         .client = v9p, .oldtag = req->tag, .tag = 1, .requestOnly = true
    472     }).req;
    473 
    474     /* The write request is supposed to complete. The server should
    475      * reply to the write request and the flush request.
    476      */
    477     v9fs_req_wait_for_reply(req, NULL);
    478     v9fs_rwrite(req, &count);
    479     g_assert_cmpint(count, ==, sizeof(should_block));
    480     v9fs_rflush(flush_req);
    481 
    482     g_free(wnames[0]);
    483 }
    484 
    485 static void fs_readdir_split_128(void *obj, void *data,
    486                                  QGuestAllocator *t_alloc)
    487 {
    488     v9fs_set_allocator(t_alloc);
    489     do_readdir_split(obj, 128);
    490 }
    491 
    492 static void fs_readdir_split_256(void *obj, void *data,
    493                                  QGuestAllocator *t_alloc)
    494 {
    495     v9fs_set_allocator(t_alloc);
    496     do_readdir_split(obj, 256);
    497 }
    498 
    499 static void fs_readdir_split_512(void *obj, void *data,
    500                                  QGuestAllocator *t_alloc)
    501 {
    502     v9fs_set_allocator(t_alloc);
    503     do_readdir_split(obj, 512);
    504 }
    505 
    506 
    507 /* tests using the 9pfs 'local' fs driver */
    508 
    509 static void fs_create_dir(void *obj, void *data, QGuestAllocator *t_alloc)
    510 {
    511     QVirtio9P *v9p = obj;
    512     v9fs_set_allocator(t_alloc);
    513     struct stat st;
    514     g_autofree char *root_path = virtio_9p_test_path("");
    515     g_autofree char *new_dir = virtio_9p_test_path("01");
    516 
    517     g_assert(root_path != NULL);
    518 
    519     tattach({ .client = v9p });
    520     tmkdir({ .client = v9p, .atPath = "/", .name = "01" });
    521 
    522     /* check if created directory really exists now ... */
    523     g_assert(stat(new_dir, &st) == 0);
    524     /* ... and is actually a directory */
    525     g_assert((st.st_mode & S_IFMT) == S_IFDIR);
    526 }
    527 
    528 static void fs_unlinkat_dir(void *obj, void *data, QGuestAllocator *t_alloc)
    529 {
    530     QVirtio9P *v9p = obj;
    531     v9fs_set_allocator(t_alloc);
    532     struct stat st;
    533     g_autofree char *root_path = virtio_9p_test_path("");
    534     g_autofree char *new_dir = virtio_9p_test_path("02");
    535 
    536     g_assert(root_path != NULL);
    537 
    538     tattach({ .client = v9p });
    539     tmkdir({ .client = v9p, .atPath = "/", .name = "02" });
    540 
    541     /* check if created directory really exists now ... */
    542     g_assert(stat(new_dir, &st) == 0);
    543     /* ... and is actually a directory */
    544     g_assert((st.st_mode & S_IFMT) == S_IFDIR);
    545 
    546     tunlinkat({
    547         .client = v9p, .atPath = "/", .name = "02",
    548         .flags = P9_DOTL_AT_REMOVEDIR
    549     });
    550     /* directory should be gone now */
    551     g_assert(stat(new_dir, &st) != 0);
    552 }
    553 
    554 static void fs_create_file(void *obj, void *data, QGuestAllocator *t_alloc)
    555 {
    556     QVirtio9P *v9p = obj;
    557     v9fs_set_allocator(t_alloc);
    558     struct stat st;
    559     g_autofree char *new_file = virtio_9p_test_path("03/1st_file");
    560 
    561     tattach({ .client = v9p });
    562     tmkdir({ .client = v9p, .atPath = "/", .name = "03" });
    563     tlcreate({ .client = v9p, .atPath = "03", .name = "1st_file" });
    564 
    565     /* check if created file exists now ... */
    566     g_assert(stat(new_file, &st) == 0);
    567     /* ... and is a regular file */
    568     g_assert((st.st_mode & S_IFMT) == S_IFREG);
    569 }
    570 
    571 static void fs_unlinkat_file(void *obj, void *data, QGuestAllocator *t_alloc)
    572 {
    573     QVirtio9P *v9p = obj;
    574     v9fs_set_allocator(t_alloc);
    575     struct stat st;
    576     g_autofree char *new_file = virtio_9p_test_path("04/doa_file");
    577 
    578     tattach({ .client = v9p });
    579     tmkdir({ .client = v9p, .atPath = "/", .name = "04" });
    580     tlcreate({ .client = v9p, .atPath = "04", .name = "doa_file" });
    581 
    582     /* check if created file exists now ... */
    583     g_assert(stat(new_file, &st) == 0);
    584     /* ... and is a regular file */
    585     g_assert((st.st_mode & S_IFMT) == S_IFREG);
    586 
    587     tunlinkat({ .client = v9p, .atPath = "04", .name = "doa_file" });
    588     /* file should be gone now */
    589     g_assert(stat(new_file, &st) != 0);
    590 }
    591 
    592 static void fs_symlink_file(void *obj, void *data, QGuestAllocator *t_alloc)
    593 {
    594     QVirtio9P *v9p = obj;
    595     v9fs_set_allocator(t_alloc);
    596     struct stat st;
    597     g_autofree char *real_file = virtio_9p_test_path("05/real_file");
    598     g_autofree char *symlink_file = virtio_9p_test_path("05/symlink_file");
    599 
    600     tattach({ .client = v9p });
    601     tmkdir({ .client = v9p, .atPath = "/", .name = "05" });
    602     tlcreate({ .client = v9p, .atPath = "05", .name = "real_file" });
    603     g_assert(stat(real_file, &st) == 0);
    604     g_assert((st.st_mode & S_IFMT) == S_IFREG);
    605 
    606     tsymlink({
    607         .client = v9p, .atPath = "05", .name = "symlink_file",
    608         .symtgt = "real_file"
    609     });
    610 
    611     /* check if created link exists now */
    612     g_assert(stat(symlink_file, &st) == 0);
    613 }
    614 
    615 static void fs_unlinkat_symlink(void *obj, void *data,
    616                                 QGuestAllocator *t_alloc)
    617 {
    618     QVirtio9P *v9p = obj;
    619     v9fs_set_allocator(t_alloc);
    620     struct stat st;
    621     g_autofree char *real_file = virtio_9p_test_path("06/real_file");
    622     g_autofree char *symlink_file = virtio_9p_test_path("06/symlink_file");
    623 
    624     tattach({ .client = v9p });
    625     tmkdir({ .client = v9p, .atPath = "/", .name = "06" });
    626     tlcreate({ .client = v9p, .atPath = "06", .name = "real_file" });
    627     g_assert(stat(real_file, &st) == 0);
    628     g_assert((st.st_mode & S_IFMT) == S_IFREG);
    629 
    630     tsymlink({
    631         .client = v9p, .atPath = "06", .name = "symlink_file",
    632         .symtgt = "real_file"
    633     });
    634     g_assert(stat(symlink_file, &st) == 0);
    635 
    636     tunlinkat({ .client = v9p, .atPath = "06", .name = "symlink_file" });
    637     /* symlink should be gone now */
    638     g_assert(stat(symlink_file, &st) != 0);
    639 }
    640 
    641 static void fs_hardlink_file(void *obj, void *data, QGuestAllocator *t_alloc)
    642 {
    643     QVirtio9P *v9p = obj;
    644     v9fs_set_allocator(t_alloc);
    645     struct stat st_real, st_link;
    646     g_autofree char *real_file = virtio_9p_test_path("07/real_file");
    647     g_autofree char *hardlink_file = virtio_9p_test_path("07/hardlink_file");
    648 
    649     tattach({ .client = v9p });
    650     tmkdir({ .client = v9p, .atPath = "/", .name = "07" });
    651     tlcreate({ .client = v9p, .atPath = "07", .name = "real_file" });
    652     g_assert(stat(real_file, &st_real) == 0);
    653     g_assert((st_real.st_mode & S_IFMT) == S_IFREG);
    654 
    655     tlink({
    656         .client = v9p, .atPath = "07", .name = "hardlink_file",
    657         .toPath = "07/real_file"
    658     });
    659 
    660     /* check if link exists now ... */
    661     g_assert(stat(hardlink_file, &st_link) == 0);
    662     /* ... and it's a hard link, right? */
    663     g_assert((st_link.st_mode & S_IFMT) == S_IFREG);
    664     g_assert(st_link.st_dev == st_real.st_dev);
    665     g_assert(st_link.st_ino == st_real.st_ino);
    666 }
    667 
    668 static void fs_unlinkat_hardlink(void *obj, void *data,
    669                                  QGuestAllocator *t_alloc)
    670 {
    671     QVirtio9P *v9p = obj;
    672     v9fs_set_allocator(t_alloc);
    673     struct stat st_real, st_link;
    674     g_autofree char *real_file = virtio_9p_test_path("08/real_file");
    675     g_autofree char *hardlink_file = virtio_9p_test_path("08/hardlink_file");
    676 
    677     tattach({ .client = v9p });
    678     tmkdir({ .client = v9p, .atPath = "/", .name = "08" });
    679     tlcreate({ .client = v9p, .atPath = "08", .name = "real_file" });
    680     g_assert(stat(real_file, &st_real) == 0);
    681     g_assert((st_real.st_mode & S_IFMT) == S_IFREG);
    682 
    683     tlink({
    684         .client = v9p, .atPath = "08", .name = "hardlink_file",
    685         .toPath = "08/real_file"
    686     });
    687     g_assert(stat(hardlink_file, &st_link) == 0);
    688 
    689     tunlinkat({ .client = v9p, .atPath = "08", .name = "hardlink_file" });
    690     /* symlink should be gone now */
    691     g_assert(stat(hardlink_file, &st_link) != 0);
    692     /* and old file should still exist */
    693     g_assert(stat(real_file, &st_real) == 0);
    694 }
    695 
    696 static void *assign_9p_local_driver(GString *cmd_line, void *arg)
    697 {
    698     virtio_9p_assign_local_driver(cmd_line, "security_model=mapped-xattr");
    699     return arg;
    700 }
    701 
    702 static void register_virtio_9p_test(void)
    703 {
    704 
    705     QOSGraphTestOptions opts = {
    706     };
    707 
    708     /* 9pfs test cases using the 'synth' filesystem driver */
    709     qos_add_test("synth/config", "virtio-9p", pci_config, &opts);
    710     qos_add_test("synth/version/basic", "virtio-9p", fs_version,  &opts);
    711     qos_add_test("synth/attach/basic", "virtio-9p", fs_attach,  &opts);
    712     qos_add_test("synth/walk/basic", "virtio-9p", fs_walk,  &opts);
    713     qos_add_test("synth/walk/no_slash", "virtio-9p", fs_walk_no_slash,
    714                   &opts);
    715     qos_add_test("synth/walk/none", "virtio-9p", fs_walk_none, &opts);
    716     qos_add_test("synth/walk/dotdot_from_root", "virtio-9p",
    717                  fs_walk_dotdot,  &opts);
    718     qos_add_test("synth/walk/non_existent", "virtio-9p", fs_walk_nonexistent,
    719                   &opts);
    720     qos_add_test("synth/walk/2nd_non_existent", "virtio-9p",
    721                  fs_walk_2nd_nonexistent, &opts);
    722     qos_add_test("synth/lopen/basic", "virtio-9p", fs_lopen,  &opts);
    723     qos_add_test("synth/write/basic", "virtio-9p", fs_write,  &opts);
    724     qos_add_test("synth/flush/success", "virtio-9p", fs_flush_success,
    725                   &opts);
    726     qos_add_test("synth/flush/ignored", "virtio-9p", fs_flush_ignored,
    727                   &opts);
    728     qos_add_test("synth/readdir/basic", "virtio-9p", fs_readdir,  &opts);
    729     qos_add_test("synth/readdir/split_512", "virtio-9p",
    730                  fs_readdir_split_512,  &opts);
    731     qos_add_test("synth/readdir/split_256", "virtio-9p",
    732                  fs_readdir_split_256,  &opts);
    733     qos_add_test("synth/readdir/split_128", "virtio-9p",
    734                  fs_readdir_split_128,  &opts);
    735 
    736 
    737     /* 9pfs test cases using the 'local' filesystem driver */
    738 
    739     /*
    740      * XXX: Until we are sure that these tests can run everywhere,
    741      * keep them as "slow" so that they aren't run with "make check".
    742      */
    743     if (!g_test_slow()) {
    744         return;
    745     }
    746 
    747     opts.before = assign_9p_local_driver;
    748     qos_add_test("local/config", "virtio-9p", pci_config,  &opts);
    749     qos_add_test("local/create_dir", "virtio-9p", fs_create_dir, &opts);
    750     qos_add_test("local/unlinkat_dir", "virtio-9p", fs_unlinkat_dir, &opts);
    751     qos_add_test("local/create_file", "virtio-9p", fs_create_file, &opts);
    752     qos_add_test("local/unlinkat_file", "virtio-9p", fs_unlinkat_file, &opts);
    753     qos_add_test("local/symlink_file", "virtio-9p", fs_symlink_file, &opts);
    754     qos_add_test("local/unlinkat_symlink", "virtio-9p", fs_unlinkat_symlink,
    755                  &opts);
    756     qos_add_test("local/hardlink_file", "virtio-9p", fs_hardlink_file, &opts);
    757     qos_add_test("local/unlinkat_hardlink", "virtio-9p", fs_unlinkat_hardlink,
    758                  &opts);
    759 }
    760 
    761 libqos_init(register_virtio_9p_test);
    762 
    763 static void __attribute__((constructor)) construct_9p_test(void)
    764 {
    765     /* make sure test dir for the 'local' tests exists */
    766     virtio_9p_create_local_test_dir();
    767 }
    768 
    769 static void __attribute__((destructor)) destruct_9p_test(void)
    770 {
    771     /* remove previously created test dir when test suite completed */
    772     virtio_9p_remove_local_test_dir();
    773 }