1/* modrdn.cpp - ndb backend modrdn routine */ 2/* OpenLDAP: pkg/ldap/servers/slapd/back-ndb/modrdn.cpp,v 1.3.2.3 2010/04/13 20:23:35 kurt Exp */ 3/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 2008-2010 The OpenLDAP Foundation. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted only as authorized by the OpenLDAP 10 * Public License. 11 * 12 * A copy of this license is available in the file LICENSE in the 13 * top-level directory of the distribution or, alternatively, at 14 * <http://www.OpenLDAP.org/license.html>. 15 */ 16/* ACKNOWLEDGEMENTS: 17 * This work was initially developed by Howard Chu for inclusion 18 * in OpenLDAP Software. This work was sponsored by MySQL. 19 */ 20 21#include "portable.h" 22 23#include <stdio.h> 24#include <ac/string.h> 25 26#include "back-ndb.h" 27 28int 29ndb_back_modrdn( Operation *op, SlapReply *rs ) 30{ 31 struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private; 32 AttributeDescription *children = slap_schema.si_ad_children; 33 AttributeDescription *entry = slap_schema.si_ad_entry; 34 struct berval new_dn = BER_BVNULL, new_ndn = BER_BVNULL; 35 Entry e = {0}; 36 Entry e2 = {0}; 37 char textbuf[SLAP_TEXT_BUFLEN]; 38 size_t textlen = sizeof textbuf; 39 40 struct berval *np_dn = NULL; /* newSuperior dn */ 41 struct berval *np_ndn = NULL; /* newSuperior ndn */ 42 43 int manageDSAit = get_manageDSAit( op ); 44 int num_retries = 0; 45 46 NdbArgs NA, NA2; 47 NdbRdns rdns, rdn2; 48 struct berval matched; 49 50 LDAPControl **preread_ctrl = NULL; 51 LDAPControl **postread_ctrl = NULL; 52 LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS]; 53 int num_ctrls = 0; 54 55 int rc; 56 57 Debug( LDAP_DEBUG_ARGS, "==>" LDAP_XSTRING(ndb_back_modrdn) "(%s,%s,%s)\n", 58 op->o_req_dn.bv_val,op->oq_modrdn.rs_newrdn.bv_val, 59 op->oq_modrdn.rs_newSup ? op->oq_modrdn.rs_newSup->bv_val : "NULL" ); 60 61 ctrls[num_ctrls] = NULL; 62 63 slap_mods_opattrs( op, &op->orr_modlist, 1 ); 64 65 e.e_name = op->o_req_dn; 66 e.e_nname = op->o_req_ndn; 67 68 /* Get our NDB handle */ 69 rs->sr_err = ndb_thread_handle( op, &NA.ndb ); 70 rdns.nr_num = 0; 71 NA.rdns = &rdns; 72 NA.e = &e; 73 NA2.ndb = NA.ndb; 74 NA2.e = &e2; 75 NA2.rdns = &rdn2; 76 77 if( 0 ) { 78retry: /* transaction retry */ 79 NA.txn->close(); 80 NA.txn = NULL; 81 if ( e.e_attrs ) { 82 attrs_free( e.e_attrs ); 83 e.e_attrs = NULL; 84 } 85 Debug( LDAP_DEBUG_TRACE, "==>" LDAP_XSTRING(ndb_back_modrdn) 86 ": retrying...\n", 0, 0, 0 ); 87 if ( op->o_abandon ) { 88 rs->sr_err = SLAPD_ABANDON; 89 goto return_results; 90 } 91 if ( NA2.ocs ) { 92 ber_bvarray_free_x( NA2.ocs, op->o_tmpmemctx ); 93 } 94 if ( NA.ocs ) { 95 ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx ); 96 } 97 ndb_trans_backoff( ++num_retries ); 98 } 99 NA.ocs = NULL; 100 NA2.ocs = NULL; 101 102 /* begin transaction */ 103 NA.txn = NA.ndb->startTransaction(); 104 rs->sr_text = NULL; 105 if( !NA.txn ) { 106 Debug( LDAP_DEBUG_TRACE, 107 LDAP_XSTRING(ndb_back_modrdn) ": startTransaction failed: %s (%d)\n", 108 NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 ); 109 rs->sr_err = LDAP_OTHER; 110 rs->sr_text = "internal error"; 111 goto return_results; 112 } 113 NA2.txn = NA.txn; 114 115 /* get entry */ 116 rs->sr_err = ndb_entry_get_info( op, &NA, 1, &matched ); 117 switch( rs->sr_err ) { 118 case 0: 119 break; 120 case LDAP_NO_SUCH_OBJECT: 121 Debug( LDAP_DEBUG_ARGS, 122 "<=- ndb_back_modrdn: no such object %s\n", 123 op->o_req_dn.bv_val, 0, 0 ); 124 rs->sr_matched = matched.bv_val; 125 if ( NA.ocs ) 126 ndb_check_referral( op, rs, &NA ); 127 goto return_results; 128#if 0 129 case DB_LOCK_DEADLOCK: 130 case DB_LOCK_NOTGRANTED: 131 goto retry; 132#endif 133 case LDAP_BUSY: 134 rs->sr_text = "ldap server busy"; 135 goto return_results; 136 default: 137 rs->sr_err = LDAP_OTHER; 138 rs->sr_text = "internal error"; 139 goto return_results; 140 } 141 142 /* acquire and lock entry */ 143 rs->sr_err = ndb_entry_get_data( op, &NA, 1 ); 144 if ( rs->sr_err ) 145 goto return_results; 146 147 if ( !manageDSAit && is_entry_glue( &e )) { 148 rs->sr_err = LDAP_NO_SUCH_OBJECT; 149 goto return_results; 150 } 151 152 if ( get_assert( op ) && 153 ( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE )) 154 { 155 rs->sr_err = LDAP_ASSERTION_FAILED; 156 goto return_results; 157 } 158 159 /* check write on old entry */ 160 rs->sr_err = access_allowed( op, &e, entry, NULL, ACL_WRITE, NULL ); 161 if ( ! rs->sr_err ) { 162 Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0, 163 0, 0 ); 164 rs->sr_text = "no write access to old entry"; 165 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 166 goto return_results; 167 } 168 169 /* Can't do it if we have kids */ 170 rs->sr_err = ndb_has_children( &NA, &rc ); 171 if ( rs->sr_err ) { 172 Debug(LDAP_DEBUG_ARGS, 173 "<=- " LDAP_XSTRING(ndb_back_modrdn) 174 ": has_children failed: %s (%d)\n", 175 NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 ); 176 rs->sr_err = LDAP_OTHER; 177 rs->sr_text = "internal error"; 178 goto return_results; 179 } 180 if ( rc == LDAP_COMPARE_TRUE ) { 181 Debug(LDAP_DEBUG_ARGS, 182 "<=- " LDAP_XSTRING(ndb_back_modrdn) 183 ": non-leaf %s\n", 184 op->o_req_dn.bv_val, 0, 0); 185 rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF; 186 rs->sr_text = "subtree rename not supported"; 187 goto return_results; 188 } 189 190 if (!manageDSAit && is_entry_referral( &e ) ) { 191 /* entry is a referral, don't allow modrdn */ 192 rs->sr_ref = get_entry_referrals( op, &e ); 193 194 Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn) 195 ": entry %s is referral\n", e.e_dn, 0, 0 ); 196 197 rs->sr_err = LDAP_REFERRAL, 198 rs->sr_matched = op->o_req_dn.bv_val; 199 rs->sr_flags = REP_REF_MUSTBEFREED; 200 goto return_results; 201 } 202 203 if ( be_issuffix( op->o_bd, &e.e_nname ) ) { 204 /* There can only be one suffix entry */ 205 rs->sr_err = LDAP_NAMING_VIOLATION; 206 rs->sr_text = "cannot rename suffix entry"; 207 goto return_results; 208 } else { 209 dnParent( &e.e_nname, &e2.e_nname ); 210 dnParent( &e.e_name, &e2.e_name ); 211 } 212 213 /* check parent for "children" acl */ 214 rs->sr_err = access_allowed( op, &e2, 215 children, NULL, 216 op->oq_modrdn.rs_newSup == NULL ? 217 ACL_WRITE : ACL_WDEL, 218 NULL ); 219 220 if ( ! rs->sr_err ) { 221 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 222 Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0, 223 0, 0 ); 224 rs->sr_text = "no write access to old parent's children"; 225 goto return_results; 226 } 227 228 Debug( LDAP_DEBUG_TRACE, 229 LDAP_XSTRING(ndb_back_modrdn) ": wr to children " 230 "of entry %s OK\n", e2.e_name.bv_val, 0, 0 ); 231 232 if ( op->oq_modrdn.rs_newSup != NULL ) { 233 Debug( LDAP_DEBUG_TRACE, 234 LDAP_XSTRING(ndb_back_modrdn) 235 ": new parent \"%s\" requested...\n", 236 op->oq_modrdn.rs_newSup->bv_val, 0, 0 ); 237 238 /* newSuperior == oldParent? */ 239 if( dn_match( &e2.e_nname, op->oq_modrdn.rs_nnewSup ) ) { 240 Debug( LDAP_DEBUG_TRACE, "bdb_back_modrdn: " 241 "new parent \"%s\" same as the old parent \"%s\"\n", 242 op->oq_modrdn.rs_newSup->bv_val, e2.e_name.bv_val, 0 ); 243 op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */ 244 } 245 } 246 247 if ( op->oq_modrdn.rs_newSup != NULL ) { 248 if ( op->oq_modrdn.rs_newSup->bv_len ) { 249 rdn2.nr_num = 0; 250 np_dn = op->oq_modrdn.rs_newSup; 251 np_ndn = op->oq_modrdn.rs_nnewSup; 252 253 /* newSuperior == oldParent? - checked above */ 254 /* newSuperior == entry being moved?, if so ==> ERROR */ 255 if ( dnIsSuffix( np_ndn, &e.e_nname )) { 256 rs->sr_err = LDAP_NO_SUCH_OBJECT; 257 rs->sr_text = "new superior not found"; 258 goto return_results; 259 } 260 /* Get Entry with dn=newSuperior. Does newSuperior exist? */ 261 262 e2.e_name = *np_dn; 263 e2.e_nname = *np_ndn; 264 rs->sr_err = ndb_entry_get_info( op, &NA2, 1, NULL ); 265 switch( rs->sr_err ) { 266 case 0: 267 break; 268 case LDAP_NO_SUCH_OBJECT: 269 Debug( LDAP_DEBUG_TRACE, 270 LDAP_XSTRING(ndb_back_modrdn) 271 ": newSup(ndn=%s) not here!\n", 272 np_ndn->bv_val, 0, 0); 273 rs->sr_text = "new superior not found"; 274 goto return_results; 275#if 0 276 case DB_LOCK_DEADLOCK: 277 case DB_LOCK_NOTGRANTED: 278 goto retry; 279#endif 280 case LDAP_BUSY: 281 rs->sr_text = "ldap server busy"; 282 goto return_results; 283 default: 284 rs->sr_err = LDAP_OTHER; 285 rs->sr_text = "internal error"; 286 goto return_results; 287 } 288 if ( NA2.ocs ) { 289 Attribute a; 290 int i; 291 292 for ( i=0; !BER_BVISNULL( &NA2.ocs[i] ); i++); 293 a.a_numvals = i; 294 a.a_desc = slap_schema.si_ad_objectClass; 295 a.a_vals = NA2.ocs; 296 a.a_nvals = NA2.ocs; 297 a.a_next = NULL; 298 e2.e_attrs = &a; 299 300 if ( is_entry_alias( &e2 )) { 301 /* parent is an alias, don't allow move */ 302 Debug( LDAP_DEBUG_TRACE, 303 LDAP_XSTRING(ndb_back_modrdn) 304 ": entry is alias\n", 305 0, 0, 0 ); 306 rs->sr_text = "new superior is an alias"; 307 rs->sr_err = LDAP_ALIAS_PROBLEM; 308 goto return_results; 309 } 310 311 if ( is_entry_referral( &e2 ) ) { 312 /* parent is a referral, don't allow move */ 313 Debug( LDAP_DEBUG_TRACE, 314 LDAP_XSTRING(ndb_back_modrdn) 315 ": entry is referral\n", 316 0, 0, 0 ); 317 rs->sr_text = "new superior is a referral"; 318 rs->sr_err = LDAP_OTHER; 319 goto return_results; 320 } 321 } 322 } 323 324 /* check newSuperior for "children" acl */ 325 rs->sr_err = access_allowed( op, &e2, children, 326 NULL, ACL_WADD, NULL ); 327 if( ! rs->sr_err ) { 328 Debug( LDAP_DEBUG_TRACE, 329 LDAP_XSTRING(ndb_back_modrdn) 330 ": no wr to newSup children\n", 331 0, 0, 0 ); 332 rs->sr_text = "no write access to new superior's children"; 333 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 334 goto return_results; 335 } 336 337 Debug( LDAP_DEBUG_TRACE, 338 LDAP_XSTRING(ndb_back_modrdn) 339 ": wr to new parent OK id=%ld\n", 340 (long) e2.e_id, 0, 0 ); 341 } 342 343 /* Build target dn and make sure target entry doesn't exist already. */ 344 if (!new_dn.bv_val) { 345 build_new_dn( &new_dn, &e2.e_name, &op->oq_modrdn.rs_newrdn, NULL ); 346 } 347 348 if (!new_ndn.bv_val) { 349 build_new_dn( &new_ndn, &e2.e_nname, &op->oq_modrdn.rs_nnewrdn, NULL ); 350 } 351 352 Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn) ": new ndn=%s\n", 353 new_ndn.bv_val, 0, 0 ); 354 355 /* Allow rename to same DN */ 356 if ( !bvmatch ( &new_ndn, &e.e_nname )) { 357 rdn2.nr_num = 0; 358 e2.e_name = new_dn; 359 e2.e_nname = new_ndn; 360 NA2.ocs = &matched; 361 rs->sr_err = ndb_entry_get_info( op, &NA2, 1, NULL ); 362 NA2.ocs = NULL; 363 switch( rs->sr_err ) { 364#if 0 365 case DB_LOCK_DEADLOCK: 366 case DB_LOCK_NOTGRANTED: 367 goto retry; 368#endif 369 case LDAP_NO_SUCH_OBJECT: 370 break; 371 case 0: 372 rs->sr_err = LDAP_ALREADY_EXISTS; 373 goto return_results; 374 default: 375 rs->sr_err = LDAP_OTHER; 376 rs->sr_text = "internal error"; 377 goto return_results; 378 } 379 } 380 381 assert( op->orr_modlist != NULL ); 382 383 if( op->o_preread ) { 384 if( preread_ctrl == NULL ) { 385 preread_ctrl = &ctrls[num_ctrls++]; 386 ctrls[num_ctrls] = NULL; 387 } 388 if( slap_read_controls( op, rs, &e, 389 &slap_pre_read_bv, preread_ctrl ) ) 390 { 391 Debug( LDAP_DEBUG_TRACE, 392 "<=- " LDAP_XSTRING(ndb_back_modrdn) 393 ": pre-read failed!\n", 0, 0, 0 ); 394 if ( op->o_preread & SLAP_CONTROL_CRITICAL ) { 395 /* FIXME: is it correct to abort 396 * operation if control fails? */ 397 goto return_results; 398 } 399 } 400 } 401 402 /* delete old DN */ 403 rs->sr_err = ndb_entry_del_info( op->o_bd, &NA ); 404 if ( rs->sr_err != 0 ) { 405 Debug(LDAP_DEBUG_TRACE, 406 "<=- " LDAP_XSTRING(ndb_back_modrdn) 407 ": dn2id del failed: %s (%d)\n", 408 NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 ); 409#if 0 410 switch( rs->sr_err ) { 411 case DB_LOCK_DEADLOCK: 412 case DB_LOCK_NOTGRANTED: 413 goto retry; 414 } 415#endif 416 rs->sr_err = LDAP_OTHER; 417 rs->sr_text = "DN index delete fail"; 418 goto return_results; 419 } 420 421 /* copy entry fields */ 422 e2.e_attrs = e.e_attrs; 423 e2.e_id = e.e_id; 424 425 /* add new DN */ 426 rs->sr_err = ndb_entry_put_info( op->o_bd, &NA2, 0 ); 427 if ( rs->sr_err != 0 ) { 428 Debug(LDAP_DEBUG_TRACE, 429 "<=- " LDAP_XSTRING(ndb_back_modrdn) 430 ": dn2id add failed: %s (%d)\n", 431 NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 ); 432#if 0 433 switch( rs->sr_err ) { 434 case DB_LOCK_DEADLOCK: 435 case DB_LOCK_NOTGRANTED: 436 goto retry; 437 } 438#endif 439 rs->sr_err = LDAP_OTHER; 440 rs->sr_text = "DN index add failed"; 441 goto return_results; 442 } 443 444 /* modify entry */ 445 rs->sr_err = ndb_modify_internal( op, &NA2, 446 &rs->sr_text, textbuf, textlen ); 447 if( rs->sr_err != LDAP_SUCCESS ) { 448 Debug(LDAP_DEBUG_TRACE, 449 "<=- " LDAP_XSTRING(ndb_back_modrdn) 450 ": modify failed: %s (%d)\n", 451 NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 ); 452#if 0 453 switch( rs->sr_err ) { 454 case DB_LOCK_DEADLOCK: 455 case DB_LOCK_NOTGRANTED: 456 goto retry; 457 } 458#endif 459 goto return_results; 460 } 461 462 e.e_attrs = e2.e_attrs; 463 464 if( op->o_postread ) { 465 if( postread_ctrl == NULL ) { 466 postread_ctrl = &ctrls[num_ctrls++]; 467 ctrls[num_ctrls] = NULL; 468 } 469 if( slap_read_controls( op, rs, &e2, 470 &slap_post_read_bv, postread_ctrl ) ) 471 { 472 Debug( LDAP_DEBUG_TRACE, 473 "<=- " LDAP_XSTRING(ndb_back_modrdn) 474 ": post-read failed!\n", 0, 0, 0 ); 475 if ( op->o_postread & SLAP_CONTROL_CRITICAL ) { 476 /* FIXME: is it correct to abort 477 * operation if control fails? */ 478 goto return_results; 479 } 480 } 481 } 482 483 if( op->o_noop ) { 484 if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback, 485 NdbOperation::AbortOnError, 1 )) != 0 ) { 486 rs->sr_text = "txn_abort (no-op) failed"; 487 } else { 488 rs->sr_err = LDAP_X_NO_OPERATION; 489 } 490 } else { 491 if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit, 492 NdbOperation::AbortOnError, 1 )) != 0 ) { 493 rs->sr_text = "txn_commit failed"; 494 } else { 495 rs->sr_err = LDAP_SUCCESS; 496 } 497 } 498 499 if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) { 500 Debug( LDAP_DEBUG_TRACE, 501 LDAP_XSTRING(ndb_back_modrdn) ": txn_%s failed: %s (%d)\n", 502 op->o_noop ? "abort (no-op)" : "commit", 503 NA.txn->getNdbError().message, NA.txn->getNdbError().code ); 504 rs->sr_err = LDAP_OTHER; 505 goto return_results; 506 } 507 NA.txn->close(); 508 NA.txn = NULL; 509 510 Debug(LDAP_DEBUG_TRACE, 511 LDAP_XSTRING(ndb_back_modrdn) 512 ": rdn modified%s id=%08lx dn=\"%s\"\n", 513 op->o_noop ? " (no-op)" : "", 514 e.e_id, op->o_req_dn.bv_val ); 515 516 rs->sr_err = LDAP_SUCCESS; 517 rs->sr_text = NULL; 518 if( num_ctrls ) rs->sr_ctrls = ctrls; 519 520return_results: 521 if ( NA2.ocs ) { 522 ber_bvarray_free_x( NA2.ocs, op->o_tmpmemctx ); 523 NA2.ocs = NULL; 524 } 525 526 if ( NA.ocs ) { 527 ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx ); 528 NA.ocs = NULL; 529 } 530 531 if ( e.e_attrs ) { 532 attrs_free( e.e_attrs ); 533 e.e_attrs = NULL; 534 } 535 536 if( NA.txn != NULL ) { 537 NA.txn->execute( Rollback ); 538 NA.txn->close(); 539 } 540 541 send_ldap_result( op, rs ); 542 slap_graduate_commit_csn( op ); 543 544 if( new_dn.bv_val != NULL ) free( new_dn.bv_val ); 545 if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val ); 546 547 if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) { 548 slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx ); 549 slap_sl_free( *preread_ctrl, op->o_tmpmemctx ); 550 } 551 if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) { 552 slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx ); 553 slap_sl_free( *postread_ctrl, op->o_tmpmemctx ); 554 } 555 556 rs->sr_text = NULL; 557 return rs->sr_err; 558} 559