~louis/ubuntu/trusty/clamav/lp799623_fix_logrotate

« back to all changes in this revision

Viewing changes to libclamav/fmap.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) 2009 Sourcefire, Inc.
 
3
 *
 
4
 *  Authors: aCaB <acab@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
/* an mmap "replacement" which doesn't suck */
 
22
 
 
23
#if HAVE_CONFIG_H
 
24
#include "clamav-config.h"
 
25
#endif
 
26
 
 
27
#include <sys/types.h>
 
28
#include <sys/stat.h>
 
29
#include <string.h>
 
30
#ifdef HAVE_UNISTD_H
 
31
#include <unistd.h>
 
32
#endif
 
33
#if HAVE_MMAP
 
34
#ifdef HAVE_SYS_MMAN_H
 
35
#include <sys/mman.h>
 
36
#endif
 
37
#endif
 
38
 
 
39
#ifdef C_LINUX
 
40
#include <pthread.h>
 
41
#endif
 
42
 
 
43
#include "others.h"
 
44
#include "cltypes.h"
 
45
 
 
46
static inline unsigned int fmap_align_items(unsigned int sz, unsigned int al);
 
47
static inline unsigned int fmap_align_to(unsigned int sz, unsigned int al);
 
48
static inline unsigned int fmap_which_page(fmap_t *m, size_t at);
 
49
 
 
50
#ifndef _WIN32
 
51
/* vvvvv POSIX STUFF BELOW vvvvv */
 
52
 
 
53
#define FM_MASK_COUNT 0x3fffffff
 
54
#define FM_MASK_PAGED 0x40000000
 
55
#define FM_MASK_SEEN 0x80000000
 
56
#define FM_MASK_LOCKED FM_MASK_SEEN
 
57
/* 2 high bits:
 
58
00 - not seen - not paged - N/A
 
59
01 -    N/A   -   paged   - not locked
 
60
10 -   seen   - not paged - N/A
 
61
11 -    N/A   -   paged   - locked
 
62
*/
 
63
 
 
64
/* FIXME: tune this stuff */
 
65
#define UNPAGE_THRSHLD_LO 4*1024*1024
 
66
#define UNPAGE_THRSHLD_HI 8*1024*1024
 
67
#define READAHEAD_PAGES 8
 
68
 
 
69
#if defined(HAVE_MMAP) && defined(C_LINUX) && defined(CL_THREAD_SAFE)
 
70
/*
 
71
   WORKAROUND
 
72
   Relieve some stress on mmap_sem.
 
73
   When mmap_sem is heavily hammered, the scheduler
 
74
   tends to fail to wake us up properly.
 
75
*/
 
76
pthread_mutex_t fmap_mutex = PTHREAD_MUTEX_INITIALIZER;
 
77
#define fmap_lock pthread_mutex_lock(&fmap_mutex)
 
78
#define fmap_unlock pthread_mutex_unlock(&fmap_mutex);
 
79
#else
 
80
#define fmap_lock
 
81
#define fmap_unlock
 
82
#endif
 
83
 
 
84
#ifndef MADV_DONTFORK
 
85
#define MADV_DONTFORK 0
 
86
#endif
 
87
 
 
88
#define fmap_bitmap (&m->placeholder_for_bitmap)
 
89
 
 
90
/* pread proto here in order to avoid the use of XOPEN and BSD_SOURCE
 
91
   which may in turn prevent some mmap constants to be defined */
 
92
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
 
