Coverage Report

Created: 2023-12-08 06:48

/src/clamav/libclamav/fmap.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *  Copyright (C) 2013-2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3
 *  Copyright (C) 2009-2013 Sourcefire, Inc.
4
 *
5
 *  Authors: aCaB <acab@clamav.net>
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License version 2 as
9
 *  published by the Free Software Foundation.
10
 *
11
 *  This program is distributed in the hope that it will be useful,
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 *  GNU General Public License for more details.
15
 *
16
 *  You should have received a copy of the GNU General Public License
17
 *  along with this program; if not, write to the Free Software
18
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 *  MA 02110-1301, USA.
20
 */
21
22
/* an mmap "replacement" which doesn't suck */
23
24
#if HAVE_CONFIG_H
25
#include "clamav-config.h"
26
#endif
27
28
#include <stdint.h>
29
#include <sys/types.h>
30
#include <sys/stat.h>
31
#include <string.h>
32
#include <libgen.h>
33
#ifdef HAVE_UNISTD_H
34
#include <unistd.h>
35
#endif
36
#ifdef ANONYMOUS_MAP
37
#ifdef HAVE_SYS_MMAN_H
38
#include <sys/mman.h>
39
#endif
40
#endif
41
#include <errno.h>
42
43
#ifdef C_LINUX
44
#include <pthread.h>
45
#endif
46
47
#include "clamav.h"
48
#include "others.h"
49
#include "str.h"
50
51
609M
#define FM_MASK_COUNT 0x3fffffff
52
1.24G
#define FM_MASK_PAGED 0x40000000
53
658M
#define FM_MASK_SEEN 0x80000000
54
637M
#define FM_MASK_LOCKED FM_MASK_SEEN
55
/* 2 high bits:
56
00 - not seen - not paged - N/A
57
01 -    N/A   -   paged   - not locked
58
10 -   seen   - not paged - N/A
59
11 -    N/A   -   paged   - locked
60
*/
61
62
/* FIXME: tune this stuff */
63
#define UNPAGE_THRSHLD_LO 4 * 1024 * 1024
64
#define UNPAGE_THRSHLD_HI 8 * 1024 * 1024
65
#define READAHEAD_PAGES 8
66
67
#if defined(ANONYMOUS_MAP) && defined(C_LINUX) && defined(CL_THREAD_SAFE)
68
/*
69
   WORKAROUND
70
   Relieve some stress on mmap_sem.
71
   When mmap_sem is heavily hammered, the scheduler
72
   tends to fail to wake us up properly.
73
*/
74
pthread_mutex_t fmap_mutex = PTHREAD_MUTEX_INITIALIZER;
75
#define fmap_lock pthread_mutex_lock(&fmap_mutex)
76
#define fmap_unlock pthread_mutex_unlock(&fmap_mutex);
77
#else
78
#define fmap_lock
79
#define fmap_unlock
80
#endif
81
82
#ifndef MADV_DONTFORK
83
#define MADV_DONTFORK 0
84
#endif
85
86
1.27G
#define fmap_bitmap (m->bitmap)
87
88
static inline uint64_t fmap_align_items(uint64_t sz, uint64_t al);
89
static inline uint64_t fmap_align_to(uint64_t sz, uint64_t al);
90
static inline uint64_t fmap_which_page(fmap_t *m, size_t at);
91
92
static const void *handle_need(fmap_t *m, size_t at, size_t len, int lock);
93
static void handle_unneed_off(fmap_t *m, size_t at, size_t len);
94
static const void *handle_need_offstr(fmap_t *m, size_t at, size_t len_hint);
95
static const void *handle_gets(fmap_t *m, char *dst, size_t *at, size_t max_len);
96
97
static void unmap_mmap(fmap_t *m);
98
static void unmap_malloc(fmap_t *m);
99
100
#ifndef _WIN32
101
/* pread proto here in order to avoid the use of XOPEN and BSD_SOURCE
102
   which may in turn prevent some mmap constants to be defined */
