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
dump_ref(const char * name,XsNode * n,int indent)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 */
xs_impl_delete(XenstoreImplState * s,bool last)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
compare_perms(GList * p1,GList * p2)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
compare_content(GByteArray * c1,GByteArray * c2)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
compare_nodes(struct compare_walk * cw,XsNode * n1,XsNode * n2)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
compare_child(gpointer key,gpointer val,gpointer opaque)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
compare_trees(XsNode * n1,XsNode * n2)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
compare_tx(gpointer key,gpointer val,gpointer opaque)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_not_reached();
216 }
217 g_assert(t1->nr_nodes == t2->nr_nodes);
218 }
219
write_str(XenstoreImplState * s,unsigned int dom_id,unsigned int tx_id,const char * path,const char * content)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
watch_cb(void * _str,const char * path,const char * token)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
check_serdes(XenstoreImplState * s)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_not_reached();
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
setup(void)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
test_xs_node_simple(void)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 absolute 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
do_test_xs_node_tx(bool fail,bool commit)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
test_xs_node_tx_fail(void)603 static void test_xs_node_tx_fail(void)
604 {
605 do_test_xs_node_tx(true, true);
606 }
607
test_xs_node_tx_abort(void)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 }
test_xs_node_tx_succeed(void)613 static void test_xs_node_tx_succeed(void)
614 {
615 do_test_xs_node_tx(false, true);
616 }
617
test_xs_node_tx_rm(void)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
test_xs_node_tx_resurrect(void)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
test_xs_node_tx_resurrect2(void)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
main(int argc,char ** argv)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