OSDN Git Service

0812e367b0ce4fa2db48f09f6ef13e63d6ce26c7
[qmiga/qemu.git] / hw / i386 / kvm / xenstore_impl.c
1 /*
2  * QEMU Xen emulation: The actual implementation of XenStore
3  *
4  * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5  *
6  * Authors: David Woodhouse <dwmw2@infradead.org>, Paul Durrant <paul@xen.org>
7  *
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.
10  */
11
12 #include "qemu/osdep.h"
13 #include "qom/object.h"
14
15 #include "xen_xenstore.h"
16 #include "xenstore_impl.h"
17
18 #include "hw/xen/interface/io/xs_wire.h"
19
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
25
26 #define XS_VALID_CHARS "abcdefghijklmnopqrstuvwxyz" \
27                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
28                        "0123456789-/_"
29
30 typedef struct XsNode {
31     uint32_t ref;
32     GByteArray *content;
33     GHashTable *children;
34     uint64_t gencnt;
35 #ifdef XS_NODE_UNIT_TEST
36     gchar *name; /* debug only */
37 #endif
38 } XsNode;
39
40 typedef struct XsWatch {
41     struct XsWatch *next;
42     xs_impl_watch_fn *cb;
43     void *cb_opaque;
44     char *token;
45     unsigned int dom_id;
46     int rel_prefix;
47 } XsWatch;
48
49 typedef struct XsTransaction {
50     XsNode *root;
51     unsigned int nr_nodes;
52     unsigned int base_tx;
53     unsigned int tx_id;
54     unsigned int dom_id;
55 } XsTransaction;
56
57 struct XenstoreImplState {
58     XsNode *root;
59     unsigned int nr_nodes;
60     GHashTable *watches;
61     unsigned int nr_domu_watches;
62     GHashTable *transactions;
63     unsigned int nr_domu_transactions;
64     unsigned int root_tx;
65     unsigned int last_tx;
66 };
67
68
69 static void nobble_tx(gpointer key, gpointer value, gpointer user_data)
70 {
71     unsigned int *new_tx_id = user_data;
72     XsTransaction *tx = value;
73
74     if (tx->base_tx == *new_tx_id) {
75         /* Transactions based on XBT_NULL will always fail */
76         tx->base_tx = XBT_NULL;
77     }
78 }
79
80 static inline unsigned int next_tx(struct XenstoreImplState *s)
81 {
82     unsigned int tx_id;
83
84     /* Find the next TX id which isn't either XBT_NULL or in use. */
85     do {
86         tx_id = ++s->last_tx;
87     } while (tx_id == XBT_NULL || tx_id == s->root_tx ||
88              g_hash_table_lookup(s->transactions, GINT_TO_POINTER(tx_id)));
89
90     /*
91      * It is vanishingly unlikely, but ensure that no outstanding transaction
92      * is based on the (previous incarnation of the) newly-allocated TX id.
93      */
94     g_hash_table_foreach(s->transactions, nobble_tx, &tx_id);
95
96     return tx_id;
97 }
98
99 static inline XsNode *xs_node_new(void)
100 {
101     XsNode *n = g_new0(XsNode, 1);
102     n->ref = 1;
103
104 #ifdef XS_NODE_UNIT_TEST
105     nr_xs_nodes++;
106     xs_node_list = g_list_prepend(xs_node_list, n);
107 #endif
108     return n;
109 }
110
111 static inline XsNode *xs_node_ref(XsNode *n)
112 {
113     /* With just 10 transactions, it can never get anywhere near this. */
114     g_assert(n->ref < INT_MAX);
115
116     g_assert(n->ref);
117     n->ref++;
118     return n;
119 }
120
121 static inline void xs_node_unref(XsNode *n)
122 {
123     if (!n) {
124         return;
125     }
126     g_assert(n->ref);
127     if (--n->ref) {
128         return;
129     }
130
131     if (n->content) {
132         g_byte_array_unref(n->content);
133     }
134     if (n->children) {
135         g_hash_table_unref(n->children);
136     }
137 #ifdef XS_NODE_UNIT_TEST
138     g_free(n->name);
139     nr_xs_nodes--;
140     xs_node_list = g_list_remove(xs_node_list, n);
141 #endif
142     g_free(n);
143 }
144
145 /* For copying from one hash table to another using g_hash_table_foreach() */
146 static void do_insert(gpointer key, gpointer value, gpointer user_data)
147 {
148     g_hash_table_insert(user_data, g_strdup(key), xs_node_ref(value));
149 }
150
151 static XsNode *xs_node_copy(XsNode *old)
152 {
153     XsNode *n = xs_node_new();
154
155     n->gencnt = old->gencnt;
156     if (old->children) {
157         n->children = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
158                                             (GDestroyNotify)xs_node_unref);
159         g_hash_table_foreach(old->children, do_insert, n->children);
160     }
161     if (old && old->content) {
162         n->content = g_byte_array_ref(old->content);
163     }
164     return n;
165 }
166
167 /* Returns true if it made a change to the hash table */
168 static bool xs_node_add_child(XsNode *n, const char *path_elem, XsNode *child)
169 {
170     assert(!strchr(path_elem, '/'));
171
172     if (!child) {
173         assert(n->children);
174         return g_hash_table_remove(n->children, path_elem);
175     }
176
177 #ifdef XS_NODE_UNIT_TEST
178     g_free(child->name);
179     child->name = g_strdup(path_elem);
180 #endif
181     if (!n->children) {
182         n->children = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
183                                             (GDestroyNotify)xs_node_unref);
184     }
185
186     /*
187      * The documentation for g_hash_table_insert() says that it "returns a
188      * boolean value to indicate whether the newly added value was already
189      * in the hash table or not."
190      *
191      * It could perhaps be clearer that returning TRUE means it wasn't,
192      */
193     return g_hash_table_insert(n->children, g_strdup(path_elem), child);
194 }
195
196 struct walk_op {
197     struct XenstoreImplState *s;
198     char path[XENSTORE_ABS_PATH_MAX + 2]; /* Two NUL terminators */
199     int (*op_fn)(XsNode **n, struct walk_op *op);
200     void *op_opaque;
201     void *op_opaque2;
202
203     GList *watches;
204     unsigned int dom_id;
205     unsigned int tx_id;
206
207     /* The number of nodes which will exist in the tree if this op succeeds. */
208     unsigned int new_nr_nodes;
209
210     /*
211      * This is maintained on the way *down* the walk to indicate
212      * whether nodes can be modified in place or whether COW is
213      * required. It starts off being true, as we're always going to
214      * replace the root node. If we walk into a shared subtree it
215      * becomes false. If we start *creating* new nodes for a write,
216      * it becomes true again.
217      *
218      * Do not use it on the way back up.
219      */
220     bool inplace;
221     bool mutating;
222     bool create_dirs;
223     bool in_transaction;
224 };
225
226 static void fire_watches(struct walk_op *op, bool parents)
227 {
228     GList *l = NULL;
229     XsWatch *w;
230
231     if (!op->mutating || op->in_transaction) {
232         return;
233     }
234
235     if (parents) {
236         l = op->watches;
237     }
238
239     w = g_hash_table_lookup(op->s->watches, op->path);
240     while (w || l) {
241         if (!w) {
242             /* Fire the parent nodes from 'op' if asked to */
243             w = l->data;
244             l = l->next;
245             continue;
246         }
247
248         assert(strlen(op->path) > w->rel_prefix);
249         w->cb(w->cb_opaque, op->path + w->rel_prefix, w->token);
250
251         w = w->next;
252     }
253 }
254
255 static int xs_node_add_content(XsNode **n, struct walk_op *op)
256 {
257     GByteArray *data = op->op_opaque;
258
259     if (op->dom_id) {
260         /*
261          * The real XenStored includes permissions and names of child nodes
262          * in the calculated datasize but life's too short. For a single
263          * tenant internal XenStore, we don't have to be quite as pedantic.
264          */
265         if (data->len > XS_MAX_NODE_SIZE) {
266             return E2BIG;
267         }
268     }
269     /* We *are* the node to be written. Either this or a copy. */
270     if (!op->inplace) {
271         XsNode *old = *n;
272         *n = xs_node_copy(old);
273         xs_node_unref(old);
274     }
275
276     if ((*n)->content) {
277         g_byte_array_unref((*n)->content);
278     }
279     (*n)->content = g_byte_array_ref(data);
280     return 0;
281 }
282
283 static int xs_node_get_content(XsNode **n, struct walk_op *op)
284 {
285     GByteArray *data = op->op_opaque;
286     GByteArray *node_data;
287
288     assert(op->inplace);
289     assert(*n);
290
291     node_data = (*n)->content;
292     if (node_data) {
293         g_byte_array_append(data, node_data->data, node_data->len);
294     }
295
296     return 0;
297 }
298
299 static int node_rm_recurse(gpointer key, gpointer value, gpointer user_data)
300 {
301     struct walk_op *op = user_data;
302     int path_len = strlen(op->path);
303     int key_len = strlen(key);
304     XsNode *n = value;
305     bool this_inplace = op->inplace;
306
307     if (n->ref != 1) {
308         op->inplace = 0;
309     }
310
311     assert(key_len + path_len + 2 <= sizeof(op->path));
312     op->path[path_len] = '/';
313     memcpy(op->path + path_len + 1, key, key_len + 1);
314
315     if (n->children) {
316         g_hash_table_foreach_remove(n->children, node_rm_recurse, op);
317     }
318     op->new_nr_nodes--;
319
320     /*
321      * Fire watches on *this* node but not the parents because they are
322      * going to be deleted too, so the watch will fire for them anyway.
323      */
324     fire_watches(op, false);
325     op->path[path_len] = '\0';
326
327     /*
328      * Actually deleting the child here is just an optimisation; if we
329      * don't then the final unref on the topmost victim will just have
330      * to cascade down again repeating all the g_hash_table_foreach()
331      * calls.
332      */
333     return this_inplace;
334 }
335
336 static int xs_node_rm(XsNode **n, struct walk_op *op)
337 {
338     bool this_inplace = op->inplace;
339
340     /* Fire watches for, and count, nodes in the subtree which get deleted */
341     if ((*n)->children) {
342         g_hash_table_foreach_remove((*n)->children, node_rm_recurse, op);
343     }
344     op->new_nr_nodes--;
345
346     if (this_inplace) {
347         xs_node_unref(*n);
348     }
349     *n = NULL;
350     return 0;
351 }
352
353 /*
354  * Passed a full reference in *n which it may free if it needs to COW.
355  *
356  * When changing the tree, the op->inplace flag indicates whether this
357  * node may be modified in place (i.e. it and all its parents had a
358  * refcount of one). If walking down the tree we find a node whose
359  * refcount is higher, we must clear op->inplace and COW from there
360  * down. Unless we are creating new nodes as scaffolding for a write
361  * (which works like 'mkdir -p' does). In which case those newly
362  * created nodes can (and must) be modified in place again.
363  */
364 static int xs_node_walk(XsNode **n, struct walk_op *op)
365 {
366     char *child_name = NULL;
367     size_t namelen;
368     XsNode *old = *n, *child = NULL;
369     bool stole_child = false;
370     bool this_inplace;
371     XsWatch *watch;
372     int err;
373
374     namelen = strlen(op->path);
375     watch = g_hash_table_lookup(op->s->watches, op->path);
376
377     /* Is there a child, or do we hit the double-NUL termination? */
378     if (op->path[namelen + 1]) {
379         char *slash;
380         child_name = op->path + namelen + 1;
381         slash = strchr(child_name, '/');
382         if (slash) {
383             *slash = '\0';
384         }
385         op->path[namelen] = '/';
386     }
387
388     /* If we walk into a subtree which is shared, we must COW */
389     if (op->mutating && old->ref != 1) {
390         op->inplace = false;
391     }
392
393     if (!child_name) {
394         /* This is the actual node on which the operation shall be performed */
395         err = op->op_fn(n, op);
396         if (!err) {
397             fire_watches(op, true);
398         }
399         goto out;
400     }
401
402     /* op->inplace will be further modified during the recursion */
403     this_inplace = op->inplace;
404
405     if (old && old->children) {
406         child = g_hash_table_lookup(old->children, child_name);
407         /* This is a *weak* reference to 'child', owned by the hash table */
408     }
409
410     if (child) {
411         xs_node_ref(child);
412         /*
413          * Now we own it too. But if we can modify inplace, that's going to
414          * foil the check and force it to COW. We want to be the *only* owner
415          * so that it can be modified in place, so remove it from the hash
416          * table in that case. We'll add it (or its replacement) back later.
417          */
418         if (op->mutating && this_inplace) {
419             g_hash_table_remove(old->children, child_name);
420             stole_child = true;
421         }
422     } else if (op->create_dirs) {
423         if (op->dom_id && op->new_nr_nodes >= XS_MAX_DOMAIN_NODES) {
424             err = ENOSPC;
425             goto out;
426         }
427         op->new_nr_nodes++;
428         child = xs_node_new();
429
430         /*
431          * If we're creating a new child, we can clearly modify it (and its
432          * children) in place from here on down.
433          */
434         op->inplace = true;
435     } else {
436         err = ENOENT;
437         goto out;
438     }
439
440     /*
441      * If there's a watch on this node, add it to the list to be fired
442      * (with the correct full pathname for the modified node) at the end.
443      */
444     if (watch) {
445         op->watches = g_list_append(op->watches, watch);
446     }
447
448     /*
449      * Except for the temporary child-stealing as noted, our node has not
450      * changed yet. We don't yet know the overall operation will complete.
451      */
452     err = xs_node_walk(&child, op);
453
454     if (watch) {
455         op->watches = g_list_remove(op->watches, watch);
456     }
457
458     if (err || !op->mutating) {
459         if (stole_child) {
460             /* Put it back as it was. */
461             g_hash_table_replace(old->children, g_strdup(child_name), child);
462         } else {
463             xs_node_unref(child);
464         }
465         goto out;
466     }
467
468     /*
469      * Now we know the operation has completed successfully and we're on
470      * the way back up. Make the change, substituting 'child' in the
471      * node at our level.
472      */
473     if (!this_inplace) {
474         *n = xs_node_copy(old);
475         xs_node_unref(old);
476     }
477
478     /*
479      * The child may be NULL here, for a remove operation. Either way,
480      * xs_node_add_child() will do the right thing and return a value
481      * indicating whether it changed the parent's hash table or not.
482      *
483      * We bump the parent gencnt if it adds a child that we *didn't*
484      * steal from it in the first place, or if child==NULL and was
485      * thus removed (whether we stole it earlier and didn't put it
486      * back, or xs_node_add_child() actually removed it now).
487      */
488     if ((xs_node_add_child(*n, child_name, child) && !stole_child) || !child) {
489         (*n)->gencnt++;
490     }
491
492  out:
493     op->path[namelen] = '\0';
494     if (!namelen) {
495         assert(!op->watches);
496         /*
497          * On completing the recursion back up the path walk and reaching the
498          * top, assign the new node count if the operation was successful. If
499          * the main tree was changed, bump its tx ID so that outstanding
500          * transactions correctly fail. But don't bump it every time; only
501          * if it makes a difference.
502          */
503         if (!err && op->mutating) {
504             if (!op->in_transaction) {
505                 if (op->s->root_tx != op->s->last_tx) {
506                     op->s->root_tx = next_tx(op->s);
507                 }
508                 op->s->nr_nodes = op->new_nr_nodes;
509             } else {
510                 XsTransaction *tx = g_hash_table_lookup(op->s->transactions,
511                                                         GINT_TO_POINTER(op->tx_id));
512                 assert(tx);
513                 tx->nr_nodes = op->new_nr_nodes;
514             }
515         }
516     }
517     return err;
518 }
519
520 static void append_directory_item(gpointer key, gpointer value,
521                                   gpointer user_data)
522 {
523     GList **items = user_data;
524
525     *items = g_list_insert_sorted(*items, g_strdup(key), (GCompareFunc)strcmp);
526 }
527
528 /* Populates items with char * names which caller must free. */
529 static int xs_node_directory(XsNode **n, struct walk_op *op)
530 {
531     GList **items = op->op_opaque;
532
533     assert(op->inplace);
534     assert(*n);
535
536     if ((*n)->children) {
537         g_hash_table_foreach((*n)->children, append_directory_item, items);
538     }
539
540     if (op->op_opaque2) {
541         *(uint64_t *)op->op_opaque2 = (*n)->gencnt;
542     }
543
544     return 0;
545 }
546
547 static int validate_path(char *outpath, const char *userpath,
548                          unsigned int dom_id)
549 {
550     size_t i, pathlen = strlen(userpath);
551
552     if (!pathlen || userpath[pathlen] == '/' || strstr(userpath, "//")) {
553         return EINVAL;
554     }
555     for (i = 0; i < pathlen; i++) {
556         if (!strchr(XS_VALID_CHARS, userpath[i])) {
557             return EINVAL;
558         }
559     }
560     if (userpath[0] == '/') {
561         if (pathlen > XENSTORE_ABS_PATH_MAX) {
562             return E2BIG;
563         }
564         memcpy(outpath, userpath, pathlen + 1);
565     } else {
566         if (pathlen > XENSTORE_REL_PATH_MAX) {
567             return E2BIG;
568         }
569         snprintf(outpath, XENSTORE_ABS_PATH_MAX, "/local/domain/%u/%s", dom_id,
570                  userpath);
571     }
572     return 0;
573 }
574
575
576 static int init_walk_op(XenstoreImplState *s, struct walk_op *op,
577                         xs_transaction_t tx_id, unsigned int dom_id,
578                         const char *path, XsNode ***rootp)
579 {
580     int ret = validate_path(op->path, path, dom_id);
581     if (ret) {
582         return ret;
583     }
584
585     /*
586      * We use *two* NUL terminators at the end of the path, as during the walk
587      * we will temporarily turn each '/' into a NUL to allow us to use that
588      * path element for the lookup.
589      */
590     op->path[strlen(op->path) + 1] = '\0';
591     op->watches = NULL;
592     op->path[0] = '\0';
593     op->inplace = true;
594     op->mutating = false;
595     op->create_dirs = false;
596     op->in_transaction = false;
597     op->dom_id = dom_id;
598     op->tx_id = tx_id;
599     op->s = s;
600
601     if (tx_id == XBT_NULL) {
602         *rootp = &s->root;
603         op->new_nr_nodes = s->nr_nodes;
604     } else {
605         XsTransaction *tx = g_hash_table_lookup(s->transactions,
606                                                 GINT_TO_POINTER(tx_id));
607         if (!tx) {
608             return ENOENT;
609         }
610         *rootp = &tx->root;
611         op->new_nr_nodes = tx->nr_nodes;
612         op->in_transaction = true;
613     }
614
615     return 0;
616 }
617
618 int xs_impl_read(XenstoreImplState *s, unsigned int dom_id,
619                  xs_transaction_t tx_id, const char *path, GByteArray *data)
620 {
621     /*
622      * The data GByteArray shall exist, and will be freed by caller.
623      * Just g_byte_array_append() to it.
624      */
625     struct walk_op op;
626     XsNode **n;
627     int ret;
628
629     ret = init_walk_op(s, &op, tx_id, dom_id, path, &n);
630     if (ret) {
631         return ret;
632     }
633     op.op_fn = xs_node_get_content;
634     op.op_opaque = data;
635     return xs_node_walk(n, &op);
636 }
637
638 int xs_impl_write(XenstoreImplState *s, unsigned int dom_id,
639                   xs_transaction_t tx_id, const char *path, GByteArray *data)
640 {
641     /*
642      * The data GByteArray shall exist, will be freed by caller. You are
643      * free to use g_byte_array_steal() and keep the data. Or just ref it.
644      */
645     struct walk_op op;
646     XsNode **n;
647     int ret;
648
649     ret = init_walk_op(s, &op, tx_id, dom_id, path, &n);
650     if (ret) {
651         return ret;
652     }
653     op.op_fn = xs_node_add_content;
654     op.op_opaque = data;
655     op.mutating = true;
656     op.create_dirs = true;
657     return xs_node_walk(n, &op);
658 }
659
660 int xs_impl_directory(XenstoreImplState *s, unsigned int dom_id,
661                       xs_transaction_t tx_id, const char *path,
662                       uint64_t *gencnt, GList **items)
663 {
664     /*
665      * The items are (char *) to be freed by caller. Although it's consumed
666      * immediately so if you want to change it to (const char *) and keep
667      * them, go ahead and change the caller.
668      */
669     struct walk_op op;
670     XsNode **n;
671     int ret;
672
673     ret = init_walk_op(s, &op, tx_id, dom_id, path, &n);
674     if (ret) {
675         return ret;
676     }
677     op.op_fn = xs_node_directory;
678     op.op_opaque = items;
679     op.op_opaque2 = gencnt;
680     return xs_node_walk(n, &op);
681 }
682
683 int xs_impl_transaction_start(XenstoreImplState *s, unsigned int dom_id,
684                               xs_transaction_t *tx_id)
685 {
686     XsTransaction *tx;
687
688     if (*tx_id != XBT_NULL) {
689         return EINVAL;
690     }
691
692     if (dom_id && s->nr_domu_transactions >= XS_MAX_TRANSACTIONS) {
693         return ENOSPC;
694     }
695
696     tx = g_new0(XsTransaction, 1);
697
698     tx->nr_nodes = s->nr_nodes;
699     tx->tx_id = next_tx(s);
700     tx->base_tx = s->root_tx;
701     tx->root = xs_node_ref(s->root);
702     tx->dom_id = dom_id;
703
704     g_hash_table_insert(s->transactions, GINT_TO_POINTER(tx->tx_id), tx);
705     if (dom_id) {
706         s->nr_domu_transactions++;
707     }
708     *tx_id = tx->tx_id;
709     return 0;
710 }
711
712 static int transaction_commit(XenstoreImplState *s, XsTransaction *tx)
713 {
714     if (s->root_tx != tx->base_tx) {
715         return EAGAIN;
716     }
717     xs_node_unref(s->root);
718     s->root = tx->root;
719     tx->root = NULL;
720     s->root_tx = tx->tx_id;
721     s->nr_nodes = tx->nr_nodes;
722
723     /*
724      * XX: Walk the new root and fire watches on any node which has a
725      * refcount of one (which is therefore unique to this transaction).
726      */
727     return 0;
728 }
729
730 int xs_impl_transaction_end(XenstoreImplState *s, unsigned int dom_id,
731                             xs_transaction_t tx_id, bool commit)
732 {
733     int ret = 0;
734     XsTransaction *tx = g_hash_table_lookup(s->transactions,
735                                             GINT_TO_POINTER(tx_id));
736
737     if (!tx || tx->dom_id != dom_id) {
738         return ENOENT;
739     }
740
741     if (commit) {
742         ret = transaction_commit(s, tx);
743     }
744
745     g_hash_table_remove(s->transactions, GINT_TO_POINTER(tx_id));
746     if (dom_id) {
747         assert(s->nr_domu_transactions);
748         s->nr_domu_transactions--;
749     }
750     return ret;
751 }
752
753 int xs_impl_rm(XenstoreImplState *s, unsigned int dom_id,
754                xs_transaction_t tx_id, const char *path)
755 {
756     struct walk_op op;
757     XsNode **n;
758     int ret;
759
760     ret = init_walk_op(s, &op, tx_id, dom_id, path, &n);
761     if (ret) {
762         return ret;
763     }
764     op.op_fn = xs_node_rm;
765     op.mutating = true;
766     return xs_node_walk(n, &op);
767 }
768
769 int xs_impl_get_perms(XenstoreImplState *s, unsigned int dom_id,
770                       xs_transaction_t tx_id, const char *path, GList **perms)
771 {
772     /*
773      * The perms are (char *) in the <perm-as-string> wire format to be
774      * freed by the caller.
775      */
776     return ENOSYS;
777 }
778
779 int xs_impl_set_perms(XenstoreImplState *s, unsigned int dom_id,
780                       xs_transaction_t tx_id, const char *path, GList *perms)
781 {
782     /*
783      * The perms are (const char *) in the <perm-as-string> wire format.
784      */
785     return ENOSYS;
786 }
787
788 int xs_impl_watch(XenstoreImplState *s, unsigned int dom_id, const char *path,
789                   const char *token, xs_impl_watch_fn fn, void *opaque)
790 {
791     char abspath[XENSTORE_ABS_PATH_MAX + 1];
792     XsWatch *w, *l;
793     int ret;
794
795     ret = validate_path(abspath, path, dom_id);
796     if (ret) {
797         return ret;
798     }
799
800     /* Check for duplicates */
801     l = w = g_hash_table_lookup(s->watches, abspath);
802     while (w) {
803         if (!g_strcmp0(token, w->token) &&  opaque == w->cb_opaque &&
804             fn == w->cb && dom_id == w->dom_id) {
805             return EEXIST;
806         }
807         w = w->next;
808     }
809
810     if (dom_id && s->nr_domu_watches >= XS_MAX_WATCHES) {
811         return E2BIG;
812     }
813
814     w = g_new0(XsWatch, 1);
815     w->token = g_strdup(token);
816     w->cb = fn;
817     w->cb_opaque = opaque;
818     w->dom_id = dom_id;
819     w->rel_prefix = strlen(abspath) - strlen(path);
820
821     /* l was looked up above when checking for duplicates */
822     if (l) {
823         w->next = l->next;
824         l->next = w;
825     } else {
826         g_hash_table_insert(s->watches, g_strdup(abspath), w);
827     }
828     if (dom_id) {
829         s->nr_domu_watches++;
830     }
831
832     /* A new watch should fire immediately */
833     fn(opaque, path, token);
834
835     return 0;
836 }
837
838 static XsWatch *free_watch(XenstoreImplState *s, XsWatch *w)
839 {
840     XsWatch *next = w->next;
841
842     if (w->dom_id) {
843         assert(s->nr_domu_watches);
844         s->nr_domu_watches--;
845     }
846
847     g_free(w->token);
848     g_free(w);
849
850     return next;
851 }
852
853 int xs_impl_unwatch(XenstoreImplState *s, unsigned int dom_id,
854                     const char *path, const char *token,
855                     xs_impl_watch_fn fn, void *opaque)
856 {
857     char abspath[XENSTORE_ABS_PATH_MAX + 1];
858     XsWatch *w, **l;
859     int ret;
860
861     ret = validate_path(abspath, path, dom_id);
862     if (ret) {
863         return ret;
864     }
865
866     w = g_hash_table_lookup(s->watches, abspath);
867     if (!w) {
868         return ENOENT;
869     }
870
871     /*
872      * The hash table contains the first element of a list of
873      * watches. Removing the first element in the list is a
874      * special case because we have to update the hash table to
875      * point to the next (or remove it if there's nothing left).
876      */
877     if (!g_strcmp0(token, w->token) && fn == w->cb && opaque == w->cb_opaque &&
878         dom_id == w->dom_id) {
879         if (w->next) {
880             /* Insert the previous 'next' into the hash table */
881             g_hash_table_insert(s->watches, g_strdup(abspath), w->next);
882         } else {
883             /* Nothing left; remove from hash table */
884             g_hash_table_remove(s->watches, abspath);
885         }
886         free_watch(s, w);
887         return 0;
888     }
889
890     /*
891      * We're all done messing with the hash table because the element
892      * it points to has survived the cull. Now it's just a simple
893      * linked list removal operation.
894      */
895     for (l = &w->next; *l; l = &w->next) {
896         w = *l;
897
898         if (!g_strcmp0(token, w->token) && fn == w->cb &&
899             opaque != w->cb_opaque && dom_id == w->dom_id) {
900             *l = free_watch(s, w);
901             return 0;
902         }
903     }
904
905     return ENOENT;
906 }
907
908 int xs_impl_reset_watches(XenstoreImplState *s, unsigned int dom_id)
909 {
910     char **watch_paths;
911     guint nr_watch_paths;
912     guint i;
913
914     watch_paths = (char **)g_hash_table_get_keys_as_array(s->watches,
915                                                           &nr_watch_paths);
916
917     for (i = 0; i < nr_watch_paths; i++) {
918         XsWatch *w1 = g_hash_table_lookup(s->watches, watch_paths[i]);
919         XsWatch *w2, *w, **l;
920
921         /*
922          * w1 is the original list. The hash table has this pointer.
923          * w2 is the head of our newly-filtered list.
924          * w and l are temporary for processing. w is somewhat redundant
925          * with *l but makes my eyes bleed less.
926          */
927
928         w = w2 = w1;
929         l = &w;
930         while (w) {
931             if (w->dom_id == dom_id) {
932                 /* If we're freeing the head of the list, bump w2 */
933                 if (w2 == w) {
934                     w2 = w->next;
935                 }
936                 *l = free_watch(s, w);
937             } else {
938                 l = &w->next;
939             }
940             w = *l;
941         }
942         /*
943          * If the head of the list survived the cull, we don't need to
944          * touch the hash table and we're done with this path. Else...
945          */
946         if (w1 != w2) {
947             g_hash_table_steal(s->watches, watch_paths[i]);
948
949             /*
950              * It was already freed. (Don't worry, this whole thing is
951              * single-threaded and nobody saw it in the meantime). And
952              * having *stolen* it, we now own the watch_paths[i] string
953              * so if we don't give it back to the hash table, we need
954              * to free it.
955              */
956             if (w2) {
957                 g_hash_table_insert(s->watches, watch_paths[i], w2);
958             } else {
959                 g_free(watch_paths[i]);
960             }
961         }
962     }
963     g_free(watch_paths);
964     return 0;
965 }
966
967 static void xs_tx_free(void *_tx)
968 {
969     XsTransaction *tx = _tx;
970     if (tx->root) {
971         xs_node_unref(tx->root);
972     }
973     g_free(tx);
974 }
975
976 XenstoreImplState *xs_impl_create(void)
977 {
978     XenstoreImplState *s = g_new0(XenstoreImplState, 1);
979
980     s->watches = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
981     s->transactions = g_hash_table_new_full(g_direct_hash, g_direct_equal,
982                                             NULL, xs_tx_free);
983     s->nr_nodes = 1;
984     s->root = xs_node_new();
985 #ifdef XS_NODE_UNIT_TEST
986     s->root->name = g_strdup("/");
987 #endif
988
989     s->root_tx = s->last_tx = 1;
990     return s;
991 }