From a88cc31be042cd88c7a817a3e5bd287bb78aea63 Mon Sep 17 00:00:00 2001
From: Jan Zeleny <jzeleny@redhat.com>
Date: Thu, 30 Jun 2011 16:43:50 -0400
Subject: [PATCH 2/2] First stage of the refcount patch

---
 src/ldb_modules/memberof.c |  476 +++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 422 insertions(+), 54 deletions(-)

diff --git a/src/ldb_modules/memberof.c b/src/ldb_modules/memberof.c
index 8d7fc166284cbb25288a1718766261e0e977d066..2b353dacc4440c85b34997dbb9a91800ee2834b5 100644
--- a/src/ldb_modules/memberof.c
+++ b/src/ldb_modules/memberof.c
@@ -29,6 +29,7 @@
 
 #define DB_MEMBER "member"
 #define DB_MEMBEROF "memberof"
+#define DB_MEMBEROFRC "memberofrefcount"
 #define DB_MEMBERUID "memberuid"
 #define DB_NAME "name"
 #define DB_USER_CLASS "user"
@@ -42,6 +43,8 @@
 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
 #endif
 
+enum op_status {OP_SCHEDULED, OP_DONE, OP_RESCHEDULED};
+
 struct mbof_dn {
     struct mbof_dn *next;
     struct ldb_dn *dn;
@@ -72,6 +75,7 @@ struct mbof_add_operation {
     struct ldb_dn *entry_dn;
 
     struct ldb_message *entry;
+    enum op_status status;
 };
 
 struct mbof_memberuid_op {
@@ -293,6 +297,12 @@ static int mbof_append_muop(TALLOC_CTX *memctx,
  * At the end we will add a memberuid attribute to our new object that
  * includes all direct and indeirect user members names.
  */
+static int mbof_add_dn(struct mbof_dn_array *array, int pos, struct ldb_dn *dn,
+                       unsigned ref_count);
+static void mbof_delete_dn(struct mbof_dn_array *array,
+                           struct mbof_dn *prev,
+                           struct mbof_dn *del);
+static int mbof_next_free_dn(struct mbof_dn_array *array);
 
 static int mbof_append_addop(struct mbof_add_ctx *add_ctx,
                              struct mbof_dn_array *parents,
@@ -301,6 +311,9 @@ static int mbof_append_addop(struct mbof_add_ctx *add_ctx,
 {
     struct mbof_add_operation *lastop = NULL;
     struct mbof_add_operation *addop;
+    struct mbof_dn *cur_dn;
+    struct mbof_dn *par_dn;
+    struct mbof_dn *par_prev_dn = NULL;
 
     /* test if this is a duplicate */
     /* FIXME: this is not efficient */
@@ -313,9 +326,41 @@ static int mbof_append_addop(struct mbof_add_ctx *add_ctx,
                     lastop = add_ctx->add_list;
                 }
 
-                /* FIXME: check if this is right, might have to compare parents */
                 if (ldb_dn_compare(lastop->entry_dn, entry_dn) == 0) {
-                    /* duplicate found */
+                    /* duplicate found - compare parents */
+                    par_dn = parents->first;
+                    for (; par_dn; par_dn = par_dn->next) {
+                        cur_dn = lastop->parents->first;
+                        for (; cur_dn; cur_dn = cur_dn->next) {
+                            if (ldb_dn_compare(cur_dn->dn, par_dn->dn) == 0) {
+                                cur_dn->cnt++;
+                                mbof_delete_dn(parents, par_prev_dn, par_dn);
+                                break;
+                            }
+                        }
+                        par_prev_dn = par_dn;
+                    }
+
+                    /*
+                     * Remaining parents need to be added
+                     * - at current state, this will never happen
+                     *   (no parents will remain), but that might change
+                     *   in the future
+                     */
+                    par_dn = parents->first;
+                    for (; par_dn; par_dn = par_dn->next) {
+                        mbof_add_dn(lastop->parents,
+                                    mbof_next_free_dn(lastop->parents),
+                                    par_dn->dn, par_dn->cnt);
+                    }
+
+                    /*
+                     * Rescheduled addops will be checked again once the entire
+                     * tree has been walked through. Parent refcounts and new
+                     * parents shall be updated then
+                     */
+                    if (lastop->status == OP_DONE)
+                        lastop->status = OP_RESCHEDULED;
                     return LDB_SUCCESS;
                 }
             } while (lastop->next);
@@ -400,6 +445,42 @@ static void mbof_delete_dn(struct mbof_dn_array *array,
     array->num--;
 }
 
+static int mbof_get_refcount(struct ldb_message_element *rc_el,
+                             const char *name)
+{
+    int i;
+    char *value;
+    char *rc = NULL;
+    TALLOC_CTX *tmp_ctx;
+    int ret;
+
+    tmp_ctx = talloc_new(NULL);
+
+    for (i = 0; i < rc_el->num_values; i++) {
+        value = talloc_strndup(tmp_ctx, (char *)rc_el->values[i].data,
+                               rc_el->values[i].length);
+        if (!value) {
+            ret = -1;
+            goto done;
+        }
+
+        rc = strchr(value, ':');
+        *rc = '\0';
+        if (strcmp(value, name) == 0) {
+            rc++;
+            ret = atoi(rc);
+            goto done;
+        }
+
+        talloc_free(value);
+    }
+
+    ret = 0; /* No occurences found */
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
 static int mbof_add_dn(struct mbof_dn_array *array, int pos, struct ldb_dn *dn,
                        unsigned ref_count)
 {
@@ -425,6 +506,94 @@ static int mbof_add_dn(struct mbof_dn_array *array, int pos, struct ldb_dn *dn,
     return LDB_SUCCESS;
 }
 
+static int mbof_request_from_addop(struct mbof_add_operation *addop,
+                                   struct mbof_dn_array *parents,
+                                   ldb_request_callback_t callback,
+                                   int ldb_flags, struct ldb_request **_req)
+{
+    struct mbof_ctx *ctx;
+    struct mbof_add_ctx *add_ctx;
+    struct ldb_context *ldb;
+    struct ldb_request *req;
+    struct ldb_message *msg;
+    struct ldb_message_element *el;
+    struct ldb_message_element *rc_el;
+    struct mbof_dn *cur_dn;
+    const char *val;
+    char *refcount;
+    int i, ret;
+
+    add_ctx = addop->add_ctx;
+    ctx = add_ctx->ctx;
+    ldb = ldb_module_get_ctx(ctx->module);
+
+    msg = ldb_msg_new(addop);
+    if (!msg) {
+        ret = LDB_ERR_OPERATIONS_ERROR;
+        goto fail;
+    }
+    msg->dn = addop->entry_dn;
+
+    ret = ldb_msg_add_empty(msg, DB_MEMBEROF, ldb_flags, &el);
+    if (ret != LDB_SUCCESS) {
+        goto fail;
+    }
+
+    ret = ldb_msg_add_empty(msg, DB_MEMBEROFRC, ldb_flags, &rc_el);
+    if (ret != LDB_SUCCESS) {
+        goto fail;
+    }
+
+    el->values = talloc_zero_array(msg, struct ldb_val, parents->num);
+    rc_el->values = talloc_zero_array(msg, struct ldb_val, parents->num);
+    if (!el->values || !rc_el->values) {
+        ret = LDB_ERR_OPERATIONS_ERROR;
+        goto fail;
+    }
+
+    i = 0;
+    for (cur_dn = parents->first; cur_dn; cur_dn = cur_dn->next) {
+        if (ldb_dn_compare(cur_dn->dn, msg->dn) == 0) {
+            continue;
+        }
+
+        /* The reference itself */
+        val = ldb_dn_get_linearized(cur_dn->dn);
+        el->values[i].length = strlen(val);
+        el->values[i].data = (uint8_t *)talloc_strdup(el->values, val);
+        if (!el->values[i].data) {
+            ret = LDB_ERR_OPERATIONS_ERROR;
+            goto fail;
+        }
+        /* And now the reference counter */
+        refcount = talloc_asprintf(rc_el->values, "%s:%u", val, cur_dn->cnt);
+        if (!refcount) {
+            ret = LDB_ERR_OPERATIONS_ERROR;
+            goto fail;
+        }
+        rc_el->values[i].length = strlen(refcount);
+        rc_el->values[i].data = (uint8_t *)refcount;
+        i++;
+    }
+    el->num_values = i;
+    rc_el->num_values = i;
+
+    ret = ldb_build_mod_req(&req, ldb, add_ctx, msg, NULL,
+                            add_ctx, callback,
+                            ctx->req);
+    if (ret != LDB_SUCCESS) {
+        goto fail;
+    }
+    talloc_steal(req, msg);
+    *_req = req;
+
+    return LDB_SUCCESS;
+
+fail:
+    talloc_free(msg);
+    return ret;
+}
+
 static int memberof_recompute_task(struct ldb_module *module,
                                    struct ldb_request *req);
 
@@ -441,6 +610,11 @@ static int mbof_add_cleanup_callback(struct ldb_request *req,
 static int mbof_add_muop(struct mbof_add_ctx *add_ctx);
 static int mbof_add_muop_callback(struct ldb_request *req,
                                   struct ldb_reply *ares);
+static int mbof_add_process_rescheduled(struct mbof_add_ctx *add_ctx);
+static int mbof_add_prre_callback(struct ldb_request *req,
+                                  struct ldb_reply *ares);
+static int mbof_add_prre_operation(struct mbof_add_operation *addop);
+static int mbof_add_prre_next(struct ldb_request *req, struct ldb_reply *ares);
 
 static int memberof_add(struct ldb_module *module, struct ldb_request *req)
 {
@@ -709,25 +883,17 @@ static int mbof_next_add_callback(struct ldb_request *req,
             if (ret != LDB_SUCCESS) {
                 return ldb_module_done(ctx->req, NULL, NULL, ret);
             }
+
             /* now try the next operation */
             if (add_ctx->current_op->next) {
                 ret = mbof_next_add(add_ctx->current_op->next);
             }
             else {
-                /* no more operations */
-                if (add_ctx->missing) {
-                    ret = mbof_add_cleanup(add_ctx);
-                }
-                else if (add_ctx->muops) {
-                    ret = mbof_add_muop(add_ctx);
-                }
-                else {
-                    return ldb_module_done(ctx->req,
-                                           ctx->ret_ctrls,
-                                           ctx->ret_resp,
-                                           LDB_SUCCESS);
-                }
+                /* no more operations - check for rescheduled */
+                add_ctx->current_op = add_ctx->add_list;
+                ret = mbof_add_process_rescheduled(add_ctx);
             }
+
             if (ret != LDB_SUCCESS) {
                 return ldb_module_done(ctx->req, NULL, NULL, ret);
             }
@@ -753,14 +919,12 @@ static int mbof_add_operation(struct mbof_add_operation *addop)
     struct mbof_ctx *ctx;
     struct mbof_add_ctx *add_ctx;
     struct ldb_context *ldb;
-    struct ldb_message_element *el;
     struct ldb_request *mod_req;
-    struct ldb_message *msg;
     struct ldb_dn *valdn;
     struct mbof_dn_array *parents;
+    struct ldb_message_element *el;
     struct mbof_dn *cur_dn;
     int i, ret;
-    const char *val;
     const char *name;
 
     add_ctx = addop->add_ctx;
@@ -841,46 +1005,13 @@ static int mbof_add_operation(struct mbof_add_operation *addop)
     talloc_free(addop->entry);
     addop->entry = NULL;
 
-    /* add memberof to entry */
-    msg = ldb_msg_new(addop);
-    if (!msg) return LDB_ERR_OPERATIONS_ERROR;
-
-    msg->dn = addop->entry_dn;
-
-    ret = ldb_msg_add_empty(msg, DB_MEMBEROF, LDB_FLAG_MOD_ADD, &el);
-    if (ret != LDB_SUCCESS) {
-        return ret;
-    }
-    el->values = talloc_array(msg, struct ldb_val, parents->num);
-    if (!el->values) {
-        return LDB_ERR_OPERATIONS_ERROR;
-    }
-
-    i = 0;
-    for (cur_dn = parents->first; cur_dn; cur_dn = cur_dn->next) {
-        if (ldb_dn_compare(cur_dn->dn, msg->dn) == 0) {
-            continue;
-        }
-
-        val = ldb_dn_get_linearized(cur_dn->dn);
-        el->values[i].length = strlen(val);
-        el->values[i].data = (uint8_t *)talloc_strdup(el->values, val);
-        if (!el->values[i].data) {
-            return LDB_ERR_OPERATIONS_ERROR;
-        }
-        i++;
-    }
-    el->num_values = i;
-
-    ret = ldb_build_mod_req(&mod_req, ldb, add_ctx,
-                            msg, NULL,
-                            add_ctx, mbof_add_callback,
-                            ctx->req);
+    ret = mbof_request_from_addop(addop, parents, mbof_add_callback,
+                                  LDB_FLAG_MOD_ADD, &mod_req);
     if (ret != LDB_SUCCESS) {
         return ret;
     }
-    talloc_steal(mod_req, msg);
 
+    addop->status = OP_DONE;
     return ldb_next_request(ctx->module, mod_req);
 }
 
@@ -1093,6 +1224,243 @@ static int mbof_add_muop_callback(struct ldb_request *req,
     return LDB_SUCCESS;
 }
 
+static int mbof_add_process_rescheduled(struct mbof_add_ctx *add_ctx)
+{
+    static const char *attrs[] = { DB_NAME, DB_MEMBEROF, DB_MEMBEROFRC, NULL };
+    struct mbof_add_operation *addop;
+    struct ldb_context *ldb;
+    struct ldb_request *req;
+    struct mbof_ctx *ctx;
+    bool found = false;
+    int ret;
+
+    for (addop = add_ctx->current_op; addop; addop = addop->next) {
+        if (addop->status == OP_RESCHEDULED) {
+            /* mark the operation as being handled */
+            add_ctx->current_op = addop;
+            found = true;
+            break;
+        }
+    }
+
+    ctx = add_ctx->ctx;
+    ldb = ldb_module_get_ctx(ctx->module);
+
+    if (!found) {
+        if (add_ctx->missing) {
+            ret = mbof_add_cleanup(add_ctx);
+        }
+        else if (add_ctx->muops) {
+            ret = mbof_add_muop(add_ctx);
+        }
+        else {
+            return ldb_module_done(ctx->req,
+                                   ctx->ret_ctrls,
+                                   ctx->ret_resp,
+                                   LDB_SUCCESS);
+        }
+        if (ret != LDB_SUCCESS) {
+            return ldb_module_done(ctx->req, NULL, NULL, ret);
+        }
+    }
+
+    ret = ldb_build_search_req(&req, ldb, ctx,
+                               addop->entry_dn, LDB_SCOPE_BASE,
+                               NULL, attrs, NULL,
+                               addop, mbof_add_prre_callback,
+                               ctx->req);
+    if (ret != LDB_SUCCESS) {
+        return ret;
+    }
+
+    return ldb_request(ldb, req);
+}
+
+static int mbof_add_prre_callback(struct ldb_request *req,
+                                  struct ldb_reply *ares)
+{
+    struct mbof_add_operation *addop;
+    struct mbof_add_ctx *add_ctx;
+    struct ldb_context *ldb;
+    struct mbof_ctx *ctx;
+    int ret;
+
+    addop = talloc_get_type(req->context, struct mbof_add_operation);
+    add_ctx = addop->add_ctx;
+    ctx = add_ctx->ctx;
+    ldb = ldb_module_get_ctx(ctx->module);
+
+    if (!ares) {
+        return ldb_module_done(ctx->req, NULL, NULL,
+                               LDB_ERR_OPERATIONS_ERROR);
+    }
+    if (ares->error != LDB_SUCCESS) {
+        return ldb_module_done(ctx->req,
+                               ares->controls,
+                               ares->response,
+                               ares->error);
+    }
+
+    switch (ares->type) {
+    case LDB_REPLY_ENTRY:
+        if (addop->entry != NULL) {
+            ldb_debug(ldb, LDB_DEBUG_TRACE,
+                           "Found multiple entries for (%s)",
+                           ldb_dn_get_linearized(addop->entry_dn));
+            /* more than one entry per dn ?? db corrupted ? */
+            return ldb_module_done(ctx->req, NULL, NULL,
+                                   LDB_ERR_OPERATIONS_ERROR);
+        }
+
+        addop->entry = talloc_steal(addop, ares->message);
+        if (addop->entry == NULL) {
+            return ldb_module_done(ctx->req, NULL, NULL,
+                                   LDB_ERR_OPERATIONS_ERROR);
+        }
+
+        break;
+    case LDB_REPLY_REFERRAL:
+        /* ignore */
+        break;
+
+    case LDB_REPLY_DONE:
+        /*
+         * Since the addop is rescheduled, the entry will always exist,
+         * skip the check for nonexistence
+         */
+        talloc_zfree(ares);
+        ret = mbof_add_prre_operation(addop);
+        if (ret != LDB_SUCCESS) {
+            return ldb_module_done(ctx->req, NULL, NULL, ret);
+        }
+    }
+
+    talloc_zfree(ares);
+    return LDB_SUCCESS;
+}
+
+static int mbof_add_prre_operation(struct mbof_add_operation *addop)
+{
+    struct ldb_request *req;
+    struct mbof_dn_array *parents;
+    struct mbof_dn *cur_dn;
+    struct ldb_message_element *el;
+    struct ldb_message_element *rc_el;
+    struct ldb_dn *valdn;
+    int i, ret;
+    int refcount;
+
+    struct ldb_context *ldb;
+    ldb = ldb_module_get_ctx(addop->add_ctx->ctx->module);
+
+    parents = talloc_zero(addop, struct mbof_dn_array);
+    if (!parents) {
+        return LDB_ERR_OPERATIONS_ERROR;
+    }
+
+    for (cur_dn = addop->parents->first; cur_dn; cur_dn = cur_dn->next) {
+        /* never add yourself as memberof */
+        if (ldb_dn_compare(cur_dn->dn, addop->entry_dn) == 0) {
+            continue;
+        }
+        ret = mbof_add_dn(parents, parents->num, cur_dn->dn, cur_dn->cnt);
+        if (ret != LDB_SUCCESS) {
+            goto fail;
+        }
+    }
+    el = ldb_msg_find_element(addop->entry, DB_MEMBEROF);
+    rc_el = ldb_msg_find_element(addop->entry, DB_MEMBEROFRC);
+    if (!el || !rc_el) {
+        ret = LDB_ERR_OPERATIONS_ERROR;
+        goto fail;
+    }
+
+    /* Now we need to get full list of memberofs and refcounts */
+    for (i = 0; i < el->num_values; i++) {
+        valdn = ldb_dn_from_ldb_val(addop->add_ctx, ldb, &el->values[i]);
+        for (cur_dn = addop->parents->first; cur_dn; cur_dn = cur_dn->next) {
+            if (ldb_dn_compare(valdn, cur_dn->dn) == 0) {
+                break;
+            }
+        }
+
+        /* this memberof is already in the list, possibly even with
+         * more current refcount - skip */
+        if (!cur_dn) {
+            refcount = mbof_get_refcount(rc_el, ldb_dn_get_linearized(valdn));
+            if (refcount < 0) {
+                ret = LDB_ERR_OPERATIONS_ERROR;
+                goto fail;
+            }
+            ret = mbof_add_dn(parents, mbof_next_free_dn(parents), valdn,
+                              refcount);
+            if (ret != LDB_SUCCESS) {
+                goto fail;
+            }
+        }
+    }
+
+
+    ret = mbof_request_from_addop(addop, parents, mbof_add_prre_next,
+                                  LDB_FLAG_MOD_REPLACE, &req);
+    if (ret != LDB_SUCCESS) {
+        goto fail;
+    }
+
+    addop->status = OP_DONE;
+    talloc_zfree(parents);
+    return ldb_next_request(addop->add_ctx->ctx->module, req);
+
+fail:
+    talloc_zfree(parents);
+    return ret;
+}
+
+static int mbof_add_prre_next(struct ldb_request *req,
+                              struct ldb_reply *ares)
+{
+    struct mbof_add_ctx *add_ctx;
+    struct mbof_ctx *ctx;
+    int ret;
+
+    add_ctx = talloc_get_type(req->context, struct mbof_add_ctx);
+    ctx = add_ctx->ctx;
+
+    if (!ares) {
+        return ldb_module_done(ctx->req, NULL, NULL,
+                               LDB_ERR_OPERATIONS_ERROR);
+    }
+    if (ares->error != LDB_SUCCESS) {
+        return ldb_module_done(ctx->req,
+                               ares->controls,
+                               ares->response,
+                               ares->error);
+    }
+
+    switch (ares->type) {
+    case LDB_REPLY_ENTRY:
+        /* shouldn't happen */
+        talloc_zfree(ares);
+        return ldb_module_done(ctx->req, NULL, NULL,
+                               LDB_ERR_OPERATIONS_ERROR);
+    case LDB_REPLY_REFERRAL:
+        /* ignore */
+        break;
+
+    case LDB_REPLY_DONE:
+        talloc_zfree(ares);
+        ret = mbof_add_process_rescheduled(add_ctx);
+        if (ret != LDB_SUCCESS) {
+            return ldb_module_done(ctx->req, NULL, NULL, ret);
+        }
+        return LDB_SUCCESS;
+    }
+
+    talloc_zfree(ares);
+    return LDB_SUCCESS;
+}
+
+
 
 
 
-- 
1.7.4.1

