diff options
| author | Pavel Březina <pbrezina@redhat.com> | 2016-11-10 13:11:41 +0100 |
|---|---|---|
| committer | Lukas Slebodnik <lslebodn@redhat.com> | 2016-12-19 23:28:46 +0100 |
| commit | a5a3bbb0bbaeb8946c228c2fb7f0cf450595dd3e (patch) | |
| tree | cde9d4432f98a348309755ac2611984ff56430c2 /src/util | |
| parent | 7162dc780fe9458018c577f6f1638522d74f63b0 (diff) | |
| download | sssd-a5a3bbb0bbaeb8946c228c2fb7f0cf450595dd3e.tar.gz sssd-a5a3bbb0bbaeb8946c228c2fb7f0cf450595dd3e.tar.xz sssd-a5a3bbb0bbaeb8946c228c2fb7f0cf450595dd3e.zip | |
utils: add sss_ptr_hash module
We often work with <string, pointer> type of hash table throughout sssd.
This module creates and maintains such hash table and makes sure
that hash entry is destroyed when original value is freed.
Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
Diffstat (limited to 'src/util')
| -rw-r--r-- | src/util/sss_ptr_hash.c | 375 | ||||
| -rw-r--r-- | src/util/sss_ptr_hash.h | 117 |
2 files changed, 492 insertions, 0 deletions
diff --git a/src/util/sss_ptr_hash.c b/src/util/sss_ptr_hash.c new file mode 100644 index 000000000..e34b9b2c1 --- /dev/null +++ b/src/util/sss_ptr_hash.c @@ -0,0 +1,375 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <talloc.h> +#include <dhash.h> + +#include "util/util.h" +#include "util/sss_ptr_hash.h" + +static bool sss_ptr_hash_check_type(void *ptr, const char *type) +{ + void *type_ptr; + + type_ptr = talloc_check_name(ptr, type); + if (type_ptr == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Invalid data type detected. Expected [%s], got [%s].\n", + type, talloc_get_name(ptr)); + return false; + } + + return true; +} + +struct sss_ptr_hash_delete_data { + hash_delete_callback *callback; + void *pvt; +}; + +struct sss_ptr_hash_value { + struct sss_ptr_hash_spy *spy; + void *ptr; +}; + +struct sss_ptr_hash_spy { + struct sss_ptr_hash_value *value; + hash_table_t *table; + const char *key; +}; + +static int +sss_ptr_hash_spy_destructor(struct sss_ptr_hash_spy *spy) +{ + spy->value->spy = NULL; + spy->value->ptr = NULL; + + /* This results in removing entry from hash table and freeing the value. */ + sss_ptr_hash_delete(spy->table, spy->key, false); + return 0; +} + +static struct sss_ptr_hash_spy * +sss_ptr_hash_spy_create(TALLOC_CTX *mem_ctx, + hash_table_t *table, + const char *key, + struct sss_ptr_hash_value *value) +{ + struct sss_ptr_hash_spy *spy; + + spy = talloc_zero(mem_ctx, struct sss_ptr_hash_spy); + if (spy == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n"); + return NULL; + } + + spy->key = talloc_strdup(spy, key); + if (spy->key == NULL) { + talloc_free(spy); + return NULL; + } + + spy->table = table; + spy->value = value; + talloc_set_destructor(spy, sss_ptr_hash_spy_destructor); + + return spy; +} + +static int +sss_ptr_hash_value_destructor(struct sss_ptr_hash_value *value) +{ + if (value->spy != NULL) { + /* Disable spy destructor and free it. */ + talloc_set_destructor(value->spy, NULL); + talloc_zfree(value->spy); + } + + return 0; +} + +static struct sss_ptr_hash_value * +sss_ptr_hash_value_create(hash_table_t *table, + const char *key, + void *talloc_ptr) +{ + struct sss_ptr_hash_value *value; + + value = talloc_zero(table, struct sss_ptr_hash_value); + if (value == NULL) { + return NULL; + } + + value->spy = sss_ptr_hash_spy_create(talloc_ptr, table, key, value); + if (value->spy == NULL) { + talloc_free(value); + return NULL; + } + + value->ptr = talloc_ptr; + talloc_set_destructor(value, sss_ptr_hash_value_destructor); + + return value; +} + +static void +sss_ptr_hash_delete_cb(hash_entry_t *item, + hash_destroy_enum deltype, + void *pvt) +{ + struct sss_ptr_hash_delete_data *data; + struct sss_ptr_hash_value *value; + void *ptr; + + data = talloc_get_type(pvt, struct sss_ptr_hash_delete_data); + if (data == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid data!\n"); + return; + } + + value = talloc_get_type(item->value.ptr, struct sss_ptr_hash_value); + if (value == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid value!\n"); + return; + } + + ptr = value->ptr; + + /* Free value. */ + talloc_free(value); + + /* Switch to the input value and call custom callback. */ + if (data->callback != NULL) { + item->value.ptr = ptr; + data->callback(item, deltype, data->pvt); + } +} + +hash_table_t *sss_ptr_hash_create(TALLOC_CTX *mem_ctx, + hash_delete_callback *del_cb, + void *del_cb_pvt) +{ + struct sss_ptr_hash_delete_data *data; + hash_table_t *table; + errno_t ret; + + data = talloc_zero(mem_ctx, struct sss_ptr_hash_delete_data); + if (data == NULL) { + return NULL; + } + + data->callback = del_cb; + data->pvt = del_cb_pvt; + + ret = sss_hash_create_ex(NULL, 10, &table, 0, 0, 0, 0, + sss_ptr_hash_delete_cb, data); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n", + ret, sss_strerror(ret)); + talloc_free(data); + return NULL; + } + + talloc_steal(table, data); + + return table; +} + +errno_t _sss_ptr_hash_add(hash_table_t *table, + const char *key, + void *talloc_ptr, + const char *type, + bool override) +{ + struct sss_ptr_hash_value *value; + hash_value_t table_value; + hash_key_t table_key; + int hret; + + if (table == NULL || key == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid input!\n"); + return EINVAL; + } + + if (!sss_ptr_hash_check_type(talloc_ptr, type)) { + return ERR_INVALID_DATA_TYPE; + } + + value = sss_ptr_hash_value_create(table, key, talloc_ptr); + if (value == NULL) { + return ENOMEM; + } + + table_key.type = HASH_KEY_STRING; + table_key.str = discard_const_p(char, key); + + table_value.type = HASH_VALUE_PTR; + table_value.ptr = value; + + if (override == false && hash_has_key(table, &table_key)) { + return EEXIST; + } + + hret = hash_enter(table, &table_key, &table_value); + if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add key %s!\n", key); + talloc_free(value); + return EIO; + } + + return EOK; +} + +static struct sss_ptr_hash_value * +sss_ptr_hash_lookup_internal(hash_table_t *table, + const char *key) +{ + hash_value_t table_value; + hash_key_t table_key; + int hret; + + table_key.type = HASH_KEY_STRING; + table_key.str = discard_const_p(char, key); + + hret = hash_lookup(table, &table_key, &table_value); + if (hret == HASH_ERROR_KEY_NOT_FOUND) { + return NULL; + } else if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to search hash table [%d]\n", hret); + return NULL; + } + + /* Check value type. */ + if (table_value.type != HASH_VALUE_PTR) { + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid value type found: %d\n", + table_value.type); + return NULL; + } + + if (!sss_ptr_hash_check_type(table_value.ptr, "struct sss_ptr_hash_value")) { + return NULL; + } + + return table_value.ptr; +} + +void *_sss_ptr_hash_lookup(hash_table_t *table, + const char *key, + const char *type) +{ + struct sss_ptr_hash_value *value; + + value = sss_ptr_hash_lookup_internal(table, key); + if (value == NULL || value->ptr == NULL) { + return NULL; + } + + if (!sss_ptr_hash_check_type(value->ptr, type)) { + return NULL; + } + + return value->ptr; +} + +void sss_ptr_hash_delete(hash_table_t *table, + const char *key, + bool free_value) +{ + struct sss_ptr_hash_value *value; + hash_key_t table_key; + int hret; + void *ptr; + + if (table == NULL || key == NULL) { + return; + } + + value = sss_ptr_hash_lookup_internal(table, key); + if (value == NULL) { + /* Value not found. */ + return; + } + + ptr = value->ptr; + + table_key.type = HASH_KEY_STRING; + table_key.str = discard_const_p(char, key); + + /* Delete table entry. This will free value and spy in delete callback. */ + hret = hash_delete(table, &table_key); + if (hret != HASH_SUCCESS && hret != HASH_ERROR_KEY_NOT_FOUND) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to remove key from table [%d]\n", + hret); + } + + /* Also free the original value if requested. */ + if (free_value) { + talloc_free(ptr); + } + + return; +} + +void sss_ptr_hash_delete_all(hash_table_t *table, + bool free_values) +{ + struct sss_ptr_hash_value *value; + hash_value_t *values; + size_t count; + size_t i; + int hret; + void *ptr; + + if (table == NULL) { + return; + } + + hret = hash_values(table, &count, &values); + if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get values [%d]\n", hret); + return; + } + + for (i = 0; i < count; i++) { + value = values[i].ptr; + ptr = value->ptr; + + /* This will remove the entry from hash table and free value. */ + talloc_free(value->spy); + + if (free_values) { + /* Also free the original value. */ + talloc_free(ptr); + } + } + + return; +} + +bool sss_ptr_hash_has_key(hash_table_t *table, + const char *key) +{ + hash_key_t table_key; + + table_key.type = HASH_KEY_STRING; + table_key.str = discard_const_p(char, key); + + return hash_has_key(table, &table_key); +} diff --git a/src/util/sss_ptr_hash.h b/src/util/sss_ptr_hash.h new file mode 100644 index 000000000..510b9544f --- /dev/null +++ b/src/util/sss_ptr_hash.h @@ -0,0 +1,117 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _SSS_PTR_HASH_H_ +#define _SSS_PTR_HASH_H_ + +#include <talloc.h> +#include <dhash.h> + +/** + * Create a new hash table with string key and talloc pointer value with + * possible delete callback. + */ +hash_table_t *sss_ptr_hash_create(TALLOC_CTX *mem_ctx, + hash_delete_callback *del_cb, + void *del_cb_pvt); + +/** + * Add a new value @talloc_ptr of type @type into the table. + * + * If the @key already exist in the table and @override is true, + * the value is overridden. Otherwise EEXIST error is returned. + * + * If talloc_ptr is freed the key and value are automatically + * removed from the hash table. + * + * @return EOK If the <@key, @talloc_ptr> pair was inserted. + * @return EEXIST If @key already exists and @override is false. + * @return Other errno code in case of an error. + */ +errno_t _sss_ptr_hash_add(hash_table_t *table, + const char *key, + void *talloc_ptr, + const char *type, + bool override); + +/** + * Add a new value @talloc_ptr of type @type into the table. + * + * If talloc_ptr is freed the key and value are automatically + * removed from the hash table. + * + * @return EOK If the <@key, @talloc_ptr> pair was inserted. + * @return EEXIST If @key already exists. + * @return Other errno code in case of an error. + */ +#define sss_ptr_hash_add(table, key, talloc_ptr, type) \ + _sss_ptr_hash_add(table, key, talloc_ptr, #type, false) + +/** + * Add a new value @talloc_ptr of type @type into the table. + * + * If the @key already exists in the table, its value is + * overridden. If talloc_ptr is freed the key and value + * are automatically removed from the hash table. + * + * @return EOK If the <@key, @talloc_ptr> pair was inserted. + * @return Other errno code in case of an error. + */ +#define sss_ptr_hash_add_or_override(table, key, talloc_ptr, type) \ + _sss_ptr_hash_add(table, key, talloc_ptr, #type, true) + +void *_sss_ptr_hash_lookup(hash_table_t *table, + const char *key, + const char *type); + +/** + * Lookup @key in the table and return its value as typed to @type. + * The type of the value must match with @type, otherwise NULL is returned. + * + * @return talloc_ptr If the value is found as type matches. + * @return NULL If the value is not found or if the type is invalid. + */ +#define sss_ptr_hash_lookup(table, key, type) \ + (type *)_sss_ptr_hash_lookup(table, key, #type) + +/** + * Delete @key from table. If @free_value is true then also the value + * associated with @key is freed, otherwise it is left intact. + */ +void sss_ptr_hash_delete(hash_table_t *table, + const char *key, + bool free_value); + +/** + * Delete all keys from the table. If @free_value sis true then also + * the values associated with those keys are reed, otherwise + * they are left intact. + */ +void sss_ptr_hash_delete_all(hash_table_t *table, + bool free_values); + +/** + * @return true If @key is present in the table. + * @return false Otherwise. + */ +bool sss_ptr_hash_has_key(hash_table_t *table, + const char *key); + +#endif /* _SSS_PTR_HASH_H_ */ |
