1/* Copyright (C) 2005 Red Hat, Inc. */ 2 3/* Object: dbase_join_t (Join) 4 * Extends: dbase_llist_t (Linked List) 5 * Implements: dbase_t (Database) 6 */ 7 8struct dbase_join; 9typedef struct dbase_join dbase_t; 10#define DBASE_DEFINED 11 12#include <stdlib.h> 13 14#include "user_internal.h" 15#include "debug.h" 16#include "handle.h" 17#include "database_join.h" 18#include "database_llist.h" 19 20/* JOIN dbase */ 21struct dbase_join { 22 23 /* Parent object - must always be 24 * the first field - here we are using 25 * a linked list to store the records */ 26 dbase_llist_t llist; 27 28 /* Backing databases - for each 29 * thing being joined */ 30 dbase_config_t *join1; 31 dbase_config_t *join2; 32 33 /* JOIN extension */ 34 record_join_table_t *rjtable; 35}; 36 37static int dbase_join_cache(semanage_handle_t * handle, dbase_join_t * dbase) 38{ 39 40 /* Extract all the object tables information */ 41 dbase_t *dbase1 = dbase->join1->dbase; 42 dbase_t *dbase2 = dbase->join2->dbase; 43 dbase_table_t *dtable1 = dbase->join1->dtable; 44 dbase_table_t *dtable2 = dbase->join2->dtable; 45 record_table_t *rtable = dbase_llist_get_rtable(&dbase->llist); 46 record_join_table_t *rjtable = dbase->rjtable; 47 record_table_t *rtable1 = dtable1->get_rtable(dbase1); 48 record_table_t *rtable2 = dtable2->get_rtable(dbase2); 49 50 record_key_t *rkey = NULL; 51 record_t *record = NULL; 52 record1_t **records1 = NULL; 53 record2_t **records2 = NULL; 54 unsigned int rcount1 = 0, rcount2 = 0, i = 0, j = 0; 55 56 /* Already cached */ 57 if (!dbase_llist_needs_resync(handle, &dbase->llist)) 58 return STATUS_SUCCESS; 59 60 /* Update cache serial */ 61 dbase_llist_cache_init(&dbase->llist); 62 if (dbase_llist_set_serial(handle, &dbase->llist) < 0) 63 goto err; 64 65 /* First cache any child dbase, which must 66 * be the first thing done when calling dbase 67 * functions internally */ 68 if (dtable1->cache(handle, dbase1) < 0) 69 goto err; 70 if (dtable2->cache(handle, dbase2) < 0) 71 goto err; 72 73 /* Fetch records */ 74 if (dtable1->list(handle, dbase1, &records1, &rcount1) < 0) 75 goto err; 76 if (dtable2->list(handle, dbase2, &records2, &rcount2) < 0) 77 goto err; 78 79 /* Sort for quicker merge later */ 80 qsort(records1, rcount1, sizeof(record1_t *), 81 (int (*)(const void *, const void *))rtable1->compare2_qsort); 82 qsort(records2, rcount2, sizeof(record2_t *), 83 (int (*)(const void *, const void *))rtable2->compare2_qsort); 84 85 /* Now merge into this dbase */ 86 while (i < rcount1 || j < rcount2) { 87 int rc; 88 89 /* End of one list, or the other */ 90 if (i == rcount1) 91 rc = -1; 92 else if (j == rcount2) 93 rc = 1; 94 95 /* Still more records to go, compare them */ 96 else { 97 if (rtable1->key_extract(handle, records1[i], &rkey) < 98 0) 99 goto err; 100 101 rc = rtable2->compare(records2[j], rkey); 102 103 rtable->key_free(rkey); 104 rkey = NULL; 105 } 106 107 /* Missing record1 data */ 108 if (rc < 0) { 109 if (rjtable->join(handle, NULL, 110 records2[j], &record) < 0) 111 goto err; 112 j++; 113 } 114 115 /* Missing record2 data */ 116 else if (rc > 0) { 117 if (rjtable->join(handle, records1[i], 118 NULL, &record) < 0) 119 goto err; 120 i++; 121 } 122 123 /* Both records available */ 124 else { 125 if (rjtable->join(handle, records1[i], 126 records2[j], &record) < 0) 127 goto err; 128 129 i++; 130 j++; 131 } 132 133 /* Add result record to database */ 134 if (dbase_llist_cache_prepend(handle, &dbase->llist, record) < 135 0) 136 goto err; 137 138 rtable->free(record); 139 record = NULL; 140 } 141 142 /* Update cache serial */ 143 if (dbase_llist_set_serial(handle, &dbase->llist) < 0) 144 goto err; 145 146 for (i = 0; i < rcount1; i++) 147 rtable1->free(records1[i]); 148 for (i = 0; i < rcount2; i++) 149 rtable2->free(records2[i]); 150 free(records1); 151 free(records2); 152 return STATUS_SUCCESS; 153 154 err: 155 ERR(handle, "could not cache join database"); 156 for (i = 0; i < rcount1; i++) 157 rtable1->free(records1[i]); 158 for (i = 0; i < rcount2; i++) 159 rtable2->free(records2[i]); 160 free(records1); 161 free(records2); 162 rtable->key_free(rkey); 163 rtable->free(record); 164 dbase_llist_drop_cache(&dbase->llist); 165 return STATUS_ERR; 166} 167 168/* Flush database */ 169static int dbase_join_flush(semanage_handle_t * handle, dbase_join_t * dbase) 170{ 171 172 /* Extract all the object tables information */ 173 dbase_t *dbase1 = dbase->join1->dbase; 174 dbase_t *dbase2 = dbase->join2->dbase; 175 dbase_table_t *dtable1 = dbase->join1->dtable; 176 dbase_table_t *dtable2 = dbase->join2->dtable; 177 record_table_t *rtable = dbase_llist_get_rtable(&dbase->llist); 178 record_join_table_t *rjtable = dbase->rjtable; 179 record_table_t *rtable1 = dtable1->get_rtable(dbase1); 180 record_table_t *rtable2 = dtable2->get_rtable(dbase2); 181 182 cache_entry_t *ptr; 183 record_key_t *rkey = NULL; 184 record1_t *record1 = NULL; 185 record2_t *record2 = NULL; 186 187 /* No effect of flush */ 188 if (!dbase_llist_is_modified(&dbase->llist)) 189 return STATUS_SUCCESS; 190 191 /* Then clear all records from the cache. 192 * This is *not* the same as dropping the cache - it's an explicit 193 * request to delete all current records. We need to do 194 * this because we don't store delete deltas for the join, 195 * so we must re-add all records from scratch */ 196 if (dtable1->clear(handle, dbase1) < 0) 197 goto err; 198 if (dtable2->clear(handle, dbase2) < 0) 199 goto err; 200 201 /* For each record, split, and add parts into their corresponding databases */ 202 for (ptr = dbase->llist.cache_tail; ptr != NULL; ptr = ptr->prev) { 203 204 if (rtable->key_extract(handle, ptr->data, &rkey) < 0) 205 goto err; 206 207 if (rjtable->split(handle, ptr->data, &record1, &record2) < 0) 208 goto err; 209 210 if (dtable1->add(handle, dbase1, rkey, record1) < 0) 211 goto err; 212 213 if (dtable2->add(handle, dbase2, rkey, record2) < 0) 214 goto err; 215 216 rtable->key_free(rkey); 217 rtable1->free(record1); 218 rtable2->free(record2); 219 rkey = NULL; 220 record1 = NULL; 221 record2 = NULL; 222 } 223 224 /* Note that this function does not flush the child databases, it 225 * leaves that decision up to higher-level code */ 226 227 dbase_llist_set_modified(&dbase->llist, 0); 228 return STATUS_SUCCESS; 229 230 err: 231 ERR(handle, "could not flush join database"); 232 rtable->key_free(rkey); 233 rtable1->free(record1); 234 rtable2->free(record2); 235 return STATUS_ERR; 236} 237 238int dbase_join_init(semanage_handle_t * handle, 239 record_table_t * rtable, 240 record_join_table_t * rjtable, 241 dbase_config_t * join1, 242 dbase_config_t * join2, dbase_t ** dbase) 243{ 244 245 dbase_join_t *tmp_dbase = malloc(sizeof(dbase_join_t)); 246 247 if (!tmp_dbase) 248 goto omem; 249 250 dbase_llist_init(&tmp_dbase->llist, rtable, &SEMANAGE_JOIN_DTABLE); 251 252 tmp_dbase->rjtable = rjtable; 253 tmp_dbase->join1 = join1; 254 tmp_dbase->join2 = join2; 255 256 *dbase = tmp_dbase; 257 258 return STATUS_SUCCESS; 259 260 omem: 261 ERR(handle, "out of memory, could not initialize join database"); 262 free(tmp_dbase); 263 return STATUS_ERR; 264} 265 266/* Release dbase resources */ 267void dbase_join_release(dbase_join_t * dbase) 268{ 269 270 dbase_llist_drop_cache(&dbase->llist); 271 free(dbase); 272} 273 274/* JOIN dbase - method table implementation */ 275dbase_table_t SEMANAGE_JOIN_DTABLE = { 276 277 /* Cache/Transactions */ 278 .cache = dbase_join_cache, 279 .drop_cache = (void *)dbase_llist_drop_cache, 280 .flush = dbase_join_flush, 281 .is_modified = (void *)dbase_llist_is_modified, 282 283 /* Database API */ 284 .iterate = (void *)dbase_llist_iterate, 285 .exists = (void *)dbase_llist_exists, 286 .list = (void *)dbase_llist_list, 287 .add = (void *)dbase_llist_add, 288 .set = (void *)dbase_llist_set, 289 .del = (void *)dbase_llist_del, 290 .clear = (void *)dbase_llist_clear, 291 .modify = (void *)dbase_llist_modify, 292 .query = (void *)dbase_llist_query, 293 .count = (void *)dbase_llist_count, 294 295 /* Polymorphism */ 296 .get_rtable = (void *)dbase_llist_get_rtable 297}; 298