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 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 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> 39 40 #define MDD_FREE_CHECK(mdp, ptr, sz) \ 41 do { \ 42 if (ptr) mdp->freep(ptr, sz); \ 43 _NOTE(CONSTCOND) } while (0) 44 45 #define MD_DIFF_MAGIC 0x4D445F4449464621ull /* 'MD_DIFF!' */ 46 #define MD_DIFF_NOMATCH (-1) 47 #define MD_DIFF_MATCH (1) 48 49 typedef struct { 50 mde_cookie_t *mdep; 51 uint_t nelem; 52 } md_diff_t; 53 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; 63 64 /* 65 * Internal utility functions 66 */ 67 static int mdd_scan_for_nodes(md_t *mdp, mde_cookie_t start, 68 char *compnodep, int *countp, mde_cookie_t **nodespp); 69 70 static boolean_t mdd_any_dup_nodes(md_impl_t *mdp, md_prop_match_t *pmp, 71 int count, mde_cookie_t *nodesp); 72 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); 76 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); 79 80 /* 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. 86 */ 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) 90 { 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; 99 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; 109 110 /* sanity check params */ 111 if ((md1p == NULL) || (md2p == NULL)) 112 return (MD_INVAL_DIFF_COOKIE); 113 114 if ((start1 == MDE_INVAL_ELEM_COOKIE) || 115 (start2 == MDE_INVAL_ELEM_COOKIE)) 116 return (MD_INVAL_DIFF_COOKIE); 117 118 if ((compnodep == NULL) || (match_fieldsp == NULL)) 119 return (MD_INVAL_DIFF_COOKIE); 120 121 /* 122 * Prepare an array of the matching nodes from the first MD. 123 */ 124 if (mdd_scan_for_nodes(md1p, 125 start1, compnodep, &md1count, &md1nodesp) == -1) 126 return (MD_INVAL_DIFF_COOKIE); 127 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); 134 } 135 136 137 /* 138 * Prepare an array of the matching nodes from the second MD. 139 */ 140 if (mdd_scan_for_nodes(md2p, 141 start2, compnodep, &md2count, &md2nodesp) == -1) 142 return (MD_INVAL_DIFF_COOKIE); 143 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); 152 } 153 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; 160 161 /* 162 * Special cases for empty lists 163 */ 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); 169 } 170 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); 176 } 177 178 if ((md1count == 0) && (md2count == 0)) 179 /* no nodes found */ 180 return ((mde_cookie_t)diff_res); 181 182 /* 183 * Both lists have some elements. Allocate some scratch 184 * buffers to sort them into our three categories, added, 185 * removed, and matched pairs. 186 */ 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); 191 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); 195 196 /* 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. 201 */ 202 for (idx = 0; idx < md1count; idx++) { 203 204 md_element_t *elem = &(md1->mdep[md1nodesp[idx]]); 205 206 int match = mdd_node_list_match(md1, md2, elem, md2nodesp, 207 seenp, 0, md2count - 1, match_fieldsp); 208 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++; 217 218 /* mark that this match has been recorded */ 219 seenp[match] = 1; 220 } 221 } 222 223 /* 224 * Make a pass through the md2 array. Any nodes that have 225 * not been marked as seen have been added. 226 */ 227 for (idx = 0; idx < md2count; idx++) { 228 if (!seenp[idx]) 229 /* record added node */ 230 mde_add_scr[nadd++] = md2nodesp[idx]; 231 } 232 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); 237 238 bcopy(mde_add_scr, diff_res->added.mdep, addsz); 239 240 diff_res->added.nelem = nadd; 241 } 242 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); 248 249 bcopy(mde_rem_scr, diff_res->removed.mdep, remsz); 250 diff_res->removed.nelem = nrem; 251 } 252 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); 260 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; 265 } 266 267 /* clean up */ 268 md1->freep(md1nodesp, sizeof (mde_cookie_t) * md1count); 269 md2->freep(md2nodesp, sizeof (mde_cookie_t) * md2count); 270 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); 275 276 diff_res->freep(seenp, sizeof (uint8_t) * md2count); 277 278 return ((md_diff_cookie_t)diff_res); 279 } 280 281 /* 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. 286 */ 287 int 288 md_diff_added(md_diff_cookie_t mdd, mde_cookie_t **mde_addedp) 289 { 290 md_diff_impl_t *mddp = (md_diff_impl_t *)mdd; 291 292 if ((mddp == NULL) || (mddp->mdd_magic != MD_DIFF_MAGIC)) 293 return (-1); 294 295 *mde_addedp = mddp->added.mdep; 296 297 return (mddp->added.nelem); 298 } 299 300 /* 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. 305 */ 306 int 307 md_diff_removed(md_diff_cookie_t mdd, mde_cookie_t **mde_removedp) 308 { 309 md_diff_impl_t *mddp = (md_diff_impl_t *)mdd; 310 311 if ((mddp == NULL) || (mddp->mdd_magic != MD_DIFF_MAGIC)) 312 return (-1); 313 314 *mde_removedp = mddp->removed.mdep; 315 316 return (mddp->removed.nelem); 317 } 318 319 /* 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. 325 */ 326 int 327 md_diff_matched(md_diff_cookie_t mdd, mde_cookie_t **mde_match1p, 328 mde_cookie_t **mde_match2p) 329 { 330 md_diff_impl_t *mddp = (md_diff_impl_t *)mdd; 331 332 if ((mddp == NULL) || (mddp->mdd_magic != MD_DIFF_MAGIC)) 333 return (-1); 334 335 *mde_match1p = mddp->match1.mdep; 336 *mde_match2p = mddp->match2.mdep; 337 338 return (mddp->match1.nelem); 339 } 340 341 /* 342 * Deallocate any storage used to store results of a previous 343 * md_diff_init() call. Returns 0 on success and -1 on failure. 344 */ 345 int 346 md_diff_fini(md_diff_cookie_t mdd) 347 { 348 md_diff_impl_t *mddp = (md_diff_impl_t *)mdd; 349 350 if ((mddp == NULL) || (mddp->mdd_magic != MD_DIFF_MAGIC)) 351 return (-1); 352 353 mddp->mdd_magic = 0; 354 355 MDD_FREE_CHECK(mddp, mddp->added.mdep, mddp->added.nelem * 356 sizeof (mde_cookie_t)); 357 358 MDD_FREE_CHECK(mddp, mddp->removed.mdep, mddp->removed.nelem * 359 sizeof (mde_cookie_t)); 360 361 MDD_FREE_CHECK(mddp, mddp->match1.mdep, mddp->match1.nelem * 362 sizeof (mde_cookie_t)); 363 364 MDD_FREE_CHECK(mddp, mddp->match2.mdep, mddp->match2.nelem * 365 sizeof (mde_cookie_t)); 366 367 mddp->freep(mddp, sizeof (md_diff_impl_t)); 368 369 return (0); 370 } 371 372 /* 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. 378 * 379 * Returns: 0 success; -1 failure 380 */ 381 static int 382 mdd_scan_for_nodes(md_t *mdp, 383 mde_cookie_t start, char *compnodep, int *countp, mde_cookie_t **nodespp) 384 { 385 mde_str_cookie_t cname; 386 mde_str_cookie_t aname; 387 md_impl_t *mdip = (md_impl_t *)mdp; 388 389 if (mdip == NULL) 390 return (-1); 391 392 cname = md_find_name(mdp, compnodep); 393 aname = md_find_name(mdp, "fwd"); 394 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); 400 } 401 402 /* allocate the storage */ 403 *nodespp = mdip->allocp(sizeof (mde_cookie_t) * (*countp)); 404 405 /* populate our array with the matching nodes */ 406 (void) md_scan_dag(mdp, start, cname, aname, *nodespp); 407 408 return (0); 409 } 410 411 /* 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. 416 */ 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) 420 { 421 int idx; 422 int match; 423 md_element_t *elem; 424 425 ASSERT(count > 0 || nodesp == NULL); 426 427 for (idx = 0; idx < count; idx++) { 428 elem = &(mdp->mdep[nodesp[idx]]); 429 430 match = mdd_node_list_match(mdp, mdp, elem, nodesp, NULL, 431 idx + 1, count - 1, pmp); 432 433 if (match != MD_DIFF_NOMATCH) 434 return (B_TRUE); 435 } 436 437 return (B_FALSE); 438 } 439 440 /* 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. 445 * 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. 449 */ 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) 454 { 455 int match; 456 int idx; 457 md_element_t *elem; 458 459 for (idx = start; idx <= end; idx++) { 460 461 if ((match_seenp != NULL) && (match_seenp[idx])) 462 continue; 463 464 elem = &(md2->mdep[match_listp[idx]]); 465 466 match = mdd_node_compare(md1, md2, match_nodep, elem, 467 match_elemsp); 468 if (match == MD_DIFF_MATCH) 469 return (idx); 470 } 471 472 return (MD_DIFF_NOMATCH); 473 } 474 475 /* 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. 481 */ 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) 485 { 486 md_element_t *ap; 487 md_element_t *bp; 488 boolean_t nodea_interest; 489 boolean_t nodeb_interest; 490 int idx; 491 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); 495 496 for (idx = 0; match_elemsp[idx].type != MDET_LIST_END; idx++) { 497 498 int type; 499 500 nodea_interest = B_FALSE; 501 nodeb_interest = B_FALSE; 502 503 type = match_elemsp[idx].type; 504 505 /* 506 * Check node A for the property of interest 507 */ 508 for (ap = nodeap; MDE_TAG(ap) != MDET_NODE_END; ap++) { 509 char *elemname; 510 511 if (MDE_TAG(ap) != type) 512 continue; 513 514 elemname = mdap->namep + MDE_NAME(ap); 515 516 if (strcmp(elemname, match_elemsp[idx].namep) == 0) { 517 /* found the property of interest */ 518 nodea_interest = B_TRUE; 519 break; 520 } 521 } 522 523 /* node A is not of interest */ 524 if (!nodea_interest) 525 return (MD_DIFF_NOMATCH); 526 527 /* 528 * Check node B for the property of interest 529 */ 530 for (bp = nodebp; MDE_TAG(bp) != MDET_NODE_END; bp++) { 531 char *elemname; 532 533 if (MDE_TAG(bp) != type) 534 continue; 535 536 elemname = mdbp->namep + MDE_NAME(bp); 537 538 if (strcmp(elemname, match_elemsp[idx].namep) == 0) { 539 nodeb_interest = B_TRUE; 540 break; 541 } 542 } 543 544 /* node B is not of interest */ 545 if (!nodeb_interest) 546 return (MD_DIFF_NOMATCH); 547 548 /* 549 * Both nodes have the property of interest. The 550 * nodes are not a match unless the value of that 551 * property match 552 */ 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; 558 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)); 564 565 if (strcmp(stra, strb) != 0) 566 return (MD_DIFF_NOMATCH); 567 break; 568 } 569 570 case MDET_PROP_DAT: { 571 572 caddr_t dataa; 573 caddr_t datab; 574 575 if (MDE_PROP_DATA_LEN(ap) != MDE_PROP_DATA_LEN(bp)) 576 return (MD_DIFF_NOMATCH); 577 578 dataa = (caddr_t)(mdap->datap + 579 MDE_PROP_DATA_OFFSET(ap)); 580 datab = (caddr_t)(mdbp->datap + 581 MDE_PROP_DATA_OFFSET(bp)); 582 583 if (memcmp(dataa, datab, MDE_PROP_DATA_LEN(ap)) != 0) 584 return (MD_DIFF_NOMATCH); 585 586 break; 587 } 588 589 default: 590 /* unsupported prop type */ 591 return (MD_DIFF_NOMATCH); 592 } 593 } 594 595 /* 596 * All the specified properties exist in both 597 * nodes and have the same value. The two nodes 598 * match. 599 */ 600 601 return (MD_DIFF_MATCH); 602 } 603