1 /* modrdn.c - ldbm backend modrdn routine */
4 * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
5 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
9 * LDAP v3 newSuperior support. Add new rdn as an attribute.
10 * (Full support for v2 also used software/ideas contributed
11 * by Roy Hooper rhooper@cyberus.ca, thanks to him for his
14 * Copyright 1999, Juan C. Gomez, All rights reserved.
15 * This software is not subject to any license of Silicon Graphics
16 * Inc. or Purdue University.
18 * Redistribution and use in source and binary forms are permitted
19 * without restriction or fee of any kind as long as this notice
28 #include <ac/string.h>
29 #include <ac/socket.h>
32 #include "back-ldbm.h"
33 #include "proto-back-ldbm.h"
42 struct berval *newrdn,
43 struct berval *nnewrdn,
45 struct berval *newSuperior,
46 struct berval *nnewSuperior
49 AttributeDescription *children = slap_schema.si_ad_children;
50 AttributeDescription *entry = slap_schema.si_ad_entry;
51 struct ldbminfo *li = (struct ldbminfo *) be->be_private;
52 struct berval p_dn, p_ndn;
53 struct berval new_dn = { 0, NULL}, new_ndn = { 0, NULL };
56 /* LDAP v2 supporting correct attribute handling. */
57 LDAPRDN *new_rdn = NULL;
58 LDAPRDN *old_rdn = NULL;
60 #define CAN_ROLLBACK -1
61 #define MUST_DESTROY 1
62 int rc = CAN_ROLLBACK;
65 const char *text = NULL;
66 char textbuf[SLAP_TEXT_BUFLEN];
67 size_t textlen = sizeof textbuf;
68 /* Added to support newSuperior */
69 Entry *np = NULL; /* newSuperior Entry */
70 struct berval *np_ndn = NULL; /* newSuperior ndn */
71 struct berval *new_parent_dn = NULL; /* np_dn, p_dn, or NULL */
72 /* Used to interface with ldbm_modify_internal() */
73 Modifications *mod = NULL; /* Used to delete old/add new rdn */
74 int manageDSAit = get_manageDSAit( op );
77 LDAP_LOG( BACK_LDBM, ENTRY,
78 "ldbm_back_modrdn: dn: %s newSuperior=%s\n",
79 dn->bv_len ? dn->bv_val : "NULL",
80 ( newSuperior && newSuperior->bv_len ) ? newSuperior->bv_val : "NULL",0 );
82 Debug( LDAP_DEBUG_TRACE,
83 "==>ldbm_back_modrdn: dn: %s newSuperior=%s\n",
84 dn->bv_len ? dn->bv_val : "NULL",
85 ( newSuperior && newSuperior->bv_len )
86 ? newSuperior->bv_val : "NULL", 0 );
89 /* grab giant lock for writing */
90 ldap_pvt_thread_rdwr_wlock(&li->li_giant_rwlock);
92 /* get entry with writer lock */
93 if ( (e = dn2entry_w( be, ndn, &matched )) == NULL ) {
94 char* matched_dn = NULL;
97 if( matched != NULL ) {
98 matched_dn = strdup( matched->e_dn );
99 refs = is_entry_referral( matched )
100 ? get_entry_referrals( be, conn, op, matched )
102 cache_return_entry_r( &li->li_cache, matched );
104 refs = referral_rewrite( default_referral,
105 NULL, dn, LDAP_SCOPE_DEFAULT );
108 ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);
110 send_ldap_result( conn, op, LDAP_REFERRAL,
111 matched_dn, NULL, refs, NULL );
113 if ( refs ) ber_bvarray_free( refs );
119 /* check entry for "entry" acl */
120 if ( ! access_allowed( be, conn, op, e,
121 entry, NULL, ACL_WRITE, NULL ) )
124 LDAP_LOG( BACK_LDBM, ERR,
125 "ldbm_back_modrdn: no write access to entry of (%s)\n",
128 Debug( LDAP_DEBUG_TRACE,
129 "<=- ldbm_back_modrdn: no write access to entry\n", 0,
133 send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
134 NULL, "no write access to entry", NULL, NULL );
139 if (!manageDSAit && is_entry_referral( e ) ) {
140 /* parent is a referral, don't allow add */
141 /* parent is an alias, don't allow add */
142 BerVarray refs = get_entry_referrals( be,
146 LDAP_LOG( BACK_LDBM, INFO,
147 "ldbm_back_modrdn: entry %s is a referral\n", e->e_dn, 0, 0 );
149 Debug( LDAP_DEBUG_TRACE, "entry %s is referral\n", e->e_dn,
153 send_ldap_result( conn, op, LDAP_REFERRAL,
154 e->e_dn, NULL, refs, NULL );
156 if ( refs ) ber_bvarray_free( refs );
160 if ( has_children( be, e ) ) {
162 LDAP_LOG( BACK_LDBM, INFO,
163 "ldbm_back_modrdn: entry %s has children\n", e->e_dn, 0, 0 );
165 Debug( LDAP_DEBUG_TRACE, "entry %s has children\n", e->e_dn,
169 send_ldap_result( conn, op, LDAP_NOT_ALLOWED_ON_NONLEAF,
170 NULL, "subtree rename not supported", NULL, NULL );
174 if ( be_issuffix( be, &e->e_nname ) ) {
175 p_ndn = slap_empty_bv ;
177 dnParent( &e->e_nname, &p_ndn );
180 if ( p_ndn.bv_len != 0 ) {
181 /* Make sure parent entry exist and we can write its
185 if( (p = dn2entry_w( be, &p_ndn, NULL )) == NULL) {
187 LDAP_LOG( BACK_LDBM, INFO,
188 "ldbm_back_modrdn: parent of %s does not exist\n",
191 Debug( LDAP_DEBUG_TRACE, "parent does not exist\n",
195 send_ldap_result( conn, op, LDAP_OTHER,
196 NULL, "parent entry does not exist", NULL, NULL );
201 /* check parent for "children" acl */
202 if ( ! access_allowed( be, conn, op, p,
203 children, NULL, ACL_WRITE, NULL ) )
206 LDAP_LOG( BACK_LDBM, INFO,
207 "ldbm_back_modrdn: no access to parent of (%s)\n",
210 Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
214 send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
215 NULL, NULL, NULL, NULL );
220 LDAP_LOG( BACK_LDBM, DETAIL1,
221 "ldbm_back_modrdn: wr to children of entry %s OK\n",
222 p_ndn.bv_val, 0, 0 );
224 Debug( LDAP_DEBUG_TRACE,
225 "ldbm_back_modrdn: wr to children of entry %s OK\n",
226 p_ndn.bv_val, 0, 0 );
229 if ( p_ndn.bv_val == slap_empty_bv.bv_val ) {
230 p_dn = slap_empty_bv;
232 dnParent( &e->e_name, &p_dn );
236 LDAP_LOG( BACK_LDBM, DETAIL1,
237 "ldbm_back_modrdn: parent dn=%s\n", p_dn.bv_val, 0, 0 );
239 Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: parent dn=%s\n",
244 /* no parent, must be root to modify rdn */
245 isroot = be_isroot( be, &op->o_ndn );
247 if ( be_issuffix( be, (struct berval *)&slap_empty_bv ) || be_isupdate( be, &op->o_ndn ) ) {
248 p = (Entry *)&slap_entry_root;
250 rc = access_allowed( be, conn, op, p,
251 children, NULL, ACL_WRITE, NULL );
254 /* check parent for "children" acl */
257 LDAP_LOG( BACK_LDBM, ERR,
258 "ldbm_back_modrdn: no access to parent \"\"\n", 0,0,0 );
260 Debug( LDAP_DEBUG_TRACE,
261 "<=- ldbm_back_modrdn: no "
262 "access to parent\n", 0, 0, 0 );
265 send_ldap_result( conn, op,
266 LDAP_INSUFFICIENT_ACCESS,
267 NULL, NULL, NULL, NULL );
273 LDAP_LOG( BACK_LDBM, ERR,
274 "ldbm_back_modrdn: (%s) has no parent & not a root.\n",
277 Debug( LDAP_DEBUG_TRACE,
278 "<=- ldbm_back_modrdn: no parent & "
279 "not root\n", 0, 0, 0);
282 send_ldap_result( conn, op,
283 LDAP_INSUFFICIENT_ACCESS,
284 NULL, NULL, NULL, NULL );
290 LDAP_LOG( BACK_LDBM, INFO,
291 "ldbm_back_modrdn: (%s) no parent, locked root.\n", e->e_dn, 0, 0 );
293 Debug( LDAP_DEBUG_TRACE,
294 "ldbm_back_modrdn: no parent, locked root\n",
299 new_parent_dn = &p_dn; /* New Parent unless newSuperior given */
301 if ( newSuperior != NULL ) {
303 LDAP_LOG( BACK_LDBM, DETAIL1,
304 "ldbm_back_modrdn: new parent \"%s\" requested\n",
305 newSuperior->bv_val, 0, 0 );
307 Debug( LDAP_DEBUG_TRACE,
308 "ldbm_back_modrdn: new parent \"%s\" requested...\n",
309 newSuperior->bv_val, 0, 0 );
312 np_ndn = nnewSuperior;
314 /* newSuperior == oldParent? */
315 if ( dn_match( &p_ndn, np_ndn ) ) {
317 LDAP_LOG( BACK_LDBM, INFO, "ldbm_back_modrdn: "
318 "new parent\"%s\" seems to be the same as the "
319 "old parent \"%s\"\n", newSuperior->bv_val, p_dn.bv_val, 0 );
321 Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: "
322 "new parent\"%s\" seems to be the same as the "
323 "old parent \"%s\"\n",
324 newSuperior->bv_val, p_dn.bv_val, 0 );
327 newSuperior = NULL; /* ignore newSuperior */
331 if ( newSuperior != NULL ) {
332 /* newSuperior == entry being moved?, if so ==> ERROR */
333 /* Get Entry with dn=newSuperior. Does newSuperior exist? */
335 if ( nnewSuperior->bv_len ) {
336 if( (np = dn2entry_w( be, np_ndn, NULL )) == NULL) {
338 LDAP_LOG( BACK_LDBM, ERR,
339 "ldbm_back_modrdn: newSup(ndn=%s) not found.\n",
340 np_ndn->bv_val, 0, 0 );
342 Debug( LDAP_DEBUG_TRACE,
343 "ldbm_back_modrdn: newSup(ndn=%s) not here!\n",
344 np_ndn->bv_val, 0, 0);
347 send_ldap_result( conn, op, LDAP_OTHER,
348 NULL, "newSuperior not found", NULL, NULL );
353 LDAP_LOG( BACK_LDBM, DETAIL1,
354 "ldbm_back_modrdn: wr to new parent OK np=%p, id=%ld\n",
357 Debug( LDAP_DEBUG_TRACE,
358 "ldbm_back_modrdn: wr to new parent OK np=%p, id=%ld\n",
362 /* check newSuperior for "children" acl */
363 if ( !access_allowed( be, conn, op, np, children, NULL,
367 LDAP_LOG( BACK_LDBM, INFO,
368 "ldbm_back_modrdn: no wr to newSup children.\n", 0, 0, 0 );
370 Debug( LDAP_DEBUG_TRACE,
371 "ldbm_back_modrdn: no wr to newSup children\n",
375 send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
376 NULL, NULL, NULL, NULL );
380 if ( is_entry_alias( np ) ) {
381 /* parent is an alias, don't allow add */
383 LDAP_LOG( BACK_LDBM, INFO,
384 "ldbm_back_modrdn: entry (%s) is an alias.\n", np->e_dn,0,0);
386 Debug( LDAP_DEBUG_TRACE, "entry is alias\n", 0, 0, 0 );
390 send_ldap_result( conn, op, LDAP_ALIAS_PROBLEM,
391 NULL, "newSuperior is an alias", NULL, NULL );
396 if ( is_entry_referral( np ) ) {
397 /* parent is a referral, don't allow add */
399 LDAP_LOG( BACK_LDBM, INFO,
400 "ldbm_back_modrdn: entry (%s) is a referral\n",
403 Debug( LDAP_DEBUG_TRACE, "entry (%s) is referral\n",
407 send_ldap_result( conn, op, LDAP_OTHER,
408 NULL, "newSuperior is a referral", NULL, NULL );
415 /* no parent, must be root to modify newSuperior */
416 if ( isroot == -1 ) {
417 isroot = be_isroot( be, &op->o_ndn );
421 if ( be_issuffix( be, (struct berval *)&slap_empty_bv ) || be_isupdate( be, &op->o_ndn ) ) {
422 np = (Entry *)&slap_entry_root;
424 rc = access_allowed( be, conn, op, np,
425 children, NULL, ACL_WRITE, NULL );
428 /* check parent for "children" acl */
431 LDAP_LOG( BACK_LDBM, ERR,
432 "ldbm_back_modrdn: no access "
433 "to new superior \"\"\n", 0, 0, 0 );
435 Debug( LDAP_DEBUG_TRACE,
436 "<=- ldbm_back_modrdn: no "
437 "access to new superior\n", 0, 0, 0 );
440 send_ldap_result( conn, op,
441 LDAP_INSUFFICIENT_ACCESS,
442 NULL, NULL, NULL, NULL );
448 LDAP_LOG( BACK_LDBM, ERR,
449 "ldbm_back_modrdn: \"\" not allowed as new superior\n",
452 Debug( LDAP_DEBUG_TRACE,
453 "<=- ldbm_back_modrdn: \"\" "
454 "not allowed as new superior\n",
458 send_ldap_result( conn, op,
459 LDAP_INSUFFICIENT_ACCESS,
460 NULL, NULL, NULL, NULL );
467 LDAP_LOG( BACK_LDBM, DETAIL1,
468 "ldbm_back_modrdn: wr to new parent's children OK.\n", 0, 0, 0 );
470 Debug( LDAP_DEBUG_TRACE,
471 "ldbm_back_modrdn: wr to new parent's children OK\n",
475 new_parent_dn = newSuperior;
478 /* Build target dn and make sure target entry doesn't exist already. */
479 build_new_dn( &new_dn, new_parent_dn, newrdn );
480 dnNormalize2( NULL, &new_dn, &new_ndn );
483 LDAP_LOG( BACK_LDBM, DETAIL1, "ldbm_back_modrdn: new ndn=%s\n",
484 new_ndn.bv_val, 0, 0 );
486 Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: new ndn=%s\n",
487 new_ndn.bv_val, 0, 0 );
490 /* check for abandon */
491 if ( op->o_abandon ) {
495 if ( ( rc_id = dn2id ( be, &new_ndn, &id ) ) || id != NOID ) {
496 /* if (rc_id) something bad happened to ldbm cache */
497 send_ldap_result( conn, op,
498 rc_id ? LDAP_OTHER : LDAP_ALREADY_EXISTS,
499 NULL, NULL, NULL, NULL );
504 LDAP_LOG( BACK_LDBM, INFO, "ldbm_back_modrdn: new ndn (%s) does not exist\n",
505 new_ndn.bv_val, 0, 0 );
507 Debug( LDAP_DEBUG_TRACE,
508 "ldbm_back_modrdn: new ndn=%s does not exist\n",
509 new_ndn.bv_val, 0, 0 );
512 /* Get attribute type and attribute value of our new rdn, we will
513 * need to add that to our new entry
515 if ( ldap_bv2rdn( newrdn, &new_rdn, (char **)&text,
516 LDAP_DN_FORMAT_LDAP ) )
519 LDAP_LOG ( OPERATION, ERR,
520 "ldbm_back_modrdn: can't figure out "
521 "type(s)/values(s) of newrdn\n",
524 Debug( LDAP_DEBUG_TRACE,
525 "ldbm_back_modrdn: can't figure out "
526 "type(s)/values(s) of newrdn\n",
529 rc = LDAP_INVALID_DN_SYNTAX;
530 text = "unknown type(s) used in RDN";
535 LDAP_LOG ( OPERATION, RESULTS,
536 "ldbm_back_modrdn: new_rdn_type=\"%s\", "
537 "new_rdn_val=\"%s\"\n",
538 new_rdn[ 0 ][ 0 ]->la_attr.bv_val,
539 new_rdn[ 0 ][ 0 ]->la_value.bv_val, 0 );
541 Debug( LDAP_DEBUG_TRACE,
542 "ldbm_back_modrdn: new_rdn_type=\"%s\", "
543 "new_rdn_val=\"%s\"\n",
544 new_rdn[ 0 ][ 0 ]->la_attr.bv_val,
545 new_rdn[ 0 ][ 0 ]->la_value.bv_val, 0 );
548 if ( deleteoldrdn ) {
549 if ( ldap_bv2rdn( dn, &old_rdn, (char **)&text,
550 LDAP_DN_FORMAT_LDAP ) )
553 LDAP_LOG ( OPERATION, ERR,
554 "ldbm_back_modrdn: can't figure out "
555 "type(s)/values(s) of old_rdn\n",
558 Debug( LDAP_DEBUG_TRACE,
559 "ldbm_back_modrdn: can't figure out "
560 "the old_rdn type(s)/value(s)\n",
564 text = "cannot parse RDN from old DN";
570 LDAP_LOG( BACK_LDBM, DETAIL1, "ldbm_back_modrdn: DN_X500\n", 0, 0, 0 );
572 Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: DN_X500\n",
576 rc = slap_modrdn2mods( be, conn, op, e, old_rdn, new_rdn,
577 deleteoldrdn, &mod );
578 if ( rc != LDAP_SUCCESS ) {
583 /* check for abandon */
584 if ( op->o_abandon ) {
589 if ( dn2id_delete( be, &e->e_nname, e->e_id ) != 0 ) {
590 send_ldap_result( conn, op, LDAP_OTHER,
591 NULL, "DN index delete fail", NULL, NULL );
595 (void) cache_delete_entry( &li->li_cache, e );
598 /* XXX: there is no going back! */
603 e->e_nname = new_ndn;
604 new_dn.bv_val = NULL;
605 new_ndn.bv_val = NULL;
608 if ( dn2id_add( be, &e->e_nname, e->e_id ) != 0 ) {
609 send_ldap_result( conn, op, LDAP_OTHER,
610 NULL, "DN index add failed", NULL, NULL );
614 /* modify memory copy of entry */
615 rc = ldbm_modify_internal( be, conn, op, dn->bv_val, &mod[0], e,
616 &text, textbuf, textlen );
618 if( rc != LDAP_SUCCESS ) {
619 if( rc != SLAPD_ABANDON ) {
620 send_ldap_result( conn, op, rc,
621 NULL, text, NULL, NULL );
624 /* here we may try to delete the newly added dn */
625 if ( dn2id_delete( be, &e->e_nname, e->e_id ) != 0 ) {
626 /* we already are in trouble ... */
633 (void) cache_update_entry( &li->li_cache, e );
635 /* NOTE: after this you must not free new_dn or new_ndn!
636 * They are used by cache.
640 if ( id2entry_add( be, e ) != 0 ) {
641 send_ldap_result( conn, op, LDAP_OTHER,
642 NULL, "entry update failed", NULL, NULL );
646 send_ldap_result( conn, op, LDAP_SUCCESS,
647 NULL, NULL, NULL, NULL );
649 cache_entry_commit( e );
652 if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
653 if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );
655 /* LDAP v2 supporting correct attribute handling. */
656 if ( new_rdn != NULL ) {
657 ldap_rdnfree( new_rdn );
659 if ( old_rdn != NULL ) {
660 ldap_rdnfree( old_rdn );
664 for (; mod; mod = tmp ) {
670 /* LDAP v3 Support */
672 /* free new parent and writer lock */
673 cache_return_entry_w( &li->li_cache, np );
677 /* free parent and writer lock */
678 cache_return_entry_w( &li->li_cache, p );
681 /* free entry and writer lock */
682 cache_return_entry_w( &li->li_cache, e );
683 if ( rc == MUST_DESTROY ) {
684 /* if rc == MUST_DESTROY the entry is uncached
685 * and its private data is destroyed;
686 * the entry must be freed */
689 ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);