xref: /qemu/tests/unit/test-xs-node.c (revision b77af26e973705e8fd96cff102fc978ee44043da)
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 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  
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