xref: /qemu/tests/qtest/virtio-9p-test.c (revision 3f3e9232207fc4f0e5cb5cf63f11ed9449efeefa) !
12d888c09SAndreas Färber /*
22d888c09SAndreas Färber  * QTest testcase for VirtIO 9P
32d888c09SAndreas Färber  *
42d888c09SAndreas Färber  * Copyright (c) 2014 SUSE LINUX Products GmbH
52d888c09SAndreas Färber  *
62d888c09SAndreas Färber  * This work is licensed under the terms of the GNU GPL, version 2 or later.
72d888c09SAndreas Färber  * See the COPYING file in the top-level directory.
82d888c09SAndreas Färber  */
92d888c09SAndreas Färber 
106f569084SChristian Schoenebeck /*
116f569084SChristian Schoenebeck  * Not so fast! You might want to read the 9p developer docs first:
126f569084SChristian Schoenebeck  * https://wiki.qemu.org/Documentation/9p
136f569084SChristian Schoenebeck  */
146f569084SChristian Schoenebeck 
15fbc04127SPeter Maydell #include "qemu/osdep.h"
160b8fa32fSMarkus Armbruster #include "qemu/module.h"
17684f9120SChristian Schoenebeck #include "libqos/virtio-9p-client.h"
1865b70fc7SGreg Kurz 
19569f3b63SChristian Schoenebeck #define twalk(...) v9fs_twalk((TWalkOpt) __VA_ARGS__)
20653daf38SChristian Schoenebeck 
21dfbe8b43SEmanuele Giuseppe Esposito static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
22557a4cc0SGreg Kurz {
23dfbe8b43SEmanuele Giuseppe Esposito     QVirtio9P *v9p = obj;
24684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
25dfbe8b43SEmanuele Giuseppe Esposito     size_t tag_len = qvirtio_config_readw(v9p->vdev, 0);
2665ceee0aSChristian Schoenebeck     g_autofree char *tag = NULL;
27557a4cc0SGreg Kurz     int i;
28557a4cc0SGreg Kurz 
29dfbe8b43SEmanuele Giuseppe Esposito     g_assert_cmpint(tag_len, ==, strlen(MOUNT_TAG));
30557a4cc0SGreg Kurz 
31557a4cc0SGreg Kurz     tag = g_malloc(tag_len);
32557a4cc0SGreg Kurz     for (i = 0; i < tag_len; i++) {
33dfbe8b43SEmanuele Giuseppe Esposito         tag[i] = qvirtio_config_readb(v9p->vdev, i + 2);
34557a4cc0SGreg Kurz     }
35dfbe8b43SEmanuele Giuseppe Esposito     g_assert_cmpmem(tag, tag_len, MOUNT_TAG, tag_len);
361211d81bSGreg Kurz }
37557a4cc0SGreg Kurz 
38a6821b82SChristian Schoenebeck static inline bool is_same_qid(v9fs_qid a, v9fs_qid b)
39a6821b82SChristian Schoenebeck {
40a6821b82SChristian Schoenebeck     /* don't compare QID version for checking for file ID equalness */
41a6821b82SChristian Schoenebeck     return a[0] == b[0] && memcmp(&a[5], &b[5], 8) == 0;
42a6821b82SChristian Schoenebeck }
43a6821b82SChristian Schoenebeck 
441c450e6eSGreg Kurz static void do_version(QVirtio9P *v9p)
456cc9906bSGreg Kurz {
466cc9906bSGreg Kurz     const char *version = "9P2000.L";
476cc9906bSGreg Kurz     uint16_t server_len;
4865ceee0aSChristian Schoenebeck     g_autofree char *server_version = NULL;
496cc9906bSGreg Kurz     P9Req *req;
506cc9906bSGreg Kurz 
51693b21d2SGreg Kurz     req = v9fs_tversion(v9p, P9_MAX_SIZE, version, P9_NOTAG);
52357e2f7fSGreg Kurz     v9fs_req_wait_for_reply(req, NULL);
536cc9906bSGreg Kurz     v9fs_rversion(req, &server_len, &server_version);
546cc9906bSGreg Kurz 
556cc9906bSGreg Kurz     g_assert_cmpmem(server_version, server_len, version, strlen(version));
566cc9906bSGreg Kurz }
576cc9906bSGreg Kurz 
581c450e6eSGreg Kurz static void fs_version(void *obj, void *data, QGuestAllocator *t_alloc)
591c450e6eSGreg Kurz {
60684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
611c450e6eSGreg Kurz     do_version(obj);
621c450e6eSGreg Kurz }
631c450e6eSGreg Kurz 
640e43495dSChristian Schoenebeck static void do_attach_rqid(QVirtio9P *v9p, v9fs_qid *qid)
655c3df1f0SGreg Kurz {
665c3df1f0SGreg Kurz     P9Req *req;
675c3df1f0SGreg Kurz 
681c450e6eSGreg Kurz     do_version(v9p);
69693b21d2SGreg Kurz     req = v9fs_tattach(v9p, 0, getuid(), 0);
70357e2f7fSGreg Kurz     v9fs_req_wait_for_reply(req, NULL);
710e43495dSChristian Schoenebeck     v9fs_rattach(req, qid);
720e43495dSChristian Schoenebeck }
730e43495dSChristian Schoenebeck 
740e43495dSChristian Schoenebeck static void do_attach(QVirtio9P *v9p)
750e43495dSChristian Schoenebeck {
760e43495dSChristian Schoenebeck     do_attach_rqid(v9p, NULL);
775c3df1f0SGreg Kurz }
785c3df1f0SGreg Kurz 
793fe4baf4SGreg Kurz static void fs_attach(void *obj, void *data, QGuestAllocator *t_alloc)
803fe4baf4SGreg Kurz {
81684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
823fe4baf4SGreg Kurz     do_attach(obj);
833fe4baf4SGreg Kurz }
843fe4baf4SGreg Kurz 
85dfbe8b43SEmanuele Giuseppe Esposito static void fs_walk(void *obj, void *data, QGuestAllocator *t_alloc)
8604b88c84SGreg Kurz {
87dfbe8b43SEmanuele Giuseppe Esposito     QVirtio9P *v9p = obj;
88684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
892893ddd5SGreg Kurz     char *wnames[P9_MAXWELEM];
9004b88c84SGreg Kurz     uint16_t nwqid;
9165ceee0aSChristian Schoenebeck     g_autofree v9fs_qid *wqid = NULL;
9204b88c84SGreg Kurz     int i;
9304b88c84SGreg Kurz 
9404b88c84SGreg Kurz     for (i = 0; i < P9_MAXWELEM; i++) {
952893ddd5SGreg Kurz         wnames[i] = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE, i);
9604b88c84SGreg Kurz     }
9704b88c84SGreg Kurz 
983fe4baf4SGreg Kurz     do_attach(v9p);
99*3f3e9232SChristian Schoenebeck     twalk({
100569f3b63SChristian Schoenebeck         .client = v9p, .fid = 0, .newfid = 1,
101*3f3e9232SChristian Schoenebeck         .nwname = P9_MAXWELEM, .wnames = wnames,
102*3f3e9232SChristian Schoenebeck         .rwalk = { .nwqid = &nwqid, .wqid = &wqid }
103*3f3e9232SChristian Schoenebeck     });
10404b88c84SGreg Kurz 
10504b88c84SGreg Kurz     g_assert_cmpint(nwqid, ==, P9_MAXWELEM);
10604b88c84SGreg Kurz 
10704b88c84SGreg Kurz     for (i = 0; i < P9_MAXWELEM; i++) {
10804b88c84SGreg Kurz         g_free(wnames[i]);
10904b88c84SGreg Kurz     }
11004b88c84SGreg Kurz }
11104b88c84SGreg Kurz 
1124829469fSChristian Schoenebeck static bool fs_dirents_contain_name(struct V9fsDirent *e, const char* name)
1134829469fSChristian Schoenebeck {
1144829469fSChristian Schoenebeck     for (; e; e = e->next) {
1154829469fSChristian Schoenebeck         if (!strcmp(e->name, name)) {
1164829469fSChristian Schoenebeck             return true;
1174829469fSChristian Schoenebeck         }
1184829469fSChristian Schoenebeck     }
1194829469fSChristian Schoenebeck     return false;
1204829469fSChristian Schoenebeck }
1214829469fSChristian Schoenebeck 
12246488b62SChristian Schoenebeck /* basic readdir test where reply fits into a single response message */
1234829469fSChristian Schoenebeck static void fs_readdir(void *obj, void *data, QGuestAllocator *t_alloc)
1244829469fSChristian Schoenebeck {
1254829469fSChristian Schoenebeck     QVirtio9P *v9p = obj;
126684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
127569f3b63SChristian Schoenebeck     char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR) };
1284829469fSChristian Schoenebeck     uint16_t nqid;
1294829469fSChristian Schoenebeck     v9fs_qid qid;
1304829469fSChristian Schoenebeck     uint32_t count, nentries;
1314829469fSChristian Schoenebeck     struct V9fsDirent *entries = NULL;
1324829469fSChristian Schoenebeck     P9Req *req;
1334829469fSChristian Schoenebeck 
1343fe4baf4SGreg Kurz     do_attach(v9p);
135*3f3e9232SChristian Schoenebeck     twalk({
136569f3b63SChristian Schoenebeck         .client = v9p, .fid = 0, .newfid = 1,
137*3f3e9232SChristian Schoenebeck         .nwname = 1, .wnames = wnames, .rwalk.nwqid = &nqid
138*3f3e9232SChristian Schoenebeck     });
1394829469fSChristian Schoenebeck     g_assert_cmpint(nqid, ==, 1);
1404829469fSChristian Schoenebeck 
1414829469fSChristian Schoenebeck     req = v9fs_tlopen(v9p, 1, O_DIRECTORY, 0);
1424829469fSChristian Schoenebeck     v9fs_req_wait_for_reply(req, NULL);
1434829469fSChristian Schoenebeck     v9fs_rlopen(req, &qid, NULL);
1444829469fSChristian Schoenebeck 
1454829469fSChristian Schoenebeck     /*
1464829469fSChristian Schoenebeck      * submit count = msize - 11, because 11 is the header size of Rreaddir
1474829469fSChristian Schoenebeck      */
1484829469fSChristian Schoenebeck     req = v9fs_treaddir(v9p, 1, 0, P9_MAX_SIZE - 11, 0);
1494829469fSChristian Schoenebeck     v9fs_req_wait_for_reply(req, NULL);
1504829469fSChristian Schoenebeck     v9fs_rreaddir(req, &count, &nentries, &entries);
1514829469fSChristian Schoenebeck 
1524829469fSChristian Schoenebeck     /*
1534829469fSChristian Schoenebeck      * Assuming msize (P9_MAX_SIZE) is large enough so we can retrieve all
1544829469fSChristian Schoenebeck      * dir entries with only one readdir request.
1554829469fSChristian Schoenebeck      */
1564829469fSChristian Schoenebeck     g_assert_cmpint(
1574829469fSChristian Schoenebeck         nentries, ==,
1584829469fSChristian Schoenebeck         QTEST_V9FS_SYNTH_READDIR_NFILES + 2 /* "." and ".." */
1594829469fSChristian Schoenebeck     );
1604829469fSChristian Schoenebeck 
1614829469fSChristian Schoenebeck     /*
1624829469fSChristian Schoenebeck      * Check all file names exist in returned entries, ignore their order
1634829469fSChristian Schoenebeck      * though.
1644829469fSChristian Schoenebeck      */
1654829469fSChristian Schoenebeck     g_assert_cmpint(fs_dirents_contain_name(entries, "."), ==, true);
1664829469fSChristian Schoenebeck     g_assert_cmpint(fs_dirents_contain_name(entries, ".."), ==, true);
1674829469fSChristian Schoenebeck     for (int i = 0; i < QTEST_V9FS_SYNTH_READDIR_NFILES; ++i) {
16865ceee0aSChristian Schoenebeck         g_autofree char *name =
16965ceee0aSChristian Schoenebeck             g_strdup_printf(QTEST_V9FS_SYNTH_READDIR_FILE, i);
1704829469fSChristian Schoenebeck         g_assert_cmpint(fs_dirents_contain_name(entries, name), ==, true);
1714829469fSChristian Schoenebeck     }
1724829469fSChristian Schoenebeck 
1734829469fSChristian Schoenebeck     v9fs_free_dirents(entries);
1744829469fSChristian Schoenebeck     g_free(wnames[0]);
1754829469fSChristian Schoenebeck }
1764829469fSChristian Schoenebeck 
17746488b62SChristian Schoenebeck /* readdir test where overall request is split over several messages */
1781d98613dSGreg Kurz static void do_readdir_split(QVirtio9P *v9p, uint32_t count)
17946488b62SChristian Schoenebeck {
180569f3b63SChristian Schoenebeck     char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR) };
18146488b62SChristian Schoenebeck     uint16_t nqid;
18246488b62SChristian Schoenebeck     v9fs_qid qid;
18346488b62SChristian Schoenebeck     uint32_t nentries, npartialentries;
18446488b62SChristian Schoenebeck     struct V9fsDirent *entries, *tail, *partialentries;
18546488b62SChristian Schoenebeck     P9Req *req;
18646488b62SChristian Schoenebeck     int fid;
18746488b62SChristian Schoenebeck     uint64_t offset;
18846488b62SChristian Schoenebeck 
1893fe4baf4SGreg Kurz     do_attach(v9p);
19046488b62SChristian Schoenebeck 
19146488b62SChristian Schoenebeck     fid = 1;
19246488b62SChristian Schoenebeck     offset = 0;
19346488b62SChristian Schoenebeck     entries = NULL;
19446488b62SChristian Schoenebeck     nentries = 0;
19546488b62SChristian Schoenebeck     tail = NULL;
19646488b62SChristian Schoenebeck 
197*3f3e9232SChristian Schoenebeck     twalk({
198569f3b63SChristian Schoenebeck         .client = v9p, .fid = 0, .newfid = fid,
199*3f3e9232SChristian Schoenebeck         .nwname = 1, .wnames = wnames, .rwalk.nwqid = &nqid
200*3f3e9232SChristian Schoenebeck     });
20146488b62SChristian Schoenebeck     g_assert_cmpint(nqid, ==, 1);
20246488b62SChristian Schoenebeck 
20346488b62SChristian Schoenebeck     req = v9fs_tlopen(v9p, fid, O_DIRECTORY, 0);
20446488b62SChristian Schoenebeck     v9fs_req_wait_for_reply(req, NULL);
20546488b62SChristian Schoenebeck     v9fs_rlopen(req, &qid, NULL);
20646488b62SChristian Schoenebeck 
20746488b62SChristian Schoenebeck     /*
20846488b62SChristian Schoenebeck      * send as many Treaddir requests as required to get all directory
20946488b62SChristian Schoenebeck      * entries
21046488b62SChristian Schoenebeck      */
21146488b62SChristian Schoenebeck     while (true) {
21246488b62SChristian Schoenebeck         npartialentries = 0;
21346488b62SChristian Schoenebeck         partialentries = NULL;
21446488b62SChristian Schoenebeck 
21546488b62SChristian Schoenebeck         req = v9fs_treaddir(v9p, fid, offset, count, 0);
21646488b62SChristian Schoenebeck         v9fs_req_wait_for_reply(req, NULL);
21746488b62SChristian Schoenebeck         v9fs_rreaddir(req, &count, &npartialentries, &partialentries);
21846488b62SChristian Schoenebeck         if (npartialentries > 0 && partialentries) {
21946488b62SChristian Schoenebeck             if (!entries) {
22046488b62SChristian Schoenebeck                 entries = partialentries;
22146488b62SChristian Schoenebeck                 nentries = npartialentries;
22246488b62SChristian Schoenebeck                 tail = partialentries;
22346488b62SChristian Schoenebeck             } else {
22446488b62SChristian Schoenebeck                 tail->next = partialentries;
22546488b62SChristian Schoenebeck                 nentries += npartialentries;
22646488b62SChristian Schoenebeck             }
22746488b62SChristian Schoenebeck             while (tail->next) {
22846488b62SChristian Schoenebeck                 tail = tail->next;
22946488b62SChristian Schoenebeck             }
23046488b62SChristian Schoenebeck             offset = tail->offset;
23146488b62SChristian Schoenebeck         } else {
23246488b62SChristian Schoenebeck             break;
23346488b62SChristian Schoenebeck         }
23446488b62SChristian Schoenebeck     }
23546488b62SChristian Schoenebeck 
23646488b62SChristian Schoenebeck     g_assert_cmpint(
23746488b62SChristian Schoenebeck         nentries, ==,
23846488b62SChristian Schoenebeck         QTEST_V9FS_SYNTH_READDIR_NFILES + 2 /* "." and ".." */
23946488b62SChristian Schoenebeck     );
24046488b62SChristian Schoenebeck 
24146488b62SChristian Schoenebeck     /*
24246488b62SChristian Schoenebeck      * Check all file names exist in returned entries, ignore their order
24346488b62SChristian Schoenebeck      * though.
24446488b62SChristian Schoenebeck      */
24546488b62SChristian Schoenebeck     g_assert_cmpint(fs_dirents_contain_name(entries, "."), ==, true);
24646488b62SChristian Schoenebeck     g_assert_cmpint(fs_dirents_contain_name(entries, ".."), ==, true);
24746488b62SChristian Schoenebeck     for (int i = 0; i < QTEST_V9FS_SYNTH_READDIR_NFILES; ++i) {
24846488b62SChristian Schoenebeck         char *name = g_strdup_printf(QTEST_V9FS_SYNTH_READDIR_FILE, i);
24946488b62SChristian Schoenebeck         g_assert_cmpint(fs_dirents_contain_name(entries, name), ==, true);
25046488b62SChristian Schoenebeck         g_free(name);
25146488b62SChristian Schoenebeck     }
25246488b62SChristian Schoenebeck 
25346488b62SChristian Schoenebeck     v9fs_free_dirents(entries);
25446488b62SChristian Schoenebeck 
25546488b62SChristian Schoenebeck     g_free(wnames[0]);
25646488b62SChristian Schoenebeck }
25746488b62SChristian Schoenebeck 
258dfbe8b43SEmanuele Giuseppe Esposito static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc)
259ba0d1037SGreg Kurz {
260dfbe8b43SEmanuele Giuseppe Esposito     QVirtio9P *v9p = obj;
261684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
262569f3b63SChristian Schoenebeck     char *wnames[] = { g_strdup(" /") };
263ba0d1037SGreg Kurz 
2643fe4baf4SGreg Kurz     do_attach(v9p);
265*3f3e9232SChristian Schoenebeck     twalk({
266569f3b63SChristian Schoenebeck         .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames,
267*3f3e9232SChristian Schoenebeck         .expectErr = ENOENT
268*3f3e9232SChristian Schoenebeck     });
269ba0d1037SGreg Kurz 
270ba0d1037SGreg Kurz     g_free(wnames[0]);
271ba0d1037SGreg Kurz }
272ba0d1037SGreg Kurz 
2739472a689SChristian Schoenebeck static void fs_walk_nonexistent(void *obj, void *data, QGuestAllocator *t_alloc)
2749472a689SChristian Schoenebeck {
2759472a689SChristian Schoenebeck     QVirtio9P *v9p = obj;
276684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
2779472a689SChristian Schoenebeck 
2789472a689SChristian Schoenebeck     do_attach(v9p);
27915fbff48SChristian Schoenebeck     /*
28015fbff48SChristian Schoenebeck      * The 9p2000 protocol spec says: "If the first element cannot be walked
28115fbff48SChristian Schoenebeck      * for any reason, Rerror is returned."
28215fbff48SChristian Schoenebeck      */
283569f3b63SChristian Schoenebeck     twalk({ .client = v9p, .path = "non-existent", .expectErr = ENOENT });
2849472a689SChristian Schoenebeck }
2859472a689SChristian Schoenebeck 
28615fbff48SChristian Schoenebeck static void fs_walk_2nd_nonexistent(void *obj, void *data,
28715fbff48SChristian Schoenebeck                                     QGuestAllocator *t_alloc)
28815fbff48SChristian Schoenebeck {
28915fbff48SChristian Schoenebeck     QVirtio9P *v9p = obj;
290684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
2910e43495dSChristian Schoenebeck     v9fs_qid root_qid;
29215fbff48SChristian Schoenebeck     uint16_t nwqid;
2930e43495dSChristian Schoenebeck     uint32_t fid, err;
2940e43495dSChristian Schoenebeck     P9Req *req;
29515fbff48SChristian Schoenebeck     g_autofree v9fs_qid *wqid = NULL;
29615fbff48SChristian Schoenebeck     g_autofree char *path = g_strdup_printf(
29715fbff48SChristian Schoenebeck         QTEST_V9FS_SYNTH_WALK_FILE "/non-existent", 0
29815fbff48SChristian Schoenebeck     );
29915fbff48SChristian Schoenebeck 
3000e43495dSChristian Schoenebeck     do_attach_rqid(v9p, &root_qid);
301569f3b63SChristian Schoenebeck     fid = twalk({
302569f3b63SChristian Schoenebeck         .client = v9p, .path = path,
303*3f3e9232SChristian Schoenebeck         .rwalk = { .nwqid = &nwqid, .wqid = &wqid }
304569f3b63SChristian Schoenebeck     }).newfid;
30515fbff48SChristian Schoenebeck     /*
30615fbff48SChristian Schoenebeck      * The 9p2000 protocol spec says: "nwqid is therefore either nwname or the
30715fbff48SChristian Schoenebeck      * index of the first elementwise walk that failed."
30815fbff48SChristian Schoenebeck      */
30915fbff48SChristian Schoenebeck     assert(nwqid == 1);
3100e43495dSChristian Schoenebeck 
3110e43495dSChristian Schoenebeck     /* returned QID wqid[0] is file ID of 1st subdir */
3120e43495dSChristian Schoenebeck     g_assert(wqid && wqid[0] && !is_same_qid(root_qid, wqid[0]));
3130e43495dSChristian Schoenebeck 
3140e43495dSChristian Schoenebeck     /* expect fid being unaffected by walk above */
3150e43495dSChristian Schoenebeck     req = v9fs_tgetattr(v9p, fid, P9_GETATTR_BASIC, 0);
3160e43495dSChristian Schoenebeck     v9fs_req_wait_for_reply(req, NULL);
3170e43495dSChristian Schoenebeck     v9fs_rlerror(req, &err);
3180e43495dSChristian Schoenebeck 
3190e43495dSChristian Schoenebeck     g_assert_cmpint(err, ==, ENOENT);
32015fbff48SChristian Schoenebeck }
32115fbff48SChristian Schoenebeck 
322c1668948SChristian Schoenebeck static void fs_walk_none(void *obj, void *data, QGuestAllocator *t_alloc)
323c1668948SChristian Schoenebeck {
324c1668948SChristian Schoenebeck     QVirtio9P *v9p = obj;
325684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
326c1668948SChristian Schoenebeck     v9fs_qid root_qid;
327c1668948SChristian Schoenebeck     g_autofree v9fs_qid *wqid = NULL;
328c1668948SChristian Schoenebeck     P9Req *req;
329a6821b82SChristian Schoenebeck     struct v9fs_attr attr;
330c1668948SChristian Schoenebeck 
331c1668948SChristian Schoenebeck     do_version(v9p);
332c1668948SChristian Schoenebeck     req = v9fs_tattach(v9p, 0, getuid(), 0);
333c1668948SChristian Schoenebeck     v9fs_req_wait_for_reply(req, NULL);
334c1668948SChristian Schoenebeck     v9fs_rattach(req, &root_qid);
335c1668948SChristian Schoenebeck 
336*3f3e9232SChristian Schoenebeck     twalk({
337569f3b63SChristian Schoenebeck         .client = v9p, .fid = 0, .newfid = 1, .nwname = 0, .wnames = NULL,
338*3f3e9232SChristian Schoenebeck         .rwalk.wqid = &wqid
339*3f3e9232SChristian Schoenebeck     });
340c1668948SChristian Schoenebeck 
341c1668948SChristian Schoenebeck     /* special case: no QID is returned if nwname=0 was sent */
342c1668948SChristian Schoenebeck     g_assert(wqid == NULL);
343a6821b82SChristian Schoenebeck 
344a6821b82SChristian Schoenebeck     req = v9fs_tgetattr(v9p, 1, P9_GETATTR_BASIC, 0);
345a6821b82SChristian Schoenebeck     v9fs_req_wait_for_reply(req, NULL);
346a6821b82SChristian Schoenebeck     v9fs_rgetattr(req, &attr);
347a6821b82SChristian Schoenebeck 
348a6821b82SChristian Schoenebeck     g_assert(is_same_qid(root_qid, attr.qid));
349c1668948SChristian Schoenebeck }
350c1668948SChristian Schoenebeck 
351dfbe8b43SEmanuele Giuseppe Esposito static void fs_walk_dotdot(void *obj, void *data, QGuestAllocator *t_alloc)
352a37c0702SGreg Kurz {
353dfbe8b43SEmanuele Giuseppe Esposito     QVirtio9P *v9p = obj;
354684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
355569f3b63SChristian Schoenebeck     char *wnames[] = { g_strdup("..") };
35665ceee0aSChristian Schoenebeck     v9fs_qid root_qid;
35765ceee0aSChristian Schoenebeck     g_autofree v9fs_qid *wqid = NULL;
358a37c0702SGreg Kurz     P9Req *req;
359a37c0702SGreg Kurz 
3601c450e6eSGreg Kurz     do_version(v9p);
361693b21d2SGreg Kurz     req = v9fs_tattach(v9p, 0, getuid(), 0);
362357e2f7fSGreg Kurz     v9fs_req_wait_for_reply(req, NULL);
363a37c0702SGreg Kurz     v9fs_rattach(req, &root_qid);
364a37c0702SGreg Kurz 
365*3f3e9232SChristian Schoenebeck     twalk({
366569f3b63SChristian Schoenebeck         .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames,
367*3f3e9232SChristian Schoenebeck         .rwalk.wqid = &wqid /* We now we'll get one qid */
368*3f3e9232SChristian Schoenebeck     });
369a37c0702SGreg Kurz 
370a37c0702SGreg Kurz     g_assert_cmpmem(&root_qid, 13, wqid[0], 13);
371a37c0702SGreg Kurz 
372a37c0702SGreg Kurz     g_free(wnames[0]);
373a37c0702SGreg Kurz }
374a37c0702SGreg Kurz 
375dfbe8b43SEmanuele Giuseppe Esposito static void fs_lopen(void *obj, void *data, QGuestAllocator *t_alloc)
37682469aaeSGreg Kurz {
377dfbe8b43SEmanuele Giuseppe Esposito     QVirtio9P *v9p = obj;
378684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
379569f3b63SChristian Schoenebeck     char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_LOPEN_FILE) };
38082469aaeSGreg Kurz     P9Req *req;
38182469aaeSGreg Kurz 
3823fe4baf4SGreg Kurz     do_attach(v9p);
383*3f3e9232SChristian Schoenebeck     twalk({
384*3f3e9232SChristian Schoenebeck         .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames
385*3f3e9232SChristian Schoenebeck     });
38682469aaeSGreg Kurz 
38782469aaeSGreg Kurz     req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
388357e2f7fSGreg Kurz     v9fs_req_wait_for_reply(req, NULL);
38982469aaeSGreg Kurz     v9fs_rlopen(req, NULL, NULL);
39082469aaeSGreg Kurz 
39182469aaeSGreg Kurz     g_free(wnames[0]);
39282469aaeSGreg Kurz }
39382469aaeSGreg Kurz 
394dfbe8b43SEmanuele Giuseppe Esposito static void fs_write(void *obj, void *data, QGuestAllocator *t_alloc)
395354b86f8SGreg Kurz {
396dfbe8b43SEmanuele Giuseppe Esposito     QVirtio9P *v9p = obj;
397684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
398354b86f8SGreg Kurz     static const uint32_t write_count = P9_MAX_SIZE / 2;
399569f3b63SChristian Schoenebeck     char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_WRITE_FILE) };
40065ceee0aSChristian Schoenebeck     g_autofree char *buf = g_malloc0(write_count);
401354b86f8SGreg Kurz     uint32_t count;
402354b86f8SGreg Kurz     P9Req *req;
403354b86f8SGreg Kurz 
4043fe4baf4SGreg Kurz     do_attach(v9p);
405*3f3e9232SChristian Schoenebeck     twalk({
406*3f3e9232SChristian Schoenebeck         .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames
407*3f3e9232SChristian Schoenebeck     });
408354b86f8SGreg Kurz 
409354b86f8SGreg Kurz     req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
410357e2f7fSGreg Kurz     v9fs_req_wait_for_reply(req, NULL);
411354b86f8SGreg Kurz     v9fs_rlopen(req, NULL, NULL);
412354b86f8SGreg Kurz 
413354b86f8SGreg Kurz     req = v9fs_twrite(v9p, 1, 0, write_count, buf, 0);
414357e2f7fSGreg Kurz     v9fs_req_wait_for_reply(req, NULL);
415354b86f8SGreg Kurz     v9fs_rwrite(req, &count);
416354b86f8SGreg Kurz     g_assert_cmpint(count, ==, write_count);
417354b86f8SGreg Kurz 
418354b86f8SGreg Kurz     g_free(wnames[0]);
419354b86f8SGreg Kurz }
420354b86f8SGreg Kurz 
421dfbe8b43SEmanuele Giuseppe Esposito static void fs_flush_success(void *obj, void *data, QGuestAllocator *t_alloc)
422357e2f7fSGreg Kurz {
423dfbe8b43SEmanuele Giuseppe Esposito     QVirtio9P *v9p = obj;
424684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
425569f3b63SChristian Schoenebeck     char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) };
426357e2f7fSGreg Kurz     P9Req *req, *flush_req;
427357e2f7fSGreg Kurz     uint32_t reply_len;
428357e2f7fSGreg Kurz     uint8_t should_block;
429357e2f7fSGreg Kurz 
4303fe4baf4SGreg Kurz     do_attach(v9p);
431*3f3e9232SChristian Schoenebeck     twalk({
432*3f3e9232SChristian Schoenebeck         .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames
433*3f3e9232SChristian Schoenebeck     });
434357e2f7fSGreg Kurz 
435357e2f7fSGreg Kurz     req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
436357e2f7fSGreg Kurz     v9fs_req_wait_for_reply(req, NULL);
437357e2f7fSGreg Kurz     v9fs_rlopen(req, NULL, NULL);
438357e2f7fSGreg Kurz 
439357e2f7fSGreg Kurz     /* This will cause the 9p server to try to write data to the backend,
440357e2f7fSGreg Kurz      * until the write request gets cancelled.
441357e2f7fSGreg Kurz      */
442357e2f7fSGreg Kurz     should_block = 1;
443357e2f7fSGreg Kurz     req = v9fs_twrite(v9p, 1, 0, sizeof(should_block), &should_block, 0);
444357e2f7fSGreg Kurz 
445357e2f7fSGreg Kurz     flush_req = v9fs_tflush(v9p, req->tag, 1);
446357e2f7fSGreg Kurz 
447357e2f7fSGreg Kurz     /* The write request is supposed to be flushed: the server should just
448357e2f7fSGreg Kurz      * mark the write request as used and reply to the flush request.
449357e2f7fSGreg Kurz      */
450357e2f7fSGreg Kurz     v9fs_req_wait_for_reply(req, &reply_len);
451357e2f7fSGreg Kurz     g_assert_cmpint(reply_len, ==, 0);
452357e2f7fSGreg Kurz     v9fs_req_free(req);
453357e2f7fSGreg Kurz     v9fs_rflush(flush_req);
454357e2f7fSGreg Kurz 
455357e2f7fSGreg Kurz     g_free(wnames[0]);
456357e2f7fSGreg Kurz }
457357e2f7fSGreg Kurz 
458dfbe8b43SEmanuele Giuseppe Esposito static void fs_flush_ignored(void *obj, void *data, QGuestAllocator *t_alloc)
459357e2f7fSGreg Kurz {
460dfbe8b43SEmanuele Giuseppe Esposito     QVirtio9P *v9p = obj;
461684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
462569f3b63SChristian Schoenebeck     char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) };
463357e2f7fSGreg Kurz     P9Req *req, *flush_req;
464357e2f7fSGreg Kurz     uint32_t count;
465357e2f7fSGreg Kurz     uint8_t should_block;
466357e2f7fSGreg Kurz 
4673fe4baf4SGreg Kurz     do_attach(v9p);
468*3f3e9232SChristian Schoenebeck     twalk({
469*3f3e9232SChristian Schoenebeck         .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames
470*3f3e9232SChristian Schoenebeck     });
471357e2f7fSGreg Kurz 
472357e2f7fSGreg Kurz     req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
473357e2f7fSGreg Kurz     v9fs_req_wait_for_reply(req, NULL);
474357e2f7fSGreg Kurz     v9fs_rlopen(req, NULL, NULL);
475357e2f7fSGreg Kurz 
476357e2f7fSGreg Kurz     /* This will cause the write request to complete right away, before it
477357e2f7fSGreg Kurz      * could be actually cancelled.
478357e2f7fSGreg Kurz      */
479357e2f7fSGreg Kurz     should_block = 0;
480357e2f7fSGreg Kurz     req = v9fs_twrite(v9p, 1, 0, sizeof(should_block), &should_block, 0);
481357e2f7fSGreg Kurz 
482357e2f7fSGreg Kurz     flush_req = v9fs_tflush(v9p, req->tag, 1);
483357e2f7fSGreg Kurz 
484357e2f7fSGreg Kurz     /* The write request is supposed to complete. The server should
485357e2f7fSGreg Kurz      * reply to the write request and the flush request.
486357e2f7fSGreg Kurz      */
487357e2f7fSGreg Kurz     v9fs_req_wait_for_reply(req, NULL);
488357e2f7fSGreg Kurz     v9fs_rwrite(req, &count);
489357e2f7fSGreg Kurz     g_assert_cmpint(count, ==, sizeof(should_block));
490357e2f7fSGreg Kurz     v9fs_rflush(flush_req);
491357e2f7fSGreg Kurz 
492357e2f7fSGreg Kurz     g_free(wnames[0]);
493357e2f7fSGreg Kurz }
494357e2f7fSGreg Kurz 
495c1934f63SGreg Kurz static void do_mkdir(QVirtio9P *v9p, const char *path, const char *cname)
496653daf38SChristian Schoenebeck {
49765ceee0aSChristian Schoenebeck     g_autofree char *name = g_strdup(cname);
49820018805SChristian Schoenebeck     uint32_t fid;
499653daf38SChristian Schoenebeck     P9Req *req;
500653daf38SChristian Schoenebeck 
501569f3b63SChristian Schoenebeck     fid = twalk({ .client = v9p, .path = path }).newfid;
502653daf38SChristian Schoenebeck 
503653daf38SChristian Schoenebeck     req = v9fs_tmkdir(v9p, fid, name, 0750, 0, 0);
504653daf38SChristian Schoenebeck     v9fs_req_wait_for_reply(req, NULL);
505653daf38SChristian Schoenebeck     v9fs_rmkdir(req, NULL);
506653daf38SChristian Schoenebeck }
507653daf38SChristian Schoenebeck 
508b09dbfddSChristian Schoenebeck /* create a regular file with Tlcreate and return file's fid */
509b09dbfddSChristian Schoenebeck static uint32_t do_lcreate(QVirtio9P *v9p, const char *path,
510b09dbfddSChristian Schoenebeck                            const char *cname)
511b09dbfddSChristian Schoenebeck {
51265ceee0aSChristian Schoenebeck     g_autofree char *name = g_strdup(cname);
513b09dbfddSChristian Schoenebeck     uint32_t fid;
514b09dbfddSChristian Schoenebeck     P9Req *req;
515b09dbfddSChristian Schoenebeck 
516569f3b63SChristian Schoenebeck     fid = twalk({ .client = v9p, .path = path }).newfid;
517b09dbfddSChristian Schoenebeck 
518b09dbfddSChristian Schoenebeck     req = v9fs_tlcreate(v9p, fid, name, 0, 0750, 0, 0);
519b09dbfddSChristian Schoenebeck     v9fs_req_wait_for_reply(req, NULL);
520b09dbfddSChristian Schoenebeck     v9fs_rlcreate(req, NULL, NULL);
521b09dbfddSChristian Schoenebeck 
522b09dbfddSChristian Schoenebeck     return fid;
523b09dbfddSChristian Schoenebeck }
524b09dbfddSChristian Schoenebeck 
52559ff563dSChristian Schoenebeck /* create symlink named @a clink in directory @a path pointing to @a to */
52659ff563dSChristian Schoenebeck static void do_symlink(QVirtio9P *v9p, const char *path, const char *clink,
52759ff563dSChristian Schoenebeck                        const char *to)
52859ff563dSChristian Schoenebeck {
52965ceee0aSChristian Schoenebeck     g_autofree char *name = g_strdup(clink);
53065ceee0aSChristian Schoenebeck     g_autofree char *dst = g_strdup(to);
53159ff563dSChristian Schoenebeck     uint32_t fid;
53259ff563dSChristian Schoenebeck     P9Req *req;
53359ff563dSChristian Schoenebeck 
534569f3b63SChristian Schoenebeck     fid = twalk({ .client = v9p, .path = path }).newfid;
53559ff563dSChristian Schoenebeck 
53659ff563dSChristian Schoenebeck     req = v9fs_tsymlink(v9p, fid, name, dst, 0, 0);
53759ff563dSChristian Schoenebeck     v9fs_req_wait_for_reply(req, NULL);
53859ff563dSChristian Schoenebeck     v9fs_rsymlink(req, NULL);
53959ff563dSChristian Schoenebeck }
54059ff563dSChristian Schoenebeck 
54164e3d403SChristian Schoenebeck /* create a hard link named @a clink in directory @a path pointing to @a to */
54264e3d403SChristian Schoenebeck static void do_hardlink(QVirtio9P *v9p, const char *path, const char *clink,
54364e3d403SChristian Schoenebeck                         const char *to)
54464e3d403SChristian Schoenebeck {
54564e3d403SChristian Schoenebeck     uint32_t dfid, fid;
54664e3d403SChristian Schoenebeck     P9Req *req;
54764e3d403SChristian Schoenebeck 
548569f3b63SChristian Schoenebeck     dfid = twalk({ .client = v9p, .path = path }).newfid;
549569f3b63SChristian Schoenebeck     fid = twalk({ .client = v9p, .path = to }).newfid;
55064e3d403SChristian Schoenebeck 
55164e3d403SChristian Schoenebeck     req = v9fs_tlink(v9p, dfid, fid, clink, 0);
55264e3d403SChristian Schoenebeck     v9fs_req_wait_for_reply(req, NULL);
55364e3d403SChristian Schoenebeck     v9fs_rlink(req);
55464e3d403SChristian Schoenebeck }
55564e3d403SChristian Schoenebeck 
556b37d62d6SChristian Schoenebeck static void do_unlinkat(QVirtio9P *v9p, const char *atpath, const char *rpath,
557b37d62d6SChristian Schoenebeck                         uint32_t flags)
558b37d62d6SChristian Schoenebeck {
55965ceee0aSChristian Schoenebeck     g_autofree char *name = g_strdup(rpath);
560b37d62d6SChristian Schoenebeck     uint32_t fid;
561b37d62d6SChristian Schoenebeck     P9Req *req;
562b37d62d6SChristian Schoenebeck 
563569f3b63SChristian Schoenebeck     fid = twalk({ .client = v9p, .path = atpath }).newfid;
564b37d62d6SChristian Schoenebeck 
565b37d62d6SChristian Schoenebeck     req = v9fs_tunlinkat(v9p, fid, name, flags, 0);
566b37d62d6SChristian Schoenebeck     v9fs_req_wait_for_reply(req, NULL);
567b37d62d6SChristian Schoenebeck     v9fs_runlinkat(req);
568b37d62d6SChristian Schoenebeck }
569b37d62d6SChristian Schoenebeck 
57046488b62SChristian Schoenebeck static void fs_readdir_split_128(void *obj, void *data,
57146488b62SChristian Schoenebeck                                  QGuestAllocator *t_alloc)
57246488b62SChristian Schoenebeck {
573684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
5741d98613dSGreg Kurz     do_readdir_split(obj, 128);
57546488b62SChristian Schoenebeck }
57646488b62SChristian Schoenebeck 
57746488b62SChristian Schoenebeck static void fs_readdir_split_256(void *obj, void *data,
57846488b62SChristian Schoenebeck                                  QGuestAllocator *t_alloc)
57946488b62SChristian Schoenebeck {
580684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
5811d98613dSGreg Kurz     do_readdir_split(obj, 256);
58246488b62SChristian Schoenebeck }
58346488b62SChristian Schoenebeck 
58446488b62SChristian Schoenebeck static void fs_readdir_split_512(void *obj, void *data,
58546488b62SChristian Schoenebeck                                  QGuestAllocator *t_alloc)
58646488b62SChristian Schoenebeck {
587684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
5881d98613dSGreg Kurz     do_readdir_split(obj, 512);
58946488b62SChristian Schoenebeck }
59046488b62SChristian Schoenebeck 
591653daf38SChristian Schoenebeck 
592653daf38SChristian Schoenebeck /* tests using the 9pfs 'local' fs driver */
593653daf38SChristian Schoenebeck 
594653daf38SChristian Schoenebeck static void fs_create_dir(void *obj, void *data, QGuestAllocator *t_alloc)
595653daf38SChristian Schoenebeck {
596653daf38SChristian Schoenebeck     QVirtio9P *v9p = obj;
597684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
598653daf38SChristian Schoenebeck     struct stat st;
59965ceee0aSChristian Schoenebeck     g_autofree char *root_path = virtio_9p_test_path("");
60065ceee0aSChristian Schoenebeck     g_autofree char *new_dir = virtio_9p_test_path("01");
601653daf38SChristian Schoenebeck 
602653daf38SChristian Schoenebeck     g_assert(root_path != NULL);
603653daf38SChristian Schoenebeck 
6043fe4baf4SGreg Kurz     do_attach(v9p);
605c1934f63SGreg Kurz     do_mkdir(v9p, "/", "01");
606653daf38SChristian Schoenebeck 
607653daf38SChristian Schoenebeck     /* check if created directory really exists now ... */
608653daf38SChristian Schoenebeck     g_assert(stat(new_dir, &st) == 0);
609653daf38SChristian Schoenebeck     /* ... and is actually a directory */
610653daf38SChristian Schoenebeck     g_assert((st.st_mode & S_IFMT) == S_IFDIR);
611653daf38SChristian Schoenebeck }
612653daf38SChristian Schoenebeck 
613b37d62d6SChristian Schoenebeck static void fs_unlinkat_dir(void *obj, void *data, QGuestAllocator *t_alloc)
614b37d62d6SChristian Schoenebeck {
615b37d62d6SChristian Schoenebeck     QVirtio9P *v9p = obj;
616684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
617b37d62d6SChristian Schoenebeck     struct stat st;
61865ceee0aSChristian Schoenebeck     g_autofree char *root_path = virtio_9p_test_path("");
61965ceee0aSChristian Schoenebeck     g_autofree char *new_dir = virtio_9p_test_path("02");
620b37d62d6SChristian Schoenebeck 
621b37d62d6SChristian Schoenebeck     g_assert(root_path != NULL);
622b37d62d6SChristian Schoenebeck 
623b37d62d6SChristian Schoenebeck     do_attach(v9p);
624b37d62d6SChristian Schoenebeck     do_mkdir(v9p, "/", "02");
625b37d62d6SChristian Schoenebeck 
626b37d62d6SChristian Schoenebeck     /* check if created directory really exists now ... */
627b37d62d6SChristian Schoenebeck     g_assert(stat(new_dir, &st) == 0);
628b37d62d6SChristian Schoenebeck     /* ... and is actually a directory */
629b37d62d6SChristian Schoenebeck     g_assert((st.st_mode & S_IFMT) == S_IFDIR);
630b37d62d6SChristian Schoenebeck 
631d3671fd9SWill Cohen     do_unlinkat(v9p, "/", "02", P9_DOTL_AT_REMOVEDIR);
632b37d62d6SChristian Schoenebeck     /* directory should be gone now */
633b37d62d6SChristian Schoenebeck     g_assert(stat(new_dir, &st) != 0);
634b37d62d6SChristian Schoenebeck }
635b37d62d6SChristian Schoenebeck 
636b09dbfddSChristian Schoenebeck static void fs_create_file(void *obj, void *data, QGuestAllocator *t_alloc)
637b09dbfddSChristian Schoenebeck {
638b09dbfddSChristian Schoenebeck     QVirtio9P *v9p = obj;
639684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
640b09dbfddSChristian Schoenebeck     struct stat st;
64165ceee0aSChristian Schoenebeck     g_autofree char *new_file = virtio_9p_test_path("03/1st_file");
642b09dbfddSChristian Schoenebeck 
643b09dbfddSChristian Schoenebeck     do_attach(v9p);
644b09dbfddSChristian Schoenebeck     do_mkdir(v9p, "/", "03");
645b09dbfddSChristian Schoenebeck     do_lcreate(v9p, "03", "1st_file");
646b09dbfddSChristian Schoenebeck 
647b09dbfddSChristian Schoenebeck     /* check if created file exists now ... */
648b09dbfddSChristian Schoenebeck     g_assert(stat(new_file, &st) == 0);
649b09dbfddSChristian Schoenebeck     /* ... and is a regular file */
650b09dbfddSChristian Schoenebeck     g_assert((st.st_mode & S_IFMT) == S_IFREG);
651b09dbfddSChristian Schoenebeck }
652b09dbfddSChristian Schoenebeck 
653472c18b8SChristian Schoenebeck static void fs_unlinkat_file(void *obj, void *data, QGuestAllocator *t_alloc)
654472c18b8SChristian Schoenebeck {
655472c18b8SChristian Schoenebeck     QVirtio9P *v9p = obj;
656684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
657472c18b8SChristian Schoenebeck     struct stat st;
65865ceee0aSChristian Schoenebeck     g_autofree char *new_file = virtio_9p_test_path("04/doa_file");
659472c18b8SChristian Schoenebeck 
660472c18b8SChristian Schoenebeck     do_attach(v9p);
661472c18b8SChristian Schoenebeck     do_mkdir(v9p, "/", "04");
662472c18b8SChristian Schoenebeck     do_lcreate(v9p, "04", "doa_file");
663472c18b8SChristian Schoenebeck 
664472c18b8SChristian Schoenebeck     /* check if created file exists now ... */
665472c18b8SChristian Schoenebeck     g_assert(stat(new_file, &st) == 0);
666472c18b8SChristian Schoenebeck     /* ... and is a regular file */
667472c18b8SChristian Schoenebeck     g_assert((st.st_mode & S_IFMT) == S_IFREG);
668472c18b8SChristian Schoenebeck 
669472c18b8SChristian Schoenebeck     do_unlinkat(v9p, "04", "doa_file", 0);
670472c18b8SChristian Schoenebeck     /* file should be gone now */
671472c18b8SChristian Schoenebeck     g_assert(stat(new_file, &st) != 0);
672472c18b8SChristian Schoenebeck }
673472c18b8SChristian Schoenebeck 
67459ff563dSChristian Schoenebeck static void fs_symlink_file(void *obj, void *data, QGuestAllocator *t_alloc)
67559ff563dSChristian Schoenebeck {
67659ff563dSChristian Schoenebeck     QVirtio9P *v9p = obj;
677684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
67859ff563dSChristian Schoenebeck     struct stat st;
67965ceee0aSChristian Schoenebeck     g_autofree char *real_file = virtio_9p_test_path("05/real_file");
68065ceee0aSChristian Schoenebeck     g_autofree char *symlink_file = virtio_9p_test_path("05/symlink_file");
68159ff563dSChristian Schoenebeck 
68259ff563dSChristian Schoenebeck     do_attach(v9p);
68359ff563dSChristian Schoenebeck     do_mkdir(v9p, "/", "05");
68459ff563dSChristian Schoenebeck     do_lcreate(v9p, "05", "real_file");
68559ff563dSChristian Schoenebeck     g_assert(stat(real_file, &st) == 0);
68659ff563dSChristian Schoenebeck     g_assert((st.st_mode & S_IFMT) == S_IFREG);
68759ff563dSChristian Schoenebeck 
68859ff563dSChristian Schoenebeck     do_symlink(v9p, "05", "symlink_file", "real_file");
68959ff563dSChristian Schoenebeck 
69059ff563dSChristian Schoenebeck     /* check if created link exists now */
69159ff563dSChristian Schoenebeck     g_assert(stat(symlink_file, &st) == 0);
69259ff563dSChristian Schoenebeck }
69359ff563dSChristian Schoenebeck 
6945b28ab8bSChristian Schoenebeck static void fs_unlinkat_symlink(void *obj, void *data,
6955b28ab8bSChristian Schoenebeck                                 QGuestAllocator *t_alloc)
6965b28ab8bSChristian Schoenebeck {
6975b28ab8bSChristian Schoenebeck     QVirtio9P *v9p = obj;
698684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
6995b28ab8bSChristian Schoenebeck     struct stat st;
70065ceee0aSChristian Schoenebeck     g_autofree char *real_file = virtio_9p_test_path("06/real_file");
70165ceee0aSChristian Schoenebeck     g_autofree char *symlink_file = virtio_9p_test_path("06/symlink_file");
7025b28ab8bSChristian Schoenebeck 
7035b28ab8bSChristian Schoenebeck     do_attach(v9p);
7045b28ab8bSChristian Schoenebeck     do_mkdir(v9p, "/", "06");
7055b28ab8bSChristian Schoenebeck     do_lcreate(v9p, "06", "real_file");
7065b28ab8bSChristian Schoenebeck     g_assert(stat(real_file, &st) == 0);
7075b28ab8bSChristian Schoenebeck     g_assert((st.st_mode & S_IFMT) == S_IFREG);
7085b28ab8bSChristian Schoenebeck 
7095b28ab8bSChristian Schoenebeck     do_symlink(v9p, "06", "symlink_file", "real_file");
7105b28ab8bSChristian Schoenebeck     g_assert(stat(symlink_file, &st) == 0);
7115b28ab8bSChristian Schoenebeck 
7125b28ab8bSChristian Schoenebeck     do_unlinkat(v9p, "06", "symlink_file", 0);
7135b28ab8bSChristian Schoenebeck     /* symlink should be gone now */
7145b28ab8bSChristian Schoenebeck     g_assert(stat(symlink_file, &st) != 0);
7155b28ab8bSChristian Schoenebeck }
7165b28ab8bSChristian Schoenebeck 
71764e3d403SChristian Schoenebeck static void fs_hardlink_file(void *obj, void *data, QGuestAllocator *t_alloc)
71864e3d403SChristian Schoenebeck {
71964e3d403SChristian Schoenebeck     QVirtio9P *v9p = obj;
720684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
72164e3d403SChristian Schoenebeck     struct stat st_real, st_link;
72265ceee0aSChristian Schoenebeck     g_autofree char *real_file = virtio_9p_test_path("07/real_file");
72365ceee0aSChristian Schoenebeck     g_autofree char *hardlink_file = virtio_9p_test_path("07/hardlink_file");
72464e3d403SChristian Schoenebeck 
72564e3d403SChristian Schoenebeck     do_attach(v9p);
72664e3d403SChristian Schoenebeck     do_mkdir(v9p, "/", "07");
72764e3d403SChristian Schoenebeck     do_lcreate(v9p, "07", "real_file");
72864e3d403SChristian Schoenebeck     g_assert(stat(real_file, &st_real) == 0);
72964e3d403SChristian Schoenebeck     g_assert((st_real.st_mode & S_IFMT) == S_IFREG);
73064e3d403SChristian Schoenebeck 
73164e3d403SChristian Schoenebeck     do_hardlink(v9p, "07", "hardlink_file", "07/real_file");
73264e3d403SChristian Schoenebeck 
73364e3d403SChristian Schoenebeck     /* check if link exists now ... */
73464e3d403SChristian Schoenebeck     g_assert(stat(hardlink_file, &st_link) == 0);
73564e3d403SChristian Schoenebeck     /* ... and it's a hard link, right? */
73664e3d403SChristian Schoenebeck     g_assert((st_link.st_mode & S_IFMT) == S_IFREG);
73764e3d403SChristian Schoenebeck     g_assert(st_link.st_dev == st_real.st_dev);
73864e3d403SChristian Schoenebeck     g_assert(st_link.st_ino == st_real.st_ino);
73964e3d403SChristian Schoenebeck }
74064e3d403SChristian Schoenebeck 
7414d0746e2SChristian Schoenebeck static void fs_unlinkat_hardlink(void *obj, void *data,
7424d0746e2SChristian Schoenebeck                                  QGuestAllocator *t_alloc)
7434d0746e2SChristian Schoenebeck {
7444d0746e2SChristian Schoenebeck     QVirtio9P *v9p = obj;
745684f9120SChristian Schoenebeck     v9fs_set_allocator(t_alloc);
7464d0746e2SChristian Schoenebeck     struct stat st_real, st_link;
74765ceee0aSChristian Schoenebeck     g_autofree char *real_file = virtio_9p_test_path("08/real_file");
74865ceee0aSChristian Schoenebeck     g_autofree char *hardlink_file = virtio_9p_test_path("08/hardlink_file");
7494d0746e2SChristian Schoenebeck 
7504d0746e2SChristian Schoenebeck     do_attach(v9p);
7514d0746e2SChristian Schoenebeck     do_mkdir(v9p, "/", "08");
7524d0746e2SChristian Schoenebeck     do_lcreate(v9p, "08", "real_file");
7534d0746e2SChristian Schoenebeck     g_assert(stat(real_file, &st_real) == 0);
7544d0746e2SChristian Schoenebeck     g_assert((st_real.st_mode & S_IFMT) == S_IFREG);
7554d0746e2SChristian Schoenebeck 
7564d0746e2SChristian Schoenebeck     do_hardlink(v9p, "08", "hardlink_file", "08/real_file");
7574d0746e2SChristian Schoenebeck     g_assert(stat(hardlink_file, &st_link) == 0);
7584d0746e2SChristian Schoenebeck 
7594d0746e2SChristian Schoenebeck     do_unlinkat(v9p, "08", "hardlink_file", 0);
7604d0746e2SChristian Schoenebeck     /* symlink should be gone now */
7614d0746e2SChristian Schoenebeck     g_assert(stat(hardlink_file, &st_link) != 0);
7624d0746e2SChristian Schoenebeck     /* and old file should still exist */
7634d0746e2SChristian Schoenebeck     g_assert(stat(real_file, &st_real) == 0);
7644d0746e2SChristian Schoenebeck }
7654d0746e2SChristian Schoenebeck 
7663a565c64SChristian Schoenebeck static void *assign_9p_local_driver(GString *cmd_line, void *arg)
7673a565c64SChristian Schoenebeck {
7683a565c64SChristian Schoenebeck     virtio_9p_assign_local_driver(cmd_line, "security_model=mapped-xattr");
7693a565c64SChristian Schoenebeck     return arg;
7703a565c64SChristian Schoenebeck }
7713a565c64SChristian Schoenebeck 
772dfbe8b43SEmanuele Giuseppe Esposito static void register_virtio_9p_test(void)
7731211d81bSGreg Kurz {
7743a565c64SChristian Schoenebeck 
7753a565c64SChristian Schoenebeck     QOSGraphTestOptions opts = {
7763a565c64SChristian Schoenebeck     };
7773a565c64SChristian Schoenebeck 
7783a565c64SChristian Schoenebeck     /* 9pfs test cases using the 'synth' filesystem driver */
7793a565c64SChristian Schoenebeck     qos_add_test("synth/config", "virtio-9p", pci_config, &opts);
7803a565c64SChristian Schoenebeck     qos_add_test("synth/version/basic", "virtio-9p", fs_version,  &opts);
7813a565c64SChristian Schoenebeck     qos_add_test("synth/attach/basic", "virtio-9p", fs_attach,  &opts);
7823a565c64SChristian Schoenebeck     qos_add_test("synth/walk/basic", "virtio-9p", fs_walk,  &opts);
783eefd2394SChristian Schoenebeck     qos_add_test("synth/walk/no_slash", "virtio-9p", fs_walk_no_slash,
7843a565c64SChristian Schoenebeck                   &opts);
785c1668948SChristian Schoenebeck     qos_add_test("synth/walk/none", "virtio-9p", fs_walk_none, &opts);
786eefd2394SChristian Schoenebeck     qos_add_test("synth/walk/dotdot_from_root", "virtio-9p",
7873a565c64SChristian Schoenebeck                  fs_walk_dotdot,  &opts);
7889472a689SChristian Schoenebeck     qos_add_test("synth/walk/non_existent", "virtio-9p", fs_walk_nonexistent,
7899472a689SChristian Schoenebeck                   &opts);
79015fbff48SChristian Schoenebeck     qos_add_test("synth/walk/2nd_non_existent", "virtio-9p",
79115fbff48SChristian Schoenebeck                  fs_walk_2nd_nonexistent, &opts);
7923a565c64SChristian Schoenebeck     qos_add_test("synth/lopen/basic", "virtio-9p", fs_lopen,  &opts);
7933a565c64SChristian Schoenebeck     qos_add_test("synth/write/basic", "virtio-9p", fs_write,  &opts);
794eefd2394SChristian Schoenebeck     qos_add_test("synth/flush/success", "virtio-9p", fs_flush_success,
7953a565c64SChristian Schoenebeck                   &opts);
796eefd2394SChristian Schoenebeck     qos_add_test("synth/flush/ignored", "virtio-9p", fs_flush_ignored,
7973a565c64SChristian Schoenebeck                   &opts);
7983a565c64SChristian Schoenebeck     qos_add_test("synth/readdir/basic", "virtio-9p", fs_readdir,  &opts);
799eefd2394SChristian Schoenebeck     qos_add_test("synth/readdir/split_512", "virtio-9p",
8003a565c64SChristian Schoenebeck                  fs_readdir_split_512,  &opts);
801eefd2394SChristian Schoenebeck     qos_add_test("synth/readdir/split_256", "virtio-9p",
8023a565c64SChristian Schoenebeck                  fs_readdir_split_256,  &opts);
803eefd2394SChristian Schoenebeck     qos_add_test("synth/readdir/split_128", "virtio-9p",
8043a565c64SChristian Schoenebeck                  fs_readdir_split_128,  &opts);
8053a565c64SChristian Schoenebeck 
8063a565c64SChristian Schoenebeck 
8073a565c64SChristian Schoenebeck     /* 9pfs test cases using the 'local' filesystem driver */
808558f5c42SGreg Kurz 
809558f5c42SGreg Kurz     /*
810558f5c42SGreg Kurz      * XXX: Until we are sure that these tests can run everywhere,
811558f5c42SGreg Kurz      * keep them as "slow" so that they aren't run with "make check".
812558f5c42SGreg Kurz      */
813558f5c42SGreg Kurz     if (!g_test_slow()) {
814558f5c42SGreg Kurz         return;
815558f5c42SGreg Kurz     }
816558f5c42SGreg Kurz 
8173a565c64SChristian Schoenebeck     opts.before = assign_9p_local_driver;
8183a565c64SChristian Schoenebeck     qos_add_test("local/config", "virtio-9p", pci_config,  &opts);
819653daf38SChristian Schoenebeck     qos_add_test("local/create_dir", "virtio-9p", fs_create_dir, &opts);
820b37d62d6SChristian Schoenebeck     qos_add_test("local/unlinkat_dir", "virtio-9p", fs_unlinkat_dir, &opts);
821b09dbfddSChristian Schoenebeck     qos_add_test("local/create_file", "virtio-9p", fs_create_file, &opts);
822472c18b8SChristian Schoenebeck     qos_add_test("local/unlinkat_file", "virtio-9p", fs_unlinkat_file, &opts);
82359ff563dSChristian Schoenebeck     qos_add_test("local/symlink_file", "virtio-9p", fs_symlink_file, &opts);
8245b28ab8bSChristian Schoenebeck     qos_add_test("local/unlinkat_symlink", "virtio-9p", fs_unlinkat_symlink,
8255b28ab8bSChristian Schoenebeck                  &opts);
82664e3d403SChristian Schoenebeck     qos_add_test("local/hardlink_file", "virtio-9p", fs_hardlink_file, &opts);
8274d0746e2SChristian Schoenebeck     qos_add_test("local/unlinkat_hardlink", "virtio-9p", fs_unlinkat_hardlink,
8284d0746e2SChristian Schoenebeck                  &opts);
8291211d81bSGreg Kurz }
8301211d81bSGreg Kurz 
831dfbe8b43SEmanuele Giuseppe Esposito libqos_init(register_virtio_9p_test);
832136b7af2SChristian Schoenebeck 
833136b7af2SChristian Schoenebeck static void __attribute__((constructor)) construct_9p_test(void)
834136b7af2SChristian Schoenebeck {
835136b7af2SChristian Schoenebeck     /* make sure test dir for the 'local' tests exists */
836136b7af2SChristian Schoenebeck     virtio_9p_create_local_test_dir();
837136b7af2SChristian Schoenebeck }
838136b7af2SChristian Schoenebeck 
839136b7af2SChristian Schoenebeck static void __attribute__((destructor)) destruct_9p_test(void)
840136b7af2SChristian Schoenebeck {
841136b7af2SChristian Schoenebeck     /* remove previously created test dir when test suite completed */
842136b7af2SChristian Schoenebeck     virtio_9p_remove_local_test_dir();
843136b7af2SChristian Schoenebeck }
844