/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 | } |