Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Interna of splitting functionality.
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #ifdef HAVE_KDBCONFIG_H
10 : #include "kdbconfig.h"
11 : #endif
12 :
13 : #if VERBOSE && defined(HAVE_STDIO_H)
14 : #include <stdio.h>
15 : #endif
16 :
17 : #ifdef HAVE_STDLIB_H
18 : #include <stdlib.h>
19 : #endif
20 :
21 : #ifdef HAVE_STDARG_H
22 : #include <stdarg.h>
23 : #endif
24 :
25 : #ifdef HAVE_CTYPE_H
26 : #include <ctype.h>
27 : #endif
28 :
29 : #ifdef HAVE_STRING_H
30 : #include <string.h>
31 : #endif
32 :
33 : #ifdef HAVE_ERRNO_H
34 : #include <errno.h>
35 : #endif
36 :
37 : #include <kdbassert.h>
38 : #include <kdberrors.h>
39 : #include <kdbinternal.h>
40 :
41 :
42 : /**
43 : * Allocates a new split object.
44 : *
45 : * Splits up a keyset into multiple keysets where each
46 : * of them will passed to the correct kdbSet().
47 : *
48 : * Initially the size is 0 and alloc is APPROXIMATE_NR_OF_BACKENDS.
49 : *
50 : * @return a fresh allocated split object
51 : * @ingroup split
52 : * @see splitDel()
53 : **/
54 50155 : Split * splitNew (void)
55 : {
56 50155 : Split * ret = elektraCalloc (sizeof (Split));
57 :
58 50155 : ret->size = 0;
59 50155 : ret->alloc = APPROXIMATE_NR_OF_BACKENDS;
60 :
61 50155 : ret->keysets = elektraCalloc (sizeof (KeySet *) * ret->alloc);
62 50155 : ret->handles = elektraCalloc (sizeof (KDB *) * ret->alloc);
63 50155 : ret->parents = elektraCalloc (sizeof (Key *) * ret->alloc);
64 50155 : ret->syncbits = elektraCalloc (sizeof (int) * ret->alloc);
65 :
66 50155 : return ret;
67 : }
68 :
69 :
70 : /**
71 : * Delete a split object.
72 : *
73 : * Will free all allocated resources of a split keyset.
74 : *
75 : * @param keysets the split object to work with
76 : * @ingroup split
77 : */
78 50155 : void splitDel (Split * keysets)
79 : {
80 261224 : for (size_t i = 0; i < keysets->size; ++i)
81 : {
82 211069 : ksDel (keysets->keysets[i]);
83 211069 : keyDecRef (keysets->parents[i]);
84 211069 : keyDel (keysets->parents[i]);
85 : }
86 50155 : elektraFree (keysets->keysets);
87 50155 : elektraFree (keysets->handles);
88 50155 : elektraFree (keysets->parents);
89 50155 : elektraFree (keysets->syncbits);
90 50155 : elektraFree (keysets);
91 50155 : }
92 :
93 : /**
94 : * @brief Remove one part of split.
95 : *
96 : * @param split the split object to work with
97 : * @param where the position to cut away
98 : *
99 : * @pre where must be within the size of the split
100 : * @post split will be removed
101 : *
102 : * @ingroup split
103 : */
104 871 : void splitRemove (Split * split, size_t where)
105 : {
106 871 : ELEKTRA_ASSERT (where < split->size, "cannot remove behind size: %zu smaller than %zu", where, split->size);
107 871 : ksDel (split->keysets[where]);
108 871 : keyDel (split->parents[where]);
109 871 : --split->size; // reduce size
110 3140 : for (size_t i = where; i < split->size; ++i)
111 : {
112 2269 : split->keysets[i] = split->keysets[i + 1];
113 2269 : split->handles[i] = split->handles[i + 1];
114 2269 : split->parents[i] = split->parents[i + 1];
115 2269 : split->syncbits[i] = split->syncbits[i + 1];
116 : }
117 871 : }
118 :
119 : /**
120 : * Doubles the size of how many parts of keysets can be appended.
121 : *
122 : * @param split the split object to work with
123 : * @ingroup split
124 : */
125 686 : static void splitResize (Split * split)
126 : {
127 686 : split->alloc *= 2;
128 :
129 686 : elektraRealloc ((void **) &split->keysets, split->alloc * sizeof (KeySet *));
130 686 : elektraRealloc ((void **) &split->handles, split->alloc * sizeof (KDB *));
131 686 : elektraRealloc ((void **) &split->parents, split->alloc * sizeof (Key *));
132 686 : elektraRealloc ((void **) &split->syncbits, split->alloc * sizeof (int));
133 686 : }
134 :
135 : /**
136 : * Increases the size of split and appends a new empty keyset.
137 : *
138 : * Initializes the element with the given parameters
139 : * at size-1 to be used.
140 : *
141 : * Will automatically resize split if needed.
142 : *
143 : * @param split the split object to work with
144 : * @param backend the backend which should be appended
145 : * @param parentKey the parentKey which should be appended
146 : * @param syncbits the initial syncstate which should be appended
147 : * @ingroup split
148 : * @retval -1 if no split is found
149 : * @return the position of the new element: size-1
150 : */
151 211940 : ssize_t splitAppend (Split * split, Backend * backend, Key * parentKey, int syncbits)
152 : {
153 211940 : if (!split)
154 : {
155 : /* To make test cases work & valgrind clean */
156 0 : keyDel (parentKey);
157 0 : return -1;
158 : }
159 :
160 211940 : ++split->size;
161 211940 : if (split->size > split->alloc) splitResize (split);
162 :
163 : // index of the new element
164 211940 : const int n = split->size - 1;
165 :
166 211940 : split->keysets[n] = ksNew (0, KS_END);
167 211940 : split->handles[n] = backend;
168 211940 : split->parents[n] = parentKey;
169 211940 : split->syncbits[n] = syncbits;
170 :
171 211940 : return n;
172 : }
173 :
174 : /**
175 : * Determines if the backend is already inserted or not.
176 : *
177 : * @warning If no parent Key is given, the default/root backends won't
178 : * be searched.
179 : *
180 : * @param split the split object to work with
181 : * @param backend the backend to search for
182 : * @param parent the key to check for domains in default/root backends.
183 : * @return pos of backend if it already exist
184 : * @retval -1 if it does not exist
185 : * @ingroup split
186 : */
187 194189 : static ssize_t splitSearchBackend (Split * split, Backend * backend, Key * parent)
188 : {
189 249139 : for (size_t i = 0; i < split->size; ++i)
190 : {
191 241205 : if (backend == split->handles[i])
192 : {
193 188106 : if (test_bit (split->syncbits[i], SPLIT_FLAG_CASCADING))
194 : {
195 185470 : switch (keyGetNamespace (parent))
196 : {
197 : case KEY_NS_SPEC:
198 192 : if (keyIsSpec (split->parents[i])) return i;
199 : break;
200 : case KEY_NS_DIR:
201 285 : if (keyIsDir (split->parents[i])) return i;
202 : break;
203 : case KEY_NS_USER:
204 143259 : if (keyIsUser (split->parents[i])) return i;
205 : break;
206 : case KEY_NS_SYSTEM:
207 30397 : if (keyIsSystem (split->parents[i])) return i;
208 : break;
209 : case KEY_NS_PROC:
210 : return -1;
211 : case KEY_NS_EMPTY:
212 : return -1;
213 : case KEY_NS_NONE:
214 : return -1;
215 : case KEY_NS_META:
216 : return -1;
217 : case KEY_NS_CASCADING:
218 : return -1;
219 : }
220 1851 : continue;
221 : }
222 : /* We already have this backend, so leave */
223 2636 : return i;
224 : }
225 : }
226 : return -1;
227 : }
228 :
229 : /**
230 : * @brief Map namespace to string and decide if it should be used for kdbGet()
231 : *
232 : * @param parentKey the key name that should be changed
233 : * @param ns the namespace it should be changed to
234 : *
235 : * @retval 0 invalid namespace for kdbGet(), no action required
236 : * @retval 1 valid namespace for kdbGet()
237 : */
238 59235 : static int elektraKeySetNameByNamespace (Key * parentKey, elektraNamespace ns)
239 : {
240 59235 : switch (ns)
241 : {
242 : case KEY_NS_SPEC:
243 11847 : keySetName (parentKey, "spec");
244 11847 : break;
245 : case KEY_NS_PROC:
246 : /* only transient, should fail */
247 : return 0;
248 : case KEY_NS_DIR:
249 11847 : keySetName (parentKey, "dir");
250 11847 : break;
251 : case KEY_NS_USER:
252 11847 : keySetName (parentKey, "user");
253 11847 : break;
254 : case KEY_NS_SYSTEM:
255 11847 : keySetName (parentKey, "system");
256 11847 : break;
257 : case KEY_NS_EMPTY:
258 : case KEY_NS_NONE:
259 : case KEY_NS_META:
260 : case KEY_NS_CASCADING:
261 : return 0;
262 : }
263 : return 1;
264 : }
265 :
266 :
267 : /**
268 : * Walks through kdb->split and adds all backends below parentKey to split.
269 : *
270 : * Sets syncbits to 2 if it is a default or root backend (which needs splitting).
271 : * The information is copied from kdb->split.
272 : *
273 : * @pre split needs to be empty, directly after creation with splitNew().
274 : *
275 : * @pre there needs to be a valid defaultBackend
276 : * but its ok not to have a trie inside KDB.
277 : *
278 : * @pre parentKey must be a valid key! (could be implemented more generally,
279 : * but that would require splitting up of keysets of the same backend)
280 : *
281 : * @param split will get all backends appended
282 : * @param kdb the handle to get information about backends
283 : * @param parentKey the information below which key the backends are from interest
284 : * @ingroup split
285 : * @retval 1 always
286 : */
287 76398 : int splitBuildup (Split * split, KDB * kdb, Key * parentKey)
288 : {
289 : /* For compatibility reasons invalid names are accepted, too.
290 : * This solution is faster than checking the name of parentKey
291 : * every time in loop.
292 : * The parentKey might be null in some unit tests, so also check
293 : * for this. */
294 76398 : const char * name = keyName (parentKey);
295 76398 : if (!parentKey || !name || !strcmp (name, "") || !strcmp (name, "/"))
296 : {
297 : parentKey = 0;
298 : }
299 76345 : else if (name[0] == '/')
300 : {
301 11847 : Key * key = keyNew (0, KEY_END);
302 71082 : for (elektraNamespace ins = KEY_NS_FIRST; ins <= KEY_NS_LAST; ++ins)
303 : {
304 59235 : if (!elektraKeySetNameByNamespace (key, ins)) continue;
305 47388 : keyAddName (key, keyName (parentKey));
306 47388 : splitBuildup (split, kdb, key);
307 : }
308 11847 : keyDel (key);
309 11847 : return 1;
310 : }
311 :
312 : /* Returns the backend the key is in or the default backend
313 : otherwise */
314 64551 : Backend * backend = mountGetBackend (kdb, parentKey);
315 :
316 : #if DEBUG && VERBOSE
317 : printf (" with parent %s\n", keyName (parentKey));
318 : #endif
319 706906 : for (size_t i = 0; i < kdb->split->size; ++i)
320 : {
321 : #if DEBUG && VERBOSE
322 : printf (" %zu with parent %s\n", i, keyName (kdb->split->parents[i]));
323 : #endif
324 642355 : if (!parentKey)
325 : {
326 : #if DEBUG && VERBOSE
327 : printf (" def add %s\n", keyName (kdb->split->parents[i]));
328 : #endif
329 : /* Catch all: add all mountpoints */
330 359 : splitAppend (split, kdb->split->handles[i], keyDup (kdb->split->parents[i]), kdb->split->syncbits[i]);
331 : }
332 641996 : else if (backend == kdb->split->handles[i] && keyRel (kdb->split->parents[i], parentKey) >= 0)
333 : {
334 : #if DEBUG && VERBOSE
335 : printf (" exa add %s\n", keyName (kdb->split->parents[i]));
336 : #endif
337 : /* parentKey is exactly in this backend, so add it! */
338 64498 : splitAppend (split, kdb->split->handles[i], keyDup (kdb->split->parents[i]), kdb->split->syncbits[i]);
339 : }
340 577498 : else if (keyRel (parentKey, kdb->split->parents[i]) >= 0)
341 : {
342 : #if DEBUG && VERBOSE
343 : printf (" rel add %s\n", keyName (kdb->split->parents[i]));
344 : #endif
345 : /* this backend is completely below the parentKey, so lets add it. */
346 1117 : splitAppend (split, kdb->split->handles[i], keyDup (kdb->split->parents[i]), kdb->split->syncbits[i]);
347 : }
348 : }
349 :
350 : return 1;
351 : }
352 :
353 :
354 : /**
355 : * Splits up the keysets and search for a sync bit in every key.
356 : *
357 : * It does not check if there were removed keys,
358 : * see splitSync() for the next step.
359 : *
360 : * It does not create new backends, this has to be
361 : * done by buildup before.
362 : *
363 : * @pre splitBuildup() need to be executed before.
364 : *
365 : * @param split the split object to work with
366 : * @param handle to get information where the individual keys belong
367 : * @param ks the keyset to divide
368 : *
369 : * @retval 0 if there were no sync bits
370 : * @retval 1 if there were sync bits
371 : * @retval -1 if no backend was found for any key
372 : * @ingroup split
373 : */
374 2810 : int splitDivide (Split * split, KDB * handle, KeySet * ks)
375 : {
376 2810 : int needsSync = 0;
377 2810 : Key * curKey = 0;
378 :
379 2810 : ksRewind (ks);
380 173228 : while ((curKey = ksNext (ks)) != 0)
381 : {
382 : // TODO: handle keys in wrong namespaces
383 167608 : Backend * curHandle = mountGetBackend (handle, curKey);
384 167608 : if (!curHandle) return -1;
385 :
386 : /* If key could be appended to any of the existing split keysets */
387 167608 : ssize_t curFound = splitSearchBackend (split, curHandle, curKey);
388 :
389 167608 : if (curFound == -1)
390 : {
391 : ELEKTRA_LOG_DEBUG ("SKIPPING NOT RELEVANT KEY: %p key: %s, string: %s", (void *) curKey, keyName (curKey),
392 : keyString (curKey));
393 7571 : continue; // key not relevant in this kdbSet
394 : }
395 :
396 160037 : ksAppendKey (split->keysets[curFound], curKey);
397 160037 : if (keyNeedSync (curKey) == 1)
398 : {
399 10733 : split->syncbits[curFound] |= 1;
400 10733 : needsSync = 1;
401 : }
402 : }
403 :
404 : return needsSync;
405 : }
406 :
407 : /**
408 : * @brief Update the (configuration) file name for the parent key
409 : *
410 : * @param split the split to work with
411 : * @param handle the handle to work with
412 : * @param key the parentKey that should be updated (name must be
413 : * correct)
414 : */
415 26178 : void splitUpdateFileName (Split * split, KDB * handle, Key * key)
416 : {
417 26178 : Backend * curHandle = mountGetBackend (handle, key);
418 26178 : if (!curHandle) return;
419 26178 : ssize_t curFound = splitSearchBackend (split, curHandle, key);
420 26178 : if (curFound == -1) return;
421 : #if DEBUG && VERBOSE
422 : printf ("Update string from %s to %s\n", keyString (key), keyString (split->parents[curFound]));
423 : printf ("Names are: %s and %s\n\n", keyName (key), keyName (split->parents[curFound]));
424 : #endif
425 14651 : keySetString (key, keyString (split->parents[curFound]));
426 : }
427 :
428 :
429 : /**
430 : * Appoints all keys from ks to yet unsynced splits.
431 : *
432 : * @pre splitBuildup() need to be executed before.
433 : *
434 : * @param split the split object to work with
435 : * @param handle to determine to which backend a key belongs
436 : * @param ks the keyset to appoint to split
437 : *
438 : * @retval 1 on success
439 : * @retval -1 if no backend was found for a key
440 : * @ingroup split
441 : */
442 15784 : int splitAppoint (Split * split, KDB * handle, KeySet * ks)
443 : {
444 15784 : Key * curKey = 0;
445 15784 : ssize_t defFound = splitAppend (split, 0, 0, 0);
446 :
447 15784 : ksRewind (ks);
448 31971 : while ((curKey = ksNext (ks)) != 0)
449 : {
450 403 : Backend * curHandle = mountGetBackend (handle, curKey);
451 403 : if (!curHandle) return -1;
452 :
453 : /* If key could be appended to any of the existing split keysets */
454 403 : ssize_t curFound = splitSearchBackend (split, curHandle, curKey);
455 :
456 403 : if (curFound == -1) curFound = defFound;
457 :
458 403 : if (split->syncbits[curFound] & SPLIT_FLAG_SYNC)
459 : {
460 136 : continue;
461 : }
462 :
463 267 : ksAppendKey (split->keysets[curFound], curKey);
464 : }
465 :
466 : return 1;
467 : }
468 :
469 6 : static void elektraDropCurrentKey (KeySet * ks, Key * warningKey, const Backend * curHandle, const Backend * otherHandle, const char * msg)
470 : {
471 6 : const Key * k = ksCurrent (ks);
472 :
473 6 : const size_t sizeOfStaticText = 300;
474 6 : size_t size = keyGetNameSize (curHandle->mountpoint) + keyGetValueSize (curHandle->mountpoint) + keyGetNameSize (k) + strlen (msg) +
475 : sizeOfStaticText;
476 6 : if (otherHandle)
477 : {
478 2 : size += keyGetNameSize (otherHandle->mountpoint) + keyGetValueSize (otherHandle->mountpoint);
479 : }
480 6 : char * warningMsg = elektraMalloc (size);
481 6 : strcpy (warningMsg, "drop key ");
482 6 : const char * name = keyName (k);
483 6 : if (name)
484 : {
485 6 : strcat (warningMsg, name);
486 : }
487 : else
488 : {
489 0 : strcat (warningMsg, "(no name)");
490 : }
491 6 : strcat (warningMsg, " not belonging to \"");
492 6 : strcat (warningMsg, keyName (curHandle->mountpoint));
493 6 : strcat (warningMsg, "\" with name \"");
494 6 : strcat (warningMsg, keyString (curHandle->mountpoint));
495 6 : if (otherHandle)
496 : {
497 2 : strcat (warningMsg, "\" but instead to \"");
498 2 : strcat (warningMsg, keyName (otherHandle->mountpoint));
499 2 : strcat (warningMsg, "\" with name \"");
500 2 : strcat (warningMsg, keyString (otherHandle->mountpoint));
501 : }
502 6 : strcat (warningMsg, "\" because ");
503 6 : strcat (warningMsg, msg);
504 6 : ELEKTRA_ADD_INTERFACE_WARNINGF (warningKey, "Postcondition of backend was violated: %s", warningMsg);
505 6 : elektraFree (warningMsg);
506 6 : cursor_t c = ksGetCursor (ks);
507 6 : keyDel (elektraKsPopAtCursor (ks, c));
508 6 : ksSetCursor (ks, c);
509 6 : elektraKsPrev (ks); // next ksNext() will point correctly again
510 6 : }
511 :
512 :
513 : /**
514 : * @brief Filter out keys not in the correct keyset
515 : *
516 : * @param split the split where to do it
517 : * @param i for which split
518 : * @param warningKey the key
519 : * @param handle where to do backend lookups
520 : *
521 : * @retval -1 on error (no backend, wrong namespace)
522 : * @retval 0 otherwise
523 : */
524 23408 : static int elektraSplitPostprocess (Split * split, int i, Key * warningKey, KDB * handle)
525 : {
526 23408 : Key * cur = 0;
527 23408 : ksRewind (split->keysets[i]);
528 215302 : while ((cur = ksNext (split->keysets[i])) != 0)
529 : {
530 168486 : Backend * curHandle = mountGetBackend (handle, cur);
531 168486 : if (!curHandle) return -1;
532 :
533 168486 : keyClearSync (cur);
534 :
535 168486 : if (curHandle != split->handles[i])
536 : {
537 2 : elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, split->handles[i],
538 : "it is hidden by other mountpoint");
539 : }
540 : else
541 168484 : switch (keyGetNamespace (cur))
542 : {
543 : case KEY_NS_SPEC:
544 517 : if (!keyIsSpec (split->parents[i]))
545 0 : elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, 0, "it is not spec");
546 : break;
547 : case KEY_NS_DIR:
548 49 : if (!keyIsDir (split->parents[i]))
549 0 : elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, 0, "it is not dir");
550 : break;
551 : case KEY_NS_USER:
552 5813 : if (!keyIsUser (split->parents[i]))
553 0 : elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, 0, "it is not user");
554 : break;
555 : case KEY_NS_SYSTEM:
556 162105 : if (!keyIsSystem (split->parents[i]))
557 4 : elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, 0, "it is not system");
558 : break;
559 : case KEY_NS_PROC:
560 0 : elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, 0, "it has a proc key name");
561 0 : break;
562 : case KEY_NS_EMPTY:
563 0 : elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, 0, "it has an empty name");
564 0 : break;
565 : case KEY_NS_META:
566 0 : elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, 0, "it has a metaname");
567 0 : break;
568 : case KEY_NS_CASCADING:
569 0 : elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, 0, "it has a cascading name");
570 0 : break;
571 : case KEY_NS_NONE:
572 0 : ELEKTRA_ASSERT (0, "wrong key namespace `none'");
573 : return -1;
574 : }
575 : }
576 : return 0;
577 : }
578 :
579 :
580 : /**
581 : * Does some work after getting of backends is finished.
582 : *
583 : * - Update sizes
584 : * - Removal of wrong keys
585 : *
586 : * @pre splitAppoint() needs to be executed before.
587 : *
588 : * - check if keys are in correct backend
589 : * - remove syncbits
590 : * - update sizes in the backends
591 : *
592 : * @param split the split object to work with
593 : * @param warningKey postcondition violations are reported here
594 : * @param handle the handle to preprocess the keys
595 : * @retval 1 on success
596 : * @retval -1 if no backend was found for a key or split->parents
597 : * has invalid namespace
598 : * @ingroup split
599 : */
600 15767 : int splitGet (Split * split, Key * warningKey, KDB * handle)
601 : {
602 15767 : int ret = 1;
603 : /* Dont iterate the default split part */
604 15767 : const int bypassedSplits = 1;
605 :
606 39175 : for (size_t i = 0; i < split->size - bypassedSplits; ++i)
607 : {
608 : if (test_bit (split->syncbits[i], 0))
609 : {
610 : /* Dont process keysets which come from the user
611 : and not from the backends */
612 : continue;
613 : }
614 :
615 : /* Update sizes */
616 : #if DEBUG && VERBOSE
617 : printf ("Update size for %s\n", keyName (split->parents[i]));
618 : #endif
619 : // first we need postprocessing because that might
620 : // reduce sizes
621 23408 : if (elektraSplitPostprocess (split, i, warningKey, handle) == -1) ret = -1;
622 : // then we can set the size
623 : ELEKTRA_LOG_DEBUG ("splitGet : backendUpdateSize thingy");
624 23408 : if (backendUpdateSize (split->handles[i], split->parents[i], ksGetSize (split->keysets[i])) == -1) ret = -1;
625 : }
626 :
627 15767 : return ret;
628 : }
629 :
630 : /**
631 : * Also update sizes after kdbSet() to recognize multiple kdbSet() attempts.
632 : *
633 : * @warning cant use the same code with splitGet because there is
634 : * no default split part for kdbSet().
635 : */
636 2535 : int splitUpdateSize (Split * split)
637 : {
638 : /* Iterate everything */
639 5104 : for (size_t i = 0; i < split->size; ++i)
640 : {
641 2569 : switch (keyGetNamespace (split->parents[i]))
642 : {
643 : case KEY_NS_SPEC:
644 56 : split->handles[i]->specsize = ksGetSize (split->keysets[i]);
645 56 : break;
646 : case KEY_NS_DIR:
647 8 : split->handles[i]->dirsize = ksGetSize (split->keysets[i]);
648 8 : break;
649 : case KEY_NS_USER:
650 1295 : split->handles[i]->usersize = ksGetSize (split->keysets[i]);
651 1295 : break;
652 : case KEY_NS_SYSTEM:
653 1210 : split->handles[i]->systemsize = ksGetSize (split->keysets[i]);
654 1210 : break;
655 : case KEY_NS_PROC:
656 : case KEY_NS_EMPTY:
657 : case KEY_NS_NONE:
658 : case KEY_NS_META:
659 : case KEY_NS_CASCADING:
660 : return -1;
661 : }
662 : }
663 : return 1;
664 : }
665 :
666 : /**
667 : * Merges together the backend based parts of split into dest,
668 : * but bypasses the default split.
669 : *
670 : * @param split the split object to work with
671 : * @param dest the destination keyset where all keysets are appended.
672 : * @retval 1 on success
673 : * @ingroup split
674 : */
675 15747 : int splitMergeBackends (Split * split, KeySet * dest)
676 : {
677 : /* Bypass default split */
678 15751 : const int bypassedSplits = 1;
679 39133 : for (size_t i = 0; i < split->size - bypassedSplits; ++i)
680 : {
681 : if (test_bit (split->syncbits[i], 0))
682 : {
683 : /* Dont process keysets which come from the user
684 : and not from the backends */
685 : continue;
686 : }
687 23382 : ksAppend (dest, split->keysets[i]);
688 : }
689 15747 : return 1;
690 : }
691 :
692 : /**
693 : * Merges the default split into dest.
694 : *
695 : * @param split the split object to work with
696 : * @param dest the destination keyset where all keysets are appended.
697 : * @retval 1 on success
698 : * @ingroup split
699 : */
700 15744 : int splitMergeDefault (Split * split, KeySet * dest)
701 : {
702 : /* Merge default split */
703 15748 : ksAppend (dest, split->keysets[split->size - 1]);
704 15744 : return 1;
705 : }
706 :
707 : /** Add sync bits everywhere keys were removed/added.
708 : *
709 : * - checks if the size of a previous kdbGet() is unchanged.
710 : * - checks if in correct state (kdbGet() needs to be executed before)
711 : *
712 : * Only splitDivide() together with this function can really decide
713 : * if sync is needed or not.
714 : *
715 : * @pre split needs to be processed with splitDivide() before.
716 : *
717 : * @retval 0 if kdbSet() is not needed
718 : * @retval 1 if kdbSet() is needed
719 : * @retval -1 on wrong keys (also has assert, should not happen)
720 : * @retval -2 wrong spec state: kdbGet() was not executed before
721 : * @retval -3 wrong dir state: kdbGet() was not executed before
722 : * @retval -4 wrong user state: kdbGet() was not executed before
723 : * @retval -5 wrong system state: kdbGet() was not executed before
724 : * @pre user/system was split before.
725 : * @param split the split object to work with
726 : * @ingroup split
727 : *
728 : **/
729 2786 : int splitSync (Split * split)
730 : {
731 2786 : int needsSync = 0;
732 :
733 6842 : for (size_t i = 0; i < split->size; ++i)
734 : {
735 : // first check for wrong states etc.
736 4068 : switch (keyGetNamespace (split->parents[i]))
737 : {
738 : case KEY_NS_SPEC:
739 : // Check if we are in correct state
740 405 : if (split->handles[i]->specsize == -1)
741 : {
742 6 : return -(int) i - 2;
743 : }
744 : /* Check for spec keyset for removed keys */
745 399 : if (split->handles[i]->specsize != ksGetSize (split->keysets[i]))
746 : {
747 55 : set_bit (split->syncbits[i], SPLIT_FLAG_SYNC);
748 55 : needsSync = 1;
749 : }
750 : break;
751 : case KEY_NS_DIR:
752 : // Check if we are in correct state
753 379 : if (split->handles[i]->dirsize == -1)
754 : {
755 0 : return -(int) i - 2;
756 : }
757 : /* Check for dir keyset for removed keys */
758 379 : if (split->handles[i]->dirsize != ksGetSize (split->keysets[i]))
759 : {
760 12 : set_bit (split->syncbits[i], SPLIT_FLAG_SYNC);
761 12 : needsSync = 1;
762 : }
763 : break;
764 : case KEY_NS_USER:
765 : // Check if we are in correct state
766 1718 : if (split->handles[i]->usersize == -1)
767 : {
768 4 : return -(int) i - 2;
769 : }
770 : /* Check for user keyset for removed keys */
771 1714 : if (split->handles[i]->usersize != ksGetSize (split->keysets[i]))
772 : {
773 590 : set_bit (split->syncbits[i], SPLIT_FLAG_SYNC);
774 590 : needsSync = 1;
775 : }
776 : break;
777 : case KEY_NS_SYSTEM:
778 : // Check if we are in correct state
779 1566 : if (split->handles[i]->systemsize == -1)
780 : {
781 2 : return -(int) i - 2;
782 : }
783 : /* Check for system keyset for removed keys */
784 1564 : if (split->handles[i]->systemsize != ksGetSize (split->keysets[i]))
785 : {
786 1267 : set_bit (split->syncbits[i], SPLIT_FLAG_SYNC);
787 1267 : needsSync = 1;
788 : }
789 : break;
790 : case KEY_NS_PROC:
791 : case KEY_NS_EMPTY:
792 : case KEY_NS_META:
793 : case KEY_NS_CASCADING:
794 : case KEY_NS_NONE:
795 0 : ELEKTRA_ASSERT (0, "Got keys in wrong namespace %d in split %zu", keyGetNamespace (split->parents[i]), i);
796 : return -1;
797 : }
798 : }
799 :
800 : return needsSync;
801 : }
802 :
803 : /** Prepares for kdbSet() mainloop afterwards.
804 : *
805 : * All splits which do not need sync are removed and a deep copy
806 : * of the remaining keysets is done.
807 : *
808 : * @param split the split object to work with
809 : * @ingroup split
810 : */
811 2637 : void splitPrepare (Split * split)
812 : {
813 8827 : for (size_t i = 0; i < split->size;)
814 : {
815 3553 : int inc = 1;
816 3553 : if ((split->syncbits[i] & 1) == 1)
817 : {
818 2688 : KeySet * n = ksDeepDup (split->keysets[i]);
819 2688 : ksDel (split->keysets[i]);
820 2688 : split->keysets[i] = n;
821 : }
822 : else
823 : {
824 : /* We don't need i anymore */
825 865 : splitRemove (split, i);
826 865 : inc = 0;
827 : }
828 3553 : i += inc;
829 : }
830 2637 : }
831 :
832 0 : static char * elektraStrConcat (const char * a, const char * b)
833 : {
834 0 : size_t len = strlen (a) + strlen (b) + 1;
835 0 : char * ret = elektraMalloc (len);
836 0 : ret = strcpy (ret, a);
837 0 : ret = strcat (ret, b);
838 0 : return ret;
839 : }
840 :
841 0 : void splitCacheStoreState (KDB * handle, Split * split, KeySet * global, Key * parentKey, Key * initialParent)
842 : {
843 0 : Key * mountPoint = mountGetMountpoint (handle, parentKey);
844 0 : Key * lastParentName = keyNew (KDB_CACHE_PREFIX "/lastParentName", KEY_VALUE, keyName (mountPoint), KEY_END);
845 0 : Key * lastParentValue = keyNew (KDB_CACHE_PREFIX "/lastParentValue", KEY_VALUE, keyString (mountPoint), KEY_END);
846 0 : Key * lastInitalParentName = keyNew (KDB_CACHE_PREFIX "/lastInitialParentName", KEY_VALUE, keyName (initialParent), KEY_END);
847 0 : Key * lastSplitSize =
848 0 : keyNew (KDB_CACHE_PREFIX "/lastSplitSize", KEY_BINARY, KEY_SIZE, sizeof (size_t), KEY_VALUE, &(split->size), KEY_END);
849 0 : ksAppendKey (global, lastParentName);
850 0 : ksAppendKey (global, lastParentValue);
851 0 : ksAppendKey (global, lastInitalParentName);
852 0 : ksAppendKey (global, lastSplitSize);
853 :
854 : ELEKTRA_LOG_DEBUG ("SIZE STORAGE STORE STUFF");
855 0 : for (size_t i = 0; i < split->size; ++i)
856 : {
857 : // TODO: simplify this code below, seems like this affacts only the last split anyway
858 0 : if (!split->handles[i] || !split->handles[i]->mountpoint)
859 : {
860 : ELEKTRA_LOG_DEBUG (">>>> Skipping split->handle[%ld]: pseudo-backend or no mountpoint", i);
861 0 : ELEKTRA_ASSERT (i == (split->size - 1), "ERROR: NOT THE LAST SPLIT");
862 0 : continue;
863 : }
864 : if (test_bit (split->syncbits[i], 0))
865 : {
866 : /* Dont process keysets which come from the user
867 : and not from the backends */
868 : ELEKTRA_LOG_DEBUG (">>>> Skipping split->handle[%ld]: syncbits is 0, keyset from user", i);
869 : ELEKTRA_ASSERT (i == (split->size - 1), "ERROR: NOT THE LAST SPLIT");
870 : continue;
871 : }
872 : // TODO: simplify this code above, seems like this affacts only the last split anyway
873 :
874 0 : char * name = 0;
875 0 : if (strlen (keyName (split->handles[i]->mountpoint)) != 0)
876 : {
877 0 : name = elektraStrConcat (KDB_CACHE_PREFIX "/splitState/mountpoint/", keyName (split->handles[i]->mountpoint));
878 : }
879 : else
880 : {
881 0 : name = elektraStrConcat (KDB_CACHE_PREFIX "/splitState/", "default/");
882 : }
883 : // Append parent name for uniqueness (spec, dir, user, system, ...)
884 0 : char * tmp = name;
885 0 : name = elektraStrConcat (name, keyName (split->parents[i]));
886 0 : elektraFree (tmp);
887 :
888 : ELEKTRA_LOG_DEBUG (">>>> STORING split->handle[%ld] with name: %s :::: parentName: %s, parentValue: %s", i, name,
889 : keyName (split->parents[i]), keyString (split->parents[i]));
890 :
891 0 : Key * key = keyNew (name, KEY_END);
892 0 : keyAddBaseName (key, "splitParentName");
893 0 : keySetString (key, keyName (split->parents[i]));
894 0 : ksAppendKey (global, key);
895 : ELEKTRA_LOG_DEBUG (">>>> STORING key: %s, string: %s, strlen: %ld, valSize: %ld", keyName (key), keyString (key),
896 : strlen (keyString (key)), keyGetValueSize (key));
897 0 : keyDel (key);
898 :
899 0 : key = keyNew (name, KEY_END);
900 0 : keyAddBaseName (key, "splitParentValue");
901 0 : keySetString (key, keyString (split->parents[i]));
902 0 : ksAppendKey (global, key);
903 : ELEKTRA_LOG_DEBUG (">>>> STORING key: %s, string: %s, strlen: %ld, valSize: %ld", keyName (key), keyString (key),
904 : strlen (keyString (key)), keyGetValueSize (key));
905 0 : keyDel (key);
906 :
907 0 : key = keyNew (name, KEY_END);
908 0 : keyAddBaseName (key, "specsize");
909 0 : keySetBinary (key, &(split->handles[i]->specsize), sizeof (ssize_t));
910 0 : ksAppendKey (global, key);
911 : ELEKTRA_LOG_DEBUG (">>>> STORING key: %s, string: %s, strlen: %ld, valSize: %ld", keyName (key), keyString (key),
912 : strlen (keyString (key)), keyGetValueSize (key));
913 0 : keyDel (key);
914 :
915 0 : key = keyNew (name, KEY_END);
916 0 : keyAddBaseName (key, "dirsize");
917 0 : keySetBinary (key, &(split->handles[i]->dirsize), sizeof (ssize_t));
918 0 : ksAppendKey (global, key);
919 : ELEKTRA_LOG_DEBUG (">>>> STORING key: %s, string: %s, strlen: %ld, valSize: %ld", keyName (key), keyString (key),
920 : strlen (keyString (key)), keyGetValueSize (key));
921 0 : keyDel (key);
922 :
923 0 : key = keyNew (name, KEY_END);
924 0 : keyAddBaseName (key, "usersize");
925 0 : keySetBinary (key, &(split->handles[i]->usersize), sizeof (ssize_t));
926 0 : ksAppendKey (global, key);
927 : ELEKTRA_LOG_DEBUG (">>>> STORING key: %s, string: %s, strlen: %ld, valSize: %ld", keyName (key), keyString (key),
928 : strlen (keyString (key)), keyGetValueSize (key));
929 0 : keyDel (key);
930 :
931 0 : key = keyNew (name, KEY_END);
932 0 : keyAddBaseName (key, "systemsize");
933 0 : keySetBinary (key, &(split->handles[i]->systemsize), sizeof (ssize_t));
934 0 : ksAppendKey (global, key);
935 : ELEKTRA_LOG_DEBUG (">>>> STORING key: %s, string: %s, strlen: %ld, valSize: %ld", keyName (key), keyString (key),
936 : strlen (keyString (key)), keyGetValueSize (key));
937 0 : keyDel (key);
938 :
939 0 : key = keyNew (name, KEY_END);
940 0 : keyAddBaseName (key, "syncbits");
941 0 : keySetBinary (key, &(split->syncbits[i]), sizeof (splitflag_t));
942 0 : ksAppendKey (global, key);
943 : ELEKTRA_LOG_DEBUG (">>>> STORING key: %s, string: %s, strlen: %ld, valSize: %ld", keyName (key), keyString (key),
944 : strlen (keyString (key)), keyGetValueSize (key));
945 0 : keyDel (key);
946 :
947 0 : elektraFree (name);
948 : }
949 0 : }
950 :
951 0 : int splitCacheCheckState (Split * split, KeySet * global)
952 : {
953 : ELEKTRA_LOG_DEBUG ("SIZE STORAGE CHCK");
954 0 : Key * key = 0;
955 0 : char * name = 0;
956 :
957 0 : Key * lastSplitSize = ksLookupByName (global, KDB_CACHE_PREFIX "/lastSplitSize", KDB_O_NONE);
958 0 : if (lastSplitSize && keyGetValueSize (lastSplitSize) == sizeof (size_t))
959 : {
960 0 : size_t lastSize = 0;
961 0 : keyGetBinary (lastSplitSize, &lastSize, sizeof (size_t));
962 : ELEKTRA_LOG_DEBUG ("Split size check: lastSize %ld, cur size: %ld", lastSize, split->size);
963 0 : int bypassedSplits = 1;
964 0 : if (lastSize != split->size + bypassedSplits) return -1;
965 : }
966 : else
967 : {
968 : return -1;
969 : }
970 :
971 0 : for (size_t i = 0; i < split->size; ++i)
972 : {
973 0 : if (strlen (keyName (split->handles[i]->mountpoint)) != 0)
974 : {
975 0 : name = elektraStrConcat (KDB_CACHE_PREFIX "/splitState/mountpoint/", keyName (split->handles[i]->mountpoint));
976 : }
977 : else
978 : {
979 0 : name = elektraStrConcat (KDB_CACHE_PREFIX "/splitState/", "default/");
980 : }
981 : // Append parent name for uniqueness (spec, dir, user, system, ...)
982 0 : char * tmp = name;
983 0 : name = elektraStrConcat (name, keyName (split->parents[i]));
984 0 : elektraFree (tmp);
985 0 : key = keyNew (name, KEY_END);
986 :
987 0 : keyAddBaseName (key, "splitParentName");
988 0 : Key * found = ksLookup (global, key, KDB_O_NONE);
989 0 : if (!(found && elektraStrCmp (keyString (found), keyName (split->parents[i])) == 0))
990 : {
991 : goto error;
992 : }
993 :
994 0 : keySetBaseName (key, "splitParentValue");
995 0 : found = ksLookup (global, key, KDB_O_NONE);
996 0 : if (!(found && elektraStrCmp (keyString (found), keyString (split->parents[i])) == 0))
997 : {
998 : goto error;
999 : }
1000 :
1001 0 : keySetBaseName (key, "specsize");
1002 0 : found = ksLookup (global, key, KDB_O_NONE);
1003 0 : if (!(found && keyGetValueSize (found) == sizeof (ssize_t)) || (split->handles[i]->specsize > 0))
1004 : {
1005 : goto error;
1006 : }
1007 :
1008 0 : keySetBaseName (key, "dirsize");
1009 0 : found = ksLookup (global, key, KDB_O_NONE);
1010 0 : if (!(found && keyGetValueSize (found) == sizeof (ssize_t)) || (split->handles[i]->dirsize > 0))
1011 : {
1012 : goto error;
1013 : }
1014 :
1015 0 : keySetBaseName (key, "usersize");
1016 0 : found = ksLookup (global, key, KDB_O_NONE);
1017 0 : if (!(found && keyGetValueSize (found) == sizeof (ssize_t)) || (split->handles[i]->usersize > 0))
1018 : {
1019 : goto error;
1020 : }
1021 :
1022 0 : keySetBaseName (key, "systemsize");
1023 0 : found = ksLookup (global, key, KDB_O_NONE);
1024 0 : if (!(found && keyGetValueSize (found) == sizeof (ssize_t)) || (split->handles[i]->systemsize > 0))
1025 : {
1026 : goto error;
1027 : }
1028 :
1029 0 : keySetBaseName (key, "syncbits");
1030 0 : found = ksLookup (global, key, KDB_O_NONE);
1031 0 : if (!(found && keyGetValueSize (found) == sizeof (splitflag_t)) || test_bit (split->syncbits[i], 0))
1032 : {
1033 : goto error;
1034 : }
1035 :
1036 0 : elektraFree (name);
1037 0 : name = 0;
1038 0 : keyDel (key);
1039 : }
1040 :
1041 : return 0;
1042 :
1043 : error:
1044 : ELEKTRA_LOG_WARNING ("SIZE STORAGE KEY NOT FOUND");
1045 : ELEKTRA_LOG_DEBUG (">>>> MISSING key: %s, string: %s, strlen: %ld, valSize: %ld", keyName (key), keyString (key),
1046 : strlen (keyString (key)), keyGetValueSize (key));
1047 0 : keyDel (key);
1048 0 : if (name) elektraFree (name);
1049 : return -1;
1050 : }
1051 :
1052 0 : int splitCacheLoadState (Split * split, KeySet * global)
1053 : {
1054 : ELEKTRA_LOG_DEBUG ("SIZE STORAGE LOAD");
1055 0 : Key * key = 0;
1056 0 : char * name = 0;
1057 :
1058 0 : Key * lastSplitSize = ksLookupByName (global, KDB_CACHE_PREFIX "/lastSplitSize", KDB_O_NONE);
1059 0 : if (lastSplitSize && keyGetValueSize (lastSplitSize) == sizeof (size_t))
1060 : {
1061 0 : size_t lastSize = 0;
1062 0 : keyGetBinary (lastSplitSize, &lastSize, sizeof (size_t));
1063 : ELEKTRA_LOG_DEBUG ("Split size check: lastSize %ld, cur size: %ld", lastSize, split->size);
1064 0 : int bypassedSplits = 1;
1065 0 : if (lastSize != split->size + bypassedSplits) return -1;
1066 : }
1067 : else
1068 : {
1069 : return -1;
1070 : }
1071 :
1072 0 : for (size_t i = 0; i < split->size; ++i)
1073 : {
1074 0 : if (strlen (keyName (split->handles[i]->mountpoint)) != 0)
1075 : {
1076 0 : name = elektraStrConcat (KDB_CACHE_PREFIX "/splitState/mountpoint/", keyName (split->handles[i]->mountpoint));
1077 : }
1078 : else
1079 : {
1080 0 : name = elektraStrConcat (KDB_CACHE_PREFIX "/splitState/", "default/");
1081 : }
1082 : // Append parent name for uniqueness (spec, dir, user, system, ...)
1083 0 : char * tmp = name;
1084 0 : name = elektraStrConcat (name, keyName (split->parents[i]));
1085 0 : elektraFree (tmp);
1086 0 : key = keyNew (name, KEY_END);
1087 :
1088 0 : keyAddBaseName (key, "splitParentName");
1089 0 : Key * found = ksLookup (global, key, KDB_O_NONE);
1090 0 : if (!(found && elektraStrCmp (keyString (found), keyName (split->parents[i])) == 0))
1091 : {
1092 : goto error;
1093 : }
1094 :
1095 0 : keySetBaseName (key, "splitParentValue");
1096 0 : found = ksLookup (global, key, KDB_O_NONE);
1097 0 : if (!(found && elektraStrCmp (keyString (found), keyString (split->parents[i])) == 0))
1098 : {
1099 : goto error;
1100 : }
1101 :
1102 0 : keySetBaseName (key, "specsize");
1103 0 : found = ksLookup (global, key, KDB_O_NONE);
1104 0 : if (found && keyGetValueSize (found) == sizeof (ssize_t))
1105 : {
1106 0 : keyGetBinary (found, &(split->handles[i]->specsize), sizeof (ssize_t));
1107 : }
1108 : else
1109 : {
1110 : goto error;
1111 : }
1112 :
1113 0 : keySetBaseName (key, "dirsize");
1114 0 : found = ksLookup (global, key, KDB_O_NONE);
1115 0 : if (found && keyGetValueSize (found) == sizeof (ssize_t))
1116 : {
1117 0 : keyGetBinary (found, &(split->handles[i]->dirsize), sizeof (ssize_t));
1118 : }
1119 : else
1120 : {
1121 : goto error;
1122 : }
1123 :
1124 0 : keySetBaseName (key, "usersize");
1125 0 : found = ksLookup (global, key, KDB_O_NONE);
1126 0 : if (found && keyGetValueSize (found) == sizeof (ssize_t))
1127 : {
1128 0 : keyGetBinary (found, &(split->handles[i]->usersize), sizeof (ssize_t));
1129 : }
1130 : else
1131 : {
1132 : goto error;
1133 : }
1134 :
1135 0 : keySetBaseName (key, "systemsize");
1136 0 : found = ksLookup (global, key, KDB_O_NONE);
1137 0 : if (found && keyGetValueSize (found) == sizeof (ssize_t))
1138 : {
1139 0 : keyGetBinary (found, &(split->handles[i]->systemsize), sizeof (ssize_t));
1140 : }
1141 : else
1142 : {
1143 : goto error;
1144 : }
1145 :
1146 0 : keySetBaseName (key, "syncbits");
1147 0 : found = ksLookup (global, key, KDB_O_NONE);
1148 0 : if (found && keyGetValueSize (found) == sizeof (splitflag_t))
1149 : {
1150 0 : keyGetBinary (found, &(split->syncbits[i]), sizeof (splitflag_t));
1151 : }
1152 : else
1153 : {
1154 : goto error;
1155 : }
1156 :
1157 0 : elektraFree (name);
1158 0 : name = 0;
1159 0 : keyDel (key);
1160 : }
1161 :
1162 : return 0;
1163 :
1164 : error:
1165 : ELEKTRA_LOG_WARNING ("SIZE STORAGE KEY NOT FOUND");
1166 : ELEKTRA_LOG_DEBUG (">>>> MISSING key: %s, string: %s, strlen: %ld, valSize: %ld", keyName (key), keyString (key),
1167 : strlen (keyString (key)), keyGetValueSize (key));
1168 0 : keyDel (key);
1169 0 : if (name) elektraFree (name);
1170 : return -1;
1171 : }
|