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