]> git.sur5r.net Git - openldap/blob - servers/slapd/back-mdb/modrdn.c
496f0c7d4b2b16f615442f398b2acd06d8b038b8
[openldap] / servers / slapd / back-mdb / modrdn.c
1 /* modrdn.c - mdb backend modrdn routine */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2011 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
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>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20 #include <ac/string.h>
21
22 #include "back-mdb.h"
23
24 int
25 mdb_modrdn( Operation   *op, SlapReply *rs )
26 {
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};
32         Entry           *e = NULL;
33         Entry           *p = NULL;
34         /* LDAP v2 supporting correct attribute handling. */
35         char textbuf[SLAP_TEXT_BUFLEN];
36         size_t textlen = sizeof textbuf;
37         MDB_txn         *txn = NULL;
38         struct mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo;
39         Entry dummy = {0};
40
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 */
45
46         int             manageDSAit = get_manageDSAit( op );
47
48         ID nid;
49         LDAPControl **preread_ctrl = NULL;
50         LDAPControl **postread_ctrl = NULL;
51         LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
52         int num_ctrls = 0;
53
54         int parent_is_glue = 0;
55         int parent_is_leaf = 0;
56
57 #ifdef LDAP_X_TXN
58         int settle = 0;
59 #endif
60
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" );
64
65 #ifdef LDAP_X_TXN
66         if( op->o_txnSpec ) {
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;
72                         goto txnReturn;
73                 } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
74                         settle=1;
75                         goto txnReturn;
76                 }
77
78                 if( op->o_conn->c_txn_backend == NULL ) {
79                         op->o_conn->c_txn_backend = op->o_bd;
80
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;
84                         goto txnReturn;
85                 }
86
87                 /* insert operation into transaction */
88
89                 rs->sr_text = "transaction specified";
90                 rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
91
92 txnReturn:
93                 /* release connection lock */
94                 ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
95
96                 if( !settle ) {
97                         send_ldap_result( op, rs );
98                         return rs->sr_err;
99                 }
100         }
101 #endif
102
103         ctrls[num_ctrls] = NULL;
104
105         slap_mods_opattrs( op, &op->orr_modlist, 1 );
106
107         /* begin transaction */
108         rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi );
109         rs->sr_text = NULL;
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";
116                 goto return_results;
117         }
118
119         txn = moi->moi_txn;
120
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;
125 #else
126                 /* There can only be one suffix entry */
127                 rs->sr_err = LDAP_NAMING_VIOLATION;
128                 rs->sr_text = "cannot rename suffix entry";
129                 goto return_results;
130 #endif
131         } else {
132                 dnParent( &op->o_req_ndn, &p_ndn );
133         }
134         np_ndn = &p_ndn;
135         /* Make sure parent entry exist and we can write its
136          * children.
137          */
138         rs->sr_err = mdb_dn2entry( op, txn, &p_ndn, &p, 0 );
139         switch( rs->sr_err ) {
140         case MDB_NOTFOUND:
141                 Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_modrdn)
142                         ": parent does not exist\n", 0, 0, 0);
143                 rs->sr_ref = referral_rewrite( default_referral, NULL,
144                                         &op->o_req_dn, LDAP_SCOPE_DEFAULT );
145                 rs->sr_err = LDAP_REFERRAL;
146
147                 send_ldap_result( op, rs );
148
149                 ber_bvarray_free( rs->sr_ref );
150                 goto done;
151         case 0:
152                 break;
153         case LDAP_BUSY:
154                 rs->sr_text = "ldap server busy";
155                 goto return_results;
156         default:
157                 rs->sr_err = LDAP_OTHER;
158                 rs->sr_text = "internal error";
159                 goto return_results;
160         }
161
162         /* check parent for "children" acl */
163         rs->sr_err = access_allowed( op, p,
164                 children, NULL,
165                 op->oq_modrdn.rs_newSup == NULL ?
166                         ACL_WRITE : ACL_WDEL,
167                 NULL );
168
169         if ( ! rs->sr_err ) {
170                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
171                 Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
172                         0, 0 );
173                 rs->sr_text = "no write access to parent's children";
174                 goto return_results;
175         }
176
177         Debug( LDAP_DEBUG_TRACE,
178                 LDAP_XSTRING(mdb_modrdn) ": wr to children "
179                 "of entry %s OK\n", p_ndn.bv_val, 0, 0 );
180
181         if ( p_ndn.bv_val == slap_empty_bv.bv_val ) {
182                 p_dn = slap_empty_bv;
183         } else {
184                 dnParent( &op->o_req_dn, &p_dn );
185         }
186
187         Debug( LDAP_DEBUG_TRACE,
188                 LDAP_XSTRING(mdb_modrdn) ": parent dn=%s\n",
189                 p_dn.bv_val, 0, 0 );
190
191         /* get entry */
192         rs->sr_err = mdb_dn2entry( op, txn, &op->o_req_ndn, &e, 0 );
193         switch( rs->sr_err ) {
194         case MDB_NOTFOUND:
195                 e = p;
196                 p = NULL;
197         case 0:
198                 break;
199         case LDAP_BUSY:
200                 rs->sr_text = "ldap server busy";
201                 goto return_results;
202         default:
203                 rs->sr_err = LDAP_OTHER;
204                 rs->sr_text = "internal error";
205                 goto return_results;
206         }
207
208         /* FIXME: dn2entry() should return non-glue entry */
209         if (( rs->sr_err == MDB_NOTFOUND ) ||
210                 ( !manageDSAit && e && is_entry_glue( e )))
211         {
212                 if( e != NULL ) {
213                         rs->sr_matched = ch_strdup( e->e_dn );
214                         if ( is_entry_referral( e )) {
215                                 BerVarray ref = get_entry_referrals( op, e );
216                                 rs->sr_ref = referral_rewrite( ref, &e->e_name,
217                                         &op->o_req_dn, LDAP_SCOPE_DEFAULT );
218                                 ber_bvarray_free( ref );
219                         } else {
220                                 rs->sr_ref = NULL;
221                         }
222                         mdb_entry_return( e );
223                         e = NULL;
224
225                 } else {
226                         rs->sr_ref = referral_rewrite( default_referral, NULL,
227                                         &op->o_req_dn, LDAP_SCOPE_DEFAULT );
228                 }
229
230                 rs->sr_err = LDAP_REFERRAL;
231                 send_ldap_result( op, rs );
232
233                 ber_bvarray_free( rs->sr_ref );
234                 free( (char *)rs->sr_matched );
235                 rs->sr_ref = NULL;
236                 rs->sr_matched = NULL;
237
238                 goto done;
239         }
240
241         if ( get_assert( op ) &&
242                 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
243         {
244                 rs->sr_err = LDAP_ASSERTION_FAILED;
245                 goto return_results;
246         }
247
248         /* check write on old entry */
249         rs->sr_err = access_allowed( op, e, entry, NULL, ACL_WRITE, NULL );
250         if ( ! rs->sr_err ) {
251                 Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0,
252                         0, 0 );
253                 rs->sr_text = "no write access to old entry";
254                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
255                 goto return_results;
256         }
257
258         if (!manageDSAit && is_entry_referral( e ) ) {
259                 /* entry is a referral, don't allow rename */
260                 rs->sr_ref = get_entry_referrals( op, e );
261
262                 Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_modrdn)
263                         ": entry %s is referral\n", e->e_dn, 0, 0 );
264
265                 rs->sr_err = LDAP_REFERRAL,
266                 rs->sr_matched = e->e_name.bv_val;
267                 send_ldap_result( op, rs );
268
269                 ber_bvarray_free( rs->sr_ref );
270                 rs->sr_ref = NULL;
271                 rs->sr_matched = NULL;
272                 goto done;
273         }
274
275         new_parent_dn = &p_dn;  /* New Parent unless newSuperior given */
276
277         if ( op->oq_modrdn.rs_newSup != NULL ) {
278                 Debug( LDAP_DEBUG_TRACE,
279                         LDAP_XSTRING(mdb_modrdn)
280                         ": new parent \"%s\" requested...\n",
281                         op->oq_modrdn.rs_newSup->bv_val, 0, 0 );
282
283                 /*  newSuperior == oldParent? */
284                 if( dn_match( &p_ndn, op->oq_modrdn.rs_nnewSup ) ) {
285                         Debug( LDAP_DEBUG_TRACE, "mdb_back_modrdn: "
286                                 "new parent \"%s\" same as the old parent \"%s\"\n",
287                                 op->oq_modrdn.rs_newSup->bv_val, p_dn.bv_val, 0 );
288                         op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
289                 }
290         }
291
292         /* There's a MDB_MULTIPLE_SUFFIXES case here that this code doesn't
293          * support. E.g., two suffixes dc=foo,dc=com and dc=bar,dc=net.
294          * We do not allow modDN
295          *   dc=foo,dc=com
296          *    newrdn dc=bar
297          *    newsup dc=net
298          * and we probably should. But since MULTIPLE_SUFFIXES is deprecated
299          * I'm ignoring this problem for now.
300          */
301         if ( op->oq_modrdn.rs_newSup != NULL ) {
302                 if ( op->oq_modrdn.rs_newSup->bv_len ) {
303                         np_dn = op->oq_modrdn.rs_newSup;
304                         np_ndn = op->oq_modrdn.rs_nnewSup;
305
306                         /* newSuperior == oldParent? - checked above */
307                         /* newSuperior == entry being moved?, if so ==> ERROR */
308                         if ( dnIsSuffix( np_ndn, &e->e_nname )) {
309                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
310                                 rs->sr_text = "new superior not found";
311                                 goto return_results;
312                         }
313                         /* Get Entry with dn=newSuperior. Does newSuperior exist? */
314
315                         rs->sr_err = mdb_dn2entry( op, txn, np_ndn, &np, 0 );
316
317                         switch( rs->sr_err ) {
318                         case 0:
319                                 break;
320                         case MDB_NOTFOUND:
321                                 Debug( LDAP_DEBUG_TRACE,
322                                         LDAP_XSTRING(mdb_modrdn)
323                                         ": newSup(ndn=%s) not here!\n",
324                                         np_ndn->bv_val, 0, 0);
325                                 rs->sr_text = "new superior not found";
326                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
327                                 goto return_results;
328                         case LDAP_BUSY:
329                                 rs->sr_text = "ldap server busy";
330                                 goto return_results;
331                         default:
332                                 rs->sr_err = LDAP_OTHER;
333                                 rs->sr_text = "internal error";
334                                 goto return_results;
335                         }
336
337                         /* check newSuperior for "children" acl */
338                         rs->sr_err = access_allowed( op, np, children,
339                                 NULL, ACL_WADD, NULL );
340
341                         if( ! rs->sr_err ) {
342                                 Debug( LDAP_DEBUG_TRACE,
343                                         LDAP_XSTRING(mdb_modrdn)
344                                         ": no wr to newSup children\n",
345                                         0, 0, 0 );
346                                 rs->sr_text = "no write access to new superior's children";
347                                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
348                                 goto return_results;
349                         }
350
351                         Debug( LDAP_DEBUG_TRACE,
352                                 LDAP_XSTRING(mdb_modrdn)
353                                 ": wr to new parent OK np=%p, id=%ld\n",
354                                 (void *) np, (long) np->e_id, 0 );
355
356                         if ( is_entry_alias( np ) ) {
357                                 /* parent is an alias, don't allow add */
358                                 Debug( LDAP_DEBUG_TRACE,
359                                         LDAP_XSTRING(mdb_modrdn)
360                                         ": entry is alias\n",
361                                         0, 0, 0 );
362                                 rs->sr_text = "new superior is an alias";
363                                 rs->sr_err = LDAP_ALIAS_PROBLEM;
364                                 goto return_results;
365                         }
366
367                         if ( is_entry_referral( np ) ) {
368                                 /* parent is a referral, don't allow add */
369                                 Debug( LDAP_DEBUG_TRACE,
370                                         LDAP_XSTRING(mdb_modrdn)
371                                         ": entry is referral\n",
372                                         0, 0, 0 );
373                                 rs->sr_text = "new superior is a referral";
374                                 rs->sr_err = LDAP_OTHER;
375                                 goto return_results;
376                         }
377                         new_parent_dn = &np->e_name;
378
379                 } else {
380                         np_dn = NULL;
381
382                         /* no parent, modrdn entry directly under root */
383                         if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
384                                 || be_isupdate( op ) ) {
385                                 np = (Entry *)&slap_entry_root;
386
387                                 /* check parent for "children" acl */
388                                 rs->sr_err = access_allowed( op, np,
389                                         children, NULL, ACL_WADD, NULL );
390
391                                 np = NULL;
392
393                                 if ( ! rs->sr_err ) {
394                                         rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
395                                         Debug( LDAP_DEBUG_TRACE,
396                                                 "no access to new superior\n",
397                                                 0, 0, 0 );
398                                         rs->sr_text =
399                                                 "no write access to new superior's children";
400                                         goto return_results;
401                                 }
402                         }
403                 }
404
405                 Debug( LDAP_DEBUG_TRACE,
406                         LDAP_XSTRING(mdb_modrdn)
407                         ": wr to new parent's children OK\n",
408                         0, 0, 0 );
409
410                 new_parent_dn = np_dn;
411         }
412
413         /* Build target dn and make sure target entry doesn't exist already. */
414         if (!new_dn.bv_val) {
415                 build_new_dn( &new_dn, new_parent_dn, &op->oq_modrdn.rs_newrdn, NULL );
416         }
417
418         if (!new_ndn.bv_val) {
419                 struct berval bv = {0, NULL};
420                 dnNormalize( 0, NULL, NULL, &new_dn, &bv, op->o_tmpmemctx );
421                 ber_dupbv( &new_ndn, &bv );
422                 /* FIXME: why not call dnNormalize() w/o ctx? */
423                 op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
424         }
425
426         Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_modrdn) ": new ndn=%s\n",
427                 new_ndn.bv_val, 0, 0 );
428
429         /* Shortcut the search */
430         rs->sr_err = mdb_dn2id ( op, txn, &new_ndn, &nid, NULL, NULL );
431         switch( rs->sr_err ) {
432         case MDB_NOTFOUND:
433                 break;
434         case 0:
435                 /* Allow rename to same DN */
436                 if ( nid == e->e_id )
437                         break;
438                 rs->sr_err = LDAP_ALREADY_EXISTS;
439                 goto return_results;
440         default:
441                 rs->sr_err = LDAP_OTHER;
442                 rs->sr_text = "internal error";
443                 goto return_results;
444         }
445
446         assert( op->orr_modlist != NULL );
447
448         if( op->o_preread ) {
449                 if( preread_ctrl == NULL ) {
450                         preread_ctrl = &ctrls[num_ctrls++];
451                         ctrls[num_ctrls] = NULL;
452                 }
453                 if( slap_read_controls( op, rs, e,
454                         &slap_pre_read_bv, preread_ctrl ) )
455                 {
456                         Debug( LDAP_DEBUG_TRACE,
457                                 "<=- " LDAP_XSTRING(mdb_modrdn)
458                                 ": pre-read failed!\n", 0, 0, 0 );
459                         if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
460                                 /* FIXME: is it correct to abort
461                                  * operation if control fails? */
462                                 goto return_results;
463                         }
464                 }
465         }
466
467         /* delete old DN */
468         rs->sr_err = mdb_dn2id_delete( op, txn, p->e_id, e );
469         if ( rs->sr_err != 0 ) {
470                 Debug(LDAP_DEBUG_TRACE,
471                         "<=- " LDAP_XSTRING(mdb_modrdn)
472                         ": dn2id del failed: %s (%d)\n",
473                         mdb_strerror(rs->sr_err), rs->sr_err, 0 );
474                 rs->sr_err = LDAP_OTHER;
475                 rs->sr_text = "DN index delete fail";
476                 goto return_results;
477         }
478
479         /* copy the entry, then override some fields */
480         dummy = *e;
481         dummy.e_name = new_dn;
482         dummy.e_nname = new_ndn;
483         dummy.e_attrs = NULL;
484
485         /* add new DN */
486         rs->sr_err = mdb_dn2id_add( op, txn, np ? np->e_id : p->e_id, &dummy );
487         if ( rs->sr_err != 0 ) {
488                 Debug(LDAP_DEBUG_TRACE,
489                         "<=- " LDAP_XSTRING(mdb_modrdn)
490                         ": dn2id add failed: %s (%d)\n",
491                         mdb_strerror(rs->sr_err), rs->sr_err, 0 );
492                 rs->sr_err = LDAP_OTHER;
493                 rs->sr_text = "DN index add failed";
494                 goto return_results;
495         }
496
497         dummy.e_attrs = e->e_attrs;
498
499         /* modify entry */
500         rs->sr_err = mdb_modify_internal( op, txn, op->orr_modlist, &dummy,
501                 &rs->sr_text, textbuf, textlen );
502         if( rs->sr_err != LDAP_SUCCESS ) {
503                 Debug(LDAP_DEBUG_TRACE,
504                         "<=- " LDAP_XSTRING(mdb_modrdn)
505                         ": modify failed: %s (%d)\n",
506                         mdb_strerror(rs->sr_err), rs->sr_err, 0 );
507                 if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
508                 goto return_results;
509         }
510
511         /* id2entry index */
512         rs->sr_err = mdb_id2entry_update( op, txn, &dummy );
513         if ( rs->sr_err != 0 ) {
514                 Debug(LDAP_DEBUG_TRACE,
515                         "<=- " LDAP_XSTRING(mdb_modrdn)
516                         ": id2entry failed: %s (%d)\n",
517                         mdb_strerror(rs->sr_err), rs->sr_err, 0 );
518                 rs->sr_err = LDAP_OTHER;
519                 rs->sr_text = "entry update failed";
520                 goto return_results;
521         }
522
523         if ( p_ndn.bv_len != 0 ) {
524                 parent_is_glue = is_entry_glue(p);
525                 rs->sr_err = mdb_dn2id_children( op, txn, p );
526                 if ( rs->sr_err != MDB_NOTFOUND ) {
527                         switch( rs->sr_err ) {
528                         case 0:
529                                 break;
530                         default:
531                                 Debug(LDAP_DEBUG_ARGS,
532                                         "<=- " LDAP_XSTRING(mdb_modrdn)
533                                         ": has_children failed: %s (%d)\n",
534                                         mdb_strerror(rs->sr_err), rs->sr_err, 0 );
535                                 rs->sr_err = LDAP_OTHER;
536                                 rs->sr_text = "internal error";
537                                 goto return_results;
538                         }
539                         parent_is_leaf = 1;
540                 }
541                 mdb_entry_return( p );
542                 p = NULL;
543         }
544
545         if( op->o_postread ) {
546                 if( postread_ctrl == NULL ) {
547                         postread_ctrl = &ctrls[num_ctrls++];
548                         ctrls[num_ctrls] = NULL;
549                 }
550                 if( slap_read_controls( op, rs, &dummy,
551                         &slap_post_read_bv, postread_ctrl ) )
552                 {
553                         Debug( LDAP_DEBUG_TRACE,
554                                 "<=- " LDAP_XSTRING(mdb_modrdn)
555                                 ": post-read failed!\n", 0, 0, 0 );
556                         if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
557                                 /* FIXME: is it correct to abort
558                                  * operation if control fails? */
559                                 goto return_results;
560                         }
561                 }
562         }
563
564         if( moi == &opinfo ) {
565                 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
566                 opinfo.moi_oe.oe_key = NULL;
567                 if( op->o_noop ) {
568                         mdb_txn_abort( txn );
569                         rs->sr_err = LDAP_X_NO_OPERATION;
570                         txn = NULL;
571                         /* Only free attrs if they were dup'd.  */
572                         if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
573                         goto return_results;
574
575                 } else {
576                         dummy.e_attrs = NULL;
577                         new_dn.bv_val = NULL;
578                         new_ndn.bv_val = NULL;
579
580                         if(( rs->sr_err=mdb_txn_commit( txn )) != 0 ) {
581                                 rs->sr_text = "txn_commit failed";
582                         } else {
583                                 rs->sr_err = LDAP_SUCCESS;
584                         }
585                         txn = NULL;
586                 }
587         }
588
589         if( rs->sr_err != LDAP_SUCCESS ) {
590                 Debug( LDAP_DEBUG_TRACE,
591                         LDAP_XSTRING(mdb_modrdn) ": %s : %s (%d)\n",
592                         rs->sr_text, mdb_strerror(rs->sr_err), rs->sr_err );
593                 rs->sr_err = LDAP_OTHER;
594
595                 goto return_results;
596         }
597
598         Debug(LDAP_DEBUG_TRACE,
599                 LDAP_XSTRING(mdb_modrdn)
600                 ": rdn modified%s id=%08lx dn=\"%s\"\n",
601                 op->o_noop ? " (no-op)" : "",
602                 dummy.e_id, op->o_req_dn.bv_val );
603         rs->sr_text = NULL;
604         if( num_ctrls ) rs->sr_ctrls = ctrls;
605
606 return_results:
607         if ( dummy.e_attrs ) {
608                 attrs_free( dummy.e_attrs );
609         }
610         send_ldap_result( op, rs );
611
612 #if 0
613         if( rs->sr_err == LDAP_SUCCESS && mdb->bi_txn_cp_kbyte ) {
614                 TXN_CHECKPOINT( mdb->bi_dbenv,
615                         mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 );
616         }
617 #endif
618
619         if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
620                 op->o_delete_glue_parent = 1;
621         }
622
623 done:
624         slap_graduate_commit_csn( op );
625
626         if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
627         if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );
628
629         /* LDAP v3 Support */
630         if( np != NULL ) {
631                 /* free new parent */
632                 mdb_entry_return( np );
633         }
634
635         if( p != NULL ) {
636                 /* free parent */
637                 mdb_entry_return( p );
638         }
639
640         /* free entry */
641         if( e != NULL ) {
642                 mdb_entry_return( e );
643         }
644
645         if( moi == &opinfo ) {
646                 if( txn != NULL ) {
647                         mdb_txn_abort( txn );
648                 }
649                 if ( opinfo.moi_oe.oe_key ) {
650                         LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
651                 }
652         }
653
654         if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
655                 slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
656                 slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
657         }
658         if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
659                 slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
660                 slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
661         }
662         return rs->sr_err;
663 }