1/* Copyright (C) 2005 Red Hat, Inc. */
2
3/* Object: dbase_llist_t (Linked List)
4 * Partially Implements: dbase_t (Database)
5 */
6
7struct dbase_llist;
8typedef 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
16int 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 */
37int 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
70void 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
88int 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 */
102static 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
128int 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
148int 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
166int 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
194int 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
222hidden 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
231int 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
253int 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
279int 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
310int 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
337int 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