range_diff.c File Reference


Detailed Description

Implementation for computing semantic differences in ranges.

Author:
Jeremy A. Mowery jmowery@tresys.com

Jason Tang jtang@tresys.com

Copyright (C) 2007 Tresys Technology, LLC

This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

Definition in file range_diff.c.

#include <config.h>
#include "poldiff_internal.h"
#include <apol/util.h>
#include <assert.h>
#include <errno.h>

Go to the source code of this file.


Classes

struct  poldiff_range

Functions

apol_vector_tpoldiff_range_get_levels (const poldiff_range_t *range)
 Get the vector of level differences from a range diffence object.
const apol_mls_range_tpoldiff_range_get_original_range (const poldiff_range_t *range)
 Get the original item's range.
const apol_mls_range_tpoldiff_range_get_modified_range (const poldiff_range_t *range)
 Get the modified item's range.
apol_vector_tpoldiff_range_get_min_added_cats (const poldiff_range_t *range)
 Get the vector of categories added to the minimum set from a range diffence object.
apol_vector_tpoldiff_range_get_min_removed_cats (const poldiff_range_t *range)
 Get the vector of categories removed from the minimum set from a range diffence object.
apol_vector_tpoldiff_range_get_min_unmodified_cats (const poldiff_range_t *range)
 Get the vector of unmodified categories of the minimum set from a range diffence object.
char * poldiff_range_to_string_brief (const poldiff_t *diff, const poldiff_range_t *range)
 Allocate and return a string that represents the differences encoded by the given range.
poldiff_range_trange_create (const poldiff_t *diff, const qpol_mls_range_t *orig_range, const qpol_mls_range_t *mod_range, poldiff_form_e form)
 Allocate and return a poldiff_range_t object.
void range_destroy (poldiff_range_t **range)
 Deallocate all space for a range, including the pointer itself.
int range_comp_alphabetize (const void *a, const void *b, void *data __attribute__((unused)))
 Comparison function for two apol_mls_level_t objects from the same apol_mls_range_t.
int range_comp (const void *a, const void *b, void *data)
 Comparison function for two levels from the same poldiff_range_t.
int range_deep_diff (poldiff_t *diff, poldiff_range_t *range)
 Calculate the differences between two ranges (that are stored within the poldiff_range_t object).

Function Documentation

apol_vector_t* poldiff_range_get_levels const poldiff_range_t range  ) 
 

Get the vector of level differences from a range diffence object.

Parameters:
range Range object to query.
Returns:
A vector of elements of type poldiff_level_t, or NULL on error. The caller should not modify the returned vector.

Definition at line 44 of file range_diff.c.

References apol_vector_t, poldiff_range::levels, and poldiff_range_t.

Referenced by modified_mls_range_to_string(), and result_item_print_modified_range().

00045 {
00046         if (range == NULL) {
00047                 errno = EINVAL;
00048                 return NULL;
00049         }
00050         return range->levels;
00051 }

const apol_mls_range_t* poldiff_range_get_original_range const poldiff_range_t range  ) 
 

Get the original item's range.

This could represent a user's original assigned range or the original target range for a range_transition. If there was no original range (such as for items that are added) then this returns NULL.

Parameters:
range Range object to query.
Returns:
Original range, or NULL upon error or no range available. The caller should not modify the returned object.

Definition at line 53 of file range_diff.c.

References apol_mls_range_t, poldiff_range::orig_range, and poldiff_range_t.

Referenced by poldiff_range_trans_to_string(), and rangetrans_to_string().

00054 {
00055         if (range == NULL) {
00056                 errno = EINVAL;
00057                 return NULL;
00058         }
00059         return range->orig_range;
00060 }

const apol_mls_range_t* poldiff_range_get_modified_range const poldiff_range_t range  ) 
 

Get the modified item's range.

This could represent a user's modified assigned range or the modified target range for a range_transition. If there was no original range (such as for items that are removed) then this returns NULL.

Parameters:
range Range object to query.
Returns:
Modified range, or NULL upon error or no range available. The caller should not modify the returned object.

Definition at line 62 of file range_diff.c.

References apol_mls_range_t, poldiff_range::mod_range, and poldiff_range_t.

Referenced by poldiff_range_trans_to_string(), and rangetrans_to_string().

00063 {
00064         if (range == NULL) {
00065                 errno = EINVAL;
00066                 return NULL;
00067         }
00068         return range->mod_range;
00069 }

