1 /* 2 * QEMU XenStore XsNode testing 3 * 4 * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. 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 #include "qemu/osdep.h" 11 #include "qapi/error.h" 12 #include "qemu/module.h" 13 14 static int nr_xs_nodes; 15 static GList *xs_node_list; 16 17 #define XS_NODE_UNIT_TEST 18 19 /* 20 * We don't need the core Xen definitions. And we *do* want to be able 21 * to run the unit tests even on architectures that Xen doesn't support 22 * (because life's too short to bother doing otherwise, and test coverage 23 * doesn't hurt). 24 */ 25 #define __XEN_PUBLIC_XEN_H__ 26 typedef unsigned long xen_pfn_t; 27 28 #include "hw/i386/kvm/xenstore_impl.c" 29 30 #define DOMID_QEMU 0 31 #define DOMID_GUEST 1 32 33 static void dump_ref(const char *name, XsNode *n, int indent) 34 { 35 int i; 36 37 if (!indent && name) { 38 printf("%s:\n", name); 39 } 40 41 for (i = 0; i < indent; i++) { 42 printf(" "); 43 } 44 45 printf("->%p(%d, '%s'): '%.*s'%s%s\n", n, n->ref, n->name, 46 (int)(n->content ? n->content->len : strlen("<empty>")), 47 n->content ? (char *)n->content->data : "<empty>", 48 n->modified_in_tx ? " MODIFIED" : "", 49 n->deleted_in_tx ? " DELETED" : ""); 50 51 if (n->children) { 52 g_hash_table_foreach(n->children, (void *)dump_ref, 53 GINT_TO_POINTER(indent + 2)); 54 } 55 } 56 57 /* This doesn't happen in qemu but we want to make valgrind happy */ 58 static void xs_impl_delete(XenstoreImplState *s, bool last) 59 { 60 int err; 61 62 xs_impl_reset_watches(s, DOMID_GUEST); 63 g_assert(!s->nr_domu_watches); 64 65 err = xs_impl_rm(s, DOMID_QEMU, XBT_NULL, "/local"); 66 g_assert(!err); 67 g_assert(s->nr_nodes == 1); 68 69 g_hash_table_unref(s->watches); 70 g_hash_table_unref(s->transactions); 71 xs_node_unref(s->root); 72 g_free(s); 73 74 if (!last) { 75 return; 76 } 77 78 if (xs_node_list) { 79 GList *l; 80 for (l = xs_node_list; l; l = l->next) { 81 XsNode *n = l->data; 82 printf("Remaining node at %p name %s ref %u\n", n, n->name, 83 n->ref); 84 } 85 } 86 g_assert(!nr_xs_nodes); 87 } 88 89 struct compare_walk { 90 char path[XENSTORE_ABS_PATH_MAX + 1]; 91 XsNode *parent_2; 92 bool compare_ok; 93 }; 94 95 96 static bool compare_perms(GList *p1, GList *p2) 97 { 98 while (p1) { 99 if (!p2 || g_strcmp0(p1->data, p2->data)) { 100 return false; 101 } 102 p1 = p1->next; 103 p2 = p2->next; 104 } 105 return (p2 == NULL); 106 } 107 108 static bool compare_content(GByteArray *c1, GByteArray *c2) 109 { 110 size_t len1 = 0, len2 = 0; 111 112 if (c1) { 113 len1 = c1->len; 114 } 115 if (c2) { 116 len2 = c2->len; 117 } 118 if (len1 != len2) { 119 return false; 120 } 121 122 if (!len1) { 123 return true; 124 } 125 126 return !memcmp(c1->data, c2->data, len1); 127 } 128 129 static void compare_child(gpointer, gpointer, gpointer); 130 131 static void compare_nodes(struct compare_walk *cw, XsNode *n1, XsNode *n2) 132 { 133 int nr_children1 = 0, nr_children2 = 0; 134 135 if (n1->children) { 136 nr_children1 = g_hash_table_size(n1->children); 137 } 138 if (n2->children) { 139 nr_children2 = g_hash_table_size(n2->children); 140 } 141 142 if (n1->ref != n2->ref || 143 n1->deleted_in_tx != n2->deleted_in_tx || 144 n1->modified_in_tx != n2->modified_in_tx || 145 !compare_perms(n1->perms, n2->perms) || 146 !compare_content(n1->content, n2->content) || 147 nr_children1 != nr_children2) { 148 cw->compare_ok = false; 149 printf("Compare failure on '%s'\n", cw->path); 150 } 151 152 if (nr_children1) { 153 XsNode *oldparent = cw->parent_2; 154 cw->parent_2 = n2; 155 g_hash_table_foreach(n1->children, compare_child, cw); 156 157 cw->parent_2 = oldparent; 158 } 159 } 160 161 static void compare_child(gpointer key, gpointer val, gpointer opaque) 162 { 163 struct compare_walk *cw = opaque; 164 char *childname = key; 165 XsNode *child1 = val; 166 XsNode *child2 = g_hash_table_lookup(cw->parent_2->children, childname); 167 int pathlen = strlen(cw->path); 168 169 if (!child2) { 170 cw->compare_ok = false; 171 printf("Child '%s' does not exist under '%s'\n", childname, cw->path); 172 return; 173 } 174 175 strncat(cw->path, "/", sizeof(cw->path) - 1); 176 strncat(cw->path, childname, sizeof(cw->path) - 1); 177 178 compare_nodes(cw, child1, child2); 179 cw->path[pathlen] = '\0'; 180 } 181 182 static bool compare_trees(XsNode *n1, XsNode *n2) 183 { 184 struct compare_walk cw; 185 186 cw.path[0] = '\0'; 187 cw.parent_2 = n2; 188 cw.compare_ok = true; 189 190 if (!n1 || !n2) { 191 return false; 192 } 193 194 compare_nodes(&cw, n1, n2); 195 return cw.compare_ok; 196 } 197 198 static void compare_tx(gpointer key, gpointer val, gpointer opaque) 199 { 200 XenstoreImplState *s2 = opaque; 201 XsTransaction *t1 = val, *t2; 202 unsigned int tx_id = GPOINTER_TO_INT(key); 203 204 t2 = g_hash_table_lookup(s2->transactions, key); 205 g_assert(t2); 206 207 g_assert(t1->tx_id == tx_id); 208 g_assert(t2->tx_id == tx_id); 209 g_assert(t1->base_tx == t2->base_tx); 210 g_assert(t1->dom_id == t2->dom_id); 211 if (!compare_trees(t1->root, t2->root)) { 212 printf("Comparison failure in TX %u after serdes:\n", tx_id); 213 dump_ref("Original", t1->root, 0); 214 dump_ref("Deserialised", t2->root, 0); 215 g_assert(0); 216 } 217 g_assert(t1->nr_nodes == t2->nr_nodes); 218 } 219 220 static int write_str(XenstoreImplState *s, unsigned int dom_id, 221 unsigned int tx_id, const char *path, 222 const char *content) 223 { 224 GByteArray *d = g_byte_array_new(); 225 int err; 226 227 g_byte_array_append(d, (void *)content, strlen(content)); 228 err = xs_impl_write(s, dom_id, tx_id, path, d); 229 g_byte_array_unref(d); 230 return err; 231 } 232 233 static void watch_cb(void *_str, const char *path, const char *token) 234 { 235 GString *str = _str; 236 237 g_string_append(str, path); 238 g_string_append(str, token); 239 } 240 241 static void check_serdes(XenstoreImplState *s) 242 { 243 XenstoreImplState *s2 = xs_impl_create(DOMID_GUEST); 244 GByteArray *bytes = xs_impl_serialize(s); 245 int nr_transactions1, nr_transactions2; 246 int ret; 247 248 ret = xs_impl_deserialize(s2, bytes, DOMID_GUEST, watch_cb, NULL); 249 g_assert(!ret); 250 251 g_byte_array_unref(bytes); 252 253 g_assert(s->last_tx == s2->last_tx); 254 g_assert(s->root_tx == s2->root_tx); 255 256 if (!compare_trees(s->root, s2->root)) { 257 printf("Comparison failure in main tree after serdes:\n"); 258 dump_ref("Original", s->root, 0); 259 dump_ref("Deserialised", s2->root, 0); 260 g_assert(0); 261 } 262 263 nr_transactions1 = g_hash_table_size(s->transactions); 264 nr_transactions2 = g_hash_table_size(s2->transactions); 265 g_assert(nr_transactions1 == nr_transactions2); 266 267 g_hash_table_foreach(s->transactions, compare_tx, s2); 268 269 g_assert(s->nr_domu_watches == s2->nr_domu_watches); 270 g_assert(s->nr_domu_transactions == s2->nr_domu_transactions); 271 g_assert(s->nr_nodes == s2->nr_nodes); 272 xs_impl_delete(s2, false); 273 } 274 275 static XenstoreImplState *setup(void) 276 { 277 XenstoreImplState *s = xs_impl_create(DOMID_GUEST); 278 char *abspath; 279 GList *perms; 280 int err; 281 282 abspath = g_strdup_printf("/local/domain/%u", DOMID_GUEST); 283 284 err = write_str(s, DOMID_QEMU, XBT_NULL, abspath, ""); 285 g_assert(!err); 286 g_assert(s->nr_nodes == 4); 287 288 perms = g_list_append(NULL, g_strdup_printf("n%u", DOMID_QEMU)); 289 perms = g_list_append(perms, g_strdup_printf("r%u", DOMID_GUEST)); 290 291 err = xs_impl_set_perms(s, DOMID_QEMU, XBT_NULL, abspath, perms); 292 g_assert(!err); 293 294 g_list_free_full(perms, g_free); 295 g_free(abspath); 296 297 abspath = g_strdup_printf("/local/domain/%u/some", DOMID_GUEST); 298 299 err = write_str(s, DOMID_QEMU, XBT_NULL, abspath, ""); 300 g_assert(!err); 301 g_assert(s->nr_nodes == 5); 302 303 perms = g_list_append(NULL, g_strdup_printf("n%u", DOMID_GUEST)); 304 305 err = xs_impl_set_perms(s, DOMID_QEMU, XBT_NULL, abspath, perms); 306 g_assert(!err); 307 308 g_list_free_full(perms, g_free); 309 g_free(abspath); 310 311 return s; 312 } 313 314 static void test_xs_node_simple(void) 315 { 316 GByteArray *data = g_byte_array_new(); 317 XenstoreImplState *s = setup(); 318 GString *guest_watches = g_string_new(NULL); 319 GString *qemu_watches = g_string_new(NULL); 320 GList *items = NULL; 321 XsNode *old_root; 322 uint64_t gencnt; 323 int err; 324 325 g_assert(s); 326 327 err = xs_impl_watch(s, DOMID_GUEST, "some", "guestwatch", 328 watch_cb, guest_watches); 329 g_assert(!err); 330 g_assert(guest_watches->len == strlen("someguestwatch")); 331 g_assert(!strcmp(guest_watches->str, "someguestwatch")); 332 g_string_truncate(guest_watches, 0); 333 334 err = xs_impl_watch(s, 0, "/local/domain/1/some", "qemuwatch", 335 watch_cb, qemu_watches); 336 g_assert(!err); 337 g_assert(qemu_watches->len == strlen("/local/domain/1/someqemuwatch")); 338 g_assert(!strcmp(qemu_watches->str, "/local/domain/1/someqemuwatch")); 339 g_string_truncate(qemu_watches, 0); 340 341 /* Read gives ENOENT when it should */ 342 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "foo", data); 343 g_assert(err == ENOENT); 344 345 /* Write works */ 346 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", 347 "something"); 348 g_assert(s->nr_nodes == 7); 349 g_assert(!err); 350 g_assert(!strcmp(guest_watches->str, 351 "some/relative/pathguestwatch")); 352 g_assert(!strcmp(qemu_watches->str, 353 "/local/domain/1/some/relative/pathqemuwatch")); 354 355 g_string_truncate(qemu_watches, 0); 356 g_string_truncate(guest_watches, 0); 357 xs_impl_reset_watches(s, 0); 358 359 /* Read gives back what we wrote */ 360 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data); 361 g_assert(!err); 362 g_assert(data->len == strlen("something")); 363 g_assert(!memcmp(data->data, "something", data->len)); 364 365 /* Even if we use an abolute path */ 366 g_byte_array_set_size(data, 0); 367 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, 368 "/local/domain/1/some/relative/path", data); 369 g_assert(!err); 370 g_assert(data->len == strlen("something")); 371 372 g_assert(!qemu_watches->len); 373 g_assert(!guest_watches->len); 374 /* Keep a copy, to force COW mode */ 375 old_root = xs_node_ref(s->root); 376 377 /* Write somewhere we aren't allowed, in COW mode */ 378 err = write_str(s, DOMID_GUEST, XBT_NULL, "/local/domain/badplace", 379 "moredata"); 380 g_assert(err == EACCES); 381 g_assert(s->nr_nodes == 7); 382 383 /* Write works again */ 384 err = write_str(s, DOMID_GUEST, XBT_NULL, 385 "/local/domain/1/some/relative/path2", 386 "something else"); 387 g_assert(!err); 388 g_assert(s->nr_nodes == 8); 389 g_assert(!qemu_watches->len); 390 g_assert(!strcmp(guest_watches->str, "some/relative/path2guestwatch")); 391 g_string_truncate(guest_watches, 0); 392 393 /* Overwrite an existing node */ 394 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", 395 "another thing"); 396 g_assert(!err); 397 g_assert(s->nr_nodes == 8); 398 g_assert(!qemu_watches->len); 399 g_assert(!strcmp(guest_watches->str, "some/relative/pathguestwatch")); 400 g_string_truncate(guest_watches, 0); 401 402 /* We can list the two files we wrote */ 403 err = xs_impl_directory(s, DOMID_GUEST, XBT_NULL, "some/relative", &gencnt, 404 &items); 405 g_assert(!err); 406 g_assert(items); 407 g_assert(gencnt == 2); 408 g_assert(!strcmp(items->data, "path")); 409 g_assert(items->next); 410 g_assert(!strcmp(items->next->data, "path2")); 411 g_assert(!items->next->next); 412 g_list_free_full(items, g_free); 413 414 err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch", 415 watch_cb, guest_watches); 416 g_assert(!err); 417 418 err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch", 419 watch_cb, guest_watches); 420 g_assert(err == ENOENT); 421 422 err = xs_impl_watch(s, DOMID_GUEST, "some/relative/path2", "watchp2", 423 watch_cb, guest_watches); 424 g_assert(!err); 425 g_assert(guest_watches->len == strlen("some/relative/path2watchp2")); 426 g_assert(!strcmp(guest_watches->str, "some/relative/path2watchp2")); 427 g_string_truncate(guest_watches, 0); 428 429 err = xs_impl_watch(s, DOMID_GUEST, "/local/domain/1/some/relative", 430 "watchrel", watch_cb, guest_watches); 431 g_assert(!err); 432 g_assert(guest_watches->len == 433 strlen("/local/domain/1/some/relativewatchrel")); 434 g_assert(!strcmp(guest_watches->str, 435 "/local/domain/1/some/relativewatchrel")); 436 g_string_truncate(guest_watches, 0); 437 438 /* Write somewhere else which already existed */ 439 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "moredata"); 440 g_assert(!err); 441 g_assert(s->nr_nodes == 8); 442 443 /* Write somewhere we aren't allowed */ 444 err = write_str(s, DOMID_GUEST, XBT_NULL, "/local/domain/badplace", 445 "moredata"); 446 g_assert(err == EACCES); 447 448 g_assert(!strcmp(guest_watches->str, 449 "/local/domain/1/some/relativewatchrel")); 450 g_string_truncate(guest_watches, 0); 451 452 g_byte_array_set_size(data, 0); 453 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); 454 g_assert(!err); 455 g_assert(data->len == strlen("moredata")); 456 g_assert(!memcmp(data->data, "moredata", data->len)); 457 458 /* Overwrite existing data */ 459 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "otherdata"); 460 g_assert(!err); 461 g_string_truncate(guest_watches, 0); 462 463 g_byte_array_set_size(data, 0); 464 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); 465 g_assert(!err); 466 g_assert(data->len == strlen("otherdata")); 467 g_assert(!memcmp(data->data, "otherdata", data->len)); 468 469 /* Remove the subtree */ 470 err = xs_impl_rm(s, DOMID_GUEST, XBT_NULL, "some/relative"); 471 g_assert(!err); 472 g_assert(s->nr_nodes == 5); 473 474 /* Each watch fires with the least specific relevant path */ 475 g_assert(strstr(guest_watches->str, 476 "some/relative/path2watchp2")); 477 g_assert(strstr(guest_watches->str, 478 "/local/domain/1/some/relativewatchrel")); 479 g_string_truncate(guest_watches, 0); 480 481 g_byte_array_set_size(data, 0); 482 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); 483 g_assert(err == ENOENT); 484 g_byte_array_unref(data); 485 486 xs_impl_reset_watches(s, DOMID_GUEST); 487 g_string_free(qemu_watches, true); 488 g_string_free(guest_watches, true); 489 xs_node_unref(old_root); 490 xs_impl_delete(s, true); 491 } 492 493 494 static void do_test_xs_node_tx(bool fail, bool commit) 495 { 496 XenstoreImplState *s = setup(); 497 GString *watches = g_string_new(NULL); 498 GByteArray *data = g_byte_array_new(); 499 unsigned int tx_id = XBT_NULL; 500 int err; 501 502 g_assert(s); 503 504 /* Set a watch */ 505 err = xs_impl_watch(s, DOMID_GUEST, "some", "watch", 506 watch_cb, watches); 507 g_assert(!err); 508 g_assert(watches->len == strlen("somewatch")); 509 g_assert(!strcmp(watches->str, "somewatch")); 510 g_string_truncate(watches, 0); 511 512 /* Write something */ 513 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", 514 "something"); 515 g_assert(s->nr_nodes == 7); 516 g_assert(!err); 517 g_assert(!strcmp(watches->str, 518 "some/relative/pathwatch")); 519 g_string_truncate(watches, 0); 520 521 /* Create a transaction */ 522 err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id); 523 g_assert(!err); 524 525 if (fail) { 526 /* Write something else in the root */ 527 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", 528 "another thing"); 529 g_assert(!err); 530 g_assert(s->nr_nodes == 7); 531 g_assert(!strcmp(watches->str, 532 "some/relative/pathwatch")); 533 g_string_truncate(watches, 0); 534 } 535 536 g_assert(!watches->len); 537 538 /* Perform a write in the transaction */ 539 err = write_str(s, DOMID_GUEST, tx_id, "some/relative/path", 540 "something else"); 541 g_assert(!err); 542 g_assert(s->nr_nodes == 7); 543 g_assert(!watches->len); 544 545 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data); 546 g_assert(!err); 547 if (fail) { 548 g_assert(data->len == strlen("another thing")); 549 g_assert(!memcmp(data->data, "another thing", data->len)); 550 } else { 551 g_assert(data->len == strlen("something")); 552 g_assert(!memcmp(data->data, "something", data->len)); 553 } 554 g_byte_array_set_size(data, 0); 555 556 err = xs_impl_read(s, DOMID_GUEST, tx_id, "some/relative/path", data); 557 g_assert(!err); 558 g_assert(data->len == strlen("something else")); 559 g_assert(!memcmp(data->data, "something else", data->len)); 560 g_byte_array_set_size(data, 0); 561 562 check_serdes(s); 563 564 /* Attempt to commit the transaction */ 565 err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, commit); 566 if (commit && fail) { 567 g_assert(err == EAGAIN); 568 } else { 569 g_assert(!err); 570 } 571 if (commit && !fail) { 572 g_assert(!strcmp(watches->str, 573 "some/relative/pathwatch")); 574 g_string_truncate(watches, 0); 575 } else { 576 g_assert(!watches->len); 577 } 578 g_assert(s->nr_nodes == 7); 579 580 check_serdes(s); 581 582 err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch", 583 watch_cb, watches); 584 g_assert(!err); 585 586 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data); 587 g_assert(!err); 588 if (fail) { 589 g_assert(data->len == strlen("another thing")); 590 g_assert(!memcmp(data->data, "another thing", data->len)); 591 } else if (commit) { 592 g_assert(data->len == strlen("something else")); 593 g_assert(!memcmp(data->data, "something else", data->len)); 594 } else { 595 g_assert(data->len == strlen("something")); 596 g_assert(!memcmp(data->data, "something", data->len)); 597 } 598 g_byte_array_unref(data); 599 g_string_free(watches, true); 600 xs_impl_delete(s, true); 601 } 602 603 static void test_xs_node_tx_fail(void) 604 { 605 do_test_xs_node_tx(true, true); 606 } 607 608 static void test_xs_node_tx_abort(void) 609 { 610 do_test_xs_node_tx(false, false); 611 do_test_xs_node_tx(true, false); 612 } 613 static void test_xs_node_tx_succeed(void) 614 { 615 do_test_xs_node_tx(false, true); 616 } 617 618 static void test_xs_node_tx_rm(void) 619 { 620 XenstoreImplState *s = setup(); 621 GString *watches = g_string_new(NULL); 622 GByteArray *data = g_byte_array_new(); 623 unsigned int tx_id = XBT_NULL; 624 int err; 625 626 g_assert(s); 627 628 /* Set a watch */ 629 err = xs_impl_watch(s, DOMID_GUEST, "some", "watch", 630 watch_cb, watches); 631 g_assert(!err); 632 g_assert(watches->len == strlen("somewatch")); 633 g_assert(!strcmp(watches->str, "somewatch")); 634 g_string_truncate(watches, 0); 635 636 /* Write something */ 637 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", 638 "something"); 639 g_assert(!err); 640 g_assert(s->nr_nodes == 9); 641 g_assert(!strcmp(watches->str, 642 "some/deep/dark/relative/pathwatch")); 643 g_string_truncate(watches, 0); 644 645 /* Create a transaction */ 646 err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id); 647 g_assert(!err); 648 649 /* Delete the tree in the transaction */ 650 err = xs_impl_rm(s, DOMID_GUEST, tx_id, "some/deep/dark"); 651 g_assert(!err); 652 g_assert(s->nr_nodes == 9); 653 g_assert(!watches->len); 654 655 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", 656 data); 657 g_assert(!err); 658 g_assert(data->len == strlen("something")); 659 g_assert(!memcmp(data->data, "something", data->len)); 660 g_byte_array_set_size(data, 0); 661 662 check_serdes(s); 663 664 /* Commit the transaction */ 665 err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, true); 666 g_assert(!err); 667 g_assert(s->nr_nodes == 6); 668 669 g_assert(!strcmp(watches->str, "some/deep/darkwatch")); 670 g_string_truncate(watches, 0); 671 672 /* Now the node is gone */ 673 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", 674 data); 675 g_assert(err == ENOENT); 676 g_byte_array_unref(data); 677 678 err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch", 679 watch_cb, watches); 680 g_assert(!err); 681 682 g_string_free(watches, true); 683 xs_impl_delete(s, true); 684 } 685 686 static void test_xs_node_tx_resurrect(void) 687 { 688 XenstoreImplState *s = setup(); 689 GString *watches = g_string_new(NULL); 690 GByteArray *data = g_byte_array_new(); 691 unsigned int tx_id = XBT_NULL; 692 int err; 693 694 g_assert(s); 695 696 /* Write something */ 697 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", 698 "something"); 699 g_assert(!err); 700 g_assert(s->nr_nodes == 9); 701 702 /* Another node to remain shared */ 703 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/place/safe", "keepme"); 704 g_assert(!err); 705 g_assert(s->nr_nodes == 11); 706 707 /* This node will be wiped and resurrected */ 708 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark", 709 "foo"); 710 g_assert(!err); 711 g_assert(s->nr_nodes == 11); 712 713 /* Set a watch */ 714 err = xs_impl_watch(s, DOMID_GUEST, "some", "watch", 715 watch_cb, watches); 716 g_assert(!err); 717 g_assert(watches->len == strlen("somewatch")); 718 g_assert(!strcmp(watches->str, "somewatch")); 719 g_string_truncate(watches, 0); 720 721 /* Create a transaction */ 722 err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id); 723 g_assert(!err); 724 725 /* Delete the tree in the transaction */ 726 err = xs_impl_rm(s, DOMID_GUEST, tx_id, "some/deep"); 727 g_assert(!err); 728 g_assert(s->nr_nodes == 11); 729 g_assert(!watches->len); 730 731 /* Resurrect part of it */ 732 err = write_str(s, DOMID_GUEST, tx_id, "some/deep/dark/different/path", 733 "something"); 734 g_assert(!err); 735 g_assert(s->nr_nodes == 11); 736 737 check_serdes(s); 738 739 /* Commit the transaction */ 740 err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, true); 741 g_assert(!err); 742 g_assert(s->nr_nodes == 11); 743 744 check_serdes(s); 745 746 /* lost data */ 747 g_assert(strstr(watches->str, "some/deep/dark/different/pathwatch")); 748 /* topmost deleted */ 749 g_assert(strstr(watches->str, "some/deep/dark/relativewatch")); 750 /* lost data */ 751 g_assert(strstr(watches->str, "some/deep/darkwatch")); 752 753 g_string_truncate(watches, 0); 754 755 /* Now the node is gone */ 756 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", 757 data); 758 g_assert(err == ENOENT); 759 g_byte_array_unref(data); 760 761 check_serdes(s); 762 763 err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch", 764 watch_cb, watches); 765 g_assert(!err); 766 767 g_string_free(watches, true); 768 xs_impl_delete(s, true); 769 } 770 771 static void test_xs_node_tx_resurrect2(void) 772 { 773 XenstoreImplState *s = setup(); 774 GString *watches = g_string_new(NULL); 775 GByteArray *data = g_byte_array_new(); 776 unsigned int tx_id = XBT_NULL; 777 int err; 778 779 g_assert(s); 780 781 /* Write something */ 782 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", 783 "something"); 784 g_assert(!err); 785 g_assert(s->nr_nodes == 9); 786 787 /* Another node to remain shared */ 788 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/place/safe", "keepme"); 789 g_assert(!err); 790 g_assert(s->nr_nodes == 11); 791 792 /* This node will be wiped and resurrected */ 793 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark", 794 "foo"); 795 g_assert(!err); 796 g_assert(s->nr_nodes == 11); 797 798 /* Set a watch */ 799 err = xs_impl_watch(s, DOMID_GUEST, "some", "watch", 800 watch_cb, watches); 801 g_assert(!err); 802 g_assert(watches->len == strlen("somewatch")); 803 g_assert(!strcmp(watches->str, "somewatch")); 804 g_string_truncate(watches, 0); 805 806 /* Create a transaction */ 807 err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id); 808 g_assert(!err); 809 810 /* Delete the tree in the transaction */ 811 err = xs_impl_rm(s, DOMID_GUEST, tx_id, "some/deep"); 812 g_assert(!err); 813 g_assert(s->nr_nodes == 11); 814 g_assert(!watches->len); 815 816 /* Resurrect part of it */ 817 err = write_str(s, DOMID_GUEST, tx_id, "some/deep/dark/relative/path", 818 "something"); 819 g_assert(!err); 820 g_assert(s->nr_nodes == 11); 821 822 check_serdes(s); 823 824 /* Commit the transaction */ 825 err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, true); 826 g_assert(!err); 827 g_assert(s->nr_nodes == 11); 828 829 check_serdes(s); 830 831 /* lost data */ 832 g_assert(strstr(watches->str, "some/deep/dark/relative/pathwatch")); 833 /* lost data */ 834 g_assert(strstr(watches->str, "some/deep/darkwatch")); 835 836 g_string_truncate(watches, 0); 837 838 /* Now the node is gone */ 839 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", 840 data); 841 g_assert(!err); 842 g_assert(data->len == strlen("something")); 843 g_assert(!memcmp(data->data, "something", data->len)); 844 845 g_byte_array_unref(data); 846 847 check_serdes(s); 848 849 err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch", 850 watch_cb, watches); 851 g_assert(!err); 852 853 g_string_free(watches, true); 854 xs_impl_delete(s, true); 855 } 856 857 int main(int argc, char **argv) 858 { 859 g_test_init(&argc, &argv, NULL); 860 module_call_init(MODULE_INIT_QOM); 861 862 g_test_add_func("/xs_node/simple", test_xs_node_simple); 863 g_test_add_func("/xs_node/tx_abort", test_xs_node_tx_abort); 864 g_test_add_func("/xs_node/tx_fail", test_xs_node_tx_fail); 865 g_test_add_func("/xs_node/tx_succeed", test_xs_node_tx_succeed); 866 g_test_add_func("/xs_node/tx_rm", test_xs_node_tx_rm); 867 g_test_add_func("/xs_node/tx_resurrect", test_xs_node_tx_resurrect); 868 g_test_add_func("/xs_node/tx_resurrect2", test_xs_node_tx_resurrect2); 869 870 return g_test_run(); 871 } 872