search.c revision 1.3 1 /* $NetBSD: search.c,v 1.3 2025/09/05 21:16:32 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-2024 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 (at) 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: search.c,v 1.3 2025/09/05 21:16:32 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 "idl.h"
34
35 static int search_aliases(
36 Operation *op,
37 SlapReply *rs,
38 Entry *e,
39 WT_SESSION *session,
40 ID *ids,
41 ID *scopes,
42 ID *stack )
43 {
44 /* TODO: search_aliases does not implement yet. */
45 WT_IDL_ZERO( ids );
46 return 0;
47 }
48
49 static int base_candidate(
50 BackendDB *be,
51 Entry *e,
52 ID *ids )
53 {
54 Debug(LDAP_DEBUG_ARGS,
55 "base_candidate: base: \"%s\" (0x%08lx)\n",
56 e->e_nname.bv_val, (long) e->e_id );
57
58 ids[0] = 1;
59 ids[1] = e->e_id;
60 return 0;
61 }
62
63 /* Look for "objectClass Present" in this filter.
64 * Also count depth of filter tree while we're at it.
65 */
66 static int oc_filter(
67 Filter *f,
68 int cur,
69 int *max )
70 {
71 int rc = 0;
72
73 assert( f != NULL );
74
75 if( cur > *max ) *max = cur;
76
77 switch( f->f_choice ) {
78 case LDAP_FILTER_PRESENT:
79 if (f->f_desc == slap_schema.si_ad_objectClass) {
80 rc = 1;
81 }
82 break;
83
84 case LDAP_FILTER_AND:
85 case LDAP_FILTER_OR:
86 cur++;
87 for ( f=f->f_and; f; f=f->f_next ) {
88 (void) oc_filter(f, cur, max);
89 }
90 break;
91
92 default:
93 break;
94 }
95 return rc;
96 }
97
98 static void search_stack_free( void *key, void *data )
99 {
100 ber_memfree_x(data, NULL);
101 }
102
103 static void *search_stack( Operation *op )
104 {
105 struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
106 void *ret = NULL;
107
108 if ( op->o_threadctx ) {
109 ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)search_stack,
110 &ret, NULL );
111 } else {
112 ret = wi->wi_search_stack;
113 }
114
115 if ( !ret ) {
116 ret = ch_malloc( wi->wi_search_stack_depth * WT_IDL_UM_SIZE
117 * sizeof( ID ) );
118 if ( op->o_threadctx ) {
119 ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)search_stack,
120 ret, search_stack_free, NULL, NULL );
121 } else {
122 wi->wi_search_stack = ret;
123 }
124 }
125 return ret;
126 }
127
128 static int search_candidates(
129 Operation *op,
130 SlapReply *rs,
131 Entry *e,
132 wt_ctx *wc,
133 ID *ids,
134 ID *scopes )
135 {
136 struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
137 int rc, depth = 1;
138 Filter f, rf, xf, nf;
139 ID *stack;
140 AttributeAssertion aa_ref = ATTRIBUTEASSERTION_INIT;
141 Filter sf;
142 AttributeAssertion aa_subentry = ATTRIBUTEASSERTION_INIT;
143
144 Debug(LDAP_DEBUG_TRACE,
145 "wt_search_candidates: base=\"%s\" (0x%08lx) scope=%d\n",
146 e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope );
147
148 xf.f_or = op->oq_search.rs_filter;
149 xf.f_choice = LDAP_FILTER_OR;
150 xf.f_next = NULL;
151
152 /* If the user's filter uses objectClass=*,
153 * these clauses are redundant.
154 */
155 if (!oc_filter(op->oq_search.rs_filter, 1, &depth)
156 && !get_subentries_visibility(op)) {
157 if( !get_manageDSAit(op) && !get_domainScope(op) ) {
158 /* match referral objects */
159 struct berval bv_ref = BER_BVC( "referral" );
160 rf.f_choice = LDAP_FILTER_EQUALITY;
161 rf.f_ava = &aa_ref;
162 rf.f_av_desc = slap_schema.si_ad_objectClass;
163 rf.f_av_value = bv_ref;
164 rf.f_next = xf.f_or;
165 xf.f_or = &rf;
166 depth++;
167 }
168 }
169
170 f.f_next = NULL;
171 f.f_choice = LDAP_FILTER_AND;
172 f.f_and = &nf;
173 /* Dummy; we compute scope separately now */
174 nf.f_choice = SLAPD_FILTER_COMPUTED;
175 nf.f_result = LDAP_SUCCESS;
176 nf.f_next = ( xf.f_or == op->oq_search.rs_filter )
177 ? op->oq_search.rs_filter : &xf ;
178 /* Filter depth increased again, adding dummy clause */
179 depth++;
180
181 if( get_subentries_visibility( op ) ) {
182 struct berval bv_subentry = BER_BVC( "subentry" );
183 sf.f_choice = LDAP_FILTER_EQUALITY;
184 sf.f_ava = &aa_subentry;
185 sf.f_av_desc = slap_schema.si_ad_objectClass;
186 sf.f_av_value = bv_subentry;
187 sf.f_next = nf.f_next;
188 nf.f_next = &sf;
189 }
190
191 /* Allocate IDL stack, plus 1 more for former tmp */
192 if ( depth+1 > wi->wi_search_stack_depth ) {
193 stack = ch_malloc( (depth + 1) * WT_IDL_UM_SIZE * sizeof( ID ) );
194 } else {
195 stack = search_stack( op );
196 }
197
198 if( op->ors_deref & LDAP_DEREF_SEARCHING ) {
199 rc = search_aliases( op, rs, e, wc->session, ids, scopes, stack );
200 if ( WT_IDL_IS_ZERO( ids ) && rc == LDAP_SUCCESS )
201 rc = wt_dn2idl( op, wc, &e->e_nname, e, ids, stack );
202 } else {
203 rc = wt_dn2idl(op, wc, &e->e_nname, e, ids, stack );
204 }
205
206 if ( rc == LDAP_SUCCESS ) {
207 rc = wt_filter_candidates( op, wc, &f, ids,
208 stack, stack+WT_IDL_UM_SIZE );
209 }
210
211 if ( depth+1 > wi->wi_search_stack_depth ) {
212 ch_free( stack );
213 }
214
215 if( rc ) {
216 Debug(LDAP_DEBUG_TRACE,
217 "wt_search_candidates: failed (rc=%d)\n", rc );
218
219 } else {
220 Debug(LDAP_DEBUG_TRACE,
221 "wt_search_candidates: id=%ld first=%ld last=%ld\n",
222 (long) ids[0],
223 (long) WT_IDL_FIRST(ids),
224 (long) WT_IDL_LAST(ids));
225 }
226 return 0;
227 }
228
229 static int
230 parse_paged_cookie( Operation *op, SlapReply *rs )
231 {
232 int rc = LDAP_SUCCESS;
233 PagedResultsState *ps = op->o_pagedresults_state;
234
235 /* this function must be invoked only if the pagedResults
236 * control has been detected, parsed and partially checked
237 * by the frontend */
238 assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
239
240 /* cookie decoding/checks deferred to backend... */
241 if ( ps->ps_cookieval.bv_len ) {
242 PagedResultsCookie reqcookie;
243 if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
244 /* bad cookie */
245 rs->sr_text = "paged results cookie is invalid";
246 rc = LDAP_PROTOCOL_ERROR;
247 goto done;
248 }
249
250 memcpy( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
251
252 if ( reqcookie > ps->ps_cookie ) {
253 /* bad cookie */
254 rs->sr_text = "paged results cookie is invalid";
255 rc = LDAP_PROTOCOL_ERROR;
256 goto done;
257
258 } else if ( reqcookie < ps->ps_cookie ) {
259 rs->sr_text = "paged results cookie is invalid or old";
260 rc = LDAP_UNWILLING_TO_PERFORM;
261 goto done;
262 }
263
264 } else {
265 /* we're going to use ps_cookie */
266 op->o_conn->c_pagedresults_state.ps_cookie = 0;
267 }
268
269 done:;
270
271 return rc;
272 }
273
274 static void
275 send_paged_response(
276 Operation *op,
277 SlapReply *rs,
278 ID *lastid,
279 int tentries )
280 {
281 LDAPControl *ctrls[2];
282 BerElementBuffer berbuf;
283 BerElement *ber = (BerElement *)&berbuf;
284 PagedResultsCookie respcookie;
285 struct berval cookie;
286
287 Debug(LDAP_DEBUG_ARGS,
288 "send_paged_response: lastid=0x%08lx nentries=%d\n",
289 lastid ? *lastid : 0, rs->sr_nentries );
290
291 ctrls[1] = NULL;
292
293 ber_init2( ber, NULL, LBER_USE_DER );
294
295 if ( lastid ) {
296 respcookie = ( PagedResultsCookie )(*lastid);
297 cookie.bv_len = sizeof( respcookie );
298 cookie.bv_val = (char *)&respcookie;
299
300 } else {
301 respcookie = ( PagedResultsCookie )0;
302 BER_BVSTR( &cookie, "" );
303 }
304
305 op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
306 op->o_conn->c_pagedresults_state.ps_count =
307 ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
308 rs->sr_nentries;
309
310 /* return size of 0 -- no estimate */
311 ber_printf( ber, "{iO}", 0, &cookie );
312
313 ctrls[0] = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx );
314 if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
315 goto done;
316 }
317
318 ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
319 ctrls[0]->ldctl_iscritical = 0;
320
321 slap_add_ctrls( op, rs, ctrls );
322 rs->sr_err = LDAP_SUCCESS;
323 send_ldap_result( op, rs );
324
325 done:
326 (void) ber_free_buf( ber );
327 }
328
329 int
330 wt_search( Operation *op, SlapReply *rs )
331 {
332 struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
333 ID id, cursor;
334 ID lastid = NOID;
335 int manageDSAit;
336 wt_ctx *wc;
337 int rc = LDAP_OTHER;
338 Entry *e = NULL;
339 Entry *ae = NULL;
340 Entry *base = NULL;
341 slap_mask_t mask;
342 time_t stoptime;
343
344 ID candidates[WT_IDL_UM_SIZE];
345 ID scopes[WT_IDL_DB_SIZE];
346 int tentries = 0;
347 unsigned nentries = 0;
348
349 Debug( LDAP_DEBUG_ARGS, "==> wt_search: %s\n", op->o_req_dn.bv_val );
350
351 manageDSAit = get_manageDSAit( op );
352
353 wc = wt_ctx_get(op, wi);
354 if( !wc ){
355 Debug( LDAP_DEBUG_ANY,
356 "wt_search: wt_ctx_get failed: %d\n", rc );
357 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
358 return rc;
359 }
360
361 /* get entry */
362 rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
363 switch( rc ) {
364 case 0:
365 break;
366 case WT_NOTFOUND:
367 rc = wt_dn2aentry(op->o_bd, wc, &op->o_req_ndn, &ae);
368 break;
369 default:
370 /* TODO: error handling */
371 Debug( LDAP_DEBUG_ANY,
372 "<== wt_search: error at wt_dn2entry() rc=%d\n", rc );
373 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
374 goto done;
375 }
376
377 if ( op->ors_deref & LDAP_DEREF_FINDING ) {
378 /* not implement yet */
379 }
380
381 if ( e == NULL ) {
382 if ( ae ) {
383 struct berval matched_dn = BER_BVNULL;
384 /* found ancestor entry */
385 if ( access_allowed( op, ae,
386 slap_schema.si_ad_entry,
387 NULL, ACL_DISCLOSE, NULL ) ) {
388 BerVarray erefs = NULL;
389 ber_dupbv( &matched_dn, &ae->e_name );
390 erefs = is_entry_referral( ae )
391 ? get_entry_referrals( op, ae )
392 : NULL;
393 rs->sr_err = LDAP_REFERRAL;
394 rs->sr_matched = matched_dn.bv_val;
395 if ( erefs ) {
396 rs->sr_ref = referral_rewrite( erefs, &matched_dn,
397 &op->o_req_dn, op->oq_search.rs_scope );
398 ber_bvarray_free( erefs );
399 }
400 Debug( LDAP_DEBUG_ARGS,
401 "wt_search: ancestor is referral\n");
402 rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
403 send_ldap_result( op, rs );
404 goto done;
405 }
406 }
407 Debug( LDAP_DEBUG_ARGS,
408 "wt_search: no such object %s\n",
409 op->o_req_dn.bv_val);
410 rs->sr_err = LDAP_NO_SUCH_OBJECT;
411 send_ldap_result( op, rs );
412 goto done;
413 }
414
415 /* NOTE: __NEW__ "search" access is required
416 * on searchBase object */
417 if ( ! access_allowed_mask( op, e, slap_schema.si_ad_entry,
418 NULL, ACL_SEARCH, NULL, &mask ) )
419 {
420 if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
421 rs->sr_err = LDAP_NO_SUCH_OBJECT;
422 } else {
423 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
424 }
425
426 send_ldap_result( op, rs );
427 goto done;
428 }
429
430 if ( !manageDSAit && is_entry_referral( e ) ) {
431 struct berval matched_dn = BER_BVNULL;
432 BerVarray erefs = NULL;
433 ber_dupbv( &matched_dn, &e->e_name );
434 erefs = get_entry_referrals( op, e );
435 rs->sr_err = LDAP_REFERRAL;
436 if ( erefs ) {
437 rs->sr_ref = referral_rewrite( erefs, &matched_dn,
438 &op->o_req_dn, op->oq_search.rs_scope );
439 ber_bvarray_free( erefs );
440 if ( !rs->sr_ref ) {
441 rs->sr_text = "bad_referral object";
442 }
443 }
444 Debug( LDAP_DEBUG_ARGS, "wt_search: entry is referral\n");
445 rs->sr_matched = matched_dn.bv_val;
446 send_ldap_result( op, rs );
447 ber_bvarray_free( rs->sr_ref );
448 rs->sr_ref = NULL;
449 ber_memfree( matched_dn.bv_val );
450 rs->sr_matched = NULL;
451 goto done;
452 }
453
454 if ( get_assert( op ) &&
455 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
456 {
457 rs->sr_err = LDAP_ASSERTION_FAILED;
458 send_ldap_result( op, rs );
459 goto done;
460 }
461
462 /* compute it anyway; root does not use it */
463 stoptime = op->o_time + op->ors_tlimit;
464
465 base = e;
466
467 e = NULL;
468
469 /* select candidates */
470 if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
471 rs->sr_err = base_candidate( op->o_bd, base, candidates );
472 }else{
473 WT_IDL_ZERO( candidates );
474 WT_IDL_ZERO( scopes );
475 rc = search_candidates( op, rs, base,
476 wc, candidates, scopes );
477 switch(rc){
478 case 0:
479 case WT_NOTFOUND:
480 break;
481 default:
482 Debug( LDAP_DEBUG_ANY, "wt_search: error search_candidates\n" );
483 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
484 goto done;
485 }
486 }
487
488 /* start cursor at beginning of candidates.
489 */
490 cursor = 0;
491
492 if ( candidates[0] == 0 ) {
493 Debug( LDAP_DEBUG_TRACE, "wt_search: no candidates\n" );
494 goto nochange;
495 }
496
497 if ( op->ors_limit &&
498 op->ors_limit->lms_s_unchecked != -1 &&
499 WT_IDL_N(candidates) > (unsigned) op->ors_limit->lms_s_unchecked )
500 {
501 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
502 send_ldap_result( op, rs );
503 rs->sr_err = LDAP_SUCCESS;
504 goto done;
505 }
506
507 if ( op->ors_limit == NULL /* isroot == TRUE */ ||
508 !op->ors_limit->lms_s_pr_hide )
509 {
510 tentries = WT_IDL_N(candidates);
511 }
512
513 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
514 /* TODO: pageresult */
515 PagedResultsState *ps = op->o_pagedresults_state;
516 /* deferred cookie parsing */
517 rs->sr_err = parse_paged_cookie( op, rs );
518 if ( rs->sr_err != LDAP_SUCCESS ) {
519 send_ldap_result( op, rs );
520 goto done;
521 }
522
523 cursor = (ID) ps->ps_cookie;
524 if ( cursor && ps->ps_size == 0 ) {
525 rs->sr_err = LDAP_SUCCESS;
526 rs->sr_text = "search abandoned by pagedResult size=0";
527 send_ldap_result( op, rs );
528 goto done;
529 }
530 id = wt_idl_first( candidates, &cursor );
531 if ( id == NOID ) {
532 Debug( LDAP_DEBUG_TRACE, "wt_search: no paged results candidates\n" );
533 send_paged_response( op, rs, &lastid, 0 );
534
535 rs->sr_err = LDAP_OTHER;
536 goto done;
537 }
538 nentries = ps->ps_count;
539 if ( id == (ID)ps->ps_cookie )
540 id = wt_idl_next( candidates, &cursor );
541 goto loop_begin;
542 }
543
544 for ( id = wt_idl_first( candidates, &cursor );
545 id != NOID ; id = wt_idl_next( candidates, &cursor ) )
546 {
547 int scopeok;
548
549 loop_begin:
550
551 /* check for abandon */
552 if ( op->o_abandon ) {
553 rs->sr_err = SLAPD_ABANDON;
554 send_ldap_result( op, rs );
555 goto done;
556 }
557
558 /* mostly needed by internal searches,
559 * e.g. related to syncrepl, for whom
560 * abandon does not get set... */
561 if ( slapd_shutdown ) {
562 rs->sr_err = LDAP_UNAVAILABLE;
563 send_ldap_disconnect( op, rs );
564 goto done;
565 }
566
567 /* check time limit */
568 if ( op->ors_tlimit != SLAP_NO_LIMIT
569 && slap_get_time() > stoptime )
570 {
571 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
572 rs->sr_ref = rs->sr_v2ref;
573 send_ldap_result( op, rs );
574 rs->sr_err = LDAP_SUCCESS;
575 goto done;
576 }
577
578 nentries++;
579
580 fetch_entry_retry:
581
582 rc = wt_id2entry(op->o_bd, wc, id, &e);
583 /* TODO: error handling */
584 if ( e == NULL ) {
585 /* TODO: */
586 goto loop_continue;
587 }
588 if ( is_entry_subentry( e ) ) {
589 if( op->oq_search.rs_scope != LDAP_SCOPE_BASE ) {
590 if(!get_subentries_visibility( op )) {
591 /* only subentries are visible */
592 goto loop_continue;
593 }
594
595 } else if ( get_subentries( op ) &&
596 !get_subentries_visibility( op ))
597 {
598 /* only subentries are visible */
599 goto loop_continue;
600 }
601
602 } else if ( get_subentries_visibility( op )) {
603 /* only subentries are visible */
604 goto loop_continue;
605 }
606
607 scopeok = 0;
608 switch( op->ors_scope ) {
609 case LDAP_SCOPE_BASE:
610 /* This is always true, yes? */
611 if ( id == base->e_id ) scopeok = 1;
612 break;
613 case LDAP_SCOPE_ONELEVEL:
614 scopeok = dnIsSuffixScope(&e->e_nname, &base->e_nname, LDAP_SCOPE_ONELEVEL);
615 break;
616 case LDAP_SCOPE_CHILDREN:
617 if ( id == base->e_id ) break;
618 /* Fall-thru */
619 case LDAP_SCOPE_SUBTREE:
620 scopeok = dnIsSuffix(&e->e_nname, &base->e_nname);
621 break;
622 }
623
624 /* aliases were already dereferenced in candidate list */
625 if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
626 /* but if the search base is an alias, and we didn't
627 * deref it when finding, return it.
628 */
629 if ( is_entry_alias(e) &&
630 ((op->ors_deref & LDAP_DEREF_FINDING) ||
631 !bvmatch(&e->e_nname, &op->o_req_ndn)))
632 {
633 goto loop_continue;
634 }
635 /* TODO: alias handling */
636 }
637
638 /* Not in scope, ignore it */
639 if ( !scopeok )
640 {
641 Debug( LDAP_DEBUG_TRACE, "wt_search: %ld scope not okay\n",
642 (long) id );
643 goto loop_continue;
644 }
645
646 /*
647 * if it's a referral, add it to the list of referrals. only do
648 * this for non-base searches, and don't check the filter
649 * explicitly here since it's only a candidate anyway.
650 */
651 if ( !manageDSAit && op->oq_search.rs_scope != LDAP_SCOPE_BASE
652 && is_entry_referral( e ) )
653 {
654 BerVarray erefs = get_entry_referrals( op, e );
655 rs->sr_ref = referral_rewrite( erefs, &e->e_name, NULL,
656 op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL
657 ? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
658 rs->sr_entry = e;
659 send_search_reference( op, rs );
660 rs->sr_entry = NULL;
661 ber_bvarray_free( rs->sr_ref );
662 ber_bvarray_free( erefs );
663 goto loop_continue;
664 }
665
666 if ( !manageDSAit && is_entry_glue( e )) {
667 goto loop_continue;
668 }
669
670 /* if it matches the filter and scope, send it */
671 rs->sr_err = test_filter( op, e, op->oq_search.rs_filter );
672 if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
673 /* check size limit */
674 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
675 if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size ) {
676 wt_entry_return( e );
677 e = NULL;
678 send_paged_response( op, rs, &lastid, tentries );
679 goto done;
680 }
681 lastid = id;
682 }
683
684 if (e) {
685 /* safe default */
686 rs->sr_attrs = op->oq_search.rs_attrs;
687 rs->sr_operational_attrs = NULL;
688 rs->sr_ctrls = NULL;
689 rs->sr_entry = e;
690 RS_ASSERT( e->e_private != NULL );
691 rs->sr_flags = REP_ENTRY_MUSTRELEASE;
692 rs->sr_err = LDAP_SUCCESS;
693 rs->sr_err = send_search_entry( op, rs );
694 rs->sr_attrs = NULL;
695 rs->sr_entry = NULL;
696 e = NULL;
697
698 switch ( rs->sr_err ) {
699 case LDAP_SUCCESS: /* entry sent ok */
700 break;
701 default: /* entry not sent */
702 break;
703 case LDAP_BUSY:
704 send_ldap_result( op, rs );
705 goto done;
706 case LDAP_UNAVAILABLE:
707 rs->sr_err = LDAP_OTHER;
708 goto done;
709 case LDAP_SIZELIMIT_EXCEEDED:
710 rs->sr_ref = rs->sr_v2ref;
711 send_ldap_result( op, rs );
712 rs->sr_err = LDAP_SUCCESS;
713 goto done;
714 }
715 }
716 } else {
717 Debug( LDAP_DEBUG_TRACE,
718 "wt_search: %ld does not match filter\n", (long) id );
719 }
720
721 loop_continue:
722 if( e ) {
723 wt_entry_return( e );
724 e = NULL;
725 }
726 }
727
728 nochange:
729 rs->sr_ctrls = NULL;
730 rs->sr_ref = rs->sr_v2ref;
731 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL;
732 rs->sr_rspoid = NULL;
733 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
734 send_paged_response( op, rs, NULL, 0 );
735 } else {
736 send_ldap_result( op, rs );
737 }
738
739 rs->sr_err = LDAP_SUCCESS;
740
741 done:
742
743 if( base ) {
744 wt_entry_return( base );
745 }
746
747 if( e ) {
748 wt_entry_return( e );
749 }
750
751 if( ae ) {
752 wt_entry_return( ae );
753 }
754
755 return rs->sr_err;
756 }
757
758 /*
759 * Local variables:
760 * indent-tabs-mode: t
761 * tab-width: 4
762 * c-basic-offset: 4
763 * End:
764 */
765