93
 
 
94
fmap_t *fmap(int fd, off_t offset, size_t len) {
 
95
    unsigned int pages, mapsz, hdrsz;
 
96
    unsigned short dumb = 1;
 
97
    int pgsz = cli_getpagesize();
 
98
    struct stat st;
 
99
    fmap_t *m;
 
100
 
 
101
    if(fstat(fd, &st)) {
 
102
        cli_warnmsg("fmap: fstat failed\n");
 
103
        return NULL;
 
104
    }
 
105
    if(offset < 0 || offset != fmap_align_to(offset, pgsz)) {
 
106
        cli_warnmsg("fmap: attempted mapping with unaligned offset\n");
 
107
        return NULL;
 
108
    }
 
109
    if(!len) len = st.st_size - offset; /* bound checked later */
 
110
    if(!len) {
 
111
        cli_warnmsg("fmap: attempted void mapping\n");
 
112
        return NULL;
 
113
    }
 
114
    if(!CLI_ISCONTAINED(0, st.st_size, offset, len)) {
 
115
        cli_warnmsg("fmap: attempted oof mapping\n");
 
116
        return NULL;
 
117
    }
 
118
 
 
119
    pages = fmap_align_items(len, pgsz);
 
120
    hdrsz = fmap_align_to(sizeof(fmap_t) + (pages-1) * sizeof(uint32_t), pgsz); /* fmap_t includes 1 bitmap slot, hence (pages-1) */
 
121
    mapsz = pages * pgsz + hdrsz;
 
122
    fmap_lock;
 
123
#if HAVE_MMAP
 
124
    if ((m = (fmap_t *)mmap(NULL, mapsz, PROT_READ | PROT_WRITE, MAP_PRIVATE|/*FIXME: MAP_POPULATE is ~8% faster but more memory intensive */ANONYMOUS_MAP, -1, 0)) == MAP_FAILED) {
 
125
        m = NULL;
 
126
    } else {
 
127
        dumb = 0;
 
128
#if HAVE_MADVISE
 
129
        madvise((void *)m, mapsz, MADV_RANDOM|MADV_DONTFORK);
 
130
#endif /* madvise */
 
131
    }
 
132
#else /* ! HAVE_MMAP */
 
133
    m = (fmap_t *)cli_malloc(mapsz);
 
134
#endif /* HAVE_MMAP */
 
135
    if(!m) {
 
136
        cli_warnmsg("fmap: map allocation failed\n");
 
137
        fmap_unlock;
 
138
        return NULL;
 
139
    }
 
140
    /* fault the header while we still have the lock - we DO context switch here a lot here :@ */
 
141
    memset(fmap_bitmap, 0, sizeof(uint32_t) * pages);
 
142
    fmap_unlock;
 
143
    m->fd = fd;
 
144
    m->dumb = dumb;
 
145
    m->mtime = st.st_mtime;
 
146
    m->offset = offset;
 
147
    m->len = len;
 
148
    m->pages = pages;
 
149
    m->hdrsz = hdrsz;
 
150
    m->pgsz = pgsz;
 
151
    m->paged = 0;
 
152
    m->dont_cache_flag = 0;
 
153
    return m;
 
154
}
 
155
 
 
156
 
 
157
static void fmap_aging(fmap_t *m) {
 
158
#if HAVE_MMAP
 
159
    if(m->dumb) return;
 
160
    if(m->paged * m->pgsz > UNPAGE_THRSHLD_HI) { /* we alloc'd too much */
 
161
        unsigned int i, avail = 0, freeme[2048], maxavail = MIN(sizeof(freeme)/sizeof(*freeme), m->paged - UNPAGE_THRSHLD_LO / m->pgsz) - 1;
 
162
 
 
163
        for(i=0; i<m->pages; i++) {
 
164
            uint32_t s = fmap_bitmap[i];
 
165
            if((s & (FM_MASK_PAGED | FM_MASK_LOCKED)) == FM_MASK_PAGED ) {
 
166
                /* page is paged and not locked: dec age */
 
167
                if(s & FM_MASK_COUNT) fmap_bitmap[i]--;
 
168
                /* and make it available for unpaging */
 
169
 
 
170
                if(!avail) {
 
171
                    freeme[0] = i;
 
172
                    avail++;
 
173
                } else {
 
174
                    /* Insert sort onto a stack'd array - same performance as quickselect */
 
175
                    unsigned int insert_to = MIN(maxavail, avail) - 1, age = fmap_bitmap[i] & FM_MASK_COUNT;
 
176
                    if(avail <= maxavail || (fmap_bitmap[freeme[maxavail]] & FM_MASK_COUNT) > age) {
 
177
                        while((fmap_bitmap[freeme[insert_to]] & FM_MASK_COUNT) > age) {
 
178
                            freeme[insert_to + 1] = freeme[insert_to];
 
179
                            if(!insert_to--) break;
 
180
                        }
 
181
                        freeme[insert_to + 1] = i;
 
182
                        if(avail <= maxavail) avail++;
 
183
                    }
 
184
                }
 
185
            }
 
186
        }
 
187
        if(avail) { /* at least one page is paged and not locked */
 
188
            for(i=0; i<avail; i++) {
 
189
                char *pptr = (char *)m + freeme[i] * m->pgsz + m->hdrsz;
 
190
                /* we mark the page as seen */
 
191
                fmap_bitmap[freeme[i]] = FM_MASK_SEEN;
 
192
                /* and we mmap the page over so the kernel knows there's nothing good in there */
 
193
                fmap_lock;
 
194
                if(mmap(pptr, m->pgsz, PROT_READ | PROT_WRITE, MAP_FIXED|MAP_PRIVATE|ANONYMOUS_MAP, -1, 0) == MAP_FAILED)
 
195
                    cli_warnmsg("fmap_aging: kernel hates you\n");
 
196
                fmap_unlock;
 
197
            }
 
198
            m->paged -= avail;
 
199
        }
 
200
    }
 
201
#endif
 
202
}
 
