2 * QEMU Xen emulation: The actual implementation of XenStore
4 * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
6 * Authors: David Woodhouse <dwmw2@infradead.org>, Paul Durrant <paul@xen.org>
8 * This work is licensed under the terms of the GNU GPL, version 2 or later.
9 * See the COPYING file in the top-level directory.
12 #include "qemu/osdep.h"
13 #include "qom/object.h"
15 #include "xen_xenstore.h"
16 #include "xenstore_impl.h"
18 #include "hw/xen/interface/io/xs_wire.h"
20 #define XS_MAX_WATCHES 128
21 #define XS_MAX_DOMAIN_NODES 1000
22 #define XS_MAX_NODE_SIZE 2048
23 #define XS_MAX_TRANSACTIONS 10
24 #define XS_MAX_PERMS_PER_NODE 5
26 #define XS_VALID_CHARS "abcdefghijklmnopqrstuvwxyz" \
27 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
30 typedef struct XsNode {
37 #ifdef XS_NODE_UNIT_TEST
38 gchar *name; /* debug only */
42 typedef struct XsWatch {
51 typedef struct XsTransaction {
53 unsigned int nr_nodes;
59 struct XenstoreImplState {
61 unsigned int nr_nodes;
63 unsigned int nr_domu_watches;
64 GHashTable *transactions;
65 unsigned int nr_domu_transactions;
71 static void nobble_tx(gpointer key, gpointer value, gpointer user_data)
73 unsigned int *new_tx_id = user_data;
74 XsTransaction *tx = value;
76 if (tx->base_tx == *new_tx_id) {
77 /* Transactions based on XBT_NULL will always fail */
78 tx->base_tx = XBT_NULL;
82 static inline unsigned int next_tx(struct XenstoreImplState *s)
86 /* Find the next TX id which isn't either XBT_NULL or in use. */
89 } while (tx_id == XBT_NULL || tx_id == s->root_tx ||
90 g_hash_table_lookup(s->transactions, GINT_TO_POINTER(tx_id)));
93 * It is vanishingly unlikely, but ensure that no outstanding transaction
94 * is based on the (previous incarnation of the) newly-allocated TX id.
96 g_hash_table_foreach(s->transactions, nobble_tx, &tx_id);
101 static inline XsNode *xs_node_new(void)
103 XsNode *n = g_new0(XsNode, 1);
106 #ifdef XS_NODE_UNIT_TEST
108 xs_node_list = g_list_prepend(xs_node_list, n);
113 static inline XsNode *xs_node_ref(XsNode *n)
115 /* With just 10 transactions, it can never get anywhere near this. */
116 g_assert(n->ref < INT_MAX);
123 static inline void xs_node_unref(XsNode *n)
134 g_byte_array_unref(n->content);
137 g_hash_table_unref(n->children);
139 #ifdef XS_NODE_UNIT_TEST
142 xs_node_list = g_list_remove(xs_node_list, n);
147 /* For copying from one hash table to another using g_hash_table_foreach() */
148 static void do_insert(gpointer key, gpointer value, gpointer user_data)
150 g_hash_table_insert(user_data, g_strdup(key), xs_node_ref(value));
153 static XsNode *xs_node_copy(XsNode *old)
155 XsNode *n = xs_node_new();
157 n->gencnt = old->gencnt;
159 #ifdef XS_NODE_UNIT_TEST
161 n->name = g_strdup(old->name);
166 n->children = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
167 (GDestroyNotify)xs_node_unref);
168 g_hash_table_foreach(old->children, do_insert, n->children);
170 if (old && old->content) {
171 n->content = g_byte_array_ref(old->content);
176 /* Returns true if it made a change to the hash table */
177 static bool xs_node_add_child(XsNode *n, const char *path_elem, XsNode *child)
179 assert(!strchr(path_elem, '/'));
183 return g_hash_table_remove(n->children, path_elem);
186 #ifdef XS_NODE_UNIT_TEST
188 child->name = g_strdup(path_elem);
191 n->children = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
192 (GDestroyNotify)xs_node_unref);
196 * The documentation for g_hash_table_insert() says that it "returns a
197 * boolean value to indicate whether the newly added value was already
198 * in the hash table or not."
200 * It could perhaps be clearer that returning TRUE means it wasn't,
202 return g_hash_table_insert(n->children, g_strdup(path_elem), child);
206 struct XenstoreImplState *s;
207 char path[XENSTORE_ABS_PATH_MAX + 2]; /* Two NUL terminators */
208 int (*op_fn)(XsNode **n, struct walk_op *op);
216 /* The number of nodes which will exist in the tree if this op succeeds. */
217 unsigned int new_nr_nodes;
220 * This is maintained on the way *down* the walk to indicate
221 * whether nodes can be modified in place or whether COW is
222 * required. It starts off being true, as we're always going to
223 * replace the root node. If we walk into a shared subtree it
224 * becomes false. If we start *creating* new nodes for a write,
225 * it becomes true again.
227 * Do not use it on the way back up.
234 /* Tracking during recursion so we know which is first. */
238 static void fire_watches(struct walk_op *op, bool parents)
243 if (!op->mutating || op->in_transaction) {
251 w = g_hash_table_lookup(op->s->watches, op->path);
254 /* Fire the parent nodes from 'op' if asked to */
260 assert(strlen(op->path) > w->rel_prefix);
261 w->cb(w->cb_opaque, op->path + w->rel_prefix, w->token);
267 static int xs_node_add_content(XsNode **n, struct walk_op *op)
269 GByteArray *data = op->op_opaque;
273 * The real XenStored includes permissions and names of child nodes
274 * in the calculated datasize but life's too short. For a single
275 * tenant internal XenStore, we don't have to be quite as pedantic.
277 if (data->len > XS_MAX_NODE_SIZE) {
281 /* We *are* the node to be written. Either this or a copy. */
284 *n = xs_node_copy(old);
289 g_byte_array_unref((*n)->content);
291 (*n)->content = g_byte_array_ref(data);
292 if (op->tx_id != XBT_NULL) {
293 (*n)->modified_in_tx = true;
298 static int xs_node_get_content(XsNode **n, struct walk_op *op)
300 GByteArray *data = op->op_opaque;
301 GByteArray *node_data;
306 node_data = (*n)->content;
308 g_byte_array_append(data, node_data->data, node_data->len);
314 static int node_rm_recurse(gpointer key, gpointer value, gpointer user_data)
316 struct walk_op *op = user_data;
317 int path_len = strlen(op->path);
318 int key_len = strlen(key);
320 bool this_inplace = op->inplace;
326 assert(key_len + path_len + 2 <= sizeof(op->path));
327 op->path[path_len] = '/';
328 memcpy(op->path + path_len + 1, key, key_len + 1);
331 g_hash_table_foreach_remove(n->children, node_rm_recurse, op);
336 * Fire watches on *this* node but not the parents because they are
337 * going to be deleted too, so the watch will fire for them anyway.
339 fire_watches(op, false);
340 op->path[path_len] = '\0';
343 * Actually deleting the child here is just an optimisation; if we
344 * don't then the final unref on the topmost victim will just have
345 * to cascade down again repeating all the g_hash_table_foreach()
351 static XsNode *xs_node_copy_deleted(XsNode *old, struct walk_op *op);
352 static void copy_deleted_recurse(gpointer key, gpointer value,
355 struct walk_op *op = user_data;
356 GHashTable *siblings = op->op_opaque2;
357 XsNode *n = xs_node_copy_deleted(value, op);
360 * Reinsert the deleted_in_tx copy of the node into the parent's
361 * 'children' hash table. Having stashed it from op->op_opaque2
362 * before the recursive call to xs_node_copy_deleted() scribbled
365 g_hash_table_insert(siblings, g_strdup(key), n);
368 static XsNode *xs_node_copy_deleted(XsNode *old, struct walk_op *op)
370 XsNode *n = xs_node_new();
372 n->gencnt = old->gencnt;
374 #ifdef XS_NODE_UNIT_TEST
376 n->name = g_strdup(old->name);
381 n->children = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
382 (GDestroyNotify)xs_node_unref);
383 op->op_opaque2 = n->children;
384 g_hash_table_foreach(old->children, copy_deleted_recurse, op);
386 n->deleted_in_tx = true;
387 /* If it gets resurrected we only fire a watch if it lost its content */
389 n->modified_in_tx = true;
395 static int xs_node_rm(XsNode **n, struct walk_op *op)
397 bool this_inplace = op->inplace;
399 if (op->tx_id != XBT_NULL) {
400 /* It's not trivial to do inplace handling for this one */
402 *n = xs_node_copy_deleted(old, op);
407 /* Fire watches for, and count, nodes in the subtree which get deleted */
408 if ((*n)->children) {
409 g_hash_table_foreach_remove((*n)->children, node_rm_recurse, op);
421 * Passed a full reference in *n which it may free if it needs to COW.
423 * When changing the tree, the op->inplace flag indicates whether this
424 * node may be modified in place (i.e. it and all its parents had a
425 * refcount of one). If walking down the tree we find a node whose
426 * refcount is higher, we must clear op->inplace and COW from there
427 * down. Unless we are creating new nodes as scaffolding for a write
428 * (which works like 'mkdir -p' does). In which case those newly
429 * created nodes can (and must) be modified in place again.
431 static int xs_node_walk(XsNode **n, struct walk_op *op)
433 char *child_name = NULL;
435 XsNode *old = *n, *child = NULL;
436 bool stole_child = false;
441 namelen = strlen(op->path);
442 watch = g_hash_table_lookup(op->s->watches, op->path);
444 /* Is there a child, or do we hit the double-NUL termination? */
445 if (op->path[namelen + 1]) {
447 child_name = op->path + namelen + 1;
448 slash = strchr(child_name, '/');
452 op->path[namelen] = '/';
455 /* If we walk into a subtree which is shared, we must COW */
456 if (op->mutating && old->ref != 1) {
461 /* This is the actual node on which the operation shall be performed */
462 err = op->op_fn(n, op);
464 fire_watches(op, true);
469 /* op->inplace will be further modified during the recursion */
470 this_inplace = op->inplace;
472 if (old && old->children) {
473 child = g_hash_table_lookup(old->children, child_name);
474 /* This is a *weak* reference to 'child', owned by the hash table */
478 if (child->deleted_in_tx) {
479 assert(child->ref == 1);
480 /* Cannot actually set child->deleted_in_tx = false until later */
484 * Now we own it too. But if we can modify inplace, that's going to
485 * foil the check and force it to COW. We want to be the *only* owner
486 * so that it can be modified in place, so remove it from the hash
487 * table in that case. We'll add it (or its replacement) back later.
489 if (op->mutating && this_inplace) {
490 g_hash_table_remove(old->children, child_name);
493 } else if (op->create_dirs) {
494 if (op->dom_id && op->new_nr_nodes >= XS_MAX_DOMAIN_NODES) {
499 child = xs_node_new();
502 * If we're creating a new child, we can clearly modify it (and its
503 * children) in place from here on down.
512 * If there's a watch on this node, add it to the list to be fired
513 * (with the correct full pathname for the modified node) at the end.
516 op->watches = g_list_append(op->watches, watch);
520 * Except for the temporary child-stealing as noted, our node has not
521 * changed yet. We don't yet know the overall operation will complete.
523 err = xs_node_walk(&child, op);
526 op->watches = g_list_remove(op->watches, watch);
529 if (err || !op->mutating) {
531 /* Put it back as it was. */
532 g_hash_table_replace(old->children, g_strdup(child_name), child);
534 xs_node_unref(child);
540 * Now we know the operation has completed successfully and we're on
541 * the way back up. Make the change, substituting 'child' in the
545 *n = xs_node_copy(old);
550 * If we resurrected a deleted_in_tx node, we can mark it as no longer
551 * deleted now that we know the overall operation has succeeded.
553 if (op->create_dirs && child && child->deleted_in_tx) {
555 child->deleted_in_tx = false;
559 * The child may be NULL here, for a remove operation. Either way,
560 * xs_node_add_child() will do the right thing and return a value
561 * indicating whether it changed the parent's hash table or not.
563 * We bump the parent gencnt if it adds a child that we *didn't*
564 * steal from it in the first place, or if child==NULL and was
565 * thus removed (whether we stole it earlier and didn't put it
566 * back, or xs_node_add_child() actually removed it now).
568 if ((xs_node_add_child(*n, child_name, child) && !stole_child) || !child) {
573 op->path[namelen] = '\0';
575 assert(!op->watches);
577 * On completing the recursion back up the path walk and reaching the
578 * top, assign the new node count if the operation was successful. If
579 * the main tree was changed, bump its tx ID so that outstanding
580 * transactions correctly fail. But don't bump it every time; only
581 * if it makes a difference.
583 if (!err && op->mutating) {
584 if (!op->in_transaction) {
585 if (op->s->root_tx != op->s->last_tx) {
586 op->s->root_tx = next_tx(op->s);
588 op->s->nr_nodes = op->new_nr_nodes;
590 XsTransaction *tx = g_hash_table_lookup(op->s->transactions,
591 GINT_TO_POINTER(op->tx_id));
593 tx->nr_nodes = op->new_nr_nodes;
600 static void append_directory_item(gpointer key, gpointer value,
603 GList **items = user_data;
605 *items = g_list_insert_sorted(*items, g_strdup(key), (GCompareFunc)strcmp);
608 /* Populates items with char * names which caller must free. */
609 static int xs_node_directory(XsNode **n, struct walk_op *op)
611 GList **items = op->op_opaque;
616 if ((*n)->children) {
617 g_hash_table_foreach((*n)->children, append_directory_item, items);
620 if (op->op_opaque2) {
621 *(uint64_t *)op->op_opaque2 = (*n)->gencnt;
627 static int validate_path(char *outpath, const char *userpath,
630 size_t i, pathlen = strlen(userpath);
632 if (!pathlen || userpath[pathlen] == '/' || strstr(userpath, "//")) {
635 for (i = 0; i < pathlen; i++) {
636 if (!strchr(XS_VALID_CHARS, userpath[i])) {
640 if (userpath[0] == '/') {
641 if (pathlen > XENSTORE_ABS_PATH_MAX) {
644 memcpy(outpath, userpath, pathlen + 1);
646 if (pathlen > XENSTORE_REL_PATH_MAX) {
649 snprintf(outpath, XENSTORE_ABS_PATH_MAX, "/local/domain/%u/%s", dom_id,
656 static int init_walk_op(XenstoreImplState *s, struct walk_op *op,
657 xs_transaction_t tx_id, unsigned int dom_id,
658 const char *path, XsNode ***rootp)
660 int ret = validate_path(op->path, path, dom_id);
666 * We use *two* NUL terminators at the end of the path, as during the walk
667 * we will temporarily turn each '/' into a NUL to allow us to use that
668 * path element for the lookup.
670 op->path[strlen(op->path) + 1] = '\0';
674 op->mutating = false;
675 op->create_dirs = false;
676 op->in_transaction = false;
681 if (tx_id == XBT_NULL) {
683 op->new_nr_nodes = s->nr_nodes;
685 XsTransaction *tx = g_hash_table_lookup(s->transactions,
686 GINT_TO_POINTER(tx_id));
691 op->new_nr_nodes = tx->nr_nodes;
692 op->in_transaction = true;
698 int xs_impl_read(XenstoreImplState *s, unsigned int dom_id,
699 xs_transaction_t tx_id, const char *path, GByteArray *data)
702 * The data GByteArray shall exist, and will be freed by caller.
703 * Just g_byte_array_append() to it.
709 ret = init_walk_op(s, &op, tx_id, dom_id, path, &n);
713 op.op_fn = xs_node_get_content;
715 return xs_node_walk(n, &op);
718 int xs_impl_write(XenstoreImplState *s, unsigned int dom_id,
719 xs_transaction_t tx_id, const char *path, GByteArray *data)
722 * The data GByteArray shall exist, will be freed by caller. You are
723 * free to use g_byte_array_steal() and keep the data. Or just ref it.
729 ret = init_walk_op(s, &op, tx_id, dom_id, path, &n);
733 op.op_fn = xs_node_add_content;
736 op.create_dirs = true;
737 return xs_node_walk(n, &op);
740 int xs_impl_directory(XenstoreImplState *s, unsigned int dom_id,
741 xs_transaction_t tx_id, const char *path,
742 uint64_t *gencnt, GList **items)
745 * The items are (char *) to be freed by caller. Although it's consumed
746 * immediately so if you want to change it to (const char *) and keep
747 * them, go ahead and change the caller.
753 ret = init_walk_op(s, &op, tx_id, dom_id, path, &n);
757 op.op_fn = xs_node_directory;
758 op.op_opaque = items;
759 op.op_opaque2 = gencnt;
760 return xs_node_walk(n, &op);
763 int xs_impl_transaction_start(XenstoreImplState *s, unsigned int dom_id,
764 xs_transaction_t *tx_id)
768 if (*tx_id != XBT_NULL) {
772 if (dom_id && s->nr_domu_transactions >= XS_MAX_TRANSACTIONS) {
776 tx = g_new0(XsTransaction, 1);
778 tx->nr_nodes = s->nr_nodes;
779 tx->tx_id = next_tx(s);
780 tx->base_tx = s->root_tx;
781 tx->root = xs_node_ref(s->root);
784 g_hash_table_insert(s->transactions, GINT_TO_POINTER(tx->tx_id), tx);
786 s->nr_domu_transactions++;
792 static gboolean tx_commit_walk(gpointer key, gpointer value,
795 struct walk_op *op = user_data;
796 int path_len = strlen(op->path);
797 int key_len = strlen(key);
798 bool fire_parents = true;
806 if (n->deleted_in_tx) {
808 * We fire watches on our parents if we are the *first* node
809 * to be deleted (the topmost one). This matches the behaviour
810 * when deleting in the live tree.
812 fire_parents = !op->deleted_in_tx;
814 /* Only used on the way down so no need to clear it later */
815 op->deleted_in_tx = true;
818 assert(key_len + path_len + 2 <= sizeof(op->path));
819 op->path[path_len] = '/';
820 memcpy(op->path + path_len + 1, key, key_len + 1);
822 watch = g_hash_table_lookup(op->s->watches, op->path);
824 op->watches = g_list_append(op->watches, watch);
828 g_hash_table_foreach_remove(n->children, tx_commit_walk, op);
832 op->watches = g_list_remove(op->watches, watch);
836 * Don't fire watches if this node was only copied because a
837 * descendent was changed. The modified_in_tx flag indicates the
838 * ones which were really changed.
840 if (n->modified_in_tx || n->deleted_in_tx) {
841 fire_watches(op, fire_parents);
842 n->modified_in_tx = false;
844 op->path[path_len] = '\0';
846 /* Deleted nodes really do get expunged when we commit */
847 return n->deleted_in_tx;
850 static int transaction_commit(XenstoreImplState *s, XsTransaction *tx)
855 if (s->root_tx != tx->base_tx) {
858 xs_node_unref(s->root);
861 s->root_tx = tx->tx_id;
862 s->nr_nodes = tx->nr_nodes;
864 init_walk_op(s, &op, XBT_NULL, tx->dom_id, "/", &n);
865 op.deleted_in_tx = false;
869 * Walk the new root and fire watches on any node which has a
870 * refcount of one (which is therefore unique to this transaction).
872 if (s->root->children) {
873 g_hash_table_foreach_remove(s->root->children, tx_commit_walk, &op);
879 int xs_impl_transaction_end(XenstoreImplState *s, unsigned int dom_id,
880 xs_transaction_t tx_id, bool commit)
883 XsTransaction *tx = g_hash_table_lookup(s->transactions,
884 GINT_TO_POINTER(tx_id));
886 if (!tx || tx->dom_id != dom_id) {
891 ret = transaction_commit(s, tx);
894 g_hash_table_remove(s->transactions, GINT_TO_POINTER(tx_id));
896 assert(s->nr_domu_transactions);
897 s->nr_domu_transactions--;
902 int xs_impl_rm(XenstoreImplState *s, unsigned int dom_id,
903 xs_transaction_t tx_id, const char *path)
909 ret = init_walk_op(s, &op, tx_id, dom_id, path, &n);
913 op.op_fn = xs_node_rm;
915 return xs_node_walk(n, &op);
918 int xs_impl_get_perms(XenstoreImplState *s, unsigned int dom_id,
919 xs_transaction_t tx_id, const char *path, GList **perms)
922 * The perms are (char *) in the <perm-as-string> wire format to be
923 * freed by the caller.
928 int xs_impl_set_perms(XenstoreImplState *s, unsigned int dom_id,
929 xs_transaction_t tx_id, const char *path, GList *perms)
932 * The perms are (const char *) in the <perm-as-string> wire format.
937 int xs_impl_watch(XenstoreImplState *s, unsigned int dom_id, const char *path,
938 const char *token, xs_impl_watch_fn fn, void *opaque)
940 char abspath[XENSTORE_ABS_PATH_MAX + 1];
944 ret = validate_path(abspath, path, dom_id);
949 /* Check for duplicates */
950 l = w = g_hash_table_lookup(s->watches, abspath);
952 if (!g_strcmp0(token, w->token) && opaque == w->cb_opaque &&
953 fn == w->cb && dom_id == w->dom_id) {
959 if (dom_id && s->nr_domu_watches >= XS_MAX_WATCHES) {
963 w = g_new0(XsWatch, 1);
964 w->token = g_strdup(token);
966 w->cb_opaque = opaque;
968 w->rel_prefix = strlen(abspath) - strlen(path);
970 /* l was looked up above when checking for duplicates */
975 g_hash_table_insert(s->watches, g_strdup(abspath), w);
978 s->nr_domu_watches++;
981 /* A new watch should fire immediately */
982 fn(opaque, path, token);
987 static XsWatch *free_watch(XenstoreImplState *s, XsWatch *w)
989 XsWatch *next = w->next;
992 assert(s->nr_domu_watches);
993 s->nr_domu_watches--;
1002 int xs_impl_unwatch(XenstoreImplState *s, unsigned int dom_id,
1003 const char *path, const char *token,
1004 xs_impl_watch_fn fn, void *opaque)
1006 char abspath[XENSTORE_ABS_PATH_MAX + 1];
1010 ret = validate_path(abspath, path, dom_id);
1015 w = g_hash_table_lookup(s->watches, abspath);
1021 * The hash table contains the first element of a list of
1022 * watches. Removing the first element in the list is a
1023 * special case because we have to update the hash table to
1024 * point to the next (or remove it if there's nothing left).
1026 if (!g_strcmp0(token, w->token) && fn == w->cb && opaque == w->cb_opaque &&
1027 dom_id == w->dom_id) {
1029 /* Insert the previous 'next' into the hash table */
1030 g_hash_table_insert(s->watches, g_strdup(abspath), w->next);
1032 /* Nothing left; remove from hash table */
1033 g_hash_table_remove(s->watches, abspath);
1040 * We're all done messing with the hash table because the element
1041 * it points to has survived the cull. Now it's just a simple
1042 * linked list removal operation.
1044 for (l = &w->next; *l; l = &w->next) {
1047 if (!g_strcmp0(token, w->token) && fn == w->cb &&
1048 opaque != w->cb_opaque && dom_id == w->dom_id) {
1049 *l = free_watch(s, w);
1057 int xs_impl_reset_watches(XenstoreImplState *s, unsigned int dom_id)
1060 guint nr_watch_paths;
1063 watch_paths = (char **)g_hash_table_get_keys_as_array(s->watches,
1066 for (i = 0; i < nr_watch_paths; i++) {
1067 XsWatch *w1 = g_hash_table_lookup(s->watches, watch_paths[i]);
1068 XsWatch *w2, *w, **l;
1071 * w1 is the original list. The hash table has this pointer.
1072 * w2 is the head of our newly-filtered list.
1073 * w and l are temporary for processing. w is somewhat redundant
1074 * with *l but makes my eyes bleed less.
1080 if (w->dom_id == dom_id) {
1081 /* If we're freeing the head of the list, bump w2 */
1085 *l = free_watch(s, w);
1092 * If the head of the list survived the cull, we don't need to
1093 * touch the hash table and we're done with this path. Else...
1096 g_hash_table_steal(s->watches, watch_paths[i]);
1099 * It was already freed. (Don't worry, this whole thing is
1100 * single-threaded and nobody saw it in the meantime). And
1101 * having *stolen* it, we now own the watch_paths[i] string
1102 * so if we don't give it back to the hash table, we need
1106 g_hash_table_insert(s->watches, watch_paths[i], w2);
1108 g_free(watch_paths[i]);
1112 g_free(watch_paths);
1116 static void xs_tx_free(void *_tx)
1118 XsTransaction *tx = _tx;
1120 xs_node_unref(tx->root);
1125 XenstoreImplState *xs_impl_create(void)
1127 XenstoreImplState *s = g_new0(XenstoreImplState, 1);
1129 s->watches = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1130 s->transactions = g_hash_table_new_full(g_direct_hash, g_direct_equal,
1133 s->root = xs_node_new();
1134 #ifdef XS_NODE_UNIT_TEST
1135 s->root->name = g_strdup("/");
1138 s->root_tx = s->last_tx = 1;