apol_vector_t* poldiff_range_get_min_added_cats const poldiff_range_t range  ) 
 

Get the vector of categories added to the minimum set from a range diffence object.

Parameters:
range Range object to query.
Returns:
A vector of elements of type string, or NULL on error. The caller should not modify the returned vector.

Definition at line 71 of file range_diff.c.

References apol_vector_t, poldiff_range::min_added_cats, and poldiff_range_t.

Referenced by modified_mls_range_to_string().

00072 {
00073         if (range == NULL) {
00074                 errno = EINVAL;
00075                 return NULL;
00076         }
00077         return range->min_added_cats;
00078 }

apol_vector_t* poldiff_range_get_min_removed_cats const poldiff_range_t range  ) 
 

Get the vector of categories removed from the minimum set from a range diffence object.

Parameters:
range Range object to query.
Returns:
A vector of elements of type string, or NULL on error. The caller should not modify the returned vector.

Definition at line 80 of file range_diff.c.

References apol_vector_t, poldiff_range::min_removed_cats, and poldiff_range_t.

Referenced by modified_mls_range_to_string().

00081 {
00082         if (range == NULL) {
00083                 errno = EINVAL;
00084                 return NULL;
00085         }
00086         return range->min_removed_cats;
00087 }

apol_vector_t* poldiff_range_get_min_unmodified_cats const poldiff_range_t range  ) 
 

Get the vector of unmodified categories of the minimum set from a range diffence object.

Parameters:
range Range object to query.
Returns:
A vector of elements of type string, or NULL on error. The caller should not modify the returned vector.

Definition at line 89 of file range_diff.c.

References apol_vector_t, poldiff_range::min_unmodified_cats, and poldiff_range_t.

00090 {
00091         if (range == NULL) {
00092                 errno = EINVAL;
00093                 return NULL;
00094         }
00095         return range->min_unmodified_cats;
00096 }

char* poldiff_range_to_string_brief const poldiff_t diff,
const poldiff_range_t range
 

Allocate and return a string that represents the differences encoded by the given range.

The returned string is suitable for embedding within another item's to_string() display.

Parameters:
diff Poldiff diff structure containing policies.
range Range object to render.
Returns:
Rendered string, or NULL upon error. Caller must free() string afterwards.

Definition at line 98 of file range_diff.c.

References apol_mls_range_render(), apol_str_append(), apol_str_appendf(), apol_vector_get_element(), apol_vector_get_size(), diff, ERR, level, poldiff_range::levels, poldiff_range::min_added_cats, poldiff_range::min_removed_cats, poldiff_range::min_unmodified_cats, poldiff::mod_pol, poldiff_range::mod_range, poldiff::orig_pol, poldiff_range::orig_range, poldiff_level_t, poldiff_level_to_string_brief(), poldiff_range_t, and poldiff_t.

Referenced by poldiff_range_trans_to_string(), result_item_print_modified_range(), and user_to_modified_string().