203
 
 
204
 
 
205
static int fmap_readpage(fmap_t *m, unsigned int first_page, unsigned int count, unsigned int lock_count) {
 
206
    size_t readsz = 0, got;
 
207
    char *pptr = NULL;
 
208
    uint32_t s;
 
209
    unsigned int i, page = first_page, force_read = 0;
 
210
 
 
211
    fmap_lock;
 
212
    for(i=0; i<count; i++) { /* prefault */
 
213
        /* Not worth checking if the page is already paged, just ping each */
 
214
        /* Also not worth reusing the loop below */
 
215
        volatile char faultme;
 
216
        faultme = ((char *)m)[(first_page+i) * m->pgsz + m->hdrsz];
 
217
    }
 
218
    fmap_unlock;
 
219
    for(i=0; i<=count; i++, page++) {
 
220
        int lock;
 
221
        if(lock_count) {
 
222
            lock_count--;
 
223
            lock = 1;
 
224
        } else lock = 0;
 
225
        if(i == count) {
 
226
            /* we count one page too much to flush pending reads */
 
227
            if(!pptr) return 0; /* if we have any */
 
228
            force_read = 1;
 
229
        } else if((s=fmap_bitmap[page]) & FM_MASK_PAGED) {
 
230
            /* page already paged */
 
231
            if(lock) {
 
232
                /* we want locking */
 
233
                if(s & FM_MASK_LOCKED) {
 
234
                    /* page already locked */
 
235
                    s &= FM_MASK_COUNT;
 
236
                    if(s == FM_MASK_COUNT) { /* lock count already at max: fial! */
 
237
                        cli_errmsg("fmap_readpage: lock count exceeded\n");
 
238
                        return 1;
 
239
                    }
 
240
                    /* acceptable lock count: inc lock count */
 
241
                    fmap_bitmap[page]++;
 
242
                } else /* page not currently locked: set lock count = 1 */
 
243
                    fmap_bitmap[page] = 1 | FM_MASK_LOCKED | FM_MASK_PAGED;
 
244
            } else {
 
245
                /* we don't want locking */
 
246
                if(!(s & FM_MASK_LOCKED)) {
 
247
                    /* page is not locked: we reset aging to max */
 
248
                    fmap_bitmap[page] = FM_MASK_PAGED | FM_MASK_COUNT;
 
249
                }
 
250
            }
 
251
            if(!pptr) continue;
 
252
            force_read = 1;
 
253
        }
 
254
 
 
255
        if(force_read) {
 
256
            /* we have some pending reads to perform */
 
257
            unsigned int j;
 
258
            for(j=first_page; j<page; j++) {
 
259
                if(fmap_bitmap[j] & FM_MASK_SEEN) {
 
260
                    /* page we've seen before: check mtime */
 
261
                    struct stat st;
 
262
                    if(fstat(m->fd, &st)) {
 
263
                        cli_warnmsg("fmap_readpage: fstat failed\n");
 
264
                        return 1;
 
265
                    }
 
266
                    if(m->mtime != st.st_mtime) {
 
267
                        cli_warnmsg("fmap_readpage: file changed as we read it\n");
 
268
                        return 1;
 
269
                    }
 
270
                    break;
 
271
                }
 
272
            }
 
273
 
 
274
            if((got=pread(m->fd, pptr, readsz, m->offset + first_page * m->pgsz)) != readsz) {
 
275
                cli_warnmsg("pread fail: page %u pages %u map-offset %lu - asked for %lu bytes, got %lu\n", first_page, m->pages, (long unsigned int)m->offset, (long unsigned int)readsz, (long unsigned int)got);
 
276
                return 1;
 
277
            }
 
278
            pptr = NULL;
 
279
            force_read = 0;
 
280
            readsz = 0;
 
281
            continue;
 
282
        }
 
283
 
 
284
        /* page is not already paged */
 
285
        if(!pptr) {
 
286
            /* set a new start for pending reads if we don't have one */
 
287
            pptr = (char *)m + page * m->pgsz + m->hdrsz;
 
288
            first_page = page;
 
289
        }
 
290
        if((page == m->pages - 1) && (m->len % m->pgsz))
 
291
            readsz += m->len % m->pgsz;
 
292
        else
 
293
            readsz += m->pgsz;
 
294
        if(lock) /* lock requested: set paged, lock page and set lock count to 1 */
 
295
            fmap_bitmap[page] = FM_MASK_PAGED | FM_MASK_LOCKED | 1;
 
296
        else /* no locking: set paged and set aging to max */
 
297
            fmap_bitmap[page] = FM_MASK_PAGED | FM_MASK_COUNT;
 
298
        m->paged++;
 
299
    }
 
300
    return 0;
 
301
}
 
