X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Fback-mdb%2Fsearch.c;h=d90125e8e46dcc0d5688ec5868b447f0b666c8f5;hb=b0bc71be36cfb84bb227a805d01f33ad5f812d6c;hp=5cc0d28c00b5f3d0ff91f081a06f6a461a6bf97e;hpb=18af0bdbce961c3b5d5ee09992cea7ec972cdb1f;p=openldap diff --git a/servers/slapd/back-mdb/search.c b/servers/slapd/back-mdb/search.c index 5cc0d28c00..d90125e8e4 100644 --- a/servers/slapd/back-mdb/search.c +++ b/servers/slapd/back-mdb/search.c @@ -2,7 +2,7 @@ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * - * Copyright 2000-2013 The OpenLDAP Foundation. + * Copyright 2000-2014 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,7 +34,8 @@ static int search_candidates( MDB_txn *txn, MDB_cursor *mci, ID *ids, - ID2L scopes ); + ID2L scopes, + ID *stack ); static int parse_paged_cookie( Operation *op, SlapReply *rs ); @@ -313,6 +314,77 @@ static void scope_chunk_ret( Operation *op, ID2 *scopes ) (void *)scopes, scope_chunk_free, NULL, NULL ); } +static void *search_stack( Operation *op ); + +typedef struct ww_ctx { + MDB_txn *txn; + MDB_cursor *mcd; /* if set, save cursor context */ + ID key; + MDB_val data; + int flag; +} ww_ctx; + +/* ITS#7904 if we get blocked while writing results to client, + * release the current reader txn and reacquire it after we + * unblock. + * Slight problem - if we're doing a scope-based walk (mdb_dn2id_walk) + * to return results, we need to remember the state of the mcd cursor. + * If the node that cursor was pointing to gets deleted while we're + * blocked, we may be unable to restore the cursor position. In that + * case return an LDAP_BUSY error - let the client know this search + * couldn't succeed, but might succeed on a retry. + */ +static void +mdb_writewait( Operation *op, slap_callback *sc ) +{ + ww_ctx *ww = sc->sc_private; + if ( !ww->flag ) { + if ( ww->mcd ) { + MDB_val key, data; + mdb_cursor_get( ww->mcd, &key, &data, MDB_GET_CURRENT ); + memcpy( &ww->key, key.mv_data, sizeof(ID) ); + ww->data.mv_size = data.mv_size; + ww->data.mv_data = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx ); + memcpy(ww->data.mv_data, data.mv_data, data.mv_size); + } + mdb_txn_reset( ww->txn ); + ww->flag = 1; + } +} + +static int +mdb_waitfixup( Operation *op, ww_ctx *ww, MDB_cursor *mci, MDB_cursor *mcd ) +{ + int rc = 0; + ww->flag = 0; + mdb_txn_renew( ww->txn ); + mdb_cursor_renew( ww->txn, mci ); + mdb_cursor_renew( ww->txn, mcd ); + if ( ww->mcd ) { + MDB_val key, data; + key.mv_size = sizeof(ID); + key.mv_data = &ww->key; + data = ww->data; + rc = mdb_cursor_get( mcd, &key, &data, MDB_GET_BOTH ); + if ( rc == MDB_NOTFOUND ) { + data = ww->data; + rc = mdb_cursor_get( mcd, &key, &data, MDB_GET_BOTH_RANGE ); + /* the loop will skip this node using NEXT_DUP but we want it + * sent, so go back one space first + */ + if ( rc == MDB_SUCCESS ) + mdb_cursor_get( mcd, &key, &data, MDB_PREV_DUP ); + else + rc = LDAP_BUSY; + } else if ( rc ) { + rc = LDAP_OTHER; + } + op->o_tmpfree( ww->data.mv_data, op->o_tmpmemctx ); + ww->data.mv_data = NULL; + } + return rc; +} + int mdb_search( Operation *op, SlapReply *rs ) { @@ -322,6 +394,7 @@ mdb_search( Operation *op, SlapReply *rs ) ID candidates[MDB_IDL_UM_SIZE]; ID iscopes[MDB_IDL_DB_SIZE]; ID2 *scopes; + void *stack; Entry *e = NULL, *base = NULL; Entry *matched = NULL; AttributeName *attrs; @@ -331,6 +404,8 @@ mdb_search( Operation *op, SlapReply *rs ) int tentries = 0; IdScopes isc; MDB_cursor *mci, *mcd; + ww_ctx wwctx; + slap_callback cb = { 0 }; mdb_op_info opinfo = {{{0}}}, *moi = &opinfo; MDB_txn *ltid = NULL; @@ -365,10 +440,12 @@ mdb_search( Operation *op, SlapReply *rs ) } scopes = scope_chunk_get( op ); + stack = search_stack( op ); isc.mt = ltid; isc.mc = mcd; isc.scopes = scopes; isc.oscope = op->ors_scope; + isc.sctmp = stack; if ( op->ors_deref & LDAP_DEREF_FINDING ) { MDB_IDL_ZERO(candidates); @@ -562,7 +639,7 @@ dn2entry_retry: scopes[1].mid = base->e_id; scopes[1].mval.mv_data = NULL; rs->sr_err = search_candidates( op, rs, base, - ltid, mci, candidates, scopes ); + ltid, mci, candidates, scopes, stack ); ncand = MDB_IDL_N( candidates ); if ( !base->e_id || ncand == NOID ) { /* grab entry count from id2entry stat @@ -655,6 +732,7 @@ dn2entry_retry: iscopes[0] = 0; } + wwctx.mcd = mcd; isc.id = base->e_id; isc.numrdns = 0; rc = mdb_dn2id_walk( op, &isc ); @@ -665,6 +743,17 @@ dn2entry_retry: cscope = 0; } else { id = mdb_idl_first( candidates, &cursor ); + wwctx.mcd = NULL; + } + + wwctx.flag = 0; + /* If we're running in our own read txn */ + if ( moi == &opinfo ) { + cb.sc_writewait = mdb_writewait; + cb.sc_private = &wwctx; + wwctx.txn = ltid; + cb.sc_next = op->o_callback; + op->o_callback = &cb; } while (id != NOID) @@ -806,7 +895,7 @@ notfound: goto done; } - rs->sr_err = mdb_entry_decode( op, &edata, &e ); + rs->sr_err = mdb_entry_decode( op, ltid, &edata, &e ); if ( rs->sr_err ) { rs->sr_err = LDAP_OTHER; rs->sr_text = "internal error in mdb_entry_decode"; @@ -930,7 +1019,8 @@ notfound: send_search_reference( op, rs ); - mdb_entry_return( op, e ); + if (e != base) + mdb_entry_return( op, e ); rs->sr_entry = NULL; e = NULL; @@ -938,6 +1028,14 @@ notfound: ber_bvarray_free( erefs ); rs->sr_ref = NULL; + if ( wwctx.flag ) { + rs->sr_err = mdb_waitfixup( op, &wwctx, mci, mcd ); + if ( rs->sr_err ) { + send_ldap_result( op, rs ); + goto done; + } + } + goto loop_continue; } @@ -977,6 +1075,9 @@ notfound: break; default: /* entry not sent */ break; + case LDAP_BUSY: + send_ldap_result( op, rs ); + goto done; case LDAP_UNAVAILABLE: case LDAP_SIZELIMIT_EXCEEDED: if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED ) { @@ -989,6 +1090,13 @@ notfound: } goto done; } + if ( wwctx.flag ) { + rs->sr_err = mdb_waitfixup( op, &wwctx, mci, mcd ); + if ( rs->sr_err ) { + send_ldap_result( op, rs ); + goto done; + } + } } } else { @@ -1053,6 +1161,17 @@ nochange: rs->sr_err = LDAP_SUCCESS; done: + if ( cb.sc_private ) { + /* remove our writewait callback */ + slap_callback **scp = &op->o_callback; + while ( *scp ) { + if ( *scp == &cb ) { + *scp = cb.sc_next; + cb.sc_private = NULL; + break; + } + } + } mdb_cursor_close( mcd ); mdb_cursor_close( mci ); if ( moi == &opinfo ) { @@ -1066,7 +1185,7 @@ done: rs->sr_v2ref = NULL; } if (base) - mdb_entry_return( op,base); + mdb_entry_return( op, base ); scope_chunk_ret( op, scopes ); return rs->sr_err; @@ -1158,12 +1277,12 @@ static int search_candidates( MDB_txn *txn, MDB_cursor *mci, ID *ids, - ID2L scopes ) + ID2L scopes, + ID *stack ) { struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private; int rc, depth = 1; Filter *f, rf, xf, nf, sf; - ID *stack; AttributeAssertion aa_ref = ATTRIBUTEASSERTION_INIT; AttributeAssertion aa_subentry = ATTRIBUTEASSERTION_INIT; @@ -1218,8 +1337,6 @@ static int search_candidates( /* Allocate IDL stack, plus 1 more for former tmp */ if ( depth+1 > mdb->mi_search_stack_depth ) { stack = ch_malloc( (depth + 1) * MDB_IDL_UM_SIZE * sizeof( ID ) ); - } else { - stack = search_stack( op ); } if( op->ors_deref & LDAP_DEREF_SEARCHING ) {