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 g_hash_table_unref(s->transactions); 46 xs_node_unref(s->root); 47 g_free(s); 48 49 if (xs_node_list) { 50 GList *l; 51 for (l = xs_node_list; l; l = l->next) { 52 XsNode *n = l->data; 53 printf("Remaining node at %p name %s ref %u\n", n, n->name, 54 n->ref); 55 } 56 } 57 g_assert(!nr_xs_nodes); 58 } 59 60 static int write_str(XenstoreImplState *s, unsigned int dom_id, 61 unsigned int tx_id, const char *path, 62 const char *content) 63 { 64 GByteArray *d = g_byte_array_new(); 65 int err; 66 67 g_byte_array_append(d, (void *)content, strlen(content)); 68 err = xs_impl_write(s, dom_id, tx_id, path, d); 69 g_byte_array_unref(d); 70 return err; 71 } 72 73 static void watch_cb(void *_str, const char *path, const char *token) 74 { 75 GString *str = _str; 76 77 g_string_append(str, path); 78 g_string_append(str, token); 79 } 80 81 static XenstoreImplState *setup(void) 82 { 83 XenstoreImplState *s = xs_impl_create(); 84 char *abspath; 85 int err; 86 87 abspath = g_strdup_printf("/local/domain/%u", DOMID_GUEST); 88 89 err = write_str(s, DOMID_QEMU, XBT_NULL, abspath, ""); 90 g_assert(!err); 91 g_assert(s->nr_nodes == 4); 92 93 g_free(abspath); 94 95 abspath = g_strdup_printf("/local/domain/%u/some", DOMID_GUEST); 96 97 err = write_str(s, DOMID_QEMU, XBT_NULL, abspath, ""); 98 g_assert(!err); 99 g_assert(s->nr_nodes == 5); 100 101 g_free(abspath); 102 103 return s; 104 } 105 106 static void test_xs_node_simple(void) 107 { 108 GByteArray *data = g_byte_array_new(); 109 XenstoreImplState *s = setup(); 110 GString *guest_watches = g_string_new(NULL); 111 GString *qemu_watches = g_string_new(NULL); 112 GList *items = NULL; 113 XsNode *old_root; 114 uint64_t gencnt; 115 int err; 116 117 g_assert(s); 118 119 err = xs_impl_watch(s, DOMID_GUEST, "some", "guestwatch", 120 watch_cb, guest_watches); 121 g_assert(!err); 122 g_assert(guest_watches->len == strlen("someguestwatch")); 123 g_assert(!strcmp(guest_watches->str, "someguestwatch")); 124 g_string_truncate(guest_watches, 0); 125 126 err = xs_impl_watch(s, 0, "/local/domain/1/some", "qemuwatch", 127 watch_cb, qemu_watches); 128 g_assert(!err); 129 g_assert(qemu_watches->len == strlen("/local/domain/1/someqemuwatch")); 130 g_assert(!strcmp(qemu_watches->str, "/local/domain/1/someqemuwatch")); 131 g_string_truncate(qemu_watches, 0); 132 133 /* Read gives ENOENT when it should */ 134 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "foo", data); 135 g_assert(err == ENOENT); 136 137 /* Write works */ 138 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", 139 "something"); 140 g_assert(s->nr_nodes == 7); 141 g_assert(!err); 142 g_assert(!strcmp(guest_watches->str, 143 "some/relative/pathguestwatch")); 144 g_assert(!strcmp(qemu_watches->str, 145 "/local/domain/1/some/relative/pathqemuwatch")); 146 147 g_string_truncate(qemu_watches, 0); 148 g_string_truncate(guest_watches, 0); 149 xs_impl_reset_watches(s, 0); 150 151 /* Read gives back what we wrote */ 152 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data); 153 g_assert(!err); 154 g_assert(data->len == strlen("something")); 155 g_assert(!memcmp(data->data, "something", data->len)); 156 157 /* Even if we use an abolute path */ 158 g_byte_array_set_size(data, 0); 159 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, 160 "/local/domain/1/some/relative/path", data); 161 g_assert(!err); 162 g_assert(data->len == strlen("something")); 163 164 g_assert(!qemu_watches->len); 165 g_assert(!guest_watches->len); 166 /* Keep a copy, to force COW mode */ 167 old_root = xs_node_ref(s->root); 168 169 /* Write works again */ 170 err = write_str(s, DOMID_GUEST, XBT_NULL, 171 "/local/domain/1/some/relative/path2", 172 "something else"); 173 g_assert(!err); 174 g_assert(s->nr_nodes == 8); 175 g_assert(!qemu_watches->len); 176 g_assert(!strcmp(guest_watches->str, "some/relative/path2guestwatch")); 177 g_string_truncate(guest_watches, 0); 178 179 /* Overwrite an existing node */ 180 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", 181 "another thing"); 182 g_assert(!err); 183 g_assert(s->nr_nodes == 8); 184 g_assert(!qemu_watches->len); 185 g_assert(!strcmp(guest_watches->str, "some/relative/pathguestwatch")); 186 g_string_truncate(guest_watches, 0); 187 188 /* We can list the two files we wrote */ 189 err = xs_impl_directory(s, DOMID_GUEST, XBT_NULL, "some/relative", &gencnt, 190 &items); 191 g_assert(!err); 192 g_assert(items); 193 g_assert(gencnt == 2); 194 g_assert(!strcmp(items->data, "path")); 195 g_assert(items->next); 196 g_assert(!strcmp(items->next->data, "path2")); 197 g_assert(!items->next->next); 198 g_list_free_full(items, g_free); 199 200 err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch", 201 watch_cb, guest_watches); 202 g_assert(!err); 203 204 err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch", 205 watch_cb, guest_watches); 206 g_assert(err == ENOENT); 207 208 err = xs_impl_watch(s, DOMID_GUEST, "some/relative/path2", "watchp2", 209 watch_cb, guest_watches); 210 g_assert(!err); 211 g_assert(guest_watches->len == strlen("some/relative/path2watchp2")); 212 g_assert(!strcmp(guest_watches->str, "some/relative/path2watchp2")); 213 g_string_truncate(guest_watches, 0); 214 215 err = xs_impl_watch(s, DOMID_GUEST, "/local/domain/1/some/relative", 216 "watchrel", watch_cb, guest_watches); 217 g_assert(!err); 218 g_assert(guest_watches->len == 219 strlen("/local/domain/1/some/relativewatchrel")); 220 g_assert(!strcmp(guest_watches->str, 221 "/local/domain/1/some/relativewatchrel")); 222 g_string_truncate(guest_watches, 0); 223 224 /* Write somewhere else which already existed */ 225 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "moredata"); 226 g_assert(!err); 227 g_assert(s->nr_nodes == 8); 228 229 g_assert(!strcmp(guest_watches->str, 230 "/local/domain/1/some/relativewatchrel")); 231 g_string_truncate(guest_watches, 0); 232 233 g_byte_array_set_size(data, 0); 234 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); 235 g_assert(!err); 236 g_assert(data->len == strlen("moredata")); 237 g_assert(!memcmp(data->data, "moredata", data->len)); 238 239 /* Overwrite existing data */ 240 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "otherdata"); 241 g_assert(!err); 242 g_string_truncate(guest_watches, 0); 243 244 g_byte_array_set_size(data, 0); 245 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); 246 g_assert(!err); 247 g_assert(data->len == strlen("otherdata")); 248 g_assert(!memcmp(data->data, "otherdata", data->len)); 249 250 /* Remove the subtree */ 251 err = xs_impl_rm(s, DOMID_GUEST, XBT_NULL, "some/relative"); 252 g_assert(!err); 253 g_assert(s->nr_nodes == 5); 254 255 /* Each watch fires with the least specific relevant path */ 256 g_assert(strstr(guest_watches->str, 257 "some/relative/path2watchp2")); 258 g_assert(strstr(guest_watches->str, 259 "/local/domain/1/some/relativewatchrel")); 260 g_string_truncate(guest_watches, 0); 261 262 g_byte_array_set_size(data, 0); 263 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); 264 g_assert(err == ENOENT); 265 g_byte_array_unref(data); 266 267 xs_impl_reset_watches(s, DOMID_GUEST); 268 g_string_free(qemu_watches, true); 269 g_string_free(guest_watches, true); 270 xs_node_unref(old_root); 271 xs_impl_delete(s); 272 } 273 274 275 static void do_test_xs_node_tx(bool fail, bool commit) 276 { 277 XenstoreImplState *s = setup(); 278 GString *watches = g_string_new(NULL); 279 GByteArray *data = g_byte_array_new(); 280 unsigned int tx_id = XBT_NULL; 281 int err; 282 283 g_assert(s); 284 285 /* Set a watch */ 286 err = xs_impl_watch(s, DOMID_GUEST, "some", "watch", 287 watch_cb, watches); 288 g_assert(!err); 289 g_assert(watches->len == strlen("somewatch")); 290 g_assert(!strcmp(watches->str, "somewatch")); 291 g_string_truncate(watches, 0); 292 293 /* Write something */ 294 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", 295 "something"); 296 g_assert(s->nr_nodes == 7); 297 g_assert(!err); 298 g_assert(!strcmp(watches->str, 299 "some/relative/pathwatch")); 300 g_string_truncate(watches, 0); 301 302 /* Create a transaction */ 303 err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id); 304 g_assert(!err); 305 306 if (fail) { 307 /* Write something else in the root */ 308 err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", 309 "another thing"); 310 g_assert(!err); 311 g_assert(s->nr_nodes == 7); 312 g_assert(!strcmp(watches->str, 313 "some/relative/pathwatch")); 314 g_string_truncate(watches, 0); 315 } 316 317 g_assert(!watches->len); 318 319 /* Perform a write in the transaction */ 320 err = write_str(s, DOMID_GUEST, tx_id, "some/relative/path", 321 "something else"); 322 g_assert(!err); 323 g_assert(s->nr_nodes == 7); 324 g_assert(!watches->len); 325 326 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data); 327 g_assert(!err); 328 if (fail) { 329 g_assert(data->len == strlen("another thing")); 330 g_assert(!memcmp(data->data, "another thing", data->len)); 331 } else { 332 g_assert(data->len == strlen("something")); 333 g_assert(!memcmp(data->data, "something", data->len)); 334 } 335 g_byte_array_set_size(data, 0); 336 337 err = xs_impl_read(s, DOMID_GUEST, tx_id, "some/relative/path", data); 338 g_assert(!err); 339 g_assert(data->len == strlen("something else")); 340 g_assert(!memcmp(data->data, "something else", data->len)); 341 g_byte_array_set_size(data, 0); 342 343 /* Attempt to commit the transaction */ 344 err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, commit); 345 if (commit && fail) { 346 g_assert(err == EAGAIN); 347 } else { 348 g_assert(!err); 349 } 350 g_assert(!watches->len); 351 g_assert(s->nr_nodes == 7); 352 353 err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch", 354 watch_cb, watches); 355 g_assert(!err); 356 357 err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data); 358 g_assert(!err); 359 if (fail) { 360 g_assert(data->len == strlen("another thing")); 361 g_assert(!memcmp(data->data, "another thing", data->len)); 362 } else if (commit) { 363 g_assert(data->len == strlen("something else")); 364 g_assert(!memcmp(data->data, "something else", data->len)); 365 } else { 366 g_assert(data->len == strlen("something")); 367 g_assert(!memcmp(data->data, "something", data->len)); 368 } 369 g_byte_array_unref(data); 370 g_string_free(watches, true); 371 xs_impl_delete(s); 372 } 373 374 static void test_xs_node_tx_fail(void) 375 { 376 do_test_xs_node_tx(true, true); 377 } 378 379 static void test_xs_node_tx_abort(void) 380 { 381 do_test_xs_node_tx(false, false); 382 do_test_xs_node_tx(true, false); 383 } 384 static void test_xs_node_tx_succeed(void) 385 { 386 do_test_xs_node_tx(false, true); 387 } 388 389 int main(int argc, char **argv) 390 { 391 g_test_init(&argc, &argv, NULL); 392 module_call_init(MODULE_INIT_QOM); 393 394 g_test_add_func("/xs_node/simple", test_xs_node_simple); 395 g_test_add_func("/xs_node/tx_abort", test_xs_node_tx_abort); 396 g_test_add_func("/xs_node/tx_fail", test_xs_node_tx_fail); 397 g_test_add_func("/xs_node/tx_succeed", test_xs_node_tx_succeed); 398 399 return g_test_run(); 400 } 401