00099 {
00100         char *r1 = NULL, *r2 = NULL;
00101         char *s = NULL, *t = NULL, *sep = "", *cat;
00102         size_t len = 0, i;
00103         if (range->orig_range != NULL && (r1 = apol_mls_range_render(diff->orig_pol, range->orig_range)) == NULL) {
00104                 ERR(diff, "%s", strerror(errno));
00105                 goto cleanup;
00106         }
00107         if (range->mod_range != NULL && (r2 = apol_mls_range_render(diff->mod_pol, range->mod_range)) == NULL) {
00108                 ERR(diff, "%s", strerror(errno));
00109                 goto cleanup;
00110         }
00111         assert(r1 != NULL || r2 != NULL);
00112         if (r1 == NULL) {
00113                 if (apol_str_appendf(&s, &len, "   range: %s\n", r2) < 0) {
00114                         ERR(diff, "%s", strerror(errno));
00115                         goto cleanup;
00116                 }
00117         } else if (r2 == NULL) {
00118                 if (apol_str_appendf(&s, &len, "   range: %s\n", r1) < 0) {
00119                         ERR(diff, "%s", strerror(errno));
00120                         goto cleanup;
00121                 }
00122         } else {
00123                 if (apol_str_appendf(&s, &len, "   range: %s  -->  %s\n", r1, r2) < 0) {
00124                         ERR(diff, "%s", strerror(errno));
00125                         goto cleanup;
00126                 }
00127         }
00128         if ((range->min_added_cats != NULL && apol_vector_get_size(range->min_added_cats) > 0) ||
00129             (range->min_removed_cats != NULL && apol_vector_get_size(range->min_removed_cats) > 0) ||
00130             (range->min_unmodified_cats != NULL && apol_vector_get_size(range->min_unmodified_cats) > 0)) {
00131                 if (apol_str_append(&s, &len, "     minimum categories: ") < 0) {
00132                         ERR(diff, "%s", strerror(errno));
00133                         goto cleanup;
00134                 }
00135                 for (i = 0; range->min_unmodified_cats != NULL && i < apol_vector_get_size(range->min_unmodified_cats); i++) {
00136                         cat = apol_vector_get_element(range->min_unmodified_cats, i);
00137                         if (apol_str_appendf(&s, &len, "%s%s", sep, cat) < 0) {
00138                                 ERR(diff, "%s", strerror(errno));
00139                                 return NULL;
00140                         }
00141                         sep = ",";
00142                 }
00143                 for (i = 0; range->min_added_cats != NULL && i < apol_vector_get_size(range->min_added_cats); i++) {
00144                         cat = apol_vector_get_element(range->min_added_cats, i);
00145                         if (apol_str_appendf(&s, &len, "%s+%s", sep, cat) < 0) {
00146                                 ERR(diff, "%s", strerror(errno));
00147                                 return NULL;
00148                         }
00149                         sep = ",";
00150                 }
00151                 for (i = 0; range->min_removed_cats != NULL && i < apol_vector_get_size(range->min_removed_cats); i++) {
00152                         cat = apol_vector_get_element(range->min_removed_cats, i);
00153                         if (apol_str_appendf(&s, &len, "%s-%s", sep, cat) < 0) {
00154                                 ERR(diff, "%s", strerror(errno));
00155                                 return NULL;
00156                         }
00157                         sep = ",";
00158                 }
00159                 if (apol_str_append(&s, &len, "\n") < 0) {
00160                         ERR(diff, "%s", strerror(errno));
00161                         return NULL;
00162                 }
00163         }
00164         for (i = 0; i < apol_vector_get_size(range->levels); i++) {
00165                 poldiff_level_t *level = apol_vector_get_element(range->levels, i);
00166                 if ((t = poldiff_level_to_string_brief(diff, level)) == NULL) {
00167                         goto cleanup;
00168                 }
00169                 if (apol_str_appendf(&s, &len, "     %s", t) < 0) {
00170                         ERR(diff, "%s", strerror(errno));
00171                         goto cleanup;
00172                 }
00173                 free(t);
00174                 t = NULL;
00175         }
00176       cleanup:
00177         free(r1);
00178         free(r2);
00179         free(t);
00180         return s;
00181 }

poldiff_range_t* range_create const poldiff_t diff,
const qpol_mls_range_t orig_range,
const qpol_mls_range_t mod_range,
poldiff_form_e  form
 

Allocate and return a poldiff_range_t object.

This will fill in the orig_range and mod_range strings. If the form is modified, then this will allocate the levels vector but leave it empty. Otherwise the levels vector will be filled with the levels that were added/removed.

Parameters:
diff Diff object containing policies.
orig_range Range from original policy, or NULL if there is no original range.
mod_range Range from modified policy, or NULL if there is no modified range.
form Form of the range.
Returns:
An initialized range, or NULL upon error. Caller must call range_destroy() upon the returned value.

Definition at line 183 of file range_diff.c.

References apol_mls_level_get_cats(), apol_mls_level_get_sens(), apol_mls_level_t, apol_mls_range_create_from_qpol_mls_range(), apol_mls_range_get_levels(), apol_mls_range_t, apol_policy_t, apol_str_strdup(), apol_vector_append(), apol_vector_create(), apol_vector_create_from_vector(), apol_vector_create_with_capacity(), apol_vector_destroy(), apol_vector_get_element(), apol_vector_get_size(), apol_vector_t, diff, ERR, level_free(), poldiff::mod_pol, poldiff::orig_pol, POLDIFF_FORM_ADDED, POLDIFF_FORM_REMOVED, poldiff_level_t, poldiff_range_t, poldiff_t, and range_destroy().

Referenced by range_trans_deep_diff(), range_trans_new_diff(), and user_deep_diff_ranges().

