s4:dsdb: add dsdb_module_constrainted_update_int32/64() functions
[kamenim/samba.git] / source4 / dsdb / samdb / ldb_modules / util.c
index d3c58568acfbb4bd970e64cd5b8839ae72e0b411..fe7f46cb8bfe90193c70ff9240f186533f136245 100644 (file)
@@ -47,7 +47,8 @@ int dsdb_module_search_dn(struct ldb_module *module,
 
        res = talloc_zero(tmp_ctx, struct ldb_result);
        if (!res) {
-               return LDB_ERR_OPERATIONS_ERROR;
+               talloc_free(tmp_ctx);
+               return ldb_oom(ldb_module_get_ctx(module));
        }
 
        ret = ldb_build_search_req(&req, ldb_module_get_ctx(module), tmp_ctx,
@@ -70,7 +71,16 @@ int dsdb_module_search_dn(struct ldb_module *module,
                return ret;
        }
 
-       ret = ldb_next_request(module, req);
+       /* Run the new request */
+       if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) {
+               ret = ldb_next_request(module, req);
+       } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) {
+               ret = ldb_request(ldb_module_get_ctx(module), req);
+       } else {
+               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
+               SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE);
+               ret = ops->modify(module, req);
+       }
        if (ret == LDB_SUCCESS) {
                ret = ldb_wait(req->handle, LDB_WAIT_ALL);
        }
