]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-journal/test-journal-interleaving.c
Merge pull request #30380 from keszybz/tmpfiles-dry-run
[thirdparty/systemd.git] / src / libsystemd / sd-journal / test-journal-interleaving.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <fcntl.h>
4 #include <unistd.h>
5
6 #include "sd-id128.h"
7 #include "sd-journal.h"
8
9 #include "alloc-util.h"
10 #include "chattr-util.h"
11 #include "iovec-util.h"
12 #include "journal-file-util.h"
13 #include "journal-vacuum.h"
14 #include "log.h"
15 #include "logs-show.h"
16 #include "parse-util.h"
17 #include "random-util.h"
18 #include "rm-rf.h"
19 #include "tests.h"
20
21 /* This program tests skipping around in a multi-file journal. */
22
23 static bool arg_keep = false;
24 static dual_timestamp previous_ts = {};
25
26 _noreturn_ static void log_assert_errno(const char *text, int error, const char *file, unsigned line, const char *func) {
27 log_internal(LOG_CRIT, error, file, line, func,
28 "'%s' failed at %s:%u (%s): %m", text, file, line, func);
29 abort();
30 }
31
32 #define assert_ret(expr) \
33 do { \
34 int _r_ = (expr); \
35 if (_unlikely_(_r_ < 0)) \
36 log_assert_errno(#expr, -_r_, PROJECT_FILE, __LINE__, __func__); \
37 } while (false)
38
39 static JournalFile *test_open_internal(const char *name, JournalFileFlags flags) {
40 _cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
41 JournalFile *f;
42
43 m = mmap_cache_new();
44 assert_se(m != NULL);
45
46 assert_ret(journal_file_open(-1, name, O_RDWR|O_CREAT, flags, 0644, UINT64_MAX, NULL, m, NULL, &f));
47 return f;
48 }
49
50 static JournalFile *test_open(const char *name) {
51 return test_open_internal(name, JOURNAL_COMPRESS);
52 }
53
54 static JournalFile *test_open_strict(const char *name) {
55 return test_open_internal(name, JOURNAL_COMPRESS | JOURNAL_STRICT_ORDER);
56 }
57
58 static void test_close(JournalFile *f) {
59 (void) journal_file_offline_close(f);
60 }
61
62 static void test_done(const char *t) {
63 log_info("Done...");
64
65 if (arg_keep)
66 log_info("Not removing %s", t);
67 else {
68 journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
69
70 assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
71 }
72
73 log_info("------------------------------------------------------------");
74 }
75
76 static void append_number(JournalFile *f, int n, const sd_id128_t *boot_id, uint64_t *seqnum, uint64_t *ret_offset) {
77 _cleanup_free_ char *p = NULL, *q = NULL;
78 dual_timestamp ts;
79 struct iovec iovec[2];
80 size_t n_iov = 0;
81
82 dual_timestamp_now(&ts);
83
84 if (ts.monotonic <= previous_ts.monotonic)
85 ts.monotonic = previous_ts.monotonic + 1;
86
87 if (ts.realtime <= previous_ts.realtime)
88 ts.realtime = previous_ts.realtime + 1;
89
90 previous_ts = ts;
91
92 assert_se(asprintf(&p, "NUMBER=%d", n) >= 0);
93 iovec[n_iov++] = IOVEC_MAKE_STRING(p);
94
95 if (boot_id) {
96 assert_se(q = strjoin("_BOOT_ID=", SD_ID128_TO_STRING(*boot_id)));
97 iovec[n_iov++] = IOVEC_MAKE_STRING(q);
98 }
99
100 assert_ret(journal_file_append_entry(f, &ts, boot_id, iovec, n_iov, seqnum, NULL, NULL, ret_offset));
101 }
102
103 static void append_unreferenced_data(JournalFile *f, const sd_id128_t *boot_id) {
104 _cleanup_free_ char *q = NULL;
105 dual_timestamp ts;
106 struct iovec iovec;
107
108 assert(boot_id);
109
110 ts.monotonic = usec_sub_unsigned(previous_ts.monotonic, 10);
111 ts.realtime = usec_sub_unsigned(previous_ts.realtime, 10);
112
113 assert_se(q = strjoin("_BOOT_ID=", SD_ID128_TO_STRING(*boot_id)));
114 iovec = IOVEC_MAKE_STRING(q);
115
116 assert_se(journal_file_append_entry(f, &ts, boot_id, &iovec, 1, NULL, NULL, NULL, NULL) == -EREMCHG);
117 }
118
119 static void test_check_number(sd_journal *j, int n) {
120 sd_id128_t boot_id;
121 const void *d;
122 _cleanup_free_ char *k = NULL;
123 size_t l;
124 int x;
125
126 assert_se(sd_journal_get_monotonic_usec(j, NULL, &boot_id) >= 0);
127 assert_ret(sd_journal_get_data(j, "NUMBER", &d, &l));
128 assert_se(k = strndup(d, l));
129 printf("%s %s (expected=%i)\n", SD_ID128_TO_STRING(boot_id), k, n);
130
131 assert_se(safe_atoi(k + STRLEN("NUMBER="), &x) >= 0);
132 assert_se(n == x);
133 }
134
135 static void test_check_numbers_down(sd_journal *j, int count) {
136 int i;
137
138 for (i = 1; i <= count; i++) {
139 int r;
140 test_check_number(j, i);
141 assert_ret(r = sd_journal_next(j));
142 if (i == count)
143 assert_se(r == 0);
144 else
145 assert_se(r == 1);
146 }
147
148 }
149
150 static void test_check_numbers_up(sd_journal *j, int count) {
151 for (int i = count; i >= 1; i--) {
152 int r;
153 test_check_number(j, i);
154 assert_ret(r = sd_journal_previous(j));
155 if (i == 1)
156 assert_se(r == 0);
157 else
158 assert_se(r == 1);
159 }
160
161 }
162
163 static void setup_sequential(void) {
164 JournalFile *f1, *f2, *f3;
165 sd_id128_t id;
166
167 f1 = test_open("one.journal");
168 f2 = test_open("two.journal");
169 f3 = test_open("three.journal");
170 assert_se(sd_id128_randomize(&id) >= 0);
171 log_info("boot_id: %s", SD_ID128_TO_STRING(id));
172 append_number(f1, 1, &id, NULL, NULL);
173 append_number(f1, 2, &id, NULL, NULL);
174 append_number(f1, 3, &id, NULL, NULL);
175 append_number(f2, 4, &id, NULL, NULL);
176 assert_se(sd_id128_randomize(&id) >= 0);
177 log_info("boot_id: %s", SD_ID128_TO_STRING(id));
178 append_number(f2, 5, &id, NULL, NULL);
179 append_number(f2, 6, &id, NULL, NULL);
180 append_number(f3, 7, &id, NULL, NULL);
181 append_number(f3, 8, &id, NULL, NULL);
182 assert_se(sd_id128_randomize(&id) >= 0);
183 log_info("boot_id: %s", SD_ID128_TO_STRING(id));
184 append_number(f3, 9, &id, NULL, NULL);
185 test_close(f1);
186 test_close(f2);
187 test_close(f3);
188 }
189
190 static void setup_interleaved(void) {
191 JournalFile *f1, *f2, *f3;
192 sd_id128_t id;
193
194 f1 = test_open("one.journal");
195 f2 = test_open("two.journal");
196 f3 = test_open("three.journal");
197 assert_se(sd_id128_randomize(&id) >= 0);
198 log_info("boot_id: %s", SD_ID128_TO_STRING(id));
199 append_number(f1, 1, &id, NULL, NULL);
200 append_number(f2, 2, &id, NULL, NULL);
201 append_number(f3, 3, &id, NULL, NULL);
202 append_number(f1, 4, &id, NULL, NULL);
203 append_number(f2, 5, &id, NULL, NULL);
204 append_number(f3, 6, &id, NULL, NULL);
205 append_number(f1, 7, &id, NULL, NULL);
206 append_number(f2, 8, &id, NULL, NULL);
207 append_number(f3, 9, &id, NULL, NULL);
208 test_close(f1);
209 test_close(f2);
210 test_close(f3);
211 }
212
213 static void setup_unreferenced_data(void) {
214 JournalFile *f1, *f2, *f3;
215 sd_id128_t id;
216
217 /* For issue #29275. */
218
219 f1 = test_open_strict("one.journal");
220 f2 = test_open_strict("two.journal");
221 f3 = test_open_strict("three.journal");
222 assert_se(sd_id128_randomize(&id) >= 0);
223 log_info("boot_id: %s", SD_ID128_TO_STRING(id));
224 append_number(f1, 1, &id, NULL, NULL);
225 append_number(f1, 2, &id, NULL, NULL);
226 append_number(f1, 3, &id, NULL, NULL);
227 assert_se(sd_id128_randomize(&id) >= 0);
228 log_info("boot_id: %s", SD_ID128_TO_STRING(id));
229 append_unreferenced_data(f1, &id);
230 append_number(f2, 4, &id, NULL, NULL);
231 append_number(f2, 5, &id, NULL, NULL);
232 append_number(f2, 6, &id, NULL, NULL);
233 assert_se(sd_id128_randomize(&id) >= 0);
234 log_info("boot_id: %s", SD_ID128_TO_STRING(id));
235 append_unreferenced_data(f2, &id);
236 append_number(f3, 7, &id, NULL, NULL);
237 append_number(f3, 8, &id, NULL, NULL);
238 append_number(f3, 9, &id, NULL, NULL);
239 test_close(f1);
240 test_close(f2);
241 test_close(f3);
242 }
243
244 static void mkdtemp_chdir_chattr(char *path) {
245 assert_se(mkdtemp(path));
246 assert_se(chdir(path) >= 0);
247
248 /* Speed up things a bit on btrfs, ensuring that CoW is turned off for all files created in our
249 * directory during the test run */
250 (void) chattr_path(path, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
251 }
252
253 static void test_skip_one(void (*setup)(void)) {
254 char t[] = "/var/tmp/journal-skip-XXXXXX";
255 sd_journal *j;
256 int r;
257
258 mkdtemp_chdir_chattr(t);
259
260 setup();
261
262 /* Seek to head, iterate down. */
263 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
264 assert_ret(sd_journal_seek_head(j));
265 assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
266 test_check_numbers_down(j, 9);
267 sd_journal_close(j);
268
269 /* Seek to head, iterate down. */
270 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
271 assert_ret(sd_journal_seek_head(j));
272 assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
273 assert_se(sd_journal_previous(j) == 0); /* no-op */
274 test_check_numbers_down(j, 9);
275 sd_journal_close(j);
276
277 /* Seek to head twice, iterate down. */
278 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
279 assert_ret(sd_journal_seek_head(j));
280 assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
281 assert_ret(sd_journal_seek_head(j));
282 assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
283 test_check_numbers_down(j, 9);
284 sd_journal_close(j);
285
286 /* Seek to head, move to previous, then iterate down. */
287 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
288 assert_ret(sd_journal_seek_head(j));
289 assert_se(sd_journal_previous(j) == 0); /* no-op */
290 assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
291 test_check_numbers_down(j, 9);
292 sd_journal_close(j);
293
294 /* Seek to head, walk several steps, then iterate down. */
295 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
296 assert_ret(sd_journal_seek_head(j));
297 assert_se(sd_journal_previous(j) == 0); /* no-op */
298 assert_se(sd_journal_previous(j) == 0); /* no-op */
299 assert_se(sd_journal_previous(j) == 0); /* no-op */
300 assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
301 assert_se(sd_journal_previous(j) == 0); /* no-op */
302 assert_se(sd_journal_previous(j) == 0); /* no-op */
303 test_check_numbers_down(j, 9);
304 sd_journal_close(j);
305
306 /* Seek to tail, iterate up. */
307 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
308 assert_ret(sd_journal_seek_tail(j));
309 assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
310 test_check_numbers_up(j, 9);
311 sd_journal_close(j);
312
313 /* Seek to tail twice, iterate up. */
314 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
315 assert_ret(sd_journal_seek_tail(j));
316 assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
317 assert_ret(sd_journal_seek_tail(j));
318 assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
319 test_check_numbers_up(j, 9);
320 sd_journal_close(j);
321
322 /* Seek to tail, move to next, then iterate up. */
323 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
324 assert_ret(sd_journal_seek_tail(j));
325 assert_se(sd_journal_next(j) == 0); /* no-op */
326 assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
327 test_check_numbers_up(j, 9);
328 sd_journal_close(j);
329
330 /* Seek to tail, walk several steps, then iterate up. */
331 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
332 assert_ret(sd_journal_seek_tail(j));
333 assert_se(sd_journal_next(j) == 0); /* no-op */
334 assert_se(sd_journal_next(j) == 0); /* no-op */
335 assert_se(sd_journal_next(j) == 0); /* no-op */
336 assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry. */
337 assert_se(sd_journal_next(j) == 0); /* no-op */
338 assert_se(sd_journal_next(j) == 0); /* no-op */
339 test_check_numbers_up(j, 9);
340 sd_journal_close(j);
341
342 /* Seek to tail, skip to head, iterate down. */
343 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
344 assert_ret(sd_journal_seek_tail(j));
345 assert_se(sd_journal_previous_skip(j, 9) == 9); /* pointing to the first entry. */
346 test_check_numbers_down(j, 9);
347 sd_journal_close(j);
348
349 /* Seek to tail, skip to head in a more complex way, then iterate down. */
350 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
351 assert_ret(sd_journal_seek_tail(j));
352 assert_se(sd_journal_next(j) == 0);
353 assert_se(sd_journal_previous_skip(j, 4) == 4);
354 assert_se(sd_journal_previous_skip(j, 5) == 5);
355 assert_se(sd_journal_previous(j) == 0);
356 assert_se(sd_journal_previous_skip(j, 5) == 0);
357 assert_se(sd_journal_next(j) == 1);
358 assert_se(sd_journal_previous_skip(j, 5) == 1);
359 assert_se(sd_journal_next(j) == 1);
360 assert_se(sd_journal_next(j) == 1);
361 assert_se(sd_journal_previous(j) == 1);
362 assert_se(sd_journal_next(j) == 1);
363 assert_se(sd_journal_next(j) == 1);
364 assert_se(sd_journal_previous_skip(j, 5) == 3);
365 test_check_numbers_down(j, 9);
366 sd_journal_close(j);
367
368 /* Seek to head, skip to tail, iterate up. */
369 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
370 assert_ret(sd_journal_seek_head(j));
371 assert_se(sd_journal_next_skip(j, 9) == 9);
372 test_check_numbers_up(j, 9);
373 sd_journal_close(j);
374
375 /* Seek to head, skip to tail in a more complex way, then iterate up. */
376 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
377 assert_ret(sd_journal_seek_head(j));
378 assert_se(sd_journal_previous(j) == 0);
379 assert_se(sd_journal_next_skip(j, 4) == 4);
380 assert_se(sd_journal_next_skip(j, 5) == 5);
381 assert_se(sd_journal_next(j) == 0);
382 assert_se(sd_journal_next_skip(j, 5) == 0);
383 assert_se(sd_journal_previous(j) == 1);
384 assert_se(sd_journal_next_skip(j, 5) == 1);
385 assert_se(sd_journal_previous(j) == 1);
386 assert_se(sd_journal_previous(j) == 1);
387 assert_se(sd_journal_next(j) == 1);
388 assert_se(sd_journal_previous(j) == 1);
389 assert_se(sd_journal_previous(j) == 1);
390 assert_se(r = sd_journal_next_skip(j, 5) == 3);
391 test_check_numbers_up(j, 9);
392 sd_journal_close(j);
393
394 test_done(t);
395 }
396
397 TEST(skip) {
398 test_skip_one(setup_sequential);
399 test_skip_one(setup_interleaved);
400 }
401
402 static void test_boot_id_one(void (*setup)(void), size_t n_boots_expected) {
403 char t[] = "/var/tmp/journal-boot-id-XXXXXX";
404 sd_journal *j;
405 _cleanup_free_ BootId *boots = NULL;
406 size_t n_boots;
407
408 mkdtemp_chdir_chattr(t);
409
410 setup();
411
412 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
413 assert_se(journal_get_boots(j, &boots, &n_boots) >= 0);
414 assert_se(boots);
415 assert_se(n_boots == n_boots_expected);
416 sd_journal_close(j);
417
418 FOREACH_ARRAY(b, boots, n_boots) {
419 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
420 assert_se(journal_find_boot_by_id(j, b->id) == 1);
421 sd_journal_close(j);
422 }
423
424 for (int i = - (int) n_boots + 1; i <= (int) n_boots; i++) {
425 sd_id128_t id;
426
427 assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
428 assert_se(journal_find_boot_by_offset(j, i, &id) == 1);
429 if (i <= 0)
430 assert_se(sd_id128_equal(id, boots[n_boots + i - 1].id));
431 else
432 assert_se(sd_id128_equal(id, boots[i - 1].id));
433 sd_journal_close(j);
434 }
435
436 test_done(t);
437 }
438
439 TEST(boot_id) {
440 test_boot_id_one(setup_sequential, 3);
441 test_boot_id_one(setup_unreferenced_data, 3);
442 }
443
444 static void test_sequence_numbers_one(void) {
445 _cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
446 char t[] = "/var/tmp/journal-seq-XXXXXX";
447 JournalFile *one, *two;
448 uint64_t seqnum = 0;
449 sd_id128_t seqnum_id;
450
451 m = mmap_cache_new();
452 assert_se(m != NULL);
453
454 mkdtemp_chdir_chattr(t);
455
456 assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0644,
457 UINT64_MAX, NULL, m, NULL, &one) == 0);
458
459 append_number(one, 1, NULL, &seqnum, NULL);
460 printf("seqnum=%"PRIu64"\n", seqnum);
461 assert_se(seqnum == 1);
462 append_number(one, 2, NULL, &seqnum, NULL);
463 printf("seqnum=%"PRIu64"\n", seqnum);
464 assert_se(seqnum == 2);
465
466 assert_se(one->header->state == STATE_ONLINE);
467 assert_se(!sd_id128_equal(one->header->file_id, one->header->machine_id));
468 assert_se(!sd_id128_equal(one->header->file_id, one->header->tail_entry_boot_id));
469 assert_se(sd_id128_equal(one->header->file_id, one->header->seqnum_id));
470
471 memcpy(&seqnum_id, &one->header->seqnum_id, sizeof(sd_id128_t));
472
473 assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0644,
474 UINT64_MAX, NULL, m, one, &two) == 0);
475
476 assert_se(two->header->state == STATE_ONLINE);
477 assert_se(!sd_id128_equal(two->header->file_id, one->header->file_id));
478 assert_se(sd_id128_equal(two->header->machine_id, one->header->machine_id));
479 assert_se(sd_id128_is_null(two->header->tail_entry_boot_id)); /* Not written yet. */
480 assert_se(sd_id128_equal(two->header->seqnum_id, one->header->seqnum_id));
481
482 append_number(two, 3, NULL, &seqnum, NULL);
483 printf("seqnum=%"PRIu64"\n", seqnum);
484 assert_se(seqnum == 3);
485 append_number(two, 4, NULL, &seqnum, NULL);
486 printf("seqnum=%"PRIu64"\n", seqnum);
487 assert_se(seqnum == 4);
488
489 /* Verify tail_entry_boot_id. */
490 assert_se(sd_id128_equal(two->header->tail_entry_boot_id, one->header->tail_entry_boot_id));
491
492 test_close(two);
493
494 append_number(one, 5, NULL, &seqnum, NULL);
495 printf("seqnum=%"PRIu64"\n", seqnum);
496 assert_se(seqnum == 5);
497
498 append_number(one, 6, NULL, &seqnum, NULL);
499 printf("seqnum=%"PRIu64"\n", seqnum);
500 assert_se(seqnum == 6);
501
502 test_close(one);
503
504 /* If the machine-id is not initialized, the header file verification
505 * (which happens when reopening a journal file) will fail. */
506 if (sd_id128_get_machine(NULL) >= 0) {
507 /* restart server */
508 seqnum = 0;
509
510 assert_se(journal_file_open(-1, "two.journal", O_RDWR, JOURNAL_COMPRESS, 0,
511 UINT64_MAX, NULL, m, NULL, &two) == 0);
512
513 assert_se(sd_id128_equal(two->header->seqnum_id, seqnum_id));
514
515 append_number(two, 7, NULL, &seqnum, NULL);
516 printf("seqnum=%"PRIu64"\n", seqnum);
517 assert_se(seqnum == 5);
518
519 /* So..., here we have the same seqnum in two files with the
520 * same seqnum_id. */
521
522 test_close(two);
523 }
524
525 test_done(t);
526 }
527
528 TEST(sequence_numbers) {
529 assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "0", 1) >= 0);
530 test_sequence_numbers_one();
531
532 assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1) >= 0);
533 test_sequence_numbers_one();
534 }
535
536 static int expected_result(uint64_t needle, const uint64_t *candidates, const uint64_t *offset, size_t n, direction_t direction, uint64_t *ret) {
537 switch (direction) {
538 case DIRECTION_DOWN:
539 for (size_t i = 0; i < n; i++) {
540 if (candidates[i] == 0) {
541 *ret = 0;
542 return 0;
543 }
544 if (needle <= candidates[i]) {
545 *ret = offset[i];
546 return 1;
547 }
548 }
549 *ret = 0;
550 return 0;
551
552 case DIRECTION_UP:
553 for (size_t i = 0; i < n; i++)
554 if (needle < candidates[i] || candidates[i] == 0) {
555 if (i == 0) {
556 *ret = 0;
557 return 0;
558 }
559 *ret = offset[i - 1];
560 return 1;
561 }
562 *ret = offset[n - 1];
563 return 1;
564
565 default:
566 assert_not_reached();
567 }
568 }
569
570 static int expected_result_next(uint64_t needle, const uint64_t *candidates, const uint64_t *offset, size_t n, direction_t direction, uint64_t *ret) {
571 switch (direction) {
572 case DIRECTION_DOWN:
573 for (size_t i = 0; i < n; i++)
574 if (needle < offset[i]) {
575 *ret = candidates[i];
576 return candidates[i] > 0;
577 }
578 *ret = 0;
579 return 0;
580
581 case DIRECTION_UP:
582 for (size_t i = 0; i < n; i++)
583 if (needle <= offset[i]) {
584 n = i;
585 break;
586 }
587
588 for (; n > 0 && candidates[n - 1] == 0; n--)
589 ;
590
591 if (n == 0) {
592 *ret = 0;
593 return 0;
594 }
595
596 *ret = candidates[n - 1];
597 return candidates[n - 1] > 0;
598
599 default:
600 assert_not_reached();
601 }
602 }
603
604 static void verify(JournalFile *f, const uint64_t *seqnum, const uint64_t *offset_candidates, const uint64_t *offset, size_t n) {
605 uint64_t p, q;
606 int r, e;
607
608 /* by seqnum (sequential) */
609 for (uint64_t i = 0; i < n + 2; i++) {
610 p = 0;
611 r = journal_file_move_to_entry_by_seqnum(f, i, DIRECTION_DOWN, NULL, &p);
612 e = expected_result(i, seqnum, offset, n, DIRECTION_DOWN, &q);
613 assert_se(r == e);
614 assert_se(p == q);
615
616 p = 0;
617 r = journal_file_move_to_entry_by_seqnum(f, i, DIRECTION_UP, NULL, &p);
618 e = expected_result(i, seqnum, offset, n, DIRECTION_UP, &q);
619 assert_se(r == e);
620 assert_se(p == q);
621 }
622
623 /* by seqnum (random) */
624 for (size_t trial = 0; trial < 3 * n; trial++) {
625 uint64_t i = random_u64_range(n + 2);
626
627 p = 0;
628 r = journal_file_move_to_entry_by_seqnum(f, i, DIRECTION_DOWN, NULL, &p);
629 e = expected_result(i, seqnum, offset, n, DIRECTION_DOWN, &q);
630 assert_se(r == e);
631 assert_se(p == q);
632 }
633 for (size_t trial = 0; trial < 3 * n; trial++) {
634 uint64_t i = random_u64_range(n + 2);
635
636 p = 0;
637 r = journal_file_move_to_entry_by_seqnum(f, i, DIRECTION_UP, NULL, &p);
638 e = expected_result(i, seqnum, offset, n, DIRECTION_UP, &q);
639 assert_se(r == e);
640 assert_se(p == q);
641 }
642
643 /* by offset (sequential) */
644 for (size_t i = 0; i < n; i++) {
645 p = 0;
646 r = journal_file_move_to_entry_by_offset(f, offset[i] - 1, DIRECTION_DOWN, NULL, &p);
647 e = expected_result(offset[i] - 1, offset, offset, n, DIRECTION_DOWN, &q);
648 assert_se(r == e);
649 assert_se(p == q);
650
651 p = 0;
652 r = journal_file_move_to_entry_by_offset(f, offset[i], DIRECTION_DOWN, NULL, &p);
653 e = expected_result(offset[i], offset, offset, n, DIRECTION_DOWN, &q);
654 assert_se(r == e);
655 assert_se(p == q);
656
657 p = 0;
658 r = journal_file_move_to_entry_by_offset(f, offset[i] + 1, DIRECTION_DOWN, NULL, &p);
659 e = expected_result(offset[i] + 1, offset, offset, n, DIRECTION_DOWN, &q);
660 assert_se(r == e);
661 assert_se(p == q);
662
663 p = 0;
664 r = journal_file_move_to_entry_by_offset(f, offset[i] - 1, DIRECTION_UP, NULL, &p);
665 e = expected_result(offset[i] - 1, offset, offset, n, DIRECTION_UP, &q);
666 assert_se(r == e);
667 assert_se(p == q);
668
669 p = 0;
670 r = journal_file_move_to_entry_by_offset(f, offset[i], DIRECTION_UP, NULL, &p);
671 e = expected_result(offset[i], offset, offset, n, DIRECTION_UP, &q);
672 assert_se(r == e);
673 assert_se(p == q);
674
675 p = 0;
676 r = journal_file_move_to_entry_by_offset(f, offset[i] + 1, DIRECTION_UP, NULL, &p);
677 e = expected_result(offset[i] + 1, offset, offset, n, DIRECTION_UP, &q);
678 assert_se(r == e);
679 assert_se(p == q);
680 }
681
682 /* by offset (random) */
683 for (size_t trial = 0; trial < 3 * n; trial++) {
684 uint64_t i = offset[0] - 1 + random_u64_range(offset[n-1] - offset[0] + 2);
685
686 p = 0;
687 r = journal_file_move_to_entry_by_offset(f, i, DIRECTION_DOWN, NULL, &p);
688 e = expected_result(i, offset, offset, n, DIRECTION_DOWN, &q);
689 assert_se(r == e);
690 assert_se(p == q);
691 }
692 for (size_t trial = 0; trial < 3 * n; trial++) {
693 uint64_t i = offset[0] - 1 + random_u64_range(offset[n-1] - offset[0] + 2);
694
695 p = 0;
696 r = journal_file_move_to_entry_by_offset(f, i, DIRECTION_UP, NULL, &p);
697 e = expected_result(i, offset, offset, n, DIRECTION_UP, &q);
698 assert_se(r == e);
699 assert_se(p == q);
700 }
701
702 /* by journal_file_next_entry() */
703 for (size_t i = 0; i < n; i++) {
704 p = 0;
705 r = journal_file_next_entry(f, offset[i] - 2, DIRECTION_DOWN, NULL, &p);
706 e = expected_result_next(offset[i] - 2, offset_candidates, offset, n, DIRECTION_DOWN, &q);
707 assert_se(e == 0 ? r <= 0 : r > 0);
708 assert_se(p == q);
709
710 p = 0;
711 r = journal_file_next_entry(f, offset[i] - 1, DIRECTION_DOWN, NULL, &p);
712 e = expected_result_next(offset[i] - 1, offset_candidates, offset, n, DIRECTION_DOWN, &q);
713 assert_se(e == 0 ? r <= 0 : r > 0);
714 assert_se(p == q);
715
716 p = 0;
717 r = journal_file_next_entry(f, offset[i], DIRECTION_DOWN, NULL, &p);
718 e = expected_result_next(offset[i], offset_candidates, offset, n, DIRECTION_DOWN, &q);
719 assert_se(e == 0 ? r <= 0 : r > 0);
720 assert_se(p == q);
721
722 p = 0;
723 r = journal_file_next_entry(f, offset[i] + 1, DIRECTION_DOWN, NULL, &p);
724 e = expected_result_next(offset[i] + 1, offset_candidates, offset, n, DIRECTION_DOWN, &q);
725 assert_se(e == 0 ? r <= 0 : r > 0);
726 assert_se(p == q);
727
728 p = 0;
729 r = journal_file_next_entry(f, offset[i] - 1, DIRECTION_UP, NULL, &p);
730 e = expected_result_next(offset[i] - 1, offset_candidates, offset, n, DIRECTION_UP, &q);
731 assert_se(e == 0 ? r <= 0 : r > 0);
732 assert_se(p == q);
733
734 p = 0;
735 r = journal_file_next_entry(f, offset[i], DIRECTION_UP, NULL, &p);
736 e = expected_result_next(offset[i], offset_candidates, offset, n, DIRECTION_UP, &q);
737 assert_se(e == 0 ? r <= 0 : r > 0);
738 assert_se(p == q);
739
740 p = 0;
741 r = journal_file_next_entry(f, offset[i] + 1, DIRECTION_UP, NULL, &p);
742 e = expected_result_next(offset[i] + 1, offset_candidates, offset, n, DIRECTION_UP, &q);
743 assert_se(e == 0 ? r <= 0 : r > 0);
744 assert_se(p == q);
745
746 p = 0;
747 r = journal_file_next_entry(f, offset[i] + 2, DIRECTION_UP, NULL, &p);
748 e = expected_result_next(offset[i] + 2, offset_candidates, offset, n, DIRECTION_UP, &q);
749 assert_se(e == 0 ? r <= 0 : r > 0);
750 assert_se(p == q);
751 }
752 for (size_t trial = 0; trial < 3 * n; trial++) {
753 uint64_t i = offset[0] - 1 + random_u64_range(offset[n-1] - offset[0] + 2);
754
755 p = 0;
756 r = journal_file_next_entry(f, i, DIRECTION_DOWN, NULL, &p);
757 e = expected_result_next(i, offset_candidates, offset, n, DIRECTION_DOWN, &q);
758 assert_se(e == 0 ? r <= 0 : r > 0);
759 assert_se(p == q);
760 }
761 for (size_t trial = 0; trial < 3 * n; trial++) {
762 uint64_t i = offset[0] - 1 + random_u64_range(offset[n-1] - offset[0] + 2);
763
764 p = 0;
765 r = journal_file_next_entry(f, i, DIRECTION_UP, NULL, &p);
766 e = expected_result_next(i, offset_candidates, offset, n, DIRECTION_UP, &q);
767 assert_se(e == 0 ? r <= 0 : r > 0);
768 assert_se(p == q);
769 }
770 }
771
772 static void test_generic_array_bisect_one(size_t n, size_t num_corrupted) {
773 _cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
774 char t[] = "/var/tmp/journal-seq-XXXXXX";
775 _cleanup_free_ uint64_t *seqnum = NULL, *offset = NULL, *offset_candidates = NULL;
776 JournalFile *f;
777
778 log_info("/* %s(%zu, %zu) */", __func__, n, num_corrupted);
779
780 assert_se(m = mmap_cache_new());
781
782 mkdtemp_chdir_chattr(t);
783
784 assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0644,
785 UINT64_MAX, NULL, m, NULL, &f) == 0);
786
787 assert_se(seqnum = new0(uint64_t, n));
788 assert_se(offset = new0(uint64_t, n));
789
790 for (size_t i = 0; i < n; i++) {
791 append_number(f, i, NULL, seqnum + i, offset + i);
792 if (i == 0) {
793 assert_se(seqnum[i] > 0);
794 assert_se(offset[i] > 0);
795 } else {
796 assert_se(seqnum[i] > seqnum[i-1]);
797 assert_se(offset[i] > offset[i-1]);
798 }
799 }
800
801 assert_se(offset_candidates = newdup(uint64_t, offset, n));
802
803 verify(f, seqnum, offset_candidates, offset, n);
804
805 /* Reset chain cache. */
806 assert_se(journal_file_move_to_entry_by_offset(f, offset[0], DIRECTION_DOWN, NULL, NULL) > 0);
807
808 /* make journal corrupted by clearing seqnum. */
809 for (size_t i = n - num_corrupted; i < n; i++) {
810 Object *o;
811
812 assert_se(journal_file_move_to_object(f, OBJECT_ENTRY, offset[i], &o) >= 0);
813 assert_se(o);
814 o->entry.seqnum = 0;
815 seqnum[i] = 0;
816 offset_candidates[i] = 0;
817 }
818
819 verify(f, seqnum, offset_candidates, offset, n);
820
821 test_close(f);
822 test_done(t);
823 }
824
825 TEST(generic_array_bisect) {
826 for (size_t n = 1; n < 10; n++)
827 for (size_t m = 1; m <= n; m++)
828 test_generic_array_bisect_one(n, m);
829
830 test_generic_array_bisect_one(100, 40);
831 }
832
833 static int intro(void) {
834 /* journal_file_open() requires a valid machine id */
835 if (access("/etc/machine-id", F_OK) != 0)
836 return log_tests_skipped("/etc/machine-id not found");
837
838 arg_keep = saved_argc > 1;
839
840 return EXIT_SUCCESS;
841 }
842
843 DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro);