r11567: Ldb API change patch.
[abartlet/samba.git/.git] / source4 / lib / ldb / ldb_tdb / ldb_search.c
index fe84091303dd0f8730212d27706ece63ececc904..01a87e00b19665be025cc896fc28844f233bac88 100644 (file)
  */
 
 #include "includes.h"
+#include "ldb/include/ldb.h"
+#include "ldb/include/ldb_errors.h"
+#include "ldb/include/ldb_private.h"
 #include "ldb/ldb_tdb/ldb_tdb.h"
 
-/*
-  free a message that has all parts separately allocated
-*/
-static void msg_free_all_parts(struct ldb_message *msg)
-{
-       int i, j;
-       if (msg->dn) free(msg->dn);
-       for (i=0;i<msg->num_elements;i++) {
-               if (msg->elements[i].name) free(msg->elements[i].name);
-               for (j=0;j<msg->elements[i].num_values;j++) {
-                       if (msg->elements[i].values[j].data) 
-                               free(msg->elements[i].values[j].data);
-               }
-               if (msg->elements[i].values) free(msg->elements[i].values);
-       }
-       free(msg->elements);
-       free(msg);
-}
-
-
-/*
-  duplicate a ldb_val structure
-*/
-struct ldb_val ldb_val_dup(const struct ldb_val *v)
-{
-       struct ldb_val v2;
-       v2.length = v->length;
-       if (v->length == 0) {
-               v2.data = NULL;
-               return v2;
-       }
-
-       /* the +1 is to cope with buggy C library routines like strndup
-          that look one byte beyond */
-       v2.data = malloc(v->length+1);
-       if (!v2.data) {
-               v2.length = 0;
-               return v2;
-       }
-
-       memcpy(v2.data, v->data, v->length);
-       ((char *)v2.data)[v->length] = 0;
-       return v2;
-}
-
-
-
 /*
   add one element to a message
 */