00185 {
00186         poldiff_range_t *pr = NULL;
00187         apol_policy_t *p;
00188         apol_mls_range_t *range;
00189         apol_vector_t *levels = NULL;
00190         poldiff_level_t *pl = NULL;
00191         size_t i;
00192         int retval = -1;
00193         if ((pr = calloc(1, sizeof(*pr))) == NULL || (pr->levels = apol_vector_create(level_free)) == NULL) {
00194                 ERR(diff, "%s", strerror(errno));
00195                 goto cleanup;
00196         }
00197         if (orig_range != NULL && (pr->orig_range = apol_mls_range_create_from_qpol_mls_range(diff->orig_pol, orig_range)) == NULL) {
00198                 goto cleanup;
00199         }
00200         if (mod_range != NULL && (pr->mod_range = apol_mls_range_create_from_qpol_mls_range(diff->mod_pol, mod_range)) == NULL) {
00201                 goto cleanup;
00202         }
00203         if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
00204                 p = diff->mod_pol;
00205                 range = pr->mod_range;
00206         } else if (form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_REMOVE_TYPE) {
00207                 p = diff->orig_pol;
00208                 range = pr->orig_range;
00209         } else if (form == POLDIFF_FORM_MODIFIED) {
00210                 /* don't fill in the range's levels here */
00211                 return pr;
00212         } else {
00213                 /* should never get here */
00214                 assert(0);
00215                 return pr;
00216         }
00217         if ((levels = apol_mls_range_get_levels(p, range)) == NULL) {
00218                 goto cleanup;
00219         }
00220         for (i = 0; i < apol_vector_get_size(levels); i++) {
00221                 apol_mls_level_t *l = apol_vector_get_element(levels, i);
00222                 const char *sens = apol_mls_level_get_sens(l);
00223                 const apol_vector_t *cats = apol_mls_level_get_cats(l);
00224                 if ((pl = calloc(1, sizeof(*pl))) == NULL ||
00225                     (pl->name = strdup(sens)) == NULL || (pl->unmodified_cats = apol_vector_create_with_capacity(1, free)) == NULL)
00226                 {
00227                         ERR(diff, "%s", strerror(errno));
00228                         goto cleanup;
00229                 }
00230                 if (form == POLDIFF_FORM_ADDED) {
00231                         if ((pl->added_cats = apol_vector_create_from_vector(cats, apol_str_strdup, NULL, free)) == NULL ||
00232                             (pl->removed_cats = apol_vector_create_with_capacity(1, free)) == NULL) {
00233                                 ERR(diff, "%s", strerror(errno));
00234                                 goto cleanup;
00235                         }
00236                 } else if (form == POLDIFF_FORM_REMOVED) {
00237                         if ((pl->added_cats = apol_vector_create_with_capacity(1, free)) == NULL ||
00238                             (pl->removed_cats = apol_vector_create_from_vector(cats, apol_str_strdup, NULL, free)) == NULL) {
00239                                 ERR(diff, "%s", strerror(errno));
00240                                 goto cleanup;
00241                         }
00242                 }
00243                 if (apol_vector_append(pr->levels, pl) < 0) {
00244                         ERR(diff, "%s", strerror(errno));
00245                         goto cleanup;
00246                 }
00247                 pl = NULL;
00248         }
00249         retval = 0;
00250       cleanup:
00251         apol_vector_destroy(&levels);
00252         if (retval != 0) {
00253                 level_free(pl);
00254                 range_destroy(&pr);
00255                 return NULL;
00256         }
00257         return pr;
00258 }

void range_destroy poldiff_range_t **  range  ) 
 

Deallocate all space for a range, including the pointer itself.

Afterwards set the pointer to NULL.

Parameters:
range Reference to a range to destroy.

Definition at line 260 of file range_diff.c.

References apol_mls_range_destroy(), apol_vector_destroy(), and poldiff_range_t.

Referenced by range_create(), range_trans_deep_diff(), range_trans_free(), user_deep_diff_ranges(), and user_free().

00261 {
00262         if (range != NULL && *range != NULL) {
00263                 apol_mls_range_destroy(&(*range)->orig_range);
00264                 apol_mls_range_destroy(&(*range)->mod_range);
00265                 apol_vector_destroy(&(*range)->levels);
00266                 apol_vector_destroy(&(*range)->min_added_cats);
00267                 apol_vector_destroy(&(*range)->min_removed_cats);
00268                 apol_vector_destroy(&(*range)->min_unmodified_cats);
00269                 free(*range);
00270                 *range = NULL;
00271         }
00272 }

