]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/modrdn.c
Add GIANT rwlock! This should resolve nasty concurrency issues.
[openldap] / servers / slapd / back-ldbm / modrdn.c
1 /* modrdn.c - ldbm backend modrdn routine */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 /*
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
12  * submission!.)
13  *
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.
17  *
18  * Redistribution and use in source and binary forms are permitted
19  * without restriction or fee of any kind as long as this notice
20  * is preserved.
21  *
22  */
23
24 #include "portable.h"
25
26 #include <stdio.h>
27
28 #include <ac/string.h>
29 #include <ac/socket.h>
30
31 #include "slap.h"
32 #include "back-ldbm.h"
33 #include "proto-back-ldbm.h"
34
35 int
36 ldbm_back_modrdn(
37     Backend     *be,
38     Connection  *conn,
39     Operation   *op,
40     struct berval       *dn,
41     struct berval       *ndn,
42     struct berval       *newrdn,
43     struct berval       *nnewrdn,
44     int         deleteoldrdn,
45     struct berval       *newSuperior,
46     struct berval       *nnewSuperior
47 )
48 {
49         AttributeDescription *children = slap_schema.si_ad_children;
50         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
51         struct berval   p_dn, p_ndn;
52         struct berval   new_dn = { 0, NULL}, new_ndn = { 0, NULL };
53         Entry           *e, *p = NULL;
54         Entry           *matched;
55         int             isroot = -1;
56         int             rootlock = 0;
57 #define CAN_ROLLBACK    -1
58 #define MUST_DESTROY    1
59         int             rc = CAN_ROLLBACK;
60         int             rc_id = 0;
61         ID              id = NOID;
62         const char *text = NULL;
63         char textbuf[SLAP_TEXT_BUFLEN];
64         size_t textlen = sizeof textbuf;
65         /* Added to support LDAP v2 correctly (deleteoldrdn thing) */
66         LDAPRDN         *new_rdn;
67         LDAPRDN         *old_rdn;
68         int             a_cnt, d_cnt;
69         /* Added to support newSuperior */ 
70         Entry           *np = NULL;     /* newSuperior Entry */
71         struct berval   *np_ndn = NULL; /* newSuperior ndn */
72         struct berval   *new_parent_dn = NULL;  /* np_dn, p_dn, or NULL */
73         /* Used to interface with ldbm_modify_internal() */
74         Modifications   *mod = NULL;            /* Used to delete old/add new rdn */
75         int             manageDSAit = get_manageDSAit( op );
76
77 #ifdef NEW_LOGGING
78         LDAP_LOG(( "backend", LDAP_LEVEL_ENTRY,
79                 "ldbm_back_modrdn: dn: %s newSuperior=%s\n", 
80                 dn->bv_len ? dn->bv_val : "NULL",
81                 ( newSuperior && newSuperior->bv_len )
82                         ? newSuperior->bv_val : "NULL" ));
83 #else
84         Debug( LDAP_DEBUG_TRACE,
85                 "==>ldbm_back_modrdn: dn: %s newSuperior=%s\n", 
86                 dn->bv_len ? dn->bv_val : "NULL",
87                 ( newSuperior && newSuperior->bv_len )
88                         ? newSuperior->bv_val : "NULL", 0 );
89 #endif
90
91         /* grab giant lock for writing */
92         ldap_pvt_thread_rdwr_wlock(&li->li_giant_rwlock);
93
94         /* get entry with writer lock */
95         if ( (e = dn2entry_w( be, ndn, &matched )) == NULL ) {
96                 char* matched_dn = NULL;
97                 BerVarray refs;
98
99                 if( matched != NULL ) {
100                         matched_dn = strdup( matched->e_dn );
101                         refs = is_entry_referral( matched )
102                                 ? get_entry_referrals( be, conn, op, matched )
103                                 : NULL;
104                         cache_return_entry_r( &li->li_cache, matched );
105                 } else {
106                         refs = referral_rewrite( default_referral,
107                                 NULL, dn, LDAP_SCOPE_DEFAULT );
108                 }
109
110                 ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);
111
112                 send_ldap_result( conn, op, LDAP_REFERRAL,
113                         matched_dn, NULL, refs, NULL );
114
115                 if ( refs ) ber_bvarray_free( refs );
116                 free( matched_dn );
117
118                 return( -1 );
119         }
120
121         if (!manageDSAit && is_entry_referral( e ) ) {
122                 /* parent is a referral, don't allow add */
123                 /* parent is an alias, don't allow add */
124                 BerVarray refs = get_entry_referrals( be,
125                         conn, op, e );
126
127 #ifdef NEW_LOGGING
128                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
129                         "ldbm_back_modrdn: entry %s is a referral\n", e->e_dn ));
130 #else
131                 Debug( LDAP_DEBUG_TRACE, "entry %s is referral\n", e->e_dn,
132                     0, 0 );
133 #endif
134
135                 send_ldap_result( conn, op, LDAP_REFERRAL,
136                     e->e_dn, NULL, refs, NULL );
137
138                 if ( refs ) ber_bvarray_free( refs );
139                 goto return_results;
140         }
141
142         if ( has_children( be, e ) ) {
143 #ifdef NEW_LOGGING
144                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
145                         "ldbm_back_modrdn: entry %s has children\n", e->e_dn ));
146 #else
147                 Debug( LDAP_DEBUG_TRACE, "entry %s has children\n", e->e_dn,
148                     0, 0 );
149 #endif
150
151                 send_ldap_result( conn, op, LDAP_NOT_ALLOWED_ON_NONLEAF,
152                     NULL, "subtree rename not supported", NULL, NULL );
153                 goto return_results;
154         }
155
156         if ( be_issuffix( be, &e->e_nname ) ) {
157                 p_ndn = slap_empty_bv ;
158         } else {
159                 dnParent( &e->e_nname, &p_ndn );
160         }
161
162         if ( p_ndn.bv_len != 0 ) {
163                 /* Make sure parent entry exist and we can write its 
164                  * children.
165                  */
166
167                 if( (p = dn2entry_w( be, &p_ndn, NULL )) == NULL) {
168 #ifdef NEW_LOGGING
169                         LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
170                                 "ldbm_back_modrdn: parent of %s does not exist\n", e->e_ndn ));
171 #else
172                         Debug( LDAP_DEBUG_TRACE, "parent does not exist\n",
173                                 0, 0, 0);
174 #endif
175
176                         send_ldap_result( conn, op, LDAP_OTHER,
177                                 NULL, "parent entry does not exist", NULL, NULL );
178
179                         goto return_results;
180                 }
181
182                 /* check parent for "children" acl */
183                 if ( ! access_allowed( be, conn, op, p,
184                         children, NULL, ACL_WRITE ) )
185                 {
186 #ifdef NEW_LOGGING
187                         LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
188                                    "ldbm_back_modrdn: no access to parent of (%s)\n", e->e_dn ));
189 #else
190                         Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
191                                 0, 0 );
192 #endif
193
194                         send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
195                                 NULL, NULL, NULL, NULL );
196                         goto return_results;
197                 }
198
199 #ifdef NEW_LOGGING
200                 LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
201                            "ldbm_back_modrdn: wr to children of entry %s OK\n",
202                            p_ndn.bv_val ));
203 #else
204                 Debug( LDAP_DEBUG_TRACE,
205                        "ldbm_back_modrdn: wr to children of entry %s OK\n",
206                        p_ndn.bv_val, 0, 0 );
207 #endif
208
209                 if ( p_ndn.bv_val == slap_empty_bv.bv_val ) {
210                         p_dn = slap_empty_bv;
211                 } else {
212                         dnParent( &e->e_name, &p_dn );
213                 }
214
215 #ifdef NEW_LOGGING
216                 LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
217                            "ldbm_back_modrdn: parent dn=%s\n", p_dn.bv_val ));
218 #else
219                 Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: parent dn=%s\n",
220                        p_dn.bv_val, 0, 0 );
221 #endif
222
223         } else {
224                 /* no parent, must be root to modify rdn */
225                 isroot = be_isroot( be, &op->o_ndn );
226                 if ( ! isroot ) {
227                         if ( be_issuffix( be, (struct berval *)&slap_empty_bv ) || be_isupdate( be, &op->o_ndn ) ) {
228                                 p = (Entry *)&slap_entry_root;
229                                 
230                                 rc = access_allowed( be, conn, op, p,
231                                                 children, NULL, ACL_WRITE );
232                                 p = NULL;
233                                                                 
234                                 /* check parent for "children" acl */
235                                 if ( ! rc ) {
236 #ifdef NEW_LOGGING
237                                         LDAP_LOG(( "backend", LDAP_LEVEL_ERR,
238                                                 "ldbm_back_modrdn: no access "
239                                                 "to parent \"\"\n" ));
240 #else
241                                         Debug( LDAP_DEBUG_TRACE,
242                                                 "<=- ldbm_back_modrdn: no "
243                                                 "access to parent\n", 0, 0, 0 );
244 #endif
245
246                                         send_ldap_result( conn, op, 
247                                                 LDAP_INSUFFICIENT_ACCESS,
248                                                 NULL, NULL, NULL, NULL );
249                                         goto return_results;
250                                 }
251
252                         } else {
253 #ifdef NEW_LOGGING
254                                 LDAP_LOG(( "backend", LDAP_LEVEL_ERR,
255                                            "ldbm_back_modrdn: (%s) has no "
256                                            "parent & not a root.\n", dn ));
257 #else
258                                 Debug( LDAP_DEBUG_TRACE,
259                                         "<=- ldbm_back_modrdn: no parent & "
260                                         "not root\n", 0, 0, 0);
261 #endif
262
263                                 send_ldap_result( conn, op, 
264                                         LDAP_INSUFFICIENT_ACCESS,
265                                         NULL, NULL, NULL, NULL );
266                                 goto return_results;
267                         }
268                 }
269
270 #ifdef NEW_LOGGING
271                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
272                            "ldbm_back_modrdn: (%s) no parent, locked root.\n", e->e_dn ));
273 #else
274                 Debug( LDAP_DEBUG_TRACE,
275                        "ldbm_back_modrdn: no parent, locked root\n",
276                        0, 0, 0 );
277 #endif
278         }
279
280         new_parent_dn = &p_dn;  /* New Parent unless newSuperior given */
281
282         if ( newSuperior != NULL ) {
283 #ifdef NEW_LOGGING
284                 LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
285                         "ldbm_back_modrdn: new parent \"%s\" requested\n",
286                         newSuperior->bv_val ));
287 #else
288                 Debug( LDAP_DEBUG_TRACE, 
289                         "ldbm_back_modrdn: new parent \"%s\" requested...\n",
290                         newSuperior->bv_val, 0, 0 );
291 #endif
292
293                 np_ndn = nnewSuperior;
294
295                 /* newSuperior == oldParent? */
296                 if ( dn_match( &p_ndn, np_ndn ) ) {
297 #ifdef NEW_LOGGING
298                         LDAP_LOG(( "backend", LDAP_LEVEL_INFO, "ldbm_back_modrdn: "
299                                 "new parent\"%s\" seems to be the same as the "
300                                 "old parent \"%s\"\n",
301                                 newSuperior->bv_val, p_dn.bv_val ));
302 #else
303                         Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: "
304                                 "new parent\"%s\" seems to be the same as the "
305                                 "old parent \"%s\"\n",
306                                 newSuperior->bv_val, p_dn.bv_val, 0 );
307 #endif
308
309                         newSuperior = NULL; /* ignore newSuperior */
310                 }
311         }
312
313         if ( newSuperior != NULL ) {
314                 /* newSuperior == entry being moved?, if so ==> ERROR */
315                 /* Get Entry with dn=newSuperior. Does newSuperior exist? */
316
317                 if ( nnewSuperior->bv_len ) {
318                         if( (np = dn2entry_w( be, np_ndn, NULL )) == NULL) {
319 #ifdef NEW_LOGGING
320                                 LDAP_LOG(( "backend", LDAP_LEVEL_ERR,
321                                         "ldbm_back_modrdn: newSup(ndn=%s) not found.\n", np_ndn->bv_val ));
322 #else
323                                 Debug( LDAP_DEBUG_TRACE,
324                                     "ldbm_back_modrdn: newSup(ndn=%s) not here!\n",
325                                     np_ndn->bv_val, 0, 0);
326 #endif
327
328                                 send_ldap_result( conn, op, LDAP_OTHER,
329                                         NULL, "newSuperior not found", NULL, NULL );
330                                 goto return_results;
331                         }
332
333 #ifdef NEW_LOGGING
334                         LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
335                                 "ldbm_back_modrdn: wr to new parent OK np=%p, id=%ld\n",
336                                 np, np->e_id ));
337 #else
338                         Debug( LDAP_DEBUG_TRACE,
339                                 "ldbm_back_modrdn: wr to new parent OK np=%p, id=%ld\n",
340                                 np, np->e_id, 0 );
341 #endif
342
343                         /* check newSuperior for "children" acl */
344                         if ( !access_allowed( be, conn, op, np, children, NULL,
345                                               ACL_WRITE ) )
346                         {
347 #ifdef NEW_LOGGING
348                                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
349                                            "ldbm_back_modrdn: no wr to newSup children.\n" ));
350 #else
351                                 Debug( LDAP_DEBUG_TRACE,
352                                        "ldbm_back_modrdn: no wr to newSup children\n",
353                                        0, 0, 0 );
354 #endif
355
356                                 send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
357                                         NULL, NULL, NULL, NULL );
358                                 goto return_results;
359                         }
360
361                         if ( is_entry_alias( np ) ) {
362                                 /* parent is an alias, don't allow add */
363 #ifdef NEW_LOGGING
364                                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
365                                            "ldbm_back_modrdn: entry (%s) is an alias.\n", np->e_dn ));
366 #else
367                                 Debug( LDAP_DEBUG_TRACE, "entry is alias\n", 0, 0, 0 );
368 #endif
369
370
371                                 send_ldap_result( conn, op, LDAP_ALIAS_PROBLEM,
372                                     NULL, "newSuperior is an alias", NULL, NULL );
373
374                                 goto return_results;
375                         }
376
377                         if ( is_entry_referral( np ) ) {
378                                 /* parent is a referral, don't allow add */
379 #ifdef NEW_LOGGING
380                                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
381                                         "ldbm_back_modrdn: entry (%s) is a referral\n",
382                                 np->e_dn ));
383 #else
384                                 Debug( LDAP_DEBUG_TRACE, "entry (%s) is referral\n",
385                                         np->e_dn, 0, 0 );
386 #endif
387
388                                 send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR,
389                                     NULL, "newSuperior is a referral", NULL, NULL );
390
391                                 goto return_results;
392                         }
393
394                 } else {
395
396                         /* no parent, must be root to modify newSuperior */
397                         if ( isroot == -1 ) {
398                                 isroot = be_isroot( be, &op->o_ndn );
399                         }
400
401                         if ( ! isroot ) {
402                                 if ( be_issuffix( be, (struct berval *)&slap_empty_bv ) || be_isupdate( be, &op->o_ndn ) ) {
403                                         np = (Entry *)&slap_entry_root;
404                                 
405                                         rc = access_allowed( be, conn, op, np,
406                                                         children, NULL, ACL_WRITE );
407                                         np = NULL;
408                                                                 
409                                         /* check parent for "children" acl */
410                                         if ( ! rc ) {
411 #ifdef NEW_LOGGING
412                                                 LDAP_LOG(( "backend", LDAP_LEVEL_ERR,
413                                                         "ldbm_back_modrdn: no access "
414                                                         "to new superior \"\"\n" ));
415 #else
416                                                 Debug( LDAP_DEBUG_TRACE,
417                                                         "<=- ldbm_back_modrdn: no "
418                                                         "access to new superior\n", 0, 0, 0 );
419 #endif
420
421                                                 send_ldap_result( conn, op, 
422                                                         LDAP_INSUFFICIENT_ACCESS,
423                                                         NULL, NULL, NULL, NULL );
424                                                 goto return_results;
425                                         }
426
427                                 } else {
428 #ifdef NEW_LOGGING
429                                         LDAP_LOG(( "backend", LDAP_LEVEL_ERR,
430                                                    "ldbm_back_modrdn: \"\" "
431                                                    "not allowed as new superior\n" ));
432 #else
433                                         Debug( LDAP_DEBUG_TRACE,
434                                                 "<=- ldbm_back_modrdn: \"\" "
435                                                 "not allowed as new superior\n", 
436                                                 0, 0, 0);
437 #endif
438
439                                         send_ldap_result( conn, op, 
440                                                 LDAP_INSUFFICIENT_ACCESS,
441                                                 NULL, NULL, NULL, NULL );
442                                         goto return_results;
443                                 }
444                         }
445                 }
446
447 #ifdef NEW_LOGGING
448                 LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
449                         "ldbm_back_modrdn: wr to new parent's children OK.\n" ));
450 #else
451                 Debug( LDAP_DEBUG_TRACE,
452                     "ldbm_back_modrdn: wr to new parent's children OK\n",
453                     0, 0, 0 );
454 #endif
455
456                 new_parent_dn = newSuperior;
457         }
458         
459         /* Build target dn and make sure target entry doesn't exist already. */
460         build_new_dn( &new_dn, new_parent_dn, newrdn ); 
461         dnNormalize2( NULL, &new_dn, &new_ndn );
462
463 #ifdef NEW_LOGGING
464         LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
465                 "ldbm_back_modrdn: new ndn=%s\n", new_ndn.bv_val ));
466 #else
467         Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: new ndn=%s\n",
468             new_ndn.bv_val, 0, 0 );
469 #endif
470
471         /* check for abandon */
472         ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
473         if ( op->o_abandon ) {
474                 ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
475                 goto return_results;
476         }
477
478         ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
479         if ( ( rc_id = dn2id ( be, &new_ndn, &id ) ) || id != NOID ) {
480                 /* if (rc_id) something bad happened to ldbm cache */
481                 send_ldap_result( conn, op, 
482                         rc_id ? LDAP_OPERATIONS_ERROR : LDAP_ALREADY_EXISTS,
483                         NULL, NULL, NULL, NULL );
484                 goto return_results;
485         }
486
487 #ifdef NEW_LOGGING
488         LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
489                 "ldbm_back_modrdn: new ndn (%s) does not exist\n", new_ndn.bv_val ));
490 #else
491         Debug( LDAP_DEBUG_TRACE,
492             "ldbm_back_modrdn: new ndn=%s does not exist\n",
493             new_ndn.bv_val, 0, 0 );
494 #endif
495
496
497         /* Get attribute types and values of our new rdn, we will
498          * need to add that to our new entry
499          */
500         if ( ldap_str2rdn( newrdn->bv_val, &new_rdn, (char **)&text,
501                 LDAP_DN_FORMAT_LDAP ) )
502         {
503 #ifdef NEW_LOGGING
504                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
505                         "ldbm_back_modrdn: can't figure out type(s)/value(s) of newrdn\n" ));
506 #else
507                 Debug( LDAP_DEBUG_TRACE,
508                     "ldbm_back_modrdn: can't figure out type(s)/value(s) of newrdn\n",
509                     0, 0, 0 );
510 #endif
511
512                 send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR,
513                         NULL, "unable to parse type(s)/value(s) used in RDN", NULL, NULL );
514                 goto return_results;            
515         }
516
517 #ifdef NEW_LOGGING
518         LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
519                    "ldbm_back_modrdn: new_rdn_type=\"%s\", new_rdn_val=\"%s\"\n",
520                    new_rdn[0][0]->la_attr.bv_val, new_rdn[0][0]->la_value.bv_val ));
521 #else
522         Debug( LDAP_DEBUG_TRACE,
523                "ldbm_back_modrdn: new_rdn_type=\"%s\", new_rdn_val=\"%s\"\n",
524                new_rdn[0][0]->la_attr.bv_val, new_rdn[0][0]->la_value.bv_val, 0 );
525 #endif
526
527         /* Retrieve the old rdn from the entry's dn */
528         if ( ldap_str2rdn( dn->bv_val, &old_rdn, (char **)&text,
529                 LDAP_DN_FORMAT_LDAP ) )
530         {
531 #ifdef NEW_LOGGING
532                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
533                            "ldbm_back_modrdn: can't figure out the old_rdn type(s)/value(s).\n" ));
534 #else
535                 Debug( LDAP_DEBUG_TRACE,
536                        "ldbm_back_modrdn: can't figure out the old_rdn type(s)/value(s)\n",
537                        0, 0, 0 );
538 #endif
539
540                 send_ldap_result( conn, op, LDAP_OTHER,
541                         NULL, "unable to parse type(s)/value(s) used in RDN from old DN", NULL, NULL );
542                 goto return_results;            
543         }
544
545 #if 0
546         if ( newSuperior == NULL
547                 && charray_strcasecmp( (const char **)old_rdn_types, (const char **)new_rdn_types ) != 0 )
548         {
549             /* Not a big deal but we may say something */
550 #ifdef NEW_LOGGING
551             LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
552                        "ldbm_back_modrdn: old_rdn_type=%s new_rdn_type=%s\n",
553                        old_rdn_types[0], new_rdn_types[0] ));
554 #else
555             Debug( LDAP_DEBUG_TRACE,
556                    "ldbm_back_modrdn: old_rdn_type=%s, new_rdn_type=%s!\n",
557                    old_rdn_types[0], new_rdn_types[0], 0 );
558 #endif
559         }               
560 #endif
561
562 #ifdef NEW_LOGGING
563         LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
564                    "ldbm_back_modrdn:  DN_X500\n" ));
565 #else
566         Debug( LDAP_DEBUG_TRACE, "ldbm_back_modrdn: DN_X500\n",
567                0, 0, 0 );
568 #endif
569
570         mod = NULL;
571         for ( a_cnt = 0; new_rdn[0][a_cnt]; a_cnt++ ) {
572                 int                     rc;
573                 AttributeDescription    *desc = NULL;
574                 Modifications           *mod_tmp;
575
576                 rc = slap_bv2ad( &new_rdn[0][a_cnt]->la_attr, &desc, &text );
577
578                 if ( rc != LDAP_SUCCESS ) {
579 #ifdef NEW_LOGGING
580                         LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
581                                    "ldbm_back_modrdn: slap_bv2ad error: %s (%s)\n",
582                                    text, new_rdn[0][a_cnt]->la_attr.bv_val ));
583 #else
584                         Debug( LDAP_DEBUG_TRACE,
585                                 "ldbm_back_modrdn: %s: %s (new)\n",
586                                 text, new_rdn[0][a_cnt]->la_attr.bv_val, 0 );
587 #endif
588
589                         send_ldap_result( conn, op, rc,
590                                 NULL, text, NULL, NULL );
591
592                         goto return_results;            
593                 }
594
595                 if ( ! access_allowed( be, conn, op, e, 
596                                 desc, &new_rdn[0][a_cnt]->la_value, ACL_WRITE ) ) {
597 #ifdef NEW_LOGGING
598                         LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
599                                    "ldbm_back_modrdn: access "
600                                    "not allowed to attr \"%s\"\n",
601                                    new_rdn[0][a_cnt]->la_attr.bv_val ));
602 #else
603                         Debug( LDAP_DEBUG_TRACE,
604                                 "ldbm_back_modrdn: access not allowed "
605                                 "to attr \"%s\"\n%s%s",
606                                 new_rdn[0][a_cnt]->la_attr.bv_val, "", "" );
607 #endif
608                         send_ldap_result( conn, op, 
609                                 LDAP_INSUFFICIENT_ACCESS,
610                                 NULL, NULL, NULL, NULL );
611
612                         goto return_results;
613                 }
614
615                 mod_tmp = (Modifications *)ch_malloc( sizeof( Modifications )
616                         + 2 * sizeof( struct berval ) );
617                 mod_tmp->sml_desc = desc;
618                 mod_tmp->sml_bvalues = (BerVarray)( mod_tmp + 1 );
619                 mod_tmp->sml_bvalues[0] = new_rdn[0][a_cnt]->la_value;
620                 mod_tmp->sml_bvalues[1].bv_val = NULL;
621                 mod_tmp->sml_op = SLAP_MOD_SOFTADD;
622                 mod_tmp->sml_next = mod;
623                 mod = mod_tmp;
624         }
625
626         /* Remove old rdn value if required */
627         if ( deleteoldrdn ) {
628                 /* Get value of old rdn */
629                 if ( old_rdn == NULL ) {
630 #ifdef NEW_LOGGING
631                         LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
632                                    "ldbm_back_modrdn: can't figure out old RDN value(s) from old RDN\n" ));
633 #else
634                         Debug( LDAP_DEBUG_TRACE,
635                                "ldbm_back_modrdn: can't figure out oldRDN value(s) from old RDN\n",
636                                0, 0, 0 );
637 #endif
638
639                         send_ldap_result( conn, op, LDAP_OTHER,
640                                 NULL, "could not parse value(s) from old RDN", NULL, NULL );
641                         goto return_results;            
642                 }
643
644                 for ( d_cnt = 0; old_rdn[0][d_cnt]; d_cnt++ ) {    
645                         int                     rc;
646                         AttributeDescription    *desc = NULL;
647                         Modifications           *mod_tmp;
648
649                         rc = slap_bv2ad( &old_rdn[0][d_cnt]->la_attr, &desc, &text );
650
651                         if ( rc != LDAP_SUCCESS ) {
652 #ifdef NEW_LOGGING
653                                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
654                                            "ldbm_back_modrdn: %s: %s (old)\n",
655                                            text, old_rdn[0][d_cnt]->la_attr.bv_val ));
656 #else
657                                 Debug( LDAP_DEBUG_TRACE,
658                                         "ldbm_back_modrdn: %s: %s (old)\n",
659                                         text, old_rdn[0][d_cnt]->la_attr.bv_val, 0 );
660 #endif
661
662                                 send_ldap_result( conn, op, rc,
663                                         NULL, text, NULL, NULL );
664
665                                 goto return_results;
666                         }
667
668                         if ( ! access_allowed( be, conn, op, e, 
669                                         desc, &old_rdn[0][d_cnt]->la_value, ACL_WRITE ) ) {
670 #ifdef NEW_LOGGING
671                                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
672                                            "ldbm_back_modrdn: access "
673                                            "not allowed to attr \"%s\"\n",
674                                            old_rdn[0][d_cnt]->la_attr.bv_val ));
675 #else
676                                 Debug( LDAP_DEBUG_TRACE,
677                                         "ldbm_back_modrdn: access not allowed "
678                                         "to attr \"%s\"\n%s%s",
679                                         old_rdn[0][d_cnt]->la_attr.bv_val, "", "" );
680 #endif
681                                 send_ldap_result( conn, op, 
682                                         LDAP_INSUFFICIENT_ACCESS,
683                                         NULL, NULL, NULL, NULL );
684
685                                 goto return_results;
686                         }
687
688                         /* Remove old value of rdn as an attribute. */
689                         mod_tmp = (Modifications *)ch_malloc( sizeof( Modifications )
690                                 + 2 * sizeof( struct berval ) );
691                         mod_tmp->sml_desc = desc;
692                         mod_tmp->sml_bvalues = (BerVarray)(mod_tmp+1);
693                         mod_tmp->sml_bvalues[0] = old_rdn[0][d_cnt]->la_value;
694                         mod_tmp->sml_bvalues[1].bv_val = NULL;
695                         mod_tmp->sml_op = LDAP_MOD_DELETE;
696                         mod_tmp->sml_next = mod;
697                         mod = mod_tmp;
698
699 #ifdef NEW_LOGGING
700                         LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
701                                    "ldbm_back_modrdn: removing old_rdn_val=%s\n", old_rdn[0][d_cnt]->la_value.bv_val ));
702 #else
703                         Debug( LDAP_DEBUG_TRACE,
704                                "ldbm_back_modrdn: removing old_rdn_val=%s\n",
705                                old_rdn[0][d_cnt]->la_value.bv_val, 0, 0 );
706 #endif
707                 }
708         }
709
710         
711         /* check for abandon */
712         ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
713         if ( op->o_abandon ) {
714                 ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
715                 goto return_results;
716         }
717         ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
718
719         /* delete old one */
720         if ( dn2id_delete( be, &e->e_nname, e->e_id ) != 0 ) {
721                 send_ldap_result( conn, op, LDAP_OTHER,
722                         NULL, "DN index delete fail", NULL, NULL );
723                 goto return_results;
724         }
725
726         (void) cache_delete_entry( &li->li_cache, e );
727         rc = MUST_DESTROY;
728
729         /* XXX: there is no going back! */
730
731         free( e->e_dn );
732         free( e->e_ndn );
733         e->e_name = new_dn;
734         e->e_nname = new_ndn;
735         new_dn.bv_val = NULL;
736         new_ndn.bv_val = NULL;
737
738         /* add new one */
739         if ( dn2id_add( be, &e->e_nname, e->e_id ) != 0 ) {
740                 send_ldap_result( conn, op, LDAP_OTHER,
741                         NULL, "DN index add failed", NULL, NULL );
742                 goto return_results;
743         }
744
745         /* modify memory copy of entry */
746         rc = ldbm_modify_internal( be, conn, op, dn->bv_val, &mod[0], e,
747                 &text, textbuf, textlen );
748
749         if( rc != LDAP_SUCCESS ) {
750                 if( rc != SLAPD_ABANDON ) {
751                         send_ldap_result( conn, op, rc,
752                                 NULL, text, NULL, NULL );
753                 }
754
755                 /* here we may try to delete the newly added dn */
756                 if ( dn2id_delete( be, &e->e_nname, e->e_id ) != 0 ) {
757                         /* we already are in trouble ... */
758                         ;
759                 }
760             
761                 goto return_results;
762         }
763         
764         (void) cache_update_entry( &li->li_cache, e );
765
766         /* NOTE: after this you must not free new_dn or new_ndn!
767          * They are used by cache.
768          */
769
770         /* id2entry index */
771         if ( id2entry_add( be, e ) != 0 ) {
772                 send_ldap_result( conn, op, LDAP_OTHER,
773                         NULL, "entry update failed", NULL, NULL );
774                 goto return_results;
775         }
776
777         send_ldap_result( conn, op, LDAP_SUCCESS,
778                 NULL, NULL, NULL, NULL );
779         rc = 0;
780         cache_entry_commit( e );
781
782 return_results:
783         if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
784         if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );
785
786         /* LDAP v2 supporting correct attribute handling. */
787         if( new_rdn ) ldap_rdnfree( new_rdn );
788         if( old_rdn ) ldap_rdnfree( old_rdn );
789
790         if ( mod != NULL ) {
791                 Modifications *tmp;
792                 for (; mod; mod = tmp ) {
793                         tmp = mod->sml_next;
794                         free( mod );
795                 }
796         }
797
798         /* LDAP v3 Support */
799         if( np != NULL ) {
800                 /* free new parent and writer lock */
801                 cache_return_entry_w( &li->li_cache, np );
802         }
803
804         if( p != NULL ) {
805                 /* free parent and writer lock */
806                 cache_return_entry_w( &li->li_cache, p );
807         }
808
809         /* free entry and writer lock */
810         cache_return_entry_w( &li->li_cache, e );
811         if ( rc == MUST_DESTROY ) {
812                 /* if rc == MUST_DESTROY the entry is uncached 
813                  * and its private data is destroyed; 
814                  * the entry must be freed */
815                 entry_free( e );
816         }
817         ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);
818         return( rc );
819 }