xref: /illumos-gate/usr/src/cmd/ldmad/mdesc_lib.c (revision df3cd224ef765c29101e4110546062199562f757)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <strings.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <strings.h>
32 #include <note.h>
33 #include <errno.h>
34 #include <sys/mdesc.h>
35 #include <sys/mdesc_impl.h>
36 #include <sys/sysmacros.h>
37 #include "mdesc_mutable.h"
38 
39 static void md_free_prop(mmd_t *mdp, md_prop_t *propp);
40 static void md_free_string(mmd_t *mdp, md_string_t *msp);
41 static void md_free_data_block(mmd_t *mdp, md_data_block_t *mdbp);
42 
43 static uint32_t
44 md_byte_hash(uint8_t *bp, int len)
45 {
46 	uint32_t hash = 0;
47 	int i;
48 
49 	for (i = 0; i < len; i++) {
50 		/* 5 bit rotation */
51 		hash = (hash >> 27) | (hash << 5) | bp[i];
52 	}
53 
54 	return (hash);
55 }
56 
57 static md_string_t *
58 md_find_string(mmd_t *mdp, char *strp, uint32_t *hashp)
59 {
60 	md_string_t *msp;
61 	uint32_t hash;
62 
63 	hash = md_byte_hash((uint8_t *)strp, strlen(strp));
64 
65 	if (hashp != NULL)
66 		*hashp = hash;
67 
68 	CHAIN_ITER(mdp->string_list, msp) {
69 		if (msp->hash == hash && strcmp(msp->strp, strp) == 0)
70 			return (msp);
71 	}
72 
73 	return (NULL);
74 }
75 
76 static md_string_t *
77 md_new_string(mmd_t *mdp, char *strp)
78 {
79 	md_string_t *msp;
80 	uint32_t hash;
81 
82 	msp = md_find_string(mdp, strp, &hash);
83 	if (msp == NULL) {
84 		msp = calloc(1, sizeof (md_string_t));
85 		if (msp == NULL)
86 			return (NULL);
87 		msp->strp = strdup(strp);
88 		if (msp->strp == NULL) {
89 			free(msp);
90 			return (NULL);
91 		}
92 		msp->size = strlen(strp) + 1;
93 		msp->hash = hash;
94 		msp->ref_cnt = 0;
95 		msp->build_offset = MD_OFFSET_UNDEF;
96 		CHAIN_ADD(mdp->string_list, msp);
97 	}
98 	msp->ref_cnt++;
99 
100 	return (msp);
101 }
102 
103 static md_data_block_t *
104 md_find_data_block(mmd_t *mdp, uint8_t *datap, int len, uint32_t *hashp)
105 {
106 	md_data_block_t *dbp;
107 	uint32_t hash;
108 
109 	hash = md_byte_hash(datap, len);
110 
111 	if (hashp != NULL)
112 		*hashp = hash;
113 
114 	CHAIN_ITER(mdp->data_block_list, dbp) {
115 		if (dbp->size == len &&
116 		    dbp->hash == hash && bcmp(dbp->datap, datap, len) == 0)
117 			return (dbp);
118 	}
119 
120 	return (NULL);
121 }
122 
123 static md_data_block_t *
124 md_new_data_block(mmd_t *mdp, uint8_t *bufp, int len)
125 {
126 	md_data_block_t *dbp;
127 	uint32_t	hash;
128 
129 	dbp = md_find_data_block(mdp, bufp, len, &hash);
130 	if (dbp == NULL) {
131 		dbp = calloc(1, sizeof (md_data_block_t));
132 		if (dbp == NULL)
133 			return (NULL);
134 		dbp->datap = malloc(len);
135 		if (dbp->datap == NULL) {
136 			free(dbp);
137 			return (NULL);
138 		}
139 		(void) memcpy(dbp->datap, bufp, len);
140 		dbp->size = len;
141 		dbp->hash = hash;
142 		dbp->ref_cnt = 0;
143 		dbp->build_offset = MD_OFFSET_UNDEF;
144 		CHAIN_ADD(mdp->data_block_list, dbp);
145 	}
146 	dbp->ref_cnt++;
147 
148 	return (dbp);
149 }
150 
151 md_node_t *
152 md_new_node(mmd_t *mdp, char *sp)
153 {
154 	md_node_t *nodep;
155 
156 	nodep = calloc(1, sizeof (md_node_t));
157 	if (nodep == NULL)
158 		return (NULL);
159 	nodep->typep = md_new_string(mdp, sp);
160 	if (nodep->typep == NULL) {
161 		free(nodep);
162 		return (NULL);
163 	}
164 	CHAIN_ADD(mdp->node_list, nodep);
165 
166 	return (nodep);
167 }
168 
169 static md_prop_t *
170 md_new_property(mmd_t *mdp, md_node_t *nodep, uint8_t type, char *sp)
171 {
172 	md_prop_t *propp;
173 
174 	propp = calloc(1, sizeof (md_prop_t));
175 	if (propp == NULL)
176 		return (NULL);
177 	propp->type = type;
178 	propp->sp = md_new_string(mdp, sp);
179 	if (propp->sp == NULL) {
180 		free(propp);
181 		return (NULL);
182 	}
183 
184 	CHAIN_ADD(nodep->prop_list, propp);
185 
186 	return (propp);
187 }
188 
189 int
190 md_add_value_property(mmd_t *mdp, md_node_t *nodep, char *sp, uint64_t value)
191 {
192 	md_prop_t *propp;
193 
194 	propp = md_new_property(mdp, nodep, MDET_PROP_VAL, sp);
195 	if (propp == NULL)
196 		return (ENOMEM);
197 	propp->d.value = value;
198 	return (0);
199 }
200 
201 int
202 md_add_string_property(mmd_t *mdp, md_node_t *nodep, char *sp, char *bufp)
203 {
204 	md_prop_t *propp;
205 	md_data_block_t *dbp;
206 
207 	dbp = md_new_data_block(mdp, (uint8_t *)bufp, strlen(bufp) + 1);
208 	if (dbp == NULL)
209 		return (ENOMEM);
210 	propp = md_new_property(mdp, nodep, MDET_PROP_STR, sp);
211 	if (propp == NULL) {
212 		md_free_data_block(mdp, dbp);
213 		return (NULL);
214 	}
215 	propp->d.dbp = dbp;
216 	return (0);
217 }
218 
219 int
220 md_add_data_property(mmd_t *mdp, md_node_t *nodep, char *sp, int len,
221     uint8_t *bufp)
222 {
223 	md_prop_t *propp;
224 	md_data_block_t *dbp;
225 
226 	dbp = md_new_data_block(mdp, bufp, len);
227 	if (dbp == NULL)
228 		return (ENOMEM);
229 
230 	propp = md_new_property(mdp, nodep, MDET_PROP_DAT, sp);
231 	if (propp == NULL) {
232 		md_free_data_block(mdp, dbp);
233 		return (ENOMEM);
234 	}
235 	propp->d.dbp = dbp;
236 	return (0);
237 }
238 
239 static int
240 md_add_arc_property(mmd_t *mdp, md_node_t *nodep, char *arcnamep,
241     md_node_t *tgtnodep)
242 {
243 	md_prop_t *propp;
244 
245 	propp = md_new_property(mdp, nodep, MDET_PROP_ARC, arcnamep);
246 	if (propp == NULL)
247 		return (ENOMEM);
248 	propp->d.arc.is_ptr = B_TRUE;
249 	propp->d.arc.val.nodep = tgtnodep;
250 	return (0);
251 }
252 
253 md_node_t *
254 md_link_new_node(mmd_t *mdp, char *nodenamep, md_node_t *parentnodep,
255     char *linktonewp, char *linkbackp)
256 {
257 	md_node_t *nodep;
258 
259 	nodep = md_new_node(mdp, nodenamep);
260 	if (nodep == NULL)
261 		return (NULL);
262 
263 	ASSERT(linktonewp != NULL);
264 	ASSERT(parentnodep != NULL && !parentnodep->deleted);
265 
266 	if (md_add_arc_property(mdp, parentnodep, linktonewp, nodep) != 0) {
267 		return (NULL);
268 	}
269 
270 	if (linkbackp != NULL) {
271 		if (md_add_arc_property(mdp,
272 		    nodep, linkbackp, parentnodep) != 0) {
273 			return (NULL);
274 		}
275 	}
276 
277 	return (nodep);
278 }
279 
280 void
281 md_destroy(mmd_t *mdp)
282 {
283 	md_node_t *nodep;
284 
285 	for (nodep = CHAIN_START(mdp->node_list); nodep != NULL; ) {
286 		md_node_t *tmp_nodep;
287 
288 		tmp_nodep = nodep->nextp;
289 		md_free_node(mdp, nodep);
290 
291 		nodep = tmp_nodep;
292 	}
293 
294 	/* should have deleted all the string refs by here */
295 	ASSERT(CHAIN_LENGTH(mdp->string_list) == 0);
296 	free(mdp);
297 }
298 
299 void
300 md_free_node(mmd_t *mdp, md_node_t *nodep)
301 {
302 	md_prop_t *propp;
303 
304 	if (nodep->typep != NULL)
305 		md_free_string(mdp, nodep->typep);
306 
307 	for (propp = CHAIN_START(nodep->prop_list); propp != NULL; ) {
308 		md_prop_t *tmp_propp;
309 
310 		tmp_propp = propp->nextp;
311 		md_free_prop(mdp, propp);
312 
313 		propp = tmp_propp;
314 	}
315 
316 	free(nodep);
317 }
318 
319 static void
320 md_free_prop(mmd_t *mdp, md_prop_t *propp)
321 {
322 	if (propp->sp != NULL)
323 		md_free_string(mdp, propp->sp);
324 
325 	switch (propp->type) {
326 	case MDET_PROP_VAL:
327 		break;
328 
329 	case MDET_PROP_ARC:
330 		break;
331 
332 	case MDET_PROP_STR:
333 	case MDET_PROP_DAT:
334 		md_free_data_block(mdp, propp->d.dbp);
335 		break;
336 
337 	default:
338 		ASSERT(B_FALSE);
339 	}
340 
341 	free(propp);
342 }
343 
344 static void
345 md_free_string(mmd_t *mdp, md_string_t *msp)
346 {
347 	ASSERT(msp->ref_cnt > 0);
348 
349 	msp->ref_cnt--;
350 
351 	if (msp->ref_cnt == 0) {
352 		free(msp->strp);
353 		mdp->string_list.startp = msp->nextp;
354 		free(msp);
355 	}
356 }
357 
358 static void
359 md_free_data_block(mmd_t *mdp, md_data_block_t *mdbp)
360 {
361 	ASSERT(mdbp->ref_cnt > 0);
362 
363 	mdbp->ref_cnt--;
364 
365 	if (mdbp->ref_cnt == 0) {
366 		free(mdbp->datap);
367 		mdp->data_block_list.startp = mdbp->nextp;
368 		free(mdbp);
369 	}
370 }
371 
372 mmd_t *
373 md_new_md(void)
374 {
375 	return ((mmd_t *)calloc(1, sizeof (mmd_t)));
376 }
377 
378 static void
379 md_fix_name(md_element_t *mdep, md_prop_t *propp)
380 {
381 	mdep->name_len = htomd8(propp->sp->size - 1);
382 	mdep->name_offset = htomd32(propp->sp->build_offset);
383 }
384 
385 void
386 create_mde(md_element_t *mdep, int type, md_node_t *nodep, md_prop_t *propp)
387 {
388 	(void) memset(mdep, 0, MD_ELEMENT_SIZE);
389 	mdep->tag = htomd8(type);
390 
391 	switch (type) {
392 	case MDET_NODE:
393 		mdep->d.prop_idx = htomd32(nodep->next_index);
394 		mdep->name_len = htomd8(nodep->typep->size - 1);
395 		mdep->name_offset = htomd32(nodep->typep->build_offset);
396 		break;
397 
398 	case MDET_PROP_ARC:
399 		ASSERT(propp->d.arc.is_ptr);
400 		mdep->d.prop_idx = htomd64(propp->d.arc.val.nodep->build_index);
401 		md_fix_name(mdep, propp);
402 		break;
403 
404 	case MDET_PROP_VAL:
405 		mdep->d.prop_val = htomd64(propp->d.value);
406 		md_fix_name(mdep, propp);
407 		break;
408 
409 	case MDET_PROP_STR:
410 	case MDET_PROP_DAT:
411 		mdep->d.prop_data.offset = htomd32(propp->d.dbp->build_offset);
412 		mdep->d.prop_data.len = htomd32(propp->d.dbp->size);
413 		md_fix_name(mdep, propp);
414 		break;
415 
416 	case MDET_NULL:
417 	case MDET_NODE_END:
418 	case MDET_LIST_END:
419 		break;
420 
421 	default:
422 		ASSERT(B_FALSE);
423 	}
424 }
425 
426 int
427 md_gen_bin(mmd_t *mdp, uint8_t **bufvalp)
428 {
429 	uint32_t offset;
430 	md_node_t *nodep;
431 	md_data_block_t *mdbp;
432 	md_string_t *msp;
433 	md_header_t *mdhp;
434 	md_element_t *mdep;
435 	uint32_t strings_size;
436 	uint32_t data_block_size;
437 	int total_size;
438 	uint8_t *bufferp;
439 	uint8_t *string_bufferp;
440 	uint8_t *data_block_bufferp;
441 
442 	/*
443 	 * Skip through strings to compute offsets.
444 	 */
445 	offset = 0;
446 	for (msp = CHAIN_START(mdp->string_list); msp != NULL;
447 	    msp = msp->nextp) {
448 		msp->build_offset = offset;
449 		offset += msp->size;
450 	}
451 	strings_size = P2ROUNDUP(offset, MD_ALIGNMENT_SIZE);
452 
453 	/*
454 	 * Skip through data blocks to compute offsets.
455 	 */
456 
457 	offset = 0;
458 	for (mdbp = CHAIN_START(mdp->data_block_list); mdbp != NULL;
459 	    mdbp = mdbp->nextp) {
460 		mdbp->build_offset = offset;
461 		offset += mdbp->size;
462 		offset = P2ROUNDUP(offset, MD_ALIGNMENT_SIZE);
463 	}
464 	data_block_size = P2ROUNDUP(offset, MD_ALIGNMENT_SIZE);
465 
466 	/*
467 	 * Compute the MD elements required to build the element list.
468 	 * For each node there is a node start and end, and one
469 	 * element for each property.
470 	 */
471 
472 	offset = 0;
473 	for (nodep = CHAIN_START(mdp->node_list); nodep != NULL;
474 	    nodep = nodep->nextp) {
475 		nodep->build_index = offset;
476 		offset += 2 + CHAIN_LENGTH(nodep->prop_list);
477 		nodep->next_index = offset;
478 	}
479 	offset += 1;	/* add the LIST_END element */
480 
481 	total_size = MD_HEADER_SIZE + offset * MD_ELEMENT_SIZE +
482 	    strings_size + data_block_size;
483 
484 	/*
485 	 * Allocate output buffer.
486 	 */
487 
488 	bufferp = calloc(total_size, sizeof (uint8_t));
489 	if (bufferp == NULL)
490 		return (0);
491 
492 	/* LINTED */
493 	mdhp = (md_header_t *)bufferp;
494 
495 	string_bufferp = bufferp + MD_HEADER_SIZE + offset * MD_ELEMENT_SIZE;
496 	data_block_bufferp = string_bufferp + strings_size;
497 
498 	mdhp->transport_version = htomd32(MD_TRANSPORT_VERSION);
499 	mdhp->node_blk_sz = htomd32(offset * MD_ELEMENT_SIZE);
500 	mdhp->name_blk_sz = htomd32(strings_size);
501 	mdhp->data_blk_sz = htomd32(data_block_size);
502 
503 	/*
504 	 * Build the element list.
505 	 * For each node there is a node start and end, and one
506 	 * element for each property.
507 	 */
508 
509 	offset = 0;
510 	/* LINTED */
511 	mdep = (md_element_t *)(bufferp + MD_HEADER_SIZE);
512 	for (nodep = CHAIN_START(mdp->node_list); nodep != NULL;
513 	    nodep = nodep->nextp) {
514 		md_prop_t *propp;
515 
516 		create_mde(mdep, MDET_NODE, nodep, NULL);
517 		mdep++;
518 
519 		for (propp = CHAIN_START(nodep->prop_list); propp != NULL;
520 		    propp = propp->nextp) {
521 			create_mde(mdep, propp->type, nodep, propp);
522 			mdep++;
523 		}
524 
525 		create_mde(mdep, MDET_NODE_END, NULL, NULL);
526 		mdep++;
527 	}
528 
529 	create_mde(mdep, MDET_LIST_END, NULL, NULL);
530 	mdep++;
531 
532 	/*
533 	 * Quick sanity check.
534 	 */
535 
536 	ASSERT(((uint8_t *)mdep) == ((uint8_t *)string_bufferp));
537 
538 	/*
539 	 * Skip through strings and stash them..
540 	 */
541 
542 	offset = 0;
543 	for (msp = CHAIN_START(mdp->string_list); msp != NULL;
544 	    msp = msp->nextp) {
545 		(void) memcpy(string_bufferp + msp->build_offset, msp->strp,
546 		    msp->size);
547 	}
548 
549 	/*
550 	 * Skip through data blocks and stash them.
551 	 */
552 
553 	offset = 0;
554 	for (mdbp = CHAIN_START(mdp->data_block_list); mdbp != NULL;
555 	    mdbp = mdbp->nextp) {
556 		(void) memcpy(data_block_bufferp + mdbp->build_offset,
557 		    mdbp->datap, mdbp->size);
558 	}
559 
560 	*bufvalp = bufferp;
561 	return (total_size);
562 }
563