xref: /openbsd-src/usr.sbin/ldomctl/mdesc.c (revision 9f11ffb7133c203312a01e4b986886bc88c7d74b)
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