1 /* modrdn.c - ldbm backend modrdn routine */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2004 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>.
16 /* Portions Copyright 1999, Juan C. Gomez, All rights reserved.
17 * This software is not subject to any license of Silicon Graphics
18 * Inc. or Purdue University.
20 * Redistribution and use in source and binary forms are permitted
21 * without restriction or fee of any kind as long as this notice
29 #include <ac/string.h>
30 #include <ac/socket.h>
33 #include "back-ldbm.h"
34 #include "proto-back-ldbm.h"
41 AttributeDescription *children = slap_schema.si_ad_children;
42 AttributeDescription *entry = slap_schema.si_ad_entry;
43 struct ldbminfo *li = (struct ldbminfo *) op->o_bd->be_private;
44 struct berval p_dn, p_ndn;
45 struct berval new_dn = { 0, NULL}, new_ndn = { 0, NULL };
46 struct berval old_ndn = { 0, NULL };
49 /* LDAP v2 supporting correct attribute handling. */
50 LDAPRDN new_rdn = NULL;
51 LDAPRDN old_rdn = NULL;
55 const char *text = NULL;
56 char textbuf[SLAP_TEXT_BUFLEN];
57 size_t textlen = sizeof textbuf;
58 /* Added to support newSuperior */
59 Entry *np = NULL; /* newSuperior Entry */
60 struct berval *np_ndn = NULL; /* newSuperior ndn */
61 struct berval *new_parent_dn = NULL; /* np_dn, p_dn, or NULL */
62 /* Used to interface with ldbm_modify_internal() */
63 Modifications *mod = NULL; /* Used to delete old/add new rdn */
64 int manageDSAit = get_manageDSAit( op );
67 LDAP_LOG( BACK_LDBM, ENTRY,
68 "ldbm_back_modrdn: dn: %s newSuperior=%s\n",
69 op->o_req_dn.bv_len ? op->o_req_dn.bv_val : "NULL",
70 ( op->oq_modrdn.rs_newSup && op->oq_modrdn.rs_newSup->bv_len ) ? op->oq_modrdn.rs_newSup->bv_val : "NULL",0 );
72 Debug( LDAP_DEBUG_TRACE,
73 "==>ldbm_back_modrdn: dn: %s newSuperior=%s\n",
74 op->o_req_dn.bv_len ? op->o_req_dn.bv_val : "NULL",
75 ( op->oq_modrdn.rs_newSup && op->oq_modrdn.rs_newSup->bv_len )
76 ? op->oq_modrdn.rs_newSup->bv_val : "NULL", 0 );
79 /* grab giant lock for writing */
80 ldap_pvt_thread_rdwr_wlock(&li->li_giant_rwlock);
82 e = dn2entry_w( op->o_bd, &op->o_req_ndn, &matched );
84 /* get entry with writer lock */
85 /* FIXME: dn2entry() should return non-glue entry */
86 if (( e == NULL ) || ( !manageDSAit && e && is_entry_glue( e ))) {
87 if ( matched != NULL ) {
88 rs->sr_matched = strdup( matched->e_dn );
89 rs->sr_ref = is_entry_referral( matched )
90 ? get_entry_referrals( op, matched )
92 cache_return_entry_r( &li->li_cache, matched );
94 BerVarray deref = NULL;
95 if ( !LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
97 LDAP_STAILQ_FOREACH( si, &op->o_bd->be_syncinfo, si_next ) {
99 ber_dupbv( &tmpbv, &si->si_provideruri_bv[0] );
100 ber_bvarray_add( &deref, &tmpbv );
103 deref = default_referral;
105 rs->sr_ref = referral_rewrite( deref, NULL, &op->o_req_dn,
106 LDAP_SCOPE_DEFAULT );
109 ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);
111 rs->sr_err = LDAP_REFERRAL;
112 send_ldap_result( op, rs );
114 if ( rs->sr_ref ) ber_bvarray_free( rs->sr_ref );
115 free( (char *)rs->sr_matched );
117 rs->sr_matched = NULL;
121 /* check entry for "entry" acl */
122 if ( ! access_allowed( op, e,
123 entry, NULL, ACL_WRITE, NULL ) )
126 LDAP_LOG( BACK_LDBM, ERR,
127 "ldbm_back_modrdn: no write access to entry of (%s)\n",
128 op->o_req_dn.bv_val, 0, 0 );
130 Debug( LDAP_DEBUG_TRACE,
131 "<=- ldbm_back_modrdn: no write access to entry\n", 0,
135 send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS,
136 "no write access to entry" );
141 if (!manageDSAit && is_entry_referral( e ) ) {
142 /* parent is a referral, don't allow add */
143 /* parent is an alias, don't allow add */
144 rs->sr_ref = get_entry_referrals( op, e );
147 LDAP_LOG( BACK_LDBM, INFO,
148 "ldbm_back_modrdn: entry %s is a referral\n", e->e_dn, 0, 0 );
150 Debug( LDAP_DEBUG_TRACE, "entry %s is referral\n", e->e_dn,
154 rs->sr_err = LDAP_REFERRAL;
155 rs->sr_matched = e->e_name.bv_val;
156 send_ldap_result( op, rs );
158 if ( rs->sr_ref ) ber_bvarray_free( rs->sr_ref );
160 rs->sr_matched = NULL;
164 if ( has_children( op->o_bd, e ) ) {
166 LDAP_LOG( BACK_LDBM, INFO,
167 "ldbm_back_modrdn: entry %s has children\n", e->e_dn, 0, 0 );
169 Debug( LDAP_DEBUG_TRACE, "entry %s has children\n", e->e_dn,
173 send_ldap_error( op, rs, LDAP_NOT_ALLOWED_ON_NONLEAF,
174 "subtree rename not supported" );
178 if ( be_issuffix( op->o_bd, &e->e_nname ) ) {
179 p_ndn = slap_empty_bv ;
181 dnParent( &e->e_nname, &p_ndn );
184 if ( p_ndn.bv_len != 0 ) {
185 /* Make sure parent entry exist and we can write its
189 if( (p = dn2entry_w( op->o_bd, &p_ndn, NULL )) == NULL) {
191 LDAP_LOG( BACK_LDBM, INFO,
192 "ldbm_back_modrdn: parent of %s does not exist\n",
195 Debug( LDAP_DEBUG_TRACE, "parent does not exist\n",
199 send_ldap_error( op, rs, LDAP_OTHER,
200 "parent entry does not exist" );
205 /* check parent for "children" acl */
206 if ( ! access_allowed( op, p,
207 children, NULL, ACL_WRITE, NULL ) )
210 LDAP_LOG( BACK_LDBM, INFO,
211 "ldbm_back_modrdn: no access to parent of (%s)\n",
214 Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
218 send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS,
224 LDAP_LOG( BACK_LDBM, DETAIL1,
225 "ldbm_back_modrdn: wr to children of entry %s OK\n",
226 p_ndn.bv_val, 0, 0 );
228 Debug( LDAP_DEBUG_TRACE,
229 "ldbm_back_modrdn: wr to children of entry %s OK\n",
230 p_ndn.bv_val, 0, 0 );
233 if ( p_ndn.bv_val == slap_empty_bv.bv_val ) {
234 p_dn = slap_empty_bv;
236 dnParent( &e->e_name, &p_dn );
240 LDAP_LOG( BACK_LDBM, DETAIL1,
241 "ldbm_back_modrdn: parent dn=%s\n", p_dn.bv_val, 0, 0 );
243 Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: parent dn=%s\n",
248 /* no parent, must be root to modify rdn */
249 isroot = be_isroot( op->o_bd, &op->o_ndn );
251 if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv ) || be_isupdate( op->o_bd, &op->o_ndn ) ) {
253 p = (Entry *)&slap_entry_root;
255 can_access = access_allowed( op, p,
256 children, NULL, ACL_WRITE, NULL );
259 /* check parent for "children" acl */
260 if ( ! can_access ) {
262 LDAP_LOG( BACK_LDBM, ERR,
263 "ldbm_back_modrdn: no access to parent \"\"\n", 0,0,0 );
265 Debug( LDAP_DEBUG_TRACE,
266 "<=- ldbm_back_modrdn: no "
267 "access to parent\n", 0, 0, 0 );
270 send_ldap_error( op, rs,
271 LDAP_INSUFFICIENT_ACCESS,
278 LDAP_LOG( BACK_LDBM, ERR,
279 "ldbm_back_modrdn: (%s) has no parent & not a root.\n",
282 Debug( LDAP_DEBUG_TRACE,
283 "<=- ldbm_back_modrdn: no parent & "
284 "not root\n", 0, 0, 0);
287 send_ldap_error( op, rs,
288 LDAP_INSUFFICIENT_ACCESS,
295 LDAP_LOG( BACK_LDBM, INFO,
296 "ldbm_back_modrdn: (%s) no parent, locked root.\n", e->e_dn, 0, 0 );
298 Debug( LDAP_DEBUG_TRACE,
299 "ldbm_back_modrdn: no parent, locked root\n",
304 new_parent_dn = &p_dn; /* New Parent unless newSuperior given */
306 if ( op->oq_modrdn.rs_newSup != NULL ) {
308 LDAP_LOG( BACK_LDBM, DETAIL1,
309 "ldbm_back_modrdn: new parent \"%s\" requested\n",
310 op->oq_modrdn.rs_newSup->bv_val, 0, 0 );
312 Debug( LDAP_DEBUG_TRACE,
313 "ldbm_back_modrdn: new parent \"%s\" requested...\n",
314 op->oq_modrdn.rs_newSup->bv_val, 0, 0 );
317 np_ndn = op->oq_modrdn.rs_nnewSup;
319 /* newSuperior == oldParent? */
320 if ( dn_match( &p_ndn, np_ndn ) ) {
322 LDAP_LOG( BACK_LDBM, INFO, "ldbm_back_modrdn: "
323 "new parent\"%s\" seems to be the same as the "
324 "old parent \"%s\"\n", op->oq_modrdn.rs_newSup->bv_val, p_dn.bv_val, 0 );
326 Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: "
327 "new parent\"%s\" seems to be the same as the "
328 "old parent \"%s\"\n",
329 op->oq_modrdn.rs_newSup->bv_val, p_dn.bv_val, 0 );
332 op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
336 if ( op->oq_modrdn.rs_newSup != NULL ) {
337 /* newSuperior == entry being moved?, if so ==> ERROR */
338 /* Get Entry with dn=newSuperior. Does newSuperior exist? */
340 if ( op->oq_modrdn.rs_nnewSup->bv_len ) {
341 if( (np = dn2entry_w( op->o_bd, np_ndn, NULL )) == NULL) {
343 LDAP_LOG( BACK_LDBM, ERR,
344 "ldbm_back_modrdn: newSup(ndn=%s) not found.\n",
345 np_ndn->bv_val, 0, 0 );
347 Debug( LDAP_DEBUG_TRACE,
348 "ldbm_back_modrdn: newSup(ndn=%s) not here!\n",
349 np_ndn->bv_val, 0, 0);
352 send_ldap_error( op, rs, LDAP_OTHER,
353 "newSuperior not found" );
358 LDAP_LOG( BACK_LDBM, DETAIL1,
359 "ldbm_back_modrdn: wr to new parent OK np=%p, id=%ld\n",
360 (void *) np, np->e_id, 0 );
362 Debug( LDAP_DEBUG_TRACE,
363 "ldbm_back_modrdn: wr to new parent OK np=%p, id=%ld\n",
364 (void *) np, np->e_id, 0 );
367 /* check newSuperior for "children" acl */
368 if ( !access_allowed( op, np, children, NULL,
372 LDAP_LOG( BACK_LDBM, INFO,
373 "ldbm_back_modrdn: no wr to newSup children.\n", 0, 0, 0 );
375 Debug( LDAP_DEBUG_TRACE,
376 "ldbm_back_modrdn: no wr to newSup children\n",
380 send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, NULL );
384 if ( is_entry_alias( np ) ) {
385 /* parent is an alias, don't allow add */
387 LDAP_LOG( BACK_LDBM, INFO,
388 "ldbm_back_modrdn: entry (%s) is an alias.\n", np->e_dn,0,0);
390 Debug( LDAP_DEBUG_TRACE, "entry is alias\n", 0, 0, 0 );
394 send_ldap_error( op, rs, LDAP_ALIAS_PROBLEM,
395 "newSuperior is an alias" );
400 if ( is_entry_referral( np ) ) {
401 /* parent is a referral, don't allow add */
403 LDAP_LOG( BACK_LDBM, INFO,
404 "ldbm_back_modrdn: entry (%s) is a referral\n",
407 Debug( LDAP_DEBUG_TRACE, "entry (%s) is referral\n",
411 send_ldap_error( op, rs, LDAP_OTHER,
412 "newSuperior is a referral" );
419 /* no parent, must be root to modify newSuperior */
420 if ( isroot == -1 ) {
421 isroot = be_isroot( op->o_bd, &op->o_ndn );
425 if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv ) || be_isupdate( op->o_bd, &op->o_ndn ) ) {
427 np = (Entry *)&slap_entry_root;
429 can_access = access_allowed( op, np,
430 children, NULL, ACL_WRITE, NULL );
433 /* check parent for "children" acl */
434 if ( ! can_access ) {
436 LDAP_LOG( BACK_LDBM, ERR,
437 "ldbm_back_modrdn: no access "
438 "to new superior \"\"\n", 0, 0, 0 );
440 Debug( LDAP_DEBUG_TRACE,
441 "<=- ldbm_back_modrdn: no "
442 "access to new superior\n", 0, 0, 0 );
445 send_ldap_error( op, rs,
446 LDAP_INSUFFICIENT_ACCESS,
453 LDAP_LOG( BACK_LDBM, ERR,
454 "ldbm_back_modrdn: \"\" not allowed as new superior\n",
457 Debug( LDAP_DEBUG_TRACE,
458 "<=- ldbm_back_modrdn: \"\" "
459 "not allowed as new superior\n",
463 send_ldap_error( op, rs,
464 LDAP_INSUFFICIENT_ACCESS,
472 LDAP_LOG( BACK_LDBM, DETAIL1,
473 "ldbm_back_modrdn: wr to new parent's children OK.\n", 0, 0, 0 );
475 Debug( LDAP_DEBUG_TRACE,
476 "ldbm_back_modrdn: wr to new parent's children OK\n",
480 new_parent_dn = op->oq_modrdn.rs_newSup;
483 /* Build target dn and make sure target entry doesn't exist already. */
484 build_new_dn( &new_dn, new_parent_dn, &op->oq_modrdn.rs_newrdn, NULL );
485 dnNormalize( 0, NULL, NULL, &new_dn, &new_ndn, NULL );
488 LDAP_LOG( BACK_LDBM, DETAIL1, "ldbm_back_modrdn: new ndn=%s\n",
489 new_ndn.bv_val, 0, 0 );
491 Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: new ndn=%s\n",
492 new_ndn.bv_val, 0, 0 );
495 /* check for abandon */
496 if ( op->o_abandon ) {
500 if ( ( rc_id = dn2id ( op->o_bd, &new_ndn, &id ) ) || id != NOID ) {
501 /* if (rc_id) something bad happened to ldbm cache */
502 rs->sr_err = rc_id ? LDAP_OTHER : LDAP_ALREADY_EXISTS;
503 send_ldap_result( op, rs );
508 LDAP_LOG( BACK_LDBM, INFO, "ldbm_back_modrdn: new ndn (%s) does not exist\n",
509 new_ndn.bv_val, 0, 0 );
511 Debug( LDAP_DEBUG_TRACE,
512 "ldbm_back_modrdn: new ndn=%s does not exist\n",
513 new_ndn.bv_val, 0, 0 );
516 /* Get attribute type and attribute value of our new rdn, we will
517 * need to add that to our new entry
519 if ( ldap_bv2rdn( &op->oq_modrdn.rs_newrdn, &new_rdn, (char **)&rs->sr_text,
520 LDAP_DN_FORMAT_LDAP ) )
523 LDAP_LOG ( OPERATION, ERR,
524 "ldbm_back_modrdn: can't figure out "
525 "type(s)/values(s) of newrdn\n",
528 Debug( LDAP_DEBUG_TRACE,
529 "ldbm_back_modrdn: can't figure out "
530 "type(s)/values(s) of newrdn\n",
533 send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX,
534 "unknown type(s) used in RDN" );
539 LDAP_LOG ( OPERATION, RESULTS,
540 "ldbm_back_modrdn: new_rdn_type=\"%s\", "
541 "new_rdn_val=\"%s\"\n",
542 new_rdn[ 0 ]->la_attr.bv_val,
543 new_rdn[ 0 ]->la_value.bv_val, 0 );
545 Debug( LDAP_DEBUG_TRACE,
546 "ldbm_back_modrdn: new_rdn_type=\"%s\", "
547 "new_rdn_val=\"%s\"\n",
548 new_rdn[ 0 ]->la_attr.bv_val,
549 new_rdn[ 0 ]->la_value.bv_val, 0 );
552 if ( op->oq_modrdn.rs_deleteoldrdn ) {
553 if ( ldap_bv2rdn( &op->o_req_dn, &old_rdn, (char **)&rs->sr_text,
554 LDAP_DN_FORMAT_LDAP ) )
557 LDAP_LOG ( OPERATION, ERR,
558 "ldbm_back_modrdn: can't figure out "
559 "type(s)/values(s) of old_rdn\n",
562 Debug( LDAP_DEBUG_TRACE,
563 "ldbm_back_modrdn: can't figure out "
564 "the old_rdn type(s)/value(s)\n",
567 send_ldap_error( op, rs, LDAP_OTHER,
568 "cannot parse RDN from old DN" );
574 LDAP_LOG( BACK_LDBM, DETAIL1, "ldbm_back_modrdn: DN_X500\n", 0, 0, 0 );
576 Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: DN_X500\n",
580 if ( slap_modrdn2mods( op, rs, e, old_rdn, new_rdn, &mod ) != LDAP_SUCCESS ) {
581 send_ldap_result( op, rs );
585 /* check for abandon */
586 if ( op->o_abandon ) {
590 (void) cache_delete_entry( &li->li_cache, e );
593 old_ndn = e->e_nname;
595 e->e_nname = new_ndn;
596 new_dn.bv_val = NULL;
597 new_ndn.bv_val = NULL;
599 /* NOTE: after this you must not free new_dn or new_ndn!
600 * They are used by cache.
603 /* modify memory copy of entry */
604 rs->sr_err = ldbm_modify_internal( op, &mod[0], e,
605 &rs->sr_text, textbuf, textlen );
606 switch ( rs->sr_err ) {
611 send_ldap_result( op, rs );
618 if ( dn2id_add( op->o_bd, &e->e_nname, e->e_id ) != 0 ) {
619 send_ldap_error( op, rs, LDAP_OTHER,
620 "DN index add failed" );
624 if ( dn2id_delete( op->o_bd, &old_ndn, e->e_id ) != 0 ) {
625 /* undo add of new one */
626 dn2id_delete( op->o_bd, &e->e_nname, e->e_id );
627 send_ldap_error( op, rs, LDAP_OTHER,
628 "DN index delete fail" );
633 if ( id2entry_add( op->o_bd, e ) != 0 ) {
635 dn2id_delete( op->o_bd, &e->e_nname, e->e_id );
636 dn2id_add( op->o_bd, &old_ndn, e->e_id );
637 send_ldap_error( op, rs, LDAP_OTHER,
638 "entry update failed" );
642 (void) cache_update_entry( &li->li_cache, e );
644 rs->sr_err = LDAP_SUCCESS;
646 send_ldap_result( op, rs );
647 cache_entry_commit( e );
650 if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
651 if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );
652 if( old_ndn.bv_val != NULL ) free( old_ndn.bv_val );
654 /* LDAP v2 supporting correct attribute handling. */
655 if ( new_rdn != NULL ) {
656 ldap_rdnfree( new_rdn );
658 if ( old_rdn != NULL ) {
659 ldap_rdnfree( old_rdn );
663 for (; mod; mod = tmp ) {
664 /* slap_modrdn2mods does things one way,
665 * slap_mods_opattrs does it differently
667 if ( mod->sml_op != SLAP_MOD_SOFTADD &&
668 mod->sml_op != LDAP_MOD_DELETE ) break;
669 if ( mod->sml_nvalues ) free( mod->sml_nvalues[0].bv_val );
673 slap_mods_free( mod );
676 /* LDAP v3 Support */
678 /* free new parent and writer lock */
679 cache_return_entry_w( &li->li_cache, np );
683 /* free parent and writer lock */
684 cache_return_entry_w( &li->li_cache, p );
687 /* free entry and writer lock */
688 cache_return_entry_w( &li->li_cache, e );
689 ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);
691 return( rs->sr_err );