LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - linked_attributes.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 435 608 71.5 %
Date: 2021-09-23 10:06:22 Functions: 24 26 92.3 %

          Line data    Source code
       1             : /*
       2             :    ldb database library
       3             : 
       4             :    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
       5             :    Copyright (C) Simo Sorce <idra@samba.org> 2008
       6             :    Copyright (C) Matthieu Patou <mat@matws.net> 2011
       7             :    Copyright (C) Andrew Tridgell 2009
       8             : 
       9             :    This program is free software; you can redistribute it and/or modify
      10             :    it under the terms of the GNU General Public License as published by
      11             :    the Free Software Foundation; either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    This program is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU General Public License
      20             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      21             : */
      22             : 
      23             : /*
      24             :  *  Name: ldb
      25             :  *
      26             :  *  Component: ldb linked_attributes module
      27             :  *
      28             :  *  Description: Module to ensure linked attribute pairs (i.e. forward-links
      29             :  *  and backlinks) remain in sync.
      30             :  *
      31             :  *  Backlinks are 'plain' links (without extra metadata). When the link target
      32             :  *  object is modified (e.g. renamed), we use the backlinks to keep the link
      33             :  *  source object updated. Note there are some cases where we can't do this:
      34             :  *    - one-way links, which don't have a corresponding backlink
      35             :  *    - two-way deactivated links, i.e. when a user is removed from a group,
      36             :  *      the forward 'member' link still exists (but is inactive), however, the
      37             :  *      'memberOf' backlink is deleted.
      38             :  *  In these cases, we can end up with a dangling forward link which is
      39             :  *  incorrect (i.e. the target has been renamed or deleted). We have dbcheck
      40             :  *  rules to detect and fix this, and cope otherwise by filtering at runtime
      41             :  *  (i.e. in the extended_dn module).
      42             :  *
      43             :  *  See also repl_meta_data.c, which handles updating links for deleted
      44             :  *  objects, as well as link changes received from another DC.
      45             :  *
      46             :  *  Author: Andrew Bartlett
      47             :  */
      48             : 
      49             : #include "includes.h"
      50             : #include "ldb_module.h"
      51             : #include "util/dlinklist.h"
      52             : #include "dsdb/samdb/samdb.h"
      53             : #include "librpc/gen_ndr/ndr_misc.h"
      54             : #include "dsdb/samdb/ldb_modules/util.h"
      55             : 
      56             : #undef strcasecmp
      57             : 
      58             : struct la_private_transaction {
      59             :         struct la_context *la_list;
      60             : };
      61             : 
      62             : 
      63             : struct la_private {
      64             :         struct la_private_transaction *transaction;
      65             :         bool sorted_links;
      66             : };
      67             : 
      68             : struct la_op_store {
      69             :         struct la_op_store *next;
      70             :         struct la_op_store *prev;
      71             :         enum la_op {LA_OP_ADD, LA_OP_DEL} op;
      72             :         struct GUID guid;
      73             :         char *name;
      74             : };
      75             : 
      76             : struct replace_context {
      77             :         struct la_context *ac;
      78             :         unsigned int num_elements;
      79             :         struct ldb_message_element *el;
      80             : };
      81             : 
      82             : struct la_context {
      83             :         struct la_context *next, *prev;
      84             :         const struct dsdb_schema *schema;
      85             :         struct ldb_module *module;
      86             :         struct ldb_request *req;
      87             :         struct ldb_dn *mod_dn;
      88             :         struct replace_context *rc;
      89             :         struct la_op_store *ops;
      90             :         struct ldb_extended *op_response;
      91             :         struct ldb_control **op_controls;
      92             :         /*
      93             :          * For futur use
      94             :          * will tell which GC to use for resolving links
      95             :          */
      96             :         char *gc_dns_name;
      97             : };
      98             : 
      99             : 
     100           0 : static int handle_verify_name_control(TALLOC_CTX *ctx, struct ldb_context *ldb,
     101             :                                         struct ldb_control *control, struct la_context *ac)
     102             : {
     103             :         /*
     104             :          * If we are a GC let's remove the control,
     105             :          * if there is a specified GC check that is us.
     106             :          */
     107           0 :         struct ldb_verify_name_control *lvnc = (struct ldb_verify_name_control *)control->data;
     108           0 :         if (samdb_is_gc(ldb)) {
     109             :                 /* Because we can't easily talloc a struct ldb_dn*/
     110           0 :                 struct ldb_dn **dn = talloc_array(ctx, struct ldb_dn *, 1);
     111           0 :                 int ret = samdb_server_reference_dn(ldb, ctx, dn);
     112             :                 const char *dns;
     113             : 
     114           0 :                 if (ret != LDB_SUCCESS) {
     115           0 :                         return ldb_operr(ldb);
     116             :                 }
     117             : 
     118           0 :                 dns = samdb_dn_to_dnshostname(ldb, ctx, *dn);
     119           0 :                 if (!dns) {
     120           0 :                         return ldb_operr(ldb);
     121             :                 }
     122           0 :                 if (!lvnc->gc || strcasecmp(dns, lvnc->gc) == 0) {
     123           0 :                         if (!ldb_save_controls(control, ctx, NULL)) {
     124           0 :                                 return ldb_operr(ldb);
     125             :                         }
     126             :                 } else {
     127           0 :                         control->critical = true;
     128             :                 }
     129           0 :                 talloc_free(dn);
     130             :         } else {
     131             :                 /* For the moment we don't remove the control is this case in order
     132             :                  * to fail the request. It's better than having the client thinking
     133             :                  * that we honnor its control.
     134             :                  * Hopefully only a very small set of usecase should hit this problem.
     135             :                  */
     136           0 :                 if (lvnc->gc) {
     137           0 :                         ac->gc_dns_name = talloc_strdup(ac, lvnc->gc);
     138             :                 }
     139           0 :                 control->critical = true;
     140             :         }
     141             : 
     142           0 :         return LDB_SUCCESS;
     143             : }
     144             : 
     145     1556923 : static struct la_context *linked_attributes_init(struct ldb_module *module,
     146             :                                                  struct ldb_request *req)
     147             : {
     148             :         struct ldb_context *ldb;
     149             :         struct la_context *ac;
     150             : 
     151     1556923 :         ldb = ldb_module_get_ctx(module);
     152             : 
     153     1556923 :         ac = talloc_zero(req, struct la_context);
     154     1556923 :         if (ac == NULL) {
     155           0 :                 ldb_oom(ldb);
     156           0 :                 return NULL;
     157             :         }
     158             : 
     159     1556923 :         ac->schema = dsdb_get_schema(ldb, ac);
     160     1556923 :         ac->module = module;
     161     1556923 :         ac->req = req;
     162             : 
     163     1556923 :         return ac;
     164             : }
     165             : 
     166             : /*
     167             :   turn a DN into a GUID
     168             :  */
     169       84768 : static int la_guid_from_dn(struct ldb_module *module,
     170             :                            struct ldb_request *parent,
     171             :                            struct ldb_dn *dn, struct GUID *guid)
     172             : {
     173             :         NTSTATUS status;
     174             :         int ret;
     175             : 
     176       84768 :         status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
     177       84768 :         if (NT_STATUS_IS_OK(status)) {
     178       83246 :                 return LDB_SUCCESS;
     179             :         }
     180        1246 :         if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
     181           0 :                 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
     182             :                          ldb_dn_get_linearized(dn)));
     183           0 :                 return ldb_operr(ldb_module_get_ctx(module));
     184             :         }
     185             : 
     186        1246 :         ret = dsdb_module_guid_by_dn(module, dn, guid, parent);
     187        1246 :         if (ret != LDB_SUCCESS) {
     188           0 :                 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
     189             :                          ldb_dn_get_linearized(dn)));
     190           0 :                 return ret;
     191             :         }
     192        1246 :         return LDB_SUCCESS;
     193             : }
     194             : 
     195             : 
     196             : /* Common routine to handle reading the attributes and creating a
     197             :  * series of modify requests */
     198        2878 : static int la_store_op(struct la_context *ac,
     199             :                        enum la_op op, 
     200             :                        const struct dsdb_attribute *schema_attr,
     201             :                        struct ldb_val *dn,
     202             :                        const char *name)
     203             : {
     204             :         struct ldb_context *ldb;
     205             :         struct la_op_store *os;
     206             :         struct ldb_dn *op_dn;
     207             :         struct dsdb_dn *dsdb_dn;
     208             :         int ret;
     209             : 
     210        2878 :         ldb = ldb_module_get_ctx(ac->module);
     211             : 
     212             : 
     213        2878 :         os = talloc_zero(ac, struct la_op_store);
     214        2878 :         if (!os) {
     215           0 :                 return ldb_oom(ldb);
     216             :         }
     217             : 
     218        2878 :         dsdb_dn = dsdb_dn_parse(os, ldb, dn, schema_attr->syntax->ldap_oid);
     219             : 
     220        2878 :         if (!dsdb_dn) {
     221           0 :                 ldb_asprintf_errstring(ldb,
     222             :                                        "could not parse attribute as a DN");
     223           0 :                 TALLOC_FREE(os);
     224           0 :                 return LDB_ERR_INVALID_DN_SYNTAX;
     225             :         }
     226             : 
     227        2878 :         op_dn = dsdb_dn->dn;
     228             : 
     229        2878 :         os->op = op;
     230             : 
     231        2878 :         ret = la_guid_from_dn(ac->module, ac->req, op_dn, &os->guid);
     232        2878 :         talloc_free(op_dn);
     233        2878 :         if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
     234             :                 /* we are deleting an object, and we've found it has a
     235             :                  * forward link to a target that no longer
     236             :                  * exists. This is not an error in the delete, and we
     237             :                  * should just not do the deferred delete of the
     238             :                  * target attribute
     239             :                  */
     240           0 :                 talloc_free(os);
     241           0 :                 return LDB_SUCCESS;
     242             :         }
     243        2878 :         if (ret != LDB_SUCCESS) {
     244           0 :                 return ret;
     245             :         }
     246             : 
     247        2878 :         os->name = talloc_strdup(os, name);
     248        2878 :         if (!os->name) {
     249           0 :                 return ldb_oom(ldb);
     250             :         }
     251             : 
     252             :         /* Do deletes before adds */
     253        2878 :         if (op == LA_OP_ADD) {
     254        1555 :                 DLIST_ADD_END(ac->ops, os);
     255             :         } else {
     256             :                 /* By adding to the head of the list, we do deletes before
     257             :                  * adds when processing a replace */
     258        1323 :                 DLIST_ADD(ac->ops, os);
     259             :         }
     260             : 
     261        2878 :         return LDB_SUCCESS;
     262             : }
     263             : 
     264             : static int la_queue_mod_request(struct la_context *ac);
     265             : static int la_down_req(struct la_context *ac);
     266             : 
     267             : 
     268             : 
     269             : /* add */
     270      822091 : static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
     271             : {
     272             :         struct ldb_context *ldb;
     273             :         const struct dsdb_attribute *target_attr;
     274             :         struct la_context *ac;
     275             :         const char *attr_name;
     276             :         struct ldb_control *ctrl;
     277             :         unsigned int i, j;
     278             :         struct ldb_control *control;
     279             :         int ret;
     280             : 
     281      822091 :         ldb = ldb_module_get_ctx(module);
     282             : 
     283      822091 :         if (ldb_dn_is_special(req->op.add.message->dn)) {
     284             :                 /* do not manipulate our control entries */
     285        1427 :                 return ldb_next_request(module, req);
     286             :         }
     287             : 
     288      820664 :         ac = linked_attributes_init(module, req);
     289      820664 :         if (!ac) {
     290           0 :                 return ldb_operr(ldb);
     291             :         }
     292             : 
     293      820664 :         control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
     294      820664 :         if (control != NULL && control->data != NULL) {
     295           0 :                 ret = handle_verify_name_control(req, ldb, control, ac);
     296           0 :                 if (ret != LDB_SUCCESS) {
     297           0 :                         return ldb_operr(ldb);
     298             :                 }
     299             :         }
     300             : 
     301      820664 :         if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
     302             :                 /* don't do anything special for linked attributes, repl_meta_data has done it */
     303      475276 :                 talloc_free(ac);
     304      475276 :                 return ldb_next_request(module, req);
     305             :         }
     306      345388 :         ctrl->critical = false;
     307             : 
     308      345388 :         if (!ac->schema) {
     309             :                 /* without schema, this doesn't make any sense */
     310           0 :                 talloc_free(ac);
     311           0 :                 return ldb_next_request(module, req);
     312             :         }
     313             : 
     314             : 
     315             :         /* Need to ensure we only have forward links being specified */
     316     7157979 :         for (i=0; i < req->op.add.message->num_elements; i++) {
     317     6812591 :                 const struct ldb_message_element *el = &req->op.add.message->elements[i];
     318     6362686 :                 const struct dsdb_attribute *schema_attr
     319     6812591 :                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
     320     6812591 :                 if (!schema_attr) {
     321           0 :                         ldb_asprintf_errstring(ldb,
     322             :                                                "%s: attribute %s is not a valid attribute in schema",
     323             :                                                __FUNCTION__,
     324           0 :                                                el->name);
     325           0 :                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
     326             :                 }
     327             : 
     328             :                 /* this could be a link with no partner, in which case
     329             :                    there is no special work to do */
     330     6812591 :                 if (schema_attr->linkID == 0) {
     331     6812410 :                         continue;
     332             :                 }
     333             : 
     334             :                 /* this part of the code should only be handling forward links */
     335         181 :                 SMB_ASSERT((schema_attr->linkID & 1) == 0);
     336             : 
     337             :                 /* Even link IDs are for the originating attribute */
     338         181 :                 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
     339         181 :                 if (!target_attr) {
     340             :                         /*
     341             :                          * windows 2003 has a broken schema where
     342             :                          * the definition of msDS-IsDomainFor
     343             :                          * is missing (which is supposed to be
     344             :                          * the backlink of the msDS-HasDomainNCs
     345             :                          * attribute
     346             :                          */
     347          26 :                         continue;
     348             :                 }
     349             : 
     350         155 :                 attr_name = target_attr->lDAPDisplayName;
     351             : 
     352         450 :                 for (j = 0; j < el->num_values; j++) {
     353         295 :                         ret = la_store_op(ac, LA_OP_ADD,
     354             :                                           schema_attr,
     355         295 :                                           &el->values[j],
     356             :                                           attr_name);
     357         295 :                         if (ret != LDB_SUCCESS) {
     358           0 :                                 return ret;
     359             :                         }
     360             :                 }
     361             :         }
     362             : 
     363             :         /* if no linked attributes are present continue */
     364      345388 :         if (ac->ops == NULL) {
     365             :                 /* nothing to do for this module, proceed */
     366      345257 :                 talloc_free(ac);
     367      345257 :                 return ldb_next_request(module, req);
     368             :         }
     369             : 
     370             :         /* start with the original request */
     371         131 :         return la_down_req(ac);
     372             : }
     373             : 
     374             : /* For a delete or rename, we need to find out what linked attributes
     375             :  * are currently on this DN, and then deal with them.  This is the
     376             :  * callback to the base search */
     377             : 
     378        3572 : static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
     379             : {
     380             :         struct ldb_context *ldb;
     381             :         const struct dsdb_attribute *schema_attr;
     382             :         const struct dsdb_attribute *target_attr;
     383             :         struct ldb_message_element *search_el;
     384             :         struct replace_context *rc;
     385             :         struct la_context *ac;
     386             :         const char *attr_name;
     387             :         unsigned int i, j;
     388        3572 :         int ret = LDB_SUCCESS;
     389             : 
     390        3572 :         ac = talloc_get_type(req->context, struct la_context);
     391        3572 :         ldb = ldb_module_get_ctx(ac->module);
     392        3572 :         rc = ac->rc;
     393             : 
     394        3572 :         if (!ares) {
     395           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     396             :                                         LDB_ERR_OPERATIONS_ERROR);
     397             :         }
     398        3572 :         if (ares->error != LDB_SUCCESS) {
     399           0 :                 return ldb_module_done(ac->req, ares->controls,
     400             :                                         ares->response, ares->error);
     401             :         }
     402             : 
     403             :         /* Only entries are interesting, and we only want the olddn */
     404        3572 :         switch (ares->type) {
     405        1786 :         case LDB_REPLY_ENTRY:
     406             : 
     407        1786 :                 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
     408           0 :                         ldb_asprintf_errstring(ldb,
     409             :                                                "linked_attributes: %s is not the DN we were looking for",
     410           0 :                                                ldb_dn_get_linearized(ares->message->dn));
     411             :                         /* Guh?  We only asked for this DN */
     412           0 :                         talloc_free(ares);
     413           0 :                         return ldb_module_done(ac->req, NULL, NULL,
     414             :                                                 LDB_ERR_OPERATIONS_ERROR);
     415             :                 }
     416             : 
     417        1786 :                 ac->mod_dn = talloc_steal(ac, ares->message->dn);
     418             : 
     419             :                 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
     420        1834 :                 for (i = 0; rc && i < rc->num_elements; i++) {
     421             : 
     422          48 :                         schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
     423          48 :                         if (!schema_attr) {
     424           0 :                                 ldb_asprintf_errstring(ldb,
     425             :                                         "%s: attribute %s is not a valid attribute in schema",
     426             :                                         __FUNCTION__,
     427           0 :                                         rc->el[i].name);
     428           0 :                                 talloc_free(ares);
     429           0 :                                 return ldb_module_done(ac->req, NULL, NULL,
     430             :                                                 LDB_ERR_OBJECT_CLASS_VIOLATION);
     431             :                         }
     432             : 
     433          48 :                         search_el = ldb_msg_find_element(ares->message,
     434          48 :                                                          rc->el[i].name);
     435             : 
     436             :                         /* See if this element already exists */
     437             :                         /* otherwise just ignore as
     438             :                          * the add has already been scheduled */
     439          48 :                         if ( ! search_el) {
     440           6 :                                 continue;
     441             :                         }
     442             : 
     443          42 :                         target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
     444          42 :                         if (!target_attr) {
     445             :                                 /*
     446             :                                  * windows 2003 has a broken schema where
     447             :                                  * the definition of msDS-IsDomainFor
     448             :                                  * is missing (which is supposed to be
     449             :                                  * the backlink of the msDS-HasDomainNCs
     450             :                                  * attribute
     451             :                                  */
     452           0 :                                 continue;
     453             :                         }
     454          42 :                         attr_name = target_attr->lDAPDisplayName;
     455             : 
     456             :                         /* Now we know what was there, we can remove it for the re-add */
     457         116 :                         for (j = 0; j < search_el->num_values; j++) {
     458          74 :                                 ret = la_store_op(ac, LA_OP_DEL,
     459             :                                                   schema_attr, 
     460          74 :                                                   &search_el->values[j],
     461             :                                                   attr_name);
     462          74 :                                 if (ret != LDB_SUCCESS) {
     463           0 :                                         talloc_free(ares);
     464           0 :                                         return ldb_module_done(ac->req,
     465             :                                                                NULL, NULL, ret);
     466             :                                 }
     467             :                         }
     468             :                 }
     469             : 
     470        1786 :                 break;
     471             : 
     472           0 :         case LDB_REPLY_REFERRAL:
     473             :                 /* ignore */
     474           0 :                 break;
     475             : 
     476        1786 :         case LDB_REPLY_DONE:
     477             : 
     478        1786 :                 talloc_free(ares);
     479             : 
     480        1786 :                 if (ac->req->operation == LDB_ADD) {
     481             :                         /* Start the modifies to the backlinks */
     482         131 :                         ret = la_queue_mod_request(ac);
     483             : 
     484         131 :                         if (ret != LDB_SUCCESS) {
     485           0 :                                 return ldb_module_done(ac->req, NULL, NULL,
     486             :                                                        ret);
     487             :                         }
     488             :                 } else {
     489             :                         /* Start with the original request */
     490        1655 :                         ret = la_down_req(ac);
     491        1655 :                         if (ret != LDB_SUCCESS) {
     492           0 :                                 return ldb_module_done(ac->req, NULL, NULL, ret);
     493             :                         }
     494             :                 }
     495        1786 :                 return LDB_SUCCESS;
     496             :         }
     497             : 
     498        1786 :         talloc_free(ares);
     499        1786 :         return ret;
     500             : }
     501             : 
     502             : 
     503             : /* modify */
     504      896292 : static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
     505             : {
     506             :         /* Look over list of modifications */
     507             :         /* Find if any are for linked attributes */
     508             :         /* Determine the effect of the modification */
     509             :         /* Apply the modify to the linked entry */
     510             : 
     511             :         struct ldb_control *control;
     512             :         struct ldb_context *ldb;
     513             :         unsigned int i, j;
     514             :         struct la_context *ac;
     515             :         struct ldb_request *search_req;
     516             :         const char **attrs;
     517             :         struct ldb_control *ctrl;
     518             :         int ret;
     519             : 
     520      896292 :         ldb = ldb_module_get_ctx(module);
     521             : 
     522      896292 :         if (ldb_dn_is_special(req->op.mod.message->dn)) {
     523             :                 /* do not manipulate our control entries */
     524      160033 :                 return ldb_next_request(module, req);
     525             :         }
     526             : 
     527      736259 :         ac = linked_attributes_init(module, req);
     528      736259 :         if (!ac) {
     529           0 :                 return ldb_operr(ldb);
     530             :         }
     531             : 
     532      736259 :         control = ldb_request_get_control(req, LDB_CONTROL_VERIFY_NAME_OID);
     533      736259 :         if (control != NULL && control->data != NULL) {
     534           0 :                 ret = handle_verify_name_control(req, ldb, control, ac);
     535           0 :                 if (ret != LDB_SUCCESS) {
     536           0 :                         return ldb_operr(ldb);
     537             :                 }
     538             :         }
     539             : 
     540      736259 :         if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
     541             :                 /* don't do anything special for linked attributes, repl_meta_data has done it */
     542      595041 :                 talloc_free(ac);
     543      595041 :                 return ldb_next_request(module, req);
     544             :         }
     545      141218 :         ctrl->critical = false;
     546             : 
     547      141218 :         if (!ac->schema) {
     548             :                 /* without schema, this doesn't make any sense */
     549           0 :                 return ldb_next_request(module, req);
     550             :         }
     551             : 
     552      141218 :         ac->rc = talloc_zero(ac, struct replace_context);
     553      141218 :         if (!ac->rc) {
     554           0 :                 return ldb_oom(ldb);
     555             :         }
     556             : 
     557     1251465 :         for (i=0; i < req->op.mod.message->num_elements; i++) {
     558     1110247 :                 bool store_el = false;
     559             :                 const char *attr_name;
     560             :                 const struct dsdb_attribute *target_attr;
     561     1110247 :                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
     562     1022447 :                 const struct dsdb_attribute *schema_attr
     563     1110247 :                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
     564     1110247 :                 if (!schema_attr) {
     565           0 :                         ldb_asprintf_errstring(ldb,
     566             :                                                "%s: attribute %s is not a valid attribute in schema",
     567             :                                                __FUNCTION__,
     568           0 :                                                el->name);
     569           0 :                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
     570             :                 }
     571             :                 /* We have a valid attribute, now find out if it is a forward link
     572             :                    (Even link IDs are for the originating attribute) */
     573     1110247 :                 if (schema_attr->linkID == 0) {
     574     1107686 :                         continue;
     575             :                 }
     576             : 
     577             :                 /* this part of the code should only be handling forward links */
     578        2561 :                 SMB_ASSERT((schema_attr->linkID & 1) == 0);
     579             : 
     580             :                 /* Now find the target attribute */
     581        2561 :                 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID ^ 1);
     582        2561 :                 if (!target_attr) {
     583             :                         /*
     584             :                          * windows 2003 has a broken schema where
     585             :                          * the definition of msDS-IsDomainFor
     586             :                          * is missing (which is supposed to be
     587             :                          * the backlink of the msDS-HasDomainNCs
     588             :                          * attribute
     589             :                          */
     590          12 :                         continue;
     591             :                 }
     592             : 
     593        2549 :                 attr_name = target_attr->lDAPDisplayName;
     594             : 
     595        2549 :                 switch (el->flags & LDB_FLAG_MOD_MASK) {
     596           8 :                 case LDB_FLAG_MOD_REPLACE:
     597             :                         /* treat as just a normal add the delete part is handled by the callback */
     598           8 :                         store_el = true;
     599             : 
     600             :                         FALL_THROUGH;
     601        1260 :                 case LDB_FLAG_MOD_ADD:
     602             : 
     603             :                         /* For each value being added, we need to setup the adds */
     604        2520 :                         for (j = 0; j < el->num_values; j++) {
     605        1260 :                                 ret = la_store_op(ac, LA_OP_ADD,
     606             :                                                   schema_attr,
     607        1260 :                                                   &el->values[j],
     608             :                                                   attr_name);
     609        1260 :                                 if (ret != LDB_SUCCESS) {
     610           0 :                                         return ret;
     611             :                                 }
     612             :                         }
     613        1260 :                         break;
     614             : 
     615        1289 :                 case LDB_FLAG_MOD_DELETE:
     616             : 
     617        1289 :                         if (el->num_values) {
     618             :                                 /* For each value being deleted, we need to setup the delete */
     619        2498 :                                 for (j = 0; j < el->num_values; j++) {
     620        1249 :                                         ret = la_store_op(ac, LA_OP_DEL,
     621             :                                                           schema_attr,
     622        1249 :                                                           &el->values[j],
     623             :                                                           attr_name);
     624        1249 :                                         if (ret != LDB_SUCCESS) {
     625           0 :                                                 return ret;
     626             :                                         }
     627             :                                 }
     628             :                         } else {
     629             :                                 /* Flag that there was a DELETE
     630             :                                  * without a value specified, so we
     631             :                                  * need to look for the old value */
     632          40 :                                 store_el = true;
     633             :                         }
     634             : 
     635        1289 :                         break;
     636             :                 }
     637             : 
     638        2549 :                 if (store_el) {
     639             :                         struct ldb_message_element *search_el;
     640             : 
     641          48 :                         search_el = talloc_realloc(ac->rc, ac->rc->el,
     642             :                                                    struct ldb_message_element,
     643             :                                                    ac->rc->num_elements +1);
     644          48 :                         if (!search_el) {
     645           0 :                                 return ldb_oom(ldb);
     646             :                         }
     647          48 :                         ac->rc->el = search_el;
     648             : 
     649          48 :                         ac->rc->el[ac->rc->num_elements] = *el;
     650          48 :                         ac->rc->num_elements++;
     651             :                 }
     652             :         }
     653             : 
     654      141218 :         if (ac->ops || ac->rc->el) {
     655             :                 /* both replace and delete without values are handled in the callback
     656             :                  * after the search on the entry to be modified is performed */
     657             : 
     658        1655 :                 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
     659        1655 :                 if (!attrs) {
     660           0 :                         return ldb_oom(ldb);
     661             :                 }
     662        1703 :                 for (i = 0; i < ac->rc->num_elements; i++) {
     663          48 :                         attrs[i] = ac->rc->el[i].name;
     664             :                 }
     665        1655 :                 attrs[i] = NULL;
     666             : 
     667             :                 /* The callback does all the hard work here */
     668        1655 :                 ret = ldb_build_search_req(&search_req, ldb, ac,
     669        1655 :                                            req->op.mod.message->dn,
     670             :                                            LDB_SCOPE_BASE,
     671             :                                            "(objectClass=*)", attrs,
     672             :                                            NULL,
     673             :                                            ac, la_mod_search_callback,
     674             :                                            req);
     675        1655 :                 LDB_REQ_SET_LOCATION(search_req);
     676             : 
     677             :                 /* We need to figure out our own extended DN, to fill in as the backlink target */
     678        1655 :                 if (ret == LDB_SUCCESS) {
     679        1655 :                         ret = dsdb_request_add_controls(search_req,
     680             :                                                         DSDB_SEARCH_SHOW_RECYCLED |
     681             :                                                         DSDB_SEARCH_SHOW_EXTENDED_DN);
     682             :                 }
     683        2488 :                 if (ret == LDB_SUCCESS) {
     684        1655 :                         talloc_steal(search_req, attrs);
     685             : 
     686        1655 :                         ret = ldb_next_request(module, search_req);
     687             :                 }
     688             : 
     689             :         } else {
     690             :                 /* nothing to do for this module, proceed */
     691      139563 :                 talloc_free(ac);
     692      139563 :                 ret = ldb_next_request(module, req);
     693             :         }
     694             : 
     695      141218 :         return ret;
     696             : }
     697             : 
     698             : 
     699        4813 : static int linked_attributes_fix_link_slow(struct ldb_module *module,
     700             :                                            struct ldb_request *parent,
     701             :                                            struct ldb_message *msg,
     702             :                                            struct ldb_dn *new_dn,
     703             :                                            struct GUID self_guid,
     704             :                                            const char *syntax_oid,
     705             :                                            const char *reverse_syntax_oid)
     706             : {
     707             :         int ret;
     708             :         unsigned int i;
     709             :         struct GUID link_guid;
     710        4813 :         struct ldb_message_element *el = &msg->elements[0];
     711        4813 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
     712        4813 :         bool has_unique_value = strcmp(reverse_syntax_oid, LDB_SYNTAX_DN) == 0;
     713        4813 :         TALLOC_CTX *tmp_ctx = talloc_new(module);
     714        4813 :         if (tmp_ctx == NULL) {
     715           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     716             :         }
     717             :         /*
     718             :          * The msg has one element (el) containing links of one particular
     719             :          * type from the remote object. We know that at least one of those
     720             :          * links points to the object being renamed (identified by self_guid,
     721             :          * renamed to new_dn). Usually only one of the links will point back
     722             :          * to renamed object, but there can be more when the reverse link is a
     723             :          * DN+Binary link.
     724             :          *
     725             :          * This is used for unsorted links, which is to say back links and
     726             :          * forward links on old databases. It necessarily involves a linear
     727             :          * search, though when the link is a plain DN link, we can skip
     728             :          * checking as soon as we find it.
     729             :          *
     730             :          * NOTE: if there are duplicate links, the extra ones will end up as
     731             :          * dangling links to the old DN. This may or may not be worse than
     732             :          * leaving them as duplicate links.
     733             :          */
     734       14086 :         for (i = 0; i < el->num_values; i++) {
     735       13709 :                 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(msg,
     736             :                                                         ldb,
     737       13709 :                                                         &el->values[i],
     738             :                                                         syntax_oid);
     739       13709 :                 if (dsdb_dn == NULL) {
     740           0 :                         talloc_free(tmp_ctx);
     741           0 :                         return LDB_ERR_INVALID_DN_SYNTAX;
     742             :                 }
     743             : 
     744       13709 :                 ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
     745       13709 :                 if (ret != LDB_SUCCESS) {
     746           0 :                         talloc_free(tmp_ctx);
     747           0 :                         return ret;
     748             :                 }
     749             : 
     750             :                 /*
     751             :                  * By comparing using the GUID we ensure that even if somehow
     752             :                  * the name has got out of sync, this rename will fix it.
     753             :                  *
     754             :                  * If somehow we don't have a GUID on the DN in the DB, the
     755             :                  * la_guid_from_dn call will be more costly, but still give us
     756             :                  * a GUID. dbcheck will fix this if run.
     757             :                  */
     758       13709 :                 if (!GUID_equal(&self_guid, &link_guid)) {
     759        8521 :                         continue;
     760             :                 }
     761             : 
     762        5188 :                 ret = ldb_dn_update_components(dsdb_dn->dn, new_dn);
     763        5188 :                 if (ret != LDB_SUCCESS) {
     764           0 :                         talloc_free(tmp_ctx);
     765           0 :                         return ret;
     766             :                 }
     767             : 
     768        5188 :                 el->values[i] = data_blob_string_const(
     769        5188 :                         dsdb_dn_get_extended_linearized(el->values, dsdb_dn, 1));
     770        5188 :                 if (has_unique_value) {
     771        4333 :                         break;
     772             :                 }
     773             :         }
     774             : 
     775        4813 :         talloc_free(tmp_ctx);
     776        4813 :         return LDB_SUCCESS;
     777             : }
     778             : 
     779             : 
     780        2123 : static int linked_attributes_fix_forward_link(struct ldb_module *module,
     781             :                                               struct ldb_message *msg,
     782             :                                               struct ldb_dn *new_dn,
     783             :                                               struct GUID self_guid,
     784             :                                               const char *syntax_oid)
     785             : {
     786             :         int ret;
     787        2123 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
     788        2123 :         struct parsed_dn *pdn_list = NULL;
     789        2123 :         struct parsed_dn *exact = NULL;
     790        2123 :         struct parsed_dn *next = NULL;
     791             :         bool is_plain_dn;
     792        2123 :         struct ldb_message_element *el = &msg->elements[0];
     793        2123 :         unsigned int num_parsed_dns = el->num_values;
     794             : 
     795        2123 :         TALLOC_CTX *tmp_ctx = talloc_new(module);
     796        2123 :         if (tmp_ctx == NULL) {
     797           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     798             :         }
     799             : 
     800             :         /*
     801             :          * The msg has a single element (el) containing forward links which we
     802             :          * trust are sorted in GUID order. We know that at least one of those
     803             :          * links points to the object being renamed (identified by self_guid,
     804             :          * renamed to new_dn), because that object has a backlink pointing
     805             :          * here.
     806             :          *
     807             :          * In most cases we assume there will only be one forward link, which
     808             :          * is found by parsed_dn_find(), but in the case of DN+Binary links
     809             :          * (e.g. msDS-RevealedUsers) there may be many forward links that
     810             :          * share the same DN/GUID but differ in the binary part. For those we
     811             :          * need to look around the link found by parsed_dn_find() and convert
     812             :          * them all -- there is no way to know which forward link belongs to
     813             :          * which backlink.
     814             :          */
     815             : 
     816        2123 :         ret = get_parsed_dns_trusted(tmp_ctx, el, &pdn_list);
     817        2123 :         if (ret != LDB_SUCCESS) {
     818           0 :                 ldb_asprintf_errstring(ldb, "get_parsed_dn_trusted() "
     819             :                                        "error fixing %s links for %s",
     820             :                                        el->name,
     821             :                                        ldb_dn_get_linearized(msg->dn));
     822           0 :                 talloc_free(tmp_ctx);
     823           0 :                 return ret;
     824             :         }
     825             : 
     826             :         /* find our DN in the values */
     827        2123 :         ret = parsed_dn_find(ldb, pdn_list, num_parsed_dns,
     828             :                              &self_guid,
     829             :                              NULL,
     830             :                              data_blob_null, 0,
     831             :                              &exact, &next,
     832             :                              syntax_oid,
     833             :                              false);
     834             : 
     835        2123 :         if (ret != LDB_SUCCESS) {
     836           0 :                 ldb_asprintf_errstring(ldb, "parsed_dn_find() "
     837             :                                        "error fixing %s links for %s",
     838             :                                        el->name,
     839             :                                        ldb_dn_get_linearized(msg->dn));
     840           0 :                 talloc_free(tmp_ctx);
     841           0 :                 return ret;
     842             :         }
     843             : 
     844        2123 :         if (exact == NULL) {
     845        1204 :                 ldb_asprintf_errstring(
     846             :                         ldb,
     847             :                         "parsed_dn_find could not find %s link for %s",
     848             :                         el->name,
     849             :                         ldb_dn_get_linearized(msg->dn));
     850        1204 :                 talloc_free(tmp_ctx);
     851        1204 :                 return LDB_ERR_OPERATIONS_ERROR;
     852             :         }
     853             : 
     854         919 :         is_plain_dn = strcmp(syntax_oid, LDB_SYNTAX_DN) == 0;
     855             : 
     856         919 :         if (is_plain_dn) {
     857             :                 /*
     858             :                  *  The common case -- we only have to update a single link
     859             :                  */
     860         667 :                 ret = ldb_dn_update_components(exact->dsdb_dn->dn, new_dn);
     861         667 :                 if (ret != LDB_SUCCESS) {
     862           0 :                         DBG_ERR("could not update components  %s  %s\n",
     863             :                                 ldb_dn_get_linearized(exact->dsdb_dn->dn),
     864             :                                 ldb_dn_get_linearized(new_dn)
     865             :                                 );
     866             : 
     867           0 :                         talloc_free(tmp_ctx);
     868           0 :                         return ret;
     869             :                 }
     870         667 :                 *(exact->v) = data_blob_string_const(
     871         667 :                                 dsdb_dn_get_extended_linearized(el->values,
     872         667 :                                                                 exact->dsdb_dn,
     873             :                                                                 1));
     874             :         } else {
     875             :                 /*
     876             :                  * The forward link is a DN+Binary (or in some alternate
     877             :                  * universes, DN+String), which means the parsed_dns are keyed
     878             :                  * on GUID+Binary. We don't know the binary part, which means
     879             :                  * from our point of view the list can have entries with
     880             :                  * duplicate GUIDs that we can't tell apart. We don't know
     881             :                  * which backlink belongs to which GUID+binary, and the binary
     882             :                  * search will always find the same one. That means one link
     883             :                  * link will get fixed n times, whil n-1 links get fixed
     884             :                  * never.
     885             :                  *
     886             :                  * If we instead fixing all the possible links, we end up
     887             :                  * fixing n links n times, which at least works and is
     888             :                  * probably not too costly because n is probably small.
     889             :                  */
     890         252 :                 struct parsed_dn *first = exact;
     891         252 :                 struct parsed_dn *last = exact;
     892         252 :                 struct parsed_dn *p = NULL;
     893             :                 int cmp;
     894         504 :                 while (first > pdn_list) {
     895         202 :                         p = first - 1;
     896         202 :                         if (p->dsdb_dn == NULL) {
     897           0 :                                 ret = really_parse_trusted_dn(tmp_ctx,
     898             :                                                               ldb, p,
     899             :                                                               syntax_oid);
     900           0 :                                 if (ret != LDB_SUCCESS) {
     901           0 :                                         talloc_free(tmp_ctx);
     902           0 :                                         return ret;
     903             :                                 }
     904             :                         }
     905         202 :                         cmp = ndr_guid_compare(&exact->guid, &p->guid);
     906         202 :                         if (cmp != 0) {
     907         202 :                                 break;
     908             :                         }
     909           0 :                         first = p;
     910             :                 }
     911             : 
     912         820 :                 while (last < pdn_list + num_parsed_dns - 1) {
     913         518 :                         p = last + 1;
     914         518 :                         if (p->dsdb_dn == NULL) {
     915         360 :                                 ret = really_parse_trusted_dn(tmp_ctx,
     916             :                                                               ldb, p,
     917             :                                                               syntax_oid);
     918         360 :                                 if (ret != LDB_SUCCESS) {
     919           0 :                                         talloc_free(tmp_ctx);
     920           0 :                                         return ret;
     921             :                                 }
     922             :                         }
     923         518 :                         cmp = ndr_guid_compare(&exact->guid, &p->guid);
     924         518 :                         if (cmp != 0) {
     925         202 :                                 break;
     926             :                         }
     927         316 :                         last = p;
     928             :                 }
     929             : 
     930         820 :                 for (p = first; p <= last; p++) {
     931         568 :                         ret = ldb_dn_update_components(p->dsdb_dn->dn, new_dn);
     932         568 :                         if (ret != LDB_SUCCESS) {
     933           0 :                                 DBG_ERR("could not update components  %s  %s\n",
     934             :                                         ldb_dn_get_linearized(p->dsdb_dn->dn),
     935             :                                         ldb_dn_get_linearized(new_dn)
     936             :                                         );
     937           0 :                                 talloc_free(tmp_ctx);
     938           0 :                                 return ret;
     939             :                         }
     940         568 :                         *(p->v) = data_blob_string_const(
     941         568 :                                    dsdb_dn_get_extended_linearized(el->values,
     942             :                                                                    p->dsdb_dn,
     943             :                                                                    1));
     944             :                 }
     945             :         }
     946             : 
     947         919 :         talloc_free(tmp_ctx);
     948         917 :         return LDB_SUCCESS;
     949             : }
     950             : 
     951             : 
     952        3843 : static int linked_attributes_fix_links(struct ldb_module *module,
     953             :                                        struct GUID self_guid,
     954             :                                        struct ldb_dn *old_dn,
     955             :                                        struct ldb_dn *new_dn,
     956             :                                        struct ldb_message_element *el,
     957             :                                        struct dsdb_schema *schema,
     958             :                                        const struct dsdb_attribute *schema_attr,
     959             :                                        struct ldb_request *parent)
     960             : {
     961             :         unsigned int i;
     962        3843 :         TALLOC_CTX *tmp_ctx = NULL;
     963        3843 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
     964        3843 :         const struct dsdb_attribute *target = NULL;
     965             :         const char *attrs[2];
     966             :         int ret;
     967        3843 :         struct la_private *la_private = NULL;
     968             : 
     969        3843 :         target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
     970        3843 :         if (target == NULL) {
     971             :                 /* there is no counterpart link to change */
     972         128 :                 return LDB_SUCCESS;
     973             :         }
     974             : 
     975        3712 :         tmp_ctx = talloc_new(module);
     976        3712 :         if (tmp_ctx == NULL) {
     977           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     978             :         }
     979             : 
     980        3712 :         la_private = talloc_get_type(ldb_module_get_private(module),
     981             :                                      struct la_private);
     982        3712 :         if (la_private == NULL) {
     983           0 :                 talloc_free(tmp_ctx);
     984           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     985             :         }
     986             : 
     987        3712 :         attrs[0] = target->lDAPDisplayName;
     988        3712 :         attrs[1] = NULL;
     989             : 
     990       10708 :         for (i=0; i<el->num_values; i++) {
     991        6996 :                 struct dsdb_dn *dsdb_dn = NULL;
     992        6996 :                 struct ldb_result *res = NULL;
     993        6996 :                 struct ldb_message *msg  = NULL;
     994        6996 :                 struct ldb_message_element *el2 = NULL;
     995             :                 struct GUID link_guid;
     996        6996 :                 char *link_guid_str = NULL;
     997             : 
     998        6996 :                 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i],
     999        6996 :                                         schema_attr->syntax->ldap_oid);
    1000        6996 :                 if (dsdb_dn == NULL) {
    1001           0 :                         talloc_free(tmp_ctx);
    1002           0 :                         return LDB_ERR_INVALID_DN_SYNTAX;
    1003             :                 }
    1004             : 
    1005        6996 :                 ret = la_guid_from_dn(module, parent, dsdb_dn->dn, &link_guid);
    1006        6996 :                 if (ret != LDB_SUCCESS) {
    1007           0 :                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - GUID not found - %s",
    1008           0 :                                                el->name, target->lDAPDisplayName,
    1009             :                                                ldb_dn_get_linearized(old_dn),
    1010             :                                                ldb_dn_get_linearized(dsdb_dn->dn),
    1011             :                                                ldb_errstring(ldb));
    1012           0 :                         talloc_free(tmp_ctx);
    1013           0 :                         return ret;
    1014             :                 }
    1015             : 
    1016        6996 :                 link_guid_str = GUID_string(tmp_ctx, &link_guid);
    1017        6996 :                 if (link_guid_str == NULL) {
    1018           0 :                         talloc_free(tmp_ctx);
    1019           0 :                         return LDB_ERR_OPERATIONS_ERROR;
    1020             :                 }
    1021             : 
    1022             :                 /*
    1023             :                  * get the existing message from the db for the object with
    1024             :                  * this GUID, returning attribute being modified. We will then
    1025             :                  * use this msg as the basis for a modify call
    1026             :                  */
    1027             : 
    1028        6996 :                 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
    1029             :                                          DSDB_FLAG_NEXT_MODULE |
    1030             :                                          DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
    1031             :                                          DSDB_SEARCH_SHOW_RECYCLED |
    1032             :                                          DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
    1033             :                                          DSDB_SEARCH_REVEAL_INTERNALS,
    1034             :                                          parent,
    1035             :                                          "objectGUID=%s", link_guid_str);
    1036        6996 :                 if (ret != LDB_SUCCESS) {
    1037           0 :                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s not found - %s",
    1038           0 :                                                el->name, target->lDAPDisplayName,
    1039             :                                                ldb_dn_get_linearized(old_dn),
    1040             :                                                ldb_dn_get_linearized(dsdb_dn->dn),
    1041             :                                                link_guid_str,
    1042             :                                                ldb_errstring(ldb));
    1043           0 :                         talloc_free(tmp_ctx);
    1044           0 :                         return ret;
    1045             :                 }
    1046        6996 :                 if (res->count == 0) {
    1047             :                         /* Forward link without backlink object remaining - nothing to do here */
    1048          62 :                         continue;
    1049             :                 }
    1050        6994 :                 if (res->count != 1) {
    1051           0 :                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s found more than once!",
    1052           0 :                                                el->name, target->lDAPDisplayName,
    1053             :                                                ldb_dn_get_linearized(old_dn),
    1054             :                                                ldb_dn_get_linearized(dsdb_dn->dn),
    1055             :                                                link_guid_str);
    1056           0 :                         talloc_free(tmp_ctx);
    1057           0 :                         return LDB_ERR_OPERATIONS_ERROR;
    1058             :                 }
    1059             : 
    1060        6994 :                 msg = res->msgs[0];
    1061             : 
    1062        6994 :                 if (msg->num_elements == 0) {
    1063             :                         /* Forward link without backlink remaining - nothing to do here */
    1064          58 :                         continue;
    1065        6936 :                 } else if (msg->num_elements != 1) {
    1066           0 :                         ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s",
    1067             :                                                msg->num_elements, ldb_dn_get_linearized(msg->dn));
    1068           0 :                         talloc_free(tmp_ctx);
    1069           0 :                         return LDB_ERR_OPERATIONS_ERROR;
    1070             :                 }
    1071        6936 :                 if (ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
    1072           0 :                         ldb_asprintf_errstring(ldb, "Bad returned attribute in linked_attributes_fix_links: got %s, expected %s for %s", msg->elements[0].name, target->lDAPDisplayName, ldb_dn_get_linearized(msg->dn));
    1073           0 :                         talloc_free(tmp_ctx);
    1074           0 :                         return LDB_ERR_OPERATIONS_ERROR;
    1075             :                 }
    1076        6936 :                 el2 = &msg->elements[0];
    1077             : 
    1078        6936 :                 el2->flags = LDB_FLAG_MOD_REPLACE;
    1079             : 
    1080        8420 :                 if (target->linkID & 1 ||
    1081        2128 :                         ! la_private->sorted_links) {
    1082             :                         /* handle backlinks (which aren't sorted in the DB)
    1083             :                            and forward links in old unsorted databases. */
    1084        9333 :                         ret = linked_attributes_fix_link_slow(
    1085             :                                 module,
    1086             :                                 parent,
    1087             :                                 msg,
    1088             :                                 new_dn,
    1089             :                                 self_guid,
    1090        4813 :                                 target->syntax->ldap_oid,
    1091        4813 :                                 schema_attr->syntax->ldap_oid);
    1092             :                 } else {
    1093             :                         /* we can binary search to find forward links */
    1094        2123 :                         ret = linked_attributes_fix_forward_link(
    1095             :                                 module,
    1096             :                                 msg,
    1097             :                                 new_dn,
    1098             :                                 self_guid,
    1099        2123 :                                 target->syntax->ldap_oid);
    1100             :                 }
    1101        6936 :                 ret = dsdb_check_single_valued_link(target, el2);
    1102        6936 :                 if (ret != LDB_SUCCESS) {
    1103           0 :                         talloc_free(tmp_ctx);
    1104           0 :                         return ret;
    1105             :                 }
    1106             : 
    1107             :                 /* we may be putting multiple values in an attribute -
    1108             :                    disable checking for this attribute */
    1109        6936 :                 el2->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
    1110             : 
    1111        6936 :                 ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
    1112        6936 :                 if (ret != LDB_SUCCESS) {
    1113           0 :                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
    1114           0 :                                                el->name, target->lDAPDisplayName,
    1115             :                                                ldb_dn_get_linearized(old_dn),
    1116             :                                                ldb_dn_get_linearized(dsdb_dn->dn),
    1117             :                                                ldb_errstring(ldb));
    1118           0 :                         talloc_free(tmp_ctx);
    1119           0 :                         return ret;
    1120             :                 }
    1121             :         }
    1122             : 
    1123        3712 :         talloc_free(tmp_ctx);
    1124        3688 :         return LDB_SUCCESS;
    1125             : }
    1126             : 
    1127             : 
    1128             : /* rename */
    1129       61185 : static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
    1130             : {
    1131             :         struct ldb_result *res;
    1132             :         struct ldb_message *msg;
    1133             :         unsigned int i;
    1134       61185 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
    1135             :         struct dsdb_schema *schema;
    1136             :         int ret;
    1137             :         struct GUID guid;
    1138             : 
    1139             :         /*
    1140             :            - load the current msg
    1141             :            - find any linked attributes
    1142             :            - if its a link then find the target object
    1143             :            - modify the target linked attributes with the new DN
    1144             :         */
    1145       61185 :         ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
    1146             :                                     NULL,
    1147             :                                     DSDB_FLAG_NEXT_MODULE |
    1148             :                                     DSDB_SEARCH_SHOW_EXTENDED_DN |
    1149             :                                     DSDB_SEARCH_SHOW_RECYCLED, req);
    1150       61185 :         if (ret != LDB_SUCCESS) {
    1151           0 :                 return ret;
    1152             :         }
    1153             : 
    1154       61185 :         schema = dsdb_get_schema(ldb, res);
    1155       61185 :         if (!schema) {
    1156           0 :                 return ldb_oom(ldb);
    1157             :         }
    1158             : 
    1159       61185 :         msg = res->msgs[0];
    1160             : 
    1161       61185 :         ret = la_guid_from_dn(module, req, msg->dn, &guid);
    1162       61185 :         if (ret != LDB_SUCCESS) {
    1163           0 :                 return ret;
    1164             :         }
    1165             : 
    1166     1275569 :         for (i=0; i<msg->num_elements; i++) {
    1167     1214535 :                 struct ldb_message_element *el = &msg->elements[i];
    1168      951081 :                 const struct dsdb_attribute *schema_attr
    1169     1214535 :                         = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
    1170     1214535 :                 if (!schema_attr || schema_attr->linkID == 0) {
    1171     1210692 :                         continue;
    1172             :                 }
    1173        3843 :                 ret = linked_attributes_fix_links(module, guid, msg->dn, req->op.rename.newdn, el,
    1174             :                                                   schema, schema_attr, req);
    1175        3843 :                 if (ret != LDB_SUCCESS) {
    1176           0 :                         talloc_free(res);
    1177           0 :                         return ret;
    1178             :                 }
    1179             :         }
    1180             : 
    1181       61185 :         talloc_free(res);
    1182             : 
    1183       61185 :         return ldb_next_request(module, req);
    1184             : }
    1185             : 
    1186             : 
    1187             : /* queue a linked attributes modify request in the la_private
    1188             :    structure */
    1189        1784 : static int la_queue_mod_request(struct la_context *ac)
    1190             : {
    1191         915 :         struct la_private *la_private =
    1192        1784 :                 talloc_get_type(ldb_module_get_private(ac->module),
    1193             :                                 struct la_private);
    1194             : 
    1195        1784 :         if (la_private == NULL || la_private->transaction == NULL) {
    1196           0 :                 ldb_debug(ldb_module_get_ctx(ac->module),
    1197             :                           LDB_DEBUG_ERROR,
    1198             :                           __location__ ": No la_private transaction setup\n");
    1199           0 :                 return ldb_operr(ldb_module_get_ctx(ac->module));
    1200             :         }
    1201             : 
    1202        1784 :         talloc_steal(la_private->transaction, ac);
    1203        1784 :         DLIST_ADD(la_private->transaction->la_list, ac);
    1204             : 
    1205        1784 :         return ldb_module_done(ac->req, ac->op_controls,
    1206             :                                ac->op_response, LDB_SUCCESS);
    1207             : }
    1208             : 
    1209             : /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
    1210        1655 : static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
    1211             : {
    1212             :         struct la_context *ac;
    1213             :         struct ldb_context *ldb;
    1214             :         int ret;
    1215             : 
    1216        1655 :         ac = talloc_get_type(req->context, struct la_context);
    1217        1655 :         ldb = ldb_module_get_ctx(ac->module);
    1218             : 
    1219        1655 :         if (!ares) {
    1220           0 :                 return ldb_module_done(ac->req, NULL, NULL,
    1221             :                                         LDB_ERR_OPERATIONS_ERROR);
    1222             :         }
    1223        1655 :         if (ares->error != LDB_SUCCESS) {
    1224           2 :                 return ldb_module_done(ac->req, ares->controls,
    1225             :                                         ares->response, ares->error);
    1226             :         }
    1227             : 
    1228        1653 :         if (ares->type != LDB_REPLY_DONE) {
    1229           0 :                 ldb_set_errstring(ldb,
    1230             :                      "invalid reply type in linked attributes delete callback");
    1231           0 :                 talloc_free(ares);
    1232           0 :                 return ldb_module_done(ac->req, NULL, NULL,
    1233             :                                         LDB_ERR_OPERATIONS_ERROR);
    1234             :         }
    1235             : 
    1236        1653 :         ac->op_controls = talloc_steal(ac, ares->controls);
    1237        1653 :         ac->op_response = talloc_steal(ac, ares->response);
    1238             : 
    1239             :         /* If we have modfies to make, this is the time to do them for modify and delete */
    1240        1653 :         ret = la_queue_mod_request(ac);
    1241             : 
    1242        1653 :         if (ret != LDB_SUCCESS) {
    1243           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
    1244             :         }
    1245        1653 :         talloc_free(ares);
    1246             : 
    1247             :         /* la_queue_mod_request has already sent the callbacks */
    1248        1653 :         return LDB_SUCCESS;
    1249             : 
    1250             : }
    1251             : 
    1252             : /* Having done the original add, then try to fix up all the linked attributes
    1253             : 
    1254             :   This is done after the add so the links can get the extended DNs correctly.
    1255             :  */
    1256         131 : static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
    1257             : {
    1258             :         struct la_context *ac;
    1259             :         struct ldb_context *ldb;
    1260             :         int ret;
    1261             : 
    1262         131 :         ac = talloc_get_type(req->context, struct la_context);
    1263         131 :         ldb = ldb_module_get_ctx(ac->module);
    1264             : 
    1265         131 :         if (!ares) {
    1266           0 :                 return ldb_module_done(ac->req, NULL, NULL,
    1267             :                                         LDB_ERR_OPERATIONS_ERROR);
    1268             :         }
    1269         131 :         if (ares->error != LDB_SUCCESS) {
    1270           0 :                 return ldb_module_done(ac->req, ares->controls,
    1271             :                                         ares->response, ares->error);
    1272             :         }
    1273             : 
    1274         131 :         if (ares->type != LDB_REPLY_DONE) {
    1275           0 :                 ldb_set_errstring(ldb,
    1276             :                         "invalid reply type in linked attributes add callback");
    1277           0 :                 talloc_free(ares);
    1278           0 :                 return ldb_module_done(ac->req, NULL, NULL,
    1279             :                                         LDB_ERR_OPERATIONS_ERROR);
    1280             :         }
    1281             : 
    1282         131 :         if (ac->ops) {
    1283             :                 struct ldb_request *search_req;
    1284             :                 static const char *attrs[] = { NULL };
    1285             : 
    1286             :                 /* The callback does all the hard work here - we need
    1287             :                  * the objectGUID and SID of the added record */
    1288         214 :                 ret = ldb_build_search_req(&search_req, ldb, ac,
    1289         131 :                                            ac->req->op.add.message->dn,
    1290             :                                            LDB_SCOPE_BASE,
    1291             :                                            "(objectClass=*)", attrs,
    1292             :                                            NULL,
    1293             :                                            ac, la_mod_search_callback,
    1294             :                                            ac->req);
    1295         131 :                 LDB_REQ_SET_LOCATION(search_req);
    1296             : 
    1297         131 :                 if (ret == LDB_SUCCESS) {
    1298         131 :                         ret = dsdb_request_add_controls(search_req,
    1299             :                                                         DSDB_SEARCH_SHOW_RECYCLED |
    1300             :                                                         DSDB_SEARCH_SHOW_EXTENDED_DN);
    1301             :                 }
    1302         131 :                 if (ret != LDB_SUCCESS) {
    1303           0 :                         return ldb_module_done(ac->req, NULL, NULL,
    1304             :                                                ret);
    1305             :                 }
    1306             : 
    1307         131 :                 ac->op_controls = talloc_steal(ac, ares->controls);
    1308         131 :                 ac->op_response = talloc_steal(ac, ares->response);
    1309             : 
    1310         131 :                 return ldb_next_request(ac->module, search_req);
    1311             : 
    1312             :         } else {
    1313           0 :                 return ldb_module_done(ac->req, ares->controls,
    1314             :                                        ares->response, ares->error);
    1315             :         }
    1316             : }
    1317             : 
    1318             : /* Reconstruct the original request, but pointing at our local callback to finish things off */
    1319        1786 : static int la_down_req(struct la_context *ac)
    1320             : {
    1321             :         struct ldb_request *down_req;
    1322             :         struct ldb_context *ldb;
    1323             :         int ret;
    1324             : 
    1325        1786 :         ldb = ldb_module_get_ctx(ac->module);
    1326             : 
    1327        1786 :         switch (ac->req->operation) {
    1328         131 :         case LDB_ADD:
    1329         297 :                 ret = ldb_build_add_req(&down_req, ldb, ac,
    1330         131 :                                         ac->req->op.add.message,
    1331         131 :                                         ac->req->controls,
    1332             :                                         ac, la_add_callback,
    1333             :                                         ac->req);
    1334         131 :                 LDB_REQ_SET_LOCATION(down_req);
    1335         131 :                 break;
    1336        1655 :         case LDB_MODIFY:
    1337        3321 :                 ret = ldb_build_mod_req(&down_req, ldb, ac,
    1338        1655 :                                         ac->req->op.mod.message,
    1339        1655 :                                         ac->req->controls,
    1340             :                                         ac, la_mod_del_callback,
    1341             :                                         ac->req);
    1342        1655 :                 LDB_REQ_SET_LOCATION(down_req);
    1343        1655 :                 break;
    1344           0 :         default:
    1345           0 :                 ret = LDB_ERR_OPERATIONS_ERROR;
    1346             :         }
    1347        1786 :         if (ret != LDB_SUCCESS) {
    1348           0 :                 return ret;
    1349             :         }
    1350             : 
    1351        1786 :         return ldb_next_request(ac->module, down_req);
    1352             : }
    1353             : 
    1354             : /*
    1355             :   use the GUID part of an extended DN to find the target DN, in case
    1356             :   it has moved
    1357             :  */
    1358        2876 : static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
    1359             :                              struct GUID *guid, struct ldb_dn **dn)
    1360             : {
    1361        2876 :         return dsdb_module_dn_by_guid(ac->module, ac, guid, dn, ac->req);
    1362             : }
    1363             : 
    1364             : /* apply one la_context op change */
    1365        2876 : static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
    1366             : {
    1367             :         struct ldb_message_element *ret_el;
    1368             :         struct ldb_message *new_msg;
    1369             :         struct ldb_context *ldb;
    1370             :         int ret;
    1371             : 
    1372        2876 :         if (ac->mod_dn == NULL) {
    1373             :                 /* we didn't find the DN that we searched for */
    1374           0 :                 return LDB_SUCCESS;
    1375             :         }
    1376             : 
    1377        2876 :         ldb = ldb_module_get_ctx(ac->module);
    1378             : 
    1379             :         /* Create the modify request */
    1380        2876 :         new_msg = ldb_msg_new(ac);
    1381        2876 :         if (!new_msg) {
    1382           0 :                 return ldb_oom(ldb);
    1383             :         }
    1384             : 
    1385        2876 :         ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
    1386        2876 :         if (ret != LDB_SUCCESS) {
    1387          21 :                 return ret;
    1388             :         }
    1389             : 
    1390        2855 :         if (op->op == LA_OP_ADD) {
    1391        1532 :                 ret = ldb_msg_add_empty(new_msg, op->name,
    1392             :                                         LDB_FLAG_MOD_ADD, &ret_el);
    1393             :         } else {
    1394        1323 :                 ret = ldb_msg_add_empty(new_msg, op->name,
    1395             :                                         LDB_FLAG_MOD_DELETE, &ret_el);
    1396             :         }
    1397        2855 :         if (ret != LDB_SUCCESS) {
    1398           0 :                 return ret;
    1399             :         }
    1400        2855 :         ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
    1401        2855 :         if (!ret_el->values) {
    1402           0 :                 return ldb_oom(ldb);
    1403             :         }
    1404        2855 :         ret_el->num_values = 1;
    1405        2855 :         ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->mod_dn, 1));
    1406             : 
    1407             :         /* a backlink should never be single valued. Unfortunately the
    1408             :            exchange schema has a attribute
    1409             :            msExchBridgeheadedLocalConnectorsDNBL which is single
    1410             :            valued and a backlink. We need to cope with that by
    1411             :            ignoring the single value flag */
    1412        2855 :         ret_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
    1413             : 
    1414             : #if 0
    1415             :         ldb_debug(ldb, LDB_DEBUG_WARNING,
    1416             :                   "link on %s %s: %s %s\n",
    1417             :                   ldb_dn_get_linearized(new_msg->dn), ret_el->name,
    1418             :                   ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
    1419             : #endif
    1420             : 
    1421        2855 :         if (DEBUGLVL(4)) {
    1422           0 :                 DEBUG(4,("Applying linked attribute change:\n%s\n",
    1423             :                          ldb_ldif_message_redacted_string(ldb, op,
    1424             :                                                           LDB_CHANGETYPE_MODIFY,
    1425             :                                                           new_msg)));
    1426             :         }
    1427             : 
    1428        2855 :         ret = dsdb_module_modify(module, new_msg, DSDB_FLAG_NEXT_MODULE, ac->req);
    1429        2855 :         if (ret != LDB_SUCCESS) {
    1430           0 :                 ldb_debug(ldb, LDB_DEBUG_WARNING, __location__ ": failed to apply linked attribute change '%s'\n%s\n",
    1431             :                           ldb_errstring(ldb),
    1432             :                           ldb_ldif_message_redacted_string(ldb, op,
    1433             :                                                            LDB_CHANGETYPE_MODIFY,
    1434             :                                                            new_msg));
    1435             :         }
    1436             : 
    1437        2855 :         return ret;
    1438             : }
    1439             : 
    1440             : /* apply one set of la_context changes */
    1441        1784 : static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
    1442             : {
    1443             :         struct la_op_store *op;
    1444             : 
    1445        4660 :         for (op = ac->ops; op; op=op->next) {
    1446        2876 :                 int ret = la_do_op_request(module, ac, op);
    1447        2876 :                 if (ret != LDB_SUCCESS) {
    1448          21 :                         if (ret != LDB_ERR_NO_SUCH_OBJECT) {
    1449           0 :                                 return ret;
    1450             :                         }
    1451             :                 }
    1452             :         }
    1453             : 
    1454        1784 :         return LDB_SUCCESS;
    1455             : }
    1456             : 
    1457             : 
    1458             : /*
    1459             :   we hook into the transaction operations to allow us to
    1460             :   perform the linked attribute updates at the end of the whole
    1461             :   transaction. This allows a forward linked attribute to be created
    1462             :   before the target is created, as long as the target is created
    1463             :   in the same transaction
    1464             :  */
    1465      258981 : static int linked_attributes_start_transaction(struct ldb_module *module)
    1466             : {
    1467             :         /* create our private structure for this transaction */
    1468      195572 :         struct la_private *la_private =
    1469      258981 :                 talloc_get_type(ldb_module_get_private(module),
    1470             :                                 struct la_private);
    1471             : 
    1472      258981 :         if (la_private == NULL) {
    1473           0 :                 return ldb_oom(ldb_module_get_ctx(module));
    1474             :         }
    1475      258981 :         talloc_free(la_private->transaction);
    1476      258981 :         la_private->transaction = talloc(module, struct la_private_transaction);
    1477      258981 :         if (la_private->transaction == NULL) {
    1478           0 :                 return ldb_oom(ldb_module_get_ctx(module));
    1479             :         }
    1480      258981 :         la_private->transaction->la_list = NULL;
    1481      258981 :         return ldb_next_start_trans(module);
    1482             : }
    1483             : 
    1484             : /*
    1485             :   on prepare commit we loop over our queued la_context structures
    1486             :   and apply each of them
    1487             :  */
    1488      231527 : static int linked_attributes_prepare_commit(struct ldb_module *module)
    1489             : {
    1490             :         struct la_context *ac;
    1491      178492 :         struct la_private *la_private =
    1492      231527 :                 talloc_get_type(ldb_module_get_private(module),
    1493             :                                 struct la_private);
    1494      231527 :         if (la_private == NULL || la_private->transaction == NULL) {
    1495           0 :                 DBG_ERR("prepare_commit without begin_transaction\n");
    1496             :                 /* prepare commit without begin_transaction - let someone else
    1497             :                  * return the error, just don't segfault */
    1498           0 :                 return ldb_next_prepare_commit(module);
    1499             :         }
    1500             :         /* walk the list backwards, to do the first entry first, as we
    1501             :          * added the entries with DLIST_ADD() which puts them at the
    1502             :          * start of the list */
    1503             : 
    1504             :         /* Start at the end of the list - so we can start
    1505             :          * there, but ensure we don't create a loop by NULLing
    1506             :          * it out in the first element */
    1507      231527 :         ac = DLIST_TAIL(la_private->transaction->la_list);
    1508             : 
    1509      231730 :         for (; ac; ac=DLIST_PREV(ac)) {
    1510             :                 int ret;
    1511        1784 :                 ac->req = NULL;
    1512        1784 :                 ret = la_do_mod_request(module, ac);
    1513        1784 :                 if (ret != LDB_SUCCESS) {
    1514           0 :                         DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
    1515           0 :                         TALLOC_FREE(la_private->transaction);
    1516           0 :                         return ret;
    1517             :                 }
    1518             :         }
    1519             : 
    1520      231527 :         TALLOC_FREE(la_private->transaction);
    1521             : 
    1522      231527 :         return ldb_next_prepare_commit(module);
    1523             : }
    1524             : 
    1525       27388 : static int linked_attributes_del_transaction(struct ldb_module *module)
    1526             : {
    1527       17014 :         struct la_private *la_private =
    1528       27388 :                 talloc_get_type(ldb_module_get_private(module),
    1529             :                                 struct la_private);
    1530       27388 :         TALLOC_FREE(la_private->transaction);
    1531       27388 :         return ldb_next_del_trans(module);
    1532             : }
    1533             : 
    1534      132453 : static int linked_attributes_ldb_init(struct ldb_module *module)
    1535             : {
    1536             :         int ret;
    1537      132453 :         struct la_private *la_private = NULL;
    1538      132453 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
    1539             : 
    1540      132453 :         ret = ldb_mod_register_control(module, LDB_CONTROL_VERIFY_NAME_OID);
    1541      132453 :         if (ret != LDB_SUCCESS) {
    1542           0 :                 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
    1543             :                         "verify_name: Unable to register control with rootdse!\n");
    1544           0 :                 return ldb_operr(ldb_module_get_ctx(module));
    1545             :         }
    1546             : 
    1547      132453 :         la_private = talloc_zero(module, struct la_private);
    1548      132453 :         if (la_private == NULL) {
    1549           0 :                 ldb_oom(ldb);
    1550           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    1551             :         }
    1552             : 
    1553      132453 :         ret = dsdb_check_samba_compatible_feature(module,
    1554             :                                                   SAMBA_SORTED_LINKS_FEATURE,
    1555             :                                                   &la_private->sorted_links);
    1556      132453 :         if (ret != LDB_SUCCESS) {
    1557           0 :                 talloc_free(la_private);
    1558           0 :                 return ret;
    1559             :         }
    1560             : 
    1561      132453 :         ldb_module_set_private(module, la_private);
    1562      132453 :         return ldb_next_init(module);
    1563             : }
    1564             : 
    1565             : 
    1566             : static const struct ldb_module_ops ldb_linked_attributes_module_ops = {
    1567             :         .name              = "linked_attributes",
    1568             :         .add               = linked_attributes_add,
    1569             :         .modify            = linked_attributes_modify,
    1570             :         .rename            = linked_attributes_rename,
    1571             :         .init_context      = linked_attributes_ldb_init,
    1572             :         .start_transaction = linked_attributes_start_transaction,
    1573             :         .prepare_commit    = linked_attributes_prepare_commit,
    1574             :         .del_transaction   = linked_attributes_del_transaction,
    1575             : };
    1576             : 
    1577        5536 : int ldb_linked_attributes_module_init(const char *version)
    1578             : {
    1579        5536 :         LDB_MODULE_CHECK_VERSION(version);
    1580        5536 :         return ldb_register_module(&ldb_linked_attributes_module_ops);
    1581             : }

Generated by: LCOV version 1.13