302
 
 
303
 
 
304
static void *fmap_need(fmap_t *m, size_t at, size_t len, int lock) {
 
305
    unsigned int first_page, last_page, lock_count;
 
306
    char *ret;
 
307
 
 
308
    if(!len)
 
309
        return NULL;
 
310
 
 
311
    if(!CLI_ISCONTAINED(0, m->len, at, len)) {
 
312
        cli_dbgmsg("fmap: attempted oof need\n");
 
313
        return NULL;
 
314
    }
 
315
 
 
316
    fmap_aging(m);
 
317
 
 
318
    first_page = fmap_which_page(m, at);
 
319
    last_page = fmap_which_page(m, at + len - 1);
 
320
    lock_count = (lock!=0) * (last_page-first_page+1);
 
321
#ifdef READAHED_PAGES
 
322
    last_page += READAHED_PAGES;
 
323
    if(last_page >= m->pages) last_page = m->pages - 1;
 
324
#endif
 
325
 
 
326
    if(fmap_readpage(m, first_page, last_page-first_page+1, lock_count))
 
327
        return NULL;
 
328
 
 
329
    ret = (char *)m;
 
330
    ret += at + m->hdrsz;
 
331
    return (void *)ret;
 
332
}
 
333
 
 
334
void *fmap_need_off(fmap_t *m, size_t at, size_t len) {
 
335
    return fmap_need(m, at, len, 1);
 
336
}
 
337
void *fmap_need_off_once(fmap_t *m, size_t at, size_t len) {
 
338
    return fmap_need(m, at, len, 0);
 
339
}
 
340
void *fmap_need_ptr(fmap_t *m, void *ptr, size_t len) {
 
341
    return fmap_need_off(m, (char *)ptr - (char *)m - m->hdrsz, len);
 
342
}
 
343
void *fmap_need_ptr_once(fmap_t *m, void *ptr, size_t len) {
 
344
    return fmap_need_off_once(m, (char *)ptr - (char *)m - m->hdrsz, len);
 
345
}
 
346
void *fmap_need_str(fmap_t *m, void *ptr, size_t len_hint) {
 
347
    size_t at = (char *)ptr - (char *)m - m->hdrsz;
 
348
    return fmap_need_offstr(m, at, len_hint);
 
349
}
 
350
 
 
351
static void fmap_unneed_page(fmap_t *m, unsigned int page) {
 
352
    uint32_t s = fmap_bitmap[page];
 
353
 
 
354
    if((s & (FM_MASK_PAGED | FM_MASK_LOCKED)) == (FM_MASK_PAGED | FM_MASK_LOCKED)) {
 
355
        /* page is paged and locked: check lock count */
 
356
        s &= FM_MASK_COUNT;
 
357
        if(s > 1) /* locked more than once: dec lock count */
 
358
            fmap_bitmap[page]--;
 
359
        else if (s == 1) /* only one lock left: unlock and begin aging */
 
360
            fmap_bitmap[page] = FM_MASK_COUNT | FM_MASK_PAGED;
 
361
        else 
 
362
            cli_errmsg("fmap_unneed: inconsistent map state\n");
 
363
        return;
 
364
    }
 
365
    cli_warnmsg("fmap_unneed: unneed on a unlocked page\n");
 
366
    return;
 
367
}
 
