1/*	$NetBSD$	*/
2
3/* modrdn.c - bdb backend modrdn routine */
4/* OpenLDAP: pkg/ldap/servers/slapd/back-bdb/modrdn.c,v 1.185.2.15 2010/04/13 20:23:25 kurt Exp */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2000-2010 The OpenLDAP Foundation.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18
19#include "portable.h"
20
21#include <stdio.h>
22#include <ac/string.h>
23
24#include "back-bdb.h"
25
26int
27bdb_modrdn( Operation	*op, SlapReply *rs )
28{
29	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
30	AttributeDescription *children = slap_schema.si_ad_children;
31	AttributeDescription *entry = slap_schema.si_ad_entry;
32	struct berval	p_dn, p_ndn;
33	struct berval	new_dn = {0, NULL}, new_ndn = {0, NULL};
34	Entry		*e = NULL;
35	Entry		*p = NULL;
36	EntryInfo	*ei = NULL, *eip = NULL, *nei = NULL, *neip = NULL;
37	/* LDAP v2 supporting correct attribute handling. */
38	char textbuf[SLAP_TEXT_BUFLEN];
39	size_t textlen = sizeof textbuf;
40	DB_TXN		*ltid = NULL, *lt2;
41	struct bdb_op_info opinfo = {{{ 0 }}};
42	Entry dummy = {0};
43
44	Entry		*np = NULL;			/* newSuperior Entry */
45	struct berval	*np_dn = NULL;			/* newSuperior dn */
46	struct berval	*np_ndn = NULL;			/* newSuperior ndn */
47	struct berval	*new_parent_dn = NULL;	/* np_dn, p_dn, or NULL */
48
49	int		manageDSAit = get_manageDSAit( op );
50
51	DB_LOCK		lock, plock, nplock;
52
53	int		num_retries = 0;
54
55	LDAPControl **preread_ctrl = NULL;
56	LDAPControl **postread_ctrl = NULL;
57	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
58	int num_ctrls = 0;
59
60	int	rc;
61
62	int parent_is_glue = 0;
63	int parent_is_leaf = 0;
64
65#ifdef LDAP_X_TXN
66	int settle = 0;
67#endif
68
69	Debug( LDAP_DEBUG_TRACE, "==>" LDAP_XSTRING(bdb_modrdn) "(%s,%s,%s)\n",
70		op->o_req_dn.bv_val,op->oq_modrdn.rs_newrdn.bv_val,
71		op->oq_modrdn.rs_newSup ? op->oq_modrdn.rs_newSup->bv_val : "NULL" );
72
73#ifdef LDAP_X_TXN
74	if( op->o_txnSpec ) {
75		/* acquire connection lock */
76		ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
77		if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
78			rs->sr_text = "invalid transaction identifier";
79			rs->sr_err = LDAP_X_TXN_ID_INVALID;
80			goto txnReturn;
81		} else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
82			settle=1;
83			goto txnReturn;
84		}
85
86		if( op->o_conn->c_txn_backend == NULL ) {
87			op->o_conn->c_txn_backend = op->o_bd;
88
89		} else if( op->o_conn->c_txn_backend != op->o_bd ) {
90			rs->sr_text = "transaction cannot span multiple database contexts";
91			rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
92			goto txnReturn;
93		}
94
95		/* insert operation into transaction */
96
97		rs->sr_text = "transaction specified";
98		rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
99
100txnReturn:
101		/* release connection lock */
102		ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
103
104		if( !settle ) {
105			send_ldap_result( op, rs );
106			return rs->sr_err;
107		}
108	}
109#endif
110
111	ctrls[num_ctrls] = NULL;
112
113	slap_mods_opattrs( op, &op->orr_modlist, 1 );
114
115	if( 0 ) {
116retry:	/* transaction retry */
117		if ( dummy.e_attrs ) {
118			attrs_free( dummy.e_attrs );
119			dummy.e_attrs = NULL;
120		}
121		if (e != NULL) {
122			bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
123			e = NULL;
124		}
125		if (p != NULL) {
126			bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
127			p = NULL;
128		}
129		if (np != NULL) {
130			bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, np);
131			np = NULL;
132		}
133		Debug( LDAP_DEBUG_TRACE, "==>" LDAP_XSTRING(bdb_modrdn)
134				": retrying...\n", 0, 0, 0 );
135
136		rs->sr_err = TXN_ABORT( ltid );
137		ltid = NULL;
138		LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
139		opinfo.boi_oe.oe_key = NULL;
140		op->o_do_not_cache = opinfo.boi_acl_cache;
141		if( rs->sr_err != 0 ) {
142			rs->sr_err = LDAP_OTHER;
143			rs->sr_text = "internal error";
144			goto return_results;
145		}
146		if ( op->o_abandon ) {
147			rs->sr_err = SLAPD_ABANDON;
148			goto return_results;
149		}
150		parent_is_glue = 0;
151		parent_is_leaf = 0;
152		bdb_trans_backoff( ++num_retries );
153	}
154
155	/* begin transaction */
156	rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid,
157		bdb->bi_db_opflags );
158	rs->sr_text = NULL;
159	if( rs->sr_err != 0 ) {
160		Debug( LDAP_DEBUG_TRACE,
161			LDAP_XSTRING(bdb_modrdn) ": txn_begin failed: "
162			"%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
163		rs->sr_err = LDAP_OTHER;
164		rs->sr_text = "internal error";
165		goto return_results;
166	}
167
168	opinfo.boi_oe.oe_key = bdb;
169	opinfo.boi_txn = ltid;
170	opinfo.boi_err = 0;
171	opinfo.boi_acl_cache = op->o_do_not_cache;
172	LDAP_SLIST_INSERT_HEAD( &op->o_extra, &opinfo.boi_oe, oe_next );
173
174	/* get entry */
175	rs->sr_err = bdb_dn2entry( op, ltid, &op->o_req_ndn, &ei, 1,
176		&lock );
177
178	switch( rs->sr_err ) {
179	case 0:
180	case DB_NOTFOUND:
181		break;
182	case DB_LOCK_DEADLOCK:
183	case DB_LOCK_NOTGRANTED:
184		goto retry;
185	case LDAP_BUSY:
186		rs->sr_text = "ldap server busy";
187		goto return_results;
188	default:
189		rs->sr_err = LDAP_OTHER;
190		rs->sr_text = "internal error";
191		goto return_results;
192	}
193
194	e = ei->bei_e;
195	/* FIXME: dn2entry() should return non-glue entry */
196	if (( rs->sr_err == DB_NOTFOUND ) ||
197		( !manageDSAit && e && is_entry_glue( e )))
198	{
199		if( e != NULL ) {
200			rs->sr_matched = ch_strdup( e->e_dn );
201			rs->sr_ref = is_entry_referral( e )
202				? get_entry_referrals( op, e )
203				: NULL;
204			bdb_unlocked_cache_return_entry_r( &bdb->bi_cache, e);
205			e = NULL;
206
207		} else {
208			rs->sr_ref = referral_rewrite( default_referral, NULL,
209					&op->o_req_dn, LDAP_SCOPE_DEFAULT );
210		}
211
212		rs->sr_err = LDAP_REFERRAL;
213		send_ldap_result( op, rs );
214
215		ber_bvarray_free( rs->sr_ref );
216		free( (char *)rs->sr_matched );
217		rs->sr_ref = NULL;
218		rs->sr_matched = NULL;
219
220		goto done;
221	}
222
223	if ( get_assert( op ) &&
224		( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
225	{
226		rs->sr_err = LDAP_ASSERTION_FAILED;
227		goto return_results;
228	}
229
230	/* check write on old entry */
231	rs->sr_err = access_allowed( op, e, entry, NULL, ACL_WRITE, NULL );
232	if ( ! rs->sr_err ) {
233		switch( opinfo.boi_err ) {
234		case DB_LOCK_DEADLOCK:
235		case DB_LOCK_NOTGRANTED:
236			goto retry;
237		}
238
239		Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0,
240			0, 0 );
241		rs->sr_text = "no write access to old entry";
242		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
243		goto return_results;
244	}
245
246#ifndef BDB_HIER
247	rs->sr_err = bdb_cache_children( op, ltid, e );
248	if ( rs->sr_err != DB_NOTFOUND ) {
249		switch( rs->sr_err ) {
250		case DB_LOCK_DEADLOCK:
251		case DB_LOCK_NOTGRANTED:
252			goto retry;
253		case 0:
254			Debug(LDAP_DEBUG_ARGS,
255				"<=- " LDAP_XSTRING(bdb_modrdn)
256				": non-leaf %s\n",
257				op->o_req_dn.bv_val, 0, 0);
258			rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
259			rs->sr_text = "subtree rename not supported";
260			break;
261		default:
262			Debug(LDAP_DEBUG_ARGS,
263				"<=- " LDAP_XSTRING(bdb_modrdn)
264				": has_children failed: %s (%d)\n",
265				db_strerror(rs->sr_err), rs->sr_err, 0 );
266			rs->sr_err = LDAP_OTHER;
267			rs->sr_text = "internal error";
268		}
269		goto return_results;
270	}
271	ei->bei_state |= CACHE_ENTRY_NO_KIDS;
272#endif
273
274	if (!manageDSAit && is_entry_referral( e ) ) {
275		/* parent is a referral, don't allow add */
276		rs->sr_ref = get_entry_referrals( op, e );
277
278		Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(bdb_modrdn)
279			": entry %s is referral\n", e->e_dn, 0, 0 );
280
281		rs->sr_err = LDAP_REFERRAL,
282		rs->sr_matched = e->e_name.bv_val;
283		send_ldap_result( op, rs );
284
285		ber_bvarray_free( rs->sr_ref );
286		rs->sr_ref = NULL;
287		rs->sr_matched = NULL;
288		goto done;
289	}
290
291	if ( be_issuffix( op->o_bd, &e->e_nname ) ) {
292#ifdef BDB_MULTIPLE_SUFFIXES
293		/* Allow renaming one suffix entry to another */
294		p_ndn = slap_empty_bv;
295#else
296		/* There can only be one suffix entry */
297		rs->sr_err = LDAP_NAMING_VIOLATION;
298		rs->sr_text = "cannot rename suffix entry";
299		goto return_results;
300#endif
301	} else {
302		dnParent( &e->e_nname, &p_ndn );
303	}
304	np_ndn = &p_ndn;
305	eip = ei->bei_parent;
306	if ( eip && eip->bei_id ) {
307		/* Make sure parent entry exist and we can write its
308		 * children.
309		 */
310		rs->sr_err = bdb_cache_find_id( op, ltid,
311			eip->bei_id, &eip, 0, &plock );
312
313		switch( rs->sr_err ) {
314		case 0:
315		case DB_NOTFOUND:
316			break;
317		case DB_LOCK_DEADLOCK:
318		case DB_LOCK_NOTGRANTED:
319			goto retry;
320		case LDAP_BUSY:
321			rs->sr_text = "ldap server busy";
322			goto return_results;
323		default:
324			rs->sr_err = LDAP_OTHER;
325			rs->sr_text = "internal error";
326			goto return_results;
327		}
328
329		p = eip->bei_e;
330		if( p == NULL) {
331			Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(bdb_modrdn)
332				": parent does not exist\n", 0, 0, 0);
333			rs->sr_err = LDAP_OTHER;
334			rs->sr_text = "old entry's parent does not exist";
335			goto return_results;
336		}
337	} else {
338		p = (Entry *)&slap_entry_root;
339	}
340
341	/* check parent for "children" acl */
342	rs->sr_err = access_allowed( op, p,
343		children, NULL,
344		op->oq_modrdn.rs_newSup == NULL ?
345			ACL_WRITE : ACL_WDEL,
346		NULL );
347
348	if ( !p_ndn.bv_len )
349		p = NULL;
350
351	if ( ! rs->sr_err ) {
352		switch( opinfo.boi_err ) {
353		case DB_LOCK_DEADLOCK:
354		case DB_LOCK_NOTGRANTED:
355			goto retry;
356		}
357
358		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
359		Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
360			0, 0 );
361		rs->sr_text = "no write access to old parent's children";
362		goto return_results;
363	}
364
365	Debug( LDAP_DEBUG_TRACE,
366		LDAP_XSTRING(bdb_modrdn) ": wr to children "
367		"of entry %s OK\n", p_ndn.bv_val, 0, 0 );
368
369	if ( p_ndn.bv_val == slap_empty_bv.bv_val ) {
370		p_dn = slap_empty_bv;
371	} else {
372		dnParent( &e->e_name, &p_dn );
373	}
374
375	Debug( LDAP_DEBUG_TRACE,
376		LDAP_XSTRING(bdb_modrdn) ": parent dn=%s\n",
377		p_dn.bv_val, 0, 0 );
378
379	new_parent_dn = &p_dn;	/* New Parent unless newSuperior given */
380
381	if ( op->oq_modrdn.rs_newSup != NULL ) {
382		Debug( LDAP_DEBUG_TRACE,
383			LDAP_XSTRING(bdb_modrdn)
384			": new parent \"%s\" requested...\n",
385			op->oq_modrdn.rs_newSup->bv_val, 0, 0 );
386
387		/*  newSuperior == oldParent? */
388		if( dn_match( &p_ndn, op->oq_modrdn.rs_nnewSup ) ) {
389			Debug( LDAP_DEBUG_TRACE, "bdb_back_modrdn: "
390				"new parent \"%s\" same as the old parent \"%s\"\n",
391				op->oq_modrdn.rs_newSup->bv_val, p_dn.bv_val, 0 );
392			op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
393		}
394	}
395
396	/* There's a BDB_MULTIPLE_SUFFIXES case here that this code doesn't
397	 * support. E.g., two suffixes dc=foo,dc=com and dc=bar,dc=net.
398	 * We do not allow modDN
399	 *   dc=foo,dc=com
400	 *    newrdn dc=bar
401	 *    newsup dc=net
402	 * and we probably should. But since MULTIPLE_SUFFIXES is deprecated
403	 * I'm ignoring this problem for now.
404	 */
405	if ( op->oq_modrdn.rs_newSup != NULL ) {
406		if ( op->oq_modrdn.rs_newSup->bv_len ) {
407			np_dn = op->oq_modrdn.rs_newSup;
408			np_ndn = op->oq_modrdn.rs_nnewSup;
409
410			/* newSuperior == oldParent? - checked above */
411			/* newSuperior == entry being moved?, if so ==> ERROR */
412			if ( dnIsSuffix( np_ndn, &e->e_nname )) {
413				rs->sr_err = LDAP_NO_SUCH_OBJECT;
414				rs->sr_text = "new superior not found";
415				goto return_results;
416			}
417			/* Get Entry with dn=newSuperior. Does newSuperior exist? */
418
419			rs->sr_err = bdb_dn2entry( op, ltid, np_ndn,
420				&neip, 0, &nplock );
421
422			switch( rs->sr_err ) {
423			case 0: np = neip->bei_e;
424			case DB_NOTFOUND:
425				break;
426			case DB_LOCK_DEADLOCK:
427			case DB_LOCK_NOTGRANTED:
428				goto retry;
429			case LDAP_BUSY:
430				rs->sr_text = "ldap server busy";
431				goto return_results;
432			default:
433				rs->sr_err = LDAP_OTHER;
434				rs->sr_text = "internal error";
435				goto return_results;
436			}
437
438			if( np == NULL) {
439				Debug( LDAP_DEBUG_TRACE,
440					LDAP_XSTRING(bdb_modrdn)
441					": newSup(ndn=%s) not here!\n",
442					np_ndn->bv_val, 0, 0);
443				rs->sr_text = "new superior not found";
444				rs->sr_err = LDAP_NO_SUCH_OBJECT;
445				goto return_results;
446			}
447
448			Debug( LDAP_DEBUG_TRACE,
449				LDAP_XSTRING(bdb_modrdn)
450				": wr to new parent OK np=%p, id=%ld\n",
451				(void *) np, (long) np->e_id, 0 );
452
453			/* check newSuperior for "children" acl */
454			rs->sr_err = access_allowed( op, np, children,
455				NULL, ACL_WADD, NULL );
456
457			if( ! rs->sr_err ) {
458				switch( opinfo.boi_err ) {
459				case DB_LOCK_DEADLOCK:
460				case DB_LOCK_NOTGRANTED:
461					goto retry;
462				}
463
464				Debug( LDAP_DEBUG_TRACE,
465					LDAP_XSTRING(bdb_modrdn)
466					": no wr to newSup children\n",
467					0, 0, 0 );
468				rs->sr_text = "no write access to new superior's children";
469				rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
470				goto return_results;
471			}
472
473			if ( is_entry_alias( np ) ) {
474				/* parent is an alias, don't allow add */
475				Debug( LDAP_DEBUG_TRACE,
476					LDAP_XSTRING(bdb_modrdn)
477					": entry is alias\n",
478					0, 0, 0 );
479				rs->sr_text = "new superior is an alias";
480				rs->sr_err = LDAP_ALIAS_PROBLEM;
481				goto return_results;
482			}
483
484			if ( is_entry_referral( np ) ) {
485				/* parent is a referral, don't allow add */
486				Debug( LDAP_DEBUG_TRACE,
487					LDAP_XSTRING(bdb_modrdn)
488					": entry is referral\n",
489					0, 0, 0 );
490				rs->sr_text = "new superior is a referral";
491				rs->sr_err = LDAP_OTHER;
492				goto return_results;
493			}
494
495		} else {
496			np_dn = NULL;
497
498			/* no parent, modrdn entry directly under root */
499			if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
500				|| be_isupdate( op ) ) {
501				np = (Entry *)&slap_entry_root;
502
503				/* check parent for "children" acl */
504				rs->sr_err = access_allowed( op, np,
505					children, NULL, ACL_WADD, NULL );
506
507				np = NULL;
508
509				if ( ! rs->sr_err ) {
510					switch( opinfo.boi_err ) {
511					case DB_LOCK_DEADLOCK:
512					case DB_LOCK_NOTGRANTED:
513						goto retry;
514					}
515
516					rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
517					Debug( LDAP_DEBUG_TRACE,
518						"no access to new superior\n",
519						0, 0, 0 );
520					rs->sr_text =
521						"no write access to new superior's children";
522					goto return_results;
523				}
524			}
525		}
526
527		Debug( LDAP_DEBUG_TRACE,
528			LDAP_XSTRING(bdb_modrdn)
529			": wr to new parent's children OK\n",
530			0, 0, 0 );
531
532		new_parent_dn = np_dn;
533	}
534
535	/* Build target dn and make sure target entry doesn't exist already. */
536	if (!new_dn.bv_val) {
537		build_new_dn( &new_dn, new_parent_dn, &op->oq_modrdn.rs_newrdn, NULL );
538	}
539
540	if (!new_ndn.bv_val) {
541		struct berval bv = {0, NULL};
542		dnNormalize( 0, NULL, NULL, &new_dn, &bv, op->o_tmpmemctx );
543		ber_dupbv( &new_ndn, &bv );
544		/* FIXME: why not call dnNormalize() w/o ctx? */
545		op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
546	}
547
548	Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(bdb_modrdn) ": new ndn=%s\n",
549		new_ndn.bv_val, 0, 0 );
550
551	/* Shortcut the search */
552	nei = neip ? neip : eip;
553	rs->sr_err = bdb_cache_find_ndn ( op, ltid, &new_ndn, &nei );
554	if ( nei ) bdb_cache_entryinfo_unlock( nei );
555	switch( rs->sr_err ) {
556	case DB_LOCK_DEADLOCK:
557	case DB_LOCK_NOTGRANTED:
558		goto retry;
559	case DB_NOTFOUND:
560		break;
561	case 0:
562		/* Allow rename to same DN */
563		if ( nei == ei )
564			break;
565		rs->sr_err = LDAP_ALREADY_EXISTS;
566		goto return_results;
567	default:
568		rs->sr_err = LDAP_OTHER;
569		rs->sr_text = "internal error";
570		goto return_results;
571	}
572
573	assert( op->orr_modlist != NULL );
574
575	if( op->o_preread ) {
576		if( preread_ctrl == NULL ) {
577			preread_ctrl = &ctrls[num_ctrls++];
578			ctrls[num_ctrls] = NULL;
579		}
580		if( slap_read_controls( op, rs, e,
581			&slap_pre_read_bv, preread_ctrl ) )
582		{
583			Debug( LDAP_DEBUG_TRACE,
584				"<=- " LDAP_XSTRING(bdb_modrdn)
585				": pre-read failed!\n", 0, 0, 0 );
586			if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
587				/* FIXME: is it correct to abort
588				 * operation if control fails? */
589				goto return_results;
590			}
591		}
592	}
593
594	/* nested transaction */
595	rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, &lt2, bdb->bi_db_opflags );
596	rs->sr_text = NULL;
597	if( rs->sr_err != 0 ) {
598		Debug( LDAP_DEBUG_TRACE,
599			LDAP_XSTRING(bdb_modrdn)
600			": txn_begin(2) failed: %s (%d)\n",
601			db_strerror(rs->sr_err), rs->sr_err, 0 );
602		rs->sr_err = LDAP_OTHER;
603		rs->sr_text = "internal error";
604		goto return_results;
605	}
606
607	/* delete old DN */
608	rs->sr_err = bdb_dn2id_delete( op, lt2, eip, e );
609	if ( rs->sr_err != 0 ) {
610		Debug(LDAP_DEBUG_TRACE,
611			"<=- " LDAP_XSTRING(bdb_modrdn)
612			": dn2id del failed: %s (%d)\n",
613			db_strerror(rs->sr_err), rs->sr_err, 0 );
614		switch( rs->sr_err ) {
615		case DB_LOCK_DEADLOCK:
616		case DB_LOCK_NOTGRANTED:
617			goto retry;
618		}
619		rs->sr_err = LDAP_OTHER;
620		rs->sr_text = "DN index delete fail";
621		goto return_results;
622	}
623
624	/* copy the entry, then override some fields */
625	dummy = *e;
626	dummy.e_name = new_dn;
627	dummy.e_nname = new_ndn;
628	dummy.e_attrs = NULL;
629
630	/* add new DN */
631	rs->sr_err = bdb_dn2id_add( op, lt2, neip ? neip : eip, &dummy );
632	if ( rs->sr_err != 0 ) {
633		Debug(LDAP_DEBUG_TRACE,
634			"<=- " LDAP_XSTRING(bdb_modrdn)
635			": dn2id add failed: %s (%d)\n",
636			db_strerror(rs->sr_err), rs->sr_err, 0 );
637		switch( rs->sr_err ) {
638		case DB_LOCK_DEADLOCK:
639		case DB_LOCK_NOTGRANTED:
640			goto retry;
641		}
642		rs->sr_err = LDAP_OTHER;
643		rs->sr_text = "DN index add failed";
644		goto return_results;
645	}
646
647	dummy.e_attrs = e->e_attrs;
648
649	/* modify entry */
650	rs->sr_err = bdb_modify_internal( op, lt2, op->orr_modlist, &dummy,
651		&rs->sr_text, textbuf, textlen );
652	if( rs->sr_err != LDAP_SUCCESS ) {
653		Debug(LDAP_DEBUG_TRACE,
654			"<=- " LDAP_XSTRING(bdb_modrdn)
655			": modify failed: %s (%d)\n",
656			db_strerror(rs->sr_err), rs->sr_err, 0 );
657		if ( ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) && opinfo.boi_err ) {
658			rs->sr_err = opinfo.boi_err;
659		}
660		if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
661		switch( rs->sr_err ) {
662		case DB_LOCK_DEADLOCK:
663		case DB_LOCK_NOTGRANTED:
664			goto retry;
665		}
666		goto return_results;
667	}
668
669	/* id2entry index */
670	rs->sr_err = bdb_id2entry_update( op->o_bd, lt2, &dummy );
671	if ( rs->sr_err != 0 ) {
672		Debug(LDAP_DEBUG_TRACE,
673			"<=- " LDAP_XSTRING(bdb_modrdn)
674			": id2entry failed: %s (%d)\n",
675			db_strerror(rs->sr_err), rs->sr_err, 0 );
676		switch( rs->sr_err ) {
677		case DB_LOCK_DEADLOCK:
678		case DB_LOCK_NOTGRANTED:
679			goto retry;
680		}
681		rs->sr_err = LDAP_OTHER;
682		rs->sr_text = "entry update failed";
683		goto return_results;
684	}
685
686	if ( p_ndn.bv_len != 0 ) {
687		parent_is_glue = is_entry_glue(p);
688		rs->sr_err = bdb_cache_children( op, lt2, p );
689		if ( rs->sr_err != DB_NOTFOUND ) {
690			switch( rs->sr_err ) {
691			case DB_LOCK_DEADLOCK:
692			case DB_LOCK_NOTGRANTED:
693				goto retry;
694			case 0:
695				break;
696			default:
697				Debug(LDAP_DEBUG_ARGS,
698					"<=- " LDAP_XSTRING(bdb_modrdn)
699					": has_children failed: %s (%d)\n",
700					db_strerror(rs->sr_err), rs->sr_err, 0 );
701				rs->sr_err = LDAP_OTHER;
702				rs->sr_text = "internal error";
703				goto return_results;
704			}
705			parent_is_leaf = 1;
706		}
707		bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
708		p = NULL;
709	}
710
711	if ( TXN_COMMIT( lt2, 0 ) != 0 ) {
712		rs->sr_err = LDAP_OTHER;
713		rs->sr_text = "txn_commit(2) failed";
714		goto return_results;
715	}
716
717	if( op->o_postread ) {
718		if( postread_ctrl == NULL ) {
719			postread_ctrl = &ctrls[num_ctrls++];
720			ctrls[num_ctrls] = NULL;
721		}
722		if( slap_read_controls( op, rs, &dummy,
723			&slap_post_read_bv, postread_ctrl ) )
724		{
725			Debug( LDAP_DEBUG_TRACE,
726				"<=- " LDAP_XSTRING(bdb_modrdn)
727				": post-read failed!\n", 0, 0, 0 );
728			if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
729				/* FIXME: is it correct to abort
730				 * operation if control fails? */
731				goto return_results;
732			}
733		}
734	}
735
736	if( op->o_noop ) {
737		if(( rs->sr_err=TXN_ABORT( ltid )) != 0 ) {
738			rs->sr_text = "txn_abort (no-op) failed";
739		} else {
740			rs->sr_err = LDAP_X_NO_OPERATION;
741			ltid = NULL;
742			/* Only free attrs if they were dup'd.  */
743			if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
744			goto return_results;
745		}
746
747	} else {
748		rc = bdb_cache_modrdn( bdb, e, &op->orr_nnewrdn, &dummy, neip,
749			ltid, &lock );
750		switch( rc ) {
751		case DB_LOCK_DEADLOCK:
752		case DB_LOCK_NOTGRANTED:
753			goto retry;
754		}
755		dummy.e_attrs = NULL;
756		new_dn.bv_val = NULL;
757		new_ndn.bv_val = NULL;
758
759		if(( rs->sr_err=TXN_COMMIT( ltid, 0 )) != 0 ) {
760			rs->sr_text = "txn_commit failed";
761		} else {
762			rs->sr_err = LDAP_SUCCESS;
763		}
764	}
765
766	ltid = NULL;
767	LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
768	opinfo.boi_oe.oe_key = NULL;
769
770	if( rs->sr_err != LDAP_SUCCESS ) {
771		Debug( LDAP_DEBUG_TRACE,
772			LDAP_XSTRING(bdb_modrdn) ": %s : %s (%d)\n",
773			rs->sr_text, db_strerror(rs->sr_err), rs->sr_err );
774		rs->sr_err = LDAP_OTHER;
775
776		goto return_results;
777	}
778
779	Debug(LDAP_DEBUG_TRACE,
780		LDAP_XSTRING(bdb_modrdn)
781		": rdn modified%s id=%08lx dn=\"%s\"\n",
782		op->o_noop ? " (no-op)" : "",
783		dummy.e_id, op->o_req_dn.bv_val );
784	rs->sr_text = NULL;
785	if( num_ctrls ) rs->sr_ctrls = ctrls;
786
787return_results:
788	if ( dummy.e_attrs ) {
789		attrs_free( dummy.e_attrs );
790	}
791	send_ldap_result( op, rs );
792
793	if( rs->sr_err == LDAP_SUCCESS && bdb->bi_txn_cp_kbyte ) {
794		TXN_CHECKPOINT( bdb->bi_dbenv,
795			bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
796	}
797
798	if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
799		op->o_delete_glue_parent = 1;
800	}
801
802done:
803	slap_graduate_commit_csn( op );
804
805	if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
806	if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );
807
808	/* LDAP v3 Support */
809	if( np != NULL ) {
810		/* free new parent and reader lock */
811		bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, np);
812	}
813
814	if( p != NULL ) {
815		/* free parent and reader lock */
816		bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
817	}
818
819	/* free entry */
820	if( e != NULL ) {
821		bdb_unlocked_cache_return_entry_w( &bdb->bi_cache, e);
822	}
823
824	if( ltid != NULL ) {
825		TXN_ABORT( ltid );
826	}
827	if ( opinfo.boi_oe.oe_key ) {
828		LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
829	}
830
831	if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
832		slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
833		slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
834	}
835	if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
836		slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
837		slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
838	}
839	return rs->sr_err;
840}
841