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