Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <fcntl.h>
4 : #include <unistd.h>
5 :
6 : #include "chattr-util.h"
7 : #include "io-util.h"
8 : #include "journal-authenticate.h"
9 : #include "journal-file.h"
10 : #include "journal-vacuum.h"
11 : #include "log.h"
12 : #include "rm-rf.h"
13 : #include "tests.h"
14 :
15 : static bool arg_keep = false;
16 :
17 9 : static void mkdtemp_chdir_chattr(char *path) {
18 9 : assert_se(mkdtemp(path));
19 9 : assert_se(chdir(path) >= 0);
20 :
21 : /* Speed up things a bit on btrfs, ensuring that CoW is turned off for all files created in our
22 : * directory during the test run */
23 9 : (void) chattr_path(path, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
24 9 : }
25 :
26 1 : static void test_non_empty(void) {
27 : dual_timestamp ts;
28 : JournalFile *f;
29 : struct iovec iovec;
30 : static const char test[] = "TEST1=1", test2[] = "TEST2=2";
31 : Object *o;
32 : uint64_t p;
33 : sd_id128_t fake_boot_id;
34 1 : char t[] = "/var/tmp/journal-XXXXXX";
35 :
36 1 : test_setup_logging(LOG_DEBUG);
37 :
38 1 : mkdtemp_chdir_chattr(t);
39 :
40 1 : assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, (uint64_t) -1, true, NULL, NULL, NULL, NULL, &f) == 0);
41 :
42 1 : assert_se(dual_timestamp_get(&ts));
43 1 : assert_se(sd_id128_randomize(&fake_boot_id) == 0);
44 :
45 1 : iovec = IOVEC_MAKE_STRING(test);
46 1 : assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
47 :
48 1 : iovec = IOVEC_MAKE_STRING(test2);
49 1 : assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
50 :
51 1 : iovec = IOVEC_MAKE_STRING(test);
52 1 : assert_se(journal_file_append_entry(f, &ts, &fake_boot_id, &iovec, 1, NULL, NULL, NULL) == 0);
53 :
54 : #if HAVE_GCRYPT
55 1 : journal_file_append_tag(f);
56 : #endif
57 1 : journal_file_dump(f);
58 :
59 1 : assert_se(journal_file_next_entry(f, 0, DIRECTION_DOWN, &o, &p) == 1);
60 1 : assert_se(le64toh(o->entry.seqnum) == 1);
61 :
62 1 : assert_se(journal_file_next_entry(f, p, DIRECTION_DOWN, &o, &p) == 1);
63 1 : assert_se(le64toh(o->entry.seqnum) == 2);
64 :
65 1 : assert_se(journal_file_next_entry(f, p, DIRECTION_DOWN, &o, &p) == 1);
66 1 : assert_se(le64toh(o->entry.seqnum) == 3);
67 1 : assert_se(sd_id128_equal(o->entry.boot_id, fake_boot_id));
68 :
69 1 : assert_se(journal_file_next_entry(f, p, DIRECTION_DOWN, &o, &p) == 0);
70 :
71 1 : assert_se(journal_file_next_entry(f, 0, DIRECTION_DOWN, &o, &p) == 1);
72 1 : assert_se(le64toh(o->entry.seqnum) == 1);
73 :
74 1 : assert_se(journal_file_find_data_object(f, test, strlen(test), NULL, &p) == 1);
75 1 : assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1);
76 1 : assert_se(le64toh(o->entry.seqnum) == 1);
77 :
78 1 : assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1);
79 1 : assert_se(le64toh(o->entry.seqnum) == 3);
80 :
81 1 : assert_se(journal_file_find_data_object(f, test2, strlen(test2), NULL, &p) == 1);
82 1 : assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1);
83 1 : assert_se(le64toh(o->entry.seqnum) == 2);
84 :
85 1 : assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1);
86 1 : assert_se(le64toh(o->entry.seqnum) == 2);
87 :
88 1 : assert_se(journal_file_find_data_object(f, "quux", 4, NULL, &p) == 0);
89 :
90 1 : assert_se(journal_file_move_to_entry_by_seqnum(f, 1, DIRECTION_DOWN, &o, NULL) == 1);
91 1 : assert_se(le64toh(o->entry.seqnum) == 1);
92 :
93 1 : assert_se(journal_file_move_to_entry_by_seqnum(f, 3, DIRECTION_DOWN, &o, NULL) == 1);
94 1 : assert_se(le64toh(o->entry.seqnum) == 3);
95 :
96 1 : assert_se(journal_file_move_to_entry_by_seqnum(f, 2, DIRECTION_DOWN, &o, NULL) == 1);
97 1 : assert_se(le64toh(o->entry.seqnum) == 2);
98 :
99 1 : assert_se(journal_file_move_to_entry_by_seqnum(f, 10, DIRECTION_DOWN, &o, NULL) == 0);
100 :
101 1 : journal_file_rotate(&f, true, (uint64_t) -1, true, NULL);
102 1 : journal_file_rotate(&f, true, (uint64_t) -1, true, NULL);
103 :
104 1 : (void) journal_file_close(f);
105 :
106 1 : log_info("Done...");
107 :
108 1 : if (arg_keep)
109 0 : log_info("Not removing %s", t);
110 : else {
111 1 : journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
112 :
113 1 : assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
114 : }
115 :
116 1 : puts("------------------------------------------------------------");
117 1 : }
118 :
119 1 : static void test_empty(void) {
120 : JournalFile *f1, *f2, *f3, *f4;
121 1 : char t[] = "/var/tmp/journal-XXXXXX";
122 :
123 1 : test_setup_logging(LOG_DEBUG);
124 :
125 1 : mkdtemp_chdir_chattr(t);
126 :
127 1 : assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, false, (uint64_t) -1, false, NULL, NULL, NULL, NULL, &f1) == 0);
128 :
129 1 : assert_se(journal_file_open(-1, "test-compress.journal", O_RDWR|O_CREAT, 0666, true, (uint64_t) -1, false, NULL, NULL, NULL, NULL, &f2) == 0);
130 :
131 1 : assert_se(journal_file_open(-1, "test-seal.journal", O_RDWR|O_CREAT, 0666, false, (uint64_t) -1, true, NULL, NULL, NULL, NULL, &f3) == 0);
132 :
133 1 : assert_se(journal_file_open(-1, "test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, (uint64_t) -1, true, NULL, NULL, NULL, NULL, &f4) == 0);
134 :
135 1 : journal_file_print_header(f1);
136 1 : puts("");
137 1 : journal_file_print_header(f2);
138 1 : puts("");
139 1 : journal_file_print_header(f3);
140 1 : puts("");
141 1 : journal_file_print_header(f4);
142 1 : puts("");
143 :
144 1 : log_info("Done...");
145 :
146 1 : if (arg_keep)
147 0 : log_info("Not removing %s", t);
148 : else {
149 1 : journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
150 :
151 1 : assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
152 : }
153 :
154 1 : (void) journal_file_close(f1);
155 1 : (void) journal_file_close(f2);
156 1 : (void) journal_file_close(f3);
157 1 : (void) journal_file_close(f4);
158 1 : }
159 :
160 : #if HAVE_XZ || HAVE_LZ4
161 7 : static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) {
162 : dual_timestamp ts;
163 : JournalFile *f;
164 : struct iovec iovec;
165 : Object *o;
166 : uint64_t p;
167 7 : char t[] = "/var/tmp/journal-XXXXXX";
168 7 : char data[2048] = {0};
169 : bool is_compressed;
170 : int r;
171 :
172 7 : assert_se(data_size <= sizeof(data));
173 :
174 7 : test_setup_logging(LOG_DEBUG);
175 :
176 7 : mkdtemp_chdir_chattr(t);
177 :
178 7 : assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, compress_threshold, true, NULL, NULL, NULL, NULL, &f) == 0);
179 :
180 7 : dual_timestamp_get(&ts);
181 :
182 7 : iovec = IOVEC_MAKE(data, data_size);
183 7 : assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
184 :
185 : #if HAVE_GCRYPT
186 7 : journal_file_append_tag(f);
187 : #endif
188 7 : journal_file_dump(f);
189 :
190 : /* We have to partially reimplement some of the dump logic, because the normal next_entry does the
191 : * decompression for us. */
192 7 : p = le64toh(f->header->header_size);
193 : for (;;) {
194 21 : r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
195 21 : assert_se(r == 0);
196 21 : if (o->object.type == OBJECT_DATA)
197 7 : break;
198 :
199 14 : assert_se(p < le64toh(f->header->tail_object_offset));
200 14 : p = p + ALIGN64(le64toh(o->object.size));
201 : }
202 :
203 7 : is_compressed = (o->object.flags & OBJECT_COMPRESSION_MASK) != 0;
204 :
205 7 : (void) journal_file_close(f);
206 :
207 7 : log_info("Done...");
208 :
209 7 : if (arg_keep)
210 0 : log_info("Not removing %s", t);
211 : else {
212 7 : journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
213 :
214 7 : assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
215 : }
216 :
217 7 : puts("------------------------------------------------------------");
218 :
219 7 : return is_compressed;
220 : }
221 :
222 1 : static void test_min_compress_size(void) {
223 : /* Note that XZ will actually fail to compress anything under 80 bytes, so you have to choose the limits
224 : * carefully */
225 :
226 : /* DEFAULT_MIN_COMPRESS_SIZE is 512 */
227 1 : assert_se(!check_compressed((uint64_t) -1, 255));
228 1 : assert_se(check_compressed((uint64_t) -1, 513));
229 :
230 : /* compress everything */
231 1 : assert_se(check_compressed(0, 96));
232 1 : assert_se(check_compressed(8, 96));
233 :
234 : /* Ensure we don't try to compress less than 8 bytes */
235 1 : assert_se(!check_compressed(0, 7));
236 :
237 : /* check boundary conditions */
238 1 : assert_se(check_compressed(256, 256));
239 1 : assert_se(!check_compressed(256, 255));
240 1 : }
241 : #endif
242 :
243 1 : int main(int argc, char *argv[]) {
244 1 : arg_keep = argc > 1;
245 :
246 1 : test_setup_logging(LOG_INFO);
247 :
248 : /* journal_file_open requires a valid machine id */
249 1 : if (access("/etc/machine-id", F_OK) != 0)
250 0 : return log_tests_skipped("/etc/machine-id not found");
251 :
252 1 : test_non_empty();
253 1 : test_empty();
254 : #if HAVE_XZ || HAVE_LZ4
255 1 : test_min_compress_size();
256 : #endif
257 :
258 1 : return 0;
259 : }
|