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
md_byte_hash(uint8_t * bp,int len)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 *
md_find_string(mmd_t * mdp,char * strp,uint32_t * hashp)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 *
md_new_string(mmd_t * mdp,char * strp)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 *
md_find_data_block(mmd_t * mdp,uint8_t * datap,int len,uint32_t * hashp)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 *
md_new_data_block(mmd_t * mdp,uint8_t * bufp,int len)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 *
md_new_node(mmd_t * mdp,char * sp)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 *
md_new_property(mmd_t * mdp,md_node_t * nodep,uint8_t type,char * sp)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
md_add_value_property(mmd_t * mdp,md_node_t * nodep,char * sp,uint64_t value)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
md_add_string_property(mmd_t * mdp,md_node_t * nodep,char * sp,char * bufp)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 (ENOMEM);
214 }
215 propp->d.dbp = dbp;
216 return (0);
217 }
218
219 int
md_add_data_property(mmd_t * mdp,md_node_t * nodep,char * sp,int len,uint8_t * bufp)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
md_add_arc_property(mmd_t * mdp,md_node_t * nodep,char * arcnamep,md_node_t * tgtnodep)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 *
md_link_new_node(mmd_t * mdp,char * nodenamep,md_node_t * parentnodep,char * linktonewp,char * linkbackp)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
md_destroy(mmd_t * mdp)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
md_free_node(mmd_t * mdp,md_node_t * nodep)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
md_free_prop(mmd_t * mdp,md_prop_t * propp)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
md_free_string(mmd_t * mdp,md_string_t * msp)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
md_free_data_block(mmd_t * mdp,md_data_block_t * mdbp)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 *
md_new_md(void)373 md_new_md(void)
374 {
375 return ((mmd_t *)calloc(1, sizeof (mmd_t)));
376 }
377
378 static void
md_fix_name(md_element_t * mdep,md_prop_t * propp)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
create_mde(md_element_t * mdep,int type,md_node_t * nodep,md_prop_t * propp)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
md_gen_bin(mmd_t * mdp,uint8_t ** bufvalp)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