2 * QTest testcase for VirtIO 9P
4 * Copyright (c) 2014 SUSE LINUX Products GmbH
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.
11 * Not so fast! You might want to read the 9p developer docs first:
12 * https://wiki.qemu.org/Documentation/9p
15 #include "qemu/osdep.h"
16 #include "qemu/module.h"
17 #include "libqos/virtio-9p-client.h"
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__)
24 static void pci_config(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
27 v9fs_set_allocator(t_alloc
);
28 size_t tag_len
= qvirtio_config_readw(v9p
->vdev
, 0);
29 g_autofree
char *tag
= NULL
;
32 g_assert_cmpint(tag_len
, ==, strlen(MOUNT_TAG
));
34 tag
= g_malloc(tag_len
);
35 for (i
= 0; i
< tag_len
; i
++) {
36 tag
[i
] = qvirtio_config_readb(v9p
->vdev
, i
+ 2);
38 g_assert_cmpmem(tag
, tag_len
, MOUNT_TAG
, tag_len
);
41 static inline bool is_same_qid(v9fs_qid a
, v9fs_qid b
)
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;
47 static void fs_version(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
49 v9fs_set_allocator(t_alloc
);
50 tversion({ .client
= obj
});
53 static void fs_attach(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
55 v9fs_set_allocator(t_alloc
);
56 tattach({ .client
= obj
});
59 static void fs_walk(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
62 v9fs_set_allocator(t_alloc
);
63 char *wnames
[P9_MAXWELEM
];
65 g_autofree v9fs_qid
*wqid
= NULL
;
68 for (i
= 0; i
< P9_MAXWELEM
; i
++) {
69 wnames
[i
] = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE
, i
);
72 tattach({ .client
= v9p
});
74 .client
= v9p
, .fid
= 0, .newfid
= 1,
75 .nwname
= P9_MAXWELEM
, .wnames
= wnames
,
76 .rwalk
= { .nwqid
= &nwqid
, .wqid
= &wqid
}
79 g_assert_cmpint(nwqid
, ==, P9_MAXWELEM
);
81 for (i
= 0; i
< P9_MAXWELEM
; i
++) {
86 static bool fs_dirents_contain_name(struct V9fsDirent
*e
, const char* name
)
88 for (; e
; e
= e
->next
) {
89 if (!strcmp(e
->name
, name
)) {
96 /* basic readdir test where reply fits into a single response message */
97 static void fs_readdir(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
100 v9fs_set_allocator(t_alloc
);
101 char *wnames
[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR
) };
104 uint32_t count
, nentries
;
105 struct V9fsDirent
*entries
= NULL
;
108 tattach({ .client
= v9p
});
110 .client
= v9p
, .fid
= 0, .newfid
= 1,
111 .nwname
= 1, .wnames
= wnames
, .rwalk
.nwqid
= &nqid
113 g_assert_cmpint(nqid
, ==, 1);
115 req
= v9fs_tlopen(v9p
, 1, O_DIRECTORY
, 0);
116 v9fs_req_wait_for_reply(req
, NULL
);
117 v9fs_rlopen(req
, &qid
, NULL
);
120 * submit count = msize - 11, because 11 is the header size of Rreaddir
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
);
127 * Assuming msize (P9_MAX_SIZE) is large enough so we can retrieve all
128 * dir entries with only one readdir request.
132 QTEST_V9FS_SYNTH_READDIR_NFILES
+ 2 /* "." and ".." */
136 * Check all file names exist in returned entries, ignore their order
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);
147 v9fs_free_dirents(entries
);
151 /* readdir test where overall request is split over several messages */
152 static void do_readdir_split(QVirtio9P
*v9p
, uint32_t count
)
154 char *wnames
[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR
) };
157 uint32_t nentries
, npartialentries
;
158 struct V9fsDirent
*entries
, *tail
, *partialentries
;
163 tattach({ .client
= v9p
});
172 .client
= v9p
, .fid
= 0, .newfid
= fid
,
173 .nwname
= 1, .wnames
= wnames
, .rwalk
.nwqid
= &nqid
175 g_assert_cmpint(nqid
, ==, 1);
177 req
= v9fs_tlopen(v9p
, fid
, O_DIRECTORY
, 0);
178 v9fs_req_wait_for_reply(req
, NULL
);
179 v9fs_rlopen(req
, &qid
, NULL
);
182 * send as many Treaddir requests as required to get all directory
187 partialentries
= NULL
;
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
) {
194 entries
= partialentries
;
195 nentries
= npartialentries
;
196 tail
= partialentries
;
198 tail
->next
= partialentries
;
199 nentries
+= npartialentries
;
204 offset
= tail
->offset
;
212 QTEST_V9FS_SYNTH_READDIR_NFILES
+ 2 /* "." and ".." */
216 * Check all file names exist in returned entries, ignore their order
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);
227 v9fs_free_dirents(entries
);
232 static void fs_walk_no_slash(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
234 QVirtio9P
*v9p
= obj
;
235 v9fs_set_allocator(t_alloc
);
236 char *wnames
[] = { g_strdup(" /") };
238 tattach({ .client
= v9p
});
240 .client
= v9p
, .fid
= 0, .newfid
= 1, .nwname
= 1, .wnames
= wnames
,
247 static void fs_walk_nonexistent(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
249 QVirtio9P
*v9p
= obj
;
250 v9fs_set_allocator(t_alloc
);
252 tattach({ .client
= v9p
});
254 * The 9p2000 protocol spec says: "If the first element cannot be walked
255 * for any reason, Rerror is returned."
257 twalk({ .client
= v9p
, .path
= "non-existent", .expectErr
= ENOENT
});
260 static void fs_walk_2nd_nonexistent(void *obj
, void *data
,
261 QGuestAllocator
*t_alloc
)
263 QVirtio9P
*v9p
= obj
;
264 v9fs_set_allocator(t_alloc
);
268 g_autofree v9fs_qid
*wqid
= NULL
;
269 g_autofree
char *path
= g_strdup_printf(
270 QTEST_V9FS_SYNTH_WALK_FILE
"/non-existent", 0
273 tattach({ .client
= v9p
, .rattach
.qid
= &root_qid
});
275 .client
= v9p
, .path
= path
,
276 .rwalk
= { .nwqid
= &nwqid
, .wqid
= &wqid
}
279 * The 9p2000 protocol spec says: "nwqid is therefore either nwname or the
280 * index of the first elementwise walk that failed."
284 /* returned QID wqid[0] is file ID of 1st subdir */
285 g_assert(wqid
&& wqid
[0] && !is_same_qid(root_qid
, wqid
[0]));
287 /* expect fid being unaffected by walk above */
289 .client
= v9p
, .fid
= fid
, .request_mask
= P9_GETATTR_BASIC
,
294 static void fs_walk_none(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
296 QVirtio9P
*v9p
= obj
;
297 v9fs_set_allocator(t_alloc
);
299 g_autofree v9fs_qid
*wqid
= NULL
;
300 struct v9fs_attr attr
;
302 tversion({ .client
= v9p
});
304 .client
= v9p
, .fid
= 0, .n_uname
= getuid(),
305 .rattach
.qid
= &root_qid
309 .client
= v9p
, .fid
= 0, .newfid
= 1, .nwname
= 0, .wnames
= NULL
,
313 /* special case: no QID is returned if nwname=0 was sent */
314 g_assert(wqid
== NULL
);
317 .client
= v9p
, .fid
= 1, .request_mask
= P9_GETATTR_BASIC
,
318 .rgetattr
.attr
= &attr
321 g_assert(is_same_qid(root_qid
, attr
.qid
));
324 static void fs_walk_dotdot(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
326 QVirtio9P
*v9p
= obj
;
327 v9fs_set_allocator(t_alloc
);
328 char *wnames
[] = { g_strdup("..") };
330 g_autofree v9fs_qid
*wqid
= NULL
;
332 tversion({ .client
= v9p
});
334 .client
= v9p
, .fid
= 0, .n_uname
= getuid(),
335 .rattach
.qid
= &root_qid
339 .client
= v9p
, .fid
= 0, .newfid
= 1, .nwname
= 1, .wnames
= wnames
,
340 .rwalk
.wqid
= &wqid
/* We now we'll get one qid */
343 g_assert_cmpmem(&root_qid
, 13, wqid
[0], 13);
348 static void fs_lopen(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
350 QVirtio9P
*v9p
= obj
;
351 v9fs_set_allocator(t_alloc
);
352 char *wnames
[] = { g_strdup(QTEST_V9FS_SYNTH_LOPEN_FILE
) };
355 tattach({ .client
= v9p
});
357 .client
= v9p
, .fid
= 0, .newfid
= 1, .nwname
= 1, .wnames
= wnames
360 req
= v9fs_tlopen(v9p
, 1, O_WRONLY
, 0);
361 v9fs_req_wait_for_reply(req
, NULL
);
362 v9fs_rlopen(req
, NULL
, NULL
);
367 static void fs_write(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
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
);
377 tattach({ .client
= v9p
});
379 .client
= v9p
, .fid
= 0, .newfid
= 1, .nwname
= 1, .wnames
= wnames
382 req
= v9fs_tlopen(v9p
, 1, O_WRONLY
, 0);
383 v9fs_req_wait_for_reply(req
, NULL
);
384 v9fs_rlopen(req
, NULL
, NULL
);
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
);
394 static void fs_flush_success(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
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
;
401 uint8_t should_block
;
403 tattach({ .client
= v9p
});
405 .client
= v9p
, .fid
= 0, .newfid
= 1, .nwname
= 1, .wnames
= wnames
408 req
= v9fs_tlopen(v9p
, 1, O_WRONLY
, 0);
409 v9fs_req_wait_for_reply(req
, NULL
);
410 v9fs_rlopen(req
, NULL
, NULL
);
412 /* This will cause the 9p server to try to write data to the backend,
413 * until the write request gets cancelled.
416 req
= v9fs_twrite(v9p
, 1, 0, sizeof(should_block
), &should_block
, 0);
418 flush_req
= v9fs_tflush(v9p
, req
->tag
, 1);
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.
423 v9fs_req_wait_for_reply(req
, &reply_len
);
424 g_assert_cmpint(reply_len
, ==, 0);
426 v9fs_rflush(flush_req
);
431 static void fs_flush_ignored(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
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
;
438 uint8_t should_block
;
440 tattach({ .client
= v9p
});
442 .client
= v9p
, .fid
= 0, .newfid
= 1, .nwname
= 1, .wnames
= wnames
445 req
= v9fs_tlopen(v9p
, 1, O_WRONLY
, 0);
446 v9fs_req_wait_for_reply(req
, NULL
);
447 v9fs_rlopen(req
, NULL
, NULL
);
449 /* This will cause the write request to complete right away, before it
450 * could be actually cancelled.
453 req
= v9fs_twrite(v9p
, 1, 0, sizeof(should_block
), &should_block
, 0);
455 flush_req
= v9fs_tflush(v9p
, req
->tag
, 1);
457 /* The write request is supposed to complete. The server should
458 * reply to the write request and the flush request.
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
);
468 static void do_mkdir(QVirtio9P
*v9p
, const char *path
, const char *cname
)
470 g_autofree
char *name
= g_strdup(cname
);
474 fid
= twalk({ .client
= v9p
, .path
= path
}).newfid
;
476 req
= v9fs_tmkdir(v9p
, fid
, name
, 0750, 0, 0);
477 v9fs_req_wait_for_reply(req
, NULL
);
478 v9fs_rmkdir(req
, NULL
);
481 /* create a regular file with Tlcreate and return file's fid */
482 static uint32_t do_lcreate(QVirtio9P
*v9p
, const char *path
,
485 g_autofree
char *name
= g_strdup(cname
);
489 fid
= twalk({ .client
= v9p
, .path
= path
}).newfid
;
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
);
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
,
502 g_autofree
char *name
= g_strdup(clink
);
503 g_autofree
char *dst
= g_strdup(to
);
507 fid
= twalk({ .client
= v9p
, .path
= path
}).newfid
;
509 req
= v9fs_tsymlink(v9p
, fid
, name
, dst
, 0, 0);
510 v9fs_req_wait_for_reply(req
, NULL
);
511 v9fs_rsymlink(req
, NULL
);
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
,
521 dfid
= twalk({ .client
= v9p
, .path
= path
}).newfid
;
522 fid
= twalk({ .client
= v9p
, .path
= to
}).newfid
;
524 req
= v9fs_tlink(v9p
, dfid
, fid
, clink
, 0);
525 v9fs_req_wait_for_reply(req
, NULL
);
529 static void do_unlinkat(QVirtio9P
*v9p
, const char *atpath
, const char *rpath
,
532 g_autofree
char *name
= g_strdup(rpath
);
536 fid
= twalk({ .client
= v9p
, .path
= atpath
}).newfid
;
538 req
= v9fs_tunlinkat(v9p
, fid
, name
, flags
, 0);
539 v9fs_req_wait_for_reply(req
, NULL
);
543 static void fs_readdir_split_128(void *obj
, void *data
,
544 QGuestAllocator
*t_alloc
)
546 v9fs_set_allocator(t_alloc
);
547 do_readdir_split(obj
, 128);
550 static void fs_readdir_split_256(void *obj
, void *data
,
551 QGuestAllocator
*t_alloc
)
553 v9fs_set_allocator(t_alloc
);
554 do_readdir_split(obj
, 256);
557 static void fs_readdir_split_512(void *obj
, void *data
,
558 QGuestAllocator
*t_alloc
)
560 v9fs_set_allocator(t_alloc
);
561 do_readdir_split(obj
, 512);
565 /* tests using the 9pfs 'local' fs driver */
567 static void fs_create_dir(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
569 QVirtio9P
*v9p
= obj
;
570 v9fs_set_allocator(t_alloc
);
572 g_autofree
char *root_path
= virtio_9p_test_path("");
573 g_autofree
char *new_dir
= virtio_9p_test_path("01");
575 g_assert(root_path
!= NULL
);
577 tattach({ .client
= v9p
});
578 do_mkdir(v9p
, "/", "01");
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
);
586 static void fs_unlinkat_dir(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
588 QVirtio9P
*v9p
= obj
;
589 v9fs_set_allocator(t_alloc
);
591 g_autofree
char *root_path
= virtio_9p_test_path("");
592 g_autofree
char *new_dir
= virtio_9p_test_path("02");
594 g_assert(root_path
!= NULL
);
596 tattach({ .client
= v9p
});
597 do_mkdir(v9p
, "/", "02");
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
);
604 do_unlinkat(v9p
, "/", "02", P9_DOTL_AT_REMOVEDIR
);
605 /* directory should be gone now */
606 g_assert(stat(new_dir
, &st
) != 0);
609 static void fs_create_file(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
611 QVirtio9P
*v9p
= obj
;
612 v9fs_set_allocator(t_alloc
);
614 g_autofree
char *new_file
= virtio_9p_test_path("03/1st_file");
616 tattach({ .client
= v9p
});
617 do_mkdir(v9p
, "/", "03");
618 do_lcreate(v9p
, "03", "1st_file");
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
);
626 static void fs_unlinkat_file(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
628 QVirtio9P
*v9p
= obj
;
629 v9fs_set_allocator(t_alloc
);
631 g_autofree
char *new_file
= virtio_9p_test_path("04/doa_file");
633 tattach({ .client
= v9p
});
634 do_mkdir(v9p
, "/", "04");
635 do_lcreate(v9p
, "04", "doa_file");
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
);
642 do_unlinkat(v9p
, "04", "doa_file", 0);
643 /* file should be gone now */
644 g_assert(stat(new_file
, &st
) != 0);
647 static void fs_symlink_file(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
649 QVirtio9P
*v9p
= obj
;
650 v9fs_set_allocator(t_alloc
);
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");
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
);
661 do_symlink(v9p
, "05", "symlink_file", "real_file");
663 /* check if created link exists now */
664 g_assert(stat(symlink_file
, &st
) == 0);
667 static void fs_unlinkat_symlink(void *obj
, void *data
,
668 QGuestAllocator
*t_alloc
)
670 QVirtio9P
*v9p
= obj
;
671 v9fs_set_allocator(t_alloc
);
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");
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
);
682 do_symlink(v9p
, "06", "symlink_file", "real_file");
683 g_assert(stat(symlink_file
, &st
) == 0);
685 do_unlinkat(v9p
, "06", "symlink_file", 0);
686 /* symlink should be gone now */
687 g_assert(stat(symlink_file
, &st
) != 0);
690 static void fs_hardlink_file(void *obj
, void *data
, QGuestAllocator
*t_alloc
)
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");
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
);
704 do_hardlink(v9p
, "07", "hardlink_file", "07/real_file");
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
);
714 static void fs_unlinkat_hardlink(void *obj
, void *data
,
715 QGuestAllocator
*t_alloc
)
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");
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
);
729 do_hardlink(v9p
, "08", "hardlink_file", "08/real_file");
730 g_assert(stat(hardlink_file
, &st_link
) == 0);
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);
739 static void *assign_9p_local_driver(GString
*cmd_line
, void *arg
)
741 virtio_9p_assign_local_driver(cmd_line
, "security_model=mapped-xattr");
745 static void register_virtio_9p_test(void)
748 QOSGraphTestOptions opts
= {
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
,
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
,
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
,
769 qos_add_test("synth/flush/ignored", "virtio-9p", fs_flush_ignored
,
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
);
780 /* 9pfs test cases using the 'local' filesystem driver */
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".
786 if (!g_test_slow()) {
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
,
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
,
804 libqos_init(register_virtio_9p_test
);
806 static void __attribute__((constructor
)) construct_9p_test(void)
808 /* make sure test dir for the 'local' tests exists */
809 virtio_9p_create_local_test_dir();
812 static void __attribute__((destructor
)) destruct_9p_test(void)
814 /* remove previously created test dir when test suite completed */
815 virtio_9p_remove_local_test_dir();