]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/search.c
d2d5812ee559ffa6ca6c0e45562c0b84c787d10f
[openldap] / servers / slapd / back-bdb / search.c
1 /* search.c - search operation */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2005 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
17 #include "portable.h"
18
19 #include <stdio.h>
20 #include <ac/string.h>
21
22 #include "back-bdb.h"
23 #include "idl.h"
24
25 static int base_candidate(
26         BackendDB       *be,
27         Entry   *e,
28         ID              *ids );
29
30 static int search_candidates(
31         Operation *op,
32         SlapReply *rs,
33         Entry *e,
34         u_int32_t locker,
35         ID      *ids,
36         ID      *scopes );
37
38 static int parse_paged_cookie( Operation *op, SlapReply *rs );
39
40 static void send_paged_response( 
41         Operation *op,
42         SlapReply *rs,
43         ID  *lastid,
44         int tentries );
45
46 static int bdb_pfid_cmp( const void *v_id1, const void *v_id2 );
47 static ID* bdb_id_dup( Operation *op, ID *id );
48
49 /* Dereference aliases for a single alias entry. Return the final
50  * dereferenced entry on success, NULL on any failure.
51  */
52 static Entry * deref_base (
53         Operation *op,
54         SlapReply *rs,
55         Entry *e,
56         Entry **matched,
57         u_int32_t locker,
58         DB_LOCK *lock,
59         ID      *tmp,
60         ID      *visited )
61 {
62         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
63         struct berval ndn;
64         EntryInfo *ei;
65         DB_LOCK lockr;
66
67         rs->sr_err = LDAP_ALIAS_DEREF_PROBLEM;
68         rs->sr_text = "maximum deref depth exceeded";
69
70         while (BDB_IDL_N(tmp) < op->o_bd->be_max_deref_depth) {
71                 /* Remember the last entry we looked at, so we can
72                  * report broken links
73                  */
74                 *matched = e;
75
76                 /* If this is part of a subtree or onelevel search,
77                  * have we seen this ID before? If so, quit.
78                  */
79                 if ( visited && bdb_idl_insert( visited, e->e_id ) ) {
80                         e = NULL;
81                         break;
82                 }
83
84                 /* If we've seen this ID during this deref iteration,
85                  * we've hit a loop.
86                  */
87                 if ( bdb_idl_insert( tmp, e->e_id ) ) {
88                         rs->sr_err = LDAP_ALIAS_PROBLEM;
89                         rs->sr_text = "circular alias";
90                         e = NULL;
91                         break;
92                 }
93
94                 /* If there was a problem getting the aliasedObjectName,
95                  * get_alias_dn will have set the error status.
96                  */
97                 if ( get_alias_dn(e, &ndn, &rs->sr_err, &rs->sr_text) ) {
98                         e = NULL;
99                         break;
100                 }
101
102                 rs->sr_err = bdb_dn2entry( op, NULL, &ndn, &ei,
103                         0, locker, &lockr );
104
105                 if ( ei ) {
106                         e = ei->bei_e;
107                 } else {
108                         e = NULL;
109                 }
110
111                 if (!e) {
112                         rs->sr_err = LDAP_ALIAS_PROBLEM;
113                         rs->sr_text = "aliasedObject not found";
114                         break;
115                 }
116
117                 /* Free the previous entry, continue to work with the
118                  * one we just retrieved.
119                  */
120                 bdb_cache_return_entry_r( bdb->bi_dbenv, &bdb->bi_cache,
121                         *matched, lock);
122                 *lock = lockr;
123
124                 /* We found a regular entry. Return this to the caller. The
125                  * entry is still locked for Read.
126                  */
127                 if (!is_entry_alias(e)) {
128                         rs->sr_err = LDAP_SUCCESS;
129                         rs->sr_text = NULL;
130                         break;
131                 }
132         }
133         return e;
134 }
135
136 /* Look for and dereference all aliases within the search scope. Adds
137  * the dereferenced entries to the "ids" list. Requires "stack" to be
138  * able to hold 8 levels of DB_SIZE IDLs. Of course we're hardcoded to
139  * require a minimum of 8 UM_SIZE IDLs so this is never a problem.
140  */
141 static int search_aliases(
142         Operation *op,
143         SlapReply *rs,
144         Entry *e,
145         u_int32_t locker,
146         ID *ids,
147         ID *scopes,
148         ID *stack )
149 {
150         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
151         ID *aliases, *curscop, *subscop, *visited, *newsubs, *oldsubs, *tmp;
152         ID cursora, ida, cursoro, ido, *subscop2;
153         Entry *matched, *a;
154         EntryInfo *ei;
155         struct berval bv_alias = BER_BVC( "alias" );
156         AttributeAssertion aa_alias;
157         Filter  af;
158         DB_LOCK locka, lockr;
159         int first = 1;
160
161         aliases = stack;        /* IDL of all aliases in the database */
162         curscop = aliases + BDB_IDL_DB_SIZE;    /* Aliases in the current scope */
163         subscop = curscop + BDB_IDL_DB_SIZE;    /* The current scope */
164         visited = subscop + BDB_IDL_DB_SIZE;    /* IDs we've seen in this search */
165         newsubs = visited + BDB_IDL_DB_SIZE;    /* New subtrees we've added */
166         oldsubs = newsubs + BDB_IDL_DB_SIZE;    /* Subtrees added previously */
167         tmp = oldsubs + BDB_IDL_DB_SIZE;        /* Scratch space for deref_base() */
168
169         /* A copy of subscop, because subscop gets clobbered by
170          * the bdb_idl_union/intersection routines
171          */
172         subscop2 = tmp + BDB_IDL_DB_SIZE;
173
174         af.f_choice = LDAP_FILTER_EQUALITY;
175         af.f_ava = &aa_alias;
176         af.f_av_desc = slap_schema.si_ad_objectClass;
177         af.f_av_value = bv_alias;
178         af.f_next = NULL;
179
180         /* Find all aliases in database */
181         BDB_IDL_ZERO( aliases );
182         rs->sr_err = bdb_filter_candidates( op, &af, aliases,
183                 curscop, visited );
184         if (rs->sr_err != LDAP_SUCCESS) {
185                 return rs->sr_err;
186         }
187         oldsubs[0] = 1;
188         oldsubs[1] = e->e_id;
189
190         BDB_IDL_ZERO( ids );
191         BDB_IDL_ZERO( visited );
192         BDB_IDL_ZERO( newsubs );
193
194         cursoro = 0;
195         ido = bdb_idl_first( oldsubs, &cursoro );
196
197         for (;;) {
198                 /* Set curscop to only the aliases in the current scope. Start with
199                  * all the aliases, obtain the IDL for the current scope, and then
200                  * get the intersection of these two IDLs. Add the current scope
201                  * to the cumulative list of candidates.
202                  */
203                 BDB_IDL_CPY( curscop, aliases );
204                 rs->sr_err = bdb_dn2idl( op, e, subscop,
205                         subscop2+BDB_IDL_DB_SIZE );
206                 if (first) {
207                         first = 0;
208                 } else {
209                         bdb_cache_return_entry_r (bdb->bi_dbenv, &bdb->bi_cache, e, &locka);
210                 }
211                 BDB_IDL_CPY(subscop2, subscop);
212                 rs->sr_err = bdb_idl_intersection(curscop, subscop);
213                 bdb_idl_union( ids, subscop2 );
214
215                 /* Dereference all of the aliases in the current scope. */
216                 cursora = 0;
217                 for (ida = bdb_idl_first(curscop, &cursora); ida != NOID;
218                         ida = bdb_idl_next(curscop, &cursora))
219                 {
220                         ei = NULL;
221 retry1:
222                         rs->sr_err = bdb_cache_find_id(op, NULL,
223                                 ida, &ei, 0, locker, &lockr );
224                         if (rs->sr_err != LDAP_SUCCESS) {
225                                 if ( rs->sr_err == DB_LOCK_DEADLOCK ||
226                                         rs->sr_err == DB_LOCK_NOTGRANTED ) goto retry1;
227                                 continue;
228                         }
229                         a = ei->bei_e;
230
231                         /* This should only happen if the curscop IDL has maxed out and
232                          * turned into a range that spans IDs indiscriminately
233                          */
234                         if (!is_entry_alias(a)) {
235                                 bdb_cache_return_entry_r (bdb->bi_dbenv, &bdb->bi_cache,
236                                         a, &lockr);
237                                 continue;
238                         }
239
240                         /* Actually dereference the alias */
241                         BDB_IDL_ZERO(tmp);
242                         a = deref_base( op, rs, a, &matched, locker, &lockr,
243                                 tmp, visited );
244                         if (a) {
245                                 /* If the target was not already in our current candidates,
246                                  * make note of it in the newsubs list. Also
247                                  * set it in the scopes list so that bdb_search
248                                  * can check it.
249                                  */
250                                 if (bdb_idl_insert(ids, a->e_id) == 0) {
251                                         bdb_idl_insert(newsubs, a->e_id);
252                                         bdb_idl_insert(scopes, a->e_id);
253                                 }
254                                 bdb_cache_return_entry_r( bdb->bi_dbenv, &bdb->bi_cache,
255                                         a, &lockr);
256
257                         } else if (matched) {
258                                 /* Alias could not be dereferenced, or it deref'd to
259                                  * an ID we've already seen. Ignore it.
260                                  */
261                                 bdb_cache_return_entry_r( bdb->bi_dbenv, &bdb->bi_cache,
262                                         matched, &lockr );
263                                 rs->sr_text = NULL;
264                         }
265                 }
266                 /* If this is a OneLevel search, we're done; oldsubs only had one
267                  * ID in it. For a Subtree search, oldsubs may be a list of scope IDs.
268                  */
269                 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ) break;
270 nextido:
271                 ido = bdb_idl_next( oldsubs, &cursoro );
272                 
273                 /* If we're done processing the old scopes, did we add any new
274                  * scopes in this iteration? If so, go back and do those now.
275                  */
276                 if (ido == NOID) {
277                         if (BDB_IDL_IS_ZERO(newsubs)) break;
278                         BDB_IDL_CPY(oldsubs, newsubs);
279                         BDB_IDL_ZERO(newsubs);
280                         cursoro = 0;
281                         ido = bdb_idl_first( oldsubs, &cursoro );
282                 }
283
284                 /* Find the entry corresponding to the next scope. If it can't
285                  * be found, ignore it and move on. This should never happen;
286                  * we should never see the ID of an entry that doesn't exist.
287                  * Set the name so that the scope's IDL can be retrieved.
288                  */
289                 ei = NULL;
290 sameido:
291                 rs->sr_err = bdb_cache_find_id(op, NULL, ido, &ei,
292                         0, locker, &locka );
293                 if ( rs->sr_err != LDAP_SUCCESS ) {
294                         if ( rs->sr_err == DB_LOCK_DEADLOCK ||
295                                 rs->sr_err == DB_LOCK_NOTGRANTED )
296                                 goto sameido;
297                         goto nextido;
298                 }
299                 e = ei->bei_e;
300         }
301         return rs->sr_err;
302 }
303
304 int
305 bdb_search( Operation *op, SlapReply *rs )
306 {
307         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
308         time_t          stoptime;
309         ID              id, cursor;
310         ID              candidates[BDB_IDL_UM_SIZE];
311         ID              scopes[BDB_IDL_DB_SIZE];
312         Entry           *e = NULL, base, e_root = {0};
313         Entry           *matched = NULL;
314         EntryInfo       *ei, ei_root = {0};
315         struct berval   realbase = BER_BVNULL;
316         int             manageDSAit;
317         int             tentries = 0;
318         ID              lastid = NOID;
319         AttributeName   *attrs;
320
321         u_int32_t       locker = 0;
322         DB_LOCK         lock;
323         struct  bdb_op_info     *opinfo = NULL;
324         DB_TXN                  *ltid = NULL;
325
326         Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(bdb_search) "\n", 0, 0, 0);
327         attrs = op->oq_search.rs_attrs;
328
329         opinfo = (struct bdb_op_info *) op->o_private;
330
331         manageDSAit = get_manageDSAit( op );
332
333         if ( opinfo && opinfo->boi_txn ) {
334                 ltid = opinfo->boi_txn;
335                 locker = TXN_ID( ltid );
336         } else {
337                 rs->sr_err = LOCK_ID( bdb->bi_dbenv, &locker );
338
339                 switch(rs->sr_err) {
340                 case 0:
341                         break;
342                 default:
343                         send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
344                         return rs->sr_err;
345                 }
346         }
347
348         if ( op->o_req_ndn.bv_len == 0 ) {
349                 /* DIT root special case */
350                 ei_root.bei_e = &e_root;
351                 ei_root.bei_parent = &ei_root;
352                 e_root.e_private = &ei_root;
353                 e_root.e_id = 0;
354                 BER_BVSTR( &e_root.e_nname, "" );
355                 BER_BVSTR( &e_root.e_name, "" );
356                 ei = &ei_root;
357                 rs->sr_err = LDAP_SUCCESS;
358         } else {
359 dn2entry_retry:
360                 /* get entry with reader lock */
361                 rs->sr_err = bdb_dn2entry( op, ltid, &op->o_req_ndn, &ei,
362                         1, locker, &lock );
363         }
364
365         switch(rs->sr_err) {
366         case DB_NOTFOUND:
367                 matched = ei->bei_e;
368                 break;
369         case 0:
370                 e = ei->bei_e;
371                 break;
372         case LDAP_BUSY:
373                 send_ldap_error( op, rs, LDAP_BUSY, "ldap server busy" );
374                 if ( !opinfo )
375                         LOCK_ID_FREE (bdb->bi_dbenv, locker );
376                 return LDAP_BUSY;
377         case DB_LOCK_DEADLOCK:
378         case DB_LOCK_NOTGRANTED:
379                 goto dn2entry_retry;
380         default:
381                 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
382                 if ( !opinfo )
383                         LOCK_ID_FREE (bdb->bi_dbenv, locker );
384                 return rs->sr_err;
385         }
386
387         if ( e && (op->ors_deref & LDAP_DEREF_FINDING) && is_entry_alias(e) ) {
388                 BDB_IDL_ZERO(candidates);
389                 e = deref_base( op, rs, e, &matched, locker, &lock,
390                         candidates, NULL );
391         }
392
393         if ( e == NULL ) {
394                 struct berval matched_dn = BER_BVNULL;
395
396                 if ( matched != NULL ) {
397                         BerVarray erefs = NULL;
398
399                         if ( ! access_allowed( op, matched,
400                                                 slap_schema.si_ad_entry,
401                                                 NULL, ACL_DISCLOSE, NULL ) )
402                         {
403                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
404
405                         } else {
406                                 ber_dupbv( &matched_dn, &matched->e_name );
407
408                                 erefs = is_entry_referral( matched )
409                                         ? get_entry_referrals( op, matched )
410                                         : NULL;
411                                 rs->sr_err = LDAP_REFERRAL;
412                                 rs->sr_matched = matched_dn.bv_val;
413                         }
414
415 #ifdef SLAP_ZONE_ALLOC
416                         slap_zn_runlock(bdb->bi_cache.c_zctx, matched);
417 #endif
418                         bdb_cache_return_entry_r (bdb->bi_dbenv, &bdb->bi_cache,
419                                 matched, &lock);
420                         matched = NULL;
421
422                         if ( erefs ) {
423                                 rs->sr_ref = referral_rewrite( erefs, &matched_dn,
424                                         &op->o_req_dn, op->oq_search.rs_scope );
425                                 ber_bvarray_free( erefs );
426                         }
427
428                 } else {
429 #ifdef SLAP_ZONE_ALLOC
430                         slap_zn_runlock(bdb->bi_cache.c_zctx, matched);
431 #endif
432                         rs->sr_ref = referral_rewrite( default_referral,
433                                 NULL, &op->o_req_dn, op->oq_search.rs_scope );
434                         rs->sr_err = LDAP_REFERRAL;
435                 }
436
437                 send_ldap_result( op, rs );
438
439                 if ( !opinfo )
440                         LOCK_ID_FREE (bdb->bi_dbenv, locker );
441                 if ( rs->sr_ref ) {
442                         ber_bvarray_free( rs->sr_ref );
443                         rs->sr_ref = NULL;
444                 }
445                 if ( !BER_BVISNULL( &matched_dn ) ) {
446                         ber_memfree( matched_dn.bv_val );
447                         rs->sr_matched = NULL;
448                 }
449                 return rs->sr_err;
450         }
451
452         if ( ! access_allowed( op, e, slap_schema.si_ad_entry,
453                                 NULL, ACL_DISCLOSE, NULL ) )
454         {
455                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
456
457 #ifdef SLAP_ZONE_ALLOC
458                 slap_zn_runlock(bdb->bi_cache.c_zctx, e);
459 #endif
460                 if ( e != &e_root ) {
461                         bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock);
462                 }
463                 send_ldap_result( op, rs );
464                 return 1;
465         }
466
467         if ( !manageDSAit && e != &e_root && is_entry_referral( e ) ) {
468                 /* entry is a referral, don't allow add */
469                 struct berval matched_dn = BER_BVNULL;
470                 BerVarray erefs = NULL;
471                 
472                 ber_dupbv( &matched_dn, &e->e_name );
473                 erefs = get_entry_referrals( op, e );
474
475                 rs->sr_err = LDAP_REFERRAL;
476
477 #ifdef SLAP_ZONE_ALLOC
478                 slap_zn_runlock(bdb->bi_cache.c_zctx, e);
479 #endif
480                 bdb_cache_return_entry_r( bdb->bi_dbenv, &bdb->bi_cache, e, &lock );
481                 e = NULL;
482
483                 if ( erefs ) {
484                         rs->sr_ref = referral_rewrite( erefs, &matched_dn,
485                                 &op->o_req_dn, op->oq_search.rs_scope );
486                         ber_bvarray_free( erefs );
487
488                         if ( !rs->sr_ref ) {
489                                 rs->sr_text = "bad_referral object";
490                         }
491                 }
492
493                 Debug( LDAP_DEBUG_TRACE,
494                         LDAP_XSTRING(bdb_search) ": entry is referral\n",
495                         0, 0, 0 );
496
497                 rs->sr_matched = matched_dn.bv_val;
498                 send_ldap_result( op, rs );
499
500                 if ( !opinfo ) {
501                         LOCK_ID_FREE (bdb->bi_dbenv, locker );
502                 }
503                 ber_bvarray_free( rs->sr_ref );
504                 rs->sr_ref = NULL;
505                 ber_memfree( matched_dn.bv_val );
506                 rs->sr_matched = NULL;
507                 return 1;
508         }
509
510         if ( get_assert( op ) &&
511                 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
512         {
513                 rs->sr_err = LDAP_ASSERTION_FAILED;
514 #ifdef SLAP_ZONE_ALLOC
515                 slap_zn_runlock(bdb->bi_cache.c_zctx, e);
516 #endif
517                 if ( e != &e_root ) {
518                         bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock);
519                 }
520                 send_ldap_result( op, rs );
521                 return 1;
522         }
523
524         /* compute it anyway; root does not use it */
525         stoptime = op->o_time + op->ors_tlimit;
526
527         /* need normalized dn below */
528         ber_dupbv( &realbase, &e->e_nname );
529
530         /* Copy info to base, must free entry before accessing the database
531          * in search_candidates, to avoid deadlocks.
532          */
533         base.e_private = e->e_private;
534         base.e_nname = realbase;
535         base.e_id = e->e_id;
536
537 #ifdef SLAP_ZONE_ALLOC
538         slap_zn_runlock(bdb->bi_cache.c_zctx, e);
539 #endif
540         if ( e != &e_root ) {
541                 bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock);
542         }
543         e = NULL;
544
545         /* select candidates */
546         if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
547                 rs->sr_err = base_candidate( op->o_bd, &base, candidates );
548
549         } else {
550                 BDB_IDL_ZERO( candidates );
551                 BDB_IDL_ZERO( scopes );
552                 rs->sr_err = search_candidates( op, rs, &base,
553                         locker, candidates, scopes );
554         }
555
556         /* start cursor at beginning of candidates.
557          */
558         cursor = 0;
559
560         if ( candidates[0] == 0 ) {
561                 Debug( LDAP_DEBUG_TRACE,
562                         LDAP_XSTRING(bdb_search) ": no candidates\n",
563                         0, 0, 0 );
564
565                 goto nochange;
566         }
567
568         /* if not root and candidates exceed to-be-checked entries, abort */
569         if ( op->ors_limit      /* isroot == FALSE */ &&
570                 op->ors_limit->lms_s_unchecked != -1 &&
571                 BDB_IDL_N(candidates) > (unsigned) op->ors_limit->lms_s_unchecked )
572         {
573                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
574                 send_ldap_result( op, rs );
575                 rs->sr_err = LDAP_SUCCESS;
576                 goto done;
577         }
578
579         if ( op->ors_limit == NULL      /* isroot == TRUE */ ||
580                 !op->ors_limit->lms_s_pr_hide )
581         {
582                 tentries = BDB_IDL_N(candidates);
583         }
584
585         if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
586                 PagedResultsState *ps = op->o_pagedresults_state;
587                 /* deferred cookie parsing */
588                 rs->sr_err = parse_paged_cookie( op, rs );
589                 if ( rs->sr_err != LDAP_SUCCESS ) {
590                         send_ldap_result( op, rs );
591                         goto done;
592                 }
593
594                 if ( (ID)( ps->ps_cookie ) == 0 ) {
595                         id = bdb_idl_first( candidates, &cursor );
596
597                 } else {
598                         if ( ps->ps_size == 0 ) {
599                                 rs->sr_err = LDAP_SUCCESS;
600                                 rs->sr_text = "search abandoned by pagedResult size=0";
601                                 send_ldap_result( op, rs );
602                                 goto done;
603                         }
604                         for ( id = bdb_idl_first( candidates, &cursor );
605                                 id != NOID &&
606                                         id <= (ID)( ps->ps_cookie );
607                                 id = bdb_idl_next( candidates, &cursor ) )
608                         {
609                                 /* empty */;
610                         }
611                 }
612
613                 if ( cursor == NOID ) {
614                         Debug( LDAP_DEBUG_TRACE, 
615                                 LDAP_XSTRING(bdb_search)
616                                 ": no paged results candidates\n",
617                                 0, 0, 0 );
618                         send_paged_response( op, rs, &lastid, 0 );
619
620                         rs->sr_err = LDAP_OTHER;
621                         goto done;
622                 }
623                 goto loop_begin;
624         }
625
626 loop_start:
627
628         for ( id = bdb_idl_first( candidates, &cursor );
629                   id != NOID ; id = bdb_idl_next( candidates, &cursor ) )
630         {
631                 int scopeok = 0;
632                 ID* idhole = NULL;
633
634 loop_begin:
635
636                 /* check for abandon */
637                 if ( op->o_abandon ) {
638                         rs->sr_err = LDAP_SUCCESS;
639                         goto done;
640                 }
641
642                 /* check time limit */
643                 if ( op->ors_tlimit != SLAP_NO_LIMIT
644                                 && slap_get_time() > stoptime )
645                 {
646                         rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
647                         rs->sr_ref = rs->sr_v2ref;
648                         send_ldap_result( op, rs );
649                         rs->sr_err = LDAP_SUCCESS;
650                         goto done;
651                 }
652
653 fetch_entry_retry:
654                         /* get the entry with reader lock */
655                         ei = NULL;
656                         rs->sr_err = bdb_cache_find_id( op, ltid,
657                                 id, &ei, 0, locker, &lock );
658
659                         if (rs->sr_err == LDAP_BUSY) {
660                                 rs->sr_text = "ldap server busy";
661                                 send_ldap_result( op, rs );
662                                 goto done;
663
664                         } else if ( rs->sr_err == DB_LOCK_DEADLOCK
665                                 || rs->sr_err == DB_LOCK_NOTGRANTED )
666                         {
667                                 goto fetch_entry_retry;
668                         }
669
670                         if ( ei && rs->sr_err == LDAP_SUCCESS ) {
671                                 e = ei->bei_e;
672                         } else {
673                                 e = NULL;
674                         }
675
676                         if ( e == NULL ) {
677                                 if( !BDB_IDL_IS_RANGE(candidates) ) {
678                                         /* only complain for non-range IDLs */
679                                         Debug( LDAP_DEBUG_TRACE,
680                                                 LDAP_XSTRING(bdb_search)
681                                                 ": candidate %ld not found\n",
682                                                 (long) id, 0, 0 );
683                                 }
684
685                                 goto loop_continue;
686                         }
687
688                 rs->sr_entry = e;
689
690 #ifdef BDB_SUBENTRIES
691                 {
692                         if ( is_entry_subentry( e ) ) {
693                                 if( op->oq_search.rs_scope != LDAP_SCOPE_BASE ) {
694                                         if(!get_subentries_visibility( op )) {
695                                                 /* only subentries are visible */
696                                                 goto loop_continue;
697                                         }
698
699                                 } else if ( get_subentries( op ) &&
700                                         !get_subentries_visibility( op ))
701                                 {
702                                         /* only subentries are visible */
703                                         goto loop_continue;
704                                 }
705
706                         } else if ( get_subentries_visibility( op )) {
707                                 /* only subentries are visible */
708                                 goto loop_continue;
709                         }
710                 }
711 #endif /* BDB_SUBENTRIES */
712
713                 /* Does this candidate actually satisfy the search scope?
714                  *
715                  * Note that we don't lock access to the bei_parent pointer.
716                  * Since only leaf nodes can be deleted, the parent of any
717                  * node will always be a valid node. Also since we have
718                  * a Read lock on the data, it cannot be renamed out of the
719                  * scope while we are looking at it, and unless we're using
720                  * BDB_HIER, its parents cannot be moved either.
721                  */
722                 switch( op->ors_scope ) {
723                 case LDAP_SCOPE_BASE:
724                         /* This is always true, yes? */
725                         if ( id == base.e_id ) scopeok = 1;
726                         break;
727
728                 case LDAP_SCOPE_ONELEVEL:
729                         if ( ei->bei_parent->bei_id == base.e_id ) scopeok = 1;
730                         break;
731
732 #ifdef LDAP_SCOPE_CHILDREN
733                 case LDAP_SCOPE_CHILDREN:
734                         if ( id == base.e_id ) break;
735                         /* Fall-thru */
736 #endif
737                 case LDAP_SCOPE_SUBTREE: {
738                         EntryInfo *tmp;
739                         for ( tmp = BEI(e); tmp; tmp = tmp->bei_parent ) {
740                                 if ( tmp->bei_id == base.e_id ) {
741                                         scopeok = 1;
742                                         break;
743                                 }
744                         }
745                         } break;
746                 }
747
748                 /* aliases were already dereferenced in candidate list */
749                 if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
750                         /* but if the search base is an alias, and we didn't
751                          * deref it when finding, return it.
752                          */
753                         if ( is_entry_alias(e) &&
754                                 ((op->ors_deref & LDAP_DEREF_FINDING) ||
755                                         !bvmatch(&e->e_nname, &op->o_req_ndn)))
756                         {
757                                 goto loop_continue;
758                         }
759
760                         /* scopes is only non-empty for onelevel or subtree */
761                         if ( !scopeok && BDB_IDL_N(scopes) ) {
762                                 unsigned x;
763                                 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ) {
764                                         x = bdb_idl_search( scopes, e->e_id );
765                                         if ( scopes[x] == e->e_id ) scopeok = 1;
766                                 } else {
767                                         /* subtree, walk up the tree */
768                                         EntryInfo *tmp = BEI(e);
769                                         for (;tmp->bei_parent; tmp=tmp->bei_parent) {
770                                                 x = bdb_idl_search( scopes, tmp->bei_id );
771                                                 if ( scopes[x] == tmp->bei_id ) {
772                                                         scopeok = 1;
773                                                         break;
774                                                 }
775                                         }
776                                 }
777                         }
778                 }
779
780                 /* Not in scope, ignore it */
781                 if ( !scopeok )
782                 {
783                         Debug( LDAP_DEBUG_TRACE,
784                                 LDAP_XSTRING(bdb_search)
785                                 ": %ld scope not okay\n",
786                                 (long) id, 0, 0 );
787                         goto loop_continue;
788                 }
789
790                 /*
791                  * if it's a referral, add it to the list of referrals. only do
792                  * this for non-base searches, and don't check the filter
793                  * explicitly here since it's only a candidate anyway.
794                  */
795                 if ( !manageDSAit && op->oq_search.rs_scope != LDAP_SCOPE_BASE
796                         && is_entry_referral( e ) )
797                 {
798                         BerVarray erefs = get_entry_referrals( op, e );
799                         rs->sr_ref = referral_rewrite( erefs, &e->e_name, NULL,
800                                 op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL
801                                         ? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
802
803                         send_search_reference( op, rs );
804
805                         ber_bvarray_free( rs->sr_ref );
806                         ber_bvarray_free( erefs );
807                         rs->sr_ref = NULL;
808
809                         goto loop_continue;
810                 }
811
812                 if ( !manageDSAit && is_entry_glue( e )) {
813                         goto loop_continue;
814                 }
815
816                 /* if it matches the filter and scope, send it */
817                 rs->sr_err = test_filter( op, rs->sr_entry, op->oq_search.rs_filter );
818
819                 if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
820                         /* check size limit */
821                         if ( --op->ors_slimit == -1) {
822 #ifdef SLAP_ZONE_ALLOC
823                                 slap_zn_runlock(bdb->bi_cache.c_zctx, e);
824 #endif
825                                 bdb_cache_return_entry_r( bdb->bi_dbenv,
826                                                 &bdb->bi_cache, e, &lock );
827                                 e = NULL;
828                                 rs->sr_entry = NULL;
829                                 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
830                                 rs->sr_ref = rs->sr_v2ref;
831                                 send_ldap_result( op, rs );
832                                 rs->sr_err = LDAP_SUCCESS;
833                                 goto done;
834                         }
835
836                         if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
837                                 if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size ) {
838                                         send_paged_response( op, rs, &lastid, tentries );
839                                         goto done;
840                                 }
841                                 lastid = id;
842                         }
843
844                         if (e) {
845                                 /* safe default */
846                                 int result = -1;
847                                 rs->sr_attrs = op->oq_search.rs_attrs;
848                                 rs->sr_operational_attrs = NULL;
849                                 rs->sr_ctrls = NULL;
850                                 rs->sr_flags = 0;
851                                 rs->sr_err = LDAP_SUCCESS;
852                                 result = send_search_entry( op, rs );
853
854                                 switch (result) {
855                                 case 0:         /* entry sent ok */
856                                         break;
857                                 case 1:         /* entry not sent */
858                                         break;
859                                 case -1:        /* connection closed */
860 #ifdef SLAP_ZONE_ALLOC
861                                         slap_zn_runlock(bdb->bi_cache.c_zctx, e);
862 #endif
863                                         bdb_cache_return_entry_r(bdb->bi_dbenv,
864                                                 &bdb->bi_cache, e, &lock);
865                                         e = NULL;
866                                         rs->sr_entry = NULL;
867                                         rs->sr_err = LDAP_OTHER;
868                                         goto done;
869                                 }
870                         }
871
872                 } else {
873                         Debug( LDAP_DEBUG_TRACE,
874                                 LDAP_XSTRING(bdb_search)
875                                 ": %ld does not match filter\n",
876                                 (long) id, 0, 0 );
877                 }
878
879 loop_continue:
880                 if( e != NULL ) {
881                         /* free reader lock */
882 #ifdef SLAP_ZONE_ALLOC
883                         slap_zn_runlock(bdb->bi_cache.c_zctx, e);
884 #endif
885                         bdb_cache_return_entry_r( bdb->bi_dbenv,
886                                 &bdb->bi_cache, e , &lock );
887                         e = NULL;
888                         rs->sr_entry = NULL;
889                 }
890                 
891                 ldap_pvt_thread_yield();
892         }
893
894 nochange:
895         rs->sr_ctrls = NULL;
896         rs->sr_ref = rs->sr_v2ref;
897         rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL;
898         rs->sr_rspoid = NULL;
899         if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
900                 send_paged_response( op, rs, NULL, 0 );
901         } else {
902                 send_ldap_result( op, rs );
903         }
904
905         rs->sr_err = LDAP_SUCCESS;
906
907 done:
908         if ( !opinfo )
909                 LOCK_ID_FREE( bdb->bi_dbenv, locker );
910
911         if( rs->sr_v2ref ) {
912                 ber_bvarray_free( rs->sr_v2ref );
913                 rs->sr_v2ref = NULL;
914         }
915         if( realbase.bv_val ) ch_free( realbase.bv_val );
916
917         return rs->sr_err;
918 }
919
920
921 static int base_candidate(
922         BackendDB       *be,
923         Entry   *e,
924         ID              *ids )
925 {
926         Debug(LDAP_DEBUG_ARGS, "base_candidates: base: \"%s\" (0x%08lx)\n",
927                 e->e_nname.bv_val, (long) e->e_id, 0);
928
929         ids[0] = 1;
930         ids[1] = e->e_id;
931         return 0;
932 }
933
934 /* Look for "objectClass Present" in this filter.
935  * Also count depth of filter tree while we're at it.
936  */
937 static int oc_filter(
938         Filter *f,
939         int cur,
940         int *max )
941 {
942         int rc = 0;
943
944         assert( f );
945
946         if( cur > *max ) *max = cur;
947
948         switch( f->f_choice ) {
949         case LDAP_FILTER_PRESENT:
950                 if (f->f_desc == slap_schema.si_ad_objectClass) {
951                         rc = 1;
952                 }
953                 break;
954
955         case LDAP_FILTER_AND:
956         case LDAP_FILTER_OR:
957                 cur++;
958                 for ( f=f->f_and; f; f=f->f_next ) {
959                         (void) oc_filter(f, cur, max);
960                 }
961                 break;
962
963         default:
964                 break;
965         }
966         return rc;
967 }
968
969 static void search_stack_free( void *key, void *data )
970 {
971         ber_memfree_x(data, NULL);
972 }
973
974 static void *search_stack( Operation *op )
975 {
976         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
977         void *ret = NULL;
978
979         if ( op->o_threadctx ) {
980                 ldap_pvt_thread_pool_getkey( op->o_threadctx, search_stack,
981                         &ret, NULL );
982         } else {
983                 ret = bdb->bi_search_stack;
984         }
985
986         if ( !ret ) {
987                 ret = ch_malloc( bdb->bi_search_stack_depth * BDB_IDL_UM_SIZE
988                         * sizeof( ID ) );
989                 if ( op->o_threadctx ) {
990                         ldap_pvt_thread_pool_setkey( op->o_threadctx, search_stack,
991                                 ret, search_stack_free );
992                 } else {
993                         bdb->bi_search_stack = ret;
994                 }
995         }
996         return ret;
997 }
998
999 static int search_candidates(
1000         Operation *op,
1001         SlapReply *rs,
1002         Entry *e,
1003         u_int32_t locker,
1004         ID      *ids,
1005         ID      *scopes )
1006 {
1007         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
1008         int rc, depth = 1;
1009         Filter          f, rf, xf, nf;
1010         ID              *stack;
1011         AttributeAssertion aa_ref;
1012 #ifdef BDB_SUBENTRIES
1013         Filter  sf;
1014         AttributeAssertion aa_subentry;
1015 #endif
1016
1017         /*
1018          * This routine takes as input a filter (user-filter)
1019          * and rewrites it as follows:
1020          *      (&(scope=DN)[(objectClass=subentry)]
1021          *              (|[(objectClass=referral)(objectClass=alias)](user-filter))
1022          */
1023
1024         Debug(LDAP_DEBUG_TRACE,
1025                 "search_candidates: base=\"%s\" (0x%08lx) scope=%d\n",
1026                 e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope );
1027
1028         xf.f_or = op->oq_search.rs_filter;
1029         xf.f_choice = LDAP_FILTER_OR;
1030         xf.f_next = NULL;
1031
1032         /* If the user's filter uses objectClass=*,
1033          * these clauses are redundant.
1034          */
1035         if (!oc_filter(op->oq_search.rs_filter, 1, &depth)
1036                 && !get_subentries_visibility(op)) {
1037                 if( !get_manageDSAit(op) && !get_domainScope(op) ) {
1038                         /* match referral objects */
1039                         struct berval bv_ref = BER_BVC( "referral" );
1040                         rf.f_choice = LDAP_FILTER_EQUALITY;
1041                         rf.f_ava = &aa_ref;
1042                         rf.f_av_desc = slap_schema.si_ad_objectClass;
1043                         rf.f_av_value = bv_ref;
1044                         rf.f_next = xf.f_or;
1045                         xf.f_or = &rf;
1046                         depth++;
1047                 }
1048         }
1049
1050         f.f_next = NULL;
1051         f.f_choice = LDAP_FILTER_AND;
1052         f.f_and = &nf;
1053         /* Dummy; we compute scope separately now */
1054         nf.f_choice = SLAPD_FILTER_COMPUTED;
1055         nf.f_result = LDAP_SUCCESS;
1056         nf.f_next = ( xf.f_or == op->oq_search.rs_filter )
1057                 ? op->oq_search.rs_filter : &xf ;
1058         /* Filter depth increased again, adding dummy clause */
1059         depth++;
1060
1061 #ifdef BDB_SUBENTRIES
1062         if( get_subentries_visibility( op ) ) {
1063                 struct berval bv_subentry = BER_BVC( "subentry" );
1064                 sf.f_choice = LDAP_FILTER_EQUALITY;
1065                 sf.f_ava = &aa_subentry;
1066                 sf.f_av_desc = slap_schema.si_ad_objectClass;
1067                 sf.f_av_value = bv_subentry;
1068                 sf.f_next = nf.f_next;
1069                 nf.f_next = &sf;
1070         }
1071 #endif
1072
1073         /* Allocate IDL stack, plus 1 more for former tmp */
1074         if ( depth+1 > bdb->bi_search_stack_depth ) {
1075                 stack = ch_malloc( (depth + 1) * BDB_IDL_UM_SIZE * sizeof( ID ) );
1076         } else {
1077                 stack = search_stack( op );
1078         }
1079
1080         if( op->ors_deref & LDAP_DEREF_SEARCHING ) {
1081                 rc = search_aliases( op, rs, e, locker, ids, scopes, stack );
1082         } else {
1083                 rc = bdb_dn2idl( op, e, ids, stack );
1084         }
1085
1086         if ( rc == LDAP_SUCCESS ) {
1087                 rc = bdb_filter_candidates( op, &f, ids,
1088                         stack, stack+BDB_IDL_UM_SIZE );
1089         }
1090
1091         if ( depth+1 > bdb->bi_search_stack_depth ) {
1092                 ch_free( stack );
1093         }
1094
1095         if( rc ) {
1096                 Debug(LDAP_DEBUG_TRACE,
1097                         "bdb_search_candidates: failed (rc=%d)\n",
1098                         rc, NULL, NULL );
1099
1100         } else {
1101                 Debug(LDAP_DEBUG_TRACE,
1102                         "bdb_search_candidates: id=%ld first=%ld last=%ld\n",
1103                         (long) ids[0],
1104                         (long) BDB_IDL_FIRST(ids),
1105                         (long) BDB_IDL_LAST(ids) );
1106         }
1107
1108         return rc;
1109 }
1110
1111 static int
1112 parse_paged_cookie( Operation *op, SlapReply *rs )
1113 {
1114         LDAPControl     **c;
1115         int             rc = LDAP_SUCCESS;
1116         ber_tag_t       tag;
1117         ber_int_t       size;
1118         BerElement      *ber;
1119         struct berval   cookie = BER_BVNULL;
1120         PagedResultsState *ps = op->o_pagedresults_state;
1121
1122         /* this function must be invoked only if the pagedResults
1123          * control has been detected, parsed and partially checked
1124          * by the frontend */
1125         assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
1126
1127         /* look for the appropriate ctrl structure */
1128         for ( c = op->o_ctrls; c[0] != NULL; c++ ) {
1129                 if ( strcmp( c[0]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS ) == 0 )
1130                 {
1131                         break;
1132                 }
1133         }
1134
1135         if ( c[0] == NULL ) {
1136                 rs->sr_text = "missing pagedResults control";
1137                 return LDAP_PROTOCOL_ERROR;
1138         }
1139
1140         /* Tested by frontend */
1141         assert( c[0]->ldctl_value.bv_len > 0 );
1142
1143         /* Parse the control value
1144          *      realSearchControlValue ::= SEQUENCE {
1145          *              size    INTEGER (0..maxInt),
1146          *                              -- requested page size from client
1147          *                              -- result set size estimate from server
1148          *              cookie  OCTET STRING
1149          * }
1150          */
1151         ber = ber_init( &c[0]->ldctl_value );
1152         if ( ber == NULL ) {
1153                 rs->sr_text = "internal error";
1154                 return LDAP_OTHER;
1155         }
1156
1157         tag = ber_scanf( ber, "{im}", &size, &cookie );
1158
1159         /* Tested by frontend */
1160         assert( tag != LBER_ERROR );
1161         assert( size >= 0 );
1162
1163         /* cookie decoding/checks deferred to backend... */
1164         if ( cookie.bv_len ) {
1165                 PagedResultsCookie reqcookie;
1166                 if( cookie.bv_len != sizeof( reqcookie ) ) {
1167                         /* bad cookie */
1168                         rs->sr_text = "paged results cookie is invalid";
1169                         rc = LDAP_PROTOCOL_ERROR;
1170                         goto done;
1171                 }
1172
1173                 AC_MEMCPY( &reqcookie, cookie.bv_val, sizeof( reqcookie ));
1174
1175                 if ( reqcookie > ps->ps_cookie ) {
1176                         /* bad cookie */
1177                         rs->sr_text = "paged results cookie is invalid";
1178                         rc = LDAP_PROTOCOL_ERROR;
1179                         goto done;
1180
1181                 } else if ( reqcookie < ps->ps_cookie ) {
1182                         rs->sr_text = "paged results cookie is invalid or old";
1183                         rc = LDAP_UNWILLING_TO_PERFORM;
1184                         goto done;
1185                 }
1186
1187         } else {
1188                 /* Initial request.  Initialize state. */
1189 #if 0
1190                 if ( op->o_conn->c_pagedresults_state.ps_cookie != 0 ) {
1191                         /* There's another pagedResults control on the
1192                          * same connection; reject new pagedResults controls 
1193                          * (allowed by RFC2696) */
1194                         rs->sr_text = "paged results cookie unavailable; try later";
1195                         rc = LDAP_UNWILLING_TO_PERFORM;
1196                         goto done;
1197                 }
1198 #endif
1199                 ps->ps_cookie = 0;
1200                 ps->ps_count = 0;
1201         }
1202
1203 done:;
1204         (void)ber_free( ber, 1 );
1205
1206         return rc;
1207 }
1208
1209 static void
1210 send_paged_response( 
1211         Operation       *op,
1212         SlapReply       *rs,
1213         ID              *lastid,
1214         int             tentries )
1215 {
1216         LDAPControl     ctrl, *ctrls[2];
1217         BerElementBuffer berbuf;
1218         BerElement      *ber = (BerElement *)&berbuf;
1219         PagedResultsCookie respcookie;
1220         struct berval cookie;
1221
1222         Debug(LDAP_DEBUG_ARGS,
1223                 "send_paged_response: lastid=0x%08lx nentries=%d\n", 
1224                 lastid ? *lastid : 0, rs->sr_nentries, NULL );
1225
1226         BER_BVZERO( &ctrl.ldctl_value );
1227         ctrls[0] = &ctrl;
1228         ctrls[1] = NULL;
1229
1230         ber_init2( ber, NULL, LBER_USE_DER );
1231
1232         if ( lastid ) {
1233                 respcookie = ( PagedResultsCookie )(*lastid);
1234                 cookie.bv_len = sizeof( respcookie );
1235                 cookie.bv_val = (char *)&respcookie;
1236
1237         } else {
1238                 respcookie = ( PagedResultsCookie )0;
1239                 BER_BVSTR( &cookie, "" );
1240         }
1241
1242         op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
1243         op->o_conn->c_pagedresults_state.ps_count =
1244                 ((PagedResultsState *)op->o_pagedresults_state)->ps_count + rs->sr_nentries;
1245
1246         /* return size of 0 -- no estimate */
1247         ber_printf( ber, "{iO}", 0, &cookie ); 
1248
1249         if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
1250                 goto done;
1251         }
1252
1253         ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
1254         ctrls[0]->ldctl_iscritical = 0;
1255
1256         rs->sr_ctrls = ctrls;
1257         rs->sr_err = LDAP_SUCCESS;
1258         send_ldap_result( op, rs );
1259         rs->sr_ctrls = NULL;
1260
1261 done:
1262         (void) ber_free_buf( ber );
1263 }
1264
1265 static int
1266 bdb_pfid_cmp( const void *v_id1, const void *v_id2 )
1267 {
1268     const ID *p1 = v_id1, *p2 = v_id2;
1269         return *p1 - *p2;
1270 }
1271
1272 static ID*
1273 bdb_id_dup( Operation *op, ID *id )
1274 {
1275         ID *new;
1276         new = ch_malloc( sizeof(ID) );
1277         *new = *id;
1278         return new;
1279 }