511c2e79 |
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include "md5.h"
#include "mpool.h"
#include "clamav.h"
#include "cache.h"
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#define CACHE_PERTURB 10
/* 1/10th */
static mpool_t *mempool = NULL;
static struct CACHE {
struct CACHE_ENTRY {
unsigned char hash[15];
uint32_t dbver;
uint32_t hits;
} *items;
pthread_mutex_t mutex;
uint32_t lastdb;
} *cache = NULL;
static unsigned int cache_entries = 0;
int cache_init(unsigned int entries) {
unsigned int i;
if(!(mempool = mpool_create())) {
cli_errmsg("mpool init fail\n");
return 1;
}
if(!(cache = mpool_malloc(mempool, sizeof(struct CACHE) * 256))) {
cli_errmsg("mpool malloc fail\n");
mpool_destroy(mempool);
return 1;
}
for(i=0; i<256; i++) {
struct CACHE_ENTRY *e = mpool_calloc(mempool, sizeof(struct CACHE_ENTRY), entries);
if(!e) {
cli_errmsg("mpool calloc fail\n");
mpool_destroy(mempool);
return 1;
}
cache[i].items = e;
cache[i].lastdb = 0;
if(pthread_mutex_init(&cache[i].mutex, NULL)) {
cli_errmsg("mutex init fail\n");
mpool_destroy(mempool);
return 1;
}
}
cache_entries = entries;
return 0;
}
void cache_swap(struct CACHE_ENTRY *e, unsigned int a) {
struct CACHE_ENTRY t;
unsigned int b = a-1;
if(!a || e[a].hits <= e[b].hits)
return;
do {
if(e[a].hits > e[b].hits)
continue;
break;
} while(b--);
b++;
memcpy(&t, &e[a], sizeof(t));
memcpy(&e[a], &e[b], sizeof(t));
memcpy(&e[b], &t, sizeof(t));
}
void updb(uint32_t db, unsigned int skip) {
unsigned int i;
for(i=0; i<256; i++) {
if(i==skip) continue;
if(pthread_mutex_lock(&cache[i].mutex)) {
cli_errmsg("mutex lock fail\n");
continue;
}
cache[i].lastdb = db;
pthread_mutex_unlock(&cache[i].mutex);
}
}
int cache_check(unsigned char *md5, cli_ctx *ctx) {
unsigned int i;
int ret = CL_VIRUS;
struct CACHE_ENTRY *e;
struct CACHE *c;
if(!cache) return ret;
c = &cache[*md5];
e = c->items;
if(pthread_mutex_lock(&c->mutex)) {
cli_errmsg("mutex lock fail\n");
return ret;
}
if(c->lastdb <= ctx->engine->dbversion[0]) {
if(c->lastdb < ctx->engine->dbversion[0]) {
c->lastdb = ctx->engine->dbversion[0];
updb(c->lastdb, *md5);
} else {
for(i=0; i<cache_entries; i++) {
if(!e[i].hits) break;
if(e[i].dbver == c->lastdb && !memcmp(e[i].hash, md5 + 1, 15)) {
e[i].hits++;
cache_swap(e, i);
ret = CL_CLEAN;
cli_warnmsg("cached\n");
break;
}
}
}
}
pthread_mutex_unlock(&c->mutex);
return ret;
}
void cache_add(unsigned char *md5, cli_ctx *ctx) {
unsigned int i, replace;
struct CACHE_ENTRY *e;
struct CACHE *c;
if(!cache) return;
c = &cache[*md5];
e = c->items;
if(pthread_mutex_lock(&c->mutex)) {
cli_errmsg("mutex lock fail\n");
return;
}
if(c->lastdb == ctx->engine->dbversion[0]) {
replace = cache_entries;
for(i=0; i<cache_entries; i++) {
if(!e[i].hits) break;
if(replace == cache_entries && e[i].dbver < c->lastdb) {
replace = i;
} else if(e[i].hits && !memcmp(e[i].hash, md5 + 1, 15)) {
e[i].hits++;
cache_swap(e, i);
pthread_mutex_unlock(&c->mutex);
return;
}
}
if(replace == cache_entries)
replace = cache_entries - 1 - (rand() % (cache_entries / CACHE_PERTURB));
e[replace].hits = 1;
e[replace].dbver = c->lastdb;
memcpy(e[replace].hash, md5 + 1, 15);
cache_swap(e, replace);
}
pthread_mutex_unlock(&c->mutex);
return;
}
int cache_chekdesc(int desc, size_t size, unsigned char *hash, cli_ctx *ctx) {
cli_md5_ctx md5;
unsigned char buf[8192];
off_t seekback = lseek(desc, 0, SEEK_CUR);
if(!cache) return CL_VIRUS;
cli_md5_init(&md5);
while(size) {
size_t readme = size < sizeof(buf) ? size : sizeof(buf);
if(cli_readn(desc, buf, readme)!=readme) {
lseek(desc, seekback, SEEK_SET);
return CL_VIRUS;
}
cli_md5_update(&md5, buf, readme);
size-=readme;
}
cli_md5_final(hash, &md5);
lseek(desc, seekback, SEEK_SET);
return cache_check(hash, ctx);
} |