@@ -113,13 +123,23 @@ int dsdb_module_search(struct ldb_module *module,
 
        tmp_ctx = talloc_new(mem_ctx);
 
-       va_start(ap, format);
-       expression = talloc_vasprintf(tmp_ctx, format, ap);
-       va_end(ap);
+       if (format) {
+               va_start(ap, format);
+               expression = talloc_vasprintf(tmp_ctx, format, ap);
+               va_end(ap);
+
+               if (!expression) {
+                       talloc_free(tmp_ctx);
+                       return ldb_oom(ldb_module_get_ctx(module));
+               }
+       } else {
+               expression = NULL;
+       }
 
        res = talloc_zero(tmp_ctx, struct ldb_result);
        if (!res) {
-               return LDB_ERR_OPERATIONS_ERROR;
+               talloc_free(tmp_ctx);
+               return ldb_oom(ldb_module_get_ctx(module));
        }
 
        ret = ldb_build_search_req(&req, ldb_module_get_ctx(module), tmp_ctx,
@@ -142,13 +162,14 @@ int dsdb_module_search(struct ldb_module *module,
                return ret;
        }
 
-       if (dsdb_flags & DSDB_FLAG_OWN_MODULE) {
-               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
-               ret = ops->search(module, req);
+       if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) {
+               ret = ldb_next_request(module, req);
        } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) {
                ret = ldb_request(ldb_module_get_ctx(module), req);
        } else {
-               ret = ldb_next_request(module, req);
+               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
+               SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE);
+               ret = ops->search(module, req);
        }
        if (ret == LDB_SUCCESS) {
                ret = ldb_wait(req->handle, LDB_WAIT_ALL);
@@ -175,6 +196,7 @@ int dsdb_module_dn_by_guid(struct ldb_module *module, TALLOC_CTX *mem_ctx,
 
        ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
                                 attrs,
+                                DSDB_FLAG_NEXT_MODULE |
                                 DSDB_SEARCH_SHOW_DELETED |
                                 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
                                 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
@@ -212,7 +234,8 @@ int dsdb_module_guid_by_dn(struct ldb_module *module, struct ldb_dn *dn, struct
        NTSTATUS status;
 
        ret = dsdb_module_search_dn(module, tmp_ctx, &res, dn, attrs,
-                                   DSDB_SEARCH_SHOW_DELETED|
+                                   DSDB_FLAG_NEXT_MODULE |
+                                   DSDB_SEARCH_SHOW_DELETED |
                                    DSDB_SEARCH_SHOW_EXTENDED_DN);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to find GUID for %s",
@@ -224,7 +247,7 @@ int dsdb_module_guid_by_dn(struct ldb_module *module, struct ldb_dn *dn, struct
        status = dsdb_get_extended_dn_guid(res->msgs[0]->dn, guid, "GUID");
        if (!NT_STATUS_IS_OK(status)) {
                talloc_free(tmp_ctx);
-               return LDB_ERR_OPERATIONS_ERROR;
+               return ldb_operr(ldb_module_get_ctx(module));
        }
 
        talloc_free(tmp_ctx);
@@ -243,12 +266,19 @@ int dsdb_module_modify(struct ldb_module *module,
        int ret;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        TALLOC_CTX *tmp_ctx = talloc_new(module);
+       struct ldb_result *res;
+
+       res = talloc_zero(tmp_ctx, struct ldb_result);
+       if (!res) {
+               talloc_free(tmp_ctx);
+               return ldb_oom(ldb_module_get_ctx(module));
+       }
 
        ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
                                message,
                                NULL,
-                               NULL,
-                               ldb_op_default_callback,
+                               res,
+                               ldb_modify_default_callback,
                                NULL);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -262,13 +292,14 @@ int dsdb_module_modify(struct ldb_module *module,
        }
 
        /* Run the new request */
-       if (dsdb_flags & DSDB_FLAG_OWN_MODULE) {
-               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
-               ret = ops->modify(module, mod_req);
+       if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) {
+               ret = ldb_next_request(module, mod_req);
        } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) {
                ret = ldb_request(ldb_module_get_ctx(module), mod_req);
        } else {
-               ret = ldb_next_request(module, mod_req);
+               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
+               SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE);
+               ret = ops->modify(module, mod_req);
        }
        if (ret == LDB_SUCCESS) {
                ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
@@ -292,13 +323,20 @@ int dsdb_module_rename(struct ldb_module *module,
        int ret;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        TALLOC_CTX *tmp_ctx = talloc_new(module);
+       struct ldb_result *res;
+
+       res = talloc_zero(tmp_ctx, struct ldb_result);
+       if (!res) {
+               talloc_free(tmp_ctx);
+               return ldb_oom(ldb_module_get_ctx(module));
+       }
 
        ret = ldb_build_rename_req(&req, ldb, tmp_ctx,
                                   olddn,
                                   newdn,
                                   NULL,
-                                  NULL,
-                                  ldb_op_default_callback,
+                                  res,
+                                  ldb_modify_default_callback,
                                   NULL);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -312,13 +350,14 @@ int dsdb_module_rename(struct ldb_module *module,
        }
 
        /* Run the new request */
-       if (dsdb_flags & DSDB_FLAG_OWN_MODULE) {
-               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
-               ret = ops->rename(module, req);
+       if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) {
+               ret = ldb_next_request(module, req);
        } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) {
                ret = ldb_request(ldb_module_get_ctx(module), req);
        } else {
-               ret = ldb_next_request(module, req);
+               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
+               SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE);
+               ret = ops->rename(module, req);
        }
        if (ret == LDB_SUCCESS) {
                ret = ldb_wait(req->handle, LDB_WAIT_ALL);
@@ -340,12 +379,19 @@ int dsdb_module_add(struct ldb_module *module,
        int ret;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        TALLOC_CTX *tmp_ctx = talloc_new(module);
+       struct ldb_result *res;
+
+       res = talloc_zero(tmp_ctx, struct ldb_result);
+       if (!res) {
+               talloc_free(tmp_ctx);
+               return ldb_oom(ldb_module_get_ctx(module));
+       }
 
        ret = ldb_build_add_req(&req, ldb, tmp_ctx,
                                message,
                                NULL,
-                               NULL,
-                               ldb_op_default_callback,
+                               res,
+                               ldb_modify_default_callback,
                                NULL);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -359,13 +405,69 @@ int dsdb_module_add(struct ldb_module *module,
        }
 
        /* Run the new request */
-       if (dsdb_flags & DSDB_FLAG_OWN_MODULE) {
+       if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) {
+               ret = ldb_next_request(module, req);
+       } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) {
+               ret = ldb_request(ldb_module_get_ctx(module), req);
+       } else {
                const struct ldb_module_ops *ops = ldb_module_get_ops(module);
+               SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE);
                ret = ops->add(module, req);
+       }
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+       }
+
+       talloc_free(tmp_ctx);
+       return ret;
+}
+
+/*
+  a ldb_delete request operating on modules below the
+  current module
+ */
+int dsdb_module_del(struct ldb_module *module,
+                   struct ldb_dn *dn,
+                   uint32_t dsdb_flags)
+{
+       struct ldb_request *req;
+       int ret;
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       TALLOC_CTX *tmp_ctx = talloc_new(module);
+       struct ldb_result *res;
+
+       res = talloc_zero(tmp_ctx, struct ldb_result);
+       if (!res) {
+               talloc_free(tmp_ctx);
+               return ldb_oom(ldb);
+       }
+
+       ret = ldb_build_del_req(&req, ldb, tmp_ctx,
+                               dn,
+                               NULL,
+                               res,
+                               ldb_modify_default_callback,
+                               NULL);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       ret = dsdb_request_add_controls(req, dsdb_flags);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       /* Run the new request */
+       if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) {
+               ret = ldb_next_request(module, req);
        } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) {
                ret = ldb_request(ldb_module_get_ctx(module), req);
        } else {
-               ret = ldb_next_request(module, req);
+               const struct ldb_module_ops *ops = ldb_module_get_ops(module);
+               SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE);
+               ret = ops->del(module, req);
        }
        if (ret == LDB_SUCCESS) {
                ret = ldb_wait(req->handle, LDB_WAIT_ALL);
@@ -375,11 +477,10 @@ int dsdb_module_add(struct ldb_module *module,
        return ret;
 }
 
-
 const struct dsdb_class * get_last_structural_class(const struct dsdb_schema *schema,const struct ldb_message_element *element)
 {
        const struct dsdb_class *last_class = NULL;
-       int i;
+       unsigned int i;
 
        for (i = 0; i < element->num_values; i++){
                const struct dsdb_class *tmp_class = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &element->values[i]);
@@ -388,7 +489,7 @@ const struct dsdb_class * get_last_structural_class(const struct dsdb_schema *sc
                        continue;
                }
 
-               if(tmp_class->objectClassCategory == 3) {
+               if(tmp_class->objectClassCategory > 1) {
                        continue;
                }
 
@@ -413,7 +514,7 @@ int dsdb_check_single_valued_link(const struct dsdb_attribute *attr,
                                  const struct ldb_message_element *el)
 {
        bool found_active = false;
-       int i;
+       unsigned int i;
 
        if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE) ||
            el->num_values < 2) {
@@ -432,6 +533,64 @@ int dsdb_check_single_valued_link(const struct dsdb_attribute *attr,
        return LDB_SUCCESS;
 }
 
+int dsdb_check_optional_feature(struct ldb_module *module, struct ldb_dn *scope,
+                                       struct GUID op_feature_guid, bool *feature_enabled)
+{
+       TALLOC_CTX *tmp_ctx;
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       struct ldb_result *res;
+       struct ldb_dn *search_dn;
+       struct GUID search_guid;
+       const char *attrs[] = {"msDS-EnabledFeature", NULL};
+       int ret;
+       unsigned int i;
+       struct ldb_message_element *el;
+
+       *feature_enabled = false;
+
+       tmp_ctx = talloc_new(ldb);
+
+       ret = ldb_search(ldb, tmp_ctx, &res,
+                                       scope, LDB_SCOPE_BASE, attrs,
+                                       NULL);
+       if (ret != LDB_SUCCESS) {
+               ldb_asprintf_errstring(ldb,
+                               "Could no find the scope object - dn: %s\n",
+                               ldb_dn_get_linearized(scope));
+               talloc_free(tmp_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       if (res->msgs[0]->num_elements > 0) {
+
+               el = ldb_msg_find_element(res->msgs[0],"msDS-EnabledFeature");
+
+               attrs[0] = "msDS-OptionalFeatureGUID";
+
+               for (i=0; i<el->num_values; i++) {
+                       search_dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &el->values[i]);
+
+                       ret = ldb_search(ldb, tmp_ctx, &res,
+                                                       search_dn, LDB_SCOPE_BASE, attrs,
+                                                       NULL);
+                       if (ret != LDB_SUCCESS) {
+                               ldb_asprintf_errstring(ldb,
+                                               "Could no find object dn: %s\n",
+                                               ldb_dn_get_linearized(search_dn));
+                               talloc_free(tmp_ctx);
+                               return LDB_ERR_OPERATIONS_ERROR;
+                       }
+
+                       search_guid = samdb_result_guid(res->msgs[0], "msDS-OptionalFeatureGUID");
+
+                       if (GUID_compare(&search_guid, &op_feature_guid) == 0){
+                               *feature_enabled = true;
+                               break;
+                       }
+               }
+       }
+       talloc_free(tmp_ctx);
+       return LDB_SUCCESS;
+}
 
 /*
   find a 'reference' DN that points at another object
@@ -447,7 +606,8 @@ int dsdb_module_reference_dn(struct ldb_module *module, TALLOC_CTX *mem_ctx, str
        attrs[0] = attribute;
        attrs[1] = NULL;
 
-       ret = dsdb_module_search_dn(module, mem_ctx, &res, base, attrs, 0);
+       ret = dsdb_module_search_dn(module, mem_ctx, &res, base, attrs,
+                                   DSDB_FLAG_NEXT_MODULE);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
@@ -470,7 +630,7 @@ int dsdb_module_reference_dn(struct ldb_module *module, TALLOC_CTX *mem_ctx, str
 int dsdb_module_rid_manager_dn(struct ldb_module *module, TALLOC_CTX *mem_ctx, struct ldb_dn **dn)
 {
        return dsdb_module_reference_dn(module, mem_ctx,
-                                       samdb_base_dn(ldb_module_get_ctx(module)),
+                                       ldb_get_default_basedn(ldb_module_get_ctx(module)),
                                        "rIDManagerReference", dn);
 }
 
@@ -499,9 +659,8 @@ int dsdb_module_constrainted_update_integer(struct ldb_module *module, struct ld
        el->values = &v1;
        vstring = talloc_asprintf(msg, "%llu", (unsigned long long)old_val);
        if (!vstring) {
-               ldb_module_oom(module);
                talloc_free(msg);
-               return LDB_ERR_OPERATIONS_ERROR;
+               return ldb_module_oom(module);
        }
        v1 = data_blob_string_const(vstring);
 
@@ -514,13 +673,12 @@ int dsdb_module_constrainted_update_integer(struct ldb_module *module, struct ld
        el->values = &v2;
        vstring = talloc_asprintf(msg, "%llu", (unsigned long long)new_val);
        if (!vstring) {
-               ldb_module_oom(module);
                talloc_free(msg);
-               return LDB_ERR_OPERATIONS_ERROR;
+               return ldb_module_oom(module);
        }
        v2 = data_blob_string_const(vstring);
 
-       ret = dsdb_module_modify(module, msg, 0);
+       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
        talloc_free(msg);
        return ret;
 }
@@ -556,8 +714,191 @@ int dsdb_module_set_integer(struct ldb_module *module, struct ldb_dn *dn,
        }
        msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
 
-       ret = dsdb_module_modify(module, msg, 0);
+       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
+       talloc_free(msg);
+       return ret;
+}
+
+/*
+  load the uSNHighest and the uSNUrgent attributes from the @REPLCHANGED
+  object for a partition
+ */
+int dsdb_module_load_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
+                                 uint64_t *uSN, uint64_t *urgent_uSN)
+{
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       struct ldb_request *req;
+       int ret;
+       TALLOC_CTX *tmp_ctx = talloc_new(module);
+       struct dsdb_control_current_partition *p_ctrl;
+       struct ldb_result *res;
+
+       res = talloc_zero(tmp_ctx, struct ldb_result);
+       if (!res) {
+               talloc_free(tmp_ctx);
+               return ldb_module_oom(module);
+       }
+
+       ret = ldb_build_search_req(&req, ldb, tmp_ctx,
+                                  ldb_dn_new(tmp_ctx, ldb, "@REPLCHANGED"),
+                                  LDB_SCOPE_BASE,
+                                  NULL, NULL,
+                                  NULL,
+                                  res, ldb_search_default_callback,
+                                  NULL);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       p_ctrl = talloc(req, struct dsdb_control_current_partition);
+       if (p_ctrl == NULL) {
+               talloc_free(tmp_ctx);
+               return ldb_module_oom(module);
+       }
+       p_ctrl->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION;
+       p_ctrl->dn = dn;
+
+
+       ret = ldb_request_add_control(req,
+                                     DSDB_CONTROL_CURRENT_PARTITION_OID,
+                                     false, p_ctrl);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       /* Run the new request */
+       ret = ldb_next_request(module, req);
+
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+       }
+
+       if (ret == LDB_ERR_NO_SUCH_OBJECT || ret == LDB_ERR_INVALID_DN_SYNTAX) {
+               /* it hasn't been created yet, which means
+                  an implicit value of zero */
+               *uSN = 0;
+               talloc_free(tmp_ctx);
+               return LDB_SUCCESS;
+       }
+
+       if (ret != LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       if (res->count != 1) {
+               *uSN = 0;
+               if (urgent_uSN) {
+                       *urgent_uSN = 0;
+               }
+       } else {
+               *uSN = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNHighest", 0);
+               if (urgent_uSN) {
+                       *urgent_uSN = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNUrgent", 0);
+               }
+       }
+
+       talloc_free(tmp_ctx);
+
+       return LDB_SUCCESS;
+}
+
+/*
+  save uSNHighest and uSNUrgent attributes in the @REPLCHANGED object for a
+  partition
+ */
+int dsdb_module_save_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
+                                  uint64_t uSN, uint64_t urgent_uSN)
+{
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       struct ldb_request *req;
+       struct ldb_message *msg;
+       struct dsdb_control_current_partition *p_ctrl;
+       int ret;
+       struct ldb_result *res;
+
+       msg = ldb_msg_new(module);
+       if (msg == NULL) {
+               return ldb_module_oom(module);
+       }
+
+       msg->dn = ldb_dn_new(msg, ldb, "@REPLCHANGED");
+       if (msg->dn == NULL) {
+               talloc_free(msg);
+               return ldb_operr(ldb_module_get_ctx(module));
+       }
+
+       res = talloc_zero(msg, struct ldb_result);
+       if (!res) {
+               talloc_free(msg);
+               return ldb_module_oom(module);
+       }
+
+       ret = ldb_msg_add_fmt(msg, "uSNHighest", "%llu", (unsigned long long)uSN);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(msg);
+               return ret;
+       }
+       msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
+
+       /* urgent_uSN is optional so may not be stored */
+       if (urgent_uSN) {
+               ret = ldb_msg_add_fmt(msg, "uSNUrgent", "%llu", (unsigned long long)urgent_uSN);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(msg);
+                       return ret;
+               }
+               msg->elements[1].flags = LDB_FLAG_MOD_REPLACE;
+       }
+
+
+       p_ctrl = talloc(msg, struct dsdb_control_current_partition);
+       if (p_ctrl == NULL) {
+               talloc_free(msg);
+               return ldb_oom(ldb);
+       }
+       p_ctrl->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION;
+       p_ctrl->dn = dn;
+       ret = ldb_build_mod_req(&req, ldb, msg,
+                               msg,
+                               NULL,
+                               res,
+                               ldb_modify_default_callback,
+                               NULL);
+again:
+       if (ret != LDB_SUCCESS) {
+               talloc_free(msg);
+               return ret;
+       }
+
+       ret = ldb_request_add_control(req,
+                                     DSDB_CONTROL_CURRENT_PARTITION_OID,
+                                     false, p_ctrl);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(msg);
+               return ret;
+       }
+
+       /* Run the new request */
+       ret = ldb_next_request(module, req);
+
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+       }
+       if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+               ret = ldb_build_add_req(&req, ldb, msg,
+                                       msg,
+                                       NULL,
+                                       res,
+                                       ldb_modify_default_callback,
+                                       NULL);
+               goto again;
+       }
+
        talloc_free(msg);
