]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/mmap-cache.c
journal: implement basic journal file verification logic
[thirdparty/systemd.git] / src / journal / mmap-cache.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <assert.h>
23 #include <sys/mman.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "util.h"
29
30 #include "mmap-cache.h"
31
32 #define WINDOW_SIZE (8ULL*1024ULL*1024ULL)
33 #define WINDOWS_MAX 32
34
35 typedef struct Window {
36 int fd;
37 void *ptr;
38 uint64_t offset;
39 uint64_t size;
40
41 unsigned n_ref;
42 unsigned lru_prev;
43 unsigned lru_next;
44
45 unsigned by_fd_prev;
46 unsigned by_fd_next;
47 } Window;
48
49 typedef struct FileDescriptor {
50 int fd;
51 unsigned windows;
52 } FileDescriptor;
53
54 struct MMapCache {
55 unsigned n_ref;
56
57 unsigned contexts_max;
58 unsigned windows_max;
59 unsigned fds_max;
60
61 unsigned n_windows;
62 unsigned n_fds;
63
64 unsigned lru_first, lru_last;
65
66 Window *windows;
67 unsigned *by_context;
68 FileDescriptor *by_fd;
69 };
70
71 static void mmap_cache_window_unmap(MMapCache *m, unsigned w) {
72 Window *v;
73
74 assert(m);
75 assert(w < m->n_windows);
76
77 v = m->windows + w;
78 if (!v->ptr)
79 return;
80
81 munmap(v->ptr, v->size);
82 v->ptr = NULL;
83 }
84
85 static void mmap_cache_window_add_lru(MMapCache *m, unsigned w) {
86 Window *v;
87
88 assert(m);
89 assert(w < m->n_windows);
90
91 v = m->windows + w;
92 v->lru_prev = m->lru_last;
93 v->lru_next = (unsigned) -1;
94
95 m->lru_last = w;
96 if (m->lru_first == (unsigned) -1)
97 m->lru_first = w;
98 }
99
100 static void mmap_cache_window_remove_lru(MMapCache *m, unsigned w) {
101 Window *v;
102
103 assert(m);
104 assert(w < m->n_windows);
105
106 v = m->windows + w;
107
108 if (v->lru_prev == (unsigned) -1)
109 m->lru_first = v->lru_next;
110 else
111 m->windows[v->lru_prev].lru_next = v->lru_next;
112
113 if (v->lru_next == (unsigned) -1)
114 m->lru_last = v->lru_prev;
115 else
116 m->windows[v->lru_next].lru_prev = v->lru_prev;
117 }
118
119 static void mmap_cache_fd_add(MMapCache *m, unsigned fd_index, unsigned w) {
120 Window *v;
121
122 assert(m);
123 assert(fd_index < m->n_fds);
124
125 v = m->windows + w;
126 v->by_fd_next = m->by_fd[fd_index].windows;
127 v->by_fd_prev = (unsigned) -1;
128
129 m->by_fd[fd_index].windows = w;
130 }
131
132 static void mmap_cache_fd_remove(MMapCache *m, unsigned fd_index, unsigned w) {
133 Window *v;
134
135 assert(m);
136 assert(fd_index < m->n_fds);
137
138 v = m->windows + w;
139 if (v->by_fd_prev == (unsigned) -1)
140 m->by_fd[fd_index].windows = v->by_fd_next;
141 else
142 m->windows[v->by_fd_prev].by_fd_next = v->by_fd_next;
143
144 if (v->by_fd_next != (unsigned) -1)
145 m->windows[v->by_fd_next].by_fd_prev = v->by_fd_prev;
146 }
147
148 static void mmap_cache_context_unset(MMapCache *m, unsigned c) {
149 Window *v;
150 unsigned w;
151
152 assert(m);
153 assert(c < m->contexts_max);
154
155 if (m->by_context[c] == (unsigned) -1)
156 return;
157
158 w = m->by_context[c];
159 m->by_context[c] = (unsigned) -1;
160
161 v = m->windows + w;
162 assert(v->n_ref > 0);
163 v->n_ref --;
164
165 if (v->n_ref == 0)
166 mmap_cache_window_add_lru(m, w);
167 }
168
169 static void mmap_cache_context_set(MMapCache *m, unsigned c, unsigned w) {
170 Window *v;
171
172 assert(m);
173 assert(c < m->contexts_max);
174 assert(w < m->n_windows);
175
176 if (m->by_context[c] == w)
177 return;
178
179 mmap_cache_context_unset(m, c);
180
181 m->by_context[c] = w;
182
183 v = m->windows + w;
184 v->n_ref ++;
185 if (v->n_ref == 1)
186 mmap_cache_window_remove_lru(m, w);
187 }
188
189 static void mmap_cache_free(MMapCache *m) {
190
191 assert(m);
192
193 if (m->windows) {
194 unsigned w;
195
196 for (w = 0; w < m->n_windows; w++)
197 mmap_cache_window_unmap(m, w);
198
199 free(m->windows);
200 }
201
202 free(m->by_context);
203 free(m->by_fd);
204 free(m);
205 }
206
207 MMapCache* mmap_cache_new(unsigned contexts_max, unsigned fds_max) {
208 MMapCache *m;
209
210 assert(contexts_max > 0);
211 assert(fds_max > 0);
212
213 m = new0(MMapCache, 1);
214 if (!m)
215 return NULL;
216
217 m->contexts_max = contexts_max;
218 m->fds_max = fds_max;
219 m->windows_max = MAX(m->contexts_max, WINDOWS_MAX);
220 m->n_ref = 1;
221 m->lru_first = (unsigned) -1;
222 m->lru_last = (unsigned) -1;
223
224 m->windows = new(Window, m->windows_max);
225 if (!m->windows) {
226 mmap_cache_free(m);
227 return NULL;
228 }
229
230 m->by_context = new(unsigned, m->contexts_max);
231 if (!m->by_context) {
232 mmap_cache_free(m);
233 return NULL;
234 }
235 memset(m->by_context, -1, m->contexts_max * sizeof(unsigned));
236
237 m->by_fd = new(FileDescriptor, m->fds_max);
238 if (!m->by_fd) {
239 mmap_cache_free(m);
240 return NULL;
241 }
242
243 return m;
244 }
245
246 MMapCache* mmap_cache_ref(MMapCache *m) {
247 assert(m);
248 assert(m->n_ref > 0);
249
250 m->n_ref++;
251 return m;
252 }
253
254 MMapCache* mmap_cache_unref(MMapCache *m) {
255 assert(m);
256 assert(m->n_ref > 0);
257
258 if (m->n_ref == 1)
259 mmap_cache_free(m);
260 else
261 m->n_ref--;
262
263 return NULL;
264 }
265
266 static int mmap_cache_allocate_window(MMapCache *m, unsigned *w) {
267 assert(m);
268 assert(w);
269
270 if (m->n_windows < m->windows_max) {
271 *w = m->n_windows ++;
272 return 0;
273 }
274
275 if (m->lru_first == (unsigned) -1)
276 return -E2BIG;
277
278 *w = m->lru_first;
279 mmap_cache_window_unmap(m, *w);
280 mmap_cache_window_remove_lru(m, *w);
281
282 return 0;
283 }
284
285 static int mmap_cache_make_room(MMapCache *m) {
286 unsigned w;
287
288 assert(m);
289
290 w = m->lru_first;
291 while (w != (unsigned) -1) {
292 Window *v;
293
294 v = m->windows + w;
295
296 if (v->ptr) {
297 mmap_cache_window_unmap(m, w);
298 return 1;
299 }
300
301 w = v->lru_next;
302 }
303
304 return 0;
305 }
306
307 static int mmap_cache_put(
308 MMapCache *m,
309 int fd,
310 unsigned fd_index,
311 int prot,
312 unsigned context,
313 uint64_t offset,
314 uint64_t size,
315 void **ret) {
316
317 unsigned w;
318 Window *v;
319 void *d;
320 uint64_t woffset, wsize;
321 int r;
322
323 assert(m);
324 assert(fd >= 0);
325 assert(context < m->contexts_max);
326 assert(size > 0);
327 assert(ret);
328
329 woffset = offset & ~((uint64_t) page_size() - 1ULL);
330 wsize = size + (offset - woffset);
331 wsize = PAGE_ALIGN(wsize);
332
333 if (wsize < WINDOW_SIZE) {
334 uint64_t delta;
335
336 delta = PAGE_ALIGN((WINDOW_SIZE - wsize) / 2);
337
338 if (delta > offset)
339 woffset = 0;
340 else
341 woffset -= delta;
342
343 wsize = WINDOW_SIZE;
344 }
345
346 for (;;) {
347 d = mmap(NULL, wsize, prot, MAP_SHARED, fd, woffset);
348 if (d != MAP_FAILED)
349 break;
350 if (errno != ENOMEM)
351 return -errno;
352
353 r = mmap_cache_make_room(m);
354 if (r < 0)
355 return r;
356 if (r == 0)
357 return -ENOMEM;
358 }
359
360 r = mmap_cache_allocate_window(m, &w);
361 if (r < 0) {
362 munmap(d, wsize);
363 return r;
364 }
365
366 v = m->windows + w;
367 v->fd = fd;
368 v->ptr = d;
369 v->offset = woffset;
370 v->size = wsize;
371
372 v->n_ref = 0;
373 v->lru_prev = v->lru_next = (unsigned) -1;
374
375 mmap_cache_fd_add(m, fd_index, w);
376 mmap_cache_context_set(m, context, w);
377
378 *ret = (uint8_t*) d + (offset - woffset);
379 return 1;
380 }
381
382 static int fd_cmp(const void *_a, const void *_b) {
383 const FileDescriptor *a = _a, *b = _b;
384
385 if (a->fd < b->fd)
386 return -1;
387 if (a->fd > b->fd)
388 return 1;
389
390 return 0;
391 }
392
393 static int mmap_cache_get_fd_index(MMapCache *m, int fd, unsigned *fd_index) {
394 FileDescriptor *j;
395
396 assert(m);
397 assert(fd >= 0);
398 assert(fd_index);
399
400 j = bsearch(&fd, m->by_fd, m->n_fds, sizeof(m->by_fd[0]), fd_cmp);
401 if (!j) {
402 if (m->n_fds >= m->fds_max)
403 return -E2BIG;
404
405 j = m->by_fd + m->n_fds ++;
406 j->fd = fd;
407 j->windows = (unsigned) -1;
408
409 qsort(m->by_fd, m->n_fds, sizeof(m->by_fd[0]), fd_cmp);
410 j = bsearch(&fd, m->by_fd, m->n_fds, sizeof(m->by_fd[0]), fd_cmp);
411 }
412
413 *fd_index = (unsigned) (j - m->by_fd);
414 return 0;
415 }
416
417 static bool mmap_cache_test_window(
418 MMapCache *m,
419 unsigned w,
420 uint64_t offset,
421 uint64_t size) {
422 Window *v;
423
424 assert(m);
425 assert(w < m->n_windows);
426 assert(size > 0);
427
428 v = m->windows + w;
429
430 return offset >= v->offset &&
431 offset + size <= v->offset + v->size;
432 }
433
434 static int mmap_cache_current(
435 MMapCache *m,
436 int fd,
437 unsigned context,
438 uint64_t offset,
439 uint64_t size,
440 void **ret) {
441
442 Window *v;
443 unsigned w;
444
445 assert(m);
446 assert(fd >= 0);
447 assert(context < m->contexts_max);
448 assert(size > 0);
449 assert(ret);
450
451 if (m->by_context[context] == (unsigned) -1)
452 return 0;
453
454 w = m->by_context[context];
455 v = m->windows + w;
456
457 if (v->fd != fd)
458 return 0;
459
460 if (!mmap_cache_test_window(m, w, offset, size))
461 return 0;
462
463 *ret = (uint8_t*) v->ptr + (offset - v->offset);
464 return 1;
465 }
466
467 static int mmap_cache_find(
468 MMapCache *m,
469 unsigned fd_index,
470 unsigned context,
471 uint64_t offset,
472 uint64_t size,
473 void **ret) {
474
475 Window *v = NULL;
476 unsigned w;
477
478 assert(m);
479 assert(fd_index < m->n_fds);
480 assert(context < m->contexts_max);
481 assert(size > 0);
482 assert(ret);
483
484 w = m->by_fd[fd_index].windows;
485 while (w != (unsigned) -1) {
486 if (mmap_cache_test_window(m, w, offset, size))
487 break;
488
489 w = m->windows[w].by_fd_next;
490 }
491
492 if (w == (unsigned) -1)
493 return 0;
494
495 mmap_cache_context_set(m, context, w);
496
497 v = m->windows + w;
498 *ret = (uint8_t*) v->ptr + (offset - v->offset);
499 return 1;
500 }
501
502 int mmap_cache_get(
503 MMapCache *m,
504 int fd,
505 int prot,
506 unsigned context,
507 uint64_t offset,
508 uint64_t size,
509 void **ret) {
510
511 unsigned fd_index;
512 int r;
513
514 assert(m);
515 assert(fd >= 0);
516 assert(context < m->contexts_max);
517 assert(size > 0);
518 assert(ret);
519
520 /* Maybe the current pointer for this context is already the
521 * right one? */
522 r = mmap_cache_current(m, fd, context, offset, size, ret);
523 if (r != 0)
524 return r;
525
526 /* OK, let's find the chain for this FD */
527 r = mmap_cache_get_fd_index(m, fd, &fd_index);
528 if (r < 0)
529 return r;
530
531 /* And let's look through the available mmaps */
532 r = mmap_cache_find(m, fd_index, context, offset, size, ret);
533 if (r != 0)
534 return r;
535
536 /* Not found? Then, let's add it */
537 return mmap_cache_put(m, fd, fd_index, prot, context, offset, size, ret);
538 }
539
540 void mmap_cache_close_fd(MMapCache *m, int fd) {
541 FileDescriptor *j;
542 unsigned fd_index, c, w;
543
544 assert(m);
545 assert(fd > 0);
546
547 j = bsearch(&fd, m->by_fd, m->n_fds, sizeof(m->by_fd[0]), fd_cmp);
548 if (!j)
549 return;
550 fd_index = (unsigned) (j - m->by_fd);
551
552 for (c = 0; c < m->contexts_max; c++) {
553 w = m->by_context[c];
554 if (w == (unsigned) -1)
555 continue;
556
557 if (m->windows[w].fd == fd)
558 mmap_cache_context_unset(m, c);
559 }
560
561 w = m->by_fd[fd_index].windows;
562 while (w != (unsigned) -1) {
563
564 mmap_cache_fd_remove(m, fd_index, w);
565 mmap_cache_window_unmap(m, w);
566
567 w = m->by_fd[fd_index].windows;
568 }
569
570 memmove(m->by_fd + fd_index, m->by_fd + fd_index + 1, (m->n_fds - (fd_index + 1)) * sizeof(FileDescriptor));
571 m->n_fds --;
572 }
573
574 void mmap_cache_close_context(MMapCache *m, unsigned context) {
575 mmap_cache_context_unset(m, context);
576 }