ldb: add LDB_FLG_DONT_CREATE_DB
[samba.git] / lib / ldb / ldb_tdb / ldb_tdb.c
index 3630a185942b1c08ff12b18e6d97923d5f553e6e..8c4989f6095a48a6209db33cac7eff33750e0d22 100644 (file)
@@ -50,7 +50,8 @@
  */
 
 #include "ldb_tdb.h"
-#include <lib/tdb_compat/tdb_compat.h>
+#include "ldb_private.h"
+#include <tdb.h>
 
 /*
   prevent memory errors on callbacks
@@ -74,13 +75,9 @@ int ltdb_err_map(enum TDB_ERROR tdb_code)
        case TDB_ERR_IO:
                return LDB_ERR_PROTOCOL_ERROR;
        case TDB_ERR_LOCK:
-#ifndef BUILD_TDB2
        case TDB_ERR_NOLOCK:
-#endif
                return LDB_ERR_BUSY;
-#ifndef BUILD_TDB2
        case TDB_ERR_LOCK_TIMEOUT:
-#endif
                return LDB_ERR_TIME_LIMIT_EXCEEDED;
        case TDB_ERR_EXISTS:
                return LDB_ERR_ENTRY_ALREADY_EXISTS;
@@ -232,7 +229,13 @@ static int ltdb_modified(struct ldb_module *module, struct ldb_dn *dn)
 
        if (ldb_dn_is_special(dn) &&
            (ldb_dn_check_special(dn, LTDB_INDEXLIST) ||
-            ldb_dn_check_special(dn, LTDB_ATTRIBUTES)) ) {
+            ldb_dn_check_special(dn, LTDB_ATTRIBUTES)) )
+       {
+               if (ltdb->warn_reindex) {
+                       ldb_debug(ldb_module_get_ctx(module),
+                               LDB_DEBUG_ERROR, "Reindexing %s due to modification on %s",
+                               tdb_name(ltdb->tdb), ldb_dn_get_linearized(dn));
+               }
                ret = ltdb_reindex(module);
        }
 
@@ -261,6 +264,7 @@ int ltdb_store(struct ldb_module *module, const struct ldb_message *msg, int flg
        void *data = ldb_module_get_private(module);
        struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
        TDB_DATA tdb_key, tdb_data;
+       struct ldb_val ldb_data;
        int ret = LDB_SUCCESS;
 
        tdb_key = ltdb_key(module, msg->dn);
@@ -268,12 +272,16 @@ int ltdb_store(struct ldb_module *module, const struct ldb_message *msg, int flg
                return LDB_ERR_OTHER;
        }
 
-       ret = ltdb_pack_data(module, msg, &tdb_data);
+       ret = ldb_pack_data(ldb_module_get_ctx(module),
+                           msg, &ldb_data);
        if (ret == -1) {
                talloc_free(tdb_key.dptr);
                return LDB_ERR_OTHER;
        }
 
+       tdb_data.dptr = ldb_data.data;
+       tdb_data.dsize = ldb_data.length;
+
        ret = tdb_store(ltdb->tdb, tdb_key, tdb_data, flgs);
        if (ret != 0) {
                ret = ltdb_err_map(tdb_error(ltdb->tdb));
@@ -282,7 +290,7 @@ int ltdb_store(struct ldb_module *module, const struct ldb_message *msg, int flg
 
 done:
        talloc_free(tdb_key.dptr);
-       talloc_free(tdb_data.dptr);
+       talloc_free(ldb_data.data);
 
        return ret;
 }
@@ -322,7 +330,7 @@ static int ltdb_add_internal(struct ldb_module *module,
 {
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        int ret = LDB_SUCCESS;
-       unsigned int i;
+       unsigned int i, j;
 
        for (i=0;i<msg->num_elements;i++) {
                struct ldb_message_element *el = &msg->elements[i];
@@ -340,6 +348,22 @@ static int ltdb_add_internal(struct ldb_module *module,
                                               el->name, ldb_dn_get_linearized(msg->dn));
                        return LDB_ERR_CONSTRAINT_VIOLATION;
                }
+
+               /* Do not check "@ATTRIBUTES" for duplicated values */
+               if (ldb_dn_is_special(msg->dn) &&
+                   ldb_dn_check_special(msg->dn, LTDB_ATTRIBUTES)) {
+                       continue;
+               }
+
+               /* TODO: This is O(n^2) - replace with more efficient check */
+               for (j=0; j<el->num_values; j++) {
+                       if (ldb_msg_find_val(el, &el->values[j]) != &el->values[j]) {
+                               ldb_asprintf_errstring(ldb,
+                                                      "attribute '%s': value #%u on '%s' provided more than once",
+                                                      el->name, j, ldb_dn_get_linearized(msg->dn));
+                               return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
+                       }
+               }
        }
 
        ret = ltdb_store(module, msg, TDB_INSERT);
