summaryrefslogtreecommitdiffstats
path: root/ldap/servers/slapd/back-ldbm/vlv.c
diff options
context:
space:
mode:
authorcvsadm <cvsadm>2005-01-21 00:44:34 +0000
committercvsadm <cvsadm>2005-01-21 00:44:34 +0000
commitb2093e3016027d6b5cf06b3f91f30769bfc099e2 (patch)
treecf58939393a9032182c4fbc4441164a9456e82f8 /ldap/servers/slapd/back-ldbm/vlv.c
downloadds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.gz
ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.xz
ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.zip
Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth)ldapserver7x
Diffstat (limited to 'ldap/servers/slapd/back-ldbm/vlv.c')
-rw-r--r--ldap/servers/slapd/back-ldbm/vlv.c1947
1 files changed, 1947 insertions, 0 deletions
diff --git a/ldap/servers/slapd/back-ldbm/vlv.c b/ldap/servers/slapd/back-ldbm/vlv.c
new file mode 100644
index 00000000..8397ef0e
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/vlv.c
@@ -0,0 +1,1947 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* vlv.c */
+
+
+/*
+ * References to on-line documentation here.
+ *
+ * http://BLUES/users/dboreham/publish/Design_Documentation/RFCs/draft-ietf-asid-ldapv3-virtuallistview-01.html
+ * http://warp.mcom.com/server/directory-server/clientsdk/hammerhead/design/virtuallistview.html
+ * ftp://ftp.ietf.org/internet-drafts/draft-ietf-ldapext-ldapv3-vlv-00.txt
+ * http://rocknroll/users/merrells/publish/vlvimplementation.html
+ */
+
+
+#include "back-ldbm.h"
+#include "vlv_srch.h"
+#include "vlv_key.h"
+
+static PRUint32 vlv_trim_candidates_byindex(PRUint32 length, const struct vlv_request *vlv_request_control);
+static PRUint32 vlv_trim_candidates_byvalue(backend *be, const IDList *candidates, const sort_spec* sort_control, const struct vlv_request *vlv_request_control);
+static int vlv_build_candidate_list( backend *be, struct vlvIndex* p, const struct vlv_request *vlv_request_control, IDList** candidates, struct vlv_response *vlv_response_control);
+
+/* New mutex for vlv locking
+PRRWLock * vlvSearchList_lock=NULL;
+static struct vlvSearch *vlvSearchList= NULL;
+*/
+
+#define ISLEGACY(be) (be?(be->be_instance_info?(((ldbm_instance *)be->be_instance_info)->inst_li?(((ldbm_instance *)be->be_instance_info)->inst_li->li_legacy_errcode):0):0):0)
+
+/* Callback to add a new VLV Search specification. Added write lock.*/
+
+int vlv_AddSearchEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ ldbm_instance *inst = (ldbm_instance *)arg;
+ struct vlvSearch* newVlvSearch= vlvSearch_new();
+ backend *be = inst->inst_be;
+
+ vlvSearch_init(newVlvSearch, pb, entryBefore, inst);
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+ vlvSearch_addtolist(newVlvSearch, (struct vlvSearch **)&be->vlvSearchList);
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* Callback to add a new VLV Index specification. Added write lock.*/
+
+int vlv_AddIndexEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ struct vlvSearch *parent;
+ backend *be= ((ldbm_instance*)arg)->inst_be;
+ Slapi_DN parentdn;
+
+ slapi_sdn_init(&parentdn);
+ slapi_sdn_get_parent(slapi_entry_get_sdn(entryBefore),&parentdn);
+ {
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+ parent= vlvSearch_finddn((struct vlvSearch *)be->vlvSearchList, &parentdn);
+ if(parent!=NULL)
+ {
+ struct vlvIndex* newVlvIndex= vlvIndex_new();
+ newVlvIndex->vlv_be=be;
+ vlvIndex_init(newVlvIndex, be, parent, entryBefore);
+ vlvSearch_addIndex(parent, newVlvIndex);
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ }
+ slapi_sdn_done(&parentdn);
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* Callback to delete a VLV Index specification. Added write lock.*/
+
+int vlv_DeleteSearchEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ struct vlvSearch* p=NULL;
+ backend *be= ((ldbm_instance*)arg)->inst_be;
+
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+ p = vlvSearch_finddn((struct vlvSearch *)be->vlvSearchList, slapi_entry_get_sdn(entryBefore));
+ if(p!=NULL)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Deleted Virtual List View Search (%s).\n", p->vlv_name, 0, 0);
+ vlvSearch_removefromlist((struct vlvSearch **)&be->vlvSearchList,p->vlv_dn);
+ vlvSearch_delete(&p);
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+
+/* Stub Callback to delete a VLV Index specification.*/
+
+int vlv_DeleteIndexEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ LDAPDebug( LDAP_DEBUG_ANY, "Deleted Virtual List View Index.\n", 0, 0, 0);
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+
+/* Callback to modify a VLV Search specification. Added read lock.*/
+
+int vlv_ModifySearchEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ struct vlvSearch* p=NULL;
+ backend *be= ((ldbm_instance*)arg)->inst_be;
+
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ p= vlvSearch_finddn((struct vlvSearch *)be->vlvSearchList, slapi_entry_get_sdn(entryBefore));
+ if(p!=NULL)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Modified Virtual List View Search (%s), which will be enabled when the database is rebuilt.\n", p->vlv_name, 0, 0);
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return SLAPI_DSE_CALLBACK_DO_NOT_APPLY;
+}
+
+
+/* Stub callback to modify a VLV Index specification. */
+
+int vlv_ModifyIndexEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ LDAPDebug( LDAP_DEBUG_ANY, "Modified Virtual List View Index.\n", 0, 0, 0);
+ return SLAPI_DSE_CALLBACK_DO_NOT_APPLY;
+}
+
+
+/* Callback to rename a VLV Search specification. Added read lock.*/
+
+int vlv_ModifyRDNSearchEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ struct vlvSearch* p=NULL;
+ backend *be= ((ldbm_instance*)arg)->inst_be;
+
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ p= vlvSearch_finddn((struct vlvSearch *)be->vlvSearchList, slapi_entry_get_sdn(entryBefore));
+ if(p!=NULL)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Modified Virtual List View Search (%s), which will be enabled when the database is rebuilt.\n", p->vlv_name, 0, 0);
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return SLAPI_DSE_CALLBACK_DO_NOT_APPLY;
+}
+
+
+/* Stub callback to modify a VLV Index specification. */
+
+int vlv_ModifyRDNIndexEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ LDAPDebug( LDAP_DEBUG_ANY, "Modified Virtual List View Index.\n", 0, 0, 0);
+ return SLAPI_DSE_CALLBACK_DO_NOT_APPLY;
+}
+
+/* Something may have just read a VLV Entry. */
+
+int vlv_SearchIndexEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ char *name= slapi_entry_attr_get_charptr(entryBefore,type_vlvName);
+ backend *be= ((ldbm_instance*)arg)->inst_be;
+ if (name!=NULL)
+ {
+ struct vlvIndex* p= vlv_find_searchname(name, be); /* lock list */
+ slapi_ch_free((void **) &name);
+ if(p!=NULL)
+ {
+ if(vlvIndex_enabled(p))
+ {
+ slapi_entry_attr_set_charptr(entryBefore, type_vlvEnabled, "1");
+ }
+ else
+ {
+ slapi_entry_attr_set_charptr(entryBefore, type_vlvEnabled, "0");
+ }
+ slapi_entry_attr_set_ulong(entryBefore, type_vlvUses, p->vlv_uses);
+ }
+ }
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* Handle results of a search for objectclass "vlvIndex". Called by vlv_init at inittime -- no need to lock*/
+
+static int
+vlv_init_index_entry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ struct vlvIndex* newVlvIndex;
+ struct vlvSearch* pSearch;
+ Slapi_Backend *be= ((ldbm_instance*)arg)->inst_be;
+ char ebuf[BUFSIZ];
+
+ if(be!=NULL)
+ {
+ Slapi_DN parentdn;
+
+ slapi_sdn_init(&parentdn);
+ newVlvIndex= vlvIndex_new();
+ slapi_sdn_get_parent(slapi_entry_get_sdn(entryBefore),&parentdn);
+ pSearch= vlvSearch_finddn((struct vlvSearch *)be->vlvSearchList, &parentdn);
+ if (pSearch == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Parent doesn't exist for entry %s.\n",
+ escape_string(slapi_entry_get_dn(entryBefore), ebuf), 0, 0);
+ }
+ else {
+ vlvIndex_init(newVlvIndex, be, pSearch, entryBefore);
+ vlvSearch_addIndex(pSearch, newVlvIndex);
+ }
+ slapi_sdn_done(&parentdn);
+ }
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* Handle results of a search for objectclass "vlvSearch". Called by vlv_init at inittime -- no need to lock*/
+
+static int
+vlv_init_search_entry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ struct vlvSearch* newVlvSearch= vlvSearch_new();
+ ldbm_instance *inst = (ldbm_instance*)arg;
+ backend *be= inst->inst_be;
+
+ vlvSearch_init(newVlvSearch, pb, entryBefore, inst);
+ vlvSearch_addtolist(newVlvSearch, (struct vlvSearch **)&be->vlvSearchList);
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* Look at a new entry, and the set of VLV searches, and see whether
+there are any which have deferred initialization and which can now
+be initialized given the new entry. Added write lock. */
+
+
+void vlv_grok_new_import_entry(const struct backentry *e, backend *be)
+{
+ struct vlvSearch* p = NULL;
+ static int seen_them_all = 0;
+ int any_not_done = 0;
+
+
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+ if (seen_them_all) {
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return;
+ }
+ p=(struct vlvSearch *)be->vlvSearchList;
+
+ /* Walk the list of searches */
+ for(;p!=NULL;p= p->vlv_next)
+ /* is this one not initialized ? */
+ if (0 == p->vlv_initialized) {
+ any_not_done = 1;
+ /* Is its base the entry we have here ? */
+ if (0 == slapi_sdn_compare(backentry_get_sdn(e),p->vlv_base) ) {
+ /* Then initialize it */
+ vlvSearch_reinit(p,e);
+ }
+ }
+ if (!any_not_done) {
+ seen_them_all = 1;
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+}
+
+/*
+ * Search for the VLV entries which describe the pre-computed indexes we
+ * support. Register administartion DSE callback functions.
+ * This is exported to the backend initialisation routine.
+ * 'inst' may be NULL for non-slapd initialization...
+ */
+int
+vlv_init(ldbm_instance *inst)
+{
+ /* The FE DSE *must* be initialised before we get here */
+ int return_value= LDAP_SUCCESS;
+ int scope= LDAP_SCOPE_SUBTREE;
+ char *basedn, buf[512];
+ const char *searchfilter = "(objectclass=vlvsearch)";
+ const char *indexfilter = "(objectclass=vlvindex)";
+ backend *be= inst->inst_be;
+
+ /* Initialize lock first time through */
+ if(be->vlvSearchList_lock == NULL) {
+ char *rwlockname = (char *)slapi_ch_malloc(sizeof("vlvSearchList") +
+ strlen(inst->inst_name) + 2);
+ sprintf(rwlockname, "vlvSearchList_%s", inst->inst_name);
+ be->vlvSearchList_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, rwlockname);
+ slapi_ch_free((void**)&rwlockname);
+ }
+ if (NULL != (struct vlvSearch *)be->vlvSearchList)
+ {
+ struct vlvSearch *t = NULL;
+ struct vlvSearch *nt = NULL;
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+ for (t = (struct vlvSearch *)be->vlvSearchList; NULL != t; )
+ {
+ nt = t->vlv_next;
+ vlvSearch_delete(&t);
+ t = nt;
+ }
+ be->vlvSearchList = NULL;
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ }
+ if (inst == NULL) {
+ basedn = NULL;
+ } else {
+ sprintf(buf, "cn=%s,cn=%s,cn=plugins,cn=config",
+ inst->inst_name, inst->inst_li->li_plugin->plg_name);
+ basedn = buf;
+ }
+
+ /* Find the VLV Search Entries */
+ {
+ Slapi_PBlock *tmp_pb;
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_init_search_entry,(void *)inst);
+ tmp_pb= slapi_search_internal(basedn, scope, searchfilter, NULL, NULL, 0);
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_init_search_entry);
+ slapi_free_search_results_internal(tmp_pb);
+ slapi_pblock_destroy(tmp_pb);
+ }
+
+ /* Find the VLV Index Entries */
+ {
+ Slapi_PBlock *tmp_pb;
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_init_index_entry,(void*)inst);
+ tmp_pb= slapi_search_internal(basedn, scope, indexfilter, NULL, NULL, 0);
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_init_index_entry);
+ slapi_free_search_results_internal(tmp_pb);
+ slapi_pblock_destroy(tmp_pb);
+ }
+
+ /* Only need to register these callbacks for SLAPD mode... */
+ if(basedn!=NULL)
+ {
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_SearchIndexEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_ADD,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_AddSearchEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_ADD,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_AddIndexEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_ModifySearchEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_ModifyIndexEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_DeleteSearchEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_DeleteIndexEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_MODRDN,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_ModifyRDNSearchEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_MODRDN,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_ModifyRDNIndexEntry,(void*)inst);
+ }
+
+ return return_value;
+}
+
+/* Removes callbacks from above when instance is removed. */
+
+int
+vlv_remove_callbacks(ldbm_instance *inst) {
+
+ int return_value= LDAP_SUCCESS;
+ int scope= LDAP_SCOPE_SUBTREE;
+ char *basedn, buf[512];
+ const char *searchfilter = "(objectclass=vlvsearch)";
+ const char *indexfilter = "(objectclass=vlvindex)";
+
+ if (inst == NULL) {
+ basedn = NULL;
+ } else {
+ sprintf(buf, "cn=%s,cn=%s,cn=plugins,cn=config",
+ inst->inst_name, inst->inst_li->li_plugin->plg_name);
+ basedn = buf;
+ }
+ if(basedn!=NULL)
+ {
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_SearchIndexEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_AddSearchEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_AddIndexEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_ModifySearchEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_ModifyIndexEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_DeleteSearchEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_DeleteIndexEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODRDN,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_ModifyRDNSearchEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODRDN,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_ModifyRDNIndexEntry);
+ }
+ return return_value;
+}
+
+/* Find an enabled index which matches this description. */
+
+static struct vlvIndex*
+vlv_find_search(backend *be, const Slapi_DN *base, int scope, const char *filter, const sort_spec* sort_control)
+{
+ return vlvSearch_findenabled(be,(struct vlvSearch *)be->vlvSearchList,base,scope,filter,sort_control);
+}
+
+
+/* Find a search which matches this name. Added read lock. */
+
+struct vlvIndex*
+vlv_find_searchname(const char * name, backend *be)
+{
+ struct vlvIndex *p=NULL;
+
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ p=vlvSearch_findname((struct vlvSearch *)be->vlvSearchList,name);
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return p;
+}
+
+/* Find a search which matches this indexname. Added to read lock */
+
+struct vlvIndex*
+vlv_find_indexname(const char * name, backend *be)
+{
+
+ struct vlvIndex *p=NULL;
+
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ p=vlvSearch_findindexname((struct vlvSearch *)be->vlvSearchList,name);
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return p;
+}
+
+
+/* Get a list of known VLV Indexes. Added read lock */
+
+char *
+vlv_getindexnames(backend *be)
+{
+ char *n=NULL;
+
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ n=vlvSearch_getnames((struct vlvSearch *)be->vlvSearchList);
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return n;
+}
+
+/* Return the list of VLV indices to the import code. Added read lock */
+
+void
+vlv_getindices(IFP callback_fn,void *param, backend *be)
+{
+ /* Traverse the list, calling the import code's callback function */
+ struct vlvSearch* ps = NULL;
+
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ ps = (struct vlvSearch *)be->vlvSearchList;
+ for(;ps!=NULL;ps= ps->vlv_next)
+ {
+ struct vlvIndex* pi= ps->vlv_index;
+ for(;pi!=NULL;pi= pi->vlv_next)
+ {
+ callback_fn(pi->vlv_attrinfo,param);
+ }
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+}
+
+/*
+ * Create a key for the entry in the vlv index.
+ *
+ * The key is a composite of a value from each sorted attribute.
+ *
+ * If a sorted attribute has many values, then the key is built
+ * with the attribute value with the lowest value.
+ *
+ * The primary sorted attribute value is followed by a 0x00 to
+ * ensure that short attribute values appear before longer ones.
+ *
+ * Many entries may have the same attribute values, which would
+ * generate the same composite key, so we append the EntryID
+ * to ensure the uniqueness of the key.
+ *
+ * Always creates a key. Never returns NULL.
+ */
+static struct vlv_key *
+vlv_create_key(struct vlvIndex* p, struct backentry* e)
+{
+ struct berval val, *lowest_value = NULL;
+ unsigned char char_min = 0x00;
+ unsigned char char_max = 0xFF;
+ struct vlv_key *key= vlv_key_new();
+ if(p->vlv_sortkey!=NULL)
+ {
+ /* Foreach sorted attribute... */
+ int sortattr= 0;
+ while(p->vlv_sortkey[sortattr]!=NULL)
+ {
+ Slapi_Attr* attr= attrlist_find(e->ep_entry->e_attrs, p->vlv_sortkey[sortattr]->sk_attrtype);
+ {
+ /*
+ * If there's a matching rule associated with the sorted
+ * attribute then use the indexer to mangle the attr values.
+ * This ensures that the international characters will
+ * collate in the correct order.
+ */
+
+ /* xxxPINAKI */
+ /* need to free some stuff! */
+ Slapi_Value **cvalue = NULL;
+ struct berval **value = NULL;
+ int free_value= 0;
+ if (attr != NULL && !valueset_isempty(&attr->a_present_values))
+ {
+ /* Sorted attribute found. */
+ int totalattrs;
+ if (p->vlv_sortkey[sortattr]->sk_matchruleoid==NULL)
+ {
+ /* No matching rule. Syntax Plugin mangles value. */
+ Slapi_Value **va= valueset_get_valuearray(&attr->a_present_values);
+ slapi_call_syntax_values2keys_sv( p->vlv_syntax_plugin[sortattr], va, &cvalue, LDAP_FILTER_EQUALITY );
+ valuearray_get_bervalarray(cvalue,&value);
+
+ /* XXXSD need to free some more stuff */
+ {
+ int numval;
+ for (numval=0; cvalue&&cvalue[numval];numval++) {
+ slapi_value_free(&cvalue[numval]);
+ }
+ if (cvalue)
+ slapi_ch_free((void **)&cvalue);
+ }
+
+ free_value= 1;
+ }
+ else
+ {
+ /* Matching rule. Do the magic mangling. Plugin owns the memory. */
+ if(p->vlv_mrpb[sortattr]!=NULL)
+ {
+ /* xxxPINAKI */
+ struct berval **bval=NULL;
+ Slapi_Value **va= valueset_get_valuearray(&attr->a_present_values);
+ valuearray_get_bervalarray(va,&bval);
+ matchrule_values_to_keys(p->vlv_mrpb[sortattr],bval,&value);
+ }
+ }
+ for(totalattrs=0;value[totalattrs]!=NULL;totalattrs++) {}; /* Total Number of Attributes */
+ if(totalattrs==1)
+ {
+ lowest_value= value[0];
+ }
+ else
+ {
+ lowest_value = attr_value_lowest(value, slapi_berval_cmp);
+ }
+ } /* end of if (attr != NULL && ...) */
+ if(p->vlv_sortkey[sortattr]->sk_reverseorder)
+ {
+ /*
+ * This attribute is reverse sorted, so we must
+ * invert the attribute value so that the keys
+ * will be in the correct order.
+ */
+ unsigned int i;
+ char *attributeValue = NULL;
+ /* Bug 605477 : Don't malloc 0 bytes */
+ if (attr != NULL && lowest_value->bv_len != 0) {
+ attributeValue = (char*)slapi_ch_malloc(lowest_value->bv_len);
+ for(i=0;i<lowest_value->bv_len;i++)
+ {
+ attributeValue[i]= UCHAR_MAX - ((char*)lowest_value->bv_val)[i];
+ }
+ val.bv_len= lowest_value->bv_len;
+ val.bv_val= (void*)attributeValue;
+ } else {
+ /* Reverse Sort: We use an attribute value of 0x00 when
+ * there is no attribute value or attrbute is absent
+ */
+ val.bv_val= (void*)&char_min;
+ val.bv_len= 1;
+ }
+ vlv_key_addattr(key,&val);
+ slapi_ch_free((void**)&attributeValue);
+ }
+ else
+ {
+ /*
+ * This attribute is forward sorted, so add the
+ * attribute value to the end of all the keys.
+ */
+
+ /* If the forward-sorted attribute is absent or has no
+ * value, we need to use the value of 0xFF.
+ */
+ if (attr != NULL && lowest_value->bv_len > 0) {
+ vlv_key_addattr(key,lowest_value);
+ } else {
+ val.bv_val = (void*)&char_max;
+ val.bv_len = 1;
+ vlv_key_addattr(key,&val);
+ }
+ }
+ if(sortattr==0)
+ {
+ /*
+ * If this is the first attribute (the typedown attribute)
+ * then it should be followed by a zero. This is to ensure
+ * that shorter attribute values appear before longer ones.
+ */
+ char zero = 0;
+ val.bv_len= 1;
+ val.bv_val= (void*)&zero;
+ vlv_key_addattr(key,&val);
+ }
+ if(free_value)
+ {
+ ber_bvecfree(value);
+ }
+ }
+ sortattr++;
+ }
+ }
+ {
+ /* Append the EntryID to the key to ensure uniqueness */
+ val.bv_len= sizeof(e->ep_id);
+ val.bv_val= (void*)&e->ep_id;
+ vlv_key_addattr(key,&val);
+ }
+ return key;
+}
+
+/*
+ * Insert or Delete the entry to or from the index
+ */
+
+static int
+do_vlv_update_index(back_txn *txn, struct ldbminfo *li, Slapi_PBlock *pb, struct vlvIndex* pIndex, struct backentry* entry, int insert)
+{
+ backend *be;
+ int rc= 0;
+ DB *db = NULL;
+ DB_TXN *db_txn = NULL;
+ struct vlv_key *key = NULL;
+
+ slapi_pblock_get(pb, SLAPI_BACKEND, &be);
+
+ rc = dblayer_get_index_file(be, pIndex->vlv_attrinfo, &db, DBOPEN_CREATE);
+ if (rc != 0) {
+ if(rc != DB_LOCK_DEADLOCK)
+ LDAPDebug(LDAP_DEBUG_ANY, "VLV: can't get index file '%s' (err %d)\n",
+ pIndex->vlv_attrinfo->ai_type, rc, 0);
+ return rc;
+ }
+
+ key = vlv_create_key(pIndex,entry);
+ if (NULL != txn) {
+ db_txn = txn->back_txn_txn;
+ } else {
+ /* Very bad idea to do this outside of a transaction */
+ }
+
+ if (insert) {
+ DBT data = {0};
+ data.size = sizeof(entry->ep_id);
+ data.data = &entry->ep_id;
+ rc = db->put(db, db_txn, &key->key, &data, 0);
+ if (rc == 0) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "vlv_update_index: %s Insert %s ID=%lu\n",
+ pIndex->vlv_name, key->key.data, (u_long)entry->ep_id);
+ vlvIndex_increment_indexlength(pIndex, db, txn);
+ } else if (rc == DB_RUNRECOVERY) {
+ ldbm_nasty(pIndex->vlv_name,77,rc);
+ } else if(rc != DB_LOCK_DEADLOCK) {
+ /* jcm: This error is valid if the key already exists.
+ * Identical multi valued attr values could do this. */
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "vlv_update_index: %s Insert %s ID=%lu FAILED\n",
+ pIndex->vlv_name, key->key.data, (u_long)entry->ep_id);
+ }
+ } else {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "vlv_update_index: %s Delete %s\n",
+ pIndex->vlv_name, key->key.data, 0);
+ rc = db->del(db, db_txn, &key->key, 0);
+ if (rc == 0) {
+ vlvIndex_decrement_indexlength(pIndex, db, txn);
+ } else if (rc == DB_RUNRECOVERY) {
+ ldbm_nasty(pIndex->vlv_name,78,rc);
+ } else if (rc != DB_LOCK_DEADLOCK) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "vlv_update_index: %s Delete %s FAILED\n",
+ pIndex->vlv_name, key->key.data, 0);
+ }
+ }
+
+ vlv_key_delete(&key);
+ dblayer_release_index_file(be, pIndex->vlv_attrinfo, db);
+ return rc;
+}
+
+/*
+ * Given an entry modification check if a VLV index needs to be updated.
+ */
+
+int
+vlv_update_index(struct vlvIndex* p, back_txn *txn, struct ldbminfo *li, Slapi_PBlock *pb, struct backentry* oldEntry, struct backentry* newEntry)
+{
+ int return_value=0;
+ /* Check if the old entry is in this VLV index */
+ if(oldEntry!=NULL)
+ {
+ if(slapi_sdn_scope_test(backentry_get_sdn(oldEntry),vlvIndex_getBase(p),vlvIndex_getScope(p)))
+ {
+ if(slapi_filter_test( pb, oldEntry->ep_entry, vlvIndex_getFilter(p), 0 /* No ACL Check */) == 0 )
+ {
+ /* Remove the entry from the index */
+ return_value=do_vlv_update_index(txn, li, pb, p, oldEntry, 0 /* Delete Key */);
+ }
+ }
+ }
+ /* Check if the new entry should be in the VLV index */
+ if(newEntry!=NULL)
+ {
+ if(slapi_sdn_scope_test(backentry_get_sdn(newEntry),vlvIndex_getBase(p),vlvIndex_getScope(p)))
+ {
+ if(slapi_filter_test( pb, newEntry->ep_entry, vlvIndex_getFilter(p), 0 /* No ACL Check */) == 0 )
+ {
+ /* Add the entry to the index */
+ return_value=do_vlv_update_index(txn, li, pb, p, newEntry, 1 /* Insert Key */);
+ }
+ }
+ }
+ return return_value;
+}
+
+/*
+ * Given an entry modification check if a VLV index needs to be updated.
+ *
+ * This is called for every modifying operation, so it must be very efficient.
+ *
+ * We need to know if we're adding, deleting, or modifying
+ * because we could be leaving and/or joining an index
+ *
+ * ADD: oldEntry==NULL && newEntry!=NULL
+ * DEL: oldEntry!=NULL && newEntry==NULL
+ * MOD: oldEntry!=NULL && newEntry!=NULL
+ *
+ * JCM: If only non-sorted attributes are changed, then the indexes don't need updating.
+ * JCM: Detecting this fact, given multi-valued atribibutes, might be tricky...
+ * Added write lock
+*/
+
+int
+vlv_update_all_indexes(back_txn *txn, backend *be, Slapi_PBlock *pb, struct backentry* oldEntry, struct backentry* newEntry)
+{
+ int return_value= LDAP_SUCCESS;
+ struct vlvSearch* ps=NULL;
+ struct ldbminfo *li = ((ldbm_instance *)be->be_instance_info)->inst_li;
+
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+ ps = (struct vlvSearch *)be->vlvSearchList;
+ for(;ps!=NULL;ps= ps->vlv_next)
+ {
+ struct vlvIndex* pi= ps->vlv_index;
+ for (return_value = LDAP_SUCCESS; return_value == LDAP_SUCCESS && pi!=NULL; pi=pi->vlv_next)
+ return_value=vlv_update_index(pi, txn, li, pb, oldEntry, newEntry);
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return return_value;
+}
+
+/*
+ * Determine the range of record numbers to return.
+ * Prevent an underrun, or overrun.
+ */
+ /* jcm: Should we make sure that start < stop */
+
+static void
+determine_result_range(const struct vlv_request *vlv_request_control, PRUint32 index, PRUint32 length, PRUint32* pstart, PRUint32 *pstop)
+{
+ if (vlv_request_control == NULL)
+ {
+ *pstart= 0;
+ if (0 == length) /* 609377: index size could be 0 */
+ {
+ *pstop= 0;
+ }
+ else
+ {
+ *pstop= length - 1;
+ }
+ }
+ else
+ {
+ /* Make sure we don't run off the start */
+ if(index < vlv_request_control->beforeCount)
+ {
+ *pstart= 0;
+ }
+ else
+ {
+ *pstart= index - vlv_request_control->beforeCount;
+ }
+ /* Make sure we don't run off the end */
+ if(ULONG_MAX - index > vlv_request_control->afterCount)
+ {
+ *pstop= index + vlv_request_control->afterCount;
+ }
+ else
+ {
+ *pstop= ULONG_MAX;
+ }
+ /* Client tried to index off the end */
+ if (0 == length) /* 609377: index size could be 0 */
+ {
+ *pstop= 0;
+ }
+ else if(*pstop > length - 1)
+ {
+ *pstop= length - 1;
+ }
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_determine_result_range: Result Range %lu-%lu\n", *pstart, *pstop, 0 );
+}
+
+/*
+ * This is a utility function to pass the client
+ * supplied attribute value through the appropriate
+ * matching rule indexer.
+ *
+ * It allocates a berval vector which the caller
+ * must free.
+ */
+
+static struct berval **
+vlv_create_matching_rule_value( Slapi_PBlock* pb, struct berval *original_value)
+{
+ struct berval **value= NULL;
+ if(pb!=NULL)
+ {
+ struct berval **outvalue = NULL;
+ struct berval *invalue[2];
+ invalue[0]= original_value; /* jcm: cast away const */
+ invalue[1]= NULL;
+ /* The plugin owns the memory it returns in outvalue */
+ matchrule_values_to_keys(pb,invalue,&outvalue);
+ if(outvalue!=NULL)
+ {
+ value= slapi_ch_bvecdup(outvalue);
+ }
+ }
+ if(value==NULL)
+ {
+ struct berval *outvalue[2];
+ outvalue[0]= original_value; /* jcm: cast away const */
+ outvalue[1]= NULL;
+ value= slapi_ch_bvecdup(outvalue);
+ }
+ return value;
+}
+
+
+/*
+ * Find the record number in a VLV index for a given attribute value.
+ * The returned index is counted from zero.
+ */
+
+static PRUint32
+vlv_build_candidate_list_byvalue( struct vlvIndex* p, DBC *dbc, PRUint32 length, const struct vlv_request *vlv_request_control)
+{
+ PRUint32 si= 0; /* The Selected Index */
+ int err= 0;
+ DBT key= {0};
+ DBT data= {0};
+ /*
+ * If the primary sorted attribute has an associated
+ * matching rule, then we must mangle the typedown
+ * value.
+ */
+ struct berval **typedown_value= NULL;
+ struct berval *invalue[2];
+ invalue[0]= (struct berval *)&vlv_request_control->value; /* jcm: cast away const */
+ invalue[1]= NULL;
+ if (p->vlv_sortkey[0]->sk_matchruleoid==NULL)
+ {
+ slapi_call_syntax_values2keys(p->vlv_syntax_plugin[0],invalue,&typedown_value,LDAP_FILTER_EQUALITY); /* JCM SLOW FUNCTION */
+ }
+ else
+ {
+ typedown_value= vlv_create_matching_rule_value(p->vlv_mrpb[0],(struct berval *)&vlv_request_control->value); /* jcm: cast away const */
+ }
+ if(p->vlv_sortkey[0]->sk_reverseorder)
+ {
+ /*
+ * The primary attribute is reverse sorted, so we must
+ * invert the typedown value in order to match the key.
+ */
+ unsigned int i;
+ for(i=0;i<(*typedown_value)->bv_len;i++)
+ {
+ ((char*)(*typedown_value)->bv_val)[i]= UCHAR_MAX - ((char*)(*typedown_value)->bv_val)[i];
+ }
+ }
+
+ key.flags= DB_DBT_MALLOC;
+ key.size= typedown_value[0]->bv_len;
+ key.data= typedown_value[0]->bv_val;
+ data.flags= DB_DBT_MALLOC;
+ err= dbc->c_get(dbc,&key,&data,DB_SET_RANGE);
+ if(err==0)
+ {
+ free(data.data);
+ err= dbc->c_get(dbc,&key,&data,DB_GET_RECNO);
+ if(err==0)
+ {
+ si= *((db_recno_t*)data.data);
+ /* Records are numbered from one. */
+ si--;
+ free(data.data);
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_build_candidate_list_byvalue: Found. Index=%lu\n",si,0,0);
+ }
+ else
+ {
+ /* Couldn't get the record number for the record we found. */
+ }
+ }
+ else
+ {
+ /* Couldn't find an entry which matches the value,
+ * so return the last entry
+ * (609377) when the index file is empty, there is no "last entry".
+ */
+ if (0 == length)
+ {
+ si = 0;
+ }
+ else
+ {
+ si = length - 1;
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_build_candidate_list_byvalue: Not Found. Index=%lu\n",si,0,0);
+ }
+ ber_bvecfree((struct berval**)typedown_value);
+ return si;
+}
+
+static int
+vlv_idl_sort_cmp(const void *x, const void *y)
+{
+ return *(ID *)x - *(ID *)y;
+}
+
+/* build a candidate list (IDL) from a VLV index, given the starting index
+ * and the ending index (as an inclusive list).
+ * returns 0 on success, or an LDAP error code.
+ */
+int vlv_build_idl(PRUint32 start, PRUint32 stop, DB *db, DBC *dbc,
+ IDList **candidates, int dosort)
+{
+ IDList *idl = NULL;
+ int err;
+ PRUint32 recno;
+ DBT key = {0};
+ DBT data = {0};
+ ID id;
+
+ idl = idl_alloc(stop-start+1);
+ if (!idl) {
+ /* out of memory :( */
+ return LDAP_OPERATIONS_ERROR;
+ }
+ recno = start+1;
+ key.size = sizeof(recno);
+ key.data = &recno;
+ key.flags = DB_DBT_MALLOC;
+ data.ulen = sizeof(ID);
+ data.data = &id;
+ data.flags = DB_DBT_USERMEM; /* don't alloc */
+ err = dbc->c_get(dbc, &key, &data, DB_SET_RECNO);
+ while ((err == 0) && (recno <= stop+1)) {
+ if (key.data != &recno)
+ free(key.data);
+ idl_append(idl, *(ID *)data.data);
+ if (++recno <= stop+1) {
+ err = dbc->c_get(dbc, &key, &data, DB_NEXT);
+ }
+ }
+ if (err != 0) {
+ /* some db error...? */
+ LDAPDebug(LDAP_DEBUG_ANY, "vlv_build_idl: can't follow db cursor "
+ "(err %d)\n", err, 0, 0);
+ if (err == ENOMEM)
+ LDAPDebug(LDAP_DEBUG_ANY, " nomem: wants %d key, %d data\n",
+ key.size, data.size, 0);
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /* success! */
+ if (idl) {
+ if (candidates)
+ {
+ if (dosort)
+ {
+ qsort((void *)&idl->b_ids[0], idl->b_nids,
+ (size_t)sizeof(ID), vlv_idl_sort_cmp);
+ }
+ *candidates = idl;
+ }
+ else
+ idl_free(idl); /* ??? */
+ }
+ return LDAP_SUCCESS;
+}
+
+
+/* This function does vlv_access, searching and building list all while holding read lock
+
+ 1. vlv_find_search fails, set:
+ unsigned int opnote = SLAPI_OP_NOTE_UNINDEXED;
+ slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
+ return FIND_SEARCH FAILED
+
+ 2. vlvIndex_accessallowed fails
+ return VLV_LDBM_ACCESS_DENIED
+
+ 3. vlv_build_candidate_list fails:
+ return VLV_BLD_LIST_FAILED
+
+ 4. return LDAP_SUCCESS
+*/
+
+int
+vlv_search_build_candidate_list(Slapi_PBlock *pb, const Slapi_DN *base, int *vlv_rc, const sort_spec* sort_control,
+ const struct vlv_request *vlv_request_control,
+ IDList** candidates, struct vlv_response *vlv_response_control) {
+ struct vlvIndex* pi = NULL;
+ backend *be;
+ int scope, rc=LDAP_SUCCESS;
+ char *fstr;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope );
+ slapi_pblock_get( pb, SLAPI_SEARCH_STRFILTER, &fstr );
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ if((pi=vlv_find_search(be, base, scope, fstr, sort_control)) == NULL) {
+ unsigned int opnote = SLAPI_OP_NOTE_UNINDEXED;
+ slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
+ rc = VLV_FIND_SEARCH_FAILED;
+ } else if((*vlv_rc=vlvIndex_accessallowed(pi, pb)) != LDAP_SUCCESS) {
+ rc = VLV_ACCESS_DENIED;
+ } else if ((*vlv_rc=vlv_build_candidate_list(be,pi,vlv_request_control,candidates,vlv_response_control)) != LDAP_SUCCESS) {
+ rc = VLV_BLD_LIST_FAILED;
+ vlv_response_control->result=*vlv_rc;
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return rc;
+}
+
+/*
+ * Given the SORT and VLV controls return a candidate list from the
+ * pre-computed index file.
+ *
+ * Returns:
+ * success (0),
+ * operationsError (1),
+ * unwillingToPerform (53),
+ * timeLimitExceeded (3),
+ * adminLimitExceeded (11),
+ * indexRangeError (61),
+ * other (80)
+ */
+
+
+static int
+vlv_build_candidate_list( backend *be, struct vlvIndex* p, const struct vlv_request *vlv_request_control, IDList** candidates, struct vlv_response *vlv_response_control)
+{
+ int return_value = LDAP_SUCCESS;
+ DB *db = NULL;
+ DBC *dbc = NULL;
+ int rc, err;
+ PRUint32 si = 0; /* The Selected Index */
+ PRUint32 length;
+ int do_trim= 1;
+
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "=> vlv_build_candidate_list: %s %s Using VLV Index %s\n",
+ slapi_sdn_get_dn(vlvIndex_getBase(p)), p->vlv_search->vlv_filter,
+ vlvIndex_getName(p));
+ if (!vlvIndex_online(p)) {
+ return -1;
+ }
+ rc = dblayer_get_index_file(be, p->vlv_attrinfo, &db, 0);
+ if (rc != 0) {
+ /* shouldn't happen */
+ LDAPDebug(LDAP_DEBUG_ANY, "VLV: can't get index file '%s' (err %d)\n",
+ p->vlv_attrinfo->ai_type, rc, 0);
+ return -1;
+ }
+
+ err = db->cursor(db, 0 /* txn */, &dbc, 0);
+ if (err != 0) {
+ /* shouldn't happen */
+ LDAPDebug(LDAP_DEBUG_ANY, "VLV: couldn't get cursor (err %d)\n",
+ rc, 0, 0);
+ return -1;
+ }
+
+ length = vlvIndex_get_indexlength(p, db, 0 /* txn */);
+
+ /* Increment the usage counter */
+ vlvIndex_incrementUsage(p);
+
+ if (vlv_request_control)
+ {
+ switch(vlv_request_control->tag) {
+ case 0: /* byIndex */
+ si = vlv_trim_candidates_byindex(length, vlv_request_control);
+ break;
+ case 1: /* byValue */
+ si = vlv_build_candidate_list_byvalue(p, dbc, length,
+ vlv_request_control);
+ if (si==length) {
+ do_trim = 0;
+ *candidates = idl_alloc(0);
+ }
+ break;
+ default:
+ /* Some wierd tag value. Shouldn't ever happen */
+ if (ISLEGACY(be)) {
+ return_value = LDAP_OPERATIONS_ERROR;
+ } else {
+ return_value = LDAP_VIRTUAL_LIST_VIEW_ERROR;
+ }
+ break;
+ }
+
+ /* Tell the client what the real content count is.
+ * Client counts from 1. */
+ vlv_response_control->targetPosition = si + 1;
+ vlv_response_control->contentCount = length;
+ vlv_response_control->result = return_value;
+ }
+
+ if ((return_value == LDAP_SUCCESS) && do_trim) {
+ /* Work out the range of records to return */
+ PRUint32 start, stop;
+ determine_result_range(vlv_request_control, si, length, &start, &stop);
+
+ /* fetch the idl */
+ return_value = vlv_build_idl(start, stop, db, dbc, candidates, 0);
+ }
+ dbc->c_close(dbc);
+
+ dblayer_release_index_file( be, p->vlv_attrinfo, db );
+ return return_value;
+}
+
+/*
+ * Given a candidate list and a filter specification, filter the candidate list
+ *
+ * Returns:
+ * success (0),
+ * operationsError (1),
+ * unwillingToPerform (53),
+ * timeLimitExceeded (3),
+ * adminLimitExceeded (11),
+ * indexRangeError (61),
+ * other (80)
+ */
+int
+vlv_filter_candidates(backend *be, Slapi_PBlock *pb, const IDList *candidates, const Slapi_DN *base, int scope, Slapi_Filter *filter, IDList** filteredCandidates, int lookthrough_limit, time_t time_up)
+{
+ IDList* resultIdl= NULL;
+ int return_value = LDAP_SUCCESS;
+
+ /* Refuse to filter a non-existent IDlist */
+ if (NULL == candidates)
+ {
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> vlv_filter_candidates: Filtering %lu Candidates\n",(u_long)candidates->b_nids, 0, 0 );
+
+ if (0 == return_value && candidates->b_nids>0)
+ {
+ /* jcm: Could be an idlist function. create_filtered_idlist */
+ /* Iterate over the ID List applying the filter */
+ int lookedat= 0;
+ int done= 0;
+ int counter= 0;
+ ID id = NOID;
+ idl_iterator current = idl_iterator_init(candidates);
+ resultIdl= idl_alloc(candidates->b_nids);
+ do
+ {
+ id = idl_iterator_dereference_increment(&current, candidates);
+ if ( id != NOID )
+ {
+ int err= 0;
+ struct backentry *e= NULL;
+ e = id2entry( be, id, NULL, &err );
+ if ( e == NULL )
+ {
+ /*
+ * The ALLIDS ID List contains IDs for which there is no entry.
+ * This is because the entries have been deleted. An error in
+ * this case is ok.
+ */
+ if(!(ALLIDS(candidates) && err==DB_NOTFOUND))
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "vlv_filter_candidates: Candidate %lu not found err=%d\n", (u_long)id, err, 0 );
+ }
+ }
+ else
+ {
+ lookedat++;
+ if(slapi_sdn_scope_test(backentry_get_sdn(e),base,scope))
+ {
+ if ( slapi_filter_test( pb, e->ep_entry, filter, 0 /* No ACL Check */) == 0 )
+ {
+ /* The entry passed the filter test, add the id to the list */
+ LDAPDebug( LDAP_DEBUG_TRACE, "vlv_filter_candidates: Candidate %lu Passed Filter\n", (u_long)id, 0, 0 );
+ idl_append(resultIdl,id);
+ }
+ }
+ cache_return(&(((ldbm_instance *) be->be_instance_info)->inst_cache), &e);
+ }
+ }
+
+ done= slapi_op_abandoned(pb);
+
+ /* Check to see if our journey is really necessary */
+ if ( counter++ % 10 == 0 )
+ {
+ /* check time limit */
+ time_t curtime = current_time();
+ if ( time_up != -1 && curtime > time_up )
+ {
+ return_value= LDAP_TIMELIMIT_EXCEEDED;
+ done= 1;
+ }
+ /* check lookthrough limit */
+ if ( lookthrough_limit != -1 && lookedat>lookthrough_limit )
+ {
+ return_value= LDAP_ADMINLIMIT_EXCEEDED;
+ done= 1;
+ }
+ }
+ } while (!done && id!=NOID);
+ }
+ if(filteredCandidates!=NULL)
+ *filteredCandidates= resultIdl;
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_filter_candidates: Filtering done\n",0, 0, 0 );
+
+ return return_value;
+}
+
+/*
+ * Given a candidate list and a virtual list view specification, trim the candidate list
+ *
+ * Returns:
+ * success (0),
+ * operationsError (1),
+ * unwillingToPerform (53),
+ * timeLimitExceeded (3),
+ * adminLimitExceeded (11),
+ * indexRangeError (61),
+ * other (80)
+ */
+int
+vlv_trim_candidates(backend *be, const IDList *candidates, const sort_spec* sort_control, const struct vlv_request *vlv_request_control, IDList** trimmedCandidates,struct vlv_response *vlv_response_control)
+{
+ IDList* resultIdl= NULL;
+ int return_value= LDAP_SUCCESS;
+ PRUint32 si= 0; /* The Selected Index */
+ int do_trim= 1;
+
+ /* Refuse to trim a non-existent IDlist */
+ if (NULL == candidates || candidates->b_nids==0)
+ {
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ switch(vlv_request_control->tag)
+ {
+ case 0: /* byIndex */
+ si= vlv_trim_candidates_byindex(candidates->b_nids, vlv_request_control);
+ break;
+ case 1: /* byValue */
+ si= vlv_trim_candidates_byvalue(be, candidates, sort_control, vlv_request_control);
+ /* Don't bother sending results if the attribute value wasn't found */
+ if(si==candidates->b_nids)
+ {
+ do_trim= 0;
+ resultIdl= idl_alloc(0);
+ }
+ break;
+ default:
+ /* Some wierd tag value. Shouldn't ever happen */
+ if (ISLEGACY(be)) {
+ return_value = LDAP_OPERATIONS_ERROR;
+ } else {
+ return_value = LDAP_VIRTUAL_LIST_VIEW_ERROR;
+ }
+ break;
+ }
+
+ /* Tell the client what the real content count is. Clients count from 1 */
+ vlv_response_control->targetPosition= si + 1;
+ vlv_response_control->contentCount= candidates->b_nids;
+
+ if(return_value==LDAP_SUCCESS && do_trim)
+ {
+ /* Work out the range of records to return */
+ PRUint32 start, stop;
+ determine_result_range(vlv_request_control,si,candidates->b_nids,&start,&stop);
+ /* Build a new list containing the (start..stop) range */
+ /* JCM: Should really be a function in idlist.c to copy a range */
+ resultIdl= idl_alloc(stop-start+1);
+ {
+ PRUint32 cursor= 0;
+ for(cursor=start;cursor<=stop;cursor++)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "vlv_trim_candidates: Include ID %lu\n",(u_long)candidates->b_ids[cursor], 0, 0 );
+ idl_append(resultIdl,candidates->b_ids[cursor]);
+ }
+ }
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_trim_candidates: Trimmed list contains %lu entries.\n",(u_long)resultIdl->b_nids, 0, 0 );
+ if(trimmedCandidates!=NULL)
+ *trimmedCandidates= resultIdl;
+ return return_value;
+}
+
+/*
+ * Work out the Selected Index given the length of the candidate list
+ * and the request control from the client.
+ *
+ * If the client sends Index==0 we behave as if I=1
+ * If the client sends Index==Size==1 we behave as if I=1, S=0
+ */
+static PRUint32
+vlv_trim_candidates_byindex(PRUint32 length, const struct vlv_request *vlv_request_control)
+{
+ PRUint32 si= 0; /* The Selected Index */
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> vlv_trim_candidates_byindex: length=%lu index=%lu size=%lu\n",length, vlv_request_control->index, vlv_request_control->contentCount );
+ if(vlv_request_control->index==0)
+ {
+ /* Always select the first entry in the list */
+ si= 0;
+ }
+ else
+ {
+ if(vlv_request_control->contentCount==0)
+ {
+ /* The client has no idea what the content count might be. */
+ /* Can't scale the index, so use as is */
+ si= vlv_request_control->index;
+ if (0 == length) /* 609377: index size could be 0 */
+ {
+ if (si > 0)
+ {
+ si = length;
+ }
+ }
+ else if(si > length - 1)
+ {
+ si= length - 1;
+ }
+ }
+ else
+ {
+ if(vlv_request_control->index>=vlv_request_control->contentCount)
+ {
+ /* Always select the last entry in the list */
+ if (0 == length) /* 609377: index size could be 0 */
+ {
+ si = 0;
+ }
+ else
+ {
+ si= length-1;
+ }
+ }
+ else
+ {
+ /* The three components of this expression are (PRUint32) and may well have a value up to ULONG_MAX */
+ /* SelectedIndex = ActualContentCount * ( ClientIndex / ClientContentCount ) */
+ si= ((PRUint32)((double)length * (double)(vlv_request_control->index / (double)vlv_request_control->contentCount )));
+ }
+ }
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_trim_candidates_byindex: Selected Index %lu\n",si, 0, 0 );
+ return si;
+}
+
+/*
+ * Iterate over the Candidate ID List looking for an entry >= the provided attribute value.
+ */
+static PRUint32
+vlv_trim_candidates_byvalue(backend *be, const IDList *candidates, const sort_spec* sort_control, const struct vlv_request *vlv_request_control)
+{
+ PRUint32 si= 0; /* The Selected Index */
+ PRUint32 low= 0;
+ PRUint32 high= candidates->b_nids-1;
+ PRUint32 current= 0;
+ ID id = NOID;
+ int found= 0;
+ struct berval **typedown_value;
+
+ /* For non-matchrule indexing */
+ value_compare_fn_type compare_fn= NULL;
+
+ /*
+ * If the primary sorted attribute has an associated
+ * matching rule, then we must mangle the typedown
+ * value.
+ */
+ if (sort_control->matchrule==NULL)
+ {
+ void *pi= NULL;
+ if(slapi_attr_type2plugin(sort_control->type, &pi)==0)
+ {
+ struct berval *invalue[2];
+ invalue[0]= (struct berval *)&vlv_request_control->value; /* jcm: cast away const */
+ invalue[1]= NULL;
+ slapi_call_syntax_values2keys(pi,invalue,&typedown_value,LDAP_FILTER_EQUALITY); /* JCM SLOW FUNCTION */
+ plugin_call_syntax_get_compare_fn( pi, &compare_fn );
+ if (compare_fn == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "vlv_trim_candidates_byvalue: "
+ "attempt to compare an unordered attribute",
+ 0, 0, 0);
+ compare_fn = slapi_berval_cmp;
+ }
+ }
+ }
+ else
+ {
+ typedown_value= vlv_create_matching_rule_value(sort_control->mr_pb,(struct berval *)&vlv_request_control->value);
+ compare_fn= slapi_berval_cmp;
+ }
+ /*
+ * Perform a binary search over the candidate list
+ */
+ do {
+ int err= 0;
+ struct backentry *e= NULL;
+ if(!sort_control->order)
+ {
+ current = (low + high)/2;
+ }
+ else
+ {
+ current = (1 + low + high)/2;
+ }
+ id= candidates->b_ids[current];
+ e = id2entry( be, id, NULL, &err );
+ if ( e == NULL )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "vlv_trim_candidates_byvalue: Candidate ID %lu not found err=%d\n", (u_long)id, err, 0 );
+ }
+ else
+ {
+ /* Check if vlv_request_control->value is greater than or equal to the primary key. */
+ int match;
+ Slapi_Attr *attr;
+ if ( (NULL != compare_fn) && (slapi_entry_attr_find( e->ep_entry, sort_control->type, &attr ) == 0) )
+ {
+ /*
+ * If there's a matching rule associated with the primary
+ * attribute then use the indexer to mangle the attr values.
+ */
+ Slapi_Value **csn_value = valueset_get_valuearray(&attr->a_present_values);
+ struct berval **entry_value = /* xxxPINAKI needs modification attr->a_vals */NULL;
+ if(sort_control->mr_pb!=NULL)
+ {
+ struct berval **tmp_entry_value = NULL;
+
+ valuearray_get_bervalarray(csn_value,&tmp_entry_value);
+ /* Matching rule. Do the magic mangling. Plugin owns the memory. */
+ matchrule_values_to_keys(sort_control->mr_pb,/* xxxPINAKI needs modification attr->a_vals */tmp_entry_value,&entry_value);
+ }
+ else
+ {
+ valuearray_get_bervalarray(csn_value,&entry_value);
+ }
+ if(!sort_control->order)
+ {
+ match= sort_attr_compare(entry_value, (struct berval**)typedown_value, compare_fn);
+ }
+ else
+ {
+ match= sort_attr_compare((struct berval**)typedown_value, entry_value, compare_fn);
+ }
+ }
+ else
+ {
+ /*
+ * This attribute doesn't exist on this entry.
+ */
+ if(sort_control->order)
+ {
+ match= 1;
+ }
+ else
+ {
+ match= 0;
+ }
+ }
+ if(!sort_control->order)
+ {
+ if (match>=0)
+ {
+ high= current;
+ }
+ else
+ {
+ low= current+1;
+ }
+ }
+ else
+ {
+ if (match>=0)
+ {
+ high= current-1;
+ }
+ else
+ {
+ low= current;
+ }
+ }
+ if (low>=high)
+ {
+ found= 1;
+ si= high;
+ if(si==candidates->b_nids && !match)
+ {
+ /* Couldn't find an entry which matches the value, so return contentCount */
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_trim_candidates_byvalue: Not Found. Index %lu\n",si, 0, 0 );
+ si= candidates->b_nids;
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_trim_candidates_byvalue: Found. Index %lu\n",si, 0, 0 );
+ }
+ }
+ }
+ } while (!found);
+ ber_bvecfree((struct berval**)typedown_value);
+ return si;
+}
+
+/*
+ * Encode the VLV RESPONSE control.
+ *
+ * Create a virtual list view response control,
+ * and add it to the PBlock to be returned to the client.
+ *
+ * Returns:
+ * success ( 0 )
+ * operationsError (1),
+ */
+int
+vlv_make_response_control (Slapi_PBlock *pb, const struct vlv_response* vlvp)
+{
+ BerElement *ber= NULL;
+ struct berval *bvp = NULL;
+ int rc = -1;
+
+ /*
+ VirtualListViewResponse ::= SEQUENCE {
+ targetPosition INTEGER (0 .. maxInt),
+ contentCount INTEGER (0 .. maxInt),
+ virtualListViewResult ENUMERATED {
+ success (0),
+ operationsError (1),
+ unwillingToPerform (53),
+ insufficientAccessRights (50),
+ busy (51),
+ timeLimitExceeded (3),
+ adminLimitExceeded (11),
+ sortControlMissing (60),
+ indexRangeError (61),
+ other (80) } }
+ */
+
+ if ( ( ber = ber_alloc()) == NULL )
+ {
+ return rc;
+ }
+
+ rc = ber_printf( ber, "{iie}", vlvp->targetPosition, vlvp->contentCount, vlvp->result );
+ if ( rc != -1 )
+ {
+ rc = ber_flatten( ber, &bvp );
+ }
+
+ ber_free( ber, 1 );
+
+ if ( rc != -1 )
+ {
+ LDAPControl new_ctrl = {0};
+ new_ctrl.ldctl_oid = LDAP_CONTROL_VLVRESPONSE;
+ new_ctrl.ldctl_value = *bvp;
+ new_ctrl.ldctl_iscritical = 1;
+ rc= slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, &new_ctrl );
+ ber_bvfree(bvp);
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_make_response_control: Index=%lu Size=%lu Result=%lu\n", vlvp->targetPosition, vlvp->contentCount, vlvp->result );
+
+ return (rc==-1?LDAP_OPERATIONS_ERROR:LDAP_SUCCESS);
+}
+
+/*
+ * Generate a logging string for the vlv request and response
+ */
+void vlv_print_access_log(Slapi_PBlock *pb,struct vlv_request* vlvi, struct vlv_response *vlvo)
+{
+#define VLV_LOG_BS (21*6 + 4 + 5) /* space for 20-digit values for all parameters + 'VLV ' + status */
+ char stack_buffer[VLV_LOG_BS];
+ char *buffer = stack_buffer;
+ char *p;
+
+ if (vlvi->value.bv_len > 20) {
+ buffer = slapi_ch_malloc(VLV_LOG_BS + vlvi->value.bv_len);
+ }
+ p = buffer;
+ p+= sprintf(p,"VLV ");
+ if (0 == vlvi->tag) {
+ /* By Index case */
+ p+= sprintf(p,"%ld:%ld:%ld:%ld",
+ vlvi->beforeCount ,
+ vlvi->afterCount ,
+ vlvi->index ,
+ vlvi->contentCount
+ );
+ } else {
+ /* By value case */
+#define VLV_LOG_SS 32
+ char stack_string[VLV_LOG_SS];
+ char *string = stack_string;
+
+ if (vlvi->value.bv_len >= VLV_LOG_SS) {
+ string = slapi_ch_malloc(vlvi->value.bv_len+1);
+ }
+ strncpy(string,vlvi->value.bv_val,vlvi->value.bv_len);
+ string[vlvi->value.bv_len] = '\0';
+ p += sprintf(p,"%ld:%ld:%s",
+ vlvi->beforeCount ,
+ vlvi->afterCount ,
+ string
+ );
+ if (string != stack_string) {
+ slapi_ch_free( (void**)&string);
+ }
+ }
+ /* Now the response info */
+ p += sprintf(p," %ld:%ld (%ld)",
+ vlvo->targetPosition ,
+ vlvo->contentCount,
+ vlvo->result
+ );
+
+
+ ldbm_log_access_message(pb,buffer);
+
+ if (buffer != stack_buffer) {
+ slapi_ch_free( (void**)&buffer);
+ }
+}
+
+/*
+ * Decode the VLV REQUEST control.
+ *
+ * If the client sends Index==0 we behave as if I=1
+ *
+ * Returns:
+ * success (0),
+ * operationsError (1),
+ *
+ */
+int
+vlv_parse_request_control( backend *be, struct berval *vlv_spec_ber,struct vlv_request* vlvp)
+{
+ /* This control looks like this :
+
+ VirtualListViewRequest ::= SEQUENCE {
+ beforeCount INTEGER (0 .. maxInt),
+ afterCount INTEGER (0 .. maxInt),
+ CHOICE {
+ byIndex [0] SEQUENCE {
+ index INTEGER (0 .. maxInt),
+ contentCount INTEGER (0 .. maxInt) }
+ greaterThanOrEqual [1] assertionValue }
+ */
+ BerElement *ber = NULL;
+ int return_value = LDAP_SUCCESS;
+ PRUint32 rc= 0;
+ long long_beforeCount;
+ long long_afterCount;
+ long long_index;
+ long long_contentCount;
+
+ vlvp->value.bv_len = 0;
+ vlvp->value.bv_val = NULL;
+
+ ber = ber_init(vlv_spec_ber);
+ rc = ber_scanf(ber,"{ii",&long_beforeCount,&long_afterCount);
+ vlvp->beforeCount = long_beforeCount;
+ vlvp->afterCount = long_afterCount;
+ if (LBER_ERROR == rc)
+ {
+ return_value= LDAP_OPERATIONS_ERROR;
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "vlv_parse_request_control: Before=%lu After=%lu\n", vlvp->beforeCount, vlvp->afterCount, 0 );
+ rc = ber_scanf(ber,"t",&vlvp->tag);
+ switch(vlvp->tag)
+ {
+ case LDAP_TAG_VLV_BY_INDEX:
+ /* byIndex */
+ vlvp->tag= 0;
+ rc = ber_scanf(ber,"{ii}}",&long_index,&long_contentCount);
+ vlvp->index = long_index;
+ vlvp->contentCount = long_contentCount;
+ if (LBER_ERROR == rc)
+ {
+ if (ISLEGACY(be)) {
+ return_value = LDAP_OPERATIONS_ERROR;
+ } else {
+ return_value = LDAP_VIRTUAL_LIST_VIEW_ERROR;
+ }
+ }
+ else
+ {
+ /* Client Counts from 1. */
+ if(vlvp->index!=0)
+ {
+ vlvp->index--;
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "vlv_parse_request_control: Index=%lu Content=%lu\n", vlvp->index, vlvp->contentCount, 0 );
+ }
+ break;
+ case LDAP_TAG_VLV_BY_VALUE:
+ /* byValue */
+ vlvp->tag= 1;
+ rc = ber_scanf(ber,"o}",&vlvp->value);
+ if (LBER_ERROR == rc)
+ {
+ if (ISLEGACY(be)) {
+ return_value = LDAP_OPERATIONS_ERROR;
+ } else {
+ return_value = LDAP_VIRTUAL_LIST_VIEW_ERROR;
+ }
+ }
+ {
+ /* jcm: isn't there a utility fn to do this? */
+ char *p= slapi_ch_malloc(vlvp->value.bv_len+1);
+ strncpy(p,vlvp->value.bv_val,vlvp->value.bv_len);
+ p[vlvp->value.bv_len]= '\0';
+ LDAPDebug( LDAP_DEBUG_TRACE, "vlv_parse_request_control: Value=%s\n", p, 0, 0 );
+ slapi_ch_free( (void**)&p);
+ }
+ break;
+ default:
+ if (ISLEGACY(be)) {
+ return_value = LDAP_OPERATIONS_ERROR;
+ } else {
+ return_value = LDAP_VIRTUAL_LIST_VIEW_ERROR;
+ }
+ }
+ }
+
+ /* the ber encoding is no longer needed */
+ ber_free(ber,1);
+
+ return return_value;
+}
+
+/* given a slapi_filter, check if there's a vlv index that matches that
+ * filter. if so, return the IDL for that index (else return NULL).
+ * -- a vlv index will match ONLY if that vlv index is subtree-scope and
+ * has the same search base and search filter.
+ * added read lock */
+
+IDList *vlv_find_index_by_filter(struct backend *be, const char *base,
+ Slapi_Filter *f)
+{
+ struct vlvSearch *t = NULL;
+ struct vlvIndex *vi;
+ Slapi_DN base_sdn;
+ PRUint32 length;
+ int err;
+ DB *db = NULL;
+ DBC *dbc = NULL;
+ IDList *idl;
+ Slapi_Filter *vlv_f;
+
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ slapi_sdn_init_dn_byref(&base_sdn, base);
+ for (t = (struct vlvSearch *)be->vlvSearchList; t; t = t->vlv_next) {
+ /* all vlv "filters" start with (|(xxx)(objectclass=referral)).
+ * we only care about the (xxx) part.
+ */
+ vlv_f = t->vlv_slapifilter->f_or;
+ if ((t->vlv_scope == LDAP_SCOPE_SUBTREE) &&
+ (slapi_sdn_compare(t->vlv_base, &base_sdn) == 0) &&
+ (slapi_filter_compare(vlv_f, f) == 0)) {
+ /* found match! */
+ slapi_sdn_done(&base_sdn);
+
+ /* is there an index that's ready? */
+ vi = t->vlv_index;
+ while (!vlvIndex_online(vi) && vi) {
+ vi = vi->vlv_next;
+ }
+ if (!vi) {
+ /* no match */
+ LDAPDebug(LDAP_DEBUG_TRACE, "vlv: no index online for %s\n",
+ t->vlv_filter, 0, 0);
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return NULL;
+ }
+
+ if (dblayer_get_index_file(be, vi->vlv_attrinfo, &db, 0) == 0) {
+ err = db->cursor(db, 0 /* txn */, &dbc, 0);
+ if (err == 0) {
+ length = vlvIndex_get_indexlength(vi, db, 0 /* txn */);
+ if (length == 0) /* 609377: index size could be 0 */
+ {
+ LDAPDebug(LDAP_DEBUG_TRACE, "vlv: index %s is empty\n",
+ t->vlv_filter, 0, 0);
+ idl = NULL;
+ }
+ else
+ {
+ err = vlv_build_idl(0, length-1, db, dbc, &idl, 1 /* dosort */);
+ }
+ dbc->c_close(dbc);
+ }
+ dblayer_release_index_file(be, vi->vlv_attrinfo, db);
+ if (err == 0) {
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return idl;
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY, "vlv find index: err %d\n",
+ err, 0, 0);
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return NULL;
+ }
+ }
+ }
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ /* no match */
+ slapi_sdn_done(&base_sdn);
+ return NULL;
+}
+
+
+
+/* replace c with c2 in string -- probably exists somewhere but I can't find it slapi maybe? */
+
+static void replace_char(char *name, char c, char c2)
+{
+ int x;
+
+ for (x = 0; name[x] != '\0'; x++) {
+ if (c == name[x]) {
+ name[x] = c2;
+ }
+ }
+}
+
+/* similar to what the console GUI does */
+
+char *create_vlv_search_tag(const char* dn) {
+ char *tmp2=strdup(dn);
+
+ replace_char(tmp2,',',' ');
+ replace_char(tmp2,'"','-');
+ replace_char(tmp2,'+','_');
+ return tmp2;
+}
+
+/* Builds strings from Slapi_DN similar console GUI. Uses those dns to
+ delete vlvsearch's if they match. New write lock.
+ */
+
+#define LDBM_PLUGIN_ROOT ", cn=ldbm database, cn=plugins, cn=config"
+#define TAG "cn=by MCC "
+
+int vlv_delete_search_entry(Slapi_PBlock *pb, Slapi_Entry* e, ldbm_instance *inst)
+{
+ int rc=0;
+ Slapi_PBlock *tmppb;
+ Slapi_DN *newdn;
+ struct vlvSearch* p=NULL;
+ char *buf, *buf2, *tag1, *tag2;
+ const char *dn= slapi_sdn_get_dn(&e->e_sdn);
+ backend *be= inst->inst_be;
+
+ tag1=create_vlv_search_tag(dn);
+ buf=slapi_ch_malloc(strlen("cn=MCC ")+strlen(tag1)+strlen(", cn=")+strlen(inst->inst_name)+strlen(LDBM_PLUGIN_ROOT) + 1);
+ sprintf(buf,"%s%s%s%s%s","cn=MCC ",tag1,", cn=",inst->inst_name,LDBM_PLUGIN_ROOT);
+ newdn=slapi_sdn_new_dn_byval(buf);
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+ p = vlvSearch_finddn((struct vlvSearch *)be->vlvSearchList, newdn);
+ if(p!=NULL)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Deleted Virtual List View Search (%s).\n", p->vlv_name, 0, 0);
+ tag2=create_vlv_search_tag(dn);
+ buf2=slapi_ch_malloc(strlen(TAG)+strlen(tag2)+strlen(buf)+2);
+ sprintf(buf2,"%s%s,%s",TAG,tag2,buf);
+ vlvSearch_removefromlist((struct vlvSearch **)&be->vlvSearchList,p->vlv_dn);
+ /* This line release lock to prevent recursive deadlock caused by slapi_internal_delete calling vlvDeleteSearchEntry */
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ vlvSearch_delete(&p);
+ tmppb = slapi_pblock_new();
+ slapi_delete_internal_set_pb(tmppb, buf2, NULL, NULL,
+ (void *)plugin_get_default_component_id(), 0);
+ slapi_delete_internal_pb(tmppb);
+ slapi_pblock_get (tmppb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if(rc != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY, "vlv_delete_search_entry:can't delete dse entry '%s'\n", buf2, 0, 0);
+ }
+ pblock_done(tmppb);
+ pblock_init(tmppb);
+ slapi_delete_internal_set_pb(tmppb, buf, NULL, NULL,
+ (void *)plugin_get_default_component_id(), 0);
+ slapi_delete_internal_pb(tmppb);
+ slapi_pblock_get (tmppb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if(rc != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY, "vlv_delete_search_entry:can't delete dse entry '%s'\n", buf, 0, 0);
+ }
+ slapi_pblock_destroy(tmppb);
+ slapi_ch_free((void **)&tag2);
+ slapi_ch_free((void **)&buf2);
+ } else {
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ }
+ slapi_ch_free((void **)&tag1);
+ slapi_ch_free((void **)&buf);
+ slapi_sdn_free(&newdn);
+ return rc;
+}
+
+void
+vlv_acquire_lock(backend *be)
+{
+ LDAPDebug(LDAP_DEBUG_TRACE, "vlv_acquire_lock => trying to acquire the lock\n", 0, 0, 0);
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+}
+
+void
+vlv_release_lock(backend *be)
+{
+ LDAPDebug(LDAP_DEBUG_TRACE, "vlv_release_lock => trying to release the lock\n", 0, 0, 0);
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+}