~louis/ubuntu/trusty/clamav/lp799623_fix_logrotate

« back to all changes in this revision

Viewing changes to libclamav/cache.c

  • Committer: Bazaar Package Importer
  • Author(s): Scott Kitterman
  • Date: 2010-03-12 11:30:04 UTC
  • mfrom: (0.41.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20100312113004-b0fop4bkycszdd0z
Tags: 0.96~rc1+dfsg-0ubuntu1
* New upstream RC - FFE (LP: #537636):
  - Add OfficialDatabaseOnly option to clamav-base.postinst.in
  - Add LocalSocketGroup option to clamav-base.postinst.in
  - Add LocalSocketMode option to clamav-base.postinst.in
  - Add CrossFilesystems option to clamav-base.postinst.in
  - Add ClamukoScannerCount option to clamav-base.postinst.in
  - Add BytecodeSecurity opiton to clamav-base.postinst.in
  - Add DetectionStatsHostID option to clamav-freshclam.postinst.in
  - Add Bytecode option to clamav-freshclam.postinst.in
  - Add MilterSocketGroup option to clamav-milter.postinst.in
  - Add MilterSocketMode option to clamav-milter.postinst.in
  - Add ReportHostname option to clamav-milter.postinst.in
  - Bump libclamav SO version to 6.1.0 in libclamav6.install
  - Drop clamdmon from clamav.examples (no longer shipped by upstream)
  - Drop libclamav.a from libclamav-dev.install (not built by upstream)
  - Update SO version for lintian override for libclamav6
  - Add new Bytecode Testing Tool, usr/bin/clambc, to clamav.install
  - Add build-depends on python and python-setuptools for new test suite
  - Update debian/copyright for the embedded copy of llvm (using the system
    llvm is not currently feasible)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  Copyright (C) 2010 Sourcefire, Inc.
 
3
 *
 
4
 *  Authors: aCaB <acab@clamav.net>, Török Edvin <edwin@clamav.net>
 
5
 *
 
6
 *  This program is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License version 2 as
 
8
 *  published by the Free Software Foundation.
 
9
 *
 
10
 *  This program is distributed in the hope that it will be useful,
 
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 *  GNU General Public License for more details.
 
14
 *
 
15
 *  You should have received a copy of the GNU General Public License
 
16
 *  along with this program; if not, write to the Free Software
 
17
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
18
 *  MA 02110-1301, USA.
 
19
 */
 
20
 
 
21
#if HAVE_CONFIG_H
 
22
#include "clamav-config.h"
 
23
#endif
 
24
 
 
25
#include <string.h>
 
26
#include <stdlib.h>
 
27
#include <pthread.h>
 
28
#include <assert.h>
 
29
 
 
30
#include "md5.h"
 
31
#include "mpool.h"
 
32
#include "clamav.h"
 
33
#include "cache.h"
 
34
#include "fmap.h"
 
35
 
 
36
 
 
37
/* The number of root trees and the chooser function 
 
38
   Each tree is protected by a mutex against concurrent access */
 
39
/* #define TREES 1 */
 
40
/* static inline unsigned int getkey(uint8_t *hash) { return 0; } */
 
41
#define TREES 256
 
42
static inline unsigned int getkey(uint8_t *hash) { return *hash; }
 
43
/* #define TREES 4096 */
 
44
/* static inline unsigned int getkey(uint8_t *hash) { return hash[0] | ((unsigned int)(hash[1] & 0xf)<<8) ; } */
 
45
/* #define TREES 65536 */
 
46
/* static inline unsigned int getkey(uint8_t *hash) { return hash[0] | (((unsigned int)hash[1])<<8) ; } */
 
47
 
 
48
/* The number of nodes in each tree */
 
49
#define NODES 256
 
50
 
 
51
 
 
52
/* The replacement policy algorithm to use */
 
53
/* #define USE_LRUHASHCACHE */
 
54
#define USE_SPLAY
 
55
 
 
56
/* LRUHASHCACHE --------------------------------------------------------------------- */
 
57
#ifdef USE_LRUHASHCACHE
 
58
struct cache_key {
 
59
    int64_t digest[2];
 
60
    uint32_t size; /* 0 is used to mark an empty hash slot! */
 
61
    struct cache_key *lru_next, *lru_prev;
 
62
};
 
63
 
 
64
struct cache_set {
 
65
    struct cache_key *data;
 
66
    size_t maxelements; /* considering load factor */
 
67
    size_t maxdeleted;
 
68
    size_t elements;
 
69
    size_t deleted;
 
70
    struct cache_key *lru_head, *lru_tail;
 
71
};
 
72
 
 
73
#define CACHE_KEY_DELETED ~0u
 
74
#define CACHE_KEY_EMPTY 0
 
75
 
 
76
static void cacheset_lru_remove(struct cache_set *map, size_t howmany)
 
77
{
 
78
    while (howmany--) {
 
79
        struct cache_key *old;
 
80
        assert(map->lru_head);
 
81
        assert(!old->lru_prev);
 
82
        /* Remove a key from the head of the list */
 
83
        old = map->lru_head;
 
84
        map->lru_head = old->lru_next;
 
85
        old->size = CACHE_KEY_DELETED;
 
86
        /* This slot is now deleted, it is not empty,
 
87
         * because previously we could have inserted a key that has seen this
 
88
         * slot as occupied, to find that key we need to ensure that all keys
 
89
         * that were occupied when the key was inserted, are seen as occupied
 
90
         * when searching too.
 
91
         * Of course when inserting a new value, we treat deleted slots as
 
92
         * empty.
 
93
         * We only replace old values with new values, but there is no guarantee
 
94
         * that the newly inserted value would hash to same place as the value
 
95
         * we remove due to LRU! */
 
96
        if (old == map->lru_tail)
 
97
            map->lru_tail = 0;
 
98
        map->elements--;
 
99
        map->deleted++;
 
100
    }
 
101
}
 
102
 
 
103
static inline int cacheset_lookup_internal(struct cache_set *map,
 
104
                                           const char *md5,  size_t size,
 
105
                                           uint32_t *insert_pos, int deletedok)
 
106
{
 
107
    const struct cache_key*data = map->data;
 
108
    uint32_t capmask = NODES - 1;
 
109
    const struct cache_key *k;
 
110
    uint32_t idx, tries = 0;
 
111
    uint64_t md5_0, md5_1;
 
112
    uint64_t md5a[2];
 
113
 
 
114
    memcpy(&md5a, md5, 16);
 
115
    md5_0 = md5a[0];
 
116
    md5_1 = md5a[1];
 
117
    idx = md5_1 & capmask;
 
118
    k = &data[idx];
 
119
    while (k->size != CACHE_KEY_EMPTY && tries <= capmask) {
 
120
        if (k->digest[0] == md5_0 &&
 
121
            k->digest[1] == md5_1 &&
 
122
            k->size == size) {
 
123
            /* found key */
 
124
            *insert_pos = idx;
 
125
            return 1;
 
126
        }
 
127
        if (deletedok && k->size == CACHE_KEY_DELETED) {
 
128
           /* treat deleted slot as empty */
 
129
           *insert_pos = idx;
 
130
           return 0;
 
131
        }
 
132
        idx = (idx + tries++) & capmask;
 
133
        k = &data[idx];
 
134
    }
 
135
    /* found empty pos */
 
136
    *insert_pos = idx;
 
137
    return 0;
 
138
}
 
139
 
 
140
static inline void lru_remove(struct cache_set *map, struct cache_key *newkey)
 
141
{
 
142
    if (newkey->lru_next)
 
143
        newkey->lru_next->lru_prev = newkey->lru_prev;
 
144
    if (newkey->lru_prev)
 
145
        newkey->lru_prev->lru_next = newkey->lru_next;
 
146
    if (newkey == map->lru_head)
 
147
        map->lru_head = newkey->lru_next;
 
148
}
 
149
 
 
150
static inline void lru_addtail(struct cache_set *map, struct cache_key *newkey)
 
151
{
 
152
    if (!map->lru_head)
 
153
        map->lru_head = newkey;
 
154
    if (map->lru_tail)
 
155
        map->lru_tail->lru_next = newkey;
 
156
    newkey->lru_next = NULL;
 
157
    newkey->lru_prev = map->lru_tail;
 
158
    map->lru_tail = newkey;
 
159
}
 
160
 
 
161
static pthread_mutex_t pool_mutex = PTHREAD_MUTEX_INITIALIZER;
 
162
 
 
163
static void cacheset_add(struct cache_set *map, unsigned char *md5, size_t size, mpool_t *mempool);
 
164
static int cacheset_init(struct cache_set *map, mpool_t *mempool);
 
165
 
 
166
static void cacheset_rehash(struct cache_set *map, mpool_t *mempool)
 
167
{
 
168
    unsigned i;
 
169
    int ret;
 
170
    struct cache_set tmp_set;
 
171
    struct cache_key *key;
 
172
    pthread_mutex_lock(&pool_mutex);
 
173
    ret = cacheset_init(&tmp_set, mempool);
 
174
    pthread_mutex_unlock(&pool_mutex);
 
175
    if (ret)
 
176
        return;
 
177
 
 
178
    key = map->lru_head;
 
179
    for (i=0;key && i < tmp_set.maxelements/2;i++) {
 
180
        cacheset_add(&tmp_set, (unsigned char*)&key->digest, key->size, mempool);
 
181
        key = key->lru_next;
 
182
    }
 
183
    pthread_mutex_lock(&pool_mutex);
 
184
    mpool_free(mempool, map->data);
 
185
    pthread_mutex_unlock(&pool_mutex);
 
186
    memcpy(map, &tmp_set, sizeof(tmp_set));
 
187
}
 
188
 
 
189
static void cacheset_add(struct cache_set *map, unsigned char *md5, size_t size, mpool_t *mempool)
 
190
{
 
191
    int ret;
 
192
    uint32_t pos;
 
193
    struct cache_key *newkey;
 
194
 
 
195
    if (map->elements >= map->maxelements) {
 
196
        cacheset_lru_remove(map, 1);
 
197
        if (map->deleted >= map->maxdeleted) {
 
198
            cacheset_rehash(map, mempool);
 
199
        }
 
200
    }
 
201
    assert(map->elements < map->maxelements);
 
202
 
 
203
    ret = cacheset_lookup_internal(map, md5, size, &pos, 1);
 
204
    newkey = &map->data[pos];
 
205
    if (newkey->size == CACHE_KEY_DELETED)
 
206
        map->deleted--;
 
207
    if (ret) {
 
208
        /* was already added, remove from LRU list */
 
209
        lru_remove(map, newkey);
 
210
    }
 
211
    /* add new key to tail of LRU list */
 
212
    memcpy(&map->data[pos].digest, md5, sizeof(map->data[pos].digest));
 
213
    map->data[pos].size = size;
 
214
    lru_addtail(map, newkey);
 
215
 
 
216
    map->elements++;
 
217
 
 
218
    assert(pos < map->maxelements);
 
219
}
 
220
 
 
221
static int cacheset_lookup(struct cache_set *map, unsigned char *md5, size_t size)
 
222
{
 
223
    struct cache_key *newkey;
 
224
    int ret;
 
225
    uint32_t pos;
 
226
 
 
227
    ret = cacheset_lookup_internal(map, md5, size, &pos, 0);
 
228
    if (!ret)
 
229
        return 0;
 
230
    newkey = &map->data[pos];
 
231
    /* update LRU position: move to tail */
 
232
    lru_remove(map, newkey);
 
233
    lru_addtail(map, newkey);
 
234
    return 1;
 
235
}
 
236
 
 
237
static int cacheset_init(struct cache_set *map, mpool_t *mempool) {
 
238
    map->data = mpool_calloc(mempool, NODES, sizeof(*map->data));
 
239
    if (!map->data)
 
240
        return CL_EMEM;
 
241
    map->maxelements = 80 * NODES / 100;
 
242
    map->maxdeleted = NODES - map->maxelements - 1;
 
243
    map->elements = 0;
 
244
    map->lru_head = map->lru_tail = NULL;
 
245
    return 0;
 
246
}
 
247
 
 
248
static inline void cacheset_destroy(struct cache_set *cs, mpool_t *mempool) {
 
249
    mpool_free(mempool, cs->data);
 
250
    cs->data = NULL;
 
251
}
 
252
 
 
253
#endif /* USE_LRUHASHCACHE */
 
254
 
 
255
/* SPLAY --------------------------------------------------------------------- */
 
256
#ifdef USE_SPLAY
 
257
 
 
258
struct node { /* a node */
 
259
    int64_t digest[2];
 
260
    struct node *left;
 
261
    struct node *right;
 
262
    struct node *up;
 
263
    struct node *next;
 
264
    struct node *prev;
 
265
    uint32_t size;
 
266
    uint32_t minrec;
 
267
};
 
268
 
 
269
struct cache_set { /* a tree */
 
270
    struct node *data;
 
271
    struct node *root;
 
272
    struct node *first;
 
273
    struct node *last;
 
274
};
 
275
 
 
276
/* Allocates all the nodes and sets up the replacement chain */
 
277
static int cacheset_init(struct cache_set *cs, mpool_t *mempool) {
 
278
    unsigned int i;
 
279
    cs->data = mpool_calloc(mempool, NODES,  sizeof(*cs->data));
 
280
    cs->root = NULL;
 
281
 
 
282
    if(!cs->data)
 
283
        return 1;
 
284
 
 
285
    for(i=1; i<NODES; i++) {
 
286
        cs->data[i-1].next = &cs->data[i];
 
287
        cs->data[i].prev = &cs->data[i-1];
 
288
    }
 
289
 
 
290
    cs->first = cs->data;
 
291
    cs->last = &cs->data[NODES-1];
 
292
 
 
293
    return 0;
 
294
}
 
295
 
 
296
/* Frees all the nodes */
 
297
static inline void cacheset_destroy(struct cache_set *cs, mpool_t *mempool) {
 
298
    mpool_free(mempool, cs->data);
 
299
    cs->data = NULL;
 
300
}
 
301
 
 
302
/* The left/right cooser for the splay tree */
 
303
static inline int cmp(int64_t *a, ssize_t sa, int64_t *b, ssize_t sb) {
 
304
    if(a[1] < b[1]) return -1;
 
305
    if(a[1] > b[1]) return 1;
 
306
    if(a[0] < b[0]) return -1;
 
307
    if(a[0] > b[0]) return 1;
 
308
    if(sa < sb) return -1;
 
309
    if(sa > sb) return 1;
 
310
    return 0;
 
311
}
 
312
 
 
313
 
 
314
/* #define PRINT_TREE */
 
315
#ifdef PRINT_TREE
 
316
#define ptree printf
 
317
#else
 
318
#define ptree(...)
 
319
#endif
 
320
 
 
321
/* Debug function to print the tree and check its consistency */
 
322
/* #define CHECK_TREE */
 
323
#ifdef CHECK_TREE
 
324
static int printtree(struct cache_set *cs, struct node *n, int d) {
 
325
    int i;
 
326
    int ab = 0;
 
327
    if (n == NULL) return 0;
 
328
    if(n == cs->root) { ptree("--------------------------\n"); }
 
329
    ab |= printtree(cs, n->right, d+1);
 
330
    if(n->right) {
 
331
        if(cmp(n->digest, n->size, n->right->digest, n->right->size) >= 0) {
 
332
            for (i=0; i<d; i++) ptree("        ");
 
333
            ptree("^^^^ %lld >= %lld\n", n->digest[1], n->right->digest[1]);
 
334
            ab = 1;
 
335
        }
 
336
    }
 
337
    for (i=0; i<d; i++) ptree("        ");
 
338
    ptree("%08x(%02u)\n", n->digest[1]>>48, n - cs->data);
 
339
    if(n->left) {
 
340
        if(cmp(n->digest, n->size, n->left->digest, n->left->size) <= 0) {
 
341
            for (i=0; i<d; i++) ptree("        ");
 
342
            ptree("vvvv %lld <= %lld\n", n->digest[1], n->left->digest[1]);
 
343
            ab = 1;
 
344
        }
 
345
    }
 
346
    if(d){
 
347
        if(!n->up) {
 
348
            ptree("no parent!\n");
 
349
            ab = 1;
 
350
        } else {
 
351
            if(n->up->left != n && n->up->right != n) {
 
352
                ptree("broken parent\n");
 
353
                ab = 1;
 
354
            }
 
355
        }
 
356
    } else {
 
357
        if(n->up) {
 
358
            ptree("root with a parent!\n");
 
359
            ab = 1;
 
360
        }
 
361
    }
 
362
    ab |= printtree(cs, n->left, d+1);
 
363
    return ab;
 
364
}
 
365
#else
 
366
#define printtree(a,b,c) (0)
 
367
#endif
 
368
 
 
369
/* Looks up a node and splays it up to the root of the tree */
 
370
static int splay(int64_t *md5, size_t len, struct cache_set *cs) {
 
371
    struct node next = {{0, 0}, NULL, NULL, NULL, NULL, NULL, 0, 0}, *right = &next, *left = &next, *temp, *root = cs->root;
 
372
    int comp, found = 0;
 
373
 
 
374
    if(!root)
 
375
        return 0;
 
376
 
 
377
    while(1) {
 
378
        comp = cmp(md5, len, root->digest, root->size);
 
379
        if(comp < 0) {
 
380
            if(!root->left) break;
 
381
            if(cmp(md5, len, root->left->digest, root->left->size) < 0) {
 
382
                temp = root->left;
 
383
                root->left = temp->right;
 
384
                if(temp->right) temp->right->up = root;
 
385
                temp->right = root;
 
386
                root->up = temp;
 
387
                root = temp;
 
388
                if(!root->left) break;
 
389
            }
 
390
            right->left = root;
 
391
            root->up = right;
 
392
            right = root;
 
393
            root = root->left;
 
394
        } else if(comp > 0) {
 
395
            if(!root->right) break;
 
396
            if(cmp(md5, len, root->right->digest, root->right->size) > 0) {
 
397
                temp = root->right;
 
398
                root->right = temp->left;
 
399
                if(temp->left) temp->left->up = root;
 
400
                temp->left = root;
 
401
                root->up = temp;
 
402
                root = temp;
 
403
                if(!root->right) break;
 
404
            }
 
405
            left->right = root;
 
406
            root->up = left;
 
407
            left = root;
 
408
            root = root->right;
 
409
        } else {
 
410
            found = 1;
 
411
            break;
 
412
        }
 
413
    }
 
414
 
 
415
    left->right = root->left;
 
416
    if(root->left) root->left->up = left;
 
417
    right->left = root->right;
 
418
    if(root->right) root->right->up = right;
 
419
    root->left = next.right;
 
420
    if(next.right) next.right->up = root;
 
421
    root->right = next.left;
 
422
    if(next.left) next.left->up = root;
 
423
    root->up = NULL;
 
424
    cs->root = root;
 
425
    return found;
 
426
}
 
427
 
 
428
 
 
429
/* Looks up an hash in the tree and maintains the replacement chain */
 
430
static inline int cacheset_lookup(struct cache_set *cs, unsigned char *md5, size_t size, uint32_t reclevel) {
 
431
    int64_t hash[2];
 
432
 
 
433
    memcpy(hash, md5, 16);
 
434
    if(splay(hash, size, cs)) {
 
435
        struct node *o = cs->root->prev, *p = cs->root, *q = cs->root->next;
 
436
#ifdef PRINT_CHAINS
 
437
        printf("promoting %02d\n", p - cs->data);
 
438
        {
 
439
            struct node *x = cs->first;
 
440
            printf("before: ");
 
441
            while(x) {
 
442
                printf("%02d,", x - cs->data);
 
443
                x=x->next;
 
444
            }
 
445
            printf(" --- ");
 
446
            x=cs->last;
 
447
            while(x) {
 
448
                printf("%02d,", x - cs->data);
 
449
                x=x->prev;
 
450
            }
 
451
            printf("\n");
 
452
        }
 
453
#endif
 
454
        if(q) {
 
455
            if(o)
 
456
                o->next = q;
 
457
            else
 
458
                cs->first = q;
 
459
            q->prev = o;
 
460
            cs->last->next = p;
 
461
            p->prev = cs->last;
 
462
            p->next = NULL;
 
463
            cs->last = p;
 
464
        }
 
465
#ifdef PRINT_CHAINS
 
466
        {
 
467
            struct node *x = cs->first;
 
468
            printf("after : ");
 
469
            while(x) {
 
470
                printf("%02d,", x - cs->data);
 
471
                x=x->next;
 
472
            }
 
473
            printf(" --- ");
 
474
            x=cs->last;
 
475
            while(x) {
 
476
                printf("%02d,", x - cs->data);
 
477
                x=x->prev;
 
478
            }
 
479
            printf("\n");
 
480
        }
 
481
#endif
 
482
        if(reclevel >= p->minrec)
 
483
            return 1;
 
484
    }
 
485
    return 0;
 
486
}
 
487
 
 
488
/* If the hash is present nothing happens.
 
489
   Otherwise a new node is created for the hash picking one from the begin of the chain.
 
490
   Used nodes are moved to the end of the chain */
 
491
static inline void cacheset_add(struct cache_set *cs, unsigned char *md5, size_t size, uint32_t reclevel) {
 
492
    struct node *newnode;
 
493
    int64_t hash[2];
 
494
 
 
495
    memcpy(hash, md5, 16);
 
496
    if(splay(hash, size, cs)) {
 
497
        if(cs->root->minrec > reclevel)
 
498
            cs->root->minrec = reclevel;
 
499
        return; /* Already there */
 
500
    }
 
501
 
 
502
    ptree("1:\n");
 
503
    if(printtree(cs, cs->root, 0)) {
 
504
        cli_errmsg("cacheset_add: inconsistent tree before choosing newnode, good luck\n");
 
505
        return;
 
506
    }
 
507
 
 
508
    newnode = cs->first;
 
509
    while(newnode) {
 
510
        if(!newnode->right && !newnode->left)
 
511
            break;
 
512
        newnode = newnode->next;
 
513
    }
 
514
    if(!newnode) {
 
515
        cli_errmsg("cacheset_add: tree has got no end nodes\n");
 
516
        return;
 
517
    }
 
518
    if(newnode->up) {
 
519
        if(newnode->up->left == newnode)
 
520
            newnode->up->left = NULL;
 
521
        else
 
522
            newnode->up->right = NULL;
 
523
    }
 
524
    if(newnode->prev)
 
525
        newnode->prev->next = newnode->next;
 
526
    if(newnode->next)
 
527
        newnode->next->prev = newnode->prev;
 
528
    if(cs->first == newnode)
 
529
        cs->first = newnode->next;
 
530
 
 
531
    newnode->prev = cs->last;
 
532
    newnode->next = NULL;
 
533
    cs->last->next = newnode;
 
534
    cs->last = newnode;
 
535
 
 
536
    ptree("2:\n");
 
537
    if(printtree(cs, cs->root, 0)) {
 
538
        cli_errmsg("cacheset_add: inconsistent tree before adding newnode, good luck\n");
 
539
        return;
 
540
    }
 
541
 
 
542
    if(!cs->root) {
 
543
        newnode->left = NULL;
 
544
        newnode->right = NULL;
 
545
    } else {
 
546
        if(cmp(hash, size, cs->root->digest, cs->root->size) < 0) {
 
547
            newnode->left = cs->root->left;
 
548
            newnode->right = cs->root;
 
549
            cs->root->left = NULL;
 
550
        } else {
 
551
            newnode->right = cs->root->right;
 
552
            newnode->left = cs->root;
 
553
            cs->root->right = NULL;
 
554
        }
 
555
        if(newnode->left) newnode->left->up = newnode;
 
556
        if(newnode->right) newnode->right->up = newnode;
 
557
    }
 
558
    newnode->digest[0] = hash[0];
 
559
    newnode->digest[1] = hash[1];
 
560
    newnode->up = NULL;
 
561
    newnode->size = size;
 
562
    newnode->minrec = reclevel;
 
563
    cs->root = newnode;
 
564
 
 
565
    ptree("3: %lld\n", hash[1]);
 
566
    if(printtree(cs, cs->root, 0)) {
 
567
        cli_errmsg("cacheset_add: inconsistent tree after adding newnode, good luck\n");
 
568
        return;
 
569
    }
 
570
}
 
571
#endif /* USE_SPLAY */
 
572
 
 
573
 
 
574
/* COMMON STUFF --------------------------------------------------------------------- */
 
575
 
 
576
struct CACHE {
 
577
    struct cache_set cacheset;
 
578
    pthread_mutex_t mutex;
 
579
};
 
580
 
 
581
/* Allocates the trees for the engine cache */
 
582
int cli_cache_init(struct cl_engine *engine) {
 
583
    static struct CACHE *cache;
 
584
    unsigned int i, j;
 
585
 
 
586
    if(!engine) {
 
587
        cli_errmsg("cli_cache_init: mpool malloc fail\n");
 
588
        return 1;
 
589
    }
 
590
 
 
591
    if(!(cache = mpool_malloc(engine->mempool, sizeof(struct CACHE) * TREES))) {
 
592
        cli_errmsg("cli_cache_init: mpool malloc fail\n");
 
593
        return 1;
 
594
    }
 
595
 
 
596
    for(i=0; i<TREES; i++) {
 
597
        if(pthread_mutex_init(&cache[i].mutex, NULL)) {
 
598
            cli_errmsg("cli_cache_init: mutex init fail\n");
 
599
            for(j=0; j<i; j++) cacheset_destroy(&cache[j].cacheset, engine->mempool);
 
600
            for(j=0; j<i; j++) pthread_mutex_destroy(&cache[j].mutex);
 
601
            mpool_free(engine->mempool, cache);
 
602
            return 1;
 
603
        }
 
604
        if(cacheset_init(&cache[i].cacheset, engine->mempool)) {
 
605
            for(j=0; j<i; j++) cacheset_destroy(&cache[j].cacheset, engine->mempool);
 
606
            for(j=0; j<=i; j++) pthread_mutex_destroy(&cache[j].mutex);
 
607
            mpool_free(engine->mempool, cache);
 
608
            return 1;
 
609
        }
 
610
    }
 
611
    engine->cache = cache;
 
612
    return 0;
 
613
}
 
614
 
 
615
/* Frees the engine cache */
 
616
void cli_cache_destroy(struct cl_engine *engine) {
 
617
    static struct CACHE *cache;
 
618
    unsigned int i;
 
619
 
 
620
    if(!engine || !(cache = engine->cache))
 
621
        return;
 
622
 
 
623
    for(i=0; i<TREES; i++) {
 
624
        cacheset_destroy(&cache[i].cacheset, engine->mempool);
 
625
        pthread_mutex_destroy(&cache[i].mutex);
 
626
    }
 
627
    mpool_free(engine->mempool, cache);
 
628
}
 
629
 
 
630
/* Looks up an hash in the proper tree */
 
631
static int cache_lookup_hash(unsigned char *md5, size_t len, struct CACHE *cache, uint32_t reclevel) {
 
632
    unsigned int key = getkey(md5);
 
633
    int ret = CL_VIRUS;
 
634
    struct CACHE *c;
 
635
 
 
636
    c = &cache[key];
 
637
    if(pthread_mutex_lock(&c->mutex)) {
 
638
        cli_errmsg("cache_lookup_hash: cache_lookup_hash: mutex lock fail\n");
 
639
        return ret;
 
640
    }
 
641
 
 
642
    ret = (cacheset_lookup(&c->cacheset, md5, len, reclevel)) ? CL_CLEAN : CL_VIRUS;
 
643
    pthread_mutex_unlock(&c->mutex);
 
644
    /* if(ret == CL_CLEAN) cli_warnmsg("cached\n"); */
 
645
    return ret;
 
646
}
 
647
 
 
648
/* Adds an hash to the cache */
 
649
void cache_add(unsigned char *md5, size_t size, cli_ctx *ctx) {
 
650
    unsigned int key = getkey(md5);
 
651
    uint32_t level;
 
652
    struct CACHE *c;
 
653
 
 
654
    if(!ctx || !ctx->engine || !ctx->engine->cache)
 
655
       return;
 
656
 
 
657
    level =  (*ctx->fmap && (*ctx->fmap)->dont_cache_flag) ? ctx->recursion : 0;
 
658
    if (ctx->found_possibly_unwanted && (level || !ctx->recursion))
 
659
        return;
 
660
    c = &ctx->engine->cache[key];
 
661
    if(pthread_mutex_lock(&c->mutex)) {
 
662
        cli_errmsg("cli_add: mutex lock fail\n");
 
663
        return;
 
664
    }
 
665
 
 
666
#ifdef USE_LRUHASHCACHE
 
667
    cacheset_add(&c->cacheset, md5, size, ctx->engine->mempool);
 
668
#else
 
669
#ifdef USE_SPLAY
 
670
    cacheset_add(&c->cacheset, md5, size, level);
 
671
#else
 
672
#error #define USE_SPLAY or USE_LRUHASHCACHE
 
673
#endif
 
674
#endif
 
675
 
 
676
    pthread_mutex_unlock(&c->mutex);
 
677
    cli_dbgmsg("cache_add: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x (level %u)\n", md5[0], md5[1], md5[2], md5[3], md5[4], md5[5], md5[6], md5[7], md5[8], md5[9], md5[10], md5[11], md5[12], md5[13], md5[14], md5[15], level);
 
678
    return;
 
679
}
 
680
 
 
681
/* Hashes a file onto the provided buffer and looks it up the cache.
 
682
   Returns CL_VIRUS if found, CL_CLEAN if not FIXME or an error */
 
683
int cache_check(unsigned char *hash, cli_ctx *ctx) {
 
684
    fmap_t *map;
 
685
    size_t todo, at = 0;
 
686
    cli_md5_ctx md5;
 
687
    int ret;
 
688
 
 
689
    if(!ctx || !ctx->engine || !ctx->engine->cache)
 
690
       return CL_VIRUS;
 
691
 
 
692
    map = *ctx->fmap;
 
693
    todo = map->len;
 
694
 
 
695
    cli_md5_init(&md5);
 
696
    while(todo) {
 
697
        void *buf;
 
698
        size_t readme = todo < FILEBUFF ? todo : FILEBUFF;
 
699
        if(!(buf = fmap_need_off_once(map, at, readme)))
 
700
            return CL_VIRUS;
 
701
        todo -= readme;
 
702
        at += readme;
 
703
        cli_md5_update(&md5, buf, readme);
 
704
    }
 
705
    cli_md5_final(hash, &md5);
 
706
    ret = cache_lookup_hash(hash, map->len, ctx->engine->cache, ctx->recursion);
 
707
    cli_dbgmsg("cache_check: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x is %s\n", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15], (ret == CL_VIRUS) ? "negative" : "positive");
 
708
    return ret;
 
709
}