368
 
 
369
void fmap_unneed_off(fmap_t *m, size_t at, size_t len) {
 
370
    unsigned int i, first_page, last_page;
 
371
    if(m->dumb) return;
 
372
    if(!len) {
 
373
        cli_warnmsg("fmap_unneed: attempted void unneed\n");
 
374
        return;
 
375
    }
 
376
 
 
377
    if(!CLI_ISCONTAINED(0, m->len, at, len)) {
 
378
        cli_warnmsg("fmap: attempted oof unneed\n");
 
379
        return;
 
380
    }
 
381
 
 
382
    first_page = fmap_which_page(m, at);
 
383
    last_page = fmap_which_page(m, at + len - 1);
 
384
 
 
385
    for(i=first_page; i<=last_page; i++) {
 
386
        fmap_unneed_page(m, i);
 
387
    }
 
388
}
 
389
 
 
390
void fmap_unneed_ptr(fmap_t *m, void *ptr, size_t len) {
 
391
    fmap_unneed_off(m, (char *)ptr - (char *)m - m->hdrsz, len);
 
392
}
 
393
 
 
394
void funmap(fmap_t *m) {
 
395
#if HAVE_MMAP
 
396
    if(!m->dumb) {
 
397
        size_t len = m->pages * m->pgsz + m->hdrsz;
 
398
        fmap_lock;
 
399
        munmap((void *)m, len);
 
400
        fmap_unlock;
 
401
    } else
 
402
#endif
 
403
        free((void *)m);
 
404
}
 
405
 
 
406
void *fmap_need_offstr(fmap_t *m, size_t at, size_t len_hint) {
 
407
    unsigned int i, first_page, last_page;
 
408
    void *ptr = (void *)((char *)m + m->hdrsz + at);
 
409
 
 
410
    if(!len_hint || len_hint > m->len - at)
 
411
        len_hint = m->len - at;
 
412
 
 
413
    if(!CLI_ISCONTAINED(0, m->len, at, len_hint))
 
414
        return NULL;
 
415
 
 
416
    fmap_aging(m);
 
417
 
 
418
    first_page = fmap_which_page(m, at);
 
419
    last_page = fmap_which_page(m, at + len_hint - 1);
 
420
 
 
421
    for(i=first_page; i<=last_page; i++) {
 
422
        char *thispage = (char *)m + m->hdrsz + i * m->pgsz;
 
423
        unsigned int scanat, scansz;
 
424
 
 
425
        if(fmap_readpage(m, i, 1, 1)) {
 
426
            last_page = i-1;
 
427
            break;
 
428
        }
 
429
        if(i == first_page) {
 
430
            scanat = at % m->pgsz;
 
431
            scansz = MIN(len_hint, m->pgsz - scanat);
 
432
        } else {
 
433
            scanat = 0;
 
434
            scansz = MIN(len_hint, m->pgsz);
 
435
        }
 
436
        len_hint -= scansz;
 
437
        if(memchr(&thispage[scanat], 0, scansz))
 
438
            return ptr;
 
439
    }
 
440
    for(i=first_page; i<=last_page; i++)
 
441
        fmap_unneed_page(m, i);
 
442
    return NULL;
 
443
}
 
444
 
 
445
 
 
446
void *fmap_gets(fmap_t *m, char *dst, size_t *at, size_t max_len) {
 
447
    unsigned int i, first_page, last_page;
 
448
    char *src = (void *)((char *)m + m->hdrsz + *at), *endptr = NULL;
 
449
    size_t len = MIN(max_len-1, m->len - *at), fullen = len;
 
450
 
 
451
    if(!len || !CLI_ISCONTAINED(0, m->len, *at, len))
 
452
        return NULL;
 
453
 
 
454
    fmap_aging(m);
 
455
 
 
456
    first_page = fmap_which_page(m, *at);
 
457
    last_page = fmap_which_page(m, *at + len - 1);
 
458
 
 
459
    for(i=first_page; i<=last_page; i++) {
 
460
        char *thispage = (char *)m + m->hdrsz + i * m->pgsz;
 
461
        unsigned int scanat, scansz;
 
462
 
 
463
        if(fmap_readpage(m, i, 1, 0))
 
464
            return NULL;
 
465
 
 
466
        if(i == first_page) {
 
467
            scanat = *at % m->pgsz;
 
468
            scansz = MIN(len, m->pgsz - scanat);
 
469
        } else {
 
470
            scanat = 0;
 
471
            scansz = MIN(len, m->pgsz);
 
472
        }
 
473
        len -= scansz;
 
474
 
 
475
        if((endptr = memchr(&thispage[scanat], '\n', scansz))) {
 
476
            endptr++;
 
477
            break;
 
478
        }
 
479
    }
 
480
    if(endptr) {
 
481
        memcpy(dst, src, endptr - src);
 
482
        dst[endptr - src] = '\0';
 
483
        *at += endptr - src;
 
484
    } else {
 
485
        memcpy(dst, src, fullen);
 
486
        dst[fullen] = '\0';
 
487
        *at += fullen;
 
488
    }
 
489
    return dst;
 
490
}
 
