xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-ndb/modify.cpp (revision e670fd5c413e99c2f6a37901bb21c537fcd322d2)
1 /* modify.cpp - ndb backend modify routine */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2008-2021 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 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Howard Chu for inclusion
18  * in OpenLDAP Software. This work was sponsored by MySQL.
19  */
20 
21 #include "portable.h"
22 
23 #include <stdio.h>
24 #include <ac/string.h>
25 #include <ac/time.h>
26 
27 #include "back-ndb.h"
28 
29 /* This is a copy from slapd/mods.c, but with compaction tweaked
30  * to swap values from the tail into deleted slots, to reduce the
31  * overall update traffic.
32  */
33 static int
ndb_modify_delete(Entry * e,Modification * mod,int permissive,const char ** text,char * textbuf,size_t textlen,int * idx)34 ndb_modify_delete(
35 	Entry	*e,
36 	Modification	*mod,
37 	int	permissive,
38 	const char	**text,
39 	char *textbuf, size_t textlen,
40 	int *idx )
41 {
42 	Attribute	*a;
43 	MatchingRule 	*mr = mod->sm_desc->ad_type->sat_equality;
44 	struct berval *cvals;
45 	int		*id2 = NULL;
46 	int		i, j, rc = 0, num;
47 	unsigned flags;
48 	char		dummy = '\0';
49 
50 	/* For ordered vals, we have no choice but to preserve order */
51 	if ( mod->sm_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL )
52 		return modify_delete_vindex( e, mod, permissive, text,
53 			textbuf, textlen, idx );
54 
55 	/*
56 	 * If permissive is set, then the non-existence of an
57 	 * attribute is not treated as an error.
58 	 */
59 
60 	/* delete the entire attribute */
61 	if ( mod->sm_values == NULL ) {
62 		rc = attr_delete( &e->e_attrs, mod->sm_desc );
63 
64 		if( permissive ) {
65 			rc = LDAP_SUCCESS;
66 		} else if( rc != LDAP_SUCCESS ) {
67 			*text = textbuf;
68 			snprintf( textbuf, textlen,
69 				"modify/delete: %s: no such attribute",
70 				mod->sm_desc->ad_cname.bv_val );
71 			rc = LDAP_NO_SUCH_ATTRIBUTE;
72 		}
73 		return rc;
74 	}
75 
76 	/* FIXME: Catch old code that doesn't set sm_numvals.
77 	 */
78 	if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) {
79 		for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ );
80 		assert( mod->sm_numvals == i );
81 	}
82 	if ( !idx ) {
83 		id2 = (int *)ch_malloc( mod->sm_numvals * sizeof( int ));
84 		idx = id2;
85 	}
86 
87 	if( mr == NULL || !mr->smr_match ) {
88 		/* disallow specific attributes from being deleted if
89 			no equality rule */
90 		*text = textbuf;
91 		snprintf( textbuf, textlen,
92 			"modify/delete: %s: no equality matching rule",
93 			mod->sm_desc->ad_cname.bv_val );
94 		rc = LDAP_INAPPROPRIATE_MATCHING;
95 		goto return_result;
96 	}
97 
98 	/* delete specific values - find the attribute first */
99 	if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
100 		if( permissive ) {
101 			rc = LDAP_SUCCESS;
102 			goto return_result;
103 		}
104 		*text = textbuf;
105 		snprintf( textbuf, textlen,
106 			"modify/delete: %s: no such attribute",
107 			mod->sm_desc->ad_cname.bv_val );
108 		rc = LDAP_NO_SUCH_ATTRIBUTE;
109 		goto return_result;
110 	}
111 
112 	if ( mod->sm_nvalues ) {
113 		flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX
114 			| SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
115 			| SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
116 		cvals = mod->sm_nvalues;
117 	} else {
118 		flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
119 		cvals = mod->sm_values;
120 	}
121 
122 	/* Locate values to delete */
123 	for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
124 		unsigned sort;
125 		rc = attr_valfind( a, flags, &cvals[i], &sort, NULL );
126 		if ( rc == LDAP_SUCCESS ) {
127 			idx[i] = sort;
128 		} else if ( rc == LDAP_NO_SUCH_ATTRIBUTE ) {
129 			if ( permissive ) {
130 				idx[i] = -1;
131 				continue;
132 			}
133 			*text = textbuf;
134 			snprintf( textbuf, textlen,
135 				"modify/delete: %s: no such value",
136 				mod->sm_desc->ad_cname.bv_val );
137 			goto return_result;
138 		} else {
139 			*text = textbuf;
140 			snprintf( textbuf, textlen,
141 				"modify/delete: %s: matching rule failed",
142 				mod->sm_desc->ad_cname.bv_val );
143 			goto return_result;
144 		}
145 	}
146 
147 	num = a->a_numvals;
148 
149 	/* Delete the values */
150 	for ( i = 0; i < mod->sm_numvals; i++ ) {
151 		/* Skip permissive values that weren't found */
152 		if ( idx[i] < 0 )
153 			continue;
154 		/* Skip duplicate delete specs */
155 		if ( a->a_vals[idx[i]].bv_val == &dummy )
156 			continue;
157 		/* delete value and mark it as gone */
158 		free( a->a_vals[idx[i]].bv_val );
159 		a->a_vals[idx[i]].bv_val = &dummy;
160 		if( a->a_nvals != a->a_vals ) {
161 			free( a->a_nvals[idx[i]].bv_val );
162 			a->a_nvals[idx[i]].bv_val = &dummy;
163 		}
164 		a->a_numvals--;
165 	}
166 
167 	/* compact array */
168 	for ( i=0; i<num; i++ ) {
169 		if ( a->a_vals[i].bv_val != &dummy )
170 			continue;
171 		for ( --num; num > i && a->a_vals[num].bv_val == &dummy; num-- )
172 			;
173 		a->a_vals[i] = a->a_vals[num];
174 		if ( a->a_nvals != a->a_vals )
175 			a->a_nvals[i] = a->a_nvals[num];
176 	}
177 
178 	BER_BVZERO( &a->a_vals[num] );
179 	if (a->a_nvals != a->a_vals) {
180 		BER_BVZERO( &a->a_nvals[num] );
181 	}
182 
183 	/* if no values remain, delete the entire attribute */
184 	if ( !a->a_numvals ) {
185 		if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
186 			/* Can never happen */
187 			*text = textbuf;
188 			snprintf( textbuf, textlen,
189 				"modify/delete: %s: no such attribute",
190 				mod->sm_desc->ad_cname.bv_val );
191 			rc = LDAP_NO_SUCH_ATTRIBUTE;
192 		}
193 	}
194 return_result:
195 	if ( id2 )
196 		ch_free( id2 );
197 	return rc;
198 }
199 
ndb_modify_internal(Operation * op,NdbArgs * NA,const char ** text,char * textbuf,size_t textlen)200 int ndb_modify_internal(
201 	Operation *op,
202 	NdbArgs *NA,
203 	const char **text,
204 	char *textbuf,
205 	size_t textlen )
206 {
207 	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
208 	Modification	*mod;
209 	Modifications	*ml;
210 	Modifications	*modlist = op->orm_modlist;
211 	NdbAttrInfo **modai, *atmp;
212 	const NdbDictionary::Dictionary *myDict;
213 	const NdbDictionary::Table *myTable;
214 	int got_oc = 0, nmods = 0, nai = 0, i, j;
215 	int rc, indexed = 0;
216 	Attribute *old = NULL;
217 
218 	Debug( LDAP_DEBUG_TRACE, "ndb_modify_internal: 0x%08lx: %s\n",
219 		NA->e->e_id, NA->e->e_dn, 0);
220 
221 	if ( !acl_check_modlist( op, NA->e, modlist )) {
222 		return LDAP_INSUFFICIENT_ACCESS;
223 	}
224 
225 	old = attrs_dup( NA->e->e_attrs );
226 
227 	for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
228 		mod = &ml->sml_mod;
229 		nmods++;
230 
231 		switch ( mod->sm_op ) {
232 		case LDAP_MOD_ADD:
233 			Debug(LDAP_DEBUG_ARGS,
234 				"ndb_modify_internal: add %s\n",
235 				mod->sm_desc->ad_cname.bv_val, 0, 0);
236 			rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
237 				text, textbuf, textlen );
238 			if( rc != LDAP_SUCCESS ) {
239 				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
240 					rc, *text, 0);
241 			}
242 			break;
243 
244 		case LDAP_MOD_DELETE:
245 			Debug(LDAP_DEBUG_ARGS,
246 				"ndb_modify_internal: delete %s\n",
247 				mod->sm_desc->ad_cname.bv_val, 0, 0);
248 			rc = ndb_modify_delete( NA->e, mod, get_permissiveModify(op),
249 				text, textbuf, textlen, NULL );
250 			assert( rc != LDAP_TYPE_OR_VALUE_EXISTS );
251 			if( rc != LDAP_SUCCESS ) {
252 				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
253 					rc, *text, 0);
254 			}
255 			break;
256 
257 		case LDAP_MOD_REPLACE:
258 			Debug(LDAP_DEBUG_ARGS,
259 				"ndb_modify_internal: replace %s\n",
260 				mod->sm_desc->ad_cname.bv_val, 0, 0);
261 			rc = modify_replace_values( NA->e, mod, get_permissiveModify(op),
262 				text, textbuf, textlen );
263 			if( rc != LDAP_SUCCESS ) {
264 				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
265 					rc, *text, 0);
266 			}
267 			break;
268 
269 		case LDAP_MOD_INCREMENT:
270 			Debug(LDAP_DEBUG_ARGS,
271 				"ndb_modify_internal: increment %s\n",
272 				mod->sm_desc->ad_cname.bv_val, 0, 0);
273 			rc = modify_increment_values( NA->e, mod, get_permissiveModify(op),
274 				text, textbuf, textlen );
275 			if( rc != LDAP_SUCCESS ) {
276 				Debug(LDAP_DEBUG_ARGS,
277 					"ndb_modify_internal: %d %s\n",
278 					rc, *text, 0);
279 			}
280 			break;
281 
282 		case SLAP_MOD_SOFTADD:
283 			Debug(LDAP_DEBUG_ARGS,
284 				"ndb_modify_internal: softadd %s\n",
285 				mod->sm_desc->ad_cname.bv_val, 0, 0);
286  			mod->sm_op = LDAP_MOD_ADD;
287 
288 			rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
289 				text, textbuf, textlen );
290 
291  			mod->sm_op = SLAP_MOD_SOFTADD;
292 
293  			if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
294  				rc = LDAP_SUCCESS;
295  			}
296 
297 			if( rc != LDAP_SUCCESS ) {
298 				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
299 					rc, *text, 0);
300 			}
301  			break;
302 
303 		case SLAP_MOD_SOFTDEL:
304 			Debug(LDAP_DEBUG_ARGS,
305 				"ndb_modify_internal: softdel %s\n",
306 				mod->sm_desc->ad_cname.bv_val, 0, 0);
307  			mod->sm_op = LDAP_MOD_DELETE;
308 
309 			rc = modify_delete_values( NA->e, mod, get_permissiveModify(op),
310 				text, textbuf, textlen );
311 
312  			mod->sm_op = SLAP_MOD_SOFTDEL;
313 
314  			if ( rc == LDAP_NO_SUCH_ATTRIBUTE) {
315  				rc = LDAP_SUCCESS;
316  			}
317 
318 			if( rc != LDAP_SUCCESS ) {
319 				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
320 					rc, *text, 0);
321 			}
322  			break;
323 
324 		case SLAP_MOD_ADD_IF_NOT_PRESENT:
325 			Debug(LDAP_DEBUG_ARGS,
326 				"ndb_modify_internal: add_if_not_present %s\n",
327 				mod->sm_desc->ad_cname.bv_val, 0, 0);
328 			if ( attr_find( NA->e->e_attrs, mod->sm_desc ) ) {
329 				rc = LDAP_SUCCESS;
330 				break;
331 			}
332 
333  			mod->sm_op = LDAP_MOD_ADD;
334 
335 			rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
336 				text, textbuf, textlen );
337 
338  			mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
339 
340 			if( rc != LDAP_SUCCESS ) {
341 				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
342 					rc, *text, 0);
343 			}
344  			break;
345 
346 		default:
347 			Debug(LDAP_DEBUG_ANY, "ndb_modify_internal: invalid op %d\n",
348 				mod->sm_op, 0, 0);
349 			*text = "Invalid modify operation";
350 			rc = LDAP_OTHER;
351 			Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
352 				rc, *text, 0);
353 		}
354 
355 		if ( rc != LDAP_SUCCESS ) {
356 			attrs_free( old );
357 			return rc;
358 		}
359 
360 		/* If objectClass was modified, reset the flags */
361 		if ( mod->sm_desc == slap_schema.si_ad_objectClass ) {
362 			NA->e->e_ocflags = 0;
363 			got_oc = 1;
364 		}
365 	}
366 
367 	/* check that the entry still obeys the schema */
368 	rc = entry_schema_check( op, NA->e, NULL, get_relax(op), 0, NULL,
369 		text, textbuf, textlen );
370 	if ( rc != LDAP_SUCCESS || op->o_noop ) {
371 		if ( rc != LDAP_SUCCESS ) {
372 			Debug( LDAP_DEBUG_ANY,
373 				"entry failed schema check: %s\n",
374 				*text, 0, 0 );
375 		}
376 		attrs_free( old );
377 		return rc;
378 	}
379 
380 	if ( got_oc ) {
381 		rc = ndb_entry_put_info( op->o_bd, NA, 1 );
382 		if ( rc ) {
383 			attrs_free( old );
384 			return rc;
385 		}
386 	}
387 
388 	/* apply modifications to DB */
389 	modai = (NdbAttrInfo **)op->o_tmpalloc( nmods * sizeof(NdbAttrInfo*), op->o_tmpmemctx );
390 
391 	/* Get the unique list of modified attributes */
392 	ldap_pvt_thread_rdwr_rlock( &ni->ni_ai_rwlock );
393 	for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
394 		/* Already took care of objectclass */
395 		if ( ml->sml_desc == slap_schema.si_ad_objectClass )
396 			continue;
397 		for ( i=0; i<nai; i++ ) {
398 			if ( ml->sml_desc->ad_type == modai[i]->na_attr )
399 				break;
400 		}
401 		/* This attr was already updated */
402 		if ( i < nai )
403 			continue;
404 		modai[nai] = ndb_ai_find( ni, ml->sml_desc->ad_type );
405 		if ( modai[nai]->na_flag & NDB_INFO_INDEX )
406 			indexed++;
407 		nai++;
408 	}
409 	ldap_pvt_thread_rdwr_runlock( &ni->ni_ai_rwlock );
410 
411 	/* If got_oc, this was already done above */
412 	if ( indexed && !got_oc) {
413 		rc = ndb_entry_put_info( op->o_bd, NA, 1 );
414 		if ( rc ) {
415 			attrs_free( old );
416 			return rc;
417 		}
418 	}
419 
420 	myDict = NA->ndb->getDictionary();
421 
422 	/* sort modai so that OcInfo's are contiguous */
423 	{
424 		int j, k;
425 		for ( i=0; i<nai; i++ ) {
426 			for ( j=i+1; j<nai; j++ ) {
427 				if ( modai[i]->na_oi == modai[j]->na_oi )
428 					continue;
429 				for ( k=j+1; k<nai; k++ ) {
430 					if ( modai[i]->na_oi == modai[k]->na_oi ) {
431 						atmp = modai[j];
432 						modai[j] = modai[k];
433 						modai[k] = atmp;
434 						break;
435 					}
436 				}
437 				/* there are no more na_oi's that match modai[i] */
438 				if ( k == nai ) {
439 					i = j;
440 				}
441 			}
442 		}
443 	}
444 
445 	/* One call per table... */
446 	for ( i=0; i<nai; i += j ) {
447 		atmp = modai[i];
448 		for ( j=i+1; j<nai; j++ )
449 			if ( atmp->na_oi != modai[j]->na_oi )
450 				break;
451 		j -= i;
452 		myTable = myDict->getTable( atmp->na_oi->no_table.bv_val );
453 		if ( !myTable )
454 			continue;
455 		rc = ndb_oc_attrs( NA->txn, myTable, NA->e, atmp->na_oi, &modai[i], j, old );
456 		if ( rc ) break;
457 	}
458 	attrs_free( old );
459 	return rc;
460 }
461 
462 
463 int
ndb_back_modify(Operation * op,SlapReply * rs)464 ndb_back_modify( Operation *op, SlapReply *rs )
465 {
466 	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
467 	Entry		e = {0};
468 	int		manageDSAit = get_manageDSAit( op );
469 	char textbuf[SLAP_TEXT_BUFLEN];
470 	size_t textlen = sizeof textbuf;
471 
472 	int		num_retries = 0;
473 
474 	NdbArgs NA;
475 	NdbRdns rdns;
476 	struct berval matched;
477 
478 	LDAPControl **preread_ctrl = NULL;
479 	LDAPControl **postread_ctrl = NULL;
480 	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
481 	int num_ctrls = 0;
482 
483 	Debug( LDAP_DEBUG_ARGS, LDAP_XSTRING(ndb_back_modify) ": %s\n",
484 		op->o_req_dn.bv_val, 0, 0 );
485 
486 	ctrls[num_ctrls] = NULL;
487 
488 	slap_mods_opattrs( op, &op->orm_modlist, 1 );
489 
490 	e.e_name = op->o_req_dn;
491 	e.e_nname = op->o_req_ndn;
492 
493 	/* Get our NDB handle */
494 	rs->sr_err = ndb_thread_handle( op, &NA.ndb );
495 	rdns.nr_num = 0;
496 	NA.rdns = &rdns;
497 	NA.e = &e;
498 
499 	if( 0 ) {
500 retry:	/* transaction retry */
501 		NA.txn->close();
502 		NA.txn = NULL;
503 		if( e.e_attrs ) {
504 			attrs_free( e.e_attrs );
505 			e.e_attrs = NULL;
506 		}
507 		Debug(LDAP_DEBUG_TRACE,
508 			LDAP_XSTRING(ndb_back_modify) ": retrying...\n", 0, 0, 0);
509 		if ( op->o_abandon ) {
510 			rs->sr_err = SLAPD_ABANDON;
511 			goto return_results;
512 		}
513 		if ( NA.ocs ) {
514 			ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
515 		}
516 		ndb_trans_backoff( ++num_retries );
517 	}
518 	NA.ocs = NULL;
519 
520 	/* begin transaction */
521 	NA.txn = NA.ndb->startTransaction();
522 	rs->sr_text = NULL;
523 	if( !NA.txn ) {
524 		Debug( LDAP_DEBUG_TRACE,
525 			LDAP_XSTRING(ndb_back_modify) ": startTransaction failed: %s (%d)\n",
526 			NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
527 		rs->sr_err = LDAP_OTHER;
528 		rs->sr_text = "internal error";
529 		goto return_results;
530 	}
531 
532 	/* get entry or ancestor */
533 	rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
534 	switch( rs->sr_err ) {
535 	case 0:
536 		break;
537 	case LDAP_NO_SUCH_OBJECT:
538 		Debug( LDAP_DEBUG_ARGS,
539 			"<=- ndb_back_modify: no such object %s\n",
540 			op->o_req_dn.bv_val, 0, 0 );
541 		rs->sr_matched = matched.bv_val;
542 		if (NA.ocs )
543 			ndb_check_referral( op, rs, &NA );
544 		goto return_results;
545 #if 0
546 	case DB_LOCK_DEADLOCK:
547 	case DB_LOCK_NOTGRANTED:
548 		goto retry;
549 #endif
550 	case LDAP_BUSY:
551 		rs->sr_text = "ldap server busy";
552 		goto return_results;
553 	default:
554 		rs->sr_err = LDAP_OTHER;
555 		rs->sr_text = "internal error";
556 		goto return_results;
557 	}
558 
559 	/* acquire and lock entry */
560 	rs->sr_err = ndb_entry_get_data( op, &NA, 1 );
561 
562 	if ( !manageDSAit && is_entry_referral( &e ) ) {
563 		/* entry is a referral, don't allow modify */
564 		rs->sr_ref = get_entry_referrals( op, &e );
565 
566 		Debug( LDAP_DEBUG_TRACE,
567 			LDAP_XSTRING(ndb_back_modify) ": entry is referral\n",
568 			0, 0, 0 );
569 
570 		rs->sr_err = LDAP_REFERRAL;
571 		rs->sr_matched = e.e_name.bv_val;
572 		rs->sr_flags = REP_REF_MUSTBEFREED;
573 		goto return_results;
574 	}
575 
576 	if ( get_assert( op ) &&
577 		( test_filter( op, &e, (Filter*)get_assertion( op )) != LDAP_COMPARE_TRUE ))
578 	{
579 		rs->sr_err = LDAP_ASSERTION_FAILED;
580 		goto return_results;
581 	}
582 
583 	if( op->o_preread ) {
584 		if( preread_ctrl == NULL ) {
585 			preread_ctrl = &ctrls[num_ctrls++];
586 			ctrls[num_ctrls] = NULL;
587 		}
588 		if ( slap_read_controls( op, rs, &e,
589 			&slap_pre_read_bv, preread_ctrl ) )
590 		{
591 			Debug( LDAP_DEBUG_TRACE,
592 				"<=- " LDAP_XSTRING(ndb_back_modify) ": pre-read "
593 				"failed!\n", 0, 0, 0 );
594 			if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
595 				/* FIXME: is it correct to abort
596 				 * operation if control fails? */
597 				goto return_results;
598 			}
599 		}
600 	}
601 
602 	/* Modify the entry */
603 	rs->sr_err = ndb_modify_internal( op, &NA, &rs->sr_text, textbuf, textlen );
604 
605 	if( rs->sr_err != LDAP_SUCCESS ) {
606 		Debug( LDAP_DEBUG_TRACE,
607 			LDAP_XSTRING(ndb_back_modify) ": modify failed (%d)\n",
608 			rs->sr_err, 0, 0 );
609 #if 0
610 		switch( rs->sr_err ) {
611 		case DB_LOCK_DEADLOCK:
612 		case DB_LOCK_NOTGRANTED:
613 			goto retry;
614 		}
615 #endif
616 		goto return_results;
617 	}
618 
619 	if( op->o_postread ) {
620 		if( postread_ctrl == NULL ) {
621 			postread_ctrl = &ctrls[num_ctrls++];
622 			ctrls[num_ctrls] = NULL;
623 		}
624 		if( slap_read_controls( op, rs, &e,
625 			&slap_post_read_bv, postread_ctrl ) )
626 		{
627 			Debug( LDAP_DEBUG_TRACE,
628 				"<=- " LDAP_XSTRING(ndb_back_modify)
629 				": post-read failed!\n", 0, 0, 0 );
630 			if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
631 				/* FIXME: is it correct to abort
632 				 * operation if control fails? */
633 				goto return_results;
634 			}
635 		}
636 	}
637 
638 	if( op->o_noop ) {
639 		if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
640 			NdbOperation::AbortOnError, 1 )) != 0 ) {
641 			rs->sr_text = "txn_abort (no-op) failed";
642 		} else {
643 			rs->sr_err = LDAP_X_NO_OPERATION;
644 		}
645 	} else {
646 		if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
647 			NdbOperation::AbortOnError, 1 )) != 0 ) {
648 			rs->sr_text = "txn_commit failed";
649 		} else {
650 			rs->sr_err = LDAP_SUCCESS;
651 		}
652 	}
653 
654 	if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
655 		Debug( LDAP_DEBUG_TRACE,
656 			LDAP_XSTRING(ndb_back_modify) ": txn_%s failed: %s (%d)\n",
657 			op->o_noop ? "abort (no-op)" : "commit",
658 			NA.txn->getNdbError().message, NA.txn->getNdbError().code );
659 		rs->sr_err = LDAP_OTHER;
660 		goto return_results;
661 	}
662 	NA.txn->close();
663 	NA.txn = NULL;
664 
665 	Debug( LDAP_DEBUG_TRACE,
666 		LDAP_XSTRING(ndb_back_modify) ": updated%s id=%08lx dn=\"%s\"\n",
667 		op->o_noop ? " (no-op)" : "",
668 		e.e_id, op->o_req_dn.bv_val );
669 
670 	rs->sr_err = LDAP_SUCCESS;
671 	rs->sr_text = NULL;
672 	if( num_ctrls ) rs->sr_ctrls = ctrls;
673 
674 return_results:
675 	if ( NA.ocs ) {
676 		ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
677 		NA.ocs = NULL;
678 	}
679 
680 	if ( e.e_attrs != NULL ) {
681 		attrs_free( e.e_attrs );
682 		e.e_attrs = NULL;
683 	}
684 
685 	if( NA.txn != NULL ) {
686 		NA.txn->execute( Rollback );
687 		NA.txn->close();
688 	}
689 
690 	send_ldap_result( op, rs );
691 	slap_graduate_commit_csn( op );
692 
693 	if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
694 		slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
695 		slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
696 	}
697 	if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
698 		slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
699 		slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
700 	}
701 
702 	rs->sr_text = NULL;
703 	return rs->sr_err;
704 }
705