9128 cw(1onbld) should be able to run multiple shadows
[unleashed.git] / usr / src / common / mdesc / mdesc_diff.c
blob28f55abc9252dfaa8dc29ed8bf76091a7013fd1c
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
29 #include <sys/types.h>
30 #ifdef _KERNEL
31 #include <sys/systm.h>
32 #else /* _KERNEL */
33 #include <string.h>
34 #include <strings.h>
35 #endif /* _KERNEL */
36 #include <sys/note.h>
37 #include <sys/mdesc.h>
38 #include <sys/mdesc_impl.h>
40 #define MDD_FREE_CHECK(mdp, ptr, sz) \
41 do { \
42 if (ptr) mdp->freep(ptr, sz); \
43 _NOTE(CONSTCOND) } while (0)
45 #define MD_DIFF_MAGIC 0x4D445F4449464621ull /* 'MD_DIFF!' */
46 #define MD_DIFF_NOMATCH (-1)
47 #define MD_DIFF_MATCH (1)
49 typedef struct {
50 mde_cookie_t *mdep;
51 uint_t nelem;
52 } md_diff_t;
54 typedef struct {
55 uint64_t mdd_magic;
56 md_diff_t added;
57 md_diff_t removed;
58 md_diff_t match1;
59 md_diff_t match2;
60 void *(*allocp)(size_t);
61 void (*freep)(void *, size_t);
62 } md_diff_impl_t;
65 * Internal utility functions
67 static int mdd_scan_for_nodes(md_t *mdp, mde_cookie_t start,
68 char *compnodep, int *countp, mde_cookie_t **nodespp);
70 static boolean_t mdd_any_dup_nodes(md_impl_t *mdp, md_prop_match_t *pmp,
71 int count, mde_cookie_t *nodesp);
73 static int mdd_node_list_match(md_impl_t *md1, md_impl_t *md2,
74 md_element_t *match_nodep, mde_cookie_t *match_listp,
75 uint8_t *match_seenp, int start, int end, md_prop_match_t *match_elemsp);
77 static int mdd_node_compare(md_impl_t *mdap, md_impl_t *mdbp,
78 md_element_t *nodeap, md_element_t *nodebp, md_prop_match_t *match_elemsp);
81 * Given two DAGs and information about how to uniquely identify
82 * the nodes of interest, determine which nodes have been added
83 * to the second MD, removed from the first MD, or exist in both
84 * MDs. This information is recorded and can be accessed using the
85 * opaque cookie returned to the caller.
87 md_diff_cookie_t
88 md_diff_init(md_t *md1p, mde_cookie_t start1, md_t *md2p, mde_cookie_t start2,
89 char *compnodep, md_prop_match_t *match_fieldsp)
91 int idx;
92 md_impl_t *md1 = (md_impl_t *)md1p;
93 md_impl_t *md2 = (md_impl_t *)md2p;
94 mde_cookie_t *md1nodesp = NULL;
95 mde_cookie_t *md2nodesp = NULL;
96 int md1count = 0;
97 int md2count = 0;
98 uint8_t *seenp = NULL;
100 /* variables used to gather results */
101 md_diff_impl_t *diff_res;
102 mde_cookie_t *mde_add_scr;
103 mde_cookie_t *mde_rem_scr;
104 mde_cookie_t *mde_match1_scr;
105 mde_cookie_t *mde_match2_scr;
106 int nadd = 0;
107 int nrem = 0;
108 int nmatch = 0;
110 /* sanity check params */
111 if ((md1p == NULL) || (md2p == NULL))
112 return (MD_INVAL_DIFF_COOKIE);
114 if ((start1 == MDE_INVAL_ELEM_COOKIE) ||
115 (start2 == MDE_INVAL_ELEM_COOKIE))
116 return (MD_INVAL_DIFF_COOKIE);
118 if ((compnodep == NULL) || (match_fieldsp == NULL))
119 return (MD_INVAL_DIFF_COOKIE);
122 * Prepare an array of the matching nodes from the first MD.
124 if (mdd_scan_for_nodes(md1p,
125 start1, compnodep, &md1count, &md1nodesp) == -1)
126 return (MD_INVAL_DIFF_COOKIE);
128 /* sanity check that all nodes are unique */
129 if (md1nodesp &&
130 mdd_any_dup_nodes(md1, match_fieldsp, md1count, md1nodesp)) {
131 MDD_FREE_CHECK(md1, md1nodesp, sizeof (mde_cookie_t) *
132 md1count);
133 return (MD_INVAL_DIFF_COOKIE);
138 * Prepare an array of the matching nodes from the second MD.
140 if (mdd_scan_for_nodes(md2p,
141 start2, compnodep, &md2count, &md2nodesp) == -1)
142 return (MD_INVAL_DIFF_COOKIE);
144 /* sanity check that all nodes are unique */
145 if (md2nodesp &&
146 mdd_any_dup_nodes(md2, match_fieldsp, md2count, md2nodesp)) {
147 MDD_FREE_CHECK(md1, md1nodesp, sizeof (mde_cookie_t) *
148 md1count);
149 MDD_FREE_CHECK(md2, md2nodesp, sizeof (mde_cookie_t) *
150 md2count);
151 return (MD_INVAL_DIFF_COOKIE);
154 /* setup our result structure */
155 diff_res = md1->allocp(sizeof (md_diff_impl_t));
156 bzero(diff_res, sizeof (md_diff_impl_t));
157 diff_res->allocp = md1->allocp;
158 diff_res->freep = md1->freep;
159 diff_res->mdd_magic = MD_DIFF_MAGIC;
162 * Special cases for empty lists
164 if ((md1count == 0) && (md2count != 0)) {
165 /* all the nodes found were added */
166 diff_res->added.mdep = md2nodesp;
167 diff_res->added.nelem = md2count;
168 return ((mde_cookie_t)diff_res);
171 if ((md1count != 0) && (md2count == 0)) {
172 /* all the nodes found were removed */
173 diff_res->removed.mdep = md1nodesp;
174 diff_res->removed.nelem = md1count;
175 return ((mde_cookie_t)diff_res);
178 if ((md1count == 0) && (md2count == 0))
179 /* no nodes found */
180 return ((mde_cookie_t)diff_res);
183 * Both lists have some elements. Allocate some scratch
184 * buffers to sort them into our three categories, added,
185 * removed, and matched pairs.
187 mde_add_scr = diff_res->allocp(sizeof (mde_cookie_t) * md2count);
188 mde_rem_scr = diff_res->allocp(sizeof (mde_cookie_t) * md1count);
189 mde_match1_scr = diff_res->allocp(sizeof (mde_cookie_t) * md1count);
190 mde_match2_scr = diff_res->allocp(sizeof (mde_cookie_t) * md2count);
192 /* array of seen flags only needed for md2 */
193 seenp = (uint8_t *)diff_res->allocp(sizeof (uint8_t) * md2count);
194 bzero(seenp, sizeof (uint8_t) * md2count);
197 * Make a pass through the md1 node array. Make note of
198 * any nodes not in the md2 array, indicating that they
199 * have been removed. Also keep track of the nodes that
200 * are present in both arrays for the matched pair results.
202 for (idx = 0; idx < md1count; idx++) {
204 md_element_t *elem = &(md1->mdep[md1nodesp[idx]]);
206 int match = mdd_node_list_match(md1, md2, elem, md2nodesp,
207 seenp, 0, md2count - 1, match_fieldsp);
209 if (match == MD_DIFF_NOMATCH)
210 /* record deleted node */
211 mde_rem_scr[nrem++] = md1nodesp[idx];
212 else {
213 /* record matched node pair */
214 mde_match1_scr[nmatch] = md1nodesp[idx];
215 mde_match2_scr[nmatch] = md2nodesp[match];
216 nmatch++;
218 /* mark that this match has been recorded */
219 seenp[match] = 1;
224 * Make a pass through the md2 array. Any nodes that have
225 * not been marked as seen have been added.
227 for (idx = 0; idx < md2count; idx++) {
228 if (!seenp[idx])
229 /* record added node */
230 mde_add_scr[nadd++] = md2nodesp[idx];
233 /* fill in the added node list */
234 if (nadd) {
235 int addsz = sizeof (mde_cookie_t) * nadd;
236 diff_res->added.mdep = (mde_cookie_t *)diff_res->allocp(addsz);
238 bcopy(mde_add_scr, diff_res->added.mdep, addsz);
240 diff_res->added.nelem = nadd;
243 /* fill in the removed node list */
244 if (nrem) {
245 int remsz = sizeof (mde_cookie_t) * nrem;
246 diff_res->removed.mdep =
247 (mde_cookie_t *)diff_res->allocp(remsz);
249 bcopy(mde_rem_scr, diff_res->removed.mdep, remsz);
250 diff_res->removed.nelem = nrem;
253 /* fill in the matching node lists */
254 if (nmatch) {
255 int matchsz = sizeof (mde_cookie_t) * nmatch;
256 diff_res->match1.mdep =
257 (mde_cookie_t *)diff_res->allocp(matchsz);
258 diff_res->match2.mdep =
259 (mde_cookie_t *)diff_res->allocp(matchsz);
261 bcopy(mde_match1_scr, diff_res->match1.mdep, matchsz);
262 bcopy(mde_match2_scr, diff_res->match2.mdep, matchsz);
263 diff_res->match1.nelem = nmatch;
264 diff_res->match2.nelem = nmatch;
267 /* clean up */
268 md1->freep(md1nodesp, sizeof (mde_cookie_t) * md1count);
269 md2->freep(md2nodesp, sizeof (mde_cookie_t) * md2count);
271 diff_res->freep(mde_add_scr, sizeof (mde_cookie_t) * md2count);
272 diff_res->freep(mde_rem_scr, sizeof (mde_cookie_t) * md1count);
273 diff_res->freep(mde_match1_scr, sizeof (mde_cookie_t) * md1count);
274 diff_res->freep(mde_match2_scr, sizeof (mde_cookie_t) * md2count);
276 diff_res->freep(seenp, sizeof (uint8_t) * md2count);
278 return ((md_diff_cookie_t)diff_res);
282 * Returns an array of the nodes added to the second MD in a
283 * previous md_diff_init() call. Returns the number of elements
284 * in the returned array. If the value is zero, the pointer
285 * passed back will be NULL.
288 md_diff_added(md_diff_cookie_t mdd, mde_cookie_t **mde_addedp)
290 md_diff_impl_t *mddp = (md_diff_impl_t *)mdd;
292 if ((mddp == NULL) || (mddp->mdd_magic != MD_DIFF_MAGIC))
293 return (-1);
295 *mde_addedp = mddp->added.mdep;
297 return (mddp->added.nelem);
301 * Returns an array of the nodes removed from the first MD in a
302 * previous md_diff_init() call. Returns the number of elements
303 * in the returned array. If the value is zero, the pointer
304 * passed back will be NULL.
307 md_diff_removed(md_diff_cookie_t mdd, mde_cookie_t **mde_removedp)
309 md_diff_impl_t *mddp = (md_diff_impl_t *)mdd;
311 if ((mddp == NULL) || (mddp->mdd_magic != MD_DIFF_MAGIC))
312 return (-1);
314 *mde_removedp = mddp->removed.mdep;
316 return (mddp->removed.nelem);
320 * Returns a pair of parallel arrays that contain nodes that were
321 * considered matching based on the match criteria passed in to
322 * a previous md_diff_init() call. Returns the number of elements
323 * in the arrays. If the value is zero, both pointers passed back
324 * will be NULL.
327 md_diff_matched(md_diff_cookie_t mdd, mde_cookie_t **mde_match1p,
328 mde_cookie_t **mde_match2p)
330 md_diff_impl_t *mddp = (md_diff_impl_t *)mdd;
332 if ((mddp == NULL) || (mddp->mdd_magic != MD_DIFF_MAGIC))
333 return (-1);
335 *mde_match1p = mddp->match1.mdep;
336 *mde_match2p = mddp->match2.mdep;
338 return (mddp->match1.nelem);
342 * Deallocate any storage used to store results of a previous
343 * md_diff_init() call. Returns 0 on success and -1 on failure.
346 md_diff_fini(md_diff_cookie_t mdd)
348 md_diff_impl_t *mddp = (md_diff_impl_t *)mdd;
350 if ((mddp == NULL) || (mddp->mdd_magic != MD_DIFF_MAGIC))
351 return (-1);
353 mddp->mdd_magic = 0;
355 MDD_FREE_CHECK(mddp, mddp->added.mdep, mddp->added.nelem *
356 sizeof (mde_cookie_t));
358 MDD_FREE_CHECK(mddp, mddp->removed.mdep, mddp->removed.nelem *
359 sizeof (mde_cookie_t));
361 MDD_FREE_CHECK(mddp, mddp->match1.mdep, mddp->match1.nelem *
362 sizeof (mde_cookie_t));
364 MDD_FREE_CHECK(mddp, mddp->match2.mdep, mddp->match2.nelem *
365 sizeof (mde_cookie_t));
367 mddp->freep(mddp, sizeof (md_diff_impl_t));
369 return (0);
373 * Walk the "fwd" DAG in an MD and return an array of nodes that are
374 * of the specified type. The start param is used to start the walk
375 * from an arbitrary location in the DAG. Returns an array of nodes
376 * as well as a count of the number of nodes in the array. If the
377 * count is zero, the node pointer will be passed back as NULL.
379 * Returns: 0 success; -1 failure
381 static int
382 mdd_scan_for_nodes(md_t *mdp,
383 mde_cookie_t start, char *compnodep, int *countp, mde_cookie_t **nodespp)
385 mde_str_cookie_t cname;
386 mde_str_cookie_t aname;
387 md_impl_t *mdip = (md_impl_t *)mdp;
389 if (mdip == NULL)
390 return (-1);
392 cname = md_find_name(mdp, compnodep);
393 aname = md_find_name(mdp, "fwd");
395 /* get the number of nodes of interest in the DAG */
396 *countp = md_scan_dag(mdp, start, cname, aname, NULL);
397 if (*countp == 0) {
398 *nodespp = NULL;
399 return (0);
402 /* allocate the storage */
403 *nodespp = mdip->allocp(sizeof (mde_cookie_t) * (*countp));
405 /* populate our array with the matching nodes */
406 (void) md_scan_dag(mdp, start, cname, aname, *nodespp);
408 return (0);
412 * Walk an array of nodes and check if there are any duplicate
413 * nodes. A duplicate is determined based on the specified match
414 * criteria. Returns B_TRUE if there are any duplicates and B_FALSE
415 * otherwise.
417 static boolean_t
418 mdd_any_dup_nodes(md_impl_t *mdp, md_prop_match_t *pmp, int count,
419 mde_cookie_t *nodesp)
421 int idx;
422 int match;
423 md_element_t *elem;
425 ASSERT(count > 0 || nodesp == NULL);
427 for (idx = 0; idx < count; idx++) {
428 elem = &(mdp->mdep[nodesp[idx]]);
430 match = mdd_node_list_match(mdp, mdp, elem, nodesp, NULL,
431 idx + 1, count - 1, pmp);
433 if (match != MD_DIFF_NOMATCH)
434 return (B_TRUE);
437 return (B_FALSE);
441 * Given a node and a array of nodes, compare the node to all elements
442 * in the specified start-end range of the array. If the node matches
443 * one of the nodes in the array, return the index of that node. Otherwise
444 * return MD_DIFF_NOMATCH.
446 * The optional seen array parameter can be used to optimize repeated
447 * calls to this function. If the seen array indicates that an element
448 * has already been matched, the full comparison is not necessary.
450 static int
451 mdd_node_list_match(md_impl_t *md1, md_impl_t *md2, md_element_t *match_nodep,
452 mde_cookie_t *match_listp, uint8_t *match_seenp, int start, int end,
453 md_prop_match_t *match_elemsp)
455 int match;
456 int idx;
457 md_element_t *elem;
459 for (idx = start; idx <= end; idx++) {
461 if ((match_seenp != NULL) && (match_seenp[idx]))
462 continue;
464 elem = &(md2->mdep[match_listp[idx]]);
466 match = mdd_node_compare(md1, md2, match_nodep, elem,
467 match_elemsp);
468 if (match == MD_DIFF_MATCH)
469 return (idx);
472 return (MD_DIFF_NOMATCH);
476 * Given two nodes and a list of properties, compare the nodes.
477 * A match is concluded if both nodes have all of the specified
478 * properties and all the values of those properties are the
479 * same. Returns MD_DIFF_NOMATCH if the nodes do not match and
480 * MD_DIFF_MATCH otherwise.
482 static int
483 mdd_node_compare(md_impl_t *mdap, md_impl_t *mdbp, md_element_t *nodeap,
484 md_element_t *nodebp, md_prop_match_t *match_elemsp)
486 md_element_t *ap;
487 md_element_t *bp;
488 boolean_t nodea_interest;
489 boolean_t nodeb_interest;
490 int idx;
492 /* make sure we are starting at the beginning of the nodes */
493 if ((MDE_TAG(nodeap) != MDET_NODE) || (MDE_TAG(nodebp) != MDET_NODE))
494 return (MD_DIFF_NOMATCH);
496 for (idx = 0; match_elemsp[idx].type != MDET_LIST_END; idx++) {
498 int type;
500 nodea_interest = B_FALSE;
501 nodeb_interest = B_FALSE;
503 type = match_elemsp[idx].type;
506 * Check node A for the property of interest
508 for (ap = nodeap; MDE_TAG(ap) != MDET_NODE_END; ap++) {
509 char *elemname;
511 if (MDE_TAG(ap) != type)
512 continue;
514 elemname = mdap->namep + MDE_NAME(ap);
516 if (strcmp(elemname, match_elemsp[idx].namep) == 0) {
517 /* found the property of interest */
518 nodea_interest = B_TRUE;
519 break;
523 /* node A is not of interest */
524 if (!nodea_interest)
525 return (MD_DIFF_NOMATCH);
528 * Check node B for the property of interest
530 for (bp = nodebp; MDE_TAG(bp) != MDET_NODE_END; bp++) {
531 char *elemname;
533 if (MDE_TAG(bp) != type)
534 continue;
536 elemname = mdbp->namep + MDE_NAME(bp);
538 if (strcmp(elemname, match_elemsp[idx].namep) == 0) {
539 nodeb_interest = B_TRUE;
540 break;
544 /* node B is not of interest */
545 if (!nodeb_interest)
546 return (MD_DIFF_NOMATCH);
549 * Both nodes have the property of interest. The
550 * nodes are not a match unless the value of that
551 * property match
553 switch (type) {
554 case MDET_PROP_VAL:
555 if (MDE_PROP_VALUE(ap) != MDE_PROP_VALUE(bp))
556 return (MD_DIFF_NOMATCH);
557 break;
559 case MDET_PROP_STR: {
560 char *stra = (char *)(mdap->datap +
561 MDE_PROP_DATA_OFFSET(ap));
562 char *strb = (char *)(mdbp->datap +
563 MDE_PROP_DATA_OFFSET(bp));
565 if (strcmp(stra, strb) != 0)
566 return (MD_DIFF_NOMATCH);
567 break;
570 case MDET_PROP_DAT: {
572 caddr_t dataa;
573 caddr_t datab;
575 if (MDE_PROP_DATA_LEN(ap) != MDE_PROP_DATA_LEN(bp))
576 return (MD_DIFF_NOMATCH);
578 dataa = (caddr_t)(mdap->datap +
579 MDE_PROP_DATA_OFFSET(ap));
580 datab = (caddr_t)(mdbp->datap +
581 MDE_PROP_DATA_OFFSET(bp));
583 if (memcmp(dataa, datab, MDE_PROP_DATA_LEN(ap)) != 0)
584 return (MD_DIFF_NOMATCH);
586 break;
589 default:
590 /* unsupported prop type */
591 return (MD_DIFF_NOMATCH);
596 * All the specified properties exist in both
597 * nodes and have the same value. The two nodes
598 * match.
601 return (MD_DIFF_MATCH);