491
 
 
492
/* ^^^^^ POSIX STUFF AVOVE ^^^^^ */
 
493
 
 
494
#else /* _WIN32 */
 
495
 
 
496
/* vvvvv WIN32 STUFF BELOW vvvvv */
 
497
 
 
498
fmap_t *fmap(int fd, off_t offset, size_t len) { /* WIN32 */
 
499
    unsigned int pages, mapsz, hdrsz;
 
500
    unsigned short dumb = 1;
 
501
    int pgsz = cli_getpagesize();
 
502
    struct stat st;
 
503
    fmap_t *m;
 
504
 
 
505
    if(fstat(fd, &st)) {
 
506
        cli_warnmsg("fmap: fstat failed\n");
 
507
        return NULL;
 
508
    }
 
509
    if(offset < 0 || offset != fmap_align_to(offset, pgsz)) {
 
510
        cli_warnmsg("fmap: attempted mapping with unaligned offset\n");
 
511
        return NULL;
 
512
    }
 
513
    if(!len) len = st.st_size - offset; /* bound checked later */
 
514
    if(!len) {
 
515
        cli_warnmsg("fmap: attempted void mapping\n");
 
516
        return NULL;
 
517
    }
 
518
    if(!CLI_ISCONTAINED(0, st.st_size, offset, len)) {
 
519
        cli_warnmsg("fmap: attempted oof mapping\n");
 
520
        return NULL;
 
521
    }
 
522
 
 
523
    pages = fmap_align_items(len, pgsz);
 
524
    hdrsz = fmap_align_to(sizeof(fmap_t), pgsz);
 
525
 
 
526
    if(!(m = (fmap_t *)cli_malloc(sizeof(fmap_t)))) {
 
527
        cli_errmsg("fmap: canot allocate fmap_t\n", fd);
 
528
        return NULL;
 
529
    }
 
530
    if((m->fh = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE) {
 
531
        cli_errmsg("fmap: cannot get a valid handle for descriptor %d\n", fd);
 
532
        free(m);
 
533
        return NULL;
 
534
    }
 
535
    if(!(m->mh = CreateFileMapping(m->fh, NULL, PAGE_READONLY, (DWORD)((len>>31)>>1), (DWORD)len, NULL))) {
 
536
        cli_errmsg("fmap: cannot create a map of descriptor %d\n", fd);
 
537
        free(m);
 
538
        return NULL;
 
539
    }
 
540
    if(!(m->data = MapViewOfFile(m->mh, FILE_MAP_READ, (DWORD)((offset>>31)>>1), (DWORD)(offset), len))) {
 
541
        cli_errmsg("fmap: cannot map file descriptor %d\n", fd);
 
542
        CloseHandle(m->mh);
 
543
        free(m);
 
544
        return NULL;
 
545
    }
 
546
    m->fd = fd;
 
547
    m->dumb = dumb;
 
548
    m->mtime = st.st_mtime;
 
549
    m->offset = offset;
 
550
    m->len = len;
 
551
    m->pages = pages;
 
552
    m->hdrsz = hdrsz;
 
553
    m->pgsz = pgsz;
 
554
    m->paged = 0;
 
555
    m->dont_cache_flag = 0;
 
556
    return m;
 
557
}
 
558
 
 
559
void funmap(fmap_t *m) { /* WIN32 */
 
560
    UnmapViewOfFile(m->data);
 
561
    CloseHandle(m->mh);
 
562
    free((void *)m);
 
563
}
 
564
 
 
565
static void *fmap_need(fmap_t *m, size_t at, size_t len) { /* WIN32 */
 
566
    if(!CLI_ISCONTAINED(0, m->len, at, len)) {
 
567
        cli_dbgmsg("fmap: attempted oof need\n");
 
568
        return NULL;
 
569
    }
 
570
    if(!len)
 
571
        return NULL;
 
572
    return (void *)((char *)m->data + at);
 
573
}
 
574
 
 
575
void *fmap_need_off(fmap_t *m, size_t at, size_t len) { /* WIN32 */
 
576
    return fmap_need(m, at, len);
 
577
}
 
578
void *fmap_need_off_once(fmap_t *m, size_t at, size_t len) { /* WIN32 */
 
579
    return fmap_need(m, at, len);
 
580
}
 
581
void *fmap_need_ptr(fmap_t *m, void *ptr, size_t len) { /* WIN32 */
 
582
    return fmap_need(m, (char *)ptr - (char *)m->data, len);
 
583
}
 
584
void *fmap_need_ptr_once(fmap_t *m, void *ptr, size_t len) { /* WIN32 */
 
585
    return fmap_need(m, (char *)ptr - (char *)m->data, len);
 
586
}
 
587
void fmap_unneed_off(fmap_t *m, size_t at, size_t len) { /* WIN32 */
 
588
}
 
589
void fmap_unneed_ptr(fmap_t *m, void *ptr, size_t len) { /* WIN32 */
 
590
}
 
591
 
 
592
void *fmap_need_offstr(fmap_t *m, size_t at, size_t len_hint) { /* WIN32 */
 
593
    char *ptr = (char *)m->data + at;
 
594
 
 
595
    if(!len_hint || len_hint > m->len - at)
 
596
        len_hint = m->len - at;
 
597
 
 
598
    if(!CLI_ISCONTAINED(0, m->len, at, len_hint))
 
599
        return NULL;
 
600
 
 
601
    if(memchr(ptr, 0, len_hint))
 
602
        return (void *)ptr;
 
603
    return NULL;
 
604
}
 
605
 
 
606
void *fmap_need_str(fmap_t *m, void *ptr, size_t len_hint) { /* WIN32 */
 
607
    size_t at = (char *)ptr - (char *)m->data;
 
608
    return fmap_need_offstr(m, at, len_hint);
 
609
}
 
610
 
 
611
void *fmap_gets(fmap_t *m, char *dst, size_t *at, size_t max_len) { /* WIN32 */
 
612
    char *src = (char *)m->data + *at, *endptr = NULL;
 
613
    size_t len = MIN(max_len-1, m->len - *at);
 
614
 
 
615
    if(!len || !CLI_ISCONTAINED(0, m->len, *at, len))
 
616
        return NULL;
 
617
 
 
618
    if((endptr = memchr(src, '\n', len))) {
 
619
        endptr++;
 
620
        memcpy(dst, src, endptr - src);
 
621
        dst[endptr - src] = '\0';
 
622
        *at += endptr - src;
 
623
    } else {
 
624
        memcpy(dst, src, len);
 
625
        dst[len] = '\0';
 
626
        *at += len;
 
627
    }
 
628
    return dst;
 
629
}
 
630
 
 
631
#endif /* _WIN32 */
 
632
 
 
633
 
 
634
/* vvvvv SHARED STUFF BELOW vvvvv */
 
635
 
 
636
int fmap_readn(fmap_t *m, void *dst, size_t at, size_t len) {
 
637
    char *src;
 
638
 
 
639
    if(at > m->len)
 
640
        return -1;
 
641
    if(len > m->len - at)
 
642
        len = m->len - at;
 
643
    src = fmap_need_off_once(m, at, len);
 
644
    if(!src)
 
645
        return -1;
 
646
    memcpy(dst, src, len);
 
647
    return len;
 
648
}
 
649
 
 
650
static inline unsigned int fmap_align_items(unsigned int sz, unsigned int al) {
 
651
    return sz / al + (sz % al != 0);
 
652
}
 
653
 
 
654
static inline unsigned int fmap_align_to(unsigned int sz, unsigned int al) {
 
655
    return al * fmap_align_items(sz, al);
 
656
}
 
657
 
 
658
static inline unsigned int fmap_which_page(fmap_t *m, size_t at) {
 
659
    return at / m->pgsz;
 
660
}