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_err = LDAP_OTHER;
144 rs->sr_text = "entry's parent does not exist";
149 rs->sr_text = "ldap server busy";
152 rs->sr_err = LDAP_OTHER;
153 rs->sr_text = "internal error";
157 /* check parent for "children" acl */
158 rs->sr_err = access_allowed( op, p,
160 op->oq_modrdn.rs_newSup == NULL ?
161 ACL_WRITE : ACL_WDEL,
164 if ( ! rs->sr_err ) {
165 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
166 Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
168 rs->sr_text = "no write access to parent's children";
172 Debug( LDAP_DEBUG_TRACE,
173 LDAP_XSTRING(mdb_modrdn) ": wr to children "
174 "of entry %s OK\n", p_ndn.bv_val, 0, 0 );
176 if ( p_ndn.bv_val == slap_empty_bv.bv_val ) {
177 p_dn = slap_empty_bv;
179 dnParent( &op->o_req_dn, &p_dn );
182 Debug( LDAP_DEBUG_TRACE,
183 LDAP_XSTRING(mdb_modrdn) ": parent dn=%s\n",
187 rs->sr_err = mdb_dn2entry( op, txn, &op->o_req_ndn, &e, 0 );
188 switch( rs->sr_err ) {
193 rs->sr_text = "ldap server busy";
196 rs->sr_err = LDAP_OTHER;
197 rs->sr_text = "internal error";
201 /* FIXME: dn2entry() should return non-glue entry */
202 if (( rs->sr_err == MDB_NOTFOUND ) ||
203 ( !manageDSAit && e && is_entry_glue( e )))
206 rs->sr_matched = ch_strdup( e->e_dn );
207 rs->sr_ref = is_entry_referral( e )
208 ? get_entry_referrals( op, e )
210 mdb_entry_return( e );
214 rs->sr_ref = referral_rewrite( default_referral, NULL,
215 &op->o_req_dn, LDAP_SCOPE_DEFAULT );
218 rs->sr_err = LDAP_REFERRAL;
219 send_ldap_result( op, rs );
221 ber_bvarray_free( rs->sr_ref );
222 free( (char *)rs->sr_matched );
224 rs->sr_matched = NULL;
229 if ( get_assert( op ) &&
230 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
232 rs->sr_err = LDAP_ASSERTION_FAILED;
236 /* check write on old entry */
237 rs->sr_err = access_allowed( op, e, entry, NULL, ACL_WRITE, NULL );
238 if ( ! rs->sr_err ) {
239 Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0,
241 rs->sr_text = "no write access to old entry";
242 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
246 if (!manageDSAit && is_entry_referral( e ) ) {
247 /* entry is a referral, don't allow rename */
248 rs->sr_ref = get_entry_referrals( op, e );
250 Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_modrdn)
251 ": entry %s is referral\n", e->e_dn, 0, 0 );
253 rs->sr_err = LDAP_REFERRAL,
254 rs->sr_matched = e->e_name.bv_val;
255 send_ldap_result( op, rs );
257 ber_bvarray_free( rs->sr_ref );
259 rs->sr_matched = NULL;
263 new_parent_dn = &p_dn; /* New Parent unless newSuperior given */
265 if ( op->oq_modrdn.rs_newSup != NULL ) {
266 Debug( LDAP_DEBUG_TRACE,
267 LDAP_XSTRING(mdb_modrdn)
268 ": new parent \"%s\" requested...\n",
269 op->oq_modrdn.rs_newSup->bv_val, 0, 0 );
271 /* newSuperior == oldParent? */
272 if( dn_match( &p_ndn, op->oq_modrdn.rs_nnewSup ) ) {
273 Debug( LDAP_DEBUG_TRACE, "mdb_back_modrdn: "
274 "new parent \"%s\" same as the old parent \"%s\"\n",
275 op->oq_modrdn.rs_newSup->bv_val, p_dn.bv_val, 0 );
276 op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
280 /* There's a MDB_MULTIPLE_SUFFIXES case here that this code doesn't
281 * support. E.g., two suffixes dc=foo,dc=com and dc=bar,dc=net.
282 * We do not allow modDN
286 * and we probably should. But since MULTIPLE_SUFFIXES is deprecated
287 * I'm ignoring this problem for now.
289 if ( op->oq_modrdn.rs_newSup != NULL ) {
290 if ( op->oq_modrdn.rs_newSup->bv_len ) {
291 np_dn = op->oq_modrdn.rs_newSup;
292 np_ndn = op->oq_modrdn.rs_nnewSup;
294 /* newSuperior == oldParent? - checked above */
295 /* newSuperior == entry being moved?, if so ==> ERROR */
296 if ( dnIsSuffix( np_ndn, &e->e_nname )) {
297 rs->sr_err = LDAP_NO_SUCH_OBJECT;
298 rs->sr_text = "new superior not found";
301 /* Get Entry with dn=newSuperior. Does newSuperior exist? */
303 rs->sr_err = mdb_dn2entry( op, txn, np_ndn, &np, 0 );
305 switch( rs->sr_err ) {
309 Debug( LDAP_DEBUG_TRACE,
310 LDAP_XSTRING(mdb_modrdn)
311 ": newSup(ndn=%s) not here!\n",
312 np_ndn->bv_val, 0, 0);
313 rs->sr_text = "new superior not found";
314 rs->sr_err = LDAP_NO_SUCH_OBJECT;
317 rs->sr_text = "ldap server busy";
320 rs->sr_err = LDAP_OTHER;
321 rs->sr_text = "internal error";
325 /* check newSuperior for "children" acl */
326 rs->sr_err = access_allowed( op, np, children,
327 NULL, ACL_WADD, NULL );
330 Debug( LDAP_DEBUG_TRACE,
331 LDAP_XSTRING(mdb_modrdn)
332 ": no wr to newSup children\n",
334 rs->sr_text = "no write access to new superior's children";
335 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
339 Debug( LDAP_DEBUG_TRACE,
340 LDAP_XSTRING(mdb_modrdn)
341 ": wr to new parent OK np=%p, id=%ld\n",
342 (void *) np, (long) np->e_id, 0 );
344 if ( is_entry_alias( np ) ) {
345 /* parent is an alias, don't allow add */
346 Debug( LDAP_DEBUG_TRACE,
347 LDAP_XSTRING(mdb_modrdn)
348 ": entry is alias\n",
350 rs->sr_text = "new superior is an alias";
351 rs->sr_err = LDAP_ALIAS_PROBLEM;
355 if ( is_entry_referral( np ) ) {
356 /* parent is a referral, don't allow add */
357 Debug( LDAP_DEBUG_TRACE,
358 LDAP_XSTRING(mdb_modrdn)
359 ": entry is referral\n",
361 rs->sr_text = "new superior is a referral";
362 rs->sr_err = LDAP_OTHER;
365 new_parent_dn = &np->e_name;
370 /* no parent, modrdn entry directly under root */
371 if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
372 || be_isupdate( op ) ) {
373 np = (Entry *)&slap_entry_root;
375 /* check parent for "children" acl */
376 rs->sr_err = access_allowed( op, np,
377 children, NULL, ACL_WADD, NULL );
381 if ( ! rs->sr_err ) {
382 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
383 Debug( LDAP_DEBUG_TRACE,
384 "no access to new superior\n",
387 "no write access to new superior's children";
393 Debug( LDAP_DEBUG_TRACE,
394 LDAP_XSTRING(mdb_modrdn)
395 ": wr to new parent's children OK\n",
398 new_parent_dn = np_dn;
401 /* Build target dn and make sure target entry doesn't exist already. */
402 if (!new_dn.bv_val) {
403 build_new_dn( &new_dn, new_parent_dn, &op->oq_modrdn.rs_newrdn, NULL );
406 if (!new_ndn.bv_val) {
407 struct berval bv = {0, NULL};
408 dnNormalize( 0, NULL, NULL, &new_dn, &bv, op->o_tmpmemctx );
409 ber_dupbv( &new_ndn, &bv );
410 /* FIXME: why not call dnNormalize() w/o ctx? */
411 op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
414 Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_modrdn) ": new ndn=%s\n",
415 new_ndn.bv_val, 0, 0 );
417 /* Shortcut the search */
418 rs->sr_err = mdb_dn2id ( op, txn, &new_ndn, &nid, NULL );
419 switch( rs->sr_err ) {
423 /* Allow rename to same DN */
424 if ( nid == e->e_id )
426 rs->sr_err = LDAP_ALREADY_EXISTS;
429 rs->sr_err = LDAP_OTHER;
430 rs->sr_text = "internal error";
434 assert( op->orr_modlist != NULL );
436 if( op->o_preread ) {
437 if( preread_ctrl == NULL ) {
438 preread_ctrl = &ctrls[num_ctrls++];
439 ctrls[num_ctrls] = NULL;
441 if( slap_read_controls( op, rs, e,
442 &slap_pre_read_bv, preread_ctrl ) )
444 Debug( LDAP_DEBUG_TRACE,
445 "<=- " LDAP_XSTRING(mdb_modrdn)
446 ": pre-read failed!\n", 0, 0, 0 );
447 if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
448 /* FIXME: is it correct to abort
449 * operation if control fails? */
456 rs->sr_err = mdb_dn2id_delete( op, txn, p->e_id, e );
457 if ( rs->sr_err != 0 ) {
458 Debug(LDAP_DEBUG_TRACE,
459 "<=- " LDAP_XSTRING(mdb_modrdn)
460 ": dn2id del failed: %s (%d)\n",
461 mdb_strerror(rs->sr_err), rs->sr_err, 0 );
462 rs->sr_err = LDAP_OTHER;
463 rs->sr_text = "DN index delete fail";
467 /* copy the entry, then override some fields */
469 dummy.e_name = new_dn;
470 dummy.e_nname = new_ndn;
471 dummy.e_attrs = NULL;
474 rs->sr_err = mdb_dn2id_add( op, txn, np ? np->e_id : p->e_id, &dummy );
475 if ( rs->sr_err != 0 ) {
476 Debug(LDAP_DEBUG_TRACE,
477 "<=- " LDAP_XSTRING(mdb_modrdn)
478 ": dn2id add failed: %s (%d)\n",
479 mdb_strerror(rs->sr_err), rs->sr_err, 0 );
480 rs->sr_err = LDAP_OTHER;
481 rs->sr_text = "DN index add failed";
485 dummy.e_attrs = e->e_attrs;
488 rs->sr_err = mdb_modify_internal( op, txn, op->orr_modlist, &dummy,
489 &rs->sr_text, textbuf, textlen );
490 if( rs->sr_err != LDAP_SUCCESS ) {
491 Debug(LDAP_DEBUG_TRACE,
492 "<=- " LDAP_XSTRING(mdb_modrdn)
493 ": modify failed: %s (%d)\n",
494 mdb_strerror(rs->sr_err), rs->sr_err, 0 );
495 if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
500 rs->sr_err = mdb_id2entry_update( op, txn, &dummy );
501 if ( rs->sr_err != 0 ) {
502 Debug(LDAP_DEBUG_TRACE,
503 "<=- " LDAP_XSTRING(mdb_modrdn)
504 ": id2entry failed: %s (%d)\n",
505 mdb_strerror(rs->sr_err), rs->sr_err, 0 );
506 rs->sr_err = LDAP_OTHER;
507 rs->sr_text = "entry update failed";
511 if ( p_ndn.bv_len != 0 ) {
512 parent_is_glue = is_entry_glue(p);
513 rs->sr_err = mdb_dn2id_children( op, txn, p );
514 if ( rs->sr_err != MDB_NOTFOUND ) {
515 switch( rs->sr_err ) {
519 Debug(LDAP_DEBUG_ARGS,
520 "<=- " LDAP_XSTRING(mdb_modrdn)
521 ": has_children failed: %s (%d)\n",
522 mdb_strerror(rs->sr_err), rs->sr_err, 0 );
523 rs->sr_err = LDAP_OTHER;
524 rs->sr_text = "internal error";
529 mdb_entry_return( p );
533 if( op->o_postread ) {
534 if( postread_ctrl == NULL ) {
535 postread_ctrl = &ctrls[num_ctrls++];
536 ctrls[num_ctrls] = NULL;
538 if( slap_read_controls( op, rs, &dummy,
539 &slap_post_read_bv, postread_ctrl ) )
541 Debug( LDAP_DEBUG_TRACE,
542 "<=- " LDAP_XSTRING(mdb_modrdn)
543 ": post-read failed!\n", 0, 0, 0 );
544 if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
545 /* FIXME: is it correct to abort
546 * operation if control fails? */
552 if( moi == &opinfo ) {
553 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
554 opinfo.moi_oe.oe_key = NULL;
556 mdb_txn_abort( txn );
557 rs->sr_err = LDAP_X_NO_OPERATION;
559 /* Only free attrs if they were dup'd. */
560 if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
564 dummy.e_attrs = NULL;
565 new_dn.bv_val = NULL;
566 new_ndn.bv_val = NULL;
568 if(( rs->sr_err=mdb_txn_commit( txn )) != 0 ) {
569 rs->sr_text = "txn_commit failed";
571 rs->sr_err = LDAP_SUCCESS;
577 if( rs->sr_err != LDAP_SUCCESS ) {
578 Debug( LDAP_DEBUG_TRACE,
579 LDAP_XSTRING(mdb_modrdn) ": %s : %s (%d)\n",
580 rs->sr_text, mdb_strerror(rs->sr_err), rs->sr_err );
581 rs->sr_err = LDAP_OTHER;
586 Debug(LDAP_DEBUG_TRACE,
587 LDAP_XSTRING(mdb_modrdn)
588 ": rdn modified%s id=%08lx dn=\"%s\"\n",
589 op->o_noop ? " (no-op)" : "",
590 dummy.e_id, op->o_req_dn.bv_val );
592 if( num_ctrls ) rs->sr_ctrls = ctrls;
595 if ( dummy.e_attrs ) {
596 attrs_free( dummy.e_attrs );
598 send_ldap_result( op, rs );
601 if( rs->sr_err == LDAP_SUCCESS && mdb->bi_txn_cp_kbyte ) {
602 TXN_CHECKPOINT( mdb->bi_dbenv,
603 mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 );
607 if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
608 op->o_delete_glue_parent = 1;
612 slap_graduate_commit_csn( op );
614 if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
615 if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );
617 /* LDAP v3 Support */
619 /* free new parent */
620 mdb_entry_return( np );
625 mdb_entry_return( p );
630 mdb_entry_return( e );
633 if( moi == &opinfo ) {
635 mdb_txn_abort( txn );
637 if ( opinfo.moi_oe.oe_key ) {
638 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
642 if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
643 slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
644 slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
646 if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
647 slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
648 slap_sl_free( *postread_ctrl, op->o_tmpmemctx );