@@ -425,7 +449,7 @@ static int ltdb_delete_internal(struct ldb_module *module, struct ldb_dn *dn)
 
        /* in case any attribute of the message was indexed, we need
           to fetch the old record */
-       ret = ltdb_search_dn1(module, dn, msg);
+       ret = ltdb_search_dn1(module, dn, msg, LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC);
        if (ret != LDB_SUCCESS) {
                /* not finding the old record is an error */
                goto done;
@@ -653,6 +677,7 @@ int ltdb_modify_internal(struct ldb_module *module,
        void *data = ldb_module_get_private(module);
        struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
        TDB_DATA tdb_key, tdb_data;
+       struct ldb_val ldb_data;
        struct ldb_message *msg2;
        unsigned int i, j, k;
        int ret = LDB_SUCCESS, idx;
@@ -668,7 +693,7 @@ int ltdb_modify_internal(struct ldb_module *module,
                return LDB_ERR_OTHER;
        }
 
-       tdb_data = tdb_fetch_compat(ltdb->tdb, tdb_key);
+       tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
        if (!tdb_data.dptr) {
                talloc_free(tdb_key.dptr);
                return ltdb_err_map(tdb_error(ltdb->tdb));
@@ -681,7 +706,10 @@ int ltdb_modify_internal(struct ldb_module *module,
                goto done;
        }
 
-       ret = ltdb_unpack_data(module, &tdb_data, msg2);
+       ldb_data.data = tdb_data.dptr;
+       ldb_data.length = tdb_data.dsize;
+
+       ret = ldb_unpack_data(ldb_module_get_ctx(module), &ldb_data, msg2);
        free(tdb_data.dptr);
        if (ret == -1) {
                ret = LDB_ERR_OTHER;
@@ -765,6 +793,7 @@ int ltdb_modify_internal(struct ldb_module *module,
 
                                /* Check that values don't exist yet on multi-
                                   valued attributes or aren't provided twice */
+                               /* TODO: This is O(n^2) - replace with more efficient check */
                                for (j = 0; j < el->num_values; j++) {
                                        if (ldb_msg_find_val(el2, &el->values[j]) != NULL) {
                                                if (control_permissive) {
@@ -829,14 +858,22 @@ int ltdb_modify_internal(struct ldb_module *module,
                                goto done;
                        }
 
-                       /* TODO: This is O(n^2) - replace with more efficient check */
-                       for (j=0; j<el->num_values; j++) {
-                               if (ldb_msg_find_val(el, &el->values[j]) != &el->values[j]) {
-                                       ldb_asprintf_errstring(ldb,
-                                                              "attribute '%s': value #%u on '%s' provided more than once",
-                                                              el->name, j, ldb_dn_get_linearized(msg2->dn));
-                                       ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
-                                       goto done;
+                       /*
+                        * We don't need to check this if we have been
+                        * pre-screened by the repl_meta_data module
+                        * in Samba, or someone else who can claim to
+                        * know what they are doing. 
+                        */
+                       if (!(el->flags & LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK)) { 
+                               /* TODO: This is O(n^2) - replace with more efficient check */
+                               for (j=0; j<el->num_values; j++) {
+                                       if (ldb_msg_find_val(el, &el->values[j]) != &el->values[j]) {
+                                               ldb_asprintf_errstring(ldb,
+                                                                      "attribute '%s': value #%u on '%s' provided more than once",
+                                                                      el->name, j, ldb_dn_get_linearized(msg2->dn));
+                                               ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
+                                               goto done;
+                                       }
                                }
                        }
 
@@ -845,11 +882,18 @@ int ltdb_modify_internal(struct ldb_module *module,
                        if (idx != -1) {
                                j = (unsigned int) idx;
                                el2 = &(msg2->elements[j]);
-                               if (ldb_msg_element_compare(el, el2) == 0) {
-                                       /* we are replacing with the same values */
+
+                               /* we consider two elements to be
+                                * equal only if the order
+                                * matches. This allows dbcheck to
+                                * fix the ordering on attributes
+                                * where order matters, such as
+                                * objectClass
+                                */
+                               if (ldb_msg_element_equal_ordered(el, el2)) {
                                        continue;
                                }
-                       
+
                                /* Delete the attribute if it exists in the DB */
                                if (msg_delete_attribute(module, ldb, msg2,
                                                         el->name) != 0) {
@@ -903,7 +947,7 @@ int ltdb_modify_internal(struct ldb_module *module,
                                        if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE &&
                                            control_permissive) {
                                                ret = LDB_SUCCESS;
-                                       } else {
+                                       } else if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
                                                ldb_asprintf_errstring(ldb,
                                                                       "attribute '%s': no matching attribute value while deleting attribute on '%s'",
                                                                       msg->elements[i].name, dn);
@@ -970,9 +1014,12 @@ static int ltdb_modify(struct ltdb_context *ctx)
 static int ltdb_rename(struct ltdb_context *ctx)
 {
        struct ldb_module *module = ctx->module;
+       void *data = ldb_module_get_private(module);
+       struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
        struct ldb_request *req = ctx->req;
        struct ldb_message *msg;
        int ret = LDB_SUCCESS;
+       TDB_DATA tdb_key, tdb_key_old;
 
        ldb_request_set_state(req, LDB_ASYNC_PENDING);
 
@@ -985,25 +1032,59 @@ static int ltdb_rename(struct ltdb_context *ctx)
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /* in case any attribute of the message was indexed, we need
-          to fetch the old record */
-       ret = ltdb_search_dn1(module, req->op.rename.olddn, msg);
+       /* we need to fetch the old record to re-add under the new name */
+       ret = ltdb_search_dn1(module, req->op.rename.olddn, msg,
+                             LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC);
        if (ret != LDB_SUCCESS) {
                /* not finding the old record is an error */
                return ret;
        }
 
+       /* We need to, before changing the DB, check if the new DN
+        * exists, so we can return this error to the caller with an
+        * unmodified DB */
+       tdb_key = ltdb_key(module, req->op.rename.newdn);
+       if (!tdb_key.dptr) {
+               talloc_free(msg);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       tdb_key_old = ltdb_key(module, req->op.rename.olddn);
+       if (!tdb_key_old.dptr) {
+               talloc_free(msg);
+               talloc_free(tdb_key.dptr);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* Only declare a conflict if the new DN already exists, and it isn't a case change on the old DN */
+       if (tdb_key_old.dsize != tdb_key.dsize || memcmp(tdb_key.dptr, tdb_key_old.dptr, tdb_key.dsize) != 0) {
+               if (tdb_exists(ltdb->tdb, tdb_key)) {
+                       talloc_free(tdb_key_old.dptr);
+                       talloc_free(tdb_key.dptr);
+                       ldb_asprintf_errstring(ldb_module_get_ctx(module),
+                                              "Entry %s already exists",
+                                              ldb_dn_get_linearized(req->op.rename.newdn));
+                       /* finding the new record already in the DB is an error */
+                       talloc_free(msg);
+                       return LDB_ERR_ENTRY_ALREADY_EXISTS;
+               }
+       }
+       talloc_free(tdb_key_old.dptr);
+       talloc_free(tdb_key.dptr);
+
        /* Always delete first then add, to avoid conflicts with
         * unique indexes. We rely on the transaction to make this
         * atomic
         */
        ret = ltdb_delete_internal(module, msg->dn);
        if (ret != LDB_SUCCESS) {
+               talloc_free(msg);
                return ret;
        }
 
        msg->dn = ldb_dn_copy(msg, req->op.rename.newdn);
        if (msg->dn == NULL) {
+               talloc_free(msg);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
@@ -1013,6 +1094,8 @@ static int ltdb_rename(struct ltdb_context *ctx)
         */
        ret = ltdb_add_internal(module, msg, false);
 
+       talloc_free(msg);
+
        return ret;
 }
 
@@ -1150,7 +1233,7 @@ static int ltdb_sequence_number(struct ltdb_context *ctx,
                goto done;
        }
 
-       ret = ltdb_search_dn1(module, dn, msg);
+       ret = ltdb_search_dn1(module, dn, msg, 0);
        if (ret != LDB_SUCCESS) {
                goto done;
        }
@@ -1395,11 +1478,15 @@ static int ltdb_handle_request(struct ldb_module *module,
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       tv.tv_sec = req->starttime + req->timeout;
-       ac->timeout_event = tevent_add_timer(ev, ac, tv, ltdb_timeout, ac);
-       if (NULL == ac->timeout_event) {
-               talloc_free(ac);
-               return LDB_ERR_OPERATIONS_ERROR;
+       if (req->timeout > 0) {
+               tv.tv_sec = req->starttime + req->timeout;
+               tv.tv_usec = 0;
+               ac->timeout_event = tevent_add_timer(ev, ac, tv,
+                                                    ltdb_timeout, ac);
+               if (NULL == ac->timeout_event) {
+                       talloc_free(ac);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
        }
 
        /* set a spy so that we do not try to use the request context
@@ -1418,14 +1505,8 @@ static int ltdb_handle_request(struct ldb_module *module,
 
 static int ltdb_init_rootdse(struct ldb_module *module)
 {
-       struct ldb_context *ldb;
-       int ret;
-
-       ldb = ldb_module_get_ctx(module);
-
-       ret = ldb_mod_register_control(module,
-                                      LDB_CONTROL_PERMISSIVE_MODIFY_OID);
        /* ignore errors on this - we expect it for non-sam databases */
+       ldb_mod_register_control(module, LDB_CONTROL_PERMISSIVE_MODIFY_OID);
 
        /* there can be no module beyond the backend, just return */
        return LDB_SUCCESS;
@@ -1484,6 +1565,8 @@ static int ltdb_connect(struct ldb_context *ldb, const char *url,
 
        if (flags & LDB_FLG_RDONLY) {
                open_flags = O_RDONLY;
+       } else if (flags & LDB_FLG_DONT_CREATE_DB) {
+               open_flags = O_RDWR;
        } else {
                open_flags = O_CREAT | O_RDWR;
        }
@@ -1499,9 +1582,14 @@ static int ltdb_connect(struct ldb_context *ldb, const char *url,
                                   tdb_flags, open_flags,
                                   ldb_get_create_perms(ldb), ldb);
        if (!ltdb->tdb) {
+               ldb_asprintf_errstring(ldb,
+                                      "Unable to open tdb '%s': %s", path, strerror(errno));
                ldb_debug(ldb, LDB_DEBUG_ERROR,
-                         "Unable to open tdb '%s'", path);
+                         "Unable to open tdb '%s': %s", path, strerror(errno));
                talloc_free(ltdb);
+               if (errno == EACCES || errno == EPERM) {
+                       return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+               }
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
@@ -1509,10 +1597,15 @@ static int ltdb_connect(struct ldb_context *ldb, const char *url,
                ltdb->warn_unindexed = true;
        }
 
+       if (getenv("LDB_WARN_REINDEX")) {
+               ltdb->warn_reindex = true;
+       }
+
        ltdb->sequence_number = 0;
 
        module = ldb_module_new(ldb, ldb, "ldb_tdb backend", &ltdb_ops);
        if (!module) {
+               ldb_oom(ldb);
                talloc_free(ltdb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
@@ -1520,6 +1613,8 @@ static int ltdb_connect(struct ldb_context *ldb, const char *url,
        talloc_steal(module, ltdb);
 
        if (ltdb_cache_load(module) != 0) {
+               ldb_asprintf_errstring(ldb,
+                                      "Unable to load ltdb cache records of tdb '%s'", path);
                talloc_free(module);
                return LDB_ERR_OPERATIONS_ERROR;
        }