diff options
Diffstat (limited to 'libsemanage/src/database_llist.c')
-rw-r--r-- | libsemanage/src/database_llist.c | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/libsemanage/src/database_llist.c b/libsemanage/src/database_llist.c new file mode 100644 index 00000000..1cb7454d --- /dev/null +++ b/libsemanage/src/database_llist.c @@ -0,0 +1,376 @@ +/* Copyright (C) 2005 Red Hat, Inc. */ + +/* Object: dbase_llist_t (Linked List) + * Partially Implements: dbase_t (Database) + */ + +struct dbase_llist; +typedef struct dbase_llist dbase_t; +#define DBASE_DEFINED + +#include <stdlib.h> +#include "debug.h" +#include "handle.h" +#include "database_llist.h" + +int dbase_llist_needs_resync(semanage_handle_t * handle, dbase_llist_t * dbase) +{ + + int cache_serial; + + if (dbase->cache_serial < 0) + return 1; + + cache_serial = handle->funcs->get_serial(handle); + if (cache_serial < 0) + return 1; + + if (cache_serial != dbase->cache_serial) { + dbase_llist_drop_cache(dbase); + dbase->cache_serial = -1; + return 1; + } + return 0; +} + +/* Helper for adding records to the cache */ +int dbase_llist_cache_prepend(semanage_handle_t * handle, + dbase_llist_t * dbase, const record_t * data) +{ + + /* Initialize */ + cache_entry_t *entry = (cache_entry_t *) malloc(sizeof(cache_entry_t)); + if (entry == NULL) + goto omem; + + if (dbase->rtable->clone(handle, data, &entry->data) < 0) + goto err; + + entry->prev = NULL; + entry->next = dbase->cache; + + /* Link */ + if (dbase->cache != NULL) + dbase->cache->prev = entry; + if (dbase->cache_tail == NULL) + dbase->cache_tail = entry; + dbase->cache = entry; + dbase->cache_sz++; + return STATUS_SUCCESS; + + omem: + ERR(handle, "out of memory"); + + err: + ERR(handle, "could not cache record"); + free(entry); + return STATUS_ERR; +} + +void dbase_llist_drop_cache(dbase_llist_t * dbase) +{ + + if (dbase->cache_serial < 0) + return; + + cache_entry_t *prev, *ptr = dbase->cache; + while (ptr != NULL) { + prev = ptr; + ptr = ptr->next; + dbase->rtable->free(prev->data); + free(prev); + } + + dbase->cache_serial = -1; + dbase->modified = 0; +} + +int dbase_llist_set_serial(semanage_handle_t * handle, dbase_llist_t * dbase) +{ + + int cache_serial = handle->funcs->get_serial(handle); + if (cache_serial < 0) { + ERR(handle, "could not update cache serial"); + return STATUS_ERR; + } + + dbase->cache_serial = cache_serial; + return STATUS_SUCCESS; +} + +/* Helper for finding records in the cache */ +static int dbase_llist_cache_locate(semanage_handle_t * handle, + dbase_llist_t * dbase, + const record_key_t * key, + cache_entry_t ** entry) +{ + + cache_entry_t *ptr; + + /* Implemented in parent */ + if (dbase->dtable->cache(handle, dbase) < 0) + goto err; + + for (ptr = dbase->cache; ptr != NULL; ptr = ptr->next) { + if (!dbase->rtable->compare(ptr->data, key)) { + *entry = ptr; + return STATUS_SUCCESS; + } + } + + return STATUS_NODATA; + + err: + ERR(handle, "could not complete cache lookup"); + return STATUS_ERR; +} + +int dbase_llist_exists(semanage_handle_t * handle, + dbase_llist_t * dbase, + const record_key_t * key, int *response) +{ + + cache_entry_t *entry; + int status; + + status = dbase_llist_cache_locate(handle, dbase, key, &entry); + if (status < 0) + goto err; + + *response = (status != STATUS_NODATA); + return STATUS_SUCCESS; + + err: + ERR(handle, "could not check if record exists"); + return STATUS_ERR; +} + +int dbase_llist_add(semanage_handle_t * handle, + dbase_llist_t * dbase, + const record_key_t * key, const record_t * data) +{ + + if (dbase_llist_cache_prepend(handle, dbase, data) < 0) + goto err; + + key = NULL; + dbase->modified = 1; + return STATUS_SUCCESS; + + err: + ERR(handle, "could not add record to the database"); + return STATUS_ERR; +} + +int dbase_llist_set(semanage_handle_t * handle, + dbase_llist_t * dbase, + const record_key_t * key, const record_t * data) +{ + + cache_entry_t *entry; + int status; + + status = dbase_llist_cache_locate(handle, dbase, key, &entry); + if (status < 0) + goto err; + if (status == STATUS_NODATA) { + ERR(handle, "record not found in the database"); + goto err; + } else { + dbase->rtable->free(entry->data); + if (dbase->rtable->clone(handle, data, &entry->data) < 0) + goto err; + } + + dbase->modified = 1; + return STATUS_SUCCESS; + + err: + ERR(handle, "could not set record value"); + return STATUS_ERR; +} + +int dbase_llist_modify(semanage_handle_t * handle, + dbase_llist_t * dbase, + const record_key_t * key, const record_t * data) +{ + + cache_entry_t *entry; + int status; + + status = dbase_llist_cache_locate(handle, dbase, key, &entry); + if (status < 0) + goto err; + if (status == STATUS_NODATA) { + if (dbase_llist_cache_prepend(handle, dbase, data) < 0) + goto err; + } else { + dbase->rtable->free(entry->data); + if (dbase->rtable->clone(handle, data, &entry->data) < 0) + goto err; + } + + dbase->modified = 1; + return STATUS_SUCCESS; + + err: + ERR(handle, "could not modify record value"); + return STATUS_ERR; +} + +hidden int dbase_llist_count(semanage_handle_t * handle, + dbase_llist_t * dbase, unsigned int *response) +{ + + *response = dbase->cache_sz; + handle = NULL; + return STATUS_SUCCESS; +} + +int dbase_llist_query(semanage_handle_t * handle, + dbase_llist_t * dbase, + const record_key_t * key, record_t ** response) +{ + + cache_entry_t *entry; + int status; + + status = dbase_llist_cache_locate(handle, dbase, key, &entry); + if (status < 0 || status == STATUS_NODATA) + goto err; + + if (dbase->rtable->clone(handle, entry->data, response) < 0) + goto err; + + return STATUS_SUCCESS; + + err: + ERR(handle, "could not query record value"); + return STATUS_ERR; +} + +int dbase_llist_iterate(semanage_handle_t * handle, + dbase_llist_t * dbase, + int (*fn) (const record_t * record, + void *fn_arg), void *arg) +{ + + int rc; + cache_entry_t *ptr; + + for (ptr = dbase->cache_tail; ptr != NULL; ptr = ptr->prev) { + + rc = fn(ptr->data, arg); + if (rc < 0) + goto err; + + else if (rc > 1) + break; + } + + return STATUS_SUCCESS; + + err: + ERR(handle, "could not iterate over records"); + return STATUS_ERR; +} + +int dbase_llist_del(semanage_handle_t * handle, + dbase_llist_t * dbase, const record_key_t * key) +{ + + cache_entry_t *ptr, *prev = NULL; + + for (ptr = dbase->cache; ptr != NULL; ptr = ptr->next) { + if (!dbase->rtable->compare(ptr->data, key)) { + if (prev != NULL) + prev->next = ptr->next; + else + dbase->cache = ptr->next; + + if (ptr->next != NULL) + ptr->next->prev = ptr->prev; + else + dbase->cache_tail = ptr->prev; + + dbase->rtable->free(ptr->data); + dbase->cache_sz--; + free(ptr); + dbase->modified = 1; + return STATUS_SUCCESS; + } else + prev = ptr; + } + + handle = NULL; + return STATUS_SUCCESS; +} + +int dbase_llist_clear(semanage_handle_t * handle, dbase_llist_t * dbase) +{ + + int old_serial = dbase->cache_serial; + + if (dbase_llist_set_serial(handle, dbase) < 0) { + ERR(handle, "could not set serial of cleared dbase"); + return STATUS_ERR; + } + + if (old_serial >= 0) { + cache_entry_t *prev, *ptr = dbase->cache; + while (ptr != NULL) { + prev = ptr; + ptr = ptr->next; + dbase->rtable->free(prev->data); + free(prev); + } + } + + dbase->cache = NULL; + dbase->cache_tail = NULL; + dbase->cache_sz = 0; + dbase->modified = 1; + return STATUS_SUCCESS; +} + +int dbase_llist_list(semanage_handle_t * handle, + dbase_llist_t * dbase, + record_t *** records, unsigned int *count) +{ + + cache_entry_t *ptr; + record_t **tmp_records = NULL; + unsigned int tmp_count; + int i = 0; + + tmp_count = dbase->cache_sz; + if (tmp_count > 0) { + tmp_records = (record_t **) + calloc(tmp_count, sizeof(record_t *)); + + if (tmp_records == NULL) + goto omem; + + for (ptr = dbase->cache_tail; ptr != NULL; ptr = ptr->prev) { + if (dbase->rtable->clone(handle, + ptr->data, + &tmp_records[i]) < 0) + goto err; + i++; + } + } + + *records = tmp_records; + *count = tmp_count; + return STATUS_SUCCESS; + + omem: + ERR(handle, "out of memory"); + + err: + for (; i >= 0; i--) + dbase->rtable->free(tmp_records[i]); + free(tmp_records); + ERR(handle, "could not allocate record array"); + return STATUS_ERR; +} |