-static int msg_add_element(struct ldb_message *ret, const struct ldb_message_element *el)
+static int msg_add_element(struct ldb_context *ldb, 
+                          struct ldb_message *ret, 
+                          const struct ldb_message_element *el,
+                          int check_duplicates)
 {
-       int i;
+       unsigned int i;
        struct ldb_message_element *e2, *elnew;
 
-       e2 = realloc_p(ret->elements, struct ldb_message_element, ret->num_elements+1);
+       if (check_duplicates && ldb_msg_find_element(ret, el->name)) {
+               /* its already there */
+               return 0;
+       }
+
+       e2 = talloc_realloc(ret, ret->elements, struct ldb_message_element, ret->num_elements+1);
        if (!e2) {
                return -1;
        }
@@ -98,13 +62,13 @@ static int msg_add_element(struct ldb_message *ret, const struct ldb_message_ele
        
        elnew = &e2[ret->num_elements];
 
-       elnew->name = strdup(el->name);
+       elnew->name = talloc_strdup(ret->elements, el->name);
        if (!elnew->name) {
                return -1;
        }
 
        if (el->num_values) {
-               elnew->values = malloc_array_p(struct ldb_val, el->num_values);
+               elnew->values = talloc_array(ret->elements, struct ldb_val, el->num_values);
                if (!elnew->values) {
                        return -1;
                }
@@ -113,7 +77,7 @@ static int msg_add_element(struct ldb_message *ret, const struct ldb_message_ele
        }
 
        for (i=0;i<el->num_values;i++) {
-               elnew->values[i] = ldb_val_dup(&el->values[i]);
+               elnew->values[i] = ldb_val_dup(elnew->values, &el->values[i]);
                if (elnew->values[i].length != el->values[i].length) {
                        return -1;
                }
@@ -126,15 +90,48 @@ static int msg_add_element(struct ldb_message *ret, const struct ldb_message_ele
        return 0;
 }
 
+/*
+  add the special distinguishedName element
+*/
+static int msg_add_distinguished_name(struct ldb_module *module, struct ldb_message *msg)
+{
+       struct ldb_message_element el;
+       struct ldb_val val;
+       int ret;
+
+       el.flags = 0;
+       el.name = "distinguishedName";
+       el.num_values = 1;
+       el.values = &val;
+       val.data = (uint8_t *)ldb_dn_linearize(msg, msg->dn);
+       val.length = strlen((char *)val.data);
+       
+       ret = msg_add_element(module->ldb, msg, &el, 1);
+       return ret;
+}
+
 /*
   add all elements from one message into another
  */
-static int msg_add_all_elements(struct ldb_message *ret,
+static int msg_add_all_elements(struct ldb_module *module, struct ldb_message *ret,
                                const struct ldb_message *msg)
 {
-       int i;
+       struct ldb_context *ldb = module->ldb;
+       unsigned int i;
+       int check_duplicates = (ret->num_elements != 0);
+
+       if (msg_add_distinguished_name(module, ret) != 0) {
+               return -1;
+       }
+
        for (i=0;i<msg->num_elements;i++) {
-               if (msg_add_element(ret, &msg->elements[i]) != 0) {
+               const struct ldb_attrib_handler *h;
+               h = ldb_attrib_handler(ldb, msg->elements[i].name);
+               if (h->flags & LDB_ATTR_FLAG_HIDDEN) {
+                       continue;
+               }
+               if (msg_add_element(ldb, ret, &msg->elements[i],
+                                   check_duplicates) != 0) {
                        return -1;
                }
        }
@@ -146,31 +143,31 @@ static int msg_add_all_elements(struct ldb_message *ret,
 /*
   pull the specified list of attributes from a message
  */
-static struct ldb_message *ltdb_pull_attrs(struct ldb_context *ldb
+static struct ldb_message *ltdb_pull_attrs(struct ldb_module *module
                                           const struct ldb_message *msg, 
-                                          const char **attrs)
+                                          const char * const *attrs)
 {
+       struct ldb_context *ldb = module->ldb;
        struct ldb_message *ret;
        int i;
 
-       ret = malloc_p(struct ldb_message);
+       ret = talloc(ldb, struct ldb_message);
        if (!ret) {
                return NULL;
        }
 
-       ret->dn = strdup(msg->dn);
+       ret->dn = ldb_dn_copy(ret, msg->dn);
        if (!ret->dn) {
-               free(ret);
+               talloc_free(ret);
                return NULL;
        }
 
        ret->num_elements = 0;
        ret->elements = NULL;
-       ret->private_data = NULL;
 
        if (!attrs) {
-               if (msg_add_all_elements(ret, msg) != 0) {
-                       msg_free_all_parts(ret);
+               if (msg_add_all_elements(module, ret, msg) != 0) {
+                       talloc_free(ret);
                        return NULL;
                }
                return ret;
@@ -180,8 +177,15 @@ static struct ldb_message *ltdb_pull_attrs(struct ldb_context *ldb,
                struct ldb_message_element *el;
 
                if (strcmp(attrs[i], "*") == 0) {
-                       if (msg_add_all_elements(ret, msg) != 0) {
-                               msg_free_all_parts(ret);
+                       if (msg_add_all_elements(module, ret, msg) != 0) {
+                               talloc_free(ret);
+                               return NULL;
+                       }
+                       continue;
+               }
+
+               if (ldb_attr_cmp(attrs[i], "distinguishedName") == 0) {
+                       if (msg_add_distinguished_name(module, ret) != 0) {
                                return NULL;
                        }
                        continue;
@@ -191,8 +195,8 @@ static struct ldb_message *ltdb_pull_attrs(struct ldb_context *ldb,
                if (!el) {
                        continue;
                }
-               if (msg_add_element(ret, el) != 0) {
-                       msg_free_all_parts(ret);
+               if (msg_add_element(ldb, ret, el, 1) != 0) {
+                       talloc_free(ret);
                        return NULL;                            
                }
        }
@@ -201,140 +205,123 @@ static struct ldb_message *ltdb_pull_attrs(struct ldb_context *ldb,
 }
 
 
-
-/*
-  see if a ldb_val is a wildcard
-*/
-int ltdb_has_wildcard(const struct ldb_val *val)
-{
-       if (val->length == 1 && ((char *)val->data)[0] == '*') {
-               return 1;
-       }
-       return 0;
-}
-
-
-/*
-  free the results of a ltdb_search_dn1 search
-*/
-void ltdb_search_dn1_free(struct ldb_context *ldb, struct ldb_message *msg)
-{
-       int i;
-       if (msg->dn) free(msg->dn);
-       if (msg->private_data) free(msg->private_data);
-       for (i=0;i<msg->num_elements;i++) {
-               if (msg->elements[i].values) free(msg->elements[i].values);
-       }
-       if (msg->elements) free(msg->elements);
-}
-
-
 /*
   search the database for a single simple dn, returning all attributes
   in a single message
 
   return 1 on success, 0 on record-not-found and -1 on error
 */
-int ltdb_search_dn1(struct ldb_context *ldb, const char *dn, struct ldb_message *msg)
+int ltdb_search_dn1(struct ldb_module *module, const struct ldb_dn *dn, struct ldb_message *msg)
 {
-       struct ltdb_private *ltdb = ldb->private_data;
+       struct ltdb_private *ltdb = module->private_data;
        int ret;
-       TDB_DATA tdb_key, tdb_data;
+       TDB_DATA tdb_key, tdb_data, tdb_data2;
+
+       memset(msg, 0, sizeof(*msg));
 
        /* form the key */
-       tdb_key = ltdb_key(dn);
+       tdb_key = ltdb_key(module, dn);
        if (!tdb_key.dptr) {
                return -1;
        }
 
        tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
-       free(tdb_key.dptr);
+       talloc_free(tdb_key.dptr);
        if (!tdb_data.dptr) {
                return 0;
        }
 
-       msg->dn = strdup(dn);
-       if (!msg->dn) {
-               free(tdb_data.dptr);
+       tdb_data2.dptr = talloc_memdup(msg, tdb_data.dptr, tdb_data.dsize);
+       free(tdb_data.dptr);
+       if (!tdb_data2.dptr) {
                return -1;
        }
-       msg->private_data = tdb_data.dptr;
+       tdb_data2.dsize = tdb_data.dsize;
+
        msg->num_elements = 0;
        msg->elements = NULL;
 
-       ret = ltdb_unpack_data(ldb, &tdb_data, msg);
+       ret = ltdb_unpack_data(module, &tdb_data2, msg);
        if (ret == -1) {
-               free(tdb_data.dptr);
+               talloc_free(tdb_data2.dptr);
                return -1;              
        }
 
+       if (!msg->dn) {
+               msg->dn = ldb_dn_copy(tdb_data2.dptr, dn);
+       }
+       if (!msg->dn) {
+               talloc_free(tdb_data2.dptr);
+               return -1;
+       }
+
        return 1;
 }
 
+/* the lock key for search locking. Note that this is not a DN, its
+   just an arbitrary key to give to tdb. Also note that as we and
+   using transactions for all write operations and transactions take
+   care of their own locks, we don't need to do any locking anywhere
+   other than in ldb_search() */
+#define LDBLOCK        "INT_LDBLOCK"
 
 /*
-  search the database for a single simple dn
+  lock the database for read - use by ltdb_search
 */
-int ltdb_search_dn(struct ldb_context *ldb, char *dn,
-                  const char *attrs[], struct ldb_message ***res)
+static int ltdb_lock_read(struct ldb_module *module)
 {
-       int ret;
-       struct ldb_message msg, *msg2;
-
-       ret = ltdb_search_dn1(ldb, dn, &msg);
-       if (ret != 1) {
-               return ret;
-       }
+       struct ltdb_private *ltdb = module->private_data;
+       TDB_DATA key;
 
-       msg2 = ltdb_pull_attrs(ldb, &msg, attrs);
+       key.dptr = discard_const(LDBLOCK);
+       key.dsize = strlen(LDBLOCK);
 
-       ltdb_search_dn1_free(ldb, &msg);
-
-       if (!msg2) {
-               return -1;              
-       }
+       return tdb_chainlock_read(ltdb->tdb, key);
+}
 
-       *res = malloc_array_p(struct ldb_message *, 2);
-       if (! *res) {
-               msg_free_all_parts(msg2);
-               return -1;              
-       }
+/*
+  unlock the database after a ltdb_lock_read()
+*/
+static int ltdb_unlock_read(struct ldb_module *module)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       TDB_DATA key;
 
-       (*res)[0] = msg2;
-       (*res)[1] = NULL;
+       key.dptr = discard_const(LDBLOCK);
+       key.dsize = strlen(LDBLOCK);
 
-       return 1;
+       return tdb_chainunlock_read(ltdb->tdb, key);
 }
 
-
 /*
   add a set of attributes from a record to a set of results
   return 0 on success, -1 on failure
 */
-int ltdb_add_attr_results(struct ldb_context *ldb, struct ldb_message *msg,
-                         const char *attrs[], 
+int ltdb_add_attr_results(struct ldb_module *module, struct ldb_message *msg,
+                         const char * const attrs[], 
                          unsigned int *count, 
                          struct ldb_message ***res)
 {
+       struct ldb_context *ldb = module->ldb;
        struct ldb_message *msg2;
        struct ldb_message **res2;
 
        /* pull the attributes that the user wants */
-       msg2 = ltdb_pull_attrs(ldb, msg, attrs);
+       msg2 = ltdb_pull_attrs(module, msg, attrs);
        if (!msg2) {
                return -1;
        }
 
        /* add to the results list */
-       res2 = realloc_p(*res, struct ldb_message *, (*count)+2);
+       res2 = talloc_realloc(ldb, *res, struct ldb_message *, (*count)+2);
        if (!res2) {
-               msg_free_all_parts(msg2);
+               talloc_free(msg2);
                return -1;
        }
 
        (*res) = res2;
 
-       (*res)[*count] = msg2;
+       (*res)[*count] = talloc_steal(*res, msg2);
        (*res)[(*count)+1] = NULL;
        (*count)++;
 
@@ -346,14 +333,14 @@ int ltdb_add_attr_results(struct ldb_context *ldb, struct ldb_message *msg,
   internal search state during a full db search
 */
 struct ltdb_search_info {
-       struct ldb_context *ldb;
+       struct ldb_module *module;
        struct ldb_parse_tree *tree;
-       const char *base;
+       const struct ldb_dn *base;
        enum ldb_scope scope;
-       const char **attrs;
+       const char * const *attrs;
        struct ldb_message **msgs;
-       int failures;
-       int count;
+       unsigned int failures;
+       unsigned int count;
 };
 
 
@@ -363,92 +350,104 @@ struct ltdb_search_info {
 static int search_func(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
 {
        struct ltdb_search_info *sinfo = state;
-       struct ldb_message msg;
+       struct ldb_message *msg;
        int ret;
 
        if (key.dsize < 4 || 
-           strncmp(key.dptr, "DN=", 3) != 0) {
+           strncmp((char *)key.dptr, "DN=", 3) != 0) {
                return 0;
        }
 
-       msg.dn = key.dptr + 3;
+       msg = talloc(sinfo, struct ldb_message);
+       if (msg == NULL) {
+               return -1;
+       }
 
        /* unpack the record */
-       ret = ltdb_unpack_data(sinfo->ldb, &data, &msg);
+       ret = ltdb_unpack_data(sinfo->module, &data, msg);
        if (ret == -1) {
                sinfo->failures++;
+               talloc_free(msg);
                return 0;
        }
 
+       if (!msg->dn) {
+               msg->dn = ldb_dn_explode(msg, (char *)key.dptr + 3);
+               if (msg->dn == NULL) {
+                       talloc_free(msg);
+                       return -1;
+               }
+       }
+
        /* see if it matches the given expression */
-       if (!ldb_message_match(sinfo->ldb, &msg, sinfo->tree, 
+       if (!ldb_match_msg(sinfo->module->ldb, msg, sinfo->tree, 
                               sinfo->base, sinfo->scope)) {
-               ltdb_unpack_data_free(&msg);
+               talloc_free(msg);
                return 0;
        }
 
-       ret = ltdb_add_attr_results(sinfo->ldb, &msg, sinfo->attrs, &sinfo->count, &sinfo->msgs);
+       ret = ltdb_add_attr_results(sinfo->module, msg, sinfo->attrs, &sinfo->count, &sinfo->msgs);
 
        if (ret == -1) {
                sinfo->failures++;
        }
 
-       ltdb_unpack_data_free(&msg);
+       talloc_free(msg);
 
        return ret;
 }
 
 
-/*
-  free a set of search results
-*/
-int ltdb_search_free(struct ldb_context *ldb, struct ldb_message **msgs)
-{
-       int i;
-
-       if (!msgs) return 0;
-
-       for (i=0;msgs[i];i++) {
-               msg_free_all_parts(msgs[i]);
-       }
-
-       free(msgs);
-
-       return 0;
-}
-
 /*
   search the database with a LDAP-like expression.
-  this is the "full search" non-indexed varient
+  this is the "full search" non-indexed variant
 */
-static int ltdb_search_full(struct ldb_context *ldb
-                           const char *base,
+static int ltdb_search_full(struct ldb_module *module
+                           const struct ldb_dn *base,
                            enum ldb_scope scope,
                            struct ldb_parse_tree *tree,
-                           const char *attrs[], struct ldb_message ***res)
+                           const char * const attrs[], struct ldb_result **res)
 {
-       struct ltdb_private *ltdb = ldb->private_data;
+       struct ltdb_private *ltdb = module->private_data;
+       struct ldb_result *result;
        int ret;
-       struct ltdb_search_info sinfo;
+       struct ltdb_search_info *sinfo;
 
-       sinfo.tree = tree;
-       sinfo.ldb = ldb;
-       sinfo.scope = scope;
-       sinfo.base = base;
-       sinfo.attrs = attrs;
-       sinfo.msgs = NULL;
-       sinfo.count = 0;
-       sinfo.failures = 0;
+       result = talloc(ltdb, struct ldb_result);
+       if (result == NULL) {
+               return LDB_ERR_OTHER;
+       }
 
-       ret = tdb_traverse(ltdb->tdb, search_func, &sinfo);
+       sinfo = talloc(ltdb, struct ltdb_search_info);
+       if (sinfo == NULL) {
+               talloc_free(result);
+               return LDB_ERR_OTHER;
+       }
+
+       sinfo->tree = tree;
+       sinfo->module = module;
+       sinfo->scope = scope;
+       sinfo->base = base;
+       sinfo->attrs = attrs;
+       sinfo->msgs = NULL;
+       sinfo->count = 0;
+       sinfo->failures = 0;
+
+       ret = tdb_traverse_read(ltdb->tdb, search_func, sinfo);
 
        if (ret == -1) {
-               ltdb_search_free(ldb, sinfo.msgs);
+               talloc_free(result);
+               talloc_free(sinfo);
                return -1;
        }
 
-       *res = sinfo.msgs;
-       return sinfo.count;
+       result->msgs = talloc_steal(result, sinfo->msgs);
+       result->count = sinfo->count;
+       *res = result;
+
+       talloc_free(sinfo);
+
+       return LDB_SUCCESS;
 }
 
 
@@ -456,35 +455,34 @@ static int ltdb_search_full(struct ldb_context *ldb,
   search the database with a LDAP-like expression.
   choses a search method
 */
-int ltdb_search(struct ldb_context *ldb, const char *base,
-               enum ldb_scope scope, const char *expression,
-               const char *attrs[], struct ldb_message ***res)
+int ltdb_search_bytree(struct ldb_module *module, const struct ldb_dn *base,
+                      enum ldb_scope scope, struct ldb_parse_tree *tree,
+                      const char * const attrs[], struct ldb_result **res)
 {
-       struct ldb_parse_tree *tree;
        int ret;
 
-       *res = NULL;
+       if ((base == NULL || base->comp_num == 0) &&
+           (scope == LDB_SCOPE_BASE || scope == LDB_SCOPE_ONELEVEL)) return -1;
 
-       /* form a parse tree for the expression */
-       tree = ldb_parse_tree(expression);
-       if (!tree) {
+       if (ltdb_lock_read(module) != 0) {
                return -1;
        }
 
-       if (tree->operation == LDB_OP_SIMPLE && 
-           strcmp(tree->u.simple.attr, "dn") == 0 &&
-           !ltdb_has_wildcard(&tree->u.simple.value)) {
-               /* yay! its a nice simple one */
-               ret = ltdb_search_dn(ldb, tree->u.simple.value.data, attrs, res);
-       } else {
-               ret = ltdb_search_indexed(ldb, base, scope, tree, attrs, res);
-               if (ret == -1) {
-                       ret = ltdb_search_full(ldb, base, scope, tree, attrs, res);
-               }
+       if (ltdb_cache_load(module) != 0) {
+               ltdb_unlock_read(module);
+               return -1;
        }
 
-       ldb_parse_tree_free(tree);
+       *res = NULL;
+
+       ret = ltdb_search_indexed(module, base, scope, tree, attrs, res);
+       if (ret == -1) {
+               ret = ltdb_search_full(module, base, scope, tree, attrs, res);
+       }
+
+       ltdb_unlock_read(module);
 
        return ret;
 }
 
+