int range_comp_alphabetize const void *  a,
const void *  b,
void *data   __attribute__((unused))
[static]
 

Comparison function for two apol_mls_level_t objects from the same apol_mls_range_t.

Sorts the levels in alphabetical order according to sensitivity.

Definition at line 279 of file range_diff.c.

References apol_mls_level_get_sens(), and apol_mls_level_t.

Referenced by range_deep_diff().

00280 {
00281         const apol_mls_level_t *l1 = a;
00282         const apol_mls_level_t *l2 = b;
00283         const char *sens1 = apol_mls_level_get_sens(l1);
00284         const char *sens2 = apol_mls_level_get_sens(l2);
00285         return strcmp(sens1, sens2);
00286 }

int range_comp const void *  a,
const void *  b,
void *  data
[static]
 

Comparison function for two levels from the same poldiff_range_t.

Sorts the levels by form; within each form sort them by policy order.

Definition at line 293 of file range_diff.c.

References diff, poldiff_level::form, poldiff::mod_qpol, poldiff_level::name, poldiff::orig_qpol, poldiff_level_t, poldiff_t, qpol_level_get_value(), qpol_level_t, qpol_policy_get_level_by_name(), and qpol_policy_t.

Referenced by range_deep_diff().

00294 {
00295         const poldiff_level_t *l1 = a;
00296         const poldiff_level_t *l2 = b;
00297         poldiff_t *diff = data;
00298         qpol_policy_t *q;
00299         const qpol_level_t *ql1, *ql2;
00300         uint32_t v1, v2;
00301         if (l1->form != l2->form) {
00302                 return l1->form - l2->form;
00303         }
00304         if (l1->form == POLDIFF_FORM_ADDED) {
00305                 q = diff->mod_qpol;
00306         } else {
00307                 q = diff->orig_qpol;
00308         }
00309         qpol_policy_get_level_by_name(q, l1->name, &ql1);
00310         qpol_policy_get_level_by_name(q, l2->name, &ql2);
00311         qpol_level_get_value(q, ql1, &v1);
00312         qpol_level_get_value(q, ql2, &v2);
00313         assert(v1 != 0 && v2 != 0);
00314         return v1 - v2;
00315 }

int range_deep_diff poldiff_t diff,
poldiff_range_t range
 

Calculate the differences between two ranges (that are stored within the poldiff_range_t object).

This involves two things: changes in the expanded levels, and changes to minimum category sets. If differences are found then the range's levels vector will be filled with those differences.

Parameters:
diff Diff object containing policies.
range Range object to diff.
Returns:
Greater than zero if a diff was found, zero if none found, less than zero for errors.

Definition at line 317 of file range_diff.c.

References apol_mls_level_get_cats(), apol_mls_level_get_sens(), apol_mls_level_t, apol_mls_range_get_levels(), apol_mls_range_get_low(), apol_vector_append(), apol_vector_destroy(), apol_vector_get_element(), apol_vector_get_size(), apol_vector_sort(), apol_vector_t, diff, level_create_from_apol_mls_level(), level_deep_diff_apol_mls_levels(), level_deep_diff_cats(), level_free(), poldiff_range::levels, poldiff_range::min_added_cats, poldiff_range::min_removed_cats, poldiff_range::min_unmodified_cats, poldiff::mod_pol, poldiff_range::mod_range, poldiff::orig_pol, poldiff_range::orig_range, POLDIFF_FORM_ADDED, POLDIFF_FORM_REMOVED, poldiff_level_t, poldiff_range_t, poldiff_t, range_comp(), and range_comp_alphabetize().

Referenced by range_trans_deep_diff(), and user_deep_diff_ranges().

