Back to index

openldap  2.4.31
modrdn.cpp
Go to the documentation of this file.
00001 /* modrdn.cpp - ndb backend modrdn routine */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2008-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 /* ACKNOWLEDGEMENTS:
00017  * This work was initially developed by Howard Chu for inclusion
00018  * in OpenLDAP Software. This work was sponsored by MySQL.
00019  */
00020 
00021 #include "portable.h"
00022 
00023 #include <stdio.h>
00024 #include <ac/string.h>
00025 
00026 #include "back-ndb.h"
00027 
00028 int
00029 ndb_back_modrdn( Operation *op, SlapReply *rs )
00030 {
00031        struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
00032        AttributeDescription *children = slap_schema.si_ad_children;
00033        AttributeDescription *entry = slap_schema.si_ad_entry;
00034        struct berval new_dn = BER_BVNULL, new_ndn = BER_BVNULL;
00035        Entry         e = {0};
00036        Entry         e2 = {0};
00037        char textbuf[SLAP_TEXT_BUFLEN];
00038        size_t textlen = sizeof textbuf;
00039 
00040        struct berval *np_dn = NULL;                     /* newSuperior dn */
00041        struct berval *np_ndn = NULL;                    /* newSuperior ndn */
00042 
00043        int           manageDSAit = get_manageDSAit( op );
00044        int           num_retries = 0;
00045 
00046        NdbArgs NA, NA2;
00047        NdbRdns rdns, rdn2;
00048        struct berval matched;
00049 
00050        LDAPControl **preread_ctrl = NULL;
00051        LDAPControl **postread_ctrl = NULL;
00052        LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
00053        int num_ctrls = 0;
00054 
00055        int    rc;
00056 
00057        Debug( LDAP_DEBUG_ARGS, "==>" LDAP_XSTRING(ndb_back_modrdn) "(%s,%s,%s)\n",
00058               op->o_req_dn.bv_val,op->oq_modrdn.rs_newrdn.bv_val,
00059               op->oq_modrdn.rs_newSup ? op->oq_modrdn.rs_newSup->bv_val : "NULL" );
00060 
00061        ctrls[num_ctrls] = NULL;
00062 
00063        slap_mods_opattrs( op, &op->orr_modlist, 1 );
00064 
00065        e.e_name = op->o_req_dn;
00066        e.e_nname = op->o_req_ndn;
00067 
00068        /* Get our NDB handle */
00069        rs->sr_err = ndb_thread_handle( op, &NA.ndb );
00070        rdns.nr_num = 0;
00071        NA.rdns = &rdns;
00072        NA.e = &e;
00073        NA2.ndb = NA.ndb;
00074        NA2.e = &e2;
00075        NA2.rdns = &rdn2;
00076 
00077        if( 0 ) {
00078 retry: /* transaction retry */
00079               NA.txn->close();
00080               NA.txn = NULL;
00081               if ( e.e_attrs ) {
00082                      attrs_free( e.e_attrs );
00083                      e.e_attrs = NULL;
00084               }
00085               Debug( LDAP_DEBUG_TRACE, "==>" LDAP_XSTRING(ndb_back_modrdn)
00086                             ": retrying...\n", 0, 0, 0 );
00087               if ( op->o_abandon ) {
00088                      rs->sr_err = SLAPD_ABANDON;
00089                      goto return_results;
00090               }
00091               if ( NA2.ocs ) {
00092                      ber_bvarray_free_x( NA2.ocs, op->o_tmpmemctx );
00093               }
00094               if ( NA.ocs ) {
00095                      ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
00096               }
00097               ndb_trans_backoff( ++num_retries );
00098        }
00099        NA.ocs = NULL;
00100        NA2.ocs = NULL;
00101 
00102        /* begin transaction */
00103        NA.txn = NA.ndb->startTransaction();
00104        rs->sr_text = NULL;
00105        if( !NA.txn ) {
00106               Debug( LDAP_DEBUG_TRACE,
00107                      LDAP_XSTRING(ndb_back_modrdn) ": startTransaction failed: %s (%d)\n",
00108                      NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
00109               rs->sr_err = LDAP_OTHER;
00110               rs->sr_text = "internal error";
00111               goto return_results;
00112        }
00113        NA2.txn = NA.txn;
00114 
00115        /* get entry */
00116        rs->sr_err = ndb_entry_get_info( op, &NA, 1, &matched );
00117        switch( rs->sr_err ) {
00118        case 0:
00119               break;
00120        case LDAP_NO_SUCH_OBJECT:
00121               Debug( LDAP_DEBUG_ARGS,
00122                      "<=- ndb_back_modrdn: no such object %s\n",
00123                      op->o_req_dn.bv_val, 0, 0 );
00124               rs->sr_matched = matched.bv_val;
00125               if ( NA.ocs )
00126                      ndb_check_referral( op, rs, &NA );
00127               goto return_results;
00128 #if 0
00129        case DB_LOCK_DEADLOCK:
00130        case DB_LOCK_NOTGRANTED:
00131               goto retry;
00132 #endif
00133        case LDAP_BUSY:
00134               rs->sr_text = "ldap server busy";
00135               goto return_results;
00136        default:
00137               rs->sr_err = LDAP_OTHER;
00138               rs->sr_text = "internal error";
00139               goto return_results;
00140        }
00141 
00142        /* acquire and lock entry */
00143        rs->sr_err = ndb_entry_get_data( op, &NA, 1 );
00144        if ( rs->sr_err )
00145               goto return_results;
00146 
00147        if ( !manageDSAit && is_entry_glue( &e )) {
00148               rs->sr_err = LDAP_NO_SUCH_OBJECT;
00149               goto return_results;
00150        }
00151        
00152        if ( get_assert( op ) &&
00153               ( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
00154        {
00155               rs->sr_err = LDAP_ASSERTION_FAILED;
00156               goto return_results;
00157        }
00158 
00159        /* check write on old entry */
00160        rs->sr_err = access_allowed( op, &e, entry, NULL, ACL_WRITE, NULL );
00161        if ( ! rs->sr_err ) {
00162               Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0,
00163                      0, 0 );
00164               rs->sr_text = "no write access to old entry";
00165               rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
00166               goto return_results;
00167        }
00168 
00169        /* Can't do it if we have kids */
00170        rs->sr_err = ndb_has_children( &NA, &rc );
00171        if ( rs->sr_err ) {
00172               Debug(LDAP_DEBUG_ARGS,
00173                      "<=- " LDAP_XSTRING(ndb_back_modrdn)
00174                      ": has_children failed: %s (%d)\n",
00175                      NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
00176               rs->sr_err = LDAP_OTHER;
00177               rs->sr_text = "internal error";
00178               goto return_results;
00179        }
00180        if ( rc == LDAP_COMPARE_TRUE ) {
00181               Debug(LDAP_DEBUG_ARGS,
00182                      "<=- " LDAP_XSTRING(ndb_back_modrdn)
00183                      ": non-leaf %s\n",
00184                      op->o_req_dn.bv_val, 0, 0);
00185               rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
00186               rs->sr_text = "subtree rename not supported";
00187               goto return_results;
00188        }
00189 
00190        if (!manageDSAit && is_entry_referral( &e ) ) {
00191               /* entry is a referral, don't allow modrdn */
00192               rs->sr_ref = get_entry_referrals( op, &e );
00193 
00194               Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn)
00195                      ": entry %s is referral\n", e.e_dn, 0, 0 );
00196 
00197               rs->sr_err = LDAP_REFERRAL,
00198               rs->sr_matched = op->o_req_dn.bv_val;
00199               rs->sr_flags = REP_REF_MUSTBEFREED;
00200               goto return_results;
00201        }
00202 
00203        if ( be_issuffix( op->o_bd, &e.e_nname ) ) {
00204               /* There can only be one suffix entry */
00205               rs->sr_err = LDAP_NAMING_VIOLATION;
00206               rs->sr_text = "cannot rename suffix entry";
00207               goto return_results;
00208        } else {
00209               dnParent( &e.e_nname, &e2.e_nname );
00210               dnParent( &e.e_name, &e2.e_name );
00211        }
00212 
00213        /* check parent for "children" acl */
00214        rs->sr_err = access_allowed( op, &e2,
00215               children, NULL,
00216               op->oq_modrdn.rs_newSup == NULL ?
00217                      ACL_WRITE : ACL_WDEL,
00218               NULL );
00219 
00220        if ( ! rs->sr_err ) {
00221               rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
00222               Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
00223                      0, 0 );
00224               rs->sr_text = "no write access to old parent's children";
00225               goto return_results;
00226        }
00227 
00228        Debug( LDAP_DEBUG_TRACE,
00229               LDAP_XSTRING(ndb_back_modrdn) ": wr to children "
00230               "of entry %s OK\n", e2.e_name.bv_val, 0, 0 );
00231        
00232        if ( op->oq_modrdn.rs_newSup != NULL ) {
00233               Debug( LDAP_DEBUG_TRACE, 
00234                      LDAP_XSTRING(ndb_back_modrdn)
00235                      ": new parent \"%s\" requested...\n",
00236                      op->oq_modrdn.rs_newSup->bv_val, 0, 0 );
00237 
00238               /*  newSuperior == oldParent? */
00239               if( dn_match( &e2.e_nname, op->oq_modrdn.rs_nnewSup ) ) {
00240                      Debug( LDAP_DEBUG_TRACE, "bdb_back_modrdn: "
00241                             "new parent \"%s\" same as the old parent \"%s\"\n",
00242                             op->oq_modrdn.rs_newSup->bv_val, e2.e_name.bv_val, 0 );
00243                      op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
00244               }
00245        }
00246 
00247        if ( op->oq_modrdn.rs_newSup != NULL ) {
00248               if ( op->oq_modrdn.rs_newSup->bv_len ) {
00249                      rdn2.nr_num = 0;
00250                      np_dn = op->oq_modrdn.rs_newSup;
00251                      np_ndn = op->oq_modrdn.rs_nnewSup;
00252 
00253                      /* newSuperior == oldParent? - checked above */
00254                      /* newSuperior == entry being moved?, if so ==> ERROR */
00255                      if ( dnIsSuffix( np_ndn, &e.e_nname )) {
00256                             rs->sr_err = LDAP_NO_SUCH_OBJECT;
00257                             rs->sr_text = "new superior not found";
00258                             goto return_results;
00259                      }
00260                      /* Get Entry with dn=newSuperior. Does newSuperior exist? */
00261 
00262                      e2.e_name = *np_dn;
00263                      e2.e_nname = *np_ndn;
00264                      rs->sr_err = ndb_entry_get_info( op, &NA2, 1, NULL );
00265                      switch( rs->sr_err ) {
00266                      case 0:
00267                             break;
00268                      case LDAP_NO_SUCH_OBJECT:
00269                             Debug( LDAP_DEBUG_TRACE,
00270                                    LDAP_XSTRING(ndb_back_modrdn)
00271                                    ": newSup(ndn=%s) not here!\n",
00272                                    np_ndn->bv_val, 0, 0);
00273                             rs->sr_text = "new superior not found";
00274                             goto return_results;
00275 #if 0
00276                      case DB_LOCK_DEADLOCK:
00277                      case DB_LOCK_NOTGRANTED:
00278                             goto retry;
00279 #endif
00280                      case LDAP_BUSY:
00281                             rs->sr_text = "ldap server busy";
00282                             goto return_results;
00283                      default:
00284                             rs->sr_err = LDAP_OTHER;
00285                             rs->sr_text = "internal error";
00286                             goto return_results;
00287                      }
00288                      if ( NA2.ocs ) {
00289                             Attribute a;
00290                             int i;
00291 
00292                             for ( i=0; !BER_BVISNULL( &NA2.ocs[i] ); i++);
00293                             a.a_numvals = i;
00294                             a.a_desc = slap_schema.si_ad_objectClass;
00295                             a.a_vals = NA2.ocs;
00296                             a.a_nvals = NA2.ocs;
00297                             a.a_next = NULL;
00298                             e2.e_attrs = &a;
00299 
00300                             if ( is_entry_alias( &e2 )) {
00301                                    /* parent is an alias, don't allow move */
00302                                    Debug( LDAP_DEBUG_TRACE,
00303                                           LDAP_XSTRING(ndb_back_modrdn)
00304                                           ": entry is alias\n",
00305                                           0, 0, 0 );
00306                                    rs->sr_text = "new superior is an alias";
00307                                    rs->sr_err = LDAP_ALIAS_PROBLEM;
00308                                    goto return_results;
00309                             }
00310 
00311                             if ( is_entry_referral( &e2 ) ) {
00312                                    /* parent is a referral, don't allow move */
00313                                    Debug( LDAP_DEBUG_TRACE,
00314                                           LDAP_XSTRING(ndb_back_modrdn)
00315                                           ": entry is referral\n",
00316                                           0, 0, 0 );
00317                                    rs->sr_text = "new superior is a referral";
00318                                    rs->sr_err = LDAP_OTHER;
00319                                    goto return_results;
00320                             }
00321                      }
00322               }
00323 
00324               /* check newSuperior for "children" acl */
00325               rs->sr_err = access_allowed( op, &e2, children,
00326                      NULL, ACL_WADD, NULL );
00327               if( ! rs->sr_err ) {
00328                      Debug( LDAP_DEBUG_TRACE,
00329                             LDAP_XSTRING(ndb_back_modrdn)
00330                             ": no wr to newSup children\n",
00331                             0, 0, 0 );
00332                      rs->sr_text = "no write access to new superior's children";
00333                      rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
00334                      goto return_results;
00335               }
00336 
00337               Debug( LDAP_DEBUG_TRACE,
00338                      LDAP_XSTRING(ndb_back_modrdn)
00339                      ": wr to new parent OK id=%ld\n",
00340                      (long) e2.e_id, 0, 0 );
00341        }
00342 
00343        /* Build target dn and make sure target entry doesn't exist already. */
00344        if (!new_dn.bv_val) {
00345               build_new_dn( &new_dn, &e2.e_name, &op->oq_modrdn.rs_newrdn, NULL ); 
00346        }
00347 
00348        if (!new_ndn.bv_val) {
00349               build_new_dn( &new_ndn, &e2.e_nname, &op->oq_modrdn.rs_nnewrdn, NULL ); 
00350        }
00351 
00352        Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn) ": new ndn=%s\n",
00353               new_ndn.bv_val, 0, 0 );
00354 
00355        /* Allow rename to same DN */
00356        if ( !bvmatch ( &new_ndn, &e.e_nname )) {
00357               rdn2.nr_num = 0;
00358               e2.e_name = new_dn;
00359               e2.e_nname = new_ndn;
00360               NA2.ocs = &matched;
00361               rs->sr_err = ndb_entry_get_info( op, &NA2, 1, NULL );
00362               NA2.ocs = NULL;
00363               switch( rs->sr_err ) {
00364 #if 0
00365               case DB_LOCK_DEADLOCK:
00366               case DB_LOCK_NOTGRANTED:
00367                      goto retry;
00368 #endif
00369               case LDAP_NO_SUCH_OBJECT:
00370                      break;
00371               case 0:
00372                      rs->sr_err = LDAP_ALREADY_EXISTS;
00373                      goto return_results;
00374               default:
00375                      rs->sr_err = LDAP_OTHER;
00376                      rs->sr_text = "internal error";
00377                      goto return_results;
00378               }
00379        }
00380 
00381        assert( op->orr_modlist != NULL );
00382 
00383        if( op->o_preread ) {
00384               if( preread_ctrl == NULL ) {
00385                      preread_ctrl = &ctrls[num_ctrls++];
00386                      ctrls[num_ctrls] = NULL;
00387               }
00388               if( slap_read_controls( op, rs, &e,
00389                      &slap_pre_read_bv, preread_ctrl ) )
00390               {
00391                      Debug( LDAP_DEBUG_TRACE,        
00392                             "<=- " LDAP_XSTRING(ndb_back_modrdn)
00393                             ": pre-read failed!\n", 0, 0, 0 );
00394                      if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
00395                             /* FIXME: is it correct to abort
00396                              * operation if control fails? */
00397                             goto return_results;
00398                      }
00399               }                   
00400        }
00401 
00402        /* delete old DN */
00403        rs->sr_err = ndb_entry_del_info( op->o_bd, &NA );
00404        if ( rs->sr_err != 0 ) {
00405               Debug(LDAP_DEBUG_TRACE,
00406                      "<=- " LDAP_XSTRING(ndb_back_modrdn)
00407                      ": dn2id del failed: %s (%d)\n",
00408                      NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
00409 #if 0
00410               switch( rs->sr_err ) {
00411               case DB_LOCK_DEADLOCK:
00412               case DB_LOCK_NOTGRANTED:
00413                      goto retry;
00414               }
00415 #endif
00416               rs->sr_err = LDAP_OTHER;
00417               rs->sr_text = "DN index delete fail";
00418               goto return_results;
00419        }
00420 
00421        /* copy entry fields */
00422        e2.e_attrs = e.e_attrs;
00423        e2.e_id = e.e_id;
00424 
00425        /* add new DN */
00426        rs->sr_err = ndb_entry_put_info( op->o_bd, &NA2, 0 );
00427        if ( rs->sr_err != 0 ) {
00428               Debug(LDAP_DEBUG_TRACE,
00429                      "<=- " LDAP_XSTRING(ndb_back_modrdn)
00430                      ": dn2id add failed: %s (%d)\n",
00431                      NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
00432 #if 0
00433               switch( rs->sr_err ) {
00434               case DB_LOCK_DEADLOCK:
00435               case DB_LOCK_NOTGRANTED:
00436                      goto retry;
00437               }
00438 #endif
00439               rs->sr_err = LDAP_OTHER;
00440               rs->sr_text = "DN index add failed";
00441               goto return_results;
00442        }
00443 
00444        /* modify entry */
00445        rs->sr_err = ndb_modify_internal( op, &NA2,
00446               &rs->sr_text, textbuf, textlen );
00447        if( rs->sr_err != LDAP_SUCCESS ) {
00448               Debug(LDAP_DEBUG_TRACE,
00449                      "<=- " LDAP_XSTRING(ndb_back_modrdn)
00450                      ": modify failed: %s (%d)\n",
00451                      NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
00452 #if 0
00453               switch( rs->sr_err ) {
00454               case DB_LOCK_DEADLOCK:
00455               case DB_LOCK_NOTGRANTED:
00456                      goto retry;
00457               }
00458 #endif
00459               goto return_results;
00460        }
00461 
00462        e.e_attrs = e2.e_attrs;
00463 
00464        if( op->o_postread ) {
00465               if( postread_ctrl == NULL ) {
00466                      postread_ctrl = &ctrls[num_ctrls++];
00467                      ctrls[num_ctrls] = NULL;
00468               }
00469               if( slap_read_controls( op, rs, &e2,
00470                      &slap_post_read_bv, postread_ctrl ) )
00471               {
00472                      Debug( LDAP_DEBUG_TRACE,        
00473                             "<=- " LDAP_XSTRING(ndb_back_modrdn)
00474                             ": post-read failed!\n", 0, 0, 0 );
00475                      if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
00476                             /* FIXME: is it correct to abort
00477                              * operation if control fails? */
00478                             goto return_results;
00479                      }
00480               }                   
00481        }
00482 
00483        if( op->o_noop ) {
00484               if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
00485                      NdbOperation::AbortOnError, 1 )) != 0 ) {
00486                      rs->sr_text = "txn_abort (no-op) failed";
00487               } else {
00488                      rs->sr_err = LDAP_X_NO_OPERATION;
00489               }
00490        } else {
00491               if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
00492                      NdbOperation::AbortOnError, 1 )) != 0 ) {
00493                      rs->sr_text = "txn_commit failed";
00494               } else {
00495                      rs->sr_err = LDAP_SUCCESS;
00496               }
00497        }
00498  
00499        if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
00500               Debug( LDAP_DEBUG_TRACE,
00501                      LDAP_XSTRING(ndb_back_modrdn) ": txn_%s failed: %s (%d)\n",
00502                      op->o_noop ? "abort (no-op)" : "commit",
00503                      NA.txn->getNdbError().message, NA.txn->getNdbError().code );
00504               rs->sr_err = LDAP_OTHER;
00505               goto return_results;
00506        }
00507        NA.txn->close();
00508        NA.txn = NULL;
00509 
00510        Debug(LDAP_DEBUG_TRACE,
00511               LDAP_XSTRING(ndb_back_modrdn)
00512               ": rdn modified%s id=%08lx dn=\"%s\"\n",
00513               op->o_noop ? " (no-op)" : "",
00514               e.e_id, op->o_req_dn.bv_val );
00515 
00516        rs->sr_err = LDAP_SUCCESS;
00517        rs->sr_text = NULL;
00518        if( num_ctrls ) rs->sr_ctrls = ctrls;
00519 
00520 return_results:
00521        if ( NA2.ocs ) {
00522               ber_bvarray_free_x( NA2.ocs, op->o_tmpmemctx );
00523               NA2.ocs = NULL;
00524        }
00525 
00526        if ( NA.ocs ) {
00527               ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
00528               NA.ocs = NULL;
00529        }
00530 
00531        if ( e.e_attrs ) {
00532               attrs_free( e.e_attrs );
00533               e.e_attrs = NULL;
00534        }
00535 
00536        if( NA.txn != NULL ) {
00537               NA.txn->execute( Rollback );
00538               NA.txn->close();
00539        }
00540 
00541        send_ldap_result( op, rs );
00542        slap_graduate_commit_csn( op );
00543 
00544        if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
00545        if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );
00546 
00547        if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
00548               slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
00549               slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
00550        }
00551        if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
00552               slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
00553               slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
00554        }
00555 
00556        rs->sr_text = NULL;
00557        return rs->sr_err;
00558 }