Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Tests for mmapstorage plugin
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 : #define _XOPEN_SOURCE 600
10 :
11 : /* -- Imports --------------------------------------------------------------------------------------------------------------------------- */
12 :
13 : #include <fcntl.h> // fcntl(), open()
14 : #include <stdio.h> // fopen(), fileno()
15 : #include <stdlib.h>
16 : #include <string.h>
17 : #include <sys/mman.h> // mmap()
18 : #include <sys/stat.h> // stat(), chmod()
19 : #include <sys/types.h> // ftruncate ()
20 : #include <sys/wait.h> // waitpit()
21 : #include <unistd.h> // ftruncate(), pipe(), fork()
22 :
23 : #include <kdbconfig.h>
24 : #include <kdbprivate.h>
25 :
26 : #include <tests_plugin.h>
27 :
28 : #include "dynarray.h"
29 : #include "internal.h"
30 : #include "mmapstorage.h"
31 :
32 : /* -- Macros ---------------------------------------------------------------------------------------------------------------------------- */
33 :
34 : #define KEY_NAME_LENGTH 1000
35 : #define NUM_DIR 10
36 : #define NUM_KEY 10
37 :
38 : #define TEST_ROOT_KEY "user/tests/mmapstorage"
39 :
40 : /* -- KeySet test data ------------------------------------------------------------------------------------------------------------------ */
41 :
42 40 : static KeySet * simpleTestKeySet (void)
43 : {
44 40 : return ksNew (10, keyNew ("user/tests/mmapstorage/simpleKey", KEY_VALUE, "root key", KEY_END),
45 : keyNew ("user/tests/mmapstorage/simpleKey/a", KEY_VALUE, "a value", KEY_END),
46 : keyNew ("user/tests/mmapstorage/simpleKey/b", KEY_VALUE, "b value", KEY_END), KS_END);
47 : }
48 :
49 22 : static KeySet * metaTestKeySet (void)
50 : {
51 22 : return ksNew (10,
52 : keyNew ("user/tests/mmapstorage", KEY_VALUE, "root key", KEY_META, "a",
53 : "b aksdjfh aksjdhf aklsjdhf aksljdhf aklsjdhf aksljdhf ", KEY_END),
54 : keyNew ("user/tests/mmapstorage/a", KEY_VALUE, "a value", KEY_META, "ab",
55 : "cd oiahsdkfhga sdjkfhgsuzdgf kashgdf aszgdf uashdf ", KEY_END),
56 : keyNew ("user/tests/mmapstorage/b", KEY_VALUE, "b value", KEY_META, "longer val",
57 : "here some even more with ugly €@\\1¹²³¼ chars", KEY_END),
58 : KS_END);
59 : }
60 :
61 : typedef struct _binaryTest
62 : {
63 : size_t A_size;
64 : ssize_t B_ssize;
65 : } BinaryTest;
66 : static BinaryTest binaryTestA = { .A_size = SIZE_MAX, .B_ssize = -1 };
67 :
68 4 : static KeySet * otherMetaTestKeySet (void)
69 : {
70 4 : return ksNew (
71 : 10, keyNew ("user/tests/mmapstorage", KEY_VALUE, "root key", KEY_META, "e", "other meta root", KEY_END),
72 : keyNew ("user/tests/mmapstorage/f", KEY_VALUE, "f value", KEY_META, "foo", "some other key in the other meta keyset",
73 : KEY_END),
74 : keyNew ("user/tests/mmapstorage/i", KEY_VALUE, "i value", KEY_META, "i is quite valuable", "happy data is happy :)",
75 : KEY_END),
76 : keyNew ("user/tests/mmapstorage/j/k/l", KEY_VALUE, "jkl value", KEY_META, "where is everyone?", "don't panic", KEY_END),
77 : keyNew ("user/tests/mmapstorage/m", KEY_BINARY, KEY_SIZE, sizeof (BinaryTest), KEY_VALUE, &(binaryTestA), KEY_END), KS_END);
78 : }
79 :
80 4 : static KeySet * largeTestKeySet (void)
81 : {
82 : int i, j;
83 : char name[KEY_NAME_LENGTH + 1];
84 4 : char value[] = "data";
85 4 : KeySet * large = ksNew (NUM_KEY * NUM_DIR, KS_END);
86 :
87 44 : for (i = 0; i < NUM_DIR; i++)
88 : {
89 40 : snprintf (name, KEY_NAME_LENGTH, "%s/%s%d", TEST_ROOT_KEY, "dir", i);
90 40 : ksAppendKey (large, keyNew (name, KEY_VALUE, value, KEY_END));
91 440 : for (j = 0; j < NUM_KEY; j++)
92 : {
93 400 : snprintf (name, KEY_NAME_LENGTH, "%s/%s%d/%s%d", TEST_ROOT_KEY, "dir", i, "key", j);
94 400 : ksAppendKey (large, keyNew (name, KEY_VALUE, value, KEY_END));
95 : }
96 : }
97 4 : return large;
98 : }
99 :
100 : /* -- Functions ------------------------------------------------------------------------------------------------------------------------- */
101 :
102 2 : static void test_mmap_get_set_empty (const char * tmpFile)
103 : {
104 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
105 2 : KeySet * conf = ksNew (0, KS_END);
106 2 : PLUGIN_OPEN ("mmapstorage");
107 :
108 2 : KeySet * ks = ksNew (0, KS_END);
109 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful");
110 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
111 :
112 2 : keyDel (parentKey);
113 2 : ksDel (ks);
114 2 : PLUGIN_CLOSE ();
115 2 : }
116 :
117 4 : static void test_mmap_get_set (const char * tmpFile)
118 : {
119 4 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
120 4 : KeySet * conf = ksNew (0, KS_END);
121 4 : PLUGIN_OPEN ("mmapstorage");
122 4 : KeySet * ks = ksNew (0, KS_END);
123 :
124 4 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful");
125 4 : ksDel (ks);
126 4 : ks = simpleTestKeySet ();
127 4 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
128 4 : ksDel (ks);
129 4 : ks = ksNew (0, KS_END);
130 4 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful");
131 :
132 4 : KeySet * expected = simpleTestKeySet ();
133 4 : compare_keyset (expected, ks);
134 4 : compare_keyset (ks, expected);
135 :
136 4 : ksDel (expected);
137 4 : keyDel (parentKey);
138 4 : ksDel (ks);
139 4 : PLUGIN_CLOSE ();
140 4 : }
141 :
142 2 : static void test_mmap_set_get_global (const char * tmpFile)
143 : {
144 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
145 2 : KeySet * conf = ksNew (0, KS_END);
146 2 : PLUGIN_OPEN ("mmapstorage");
147 2 : KeySet * ks = ksNew (0, KS_END);
148 :
149 2 : plugin->global = 0;
150 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
151 :
152 2 : plugin->global = ksNew (0, KS_END);
153 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
154 2 : ksDel (ks);
155 2 : ksDel (plugin->global);
156 :
157 2 : ks = metaTestKeySet ();
158 2 : plugin->global = simpleTestKeySet ();
159 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
160 2 : ksDel (ks);
161 2 : ksDel (plugin->global);
162 :
163 2 : plugin->global = ksNew (0, KS_END);
164 2 : ks = ksNew (0, KS_END);
165 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful");
166 :
167 2 : KeySet * expected = simpleTestKeySet ();
168 2 : compare_keyset (expected, plugin->global);
169 2 : compare_keyset (plugin->global, expected);
170 :
171 2 : ksDel (plugin->global);
172 2 : ksDel (expected);
173 2 : keyDel (parentKey);
174 2 : ksDel (ks);
175 2 : PLUGIN_CLOSE ();
176 2 : }
177 :
178 2 : static void test_mmap_get_global_after_reopen (const char * tmpFile)
179 : {
180 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
181 2 : KeySet * conf = ksNew (0, KS_END);
182 2 : PLUGIN_OPEN ("mmapstorage");
183 2 : KeySet * ks = ksNew (0, KS_END);
184 2 : plugin->global = ksNew (0, KS_END);
185 :
186 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful");
187 :
188 2 : KeySet * expected_global = simpleTestKeySet ();
189 2 : compare_keyset (expected_global, plugin->global);
190 2 : compare_keyset (plugin->global, expected_global);
191 :
192 2 : KeySet * expected = metaTestKeySet ();
193 2 : compare_keyset (expected, ks);
194 2 : compare_keyset (ks, expected);
195 :
196 2 : ksDel (expected_global);
197 2 : ksDel (plugin->global);
198 2 : ksDel (expected);
199 2 : keyDel (parentKey);
200 2 : ksDel (ks);
201 2 : PLUGIN_CLOSE ();
202 2 : }
203 :
204 2 : static void test_mmap_set_get_global_metadata (const char * tmpFile)
205 : {
206 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
207 2 : KeySet * conf = ksNew (0, KS_END);
208 2 : PLUGIN_OPEN ("mmapstorage");
209 :
210 2 : KeySet * ks = metaTestKeySet ();
211 2 : plugin->global = otherMetaTestKeySet ();
212 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
213 2 : ksDel (ks);
214 2 : ksDel (plugin->global);
215 :
216 2 : plugin->global = ksNew (0, KS_END);
217 2 : ks = ksNew (0, KS_END);
218 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful");
219 :
220 2 : KeySet * expected_global = otherMetaTestKeySet ();
221 2 : compare_keyset (expected_global, plugin->global);
222 2 : compare_keyset (plugin->global, expected_global);
223 2 : ksDel (expected_global);
224 :
225 2 : KeySet * expected = metaTestKeySet ();
226 2 : compare_keyset (expected, ks);
227 2 : compare_keyset (ks, expected);
228 2 : ksDel (expected);
229 :
230 2 : ksDel (plugin->global);
231 2 : keyDel (parentKey);
232 2 : ksDel (ks);
233 2 : PLUGIN_CLOSE ();
234 2 : }
235 :
236 2 : static void test_mmap_truncated_file (const char * tmpFile)
237 : {
238 : // first write a mmap file
239 : {
240 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
241 2 : KeySet * conf = ksNew (0, KS_END);
242 2 : PLUGIN_OPEN ("mmapstorage");
243 :
244 2 : KeySet * ks = simpleTestKeySet ();
245 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
246 :
247 2 : keyDel (parentKey);
248 2 : ksDel (ks);
249 2 : PLUGIN_CLOSE ();
250 : }
251 :
252 : // now truncate the mmap file by the size of the mmap footer
253 : FILE * fp;
254 2 : if ((fp = fopen (tmpFile, "r+")) == 0)
255 : {
256 0 : yield_error ("error opening file");
257 : }
258 : struct stat sbuf;
259 2 : if (stat (tmpFile, &sbuf) == -1)
260 : {
261 0 : yield_error ("stat error");
262 : }
263 :
264 2 : int fd = fileno (fp);
265 2 : if ((ftruncate (fd, sbuf.st_size - sizeof (MmapFooter))) == -1)
266 : {
267 0 : yield_error ("ftruncate error");
268 : }
269 2 : if (fp)
270 : {
271 2 : fclose (fp);
272 : }
273 :
274 : // truncated file should be detected by mmapstorage
275 : {
276 : // after the file was truncated, we expect an error here
277 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
278 2 : KeySet * conf = ksNew (0, KS_END);
279 2 : PLUGIN_OPEN ("mmapstorage");
280 :
281 2 : KeySet * ks = ksNew (0, KS_END);
282 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR, "kdbGet did not detect truncated file");
283 :
284 2 : keyDel (parentKey);
285 2 : ksDel (ks);
286 2 : PLUGIN_CLOSE ();
287 : }
288 2 : }
289 :
290 2 : static void test_mmap_wrong_magic_number (const char * tmpFile)
291 : {
292 : // first write a mmap file
293 : {
294 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
295 2 : KeySet * conf = ksNew (0, KS_END);
296 2 : PLUGIN_OPEN ("mmapstorage");
297 :
298 2 : KeySet * ks = simpleTestKeySet ();
299 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
300 :
301 2 : keyDel (parentKey);
302 2 : ksDel (ks);
303 2 : PLUGIN_CLOSE ();
304 : }
305 :
306 : // now manipulate magic number inside the mmap header
307 : FILE * fp;
308 2 : if ((fp = fopen (tmpFile, "r+")) == 0)
309 : {
310 0 : yield_error ("fopen() error");
311 : }
312 : struct stat sbuf;
313 2 : if (stat (tmpFile, &sbuf) == -1)
314 : {
315 0 : yield_error ("stat() error");
316 : }
317 :
318 2 : int fd = fileno (fp);
319 2 : char * mappedRegion = mmap (0, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
320 2 : if (mappedRegion == MAP_FAILED)
321 : {
322 : ELEKTRA_LOG_WARNING ("error mapping file %s\nmmapSize: " ELEKTRA_STAT_ST_SIZE_F, tmpFile, sbuf.st_size);
323 0 : yield_error ("mmap() error");
324 0 : return;
325 : }
326 2 : if (fp)
327 : {
328 2 : fclose (fp);
329 : }
330 :
331 2 : MmapHeader * mmapHeader = (MmapHeader *) mappedRegion;
332 2 : mmapHeader->mmapMagicNumber = ELEKTRA_MAGIC_MMAP_NUMBER - 1;
333 :
334 2 : if (msync ((void *) mappedRegion, sbuf.st_size, MS_SYNC) != 0)
335 : {
336 0 : yield_error ("msync() error");
337 0 : return;
338 : }
339 :
340 2 : if (munmap (mappedRegion, sbuf.st_size) != 0)
341 : {
342 0 : yield_error ("munmap() error");
343 0 : return;
344 : }
345 :
346 : // manipulated magic number should be detected now
347 : {
348 : // we expect an error here
349 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
350 2 : KeySet * conf = ksNew (0, KS_END);
351 2 : PLUGIN_OPEN ("mmapstorage");
352 :
353 2 : KeySet * ks = ksNew (0, KS_END);
354 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR,
355 : "kdbGet did not detect wrong magic number");
356 :
357 2 : keyDel (parentKey);
358 2 : ksDel (ks);
359 2 : PLUGIN_CLOSE ();
360 : }
361 : }
362 :
363 2 : static void test_mmap_wrong_format_version (const char * tmpFile)
364 : {
365 : // first write a mmap file
366 : {
367 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
368 2 : KeySet * conf = ksNew (0, KS_END);
369 2 : PLUGIN_OPEN ("mmapstorage");
370 :
371 2 : KeySet * ks = simpleTestKeySet ();
372 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
373 :
374 2 : keyDel (parentKey);
375 2 : ksDel (ks);
376 2 : PLUGIN_CLOSE ();
377 : }
378 :
379 : // set wrong version number in mmap header
380 : FILE * fp;
381 2 : if ((fp = fopen (tmpFile, "r+")) == 0)
382 : {
383 0 : yield_error ("fopen() error");
384 : }
385 : struct stat sbuf;
386 2 : if (stat (tmpFile, &sbuf) == -1)
387 : {
388 0 : yield_error ("stat() error");
389 : }
390 :
391 2 : int fd = fileno (fp);
392 2 : char * mappedRegion = mmap (0, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
393 2 : if (mappedRegion == MAP_FAILED)
394 : {
395 : ELEKTRA_LOG_WARNING ("error mapping file %s\nmmapSize: " ELEKTRA_STAT_ST_SIZE_F, tmpFile, sbuf.st_size);
396 0 : yield_error ("mmap() error");
397 0 : return;
398 : }
399 2 : if (fp)
400 : {
401 2 : fclose (fp);
402 : }
403 :
404 2 : MmapHeader * mmapHeader = (MmapHeader *) mappedRegion;
405 2 : mmapHeader->formatVersion = ELEKTRA_MMAP_FORMAT_VERSION + 1;
406 :
407 2 : if (msync ((void *) mappedRegion, sbuf.st_size, MS_SYNC) != 0)
408 : {
409 0 : yield_error ("msync() error");
410 0 : return;
411 : }
412 :
413 2 : if (munmap (mappedRegion, sbuf.st_size) != 0)
414 : {
415 0 : yield_error ("munmap() error");
416 0 : return;
417 : }
418 :
419 : // wrong format version should be detected now
420 : {
421 : // we expect an error here
422 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
423 2 : KeySet * conf = ksNew (0, KS_END);
424 2 : PLUGIN_OPEN ("mmapstorage");
425 :
426 2 : KeySet * ks = ksNew (0, KS_END);
427 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR,
428 : "kdbGet did not detect wrong format version");
429 :
430 2 : keyDel (parentKey);
431 2 : ksDel (ks);
432 2 : PLUGIN_CLOSE ();
433 : }
434 : }
435 :
436 2 : static void test_mmap_wrong_magic_keyset (const char * tmpFile)
437 : {
438 : // first write a mmap file
439 : {
440 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
441 2 : KeySet * conf = ksNew (0, KS_END);
442 2 : PLUGIN_OPEN ("mmapstorage");
443 :
444 2 : KeySet * ks = simpleTestKeySet ();
445 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
446 :
447 2 : keyDel (parentKey);
448 2 : ksDel (ks);
449 2 : PLUGIN_CLOSE ();
450 : }
451 :
452 : // now manipulate magic keyset inside the mapped region
453 : FILE * fp;
454 2 : if ((fp = fopen (tmpFile, "r+")) == 0)
455 : {
456 0 : yield_error ("fopen() error");
457 : }
458 : struct stat sbuf;
459 2 : if (stat (tmpFile, &sbuf) == -1)
460 : {
461 0 : yield_error ("stat() error");
462 : }
463 :
464 2 : int fd = fileno (fp);
465 2 : char * mappedRegion = mmap (0, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
466 2 : if (mappedRegion == MAP_FAILED)
467 : {
468 : ELEKTRA_LOG_WARNING ("error mapping file %s\nmmapSize: " ELEKTRA_STAT_ST_SIZE_F, tmpFile, sbuf.st_size);
469 0 : yield_error ("mmap() error");
470 0 : return;
471 : }
472 2 : if (fp)
473 : {
474 2 : fclose (fp);
475 : }
476 :
477 2 : KeySet * magicKs = (KeySet *) (mappedRegion + sizeof (MmapHeader));
478 2 : magicKs->size = 1234; // magic keyset contains SIZE_MAX here
479 :
480 2 : if (msync ((void *) mappedRegion, sbuf.st_size, MS_SYNC) != 0)
481 : {
482 0 : yield_error ("msync() error");
483 0 : return;
484 : }
485 :
486 2 : if (munmap (mappedRegion, sbuf.st_size) != 0)
487 : {
488 0 : yield_error ("munmap() error");
489 0 : return;
490 : }
491 :
492 : // manipulated magic keyset should be detected now
493 : {
494 : // we expect an error here
495 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
496 2 : KeySet * conf = ksNew (0, KS_END);
497 2 : PLUGIN_OPEN ("mmapstorage");
498 :
499 2 : KeySet * ks = ksNew (0, KS_END);
500 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR,
501 : "kdbGet did not detect wrong magic keyset");
502 :
503 2 : keyDel (parentKey);
504 2 : ksDel (ks);
505 2 : PLUGIN_CLOSE ();
506 : }
507 : }
508 :
509 2 : static void test_mmap_set_get (const char * tmpFile)
510 : {
511 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
512 2 : KeySet * conf = ksNew (0, KS_END);
513 2 : PLUGIN_OPEN ("mmapstorage");
514 2 : KeySet * ks = simpleTestKeySet ();
515 :
516 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
517 :
518 2 : KeySet * returned = ksNew (0, KS_END);
519 2 : succeed_if (plugin->kdbGet (plugin, returned, parentKey) == 1, "kdbGet was not successful");
520 2 : KeySet * expected = simpleTestKeySet ();
521 2 : compare_keyset (expected, returned);
522 :
523 2 : ksDel (expected);
524 2 : ksDel (returned);
525 :
526 2 : keyDel (parentKey);
527 2 : ksDel (ks);
528 2 : PLUGIN_CLOSE ();
529 2 : }
530 :
531 2 : static void test_mmap_get_after_reopen (const char * tmpFile)
532 : {
533 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
534 2 : KeySet * conf = ksNew (0, KS_END);
535 2 : PLUGIN_OPEN ("mmapstorage");
536 2 : KeySet * returned = ksNew (0, KS_END);
537 :
538 2 : succeed_if (plugin->kdbGet (plugin, returned, parentKey) == 1, "kdbGet was not successful");
539 :
540 2 : KeySet * expected = simpleTestKeySet ();
541 2 : compare_keyset (expected, returned);
542 2 : ksDel (expected);
543 2 : ksDel (returned);
544 :
545 2 : keyDel (parentKey);
546 2 : PLUGIN_CLOSE ();
547 2 : }
548 :
549 2 : static void test_mmap_set_get_large_keyset (const char * tmpFile)
550 : {
551 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
552 2 : KeySet * conf = ksNew (0, KS_END);
553 2 : PLUGIN_OPEN ("mmapstorage");
554 2 : KeySet * ks = largeTestKeySet ();
555 2 : KeySet * expected = ksDeepDup (ks);
556 :
557 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
558 2 : ksDel (ks);
559 :
560 2 : KeySet * returned = ksNew (0, KS_END);
561 2 : succeed_if (plugin->kdbGet (plugin, returned, parentKey) == 1, "kdbGet was not successful");
562 :
563 2 : compare_keyset (expected, returned);
564 :
565 2 : ksDel (expected);
566 2 : ksDel (returned);
567 :
568 2 : keyDel (parentKey);
569 2 : PLUGIN_CLOSE ();
570 2 : }
571 :
572 2 : static void test_mmap_ks_copy (const char * tmpFile)
573 : {
574 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
575 2 : KeySet * conf = ksNew (0, KS_END);
576 2 : PLUGIN_OPEN ("mmapstorage");
577 2 : KeySet * returned = simpleTestKeySet ();
578 :
579 2 : succeed_if (plugin->kdbSet (plugin, returned, parentKey) == 1, "kdbGet was not successful");
580 2 : ksDel (returned);
581 :
582 2 : returned = ksNew (0, KS_END);
583 2 : succeed_if (plugin->kdbGet (plugin, returned, parentKey) == 1, "kdbGet was not successful");
584 :
585 2 : KeySet * expected = simpleTestKeySet ();
586 2 : compare_keyset (expected, returned);
587 :
588 2 : KeySet * copiedKs = ksNew (0, KS_END);
589 2 : ksCopy (copiedKs, returned);
590 2 : compare_keyset (expected, copiedKs);
591 :
592 2 : ksDel (copiedKs);
593 2 : ksDel (expected);
594 2 : ksDel (returned);
595 :
596 2 : keyDel (parentKey);
597 2 : PLUGIN_CLOSE ();
598 2 : }
599 :
600 2 : static void test_mmap_empty_after_clear (const char * tmpFile)
601 : {
602 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
603 2 : KeySet * conf = ksNew (0, KS_END);
604 2 : PLUGIN_OPEN ("mmapstorage");
605 2 : KeySet * returned = ksNew (0, KS_END);
606 :
607 2 : succeed_if (plugin->kdbGet (plugin, returned, parentKey) == 1, "kdbGet was not successful");
608 :
609 2 : succeed_if (ksGetSize (returned) == 0, "KeySet not empty after clear (or nullptr)");
610 :
611 2 : ksDel (returned);
612 2 : keyDel (parentKey);
613 2 : PLUGIN_CLOSE ();
614 2 : }
615 :
616 2 : static void test_mmap_meta (const char * tmpFile)
617 : {
618 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
619 2 : KeySet * conf = ksNew (0, KS_END);
620 2 : PLUGIN_OPEN ("mmapstorage");
621 2 : KeySet * ks = metaTestKeySet ();
622 :
623 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
624 :
625 2 : KeySet * returned = ksNew (0, KS_END);
626 2 : succeed_if (plugin->kdbGet (plugin, returned, parentKey) == 1, "kdbGet was not successful");
627 :
628 2 : KeySet * expected = metaTestKeySet ();
629 2 : compare_keyset (expected, returned);
630 :
631 2 : ksDel (expected);
632 2 : ksDel (returned);
633 :
634 2 : keyDel (parentKey);
635 2 : ksDel (ks);
636 2 : PLUGIN_CLOSE ();
637 2 : }
638 :
639 2 : static void test_mmap_meta_get_after_reopen (const char * tmpFile)
640 : {
641 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
642 2 : KeySet * conf = ksNew (0, KS_END);
643 2 : PLUGIN_OPEN ("mmapstorage");
644 2 : KeySet * ks = ksNew (0, KS_END);
645 :
646 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful");
647 :
648 2 : KeySet * expected = metaTestKeySet ();
649 2 : compare_keyset (expected, ks);
650 :
651 2 : ksDel (expected);
652 2 : keyDel (parentKey);
653 2 : ksDel (ks);
654 2 : PLUGIN_CLOSE ();
655 2 : }
656 :
657 2 : static void test_mmap_metacopy (const char * tmpFile)
658 : {
659 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
660 2 : KeySet * conf = ksNew (0, KS_END);
661 2 : PLUGIN_OPEN ("mmapstorage");
662 2 : KeySet * ks = metaTestKeySet ();
663 :
664 2 : Key * shareMeta = keyNew (0);
665 2 : keySetMeta (shareMeta, "sharedmeta", "shared meta key test");
666 :
667 : Key * current;
668 2 : ksRewind (ks);
669 10 : while ((current = ksNext (ks)) != 0)
670 : {
671 6 : keyCopyMeta (current, shareMeta, "sharedmeta");
672 : }
673 2 : KeySet * expected = ksDeepDup (ks);
674 :
675 :
676 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
677 :
678 2 : KeySet * returned = ksNew (0, KS_END);
679 2 : succeed_if (plugin->kdbGet (plugin, returned, parentKey) == 1, "kdbGet was not successful");
680 :
681 :
682 2 : compare_keyset (expected, returned);
683 :
684 2 : ksDel (expected);
685 2 : ksDel (returned);
686 :
687 2 : keyDel (parentKey);
688 2 : keyDel (shareMeta);
689 2 : ksDel (ks);
690 2 : PLUGIN_CLOSE ();
691 2 : }
692 :
693 2 : static void test_mmap_ks_copy_with_meta (const char * tmpFile)
694 : {
695 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
696 2 : KeySet * conf = ksNew (0, KS_END);
697 2 : PLUGIN_OPEN ("mmapstorage");
698 2 : KeySet * ks = metaTestKeySet ();
699 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
700 2 : ksDel (ks);
701 :
702 2 : KeySet * returned = ksNew (0, KS_END);
703 :
704 2 : succeed_if (plugin->kdbGet (plugin, returned, parentKey) == 1, "kdbGet was not successful");
705 :
706 2 : KeySet * expected = metaTestKeySet ();
707 2 : compare_keyset (expected, returned);
708 :
709 2 : KeySet * copiedKs = ksNew (0, KS_END);
710 2 : ksCopy (copiedKs, returned);
711 2 : compare_keyset (expected, copiedKs);
712 :
713 2 : ksDel (copiedKs);
714 2 : ksDel (expected);
715 2 : ksDel (returned);
716 :
717 2 : keyDel (parentKey);
718 2 : PLUGIN_CLOSE ();
719 2 : }
720 :
721 2 : static void test_mmap_opmphm (const char * tmpFile)
722 : {
723 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
724 2 : KeySet * conf = ksNew (0, KS_END);
725 2 : PLUGIN_OPEN ("mmapstorage");
726 2 : KeySet * ks = largeTestKeySet ();
727 :
728 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
729 2 : ksDel (ks);
730 :
731 2 : ks = ksNew (0, KS_END);
732 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful");
733 :
734 2 : const char * name = "user/tests/mmapstorage/dir7/key3";
735 2 : Key * found = ksLookupByName (ks, name, KDB_O_OPMPHM);
736 :
737 2 : if (!found)
738 : {
739 0 : yield_error ("Key not found.")
740 : }
741 :
742 : // write keyset with OPMPHM structures
743 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
744 :
745 2 : ksDel (ks);
746 2 : keyDel (parentKey);
747 2 : PLUGIN_CLOSE ();
748 2 : }
749 :
750 4 : static void test_mmap_ksDupFun (const char * tmpFile, KeySet * copyFunction (const KeySet * source))
751 : {
752 4 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
753 4 : KeySet * conf = ksNew (0, KS_END);
754 4 : PLUGIN_OPEN ("mmapstorage");
755 :
756 4 : KeySet * ks = simpleTestKeySet ();
757 4 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
758 4 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful");
759 4 : succeed_if ((ks->flags & KS_FLAG_MMAP_ARRAY) == KS_FLAG_MMAP_ARRAY, "KeySet array not in mmap");
760 :
761 4 : KeySet * dupKs = copyFunction (ks);
762 4 : compare_keyset (dupKs, ks);
763 4 : compare_keyset (ks, dupKs);
764 :
765 4 : ksDel (dupKs);
766 4 : keyDel (parentKey);
767 4 : ksDel (ks);
768 4 : PLUGIN_CLOSE ();
769 4 : }
770 :
771 2 : static void test_mmap_ksCopy (const char * tmpFile)
772 : {
773 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
774 2 : KeySet * conf = ksNew (0, KS_END);
775 2 : PLUGIN_OPEN ("mmapstorage");
776 :
777 2 : KeySet * ks = simpleTestKeySet ();
778 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
779 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful");
780 2 : succeed_if ((ks->flags & KS_FLAG_MMAP_ARRAY) == KS_FLAG_MMAP_ARRAY, "KeySet array not in mmap");
781 :
782 2 : KeySet * copyKs = ksNew (0, KS_END);
783 2 : if (ksCopy (copyKs, ks) == 1)
784 : {
785 2 : compare_keyset (copyKs, ks);
786 2 : compare_keyset (ks, copyKs);
787 : }
788 : else
789 : {
790 0 : yield_error ("ksCopy failed");
791 : }
792 :
793 2 : ksDel (copyKs);
794 2 : keyDel (parentKey);
795 2 : ksDel (ks);
796 2 : PLUGIN_CLOSE ();
797 2 : }
798 :
799 2 : static void test_mmap_open_pipe (void)
800 : {
801 : // try writing to a non-regular file, we simply use a pipe here
802 : int pipefd[2];
803 2 : if (pipe (pipefd) != 0)
804 : {
805 0 : yield_error ("pipe() error");
806 : }
807 : char pipeFile[1024];
808 2 : snprintf (pipeFile, 1024, "/dev/fd/%d", pipefd[1]);
809 2 : pipeFile[1023] = '\0';
810 :
811 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, pipeFile, KEY_END);
812 2 : KeySet * conf = ksNew (0, KS_END);
813 2 : PLUGIN_OPEN ("mmapstorage");
814 :
815 2 : KeySet * ks = simpleTestKeySet ();
816 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR, "kdbSet could write to pipe, but should not");
817 :
818 2 : keyDel (parentKey);
819 2 : ksDel (ks);
820 2 : PLUGIN_CLOSE ();
821 2 : }
822 :
823 2 : static void test_mmap_bad_file_permissions (const char * tmpFile)
824 : {
825 : // try writing to a file with bad permissions
826 2 : if (getuid () == 0 || geteuid () == 0)
827 : {
828 0 : printf ("Skipping file permission test for root.\n");
829 0 : return;
830 : }
831 :
832 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
833 2 : KeySet * conf = ksNew (0, KS_END);
834 2 : PLUGIN_OPEN ("mmapstorage");
835 2 : KeySet * ks = ksNew (0, KS_END);
836 :
837 : FILE * fp;
838 2 : if ((fp = fopen (tmpFile, "r+")) == 0)
839 : {
840 0 : yield_error ("error opening file");
841 : }
842 :
843 : struct stat sbuf;
844 2 : if (stat (tmpFile, &sbuf) == -1)
845 : {
846 0 : yield_error ("stat() error");
847 : }
848 2 : fclose (fp);
849 :
850 2 : if (chmod (tmpFile, 0) != 0)
851 : {
852 0 : yield_error ("chmod() failed");
853 : }
854 :
855 : // open() call should fail because file permissions were wrong
856 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR, "kdbGet did not detect bad file permissions");
857 :
858 2 : if (chmod (tmpFile, sbuf.st_mode) != 0)
859 : {
860 0 : yield_error ("chmod() failed");
861 : }
862 :
863 2 : keyDel (parentKey);
864 2 : ksDel (ks);
865 2 : PLUGIN_CLOSE ();
866 : }
867 :
868 2 : static void test_mmap_unlink (const char * tmpFile)
869 : {
870 : // test file unlinking by overwriting config file while mapped
871 : int parentPipe[2];
872 : int childPipe[2];
873 2 : if (pipe (parentPipe) != 0 || pipe (childPipe) != 0)
874 : {
875 0 : yield_error ("pipe() error");
876 : }
877 :
878 : pid_t pid;
879 : char buf;
880 2 : pid = fork ();
881 :
882 2 : if (pid == -1)
883 : {
884 0 : yield_error ("fork() error");
885 0 : return;
886 : }
887 2 : else if (pid == 0)
888 : {
889 : // child: open a config file and leave it mapped
890 0 : int devnull = open ("/dev/null", O_RDWR);
891 0 : if (devnull == -1) _Exit (EXIT_FAILURE);
892 :
893 : // redirect any communication on standard file descriptors to /dev/null
894 0 : close (STDIN_FILENO);
895 0 : close (STDOUT_FILENO);
896 0 : close (STDERR_FILENO);
897 0 : if (dup (devnull) == -1) _Exit (EXIT_FAILURE);
898 0 : if (dup (devnull) == -1) _Exit (EXIT_FAILURE);
899 0 : if (dup (devnull) == -1) _Exit (EXIT_FAILURE);
900 0 : close (childPipe[0]);
901 0 : close (parentPipe[1]);
902 :
903 0 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
904 0 : KeySet * conf = ksNew (0, KS_END);
905 0 : PLUGIN_OPEN ("mmapstorage");
906 :
907 0 : KeySet * ks = simpleTestKeySet ();
908 0 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
909 0 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful");
910 :
911 0 : if (write (childPipe[1], "a", 1) != 1) _Exit (EXIT_FAILURE); // signal parent that we are ready
912 0 : close (childPipe[1]);
913 0 : if (read (parentPipe[0], &buf, 1) != 1) _Exit (EXIT_FAILURE); // wait for parent
914 0 : close (parentPipe[0]);
915 :
916 0 : KeySet * expected = simpleTestKeySet ();
917 0 : compare_keyset (expected, ks);
918 0 : compare_keyset (ks, expected);
919 0 : ksDel (expected);
920 0 : keyDel (parentKey);
921 0 : ksDel (ks);
922 0 : PLUGIN_CLOSE ();
923 :
924 0 : _Exit (EXIT_SUCCESS);
925 : }
926 : else
927 : {
928 : // parent: try and destroy the file that the child has mapped
929 2 : close (childPipe[1]);
930 2 : close (parentPipe[0]);
931 2 : if (read (childPipe[0], &buf, 1) != 1) _Exit (EXIT_FAILURE); // wait for child
932 2 : close (childPipe[0]);
933 :
934 2 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
935 2 : KeySet * conf = ksNew (0, KS_END);
936 2 : PLUGIN_OPEN ("mmapstorage");
937 :
938 2 : KeySet * ks = metaTestKeySet ();
939 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
940 2 : if (write (parentPipe[1], "a", 1) != 1) _Exit (EXIT_FAILURE); // signal child that we are done
941 2 : close (parentPipe[1]);
942 :
943 : int status;
944 2 : waitpid (pid, &status, 0);
945 2 : if (status != 0) yield_error ("child process did not exit successfully.");
946 :
947 2 : keyDel (parentKey);
948 2 : ksDel (ks);
949 2 : PLUGIN_CLOSE ();
950 : }
951 : }
952 :
953 20 : static void clearStorage (const char * tmpFile)
954 : {
955 20 : Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END);
956 20 : KeySet * conf = ksNew (0, KS_END);
957 20 : PLUGIN_OPEN ("mmapstorage");
958 20 : KeySet * ks = ksNew (0, KS_END);
959 :
960 20 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful");
961 :
962 20 : keyDel (parentKey);
963 20 : ksDel (ks);
964 20 : PLUGIN_CLOSE ();
965 20 : }
966 :
967 : /* -- DynArray Tests -------------------------------------------------------------------------------------------------------------------- */
968 :
969 1080 : static int cmpfunc (const void * a, const void * b)
970 : {
971 1080 : return (*(int *) a - *(int *) b);
972 : }
973 :
974 2 : static void testDynArray (void)
975 : {
976 2 : size_t testData[] = { 8466, 2651, 6624, 9575, 4628, 9361, 417, 8932, 4570, 343, 1866, 3135, 6617, 344, 9419, 2094, 5623,
977 : 4920, 2209, 8037, 8437, 7955, 5575, 8355, 1133, 6527, 8543, 3338, 1772, 2278, 7446, 8834, 7728, 665,
978 : 8519, 6079, 5060, 7429, 3843, 6923, 4073, 2245, 2784, 6620, 2887, 8497, 9360, 5752, 3195, 538, 1491,
979 : 8087, 8378, 5746, 4961, 5499, 8050, 2138, 1196, 1860, 4372, 6553, 4530, 8828, 4017, 9934, 3, 6274,
980 : 4405, 5021, 3416, 854, 4635, 9902, 5383, 7947, 5210, 8242, 1928, 3792, 7234, 759, 6571, 9514, 8451,
981 : 918, 9958, 1577, 96, 8644, 6815, 5584, 8585, 1252, 808, 5695, 910, 4157, 701, 77 };
982 :
983 2 : DynArray * dynArray = ELEKTRA_PLUGIN_FUNCTION (dynArrayNew) ();
984 :
985 202 : for (size_t i = 0; i < 100; ++i)
986 : {
987 200 : ELEKTRA_PLUGIN_FUNCTION (dynArrayFindOrInsert) ((Key *) testData[i], dynArray);
988 : }
989 :
990 2 : qsort (testData, 100, sizeof (size_t), cmpfunc);
991 :
992 2 : int error = 0;
993 202 : for (size_t i = 0; i < 100; ++i)
994 : {
995 200 : if (testData[i] != (size_t) dynArray->keyArray[i])
996 : {
997 0 : ++error;
998 : }
999 : }
1000 :
1001 2 : succeed_if (error == 0, "dynArray does not sort array properly");
1002 :
1003 2 : ELEKTRA_PLUGIN_FUNCTION (dynArrayDelete) (dynArray);
1004 2 : }
1005 :
1006 : /* -- Main ------------------------------------------------------------------------------------------------------------------------------ */
1007 :
1008 2 : int main (int argc, char ** argv)
1009 : {
1010 2 : printf ("MMAPSTORAGE TESTS\n");
1011 2 : printf ("==================\n\n");
1012 :
1013 2 : init (argc, argv);
1014 :
1015 2 : testDynArray ();
1016 :
1017 2 : const char * tmpFile = elektraFilename ();
1018 2 : printf ("%s\n", tmpFile);
1019 :
1020 : // call once before clearStorage, to test non existent file
1021 2 : test_mmap_get_set (tmpFile);
1022 :
1023 2 : test_mmap_set_get_global (tmpFile);
1024 2 : test_mmap_get_global_after_reopen (tmpFile);
1025 2 : test_mmap_set_get_global_metadata (tmpFile);
1026 :
1027 2 : clearStorage (tmpFile);
1028 2 : test_mmap_truncated_file (tmpFile);
1029 2 : test_mmap_wrong_magic_number (tmpFile);
1030 2 : test_mmap_wrong_format_version (tmpFile);
1031 2 : test_mmap_wrong_magic_keyset (tmpFile);
1032 :
1033 2 : clearStorage (tmpFile);
1034 2 : test_mmap_get_set_empty (tmpFile);
1035 :
1036 2 : clearStorage (tmpFile);
1037 2 : test_mmap_get_set (tmpFile);
1038 :
1039 2 : clearStorage (tmpFile);
1040 2 : test_mmap_set_get (tmpFile);
1041 2 : test_mmap_get_after_reopen (tmpFile);
1042 2 : test_mmap_set_get_large_keyset (tmpFile);
1043 2 : test_mmap_ks_copy (tmpFile);
1044 :
1045 2 : clearStorage (tmpFile);
1046 2 : test_mmap_empty_after_clear (tmpFile);
1047 :
1048 2 : test_mmap_meta (tmpFile);
1049 2 : test_mmap_meta_get_after_reopen (tmpFile);
1050 :
1051 2 : test_mmap_metacopy (tmpFile);
1052 :
1053 2 : clearStorage (tmpFile);
1054 2 : test_mmap_ks_copy_with_meta (tmpFile);
1055 :
1056 2 : clearStorage (tmpFile);
1057 2 : test_mmap_opmphm (tmpFile);
1058 :
1059 2 : clearStorage (tmpFile);
1060 2 : test_mmap_ksDupFun (tmpFile, ksDup);
1061 :
1062 2 : clearStorage (tmpFile);
1063 2 : test_mmap_ksDupFun (tmpFile, ksDeepDup);
1064 :
1065 2 : clearStorage (tmpFile);
1066 2 : test_mmap_ksCopy (tmpFile);
1067 :
1068 2 : test_mmap_open_pipe ();
1069 2 : test_mmap_bad_file_permissions (tmpFile);
1070 :
1071 2 : test_mmap_unlink (tmpFile);
1072 :
1073 2 : printf ("\ntestmod_mmapstorage RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError);
1074 :
1075 2 : return nbError;
1076 : }
|