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