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