1/*	$NetBSD: delete.c,v 1.1.1.3 2010/12/12 15:22:54 adam Exp $	*/
2
3/* delete.c - bdb backend delete routine */
4/* OpenLDAP: pkg/ldap/servers/slapd/back-bdb/delete.c,v 1.155.2.13 2010/04/19 16:53:03 quanah 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 "lutil.h"
25#include "back-bdb.h"
26
27int
28bdb_delete( Operation *op, SlapReply *rs )
29{
30	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
31	Entry	*matched = NULL;
32	struct berval	pdn = {0, NULL};
33	Entry	*e = NULL;
34	Entry	*p = NULL;
35	EntryInfo	*ei = NULL, *eip = NULL;
36	int		manageDSAit = get_manageDSAit( op );
37	AttributeDescription *children = slap_schema.si_ad_children;
38	AttributeDescription *entry = slap_schema.si_ad_entry;
39	DB_TXN		*ltid = NULL, *lt2;
40	struct bdb_op_info opinfo = {{{ 0 }}};
41	ID	eid;
42
43	DB_LOCK		lock, plock;
44
45	int		num_retries = 0;
46
47	int     rc;
48
49	LDAPControl **preread_ctrl = NULL;
50	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
51	int num_ctrls = 0;
52
53	int	parent_is_glue = 0;
54	int parent_is_leaf = 0;
55
56#ifdef LDAP_X_TXN
57	int settle = 0;
58#endif
59
60	Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(bdb_delete) ": %s\n",
61		op->o_req_dn.bv_val, 0, 0 );
62
63#ifdef LDAP_X_TXN
64	if( op->o_txnSpec ) {
65		/* acquire connection lock */
66		ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
67		if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
68			rs->sr_text = "invalid transaction identifier";
69			rs->sr_err = LDAP_X_TXN_ID_INVALID;
70			goto txnReturn;
71		} else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
72			settle=1;
73			goto txnReturn;
74		}
75
76		if( op->o_conn->c_txn_backend == NULL ) {
77			op->o_conn->c_txn_backend = op->o_bd;
78
79		} else if( op->o_conn->c_txn_backend != op->o_bd ) {
80			rs->sr_text = "transaction cannot span multiple database contexts";
81			rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
82			goto txnReturn;
83		}
84
85		/* insert operation into transaction */
86
87		rs->sr_text = "transaction specified";
88		rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
89
90txnReturn:
91		/* release connection lock */
92		ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
93
94		if( !settle ) {
95			send_ldap_result( op, rs );
96			return rs->sr_err;
97		}
98	}
99#endif
100
101	ctrls[num_ctrls] = 0;
102
103	/* allocate CSN */
104	if ( BER_BVISNULL( &op->o_csn ) ) {
105		struct berval csn;
106		char csnbuf[LDAP_PVT_CSNSTR_BUFSIZE];
107
108		csn.bv_val = csnbuf;
109		csn.bv_len = sizeof(csnbuf);
110		slap_get_csn( op, &csn, 1 );
111	}
112
113	if( 0 ) {
114retry:	/* transaction retry */
115		if( e != NULL ) {
116			bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
117			e = NULL;
118		}
119		if( p != NULL ) {
120			bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
121			p = NULL;
122		}
123		Debug( LDAP_DEBUG_TRACE,
124			"==> " LDAP_XSTRING(bdb_delete) ": retrying...\n",
125			0, 0, 0 );
126		rs->sr_err = TXN_ABORT( ltid );
127		ltid = NULL;
128		LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
129		opinfo.boi_oe.oe_key = NULL;
130		op->o_do_not_cache = opinfo.boi_acl_cache;
131		if( rs->sr_err != 0 ) {
132			rs->sr_err = LDAP_OTHER;
133			rs->sr_text = "internal error";
134			goto return_results;
135		}
136		if ( op->o_abandon ) {
137			rs->sr_err = SLAPD_ABANDON;
138			goto return_results;
139		}
140		parent_is_glue = 0;
141		parent_is_leaf = 0;
142		bdb_trans_backoff( ++num_retries );
143	}
144
145	/* begin transaction */
146	rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid,
147		bdb->bi_db_opflags );
148	rs->sr_text = NULL;
149	if( rs->sr_err != 0 ) {
150		Debug( LDAP_DEBUG_TRACE,
151			LDAP_XSTRING(bdb_delete) ": txn_begin failed: "
152			"%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
153		rs->sr_err = LDAP_OTHER;
154		rs->sr_text = "internal error";
155		goto return_results;
156	}
157
158	opinfo.boi_oe.oe_key = bdb;
159	opinfo.boi_txn = ltid;
160	opinfo.boi_err = 0;
161	opinfo.boi_acl_cache = op->o_do_not_cache;
162	LDAP_SLIST_INSERT_HEAD( &op->o_extra, &opinfo.boi_oe, oe_next );
163
164	if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
165		dnParent( &op->o_req_ndn, &pdn );
166	}
167
168	/* get entry */
169	rs->sr_err = bdb_dn2entry( op, ltid, &op->o_req_ndn, &ei, 1,
170		&lock );
171
172	switch( rs->sr_err ) {
173	case 0:
174	case DB_NOTFOUND:
175		break;
176	case DB_LOCK_DEADLOCK:
177	case DB_LOCK_NOTGRANTED:
178		goto retry;
179	case LDAP_BUSY:
180		rs->sr_text = "ldap server busy";
181		goto return_results;
182	default:
183		rs->sr_err = LDAP_OTHER;
184		rs->sr_text = "internal error";
185		goto return_results;
186	}
187
188	if ( rs->sr_err == 0 ) {
189		e = ei->bei_e;
190		eip = ei->bei_parent;
191	} else {
192		matched = ei->bei_e;
193	}
194
195	/* FIXME : dn2entry() should return non-glue entry */
196	if ( e == NULL || ( !manageDSAit && is_entry_glue( e ))) {
197		Debug( LDAP_DEBUG_ARGS,
198			"<=- " LDAP_XSTRING(bdb_delete) ": no such object %s\n",
199			op->o_req_dn.bv_val, 0, 0);
200
201		if ( matched != NULL ) {
202			rs->sr_matched = ch_strdup( matched->e_dn );
203			rs->sr_ref = is_entry_referral( matched )
204				? get_entry_referrals( op, matched )
205				: NULL;
206			bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, matched);
207			matched = NULL;
208
209		} else {
210			rs->sr_ref = referral_rewrite( default_referral, NULL,
211					&op->o_req_dn, LDAP_SCOPE_DEFAULT );
212		}
213
214		rs->sr_err = LDAP_REFERRAL;
215		rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
216		goto return_results;
217	}
218
219	rc = bdb_cache_find_id( op, ltid, eip->bei_id, &eip, 0, &plock );
220	switch( rc ) {
221	case DB_LOCK_DEADLOCK:
222	case DB_LOCK_NOTGRANTED:
223		goto retry;
224	case 0:
225	case DB_NOTFOUND:
226		break;
227	default:
228		rs->sr_err = LDAP_OTHER;
229		rs->sr_text = "internal error";
230		goto return_results;
231	}
232	if ( eip ) p = eip->bei_e;
233
234	if ( pdn.bv_len != 0 ) {
235		if( p == NULL || !bvmatch( &pdn, &p->e_nname )) {
236			Debug( LDAP_DEBUG_TRACE,
237				"<=- " LDAP_XSTRING(bdb_delete) ": parent "
238				"does not exist\n", 0, 0, 0 );
239			rs->sr_err = LDAP_OTHER;
240			rs->sr_text = "could not locate parent of entry";
241			goto return_results;
242		}
243
244		/* check parent for "children" acl */
245		rs->sr_err = access_allowed( op, p,
246			children, NULL, ACL_WDEL, NULL );
247
248		if ( !rs->sr_err  ) {
249			switch( opinfo.boi_err ) {
250			case DB_LOCK_DEADLOCK:
251			case DB_LOCK_NOTGRANTED:
252				goto retry;
253			}
254
255			Debug( LDAP_DEBUG_TRACE,
256				"<=- " LDAP_XSTRING(bdb_delete) ": no write "
257				"access to parent\n", 0, 0, 0 );
258			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
259			rs->sr_text = "no write access to parent";
260			goto return_results;
261		}
262
263	} else {
264		/* no parent, must be root to delete */
265		if( ! be_isroot( op ) ) {
266			if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
267				|| be_shadow_update( op ) ) {
268				p = (Entry *)&slap_entry_root;
269
270				/* check parent for "children" acl */
271				rs->sr_err = access_allowed( op, p,
272					children, NULL, ACL_WDEL, NULL );
273
274				p = NULL;
275
276				if ( !rs->sr_err  ) {
277					switch( opinfo.boi_err ) {
278					case DB_LOCK_DEADLOCK:
279					case DB_LOCK_NOTGRANTED:
280						goto retry;
281					}
282
283					Debug( LDAP_DEBUG_TRACE,
284						"<=- " LDAP_XSTRING(bdb_delete)
285						": no access to parent\n",
286						0, 0, 0 );
287					rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
288					rs->sr_text = "no write access to parent";
289					goto return_results;
290				}
291
292			} else {
293				Debug( LDAP_DEBUG_TRACE,
294					"<=- " LDAP_XSTRING(bdb_delete)
295					": no parent and not root\n", 0, 0, 0 );
296				rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
297				goto return_results;
298			}
299		}
300	}
301
302	if ( get_assert( op ) &&
303		( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
304	{
305		rs->sr_err = LDAP_ASSERTION_FAILED;
306		goto return_results;
307	}
308
309	rs->sr_err = access_allowed( op, e,
310		entry, NULL, ACL_WDEL, NULL );
311
312	if ( !rs->sr_err  ) {
313		switch( opinfo.boi_err ) {
314		case DB_LOCK_DEADLOCK:
315		case DB_LOCK_NOTGRANTED:
316			goto retry;
317		}
318
319		Debug( LDAP_DEBUG_TRACE,
320			"<=- " LDAP_XSTRING(bdb_delete) ": no write access "
321			"to entry\n", 0, 0, 0 );
322		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
323		rs->sr_text = "no write access to entry";
324		goto return_results;
325	}
326
327	if ( !manageDSAit && is_entry_referral( e ) ) {
328		/* entry is a referral, don't allow delete */
329		rs->sr_ref = get_entry_referrals( op, e );
330
331		Debug( LDAP_DEBUG_TRACE,
332			LDAP_XSTRING(bdb_delete) ": entry is referral\n",
333			0, 0, 0 );
334
335		rs->sr_err = LDAP_REFERRAL;
336		rs->sr_matched = ch_strdup( e->e_name.bv_val );
337		rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
338		goto return_results;
339	}
340
341	/* pre-read */
342	if( op->o_preread ) {
343		if( preread_ctrl == NULL ) {
344			preread_ctrl = &ctrls[num_ctrls++];
345			ctrls[num_ctrls] = NULL;
346		}
347		if( slap_read_controls( op, rs, e,
348			&slap_pre_read_bv, preread_ctrl ) )
349		{
350			Debug( LDAP_DEBUG_TRACE,
351				"<=- " LDAP_XSTRING(bdb_delete) ": pre-read "
352				"failed!\n", 0, 0, 0 );
353			if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
354				/* FIXME: is it correct to abort
355				 * operation if control fails? */
356				goto return_results;
357			}
358		}
359	}
360
361	/* nested transaction */
362	rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, &lt2,
363		bdb->bi_db_opflags );
364	rs->sr_text = NULL;
365	if( rs->sr_err != 0 ) {
366		Debug( LDAP_DEBUG_TRACE,
367			LDAP_XSTRING(bdb_delete) ": txn_begin(2) failed: "
368			"%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
369		rs->sr_err = LDAP_OTHER;
370		rs->sr_text = "internal error";
371		goto return_results;
372	}
373
374	BDB_LOG_PRINTF( bdb->bi_dbenv, lt2, "slapd Starting delete %s(%d)",
375		e->e_nname.bv_val, e->e_id );
376
377	/* Can't do it if we have kids */
378	rs->sr_err = bdb_cache_children( op, lt2, e );
379	if( rs->sr_err != DB_NOTFOUND ) {
380		switch( rs->sr_err ) {
381		case DB_LOCK_DEADLOCK:
382		case DB_LOCK_NOTGRANTED:
383			goto retry;
384		case 0:
385			Debug(LDAP_DEBUG_ARGS,
386				"<=- " LDAP_XSTRING(bdb_delete)
387				": non-leaf %s\n",
388				op->o_req_dn.bv_val, 0, 0);
389			rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
390			rs->sr_text = "subordinate objects must be deleted first";
391			break;
392		default:
393			Debug(LDAP_DEBUG_ARGS,
394				"<=- " LDAP_XSTRING(bdb_delete)
395				": has_children failed: %s (%d)\n",
396				db_strerror(rs->sr_err), rs->sr_err, 0 );
397			rs->sr_err = LDAP_OTHER;
398			rs->sr_text = "internal error";
399		}
400		goto return_results;
401	}
402
403	/* delete from dn2id */
404	rs->sr_err = bdb_dn2id_delete( op, lt2, eip, e );
405	if ( rs->sr_err != 0 ) {
406		Debug(LDAP_DEBUG_TRACE,
407			"<=- " LDAP_XSTRING(bdb_delete) ": dn2id failed: "
408			"%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
409		switch( rs->sr_err ) {
410		case DB_LOCK_DEADLOCK:
411		case DB_LOCK_NOTGRANTED:
412			goto retry;
413		}
414		rs->sr_text = "DN index delete failed";
415		rs->sr_err = LDAP_OTHER;
416		goto return_results;
417	}
418
419	/* delete indices for old attributes */
420	rs->sr_err = bdb_index_entry_del( op, lt2, e );
421	if ( rs->sr_err != LDAP_SUCCESS ) {
422		Debug(LDAP_DEBUG_TRACE,
423			"<=- " LDAP_XSTRING(bdb_delete) ": index failed: "
424			"%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
425		switch( rs->sr_err ) {
426		case DB_LOCK_DEADLOCK:
427		case DB_LOCK_NOTGRANTED:
428			goto retry;
429		}
430		rs->sr_text = "entry index delete failed";
431		rs->sr_err = LDAP_OTHER;
432		goto return_results;
433	}
434
435	/* fixup delete CSN */
436	if ( !SLAP_SHADOW( op->o_bd )) {
437		struct berval vals[2];
438
439		assert( !BER_BVISNULL( &op->o_csn ) );
440		vals[0] = op->o_csn;
441		BER_BVZERO( &vals[1] );
442		rs->sr_err = bdb_index_values( op, lt2, slap_schema.si_ad_entryCSN,
443			vals, 0, SLAP_INDEX_ADD_OP );
444	if ( rs->sr_err != LDAP_SUCCESS ) {
445			switch( rs->sr_err ) {
446			case DB_LOCK_DEADLOCK:
447			case DB_LOCK_NOTGRANTED:
448				goto retry;
449			}
450			rs->sr_text = "entryCSN index update failed";
451			rs->sr_err = LDAP_OTHER;
452			goto return_results;
453		}
454	}
455
456	/* delete from id2entry */
457	rs->sr_err = bdb_id2entry_delete( op->o_bd, lt2, e );
458	if ( rs->sr_err != 0 ) {
459		Debug( LDAP_DEBUG_TRACE,
460			"<=- " LDAP_XSTRING(bdb_delete) ": id2entry failed: "
461			"%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
462		switch( rs->sr_err ) {
463		case DB_LOCK_DEADLOCK:
464		case DB_LOCK_NOTGRANTED:
465			goto retry;
466		}
467		rs->sr_text = "entry delete failed";
468		rs->sr_err = LDAP_OTHER;
469		goto return_results;
470	}
471
472	if ( pdn.bv_len != 0 ) {
473		parent_is_glue = is_entry_glue(p);
474		rs->sr_err = bdb_cache_children( op, lt2, p );
475		if ( rs->sr_err != DB_NOTFOUND ) {
476			switch( rs->sr_err ) {
477			case DB_LOCK_DEADLOCK:
478			case DB_LOCK_NOTGRANTED:
479				goto retry;
480			case 0:
481				break;
482			default:
483				Debug(LDAP_DEBUG_ARGS,
484					"<=- " LDAP_XSTRING(bdb_delete)
485					": has_children failed: %s (%d)\n",
486					db_strerror(rs->sr_err), rs->sr_err, 0 );
487				rs->sr_err = LDAP_OTHER;
488				rs->sr_text = "internal error";
489				goto return_results;
490			}
491			parent_is_leaf = 1;
492		}
493		bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
494		p = NULL;
495	}
496
497	BDB_LOG_PRINTF( bdb->bi_dbenv, lt2, "slapd Commit1 delete %s(%d)",
498		e->e_nname.bv_val, e->e_id );
499
500	if ( TXN_COMMIT( lt2, 0 ) != 0 ) {
501		rs->sr_err = LDAP_OTHER;
502		rs->sr_text = "txn_commit(2) failed";
503		goto return_results;
504	}
505
506	eid = e->e_id;
507
508#if 0	/* Do we want to reclaim deleted IDs? */
509	ldap_pvt_thread_mutex_lock( &bdb->bi_lastid_mutex );
510	if ( e->e_id == bdb->bi_lastid ) {
511		bdb_last_id( op->o_bd, ltid );
512	}
513	ldap_pvt_thread_mutex_unlock( &bdb->bi_lastid_mutex );
514#endif
515
516	if( op->o_noop ) {
517		if ( ( rs->sr_err = TXN_ABORT( ltid ) ) != 0 ) {
518			rs->sr_text = "txn_abort (no-op) failed";
519		} else {
520			rs->sr_err = LDAP_X_NO_OPERATION;
521			ltid = NULL;
522			goto return_results;
523		}
524	} else {
525
526		BDB_LOG_PRINTF( bdb->bi_dbenv, ltid, "slapd Cache delete %s(%d)",
527			e->e_nname.bv_val, e->e_id );
528
529		rc = bdb_cache_delete( bdb, e, ltid, &lock );
530		switch( rc ) {
531		case DB_LOCK_DEADLOCK:
532		case DB_LOCK_NOTGRANTED:
533			goto retry;
534		}
535
536		rs->sr_err = TXN_COMMIT( ltid, 0 );
537	}
538	ltid = NULL;
539	LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
540	opinfo.boi_oe.oe_key = NULL;
541
542	BDB_LOG_PRINTF( bdb->bi_dbenv, NULL, "slapd Committed delete %s(%d)",
543		e->e_nname.bv_val, e->e_id );
544
545	if( rs->sr_err != 0 ) {
546		Debug( LDAP_DEBUG_TRACE,
547			LDAP_XSTRING(bdb_delete) ": txn_%s failed: %s (%d)\n",
548			op->o_noop ? "abort (no-op)" : "commit",
549			db_strerror(rs->sr_err), rs->sr_err );
550		rs->sr_err = LDAP_OTHER;
551		rs->sr_text = "commit failed";
552
553		goto return_results;
554	}
555
556	Debug( LDAP_DEBUG_TRACE,
557		LDAP_XSTRING(bdb_delete) ": deleted%s id=%08lx dn=\"%s\"\n",
558		op->o_noop ? " (no-op)" : "",
559		eid, op->o_req_dn.bv_val );
560	rs->sr_err = LDAP_SUCCESS;
561	rs->sr_text = NULL;
562	if( num_ctrls ) rs->sr_ctrls = ctrls;
563
564return_results:
565	if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
566		op->o_delete_glue_parent = 1;
567	}
568
569	if ( p )
570		bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
571
572	/* free entry */
573	if( e != NULL ) {
574		if ( rs->sr_err == LDAP_SUCCESS ) {
575			/* Free the EntryInfo and the Entry */
576			bdb_cache_entryinfo_lock( BEI(e) );
577			bdb_cache_delete_cleanup( &bdb->bi_cache, BEI(e) );
578		} else {
579			bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
580		}
581	}
582
583	if( ltid != NULL ) {
584		TXN_ABORT( ltid );
585	}
586	if ( opinfo.boi_oe.oe_key ) {
587		LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
588	}
589
590	send_ldap_result( op, rs );
591	slap_graduate_commit_csn( op );
592
593	if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
594		slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
595		slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
596	}
597
598	if( rs->sr_err == LDAP_SUCCESS && bdb->bi_txn_cp_kbyte ) {
599		TXN_CHECKPOINT( bdb->bi_dbenv,
600			bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
601	}
602	return rs->sr_err;
603}
604