diff options
Diffstat (limited to 'libpoldiff/src/level_diff.c')
-rw-r--r-- | libpoldiff/src/level_diff.c | 769 |
1 files changed, 769 insertions, 0 deletions
diff --git a/libpoldiff/src/level_diff.c b/libpoldiff/src/level_diff.c new file mode 100644 index 0000000..b384519 --- /dev/null +++ b/libpoldiff/src/level_diff.c @@ -0,0 +1,769 @@ +/** + * @file + * Implementation for computing semantic differences in levels. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author 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 + */ + +#include <config.h> + +#include "poldiff_internal.h" + +#include <apol/util.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +struct poldiff_level_summary +{ + size_t num_added; + size_t num_removed; + size_t num_modified; + apol_vector_t *diffs; +}; + +void poldiff_level_get_stats(const poldiff_t * diff, size_t stats[5]) +{ + if (diff == NULL || stats == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return; + } + stats[0] = diff->level_diffs->num_added; + stats[1] = diff->level_diffs->num_removed; + stats[2] = diff->level_diffs->num_modified; + stats[3] = 0; + stats[4] = 0; +} + +const apol_vector_t *poldiff_get_level_vector(const poldiff_t * diff) +{ + if (diff == NULL) { + errno = EINVAL; + return NULL; + } + return diff->level_diffs->diffs; +} + +char *poldiff_level_to_string(const poldiff_t * diff, const void *level) +{ + poldiff_level_t *l = (poldiff_level_t *) level; + size_t num_added, num_removed, len = 0, i; + char *s = NULL, *cat; + if (diff == NULL || level == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + num_added = apol_vector_get_size(l->added_cats); + num_removed = apol_vector_get_size(l->removed_cats); + switch (l->form) { + case POLDIFF_FORM_ADDED: + { + if (apol_str_appendf(&s, &len, "+ %s", l->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_REMOVED: + { + if (apol_str_appendf(&s, &len, "- %s", l->name) < 0) { + break; + } + return s; + } + case POLDIFF_FORM_MODIFIED: + { + if (apol_str_appendf(&s, &len, "* %s (", l->name) < 0) { + break; + } + if (num_added > 0) { + if (apol_str_appendf(&s, &len, "%zd Added %s", num_added, (num_added == 1 ? "Category" : "Categories")) < 0) { + break; + } + } + if (num_removed > 0) { + if (apol_str_appendf + (&s, &len, "%s%zd Removed %s", (num_added > 0 ? ", " : ""), num_removed, + (num_removed == 1 ? "Category" : "Categories")) < 0) { + break; + } + } + if (apol_str_append(&s, &len, ")\n") < 0) { + break; + } + for (i = 0; i < apol_vector_get_size(l->added_cats); i++) { + cat = (char *)apol_vector_get_element(l->added_cats, i); + if (apol_str_appendf(&s, &len, "\t+ %s\n", cat) < 0) { + goto err; + } + } + for (i = 0; i < apol_vector_get_size(l->removed_cats); i++) { + cat = (char *)apol_vector_get_element(l->removed_cats, i); + if (apol_str_appendf(&s, &len, "\t- %s\n", cat) < 0) { + goto err; + } + } + return s; + } + default: + { + ERR(diff, "%s", strerror(ENOTSUP)); + errno = ENOTSUP; + return NULL; + } + } + err: + /* if this is reached then an error occurred */ + free(s); + ERR(diff, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return NULL; +} + +char *poldiff_level_to_string_brief(const poldiff_t * diff, const poldiff_level_t * level) +{ + char *s = NULL, t, *cat, *sep = ""; + int show_cat_sym = 0; + size_t len = 0, i; + switch (level->form) { + case POLDIFF_FORM_ADDED: + t = '+'; + break; + case POLDIFF_FORM_REMOVED: + t = '-'; + break; + case POLDIFF_FORM_MODIFIED: + t = '*'; + show_cat_sym = 1; + break; + default: + /* don't show unmodified levels */ + if ((s = strdup("")) == NULL) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + return s; + } + if (apol_str_appendf(&s, &len, "%c %s", t, level->name) < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + if ((level->unmodified_cats != NULL && apol_vector_get_size(level->unmodified_cats) > 0) || + (level->added_cats != NULL && apol_vector_get_size(level->added_cats) > 0) || + (level->removed_cats != NULL && apol_vector_get_size(level->removed_cats) > 0)) { + if (apol_str_append(&s, &len, " : ") < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + for (i = 0; level->unmodified_cats != NULL && i < apol_vector_get_size(level->unmodified_cats); i++) { + cat = apol_vector_get_element(level->unmodified_cats, i); + if (apol_str_appendf(&s, &len, "%s%s", sep, cat) < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + sep = ","; + } + for (i = 0; level->added_cats != NULL && i < apol_vector_get_size(level->added_cats); i++) { + cat = apol_vector_get_element(level->added_cats, i); + if (apol_str_appendf(&s, &len, "%s%s%s", sep, (show_cat_sym ? "+" : ""), cat) < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + sep = ","; + } + for (i = 0; level->removed_cats != NULL && i < apol_vector_get_size(level->removed_cats); i++) { + cat = apol_vector_get_element(level->removed_cats, i); + if (apol_str_appendf(&s, &len, "%s%s%s", sep, (show_cat_sym ? "-" : ""), cat) < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + sep = ","; + } + } + if (apol_str_append(&s, &len, "\n") < 0) { + ERR(diff, "%s", strerror(errno)); + return NULL; + } + return s; +} + +const char *poldiff_level_get_name(const poldiff_level_t * level) +{ + if (level == NULL) { + errno = EINVAL; + return NULL; + } + + return level->name; +} + +poldiff_form_e poldiff_level_get_form(const void *level) +{ + if (level == NULL) { + errno = EINVAL; + return POLDIFF_FORM_NONE; + } + + return ((const poldiff_level_t *)level)->form; +} + +const apol_vector_t *poldiff_level_get_added_cats(const poldiff_level_t * level) +{ + if (level == NULL) { + errno = EINVAL; + return NULL; + } + return level->added_cats; +} + +const apol_vector_t *poldiff_level_get_removed_cats(const poldiff_level_t * level) +{ + if (level == NULL) { + errno = EINVAL; + return NULL; + } + + return level->removed_cats; +} + +const apol_vector_t *poldiff_level_get_unmodified_cats(const poldiff_level_t * level) +{ + if (level == NULL) { + errno = EINVAL; + return NULL; + } + + return level->unmodified_cats; +} + +poldiff_level_summary_t *level_create(void) +{ + poldiff_level_summary_t *ls = calloc(1, sizeof(poldiff_level_summary_t)); + if (ls == NULL) + return NULL; + if ((ls->diffs = apol_vector_create(level_free)) == NULL) { + level_destroy(&ls); + return NULL; + } + return ls; +} + +void level_destroy(poldiff_level_summary_t ** ls) +{ + if (ls == NULL || *ls == NULL) + return; + apol_vector_destroy(&(*ls)->diffs); + free(*ls); + *ls = NULL; +} + +int level_reset(poldiff_t * diff) +{ + int error = 0; + + if (diff == NULL) { + ERR(diff, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + level_destroy(&diff->level_diffs); + diff->level_diffs = level_create(); + if (diff->level_diffs == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + errno = error; + return -1; + } + + return 0; +} + +/** + * Comparison function for two levels from the same policy. + */ +static int level_name_comp(const void *x, const void *y, void *arg) +{ + const qpol_level_t *s1 = x; + const qpol_level_t *s2 = y; + apol_policy_t *p = arg; + qpol_policy_t *q = apol_policy_get_qpol(p); + const char *name1, *name2; + + if (qpol_level_get_name(q, s1, &name1) < 0 || qpol_level_get_name(q, s2, &name2) < 0) + return 0; + return strcmp(name1, name2); +} + +apol_vector_t *level_get_items(poldiff_t * diff, const apol_policy_t * policy) +{ + qpol_iterator_t *iter = NULL; + apol_vector_t *v = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policy); + int error = 0; + if (qpol_policy_get_level_iter(q, &iter) < 0) { + return NULL; + } + v = apol_vector_create_from_iter(iter, NULL); + if (v == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + qpol_iterator_destroy(&iter); + errno = error; + return NULL; + } + qpol_iterator_destroy(&iter); + apol_vector_sort(v, level_name_comp, (void *)policy); + return v; +} + +int level_comp(const void *x, const void *y, const poldiff_t * diff) +{ + const qpol_level_t *l1 = x; + const qpol_level_t *l2 = y; + const char *name1, *name2; + if (qpol_level_get_name(diff->orig_qpol, l1, &name1) < 0 || qpol_level_get_name(diff->mod_qpol, l2, &name2) < 0) { + return 0; + } + return strcmp(name1, name2); +} + +/** + * Allocate and return a new level difference object. + * + * @param diff Policy diff error handler. + * @param form Form of the difference. + * @param name Name of the level that is different. + * + * @return A newly allocated and initialized diff, or NULL upon error. + * The caller is responsible for calling level_free() upon the returned + * value. + */ +static poldiff_level_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name) +{ + poldiff_level_t *pl; + int error; + if ((pl = calloc(1, sizeof(*pl))) == NULL || (pl->name = strdup(name)) == NULL || + (pl->added_cats = apol_vector_create(free)) == NULL || + (pl->removed_cats = apol_vector_create(free)) == NULL || (pl->unmodified_cats = apol_vector_create(free)) == NULL) { + error = errno; + level_free(pl); + ERR(diff, "%s", strerror(error)); + errno = error; + return NULL; + } + pl->form = form; + return pl; +} + +/** + * Given a level, return a vector of its allowed categories (in the + * form of strings). These will be sorted in policy order. + * + * @param diff Policy diff error handler. + * @param p Policy from which the level came. + * @param level Level whose categories to get. + * + * @return Vector of category strings for the level. The caller is + * responsible for calling apol_vector_destroy(). On error, return + * NULL. + */ +static apol_vector_t *level_get_cats(const poldiff_t * diff, const apol_policy_t * p, const qpol_level_t * level) +{ + qpol_iterator_t *iter = NULL; + const qpol_cat_t *cat; + const char *cat_name; + apol_vector_t *v = NULL; + qpol_policy_t *q = apol_policy_get_qpol(p); + int retval = -1, error = 0; + + if ((v = apol_vector_create(NULL)) == NULL) { + ERR(diff, "%s", strerror(errno)); + goto cleanup; + } + if (qpol_level_get_cat_iter(q, level, &iter) < 0) { + goto cleanup; + } + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&cat) < 0 || qpol_cat_get_name(q, cat, &cat_name)) { + error = errno; + goto cleanup; + } + if (apol_vector_append(v, (void *)cat_name) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + if (retval < 0) { + apol_vector_destroy(&v); + errno = error; + return NULL; + } + return v; +} + +int level_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item) +{ + const qpol_level_t *l = item; + const char *name = NULL; + poldiff_level_t *pl = NULL; + apol_policy_t *p; + qpol_policy_t *q; + apol_vector_t *v = NULL; + int error = 0, retval = -1; + if (form == POLDIFF_FORM_ADDED) { + p = diff->mod_pol; + q = diff->mod_qpol; + } else { + p = diff->orig_pol; + q = diff->orig_qpol; + } + if (qpol_level_get_name(q, l, &name) < 0 || (pl = make_diff(diff, form, name)) == NULL) { + error = errno; + goto cleanup; + } + if ((v = level_get_cats(diff, p, l)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if (form == POLDIFF_FORM_ADDED) { + apol_vector_destroy(&pl->added_cats); + if ((pl->added_cats = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } else if (form == POLDIFF_FORM_REMOVED) { + apol_vector_destroy(&pl->removed_cats); + if ((pl->removed_cats = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + } + if (apol_vector_append(diff->level_diffs->diffs, pl) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + if (form == POLDIFF_FORM_ADDED) { + diff->level_diffs->num_added++; + } else { + diff->level_diffs->num_removed++; + } + retval = 0; + cleanup: + apol_vector_destroy(&v); + if (retval < 0) { + level_free(pl); + errno = error; + } + return retval; +} + +/** + * Comparison function for two category names from the same policy. + * + * @param a Name of a category. + * @param b Name of another category. + * @param data qpol policy from which the categories originate. + * + * @return Less than zero, zero, or greater than zero based upon the + * categories' order within the policy. + */ +static int level_cat_comp(const void *a, const void *b, void *data) +{ + const char *name1 = (const char *)a; + const char *name2 = (const char *)b; + qpol_policy_t *q = (qpol_policy_t *) data; + const qpol_cat_t *cat1, *cat2; + qpol_policy_get_cat_by_name(q, name1, &cat1); + qpol_policy_get_cat_by_name(q, name2, &cat2); + assert(cat1 != NULL && cat2 != NULL); + uint32_t val1, val2; + qpol_cat_get_value(q, cat1, &val1); + qpol_cat_get_value(q, cat2, &val2); + return val1 - val2; +} + +int level_deep_diff(poldiff_t * diff, const void *x, const void *y) +{ + const qpol_level_t *l1 = x; + const qpol_level_t *l2 = y; + apol_vector_t *v1 = NULL, *v2 = NULL; + apol_vector_t *added = NULL, *removed = NULL, *unmodified = NULL; + const char *name; + poldiff_level_t *l = NULL; + int retval = -1, error = 0, compval; + + if (qpol_level_get_name(diff->orig_qpol, l1, &name) < 0 || + (v1 = level_get_cats(diff, diff->orig_pol, l1)) == NULL || (v2 = level_get_cats(diff, diff->mod_pol, l2)) == NULL) { + error = errno; + goto cleanup; + } + apol_vector_sort(v1, apol_str_strcmp, NULL); + apol_vector_sort(v2, apol_str_strcmp, NULL); + compval = level_deep_diff_cats(diff, v1, v2, &added, &removed, &unmodified); + if (compval < 0) { + error = errno; + goto cleanup; + } else if (compval > 0) { + if ((l = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) { + error = errno; + goto cleanup; + } + apol_vector_destroy(&l->added_cats); + apol_vector_destroy(&l->removed_cats); + apol_vector_destroy(&l->unmodified_cats); + if ((l->added_cats = apol_vector_create_from_vector(added, apol_str_strdup, NULL, free)) == NULL || + (l->removed_cats = apol_vector_create_from_vector(removed, apol_str_strdup, NULL, free)) == NULL || + (l->unmodified_cats = apol_vector_create_from_vector(unmodified, apol_str_strdup, NULL, free)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + apol_vector_sort(l->removed_cats, level_cat_comp, diff->orig_qpol); + apol_vector_sort(l->added_cats, level_cat_comp, diff->mod_qpol); + apol_vector_sort(l->unmodified_cats, level_cat_comp, diff->orig_qpol); + if (apol_vector_append(diff->level_diffs->diffs, l) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + diff->level_diffs->num_modified++; + } + retval = 0; + cleanup: + apol_vector_destroy(&v1); + apol_vector_destroy(&v2); + apol_vector_destroy(&added); + apol_vector_destroy(&removed); + apol_vector_destroy(&unmodified); + if (retval != 0) { + level_free(l); + } + errno = error; + return retval; +} + +poldiff_level_t *level_create_from_apol_mls_level(const apol_mls_level_t * level, poldiff_form_e form) +{ + const char *sens = apol_mls_level_get_sens(level); + const apol_vector_t *cats = apol_mls_level_get_cats(level); + poldiff_level_t *pl = NULL; + apol_vector_t **target; + if ((pl = calloc(1, sizeof(*pl))) == NULL || + (pl->name = strdup(sens)) == NULL || (pl->unmodified_cats = apol_vector_create_with_capacity(1, free)) == NULL) { + level_free(pl); + return NULL;; + } + pl->form = form; + if (form == POLDIFF_FORM_ADDED) { + if ((pl->removed_cats = apol_vector_create_with_capacity(1, free)) == NULL) { + level_free(pl); + return NULL; + } + target = &pl->added_cats; + } else if (form == POLDIFF_FORM_REMOVED) { + if ((pl->added_cats = apol_vector_create_with_capacity(1, free)) == NULL) { + level_free(pl); + return NULL; + } + target = &pl->removed_cats; + } else { + if ((pl->added_cats = apol_vector_create_with_capacity(1, free)) == NULL || + (pl->removed_cats = apol_vector_create_with_capacity(1, free)) == NULL) { + level_free(pl); + return NULL; + } + return pl; + } + if ((*target = apol_vector_create_from_vector(cats, apol_str_strdup, NULL, free)) == NULL) { + level_free(pl); + return NULL; + } + return pl; +} + +void level_free(void *elem) +{ + poldiff_level_t *s = elem; + if (!elem) + return; + free(s->name); + apol_vector_destroy(&s->added_cats); + apol_vector_destroy(&s->removed_cats); + apol_vector_destroy(&s->unmodified_cats); + free(s); +} + +int level_deep_diff_apol_mls_levels(poldiff_t * diff, const apol_mls_level_t * level1, const apol_mls_level_t * level2, + poldiff_level_t ** orig_pl, poldiff_level_t ** mod_pl) +{ + poldiff_level_t *u1 = NULL, *u2 = NULL; + apol_vector_t *added = NULL, *removed = NULL, *unmodified = NULL; + const char *sens1 = apol_mls_level_get_sens(level1); + const apol_vector_t *cats1 = apol_mls_level_get_cats(level1); + const char *sens2 = apol_mls_level_get_sens(level2); + const apol_vector_t *cats2 = apol_mls_level_get_cats(level2); + int retval = -1, compval; + + *orig_pl = *mod_pl = NULL; + if (strcmp(sens1, sens2) != 0) { + /* sensitivities do not match, so don't check categories */ + if ((u1 = make_diff(diff, POLDIFF_FORM_REMOVED, sens1)) == NULL || + (u2 = make_diff(diff, POLDIFF_FORM_ADDED, sens2)) == NULL) { + ERR(diff, "%s", strerror(errno)); + level_free(u1); + level_free(u2); + return -1; + } + apol_vector_destroy(&u1->removed_cats); + apol_vector_destroy(&u2->added_cats); + if ((u1->removed_cats = apol_vector_create_from_vector(cats1, apol_str_strdup, NULL, free)) == NULL || + (u2->added_cats = apol_vector_create_from_vector(cats2, apol_str_strdup, NULL, free)) == NULL) { + ERR(diff, "%s", strerror(errno)); + level_free(u1); + level_free(u2); + return -1; + } + apol_vector_sort(u1->removed_cats, level_cat_comp, diff->orig_qpol); + apol_vector_sort(u2->added_cats, level_cat_comp, diff->mod_qpol); + *orig_pl = u1; + *mod_pl = u2; + return 0; + } + + compval = level_deep_diff_cats(diff, cats1, cats2, &added, &removed, &unmodified); + if (compval < 0) { + goto cleanup; + } else if (compval > 0) { + if ((u1 = calloc(1, sizeof(*u1))) == NULL || (u1->name = strdup(sens1)) == NULL || + (u1->added_cats = apol_vector_create_from_vector(added, apol_str_strdup, NULL, free)) == NULL || + (u1->removed_cats = apol_vector_create_from_vector(removed, apol_str_strdup, NULL, free)) == NULL || + (u1->unmodified_cats = apol_vector_create_from_vector(unmodified, apol_str_strdup, NULL, free)) == NULL) { + ERR(diff, "%s", strerror(errno)); + level_free(u1); + goto cleanup; + } + apol_vector_sort(u1->added_cats, level_cat_comp, diff->mod_qpol); + apol_vector_sort(u1->removed_cats, level_cat_comp, diff->orig_qpol); + apol_vector_sort(u1->unmodified_cats, level_cat_comp, diff->orig_qpol); + u1->form = POLDIFF_FORM_MODIFIED; + *orig_pl = u1; + } + retval = 0; + cleanup: + apol_vector_destroy(&added); + apol_vector_destroy(&removed); + apol_vector_destroy(&unmodified); + return retval; +} + +int level_deep_diff_cats(poldiff_t * diff, const apol_vector_t * v1, const apol_vector_t * v2, apol_vector_t ** added, + apol_vector_t ** removed, apol_vector_t ** unmodified) +{ + size_t i, j; + char *cat1, *cat2; + int compval, retval = -1, error = 0; + *added = *removed = *unmodified = NULL; + if ((*added = apol_vector_create(free)) == NULL || + (*removed = apol_vector_create(free)) == NULL || (*unmodified = apol_vector_create(free)) == NULL) { + error = errno; + ERR(diff, "%s", strerror(error)); + goto cleanup; + } + for (i = j = 0; i < apol_vector_get_size(v1);) { + if (j >= apol_vector_get_size(v2)) { + break; + } + cat1 = (char *)apol_vector_get_element(v1, i); + cat2 = (char *)apol_vector_get_element(v2, j); + compval = strcmp(cat1, cat2); + if (compval < 0) { + if ((cat1 = strdup(cat1)) == NULL || apol_vector_append(*removed, cat1) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + free(cat1); + goto cleanup; + } + i++; + } else if (compval > 0) { + if ((cat2 = strdup(cat2)) == NULL || apol_vector_append(*added, cat2) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + free(cat2); + goto cleanup; + } + j++; + } else { + if ((cat1 = strdup(cat1)) == NULL || apol_vector_append(*unmodified, cat1) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + free(cat1); + goto cleanup; + } + i++; + j++; + } + } + for (; i < apol_vector_get_size(v1); i++) { + cat1 = (char *)apol_vector_get_element(v1, i); + if ((cat1 = strdup(cat1)) == NULL || apol_vector_append(*removed, cat1) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + free(cat1); + goto cleanup; + } + } + for (; j < apol_vector_get_size(v2); j++) { + cat2 = (char *)apol_vector_get_element(v2, j); + if ((cat2 = strdup(cat2)) == NULL || apol_vector_append(*added, cat2) < 0) { + error = errno; + ERR(diff, "%s", strerror(error)); + free(cat2); + goto cleanup; + } + } + if (apol_vector_get_size(*added) > 0 || apol_vector_get_size(*removed) > 0) { + retval = 1; + } else { + retval = 0; + } + cleanup: + if (retval <= 0) { + /* if no differences found, then destroy all vectors */ + apol_vector_destroy(added); + apol_vector_destroy(removed); + apol_vector_destroy(unmodified); + } + if (retval < 0) { + error = errno; + } + return retval; +} |