103
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
104
105
/* vvvvv POSIX STUFF BELOW vvvvv */
106
static off_t pread_cb(void *handle, void *buf, size_t count, off_t offset)
107
8.37M
{
108
8.37M
    return pread((int)(ssize_t)handle, buf, count, offset);
109
8.37M
}
110
111
fmap_t *fmap_check_empty(int fd, off_t offset, size_t len, int *empty, const char *name)
112
8.91M
{
113
8.91M
    STATBUF st;
114
8.91M
    fmap_t *m = NULL;
115
116
8.91M
    *empty = 0;
117
8.91M
    if (FSTAT(fd, &st)) {
118
0
        cli_warnmsg("fmap: fstat failed\n");
119
0
        return NULL;
120
0
    }
121
122
8.91M
    if (!len) len = st.st_size - offset; /* bound checked later */
123
8.91M
    if (!len) {
124
982
        cli_dbgmsg("fmap: attempted void mapping\n");
125
982
        *empty = 1;
126
982
        return NULL;
127
982
    }
128
8.91M
    if (!CLI_ISCONTAINED_0_TO(st.st_size, offset, len)) {
129
0
        cli_warnmsg("fmap: attempted oof mapping\n");
130
0
        return NULL;
131
0
    }
132
8.91M
    m = cl_fmap_open_handle((void *)(ssize_t)fd, offset, len, pread_cb, 1);
133
8.91M
    if (!m)
134
0
        return NULL;
135
8.91M
    m->mtime = st.st_mtime;
136
137
8.91M
    if (NULL != name) {
138
5.40M
        m->name = cli_strdup(name);
139
5.40M
        if (NULL == m->name) {
140
0
            funmap(m);
141
0
            return NULL;
142
0
        }
143
5.40M
    }
144
145
8.91M
    return m;
146
8.91M
}
147
#else
148
/* vvvvv WIN32 STUFF BELOW vvvvv */
149
static void unmap_win32(fmap_t *m)
150
{
151
    if (NULL != m) {
152
        if (NULL != m->data) {
153
            UnmapViewOfFile(m->data);
154
        }
155
        if (NULL != m->mh) {
156
            CloseHandle(m->mh);
157
        }
158
        if (NULL != m->name) {
159
            free(m->name);
160
        }
161
        free((void *)m);
162
    }
163
}
164
165
fmap_t *fmap_check_empty(int fd, off_t offset, size_t len, int *empty, const char *name)
166
{ /* WIN32 */
167
    uint64_t pages, mapsz;
168
    int pgsz = cli_getpagesize();
169
    STATBUF st;
170
    fmap_t *m = NULL;
171
    const void *data;
172
    HANDLE fh;
173
    HANDLE mh;
174
175
    *empty = 0;
176
    if (FSTAT(fd, &st)) {
177
        cli_warnmsg("fmap: fstat failed\n");
178
        return NULL;
179
    }
180
    if (offset < 0 || offset != fmap_align_to(offset, pgsz)) {
181
        cli_warnmsg("fmap: attempted mapping with unaligned offset\n");
182
        return NULL;
183
    }
184
    if (!len) len = st.st_size - offset; /* bound checked later */
185
    if (!len) {
186
        cli_dbgmsg("fmap: attempted void mapping\n");
187
        *empty = 1;
188
        return NULL;
189
    }
190
    if (!CLI_ISCONTAINED_0_TO(st.st_size, offset, len)) {
191
        cli_warnmsg("fmap: attempted oof mapping\n");
192
        return NULL;
193
    }
194
195
    pages = fmap_align_items(len, pgsz);
196
197
    if ((fh = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE) {
198
        cli_errmsg("fmap: cannot get a valid handle for descriptor %d\n", fd);
199
        return NULL;
200
    }
201
    if (!(mh = CreateFileMapping(fh, NULL, PAGE_READONLY, (DWORD)((len >> 31) >> 1), (DWORD)len, NULL))) {
202
        cli_errmsg("fmap: cannot create a map of descriptor %d\n", fd);
203
        return NULL;
204
    }
205
    if (!(data = MapViewOfFile(mh, FILE_MAP_READ, (DWORD)((offset >> 31) >> 1), (DWORD)(offset), len))) {
206
        cli_errmsg("fmap: cannot map file descriptor %d\n", fd);
207
        CloseHandle(mh);
208
        return NULL;
209
    }
210
    if (!(m = cl_fmap_open_memory(data, len))) {
211
        cli_errmsg("fmap: cannot allocate fmap_t\n", fd);
212
        UnmapViewOfFile(data);
213
        CloseHandle(mh);
214
        return NULL;
215
    }
216
    m->handle       = (void *)(size_t)fd;
217
    m->handle_is_fd = 1; /* This is probably(?) needed so `fmap_fd()` can return the file descriptor. */
218
    m->fh           = fh;
219
    m->mh           = mh;
220
    m->unmap        = unmap_win32;
221
222
    if (NULL != name) {
223
        m->name = cli_strdup(name);
224
        if (NULL == m->name) {
225
            funmap(m);
226
            return NULL;
227
        }
228
    }
229
230
    return m;
231
}
232
#endif /* _WIN32 */
233
234
/* vvvvv SHARED STUFF BELOW vvvvv */
235
236
fmap_t *fmap_duplicate(cl_fmap_t *map, size_t offset, size_t length, const char *name)
237
15.3M
{
238
15.3M
    cl_error_t status        = CL_ERROR;
239
15.3M
    cl_fmap_t *duplicate_map = NULL;
240
241
15.3M
    if (NULL == map) {
242
0
        cli_warnmsg("fmap_duplicate: map is NULL!\n");
243
0
        goto done;
244
0
    }
245
246
15.3M
    duplicate_map = cli_malloc(sizeof(cl_fmap_t));
247
15.3M
    if (!duplicate_map) {
248
0
        cli_warnmsg("fmap_duplicate: map allocation failed\n");
249
0
        goto done;
250
0
    }
251
252
    /* Duplicate the state of the original map */
253
15.3M
    memcpy(duplicate_map, map, sizeof(cl_fmap_t));
254
255
15.3M
    if (offset > map->len) {
256
        /* invalid offset, exceeds length of map */
257
0
        cli_warnmsg("fmap_duplicate: requested offset exceeds end of map\n");
258
0
        goto done;
259
0
    }
260
261
15.3M
    if (offset > 0 || length < map->len) {
262
        /*
263
         * Caller requested a window into the current map, not the whole map.
264
         */
265
266
        /* Set the new nested offset and (nested) length for the new map */
267
        /* Note: We can't change offset because then we'd have to discard/move cached
268
         * data, instead use nested_offset to reuse the already cached data */
269
15.3M
        duplicate_map->nested_offset += offset;
270
15.3M
        duplicate_map->len = MIN(length, map->len - offset);
271
272
        /* The real_len is the nested_offset + the len of the nested fmap.
273
           real_len is mostly just a shorthand for when doing bounds checking.
274
           We do not need to keep track of the original length of the OG fmap */
275
15.3M
        duplicate_map->real_len = duplicate_map->nested_offset + duplicate_map->len;
276
277
15.3M
        if (!CLI_ISCONTAINED_2(map->nested_offset, map->len,
278
15.3M
                               duplicate_map->nested_offset, duplicate_map->len)) {
279
0
            size_t len1, len2;
280
0
            len1 = map->nested_offset + map->len;
281
0
            len2 = duplicate_map->nested_offset + duplicate_map->len;
282
0
            cli_warnmsg("fmap_duplicate: internal map error: %zu, %zu; %zu, %zu\n",
283
0
                        map->nested_offset, len1,
284
0
                        duplicate_map->nested_offset, len2);
285
0
        }
286
287
        /* This also means the hash will be different.
288
         * Clear the have_<hash> flags.
289
         * It will be calculated when next it is needed. */
290
15.3M
        duplicate_map->have_md5    = false;
291
15.3M
        duplicate_map->have_sha1   = false;
292
15.3M
        duplicate_map->have_sha256 = false;
293
15.3M
    }
294
295
15.3M
    if (NULL != name) {
296
1.37M
        duplicate_map->name = cli_strdup(name);
297
1.37M
        if (NULL == duplicate_map->name) {
298
0
            status = CL_EMEM;
299
0
            goto done;
300
0
        }
301
13.9M
    } else {
302
13.9M
        duplicate_map->name = NULL;
303
13.9M
    }
304
305
15.3M
    status = CL_SUCCESS;
306
307
15.3M
done:
308
15.3M
    if (CL_SUCCESS != status) {
309
0
        if (NULL != duplicate_map) {
310
0
            free(duplicate_map);
311
0
            duplicate_map = NULL;
312
0
        }
313
0
    }
314
315
15.3M
    return duplicate_map;
316
15.3M
}
317
318
void free_duplicate_fmap(cl_fmap_t *map)
319
15.3M
{
320
15.3M
    if (NULL != map) {
321
15.3M
        if (NULL != map->name) {
322
1.37M
            free(map->name);
323
1.37M
            map->name = NULL;
324
1.37M
        }
325
15.3M
        free(map);
326
15.3M
    }
327
15.3M
}
328
329
static void unmap_handle(fmap_t *m)
330
8.91M
{
331
8.91M
    if (NULL != m) {
332
8.91M
        if (NULL != m->data) {
333
8.91M
            if (m->aging) {
334
0
                unmap_mmap(m);
335
8.91M
            } else {
336
8.91M
                free((void *)m->data);
337
8.91M
            }
338
8.91M
            m->data = NULL;
339
8.91M
        }
340
8.91M
        if (NULL != m->bitmap) {
341
8.91M
            free(m->bitmap);
342
8.91M
            m->bitmap = NULL;
343
8.91M
        }
344
8.91M
        if (NULL != m->name) {
345
5.40M
            free(m->name);
346
5.40M
        }
347
8.91M
        free((void *)m);
348
8.91M
    }
349
8.91M
}
350
351
extern cl_fmap_t *cl_fmap_open_handle(void *handle, size_t offset, size_t len,
352
                                      clcb_pread pread_cb, int use_aging)
353
8.91M
{
354
8.91M
    cl_error_t status = CL_EMEM;
355
8.91M
    uint64_t pages;
356
8.91M
    size_t mapsz, bitmap_size;
357
8.91M
    cl_fmap_t *m = NULL;
358
8.91M
    int pgsz     = cli_getpagesize();
359
360
8.91M
    if ((off_t)offset < 0 || offset != fmap_align_to(offset, pgsz)) {
361
0
        cli_warnmsg("fmap: attempted mapping with unaligned offset\n");
362
0
        goto done;
363
0
    }
364
8.91M
    if (!len) {
365
0
        cli_dbgmsg("fmap: attempted void mapping\n");
366
0
        goto done;
367
0
    }
368
8.91M
    if (offset >= len) {
369
0
        cli_warnmsg("fmap: attempted oof mapping\n");
370
0
        goto done;
371
0
    }
372
373
8.91M
    pages = fmap_align_items(len, pgsz);
374
375
8.91M
    bitmap_size = pages * sizeof(uint64_t);
376
8.91M
    mapsz       = pages * pgsz;
377
378
8.91M
    m = cli_calloc(1, sizeof(fmap_t));
379
8.91M
    if (!m) {
380
0
        cli_warnmsg("fmap: map header allocation failed\n");
381
0
        goto done;
382
0
    }
383
384
8.91M
    m->bitmap = cli_calloc(1, bitmap_size);
385
8.91M
    if (!m->bitmap) {
386
0
        cli_warnmsg("fmap: map header allocation failed\n");
387
0
        goto done;
388
0
    }
389
390
8.91M
#ifndef ANONYMOUS_MAP
391
8.91M
    use_aging = 0;
392
8.91M
#endif
393
#ifdef ANONYMOUS_MAP
394
    if (use_aging) {
395
        fmap_lock;
396
        if ((m->data = (fmap_t *)mmap(NULL,
397
                                      mapsz,
398
                                      PROT_READ | PROT_WRITE, MAP_PRIVATE | /* FIXME: MAP_POPULATE is ~8% faster but more memory intensive */ ANONYMOUS_MAP,
399
                                      -1,
400
                                      0)) == MAP_FAILED) {
401
            m->data = NULL;
402
        } else {
403
#if HAVE_MADVISE
404
            madvise((void *)m->data, mapsz, MADV_RANDOM | MADV_DONTFORK);
405
#endif /* madvise */
406
        }
407
        fmap_unlock;
408
    }
409
#endif /* ANONYMOUS_MAP */
410
8.91M
    if (!use_aging) {
411
8.91M
        m->data = (fmap_t *)cli_malloc(mapsz);
412
8.91M
    }
413
8.91M
    if (!m->data) {
414
0
        cli_warnmsg("fmap: map allocation failed\n");
415
0
        goto done;
416
0
    }
417
8.91M
    m->handle          = handle;
418
8.91M
    m->pread_cb        = pread_cb;
419
8.91M
    m->aging           = use_aging;
420
8.91M
    m->offset          = offset;
421
8.91M
    m->nested_offset   = 0;
422
8.91M
    m->len             = len; /* m->nested_offset + m->len = m->real_len */
423
8.91M
    m->real_len        = len;
424
8.91M
    m->pages           = pages;
425
8.91M
    m->pgsz            = pgsz;
426
8.91M
    m->paged           = 0;
427
8.91M
    m->dont_cache_flag = false;
428
8.91M
    m->unmap           = unmap_handle;
429
8.91M
    m->need            = handle_need;
430
8.91M
    m->need_offstr     = handle_need_offstr;
431
8.91M
    m->gets            = handle_gets;
432
8.91M
    m->unneed_off      = handle_unneed_off;
433
8.91M
    m->handle_is_fd    = 1;
434
8.91M
    m->have_md5        = false;
435
8.91M
    m->have_sha1       = false;
436
8.91M
    m->have_sha256     = false;
437
438
8.91M
    status = CL_SUCCESS;
439
440
8.91M
done:
441
8.91M
    if (CL_SUCCESS != status) {
442
0
        unmap_handle(m);
443
0
        m = NULL;
444
0
    }
445
8.91M
    return m;
446
8.91M
}
447
448
static void fmap_aging(fmap_t *m)
449
592M
{
450
#ifdef ANONYMOUS_MAP
451
    if (!m->aging) return;
452
    if (m->paged * m->pgsz > UNPAGE_THRSHLD_HI) { /* we alloc'd too much */
453
        uint64_t i, avail = 0, freeme[2048], maxavail = MIN(sizeof(freeme) / sizeof(*freeme), m->paged - UNPAGE_THRSHLD_LO / m->pgsz) - 1;
454
455
        for (i = 0; i < m->pages; i++) {
456
            uint64_t s = fmap_bitmap[i];
457
            if ((s & (FM_MASK_PAGED | FM_MASK_LOCKED)) == FM_MASK_PAGED) {
458
                /* page is paged and not locked: dec age */
459
                if (s & FM_MASK_COUNT) fmap_bitmap[i]--;
460
                /* and make it available for unpaging */
461
462
                if (!avail) {
463
                    freeme[0] = i;
464
                    avail++;
465
                } else {
466
                    /* Insert sort onto a stack'd array - same performance as quickselect */
467
                    uint64_t insert_to = MIN(maxavail, avail) - 1, age = fmap_bitmap[i] & FM_MASK_COUNT;
468
                    if (avail <= maxavail || (fmap_bitmap[freeme[maxavail]] & FM_MASK_COUNT) > age) {
469
                        while ((fmap_bitmap[freeme[insert_to]] & FM_MASK_COUNT) > age) {
470
                            freeme[insert_to + 1] = freeme[insert_to];
471
                            if (!insert_to--) break;
472
                        }
473
                        freeme[insert_to + 1] = i;
474
                        if (avail <= maxavail) avail++;
475
                    }
476
                }
477
            }
478
        }
479
        if (avail) { /* at least one page is paged and not locked */
480
            char *lastpage  = NULL;
481
            char *firstpage = NULL;
482
            for (i = 0; i < avail; i++) {
483
                char *pptr = (char *)m->data + freeme[i] * m->pgsz;
484
                /* we mark the page as seen */
485
                fmap_bitmap[freeme[i]] = FM_MASK_SEEN;
486
                /* and we mmap the page over so the kernel knows there's nothing good in there */
487
                /* reduce number of mmap calls: if pages are adjacent only do 1 mmap call */
488
                if (lastpage && pptr == lastpage) {
489
                    lastpage = pptr + m->pgsz;
490
                    continue;
491
                }
492
                if (!lastpage) {
493
                    firstpage = pptr;
494
                    lastpage  = pptr + m->pgsz;
495
                    continue;
496
                }
497
                fmap_lock;
498
                if (mmap(firstpage, lastpage - firstpage, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | ANONYMOUS_MAP, -1, 0) == MAP_FAILED)
499
                    cli_dbgmsg("fmap_aging: kernel hates you\n");
500
                fmap_unlock;
501
                firstpage = pptr;
502
                lastpage  = pptr + m->pgsz;
503
            }
504
            if (lastpage) {
505
                fmap_lock;
506
                if (mmap(firstpage, lastpage - firstpage, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | ANONYMOUS_MAP, -1, 0) == MAP_FAILED)
507
                    cli_dbgmsg("fmap_aging: kernel hates you\n");
508
                fmap_unlock;
509
            }
510
            m->paged -= avail;
511
        }
512
    }
513
#else
514
592M
    UNUSEDPARAM(m);
515
592M
#endif
516
592M
}
517
518
static int fmap_readpage(fmap_t *m, uint64_t first_page, uint64_t count, uint64_t lock_count)
519
592M
{
520
592M
    size_t readsz = 0, eintr_off;
521
592M
    char *pptr    = NULL, errtxt[256];
522
592M
    uint64_t sbitmap;
523
592M
    uint64_t i, page = first_page, force_read = 0;
524
525
592M
    if ((uint64_t)(m->real_len) > (uint64_t)(m->pages * m->pgsz)) {
526
0
        cli_dbgmsg("fmap_readpage: size of file exceeds total prefaultible page size (unpacked file is too large)\n");
527
0
        return 1;
528
0
    }
529
530
592M
    fmap_lock;
531
1.24G
    for (i = 0; i < count; i++) { /* prefault */
532
        /* Not worth checking if the page is already paged, just ping each */
533
        /* Also not worth reusing the loop below */
534
655M
        volatile char faultme;
535
655M
        faultme = ((char *)m->data)[(first_page + i) * m->pgsz];
536
655M
        (void)faultme; // silence "warning: variable ‘faultme’ set but not used"
537
655M
    }
538
592M
    fmap_unlock;
539
1.25G
    for (i = 0; i <= count; i++, page++) {
540
1.24G
        int lock;
541
1.24G
        if (lock_count) {
542
12.8M
            lock_count--;
543
12.8M
            lock = 1;
544
12.8M
        } else
545
1.23G
            lock = 0;
546
1.24G
        if (i == count) {
547
            /* we count one page too much to flush pending reads */
548
592M
            if (!pptr) return 0; /* if we have any */
549
8.36M
            force_read = 1;
550
655M
        } else if ((sbitmap = fmap_bitmap[page]) & FM_MASK_PAGED) {
551
            /* page already paged */
552
634M
            if (lock) {
553
                /* we want locking */
554
12.8M
                if (sbitmap & FM_MASK_LOCKED) {
555
                    /* page already locked */
556
10.0M
                    sbitmap &= FM_MASK_COUNT;
557
10.0M
                    if (sbitmap == FM_MASK_COUNT) { /* lock count already at max: fial! */
558
0
                        cli_errmsg("fmap_readpage: lock count exceeded\n");
559
0
                        return 1;
560
0
                    }
561
                    /* acceptable lock count: inc lock count */
562
10.0M
                    fmap_bitmap[page]++;
563
10.0M
                } else /* page not currently locked: set lock count = 1 */
564
2.74M
                    fmap_bitmap[page] = 1 | FM_MASK_LOCKED | FM_MASK_PAGED;
565
621M
            } else {
566
                /* we don't want locking */
567
621M
                if (!(sbitmap & FM_MASK_LOCKED)) {
568
                    /* page is not locked: we reset aging to max */
569
567M
                    fmap_bitmap[page] = FM_MASK_PAGED | FM_MASK_COUNT;
570
567M
                }
571
621M
            }
572
634M
            if (!pptr) continue;
573
15.6k
            force_read = 1;
574
15.6k
        }
575
576
29.5M
        if (force_read) {
577
            /* we have some pending reads to perform */
578
8.37M
            if (m->handle_is_fd) {
579
8.37M
                uint64_t j;
580
8.37M
                int _fd = (int)(ptrdiff_t)m->handle;
581
29.5M
                for (j = first_page; j < page; j++) {
582
21.1M
                    if (fmap_bitmap[j] & FM_MASK_SEEN) {
583
                        /* page we've seen before: check mtime */
584
1.33k
                        STATBUF st;
585
1.33k
                        if (FSTAT(_fd, &st)) {
586
0
                            cli_strerror(errno, errtxt, sizeof(errtxt));
587
0
                            cli_warnmsg("fmap_readpage: fstat failed: %s\n", errtxt);
588
0
                            return 1;
589
0
                        }
590
1.33k
                        if (m->mtime != st.st_mtime) {
591
0
                            cli_warnmsg("fmap_readpage: file changed as we read it\n");
592
0
                            return 1;
593
0
                        }
594
1.33k
                        break;
595
1.33k
                    }
596
21.1M
                }
597
8.37M
            }
598
599
8.37M
            eintr_off = 0;
600
16.7M
            while (readsz) {
601
8.37M
                ssize_t got;
602
8.37M
                uint64_t target_offset = eintr_off + m->offset + (first_page * m->pgsz);
603
8.37M
                got                    = m->pread_cb(m->handle, pptr, readsz, target_offset);
604
605
8.37M
                if (got < 0 && errno == EINTR)
606
0
                    continue;
607
608
8.37M
                if (got > 0) {
609
8.37M
                    pptr += got;
610
8.37M
                    eintr_off += got;
611
8.37M
                    readsz -= got;
612
8.37M
                    continue;
613
8.37M
                }
614
615
15
                if (got < 0) {
616
15
                    cli_strerror(errno, errtxt, sizeof(errtxt));
617
15
                    cli_errmsg("fmap_readpage: pread error: %s\n", errtxt);
618
15
                } else {
619
0
                    cli_warnmsg("fmap_readpage: pread fail: asked for %zu bytes @ offset " STDu64 ", got %zd\n", readsz, target_offset, got);
620
0
                }
621
15
                return 1;
622
8.37M
            }
623
624
8.37M
            pptr       = NULL;
625
8.37M
            force_read = 0;
626
8.37M
            readsz     = 0;
627
8.37M
            continue;
628
8.37M
        }
629
630
        /* page is not already paged */
631
21.1M
        if (!pptr) {
632
            /* set a new start for pending reads if we don't have one */
633
8.37M
            pptr       = (char *)m->data + page * m->pgsz;
634
8.37M
            first_page = page;
635
8.37M
        }
636
21.1M
        if ((page == m->pages - 1) && (m->real_len % m->pgsz))
637
7.60M
            readsz += m->real_len % m->pgsz;
638
13.5M
        else
639
13.5M
            readsz += m->pgsz;
640
21.1M
        if (lock) /* lock requested: set paged, lock page and set lock count to 1 */
641
1.33k
            fmap_bitmap[page] = FM_MASK_PAGED | FM_MASK_LOCKED | 1;
642
21.1M
        else /* no locking: set paged and set aging to max */
643
21.1M
            fmap_bitmap[page] = FM_MASK_PAGED | FM_MASK_COUNT;
644
21.1M
        m->paged++;
645
21.1M
    }
646
8.36M
    return 0;
647
592M
}
648
649
static const void *handle_need(fmap_t *m, size_t at, size_t len, int lock)
650
587M
{
651
587M
    uint64_t first_page, last_page, lock_count;
652
587M
    char *ret;
653
654
587M
    if (!len)
655
3.01M
        return NULL;
656
657
584M
    at += m->nested_offset;
658
584M
    if (!CLI_ISCONTAINED(m->nested_offset, m->len, at, len))
659
3.71M
        return NULL;
660
661
580M
    fmap_aging(m);
662
663
580M
    first_page = fmap_which_page(m, at);
664
580M
    last_page  = fmap_which_page(m, at + len - 1);
665
580M
    lock_count = (lock != 0) * (last_page - first_page + 1);
666
#ifdef READAHED_PAGES
667
    last_page += READAHED_PAGES;
668
    if (last_page >= m->pages) last_page = m->pages - 1;
669
#endif
670
671
580M
    if (fmap_readpage(m, first_page, last_page - first_page + 1, lock_count))
672
15
        return NULL;
673
674
580M
    ret = (char *)m->data + at;
675
580M
    return (void *)ret;
676
580M
}
677
678
static void fmap_unneed_page(fmap_t *m, uint64_t page)
679
246k
{
680
246k
    uint64_t s = fmap_bitmap[page];
681
682
246k
    if ((s & (FM_MASK_PAGED | FM_MASK_LOCKED)) == (FM_MASK_PAGED | FM_MASK_LOCKED)) {
683
        /* page is paged and locked: check lock count */
684
246k
        s &= FM_MASK_COUNT;
685
246k
        if (s > 1) /* locked more than once: dec lock count */
686
214k
            fmap_bitmap[page]--;
687
32.1k
        else if (s == 1) /* only one lock left: unlock and begin aging */
688
32.1k
            fmap_bitmap[page] = FM_MASK_COUNT | FM_MASK_PAGED;
689
0
        else
690
0
            cli_errmsg("fmap_unneed: inconsistent map state\n");
691
246k
        return;
692
246k
    }
693
0
    cli_warnmsg("fmap_unneed: unneed on a unlocked page\n");
694
0
    return;
695
246k
}
696
697
static void handle_unneed_off(fmap_t *m, size_t at, size_t len)
698
3.45M
{
699
3.45M
    uint64_t i, first_page, last_page;
700
3.45M
    if (!m->aging) return;
701
0
    if (!len) {
702
0
        cli_warnmsg("fmap_unneed: attempted void unneed\n");
703
0
        return;
704
0
    }
705
706
0
    at += m->nested_offset;
707
0
    if (!CLI_ISCONTAINED(m->nested_offset, m->len, at, len)) {
708
0
        cli_warnmsg("fmap: attempted oof unneed\n");
709
0
        return;
710
0
    }
711
712
0
    first_page = fmap_which_page(m, at);
713
0
    last_page  = fmap_which_page(m, at + len - 1);
714
715
0
    for (i = first_page; i <= last_page; i++) {
716
0
        fmap_unneed_page(m, i);
717
0
    }
718
0
}
719
720
static void unmap_mmap(fmap_t *m)
721
0
{
722
#ifdef ANONYMOUS_MAP
723
    size_t len = m->pages * m->pgsz;
724
    fmap_lock;
725
    if (munmap((void *)m->data, len) == -1) /* munmap() failed */
726
        cli_warnmsg("funmap: unable to unmap memory segment at address: %p with length: %zu\n", (void *)m->data, len);
727
    fmap_unlock;
728
#else
729
0
    UNUSEDPARAM(m);
730
0
#endif
731
0
}
732
733
static void unmap_malloc(fmap_t *m)
734
56.9k
{
735
56.9k
    if (NULL != m) {
736
56.9k
        if (NULL != m->name) {
737
0
            free(m->name);
738
0
        }
739
56.9k
        free((void *)m);
740
56.9k
    }
741
56.9k
}
742
743
static const void *handle_need_offstr(fmap_t *m, size_t at, size_t len_hint)
744
7.05M
{
745
7.05M
    uint64_t i, first_page, last_page;
746
7.05M
    void *ptr;
747
748
7.05M
    at += m->nested_offset;
749
7.05M
    ptr = (void *)((char *)m->data + at);
750
751
7.05M
    if (!len_hint || len_hint > m->real_len - at)
752
6.71k
        len_hint = m->real_len - at;
753
754
7.05M
    if (!CLI_ISCONTAINED(m->nested_offset, m->len, at, len_hint))
755
2.48k
        return NULL;
756
757
7.05M
    fmap_aging(m);
758
759
7.05M
    first_page = fmap_which_page(m, at);
760
7.05M
    last_page  = fmap_which_page(m, at + len_hint - 1);
761
762
7.30M
    for (i = first_page; i <= last_page; i++) {
763
7.05M
        char *thispage = (char *)m->data + i * m->pgsz;
764
7.05M
        uint64_t scanat, scansz;
765
766
7.05M
        if (fmap_readpage(m, i, 1, 1)) {
767
0
            last_page = i - 1;
768
0
            break;
769
0
        }
770
7.05M
        if (i == first_page) {
771
7.05M
            scanat = at % m->pgsz;
772
7.05M
            scansz = MIN(len_hint, m->pgsz - scanat);
773
7.05M
        } else {
774
1.94k
            scanat = 0;
775
1.94k
            scansz = MIN(len_hint, m->pgsz);
776
1.94k
        }
777
7.05M
        len_hint -= scansz;
778
7.05M
        if (memchr(&thispage[scanat], 0, scansz))
779
6.80M
            return ptr;
780
7.05M
    }
781
492k
    for (i = first_page; i <= last_page; i++)
782
246k
        fmap_unneed_page(m, i);
783
246k
    return NULL;
784
7.05M
}
785
786
static const void *handle_gets(fmap_t *m, char *dst, size_t *at, size_t max_len)
787
5.23M
{
788
5.23M
    uint64_t i, first_page, last_page;
789
5.23M
    char *src     = (char *)m->data + m->nested_offset + *at;
790
5.23M
    char *endptr  = NULL;
791
5.23M
    size_t len    = MIN(max_len - 1, m->len - *at);
792
5.23M
    size_t fullen = len;
793
794
5.23M
    if (!len || !CLI_ISCONTAINED_0_TO(m->len, *at, len))
795
109k
        return NULL;
796
797
5.12M
    fmap_aging(m);
798
799
5.12M
    first_page = fmap_which_page(m, m->nested_offset + *at);
800
5.12M
    last_page  = fmap_which_page(m, m->nested_offset + *at + len - 1);
801
802
5.24M
    for (i = first_page; i <= last_page; i++) {
803
5.14M
        char *thispage = (char *)m->data + i * m->pgsz;
804
5.14M
        uint64_t scanat, scansz;
805
806
5.14M
        if (fmap_readpage(m, i, 1, 0))
807
0
            return NULL;
808
809
5.14M
        if (i == first_page) {
810
5.12M
            scanat = (m->nested_offset + *at) % m->pgsz;
811
5.12M
            scansz = MIN(len, m->pgsz - scanat);
812
5.12M
        } else {
813
18.4k
            scanat = 0;
814
18.4k
            scansz = MIN(len, m->pgsz);
815
18.4k
        }
816
5.14M
        len -= scansz;
817
818
5.14M
        if ((endptr = memchr(&thispage[scanat], '\n', scansz))) {
819
5.01M
            endptr++;
820
5.01M
            break;
821
5.01M
        }
822
5.14M
    }
823
5.12M
    if (endptr) {
824
5.01M
        memcpy(dst, src, endptr - src);
825
5.01M
        dst[endptr - src] = '\0';
826
5.01M
        *at += endptr - src;
827
5.01M
    } else {
828
106k
        memcpy(dst, src, fullen);
829
106k
        dst[fullen] = '\0';
830
106k
        *at += fullen;
831
106k
    }
832
5.12M
    return dst;
833
5.12M
}
834
835
/* vvvvv MEMORY STUFF BELOW vvvvv */
836
837
static const void *mem_need(fmap_t *m, size_t at, size_t len, int lock);
838
static void mem_unneed_off(fmap_t *m, size_t at, size_t len);
839
static const void *mem_need_offstr(fmap_t *m, size_t at, size_t len_hint);
840
static const void *mem_gets(fmap_t *m, char *dst, size_t *at, size_t max_len);
841
842
fmap_t *fmap_open_memory(const void *start, size_t len, const char *name)
843
56.9k
{
844
56.9k
    cl_error_t status = CL_ERROR;
845
846
56.9k
    int pgsz     = cli_getpagesize();
847
56.9k
    cl_fmap_t *m = cli_calloc(1, sizeof(*m));
848
56.9k
    if (!m) {
849
0
        cli_warnmsg("fmap: map allocation failed\n");
850
0
        goto done;
851
0
    }
852
56.9k
    m->data        = start;
853
56.9k
    m->len         = len;
854
56.9k
    m->real_len    = len;
855
56.9k
    m->pgsz        = pgsz;
856
56.9k
    m->pages       = fmap_align_items(len, pgsz);
857
56.9k
    m->unmap       = unmap_malloc;
858
56.9k
    m->need        = mem_need;
859
56.9k
    m->need_offstr = mem_need_offstr;
860
56.9k
    m->gets        = mem_gets;
861
56.9k
    m->unneed_off  = mem_unneed_off;
862
863
56.9k
    if (NULL != name) {
864
        /* Copy the name, if one is given */
865
0
        m->name = cli_strdup(name);
866
0
        if (NULL == m->name) {
867
0
            cli_warnmsg("fmap: failed to duplicate map name\n");
868
0
            goto done;
869
0
        }
870
0
    }
871
872
56.9k
    status = CL_SUCCESS;
873
874
56.9k
done:
875
56.9k
    if (CL_SUCCESS != status) {
876
0
        if (NULL != m) {
877
0
            if (NULL != m->name) {
878
0
                free(m->name);
879
0
            }
880
0
            free(m);
881
0
            m = NULL;
882
0
        }
883
0
    }
884
885
56.9k
    return m;
886
56.9k
}
887
888
extern cl_fmap_t *cl_fmap_open_memory(const void *start, size_t len)
889
47.5k
{
890
47.5k
    return (cl_fmap_t *)fmap_open_memory(start, len, NULL);
891
47.5k
}
892
893
static const void *mem_need(fmap_t *m, size_t at, size_t len, int lock)
894
58.0M
{
895
58.0M
    UNUSEDPARAM(lock);
896
58.0M
    if (!len) {
897
39.2k
        return NULL;
898
39.2k
    }
899
58.0M
    at += m->nested_offset;
900
58.0M
    if (!CLI_ISCONTAINED(m->nested_offset, m->len, at, len)) {
901
572k
        return NULL;
902
572k
    }
903
904
57.4M
    return (void *)((char *)m->data + at);
905
58.0M
}
906
907
static void mem_unneed_off(fmap_t *m, size_t at, size_t len)
908
100k
{
909
100k
    UNUSEDPARAM(m);
910
100k
    UNUSEDPARAM(at);
911
100k
    UNUSEDPARAM(len);
912
100k
}
913
914
static const void *mem_need_offstr(fmap_t *m, size_t at, size_t len_hint)
915
41.4k
{
916
41.4k
    char *ptr;
917
918
41.4k
    at += m->nested_offset;
919
41.4k
    ptr = (char *)m->data + at;
920
921
41.4k
    if (!len_hint || len_hint > m->real_len - at)
922
90
        len_hint = m->real_len - at;
923
924
41.4k
    if (!CLI_ISCONTAINED(m->nested_offset, m->len, at, len_hint))
925
9
        return NULL;
926
927
41.4k
    if (memchr(ptr, 0, len_hint))
928
39.8k
        return (void *)ptr;
929
1.55k
    return NULL;
930
41.4k
}
931
932
static const void *mem_gets(fmap_t *m, char *dst, size_t *at, size_t max_len)
933
753k
{
934
753k
    char *src    = (char *)m->data + m->nested_offset + *at;
935
753k
    char *endptr = NULL;
936
753k
    size_t len   = MIN(max_len - 1, m->len - *at);
937
938
753k
    if (!len || !CLI_ISCONTAINED_0_TO(m->len, *at, len))
939
16.3k
        return NULL;
940
941
737k
    if ((endptr = memchr(src, '\n', len))) {
942
715k
        endptr++;
943
715k
        memcpy(dst, src, endptr - src);
944
715k
        dst[endptr - src] = '\0';
945
715k
        *at += endptr - src;
946
715k
    } else {
947
21.7k
        memcpy(dst, src, len);
948
21.7k
        dst[len] = '\0';
949
21.7k
        *at += len;
950
21.7k
    }
951
737k
    return dst;
952
753k
}
953
954
fmap_t *fmap(int fd, off_t offset, size_t len, const char *name)
955
8.12M
{
956
8.12M
    int unused;
957
8.12M
    return fmap_check_empty(fd, offset, len, &unused, name);
958
8.12M
}
959
960
static inline uint64_t fmap_align_items(uint64_t sz, uint64_t al)
961
17.8M
{
962
17.8M
    return sz / al + (sz % al != 0);
963
17.8M
}
964
965
static inline uint64_t fmap_align_to(uint64_t sz, uint64_t al)
966
8.91M
{
967
8.91M
    return al * fmap_align_items(sz, al);
968
8.91M
}
969
970
static inline uint64_t fmap_which_page(fmap_t *m, size_t at)
971
1.18G
{
972
1.18G
    return at / m->pgsz;
973
1.18G
}
974
975
cl_error_t fmap_dump_to_file(fmap_t *map, const char *filepath, const char *tmpdir, char **outname, int *outfd, size_t start_offset, size_t end_offset)
976
552k
{
977
552k
    cl_error_t ret = CL_EARG;
978
979
552k
    char *filebase = NULL;
980
552k
    char *prefix   = NULL;
981
982
552k
    char *tmpname = NULL;
983
552k
    int tmpfd     = -1;
984
985
552k
    size_t pos = 0, len = 0, bytes_remaining = 0, write_size = 0;
986
987
552k
    if ((start_offset > map->real_len) || (end_offset < start_offset)) {
988
0
        cli_dbgmsg("fmap_dump_to_file: Invalid offset arguments: start %zu, end %zu\n", start_offset, end_offset);
989
0
        return ret;
990
0
    }
991
992
552k
    pos             = start_offset;
993
552k
    end_offset      = MIN(end_offset, map->real_len);
994
552k
    bytes_remaining = end_offset - start_offset;
995
996
    /* Create a filename prefix that includes the original filename, if available */
997
552k
    if (filepath != NULL) {
998
0
        if (CL_SUCCESS != cli_basename(filepath, strlen(filepath), &filebase)) {
999
0
            cli_dbgmsg("fmap_dump_to_file: Unable to determine basename from filepath.\n");
1000
0
        } else if ((start_offset != 0) && (end_offset != map->real_len)) {
1001
            /* If we're only dumping a portion of the file, include the offsets in the prefix,...
1002
             * e.g. tmp filename will become something like:  filebase.500-1200.<randhex> */
1003
0
            size_t prefix_len = strlen(filebase) + 1 + SIZE_T_CHARLEN + 1 + SIZE_T_CHARLEN + 1;
1004
0
            prefix            = malloc(prefix_len);
1005
0
            if (NULL == prefix) {
1006
0
                cli_errmsg("fmap_dump_to_file: Failed to allocate memory for tempfile prefix.\n");
1007
0
                free(filebase);
1008
0
                return CL_EMEM;
1009
0
            }
1010
0
            snprintf(prefix, prefix_len, "%s.%zu-%zu", filebase, start_offset, end_offset);
1011
1012
0
            free(filebase);
1013
0
            filebase = NULL;
1014
0
        } else {
1015
            /* Else if we're dumping the whole thing, use the filebase as the prefix */
1016
0
            prefix   = filebase;
1017
0
            filebase = NULL;
1018
0
        }
1019
0
    }
1020
1021
552k
    cli_dbgmsg("fmap_dump_to_file: dumping fmap not backed by file...\n");
1022
552k
    ret = cli_gentempfd_with_prefix(tmpdir, prefix, &tmpname, &tmpfd);
1023
552k
    if (ret != CL_SUCCESS) {
1024
0
        cli_dbgmsg("fmap_dump_to_file: failed to generate temporary file.\n");
1025
0
        if (NULL != prefix) {
1026
0
            free(prefix);
1027
0
            prefix = NULL;
1028
0
        }
1029
0
        return ret;
1030
0
    }
1031
1032
552k
    if (NULL != prefix) {
1033
0
        free(prefix);
1034
0
        prefix = NULL;
1035
0
    }
1036
1037
1.26M
    do {
1038
1.26M
        const char *b;
1039
1.26M
        len        = 0;
1040
1.26M
        write_size = MIN(BUFSIZ, bytes_remaining);
1041
1042
1.26M
        b = fmap_need_off_once_len(map, pos, write_size, &len);
1043
1.26M
        pos += len;
1044
1.26M
        if (b && (len > 0)) {
1045
721k
            if (cli_writen(tmpfd, b, len) != len) {
1046
0
                cli_warnmsg("fmap_dump_to_file: write failed to %s!\n", tmpname);
1047
0
                close(tmpfd);
1048
0
                unlink(tmpname);
1049
0
                free(tmpname);
1050
0
                return CL_EWRITE;
1051
0
            }
1052
721k
        }
1053
1.26M
        if (len <= bytes_remaining) {
1054
1.26M
            bytes_remaining -= len;
1055
1.26M
        } else {
1056
0
            bytes_remaining = 0;
1057
0
        }
1058
1.26M
    } while ((len > 0) && (bytes_remaining > 0));
1059
1060
552k
    if (lseek(tmpfd, 0, SEEK_SET) == -1) {
1061
0
        cli_dbgmsg("fmap_dump_to_file: lseek failed\n");
1062
0
    }
1063
1064
552k
    *outname = tmpname;
1065
552k
    *outfd   = tmpfd;
1066
552k
    return CL_SUCCESS;
1067
552k
}
1068
1069
int fmap_fd(fmap_t *m)
1070
37.5M
{
1071
37.5M
    int fd;
1072
37.5M
    if (NULL == m) {
1073
0
        cli_errmsg("fmap_fd: Attempted to get fd for NULL fmap\n");
1074
0
        return -1;
1075
0
    }
1076
37.5M
    if (!m->handle_is_fd) {
1077
4.70M
        return -1;
1078
4.70M
    }
1079
32.8M
    fd = (int)(ptrdiff_t)m->handle;
1080
32.8M
    lseek(fd, 0, SEEK_SET);
1081
32.8M
    return fd;
1082
37.5M
}
1083
1084
extern void cl_fmap_close(cl_fmap_t *map)
1085
47.5k
{
1086
47.5k
    funmap(map);
1087
47.5k
}
1088
1089
cl_error_t fmap_set_hash(fmap_t *map, unsigned char *hash, cli_hash_type_t type)
1090
0
{
1091
0
    cl_error_t status = CL_SUCCESS;
1092
1093
0
    if (NULL == map) {
1094
0
        cli_errmsg("fmap_set_hash: Attempted to set hash for NULL fmap\n");
1095
0
        status = CL_EARG;
1096
0
        goto done;
1097
0
    }
1098
0
    if (NULL == hash) {
1099
0
        cli_errmsg("fmap_set_hash: Attempted to set hash to NULL\n");
1100
0
        status = CL_EARG;
1101
0
        goto done;
1102
0
    }
1103
1104
0
    switch (type) {
1105
0
        case CLI_HASH_MD5:
1106
0
            memcpy(map->md5, hash, CLI_HASHLEN_MD5);
1107
0
            map->have_md5 = true;
1108
0
            break;
1109
0
        case CLI_HASH_SHA1:
1110
0
            memcpy(map->sha1, hash, CLI_HASHLEN_SHA1);
1111
0
            map->have_sha1 = true;
1112
0
            break;
1113
0
        case CLI_HASH_SHA256:
1114
0
            memcpy(map->sha256, hash, CLI_HASHLEN_SHA256);
1115
0
            map->have_sha256 = true;
1116
0
            break;
1117
0
        default:
1118
0
            cli_errmsg("fmap_set_hash: Unsupported hash type %u\n", type);
1119
0
            status = CL_EARG;
1120
0
            goto done;
1121
0
    }
1122
1123
0
done:
1124
0
    return status;
1125
0
}
1126
1127
cl_error_t fmap_get_hash(fmap_t *map, unsigned char **hash, cli_hash_type_t type)
1128
44.1M
{
1129
44.1M
    cl_error_t status = CL_ERROR;
1130
44.1M
    size_t todo, at = 0;
1131
44.1M
    void *hashctx = NULL;
1132
1133
44.1M
    todo = map->len;
1134
1135
44.1M
    switch (type) {
1136
44.1M
        case CLI_HASH_MD5:
1137
44.1M
            if (map->have_md5) {
1138
31.3M
                goto complete;
1139
31.3M
            }
1140
12.7M
            break;
1141
12.7M
        case CLI_HASH_SHA1:
1142
0
            if (map->have_sha1) {
1143
0
                goto complete;
1144
0
            }
1145
0
            break;
1146
0
        case CLI_HASH_SHA256:
1147
0
            if (map->have_sha256) {
1148
0
                goto complete;
1149
0
            }
1150
0
            break;
1151
0
        default:
1152
0
            cli_errmsg("fmap_get_hash: Unsupported hash type %u\n", type);
1153
0
            status = CL_EARG;
1154
0
            goto done;
1155
44.1M
    }
1156
1157
    /*
1158
     * Need to calculate the hash.
1159
     */
1160
1161
12.7M
    switch (type) {
1162
12.7M
        case CLI_HASH_MD5:
1163
12.7M
            hashctx = cl_hash_init("md5");
1164
12.7M
            break;
1165
0
        case CLI_HASH_SHA1:
1166
0
            hashctx = cl_hash_init("sha1");
1167
0
            break;
1168
0
        case CLI_HASH_SHA256:
1169
0
            hashctx = cl_hash_init("sha256");
1170
0
            break;
1171
0
        default:
1172
0
            cli_errmsg("fmap_get_hash: Unsupported hash type %u\n", type);
1173
0
            status = CL_EARG;
1174
0
            goto done;
1175
12.7M
    }
1176
12.7M
    if (!(hashctx)) {
1177
0
        cli_errmsg("fmap_get_hash: error initializing new md5 hash!\n");
1178
0
        goto done;
1179
0
    }
1180
1181
25.4M
    while (todo) {
1182
12.7M
        const void *buf;
1183
12.7M
        size_t readme = todo < 1024 * 1024 * 10 ? todo : 1024 * 1024 * 10;
1184
1185
12.7M
        if (!(buf = fmap_need_off_once(map, at, readme))) {
1186
0
            cli_errmsg("fmap_get_hash: error reading while generating hash!\n");
1187
0
            status = CL_EREAD;
1188
0
            goto done;
1189
0
        }
1190
1191
12.7M
        todo -= readme;
1192
12.7M
        at += readme;
1193
1194
12.7M
        if (cl_update_hash(hashctx, (void *)buf, readme)) {
1195
0
            cli_errmsg("fmap_get_hash: error calculating hash!\n");
1196
0
            status = CL_EREAD;
1197
0
            goto done;
1198
0
        }
1199
12.7M
    }
1200
1201
12.7M
    switch (type) {
1202
12.7M
        case CLI_HASH_MD5:
1203
12.7M
            cl_finish_hash(hashctx, map->md5);
1204
12.7M
            map->have_md5 = true;
1205
12.7M
            break;
1206
0
        case CLI_HASH_SHA1:
1207
0
            cl_finish_hash(hashctx, map->sha1);
1208
0
            map->have_sha1 = true;
1209
0
            break;
1210
0
        case CLI_HASH_SHA256:
1211
0
            cl_finish_hash(hashctx, map->sha256);
1212
0
            map->have_sha256 = true;
1213
0
            break;
1214
0
        default:
1215
0
            cli_errmsg("fmap_get_hash: Unsupported hash type %u\n", type);
1216
0
            status = CL_EARG;
1217
0
            goto done;
1218
12.7M
    }
1219
12.7M
    hashctx = NULL;
1220
1221
44.1M
complete:
1222
1223
44.1M
    switch (type) {
1224
44.1M
        case CLI_HASH_MD5:
1225
44.1M
            *hash = map->md5;
1226
44.1M
            break;
1227
0
        case CLI_HASH_SHA1:
1228
0
            *hash = map->sha1;
1229
0
            break;
1230
0
        case CLI_HASH_SHA256:
1231
0
            *hash = map->sha256;
1232
0
            break;
1233
0
        default:
1234
0
            cli_errmsg("fmap_get_hash: Unsupported hash type %u\n", type);
1235
0
            status = CL_EARG;
1236
0
            goto done;
1237
44.1M
    }
1238
1239
44.1M
    status = CL_SUCCESS;
1240
1241
44.1M
done:
1242
1243
44.1M
    if (NULL != hashctx) {
1244
0
        cl_hash_destroy(hashctx);
1245
0
    }
1246
1247
44.1M
    return status;
1248
44.1M
}