+
        return ret;
 }
 
@@ -566,5 +907,196 @@ bool dsdb_module_am_system(struct ldb_module *module)
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        struct auth_session_info *session_info
                = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
-       return security_session_user_level(session_info) == SECURITY_SYSTEM;
+       return security_session_user_level(session_info, NULL) == SECURITY_SYSTEM;
+}
+
+/*
+  check if the recyclebin is enabled
+ */
+int dsdb_recyclebin_enabled(struct ldb_module *module, bool *enabled)
+{
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       struct ldb_dn *partitions_dn;
+       struct GUID recyclebin_guid;
+       int ret;
+
+       partitions_dn = samdb_partitions_dn(ldb, module);
+
+       GUID_from_string(DS_GUID_FEATURE_RECYCLE_BIN, &recyclebin_guid);
+
+       ret = dsdb_check_optional_feature(module, partitions_dn, recyclebin_guid, enabled);
+       if (ret != LDB_SUCCESS) {
+               ldb_asprintf_errstring(ldb, "Could not verify if Recycle Bin is enabled \n");
+               talloc_free(partitions_dn);
+               return LDB_ERR_UNWILLING_TO_PERFORM;
+       }
+
+       talloc_free(partitions_dn);
+       return LDB_SUCCESS;
+}
+
+bool is_attr_in_list(const char * const * attrs, const char *attr)
+{
+       unsigned int i;
+
+       for (i = 0; attrs[i]; i++) {
+               if (ldb_attr_cmp(attrs[i], attr) == 0)
+                       return true;
+       }
+
+       return false;
+}
+
+int dsdb_msg_constrainted_update_int32(struct ldb_module *module,
+                                      struct ldb_message *msg,
+                                      const char *attr,
+                                      const int32_t *old_val,
+                                      const int32_t *new_val)
+{
+       struct ldb_message_element *el;
+       struct ldb_val *v1, *v2;
+       int ret;
+       char *vstring;
+
+       if (old_val) {
+               ret = ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, &el);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+               el->num_values = 1;
+               el->values = talloc_array(msg, struct ldb_val, el->num_values);
+               if (!el->values) {
+                       return ldb_module_oom(module);
+               }
+               vstring = talloc_asprintf(el->values, "%ld", (long)*old_val);
+               if (!vstring) {
+                       return ldb_module_oom(module);
+               }
+               *el->values = data_blob_string_const(vstring);
+       }
+
+       if (new_val) {
+               ret = ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_ADD, &el);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+               el->num_values = 1;
+               el->values = talloc_array(msg, struct ldb_val, el->num_values);
+               if (!el->values) {
+                       return ldb_module_oom(module);
+               }
+               vstring = talloc_asprintf(el->values, "%ld", (long)*new_val);
+               if (!vstring) {
+                       return ldb_module_oom(module);
+               }
+               *el->values = data_blob_string_const(vstring);
+       }
+
+       return LDB_SUCCESS;
+}
+
+int dsdb_msg_constrainted_update_int64(struct ldb_module *module,
+                                      struct ldb_message *msg,
+                                      const char *attr,
+                                      const int64_t *old_val,
+                                      const int64_t *new_val)
+{
+       struct ldb_message_element *el;
+       struct ldb_val *v1, *v2;
+       int ret;
+       char *vstring;
+
+       if (old_val) {
+               ret = ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, &el);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+               el->num_values = 1;
+               el->values = talloc_array(msg, struct ldb_val, el->num_values);
+               if (!el->values) {
+                       return ldb_module_oom(module);
+               }
+               vstring = talloc_asprintf(el->values, "%lld", (long long)*old_val);
+               if (!vstring) {
+                       return ldb_module_oom(module);
+               }
+               *el->values = data_blob_string_const(vstring);
+       }
+
+       if (new_val) {
+               ret = ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_ADD, &el);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+               el->num_values = 1;
+               el->values = talloc_array(msg, struct ldb_val, el->num_values);
+               if (!el->values) {
+                       return ldb_module_oom(module);
+               }
+               vstring = talloc_asprintf(el->values, "%lld", (long long)*new_val);
+               if (!vstring) {
+                       return ldb_module_oom(module);
+               }
+               *el->values = data_blob_string_const(vstring);
+       }
+
+       return LDB_SUCCESS;
+}
+
+/*
+  update an int32 attribute safely via a constrained delete/add
+ */
+int dsdb_module_constrainted_update_int32(struct ldb_module *module,
+                                         struct ldb_dn *dn,
+                                         const char *attr,
+                                         const int32_t *old_val,
+                                         const int32_t *new_val)
+{
+       struct ldb_message *msg;
+       int ret;
+
+       msg = ldb_msg_new(module);
+       msg->dn = dn;
+
+       ret = dsdb_msg_constrainted_update_int32(module,
+                                                msg, attr,
+                                                old_val,
+                                                new_val);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(msg);
+               return ret;
+       }
+
+       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
+       talloc_free(msg);
+       return ret;
+}
+
+/*
+  update an int64 attribute safely via a constrained delete/add
+ */
+int dsdb_module_constrainted_update_int64(struct ldb_module *module,
+                                         struct ldb_dn *dn,
+                                         const char *attr,
+                                         const int64_t *old_val,
+                                         const int64_t *new_val)
+{
+       struct ldb_message *msg;
+       int ret;
+
+       msg = ldb_msg_new(module);
+       msg->dn = dn;
+
+       ret = dsdb_msg_constrainted_update_int64(module,
+                                                msg, attr,
+                                                old_val,
+                                                new_val);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(msg);
+               return ret;
+       }
+
+       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE);
+       talloc_free(msg);
+       return ret;
 }