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
8 struct dbase_join;
9 typedef 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 */
21 struct 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
dbase_join_cache(semanage_handle_t * handle,dbase_join_t * dbase)37 static 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 if (rcount1 > 0) {
81 qsort(records1, rcount1, sizeof(record1_t *),
82 (int (*)(const void *, const void *))rtable1->compare2_qsort);
83 }
84 if (rcount2 > 0) {
85 qsort(records2, rcount2, sizeof(record2_t *),
86 (int (*)(const void *, const void *))rtable2->compare2_qsort);
87 }
88
89 /* Now merge into this dbase */
90 while (i < rcount1 || j < rcount2) {
91 int rc;
92
93 /* End of one list, or the other */
94 if (i == rcount1)
95 rc = -1;
96 else if (j == rcount2)
97 rc = 1;
98
99 /* Still more records to go, compare them */
100 else {
101 if (rtable1->key_extract(handle, records1[i], &rkey) <
102 0)
103 goto err;
104
105 rc = rtable2->compare(records2[j], rkey);
106
107 rtable->key_free(rkey);
108 rkey = NULL;
109 }
110
111 /* Missing record1 data */
112 if (rc < 0) {
113 if (rjtable->join(handle, NULL,
114 records2[j], &record) < 0)
115 goto err;
116 j++;
117 }
118
119 /* Missing record2 data */
120 else if (rc > 0) {
121 if (rjtable->join(handle, records1[i],
122 NULL, &record) < 0)
123 goto err;
124 i++;
125 }
126
127 /* Both records available */
128 else {
129 if (rjtable->join(handle, records1[i],
130 records2[j], &record) < 0)
131 goto err;
132
133 i++;
134 j++;
135 }
136
137 /* Add result record to database */
138 if (dbase_llist_cache_prepend(handle, &dbase->llist, record) <
139 0)
140 goto err;
141
142 rtable->free(record);
143 record = NULL;
144 }
145
146 /* Update cache serial */
147 if (dbase_llist_set_serial(handle, &dbase->llist) < 0)
148 goto err;
149
150 for (i = 0; i < rcount1; i++)
151 rtable1->free(records1[i]);
152 for (i = 0; i < rcount2; i++)
153 rtable2->free(records2[i]);
154 free(records1);
155 free(records2);
156 return STATUS_SUCCESS;
157
158 err:
159 ERR(handle, "could not cache join database");
160 for (i = 0; i < rcount1; i++)
161 rtable1->free(records1[i]);
162 for (i = 0; i < rcount2; i++)
163 rtable2->free(records2[i]);
164 free(records1);
165 free(records2);
166 rtable->key_free(rkey);
167 rtable->free(record);
168 dbase_llist_drop_cache(&dbase->llist);
169 return STATUS_ERR;
170 }
171
172 /* Flush database */
dbase_join_flush(semanage_handle_t * handle,dbase_join_t * dbase)173 static int dbase_join_flush(semanage_handle_t * handle, dbase_join_t * dbase)
174 {
175
176 /* Extract all the object tables information */
177 dbase_t *dbase1 = dbase->join1->dbase;
178 dbase_t *dbase2 = dbase->join2->dbase;
179 dbase_table_t *dtable1 = dbase->join1->dtable;
180 dbase_table_t *dtable2 = dbase->join2->dtable;
181 record_table_t *rtable = dbase_llist_get_rtable(&dbase->llist);
182 record_join_table_t *rjtable = dbase->rjtable;
183 record_table_t *rtable1 = dtable1->get_rtable(dbase1);
184 record_table_t *rtable2 = dtable2->get_rtable(dbase2);
185
186 cache_entry_t *ptr;
187 record_key_t *rkey = NULL;
188 record1_t *record1 = NULL;
189 record2_t *record2 = NULL;
190
191 /* No effect of flush */
192 if (!dbase_llist_is_modified(&dbase->llist))
193 return STATUS_SUCCESS;
194
195 /* Then clear all records from the cache.
196 * This is *not* the same as dropping the cache - it's an explicit
197 * request to delete all current records. We need to do
198 * this because we don't store delete deltas for the join,
199 * so we must re-add all records from scratch */
200 if (dtable1->clear(handle, dbase1) < 0)
201 goto err;
202 if (dtable2->clear(handle, dbase2) < 0)
203 goto err;
204
205 /* For each record, split, and add parts into their corresponding databases */
206 for (ptr = dbase->llist.cache_tail; ptr != NULL; ptr = ptr->prev) {
207
208 if (rtable->key_extract(handle, ptr->data, &rkey) < 0)
209 goto err;
210
211 if (rjtable->split(handle, ptr->data, &record1, &record2) < 0)
212 goto err;
213
214 if (dtable1->add(handle, dbase1, rkey, record1) < 0)
215 goto err;
216
217 if (dtable2->add(handle, dbase2, rkey, record2) < 0)
218 goto err;
219
220 rtable->key_free(rkey);
221 rtable1->free(record1);
222 rtable2->free(record2);
223 rkey = NULL;
224 record1 = NULL;
225 record2 = NULL;
226 }
227
228 /* Note that this function does not flush the child databases, it
229 * leaves that decision up to higher-level code */
230
231 dbase_llist_set_modified(&dbase->llist, 0);
232 return STATUS_SUCCESS;
233
234 err:
235 ERR(handle, "could not flush join database");
236 rtable->key_free(rkey);
237 rtable1->free(record1);
238 rtable2->free(record2);
239 return STATUS_ERR;
240 }
241
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)242 int dbase_join_init(semanage_handle_t * handle,
243 record_table_t * rtable,
244 record_join_table_t * rjtable,
245 dbase_config_t * join1,
246 dbase_config_t * join2, dbase_t ** dbase)
247 {
248
249 dbase_join_t *tmp_dbase = malloc(sizeof(dbase_join_t));
250
251 if (!tmp_dbase)
252 goto omem;
253
254 dbase_llist_init(&tmp_dbase->llist, rtable, &SEMANAGE_JOIN_DTABLE);
255
256 tmp_dbase->rjtable = rjtable;
257 tmp_dbase->join1 = join1;
258 tmp_dbase->join2 = join2;
259
260 *dbase = tmp_dbase;
261
262 return STATUS_SUCCESS;
263
264 omem:
265 ERR(handle, "out of memory, could not initialize join database");
266 free(tmp_dbase);
267 return STATUS_ERR;
268 }
269
270 /* Release dbase resources */
dbase_join_release(dbase_join_t * dbase)271 void dbase_join_release(dbase_join_t * dbase)
272 {
273
274 dbase_llist_drop_cache(&dbase->llist);
275 free(dbase);
276 }
277
278 /* JOIN dbase - method table implementation */
279 dbase_table_t SEMANAGE_JOIN_DTABLE = {
280
281 /* Cache/Transactions */
282 .cache = dbase_join_cache,
283 .drop_cache = (void *)dbase_llist_drop_cache,
284 .flush = dbase_join_flush,
285 .is_modified = (void *)dbase_llist_is_modified,
286
287 /* Database API */
288 .iterate = (void *)dbase_llist_iterate,
289 .exists = (void *)dbase_llist_exists,
290 .list = (void *)dbase_llist_list,
291 .add = (void *)dbase_llist_add,
292 .set = (void *)dbase_llist_set,
293 .del = (void *)dbase_llist_del,
294 .clear = (void *)dbase_llist_clear,
295 .modify = (void *)dbase_llist_modify,
296 .query = (void *)dbase_llist_query,
297 .count = (void *)dbase_llist_count,
298
299 /* Polymorphism */
300 .get_rtable = (void *)dbase_llist_get_rtable
301 };
302