xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-wt/modify.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: modify.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 #include <sys/cdefs.h>
24 __RCSID("$NetBSD: modify.c,v 1.2 2021/08/14 16:15:02 christos Exp $");
25 
26 #include "portable.h"
27 
28 #include <stdio.h>
29 #include "back-wt.h"
30 #include "slap-config.h"
31 
32 static struct berval scbva[] = {
33 	BER_BVC("glue"),
34 	BER_BVNULL
35 };
36 
37 static void
wt_modify_idxflags(Operation * op,AttributeDescription * desc,int got_delete,Attribute * newattrs,Attribute * oldattrs)38 wt_modify_idxflags(
39 	Operation *op,
40 	AttributeDescription *desc,
41 	int got_delete,
42 	Attribute *newattrs,
43 	Attribute *oldattrs )
44 {
45 	struct berval	ix_at;
46 	AttrInfo	*ai;
47 
48 	/* check if modified attribute was indexed
49 	 * but not in case of NOOP... */
50 	ai = wt_index_mask( op->o_bd, desc, &ix_at );
51 	if ( ai ) {
52 		if ( got_delete ) {
53 			Attribute	*ap;
54 			struct berval	ix2;
55 
56 			ap = attr_find( oldattrs, desc );
57 			if ( ap ) ap->a_flags |= SLAP_ATTR_IXDEL;
58 
59 			/* Find all other attrs that index to same slot */
60 			for ( ap = newattrs; ap; ap = ap->a_next ) {
61 				ai = wt_index_mask( op->o_bd, ap->a_desc, &ix2 );
62 				if ( ai && ix2.bv_val == ix_at.bv_val )
63 					ap->a_flags |= SLAP_ATTR_IXADD;
64 			}
65 
66 		} else {
67 			Attribute	*ap;
68 
69 			ap = attr_find( newattrs, desc );
70 			if ( ap ) ap->a_flags |= SLAP_ATTR_IXADD;
71 		}
72 	}
73 }
74 
wt_modify_internal(Operation * op,wt_ctx * wc,Modifications * modlist,Entry * e,const char ** text,char * textbuf,size_t textlen)75 int wt_modify_internal(
76 	Operation *op,
77 	wt_ctx *wc,
78 	Modifications *modlist,
79 	Entry *e,
80 	const char **text,
81 	char *textbuf,
82 	size_t textlen )
83 {
84 	int rc, err;
85 	Modification	*mod;
86 	Modifications	*ml;
87 	Attribute	*save_attrs;
88 	Attribute	*ap;
89 	int			glue_attr_delete = 0;
90 	int			got_delete;
91 
92 	Debug( LDAP_DEBUG_TRACE, "wt_modify_internal: 0x%08lx: %s\n",
93 		   e->e_id, e->e_dn );
94 
95 	if ( !acl_check_modlist( op, e, modlist )) {
96 		return LDAP_INSUFFICIENT_ACCESS;
97 	}
98 
99 	/* save_attrs will be disposed of by caller */
100 	save_attrs = e->e_attrs;
101 	e->e_attrs = attrs_dup( e->e_attrs );
102 
103 	for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
104 		int match;
105 		mod = &ml->sml_mod;
106 		switch( mod->sm_op ) {
107 		case LDAP_MOD_ADD:
108 		case LDAP_MOD_REPLACE:
109 			if ( mod->sm_desc == slap_schema.si_ad_structuralObjectClass ) {
110 				value_match( &match, slap_schema.si_ad_structuralObjectClass,
111 					slap_schema.si_ad_structuralObjectClass->
112 						ad_type->sat_equality,
113 					SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
114 					&mod->sm_values[0], &scbva[0], text );
115 				if ( !match ) glue_attr_delete = 1;
116 			}
117 		}
118 		if ( glue_attr_delete )
119 			break;
120 	}
121 
122 	if ( glue_attr_delete ) {
123 		Attribute	**app = &e->e_attrs;
124 		while ( *app != NULL ) {
125 			if ( !is_at_operational( (*app)->a_desc->ad_type )) {
126 				Attribute *save = *app;
127 				*app = (*app)->a_next;
128 				attr_free( save );
129 				continue;
130 			}
131 			app = &(*app)->a_next;
132 		}
133 	}
134 
135 	for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
136 		mod = &ml->sml_mod;
137 		got_delete = 0;
138 
139 		switch ( mod->sm_op ) {
140 		case LDAP_MOD_ADD:
141 			Debug(LDAP_DEBUG_ARGS,
142 				  "wt_modify_internal: add %s\n",
143 				  mod->sm_desc->ad_cname.bv_val );
144 			err = modify_add_values( e, mod, get_permissiveModify(op),
145 									 text, textbuf, textlen );
146 			if( err != LDAP_SUCCESS ) {
147 				Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
148 					  err, *text );
149 			}
150 			break;
151 
152 		case LDAP_MOD_DELETE:
153 			if ( glue_attr_delete ) {
154 				err = LDAP_SUCCESS;
155 				break;
156 			}
157 
158 			Debug(LDAP_DEBUG_ARGS,
159 				  "wt_modify_internal: delete %s\n",
160 				  mod->sm_desc->ad_cname.bv_val );
161 			err = modify_delete_values( e, mod, get_permissiveModify(op),
162 										text, textbuf, textlen );
163 			if( err != LDAP_SUCCESS ) {
164 				Debug(LDAP_DEBUG_ARGS,
165 					  "wt_modify_internal: %d %s\n", err, *text );
166 			} else {
167 				got_delete = 1;
168 			}
169 			break;
170 
171 		case LDAP_MOD_REPLACE:
172 			Debug(LDAP_DEBUG_ARGS,
173 				  "wt_modify_internal: replace %s\n",
174 				  mod->sm_desc->ad_cname.bv_val );
175 			err = modify_replace_values( e, mod, get_permissiveModify(op),
176 										 text, textbuf, textlen );
177 			if( err != LDAP_SUCCESS ) {
178 				Debug(LDAP_DEBUG_ARGS,
179 					  "wt_modify_internal: %d %s\n", err, *text );
180 			} else {
181 				got_delete = 1;
182 			}
183 			break;
184 
185 		case LDAP_MOD_INCREMENT:
186 			Debug(LDAP_DEBUG_ARGS,
187 				  "wt_modify_internal: increment %s\n",
188 				  mod->sm_desc->ad_cname.bv_val );
189 			err = modify_increment_values( e, mod, get_permissiveModify(op),
190 										   text, textbuf, textlen );
191 			if( err != LDAP_SUCCESS ) {
192 				Debug(LDAP_DEBUG_ARGS,
193 					  "wt_modify_internal: %d %s\n",
194 					  err, *text );
195 			} else {
196 				got_delete = 1;
197 			}
198 			break;
199 
200 		case SLAP_MOD_SOFTADD:
201 			Debug(LDAP_DEBUG_ARGS,
202 				  "wt_modify_internal: softadd %s\n",
203 				  mod->sm_desc->ad_cname.bv_val );
204 			/* Avoid problems in index_add_mods()
205 			 * We need to add index if necessary.
206 			 */
207 			mod->sm_op = LDAP_MOD_ADD;
208 
209 			err = modify_add_values( e, mod, get_permissiveModify(op),
210 				text, textbuf, textlen );
211 
212 			mod->sm_op = SLAP_MOD_SOFTADD;
213 
214 			if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
215 				err = LDAP_SUCCESS;
216 			}
217 
218 			if( err != LDAP_SUCCESS ) {
219 				Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
220 					err, *text );
221 			}
222 			break;
223 
224 		case SLAP_MOD_SOFTDEL:
225 			Debug(LDAP_DEBUG_ARGS,
226 				  "wt_modify_internal: softdel %s\n",
227 				  mod->sm_desc->ad_cname.bv_val );
228 			/* Avoid problems in index_delete_mods()
229 			 * We need to add index if necessary.
230 			 */
231 			mod->sm_op = LDAP_MOD_DELETE;
232 
233 			err = modify_delete_values( e, mod, get_permissiveModify(op),
234 										text, textbuf, textlen );
235 
236 			mod->sm_op = SLAP_MOD_SOFTDEL;
237 
238 			if ( err == LDAP_SUCCESS ) {
239 				got_delete = 1;
240 			} else if ( err == LDAP_NO_SUCH_ATTRIBUTE ) {
241 				err = LDAP_SUCCESS;
242 			}
243 
244 			if( err != LDAP_SUCCESS ) {
245 				Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
246 					  err, *text );
247 			}
248 			break;
249 
250 		case SLAP_MOD_ADD_IF_NOT_PRESENT:
251 			if ( attr_find( e->e_attrs, mod->sm_desc ) != NULL ) {
252 				/* skip */
253 				err = LDAP_SUCCESS;
254 				break;
255 			}
256 
257 			Debug(LDAP_DEBUG_ARGS,
258 				  "wt_modify_internal: add_if_not_present %s\n",
259 				  mod->sm_desc->ad_cname.bv_val );
260 			/* Avoid problems in index_add_mods()
261 			 * We need to add index if necessary.
262 			 */
263 			mod->sm_op = LDAP_MOD_ADD;
264 
265 			err = modify_add_values( e, mod, get_permissiveModify(op),
266 									 text, textbuf, textlen );
267 
268 			mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
269 
270 			if( err != LDAP_SUCCESS ) {
271 				Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
272 					  err, *text );
273 			}
274 			break;
275 
276 		default:
277 			Debug(LDAP_DEBUG_ANY, "wt_modify_internal: invalid op %d\n",
278 				  mod->sm_op );
279 			*text = "Invalid modify operation";
280 			err = LDAP_OTHER;
281 			Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
282 				  err, *text );
283 		}
284 
285 		if ( err != LDAP_SUCCESS ) {
286 			attrs_free( e->e_attrs );
287 			e->e_attrs = save_attrs;
288 			/* unlock entry, delete from cache */
289 			return err;
290 		}
291 
292 		/* If objectClass was modified, reset the flags */
293 		if ( mod->sm_desc == slap_schema.si_ad_objectClass ) {
294 			e->e_ocflags = 0;
295 		}
296 
297 		if ( glue_attr_delete ) e->e_ocflags = 0;
298 
299 
300 		/* check if modified attribute was indexed
301 		 * but not in case of NOOP... */
302 		if ( !op->o_noop ) {
303 			wt_modify_idxflags( op, mod->sm_desc, got_delete, e->e_attrs, save_attrs );
304 		}
305 
306 	}
307 
308 	/* check that the entry still obeys the schema */
309 	ap = NULL;
310 	rc = entry_schema_check( op, e, save_attrs, get_relax(op), 0, &ap,
311 		text, textbuf, textlen );
312 	if ( rc != LDAP_SUCCESS || op->o_noop ) {
313 		attrs_free( e->e_attrs );
314 		/* clear the indexing flags */
315 		for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
316 			ap->a_flags &= ~(SLAP_ATTR_IXADD|SLAP_ATTR_IXDEL);
317 		}
318 		e->e_attrs = save_attrs;
319 
320 		if ( rc != LDAP_SUCCESS ) {
321 			Debug( LDAP_DEBUG_ANY,
322 				"entry failed schema check: %s\n",
323 				*text );
324 		}
325 
326 		/* if NOOP then silently revert to saved attrs */
327 		return rc;
328 	}
329 
330 	/* structuralObjectClass modified! */
331 	if ( ap ) {
332 		assert( ap->a_desc == slap_schema.si_ad_structuralObjectClass );
333 		if ( !op->o_noop ) {
334 			wt_modify_idxflags( op, slap_schema.si_ad_structuralObjectClass,
335 								1, e->e_attrs, save_attrs );
336 		}
337 	}
338 
339 	/* update the indices of the modified attributes */
340 
341 	/* start with deleting the old index entries */
342 	for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
343 		if ( ap->a_flags & SLAP_ATTR_IXDEL ) {
344 			struct berval *vals;
345 			Attribute *a2;
346 			ap->a_flags &= ~SLAP_ATTR_IXDEL;
347 			a2 = attr_find( e->e_attrs, ap->a_desc );
348 			if ( a2 ) {
349 				/* need to detect which values were deleted */
350 				int i, j;
351 				/* let add know there were deletes */
352 				if ( a2->a_flags & SLAP_ATTR_IXADD )
353 					a2->a_flags |= SLAP_ATTR_IXDEL;
354 				vals = op->o_tmpalloc( (ap->a_numvals + 1) *
355 					sizeof(struct berval), op->o_tmpmemctx );
356 				j = 0;
357 				for ( i=0; i < ap->a_numvals; i++ ) {
358 					rc = attr_valfind( a2, SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
359 						&ap->a_nvals[i], NULL, op->o_tmpmemctx );
360 					/* Save deleted values */
361 					if ( rc == LDAP_NO_SUCH_ATTRIBUTE )
362 						vals[j++] = ap->a_nvals[i];
363 				}
364 				BER_BVZERO(vals+j);
365 			} else {
366 				/* attribute was completely deleted */
367 				vals = ap->a_nvals;
368 			}
369 			rc = 0;
370 			if ( !BER_BVISNULL( vals )) {
371 				rc = wt_index_values( op, wc, ap->a_desc,
372 									  vals, e->e_id, SLAP_INDEX_DELETE_OP );
373 				if ( rc != LDAP_SUCCESS ) {
374 					Debug( LDAP_DEBUG_ANY,
375 						   "%s: attribute \"%s\" index delete failure\n",
376 						   op->o_log_prefix, ap->a_desc->ad_cname.bv_val );
377 					attrs_free( e->e_attrs );
378 					e->e_attrs = save_attrs;
379 				}
380 			}
381 			if ( vals != ap->a_nvals )
382 				op->o_tmpfree( vals, op->o_tmpmemctx );
383 			if ( rc ) return rc;
384 		}
385 	}
386 
387 	/* add the new index entries */
388 	for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
389 		if (ap->a_flags & SLAP_ATTR_IXADD) {
390 			ap->a_flags &= ~SLAP_ATTR_IXADD;
391 			if ( ap->a_flags & SLAP_ATTR_IXDEL ) {
392 				/* if any values were deleted, we must readd index
393 				 * for all remaining values.
394 				 */
395 				ap->a_flags &= ~SLAP_ATTR_IXDEL;
396 				rc = wt_index_values( op, wc, ap->a_desc, ap->a_nvals,
397 									  e->e_id, SLAP_INDEX_ADD_OP );
398 			} else {
399 				int found = 0;
400 				/* if this was only an add, we only need to index
401 				 * the added values.
402 				 */
403 				for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
404 					struct berval *vals;
405 					if ( ml->sml_desc != ap->a_desc || !ml->sml_numvals )
406 						continue;
407 					found = 1;
408 					switch( ml->sml_op ) {
409 					case LDAP_MOD_ADD:
410 					case LDAP_MOD_REPLACE:
411 					case LDAP_MOD_INCREMENT:
412 					case SLAP_MOD_SOFTADD:
413 					case SLAP_MOD_ADD_IF_NOT_PRESENT:
414 						if ( ml->sml_op == LDAP_MOD_INCREMENT )
415 							vals = ap->a_nvals;
416 						else if ( ml->sml_nvalues )
417 							vals = ml->sml_nvalues;
418 						else
419 							vals = ml->sml_values;
420 						rc = wt_index_values( op, wc, ap->a_desc,
421 											  vals, e->e_id, SLAP_INDEX_ADD_OP );
422 						break;
423 					}
424 					if ( rc )
425 						break;
426 				}
427 				/* This attr was affected by a modify of a subtype, so
428 				 * there was no direct match in the modlist. Just readd
429 				 * all of its values.
430 				 */
431 				if ( !found ) {
432 					rc = wt_index_values( op, wc, ap->a_desc, ap->a_nvals,
433 										  e->e_id, SLAP_INDEX_ADD_OP );
434 				}
435 			}
436 			if ( rc != LDAP_SUCCESS ) {
437 				Debug( LDAP_DEBUG_ANY,
438 				       "%s: attribute \"%s\" index add failure\n",
439 					   op->o_log_prefix, ap->a_desc->ad_cname.bv_val );
440 				attrs_free( e->e_attrs );
441 				e->e_attrs = save_attrs;
442 				return rc;
443 			}
444 		}
445 	}
446 
447 	return rc;
448 }
449 
450 int
wt_modify(Operation * op,SlapReply * rs)451 wt_modify( Operation *op, SlapReply *rs )
452 {
453 	struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
454 	wt_ctx *wc = NULL;
455 	Entry		*e = NULL;
456 	int		manageDSAit = get_manageDSAit( op );
457 	char textbuf[SLAP_TEXT_BUFLEN];
458 	size_t textlen = sizeof textbuf;
459 	Entry		dummy = {0};
460 
461 	LDAPControl **preread_ctrl = NULL;
462 	LDAPControl **postread_ctrl = NULL;
463 	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
464 	int num_ctrls = 0;
465 
466 	int rc;
467 
468 	Debug( LDAP_DEBUG_ARGS, LDAP_XSTRING(wt_modify) ": %s\n",
469 		   op->o_req_dn.bv_val );
470 
471 	if( op->o_txnSpec && txn_preop( op, rs ))
472 		return rs->sr_err;
473 
474 	ctrls[num_ctrls] = NULL;
475 
476 	wc = wt_ctx_get(op, wi);
477 	if( !wc ){
478         Debug( LDAP_DEBUG_ANY,
479 			   LDAP_XSTRING(wt_add)
480 			   ": wt_ctx_get failed\n" );
481 		rs->sr_err = LDAP_OTHER;
482 		rs->sr_text = "internal error";
483         send_ldap_result( op, rs );
484         return rs->sr_err;
485 	}
486 
487 	/* Don't touch the opattrs, if this is a contextCSN update
488 	 * initiated from updatedn */
489 	if ( !be_isupdate(op) || !op->orm_modlist || op->orm_modlist->sml_next ||
490 		 op->orm_modlist->sml_desc != slap_schema.si_ad_contextCSN ) {
491 
492 		slap_mods_opattrs( op, &op->orm_modlist, 1 );
493 	}
494 
495 	/* get entry */
496 	rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
497 	switch( rc ) {
498 	case 0:
499 		break;
500 	case WT_NOTFOUND:
501 		Debug( LDAP_DEBUG_ARGS,
502 			   "<== " LDAP_XSTRING(wt_delete)
503 			   ": no such object %s\n",
504 			   op->o_req_dn.bv_val );
505 		/* TODO: lookup referrals */
506 		rs->sr_err = LDAP_NO_SUCH_OBJECT;
507 		goto return_results;
508 	default:
509 		Debug( LDAP_DEBUG_ANY,
510 			   LDAP_XSTRING(wt_modify)
511 			   ": wt_dn2entry failed (%d)\n",
512 			   rc );
513 		rs->sr_err = LDAP_OTHER;
514 		rs->sr_text = "internal error";
515 		goto return_results;
516 	}
517 
518 	if ( !manageDSAit && is_entry_referral( e ) ) {
519 		/* entry is a referral, don't allow modify */
520 		rs->sr_ref = get_entry_referrals( op, e );
521 
522 		Debug( LDAP_DEBUG_TRACE,
523 			   LDAP_XSTRING(wt_modify) ": entry is referral\n" );
524 
525 		rs->sr_err = LDAP_REFERRAL;
526 		rs->sr_matched = e->e_name.bv_val;
527 		rs->sr_flags = REP_REF_MUSTBEFREED;
528 		send_ldap_result( op, rs );
529 		rs->sr_matched = NULL;
530 		goto done;
531 	}
532 
533 	if ( get_assert( op ) &&
534 		 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
535 	{
536 		rs->sr_err = LDAP_ASSERTION_FAILED;
537 		goto return_results;
538 	}
539 
540 	if( op->o_preread ) {
541 		if( preread_ctrl == NULL ) {
542 			preread_ctrl = &ctrls[num_ctrls++];
543 			ctrls[num_ctrls] = NULL;
544 		}
545 		if ( slap_read_controls( op, rs, e,
546 			&slap_pre_read_bv, preread_ctrl ) )
547 		{
548 			Debug( LDAP_DEBUG_TRACE,
549 				"<=- " LDAP_XSTRING(wt_modify) ": pre-read "
550 				"failed!\n" );
551 			if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
552 				/* FIXME: is it correct to abort
553 				 * operation if control fails? */
554 				goto return_results;
555 			}
556 		}
557 	}
558 
559 	/* begin transaction */
560 	rc = wc->session->begin_transaction(wc->session, NULL);
561 	if( rc ) {
562 		Debug( LDAP_DEBUG_TRACE,
563 			   LDAP_XSTRING(wt_add) ": begin_transaction failed: %s (%d)\n",
564 			   wiredtiger_strerror(rc), rc );
565 		rs->sr_err = LDAP_OTHER;
566 		rs->sr_text = "begin_transaction failed";
567 		goto return_results;
568 	}
569 	Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(wt_modify) ": session id: %p\n",
570 		   wc->session );
571 
572 	/* Modify the entry */
573 	dummy = *e;
574 	rs->sr_err = wt_modify_internal( op, wc, op->orm_modlist,
575 									 &dummy, &rs->sr_text, textbuf, textlen );
576 	if( rs->sr_err != LDAP_SUCCESS ) {
577 		Debug( LDAP_DEBUG_TRACE,
578 			   LDAP_XSTRING(wt_modify) ": modify failed (%d)\n",
579 			   rs->sr_err );
580 		/* Only free attrs if they were dup'd.  */
581 		if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
582 		goto return_results;
583 	}
584 
585 	/* change the entry itself */
586 	rs->sr_err = wt_id2entry_update( op, wc->session, &dummy );
587 	if ( rs->sr_err != 0 ) {
588 		Debug( LDAP_DEBUG_TRACE,
589 			   LDAP_XSTRING(wt_modify) ": id2entry update failed " "(%d)\n",
590 			   rs->sr_err );
591 		if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ) {
592 			rs->sr_text = "entry too big";
593 		} else {
594 			rs->sr_err = LDAP_OTHER;
595 			rs->sr_text = "entry update failed";
596 		}
597 		goto return_results;
598 	}
599 
600 	if( op->o_noop ) {
601 		wc->session->rollback_transaction(wc->session, NULL);
602 		rs->sr_err = LDAP_X_NO_OPERATION;
603 		goto return_results;
604 	}
605 
606 	/* Only free attrs if they were dup'd.  */
607 	if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
608 
609 	rc = wc->session->commit_transaction(wc->session, NULL);
610 	if( rc ) {
611 		Debug( LDAP_DEBUG_TRACE,
612 			   "<== " LDAP_XSTRING(wt_modify)
613 			   ": commit failed: %s (%d)\n",
614 			   wiredtiger_strerror(rc), rc );
615 		rs->sr_err = LDAP_OTHER;
616 		rs->sr_text = "commit failed";
617 		goto return_results;
618 	}
619 
620 	Debug( LDAP_DEBUG_TRACE,
621 		   LDAP_XSTRING(wt_modify) ": updated%s id=%08lx dn=\"%s\"\n",
622 		   op->o_noop ? " (no-op)" : "",
623 		   dummy.e_id, op->o_req_dn.bv_val );
624 
625 	if( op->o_postread ) {
626 		if( postread_ctrl == NULL ) {
627 			postread_ctrl = &ctrls[num_ctrls++];
628 			ctrls[num_ctrls] = NULL;
629 		}
630 		if( slap_read_controls( op, rs, &dummy,
631 								&slap_post_read_bv, postread_ctrl ) )
632 		{
633 			Debug( LDAP_DEBUG_TRACE,
634 				   "<=- " LDAP_XSTRING(wt_modify)
635 				   ": post-read failed!\n" );
636 			if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
637 				/* FIXME: is it correct to abort
638 				 * operation if control fails? */
639 				goto return_results;
640 			}
641 		}
642 	}
643 	if( num_ctrls ) rs->sr_ctrls = ctrls;
644 
645 	rs->sr_err = LDAP_SUCCESS;
646 	rs->sr_text = NULL;
647 
648 return_results:
649 	if( dummy.e_attrs ) {
650 		attrs_free( dummy.e_attrs );
651 	}
652 	send_ldap_result( op, rs );
653 
654 done:
655 	slap_graduate_commit_csn( op );
656 
657 	if( e != NULL ) {
658 		wt_entry_return( e );
659 	}
660 
661 	if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
662 		slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
663 		slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
664 	}
665 	if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
666 		slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
667 		slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
668 	}
669 
670 	rs->sr_text = NULL;
671 
672 	return rs->sr_err;
673 }
674 
675 /*
676  * Local variables:
677  * indent-tabs-mode: t
678  * tab-width: 4
679  * c-basic-offset: 4
680  * End:
681  */
682