1 /* modrdn.c - ldbm backend modrdn routine */
4 * Copyright 1998-2003 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 ) ) {
249 p = (Entry *)&slap_entry_root;
251 can_access = access_allowed( be, conn, op, p,
252 children, NULL, ACL_WRITE, NULL );
255 /* check parent for "children" acl */
256 if ( ! can_access ) {
258 LDAP_LOG( BACK_LDBM, ERR,
259 "ldbm_back_modrdn: no access to parent \"\"\n", 0,0,0 );
261 Debug( LDAP_DEBUG_TRACE,
262 "<=- ldbm_back_modrdn: no "
263 "access to parent\n", 0, 0, 0 );
266 send_ldap_result( conn, op,
267 LDAP_INSUFFICIENT_ACCESS,
268 NULL, NULL, NULL, NULL );
274 LDAP_LOG( BACK_LDBM, ERR,
275 "ldbm_back_modrdn: (%s) has no parent & not a root.\n",
278 Debug( LDAP_DEBUG_TRACE,
279 "<=- ldbm_back_modrdn: no parent & "
280 "not root\n", 0, 0, 0);
283 send_ldap_result( conn, op,
284 LDAP_INSUFFICIENT_ACCESS,
285 NULL, NULL, NULL, NULL );
291 LDAP_LOG( BACK_LDBM, INFO,
292 "ldbm_back_modrdn: (%s) no parent, locked root.\n", e->e_dn, 0, 0 );
294 Debug( LDAP_DEBUG_TRACE,
295 "ldbm_back_modrdn: no parent, locked root\n",
300 new_parent_dn = &p_dn; /* New Parent unless newSuperior given */
302 if ( newSuperior != NULL ) {
304 LDAP_LOG( BACK_LDBM, DETAIL1,
305 "ldbm_back_modrdn: new parent \"%s\" requested\n",
306 newSuperior->bv_val, 0, 0 );
308 Debug( LDAP_DEBUG_TRACE,
309 "ldbm_back_modrdn: new parent \"%s\" requested...\n",
310 newSuperior->bv_val, 0, 0 );
313 np_ndn = nnewSuperior;
315 /* newSuperior == oldParent? */
316 if ( dn_match( &p_ndn, np_ndn ) ) {
318 LDAP_LOG( BACK_LDBM, INFO, "ldbm_back_modrdn: "
319 "new parent\"%s\" seems to be the same as the "
320 "old parent \"%s\"\n", newSuperior->bv_val, p_dn.bv_val, 0 );
322 Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: "
323 "new parent\"%s\" seems to be the same as the "
324 "old parent \"%s\"\n",
325 newSuperior->bv_val, p_dn.bv_val, 0 );
328 newSuperior = NULL; /* ignore newSuperior */
332 if ( newSuperior != NULL ) {
333 /* newSuperior == entry being moved?, if so ==> ERROR */
334 /* Get Entry with dn=newSuperior. Does newSuperior exist? */
336 if ( nnewSuperior->bv_len ) {
337 if( (np = dn2entry_w( be, np_ndn, NULL )) == NULL) {
339 LDAP_LOG( BACK_LDBM, ERR,
340 "ldbm_back_modrdn: newSup(ndn=%s) not found.\n",
341 np_ndn->bv_val, 0, 0 );
343 Debug( LDAP_DEBUG_TRACE,
344 "ldbm_back_modrdn: newSup(ndn=%s) not here!\n",
345 np_ndn->bv_val, 0, 0);
348 send_ldap_result( conn, op, LDAP_OTHER,
349 NULL, "newSuperior not found", NULL, NULL );
354 LDAP_LOG( BACK_LDBM, DETAIL1,
355 "ldbm_back_modrdn: wr to new parent OK np=%p, id=%ld\n",
358 Debug( LDAP_DEBUG_TRACE,
359 "ldbm_back_modrdn: wr to new parent OK np=%p, id=%ld\n",
363 /* check newSuperior for "children" acl */
364 if ( !access_allowed( be, conn, op, np, children, NULL,
368 LDAP_LOG( BACK_LDBM, INFO,
369 "ldbm_back_modrdn: no wr to newSup children.\n", 0, 0, 0 );
371 Debug( LDAP_DEBUG_TRACE,
372 "ldbm_back_modrdn: no wr to newSup children\n",
376 send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
377 NULL, NULL, NULL, NULL );
381 if ( is_entry_alias( np ) ) {
382 /* parent is an alias, don't allow add */
384 LDAP_LOG( BACK_LDBM, INFO,
385 "ldbm_back_modrdn: entry (%s) is an alias.\n", np->e_dn,0,0);
387 Debug( LDAP_DEBUG_TRACE, "entry is alias\n", 0, 0, 0 );
391 send_ldap_result( conn, op, LDAP_ALIAS_PROBLEM,
392 NULL, "newSuperior is an alias", NULL, NULL );
397 if ( is_entry_referral( np ) ) {
398 /* parent is a referral, don't allow add */
400 LDAP_LOG( BACK_LDBM, INFO,
401 "ldbm_back_modrdn: entry (%s) is a referral\n",
404 Debug( LDAP_DEBUG_TRACE, "entry (%s) is referral\n",
408 send_ldap_result( conn, op, LDAP_OTHER,
409 NULL, "newSuperior is a referral", NULL, NULL );
416 /* no parent, must be root to modify newSuperior */
417 if ( isroot == -1 ) {
418 isroot = be_isroot( be, &op->o_ndn );
422 if ( be_issuffix( be, (struct berval *)&slap_empty_bv ) || be_isupdate( be, &op->o_ndn ) ) {
424 np = (Entry *)&slap_entry_root;
426 can_access = access_allowed( be, conn, op, np,
427 children, NULL, ACL_WRITE, NULL );
430 /* check parent for "children" acl */
431 if ( ! can_access ) {
433 LDAP_LOG( BACK_LDBM, ERR,
434 "ldbm_back_modrdn: no access "
435 "to new superior \"\"\n", 0, 0, 0 );
437 Debug( LDAP_DEBUG_TRACE,
438 "<=- ldbm_back_modrdn: no "
439 "access to new superior\n", 0, 0, 0 );
442 send_ldap_result( conn, op,
443 LDAP_INSUFFICIENT_ACCESS,
444 NULL, NULL, NULL, NULL );
450 LDAP_LOG( BACK_LDBM, ERR,
451 "ldbm_back_modrdn: \"\" not allowed as new superior\n",
454 Debug( LDAP_DEBUG_TRACE,
455 "<=- ldbm_back_modrdn: \"\" "
456 "not allowed as new superior\n",
460 send_ldap_result( conn, op,
461 LDAP_INSUFFICIENT_ACCESS,
462 NULL, NULL, NULL, NULL );
469 LDAP_LOG( BACK_LDBM, DETAIL1,
470 "ldbm_back_modrdn: wr to new parent's children OK.\n", 0, 0, 0 );
472 Debug( LDAP_DEBUG_TRACE,
473 "ldbm_back_modrdn: wr to new parent's children OK\n",
477 new_parent_dn = newSuperior;
480 /* Build target dn and make sure target entry doesn't exist already. */
481 build_new_dn( &new_dn, new_parent_dn, newrdn );
482 dnNormalize2( NULL, &new_dn, &new_ndn );
485 LDAP_LOG( BACK_LDBM, DETAIL1, "ldbm_back_modrdn: new ndn=%s\n",
486 new_ndn.bv_val, 0, 0 );
488 Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: new ndn=%s\n",
489 new_ndn.bv_val, 0, 0 );
492 /* check for abandon */
493 if ( op->o_abandon ) {
497 if ( ( rc_id = dn2id ( be, &new_ndn, &id ) ) || id != NOID ) {
498 /* if (rc_id) something bad happened to ldbm cache */
499 send_ldap_result( conn, op,
500 rc_id ? LDAP_OTHER : LDAP_ALREADY_EXISTS,
501 NULL, NULL, NULL, NULL );
506 LDAP_LOG( BACK_LDBM, INFO, "ldbm_back_modrdn: new ndn (%s) does not exist\n",
507 new_ndn.bv_val, 0, 0 );
509 Debug( LDAP_DEBUG_TRACE,
510 "ldbm_back_modrdn: new ndn=%s does not exist\n",
511 new_ndn.bv_val, 0, 0 );
514 /* Get attribute type and attribute value of our new rdn, we will
515 * need to add that to our new entry
517 if ( ldap_bv2rdn( newrdn, &new_rdn, (char **)&text,
518 LDAP_DN_FORMAT_LDAP ) )
521 LDAP_LOG ( OPERATION, ERR,
522 "ldbm_back_modrdn: can't figure out "
523 "type(s)/values(s) of newrdn\n",
526 Debug( LDAP_DEBUG_TRACE,
527 "ldbm_back_modrdn: can't figure out "
528 "type(s)/values(s) of newrdn\n",
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",
568 LDAP_LOG( BACK_LDBM, DETAIL1, "ldbm_back_modrdn: DN_X500\n", 0, 0, 0 );
570 Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: DN_X500\n",
574 if ( slap_modrdn2mods( be, conn, op, e, old_rdn, new_rdn,
575 deleteoldrdn, &mod ) != LDAP_SUCCESS ) {
580 /* check for abandon */
581 if ( op->o_abandon ) {
586 if ( dn2id_delete( be, &e->e_nname, e->e_id ) != 0 ) {
587 send_ldap_result( conn, op, LDAP_OTHER,
588 NULL, "DN index delete fail", NULL, NULL );
592 (void) cache_delete_entry( &li->li_cache, e );
595 /* XXX: there is no going back! */
600 e->e_nname = new_ndn;
601 new_dn.bv_val = NULL;
602 new_ndn.bv_val = NULL;
604 /* NOTE: after this you must not free new_dn or new_ndn!
605 * They are used by cache.
609 if ( dn2id_add( be, &e->e_nname, e->e_id ) != 0 ) {
610 send_ldap_result( conn, op, LDAP_OTHER,
611 NULL, "DN index add failed", NULL, NULL );
615 /* modify memory copy of entry */
616 rc_id = ldbm_modify_internal( be, conn, op, dn->bv_val, &mod[0], e,
617 &text, textbuf, textlen );
624 send_ldap_result( conn, op, rc_id, NULL, text, NULL, NULL );
628 /* here we may try to delete the newly added dn */
629 if ( dn2id_delete( be, &e->e_nname, e->e_id ) != 0 ) {
630 /* we already are in trouble ... */
636 (void) cache_update_entry( &li->li_cache, e );
639 if ( id2entry_add( be, e ) != 0 ) {
640 send_ldap_result( conn, op, LDAP_OTHER,
641 NULL, "entry update failed", NULL, NULL );
645 send_ldap_result( conn, op, LDAP_SUCCESS,
646 NULL, NULL, NULL, NULL );
648 cache_entry_commit( e );
651 if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
652 if( new_ndn.bv_val != NULL ) free( new_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 ) {
669 /* LDAP v3 Support */
671 /* free new parent and writer lock */
672 cache_return_entry_w( &li->li_cache, np );
676 /* free parent and writer lock */
677 cache_return_entry_w( &li->li_cache, p );
680 /* free entry and writer lock */
681 cache_return_entry_w( &li->li_cache, e );
682 if ( rc == MUST_DESTROY ) {
683 /* if rc == MUST_DESTROY the entry is uncached
684 * and its private data is destroyed;
685 * the entry must be freed */
688 ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);