1 /* Copyright (C) 2005 Red Hat, Inc. */
2 
3 /* Object: dbase_llist_t (Linked List)
4  * Partially Implements: dbase_t (Database)
5  */
6 
7 struct dbase_llist;
8 typedef struct dbase_llist dbase_t;
9 #define DBASE_DEFINED
10 
11 #include <stdlib.h>
12 #include "debug.h"
13 #include "handle.h"
14 #include "database_llist.h"
15 
dbase_llist_needs_resync(semanage_handle_t * handle,dbase_llist_t * dbase)16 int dbase_llist_needs_resync(semanage_handle_t * handle, dbase_llist_t * dbase)
17 {
18 
19 	int cache_serial;
20 
21 	if (dbase->cache_serial < 0)
22 		return 1;
23 
24 	cache_serial = handle->funcs->get_serial(handle);
25 	if (cache_serial < 0)
26 		return 1;
27 
28 	if (cache_serial != dbase->cache_serial) {
29 		dbase_llist_drop_cache(dbase);
30 		dbase->cache_serial = -1;
31 		return 1;
32 	}
33 	return 0;
34 }
35 
36 /* Helper for adding records to the cache */
dbase_llist_cache_prepend(semanage_handle_t * handle,dbase_llist_t * dbase,const record_t * data)37 int dbase_llist_cache_prepend(semanage_handle_t * handle,
38 			      dbase_llist_t * dbase, const record_t * data)
39 {
40 
41 	/* Initialize */
42 	cache_entry_t *entry = (cache_entry_t *) malloc(sizeof(cache_entry_t));
43 	if (entry == NULL)
44 		goto omem;
45 
46 	if (dbase->rtable->clone(handle, data, &entry->data) < 0)
47 		goto err;
48 
49 	entry->prev = NULL;
50 	entry->next = dbase->cache;
51 
52 	/* Link */
53 	if (dbase->cache != NULL)
54 		dbase->cache->prev = entry;
55 	if (dbase->cache_tail == NULL)
56 		dbase->cache_tail = entry;
57 	dbase->cache = entry;
58 	dbase->cache_sz++;
59 	return STATUS_SUCCESS;
60 
61       omem:
62 	ERR(handle, "out of memory");
63 
64       err:
65 	ERR(handle, "could not cache record");
66 	free(entry);
67 	return STATUS_ERR;
68 }
69 
dbase_llist_drop_cache(dbase_llist_t * dbase)70 void dbase_llist_drop_cache(dbase_llist_t * dbase)
71 {
72 
73 	if (dbase->cache_serial < 0)
74 		return;
75 
76 	cache_entry_t *prev, *ptr = dbase->cache;
77 	while (ptr != NULL) {
78 		prev = ptr;
79 		ptr = ptr->next;
80 		dbase->rtable->free(prev->data);
81 		free(prev);
82 	}
83 
84 	dbase->cache_serial = -1;
85 	dbase->modified = 0;
86 }
87 
dbase_llist_set_serial(semanage_handle_t * handle,dbase_llist_t * dbase)88 int dbase_llist_set_serial(semanage_handle_t * handle, dbase_llist_t * dbase)
89 {
90 
91 	int cache_serial = handle->funcs->get_serial(handle);
92 	if (cache_serial < 0) {
93 		ERR(handle, "could not update cache serial");
94 		return STATUS_ERR;
95 	}
96 
97 	dbase->cache_serial = cache_serial;
98 	return STATUS_SUCCESS;
99 }
100 
101 /* Helper for finding records in the cache */
dbase_llist_cache_locate(semanage_handle_t * handle,dbase_llist_t * dbase,const record_key_t * key,cache_entry_t ** entry)102 static int dbase_llist_cache_locate(semanage_handle_t * handle,
103 				    dbase_llist_t * dbase,
104 				    const record_key_t * key,
105 				    cache_entry_t ** entry)
106 {
107 
108 	cache_entry_t *ptr;
109 
110 	/* Implemented in parent */
111 	if (dbase->dtable->cache(handle, dbase) < 0)
112 		goto err;
113 
114 	for (ptr = dbase->cache; ptr != NULL; ptr = ptr->next) {
115 		if (!dbase->rtable->compare(ptr->data, key)) {
116 			*entry = ptr;
117 			return STATUS_SUCCESS;
118 		}
119 	}
120 
121 	return STATUS_NODATA;
122 
123       err:
124 	ERR(handle, "could not complete cache lookup");
125 	return STATUS_ERR;
126 }
127 
dbase_llist_exists(semanage_handle_t * handle,dbase_llist_t * dbase,const record_key_t * key,int * response)128 int dbase_llist_exists(semanage_handle_t * handle,
129 		       dbase_llist_t * dbase,
130 		       const record_key_t * key, int *response)
131 {
132 
133 	cache_entry_t *entry;
134 	int status;
135 
136 	status = dbase_llist_cache_locate(handle, dbase, key, &entry);
137 	if (status < 0)
138 		goto err;
139 
140 	*response = (status != STATUS_NODATA);
141 	return STATUS_SUCCESS;
142 
143       err:
144 	ERR(handle, "could not check if record exists");
145 	return STATUS_ERR;
146 }
147 
dbase_llist_add(semanage_handle_t * handle,dbase_llist_t * dbase,const record_key_t * key,const record_t * data)148 int dbase_llist_add(semanage_handle_t * handle,
149 		    dbase_llist_t * dbase,
150 		    const record_key_t * key __attribute__ ((unused)),
151 			 const record_t * data)
152 {
153 
154 	if (dbase_llist_cache_prepend(handle, dbase, data) < 0)
155 		goto err;
156 
157 	key = NULL;
158 	dbase->modified = 1;
159 	return STATUS_SUCCESS;
160 
161       err:
162 	ERR(handle, "could not add record to the database");
163 	return STATUS_ERR;
164 }
165 
dbase_llist_set(semanage_handle_t * handle,dbase_llist_t * dbase,const record_key_t * key,const record_t * data)166 int dbase_llist_set(semanage_handle_t * handle,
167 		    dbase_llist_t * dbase,
168 		    const record_key_t * key, const record_t * data)
169 {
170 
171 	cache_entry_t *entry;
172 	int status;
173 
174 	status = dbase_llist_cache_locate(handle, dbase, key, &entry);
175 	if (status < 0)
176 		goto err;
177 	if (status == STATUS_NODATA) {
178 		ERR(handle, "record not found in the database");
179 		goto err;
180 	} else {
181 		dbase->rtable->free(entry->data);
182 		if (dbase->rtable->clone(handle, data, &entry->data) < 0)
183 			goto err;
184 	}
185 
186 	dbase->modified = 1;
187 	return STATUS_SUCCESS;
188 
189       err:
190 	ERR(handle, "could not set record value");
191 	return STATUS_ERR;
192 }
193 
dbase_llist_modify(semanage_handle_t * handle,dbase_llist_t * dbase,const record_key_t * key,const record_t * data)194 int dbase_llist_modify(semanage_handle_t * handle,
195 		       dbase_llist_t * dbase,
196 		       const record_key_t * key, const record_t * data)
197 {
198 
199 	cache_entry_t *entry;
200 	int status;
201 
202 	status = dbase_llist_cache_locate(handle, dbase, key, &entry);
203 	if (status < 0)
204 		goto err;
205 	if (status == STATUS_NODATA) {
206 		if (dbase_llist_cache_prepend(handle, dbase, data) < 0)
207 			goto err;
208 	} else {
209 		dbase->rtable->free(entry->data);
210 		if (dbase->rtable->clone(handle, data, &entry->data) < 0)
211 			goto err;
212 	}
213 
214 	dbase->modified = 1;
215 	return STATUS_SUCCESS;
216 
217       err:
218 	ERR(handle, "could not modify record value");
219 	return STATUS_ERR;
220 }
221 
dbase_llist_count(semanage_handle_t * handle,dbase_llist_t * dbase,unsigned int * response)222 hidden int dbase_llist_count(semanage_handle_t * handle __attribute__ ((unused)),
223 			     dbase_llist_t * dbase, unsigned int *response)
224 {
225 
226 	*response = dbase->cache_sz;
227 	handle = NULL;
228 	return STATUS_SUCCESS;
229 }
230 
dbase_llist_query(semanage_handle_t * handle,dbase_llist_t * dbase,const record_key_t * key,record_t ** response)231 int dbase_llist_query(semanage_handle_t * handle,
232 		      dbase_llist_t * dbase,
233 		      const record_key_t * key, record_t ** response)
234 {
235 
236 	cache_entry_t *entry;
237 	int status;
238 
239 	status = dbase_llist_cache_locate(handle, dbase, key, &entry);
240 	if (status < 0 || status == STATUS_NODATA)
241 		goto err;
242 
243 	if (dbase->rtable->clone(handle, entry->data, response) < 0)
244 		goto err;
245 
246 	return STATUS_SUCCESS;
247 
248       err:
249 	ERR(handle, "could not query record value");
250 	return STATUS_ERR;
251 }
252 
dbase_llist_iterate(semanage_handle_t * handle,dbase_llist_t * dbase,int (* fn)(const record_t * record,void * fn_arg),void * arg)253 int dbase_llist_iterate(semanage_handle_t * handle,
254 			dbase_llist_t * dbase,
255 			int (*fn) (const record_t * record,
256 				   void *fn_arg), void *arg)
257 {
258 
259 	int rc;
260 	cache_entry_t *ptr;
261 
262 	for (ptr = dbase->cache_tail; ptr != NULL; ptr = ptr->prev) {
263 
264 		rc = fn(ptr->data, arg);
265 		if (rc < 0)
266 			goto err;
267 
268 		else if (rc > 1)
269 			break;
270 	}
271 
272 	return STATUS_SUCCESS;
273 
274       err:
275 	ERR(handle, "could not iterate over records");
276 	return STATUS_ERR;
277 }
278 
dbase_llist_del(semanage_handle_t * handle,dbase_llist_t * dbase,const record_key_t * key)279 int dbase_llist_del(semanage_handle_t * handle __attribute__ ((unused)),
280 		    dbase_llist_t * dbase, const record_key_t * key)
281 {
282 
283 	cache_entry_t *ptr, *prev = NULL;
284 
285 	for (ptr = dbase->cache; ptr != NULL; ptr = ptr->next) {
286 		if (!dbase->rtable->compare(ptr->data, key)) {
287 			if (prev != NULL)
288 				prev->next = ptr->next;
289 			else
290 				dbase->cache = ptr->next;
291 
292 			if (ptr->next != NULL)
293 				ptr->next->prev = ptr->prev;
294 			else
295 				dbase->cache_tail = ptr->prev;
296 
297 			dbase->rtable->free(ptr->data);
298 			dbase->cache_sz--;
299 			free(ptr);
300 			dbase->modified = 1;
301 			return STATUS_SUCCESS;
302 		} else
303 			prev = ptr;
304 	}
305 
306 	handle = NULL;
307 	return STATUS_SUCCESS;
308 }
309 
dbase_llist_clear(semanage_handle_t * handle,dbase_llist_t * dbase)310 int dbase_llist_clear(semanage_handle_t * handle, dbase_llist_t * dbase)
311 {
312 
313 	int old_serial = dbase->cache_serial;
314 
315 	if (dbase_llist_set_serial(handle, dbase) < 0) {
316 		ERR(handle, "could not set serial of cleared dbase");
317 		return STATUS_ERR;
318 	}
319 
320 	if (old_serial >= 0) {
321 		cache_entry_t *prev, *ptr = dbase->cache;
322 		while (ptr != NULL) {
323 			prev = ptr;
324 			ptr = ptr->next;
325 			dbase->rtable->free(prev->data);
326 			free(prev);
327 		}
328 	}
329 
330 	dbase->cache = NULL;
331 	dbase->cache_tail = NULL;
332 	dbase->cache_sz = 0;
333 	dbase->modified = 1;
334 	return STATUS_SUCCESS;
335 }
336 
dbase_llist_list(semanage_handle_t * handle,dbase_llist_t * dbase,record_t *** records,unsigned int * count)337 int dbase_llist_list(semanage_handle_t * handle,
338 		     dbase_llist_t * dbase,
339 		     record_t *** records, unsigned int *count)
340 {
341 
342 	cache_entry_t *ptr;
343 	record_t **tmp_records = NULL;
344 	unsigned int tmp_count;
345 	int i = 0;
346 
347 	tmp_count = dbase->cache_sz;
348 	if (tmp_count > 0) {
349 		tmp_records = (record_t **)
350 		    calloc(tmp_count, sizeof(record_t *));
351 
352 		if (tmp_records == NULL)
353 			goto omem;
354 
355 		for (ptr = dbase->cache_tail; ptr != NULL; ptr = ptr->prev) {
356 			if (dbase->rtable->clone(handle,
357 						 ptr->data,
358 						 &tmp_records[i]) < 0)
359 				goto err;
360 			i++;
361 		}
362 	}
363 
364 	*records = tmp_records;
365 	*count = tmp_count;
366 	return STATUS_SUCCESS;
367 
368       omem:
369 	ERR(handle, "out of memory");
370 
371       err:
372 	if (tmp_records) {
373 		for (; i >= 0; i--)
374 			dbase->rtable->free(tmp_records[i]);
375 		free(tmp_records);
376 	}
377 	ERR(handle, "could not allocate record array");
378 	return STATUS_ERR;
379 }
380