mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-09 09:20:15 +01:00
2204 lines
64 KiB
C
2204 lines
64 KiB
C
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Mozilla Communicator client code, released
|
|
* March 31, 1998.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998-1999
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
/*
|
|
*
|
|
* memcache.c - routines that implement an in-memory cache.
|
|
*
|
|
* To Do: 1) ber_dup_ext().
|
|
* 2) referrals and reference?
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include "ldap-int.h"
|
|
|
|
/*
|
|
* Extra size allocated to BerElement.
|
|
* XXXmcs: must match EXBUFSIZ in liblber/io.c?
|
|
*/
|
|
#define EXTRA_SIZE 1024
|
|
|
|
/* Mode constants for function memcache_access() */
|
|
#define MEMCACHE_ACCESS_ADD 0
|
|
#define MEMCACHE_ACCESS_APPEND 1
|
|
#define MEMCACHE_ACCESS_APPEND_LAST 2
|
|
#define MEMCACHE_ACCESS_FIND 3
|
|
#define MEMCACHE_ACCESS_DELETE 4
|
|
#define MEMCACHE_ACCESS_DELETE_ALL 5
|
|
#define MEMCACHE_ACCESS_UPDATE 6
|
|
#define MEMCACHE_ACCESS_FLUSH 7
|
|
#define MEMCACHE_ACCESS_FLUSH_ALL 8
|
|
#define MEMCACHE_ACCESS_FLUSH_LRU 9
|
|
|
|
/* Mode constants for function memcache_adj_size */
|
|
#define MEMCACHE_SIZE_DEDUCT 0
|
|
#define MEMCACHE_SIZE_ADD 1
|
|
|
|
#define MEMCACHE_SIZE_ENTRIES 1
|
|
#define MEMCACHE_SIZE_NON_ENTRIES 2
|
|
|
|
/* Size used for calculation if given size of cache is 0 */
|
|
#define MEMCACHE_DEF_SIZE 131072 /* 128K bytes */
|
|
|
|
/* Index into different list structure */
|
|
#define LIST_TTL 0
|
|
#define LIST_LRU 1
|
|
#define LIST_TMP 2
|
|
#define LIST_TOTAL 3
|
|
|
|
/* Macros to make code more readable */
|
|
#define NSLDAPI_VALID_MEMCACHE_POINTER( cp ) ( (cp) != NULL )
|
|
#define NSLDAPI_STR_NONNULL( s ) ( (s) ? (s) : "" )
|
|
#define NSLDAPI_SAFE_STRLEN( s ) ( (s) ? strlen((s)) + 1 : 1 )
|
|
|
|
/* Macros dealing with mutex */
|
|
#define LDAP_MEMCACHE_MUTEX_LOCK( c ) \
|
|
if ( (c) && ((c)->ldmemc_lock_fns).ltf_mutex_lock ) { \
|
|
((c)->ldmemc_lock_fns).ltf_mutex_lock( (c)->ldmemc_lock ); \
|
|
}
|
|
|
|
#define LDAP_MEMCACHE_MUTEX_UNLOCK( c ) \
|
|
if ( (c) && ((c)->ldmemc_lock_fns).ltf_mutex_unlock ) { \
|
|
((c)->ldmemc_lock_fns).ltf_mutex_unlock( (c)->ldmemc_lock ); \
|
|
}
|
|
|
|
#define LDAP_MEMCACHE_MUTEX_ALLOC( c ) \
|
|
((c) && ((c)->ldmemc_lock_fns).ltf_mutex_alloc ? \
|
|
((c)->ldmemc_lock_fns).ltf_mutex_alloc() : NULL)
|
|
|
|
#define LDAP_MEMCACHE_MUTEX_FREE( c ) \
|
|
if ( (c) && ((c)->ldmemc_lock_fns).ltf_mutex_free ) { \
|
|
((c)->ldmemc_lock_fns).ltf_mutex_free( (c)->ldmemc_lock ); \
|
|
}
|
|
|
|
/* Macros used for triming unnecessary spaces in a basedn */
|
|
#define NSLDAPI_IS_SPACE( c ) \
|
|
(((c) == ' ') || ((c) == '\t') || ((c) == '\n'))
|
|
|
|
#define NSLDAPI_IS_SEPARATER( c ) \
|
|
((c) == ',')
|
|
|
|
/* Hash table callback function pointer definition */
|
|
typedef int (*HashFuncPtr)(int table_size, void *key);
|
|
typedef int (*PutDataPtr)(void **ppTableData, void *key, void *pData);
|
|
typedef int (*GetDataPtr)(void *pTableData, void *key, void **ppData);
|
|
typedef int (*RemoveDataPtr)(void **ppTableData, void *key, void **ppData);
|
|
typedef int (*MiscFuncPtr)(void **ppTableData, void *key, void *pData);
|
|
typedef void (*ClrTableNodePtr)(void **ppTableData, void *pData);
|
|
|
|
/* Structure of a node in a hash table */
|
|
typedef struct HashTableNode_struct {
|
|
void *pData;
|
|
} HashTableNode;
|
|
|
|
/* Structure of a hash table */
|
|
typedef struct HashTable_struct {
|
|
HashTableNode *table;
|
|
int size;
|
|
HashFuncPtr hashfunc;
|
|
PutDataPtr putdata;
|
|
GetDataPtr getdata;
|
|
MiscFuncPtr miscfunc;
|
|
RemoveDataPtr removedata;
|
|
ClrTableNodePtr clrtablenode;
|
|
} HashTable;
|
|
|
|
/* Structure uniquely identifies a search request */
|
|
typedef struct ldapmemcacheReqId_struct {
|
|
LDAP *ldmemcrid_ld;
|
|
int ldmemcrid_msgid;
|
|
} ldapmemcacheReqId;
|
|
|
|
/* Structure representing a ldap handle associated to memcache */
|
|
typedef struct ldapmemcacheld_struct {
|
|
LDAP *ldmemcl_ld;
|
|
struct ldapmemcacheld_struct *ldmemcl_next;
|
|
} ldapmemcacheld;
|
|
|
|
/* Structure representing header of a search result */
|
|
typedef struct ldapmemcacheRes_struct {
|
|
char *ldmemcr_basedn;
|
|
unsigned long ldmemcr_crc_key;
|
|
unsigned long ldmemcr_resSize;
|
|
unsigned long ldmemcr_timestamp;
|
|
LDAPMessage *ldmemcr_resHead;
|
|
LDAPMessage *ldmemcr_resTail;
|
|
ldapmemcacheReqId ldmemcr_req_id;
|
|
struct ldapmemcacheRes_struct *ldmemcr_next[LIST_TOTAL];
|
|
struct ldapmemcacheRes_struct *ldmemcr_prev[LIST_TOTAL];
|
|
struct ldapmemcacheRes_struct *ldmemcr_htable_next;
|
|
} ldapmemcacheRes;
|
|
|
|
/* Structure for memcache statistics */
|
|
typedef struct ldapmemcacheStats_struct {
|
|
unsigned long ldmemcstat_tries;
|
|
unsigned long ldmemcstat_hits;
|
|
} ldapmemcacheStats;
|
|
|
|
/* Structure of a memcache object */
|
|
struct ldapmemcache {
|
|
unsigned long ldmemc_ttl;
|
|
unsigned long ldmemc_size;
|
|
unsigned long ldmemc_size_used;
|
|
unsigned long ldmemc_size_entries;
|
|
char **ldmemc_basedns;
|
|
void *ldmemc_lock;
|
|
ldapmemcacheld *ldmemc_lds;
|
|
HashTable *ldmemc_resTmp;
|
|
HashTable *ldmemc_resLookup;
|
|
ldapmemcacheRes *ldmemc_resHead[LIST_TOTAL];
|
|
ldapmemcacheRes *ldmemc_resTail[LIST_TOTAL];
|
|
struct ldap_thread_fns ldmemc_lock_fns;
|
|
ldapmemcacheStats ldmemc_stats;
|
|
};
|
|
|
|
/* Function prototypes */
|
|
static int memcache_exist(LDAP *ld);
|
|
static int memcache_add_to_ld(LDAP *ld, int msgid, LDAPMessage *pMsg);
|
|
static int memcache_compare_dn(const char *main_dn, const char *dn, int scope);
|
|
static int memcache_dup_message(LDAPMessage *res, int msgid, int fromcache,
|
|
LDAPMessage **ppResCopy, unsigned long *pSize);
|
|
static BerElement* memcache_ber_dup(BerElement* pBer, unsigned long *pSize);
|
|
|
|
static void memcache_trim_basedn_spaces(char *basedn);
|
|
static int memcache_validate_basedn(LDAPMemCache *cache, const char *basedn);
|
|
static int memcache_get_ctrls_len(LDAPControl **ctrls);
|
|
static void memcache_append_ctrls(char *buf, LDAPControl **serverCtrls,
|
|
LDAPControl **clientCtrls);
|
|
static int memcache_adj_size(LDAPMemCache *cache, unsigned long size,
|
|
int usageFlags, int bAdd);
|
|
static int memcache_free_entry(LDAPMemCache *cache, ldapmemcacheRes *pRes);
|
|
static int memcache_expired(LDAPMemCache *cache, ldapmemcacheRes *pRes,
|
|
unsigned long curTime);
|
|
static int memcache_add_to_list(LDAPMemCache *cache, ldapmemcacheRes *pRes,
|
|
int index);
|
|
static int memcache_add_res_to_list(ldapmemcacheRes *pRes, LDAPMessage *pMsg,
|
|
unsigned long size);
|
|
static int memcache_free_from_list(LDAPMemCache *cache, ldapmemcacheRes *pRes,
|
|
int index);
|
|
static int memcache_search(LDAP *ld, unsigned long key, LDAPMessage **ppRes);
|
|
static int memcache_add(LDAP *ld, unsigned long key, int msgid,
|
|
const char *basedn);
|
|
static int memcache_append(LDAP *ld, int msgid, LDAPMessage *pRes);
|
|
static int memcache_append_last(LDAP *ld, int msgid, LDAPMessage *pRes);
|
|
static int memcache_remove(LDAP *ld, int msgid);
|
|
#if 0 /* function not used */
|
|
static int memcache_remove_all(LDAP *ld);
|
|
#endif /* 0 */
|
|
static int memcache_access(LDAPMemCache *cache, int mode,
|
|
void *pData1, void *pData2, void *pData3);
|
|
#ifdef LDAP_DEBUG
|
|
static void memcache_print_list( LDAPMemCache *cache, int index );
|
|
static void memcache_report_statistics( LDAPMemCache *cache );
|
|
#endif /* LDAP_DEBUG */
|
|
|
|
static int htable_calculate_size(int sizelimit);
|
|
static int htable_sizeinbytes(HashTable *pTable);
|
|
static int htable_put(HashTable *pTable, void *key, void *pData);
|
|
static int htable_get(HashTable *pTable, void *key, void **ppData);
|
|
static int htable_misc(HashTable *pTable, void *key, void *pData);
|
|
static int htable_remove(HashTable *pTable, void *key, void **ppData);
|
|
static int htable_removeall(HashTable *pTable, void *pData);
|
|
static int htable_create(int size_limit, HashFuncPtr hashf,
|
|
PutDataPtr putDataf, GetDataPtr getDataf,
|
|
RemoveDataPtr removeDataf, ClrTableNodePtr clrNodef,
|
|
MiscFuncPtr miscOpf, HashTable **ppTable);
|
|
static int htable_free(HashTable *pTable);
|
|
|
|
static int msgid_hashf(int table_size, void *key);
|
|
static int msgid_putdata(void **ppTableData, void *key, void *pData);
|
|
static int msgid_getdata(void *pTableData, void *key, void **ppData);
|
|
static int msgid_removedata(void **ppTableData, void *key, void **ppData);
|
|
static int msgid_clear_ld_items(void **ppTableData, void *key, void *pData);
|
|
static void msgid_clearnode(void **ppTableData, void *pData);
|
|
|
|
static int attrkey_hashf(int table_size, void *key);
|
|
static int attrkey_putdata(void **ppTableData, void *key, void *pData);
|
|
static int attrkey_getdata(void *pTableData, void *key, void **ppData);
|
|
static int attrkey_removedata(void **ppTableData, void *key, void **ppData);
|
|
static void attrkey_clearnode(void **ppTableData, void *pData);
|
|
|
|
static unsigned long crc32_convert(char *buf, int len);
|
|
|
|
/* Create a memcache object. */
|
|
int
|
|
LDAP_CALL
|
|
ldap_memcache_init( unsigned long ttl, unsigned long size,
|
|
char **baseDNs, struct ldap_thread_fns *thread_fns,
|
|
LDAPMemCache **cachep )
|
|
{
|
|
unsigned long total_size = 0;
|
|
|
|
LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_init\n", 0, 0, 0 );
|
|
|
|
if ( cachep == NULL ) {
|
|
return( LDAP_PARAM_ERROR );
|
|
}
|
|
|
|
if ((*cachep = (LDAPMemCache*)NSLDAPI_CALLOC(1,
|
|
sizeof(LDAPMemCache))) == NULL) {
|
|
return ( LDAP_NO_MEMORY );
|
|
}
|
|
|
|
total_size += sizeof(LDAPMemCache);
|
|
|
|
(*cachep)->ldmemc_ttl = ttl;
|
|
(*cachep)->ldmemc_size = size;
|
|
(*cachep)->ldmemc_lds = NULL;
|
|
|
|
/* Non-zero default size needed for calculating size of hash tables */
|
|
size = (size ? size : MEMCACHE_DEF_SIZE);
|
|
|
|
if (thread_fns) {
|
|
memcpy(&((*cachep)->ldmemc_lock_fns), thread_fns,
|
|
sizeof(struct ldap_thread_fns));
|
|
} else {
|
|
memset(&((*cachep)->ldmemc_lock_fns), 0,
|
|
sizeof(struct ldap_thread_fns));
|
|
}
|
|
|
|
(*cachep)->ldmemc_lock = LDAP_MEMCACHE_MUTEX_ALLOC( *cachep );
|
|
|
|
/* Cache basedns */
|
|
if (baseDNs != NULL) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; baseDNs[i]; i++) {
|
|
;
|
|
}
|
|
|
|
(*cachep)->ldmemc_basedns = (char**)NSLDAPI_CALLOC(i + 1,
|
|
sizeof(char*));
|
|
|
|
if ((*cachep)->ldmemc_basedns == NULL) {
|
|
ldap_memcache_destroy(*cachep);
|
|
*cachep = NULL;
|
|
return ( LDAP_NO_MEMORY );
|
|
}
|
|
|
|
total_size += (i + 1) * sizeof(char*);
|
|
|
|
for (i = 0; baseDNs[i]; i++) {
|
|
(*cachep)->ldmemc_basedns[i] = nsldapi_strdup(baseDNs[i]);
|
|
total_size += strlen(baseDNs[i]) + 1;
|
|
}
|
|
|
|
(*cachep)->ldmemc_basedns[i] = NULL;
|
|
}
|
|
|
|
/* Create hash table for temporary cache */
|
|
if (htable_create(size, msgid_hashf, msgid_putdata, msgid_getdata,
|
|
msgid_removedata, msgid_clearnode, msgid_clear_ld_items,
|
|
&((*cachep)->ldmemc_resTmp)) != LDAP_SUCCESS) {
|
|
ldap_memcache_destroy(*cachep);
|
|
*cachep = NULL;
|
|
return( LDAP_NO_MEMORY );
|
|
}
|
|
|
|
total_size += htable_sizeinbytes((*cachep)->ldmemc_resTmp);
|
|
|
|
/* Create hash table for primary cache */
|
|
if (htable_create(size, attrkey_hashf, attrkey_putdata,
|
|
attrkey_getdata, attrkey_removedata, attrkey_clearnode,
|
|
NULL, &((*cachep)->ldmemc_resLookup)) != LDAP_SUCCESS) {
|
|
ldap_memcache_destroy(*cachep);
|
|
*cachep = NULL;
|
|
return( LDAP_NO_MEMORY );
|
|
}
|
|
|
|
total_size += htable_sizeinbytes((*cachep)->ldmemc_resLookup);
|
|
|
|
/* See if there is enough room so far */
|
|
if (memcache_adj_size(*cachep, total_size, MEMCACHE_SIZE_NON_ENTRIES,
|
|
MEMCACHE_SIZE_ADD) != LDAP_SUCCESS) {
|
|
ldap_memcache_destroy(*cachep);
|
|
*cachep = NULL;
|
|
return( LDAP_SIZELIMIT_EXCEEDED );
|
|
}
|
|
|
|
LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_init new cache 0x%x\n",
|
|
*cachep, 0, 0 );
|
|
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
/* Associates a ldap handle to a memcache object. */
|
|
int
|
|
LDAP_CALL
|
|
ldap_memcache_set( LDAP *ld, LDAPMemCache *cache )
|
|
{
|
|
int nRes = LDAP_SUCCESS;
|
|
|
|
LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_set\n", 0, 0, 0 );
|
|
|
|
if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) )
|
|
return( LDAP_PARAM_ERROR );
|
|
|
|
LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
|
|
if (ld->ld_memcache != cache) {
|
|
|
|
LDAPMemCache *c = ld->ld_memcache;
|
|
ldapmemcacheld *pCur = NULL;
|
|
ldapmemcacheld *pPrev = NULL;
|
|
|
|
/* First dissociate handle from old cache */
|
|
|
|
LDAP_MEMCACHE_MUTEX_LOCK( c );
|
|
|
|
pCur = (c ? c->ldmemc_lds : NULL);
|
|
for (; pCur; pCur = pCur->ldmemcl_next) {
|
|
if (pCur->ldmemcl_ld == ld)
|
|
break;
|
|
pPrev = pCur;
|
|
}
|
|
|
|
if (pCur) {
|
|
|
|
ldapmemcacheReqId reqid;
|
|
|
|
reqid.ldmemcrid_ld = ld;
|
|
reqid.ldmemcrid_msgid = -1;
|
|
htable_misc(c->ldmemc_resTmp, (void*)&reqid, (void*)c);
|
|
|
|
if (pPrev)
|
|
pPrev->ldmemcl_next = pCur->ldmemcl_next;
|
|
else
|
|
c->ldmemc_lds = pCur->ldmemcl_next;
|
|
NSLDAPI_FREE(pCur);
|
|
pCur = NULL;
|
|
|
|
memcache_adj_size(c, sizeof(ldapmemcacheld),
|
|
MEMCACHE_SIZE_NON_ENTRIES, MEMCACHE_SIZE_DEDUCT);
|
|
}
|
|
|
|
LDAP_MEMCACHE_MUTEX_UNLOCK( c );
|
|
|
|
ld->ld_memcache = NULL;
|
|
|
|
/* Exit if no new cache is specified */
|
|
if (cache == NULL) {
|
|
LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
/* Then associate handle with new cache */
|
|
|
|
LDAP_MEMCACHE_MUTEX_LOCK( cache );
|
|
|
|
if ((nRes = memcache_adj_size(cache, sizeof(ldapmemcacheld),
|
|
MEMCACHE_SIZE_NON_ENTRIES, MEMCACHE_SIZE_ADD)) != LDAP_SUCCESS) {
|
|
LDAP_MEMCACHE_MUTEX_UNLOCK( cache );
|
|
LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
return nRes;
|
|
}
|
|
|
|
pCur = (ldapmemcacheld*)NSLDAPI_CALLOC(1, sizeof(ldapmemcacheld));
|
|
if (pCur == NULL) {
|
|
memcache_adj_size(cache, sizeof(ldapmemcacheld),
|
|
MEMCACHE_SIZE_NON_ENTRIES, MEMCACHE_SIZE_DEDUCT);
|
|
nRes = LDAP_NO_MEMORY;
|
|
} else {
|
|
pCur->ldmemcl_ld = ld;
|
|
pCur->ldmemcl_next = cache->ldmemc_lds;
|
|
cache->ldmemc_lds = pCur;
|
|
ld->ld_memcache = cache;
|
|
}
|
|
|
|
LDAP_MEMCACHE_MUTEX_UNLOCK( cache );
|
|
}
|
|
|
|
LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
|
|
return nRes;
|
|
}
|
|
|
|
/* Retrieves memcache with which the ldap handle has been associated. */
|
|
int
|
|
LDAP_CALL
|
|
ldap_memcache_get( LDAP *ld, LDAPMemCache **cachep )
|
|
{
|
|
LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_get ld: 0x%x\n", ld, 0, 0 );
|
|
|
|
if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || cachep == NULL ) {
|
|
return( LDAP_PARAM_ERROR );
|
|
}
|
|
|
|
LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
*cachep = ld->ld_memcache;
|
|
LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
/*
|
|
* Function that stays inside libldap and proactively expires items from
|
|
* the given cache. This should be called from a newly created thread since
|
|
* it will not return until after ldap_memcache_destroy() is called.
|
|
*/
|
|
void
|
|
LDAP_CALL
|
|
ldap_memcache_update( LDAPMemCache *cache )
|
|
{
|
|
LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_update: cache 0x%x\n",
|
|
cache, 0, 0 );
|
|
|
|
if ( !NSLDAPI_VALID_MEMCACHE_POINTER( cache )) {
|
|
return;
|
|
}
|
|
|
|
LDAP_MEMCACHE_MUTEX_LOCK( cache );
|
|
memcache_access(cache, MEMCACHE_ACCESS_UPDATE, NULL, NULL, NULL);
|
|
LDAP_MEMCACHE_MUTEX_UNLOCK( cache );
|
|
}
|
|
|
|
/* Removes specified entries from given memcache. */
|
|
void
|
|
LDAP_CALL
|
|
ldap_memcache_flush( LDAPMemCache *cache, char *dn, int scope )
|
|
{
|
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
|
"ldap_memcache_flush( cache: 0x%x, dn: %s, scope: %d)\n",
|
|
cache, ( dn == NULL ) ? "(null)" : dn, scope );
|
|
|
|
if ( !NSLDAPI_VALID_MEMCACHE_POINTER( cache )) {
|
|
return;
|
|
}
|
|
|
|
LDAP_MEMCACHE_MUTEX_LOCK( cache );
|
|
|
|
if (!dn) {
|
|
memcache_access(cache, MEMCACHE_ACCESS_FLUSH_ALL, NULL, NULL, NULL);
|
|
} else {
|
|
memcache_access(cache, MEMCACHE_ACCESS_FLUSH,
|
|
(void*)dn, (void*)scope, NULL);
|
|
}
|
|
|
|
LDAP_MEMCACHE_MUTEX_UNLOCK( cache );
|
|
}
|
|
|
|
/* Destroys the given memcache. */
|
|
void
|
|
LDAP_CALL
|
|
ldap_memcache_destroy( LDAPMemCache *cache )
|
|
{
|
|
int i = 0;
|
|
unsigned long size = sizeof(LDAPMemCache);
|
|
ldapmemcacheld *pNode = NULL, *pNextNode = NULL;
|
|
|
|
LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_destroy( 0x%x )\n",
|
|
cache, 0, 0 );
|
|
|
|
if ( !NSLDAPI_VALID_MEMCACHE_POINTER( cache )) {
|
|
return;
|
|
}
|
|
|
|
/* Dissociate all ldap handes from this cache. */
|
|
LDAP_MEMCACHE_MUTEX_LOCK( cache );
|
|
|
|
for (pNode = cache->ldmemc_lds; pNode; pNode = pNextNode, i++) {
|
|
LDAP_MUTEX_LOCK( pNode->ldmemcl_ld, LDAP_MEMCACHE_LOCK );
|
|
cache->ldmemc_lds = pNode->ldmemcl_next;
|
|
pNode->ldmemcl_ld->ld_memcache = NULL;
|
|
LDAP_MUTEX_UNLOCK( pNode->ldmemcl_ld, LDAP_MEMCACHE_LOCK );
|
|
pNextNode = pNode->ldmemcl_next;
|
|
NSLDAPI_FREE(pNode);
|
|
}
|
|
|
|
size += i * sizeof(ldapmemcacheld);
|
|
|
|
LDAP_MEMCACHE_MUTEX_UNLOCK( cache );
|
|
|
|
/* Free array of basedns */
|
|
if (cache->ldmemc_basedns) {
|
|
for (i = 0; cache->ldmemc_basedns[i]; i++) {
|
|
size += strlen(cache->ldmemc_basedns[i]) + 1;
|
|
NSLDAPI_FREE(cache->ldmemc_basedns[i]);
|
|
}
|
|
size += (i + 1) * sizeof(char*);
|
|
NSLDAPI_FREE(cache->ldmemc_basedns);
|
|
}
|
|
|
|
/* Free hash table used for temporary cache */
|
|
if (cache->ldmemc_resTmp) {
|
|
size += htable_sizeinbytes(cache->ldmemc_resTmp);
|
|
memcache_access(cache, MEMCACHE_ACCESS_DELETE_ALL, NULL, NULL, NULL);
|
|
htable_free(cache->ldmemc_resTmp);
|
|
}
|
|
|
|
/* Free hash table used for primary cache */
|
|
if (cache->ldmemc_resLookup) {
|
|
size += htable_sizeinbytes(cache->ldmemc_resLookup);
|
|
memcache_access(cache, MEMCACHE_ACCESS_FLUSH_ALL, NULL, NULL, NULL);
|
|
htable_free(cache->ldmemc_resLookup);
|
|
}
|
|
|
|
memcache_adj_size(cache, size, MEMCACHE_SIZE_NON_ENTRIES,
|
|
MEMCACHE_SIZE_DEDUCT);
|
|
|
|
LDAP_MEMCACHE_MUTEX_FREE( cache );
|
|
|
|
NSLDAPI_FREE(cache);
|
|
}
|
|
|
|
/************************* Internal API Functions ****************************/
|
|
|
|
/* Creates an integer key by applying the Cyclic Reduntency Check algorithm on
|
|
a long string formed by concatenating all the search parameters plus the
|
|
current bind DN. The key is used in the cache for looking up cached
|
|
entries. It is assumed that the CRC algorithm will generate
|
|
different integers from different byte strings. */
|
|
int
|
|
ldap_memcache_createkey(LDAP *ld, const char *base, int scope,
|
|
const char *filter, char **attrs,
|
|
int attrsonly, LDAPControl **serverctrls,
|
|
LDAPControl **clientctrls, unsigned long *keyp)
|
|
{
|
|
int nRes, i, j, i_smallest;
|
|
int len;
|
|
int defport;
|
|
char buf[50];
|
|
char *tmp, *defhost, *binddn, *keystr, *tmpbase;
|
|
|
|
if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || (keyp == NULL) )
|
|
return( LDAP_PARAM_ERROR );
|
|
|
|
*keyp = 0;
|
|
|
|
if (!memcache_exist(ld))
|
|
return( LDAP_LOCAL_ERROR );
|
|
|
|
LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
LDAP_MEMCACHE_MUTEX_LOCK( ld->ld_memcache );
|
|
nRes = memcache_validate_basedn(ld->ld_memcache, base);
|
|
LDAP_MEMCACHE_MUTEX_UNLOCK( ld->ld_memcache );
|
|
LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
|
|
if (nRes != LDAP_SUCCESS)
|
|
return nRes;
|
|
|
|
defhost = NSLDAPI_STR_NONNULL(ld->ld_defhost);
|
|
defport = ld->ld_defport;
|
|
tmpbase = nsldapi_strdup(NSLDAPI_STR_NONNULL(base));
|
|
memcache_trim_basedn_spaces(tmpbase);
|
|
|
|
if ((binddn = nsldapi_get_binddn(ld)) == NULL)
|
|
binddn = "";
|
|
|
|
sprintf(buf, "%i\n%i\n%i\n", defport, scope, (attrsonly ? 1 : 0));
|
|
len = NSLDAPI_SAFE_STRLEN(buf) + NSLDAPI_SAFE_STRLEN(tmpbase) +
|
|
NSLDAPI_SAFE_STRLEN(filter) + NSLDAPI_SAFE_STRLEN(defhost) +
|
|
NSLDAPI_SAFE_STRLEN(binddn);
|
|
|
|
if (attrs) {
|
|
for (i = 0; attrs[i]; i++) {
|
|
|
|
for (i_smallest = j = i; attrs[j]; j++) {
|
|
if (strcasecmp(attrs[i_smallest], attrs[j]) > 0)
|
|
i_smallest = j;
|
|
}
|
|
|
|
if (i != i_smallest) {
|
|
tmp = attrs[i];
|
|
attrs[i] = attrs[i_smallest];
|
|
attrs[i_smallest] = tmp;
|
|
}
|
|
|
|
len += NSLDAPI_SAFE_STRLEN(attrs[i]);
|
|
}
|
|
} else {
|
|
len += 1;
|
|
}
|
|
|
|
len += memcache_get_ctrls_len(serverctrls) +
|
|
memcache_get_ctrls_len(clientctrls) + 1;
|
|
|
|
if ((keystr = (char*)NSLDAPI_CALLOC(len, sizeof(char))) == NULL) {
|
|
NSLDAPI_FREE(defhost);
|
|
return( LDAP_NO_MEMORY );
|
|
}
|
|
|
|
sprintf(keystr, "%s\n%s\n%s\n%s\n%s\n", binddn, tmpbase,
|
|
NSLDAPI_STR_NONNULL(defhost), NSLDAPI_STR_NONNULL(filter),
|
|
NSLDAPI_STR_NONNULL(buf));
|
|
|
|
if (attrs) {
|
|
for (i = 0; attrs[i]; i++) {
|
|
strcat(keystr, NSLDAPI_STR_NONNULL(attrs[i]));
|
|
strcat(keystr, "\n");
|
|
}
|
|
} else {
|
|
strcat(keystr, "\n");
|
|
}
|
|
|
|
for (tmp = keystr; *tmp;
|
|
*tmp += (*tmp >= 'a' && *tmp <= 'z' ? 'A'-'a' : 0), tmp++) {
|
|
;
|
|
}
|
|
|
|
memcache_append_ctrls(keystr, serverctrls, clientctrls);
|
|
|
|
/* CRC algorithm */
|
|
*keyp = crc32_convert(keystr, len);
|
|
|
|
NSLDAPI_FREE(keystr);
|
|
NSLDAPI_FREE(tmpbase);
|
|
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
/* Searches the cache for the right cached entries, and if found, attaches
|
|
them to the given ldap handle. This function relies on locking by the
|
|
caller. */
|
|
int
|
|
ldap_memcache_result(LDAP *ld, int msgid, unsigned long key)
|
|
{
|
|
int nRes;
|
|
LDAPMessage *pMsg = NULL;
|
|
|
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
|
"ldap_memcache_result( ld: 0x%x, msgid: %d, key: 0x%8.8lx)\n",
|
|
ld, msgid, key );
|
|
|
|
if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || (msgid < 0) ) {
|
|
return( LDAP_PARAM_ERROR );
|
|
}
|
|
|
|
if (!memcache_exist(ld)) {
|
|
return( LDAP_LOCAL_ERROR );
|
|
}
|
|
|
|
LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
LDAP_MEMCACHE_MUTEX_LOCK( ld->ld_memcache );
|
|
|
|
/* Search the cache and append the results to ld if found */
|
|
++ld->ld_memcache->ldmemc_stats.ldmemcstat_tries;
|
|
if ((nRes = memcache_search(ld, key, &pMsg)) == LDAP_SUCCESS) {
|
|
nRes = memcache_add_to_ld(ld, msgid, pMsg);
|
|
++ld->ld_memcache->ldmemc_stats.ldmemcstat_hits;
|
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
|
"ldap_memcache_result: key 0x%8.8lx found in cache\n",
|
|
key, 0, 0 );
|
|
} else {
|
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
|
"ldap_memcache_result: key 0x%8.8lx not found in cache\n",
|
|
key, 0, 0 );
|
|
}
|
|
|
|
#ifdef LDAP_DEBUG
|
|
memcache_print_list( ld->ld_memcache, LIST_LRU );
|
|
memcache_report_statistics( ld->ld_memcache );
|
|
#endif /* LDAP_DEBUG */
|
|
|
|
LDAP_MEMCACHE_MUTEX_UNLOCK( ld->ld_memcache );
|
|
LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
|
|
return nRes;
|
|
}
|
|
|
|
/* Creates a new header in the cache so that entries arriving from the
|
|
directory server can later be cached under the header. */
|
|
int
|
|
ldap_memcache_new(LDAP *ld, int msgid, unsigned long key, const char *basedn)
|
|
{
|
|
int nRes;
|
|
|
|
if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) ) {
|
|
return( LDAP_PARAM_ERROR );
|
|
}
|
|
|
|
LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
|
|
if (!memcache_exist(ld)) {
|
|
LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
return( LDAP_LOCAL_ERROR );
|
|
}
|
|
|
|
LDAP_MEMCACHE_MUTEX_LOCK( ld->ld_memcache );
|
|
nRes = memcache_add(ld, key, msgid, basedn);
|
|
LDAP_MEMCACHE_MUTEX_UNLOCK( ld->ld_memcache );
|
|
LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
|
|
return nRes;
|
|
}
|
|
|
|
/* Appends a chain of entries to an existing cache header. Parameter "bLast"
|
|
indicates whether there will be more entries arriving for the search in
|
|
question. */
|
|
int
|
|
ldap_memcache_append(LDAP *ld, int msgid, int bLast, LDAPMessage *result)
|
|
{
|
|
int nRes = LDAP_SUCCESS;
|
|
|
|
LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_append( ld: 0x%x, ", ld, 0, 0 );
|
|
LDAPDebug( LDAP_DEBUG_TRACE, "msgid %d, bLast: %d, result: 0x%x)\n",
|
|
msgid, bLast, result );
|
|
|
|
if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || !result ) {
|
|
return( LDAP_PARAM_ERROR );
|
|
}
|
|
|
|
LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
|
|
if (!memcache_exist(ld)) {
|
|
LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
return( LDAP_LOCAL_ERROR );
|
|
}
|
|
|
|
LDAP_MEMCACHE_MUTEX_LOCK( ld->ld_memcache );
|
|
|
|
if (!bLast)
|
|
nRes = memcache_append(ld, msgid, result);
|
|
else
|
|
nRes = memcache_append_last(ld, msgid, result);
|
|
|
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
|
"ldap_memcache_append: %s result for msgid %d\n",
|
|
( nRes == LDAP_SUCCESS ) ? "added" : "failed to add", msgid , 0 );
|
|
|
|
LDAP_MEMCACHE_MUTEX_UNLOCK( ld->ld_memcache );
|
|
LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
|
|
return nRes;
|
|
}
|
|
|
|
/* Removes partially cached results for a search as a result of calling
|
|
ldap_abandon() by the client. */
|
|
int
|
|
ldap_memcache_abandon(LDAP *ld, int msgid)
|
|
{
|
|
int nRes;
|
|
|
|
if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || (msgid < 0) ) {
|
|
return( LDAP_PARAM_ERROR );
|
|
}
|
|
|
|
LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
|
|
if (!memcache_exist(ld)) {
|
|
LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
return( LDAP_LOCAL_ERROR );
|
|
}
|
|
|
|
LDAP_MEMCACHE_MUTEX_LOCK( ld->ld_memcache );
|
|
nRes = memcache_remove(ld, msgid);
|
|
LDAP_MEMCACHE_MUTEX_UNLOCK( ld->ld_memcache );
|
|
LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
|
|
|
|
return nRes;
|
|
}
|
|
|
|
/*************************** helper functions *******************************/
|
|
|
|
/* Removes extraneous spaces in a basedn so that basedns differ by only those
|
|
spaces will be treated as equal. Extraneous spaces are those that
|
|
precedes the basedn and those that follow a comma. */
|
|
/*
|
|
* XXXmcs: this is a bit too agressive... we need to deal with the fact that
|
|
* commas and spaces may be quoted, in which case it is wrong to remove them.
|
|
*/
|
|
static void
|
|
memcache_trim_basedn_spaces(char *basedn)
|
|
{
|
|
char *pRead, *pWrite;
|
|
|
|
if (!basedn)
|
|
return;
|
|
|
|
for (pWrite = pRead = basedn; *pRead; ) {
|
|
for (; *pRead && NSLDAPI_IS_SPACE(*pRead); pRead++) {
|
|
;
|
|
}
|
|
for (; *pRead && !NSLDAPI_IS_SEPARATER(*pRead);
|
|
*(pWrite++) = *(pRead++)) {
|
|
;
|
|
}
|
|
*(pWrite++) = (*pRead ? *(pRead++) : *pRead);
|
|
}
|
|
}
|
|
|
|
/* Verifies whether the results of a search should be cached or not by
|
|
checking if the search's basedn falls under any of the basedns for which
|
|
the memcache is responsible. */
|
|
static int
|
|
memcache_validate_basedn(LDAPMemCache *cache, const char *basedn)
|
|
{
|
|
int i;
|
|
|
|
if ( cache->ldmemc_basedns == NULL ) {
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
#if 1
|
|
if (basedn == NULL) {
|
|
basedn = "";
|
|
}
|
|
#else
|
|
/* XXXmcs: I do not understand this code... */
|
|
if (basedn == NULL)
|
|
return (cache->ldmemc_basedns && cache->ldmemc_basedns[0] ?
|
|
LDAP_OPERATIONS_ERROR : LDAP_SUCCESS);
|
|
}
|
|
#endif
|
|
|
|
for (i = 0; cache->ldmemc_basedns[i]; i++) {
|
|
if (memcache_compare_dn(basedn, cache->ldmemc_basedns[i],
|
|
LDAP_SCOPE_SUBTREE) == LDAP_COMPARE_TRUE) {
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
}
|
|
|
|
return( LDAP_OPERATIONS_ERROR );
|
|
}
|
|
|
|
/* Calculates the length of the buffer needed to concatenate the contents of
|
|
a ldap control. */
|
|
static int
|
|
memcache_get_ctrls_len(LDAPControl **ctrls)
|
|
{
|
|
int len = 0, i;
|
|
|
|
if (ctrls) {
|
|
for (i = 0; ctrls[i]; i++) {
|
|
len += strlen(NSLDAPI_STR_NONNULL(ctrls[i]->ldctl_oid)) +
|
|
(ctrls[i]->ldctl_value).bv_len + 4;
|
|
}
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
/* Contenates the contents of client and server controls to a buffer. */
|
|
static void
|
|
memcache_append_ctrls(char *buf, LDAPControl **serverCtrls,
|
|
LDAPControl **clientCtrls)
|
|
{
|
|
int i, j;
|
|
char *pCh = buf + strlen(buf);
|
|
LDAPControl **ctrls;
|
|
|
|
for (j = 0; j < 2; j++) {
|
|
|
|
if ((ctrls = (j ? clientCtrls : serverCtrls)) == NULL)
|
|
continue;
|
|
|
|
for (i = 0; ctrls[i]; i++) {
|
|
sprintf(pCh, "%s\n", NSLDAPI_STR_NONNULL(ctrls[i]->ldctl_oid));
|
|
pCh += strlen(NSLDAPI_STR_NONNULL(ctrls[i]->ldctl_oid)) + 1;
|
|
if ((ctrls[i]->ldctl_value).bv_len > 0) {
|
|
memcpy(pCh, (ctrls[i]->ldctl_value).bv_val,
|
|
(ctrls[i]->ldctl_value).bv_len);
|
|
pCh += (ctrls[i]->ldctl_value).bv_len;
|
|
}
|
|
sprintf(pCh, "\n%i\n", (ctrls[i]->ldctl_iscritical ? 1 : 0));
|
|
pCh += 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Increases or decreases the size (in bytes) the given memcache currently
|
|
uses. If the size goes over the limit, the function returns an error. */
|
|
static int
|
|
memcache_adj_size(LDAPMemCache *cache, unsigned long size,
|
|
int usageFlags, int bAdd)
|
|
{
|
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
|
"memcache_adj_size: attempting to %s %ld %s bytes...\n",
|
|
bAdd ? "add" : "remove", size,
|
|
( usageFlags & MEMCACHE_SIZE_ENTRIES ) ? "entry" : "non-entry" );
|
|
|
|
if (bAdd) {
|
|
cache->ldmemc_size_used += size;
|
|
if ((cache->ldmemc_size > 0) &&
|
|
(cache->ldmemc_size_used > cache->ldmemc_size)) {
|
|
|
|
if (size > cache->ldmemc_size_entries) {
|
|
cache->ldmemc_size_used -= size;
|
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
|
"memcache_adj_size: failed (size > size_entries %ld).\n",
|
|
cache->ldmemc_size_entries, 0, 0 );
|
|
return( LDAP_SIZELIMIT_EXCEEDED );
|
|
}
|
|
|
|
while (cache->ldmemc_size_used > cache->ldmemc_size) {
|
|
if (memcache_access(cache, MEMCACHE_ACCESS_FLUSH_LRU,
|
|
NULL, NULL, NULL) != LDAP_SUCCESS) {
|
|
cache->ldmemc_size_used -= size;
|
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
|
"memcache_adj_size: failed (LRU flush failed).\n",
|
|
0, 0, 0 );
|
|
return( LDAP_SIZELIMIT_EXCEEDED );
|
|
}
|
|
}
|
|
}
|
|
if (usageFlags & MEMCACHE_SIZE_ENTRIES)
|
|
cache->ldmemc_size_entries += size;
|
|
} else {
|
|
cache->ldmemc_size_used -= size;
|
|
assert(cache->ldmemc_size_used >= 0);
|
|
if (usageFlags & MEMCACHE_SIZE_ENTRIES)
|
|
cache->ldmemc_size_entries -= size;
|
|
}
|
|
|
|
#ifdef LDAP_DEBUG
|
|
if ( cache->ldmemc_size == 0 ) { /* no size limit */
|
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
|
"memcache_adj_size: succeeded (new size: %ld bytes).\n",
|
|
cache->ldmemc_size_used, 0, 0 );
|
|
} else {
|
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
|
"memcache_adj_size: succeeded (new size: %ld bytes, "
|
|
"free space: %ld bytes).\n", cache->ldmemc_size_used,
|
|
cache->ldmemc_size - cache->ldmemc_size_used, 0 );
|
|
}
|
|
#endif /* LDAP_DEBUG */
|
|
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
/* Searches the cache for results for a particular search identified by
|
|
parameter "key", which was generated ldap_memcache_createkey(). */
|
|
static int
|
|
memcache_search(LDAP *ld, unsigned long key, LDAPMessage **ppRes)
|
|
{
|
|
int nRes;
|
|
ldapmemcacheRes *pRes;
|
|
|
|
*ppRes = NULL;
|
|
|
|
if (!memcache_exist(ld))
|
|
return LDAP_LOCAL_ERROR;
|
|
|
|
nRes = memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_FIND,
|
|
(void*)&key, (void*)(&pRes), NULL);
|
|
|
|
if (nRes != LDAP_SUCCESS)
|
|
return nRes;
|
|
|
|
*ppRes = pRes->ldmemcr_resHead;
|
|
assert((pRes->ldmemcr_req_id).ldmemcrid_msgid == -1);
|
|
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
/* Adds a new header into the cache as a place holder for entries
|
|
arriving later. */
|
|
static int
|
|
memcache_add(LDAP *ld, unsigned long key, int msgid,
|
|
const char *basedn)
|
|
{
|
|
ldapmemcacheReqId reqid;
|
|
|
|
if (!memcache_exist(ld))
|
|
return LDAP_LOCAL_ERROR;
|
|
|
|
reqid.ldmemcrid_msgid = msgid;
|
|
reqid.ldmemcrid_ld = ld;
|
|
|
|
return memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_ADD,
|
|
(void*)&key, (void*)&reqid, (void*)basedn);
|
|
}
|
|
|
|
/* Appends search entries arriving from the dir server to the cache. */
|
|
static int
|
|
memcache_append(LDAP *ld, int msgid, LDAPMessage *pRes)
|
|
{
|
|
ldapmemcacheReqId reqid;
|
|
|
|
if (!memcache_exist(ld))
|
|
return LDAP_LOCAL_ERROR;
|
|
|
|
reqid.ldmemcrid_msgid = msgid;
|
|
reqid.ldmemcrid_ld = ld;
|
|
|
|
return memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_APPEND,
|
|
(void*)&reqid, (void*)pRes, NULL);
|
|
}
|
|
|
|
/* Same as memcache_append(), but the entries being appended are the
|
|
last from the dir server. Once all entries for a search have arrived,
|
|
the entries are moved from secondary to primary cache, and a time
|
|
stamp is given to the entries. */
|
|
static int
|
|
memcache_append_last(LDAP *ld, int msgid, LDAPMessage *pRes)
|
|
{
|
|
ldapmemcacheReqId reqid;
|
|
|
|
if (!memcache_exist(ld))
|
|
return LDAP_LOCAL_ERROR;
|
|
|
|
reqid.ldmemcrid_msgid = msgid;
|
|
reqid.ldmemcrid_ld = ld;
|
|
|
|
return memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_APPEND_LAST,
|
|
(void*)&reqid, (void*)pRes, NULL);
|
|
}
|
|
|
|
/* Removes entries from the temporary cache. */
|
|
static int
|
|
memcache_remove(LDAP *ld, int msgid)
|
|
{
|
|
ldapmemcacheReqId reqid;
|
|
|
|
if (!memcache_exist(ld))
|
|
return LDAP_LOCAL_ERROR;
|
|
|
|
reqid.ldmemcrid_msgid = msgid;
|
|
reqid.ldmemcrid_ld = ld;
|
|
|
|
return memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_DELETE,
|
|
(void*)&reqid, NULL, NULL);
|
|
}
|
|
|
|
#if 0 /* this function is not used */
|
|
/* Wipes out everything in the temporary cache directory. */
|
|
static int
|
|
memcache_remove_all(LDAP *ld)
|
|
{
|
|
if (!memcache_exist(ld))
|
|
return LDAP_LOCAL_ERROR;
|
|
|
|
return memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_DELETE_ALL,
|
|
NULL, NULL, NULL);
|
|
}
|
|
#endif /* 0 */
|
|
|
|
/* Returns TRUE or FALSE */
|
|
static int
|
|
memcache_exist(LDAP *ld)
|
|
{
|
|
return (ld->ld_memcache != NULL);
|
|
}
|
|
|
|
/* Attaches cached entries to an ldap handle. */
|
|
static int
|
|
memcache_add_to_ld(LDAP *ld, int msgid, LDAPMessage *pMsg)
|
|
{
|
|
int nRes = LDAP_SUCCESS;
|
|
LDAPMessage **r;
|
|
LDAPMessage *pCopy;
|
|
|
|
nRes = memcache_dup_message(pMsg, msgid, 1, &pCopy, NULL);
|
|
if (nRes != LDAP_SUCCESS)
|
|
return nRes;
|
|
|
|
for (r = &(ld->ld_responses); *r; r = &((*r)->lm_next))
|
|
if ((*r)->lm_msgid == msgid)
|
|
break;
|
|
|
|
if (*r)
|
|
for (r = &((*r)->lm_chain); *r; r = &((*r)->lm_chain)) {
|
|
;
|
|
}
|
|
|
|
*r = pCopy;
|
|
|
|
return nRes;
|
|
}
|
|
|
|
/* Check if main_dn is included in {dn, scope} */
|
|
static int
|
|
memcache_compare_dn(const char *main_dn, const char *dn, int scope)
|
|
{
|
|
int nRes;
|
|
char **components = NULL;
|
|
char **main_components = NULL;
|
|
|
|
components = ldap_explode_dn(dn, 0);
|
|
main_components = ldap_explode_dn(main_dn, 0);
|
|
|
|
if (!components || !main_components) {
|
|
nRes = LDAP_COMPARE_TRUE;
|
|
}
|
|
else {
|
|
|
|
int i, main_i;
|
|
|
|
main_i = ldap_count_values(main_components) - 1;
|
|
i = ldap_count_values(components) - 1;
|
|
|
|
for (; i >= 0 && main_i >= 0; i--, main_i--) {
|
|
if (strcasecmp(main_components[main_i], components[i]))
|
|
break;
|
|
}
|
|
|
|
if (i >= 0 && main_i >= 0) {
|
|
nRes = LDAP_COMPARE_FALSE;
|
|
}
|
|
else if (i < 0 && main_i < 0) {
|
|
if (scope != LDAP_SCOPE_ONELEVEL)
|
|
nRes = LDAP_COMPARE_TRUE;
|
|
else
|
|
nRes = LDAP_COMPARE_FALSE;
|
|
}
|
|
else if (main_i < 0) {
|
|
nRes = LDAP_COMPARE_FALSE;
|
|
}
|
|
else {
|
|
if (scope == LDAP_SCOPE_BASE)
|
|
nRes = LDAP_COMPARE_FALSE;
|
|
else if (scope == LDAP_SCOPE_SUBTREE)
|
|
nRes = LDAP_COMPARE_TRUE;
|
|
else if (main_i == 0)
|
|
nRes = LDAP_COMPARE_TRUE;
|
|
else
|
|
nRes = LDAP_COMPARE_FALSE;
|
|
}
|
|
}
|
|
|
|
if (components)
|
|
ldap_value_free(components);
|
|
|
|
if (main_components)
|
|
ldap_value_free(main_components);
|
|
|
|
return nRes;
|
|
}
|
|
|
|
/* Dup a complete separate copy of a berelement, including the buffers
|
|
the berelement points to. */
|
|
static BerElement*
|
|
memcache_ber_dup(BerElement* pBer, unsigned long *pSize)
|
|
{
|
|
BerElement *p = ber_dup(pBer);
|
|
|
|
*pSize = 0;
|
|
|
|
if (p) {
|
|
|
|
*pSize += sizeof(BerElement) + EXTRA_SIZE;
|
|
|
|
if (p->ber_len <= EXTRA_SIZE) {
|
|
p->ber_flags |= LBER_FLAG_NO_FREE_BUFFER;
|
|
p->ber_buf = (char*)p + sizeof(BerElement);
|
|
} else {
|
|
p->ber_flags &= ~LBER_FLAG_NO_FREE_BUFFER;
|
|
p->ber_buf = (char*)NSLDAPI_CALLOC(1, p->ber_len);
|
|
*pSize += (p->ber_buf ? p->ber_len : 0);
|
|
}
|
|
|
|
if (p->ber_buf) {
|
|
p->ber_ptr = p->ber_buf + (pBer->ber_ptr - pBer->ber_buf);
|
|
p->ber_end = p->ber_buf + p->ber_len;
|
|
memcpy(p->ber_buf, pBer->ber_buf, p->ber_len);
|
|
} else {
|
|
ber_free(p, 0);
|
|
p = NULL;
|
|
*pSize = 0;
|
|
}
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
/* Dup a entry or a chain of entries. */
|
|
static int
|
|
memcache_dup_message(LDAPMessage *res, int msgid, int fromcache,
|
|
LDAPMessage **ppResCopy, unsigned long *pSize)
|
|
{
|
|
int nRes = LDAP_SUCCESS;
|
|
unsigned long ber_size;
|
|
LDAPMessage *pCur;
|
|
LDAPMessage **ppCurNew;
|
|
|
|
*ppResCopy = NULL;
|
|
|
|
if (pSize)
|
|
*pSize = 0;
|
|
|
|
/* Make a copy of res */
|
|
for (pCur = res, ppCurNew = ppResCopy; pCur;
|
|
pCur = pCur->lm_chain, ppCurNew = &((*ppCurNew)->lm_chain)) {
|
|
|
|
if ((*ppCurNew = (LDAPMessage*)NSLDAPI_CALLOC(1,
|
|
sizeof(LDAPMessage))) == NULL) {
|
|
nRes = LDAP_NO_MEMORY;
|
|
break;
|
|
}
|
|
|
|
memcpy(*ppCurNew, pCur, sizeof(LDAPMessage));
|
|
(*ppCurNew)->lm_next = NULL;
|
|
(*ppCurNew)->lm_ber = memcache_ber_dup(pCur->lm_ber, &ber_size);
|
|
(*ppCurNew)->lm_msgid = msgid;
|
|
(*ppCurNew)->lm_fromcache = (fromcache != 0);
|
|
|
|
if (pSize)
|
|
*pSize += sizeof(LDAPMessage) + ber_size;
|
|
}
|
|
|
|
if ((nRes != LDAP_SUCCESS) && (*ppResCopy != NULL)) {
|
|
ldap_msgfree(*ppResCopy);
|
|
*ppResCopy = NULL;
|
|
if (pSize)
|
|
*pSize = 0;
|
|
}
|
|
|
|
return nRes;
|
|
}
|
|
|
|
/************************* Cache Functions ***********************/
|
|
|
|
/* Frees a cache header. */
|
|
static int
|
|
memcache_free_entry(LDAPMemCache *cache, ldapmemcacheRes *pRes)
|
|
{
|
|
if (pRes) {
|
|
|
|
unsigned long size = sizeof(ldapmemcacheRes);
|
|
|
|
if (pRes->ldmemcr_basedn) {
|
|
size += strlen(pRes->ldmemcr_basedn) + 1;
|
|
NSLDAPI_FREE(pRes->ldmemcr_basedn);
|
|
}
|
|
|
|
if (pRes->ldmemcr_resHead) {
|
|
size += pRes->ldmemcr_resSize;
|
|
ldap_msgfree(pRes->ldmemcr_resHead);
|
|
}
|
|
|
|
NSLDAPI_FREE(pRes);
|
|
|
|
memcache_adj_size(cache, size, MEMCACHE_SIZE_ENTRIES,
|
|
MEMCACHE_SIZE_DEDUCT);
|
|
}
|
|
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
/* Detaches a cache header from the list of headers. */
|
|
static int
|
|
memcache_free_from_list(LDAPMemCache *cache, ldapmemcacheRes *pRes, int index)
|
|
{
|
|
if (pRes->ldmemcr_prev[index])
|
|
pRes->ldmemcr_prev[index]->ldmemcr_next[index] =
|
|
pRes->ldmemcr_next[index];
|
|
|
|
if (pRes->ldmemcr_next[index])
|
|
pRes->ldmemcr_next[index]->ldmemcr_prev[index] =
|
|
pRes->ldmemcr_prev[index];
|
|
|
|
if (cache->ldmemc_resHead[index] == pRes)
|
|
cache->ldmemc_resHead[index] = pRes->ldmemcr_next[index];
|
|
|
|
if (cache->ldmemc_resTail[index] == pRes)
|
|
cache->ldmemc_resTail[index] = pRes->ldmemcr_prev[index];
|
|
|
|
pRes->ldmemcr_prev[index] = NULL;
|
|
pRes->ldmemcr_next[index] = NULL;
|
|
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
/* Inserts a new cache header to a list of headers. */
|
|
static int
|
|
memcache_add_to_list(LDAPMemCache *cache, ldapmemcacheRes *pRes, int index)
|
|
{
|
|
if (cache->ldmemc_resHead[index])
|
|
cache->ldmemc_resHead[index]->ldmemcr_prev[index] = pRes;
|
|
else
|
|
cache->ldmemc_resTail[index] = pRes;
|
|
|
|
pRes->ldmemcr_prev[index] = NULL;
|
|
pRes->ldmemcr_next[index] = cache->ldmemc_resHead[index];
|
|
cache->ldmemc_resHead[index] = pRes;
|
|
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
/* Appends a chain of entries to the given cache header. */
|
|
static int
|
|
memcache_add_res_to_list(ldapmemcacheRes *pRes, LDAPMessage *pMsg,
|
|
unsigned long size)
|
|
{
|
|
if (pRes->ldmemcr_resTail)
|
|
pRes->ldmemcr_resTail->lm_chain = pMsg;
|
|
else
|
|
pRes->ldmemcr_resHead = pMsg;
|
|
|
|
for (pRes->ldmemcr_resTail = pMsg;
|
|
pRes->ldmemcr_resTail->lm_chain;
|
|
pRes->ldmemcr_resTail = pRes->ldmemcr_resTail->lm_chain) {
|
|
;
|
|
}
|
|
|
|
pRes->ldmemcr_resSize += size;
|
|
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
|
|
#ifdef LDAP_DEBUG
|
|
static void
|
|
memcache_print_list( LDAPMemCache *cache, int index )
|
|
{
|
|
char *name;
|
|
ldapmemcacheRes *restmp;
|
|
|
|
switch( index ) {
|
|
case LIST_TTL:
|
|
name = "TTL";
|
|
break;
|
|
case LIST_LRU:
|
|
name = "LRU";
|
|
break;
|
|
case LIST_TMP:
|
|
name = "TMP";
|
|
break;
|
|
case LIST_TOTAL:
|
|
name = "TOTAL";
|
|
break;
|
|
default:
|
|
name = "unknown";
|
|
}
|
|
|
|
LDAPDebug( LDAP_DEBUG_TRACE, "memcache 0x%x %s list:\n",
|
|
cache, name, 0 );
|
|
for ( restmp = cache->ldmemc_resHead[index]; restmp != NULL;
|
|
restmp = restmp->ldmemcr_next[index] ) {
|
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
|
" key: 0x%8.8lx, ld: 0x%x, msgid: %d\n",
|
|
restmp->ldmemcr_crc_key,
|
|
restmp->ldmemcr_req_id.ldmemcrid_ld,
|
|
restmp->ldmemcr_req_id.ldmemcrid_msgid );
|
|
}
|
|
LDAPDebug( LDAP_DEBUG_TRACE, "memcache 0x%x end of %s list.\n",
|
|
cache, name, 0 );
|
|
}
|
|
#endif /* LDAP_DEBUG */
|
|
|
|
/* Tells whether a cached result has expired. */
|
|
static int
|
|
memcache_expired(LDAPMemCache *cache, ldapmemcacheRes *pRes,
|
|
unsigned long curTime)
|
|
{
|
|
if (!cache->ldmemc_ttl)
|
|
return 0;
|
|
|
|
return ((unsigned long)difftime(
|
|
(time_t)curTime,
|
|
(time_t)(pRes->ldmemcr_timestamp)) >=
|
|
cache->ldmemc_ttl);
|
|
}
|
|
|
|
/* Operates the cache in a central place. */
|
|
static int
|
|
memcache_access(LDAPMemCache *cache, int mode,
|
|
void *pData1, void *pData2, void *pData3)
|
|
{
|
|
int nRes = LDAP_SUCCESS;
|
|
unsigned long size = 0;
|
|
|
|
/* Add a new cache header to the cache. */
|
|
if (mode == MEMCACHE_ACCESS_ADD) {
|
|
unsigned long key = *((unsigned long*)pData1);
|
|
char *basedn = (char*)pData3;
|
|
ldapmemcacheRes *pRes = NULL;
|
|
|
|
nRes = htable_get(cache->ldmemc_resTmp, pData2, (void**)&pRes);
|
|
if (nRes == LDAP_SUCCESS)
|
|
return( LDAP_ALREADY_EXISTS );
|
|
|
|
pRes = (ldapmemcacheRes*)NSLDAPI_CALLOC(1, sizeof(ldapmemcacheRes));
|
|
if (pRes == NULL)
|
|
return( LDAP_NO_MEMORY );
|
|
|
|
pRes->ldmemcr_crc_key = key;
|
|
pRes->ldmemcr_req_id = *((ldapmemcacheReqId*)pData2);
|
|
pRes->ldmemcr_basedn = (basedn ? nsldapi_strdup(basedn) : NULL);
|
|
|
|
size += sizeof(ldapmemcacheRes) + strlen(basedn) + 1;
|
|
nRes = memcache_adj_size(cache, size, MEMCACHE_SIZE_ENTRIES,
|
|
MEMCACHE_SIZE_ADD);
|
|
if (nRes == LDAP_SUCCESS)
|
|
nRes = htable_put(cache->ldmemc_resTmp, pData2, (void*)pRes);
|
|
if (nRes == LDAP_SUCCESS)
|
|
memcache_add_to_list(cache, pRes, LIST_TMP);
|
|
else
|
|
memcache_free_entry(cache, pRes);
|
|
}
|
|
/* Append entries to an existing cache header. */
|
|
else if ((mode == MEMCACHE_ACCESS_APPEND) ||
|
|
(mode == MEMCACHE_ACCESS_APPEND_LAST)) {
|
|
|
|
LDAPMessage *pMsg = (LDAPMessage*)pData2;
|
|
LDAPMessage *pCopy = NULL;
|
|
ldapmemcacheRes *pRes = NULL;
|
|
|
|
nRes = htable_get(cache->ldmemc_resTmp, pData1, (void**)&pRes);
|
|
if (nRes != LDAP_SUCCESS)
|
|
return nRes;
|
|
|
|
nRes = memcache_dup_message(pMsg, pMsg->lm_msgid, 0, &pCopy, &size);
|
|
if (nRes != LDAP_SUCCESS) {
|
|
nRes = htable_remove(cache->ldmemc_resTmp, pData1, NULL);
|
|
assert(nRes == LDAP_SUCCESS);
|
|
memcache_free_from_list(cache, pRes, LIST_TMP);
|
|
memcache_free_entry(cache, pRes);
|
|
return nRes;
|
|
}
|
|
|
|
nRes = memcache_adj_size(cache, size, MEMCACHE_SIZE_ENTRIES,
|
|
MEMCACHE_SIZE_ADD);
|
|
if (nRes != LDAP_SUCCESS) {
|
|
ldap_msgfree(pCopy);
|
|
nRes = htable_remove(cache->ldmemc_resTmp, pData1, NULL);
|
|
assert(nRes == LDAP_SUCCESS);
|
|
memcache_free_from_list(cache, pRes, LIST_TMP);
|
|
memcache_free_entry(cache, pRes);
|
|
return nRes;
|
|
}
|
|
|
|
memcache_add_res_to_list(pRes, pCopy, size);
|
|
|
|
if (mode == MEMCACHE_ACCESS_APPEND)
|
|
return( LDAP_SUCCESS );
|
|
|
|
nRes = htable_remove(cache->ldmemc_resTmp, pData1, NULL);
|
|
assert(nRes == LDAP_SUCCESS);
|
|
memcache_free_from_list(cache, pRes, LIST_TMP);
|
|
(pRes->ldmemcr_req_id).ldmemcrid_ld = NULL;
|
|
(pRes->ldmemcr_req_id).ldmemcrid_msgid = -1;
|
|
pRes->ldmemcr_timestamp = (unsigned long)time(NULL);
|
|
|
|
if ((nRes = htable_put(cache->ldmemc_resLookup,
|
|
(void*)&(pRes->ldmemcr_crc_key),
|
|
(void*)pRes)) == LDAP_SUCCESS) {
|
|
memcache_add_to_list(cache, pRes, LIST_TTL);
|
|
memcache_add_to_list(cache, pRes, LIST_LRU);
|
|
} else {
|
|
memcache_free_entry(cache, pRes);
|
|
}
|
|
}
|
|
/* Search for cached entries for a particular search. */
|
|
else if (mode == MEMCACHE_ACCESS_FIND) {
|
|
|
|
ldapmemcacheRes **ppRes = (ldapmemcacheRes**)pData2;
|
|
|
|
nRes = htable_get(cache->ldmemc_resLookup, pData1, (void**)ppRes);
|
|
if (nRes != LDAP_SUCCESS)
|
|
return nRes;
|
|
|
|
if (!memcache_expired(cache, *ppRes, (unsigned long)time(0))) {
|
|
memcache_free_from_list(cache, *ppRes, LIST_LRU);
|
|
memcache_add_to_list(cache, *ppRes, LIST_LRU);
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
nRes = htable_remove(cache->ldmemc_resLookup, pData1, NULL);
|
|
assert(nRes == LDAP_SUCCESS);
|
|
memcache_free_from_list(cache, *ppRes, LIST_TTL);
|
|
memcache_free_from_list(cache, *ppRes, LIST_LRU);
|
|
memcache_free_entry(cache, *ppRes);
|
|
nRes = LDAP_NO_SUCH_OBJECT;
|
|
*ppRes = NULL;
|
|
}
|
|
/* Remove cached entries in the temporary cache. */
|
|
else if (mode == MEMCACHE_ACCESS_DELETE) {
|
|
|
|
ldapmemcacheRes *pCurRes = NULL;
|
|
|
|
if ((nRes = htable_remove(cache->ldmemc_resTmp, pData1,
|
|
(void**)&pCurRes)) == LDAP_SUCCESS) {
|
|
memcache_free_from_list(cache, pCurRes, LIST_TMP);
|
|
memcache_free_entry(cache, pCurRes);
|
|
}
|
|
}
|
|
/* Wipe out the temporary cache. */
|
|
else if (mode == MEMCACHE_ACCESS_DELETE_ALL) {
|
|
|
|
nRes = htable_removeall(cache->ldmemc_resTmp, (void*)cache);
|
|
}
|
|
/* Remove expired entries from primary cache. */
|
|
else if (mode == MEMCACHE_ACCESS_UPDATE) {
|
|
|
|
ldapmemcacheRes *pCurRes = cache->ldmemc_resTail[LIST_TTL];
|
|
unsigned long curTime = (unsigned long)time(NULL);
|
|
|
|
for (; pCurRes; pCurRes = cache->ldmemc_resTail[LIST_TTL]) {
|
|
|
|
if (!memcache_expired(cache, pCurRes, curTime))
|
|
break;
|
|
|
|
nRes = htable_remove(cache->ldmemc_resLookup,
|
|
(void*)&(pCurRes->ldmemcr_crc_key), NULL);
|
|
assert(nRes == LDAP_SUCCESS);
|
|
memcache_free_from_list(cache, pCurRes, LIST_TTL);
|
|
memcache_free_from_list(cache, pCurRes, LIST_LRU);
|
|
memcache_free_entry(cache, pCurRes);
|
|
}
|
|
}
|
|
/* Wipe out the primary cache. */
|
|
else if (mode == MEMCACHE_ACCESS_FLUSH_ALL) {
|
|
|
|
ldapmemcacheRes *pCurRes = cache->ldmemc_resHead[LIST_TTL];
|
|
|
|
nRes = htable_removeall(cache->ldmemc_resLookup, (void*)cache);
|
|
|
|
for (; pCurRes; pCurRes = cache->ldmemc_resHead[LIST_TTL]) {
|
|
memcache_free_from_list(cache, pCurRes, LIST_LRU);
|
|
cache->ldmemc_resHead[LIST_TTL] =
|
|
cache->ldmemc_resHead[LIST_TTL]->ldmemcr_next[LIST_TTL];
|
|
memcache_free_entry(cache, pCurRes);
|
|
}
|
|
cache->ldmemc_resTail[LIST_TTL] = NULL;
|
|
}
|
|
/* Remove cached entries in both primary and temporary cache. */
|
|
else if (mode == MEMCACHE_ACCESS_FLUSH) {
|
|
|
|
int i, list_id, bDone;
|
|
int scope = (int)pData2;
|
|
char *dn = (char*)pData1;
|
|
char *dnTmp;
|
|
BerElement ber;
|
|
LDAPMessage *pMsg;
|
|
ldapmemcacheRes *pRes;
|
|
|
|
if (cache->ldmemc_basedns) {
|
|
for (i = 0; cache->ldmemc_basedns[i]; i++) {
|
|
if ((memcache_compare_dn(cache->ldmemc_basedns[i], dn,
|
|
LDAP_SCOPE_SUBTREE) == LDAP_COMPARE_TRUE) ||
|
|
(memcache_compare_dn(dn, cache->ldmemc_basedns[i],
|
|
LDAP_SCOPE_SUBTREE) == LDAP_COMPARE_TRUE))
|
|
break;
|
|
}
|
|
if (cache->ldmemc_basedns[i] == NULL)
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
list_id = (i == 0 ? LIST_TTL : LIST_TMP);
|
|
|
|
for (pRes = cache->ldmemc_resHead[list_id]; pRes != NULL;
|
|
pRes = pRes->ldmemcr_next[list_id]) {
|
|
|
|
if ((memcache_compare_dn(pRes->ldmemcr_basedn, dn,
|
|
LDAP_SCOPE_SUBTREE) != LDAP_COMPARE_TRUE) &&
|
|
(memcache_compare_dn(dn, pRes->ldmemcr_basedn,
|
|
LDAP_SCOPE_SUBTREE) != LDAP_COMPARE_TRUE))
|
|
continue;
|
|
|
|
for (pMsg = pRes->ldmemcr_resHead, bDone = 0;
|
|
!bDone && pMsg; pMsg = pMsg->lm_chain) {
|
|
|
|
if (!NSLDAPI_IS_SEARCH_ENTRY( pMsg->lm_msgtype ))
|
|
continue;
|
|
|
|
ber = *(pMsg->lm_ber);
|
|
if (ber_scanf(&ber, "{a", &dnTmp) != LBER_ERROR) {
|
|
bDone = (memcache_compare_dn(dnTmp, dn, scope) ==
|
|
LDAP_COMPARE_TRUE);
|
|
ldap_memfree(dnTmp);
|
|
}
|
|
}
|
|
|
|
if (!bDone)
|
|
continue;
|
|
|
|
if (list_id == LIST_TTL) {
|
|
nRes = htable_remove(cache->ldmemc_resLookup,
|
|
(void*)&(pRes->ldmemcr_crc_key), NULL);
|
|
assert(nRes == LDAP_SUCCESS);
|
|
memcache_free_from_list(cache, pRes, LIST_TTL);
|
|
memcache_free_from_list(cache, pRes, LIST_LRU);
|
|
} else {
|
|
nRes = htable_remove(cache->ldmemc_resTmp,
|
|
(void*)&(pRes->ldmemcr_req_id), NULL);
|
|
assert(nRes == LDAP_SUCCESS);
|
|
memcache_free_from_list(cache, pRes, LIST_TMP);
|
|
}
|
|
memcache_free_entry(cache, pRes);
|
|
}
|
|
}
|
|
}
|
|
/* Flush least recently used entries from cache */
|
|
else if (mode == MEMCACHE_ACCESS_FLUSH_LRU) {
|
|
|
|
ldapmemcacheRes *pRes = cache->ldmemc_resTail[LIST_LRU];
|
|
|
|
if (pRes == NULL)
|
|
return LDAP_NO_SUCH_OBJECT;
|
|
|
|
LDAPDebug( LDAP_DEBUG_TRACE,
|
|
"memcache_access FLUSH_LRU: removing key 0x%8.8lx\n",
|
|
pRes->ldmemcr_crc_key, 0, 0 );
|
|
nRes = htable_remove(cache->ldmemc_resLookup,
|
|
(void*)&(pRes->ldmemcr_crc_key), NULL);
|
|
assert(nRes == LDAP_SUCCESS);
|
|
memcache_free_from_list(cache, pRes, LIST_TTL);
|
|
memcache_free_from_list(cache, pRes, LIST_LRU);
|
|
memcache_free_entry(cache, pRes);
|
|
}
|
|
/* Unknown command */
|
|
else {
|
|
nRes = LDAP_PARAM_ERROR;
|
|
}
|
|
|
|
return nRes;
|
|
}
|
|
|
|
|
|
#ifdef LDAP_DEBUG
|
|
static void
|
|
memcache_report_statistics( LDAPMemCache *cache )
|
|
{
|
|
unsigned long hitrate;
|
|
|
|
if ( cache->ldmemc_stats.ldmemcstat_tries == 0 ) {
|
|
hitrate = 0;
|
|
} else {
|
|
hitrate = ( 100L * cache->ldmemc_stats.ldmemcstat_hits ) /
|
|
cache->ldmemc_stats.ldmemcstat_tries;
|
|
}
|
|
LDAPDebug( LDAP_DEBUG_STATS, "memcache 0x%x:\n", cache, 0, 0 );
|
|
LDAPDebug( LDAP_DEBUG_STATS, " tries: %ld hits: %ld hitrate: %ld%%\n",
|
|
cache->ldmemc_stats.ldmemcstat_tries,
|
|
cache->ldmemc_stats.ldmemcstat_hits, hitrate );
|
|
if ( cache->ldmemc_size <= 0 ) { /* no size limit */
|
|
LDAPDebug( LDAP_DEBUG_STATS, " memory bytes used: %ld\n",
|
|
cache->ldmemc_size_used, 0, 0 );
|
|
} else {
|
|
LDAPDebug( LDAP_DEBUG_STATS, " memory bytes used: %ld free: %ld\n",
|
|
cache->ldmemc_size_used,
|
|
cache->ldmemc_size - cache->ldmemc_size_used, 0 );
|
|
}
|
|
}
|
|
#endif /* LDAP_DEBUG */
|
|
|
|
/************************ Hash Table Functions *****************************/
|
|
|
|
/* Calculates size (# of entries) of hash table given the size limit for
|
|
the cache. */
|
|
static int
|
|
htable_calculate_size(int sizelimit)
|
|
{
|
|
int i, j;
|
|
int size = (int)(((double)sizelimit /
|
|
(double)(sizeof(BerElement) + EXTRA_SIZE)) / 1.5);
|
|
|
|
/* Get a prime # */
|
|
size = (size & 0x1 ? size : size + 1);
|
|
for (i = 3, j = size / 2; i < j; i++) {
|
|
if ((size % i) == 0) {
|
|
size += 2;
|
|
i = 3;
|
|
j = size / 2;
|
|
}
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
/* Returns the size in bytes of the given hash table. */
|
|
static int
|
|
htable_sizeinbytes(HashTable *pTable)
|
|
{
|
|
if (!pTable)
|
|
return 0;
|
|
|
|
return (pTable->size * sizeof(HashTableNode));
|
|
}
|
|
|
|
/* Inserts an item into the hash table. */
|
|
static int
|
|
htable_put(HashTable *pTable, void *key, void *pData)
|
|
{
|
|
int index = pTable->hashfunc(pTable->size, key);
|
|
|
|
if (index >= 0 && index < pTable->size)
|
|
return pTable->putdata(&(pTable->table[index].pData), key, pData);
|
|
|
|
return( LDAP_OPERATIONS_ERROR );
|
|
}
|
|
|
|
/* Retrieves an item from the hash table. */
|
|
static int
|
|
htable_get(HashTable *pTable, void *key, void **ppData)
|
|
{
|
|
int index = pTable->hashfunc(pTable->size, key);
|
|
|
|
*ppData = NULL;
|
|
|
|
if (index >= 0 && index < pTable->size)
|
|
return pTable->getdata(pTable->table[index].pData, key, ppData);
|
|
|
|
return( LDAP_OPERATIONS_ERROR );
|
|
}
|
|
|
|
/* Performs a miscellaneous operation on a hash table entry. */
|
|
static int
|
|
htable_misc(HashTable *pTable, void *key, void *pData)
|
|
{
|
|
if (pTable->miscfunc) {
|
|
int index = pTable->hashfunc(pTable->size, key);
|
|
if (index >= 0 && index < pTable->size)
|
|
return pTable->miscfunc(&(pTable->table[index].pData), key, pData);
|
|
}
|
|
|
|
return( LDAP_OPERATIONS_ERROR );
|
|
}
|
|
|
|
/* Removes an item from the hash table. */
|
|
static int
|
|
htable_remove(HashTable *pTable, void *key, void **ppData)
|
|
{
|
|
int index = pTable->hashfunc(pTable->size, key);
|
|
|
|
if (ppData)
|
|
*ppData = NULL;
|
|
|
|
if (index >= 0 && index < pTable->size)
|
|
return pTable->removedata(&(pTable->table[index].pData), key, ppData);
|
|
|
|
return( LDAP_OPERATIONS_ERROR );
|
|
}
|
|
|
|
/* Removes everything in the hash table. */
|
|
static int
|
|
htable_removeall(HashTable *pTable, void *pData)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < pTable->size; i++)
|
|
pTable->clrtablenode(&(pTable->table[i].pData), pData);
|
|
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
/* Creates a new hash table. */
|
|
static int
|
|
htable_create(int size_limit, HashFuncPtr hashf,
|
|
PutDataPtr putDataf, GetDataPtr getDataf,
|
|
RemoveDataPtr removeDataf, ClrTableNodePtr clrNodef,
|
|
MiscFuncPtr miscOpf, HashTable **ppTable)
|
|
{
|
|
size_limit = htable_calculate_size(size_limit);
|
|
|
|
if ((*ppTable = (HashTable*)NSLDAPI_CALLOC(1, sizeof(HashTable))) == NULL)
|
|
return( LDAP_NO_MEMORY );
|
|
|
|
(*ppTable)->table = (HashTableNode*)NSLDAPI_CALLOC(size_limit,
|
|
sizeof(HashTableNode));
|
|
if ((*ppTable)->table == NULL) {
|
|
NSLDAPI_FREE(*ppTable);
|
|
*ppTable = NULL;
|
|
return( LDAP_NO_MEMORY );
|
|
}
|
|
|
|
(*ppTable)->size = size_limit;
|
|
(*ppTable)->hashfunc = hashf;
|
|
(*ppTable)->putdata = putDataf;
|
|
(*ppTable)->getdata = getDataf;
|
|
(*ppTable)->miscfunc = miscOpf;
|
|
(*ppTable)->removedata = removeDataf;
|
|
(*ppTable)->clrtablenode = clrNodef;
|
|
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
/* Destroys a hash table. */
|
|
static int
|
|
htable_free(HashTable *pTable)
|
|
{
|
|
NSLDAPI_FREE(pTable->table);
|
|
NSLDAPI_FREE(pTable);
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
/**************** Hash table callbacks for temporary cache ****************/
|
|
|
|
/* Hash function */
|
|
static int
|
|
msgid_hashf(int table_size, void *key)
|
|
{
|
|
unsigned code = (unsigned)((ldapmemcacheReqId*)key)->ldmemcrid_ld;
|
|
return (((code << 20) + (code >> 12)) % table_size);
|
|
}
|
|
|
|
/* Called by hash table to insert an item. */
|
|
static int
|
|
msgid_putdata(void **ppTableData, void *key, void *pData)
|
|
{
|
|
ldapmemcacheReqId *pReqId = (ldapmemcacheReqId*)key;
|
|
ldapmemcacheRes *pRes = (ldapmemcacheRes*)pData;
|
|
ldapmemcacheRes **ppHead = (ldapmemcacheRes**)ppTableData;
|
|
ldapmemcacheRes *pCurRes = *ppHead;
|
|
ldapmemcacheRes *pPrev = NULL;
|
|
|
|
for (; pCurRes; pCurRes = pCurRes->ldmemcr_htable_next) {
|
|
if ((pCurRes->ldmemcr_req_id).ldmemcrid_ld == pReqId->ldmemcrid_ld)
|
|
break;
|
|
pPrev = pCurRes;
|
|
}
|
|
|
|
if (pCurRes) {
|
|
for (; pCurRes; pCurRes = pCurRes->ldmemcr_next[LIST_TTL]) {
|
|
if ((pCurRes->ldmemcr_req_id).ldmemcrid_msgid ==
|
|
pReqId->ldmemcrid_msgid)
|
|
return( LDAP_ALREADY_EXISTS );
|
|
pPrev = pCurRes;
|
|
}
|
|
pPrev->ldmemcr_next[LIST_TTL] = pRes;
|
|
pRes->ldmemcr_prev[LIST_TTL] = pPrev;
|
|
pRes->ldmemcr_next[LIST_TTL] = NULL;
|
|
} else {
|
|
if (pPrev)
|
|
pPrev->ldmemcr_htable_next = pRes;
|
|
else
|
|
*ppHead = pRes;
|
|
pRes->ldmemcr_htable_next = NULL;
|
|
}
|
|
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
/* Called by hash table to retrieve an item. */
|
|
static int
|
|
msgid_getdata(void *pTableData, void *key, void **ppData)
|
|
{
|
|
ldapmemcacheReqId *pReqId = (ldapmemcacheReqId*)key;
|
|
ldapmemcacheRes *pCurRes = (ldapmemcacheRes*)pTableData;
|
|
|
|
*ppData = NULL;
|
|
|
|
for (; pCurRes; pCurRes = pCurRes->ldmemcr_htable_next) {
|
|
if ((pCurRes->ldmemcr_req_id).ldmemcrid_ld == pReqId->ldmemcrid_ld)
|
|
break;
|
|
}
|
|
|
|
if (!pCurRes)
|
|
return( LDAP_NO_SUCH_OBJECT );
|
|
|
|
for (; pCurRes; pCurRes = pCurRes->ldmemcr_next[LIST_TTL]) {
|
|
if ((pCurRes->ldmemcr_req_id).ldmemcrid_msgid ==
|
|
pReqId->ldmemcrid_msgid) {
|
|
*ppData = (void*)pCurRes;
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
}
|
|
|
|
return( LDAP_NO_SUCH_OBJECT );
|
|
}
|
|
|
|
/* Called by hash table to remove an item. */
|
|
static int
|
|
msgid_removedata(void **ppTableData, void *key, void **ppData)
|
|
{
|
|
ldapmemcacheRes *pHead = *((ldapmemcacheRes**)ppTableData);
|
|
ldapmemcacheRes *pCurRes = NULL;
|
|
ldapmemcacheRes *pPrev = NULL;
|
|
ldapmemcacheReqId *pReqId = (ldapmemcacheReqId*)key;
|
|
|
|
if (ppData)
|
|
*ppData = NULL;
|
|
|
|
for (; pHead; pHead = pHead->ldmemcr_htable_next) {
|
|
if ((pHead->ldmemcr_req_id).ldmemcrid_ld == pReqId->ldmemcrid_ld)
|
|
break;
|
|
pPrev = pHead;
|
|
}
|
|
|
|
if (!pHead)
|
|
return( LDAP_NO_SUCH_OBJECT );
|
|
|
|
for (pCurRes = pHead; pCurRes; pCurRes = pCurRes->ldmemcr_next[LIST_TTL]) {
|
|
if ((pCurRes->ldmemcr_req_id).ldmemcrid_msgid ==
|
|
pReqId->ldmemcrid_msgid)
|
|
break;
|
|
}
|
|
|
|
if (!pCurRes)
|
|
return( LDAP_NO_SUCH_OBJECT );
|
|
|
|
if (ppData) {
|
|
pCurRes->ldmemcr_next[LIST_TTL] = NULL;
|
|
pCurRes->ldmemcr_prev[LIST_TTL] = NULL;
|
|
pCurRes->ldmemcr_htable_next = NULL;
|
|
*ppData = (void*)pCurRes;
|
|
}
|
|
|
|
if (pCurRes != pHead) {
|
|
if (pCurRes->ldmemcr_prev[LIST_TTL])
|
|
pCurRes->ldmemcr_prev[LIST_TTL]->ldmemcr_next[LIST_TTL] =
|
|
pCurRes->ldmemcr_next[LIST_TTL];
|
|
if (pCurRes->ldmemcr_next[LIST_TTL])
|
|
pCurRes->ldmemcr_next[LIST_TTL]->ldmemcr_prev[LIST_TTL] =
|
|
pCurRes->ldmemcr_prev[LIST_TTL];
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
if (pPrev) {
|
|
if (pHead->ldmemcr_next[LIST_TTL]) {
|
|
pPrev->ldmemcr_htable_next = pHead->ldmemcr_next[LIST_TTL];
|
|
pHead->ldmemcr_next[LIST_TTL]->ldmemcr_htable_next =
|
|
pHead->ldmemcr_htable_next;
|
|
} else {
|
|
pPrev->ldmemcr_htable_next = pHead->ldmemcr_htable_next;
|
|
}
|
|
} else {
|
|
if (pHead->ldmemcr_next[LIST_TTL]) {
|
|
*((ldapmemcacheRes**)ppTableData) = pHead->ldmemcr_next[LIST_TTL];
|
|
pHead->ldmemcr_next[LIST_TTL]->ldmemcr_htable_next =
|
|
pHead->ldmemcr_htable_next;
|
|
} else {
|
|
*((ldapmemcacheRes**)ppTableData) = pHead->ldmemcr_htable_next;
|
|
}
|
|
}
|
|
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
/* Called by hash table to remove all cached entries associated to searches
|
|
being performed using the given ldap handle. */
|
|
static int
|
|
msgid_clear_ld_items(void **ppTableData, void *key, void *pData)
|
|
{
|
|
LDAPMemCache *cache = (LDAPMemCache*)pData;
|
|
ldapmemcacheRes *pHead = *((ldapmemcacheRes**)ppTableData);
|
|
ldapmemcacheRes *pPrev = NULL;
|
|
ldapmemcacheRes *pCurRes = NULL;
|
|
ldapmemcacheReqId *pReqId = (ldapmemcacheReqId*)key;
|
|
|
|
for (; pHead; pHead = pHead->ldmemcr_htable_next) {
|
|
if ((pHead->ldmemcr_req_id).ldmemcrid_ld == pReqId->ldmemcrid_ld)
|
|
break;
|
|
pPrev = pHead;
|
|
}
|
|
|
|
if (!pHead)
|
|
return( LDAP_NO_SUCH_OBJECT );
|
|
|
|
if (pPrev)
|
|
pPrev->ldmemcr_htable_next = pHead->ldmemcr_htable_next;
|
|
else
|
|
*((ldapmemcacheRes**)ppTableData) = pHead->ldmemcr_htable_next;
|
|
|
|
for (pCurRes = pHead; pHead; pCurRes = pHead) {
|
|
pHead = pHead->ldmemcr_next[LIST_TTL];
|
|
memcache_free_from_list(cache, pCurRes, LIST_TMP);
|
|
memcache_free_entry(cache, pCurRes);
|
|
}
|
|
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
/* Called by hash table for removing all items in the table. */
|
|
static void
|
|
msgid_clearnode(void **ppTableData, void *pData)
|
|
{
|
|
LDAPMemCache *cache = (LDAPMemCache*)pData;
|
|
ldapmemcacheRes **ppHead = (ldapmemcacheRes**)ppTableData;
|
|
ldapmemcacheRes *pSubHead = *ppHead;
|
|
ldapmemcacheRes *pCurRes = NULL;
|
|
|
|
for (; *ppHead; pSubHead = *ppHead) {
|
|
ppHead = &((*ppHead)->ldmemcr_htable_next);
|
|
for (pCurRes = pSubHead; pSubHead; pCurRes = pSubHead) {
|
|
pSubHead = pSubHead->ldmemcr_next[LIST_TTL];
|
|
memcache_free_from_list(cache, pCurRes, LIST_TMP);
|
|
memcache_free_entry(cache, pCurRes);
|
|
}
|
|
}
|
|
}
|
|
|
|
/********************* Hash table for primary cache ************************/
|
|
|
|
/* Hash function */
|
|
static int
|
|
attrkey_hashf(int table_size, void *key)
|
|
{
|
|
return ((*((unsigned long*)key)) % table_size);
|
|
}
|
|
|
|
/* Called by hash table to insert an item. */
|
|
static int
|
|
attrkey_putdata(void **ppTableData, void *key, void *pData)
|
|
{
|
|
unsigned long attrkey = *((unsigned long*)key);
|
|
ldapmemcacheRes **ppHead = (ldapmemcacheRes**)ppTableData;
|
|
ldapmemcacheRes *pRes = *ppHead;
|
|
|
|
for (; pRes; pRes = pRes->ldmemcr_htable_next) {
|
|
if (pRes->ldmemcr_crc_key == attrkey)
|
|
return( LDAP_ALREADY_EXISTS );
|
|
}
|
|
|
|
pRes = (ldapmemcacheRes*)pData;
|
|
pRes->ldmemcr_htable_next = *ppHead;
|
|
*ppHead = pRes;
|
|
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
|
|
/* Called by hash table to retrieve an item. */
|
|
static int
|
|
attrkey_getdata(void *pTableData, void *key, void **ppData)
|
|
{
|
|
unsigned long attrkey = *((unsigned long*)key);
|
|
ldapmemcacheRes *pRes = (ldapmemcacheRes*)pTableData;
|
|
|
|
for (; pRes; pRes = pRes->ldmemcr_htable_next) {
|
|
if (pRes->ldmemcr_crc_key == attrkey) {
|
|
*ppData = (void*)pRes;
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
}
|
|
|
|
*ppData = NULL;
|
|
|
|
return( LDAP_NO_SUCH_OBJECT );
|
|
}
|
|
|
|
/* Called by hash table to remove an item. */
|
|
static int
|
|
attrkey_removedata(void **ppTableData, void *key, void **ppData)
|
|
{
|
|
unsigned long attrkey = *((unsigned long*)key);
|
|
ldapmemcacheRes **ppHead = (ldapmemcacheRes**)ppTableData;
|
|
ldapmemcacheRes *pRes = *ppHead;
|
|
ldapmemcacheRes *pPrev = NULL;
|
|
|
|
for (; pRes; pRes = pRes->ldmemcr_htable_next) {
|
|
if (pRes->ldmemcr_crc_key == attrkey) {
|
|
if (ppData)
|
|
*ppData = (void*)pRes;
|
|
if (pPrev)
|
|
pPrev->ldmemcr_htable_next = pRes->ldmemcr_htable_next;
|
|
else
|
|
*ppHead = pRes->ldmemcr_htable_next;
|
|
pRes->ldmemcr_htable_next = NULL;
|
|
return( LDAP_SUCCESS );
|
|
}
|
|
pPrev = pRes;
|
|
}
|
|
|
|
if (ppData)
|
|
*ppData = NULL;
|
|
|
|
return( LDAP_NO_SUCH_OBJECT );
|
|
}
|
|
|
|
/* Called by hash table for removing all items in the table. */
|
|
static void
|
|
attrkey_clearnode(void **ppTableData, void *pData)
|
|
{
|
|
ldapmemcacheRes **ppHead = (ldapmemcacheRes**)ppTableData;
|
|
ldapmemcacheRes *pRes = *ppHead;
|
|
|
|
(void)pData;
|
|
|
|
for (; *ppHead; pRes = *ppHead) {
|
|
ppHead = &((*ppHead)->ldmemcr_htable_next);
|
|
pRes->ldmemcr_htable_next = NULL;
|
|
}
|
|
}
|
|
|
|
/***************************** CRC algorithm ********************************/
|
|
|
|
/* From http://www.faqs.org/faqs/compression-faq/part1/section-25.html */
|
|
|
|
/*
|
|
* Build auxiliary table for parallel byte-at-a-time CRC-32.
|
|
*/
|
|
#define NSLDAPI_CRC32_POLY 0x04c11db7 /* AUTODIN II, Ethernet, & FDDI */
|
|
|
|
static unsigned long crc32_table[256] = {
|
|
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
|
|
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
|
|
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
|
|
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
|
|
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
|
|
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
|
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
|
|
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
|
|
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
|
|
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
|
|
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
|
|
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
|
|
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
|
|
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
|
|
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
|
|
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
|
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
|
|
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
|
|
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
|
|
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
|
|
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
|
|
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
|
|
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
|
|
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
|
|
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
|
|
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
|
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
|
|
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
|
|
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
|
|
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
|
|
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
|
|
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
|
|
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
|
|
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
|
|
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
|
|
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
|
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
|
|
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
|
|
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
|
|
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
|
|
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
|
|
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
|
|
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 };
|
|
|
|
/* Initialized first time "crc32()" is called. If you prefer, you can
|
|
* statically initialize it at compile time. [Another exercise.]
|
|
*/
|
|
|
|
static unsigned long
|
|
crc32_convert(char *buf, int len)
|
|
{
|
|
char *p;
|
|
#ifdef OSF1V4D
|
|
unsigned int crc;
|
|
#else
|
|
unsigned long crc; /* FIXME: this is not 32-bits on all platforms! */
|
|
#endif /* OSF1V4D */
|
|
|
|
crc = 0xffffffff; /* preload shift register, per CRC-32 spec */
|
|
for (p = buf; len > 0; ++p, --len)
|
|
crc = ((crc << 8) ^ crc32_table[(crc >> 24) ^ *p]) & 0xffffffff;
|
|
|
|
return (unsigned long) ~crc; /* transmit complement, per CRC-32 spec */
|
|
}
|