/* Copyright (C) 2005 Red Hat, Inc. */ /* Object: dbase_join_t (Join) * Extends: dbase_llist_t (Linked List) * Implements: dbase_t (Database) */ struct dbase_join; typedef struct dbase_join dbase_t; #define DBASE_DEFINED #include #include "user_internal.h" #include "debug.h" #include "handle.h" #include "database_join.h" #include "database_llist.h" /* JOIN dbase */ struct dbase_join { /* Parent object - must always be * the first field - here we are using * a linked list to store the records */ dbase_llist_t llist; /* Backing databases - for each * thing being joined */ dbase_config_t *join1; dbase_config_t *join2; /* JOIN extension */ record_join_table_t *rjtable; }; static int dbase_join_cache(semanage_handle_t * handle, dbase_join_t * dbase) { /* Extract all the object tables information */ dbase_t *dbase1 = dbase->join1->dbase; dbase_t *dbase2 = dbase->join2->dbase; dbase_table_t *dtable1 = dbase->join1->dtable; dbase_table_t *dtable2 = dbase->join2->dtable; record_table_t *rtable = dbase_llist_get_rtable(&dbase->llist); record_join_table_t *rjtable = dbase->rjtable; record_table_t *rtable1 = dtable1->get_rtable(dbase1); record_table_t *rtable2 = dtable2->get_rtable(dbase2); record_key_t *rkey = NULL; record_t *record = NULL; record1_t **records1 = NULL; record2_t **records2 = NULL; unsigned int rcount1 = 0, rcount2 = 0, i = 0, j = 0; /* Already cached */ if (!dbase_llist_needs_resync(handle, &dbase->llist)) return STATUS_SUCCESS; /* Update cache serial */ dbase_llist_cache_init(&dbase->llist); if (dbase_llist_set_serial(handle, &dbase->llist) < 0) goto err; /* First cache any child dbase, which must * be the first thing done when calling dbase * functions internally */ if (dtable1->cache(handle, dbase1) < 0) goto err; if (dtable2->cache(handle, dbase2) < 0) goto err; /* Fetch records */ if (dtable1->list(handle, dbase1, &records1, &rcount1) < 0) goto err; if (dtable2->list(handle, dbase2, &records2, &rcount2) < 0) goto err; /* Sort for quicker merge later */ qsort(records1, rcount1, sizeof(record1_t *), (int (*)(const void *, const void *))rtable1->compare2_qsort); qsort(records2, rcount2, sizeof(record2_t *), (int (*)(const void *, const void *))rtable2->compare2_qsort); /* Now merge into this dbase */ while (i < rcount1 || j < rcount2) { int rc; /* End of one list, or the other */ if (i == rcount1) rc = -1; else if (j == rcount2) rc = 1; /* Still more records to go, compare them */ else { if (rtable1->key_extract(handle, records1[i], &rkey) < 0) goto err; rc = rtable2->compare(records2[j], rkey); rtable->key_free(rkey); rkey = NULL; } /* Missing record1 data */ if (rc < 0) { if (rjtable->join(handle, NULL, records2[j], &record) < 0) goto err; j++; } /* Missing record2 data */ else if (rc > 0) { if (rjtable->join(handle, records1[i], NULL, &record) < 0) goto err; i++; } /* Both records available */ else { if (rjtable->join(handle, records1[i], records2[j], &record) < 0) goto err; i++; j++; } /* Add result record to database */ if (dbase_llist_cache_prepend(handle, &dbase->llist, record) < 0) goto err; rtable->free(record); record = NULL; } /* Update cache serial */ if (dbase_llist_set_serial(handle, &dbase->llist) < 0) goto err; for (i = 0; i < rcount1; i++) rtable1->free(records1[i]); for (i = 0; i < rcount2; i++) rtable2->free(records2[i]); free(records1); free(records2); return STATUS_SUCCESS; err: ERR(handle, "could not cache join database"); for (i = 0; i < rcount1; i++) rtable1->free(records1[i]); for (i = 0; i < rcount2; i++) rtable2->free(records2[i]); free(records1); free(records2); rtable->key_free(rkey); rtable->free(record); dbase_llist_drop_cache(&dbase->llist); return STATUS_ERR; } /* Flush database */ static int dbase_join_flush(semanage_handle_t * handle, dbase_join_t * dbase) { /* Extract all the object tables information */ dbase_t *dbase1 = dbase->join1->dbase; dbase_t *dbase2 = dbase->join2->dbase; dbase_table_t *dtable1 = dbase->join1->dtable; dbase_table_t *dtable2 = dbase->join2->dtable; record_table_t *rtable = dbase_llist_get_rtable(&dbase->llist); record_join_table_t *rjtable = dbase->rjtable; record_table_t *rtable1 = dtable1->get_rtable(dbase1); record_table_t *rtable2 = dtable2->get_rtable(dbase2); cache_entry_t *ptr; record_key_t *rkey = NULL; record1_t *record1 = NULL; record2_t *record2 = NULL; /* No effect of flush */ if (!dbase_llist_is_modified(&dbase->llist)) return STATUS_SUCCESS; /* Then clear all records from the cache. * This is *not* the same as dropping the cache - it's an explicit * request to delete all current records. We need to do * this because we don't store delete deltas for the join, * so we must re-add all records from scratch */ if (dtable1->clear(handle, dbase1) < 0) goto err; if (dtable2->clear(handle, dbase2) < 0) goto err; /* For each record, split, and add parts into their corresponding databases */ for (ptr = dbase->llist.cache_tail; ptr != NULL; ptr = ptr->prev) { if (rtable->key_extract(handle, ptr->data, &rkey) < 0) goto err; if (rjtable->split(handle, ptr->data, &record1, &record2) < 0) goto err; if (dtable1->add(handle, dbase1, rkey, record1) < 0) goto err; if (dtable2->add(handle, dbase2, rkey, record2) < 0) goto err; rtable->key_free(rkey); rtable1->free(record1); rtable2->free(record2); rkey = NULL; record1 = NULL; record2 = NULL; } /* Note that this function does not flush the child databases, it * leaves that decision up to higher-level code */ dbase_llist_set_modified(&dbase->llist, 0); return STATUS_SUCCESS; err: ERR(handle, "could not flush join database"); rtable->key_free(rkey); rtable1->free(record1); rtable2->free(record2); return STATUS_ERR; } int dbase_join_init(semanage_handle_t * handle, record_table_t * rtable, record_join_table_t * rjtable, dbase_config_t * join1, dbase_config_t * join2, dbase_t ** dbase) { dbase_join_t *tmp_dbase = malloc(sizeof(dbase_join_t)); if (!tmp_dbase) goto omem; dbase_llist_init(&tmp_dbase->llist, rtable, &SEMANAGE_JOIN_DTABLE); tmp_dbase->rjtable = rjtable; tmp_dbase->join1 = join1; tmp_dbase->join2 = join2; *dbase = tmp_dbase; return STATUS_SUCCESS; omem: ERR(handle, "out of memory, could not initialize join database"); free(tmp_dbase); return STATUS_ERR; } /* Release dbase resources */ void dbase_join_release(dbase_join_t * dbase) { dbase_llist_drop_cache(&dbase->llist); free(dbase); } /* JOIN dbase - method table implementation */ dbase_table_t SEMANAGE_JOIN_DTABLE = { /* Cache/Transactions */ .cache = dbase_join_cache, .drop_cache = (void *)dbase_llist_drop_cache, .flush = dbase_join_flush, .is_modified = (void *)dbase_llist_is_modified, /* Database API */ .iterate = (void *)dbase_llist_iterate, .exists = (void *)dbase_llist_exists, .list = (void *)dbase_llist_list, .add = (void *)dbase_llist_add, .set = (void *)dbase_llist_set, .del = (void *)dbase_llist_del, .clear = (void *)dbase_llist_clear, .modify = (void *)dbase_llist_modify, .query = (void *)dbase_llist_query, .count = (void *)dbase_llist_count, /* Polymorphism */ .get_rtable = (void *)dbase_llist_get_rtable };