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