1 /* modrdn.c - mdb backend modrdn routine */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2000-2011 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
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>.
20 #include <ac/string.h>
25 mdb_modrdn( Operation *op, SlapReply *rs )
27 struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
28 AttributeDescription *children = slap_schema.si_ad_children;
29 AttributeDescription *entry = slap_schema.si_ad_entry;
30 struct berval p_dn, p_ndn;
31 struct berval new_dn = {0, NULL}, new_ndn = {0, NULL};
34 /* LDAP v2 supporting correct attribute handling. */
35 char textbuf[SLAP_TEXT_BUFLEN];
36 size_t textlen = sizeof textbuf;
38 struct mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo;
41 Entry *np = NULL; /* newSuperior Entry */
42 struct berval *np_dn = NULL; /* newSuperior dn */
43 struct berval *np_ndn = NULL; /* newSuperior ndn */
44 struct berval *new_parent_dn = NULL; /* np_dn, p_dn, or NULL */
46 int manageDSAit = get_manageDSAit( op );
49 LDAPControl **preread_ctrl = NULL;
50 LDAPControl **postread_ctrl = NULL;
51 LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
54 int parent_is_glue = 0;
55 int parent_is_leaf = 0;
61 Debug( LDAP_DEBUG_TRACE, "==>" LDAP_XSTRING(mdb_modrdn) "(%s,%s,%s)\n",
62 op->o_req_dn.bv_val,op->oq_modrdn.rs_newrdn.bv_val,
63 op->oq_modrdn.rs_newSup ? op->oq_modrdn.rs_newSup->bv_val : "NULL" );
67 /* acquire connection lock */
68 ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
69 if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
70 rs->sr_text = "invalid transaction identifier";
71 rs->sr_err = LDAP_X_TXN_ID_INVALID;
73 } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
78 if( op->o_conn->c_txn_backend == NULL ) {
79 op->o_conn->c_txn_backend = op->o_bd;
81 } else if( op->o_conn->c_txn_backend != op->o_bd ) {
82 rs->sr_text = "transaction cannot span multiple database contexts";
83 rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
87 /* insert operation into transaction */
89 rs->sr_text = "transaction specified";
90 rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
93 /* release connection lock */
94 ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
97 send_ldap_result( op, rs );
103 ctrls[num_ctrls] = NULL;
105 slap_mods_opattrs( op, &op->orr_modlist, 1 );
107 /* begin transaction */
108 rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi );
110 if( rs->sr_err != 0 ) {
111 Debug( LDAP_DEBUG_TRACE,
112 LDAP_XSTRING(mdb_modrdn) ": txn_begin failed: "
113 "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err, 0 );
114 rs->sr_err = LDAP_OTHER;
115 rs->sr_text = "internal error";
121 if ( be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
122 #ifdef MDB_MULTIPLE_SUFFIXES
123 /* Allow renaming one suffix entry to another */
124 p_ndn = slap_empty_bv;
126 /* There can only be one suffix entry */
127 rs->sr_err = LDAP_NAMING_VIOLATION;
128 rs->sr_text = "cannot rename suffix entry";
132 dnParent( &op->o_req_ndn, &p_ndn );
135 /* Make sure parent entry exist and we can write its
138 rs->sr_err = mdb_dn2entry( op, txn, &p_ndn, &p, 0 );
139 switch( rs->sr_err ) {
141 Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_modrdn)
142 ": parent does not exist\n", 0, 0, 0);
143 rs->sr_ref = referral_rewrite( default_referral, NULL,
144 &op->o_req_dn, LDAP_SCOPE_DEFAULT );
145 rs->sr_err = LDAP_REFERRAL;
147 send_ldap_result( op, rs );
149 ber_bvarray_free( rs->sr_ref );
154 rs->sr_text = "ldap server busy";
157 rs->sr_err = LDAP_OTHER;
158 rs->sr_text = "internal error";
162 /* check parent for "children" acl */
163 rs->sr_err = access_allowed( op, p,
165 op->oq_modrdn.rs_newSup == NULL ?
166 ACL_WRITE : ACL_WDEL,
169 if ( ! rs->sr_err ) {
170 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
171 Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
173 rs->sr_text = "no write access to parent's children";
177 Debug( LDAP_DEBUG_TRACE,
178 LDAP_XSTRING(mdb_modrdn) ": wr to children "
179 "of entry %s OK\n", p_ndn.bv_val, 0, 0 );
181 if ( p_ndn.bv_val == slap_empty_bv.bv_val ) {
182 p_dn = slap_empty_bv;
184 dnParent( &op->o_req_dn, &p_dn );
187 Debug( LDAP_DEBUG_TRACE,
188 LDAP_XSTRING(mdb_modrdn) ": parent dn=%s\n",
192 rs->sr_err = mdb_dn2entry( op, txn, &op->o_req_ndn, &e, 0 );
193 switch( rs->sr_err ) {
200 rs->sr_text = "ldap server busy";
203 rs->sr_err = LDAP_OTHER;
204 rs->sr_text = "internal error";
208 /* FIXME: dn2entry() should return non-glue entry */
209 if (( rs->sr_err == MDB_NOTFOUND ) ||
210 ( !manageDSAit && e && is_entry_glue( e )))
213 rs->sr_matched = ch_strdup( e->e_dn );
214 if ( is_entry_referral( e )) {
215 BerVarray ref = get_entry_referrals( op, e );
216 rs->sr_ref = referral_rewrite( ref, &e->e_name,
217 &op->o_req_dn, LDAP_SCOPE_DEFAULT );
218 ber_bvarray_free( ref );
222 mdb_entry_return( e );
226 rs->sr_ref = referral_rewrite( default_referral, NULL,
227 &op->o_req_dn, LDAP_SCOPE_DEFAULT );
230 rs->sr_err = LDAP_REFERRAL;
231 send_ldap_result( op, rs );
233 ber_bvarray_free( rs->sr_ref );
234 free( (char *)rs->sr_matched );
236 rs->sr_matched = NULL;
241 if ( get_assert( op ) &&
242 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
244 rs->sr_err = LDAP_ASSERTION_FAILED;
248 /* check write on old entry */
249 rs->sr_err = access_allowed( op, e, entry, NULL, ACL_WRITE, NULL );
250 if ( ! rs->sr_err ) {
251 Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0,
253 rs->sr_text = "no write access to old entry";
254 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
258 if (!manageDSAit && is_entry_referral( e ) ) {
259 /* entry is a referral, don't allow rename */
260 rs->sr_ref = get_entry_referrals( op, e );
262 Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_modrdn)
263 ": entry %s is referral\n", e->e_dn, 0, 0 );
265 rs->sr_err = LDAP_REFERRAL,
266 rs->sr_matched = e->e_name.bv_val;
267 send_ldap_result( op, rs );
269 ber_bvarray_free( rs->sr_ref );
271 rs->sr_matched = NULL;
275 new_parent_dn = &p_dn; /* New Parent unless newSuperior given */
277 if ( op->oq_modrdn.rs_newSup != NULL ) {
278 Debug( LDAP_DEBUG_TRACE,
279 LDAP_XSTRING(mdb_modrdn)
280 ": new parent \"%s\" requested...\n",
281 op->oq_modrdn.rs_newSup->bv_val, 0, 0 );
283 /* newSuperior == oldParent? */
284 if( dn_match( &p_ndn, op->oq_modrdn.rs_nnewSup ) ) {
285 Debug( LDAP_DEBUG_TRACE, "mdb_back_modrdn: "
286 "new parent \"%s\" same as the old parent \"%s\"\n",
287 op->oq_modrdn.rs_newSup->bv_val, p_dn.bv_val, 0 );
288 op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
292 /* There's a MDB_MULTIPLE_SUFFIXES case here that this code doesn't
293 * support. E.g., two suffixes dc=foo,dc=com and dc=bar,dc=net.
294 * We do not allow modDN
298 * and we probably should. But since MULTIPLE_SUFFIXES is deprecated
299 * I'm ignoring this problem for now.
301 if ( op->oq_modrdn.rs_newSup != NULL ) {
302 if ( op->oq_modrdn.rs_newSup->bv_len ) {
303 np_dn = op->oq_modrdn.rs_newSup;
304 np_ndn = op->oq_modrdn.rs_nnewSup;
306 /* newSuperior == oldParent? - checked above */
307 /* newSuperior == entry being moved?, if so ==> ERROR */
308 if ( dnIsSuffix( np_ndn, &e->e_nname )) {
309 rs->sr_err = LDAP_NO_SUCH_OBJECT;
310 rs->sr_text = "new superior not found";
313 /* Get Entry with dn=newSuperior. Does newSuperior exist? */
315 rs->sr_err = mdb_dn2entry( op, txn, np_ndn, &np, 0 );
317 switch( rs->sr_err ) {
321 Debug( LDAP_DEBUG_TRACE,
322 LDAP_XSTRING(mdb_modrdn)
323 ": newSup(ndn=%s) not here!\n",
324 np_ndn->bv_val, 0, 0);
325 rs->sr_text = "new superior not found";
326 rs->sr_err = LDAP_NO_SUCH_OBJECT;
329 rs->sr_text = "ldap server busy";
332 rs->sr_err = LDAP_OTHER;
333 rs->sr_text = "internal error";
337 /* check newSuperior for "children" acl */
338 rs->sr_err = access_allowed( op, np, children,
339 NULL, ACL_WADD, NULL );
342 Debug( LDAP_DEBUG_TRACE,
343 LDAP_XSTRING(mdb_modrdn)
344 ": no wr to newSup children\n",
346 rs->sr_text = "no write access to new superior's children";
347 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
351 Debug( LDAP_DEBUG_TRACE,
352 LDAP_XSTRING(mdb_modrdn)
353 ": wr to new parent OK np=%p, id=%ld\n",
354 (void *) np, (long) np->e_id, 0 );
356 if ( is_entry_alias( np ) ) {
357 /* parent is an alias, don't allow add */
358 Debug( LDAP_DEBUG_TRACE,
359 LDAP_XSTRING(mdb_modrdn)
360 ": entry is alias\n",
362 rs->sr_text = "new superior is an alias";
363 rs->sr_err = LDAP_ALIAS_PROBLEM;
367 if ( is_entry_referral( np ) ) {
368 /* parent is a referral, don't allow add */
369 Debug( LDAP_DEBUG_TRACE,
370 LDAP_XSTRING(mdb_modrdn)
371 ": entry is referral\n",
373 rs->sr_text = "new superior is a referral";
374 rs->sr_err = LDAP_OTHER;
377 new_parent_dn = &np->e_name;
382 /* no parent, modrdn entry directly under root */
383 if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
384 || be_isupdate( op ) ) {
385 np = (Entry *)&slap_entry_root;
387 /* check parent for "children" acl */
388 rs->sr_err = access_allowed( op, np,
389 children, NULL, ACL_WADD, NULL );
393 if ( ! rs->sr_err ) {
394 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
395 Debug( LDAP_DEBUG_TRACE,
396 "no access to new superior\n",
399 "no write access to new superior's children";
405 Debug( LDAP_DEBUG_TRACE,
406 LDAP_XSTRING(mdb_modrdn)
407 ": wr to new parent's children OK\n",
410 new_parent_dn = np_dn;
413 /* Build target dn and make sure target entry doesn't exist already. */
414 if (!new_dn.bv_val) {
415 build_new_dn( &new_dn, new_parent_dn, &op->oq_modrdn.rs_newrdn, op->o_tmpmemctx );
418 if (!new_ndn.bv_val) {
419 dnNormalize( 0, NULL, NULL, &new_dn, &new_ndn, op->o_tmpmemctx );
422 Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_modrdn) ": new ndn=%s\n",
423 new_ndn.bv_val, 0, 0 );
425 /* Shortcut the search */
426 rs->sr_err = mdb_dn2id ( op, txn, &new_ndn, &nid, NULL, NULL );
427 switch( rs->sr_err ) {
431 /* Allow rename to same DN */
432 if ( nid == e->e_id )
434 rs->sr_err = LDAP_ALREADY_EXISTS;
437 rs->sr_err = LDAP_OTHER;
438 rs->sr_text = "internal error";
442 assert( op->orr_modlist != NULL );
444 if( op->o_preread ) {
445 if( preread_ctrl == NULL ) {
446 preread_ctrl = &ctrls[num_ctrls++];
447 ctrls[num_ctrls] = NULL;
449 if( slap_read_controls( op, rs, e,
450 &slap_pre_read_bv, preread_ctrl ) )
452 Debug( LDAP_DEBUG_TRACE,
453 "<=- " LDAP_XSTRING(mdb_modrdn)
454 ": pre-read failed!\n", 0, 0, 0 );
455 if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
456 /* FIXME: is it correct to abort
457 * operation if control fails? */
464 rs->sr_err = mdb_dn2id_delete( op, txn, p->e_id, e );
465 if ( rs->sr_err != 0 ) {
466 Debug(LDAP_DEBUG_TRACE,
467 "<=- " LDAP_XSTRING(mdb_modrdn)
468 ": dn2id del failed: %s (%d)\n",
469 mdb_strerror(rs->sr_err), rs->sr_err, 0 );
470 rs->sr_err = LDAP_OTHER;
471 rs->sr_text = "DN index delete fail";
475 /* copy the entry, then override some fields */
477 dummy.e_name = new_dn;
478 dummy.e_nname = new_ndn;
479 dummy.e_attrs = NULL;
482 rs->sr_err = mdb_dn2id_add( op, txn, np ? np->e_id : p->e_id, &dummy );
483 if ( rs->sr_err != 0 ) {
484 Debug(LDAP_DEBUG_TRACE,
485 "<=- " LDAP_XSTRING(mdb_modrdn)
486 ": dn2id add failed: %s (%d)\n",
487 mdb_strerror(rs->sr_err), rs->sr_err, 0 );
488 rs->sr_err = LDAP_OTHER;
489 rs->sr_text = "DN index add failed";
493 dummy.e_attrs = e->e_attrs;
496 rs->sr_err = mdb_modify_internal( op, txn, op->orr_modlist, &dummy,
497 &rs->sr_text, textbuf, textlen );
498 if( rs->sr_err != LDAP_SUCCESS ) {
499 Debug(LDAP_DEBUG_TRACE,
500 "<=- " LDAP_XSTRING(mdb_modrdn)
501 ": modify failed: %s (%d)\n",
502 mdb_strerror(rs->sr_err), rs->sr_err, 0 );
503 if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
508 rs->sr_err = mdb_id2entry_update( op, txn, &dummy );
509 if ( rs->sr_err != 0 ) {
510 Debug(LDAP_DEBUG_TRACE,
511 "<=- " LDAP_XSTRING(mdb_modrdn)
512 ": id2entry failed: %s (%d)\n",
513 mdb_strerror(rs->sr_err), rs->sr_err, 0 );
514 rs->sr_err = LDAP_OTHER;
515 rs->sr_text = "entry update failed";
519 if ( p_ndn.bv_len != 0 ) {
520 parent_is_glue = is_entry_glue(p);
521 rs->sr_err = mdb_dn2id_children( op, txn, p );
522 if ( rs->sr_err != MDB_NOTFOUND ) {
523 switch( rs->sr_err ) {
527 Debug(LDAP_DEBUG_ARGS,
528 "<=- " LDAP_XSTRING(mdb_modrdn)
529 ": has_children failed: %s (%d)\n",
530 mdb_strerror(rs->sr_err), rs->sr_err, 0 );
531 rs->sr_err = LDAP_OTHER;
532 rs->sr_text = "internal error";
537 mdb_entry_return( p );
541 if( op->o_postread ) {
542 if( postread_ctrl == NULL ) {
543 postread_ctrl = &ctrls[num_ctrls++];
544 ctrls[num_ctrls] = NULL;
546 if( slap_read_controls( op, rs, &dummy,
547 &slap_post_read_bv, postread_ctrl ) )
549 Debug( LDAP_DEBUG_TRACE,
550 "<=- " LDAP_XSTRING(mdb_modrdn)
551 ": post-read failed!\n", 0, 0, 0 );
552 if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
553 /* FIXME: is it correct to abort
554 * operation if control fails? */
560 if( moi == &opinfo ) {
561 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
562 opinfo.moi_oe.oe_key = NULL;
564 mdb_txn_abort( txn );
565 rs->sr_err = LDAP_X_NO_OPERATION;
567 /* Only free attrs if they were dup'd. */
568 if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
572 if(( rs->sr_err=mdb_txn_commit( txn )) != 0 ) {
573 rs->sr_text = "txn_commit failed";
575 rs->sr_err = LDAP_SUCCESS;
581 if( rs->sr_err != LDAP_SUCCESS ) {
582 Debug( LDAP_DEBUG_TRACE,
583 LDAP_XSTRING(mdb_modrdn) ": %s : %s (%d)\n",
584 rs->sr_text, mdb_strerror(rs->sr_err), rs->sr_err );
585 rs->sr_err = LDAP_OTHER;
590 Debug(LDAP_DEBUG_TRACE,
591 LDAP_XSTRING(mdb_modrdn)
592 ": rdn modified%s id=%08lx dn=\"%s\"\n",
593 op->o_noop ? " (no-op)" : "",
594 dummy.e_id, op->o_req_dn.bv_val );
596 if( num_ctrls ) rs->sr_ctrls = ctrls;
599 if ( dummy.e_attrs ) {
600 attrs_free( dummy.e_attrs );
602 send_ldap_result( op, rs );
605 if( rs->sr_err == LDAP_SUCCESS && mdb->bi_txn_cp_kbyte ) {
606 TXN_CHECKPOINT( mdb->bi_dbenv,
607 mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 );
611 if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
612 op->o_delete_glue_parent = 1;
616 slap_graduate_commit_csn( op );
618 if( new_ndn.bv_val != NULL ) op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
619 if( new_dn.bv_val != NULL ) op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
621 /* LDAP v3 Support */
623 /* free new parent */
624 mdb_entry_return( np );
629 mdb_entry_return( p );
634 mdb_entry_return( e );
637 if( moi == &opinfo ) {
639 mdb_txn_abort( txn );
641 if ( opinfo.moi_oe.oe_key ) {
642 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
646 if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
647 slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
648 slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
650 if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
651 slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
652 slap_sl_free( *postread_ctrl, op->o_tmpmemctx );