]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/back-bdb/search.c
add paged results support to back-ldap
[openldap] / servers / slapd / back-bdb / search.c
index de9c7b1772503164b7dadc17b1d33ca7d5f73b7e..fa80e81e01c8d4876bda7287df94f128196d32f3 100644 (file)
@@ -37,6 +37,8 @@ static int search_candidates(
        ID      *ids,
        ID      *scopes );
 
+static int parse_paged_cookie( Operation *op, SlapReply *rs );
+
 static void send_paged_response( 
        Operation *op,
        SlapReply *rs,
@@ -149,7 +151,7 @@ static int search_aliases(
        ID cursora, ida, cursoro, ido, *subscop2;
        Entry *matched, *a;
        EntryInfo *ei;
-       struct berval bv_alias = { sizeof("alias")-1, "alias" };
+       struct berval bv_alias = BER_BVC( "alias" );
        AttributeAssertion aa_alias;
        Filter  af;
        DB_LOCK locka, lockr;
@@ -215,9 +217,12 @@ static int search_aliases(
                        ida = bdb_idl_next(curscop, &cursora))
                {
                        ei = NULL;
+retry1:
                        rs->sr_err = bdb_cache_find_id(op, NULL,
                                ida, &ei, 0, locker, &lockr );
                        if (rs->sr_err != LDAP_SUCCESS) {
+                               if ( rs->sr_err == DB_LOCK_DEADLOCK ||
+                                       rs->sr_err == DB_LOCK_NOTGRANTED ) goto retry1;
                                continue;
                        }
                        a = ei->bei_e;
@@ -281,9 +286,15 @@ nextido:
                 * Set the name so that the scope's IDL can be retrieved.
                 */
                ei = NULL;
+sameido:
                rs->sr_err = bdb_cache_find_id(op, NULL, ido, &ei,
                        0, locker, &locka );
-               if ( rs->sr_err != LDAP_SUCCESS ) goto nextido;
+               if ( rs->sr_err != LDAP_SUCCESS ) {
+                       if ( rs->sr_err == DB_LOCK_DEADLOCK ||
+                               rs->sr_err == DB_LOCK_NOTGRANTED )
+                               goto sameido;
+                       goto nextido;
+               }
                e = ei->bei_e;
        }
        return rs->sr_err;
@@ -364,9 +375,13 @@ int bdb_search( Operation *op, SlapReply *rs )
 int bdb_psearch( Operation *op, SlapReply *rs, Operation *sop,
        Entry *ps_e, int ps_type )
 {
+       int     rc;
+
        sop->o_private = op->o_private;
-       bdb_do_search( op, rs, sop, ps_e, ps_type );
+       rc = bdb_do_search( op, rs, sop, ps_e, ps_type );
        sop->o_private = NULL;
+
+       return rc;
 }
 
 /* For persistent searches, op is the currently executing operation,
@@ -491,8 +506,7 @@ bdb_do_search( Operation *op, SlapReply *rs, Operation *sop,
        null_attr.an_desc = NULL;
        null_attr.an_oc = NULL;
        null_attr.an_oc_exclude = 0;
-       null_attr.an_name.bv_len = 0;
-       null_attr.an_name.bv_val = NULL;
+       BER_BVZERO( &null_attr.an_name );
 
        for( num_ctrls = 0; num_ctrls < SLAP_MAX_RESPONSE_CONTROLS; num_ctrls++ ) {
                ctrls[num_ctrls] = NULL;
@@ -504,8 +518,7 @@ bdb_do_search( Operation *op, SlapReply *rs, Operation *sop,
                attrs[0].an_desc = NULL;
                attrs[0].an_oc = NULL;
                attrs[0].an_oc_exclude = 0;
-               attrs[0].an_name.bv_len = 0;
-               attrs[0].an_name.bv_val = NULL;
+               BER_BVZERO( &attrs[0].an_name );
        }
 
        manageDSAit = get_manageDSAit( sop );
@@ -542,8 +555,8 @@ bdb_do_search( Operation *op, SlapReply *rs, Operation *sop,
                ei_root.bei_parent = &ei_root;
                e_root.e_private = &ei_root;
                e_root.e_id = 0;
-               e_root.e_nname.bv_val="";
-               e_root.e_name.bv_val="";
+               BER_BVSTR( &e_root.e_nname, "" );
+               BER_BVSTR( &e_root.e_name, "" );
                ei = &ei_root;
                rs->sr_err = LDAP_SUCCESS;
        } else {
@@ -759,7 +772,7 @@ dn2entry_retry:
        }
 
        /* if not root and candidates exceed to-be-checked entries, abort */
-       if ( sop->ors_limit     /* isroot == TRUE */ &&
+       if ( sop->ors_limit     /* isroot == FALSE */ &&
                sop->ors_limit->lms_s_unchecked != -1 &&
                BDB_IDL_N(candidates) > (unsigned) sop->ors_limit->lms_s_unchecked )
        {
@@ -769,13 +782,20 @@ dn2entry_retry:
                goto done;
        }
 
-       if ( sop->ors_limit == NULL     /* isroot == FALSE */ ||
+       if ( sop->ors_limit == NULL     /* isroot == TRUE */ ||
                !sop->ors_limit->lms_s_pr_hide )
        {
                tentries = BDB_IDL_N(candidates);
        }
 
-       if ( get_pagedresults(sop) > SLAP_NO_CONTROL ) {
+       if ( get_pagedresults( sop ) > SLAP_NO_CONTROL ) {
+               /* deferred cookie parsing */
+               rs->sr_err = parse_paged_cookie( sop, rs );
+               if ( rs->sr_err != LDAP_SUCCESS ) {
+                       send_ldap_result( sop, rs );
+                       goto done;
+               }
+
                if ( (ID)( sop->o_pagedresults_state.ps_cookie ) == 0 ) {
                        id = bdb_idl_first( candidates, &cursor );
 
@@ -1223,6 +1243,7 @@ id2entry_retry:
                                                                num_ctrls++, 1, &cookie );
                                                        if ( rs->sr_err != LDAP_SUCCESS ) goto done;
                                                        rs->sr_attrs = attrs;
+                                                       rs->sr_operational_attrs = NULL;
                                                        rs->sr_ctrls = ctrls;
                                                        rs->sr_flags = 0;
                                                        result = send_search_entry( sop, rs );
@@ -1265,6 +1286,7 @@ id2entry_retry:
                                                        if ( rs->sr_err != LDAP_SUCCESS ) goto done;
                                                        rs->sr_ctrls = ctrls;
                                                        rs->sr_attrs = sop->oq_search.rs_attrs;
+                                                       rs->sr_operational_attrs = NULL;
                                                        rs->sr_flags = 0;
                                                        result = send_search_entry( sop, rs );
                                                        slap_sl_free(
@@ -1309,6 +1331,7 @@ id2entry_retry:
 
                                        } else {
                                                rs->sr_attrs = sop->oq_search.rs_attrs;
+                                               rs->sr_operational_attrs = NULL;
                                                rs->sr_ctrls = NULL;
                                                rs->sr_flags = 0;
                                                rs->sr_err = LDAP_SUCCESS;
@@ -1641,7 +1664,7 @@ static int search_candidates(
        {
                if( !get_manageDSAit(op) && !get_domainScope(op) ) {
                        /* match referral objects */
-                       struct berval bv_ref = { sizeof("referral")-1, "referral" };
+                       struct berval bv_ref = BER_BVC( "referral" );
                        rf.f_choice = LDAP_FILTER_EQUALITY;
                        rf.f_ava = &aa_ref;
                        rf.f_av_desc = slap_schema.si_ad_objectClass;
@@ -1665,7 +1688,7 @@ static int search_candidates(
 
 #ifdef BDB_SUBENTRIES
        if( get_subentries_visibility( op ) ) {
-               struct berval bv_subentry = { sizeof("SUBENTRY")-1, "SUBENTRY" };
+               struct berval bv_subentry = BER_BVC( "SUBENTRY" );
                sf.f_choice = LDAP_FILTER_EQUALITY;
                sf.f_ava = &aa_subentry;
                sf.f_av_desc = slap_schema.si_ad_objectClass;
@@ -1725,6 +1748,125 @@ static int search_candidates(
        return rc;
 }
 
+static int
+parse_paged_cookie( Operation *op, SlapReply *rs )
+{
+       LDAPControl     **c;
+       int             rc = LDAP_SUCCESS;
+       ber_tag_t       tag;
+       ber_int_t       size;
+       BerElement      *ber;
+       struct berval   cookie = BER_BVNULL;
+
+       /* this function must be invoked only if the pagedResults
+        * control has been detected, parsed and partially checked
+        * by the frontend */
+       assert( get_pagedresults( op ) > SLAP_NO_CONTROL );
+
+       /* look for the appropriate ctrl structure */
+       for ( c = op->o_ctrls; c[0] != NULL; c++ ) {
+               if ( strcmp( c[0]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS ) == 0 )
+               {
+                       break;
+               }
+       }
+
+       if ( c[0] == NULL ) {
+               rs->sr_text = "missing pagedResults control";
+               return LDAP_PROTOCOL_ERROR;
+       }
+
+       /* Already tested by frontend */
+       assert( c[0]->ldctl_value.bv_len > 0 );
+#if 0
+       if ( c[0]->ldctl_value.bv_len == 0 ) {
+               rs->sr_text = "paged results control value is empty (or absent)";
+               return LDAP_PROTOCOL_ERROR;
+       }
+#endif
+
+       /* Parse the control value
+        *      realSearchControlValue ::= SEQUENCE {
+        *              size    INTEGER (0..maxInt),
+        *                              -- requested page size from client
+        *                              -- result set size estimate from server
+        *              cookie  OCTET STRING
+        * }
+        */
+       ber = ber_init( &c[0]->ldctl_value );
+       if ( ber == NULL ) {
+               rs->sr_text = "internal error";
+               return LDAP_OTHER;
+       }
+
+       tag = ber_scanf( ber, "{im}", &size, &cookie );
+
+       /* Already tested by frontend */
+       assert( tag != LBER_ERROR );
+#if 0
+       if ( tag == LBER_ERROR ) {
+               rs->sr_text = "paged results control could not be decoded";
+               rc = LDAP_PROTOCOL_ERROR;
+               goto done;
+       }
+#endif
+
+       /* Already tested by frontend */
+       assert( size >= 0 );
+#if 0
+       if ( size < 0 ) {
+               rs->sr_text = "paged results control size invalid";
+               rc = LDAP_PROTOCOL_ERROR;
+               goto done;
+       }
+#endif
+
+       /* cookie decoding/checks deferred to backend... */
+       if ( cookie.bv_len ) {
+               PagedResultsCookie reqcookie;
+               if( cookie.bv_len != sizeof( reqcookie ) ) {
+                       /* bad cookie */
+                       rs->sr_text = "paged results cookie is invalid";
+                       rc = LDAP_PROTOCOL_ERROR;
+                       goto done;
+               }
+
+               AC_MEMCPY( &reqcookie, cookie.bv_val, sizeof( reqcookie ));
+
+               if ( reqcookie > op->o_pagedresults_state.ps_cookie ) {
+                       /* bad cookie */
+                       rs->sr_text = "paged results cookie is invalid";
+                       rc = LDAP_PROTOCOL_ERROR;
+                       goto done;
+
+               } else if ( reqcookie < op->o_pagedresults_state.ps_cookie ) {
+                       rs->sr_text = "paged results cookie is invalid or old";
+                       rc = LDAP_UNWILLING_TO_PERFORM;
+                       goto done;
+               }
+
+       } else {
+               /* Initial request.  Initialize state. */
+#if 0
+               if ( op->o_conn->c_pagedresults_state.ps_cookie != 0 ) {
+                       /* There's another pagedResults control on the
+                        * same connection; reject new pagedResults controls 
+                        * (allowed by RFC2696) */
+                       rs->sr_text = "paged results cookie unavailable; try later";
+                       rc = LDAP_UNWILLING_TO_PERFORM;
+                       goto done;
+               }
+#endif
+               op->o_pagedresults_state.ps_cookie = 0;
+               op->o_pagedresults_state.ps_count = 0;
+       }
+
+done:;
+       (void)ber_free( ber, 1 );
+
+       return rc;
+}
+
 static void
 send_paged_response( 
        Operation       *op,
@@ -1748,7 +1890,7 @@ send_paged_response(
                lastid ? *lastid : 0, rs->sr_nentries, NULL );
 #endif
 
-       ctrl.ldctl_value.bv_val = NULL;
+       BER_BVZERO( &ctrl.ldctl_value );
        ctrls[0] = &ctrl;
        ctrls[1] = NULL;
 
@@ -1761,8 +1903,7 @@ send_paged_response(
 
        } else {
                respcookie = ( PagedResultsCookie )0;
-               cookie.bv_val = "";
-               cookie.bv_len = 0;
+               BER_BVSTR( &cookie, "" );
        }
 
        op->o_conn->c_pagedresults_state.ps_cookie = respcookie;