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