1 /* 2 * QEMU XenStore XsNode testing 3 * 4 * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 6 * This work is licensed under the terms of the GNU GPL, version 2 or later. 7 * See the COPYING file in the top-level directory. 8 */ 9 10 #include "qemu/osdep.h" 11 #include "qapi/error.h" 12 #include "qemu/module.h" 13 14 static int nr_xs_nodes; 15 static GList *xs_node_list; 16 17 #define XS_NODE_UNIT_TEST 18 19 /* 20 * We don't need the core Xen definitions. And we *do* want to be able 21 * to run the unit tests even on architectures that Xen doesn't support 22 * (because life's too short to bother doing otherwise, and test coverage 23 * doesn't hurt). 24 */ 25 #define __XEN_PUBLIC_XEN_H__ 26 27 #include "hw/i386/kvm/xenstore_impl.c" 28 29 #define DOMID_QEMU 0 30 #define DOMID_GUEST 1 31 32 /* This doesn't happen in qemu but we want to make valgrind happy */ 33 static void xs_impl_delete(XenstoreImplState *s) 34 { 35 int err; 36 37 xs_impl_reset_watches(s, DOMID_GUEST); 38 g_assert(!s->nr_domu_watches); 39 40 err = xs_impl_rm(s, DOMID_QEMU, XBT_NULL, "/local"); 41 g_assert(!err); 42 g_assert(s->nr_nodes == 1); 43 44 g_hash_table_unref(s->watches); 45 xs_node_unref(s->root); 46 g_free(s); 47 48 if (xs_node_list) { 49 GList *l; 50 for (l = xs_node_list; l; l = l->next) { 51 XsNode *n = l->data; 52 printf("Remaining node at %p name %s ref %u\n", n, n->name, 53 n->ref); 54 } 55 } 56 g_assert(!nr_xs_nodes); 57 } 58 59 static int write_str(XenstoreImplState *s, unsigned int dom_id, 60 unsigned int tx_id, const char *path, 61 const char *content) 62 { 63 GByteArray *d = g_byte_array_new(); 64 int err; 65 66 g_byte_array_append(d, (void *)content, strlen(content)); 67 err = xs_impl_write(s, dom_id, tx_id, path, d); 68 g_byte_array_unref(d); 69 return err; 70 } 71 72 static void watch_cb(void *_str, const char *path, const char *token) 73 { 74 GString *str = _str; 75 76 g_string_append(str, path); 77 g_string_append(str, token); 78 } 79 80 static XenstoreImplState *setup(void) 81 { 82 XenstoreImplState *s = xs_impl_create(); 83 char *abspath; 84 int err; 85 86 abspath = g_strdup_printf("/local/domain/%u", DOMID_GUEST); 87 88 err = write_str(s, DOMID_QEMU, XBT_NULL, abspath, ""); 89 g_assert(!err); 90 g_assert(s->nr_nodes == 4); 91 92 g_free(abspath); 93 94 abspath = g_strdup_printf("/local/domain/%u/some", DOMID_GUEST); 95 96 err = write_str(s, DOMID_QEMU, XBT_NULL, abspath, ""); 97 g_assert(!err); 98 g_assert(s->nr_nodes == 5); 99 100 g_free(abspath); 101 102 return s; 103 } 104 105 static void test_xs_node_simple(void) 106 { 107 GByteArray *data = g_byte_array_new(); 108 XenstoreImplState *s = setup(); 109 GString *guest_watches = g_string_new(NULL); 110 GString *qemu_watches = g_string_new(NULL); 111 GList *items = NULL; 112 XsNode *old_root; 113 uint64_t gencnt; 114 int err; 115 116 g_assert(s); 117 118 err = xs_impl_watch(s, DOMID_GUEST, "some", "guestwatch", 119 watch_cb, guest_watches); 120 g_assert(!err); 121 g_assert(guest_watches->len == strlen("someguestwatch")); 122 g_assert(!strcmp(guest_watches->str, "someguestwatch")); 123 g_string_truncate(guest_watches, 0); 124 125 err = xs_impl_watch(s, 0, "/local/domain/1/some", "qemuwatch", 126 watch_cb, qemu_watches); 127 g_assert(!err); 128 g_assert(qemu_watches->len == strlen("/local/domain/1/someqemuwatch")); 129 g_assert(!strcmp(qemu_watches->str, "/local/domain/1/someqemuwatch")); 130 g_string_truncate(qemu_watches, 0); 131 132 /* Read gives ENOENT when it should */ 133 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "foo", data); 134 g_assert(err == ENOENT); 135 136 /* Write works */ 137 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", 138 "something"); 139 g_assert(s->nr_nodes == 7); 140 g_assert(!err); 141 g_assert(!strcmp(guest_watches->str, 142 "some/relative/pathguestwatch")); 143 g_assert(!strcmp(qemu_watches->str, 144 "/local/domain/1/some/relative/pathqemuwatch")); 145 146 g_string_truncate(qemu_watches, 0); 147 g_string_truncate(guest_watches, 0); 148 xs_impl_reset_watches(s, 0); 149 150 /* Read gives back what we wrote */ 151 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data); 152 g_assert(!err); 153 g_assert(data->len == strlen("something")); 154 g_assert(!memcmp(data->data, "something", data->len)); 155 156 /* Even if we use an abolute path */ 157 g_byte_array_set_size(data, 0); 158 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, 159 "/local/domain/1/some/relative/path", data); 160 g_assert(!err); 161 g_assert(data->len == strlen("something")); 162 163 g_assert(!qemu_watches->len); 164 g_assert(!guest_watches->len); 165 /* Keep a copy, to force COW mode */ 166 old_root = xs_node_ref(s->root); 167 168 /* Write works again */ 169 err = write_str(s, DOMID_GUEST, XBT_NULL, 170 "/local/domain/1/some/relative/path2", 171 "something else"); 172 g_assert(!err); 173 g_assert(s->nr_nodes == 8); 174 g_assert(!qemu_watches->len); 175 g_assert(!strcmp(guest_watches->str, "some/relative/path2guestwatch")); 176 g_string_truncate(guest_watches, 0); 177 178 /* Overwrite an existing node */ 179 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", 180 "another thing"); 181 g_assert(!err); 182 g_assert(s->nr_nodes == 8); 183 g_assert(!qemu_watches->len); 184 g_assert(!strcmp(guest_watches->str, "some/relative/pathguestwatch")); 185 g_string_truncate(guest_watches, 0); 186 187 /* We can list the two files we wrote */ 188 err = xs_impl_directory(s, DOMID_GUEST, XBT_NULL, "some/relative", &gencnt, 189 &items); 190 g_assert(!err); 191 g_assert(items); 192 g_assert(gencnt == 2); 193 g_assert(!strcmp(items->data, "path")); 194 g_assert(items->next); 195 g_assert(!strcmp(items->next->data, "path2")); 196 g_assert(!items->next->next); 197 g_list_free_full(items, g_free); 198 199 err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch", 200 watch_cb, guest_watches); 201 g_assert(!err); 202 203 err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch", 204 watch_cb, guest_watches); 205 g_assert(err == ENOENT); 206 207 err = xs_impl_watch(s, DOMID_GUEST, "some/relative/path2", "watchp2", 208 watch_cb, guest_watches); 209 g_assert(!err); 210 g_assert(guest_watches->len == strlen("some/relative/path2watchp2")); 211 g_assert(!strcmp(guest_watches->str, "some/relative/path2watchp2")); 212 g_string_truncate(guest_watches, 0); 213 214 err = xs_impl_watch(s, DOMID_GUEST, "/local/domain/1/some/relative", 215 "watchrel", watch_cb, guest_watches); 216 g_assert(!err); 217 g_assert(guest_watches->len == 218 strlen("/local/domain/1/some/relativewatchrel")); 219 g_assert(!strcmp(guest_watches->str, 220 "/local/domain/1/some/relativewatchrel")); 221 g_string_truncate(guest_watches, 0); 222 223 /* Write somewhere else which already existed */ 224 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "moredata"); 225 g_assert(!err); 226 g_assert(s->nr_nodes == 8); 227 228 g_assert(!strcmp(guest_watches->str, 229 "/local/domain/1/some/relativewatchrel")); 230 g_string_truncate(guest_watches, 0); 231 232 g_byte_array_set_size(data, 0); 233 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); 234 g_assert(!err); 235 g_assert(data->len == strlen("moredata")); 236 g_assert(!memcmp(data->data, "moredata", data->len)); 237 238 /* Overwrite existing data */ 239 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "otherdata"); 240 g_assert(!err); 241 g_string_truncate(guest_watches, 0); 242 243 g_byte_array_set_size(data, 0); 244 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); 245 g_assert(!err); 246 g_assert(data->len == strlen("otherdata")); 247 g_assert(!memcmp(data->data, "otherdata", data->len)); 248 249 /* Remove the subtree */ 250 err = xs_impl_rm(s, DOMID_GUEST, XBT_NULL, "some/relative"); 251 g_assert(!err); 252 g_assert(s->nr_nodes == 5); 253 254 /* Each watch fires with the least specific relevant path */ 255 g_assert(strstr(guest_watches->str, 256 "some/relative/path2watchp2")); 257 g_assert(strstr(guest_watches->str, 258 "/local/domain/1/some/relativewatchrel")); 259 g_string_truncate(guest_watches, 0); 260 261 g_byte_array_set_size(data, 0); 262 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); 263 g_assert(err == ENOENT); 264 g_byte_array_unref(data); 265 266 xs_impl_reset_watches(s, DOMID_GUEST); 267 g_string_free(qemu_watches, true); 268 g_string_free(guest_watches, true); 269 xs_node_unref(old_root); 270 xs_impl_delete(s); 271 } 272 273 274 int main(int argc, char **argv) 275 { 276 g_test_init(&argc, &argv, NULL); 277 module_call_init(MODULE_INIT_QOM); 278 279 g_test_add_func("/xs_node/simple", test_xs_node_simple); 280 281 return g_test_run(); 282 } 283