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