1 /* $OpenBSD: mdesc.c,v 1.11 2018/09/16 12:17:05 kettenis Exp $ */ 2 3 /* 4 * Copyright (c) 2012 Mark Kettenis 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/queue.h> 21 #include <err.h> 22 #include <stdbool.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 27 #include "mdesc.h" 28 #include "util.h" 29 30 struct md_name * 31 md_find_name(struct md *md, const char *str) 32 { 33 struct md_name *name; 34 35 TAILQ_FOREACH(name, &md->name_list, link) 36 if (strcmp(name->str, str) == 0) 37 return name; 38 return NULL; 39 } 40 41 struct md_name * 42 md_add_name(struct md *md, const char *str) 43 { 44 struct md_name *name; 45 46 name = md_find_name(md, str); 47 if (name == NULL) { 48 name = xmalloc(sizeof(*name)); 49 name->str = xstrdup(str); 50 TAILQ_INSERT_TAIL(&md->name_list, name, link); 51 name->refcnt = 0; 52 } 53 name->refcnt++; 54 return name; 55 } 56 57 void 58 md_free_name(struct md *md, struct md_name *name) 59 { 60 if (name->refcnt > 1) { 61 name->refcnt--; 62 return; 63 } 64 65 TAILQ_REMOVE(&md->name_list, name, link); 66 free(name); 67 } 68 69 struct md_data * 70 md_find_data(struct md *md, const uint8_t *b, size_t len) 71 { 72 struct md_data *data; 73 74 TAILQ_FOREACH(data, &md->data_list, link) 75 if (data->len == len && 76 memcmp(data->data, b, len) == 0) 77 return data; 78 79 return NULL; 80 } 81 82 struct md_data * 83 md_add_data(struct md *md, const uint8_t *b, size_t len) 84 { 85 struct md_data *data; 86 87 data = md_find_data(md, b, len); 88 if (data == NULL) { 89 data = xmalloc(sizeof(*data)); 90 data->data = xmalloc(len); 91 memcpy(data->data, b, len); 92 data->len = len; 93 TAILQ_INSERT_TAIL(&md->data_list, data, link); 94 data->refcnt = 0; 95 } 96 data->refcnt++; 97 return data; 98 } 99 100 void 101 md_free_data(struct md *md, struct md_data *data) 102 { 103 if (data->refcnt > 1) { 104 data->refcnt--; 105 return; 106 } 107 108 TAILQ_REMOVE(&md->data_list, data, link); 109 free(data); 110 } 111 112 struct md_node * 113 md_find_node(struct md *md, const char *name) 114 { 115 struct md_node *node; 116 117 TAILQ_FOREACH(node, &md->node_list, link) { 118 if (strcmp(node->name->str, name) == 0) 119 return node; 120 } 121 122 return NULL; 123 } 124 125 struct md_node * 126 md_find_subnode(struct md *md, struct md_node *node, const char *name) 127 { 128 struct md_prop *prop; 129 130 TAILQ_FOREACH(prop, &node->prop_list, link) { 131 if (prop->tag == MD_PROP_ARC && 132 strcmp(prop->name->str, "fwd") == 0 && 133 strcmp(prop->d.arc.node->name->str, name) == 0) 134 return prop->d.arc.node; 135 } 136 137 return NULL; 138 } 139 140 struct md_node * 141 md_add_node(struct md *md, const char *name) 142 { 143 struct md_node *node; 144 145 node = xmalloc(sizeof(*node)); 146 node->name = md_add_name(md, name); 147 TAILQ_INIT(&node->prop_list); 148 TAILQ_INSERT_TAIL(&md->node_list, node, link); 149 150 return node; 151 } 152 153 void 154 md_link_node(struct md *md, struct md_node *node1, struct md_node *node2) 155 { 156 md_add_prop_arc(md, node1, "fwd", node2); 157 md_add_prop_arc(md, node2, "back", node1); 158 } 159 160 struct md_prop * 161 md_find_prop(struct md *md, struct md_node *node, const char *name) 162 { 163 struct md_prop *prop; 164 165 TAILQ_FOREACH(prop, &node->prop_list, link) { 166 if (strcmp(prop->name->str, name) == 0) 167 return prop; 168 } 169 170 return NULL; 171 } 172 173 struct md_prop * 174 md_add_prop(struct md *md, struct md_node *node, const char *name) 175 { 176 struct md_prop *prop; 177 178 prop = xmalloc(sizeof(*prop)); 179 prop->name = md_add_name(md, name); 180 TAILQ_INSERT_TAIL(&node->prop_list, prop, link); 181 182 return prop; 183 } 184 185 struct md_prop * 186 md_add_prop_val(struct md *md, struct md_node *node, const char *name, 187 uint64_t val) 188 { 189 struct md_prop *prop; 190 191 prop = md_add_prop(md, node, name); 192 prop->tag = MD_PROP_VAL; 193 prop->d.val = val; 194 195 return prop; 196 } 197 198 struct md_prop * 199 md_add_prop_str(struct md *md, struct md_node *node, const char *name, 200 const char *str) 201 { 202 struct md_prop *prop; 203 204 prop = md_add_prop(md, node, name); 205 prop->tag = MD_PROP_STR; 206 prop->d.data = md_add_data(md, str, strlen(str) + 1); 207 208 return prop; 209 } 210 211 struct md_prop * 212 md_add_prop_data(struct md *md, struct md_node *node, const char *name, 213 const uint8_t *data, size_t len) 214 { 215 struct md_prop *prop; 216 217 prop = md_add_prop(md, node, name); 218 prop->tag = MD_PROP_DATA; 219 prop->d.data = md_add_data(md, data, len); 220 221 return prop; 222 } 223 224 struct md_prop * 225 md_add_prop_arc(struct md *md, struct md_node *node, const char *name, 226 struct md_node *target_node) 227 { 228 struct md_prop *prop; 229 230 prop = md_add_prop(md, node, name); 231 prop->tag = MD_PROP_ARC; 232 prop->d.arc.node = target_node; 233 234 return prop; 235 } 236 237 void 238 md_delete_prop(struct md *md, struct md_node *node, struct md_prop *prop) 239 { 240 TAILQ_REMOVE(&node->prop_list, prop, link); 241 if (prop->tag == MD_PROP_STR || prop->tag == MD_PROP_DATA) 242 md_free_data(md, prop->d.data); 243 md_free_name(md, prop->name); 244 free(prop); 245 } 246 247 bool 248 md_get_prop_val(struct md *md, struct md_node *node, const char *name, 249 uint64_t *val) 250 { 251 struct md_prop *prop; 252 253 prop = md_find_prop(md, node, name); 254 if (prop == NULL || prop->tag != MD_PROP_VAL) 255 return false; 256 257 *val = prop->d.val; 258 return true; 259 } 260 261 bool 262 md_set_prop_val(struct md *md, struct md_node *node, const char *name, 263 uint64_t val) 264 { 265 struct md_prop *prop; 266 267 prop = md_find_prop(md, node, name); 268 if (prop == NULL || prop->tag != MD_PROP_VAL) 269 return false; 270 271 prop->d.val = val; 272 return true; 273 } 274 275 bool 276 md_get_prop_str(struct md *md, struct md_node *node, const char *name, 277 const char **str) 278 { 279 struct md_prop *prop; 280 281 prop = md_find_prop(md, node, name); 282 if (prop == NULL || prop->tag != MD_PROP_STR) 283 return false; 284 285 *str = prop->d.data->data; 286 return true; 287 } 288 289 bool 290 md_get_prop_data(struct md *md, struct md_node *node, const char *name, 291 const void **data, size_t *len) 292 { 293 struct md_prop *prop; 294 295 prop = md_find_prop(md, node, name); 296 if (prop == NULL || prop->tag != MD_PROP_DATA) 297 return false; 298 299 *data = prop->d.data->data; 300 *len = prop->d.data->len; 301 return true; 302 } 303 304 void 305 md_delete_node(struct md *md, struct md_node *node) 306 { 307 struct md_node *node2; 308 struct md_prop *prop, *prop2; 309 310 TAILQ_FOREACH(node2, &md->node_list, link) { 311 TAILQ_FOREACH_SAFE(prop, &node2->prop_list, link, prop2) { 312 if (prop->tag == MD_PROP_ARC && 313 prop->d.arc.node == node) 314 md_delete_prop(md, node2, prop); 315 } 316 } 317 318 TAILQ_REMOVE(&md->node_list, node, link); 319 md_free_name(md, node->name); 320 free(node); 321 } 322 323 void 324 md_find_delete_node(struct md *md, const char *name) 325 { 326 struct md_node *node; 327 328 node = md_find_node(md, name); 329 if (node) 330 md_delete_node(md, node); 331 } 332 333 struct md * 334 md_alloc(void) 335 { 336 struct md *md; 337 338 md = xmalloc(sizeof(*md)); 339 TAILQ_INIT(&md->node_list); 340 TAILQ_INIT(&md->name_list); 341 TAILQ_INIT(&md->data_list); 342 343 return md; 344 } 345 346 struct md_node * 347 md_find_index(struct md *md, uint64_t index) 348 { 349 struct md_node *node; 350 351 TAILQ_FOREACH(node, &md->node_list, link) { 352 if (node->index == index) 353 return node; 354 } 355 356 return NULL; 357 } 358 359 void 360 md_fixup_arcs(struct md *md) 361 { 362 struct md_node *node; 363 struct md_prop *prop; 364 365 TAILQ_FOREACH(node, &md->node_list, link) { 366 TAILQ_FOREACH(prop, &node->prop_list, link) { 367 if (prop->tag == MD_PROP_ARC) 368 prop->d.arc.node = 369 md_find_index(md, prop->d.arc.index); 370 } 371 } 372 } 373 374 void 375 md_walk_graph(struct md *md, struct md_node *root) 376 { 377 struct md_prop *prop; 378 379 root->index = 1; 380 TAILQ_FOREACH(prop, &root->prop_list, link) { 381 if (prop->tag == MD_PROP_ARC && 382 strcmp(prop->name->str, "fwd") == 0) 383 md_walk_graph(md, prop->d.arc.node); 384 } 385 } 386 387 void 388 md_collect_garbage(struct md *md) 389 { 390 struct md_node *node, *node2; 391 392 TAILQ_FOREACH(node, &md->node_list, link) 393 node->index = 0; 394 395 md_walk_graph(md, md_find_node(md, "root")); 396 397 TAILQ_FOREACH_SAFE(node, &md->node_list, link, node2) { 398 if (node->index == 0) 399 md_delete_node(md, node); 400 } 401 } 402 403 struct md * 404 md_ingest(void *buf, size_t size) 405 { 406 struct md_header *mdh = buf; 407 size_t node_blk_size, name_blk_size, data_blk_size; 408 size_t total_size; 409 struct md_element *mde; 410 struct md_node *node = NULL; 411 struct md_prop *prop; 412 struct md *md; 413 const char *str; 414 const uint8_t *data; 415 uint8_t *node_blk; 416 uint8_t *name_blk; 417 uint8_t *data_blk; 418 uint64_t index; 419 420 if (size < sizeof(struct md_header)) 421 errx(1, "too small"); 422 423 if (betoh32(mdh->transport_version) != MD_TRANSPORT_VERSION) 424 errx(1, "invalid transport version"); 425 426 node_blk_size = betoh32(mdh->node_blk_sz); 427 name_blk_size = betoh32(mdh->name_blk_sz); 428 data_blk_size = betoh32(mdh->data_blk_sz); 429 total_size = node_blk_size + name_blk_size + data_blk_size; 430 431 if (size < total_size) 432 errx(1, "too small"); 433 434 md = md_alloc(); 435 436 mde = (void *)&mdh[1]; 437 node_blk = (void *)mde; 438 name_blk = node_blk + node_blk_size; 439 data_blk = name_blk + name_blk_size; 440 441 for (index = 0; index < node_blk_size / sizeof(*mde); index++, mde++) { 442 switch(mde->tag) { 443 case MD_NODE: 444 str = name_blk + betoh32(mde->name_offset); 445 node = md_add_node(md, str); 446 node->index = index; 447 break; 448 case MD_PROP_VAL: 449 if (node == NULL) 450 errx(1, "Corrupt MD"); 451 str = name_blk + betoh32(mde->name_offset); 452 md_add_prop_val(md, node, str, betoh64(mde->d.val)); 453 break; 454 case MD_PROP_STR: 455 if (node == NULL) 456 errx(1, "Corrupt MD"); 457 str = name_blk + betoh32(mde->name_offset); 458 data = data_blk + betoh32(mde->d.y.data_offset); 459 md_add_prop_str(md, node, str, data); 460 break; 461 case MD_PROP_DATA: 462 if (node == NULL) 463 errx(1, "Corrupt MD"); 464 str = name_blk + betoh32(mde->name_offset); 465 data = data_blk + betoh32(mde->d.y.data_offset); 466 md_add_prop_data(md, node, str, data, 467 betoh32(mde->d.y.data_len)); 468 break; 469 case MD_PROP_ARC: 470 if (node == NULL) 471 errx(1, "Corrupt MD"); 472 str = name_blk + betoh32(mde->name_offset); 473 prop = md_add_prop(md, node, str); 474 prop->tag = MD_PROP_ARC; 475 prop->d.arc.index = betoh64(mde->d.val); 476 prop->d.arc.node = NULL; 477 break; 478 case MD_NODE_END: 479 node = NULL; 480 break; 481 } 482 } 483 484 md_fixup_arcs(md); 485 486 return md; 487 } 488 489 size_t 490 md_exhume(struct md *md, void **buf) 491 { 492 struct md_node *node; 493 struct md_name *name; 494 struct md_data *data; 495 struct md_prop *prop; 496 size_t node_blk_size, name_blk_size, data_blk_size; 497 size_t total_size; 498 struct md_element *mde; 499 struct md_header *mdh; 500 uint32_t offset; 501 uint64_t index; 502 uint8_t *node_blk; 503 uint8_t *name_blk; 504 uint8_t *data_blk; 505 size_t len; 506 507 offset = 0; 508 TAILQ_FOREACH(name, &md->name_list, link) { 509 name->offset = offset; 510 offset += (strlen(name->str) + 1); 511 } 512 name_blk_size = roundup(offset, MD_ALIGNMENT_SIZE); 513 514 offset = 0; 515 TAILQ_FOREACH(data, &md->data_list, link) { 516 data->offset = offset; 517 offset += data->len; 518 offset = roundup(offset, MD_ALIGNMENT_SIZE); 519 } 520 data_blk_size = roundup(offset, MD_ALIGNMENT_SIZE); 521 522 index = 0; 523 TAILQ_FOREACH(node, &md->node_list, link) { 524 node->index = index; 525 TAILQ_FOREACH(prop, &node->prop_list, link) 526 index++; 527 index += 2; 528 } 529 node_blk_size = (index + 1) * sizeof(struct md_element); 530 531 total_size = 16 + node_blk_size + name_blk_size + data_blk_size; 532 mdh = xmalloc(total_size); 533 534 mdh->transport_version = htobe32(MD_TRANSPORT_VERSION); 535 mdh->node_blk_sz = htobe32(node_blk_size); 536 mdh->name_blk_sz = htobe32(name_blk_size); 537 mdh->data_blk_sz = htobe32(data_blk_size); 538 539 mde = (void *)&mdh[1]; 540 node_blk = (void *)mde; 541 name_blk = node_blk + node_blk_size; 542 data_blk = name_blk + name_blk_size; 543 544 TAILQ_FOREACH(node, &md->node_list, link) { 545 memset(mde, 0, sizeof(*mde)); 546 mde->tag = MD_NODE; 547 mde->name_len = strlen(node->name->str); 548 mde->name_offset = htobe32(node->name->offset); 549 if (TAILQ_NEXT(node, link)) 550 mde->d.val = htobe64(TAILQ_NEXT(node, link)->index); 551 else 552 mde->d.val = htobe64(index); 553 mde++; 554 TAILQ_FOREACH(prop, &node->prop_list, link) { 555 memset(mde, 0, sizeof(*mde)); 556 mde->tag = prop->tag; 557 mde->name_len = strlen(prop->name->str); 558 mde->name_offset = htobe32(prop->name->offset); 559 switch(prop->tag) { 560 case MD_PROP_VAL: 561 mde->d.val = htobe64(prop->d.val); 562 break; 563 case MD_PROP_STR: 564 case MD_PROP_DATA: 565 mde->d.y.data_len = 566 htobe32(prop->d.data->len); 567 mde->d.y.data_offset = 568 htobe32(prop->d.data->offset); 569 break; 570 case MD_PROP_ARC: 571 mde->d.val = 572 htobe64(prop->d.arc.node->index); 573 break; 574 } 575 mde++; 576 } 577 memset(mde, 0, sizeof(*mde)); 578 mde->tag = MD_NODE_END; 579 mde++; 580 } 581 memset(mde, 0, sizeof(*mde)); 582 mde->tag = MD_LIST_END; 583 584 TAILQ_FOREACH(name, &md->name_list, link) { 585 len = strlen(name->str) + 1; 586 memcpy(name_blk, name->str, len); 587 name_blk += len; 588 } 589 590 TAILQ_FOREACH(data, &md->data_list, link) { 591 memcpy(data_blk, data->data, data->len); 592 data_blk += roundup(data->len, MD_ALIGNMENT_SIZE); 593 } 594 595 *buf = mdh; 596 return total_size; 597 } 598 599 struct md * 600 md_copy(struct md *md) 601 { 602 void *buf; 603 size_t size; 604 605 size = md_exhume(md, &buf); 606 md = md_ingest(buf, size); 607 free(buf); 608 609 return md; 610 } 611 612 struct md * 613 md_read(const char *path) 614 { 615 FILE *fp; 616 size_t size; 617 void *buf; 618 619 fp = fopen(path, "r"); 620 if (fp == NULL) 621 return NULL; 622 623 if (fseek(fp, 0, SEEK_END) == -1) { 624 fclose(fp); 625 return NULL; 626 } 627 size = ftell(fp); 628 if (size == -1) { 629 fclose(fp); 630 return NULL; 631 } 632 if (fseek(fp, 0, SEEK_SET) == -1) { 633 fclose(fp); 634 return NULL; 635 } 636 637 buf = xmalloc(size); 638 if (fread(buf, size, 1, fp) != 1) { 639 fclose(fp); 640 free(buf); 641 return NULL; 642 } 643 644 fclose(fp); 645 646 return md_ingest(buf, size); 647 } 648 649 void 650 md_write(struct md *md, const char *path) 651 { 652 size_t size; 653 void *buf; 654 FILE *fp; 655 656 size = md_exhume(md, &buf); 657 658 fp = fopen(path, "w"); 659 if (fp == NULL) 660 err(1, "fopen"); 661 662 if (fwrite(buf, size, 1, fp) != 1) 663 err(1, "fwrite"); 664 665 fclose(fp); 666 } 667 668 uint32_t 669 md_size(const char *path) 670 { 671 uint32_t size; 672 FILE *fp; 673 674 fp = fopen(path, "r"); 675 if (fp == NULL) 676 err(1, "fopen"); 677 678 fseek(fp, 0, SEEK_END); 679 size = ftell(fp); 680 fclose(fp); 681 682 return size; 683 } 684