Blame SOURCES/0022-sss_ptr_hash-internal-refactoring.patch

0d097b
From 0bb1289252eec972ea26721a92adc7db47383f76 Mon Sep 17 00:00:00 2001
0d097b
From: Alexey Tikhonov <atikhono@redhat.com>
0d097b
Date: Fri, 24 Jan 2020 23:57:39 +0100
0d097b
Subject: [PATCH 22/23] sss_ptr_hash: internal refactoring
0d097b
MIME-Version: 1.0
0d097b
Content-Type: text/plain; charset=UTF-8
0d097b
Content-Transfer-Encoding: 8bit
0d097b
0d097b
sss_ptr_hash code was refactored:
0d097b
 - got rid of a "spy" to make logic cleaner
0d097b
 - table got destructor to wipe its content
0d097b
 - described some usage limitation in the documentation
0d097b
0d097b
And resolves: https://pagure.io/SSSD/sssd/issue/4135
0d097b
0d097b
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
0d097b
---
0d097b
 src/util/sss_ptr_hash.c | 183 +++++++++++++++++-----------------------
0d097b
 src/util/sss_ptr_hash.h |  17 +++-
0d097b
 2 files changed, 91 insertions(+), 109 deletions(-)
0d097b
0d097b
diff --git a/src/util/sss_ptr_hash.c b/src/util/sss_ptr_hash.c
0d097b
index 114b6edeb..6409236c7 100644
0d097b
--- a/src/util/sss_ptr_hash.c
0d097b
+++ b/src/util/sss_ptr_hash.c
0d097b
@@ -39,67 +39,35 @@ static bool sss_ptr_hash_check_type(void *ptr, const char *type)
0d097b
     return true;
0d097b
 }
0d097b
 
0d097b
+static int sss_ptr_hash_table_destructor(hash_table_t *table)
0d097b
+{
0d097b
+    sss_ptr_hash_delete_all(table, false);
0d097b
+    return 0;
0d097b
+}
0d097b
+
0d097b
 struct sss_ptr_hash_delete_data {
0d097b
     hash_delete_callback *callback;
0d097b
     void *pvt;
0d097b
 };
0d097b
 
0d097b
 struct sss_ptr_hash_value {
0d097b
-    struct sss_ptr_hash_spy *spy;
0d097b
-    void *ptr;
0d097b
-};
0d097b
-
0d097b
-struct sss_ptr_hash_spy {
0d097b
-    struct sss_ptr_hash_value *value;
0d097b
     hash_table_t *table;
0d097b
     const char *key;
0d097b
+    void *payload;
0d097b
 };
0d097b
 
0d097b
-static int
0d097b
-sss_ptr_hash_spy_destructor(struct sss_ptr_hash_spy *spy)
0d097b
-{
0d097b
-    spy->value->spy = NULL;
0d097b
-
0d097b
-    /* This results in removing entry from hash table and freeing the value. */
0d097b
-    sss_ptr_hash_delete(spy->table, spy->key, false);
0d097b
-
0d097b
-    return 0;
0d097b
-}
0d097b
-
0d097b
-static struct sss_ptr_hash_spy *
0d097b
-sss_ptr_hash_spy_create(TALLOC_CTX *mem_ctx,
0d097b
-                        hash_table_t *table,
0d097b
-                        const char *key,
0d097b
-                        struct sss_ptr_hash_value *value)
0d097b
-{
0d097b
-    struct sss_ptr_hash_spy *spy;
0d097b
-
0d097b
-    spy = talloc_zero(mem_ctx, struct sss_ptr_hash_spy);
0d097b
-    if (spy == NULL) {
0d097b
-        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
0d097b
-        return NULL;
0d097b
-    }
0d097b
-
0d097b
-    spy->key = talloc_strdup(spy, key);
0d097b
-    if (spy->key == NULL) {
0d097b
-        talloc_free(spy);
0d097b
-        return NULL;
0d097b
-    }
0d097b
-
0d097b
-    spy->table = table;
0d097b
-    spy->value = value;
0d097b
-    talloc_set_destructor(spy, sss_ptr_hash_spy_destructor);
0d097b
-
0d097b
-    return spy;
0d097b
-}
0d097b
-
0d097b
 static int
0d097b
 sss_ptr_hash_value_destructor(struct sss_ptr_hash_value *value)
