#include <sys/types.h>
#ifdef _KERNEL
#include <sys/systm.h>
#else
#include <string.h>
#include <strings.h>
#endif
#include <sys/note.h>
#include <sys/mdesc.h>
#include <sys/mdesc_impl.h>
#define MDD_FREE_CHECK(mdp, ptr, sz) \
do { \
if (ptr) mdp->freep(ptr, sz); \
_NOTE(CONSTCOND) } while (0)
#define MD_DIFF_MAGIC 0x4D445F4449464621ull
#define MD_DIFF_NOMATCH (-1)
#define MD_DIFF_MATCH (1)
typedef struct {
mde_cookie_t *mdep;
uint_t nelem;
} md_diff_t;
typedef struct {
uint64_t mdd_magic;
md_diff_t added;
md_diff_t removed;
md_diff_t match1;
md_diff_t match2;
void *(*allocp)(size_t);
void (*freep)(void *, size_t);
} md_diff_impl_t;
static int mdd_scan_for_nodes(md_t *mdp, mde_cookie_t start,
char *compnodep, int *countp, mde_cookie_t **nodespp);
static boolean_t mdd_any_dup_nodes(md_impl_t *mdp, md_prop_match_t *pmp,
int count, mde_cookie_t *nodesp);
static int mdd_node_list_match(md_impl_t *md1, md_impl_t *md2,
md_element_t *match_nodep, mde_cookie_t *match_listp,
uint8_t *match_seenp, int start, int end, md_prop_match_t *match_elemsp);
static int mdd_node_compare(md_impl_t *mdap, md_impl_t *mdbp,
md_element_t *nodeap, md_element_t *nodebp, md_prop_match_t *match_elemsp);
md_diff_cookie_t
md_diff_init(md_t *md1p, mde_cookie_t start1, md_t *md2p, mde_cookie_t start2,
char *compnodep, md_prop_match_t *match_fieldsp)
{
int idx;
md_impl_t *md1 = (md_impl_t *)md1p;
md_impl_t *md2 = (md_impl_t *)md2p;
mde_cookie_t *md1nodesp = NULL;
mde_cookie_t *md2nodesp = NULL;
int md1count = 0;
int md2count = 0;
uint8_t *seenp = NULL;
md_diff_impl_t *diff_res;
mde_cookie_t *mde_add_scr;
mde_cookie_t *mde_rem_scr;
mde_cookie_t *mde_match1_scr;
mde_cookie_t *mde_match2_scr;
int nadd = 0;
int nrem = 0;
int nmatch = 0;
if ((md1p == NULL) || (md2p == NULL))
return (MD_INVAL_DIFF_COOKIE);
if ((start1 == MDE_INVAL_ELEM_COOKIE) ||
(start2 == MDE_INVAL_ELEM_COOKIE))
return (MD_INVAL_DIFF_COOKIE);
if ((compnodep == NULL) || (match_fieldsp == NULL))
return (MD_INVAL_DIFF_COOKIE);
if (mdd_scan_for_nodes(md1p,
start1, compnodep, &md1count, &md1nodesp) == -1)
return (MD_INVAL_DIFF_COOKIE);
if (md1nodesp &&
mdd_any_dup_nodes(md1, match_fieldsp, md1count, md1nodesp)) {
MDD_FREE_CHECK(md1, md1nodesp, sizeof (mde_cookie_t) *
md1count);
return (MD_INVAL_DIFF_COOKIE);
}
if (mdd_scan_for_nodes(md2p,
start2, compnodep, &md2count, &md2nodesp) == -1)
return (MD_INVAL_DIFF_COOKIE);
if (md2nodesp &&
mdd_any_dup_nodes(md2, match_fieldsp, md2count, md2nodesp)) {
MDD_FREE_CHECK(md1, md1nodesp, sizeof (mde_cookie_t) *
md1count);
MDD_FREE_CHECK(md2, md2nodesp, sizeof (mde_cookie_t) *
md2count);
return (MD_INVAL_DIFF_COOKIE);
}
diff_res = md1->allocp(sizeof (md_diff_impl_t));
bzero(diff_res, sizeof (md_diff_impl_t));
diff_res->allocp = md1->allocp;
diff_res->freep = md1->freep;
diff_res->mdd_magic = MD_DIFF_MAGIC;
if ((md1count == 0) && (md2count != 0)) {
diff_res->added.mdep = md2nodesp;
diff_res->added.nelem = md2count;
return ((mde_cookie_t)diff_res);
}
if ((md1count != 0) && (md2count == 0)) {
diff_res->removed.mdep = md1nodesp;
diff_res->removed.nelem = md1count;
return ((mde_cookie_t)diff_res);
}
if ((md1count == 0) && (md2count == 0))
return ((mde_cookie_t)diff_res);
mde_add_scr = diff_res->allocp(sizeof (mde_cookie_t) * md2count);
mde_rem_scr = diff_res->allocp(sizeof (mde_cookie_t) * md1count);
mde_match1_scr = diff_res->allocp(sizeof (mde_cookie_t) * md1count);
mde_match2_scr = diff_res->allocp(sizeof (mde_cookie_t) * md2count);
seenp = (uint8_t *)diff_res->allocp(sizeof (uint8_t) * md2count);
bzero(seenp, sizeof (uint8_t) * md2count);
for (idx = 0; idx < md1count; idx++) {
md_element_t *elem = &(md1->mdep[md1nodesp[idx]]);
int match = mdd_node_list_match(md1, md2, elem, md2nodesp,
seenp, 0, md2count - 1, match_fieldsp);
if (match == MD_DIFF_NOMATCH)
mde_rem_scr[nrem++] = md1nodesp[idx];
else {
mde_match1_scr[nmatch] = md1nodesp[idx];
mde_match2_scr[nmatch] = md2nodesp[match];
nmatch++;
seenp[match] = 1;
}
}
for (idx = 0; idx < md2count; idx++) {
if (!seenp[idx])
mde_add_scr[nadd++] = md2nodesp[idx];
}
if (nadd) {
int addsz = sizeof (mde_cookie_t) * nadd;
diff_res->added.mdep = (mde_cookie_t *)diff_res->allocp(addsz);
bcopy(mde_add_scr, diff_res->added.mdep, addsz);
diff_res->added.nelem = nadd;
}
if (nrem) {
int remsz = sizeof (mde_cookie_t) * nrem;
diff_res->removed.mdep =
(mde_cookie_t *)diff_res->allocp(remsz);
bcopy(mde_rem_scr, diff_res->removed.mdep, remsz);
diff_res->removed.nelem = nrem;
}
if (nmatch) {
int matchsz = sizeof (mde_cookie_t) * nmatch;
diff_res->match1.mdep =
(mde_cookie_t *)diff_res->allocp(matchsz);
diff_res->match2.mdep =
(mde_cookie_t *)diff_res->allocp(matchsz);
bcopy(mde_match1_scr, diff_res->match1.mdep, matchsz);
bcopy(mde_match2_scr, diff_res->match2.mdep, matchsz);
diff_res->match1.nelem = nmatch;
diff_res->match2.nelem = nmatch;
}
md1->freep(md1nodesp, sizeof (mde_cookie_t) * md1count);
md2->freep(md2nodesp, sizeof (mde_cookie_t) * md2count);
diff_res->freep(mde_add_scr, sizeof (mde_cookie_t) * md2count);
diff_res->freep(mde_rem_scr, sizeof (mde_cookie_t) * md1count);
diff_res->freep(mde_match1_scr, sizeof (mde_cookie_t) * md1count);
diff_res->freep(mde_match2_scr, sizeof (mde_cookie_t) * md2count);
diff_res->freep(seenp, sizeof (uint8_t) * md2count);
return ((md_diff_cookie_t)diff_res);
}
int
md_diff_added(md_diff_cookie_t mdd, mde_cookie_t **mde_addedp)
{
md_diff_impl_t *mddp = (md_diff_impl_t *)mdd;
if ((mddp == NULL) || (mddp->mdd_magic != MD_DIFF_MAGIC))
return (-1);
*mde_addedp = mddp->added.mdep;
return (mddp->added.nelem);
}
int
md_diff_removed(md_diff_cookie_t mdd, mde_cookie_t **mde_removedp)
{
md_diff_impl_t *mddp = (md_diff_impl_t *)mdd;
if ((mddp == NULL) || (mddp->mdd_magic != MD_DIFF_MAGIC))
return (-1);
*mde_removedp = mddp->removed.mdep;
return (mddp->removed.nelem);
}
int
md_diff_matched(md_diff_cookie_t mdd, mde_cookie_t **mde_match1p,
mde_cookie_t **mde_match2p)
{
md_diff_impl_t *mddp = (md_diff_impl_t *)mdd;
if ((mddp == NULL) || (mddp->mdd_magic != MD_DIFF_MAGIC))
return (-1);
*mde_match1p = mddp->match1.mdep;
*mde_match2p = mddp->match2.mdep;
return (mddp->match1.nelem);
}
int
md_diff_fini(md_diff_cookie_t mdd)
{
md_diff_impl_t *mddp = (md_diff_impl_t *)mdd;
if ((mddp == NULL) || (mddp->mdd_magic != MD_DIFF_MAGIC))
return (-1);
mddp->mdd_magic = 0;
MDD_FREE_CHECK(mddp, mddp->added.mdep, mddp->added.nelem *
sizeof (mde_cookie_t));
MDD_FREE_CHECK(mddp, mddp->removed.mdep, mddp->removed.nelem *
sizeof (mde_cookie_t));
MDD_FREE_CHECK(mddp, mddp->match1.mdep, mddp->match1.nelem *
sizeof (mde_cookie_t));
MDD_FREE_CHECK(mddp, mddp->match2.mdep, mddp->match2.nelem *
sizeof (mde_cookie_t));
mddp->freep(mddp, sizeof (md_diff_impl_t));
return (0);
}
static int
mdd_scan_for_nodes(md_t *mdp,
mde_cookie_t start, char *compnodep, int *countp, mde_cookie_t **nodespp)
{
mde_str_cookie_t cname;
mde_str_cookie_t aname;
md_impl_t *mdip = (md_impl_t *)mdp;
if (mdip == NULL)
return (-1);
cname = md_find_name(mdp, compnodep);
aname = md_find_name(mdp, "fwd");
*countp = md_scan_dag(mdp, start, cname, aname, NULL);
if (*countp == 0) {
*nodespp = NULL;
return (0);
}
*nodespp = mdip->allocp(sizeof (mde_cookie_t) * (*countp));
(void) md_scan_dag(mdp, start, cname, aname, *nodespp);
return (0);
}
static boolean_t
mdd_any_dup_nodes(md_impl_t *mdp, md_prop_match_t *pmp, int count,
mde_cookie_t *nodesp)
{
int idx;
int match;
md_element_t *elem;
ASSERT(count > 0 || nodesp == NULL);
for (idx = 0; idx < count; idx++) {
elem = &(mdp->mdep[nodesp[idx]]);
match = mdd_node_list_match(mdp, mdp, elem, nodesp, NULL,
idx + 1, count - 1, pmp);
if (match != MD_DIFF_NOMATCH)
return (B_TRUE);
}
return (B_FALSE);
}
static int
mdd_node_list_match(md_impl_t *md1, md_impl_t *md2, md_element_t *match_nodep,
mde_cookie_t *match_listp, uint8_t *match_seenp, int start, int end,
md_prop_match_t *match_elemsp)
{
int match;
int idx;
md_element_t *elem;
for (idx = start; idx <= end; idx++) {
if ((match_seenp != NULL) && (match_seenp[idx]))
continue;
elem = &(md2->mdep[match_listp[idx]]);
match = mdd_node_compare(md1, md2, match_nodep, elem,
match_elemsp);
if (match == MD_DIFF_MATCH)
return (idx);
}
return (MD_DIFF_NOMATCH);
}
static int
mdd_node_compare(md_impl_t *mdap, md_impl_t *mdbp, md_element_t *nodeap,
md_element_t *nodebp, md_prop_match_t *match_elemsp)
{
md_element_t *ap;
md_element_t *bp;
boolean_t nodea_interest;
boolean_t nodeb_interest;
int idx;
if ((MDE_TAG(nodeap) != MDET_NODE) || (MDE_TAG(nodebp) != MDET_NODE))
return (MD_DIFF_NOMATCH);
for (idx = 0; match_elemsp[idx].type != MDET_LIST_END; idx++) {
int type;
nodea_interest = B_FALSE;
nodeb_interest = B_FALSE;
type = match_elemsp[idx].type;
for (ap = nodeap; MDE_TAG(ap) != MDET_NODE_END; ap++) {
char *elemname;
if (MDE_TAG(ap) != type)
continue;
elemname = mdap->namep + MDE_NAME(ap);
if (strcmp(elemname, match_elemsp[idx].namep) == 0) {
nodea_interest = B_TRUE;
break;
}
}
if (!nodea_interest)
return (MD_DIFF_NOMATCH);
for (bp = nodebp; MDE_TAG(bp) != MDET_NODE_END; bp++) {
char *elemname;
if (MDE_TAG(bp) != type)
continue;
elemname = mdbp->namep + MDE_NAME(bp);
if (strcmp(elemname, match_elemsp[idx].namep) == 0) {
nodeb_interest = B_TRUE;
break;
}
}
if (!nodeb_interest)
return (MD_DIFF_NOMATCH);
switch (type) {
case MDET_PROP_VAL:
if (MDE_PROP_VALUE(ap) != MDE_PROP_VALUE(bp))
return (MD_DIFF_NOMATCH);
break;
case MDET_PROP_STR: {
char *stra = (char *)(mdap->datap +
MDE_PROP_DATA_OFFSET(ap));
char *strb = (char *)(mdbp->datap +
MDE_PROP_DATA_OFFSET(bp));
if (strcmp(stra, strb) != 0)
return (MD_DIFF_NOMATCH);
break;
}
case MDET_PROP_DAT: {
caddr_t dataa;
caddr_t datab;
if (MDE_PROP_DATA_LEN(ap) != MDE_PROP_DATA_LEN(bp))
return (MD_DIFF_NOMATCH);
dataa = (caddr_t)(mdap->datap +
MDE_PROP_DATA_OFFSET(ap));
datab = (caddr_t)(mdbp->datap +
MDE_PROP_DATA_OFFSET(bp));
if (memcmp(dataa, datab, MDE_PROP_DATA_LEN(ap)) != 0)
return (MD_DIFF_NOMATCH);
break;
}
default:
return (MD_DIFF_NOMATCH);
}
}
return (MD_DIFF_MATCH);
}