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 	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 */
dbase_join_flush(semanage_handle_t * handle,dbase_join_t * dbase)169 static 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 
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)238 int 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 */
dbase_join_release(dbase_join_t * dbase)267 void 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 */
275 dbase_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