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]
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>
31 #include <sys/systm.h>
37 #include <sys/mdesc.h>
38 #include <sys/mdesc_impl.h>
40 #define MDD_FREE_CHECK(mdp, ptr, sz) \
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)
60 void *(*allocp
)(size_t);
61 void (*freep
)(void *, size_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.
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
)
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
;
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
;
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 */
130 mdd_any_dup_nodes(md1
, match_fieldsp
, md1count
, md1nodesp
)) {
131 MDD_FREE_CHECK(md1
, md1nodesp
, sizeof (mde_cookie_t
) *
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 */
146 mdd_any_dup_nodes(md2
, match_fieldsp
, md2count
, md2nodesp
)) {
147 MDD_FREE_CHECK(md1
, md1nodesp
, sizeof (mde_cookie_t
) *
149 MDD_FREE_CHECK(md2
, md2nodesp
, sizeof (mde_cookie_t
) *
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))
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
];
213 /* record matched node pair */
214 mde_match1_scr
[nmatch
] = md1nodesp
[idx
];
215 mde_match2_scr
[nmatch
] = md2nodesp
[match
];
218 /* mark that this match has been recorded */
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
++) {
229 /* record added node */
230 mde_add_scr
[nadd
++] = md2nodesp
[idx
];
233 /* fill in the added node list */
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 */
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 */
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
;
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
))
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
))
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
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
))
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
))
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
));
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
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
;
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
);
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
);
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
418 mdd_any_dup_nodes(md_impl_t
*mdp
, md_prop_match_t
*pmp
, int count
,
419 mde_cookie_t
*nodesp
)
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
)
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.
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
)
459 for (idx
= start
; idx
<= end
; idx
++) {
461 if ((match_seenp
!= NULL
) && (match_seenp
[idx
]))
464 elem
= &(md2
->mdep
[match_listp
[idx
]]);
466 match
= mdd_node_compare(md1
, md2
, match_nodep
, elem
,
468 if (match
== MD_DIFF_MATCH
)
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.
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
)
488 boolean_t nodea_interest
;
489 boolean_t nodeb_interest
;
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
++) {
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
++) {
511 if (MDE_TAG(ap
) != type
)
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
;
523 /* node A is not of 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
++) {
533 if (MDE_TAG(bp
) != type
)
536 elemname
= mdbp
->namep
+ MDE_NAME(bp
);
538 if (strcmp(elemname
, match_elemsp
[idx
].namep
) == 0) {
539 nodeb_interest
= B_TRUE
;
544 /* node B is not of 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
555 if (MDE_PROP_VALUE(ap
) != MDE_PROP_VALUE(bp
))
556 return (MD_DIFF_NOMATCH
);
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
);
570 case MDET_PROP_DAT
: {
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
);
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
601 return (MD_DIFF_MATCH
);