1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "sd-journal.h"
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"
15 #include "logs-show.h"
16 #include "parse-util.h"
17 #include "random-util.h"
21 /* This program tests skipping around in a multi-file journal. */
23 static bool arg_keep
= false;
24 static dual_timestamp previous_ts
= {};
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
);
32 #define assert_ret(expr) \
35 if (_unlikely_(_r_ < 0)) \
36 log_assert_errno(#expr, -_r_, PROJECT_FILE, __LINE__, __func__); \
39 static JournalFile
*test_open_internal(const char *name
, JournalFileFlags flags
) {
40 _cleanup_(mmap_cache_unrefp
) MMapCache
*m
= NULL
;
46 assert_ret(journal_file_open(-1, name
, O_RDWR
|O_CREAT
, flags
, 0644, UINT64_MAX
, NULL
, m
, NULL
, &f
));
50 static JournalFile
*test_open(const char *name
) {
51 return test_open_internal(name
, JOURNAL_COMPRESS
);
54 static JournalFile
*test_open_strict(const char *name
) {
55 return test_open_internal(name
, JOURNAL_COMPRESS
| JOURNAL_STRICT_ORDER
);
58 static void test_close(JournalFile
*f
) {
59 (void) journal_file_offline_close(f
);
62 static void test_done(const char *t
) {
66 log_info("Not removing %s", t
);
68 journal_directory_vacuum(".", 3000000, 0, 0, NULL
, true);
70 assert_se(rm_rf(t
, REMOVE_ROOT
|REMOVE_PHYSICAL
) >= 0);
73 log_info("------------------------------------------------------------");
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
;
79 struct iovec iovec
[2];
82 dual_timestamp_now(&ts
);
84 if (ts
.monotonic
<= previous_ts
.monotonic
)
85 ts
.monotonic
= previous_ts
.monotonic
+ 1;
87 if (ts
.realtime
<= previous_ts
.realtime
)
88 ts
.realtime
= previous_ts
.realtime
+ 1;
92 assert_se(asprintf(&p
, "NUMBER=%d", n
) >= 0);
93 iovec
[n_iov
++] = IOVEC_MAKE_STRING(p
);
96 assert_se(q
= strjoin("_BOOT_ID=", SD_ID128_TO_STRING(*boot_id
)));
97 iovec
[n_iov
++] = IOVEC_MAKE_STRING(q
);
100 assert_ret(journal_file_append_entry(f
, &ts
, boot_id
, iovec
, n_iov
, seqnum
, NULL
, NULL
, ret_offset
));
103 static void append_unreferenced_data(JournalFile
*f
, const sd_id128_t
*boot_id
) {
104 _cleanup_free_
char *q
= NULL
;
110 ts
.monotonic
= usec_sub_unsigned(previous_ts
.monotonic
, 10);
111 ts
.realtime
= usec_sub_unsigned(previous_ts
.realtime
, 10);
113 assert_se(q
= strjoin("_BOOT_ID=", SD_ID128_TO_STRING(*boot_id
)));
114 iovec
= IOVEC_MAKE_STRING(q
);
116 assert_se(journal_file_append_entry(f
, &ts
, boot_id
, &iovec
, 1, NULL
, NULL
, NULL
, NULL
) == -EREMCHG
);
119 static void test_check_number(sd_journal
*j
, int n
) {
122 _cleanup_free_
char *k
= NULL
;
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
);
131 assert_se(safe_atoi(k
+ STRLEN("NUMBER="), &x
) >= 0);
135 static void test_check_numbers_down(sd_journal
*j
, int count
) {
138 for (i
= 1; i
<= count
; i
++) {
140 test_check_number(j
, i
);
141 assert_ret(r
= sd_journal_next(j
));
150 static void test_check_numbers_up(sd_journal
*j
, int count
) {
151 for (int i
= count
; i
>= 1; i
--) {
153 test_check_number(j
, i
);
154 assert_ret(r
= sd_journal_previous(j
));
163 static void setup_sequential(void) {
164 JournalFile
*f1
, *f2
, *f3
;
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
);
190 static void setup_interleaved(void) {
191 JournalFile
*f1
, *f2
, *f3
;
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
);
213 static void setup_unreferenced_data(void) {
214 JournalFile
*f1
, *f2
, *f3
;
217 /* For issue #29275. */
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
);
244 static void mkdtemp_chdir_chattr(char *path
) {
245 assert_se(mkdtemp(path
));
246 assert_se(chdir(path
) >= 0);
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
);
253 static void test_skip_one(void (*setup
)(void)) {
254 char t
[] = "/var/tmp/journal-skip-XXXXXX";
258 mkdtemp_chdir_chattr(t
);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
398 test_skip_one(setup_sequential
);
399 test_skip_one(setup_interleaved
);
402 static void test_boot_id_one(void (*setup
)(void), size_t n_boots_expected
) {
403 char t
[] = "/var/tmp/journal-boot-id-XXXXXX";
405 _cleanup_free_ BootId
*boots
= NULL
;
408 mkdtemp_chdir_chattr(t
);
412 assert_ret(sd_journal_open_directory(&j
, t
, SD_JOURNAL_ASSUME_IMMUTABLE
));
413 assert_se(journal_get_boots(j
, &boots
, &n_boots
) >= 0);
415 assert_se(n_boots
== n_boots_expected
);
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);
424 for (int i
= - (int) n_boots
+ 1; i
<= (int) n_boots
; i
++) {
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);
430 assert_se(sd_id128_equal(id
, boots
[n_boots
+ i
- 1].id
));
432 assert_se(sd_id128_equal(id
, boots
[i
- 1].id
));
440 test_boot_id_one(setup_sequential
, 3);
441 test_boot_id_one(setup_unreferenced_data
, 3);
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
;
449 sd_id128_t seqnum_id
;
451 m
= mmap_cache_new();
452 assert_se(m
!= NULL
);
454 mkdtemp_chdir_chattr(t
);
456 assert_se(journal_file_open(-1, "one.journal", O_RDWR
|O_CREAT
, JOURNAL_COMPRESS
, 0644,
457 UINT64_MAX
, NULL
, m
, NULL
, &one
) == 0);
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);
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
));
471 memcpy(&seqnum_id
, &one
->header
->seqnum_id
, sizeof(sd_id128_t
));
473 assert_se(journal_file_open(-1, "two.journal", O_RDWR
|O_CREAT
, JOURNAL_COMPRESS
, 0644,
474 UINT64_MAX
, NULL
, m
, one
, &two
) == 0);
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
));
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);
489 /* Verify tail_entry_boot_id. */
490 assert_se(sd_id128_equal(two
->header
->tail_entry_boot_id
, one
->header
->tail_entry_boot_id
));
494 append_number(one
, 5, NULL
, &seqnum
, NULL
);
495 printf("seqnum=%"PRIu64
"\n", seqnum
);
496 assert_se(seqnum
== 5);
498 append_number(one
, 6, NULL
, &seqnum
, NULL
);
499 printf("seqnum=%"PRIu64
"\n", seqnum
);
500 assert_se(seqnum
== 6);
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) {
510 assert_se(journal_file_open(-1, "two.journal", O_RDWR
, JOURNAL_COMPRESS
, 0,
511 UINT64_MAX
, NULL
, m
, NULL
, &two
) == 0);
513 assert_se(sd_id128_equal(two
->header
->seqnum_id
, seqnum_id
));
515 append_number(two
, 7, NULL
, &seqnum
, NULL
);
516 printf("seqnum=%"PRIu64
"\n", seqnum
);
517 assert_se(seqnum
== 5);
519 /* So..., here we have the same seqnum in two files with the
528 TEST(sequence_numbers
) {
529 assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "0", 1) >= 0);
530 test_sequence_numbers_one();
532 assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1) >= 0);
533 test_sequence_numbers_one();
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
) {
539 for (size_t i
= 0; i
< n
; i
++) {
540 if (candidates
[i
] == 0) {
544 if (needle
<= candidates
[i
]) {
553 for (size_t i
= 0; i
< n
; i
++)
554 if (needle
< candidates
[i
] || candidates
[i
] == 0) {
559 *ret
= offset
[i
- 1];
562 *ret
= offset
[n
- 1];
566 assert_not_reached();
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
) {
573 for (size_t i
= 0; i
< n
; i
++)
574 if (needle
< offset
[i
]) {
575 *ret
= candidates
[i
];
576 return candidates
[i
] > 0;
582 for (size_t i
= 0; i
< n
; i
++)
583 if (needle
<= offset
[i
]) {
588 for (; n
> 0 && candidates
[n
- 1] == 0; n
--)
596 *ret
= candidates
[n
- 1];
597 return candidates
[n
- 1] > 0;
600 assert_not_reached();
604 static void verify(JournalFile
*f
, const uint64_t *seqnum
, const uint64_t *offset_candidates
, const uint64_t *offset
, size_t n
) {
608 /* by seqnum (sequential) */
609 for (uint64_t i
= 0; i
< n
+ 2; i
++) {
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
);
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
);
623 /* by seqnum (random) */
624 for (size_t trial
= 0; trial
< 3 * n
; trial
++) {
625 uint64_t i
= random_u64_range(n
+ 2);
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
);
633 for (size_t trial
= 0; trial
< 3 * n
; trial
++) {
634 uint64_t i
= random_u64_range(n
+ 2);
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
);
643 /* by offset (sequential) */
644 for (size_t i
= 0; i
< n
; i
++) {
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
);
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
);
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
);
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
);
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
);
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
);
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);
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
);
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);
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
);
702 /* by journal_file_next_entry() */
703 for (size_t i
= 0; i
< n
; i
++) {
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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
;
778 log_info("/* %s(%zu, %zu) */", __func__
, n
, num_corrupted
);
780 assert_se(m
= mmap_cache_new());
782 mkdtemp_chdir_chattr(t
);
784 assert_se(journal_file_open(-1, "test.journal", O_RDWR
|O_CREAT
, JOURNAL_COMPRESS
, 0644,
785 UINT64_MAX
, NULL
, m
, NULL
, &f
) == 0);
787 assert_se(seqnum
= new0(uint64_t, n
));
788 assert_se(offset
= new0(uint64_t, n
));
790 for (size_t i
= 0; i
< n
; i
++) {
791 append_number(f
, i
, NULL
, seqnum
+ i
, offset
+ i
);
793 assert_se(seqnum
[i
] > 0);
794 assert_se(offset
[i
] > 0);
796 assert_se(seqnum
[i
] > seqnum
[i
-1]);
797 assert_se(offset
[i
] > offset
[i
-1]);
801 assert_se(offset_candidates
= newdup(uint64_t, offset
, n
));
803 verify(f
, seqnum
, offset_candidates
, offset
, n
);
805 /* Reset chain cache. */
806 assert_se(journal_file_move_to_entry_by_offset(f
, offset
[0], DIRECTION_DOWN
, NULL
, NULL
) > 0);
808 /* make journal corrupted by clearing seqnum. */
809 for (size_t i
= n
- num_corrupted
; i
< n
; i
++) {
812 assert_se(journal_file_move_to_object(f
, OBJECT_ENTRY
, offset
[i
], &o
) >= 0);
816 offset_candidates
[i
] = 0;
819 verify(f
, seqnum
, offset_candidates
, offset
, n
);
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
);
830 test_generic_array_bisect_one(100, 40);
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");
838 arg_keep
= saved_argc
> 1;
843 DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG
, intro
);