0d097b
 {
0d097b
-    if (value->spy != NULL) {
0d097b
-        /* Disable spy destructor and free it. */
0d097b
-        talloc_set_destructor(value->spy, NULL);
0d097b
-        talloc_zfree(value->spy);
0d097b
+    hash_key_t table_key;
0d097b
+
0d097b
+    if (value->table && value->key) {
0d097b
+        table_key.type = HASH_KEY_STRING;
0d097b
+        table_key.str = discard_const_p(char, value->key);
0d097b
+        if (hash_delete(value->table, &table_key) != HASH_SUCCESS) {
0d097b
+            DEBUG(SSSDBG_CRIT_FAILURE,
0d097b
+                  "failed to delete entry with key '%s'\n", value->key);
0d097b
+        }
0d097b
     }
0d097b
 
0d097b
     return 0;
0d097b
@@ -112,18 +80,19 @@ sss_ptr_hash_value_create(hash_table_t *table,
0d097b
 {
0d097b
     struct sss_ptr_hash_value *value;
0d097b
 
0d097b
-    value = talloc_zero(table, struct sss_ptr_hash_value);
0d097b
+    value = talloc_zero(talloc_ptr, struct sss_ptr_hash_value);
0d097b
     if (value == NULL) {
0d097b
         return NULL;
0d097b
     }
0d097b
 
0d097b
-    value->spy = sss_ptr_hash_spy_create(talloc_ptr, table, key, value);
0d097b
-    if (value->spy == NULL) {
0d097b
+    value->key = talloc_strdup(value, key);
0d097b
+    if (value->key == NULL) {
0d097b
         talloc_free(value);
0d097b
         return NULL;
0d097b
     }
0d097b
 
0d097b
-    value->ptr = talloc_ptr;
0d097b
+    value->table = table;
0d097b
+    value->payload = talloc_ptr;
0d097b
     talloc_set_destructor(value, sss_ptr_hash_value_destructor);
0d097b
 
0d097b
     return value;
0d097b
@@ -138,29 +107,31 @@ sss_ptr_hash_delete_cb(hash_entry_t *item,
0d097b
     struct sss_ptr_hash_value *value;
0d097b
     struct hash_entry_t callback_entry;
0d097b
 
0d097b
+    if (pvt == NULL) {
0d097b
+        return;
0d097b
+    }
0d097b
+
0d097b
     value = talloc_get_type(item->value.ptr, struct sss_ptr_hash_value);
0d097b
     if (value == NULL) {
0d097b
         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid value!\n");
0d097b
         return;
0d097b
     }
0d097b
 
0d097b
+    /* Switch to the input value and call custom callback. */
0d097b
+    data = talloc_get_type(pvt, struct sss_ptr_hash_delete_data);
0d097b
+    if (data == NULL) {
0d097b
+        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid data!\n");
0d097b
+        return;
0d097b
+    }
0d097b
+
0d097b
     callback_entry.key = item->key;
0d097b
     callback_entry.value.type = HASH_VALUE_PTR;
0d097b
-    callback_entry.value.ptr = value->ptr;
0d097b
-
0d097b
-    /* Free value, this also will disable spy */
0d097b
-    talloc_free(value);
0d097b
-
0d097b
-    if (pvt != NULL) {
0d097b
-        /* Switch to the input value and call custom callback. */
0d097b
-        data = talloc_get_type(pvt, struct sss_ptr_hash_delete_data);
0d097b
-        if (data == NULL) {
0d097b
-            DEBUG(SSSDBG_CRIT_FAILURE, "Invalid data!\n");
0d097b
-            return;
0d097b
-        }
0d097b
-
0d097b
-        data->callback(&callback_entry, deltype, data->pvt);
0d097b
-    }
0d097b
+    callback_entry.value.ptr = value->payload;
0d097b
+    /* Even if execution is already in the context of
0d097b
+     * talloc_free(payload) -> talloc_free(value) -> ...
0d097b
+     * there still might be legitimate reasons to execute callback.
0d097b
+     */
0d097b
+    data->callback(&callback_entry, deltype, data->pvt);
0d097b
 }
0d097b
 
0d097b
 hash_table_t *sss_ptr_hash_create(TALLOC_CTX *mem_ctx,
0d097b
@@ -194,6 +165,8 @@ hash_table_t *sss_ptr_hash_create(TALLOC_CTX *mem_ctx,
0d097b
         talloc_steal(table, data);
0d097b
     }
0d097b
 
0d097b
+    talloc_set_destructor(table, sss_ptr_hash_table_destructor);
0d097b
+
0d097b
     return table;
0d097b
 }
0d097b
 
0d097b
@@ -282,15 +255,15 @@ void *_sss_ptr_hash_lookup(hash_table_t *table,
0d097b
     struct sss_ptr_hash_value *value;
0d097b
 
0d097b
     value = sss_ptr_hash_lookup_internal(table, key);
0d097b
-    if (value == NULL || value->ptr == NULL) {
0d097b
+    if (value == NULL || value->payload == NULL) {
0d097b
         return NULL;
0d097b
     }
0d097b
 
0d097b
-    if (!sss_ptr_hash_check_type(value->ptr, type)) {
0d097b
+    if (!sss_ptr_hash_check_type(value->payload, type)) {
0d097b
         return NULL;
0d097b
     }
0d097b
 
0d097b
-    return value->ptr;
0d097b
+    return value->payload;
0d097b
 }
0d097b
 
0d097b
 void *_sss_ptr_get_value(hash_value_t *table_value,
0d097b
@@ -311,11 +284,11 @@ void *_sss_ptr_get_value(hash_value_t *table_value,
0d097b
 
0d097b
     value = table_value->ptr;
0d097b
 
0d097b
-    if (!sss_ptr_hash_check_type(value->ptr, type)) {
0d097b
+    if (!sss_ptr_hash_check_type(value->payload, type)) {
0d097b
         return NULL;
0d097b
     }
0d097b
 
0d097b
-    return value->ptr;
0d097b
+    return value->payload;
0d097b
 }
0d097b
 
0d097b
 void sss_ptr_hash_delete(hash_table_t *table,
0d097b
@@ -323,74 +296,70 @@ void sss_ptr_hash_delete(hash_table_t *table,
0d097b
                          bool free_value)
0d097b
 {
0d097b
     struct sss_ptr_hash_value *value;
0d097b
-    hash_key_t table_key;
0d097b
-    int hret;
0d097b
-    void *payload;
0d097b
+    void *payload = NULL;
0d097b
 
0d097b
     if (table == NULL || key == NULL) {
0d097b
         return;
0d097b
     }
0d097b
 
0d097b
-    if (free_value) {
0d097b
-        value = sss_ptr_hash_lookup_internal(table, key);
0d097b
-        if (value == NULL) {
0d097b
-            free_value = false;
0d097b
-        } else {
0d097b
-            payload = value->ptr;
0d097b
-        }
0d097b
-    }
0d097b
-
0d097b
-    table_key.type = HASH_KEY_STRING;
0d097b
-    table_key.str = discard_const_p(char, key);
0d097b
-
0d097b
-    /* Delete table entry. This will free value and spy in delete callback. */
0d097b
-    hret = hash_delete(table, &table_key);
0d097b
-    if (hret != HASH_SUCCESS && hret != HASH_ERROR_KEY_NOT_FOUND) {
0d097b
-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to remove key from table [%d]\n",
0d097b
-              hret);
0d097b
+    value = sss_ptr_hash_lookup_internal(table, key);
0d097b
+    if (value == NULL) {
0d097b
+        DEBUG(SSSDBG_CRIT_FAILURE,
0d097b
+              "Unable to remove key '%s' from table\n", key);
0d097b
+        return;
0d097b
     }
0d097b
 
0d097b
-    /* Also free the original value if requested. */
0d097b
     if (free_value) {
0d097b
-        talloc_free(payload);
0d097b
+        payload = value->payload;
0d097b
     }
0d097b
 
0d097b
+    talloc_free(value); /* this will call hash_delete() in value d-tor */
0d097b
+
0d097b
+    talloc_free(payload); /* it is safe to call talloc_free(NULL) */
0d097b
+
0d097b
     return;
0d097b
 }
0d097b
 
0d097b
 void sss_ptr_hash_delete_all(hash_table_t *table,
0d097b
                              bool free_values)
0d097b
 {
0d097b
+    hash_value_t *content;
0d097b
     struct sss_ptr_hash_value *value;
0d097b
-    hash_value_t *values;
0d097b
+    void *payload = NULL;
0d097b
     unsigned long count;
0d097b
     unsigned long i;
0d097b
     int hret;
0d097b
-    void *ptr;
0d097b
 
0d097b
     if (table == NULL) {
0d097b
         return;
0d097b
     }
0d097b
 
0d097b
-    hret = hash_values(table, &count, &values);
0d097b
+    hret = hash_values(table, &count, &content);
0d097b
     if (hret != HASH_SUCCESS) {
0d097b
         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get values [%d]\n", hret);
0d097b
         return;
0d097b
     }
0d097b
 
0d097b
-    for (i = 0; i < count; i++) {
0d097b
-        value = values[i].ptr;
0d097b
-        ptr = value->ptr;
0d097b
-
0d097b
-        /* This will remove the entry from hash table and free value. */
0d097b
-        talloc_free(value->spy);
0d097b
-
0d097b
-        if (free_values) {
0d097b
-            /* Also free the original value. */
0d097b
-            talloc_free(ptr);
0d097b
+    for (i = 0; i < count; ++i) {
0d097b
+        if ((content[i].type == HASH_VALUE_PTR)  &&
0d097b
+            sss_ptr_hash_check_type(content[i].ptr,
0d097b
+                                    "struct sss_ptr_hash_value")) {
0d097b
+            value = content[i].ptr;
0d097b
+            if (free_values) {
0d097b
+                payload = value->payload;
0d097b
+            }
0d097b
+            talloc_free(value);
0d097b
+            if (free_values) {
0d097b
+                talloc_free(payload); /* it's safe to call talloc_free(NULL) */
0d097b
+            }
0d097b
+        } else {
0d097b
+            DEBUG(SSSDBG_CRIT_FAILURE,
0d097b
+                  "Unexpected type of table content, skipping");
0d097b
         }
0d097b
     }
0d097b
 
0d097b
+    talloc_free(content);
0d097b
+
0d097b
     return;
0d097b
 }
0d097b
 
0d097b
diff --git a/src/util/sss_ptr_hash.h b/src/util/sss_ptr_hash.h
0d097b
index 56bb19a65..0889b171a 100644
0d097b
--- a/src/util/sss_ptr_hash.h
0d097b
+++ b/src/util/sss_ptr_hash.h
0d097b
@@ -28,7 +28,19 @@
0d097b
 
0d097b
 /**
0d097b
  * Create a new hash table with string key and talloc pointer value with
0d097b
- * possible delete callback.
0d097b
+ * possible custom delete callback @del_cb.
0d097b
+ * Table will have destructor setup to wipe content.
0d097b
+ * Never call hash_destroy(table) and hash_delete() explicitly but rather
0d097b
+ * use talloc_free(table) and sss_ptr_hash_delete().
0d097b
+ *
0d097b
+ * A notes about @del_cb:
0d097b
+ *  - this callback must never modify hash table (i.e. add/del entries);
0d097b
+ *  - this callback is triggered when value is either explicitly removed
0d097b
+ *    from the table or simply freed (latter leads to removal of an entry
0d097b
+ *    from the table);
0d097b
+ *  - this callback is also triggered for every entry when table is freed
0d097b
+ *    entirely. In this case (deltype == HASH_TABLE_DESTROY) any table
0d097b
+ *    lookups / iteration are forbidden as table might be already invalidated.
0d097b
  */
0d097b
 hash_table_t *sss_ptr_hash_create(TALLOC_CTX *mem_ctx,
0d097b
                                   hash_delete_callback *del_cb,
0d097b
@@ -41,7 +53,8 @@ hash_table_t *sss_ptr_hash_create(TALLOC_CTX *mem_ctx,
0d097b
  * the value is overridden. Otherwise EEXIST error is returned.
0d097b
  *
0d097b
  * If talloc_ptr is freed the key and value are automatically
0d097b
- * removed from the hash table.
0d097b
+ * removed from the hash table (del_cb that was set up during
0d097b
+ * table creation is executed as a first step of this removal).
0d097b
  *
0d097b
  * @return EOK If the <@key, @talloc_ptr> pair was inserted.
0d097b
  * @return EEXIST If @key already exists and @override is false.
0d097b
-- 
0d097b
2.20.1
0d097b