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