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