... | ... |
@@ -1,7 +1,7 @@ |
1 | 1 |
/* |
2 | 2 |
* Copyright (C) 2010 Sourcefire, Inc. |
3 | 3 |
* |
4 |
- * Authors: aCaB <acab@clamav.net> |
|
4 |
+ * Authors: aCaB <acab@clamav.net>, Török Edvin <edwin@clamav.net> |
|
5 | 5 |
* |
6 | 6 |
* This program is free software; you can redistribute it and/or modify |
7 | 7 |
* it under the terms of the GNU General Public License version 2 as |
... | ... |
@@ -18,14 +18,15 @@ |
18 | 18 |
* MA 02110-1301, USA. |
19 | 19 |
*/ |
20 | 20 |
|
21 |
-#include <string.h> |
|
22 |
-#include <stdlib.h> |
|
23 |
-#include <pthread.h> |
|
24 |
- |
|
25 | 21 |
#if HAVE_CONFIG_H |
26 | 22 |
#include "clamav-config.h" |
27 | 23 |
#endif |
28 | 24 |
|
25 |
+#include <string.h> |
|
26 |
+#include <stdlib.h> |
|
27 |
+#include <pthread.h> |
|
28 |
+#include <assert.h> |
|
29 |
+ |
|
29 | 30 |
#include "md5.h" |
30 | 31 |
#include "mpool.h" |
31 | 32 |
#include "clamav.h" |
... | ... |
@@ -34,7 +35,6 @@ |
34 | 34 |
|
35 | 35 |
#define CACHE_PERTURB 8 |
36 | 36 |
/* 1/10th */ |
37 |
- |
|
38 | 37 |
static mpool_t *mempool = NULL; |
39 | 38 |
static struct CACHE { |
40 | 39 |
struct CACHE_ENTRY { |
... | ... |
@@ -220,3 +220,295 @@ int cache_check(unsigned char *hash, cli_ctx *ctx) { |
220 | 220 |
cli_md5_final(hash, &md5); |
221 | 221 |
return cache_lookup_hash(hash, ctx); |
222 | 222 |
} |
223 |
+ |
|
224 |
+/* struct cache_key { */ |
|
225 |
+/* char digest[16]; */ |
|
226 |
+/* uint32_t size; /\* 0 is used to mark an empty hash slot! *\/ */ |
|
227 |
+/* struct cache_key *lru_next, *lru_prev; */ |
|
228 |
+/* }; */ |
|
229 |
+ |
|
230 |
+/* struct cache_set { */ |
|
231 |
+/* struct cache_key *data; */ |
|
232 |
+/* size_t capacity; */ |
|
233 |
+/* size_t maxelements; /\* considering load factor *\/ */ |
|
234 |
+/* size_t elements; */ |
|
235 |
+/* size_t version; */ |
|
236 |
+/* struct cache_key *lru_head, *lru_tail; */ |
|
237 |
+/* pthread_mutex_t mutex; */ |
|
238 |
+/* }; */ |
|
239 |
+ |
|
240 |
+/* #define CACHE_INVALID_VERSION ~0u */ |
|
241 |
+/* #define CACHE_KEY_DELETED ~0u */ |
|
242 |
+/* #define CACHE_KEY_EMPTY 0 */ |
|
243 |
+ |
|
244 |
+/* /\* size must be power of 2! *\/ */ |
|
245 |
+/* static int cacheset_init(struct cache_set* map, size_t maxsize, uint8_t loadfactor) */ |
|
246 |
+/* { */ |
|
247 |
+/* map->data = cli_calloc(maxsize, sizeof(*map->data)); */ |
|
248 |
+/* if (!map->data) */ |
|
249 |
+/* return CL_EMEM; */ |
|
250 |
+/* map->capacity = maxsize; */ |
|
251 |
+/* map->maxelements = loadfactor*maxsize / 100; */ |
|
252 |
+/* map->elements = 0; */ |
|
253 |
+/* map->version = CACHE_INVALID_VERSION; */ |
|
254 |
+/* map->lru_head = map->lru_tail = NULL; */ |
|
255 |
+/* if (pthread_mutex_init(&map->mutex, NULL)) { */ |
|
256 |
+/* cli_errmsg("mutex init fail\n"); */ |
|
257 |
+/* return CL_EMEM; */ |
|
258 |
+/* } */ |
|
259 |
+/* } */ |
|
260 |
+ |
|
261 |
+/* static void cacheset_destroy(struct cache_set *map) */ |
|
262 |
+/* { */ |
|
263 |
+/* pthread_mutex_destroy(&map->mutex); */ |
|
264 |
+/* free(map->data); */ |
|
265 |
+/* } */ |
|
266 |
+ |
|
267 |
+/* static void cacheset_acquire(struct cache_set *map) */ |
|
268 |
+/* { */ |
|
269 |
+/* pthread_mutex_lock(&map->mutex); */ |
|
270 |
+/* } */ |
|
271 |
+ |
|
272 |
+/* static void cache_setversion(struct cache_set* map, uint32_t version) */ |
|
273 |
+/* { */ |
|
274 |
+/* unsigned i; */ |
|
275 |
+/* if (map->version == version) */ |
|
276 |
+/* return; */ |
|
277 |
+/* map->version = version; */ |
|
278 |
+/* map->elements = 0; /\* all elements have expired now *\/ */ |
|
279 |
+/* for (i=0;i<map->capacity;i++) */ |
|
280 |
+/* map->data[i].size = 0; */ |
|
281 |
+/* map->lru_head = map->lru_tail = NULL; */ |
|
282 |
+/* } */ |
|
283 |
+ |
|
284 |
+/* static void cacheset_lru_remove(struct cache_set *map, size_t howmany) */ |
|
285 |
+/* { */ |
|
286 |
+/* while (howmany--) { */ |
|
287 |
+/* struct cache_key *old; */ |
|
288 |
+/* assert(map->lru_head); */ |
|
289 |
+/* assert(!old->lru_prev); */ |
|
290 |
+/* // Remove a key from the head of the list */ |
|
291 |
+/* old = map->lru_head; */ |
|
292 |
+/* map->lru_head = old->lru_next; */ |
|
293 |
+/* old->size = CACHE_KEY_DELETED; */ |
|
294 |
+/* /\* This slot is now deleted, it is not empty, */ |
|
295 |
+/* * because previously we could have inserted a key that has seen this */ |
|
296 |
+/* * slot as occupied, to find that key we need to ensure that all keys */ |
|
297 |
+/* * that were occupied when the key was inserted, are seen as occupied */ |
|
298 |
+/* * when searching too. */ |
|
299 |
+/* * Of course when inserting a new value, we treat deleted slots as */ |
|
300 |
+/* * empty. */ |
|
301 |
+/* * We only replace old values with new values, but there is no guarantee */ |
|
302 |
+/* * that the newly inserted value would hash to same place as the value */ |
|
303 |
+/* * we remove due to LRU! *\/ */ |
|
304 |
+/* if (old == map->lru_tail) */ |
|
305 |
+/* map->lru_tail = 0; */ |
|
306 |
+/* } */ |
|
307 |
+/* } */ |
|
308 |
+ |
|
309 |
+/* static inline uint32_t hash32shift(uint32_t key) */ |
|
310 |
+/* { */ |
|
311 |
+/* key = ~key + (key << 15); */ |
|
312 |
+/* key = key ^ (key >> 12); */ |
|
313 |
+/* key = key + (key << 2); */ |
|
314 |
+/* key = key ^ (key >> 4); */ |
|
315 |
+/* key = (key + (key << 3)) + (key << 11); */ |
|
316 |
+/* key = key ^ (key >> 16); */ |
|
317 |
+/* return key; */ |
|
318 |
+/* } */ |
|
319 |
+ |
|
320 |
+/* static inline size_t hash(const unsigned char* k,const size_t len,const size_t SIZE) */ |
|
321 |
+/* { */ |
|
322 |
+/* size_t Hash = 1; */ |
|
323 |
+/* size_t i; */ |
|
324 |
+/* for(i=0;i<len;i++) { */ |
|
325 |
+/* /\* a simple add is good, because we use the mixing function below *\/ */ |
|
326 |
+/* Hash += k[i]; */ |
|
327 |
+/* /\* mixing function *\/ */ |
|
328 |
+/* Hash = hash32shift(Hash); */ |
|
329 |
+/* } */ |
|
330 |
+/* /\* SIZE is power of 2 *\/ */ |
|
331 |
+/* return Hash & (SIZE - 1); */ |
|
332 |
+/* } */ |
|
333 |
+ |
|
334 |
+/* int cacheset_lookup_internal(struct cache_set *map, const struct cache_key *key, */ |
|
335 |
+/* uint32_t *insert_pos, int deletedok) */ |
|
336 |
+/* { */ |
|
337 |
+/* uint32_t idx = hash((const unsigned char*)key, sizeof(*key), map->capacity); */ |
|
338 |
+/* uint32_t tries = 0; */ |
|
339 |
+/* struct cache_key *k = &map->data[idx]; */ |
|
340 |
+/* while (k->size != CACHE_KEY_EMPTY) { */ |
|
341 |
+/* if (k->size == key->size && */ |
|
342 |
+/* !memcmp(k->digest, key, 16)) { */ |
|
343 |
+/* /\* found key *\/ */ |
|
344 |
+/* *insert_pos = idx; */ |
|
345 |
+/* return 1; */ |
|
346 |
+/* } */ |
|
347 |
+/* if (deletedok && k->size == CACHE_KEY_DELETED) { */ |
|
348 |
+/* /\* treat deleted slot as empty *\/ */ |
|
349 |
+/* *insert_pos = idx; */ |
|
350 |
+/* return 0; */ |
|
351 |
+/* } */ |
|
352 |
+/* idx = (idx + tries++)&(map->capacity-1); */ |
|
353 |
+/* k = &map->data[idx]; */ |
|
354 |
+/* } */ |
|
355 |
+/* /\* found empty pos *\/ */ |
|
356 |
+/* *insert_pos = idx; */ |
|
357 |
+/* return 0; */ |
|
358 |
+/* } */ |
|
359 |
+ |
|
360 |
+/* static inline void lru_remove(struct cache_set *map, struct cache_key *newkey) */ |
|
361 |
+/* { */ |
|
362 |
+/* if (newkey->lru_next) */ |
|
363 |
+/* newkey->lru_next->lru_prev = newkey->lru_prev; */ |
|
364 |
+/* if (newkey->lru_prev) */ |
|
365 |
+/* newkey->lru_prev->lru_next = newkey->lru_next; */ |
|
366 |
+/* if (newkey == map->lru_head) */ |
|
367 |
+/* map->lru_head = newkey->lru_next; */ |
|
368 |
+/* } */ |
|
369 |
+ |
|
370 |
+/* static inline void lru_addtail(struct cache_set *map, struct cache_key *newkey) */ |
|
371 |
+/* { */ |
|
372 |
+/* if (!map->lru_head) */ |
|
373 |
+/* map->lru_head = newkey; */ |
|
374 |
+/* if (map->lru_tail) */ |
|
375 |
+/* map->lru_tail->lru_next = newkey; */ |
|
376 |
+/* newkey->lru_next = NULL; */ |
|
377 |
+/* newkey->lru_prev = map->lru_tail; */ |
|
378 |
+/* map->lru_tail = newkey; */ |
|
379 |
+/* } */ |
|
380 |
+ |
|
381 |
+/* static void cacheset_add(struct cache_set *map, const struct cache_key *key) */ |
|
382 |
+/* { */ |
|
383 |
+/* int ret; */ |
|
384 |
+/* uint32_t pos; */ |
|
385 |
+/* struct cache_key *newkey; */ |
|
386 |
+/* if (map->elements >= map->maxelements) */ |
|
387 |
+/* cacheset_lru_remove(map, 1); */ |
|
388 |
+/* assert(map->elements < map->maxelements); */ |
|
389 |
+ |
|
390 |
+/* ret = cacheset_lookup_internal(map, key, &pos, 1); */ |
|
391 |
+/* newkey = &map->data[pos]; */ |
|
392 |
+/* if (ret) { */ |
|
393 |
+/* /\* was already added, remove from LRU list *\/ */ |
|
394 |
+/* lru_remove(map, newkey); */ |
|
395 |
+/* } */ |
|
396 |
+/* /\* add new key to tail of LRU list *\/ */ |
|
397 |
+/* lru_addtail(map, newkey); */ |
|
398 |
+ |
|
399 |
+/* map->elements++; */ |
|
400 |
+ |
|
401 |
+/* assert(pos < map->maxelements); */ |
|
402 |
+ |
|
403 |
+/* memcpy(&map->data[pos], key, sizeof(*key)); */ |
|
404 |
+/* } */ |
|
405 |
+ |
|
406 |
+/* static int cacheset_lookup(struct cache_set *map, const struct cache_key *key) */ |
|
407 |
+/* { */ |
|
408 |
+/* struct cache_key *newkey; */ |
|
409 |
+/* int ret; */ |
|
410 |
+/* uint32_t pos; */ |
|
411 |
+/* ret = cacheset_lookup_internal(map, key, &pos, 0); */ |
|
412 |
+/* if (!ret) */ |
|
413 |
+/* return CACHE_INVALID_VERSION; */ |
|
414 |
+/* newkey = &map->data[pos]; */ |
|
415 |
+/* /\* update LRU position: move to tail *\/ */ |
|
416 |
+/* lru_remove(map, newkey); */ |
|
417 |
+/* lru_addtail(map, newkey); */ |
|
418 |
+ |
|
419 |
+/* return map->version; */ |
|
420 |
+/* } */ |
|
421 |
+ |
|
422 |
+/* static void cacheset_release(struct cache_set *map) */ |
|
423 |
+/* { */ |
|
424 |
+/* pthread_mutex_unlock(&map->mutex); */ |
|
425 |
+/* } */ |
|
426 |
+ |
|
427 |
+/* #if 0 */ |
|
428 |
+/* int main(int argc, char **argv) */ |
|
429 |
+/* { */ |
|
430 |
+/* struct cache_key key; */ |
|
431 |
+/* struct cache_set map; */ |
|
432 |
+/* cacheset_init(&map, 256, 80); */ |
|
433 |
+/* cacheset_acquire(&map); */ |
|
434 |
+/* cache_setversion(&map, 10); */ |
|
435 |
+ |
|
436 |
+/* key.size = 1024; */ |
|
437 |
+/* memcpy(key.digest, "1234567890123456", 16); */ |
|
438 |
+/* cacheset_add(&map, &key); */ |
|
439 |
+/* memcpy(key.digest, "1234567890123457", 16); */ |
|
440 |
+/* cacheset_add(&map, &key); */ |
|
441 |
+/* memcpy(key.digest, "0123456789012345", 16); */ |
|
442 |
+/* cacheset_add(&map, &key); */ |
|
443 |
+ |
|
444 |
+/* key.size = 1024; */ |
|
445 |
+/* memcpy(key.digest, "1234567890123456", 16); */ |
|
446 |
+/* if (cacheset_lookup(&map, &key) != 10) */ |
|
447 |
+/* abort(); */ |
|
448 |
+/* memcpy(key.digest, "1234567890123456", 16); */ |
|
449 |
+/* if (cacheset_lookup(&map, &key) != 10) */ |
|
450 |
+/* abort(); */ |
|
451 |
+/* memcpy(key.digest, "1234567890123457", 16); */ |
|
452 |
+/* if (cacheset_lookup(&map, &key) != 10) */ |
|
453 |
+/* abort(); */ |
|
454 |
+/* memcpy(key.digest, "0123456789012345", 16); */ |
|
455 |
+/* if (cacheset_lookup(&map, &key) != 10) */ |
|
456 |
+/* abort(); */ |
|
457 |
+/* memcpy(key.digest, "0123456789012346", 16); */ |
|
458 |
+/* if (cacheset_lookup(&map, &key) == 10) */ |
|
459 |
+/* abort(); */ |
|
460 |
+ |
|
461 |
+/* cache_setversion(&map, 1); */ |
|
462 |
+/* memcpy(key.digest, "1234567890123456", 16); */ |
|
463 |
+/* if (cacheset_lookup(&map, &key) != CACHE_INVALID_VERSION) */ |
|
464 |
+/* abort(); */ |
|
465 |
+/* memcpy(key.digest, "1234567890123456", 16); */ |
|
466 |
+/* if (cacheset_lookup(&map, &key) != CACHE_INVALID_VERSION) */ |
|
467 |
+/* abort(); */ |
|
468 |
+/* memcpy(key.digest, "1234567890123457", 16); */ |
|
469 |
+/* if (cacheset_lookup(&map, &key) != CACHE_INVALID_VERSION) */ |
|
470 |
+/* abort(); */ |
|
471 |
+/* memcpy(key.digest, "0123456789012345", 16); */ |
|
472 |
+/* if (cacheset_lookup(&map, &key) != CACHE_INVALID_VERSION) */ |
|
473 |
+/* abort(); */ |
|
474 |
+ |
|
475 |
+/* cacheset_release(&map); */ |
|
476 |
+ |
|
477 |
+/* cacheset_destroy(&map); */ |
|
478 |
+ |
|
479 |
+/* cacheset_init(&map, 8, 50); */ |
|
480 |
+/* cacheset_acquire(&map); */ |
|
481 |
+/* cache_setversion(&map, 10); */ |
|
482 |
+ |
|
483 |
+/* key.size = 416; */ |
|
484 |
+/* memcpy(key.digest, "1234567890123456", 16); */ |
|
485 |
+/* cacheset_add(&map, &key); */ |
|
486 |
+/* memcpy(key.digest, "1234567890123457", 16); */ |
|
487 |
+/* cacheset_add(&map, &key); */ |
|
488 |
+/* memcpy(key.digest, "1234567890123459", 16); */ |
|
489 |
+/* cacheset_add(&map, &key); */ |
|
490 |
+/* key.size = 400; */ |
|
491 |
+/* memcpy(key.digest, "1234567890123450", 16); */ |
|
492 |
+/* cacheset_add(&map, &key); */ |
|
493 |
+ |
|
494 |
+/* key.size = 416; */ |
|
495 |
+/* memcpy(key.digest, "1234567890123456", 16); */ |
|
496 |
+/* if (cacheset_lookup(&map, &key) != 10) */ |
|
497 |
+/* abort(); */ |
|
498 |
+/* if (cacheset_lookup(&map, &key) != 10) */ |
|
499 |
+/* abort(); */ |
|
500 |
+/* if (cacheset_lookup(&map, &key) != 10) */ |
|
501 |
+/* abort(); */ |
|
502 |
+ |
|
503 |
+/* key.size = 500; */ |
|
504 |
+/* cacheset_add(&map, &key); */ |
|
505 |
+/* memcpy(key.digest, "1234567890123457", 16); */ |
|
506 |
+/* if (cacheset_lookup(&map, &key) == 10) */ |
|
507 |
+/* abort(); */ |
|
508 |
+ |
|
509 |
+/* cacheset_release(&map); */ |
|
510 |
+/* cacheset_destroy(&map); */ |
|
511 |
+ |
|
512 |
+/* return 0; */ |
|
513 |
+/* } */ |
|
514 |
+/* #endif */ |