00318 {
00319         apol_vector_t *orig_levels = NULL, *mod_levels = NULL;
00320         apol_vector_t *added = NULL, *removed = NULL, *unmodified = NULL;
00321         apol_mls_level_t *l1, *l2;
00322         poldiff_level_t *pl1, *pl2;
00323         size_t i, j;
00324         int retval = -1, differences_found = 0, compval;
00325         if ((orig_levels = apol_mls_range_get_levels(diff->orig_pol, range->orig_range)) == NULL ||
00326             (mod_levels = apol_mls_range_get_levels(diff->mod_pol, range->mod_range)) == NULL) {
00327                 goto cleanup;
00328         }
00329         apol_vector_sort(orig_levels, range_comp_alphabetize, NULL);
00330         apol_vector_sort(mod_levels, range_comp_alphabetize, NULL);
00331         for (i = j = 0; i < apol_vector_get_size(orig_levels);) {
00332                 if (j >= apol_vector_get_size(mod_levels))
00333                         break;
00334                 l1 = (apol_mls_level_t *) apol_vector_get_element(orig_levels, i);
00335                 l2 = (apol_mls_level_t *) apol_vector_get_element(mod_levels, j);
00336                 pl1 = pl2 = NULL;
00337                 const char *sens1 = apol_mls_level_get_sens(l1);
00338                 const char *sens2 = apol_mls_level_get_sens(l2);
00339                 compval = strcmp(sens1, sens2);
00340                 if (compval < 0) {
00341                         if ((pl1 = level_create_from_apol_mls_level(l1, POLDIFF_FORM_REMOVED)) == NULL
00342                             || apol_vector_append(range->levels, pl1) < 0) {
00343                                 level_free(pl1);
00344                                 goto cleanup;
00345                         }
00346                         differences_found = 1;
00347                         i++;
00348                 } else if (compval > 0) {
00349                         if ((pl2 = level_create_from_apol_mls_level(l2, POLDIFF_FORM_ADDED)) == NULL
00350                             || apol_vector_append(range->levels, pl2) < 0) {
00351                                 level_free(pl2);
00352                                 goto cleanup;
00353                         }
00354                         differences_found = 1;
00355                         j++;
00356                 } else {
00357                         if (level_deep_diff_apol_mls_levels(diff, l1, l2, &pl1, &pl2) < 0) {
00358                                 goto cleanup;
00359                         }
00360                         assert(pl2 == NULL);
00361                         if (pl1 != NULL) {
00362                                 if (apol_vector_append(range->levels, pl1) < 0) {
00363                                         level_free(pl1);
00364                                         goto cleanup;
00365                                 }
00366                                 differences_found = 1;
00367                         }
00368                         i++;
00369                         j++;
00370                 }
00371         }
00372         for (; i < apol_vector_get_size(orig_levels); i++) {
00373                 l1 = (apol_mls_level_t *) apol_vector_get_element(orig_levels, i);
00374                 if ((pl1 = level_create_from_apol_mls_level(l1, POLDIFF_FORM_REMOVED)) == NULL
00375                     || apol_vector_append(range->levels, pl1) < 0) {
00376                         level_free(pl1);
00377                         goto cleanup;
00378                 }
00379                 differences_found = 1;
00380         }
00381         for (; j < apol_vector_get_size(mod_levels); j++) {
00382                 l2 = (apol_mls_level_t *) apol_vector_get_element(mod_levels, j);
00383                 if ((pl2 = level_create_from_apol_mls_level(l2, POLDIFF_FORM_ADDED)) == NULL
00384                     || apol_vector_append(range->levels, pl2) < 0) {
00385                         level_free(pl2);
00386                         goto cleanup;
00387                 }
00388                 differences_found = 1;
00389         }
00390         /* now check minimum category sets */
00391         const apol_mls_level_t *low1 = apol_mls_range_get_low(range->orig_range);
00392         const apol_vector_t *cats1 = apol_mls_level_get_cats(low1);
00393         const apol_mls_level_t *low2 = apol_mls_range_get_low(range->mod_range);
00394         const apol_vector_t *cats2 = apol_mls_level_get_cats(low2);
00395         compval = level_deep_diff_cats(diff, cats1, cats2, &added, &removed, &unmodified);
00396         if (compval < 0) {
00397                 goto cleanup;
00398         } else if (compval > 0) {
00399                 differences_found = 1;
00400                 range->min_added_cats = added;
00401                 range->min_removed_cats = removed;
00402                 range->min_unmodified_cats = unmodified;
00403                 added = NULL;
00404                 removed = NULL;
00405                 unmodified = NULL;
00406         }
00407         if (differences_found) {
00408                 apol_vector_sort(range->levels, range_comp, diff);
00409                 retval = 1;
00410         } else {
00411                 retval = 0;
00412         }
00413       cleanup:
00414         apol_vector_destroy(&orig_levels);
00415         apol_vector_destroy(&mod_levels);
00416         apol_vector_destroy(&added);
00417         apol_vector_destroy(&removed);
00418         apol_vector_destroy(&unmodified);
00419         return retval;
00420 }