xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-wt/delete.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: delete.c,v 1.2 2021/08/14 16:15:02 christos Exp $	*/
2 
3 /* OpenLDAP WiredTiger backend */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2002-2021 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 /* ACKNOWLEDGEMENTS:
19  * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
20  * based on back-bdb for inclusion in OpenLDAP Software.
21  * WiredTiger is a product of MongoDB Inc.
22  */
23 
24 #include <sys/cdefs.h>
25 __RCSID("$NetBSD: delete.c,v 1.2 2021/08/14 16:15:02 christos Exp $");
26 
27 #include "portable.h"
28 
29 #include <stdio.h>
30 #include <ac/string.h>
31 
32 #include "back-wt.h"
33 #include "slap-config.h"
34 
35 int
wt_delete(Operation * op,SlapReply * rs)36 wt_delete( Operation *op, SlapReply *rs )
37 {
38     struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
39 	struct berval   pdn = {0, NULL};
40 	Entry *e = NULL;
41 	Entry *p = NULL;
42 	int manageDSAit = get_manageDSAit( op );
43 	AttributeDescription *children = slap_schema.si_ad_children;
44 	AttributeDescription *entry = slap_schema.si_ad_entry;
45 
46 	LDAPControl **preread_ctrl = NULL;
47 	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
48 	int num_ctrls = 0;
49 
50 	wt_ctx *wc;
51 	int rc;
52 	WT_CURSOR *cursor = NULL;
53 
54 	int parent_is_glue = 0;
55 	int parent_is_leaf = 0;
56 
57 	Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(wt_delete) ": %s\n",
58 		   op->o_req_dn.bv_val );
59 
60 	if( op->o_txnSpec && txn_preop( op, rs ))
61 		return rs->sr_err;
62 
63 	ctrls[num_ctrls] = 0;
64 	rs->sr_text = NULL;
65 
66 	wc = wt_ctx_get(op, wi);
67 	if( !wc ){
68         Debug( LDAP_DEBUG_TRACE,
69 			   LDAP_XSTRING(wt_delete)
70 			   ": wt_ctx_get failed\n" );
71 		rs->sr_err = LDAP_OTHER;
72 		rs->sr_text = "internal error";
73 		goto return_results;
74 	}
75 
76 /* allocate CSN */
77 	if ( BER_BVISNULL( &op->o_csn ) ) {
78 		struct berval csn;
79 		char csnbuf[LDAP_PVT_CSNSTR_BUFSIZE];
80 
81 		csn.bv_val = csnbuf;
82 		csn.bv_len = sizeof(csnbuf);
83 		slap_get_csn( op, &csn, 1 );
84 	}
85 
86 	if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
87 		dnParent( &op->o_req_ndn, &pdn );
88 	}
89 
90 	/* get parent */
91 	rc = wt_dn2entry(op->o_bd, wc, &pdn, &p);
92 	switch( rc ) {
93 	case 0:
94 	case WT_NOTFOUND:
95 		break;
96 	default:
97 		/* TODO: error handling */
98 		rs->sr_err = LDAP_OTHER;
99 		rs->sr_text = "internal error";
100 		Debug( LDAP_DEBUG_ANY,
101 			   LDAP_XSTRING(wt_delete)
102 			   ": error at wt_dn2entry() rc=%d\n",
103 			   rc );
104 		goto return_results;
105 	}
106 
107 	if ( rc == WT_NOTFOUND && pdn.bv_len != 0 ) {
108 		Debug( LDAP_DEBUG_ARGS,
109 			   "<== " LDAP_XSTRING(wt_delete) ": no such object %s\n",
110 			   op->o_req_dn.bv_val );
111 
112 		if ( p && !BER_BVISEMPTY( &p->e_name )) {
113 			rs->sr_matched = ch_strdup( p->e_name.bv_val );
114 			if ( is_entry_referral( p )) {
115 				BerVarray ref = get_entry_referrals( op, p );
116 				rs->sr_ref = referral_rewrite( ref, &p->e_name,
117 											   &op->o_req_dn, LDAP_SCOPE_DEFAULT );
118 				ber_bvarray_free( ref );
119 			} else {
120 				rs->sr_ref = NULL;
121 			}
122 		} else {
123 			rs->sr_ref = referral_rewrite( default_referral, NULL,
124 										   &op->o_req_dn, LDAP_SCOPE_DEFAULT );
125 		}
126 
127 		rs->sr_err = LDAP_REFERRAL;
128 		rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
129 		goto return_results;
130 	}
131 
132 	/* get entry */
133 	rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
134 	switch( rc ) {
135 	case 0:
136 		break;
137 	case WT_NOTFOUND:
138 		Debug( LDAP_DEBUG_ARGS,
139 			   "<== " LDAP_XSTRING(wt_delete)
140 			   ": no such object %s\n",
141 			   op->o_req_dn.bv_val );
142 		rs->sr_err = LDAP_REFERRAL;
143 		rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
144 		goto return_results;
145 	default:
146 		/* TODO: error handling */
147 		rs->sr_err = LDAP_OTHER;
148 		rs->sr_text = "internal error";
149 		Debug( LDAP_DEBUG_ANY,
150 			   LDAP_XSTRING(wt_delete)
151 			   ": error at wt_dn2entry() rc=%d\n",
152 			   rc );
153 		goto return_results;
154 	}
155 
156 	/* FIXME : dn2entry() should return non-glue entry */
157 	if ( !manageDSAit && is_entry_glue( e ) ) {
158 		Debug( LDAP_DEBUG_ARGS,
159 			   "<== " LDAP_XSTRING(wt_delete)
160 			   ": glue entry %s\n",
161 			   op->o_req_dn.bv_val );
162 
163 		rs->sr_matched = ch_strdup( e->e_dn );
164 		if ( is_entry_referral( e )) {
165 			BerVarray ref = get_entry_referrals( op, e );
166 			rs->sr_ref = referral_rewrite( ref, &e->e_name,
167 										   &op->o_req_dn, LDAP_SCOPE_DEFAULT );
168 			ber_bvarray_free( ref );
169 		} else {
170 			rs->sr_ref = NULL;
171 		}
172 
173 		rs->sr_err = LDAP_REFERRAL;
174 		rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
175 		goto return_results;
176 	}
177 
178 	if ( pdn.bv_len != 0 ) {
179 		/* check parent for "children" acl */
180 		rs->sr_err = access_allowed( op, p,
181 									 children, NULL, ACL_WDEL, NULL );
182 
183 		if ( !rs->sr_err  ) {
184 			Debug( LDAP_DEBUG_TRACE,
185 				   "<== " LDAP_XSTRING(wt_delete) ": no write "
186 				   "access to parent\n" );
187 			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
188 			rs->sr_text = "no write access to parent";
189 			goto return_results;
190 		}
191 
192 	} else {
193 		/* no parent, must be root to delete */
194 		if( ! be_isroot( op ) ) {
195 			if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
196 				 || be_shadow_update( op ) ) {
197 				p = (Entry *)&slap_entry_root;
198 
199 				/* check parent for "children" acl */
200 				rs->sr_err = access_allowed( op, p,
201 											 children, NULL, ACL_WDEL, NULL );
202 
203 				p = NULL;
204 
205 				if ( !rs->sr_err  ) {
206 					Debug( LDAP_DEBUG_TRACE,
207 						   "<== " LDAP_XSTRING(wt_delete)
208 						   ": no access to parent\n" );
209 					rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
210 					rs->sr_text = "no write access to parent";
211 					goto return_results;
212 				}
213 
214 			} else {
215 				Debug( LDAP_DEBUG_TRACE,
216 					   "<== " LDAP_XSTRING(wt_delete)
217 					   ": no parent and not root\n" );
218 				rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
219 				goto return_results;
220 			}
221 		}
222 	}
223 
224 	if ( get_assert( op ) &&
225 		 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
226 	{
227 		rs->sr_err = LDAP_ASSERTION_FAILED;
228 		goto return_results;
229 	}
230 
231 	rs->sr_err = access_allowed( op, e,
232 								 entry, NULL, ACL_WDEL, NULL );
233 	if ( !rs->sr_err  ) {
234 		Debug( LDAP_DEBUG_TRACE,
235 			   "<== " LDAP_XSTRING(wt_delete) ": no write access "
236 			   "to entry\n" );
237 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
238 		rs->sr_text = "no write access to entry";
239 		goto return_results;
240 	}
241 
242 	if ( !manageDSAit && is_entry_referral( e ) ) {
243 		/* entry is a referral, don't allow delete */
244 		rs->sr_ref = get_entry_referrals( op, e );
245 
246 		Debug( LDAP_DEBUG_TRACE,
247 			   LDAP_XSTRING(tw_delete) ": entry is referral\n" );
248 
249 		rs->sr_err = LDAP_REFERRAL;
250 		rs->sr_matched = ch_strdup( e->e_name.bv_val );
251 		rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
252 		goto return_results;
253 	}
254 
255 	/* pre-read */
256 	if( op->o_preread ) {
257 		if( preread_ctrl == NULL ) {
258 			preread_ctrl = &ctrls[num_ctrls++];
259 			ctrls[num_ctrls] = NULL;
260 		}
261 		if( slap_read_controls( op, rs, e,
262 								&slap_pre_read_bv, preread_ctrl ) )
263 		{
264 			Debug( LDAP_DEBUG_TRACE,
265 				   "<== " LDAP_XSTRING(wt_delete) ": pre-read "
266 				   "failed!\n" );
267 			if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
268 				/* FIXME: is it correct to abort
269                  * operation if control fails? */
270 				goto return_results;
271 			}
272 		}
273 	}
274 
275     /* Can't do it if we have kids */
276 	rc = wt_dn2id_has_children( op, wc->session, e->e_id );
277 	if( rc != WT_NOTFOUND ) {
278 		switch( rc ) {
279 		case 0:
280 			Debug(LDAP_DEBUG_ARGS,
281 				  "<== " LDAP_XSTRING(wt_delete)
282 				  ": non-leaf %s\n",
283 				  op->o_req_dn.bv_val );
284 			rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
285 			rs->sr_text = "subordinate objects must be deleted first";
286 			break;
287 		default:
288 			Debug(LDAP_DEBUG_ARGS,
289 				  "<== " LDAP_XSTRING(wt_delete)
290 				  ": has_children failed: %s (%d)\n",
291 				  wiredtiger_strerror(rc), rc );
292 			rs->sr_err = LDAP_OTHER;
293 			rs->sr_text = "internal error";
294 		}
295 		goto return_results;
296 	}
297 
298 	/* begin transaction */
299 	rc = wc->session->begin_transaction(wc->session, NULL);
300 	if( rc ) {
301 		Debug( LDAP_DEBUG_TRACE,
302 			   LDAP_XSTRING(wt_add) ": begin_transaction failed: %s (%d)\n",
303 			   wiredtiger_strerror(rc), rc );
304 		rs->sr_err = LDAP_OTHER;
305 		rs->sr_text = "begin_transaction failed";
306 		goto return_results;
307 	}
308 
309 	/* delete from dn2id */
310 	rc = wt_dn2id_delete( op, wc->session, &e->e_nname);
311 	if ( rc ) {
312 		Debug(LDAP_DEBUG_TRACE,
313 			  "<== " LDAP_XSTRING(wt_delete)
314 			  ": dn2id failed: %s (%d)\n",
315 			  wiredtiger_strerror(rc), rc );
316 		rs->sr_err = LDAP_OTHER;
317 		rs->sr_text = "dn2id delete failed";
318 		wc->session->rollback_transaction(wc->session, NULL);
319 		goto return_results;
320 	}
321 
322 	/* delete indices for old attributes */
323 	rc = wt_index_entry_del( op, wc, e );
324 	if ( rc ) {
325 		Debug(LDAP_DEBUG_TRACE,
326 			  "<== " LDAP_XSTRING(wt_delete)
327 			  ": index delete failed: %s (%d)\n",
328 			  wiredtiger_strerror(rc), rc );
329 		rs->sr_err = LDAP_OTHER;
330 		rs->sr_text = "index delete failed";
331 		wc->session->rollback_transaction(wc->session, NULL);
332 		goto return_results;
333 	}
334 
335 	/* fixup delete CSN */
336 	if ( !SLAP_SHADOW( op->o_bd )) {
337 		struct berval vals[2];
338 
339 		assert( !BER_BVISNULL( &op->o_csn ) );
340 		vals[0] = op->o_csn;
341 		BER_BVZERO( &vals[1] );
342 		rs->sr_err = wt_index_values( op, wc->session, slap_schema.si_ad_entryCSN,
343 									  vals, 0, SLAP_INDEX_ADD_OP );
344 		if ( rs->sr_err != LDAP_SUCCESS ) {
345 			rs->sr_text = "entryCSN index update failed";
346 			rs->sr_err = LDAP_OTHER;
347 			wc->session->rollback_transaction(wc->session, NULL);
348 			goto return_results;
349 		}
350 	}
351 
352 	/* delete from id2entry */
353 	rc = wt_id2entry_delete( op, wc->session, e );
354 	if ( rc ) {
355 		Debug( LDAP_DEBUG_TRACE,
356 			   "<== " LDAP_XSTRING(wt_delete)
357 			   ": id2entry failed: %s (%d)\n",
358 			   wiredtiger_strerror(rc), rc );
359 		rs->sr_err = LDAP_OTHER;
360 		rs->sr_text = "entry delete failed";
361 		wc->session->rollback_transaction(wc->session, NULL);
362 		goto return_results;
363 	}
364 
365 	if ( pdn.bv_len != 0 ) {
366 		// TODO: glue entry
367 	}
368 
369 	rc = wc->session->commit_transaction(wc->session, NULL);
370 	if( rc ) {
371 		Debug( LDAP_DEBUG_TRACE,
372 			   "<== " LDAP_XSTRING(wt_delete)
373 			   ": commit_transaction failed: %s (%d)\n",
374 			   wiredtiger_strerror(rc), rc );
375 		rs->sr_err = LDAP_OTHER;
376 		rs->sr_text = "commit_transaction failed";
377 		goto return_results;
378 	}
379 
380 	Debug( LDAP_DEBUG_TRACE,
381 		   LDAP_XSTRING(wt_delete)
382 		   ": deleted%s id=%08lx dn=\"%s\"\n",
383 		   op->o_noop ? " (no-op)" : "", e->e_id, op->o_req_dn.bv_val );
384 
385 	rs->sr_err = LDAP_SUCCESS;
386 	rs->sr_text = NULL;
387 	if( num_ctrls ) {
388 		rs->sr_ctrls = ctrls;
389 	}
390 
391 return_results:
392 	if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
393 		op->o_delete_glue_parent = 1;
394 	}
395 
396 	if ( p != NULL ) {
397 		wt_entry_return( p );
398 	}
399 
400 	/* free entry */
401 	if( e != NULL ) {
402 		wt_entry_return( e );
403 	}
404 
405 	send_ldap_result( op, rs );
406 	slap_graduate_commit_csn( op );
407 
408 	if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
409 		slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
410 		slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
411 	}
412 
413 	/* TODO: checkpoint */
414 
415 	return rs->sr_err;
416 }
417 
418 /*
419  * Local variables:
420  * indent-tabs-mode: t
421  * tab-width: 4
422  * c-basic-offset: 4
423  * End:
424  */
425