1 /*
2 COLLECTION LIBRARY
3
4 Implementation of the collection library interface.
5
6 Copyright (C) Dmitri Pal <dpal@redhat.com> 2009
7
8 Collection Library is free software: you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 Collection Library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with Collection Library. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "config.h"
23 #include <string.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <ctype.h>
27 #include <time.h>
28 #include "trace.h"
29
30 /* The collection should use the real structures */
31 #include "collection_priv.h"
32 #include "collection.h"
33
34 #define INTERRUPT(d) ((d == COL_DSP_LASTDUPNS) || (d == COL_DSP_NDUPNS)) ? 0 : 1
35
36 /* Internal constants defined to denote actions that can be performed by find handler */
37 #define COLLECTION_ACTION_FIND 1
38 #define COLLECTION_ACTION_DEL 2
39 #define COLLECTION_ACTION_UPDATE 3
40 #define COLLECTION_ACTION_GET 4
41
42
43 /* Special internal error code to indicate that collection search was interrupted */
44 #define EINTR_INTERNAL 10000
45
46
47 /* Magic numbers for hashing */
48 #if SIZEOF_LONG == 8
49 #define FNV1a_prime 1099511628211ul
50 #define FNV1a_base 14695981039346656037ul
51 #elif SIZEOF_LONG_LONG == 8
52 #define FNV1a_prime 1099511628211ull
53 #define FNV1a_base 14695981039346656037ull
54 #else
55 #error "Platform cannot support 64-bit constant integers"
56 #endif
57
58 /* Struct used for passing parameter for update operation */
59 struct update_property {
60 int type;
61 void *data;
62 int length;
63 int found;
64 };
65
66 /* This struct is used to construct path
67 * to an item in the collection (tree)
68 */
69 struct path_data {
70 char *name;
71 int length;
72 struct path_data *previous_path;
73 };
74
75 /* Structure to keep data needed to
76 * copy collection
77 * while traversing it
78 */
79 struct col_copy {
80 int mode;
81 struct path_data *current_path;
82 char *given_name;
83 int given_len;
84 col_copy_cb copy_cb;
85 void *ext_data;
86 };
87
88 /******************** FUNCTION DECLARATIONS ****************************/
89
90 /* Have to declare those due to function cross referencing */
91 static int col_find_item_and_do(struct collection_item *ci,
92 const char *property_to_find,
93 int type,
94 int mode_flags,
95 col_item_fn item_handler,
96 void *custom_data,
97 int action);
98
99 /* Traverse callback for find & delete function */
100 static int col_act_traverse_handler(struct collection_item *head,
101 struct collection_item *previous,
102 struct collection_item *current,
103 void *passed_traverse_data,
104 col_item_fn user_item_handler,
105 void *custom_data,
106 int *stop);
107
108 /* Traverse handler to find parent of the item */
109 static int col_parent_traverse_handler(struct collection_item *head,
110 struct collection_item *previous,
111 struct collection_item *current,
112 void *traverse_data,
113 col_item_fn user_item_handler,
114 void *custom_data,
115 int *stop);
116
117 /* Traverse callback signature */
118 typedef int (*internal_item_fn)(struct collection_item *head,
119 struct collection_item *previous,
120 struct collection_item *current,
121 void *traverse_data,
122 col_item_fn user_item_handler,
123 void *custom_data,
124 int *stop);
125 /* Function to walk_items */
126 static int col_walk_items(struct collection_item *ci,
127 int mode_flags,
128 internal_item_fn traverse_handler,
129 void *traverse_data,
130 col_item_fn user_item_handler,
131 void *custom_data,
132 unsigned *depth);
133
134 /* Function to get sub collection */
135 static int col_get_subcollection(const char *property,
136 int property_len,
137 int type,
138 void *data,
139 int length,
140 void *found,
141 int *dummy);
142
143 /* Function to destroy collection */
144 void col_destroy_collection(struct collection_item *ci);
145
146 /******************** SUPPLEMENTARY FUNCTIONS ****************************/
147 /* BASIC OPERATIONS */
148
149 /* Function that checks if property can be added */
col_validate_property(const char * property)150 static int col_validate_property(const char *property)
151 {
152 TRACE_FLOW_STRING("col_validate_property", "Entry point.");
153 /* Only alpha numeric characters are allowed in names of the properties */
154 int invalid = 0;
155 const char *check;
156
157 check = property;
158 while (*check != '\0') {
159 /* It turned out that limiting collection charcters is bad */
160 if ((*check < ' ') || (*check == '!')) {
161 invalid = 1;
162 break;
163 }
164 check++;
165 }
166 TRACE_FLOW_NUMBER("col_validate_property. Returning ", invalid);
167 return invalid;
168 }
169
170
171 /* Function that cleans the item with callback */
col_delete_item_with_cb(struct collection_item * item,col_item_cleanup_fn cb,void * custom_data)172 void col_delete_item_with_cb(struct collection_item *item,
173 col_item_cleanup_fn cb,
174 void *custom_data)
175 {
176 struct collection_item *other_collection;
177
178 TRACE_FLOW_STRING("col_delete_item","Entry point.");
179
180 if (item == NULL) {
181 TRACE_FLOW_STRING("col_delete_item","Nothing to delete!");
182 return;
183 }
184
185 /* Handle external or embedded collection */
186 if(item->type == COL_TYPE_COLLECTIONREF) {
187 /* Our data is a pointer to a whole external collection so dereference
188 * it or delete */
189 other_collection = *((struct collection_item **)(item->data));
190 col_destroy_collection_with_cb(other_collection, cb, custom_data);
191 }
192
193 /* Call the callback */
194 if (cb) cb(item->property,
195 item->property_len,
196 item->type,
197 item->data,
198 item->length,
199 custom_data);
200
201 TRACE_INFO_STRING("Deleting property:", item->property);
202 TRACE_INFO_NUMBER("Type:", item->type);
203
204 if (item->property != NULL) free(item->property);
205 if (item->data != NULL) free(item->data);
206
207 free(item);
208
209 TRACE_FLOW_STRING("col_delete_item","Exit.");
210 }
211
212 /* Function that cleans the item */
col_delete_item(struct collection_item * item)213 void col_delete_item(struct collection_item *item)
214 {
215 TRACE_FLOW_STRING("col_delete_item","Entry point.");
216
217 col_delete_item_with_cb(item, NULL, NULL);
218
219 TRACE_FLOW_STRING("col_delete_item","Exit.");
220 }
221
222
223
224 /* A generic function to allocate a property item */
col_allocate_item(struct collection_item ** ci,const char * property,const void * item_data,int length,int type)225 int col_allocate_item(struct collection_item **ci, const char *property,
226 const void *item_data, int length, int type)
227 {
228 struct collection_item *item = NULL;
229
230 TRACE_FLOW_STRING("col_allocate_item", "Entry point.");
231 TRACE_INFO_NUMBER("Will be using type:", type);
232
233 /* Check the length */
234 if (length >= COL_MAX_DATA) {
235 TRACE_ERROR_STRING("col_allocate_item", "Data to long.");
236 return EMSGSIZE;
237 }
238
239 if (col_validate_property(property)) {
240 TRACE_ERROR_STRING("Invalid chracters in the property name", property);
241 return EINVAL;
242 }
243
244 /* Allocate memory for the structure */
245 item = (struct collection_item *)malloc(sizeof(struct collection_item));
246 if (item == NULL) {
247 TRACE_ERROR_STRING("col_allocate_item", "Malloc failed.");
248 return ENOMEM;
249 }
250
251 /* After we initialize members we can use delete_item() in case of error */
252 item->next = NULL;
253 item->property = NULL;
254 item->data = NULL;
255 TRACE_INFO_NUMBER("About to set type to:", type);
256 item->type = type;
257
258 /* Copy property */
259 item->property = strdup(property);
260 if (item->property == NULL) {
261 TRACE_ERROR_STRING("col_allocate_item", "Failed to dup property.");
262 col_delete_item(item);
263 return ENOMEM;
264 }
265
266 item->phash = col_make_hash(property, 0, &(item->property_len));
267 TRACE_INFO_NUMBER("Item hash", item->phash);
268 TRACE_INFO_NUMBER("Item property length", item->property_len);
269 TRACE_INFO_NUMBER("Item property strlen", strlen(item->property));
270
271 /* Deal with data */
272 item->data = malloc(length);
273 if (item->data == NULL) {
274 TRACE_ERROR_STRING("col_allocate_item", "Failed to dup data.");
275 col_delete_item(item);
276 return ENOMEM;
277 }
278
279 memcpy(item->data, item_data, length);
280 item->length = length;
281
282 /* Make sure that data is NULL terminated in case of string */
283 if (type == COL_TYPE_STRING) ((char *)(item->data))[length-1] = '\0';
284
285 *ci = item;
286
287 TRACE_INFO_STRING("Item property", item->property);
288 TRACE_INFO_NUMBER("Item property type", item->type);
289 TRACE_INFO_NUMBER("Item data length", item->length);
290 TRACE_FLOW_STRING("col_allocate_item", "Success exit.");
291 return EOK;
292 }
293
294 /* Structure used to find things in collection */
295 struct property_search {
296 const char *property;
297 uint64_t hash;
298 struct collection_item *parent;
299 int index;
300 int count;
301 int found;
302 int use_type;
303 int type;
304 int interrupt;
305 int exact;
306 };
307
308 /* Find the parent of the item with given name */
col_find_property_sub(struct collection_item * collection,const char * subcollection,int interrupt,const char * refprop,int idx,int exact,int use_type,int type,struct collection_item ** parent)309 static int col_find_property_sub(struct collection_item *collection,
310 const char *subcollection,
311 int interrupt,
312 const char *refprop,
313 int idx,
314 int exact,
315 int use_type,
316 int type,
317 struct collection_item **parent)
318 {
319 struct property_search ps;
320 int i = 0;
321 unsigned depth = 0;
322 struct collection_item *sub = NULL;
323 int error = EOK;
324
325 TRACE_FLOW_ENTRY();
326
327 *parent = NULL;
328
329 ps.property = refprop;
330 ps.hash = FNV1a_base;
331 ps.parent = NULL;
332 ps.index = idx;
333 ps.count = 0;
334 ps.found = 0;
335 ps.use_type = use_type;
336 ps.type = type;
337 ps.interrupt = interrupt;
338 ps.exact = exact;
339
340 /* Create hash of the string to search */
341 while(refprop[i] != 0) {
342 ps.hash = ps.hash ^ toupper(refprop[i]);
343 ps.hash *= FNV1a_prime;
344 i++;
345 }
346
347 /* Add item to collection */
348 if (subcollection == NULL) {
349 sub = collection;
350 }
351 else {
352 TRACE_INFO_STRING("Subcollection is not null, searching",
353 subcollection);
354 error = col_find_item_and_do(collection, subcollection,
355 COL_TYPE_COLLECTIONREF,
356 COL_TRAVERSE_DEFAULT,
357 col_get_subcollection, (void *)(&sub),
358 COLLECTION_ACTION_FIND);
359 if (error) {
360 TRACE_ERROR_NUMBER("Search for subcollection returned error:",
361 error);
362 /* Not found */
363 return 0;
364 }
365
366 if (sub == NULL) {
367 TRACE_ERROR_STRING("Search for subcollection returned NULL", "");
368 /* Not found */
369 return 0;
370 }
371
372 }
373
374 /* We do not care about error here */
375 (void)col_walk_items(sub, COL_TRAVERSE_ONELEVEL,
376 col_parent_traverse_handler,
377 (void *)parent, NULL, (void *)&ps,
378 &depth);
379
380 if (*parent) {
381 /* Item is found in the collection */
382 TRACE_FLOW_STRING("col_find_property", "Exit - item found");
383 return 1;
384 }
385
386 /* Item is not found */
387 TRACE_FLOW_STRING("col_find_property", "Exit - item NOT found");
388 return 0;
389 }
390
391 /* Backward compatible wrapper */
col_find_property(struct collection_item * collection,const char * refprop,int idx,int use_type,int type,struct collection_item ** parent)392 static int col_find_property(struct collection_item *collection,
393 const char *refprop,
394 int idx,
395 int use_type,
396 int type,
397 struct collection_item **parent)
398 {
399 int error = EOK;
400
401 TRACE_FLOW_ENTRY();
402
403 error = col_find_property_sub(collection,
404 NULL,
405 1,
406 refprop,
407 idx,
408 0,
409 use_type,
410 type,
411 parent);
412
413 TRACE_FLOW_RETURN(error);
414 return error;
415 }
416
417
418
419 /* Find a duplicate item */
col_get_dup_item(struct collection_item * ci,const char * subcollection,const char * property_to_find,int type,int idx,int exact,struct collection_item ** item)420 int col_get_dup_item(struct collection_item *ci,
421 const char *subcollection,
422 const char *property_to_find,
423 int type,
424 int idx,
425 int exact,
426 struct collection_item **item)
427 {
428 int error = EOK;
429 struct collection_item *parent = NULL;
430
431 TRACE_FLOW_ENTRY();
432
433 if (!ci) {
434 TRACE_ERROR_STRING("Passed in collection is invalid", "");
435 return EINVAL;
436 }
437
438 if (!item) {
439 TRACE_ERROR_STRING("Result storage is invalid", "");
440 return EINVAL;
441 }
442
443 if (!property_to_find) {
444 TRACE_ERROR_STRING("Passed in property to find is NULL", "");
445 return EINVAL;
446 }
447
448 /* Find the corresponding duplicate item */
449 if (col_find_property_sub(ci,
450 subcollection,
451 0,
452 property_to_find,
453 idx,
454 exact,
455 (type == COL_TYPE_ANY) ? 0 : 1,
456 type,
457 &parent)) {
458 *item = parent->next;
459 /* function col_get_dup_item with big value of idx should
460 * return the last item with the same property name. */
461 if (strncmp(property_to_find,
462 parent->next->property,
463 parent->next->property_len) != 0) {
464 *item = parent;
465 }
466 }
467 else error = ENOENT;
468
469 TRACE_FLOW_EXIT();
470 return error;
471 }
472
473 /* Insert item into the current collection */
col_insert_item_into_current(struct collection_item * collection,struct collection_item * item,int disposition,const char * refprop,int idx,unsigned flags)474 int col_insert_item_into_current(struct collection_item *collection,
475 struct collection_item *item,
476 int disposition,
477 const char *refprop,
478 int idx,
479 unsigned flags)
480 {
481 struct collection_header *header = NULL;
482 struct collection_item *parent = NULL;
483 struct collection_item *current = NULL;
484 int refindex = 0;
485
486 TRACE_FLOW_STRING("col_insert_item_into_current", "Entry point");
487
488 /* Do best effort on the item */
489 if ((!item) || (item->next)) {
490 TRACE_ERROR_STRING("Passed in item is invalid", "");
491 return EINVAL;
492 }
493
494 if (collection == NULL) {
495 TRACE_INFO_STRING("col_insert_item_into_current",
496 "Collection accepting is NULL");
497 if (item->type == COL_TYPE_COLLECTION) {
498 /* This is a special case of self creation */
499 TRACE_INFO_STRING("col_insert_item_into_current",
500 "Adding header item to new collection.");
501 collection = item;
502 }
503 else {
504 TRACE_ERROR_STRING("Passed in item is invalid", "");
505 return EINVAL;
506 }
507 }
508 else {
509 /* We can add items only to collections */
510 if (collection->type != COL_TYPE_COLLECTION) {
511 TRACE_ERROR_STRING("Attempt to add item to non collection.","");
512 TRACE_ERROR_STRING("Collection name:", collection->property);
513 TRACE_ERROR_NUMBER("Collection type:", collection->type);
514 return EINVAL;
515 }
516 }
517
518 /* After processing flags we can process disposition */
519
520 header = (struct collection_header *)collection->data;
521
522 /* Check flags first */
523 switch(flags) {
524 case COL_INSERT_NOCHECK: /* No check - good just fall through */
525 TRACE_INFO_STRING("Insert without check", "");
526 break;
527 case COL_INSERT_DUPOVER: /* Find item and overwrite - ignore disposition */
528 if (col_find_property(collection, item->property, 0, 0, 0, &parent)) {
529 current = parent->next;
530 item->next = current->next;
531 parent->next = item;
532 if (header->last == current) header->last = item;
533 col_delete_item(current);
534 /* Deleted one added another - count stays the same! */
535 TRACE_FLOW_STRING("col_insert_item_into_current", "Dup overwrite exit");
536 return EOK;
537 }
538 /* Not found so we fall thorough and add as requested */
539 break;
540
541 case COL_INSERT_DUPOVERT: /* Find item by name and type and overwrite - ignore disposition */
542 if (col_find_property(collection, item->property, 0, 1, item->type, &parent)) {
543 current = parent->next;
544 item->next = current->next;
545 parent->next = item;
546 if (header->last == current) header->last = item;
547 col_delete_item(current);
548 /* Deleted one added another - count stays the same! */
549 TRACE_FLOW_STRING("col_insert_item_into_current", "Dup overwrite exit");
550 return EOK;
551 }
552 /* Not found so we fall thorough and add as requested */
553 break;
554
555 case COL_INSERT_DUPERROR: if (col_find_property(collection, item->property, 0, 0, 0, &parent)) {
556 /* Return error */
557 TRACE_ERROR_NUMBER("Duplicate property", EEXIST);
558 return EEXIST;
559 }
560 break;
561
562 case COL_INSERT_DUPERRORT: if (col_find_property(collection, item->property, 0, 1, item->type, &parent)) {
563 /* Return error */
564 TRACE_ERROR_NUMBER("Duplicate property of the same type", EEXIST);
565 return EEXIST;
566 }
567 break;
568
569 case COL_INSERT_DUPMOVE: /* Find item and delete */
570 if (col_find_property(collection, item->property, 0, 0, 0, &parent)) {
571 current = parent->next;
572 parent->next = current->next;
573 if (header->last == current) header->last = parent;
574 col_delete_item(current);
575 header->count--;
576 }
577 /* Now add item according to the disposition */
578 break;
579
580 case COL_INSERT_DUPMOVET: /* Find item and delete */
581 TRACE_INFO_STRING("Property:", item->property);
582 TRACE_INFO_NUMBER("Type:", item->type);
583 if (col_find_property(collection, item->property, 0, 1, item->type, &parent)) {
584 TRACE_INFO_LNUMBER("Current:", parent->next);
585 current = parent->next;
586 parent->next = current->next;
587 if (header->last == current) header->last = parent;
588 col_delete_item(current);
589 header->count--;
590 }
591 /* Now add item according to the disposition */
592 break;
593
594 default: /* The new ones should be added here */
595 TRACE_ERROR_NUMBER("Flag is not implemented", ENOSYS);
596 return ENOSYS;
597 }
598
599
600 switch (disposition) {
601 case COL_DSP_END: /* Link new item to the last item in the list if there any */
602 if (header->count != 0) header->last->next = item;
603 /* Make sure we save a new last element */
604 header->last = item;
605 header->count++;
606 break;
607
608 case COL_DSP_FRONT: /* Same as above if there is header only */
609 if (header->count == 1) {
610 header->last->next = item;
611 header->last = item;
612 }
613 else {
614 item->next = collection->next;
615 collection->next = item;
616 }
617 header->count++;
618 break;
619
620 case COL_DSP_BEFORE: /* Check argument */
621 if (!refprop) {
622 TRACE_ERROR_STRING("In this case property is required", "");
623 return EINVAL;
624 }
625
626 /* We need to find property */
627 if (col_find_property(collection, refprop, 0, 0, 0, &parent)) {
628 item->next = parent->next;
629 parent->next = item;
630 header->count++;
631 }
632 else {
633 TRACE_ERROR_STRING("Property not found", refprop);
634 return ENOENT;
635 }
636 break;
637
638 case COL_DSP_AFTER: /* Check argument */
639 if (!refprop) {
640 TRACE_ERROR_STRING("In this case property is required", "");
641 return EINVAL;
642 }
643
644 /* We need to find property */
645 if (col_find_property(collection, refprop, 0, 0, 0, &parent)) {
646 parent = parent->next;
647 if (parent->next) {
648 /* It is not the last item */
649 item->next = parent->next;
650 parent->next = item;
651 }
652 else {
653 /* It is the last item */
654 header->last->next = item;
655 header->last = item;
656 }
657 header->count++;
658 }
659 else {
660 TRACE_ERROR_STRING("Property not found", refprop);
661 return ENOENT;
662 }
663 break;
664
665 case COL_DSP_INDEX: if(idx == 0) {
666 /* Same is first */
667 if (header->count == 1) {
668 header->last->next = item;
669 header->last = item;
670 }
671 else {
672 item->next = collection->next;
673 collection->next = item;
674 }
675 }
676 else if(idx >= header->count - 1) {
677 /* In this case add to the end */
678 header->last->next = item;
679 /* Make sure we save a new last element */
680 header->last = item;
681 }
682 else {
683 /* In the middle */
684 parent = collection;
685 /* Move to the right position counting */
686 while (idx > 0) {
687 idx--;
688 if (parent->next) parent = parent->next;
689 }
690 item->next = parent->next;
691 parent->next = item;
692 }
693 header->count++;
694 break;
695
696 case COL_DSP_FIRSTDUP:
697 case COL_DSP_LASTDUP:
698 case COL_DSP_NDUP:
699 case COL_DSP_LASTDUPNS:
700 case COL_DSP_NDUPNS:
701
702 if (disposition == COL_DSP_FIRSTDUP) refindex = 0;
703 else if ((disposition == COL_DSP_LASTDUP) ||
704 (disposition == COL_DSP_LASTDUPNS))
705 refindex = -1;
706 else refindex = idx;
707
708 /* We need to find property based on index */
709 if (col_find_property_sub(collection,
710 NULL,
711 INTERRUPT(disposition),
712 item->property,
713 refindex,
714 0,
715 0,
716 0,
717 &parent)) {
718 item->next = parent->next;
719 parent->next = item;
720 header->count++;
721 if(header->last == parent) header->last = item;
722 }
723 else {
724 TRACE_ERROR_STRING("Property not found", refprop);
725 return ENOENT;
726 }
727 break;
728
729 default:
730 TRACE_ERROR_STRING("Disposition is not implemented", "");
731 return ENOSYS;
732
733 }
734
735
736 TRACE_INFO_STRING("Collection:", collection->property);
737 TRACE_INFO_STRING("Just added item is:", item->property);
738 TRACE_INFO_NUMBER("Item type.", item->type);
739 TRACE_INFO_NUMBER("Number of items in collection now is.", header->count);
740
741 TRACE_FLOW_STRING("col_insert_item_into_current", "Exit");
742 return EOK;
743 }
744
745 /* Extract item from the current collection */
col_extract_item_from_current(struct collection_item * collection,int disposition,const char * refprop,int idx,int type,struct collection_item ** ret_ref)746 int col_extract_item_from_current(struct collection_item *collection,
747 int disposition,
748 const char *refprop,
749 int idx,
750 int type,
751 struct collection_item **ret_ref)
752 {
753 struct collection_header *header = NULL;
754 struct collection_item *parent = NULL;
755 struct collection_item *current = NULL;
756 struct collection_item *found = NULL;
757 int refindex = 0;
758 int use_type = 0;
759
760 TRACE_FLOW_STRING("col_extract_item_from_current", "Entry point");
761
762 /* Check that collection is not empty */
763 if ((collection == NULL) || (collection->type != COL_TYPE_COLLECTION)) {
764 TRACE_ERROR_STRING("Collection can't be NULL", "");
765 return EINVAL;
766 }
767
768 header = (struct collection_header *)collection->data;
769
770 /* Before moving forward we need to check if there is anything to extract */
771 if (header->count <= 1) {
772 TRACE_ERROR_STRING("Collection is empty.", "Nothing to extract.");
773 return ENOENT;
774 }
775
776 if (type != 0) use_type = 1;
777
778 switch (disposition) {
779 case COL_DSP_END: /* Extract last item in the list. */
780 parent = collection;
781 current = collection->next;
782 while (current->next != NULL) {
783 parent = current;
784 current = current->next;
785 }
786 *ret_ref = parent->next;
787 parent->next = NULL;
788 /* Special case - one data element */
789 if (header->count == 2) header->last = collection;
790 else header->last = parent;
791 break;
792
793 case COL_DSP_FRONT: /* Extract first item in the list */
794 *ret_ref = collection->next;
795 collection->next = (*ret_ref)->next;
796 /* Special case - one data element */
797 if (header->count == 2) header->last = collection;
798 break;
799
800 case COL_DSP_BEFORE: /* Check argument */
801 if (!refprop) {
802 TRACE_ERROR_STRING("In this case property is required", "");
803 return EINVAL;
804 }
805
806 /* We have to do it in two steps */
807 /* First find the property that is mentioned */
808 if (col_find_property(collection, refprop, 0, use_type, type, &found)) {
809 /* We found the requested property */
810 if (found->next == collection->next) {
811 /* The referenced property is the first in the list */
812 TRACE_ERROR_STRING("Nothing to extract. Lists starts with property", refprop);
813 return ENOENT;
814 }
815 /* Get to the parent of the item that is before the one that is found */
816 parent = collection;
817 current = collection->next;
818 while (current != found) {
819 parent = current;
820 current = current->next;
821 }
822 *ret_ref = current;
823 parent->next = current->next;
824
825 }
826 else {
827 TRACE_ERROR_STRING("Property not found", refprop);
828 return ENOENT;
829 }
830 break;
831
832 case COL_DSP_AFTER: /* Check argument */
833 if (!refprop) {
834 TRACE_ERROR_STRING("In this case property is required", "");
835 return EINVAL;
836 }
837
838 /* We need to find property */
839 if (col_find_property(collection, refprop, 0, use_type, type, &parent)) {
840 current = parent->next;
841 if (current->next) {
842 *ret_ref = current->next;
843 current->next = (*ret_ref)->next;
844 /* If we removed the last element adjust header */
845 if(current->next == NULL) header->last = current;
846 }
847 else {
848 TRACE_ERROR_STRING("Property is last in the list", refprop);
849 return ENOENT;
850 }
851 }
852 else {
853 TRACE_ERROR_STRING("Property not found", refprop);
854 return ENOENT;
855 }
856 break;
857
858 case COL_DSP_INDEX: if (idx == 0) {
859 *ret_ref = collection->next;
860 collection->next = (*ret_ref)->next;
861 /* Special case - one data element */
862 if (header->count == 2) header->last = collection;
863 }
864 /* Index 0 stands for the first data element.
865 * Count includes header element.
866 */
867 else if (idx >= (header->count - 1)) {
868 TRACE_ERROR_STRING("Index is out of boundaries", refprop);
869 return ENOENT;
870 }
871 else {
872 /* Loop till the element with right index */
873 refindex = 0;
874 parent = collection;
875 current = collection->next;
876 while (refindex < idx) {
877 parent = current;
878 current = current->next;
879 refindex++;
880 }
881 *ret_ref = parent->next;
882 parent->next = (*ret_ref)->next;
883 /* If we removed the last element adjust header */
884 if (parent->next == NULL) header->last = parent;
885 }
886 break;
887
888 case COL_DSP_FIRSTDUP:
889 case COL_DSP_LASTDUP:
890 case COL_DSP_NDUP:
891 case COL_DSP_LASTDUPNS:
892 case COL_DSP_NDUPNS:
893
894 if (disposition == COL_DSP_FIRSTDUP) refindex = 0;
895 else if ((disposition == COL_DSP_LASTDUP) ||
896 (disposition == COL_DSP_LASTDUPNS))
897 refindex = -2;
898 else refindex = idx;
899
900 /* We need to find property based on index */
901 if (col_find_property_sub(collection,
902 NULL,
903 INTERRUPT(disposition),
904 refprop,
905 refindex,
906 1,
907 use_type,
908 type,
909 &parent)) {
910 *ret_ref = parent->next;
911 parent->next = (*ret_ref)->next;
912 /* If we removed the last element adjust header */
913 if(parent->next == NULL) header->last = parent;
914 }
915 else {
916 TRACE_ERROR_STRING("Property not found", refprop);
917 return ENOENT;
918 }
919 break;
920
921 default:
922 TRACE_ERROR_STRING("Disposition is not implemented", "");
923 return ENOSYS;
924
925 }
926
927
928 /* Clear item and reduce count */
929 (*ret_ref)->next = NULL;
930 header->count--;
931
932 TRACE_INFO_STRING("Collection:", (*ret_ref)->property);
933 TRACE_INFO_NUMBER("Item type.", (*ret_ref)->type);
934 TRACE_INFO_NUMBER("Number of items in collection now is.", header->count);
935
936 TRACE_FLOW_STRING("col_extract_item_from_current", "Exit");
937 return EOK;
938 }
939
940 /* Extract item from the collection */
col_extract_item(struct collection_item * collection,const char * subcollection,int disposition,const char * refprop,int idx,int type,struct collection_item ** ret_ref)941 int col_extract_item(struct collection_item *collection,
942 const char *subcollection,
943 int disposition,
944 const char *refprop,
945 int idx,
946 int type,
947 struct collection_item **ret_ref)
948 {
949 struct collection_item *col = NULL;
950 int error = EOK;
951
952 TRACE_FLOW_STRING("col_extract_item", "Entry point");
953
954 /* Check that collection is not empty */
955 if ((collection == NULL) || (collection->type != COL_TYPE_COLLECTION)) {
956 TRACE_ERROR_STRING("Collection can't be NULL", "");
957 return EINVAL;
958 }
959
960 /* Get subcollection if needed */
961 if (subcollection == NULL) {
962 col = collection;
963 }
964 else {
965 TRACE_INFO_STRING("Subcollection id not null, searching", subcollection);
966 error = col_find_item_and_do(collection, subcollection,
967 COL_TYPE_COLLECTIONREF,
968 COL_TRAVERSE_DEFAULT,
969 col_get_subcollection, (void *)(&col),
970 COLLECTION_ACTION_FIND);
971 if (error) {
972 TRACE_ERROR_NUMBER("Search for subcollection returned error:", error);
973 return error;
974 }
975
976 if (col == NULL) {
977 TRACE_ERROR_STRING("Search for subcollection returned NULL pointer", "");
978 return ENOENT;
979 }
980
981 }
982
983 /* Extract from the current collection */
984 error = col_extract_item_from_current(col,
985 disposition,
986 refprop,
987 idx,
988 type,
989 ret_ref);
990 if (error) {
991 TRACE_ERROR_NUMBER("Failed to extract item from the current collection", error);
992 return error;
993 }
994
995 TRACE_FLOW_STRING("col_extract_item", "Exit");
996 return EOK;
997 }
998
999
1000 /* Remove item (property) from collection with callback.*/
col_remove_item_with_cb(struct collection_item * ci,const char * subcollection,int disposition,const char * refprop,int idx,int type,col_item_cleanup_fn cb,void * custom_data)1001 int col_remove_item_with_cb(struct collection_item *ci,
1002 const char *subcollection,
1003 int disposition,
1004 const char *refprop,
1005 int idx,
1006 int type,
1007 col_item_cleanup_fn cb,
1008 void *custom_data)
1009 {
1010 int error = EOK;
1011 struct collection_item *ret_ref = NULL;
1012
1013 TRACE_FLOW_STRING("col_remove_item", "Enter");
1014
1015 /* Extract from the current collection */
1016 error = col_extract_item(ci,
1017 subcollection,
1018 disposition,
1019 refprop,
1020 idx,
1021 type,
1022 &ret_ref);
1023 if (error) {
1024 TRACE_ERROR_NUMBER("Failed to extract item from the collection", error);
1025 return error;
1026 }
1027
1028 col_delete_item_with_cb(ret_ref, cb, custom_data);
1029
1030 TRACE_FLOW_STRING("col_remove_item", "Exit");
1031 return EOK;
1032 }
1033
1034 /* Remove item (property) from collection.*/
col_remove_item(struct collection_item * ci,const char * subcollection,int disposition,const char * refprop,int idx,int type)1035 int col_remove_item(struct collection_item *ci,
1036 const char *subcollection,
1037 int disposition,
1038 const char *refprop,
1039 int idx,
1040 int type)
1041 {
1042 return col_remove_item_with_cb(ci,
1043 subcollection,
1044 disposition,
1045 refprop,
1046 idx,
1047 type,
1048 NULL,
1049 NULL);
1050 }
1051
1052 /* Remove item (property) from current collection.
1053 * Just a simple wrapper.
1054 */
col_remove_item_from_current(struct collection_item * ci,int disposition,const char * refprop,int idx,int type)1055 int col_remove_item_from_current(struct collection_item *ci,
1056 int disposition,
1057 const char *refprop,
1058 int idx,
1059 int type)
1060 {
1061 int error = EOK;
1062
1063 TRACE_FLOW_STRING("col_remove_item_from_current", "Exit");
1064
1065 /* Remove item from current collection */
1066 error = col_remove_item(ci,
1067 NULL,
1068 disposition,
1069 refprop,
1070 idx,
1071 type);
1072
1073 TRACE_FLOW_NUMBER("col_remove_item_from_current. Exit. Returning", error);
1074 return error;
1075 }
1076
1077
1078 /* Insert the item into the collection or subcollection */
col_insert_item(struct collection_item * collection,const char * subcollection,struct collection_item * item,int disposition,const char * refprop,int idx,unsigned flags)1079 int col_insert_item(struct collection_item *collection,
1080 const char *subcollection,
1081 struct collection_item *item,
1082 int disposition,
1083 const char *refprop,
1084 int idx,
1085 unsigned flags)
1086 {
1087 int error;
1088 struct collection_item *acceptor = NULL;
1089
1090 TRACE_FLOW_STRING("col_insert_item", "Entry point.");
1091
1092 /* Do best effort on the item */
1093 if ((!item) || (item->next)) {
1094 TRACE_ERROR_STRING("Passed in item is invalid", "");
1095 return EINVAL;
1096 }
1097
1098 /* Check that collection is not empty */
1099 if ((collection == NULL) && (item->type != COL_TYPE_COLLECTION)) {
1100 TRACE_ERROR_STRING("Collection can't be NULL", "");
1101 return EINVAL;
1102 }
1103
1104 /* Add item to collection */
1105 if (subcollection == NULL) {
1106 acceptor = collection;
1107 }
1108 else {
1109 TRACE_INFO_STRING("Subcollection id not null, searching", subcollection);
1110 error = col_find_item_and_do(collection, subcollection,
1111 COL_TYPE_COLLECTIONREF,
1112 COL_TRAVERSE_DEFAULT,
1113 col_get_subcollection, (void *)(&acceptor),
1114 COLLECTION_ACTION_FIND);
1115 if (error) {
1116 TRACE_ERROR_NUMBER("Search for subcollection returned error:", error);
1117 return error;
1118 }
1119
1120 if (acceptor == NULL) {
1121 TRACE_ERROR_STRING("Search for subcollection returned NULL pointer", "");
1122 return ENOENT;
1123 }
1124
1125 }
1126
1127 /* Instert item to the current collection */
1128 error = col_insert_item_into_current(acceptor,
1129 item,
1130 disposition,
1131 refprop,
1132 idx,
1133 flags);
1134
1135 if (error) {
1136 TRACE_ERROR_NUMBER("Failed to insert item into current collection", error);
1137 return error;
1138 }
1139
1140 TRACE_FLOW_STRING("insert_item", "Exit");
1141 return EOK;
1142 }
1143
1144
1145 /* Insert property with reference.
1146 * This is internal function so we do not check parameters.
1147 * See external wrapper below.
1148 */
col_insert_property_with_ref_int(struct collection_item * collection,const char * subcollection,int disposition,const char * refprop,int idx,unsigned flags,const char * property,int type,const void * data,int length,struct collection_item ** ret_ref)1149 static int col_insert_property_with_ref_int(struct collection_item *collection,
1150 const char *subcollection,
1151 int disposition,
1152 const char *refprop,
1153 int idx,
1154 unsigned flags,
1155 const char *property,
1156 int type,
1157 const void *data,
1158 int length,
1159 struct collection_item **ret_ref)
1160 {
1161 struct collection_item *item = NULL;
1162 int error;
1163
1164 TRACE_FLOW_STRING("col_insert_property_with_ref_int", "Entry point.");
1165
1166 /* Create a new property out of the given parameters */
1167 error = col_allocate_item(&item, property, data, length, type);
1168 if (error) {
1169 TRACE_ERROR_NUMBER("Failed to allocate item", error);
1170 return error;
1171 }
1172
1173 /* Send the property to the insert_item function */
1174 error = col_insert_item(collection,
1175 subcollection,
1176 item,
1177 disposition,
1178 refprop,
1179 idx,
1180 flags);
1181 if (error) {
1182 TRACE_ERROR_NUMBER("Failed to insert item", error);
1183 col_delete_item(item);
1184 return error;
1185 }
1186
1187 if (ret_ref) *ret_ref = item;
1188
1189 TRACE_FLOW_STRING("col_insert_property_with_ref_int", "Exit");
1190 return EOK;
1191 }
1192
1193 /* Special function used to copy item from one
1194 * collection to another using caller's callback.
1195 */
col_copy_item_with_cb(struct collection_item * collection,const char * property,int type,const void * data,int length,col_copy_cb copy_cb,void * ext_data)1196 static int col_copy_item_with_cb(struct collection_item *collection,
1197 const char *property,
1198 int type,
1199 const void *data,
1200 int length,
1201 col_copy_cb copy_cb,
1202 void *ext_data)
1203 {
1204 struct collection_item *item = NULL;
1205 int skip = 0;
1206 int error = EOK;
1207
1208 TRACE_FLOW_STRING("col_copy_item_with_cb", "Entry point.");
1209
1210 /* Create a new property out of the given parameters */
1211 error = col_allocate_item(&item, property, data, length, type);
1212 if (error) {
1213 TRACE_ERROR_NUMBER("Failed to allocate item", error);
1214 return error;
1215 }
1216
1217 /* Call callback if any */
1218 if (copy_cb) {
1219 TRACE_INFO_STRING("Calling callback for item:", item->property);
1220 error = copy_cb(item, ext_data, &skip);
1221 if (error) {
1222 TRACE_ERROR_NUMBER("Callback failed", error);
1223 col_delete_item(item);
1224 return error;
1225 }
1226 }
1227
1228 /* Are we told to skip this item? */
1229 if (skip) col_delete_item(item);
1230 else {
1231 /* Insted property into the collection */
1232 error = col_insert_item(collection,
1233 NULL,
1234 item,
1235 COL_DSP_END,
1236 NULL,
1237 0,
1238 0);
1239 if (error) {
1240 TRACE_ERROR_NUMBER("Failed to insert item", error);
1241 col_delete_item(item);
1242 return error;
1243 }
1244 }
1245
1246 TRACE_FLOW_STRING("col_copy_item_with_cb", "Exit");
1247 return EOK;
1248 }
1249
1250
1251 /* This is public function so we need to check the validity
1252 * of the arguments.
1253 */
col_insert_property_with_ref(struct collection_item * collection,const char * subcollection,int disposition,const char * refprop,int idx,unsigned flags,const char * property,int type,const void * data,int length,struct collection_item ** ret_ref)1254 int col_insert_property_with_ref(struct collection_item *collection,
1255 const char *subcollection,
1256 int disposition,
1257 const char *refprop,
1258 int idx,
1259 unsigned flags,
1260 const char *property,
1261 int type,
1262 const void *data,
1263 int length,
1264 struct collection_item **ret_ref)
1265 {
1266 int error;
1267
1268 TRACE_FLOW_STRING("col_insert_property_with_ref", "Entry point.");
1269
1270 /* Check that collection is not empty */
1271 if (collection == NULL) {
1272 TRACE_ERROR_STRING("Collection cant be NULL", "");
1273 return EINVAL;
1274 }
1275
1276 error = col_insert_property_with_ref_int(collection,
1277 subcollection,
1278 disposition,
1279 refprop,
1280 idx,
1281 flags,
1282 property,
1283 type,
1284 data,
1285 length,
1286 ret_ref);
1287
1288 TRACE_FLOW_NUMBER("col_insert_property_with_ref_int Returning:", error);
1289 return error;
1290 }
1291 /* TRAVERSE HANDLERS */
1292
1293 /* Special handler to just set a flag if the item is found */
col_is_in_item_handler(const char * property,int property_len,int type,void * data,int length,void * found,int * dummy)1294 static int col_is_in_item_handler(const char *property,
1295 int property_len,
1296 int type,
1297 void *data,
1298 int length,
1299 void *found,
1300 int *dummy)
1301 {
1302 TRACE_FLOW_STRING("col_is_in_item_handler", "Entry.");
1303 TRACE_INFO_STRING("Property:", property);
1304 TRACE_INFO_NUMBER("Property length:", property_len);
1305 TRACE_INFO_NUMBER("Type:", type);
1306 TRACE_INFO_NUMBER("Length:", length);
1307
1308 *((int *)(found)) = COL_MATCH;
1309
1310 TRACE_FLOW_STRING("col_is_in_item_handler", "Success Exit.");
1311
1312 return EOK;
1313 }
1314
1315 /* Special handler to retrieve the sub collection */
col_get_subcollection(const char * property,int property_len,int type,void * data,int length,void * found,int * dummy)1316 static int col_get_subcollection(const char *property,
1317 int property_len,
1318 int type,
1319 void *data,
1320 int length,
1321 void *found,
1322 int *dummy)
1323 {
1324 TRACE_FLOW_STRING("col_get_subcollection", "Entry.");
1325 TRACE_INFO_STRING("Property:", property);
1326 TRACE_INFO_NUMBER("Property length:", property_len);
1327 TRACE_INFO_NUMBER("Type:", type);
1328 TRACE_INFO_NUMBER("Length:", length);
1329
1330 *((struct collection_item **)(found)) = *((struct collection_item **)(data));
1331
1332 TRACE_FLOW_STRING("col_get_subcollection","Success Exit.");
1333
1334 return EOK;
1335
1336 }
1337
1338
1339
1340 /* CLEANUP */
1341
1342 /* Cleans the collection tree including current item. */
1343 /* The passed in variable should not be used after the call
1344 * as memory is freed!!! */
col_delete_collection(struct collection_item * ci,col_item_cleanup_fn cb,void * custom_data)1345 static void col_delete_collection(struct collection_item *ci,
1346 col_item_cleanup_fn cb,
1347 void *custom_data)
1348 {
1349 TRACE_FLOW_STRING("col_delete_collection", "Entry.");
1350
1351 if (ci == NULL) {
1352 TRACE_FLOW_STRING("col_delete_collection", "Nothing to do Exit.");
1353 return;
1354 }
1355
1356 TRACE_INFO_STRING("Real work to do", "");
1357 TRACE_INFO_STRING("Property", ci->property);
1358 TRACE_INFO_NUMBER("Next item", ci->next);
1359
1360 col_delete_collection(ci->next, cb, custom_data);
1361
1362 /* Delete this item */
1363 col_delete_item_with_cb(ci, cb, custom_data);
1364 TRACE_FLOW_STRING("col_delete_collection", "Exit.");
1365 }
1366
1367
1368 /* NAME MANAGEMENT - used by search */
1369
1370 /* Internal data structures used for search */
1371
1372
1373 struct find_name {
1374 const char *name_to_find;
1375 int name_len_to_find;
1376 uint64_t hash;
1377 int type_to_match;
1378 char *given_name;
1379 int given_len;
1380 struct path_data *current_path;
1381 int action;
1382 };
1383
1384 /* Create a new name */
col_create_path_data(struct path_data ** name_path,const char * name,int length,const char * property,int property_len,char sep)1385 static int col_create_path_data(struct path_data **name_path,
1386 const char *name, int length,
1387 const char *property, int property_len,
1388 char sep)
1389 {
1390 int error = EOK;
1391 struct path_data *new_name_path;
1392
1393 TRACE_FLOW_STRING("col_create_path_data", "Entry.");
1394
1395 TRACE_INFO_STRING("Constructing path from name:", name);
1396 TRACE_INFO_STRING("Constructing path from property:", property);
1397
1398 /* Allocate structure */
1399 new_name_path = (struct path_data *)malloc(sizeof(struct path_data));
1400 if (new_name_path == NULL) {
1401 TRACE_ERROR_NUMBER("Failed to allocate memory for new path struct.", ENOMEM);
1402 return ENOMEM;
1403 }
1404 new_name_path->name = malloc(length + property_len + 2);
1405 if (new_name_path->name == NULL) {
1406 TRACE_ERROR_NUMBER("Failed to allocate memory for new path name.", ENOMEM);
1407 free(new_name_path);
1408 return ENOMEM;
1409 }
1410
1411 /* Construct the new name */
1412 new_name_path->length = 0;
1413
1414 if(length > 0) {
1415 memcpy(new_name_path->name, name, length);
1416 new_name_path->length = length;
1417 new_name_path->name[new_name_path->length] = sep;
1418 new_name_path->length++;
1419 new_name_path->name[new_name_path->length] = '\0';
1420 TRACE_INFO_STRING("Name so far:", new_name_path->name);
1421 TRACE_INFO_NUMBER("Len so far:", new_name_path->length);
1422 }
1423 memcpy(&new_name_path->name[new_name_path->length], property, property_len);
1424 new_name_path->length += property_len;
1425 new_name_path->name[new_name_path->length] = '\0';
1426
1427 /* Link to the chain */
1428 new_name_path->previous_path = *name_path;
1429 *name_path = new_name_path;
1430
1431 TRACE_INFO_STRING("Constructed path", new_name_path->name);
1432
1433
1434 TRACE_FLOW_NUMBER("col_create_path_data. Returning:", error);
1435 return error;
1436 }
1437
1438 /* Matching item name and type */
col_match_item(struct collection_item * current,struct find_name * traverse_data)1439 static int col_match_item(struct collection_item *current,
1440 struct find_name *traverse_data)
1441 {
1442
1443 const char *find_str;
1444 const char *start;
1445 const char *data_str;
1446
1447 TRACE_FLOW_STRING("col_match_item", "Entry");
1448
1449 if (traverse_data->type_to_match & current->type) {
1450
1451 /* Check if there is any value to match */
1452 if ((traverse_data->name_to_find == NULL) ||
1453 (*(traverse_data->name_to_find) == '\0')) {
1454 TRACE_INFO_STRING("col_match_item",
1455 "Returning MATCH because there is no search criteria!");
1456 return COL_MATCH;
1457 }
1458
1459 /* Check the hashes - if they do not match return */
1460 if (traverse_data->hash != current->phash) {
1461 TRACE_INFO_STRING("col_match_item","Returning NO match!");
1462 return COL_NOMATCH;
1463 }
1464
1465 /* We will do the actual string comparison only if the hashes matched */
1466
1467 /* Start comparing the two strings from the end */
1468 find_str = traverse_data->name_to_find + traverse_data->name_len_to_find;
1469 start = current->property;
1470 data_str = start + current->property_len;
1471
1472 TRACE_INFO_STRING("Searching for:", traverse_data->name_to_find);
1473 TRACE_INFO_STRING("Item name:", current->property);
1474 TRACE_INFO_STRING("Current path:", traverse_data->current_path->name);
1475 TRACE_INFO_NUMBER("Searching:", toupper(*find_str));
1476 TRACE_INFO_NUMBER("Have:", toupper(*data_str));
1477
1478 /* We start pointing to 0 so the loop will be executed at least once */
1479 while (toupper(*data_str) == toupper(*find_str)) {
1480
1481 TRACE_INFO_STRING("Loop iteration:","");
1482
1483 if (data_str == start) {
1484 if (find_str > traverse_data->name_to_find) {
1485 if (*(find_str-1) == '!') {
1486 /* We matched the property but the search string is
1487 * longer so we need to continue matching */
1488 TRACE_INFO_STRING("col_match_item",
1489 "Need to continue matching");
1490 start = traverse_data->current_path->name;
1491 data_str = &start[traverse_data->current_path->length - 1];
1492 find_str -= 2;
1493 continue;
1494 }
1495 else {
1496 TRACE_INFO_STRING("col_match_item","Returning NO match!");
1497 return COL_NOMATCH;
1498 }
1499 }
1500 else {
1501 TRACE_INFO_STRING("col_match_item","Returning MATCH!");
1502 return COL_MATCH;
1503 }
1504 }
1505 else if ((find_str == traverse_data->name_to_find) &&
1506 (*(data_str-1) == '!')) return COL_MATCH;
1507
1508 data_str--;
1509 find_str--;
1510 TRACE_INFO_NUMBER("Searching:", toupper(*find_str));
1511 TRACE_INFO_NUMBER("Have:", toupper(*data_str));
1512
1513 }
1514 }
1515
1516 TRACE_FLOW_STRING("col_match_item","Returning NO match!");
1517 return COL_NOMATCH;
1518
1519 }
1520
1521 /* Function to delete the data that contains search path */
col_delete_path_data(struct path_data * path)1522 static void col_delete_path_data(struct path_data *path)
1523 {
1524 TRACE_FLOW_STRING("col_delete_path_data","Entry.");
1525
1526 if (path != NULL) {
1527 TRACE_INFO_STRING("col_delete_path_data", "Item to delete exits.");
1528 if (path->previous_path != NULL) {
1529 TRACE_INFO_STRING("col_delete_path_data",
1530 "But previous item to delete exits to. Nesting.");
1531 col_delete_path_data(path->previous_path);
1532 }
1533 if (path->name != NULL) {
1534 TRACE_INFO_STRING("col_delete_path_data Deleting path:", path->name);
1535 free(path->name);
1536 }
1537 TRACE_INFO_STRING("col_delete_path_data", "Deleting path element");
1538 free(path);
1539 }
1540 TRACE_FLOW_STRING("col_delete_path_data", "Exit");
1541 }
1542
1543
1544 /* MAIN TRAVERSAL FUNCTION */
1545
1546 /* Internal function to walk collection */
1547 /* For each item walked it will call traverse handler.
1548 Traverse handler accepts: current item,
1549 user provided item handler and user provided custom data. */
1550 /* See below different traverse handlers for different cases */
col_walk_items(struct collection_item * ci,int mode_flags,internal_item_fn traverse_handler,void * traverse_data,col_item_fn user_item_handler,void * custom_data,unsigned * depth)1551 static int col_walk_items(struct collection_item *ci,
1552 int mode_flags,
1553 internal_item_fn traverse_handler,
1554 void *traverse_data,
1555 col_item_fn user_item_handler,
1556 void *custom_data,
1557 unsigned *depth)
1558 {
1559 struct collection_item *current;
1560 struct collection_item *parent = NULL;
1561 struct collection_item *sub;
1562 int stop = 0;
1563 int error = EOK;
1564
1565 TRACE_FLOW_STRING("col_walk_items", "Entry.");
1566 TRACE_INFO_NUMBER("Mode flags:", mode_flags);
1567
1568 /* Increase depth */
1569 /* NOTE: The depth is increased at the entry to the function.
1570 * and decreased right before the exit so it is safe to decrease it.
1571 */
1572 (*depth)++;
1573
1574 current = ci;
1575
1576 while (current) {
1577
1578 TRACE_INFO_STRING("Processing item:", current->property);
1579 TRACE_INFO_NUMBER("Item type:", current->type);
1580
1581 if (current->type == COL_TYPE_COLLECTIONREF) {
1582
1583 TRACE_INFO_STRING("Subcollection:", current->property);
1584
1585 if ((mode_flags & COL_TRAVERSE_IGNORE) == 0) {
1586
1587 TRACE_INFO_STRING("Subcollection is not ignored.", "");
1588 /* We are not ignoring sub collections */
1589
1590 if ((mode_flags & COL_TRAVERSE_FLAT) == 0) {
1591
1592 TRACE_INFO_STRING("Subcollection is not flattened.", "");
1593 /* We are not flattening sub collections.
1594 * The flattening means that we are not going
1595 * to return reference and headers for sub collections.
1596 * We will also not do special end collection
1597 * invocation for sub collections.
1598 */
1599 error = traverse_handler(ci, parent, current, traverse_data,
1600 user_item_handler, custom_data, &stop);
1601 if (stop != 0) {
1602 TRACE_INFO_STRING("Traverse handler returned STOP.", "");
1603 error = EINTR_INTERNAL;
1604 }
1605 /* Check what error we got */
1606 if (error == EINTR_INTERNAL) {
1607 TRACE_FLOW_NUMBER("Internal error - means we are stopping.", error);
1608 (*depth)--;
1609 return error;
1610 }
1611 else if (error) {
1612 TRACE_ERROR_NUMBER("Traverse handler returned error.", error);
1613 (*depth)--;
1614 return error;
1615 }
1616 }
1617
1618 if ((mode_flags & COL_TRAVERSE_ONELEVEL) == 0) {
1619 TRACE_INFO_STRING("Before diving into sub collection","");
1620 sub = *((struct collection_item **)(current->data));
1621 TRACE_INFO_STRING("Sub collection name", sub->property);
1622 TRACE_INFO_NUMBER("Header type", sub->type);
1623 /* We need to go into sub collections */
1624 error = col_walk_items(sub, mode_flags,
1625 traverse_handler, traverse_data,
1626 user_item_handler, custom_data,
1627 depth);
1628 TRACE_INFO_STRING("Returned from sub collection processing", "");
1629 TRACE_INFO_STRING("Done processing item:", current->property);
1630 TRACE_INFO_NUMBER("Done processing item type:", current->type);
1631
1632 }
1633 }
1634 }
1635 else {
1636 /* Check if it is a header and we are not on the root level.
1637 * If we are flattening collection we need to skip headers
1638 * for sub collections.
1639 */
1640
1641 /* Call handler if:
1642 * a) It is not collection header
1643 * OR
1644 * b) It is header we are flattening but we are on top level
1645 * OR
1646 * c) It is header and we are not flattening.
1647 */
1648 if ((current->type != COL_TYPE_COLLECTION) ||
1649 (((mode_flags & COL_TRAVERSE_FLAT) != 0) && (*depth == 1)) ||
1650 ((mode_flags & COL_TRAVERSE_FLAT) == 0)) {
1651 /* Call handler then move on */
1652 error = traverse_handler(ci, parent, current,
1653 traverse_data, user_item_handler,
1654 custom_data, &stop);
1655
1656 }
1657 }
1658 /* If we are stopped - return EINTR_INTERNAL */
1659 if (stop != 0) {
1660 TRACE_INFO_STRING("Traverse handler returned STOP.", "");
1661 error = EINTR_INTERNAL;
1662 }
1663 /* Check what error we got */
1664 if (error == EINTR_INTERNAL) {
1665 TRACE_FLOW_NUMBER("Internal error - means we are stopping.", error);
1666 (*depth)--;
1667 return error;
1668 }
1669 else if (error) {
1670 TRACE_ERROR_NUMBER("Traverse handler returned error.", error);
1671 (*depth)--;
1672 return error;
1673 }
1674
1675 TRACE_INFO_NUMBER("Next element", current->next);
1676
1677 parent = current;
1678 current = current->next;
1679 }
1680
1681 TRACE_INFO_STRING("Out of loop", "");
1682
1683 /* Check if we need to have a special
1684 * call at the end of the collection.
1685 */
1686 if ((mode_flags & COL_TRAVERSE_END) != 0) {
1687
1688 /* Do this dummy invocation only:
1689 * a) If we are flattening and on the root level
1690 * b) We are not flattening
1691 */
1692 if ((((mode_flags & COL_TRAVERSE_FLAT) != 0) && (*depth == 1)) ||
1693 ((mode_flags & COL_TRAVERSE_FLAT) == 0)) {
1694
1695 TRACE_INFO_STRING("About to do the special end collection invocation of handler", "");
1696 error = traverse_handler(ci, parent, current,
1697 traverse_data, user_item_handler,
1698 custom_data, &stop);
1699 }
1700 }
1701
1702 TRACE_FLOW_NUMBER("col_walk_items. Returns: ", error);
1703 (*depth)--;
1704 return error;
1705 }
1706
1707
1708 /* ACTION */
1709
1710 /* Find an item by property name and perform an action on it. */
1711 /* No pattern matching supported in the first implementation. */
1712 /* To refer to child properties use notatation like this: */
1713 /* parent!child!subchild!subsubchild etc. */
col_find_item_and_do(struct collection_item * ci,const char * property_to_find,int type,int mode_flags,col_item_fn item_handler,void * custom_data,int action)1714 static int col_find_item_and_do(struct collection_item *ci,
1715 const char *property_to_find,
1716 int type,
1717 int mode_flags,
1718 col_item_fn item_handler,
1719 void *custom_data,
1720 int action)
1721 {
1722
1723 int error = EOK;
1724 struct find_name *traverse_data = NULL;
1725 unsigned depth = 0;
1726 int count = 0;
1727 const char *last_part;
1728 char *sep;
1729
1730 TRACE_FLOW_STRING("col_find_item_and_do", "Entry.");
1731
1732 /* Item handler is always required */
1733 if ((item_handler == NULL) &&
1734 (action == COLLECTION_ACTION_FIND)) {
1735 TRACE_ERROR_NUMBER("No item handler - returning error!", EINVAL);
1736 return EINVAL;
1737 }
1738
1739 /* Collection is requered */
1740 if (ci == NULL) {
1741 TRACE_ERROR_NUMBER("No collection to search!", EINVAL);
1742 return EINVAL;
1743 }
1744
1745 /* Make sure that there is anything to search */
1746 type &= COL_TYPE_ANY;
1747 if ((type == 0) &&
1748 ((property_to_find == NULL) ||
1749 ((property_to_find != NULL) && (*property_to_find == '\0')))) {
1750 TRACE_ERROR_NUMBER("No item search criteria specified - returning error!", ENOENT);
1751 return ENOENT;
1752 }
1753
1754 /* Prepare data for traversal */
1755 traverse_data = (struct find_name *)malloc(sizeof(struct find_name));
1756 if (traverse_data == NULL) {
1757 TRACE_ERROR_NUMBER("Failed to allocate traverse data memory - returning error!", ENOMEM);
1758 return ENOMEM;
1759 }
1760
1761 TRACE_INFO_STRING("col_find_item_and_do", "Filling in traverse data.");
1762
1763 traverse_data->name_to_find = property_to_find;
1764
1765 if (property_to_find != NULL) {
1766
1767 traverse_data->name_len_to_find = strlen(property_to_find);
1768
1769 /* Check if the search string ends with "!" - this is illegal */
1770 if (traverse_data->name_to_find[traverse_data->name_len_to_find - 1] == '!') {
1771 TRACE_ERROR_NUMBER("Search string is invalid.", EINVAL);
1772 free(traverse_data);
1773 return EINVAL;
1774 }
1775
1776 /* Find last ! if any */
1777 sep = strrchr(traverse_data->name_to_find, '!');
1778 if (sep != NULL) {
1779 sep++;
1780 last_part = sep;
1781 }
1782 else last_part = traverse_data->name_to_find;
1783
1784 TRACE_INFO_STRING("Last item", last_part);
1785
1786 /* Create hash of the last part */
1787 traverse_data->hash = FNV1a_base;
1788
1789 /* Create hash of the string to search */
1790 while(last_part[count] != 0) {
1791 traverse_data->hash = traverse_data->hash ^ toupper(last_part[count]);
1792 traverse_data->hash *= FNV1a_prime;
1793 count++;
1794 }
1795 }
1796 else {
1797 /* We a looking for a first element of a given type */
1798 TRACE_INFO_STRING("No search string", "");
1799 traverse_data->name_len_to_find = 0;
1800 }
1801
1802
1803 traverse_data->type_to_match = type;
1804 traverse_data->given_name = NULL;
1805 traverse_data->given_len = 0;
1806 traverse_data->current_path = NULL;
1807 traverse_data->action = action;
1808
1809 mode_flags |= COL_TRAVERSE_END;
1810
1811 TRACE_INFO_STRING("col_find_item_and_do", "About to walk the tree.");
1812 TRACE_INFO_NUMBER("Traverse flags", mode_flags);
1813
1814 error = col_walk_items(ci, mode_flags, col_act_traverse_handler,
1815 (void *)traverse_data, item_handler, custom_data,
1816 &depth);
1817
1818 if (traverse_data->current_path != NULL) {
1819 TRACE_INFO_STRING("find_item_and_do",
1820 "Path was not cleared - deleting");
1821 col_delete_path_data(traverse_data->current_path);
1822 }
1823
1824 free(traverse_data);
1825
1826 if (error && (error != EINTR_INTERNAL)) {
1827 TRACE_ERROR_NUMBER("Walk items returned error. Returning: ", error);
1828 return error;
1829 }
1830 else {
1831 TRACE_FLOW_STRING("Walk items returned SUCCESS.", "");
1832 return EOK;
1833 }
1834 }
1835
1836 /* Function to replace data in the item */
col_update_current_item(struct collection_item * current,struct update_property * update_data)1837 static int col_update_current_item(struct collection_item *current,
1838 struct update_property *update_data)
1839 {
1840 TRACE_FLOW_STRING("col_update_current_item", "Entry");
1841
1842 /* If type is different or same but it is string or binary we need to
1843 * replace the storage */
1844 if ((current->type != update_data->type) ||
1845 ((current->type == update_data->type) &&
1846 ((current->type == COL_TYPE_STRING) ||
1847 (current->type == COL_TYPE_BINARY)))) {
1848 TRACE_INFO_STRING("Replacing item data buffer", "");
1849 free(current->data);
1850 current->data = malloc(update_data->length);
1851 if (current->data == NULL) {
1852 TRACE_ERROR_STRING("Failed to allocate memory", "");
1853 current->length = 0;
1854 return ENOMEM;
1855 }
1856 current->length = update_data->length;
1857 }
1858
1859 TRACE_INFO_STRING("Overwriting item data", "");
1860 memcpy(current->data, update_data->data, current->length);
1861 current->type = update_data->type;
1862
1863 if (current->type == COL_TYPE_STRING)
1864 ((char *)(current->data))[current->length-1] = '\0';
1865
1866 TRACE_FLOW_STRING("update_current_item", "Exit");
1867 return EOK;
1868 }
1869
1870 /* TRAVERSE CALLBACKS */
1871
1872 /* Traverse handler for simple traverse function */
1873 /* Handler must be able to deal with NULL current item */
col_simple_traverse_handler(struct collection_item * head,struct collection_item * previous,struct collection_item * current,void * traverse_data,col_item_fn user_item_handler,void * custom_data,int * stop)1874 static int col_simple_traverse_handler(struct collection_item *head,
1875 struct collection_item *previous,
1876 struct collection_item *current,
1877 void *traverse_data,
1878 col_item_fn user_item_handler,
1879 void *custom_data,
1880 int *stop)
1881 {
1882 int error = EOK;
1883 struct collection_item end_item;
1884 char zero = '\0';
1885
1886 TRACE_FLOW_STRING("col_simple_traverse_handler", "Entry.");
1887
1888 if (current == NULL) {
1889 memset((void *)&end_item, 0, sizeof(struct collection_item));
1890 end_item.type = COL_TYPE_END;
1891 end_item.property = &zero;
1892 current = &end_item;
1893 }
1894
1895 error = user_item_handler(current->property,
1896 current->property_len,
1897 current->type,
1898 current->data,
1899 current->length,
1900 custom_data,
1901 stop);
1902
1903 TRACE_FLOW_NUMBER("col_simple_traverse_handler. Returning:", error);
1904 return error;
1905 }
1906
1907 /* Traverse handler for to find parent */
col_parent_traverse_handler(struct collection_item * head,struct collection_item * previous,struct collection_item * current,void * traverse_data,col_item_fn user_item_handler,void * custom_data,int * stop)1908 static int col_parent_traverse_handler(struct collection_item *head,
1909 struct collection_item *previous,
1910 struct collection_item *current,
1911 void *traverse_data,
1912 col_item_fn user_item_handler,
1913 void *custom_data,
1914 int *stop)
1915 {
1916 struct property_search *to_find;
1917 int done = 0;
1918 int match = 0;
1919
1920 TRACE_FLOW_STRING("col_parent_traverse_handler", "Entry.");
1921
1922 to_find = (struct property_search *)custom_data;
1923
1924 TRACE_INFO_NUMBER("Looking for HASH:", (unsigned)(to_find->hash));
1925 TRACE_INFO_NUMBER("Current HASH:", (unsigned)(current->phash));
1926
1927 /* Check hashes first */
1928 if(to_find->hash == current->phash) {
1929
1930 /* Check type if we are asked to use type */
1931 if ((to_find->use_type) && (!(to_find->type & current->type))) {
1932 TRACE_FLOW_STRING("parent_traverse_handler. Returning:","Exit. Hash is Ok, type is not");
1933 return EOK;
1934 }
1935
1936 /* Validate property. Make sure we include terminating 0 in the comparison */
1937 if (strncasecmp(current->property, to_find->property, current->property_len + 1) == 0) {
1938
1939 match = 1;
1940 to_find->found = 1;
1941
1942 /* Do the right thing based on index */
1943 /* If index is 0 we are looking for the first value in the list of duplicate properties */
1944 if (to_find->index == 0) done = 1;
1945 /* If index is non zero we are looking for N-th instance of the dup property */
1946 else if (to_find->index > 0) {
1947 if (to_find->count == to_find->index) done = 1;
1948 else {
1949 /* Record found instance and move on */
1950 to_find->parent = previous;
1951 (to_find->count)++;
1952 }
1953 }
1954 /* If we are looking for last instance just record it */
1955 else to_find->parent = previous;
1956 }
1957 }
1958
1959 if (done) {
1960 *stop = 1;
1961 *((struct collection_item **)traverse_data) = previous;
1962 }
1963 else {
1964 if (to_find->interrupt) {
1965 /* As soon as we found first non matching one but there was a match
1966 * return the parent of the last found item.
1967 */
1968 if (((!match) || (current->next == NULL)) &&
1969 (to_find->index != 0) && (to_find->found)) {
1970 *stop = 1;
1971 if (to_find->index == -2)
1972 *((struct collection_item **)traverse_data) =
1973 to_find->parent;
1974 else
1975 if (to_find->exact) {
1976 /* This means that we need to match the exact
1977 * index but we did not */
1978 to_find->parent = NULL;
1979 to_find->found = 0;
1980 }
1981 else
1982 *((struct collection_item **)traverse_data) =
1983 to_find->parent->next;
1984 }
1985 }
1986 else if ((current->next == NULL) && (to_find->found)) {
1987 *stop = 1;
1988 if (to_find->index == -2)
1989 *((struct collection_item **)traverse_data) =
1990 to_find->parent;
1991 else
1992 if (to_find->exact) {
1993 /* This means that we need to match the exact
1994 * index but we did not */
1995 to_find->parent = NULL;
1996 to_find->found = 0;
1997 }
1998 else
1999 *((struct collection_item **)traverse_data) =
2000 to_find->parent->next;
2001 }
2002 }
2003
2004
2005 TRACE_FLOW_STRING("col_parent_traverse_handler. Returning:","Exit");
2006 return EOK;
2007 }
2008
2009
2010 /* Traverse callback for find & delete function */
col_act_traverse_handler(struct collection_item * head,struct collection_item * previous,struct collection_item * current,void * passed_traverse_data,col_item_fn user_item_handler,void * custom_data,int * stop)2011 static int col_act_traverse_handler(struct collection_item *head,
2012 struct collection_item *previous,
2013 struct collection_item *current,
2014 void *passed_traverse_data,
2015 col_item_fn user_item_handler,
2016 void *custom_data,
2017 int *stop)
2018 {
2019 int error = EOK;
2020 struct find_name *traverse_data = NULL;
2021 char *name;
2022 int length;
2023 struct path_data *temp;
2024 struct collection_header *header;
2025 char *property;
2026 int property_len;
2027 struct update_property *update_data;
2028
2029 TRACE_FLOW_STRING("col_act_traverse_handler", "Entry.");
2030
2031 traverse_data = (struct find_name *)passed_traverse_data;
2032
2033 /* We can be called when current points to NULL */
2034 if (current == NULL) {
2035 TRACE_INFO_STRING("col_act_traverse_handler",
2036 "Special call at the end of the collection.");
2037 temp = traverse_data->current_path;
2038 traverse_data->current_path = temp->previous_path;
2039 temp->previous_path = NULL;
2040 col_delete_path_data(temp);
2041 traverse_data->given_name = NULL;
2042 traverse_data->given_len = 0;
2043 TRACE_FLOW_NUMBER("Handling end of collection - removed path. Returning:", error);
2044 return error;
2045 }
2046
2047 /* Create new path at the beginning of a new sub collection */
2048 if (current->type == COL_TYPE_COLLECTION) {
2049
2050 TRACE_INFO_STRING("col_act_traverse_handler",
2051 "Processing collection handle.");
2052
2053 /* Create new path */
2054 if (traverse_data->current_path != NULL) {
2055 TRACE_INFO_STRING("Already have part of the path", "");
2056 name = traverse_data->current_path->name;
2057 length = traverse_data->current_path->length;
2058 TRACE_INFO_STRING("Path:", name);
2059 TRACE_INFO_NUMBER("Path len:", length);
2060 }
2061 else {
2062 name = NULL;
2063 length = 0;
2064 }
2065
2066 if (traverse_data->given_name != NULL) {
2067 property = traverse_data->given_name;
2068 property_len = traverse_data->given_len;
2069 }
2070 else {
2071 property = current->property;
2072 property_len = current->property_len;
2073 }
2074
2075 TRACE_INFO_STRING("col_act_traverse_handler", "About to create path data.");
2076
2077 error = col_create_path_data(&(traverse_data->current_path),
2078 name, length,
2079 property, property_len, '!');
2080
2081 TRACE_INFO_NUMBER("col_create_path_data returned:", error);
2082 return error;
2083 }
2084
2085 /* Handle the collection pointers */
2086 if (current->type == COL_TYPE_COLLECTIONREF) {
2087 traverse_data->given_name = current->property;
2088 traverse_data->given_len = current->property_len;
2089 TRACE_INFO_STRING("Saved given name:", traverse_data->given_name);
2090 }
2091
2092 TRACE_INFO_STRING("Processing item with property:", current->property);
2093
2094 /* Do here what we do with items */
2095 if (col_match_item(current, traverse_data)) {
2096 TRACE_INFO_STRING("Matched item:", current->property);
2097 switch (traverse_data->action) {
2098 case COLLECTION_ACTION_FIND:
2099 TRACE_INFO_STRING("It is a find action - calling handler.", "");
2100 if (user_item_handler != NULL) {
2101 /* Call user handler */
2102 error = user_item_handler(current->property,
2103 current->property_len,
2104 current->type,
2105 current->data,
2106 current->length,
2107 custom_data,
2108 stop);
2109
2110 TRACE_INFO_NUMBER("Handler returned:", error);
2111 TRACE_INFO_NUMBER("Handler set STOP to:", *stop);
2112
2113 }
2114 break;
2115
2116 case COLLECTION_ACTION_GET:
2117 TRACE_INFO_STRING("It is a get action.", "");
2118 if (custom_data != NULL)
2119 *((struct collection_item **)(custom_data)) = current;
2120 break;
2121
2122 case COLLECTION_ACTION_DEL:
2123 TRACE_INFO_STRING("It is a delete action.", "");
2124 /* Make sure we tell the caller we found a match */
2125 if (custom_data != NULL)
2126 *(int *)custom_data = COL_MATCH;
2127
2128 /* Adjust header of the collection */
2129 header = (struct collection_header *)head->data;
2130 header->count--;
2131 if (current->next == NULL)
2132 header->last = previous;
2133
2134 /* Unlink and delete iteam */
2135 /* Previous can't be NULL here becuase we never delete
2136 * header elements */
2137 previous->next = current->next;
2138 col_delete_item(current);
2139 TRACE_INFO_STRING("Did the delete of the item.", "");
2140 break;
2141
2142 case COLLECTION_ACTION_UPDATE:
2143 TRACE_INFO_STRING("It is an update action.", "");
2144 if((current->type == COL_TYPE_COLLECTION) ||
2145 (current->type == COL_TYPE_COLLECTIONREF)) {
2146 TRACE_ERROR_STRING("Can't update collections it is an error for now", "");
2147 return EINVAL;
2148 }
2149
2150 /* Make sure we tell the caller we found a match */
2151 if (custom_data != NULL) {
2152 update_data = (struct update_property *)custom_data;
2153 update_data->found = COL_MATCH;
2154 error = col_update_current_item(current, update_data);
2155 }
2156 else {
2157 TRACE_ERROR_STRING("Error - update data is required", "");
2158 return EINVAL;
2159 }
2160
2161 TRACE_INFO_STRING("Did the delete of the item.", "");
2162 break;
2163 default:
2164 break;
2165 }
2166 /* Force interrupt if we found */
2167 *stop = 1;
2168 }
2169
2170 TRACE_FLOW_NUMBER("col_act_traverse_handler returning", error);
2171 return error;
2172 }
2173
2174
2175 /* Traverse handler for copy function */
col_copy_traverse_handler(struct collection_item * head,struct collection_item * previous,struct collection_item * current,void * passed_traverse_data,col_item_fn user_item_handler,void * custom_data,int * stop)2176 static int col_copy_traverse_handler(struct collection_item *head,
2177 struct collection_item *previous,
2178 struct collection_item *current,
2179 void *passed_traverse_data,
2180 col_item_fn user_item_handler,
2181 void *custom_data,
2182 int *stop)
2183 {
2184 int error = EOK;
2185 struct collection_item *parent;
2186 struct collection_item *other = NULL;
2187 struct col_copy *traverse_data;
2188 struct path_data *temp;
2189 char *name;
2190 int length;
2191 char *property = NULL;
2192 int property_len;
2193 struct collection_header *header;
2194 char *offset;
2195
2196 TRACE_FLOW_STRING("col_copy_traverse_handler", "Entry.");
2197
2198 parent = (struct collection_item *)custom_data;
2199 traverse_data = (struct col_copy *)passed_traverse_data;
2200
2201 /* We can be called when current points to NULL */
2202 /* This will happen only in the FLATDOT case */
2203 if (current == NULL) {
2204 TRACE_INFO_STRING("col_copy_traverse_handler",
2205 "Special call at the end of the collection.");
2206 temp = traverse_data->current_path;
2207 traverse_data->current_path = temp->previous_path;
2208 temp->previous_path = NULL;
2209 col_delete_path_data(temp);
2210 traverse_data->given_name = NULL;
2211 traverse_data->given_len = 0;
2212 TRACE_FLOW_NUMBER("Handling end of collection - removed path. Returning:", error);
2213 return error;
2214 }
2215
2216 /* Create new path at the beginning of a new sub collection */
2217 if (current->type == COL_TYPE_COLLECTION) {
2218
2219 TRACE_INFO_STRING("col_copy_traverse_handler",
2220 "Processing collection handle.");
2221 if (traverse_data->mode == COL_COPY_FLATDOT) {
2222 /* Create new path */
2223 if (traverse_data->current_path != NULL) {
2224 TRACE_INFO_STRING("Already have part of the path", "");
2225 name = traverse_data->current_path->name;
2226 length = traverse_data->current_path->length;
2227 TRACE_INFO_STRING("Path:", name);
2228 TRACE_INFO_NUMBER("Path len:", length);
2229 if (traverse_data->given_name != NULL) {
2230 property = traverse_data->given_name;
2231 property_len = traverse_data->given_len;
2232 }
2233 else {
2234 property = current->property;
2235 property_len = current->property_len;
2236 }
2237 }
2238 else {
2239 /* Do not create prefix for top collection
2240 * if there is no given name.
2241 */
2242 name = NULL;
2243 length = 0;
2244 if (traverse_data->given_name != NULL) {
2245 property = traverse_data->given_name;
2246 property_len = traverse_data->given_len;
2247 }
2248 else {
2249 property = NULL;
2250 property_len = 0;
2251 }
2252 }
2253
2254 TRACE_INFO_STRING("col_copy_traverse_handler", "About to create path data.");
2255
2256 error = col_create_path_data(&(traverse_data->current_path),
2257 name, length,
2258 property, property_len, '.');
2259
2260 TRACE_FLOW_NUMBER("col_copy_traverse_handler processed header:", error);
2261 return error;
2262 }
2263 else {
2264 TRACE_FLOW_NUMBER("col_copy_traverse_handler skipping the header:", error);
2265 return error;
2266 }
2267 }
2268
2269
2270 /* Check if this is a special case of sub collection */
2271 if (current->type == COL_TYPE_COLLECTIONREF) {
2272
2273 TRACE_INFO_STRING("Found a subcollection we need to copy. Name:",
2274 current->property);
2275
2276 /* Based on the mode we need to do different things */
2277 switch (traverse_data->mode) {
2278 case COL_COPY_NORMAL:
2279
2280 error = col_copy_collection_with_cb(&other,
2281 *((struct collection_item **)(current->data)),
2282 current->property,
2283 COL_COPY_NORMAL,
2284 traverse_data->copy_cb,
2285 traverse_data->ext_data);
2286 if (error) {
2287 TRACE_ERROR_NUMBER("Copy subcollection returned error:", error);
2288 return error;
2289 }
2290
2291 /* Add new item to a collection
2292 * all references are now sub collections */
2293 error = col_insert_property_with_ref_int(parent,
2294 NULL,
2295 COL_DSP_END,
2296 NULL,
2297 0,
2298 0,
2299 current->property,
2300 COL_TYPE_COLLECTIONREF,
2301 (void *)(&other),
2302 sizeof(struct collection_item **),
2303 NULL);
2304
2305 TRACE_FLOW_NUMBER("col_copy_traverse_handler returning in NORMAL mode:", error);
2306 return error;
2307
2308 case COL_COPY_KEEPREF:
2309
2310 /* Just increase reference count of the referenced collection */
2311 other = *((struct collection_item **)(current->data));
2312 header = (struct collection_header *)(other->data);
2313 header->reference_count++;
2314
2315 /* Add new item to a collection
2316 * all references are now sub collections */
2317 error = col_insert_property_with_ref_int(parent,
2318 NULL,
2319 COL_DSP_END,
2320 NULL,
2321 0,
2322 0,
2323 current->property,
2324 COL_TYPE_COLLECTIONREF,
2325 (void *)(&other),
2326 sizeof(struct collection_item **),
2327 NULL);
2328 TRACE_FLOW_NUMBER("col_copy_traverse_handler returning in KEEPREF mode:", error);
2329 return error;
2330
2331 case COL_COPY_TOP:
2332 /* Told to ignore sub collections */
2333 TRACE_FLOW_NUMBER("col_copy_traverse_handler returning in TOP mode:", error);
2334 return error;
2335
2336 case COL_COPY_FLATDOT:
2337
2338 traverse_data->given_name = current->property;
2339 traverse_data->given_len = current->property_len;
2340 TRACE_INFO_STRING("Saved given name:", traverse_data->given_name);
2341 TRACE_FLOW_NUMBER("col_copy_traverse_handler returning in FLATDOT mode:", error);
2342 return error;
2343
2344 /* NOTE: The mode COL_COPY_FLAT is not in the list of cases becuase
2345 * in this flat mode we traverse collection using COL_TRAVERSE_FLAT flag
2346 * thus we should not be called on referenced collections at all
2347 * by the col_walk_items() function.
2348 */
2349
2350 default:
2351 TRACE_ERROR_NUMBER("col_copy_traverse_handler bad mode error:", EINVAL);
2352 return EINVAL;
2353 }
2354 }
2355 else {
2356
2357 if (traverse_data->mode == COL_COPY_FLATDOT) {
2358 /* Since this code can't use asprintf have to do it hard way */
2359 property = malloc(traverse_data->current_path->length +
2360 current->property_len + 2);
2361 if (property == NULL) {
2362 TRACE_ERROR_NUMBER("Failed to allocate memory for a new name:", error);
2363 return error;
2364 }
2365 /* Add first part and dot only if we have prefix */
2366 offset = property;
2367 if (traverse_data->current_path->length) {
2368 memcpy(offset, traverse_data->current_path->name,
2369 traverse_data->current_path->length);
2370 offset[traverse_data->current_path->length] = '.';
2371 offset += traverse_data->current_path->length + 1;
2372 }
2373 memcpy(offset, current->property, current->property_len);
2374 offset[current->property_len] = '\0';
2375 }
2376 else property = current->property;
2377
2378 TRACE_INFO_STRING("Using property:", property);
2379
2380 error = col_copy_item_with_cb(parent,
2381 property,
2382 current->type,
2383 current->data,
2384 current->length,
2385 traverse_data->copy_cb,
2386 traverse_data->ext_data);
2387
2388 /* Free property if we allocated it */
2389 if (traverse_data->mode == COL_COPY_FLATDOT) free(property);
2390
2391 if (error) {
2392 TRACE_ERROR_NUMBER("Failed to copy property:", error);
2393 return error;
2394 }
2395 }
2396
2397 TRACE_FLOW_NUMBER("col_copy_traverse_handler returning", error);
2398 return error;
2399 }
2400
2401
2402
2403
2404 /********************* MAIN INTERFACE FUNCTIONS *****************************/
2405
2406
2407 /* CREATE */
2408
2409 /* Function that creates an named collection of a given class*/
col_create_collection(struct collection_item ** ci,const char * name,unsigned cclass)2410 int col_create_collection(struct collection_item **ci, const char *name,
2411 unsigned cclass)
2412 {
2413 struct collection_item *handle = NULL;
2414 struct collection_header header;
2415 int error = EOK;
2416
2417 TRACE_FLOW_STRING("col_create_collection", "Entry.");
2418
2419 /* Prepare header */
2420 header.last = NULL;
2421 header.reference_count = 1;
2422 header.count = 0;
2423 header.cclass = cclass;
2424
2425 /* Create a collection type property */
2426 error = col_insert_property_with_ref_int(NULL,
2427 NULL,
2428 COL_DSP_END,
2429 NULL,
2430 0,
2431 0,
2432 name,
2433 COL_TYPE_COLLECTION,
2434 &header,
2435 sizeof(header),
2436 &handle);
2437
2438
2439 if (error) return error;
2440
2441 *ci = handle;
2442
2443 TRACE_FLOW_STRING("col_create_collection", "Success Exit.");
2444 return EOK;
2445 }
2446
2447
2448 /* DESTROY */
2449
2450 /* Function that destroys a collection */
col_destroy_collection_with_cb(struct collection_item * ci,col_item_cleanup_fn cb,void * custom_data)2451 void col_destroy_collection_with_cb(struct collection_item *ci,
2452 col_item_cleanup_fn cb,
2453 void *custom_data)
2454 {
2455 struct collection_header *header;
2456
2457 TRACE_FLOW_STRING("col_destroy_collection_with_cb", "Entry.");
2458
2459 /* Do not try to delete NULL */
2460 if (ci == NULL) return;
2461
2462 /* You can delete only whole collection not a part of it */
2463 if (ci->type != COL_TYPE_COLLECTION) {
2464 TRACE_ERROR_STRING("Attempt to delete a non collection - BAD!", "");
2465 TRACE_ERROR_NUMBER("Actual type is:", ci->type);
2466 return;
2467 }
2468
2469 TRACE_INFO_STRING("Name:", ci->property);
2470
2471 /* Collection can be referenced by other collection */
2472 header = (struct collection_header *)(ci->data);
2473 TRACE_INFO_NUMBER("Reference count:", header->reference_count);
2474 if (header->reference_count > 1) {
2475 TRACE_INFO_STRING("Dereferencing a referenced collection.", "");
2476 header->reference_count--;
2477 TRACE_INFO_NUMBER("Number after dereferencing.",
2478 header->reference_count);
2479 }
2480 else {
2481 col_delete_collection(ci, cb, custom_data);
2482 }
2483
2484 TRACE_FLOW_STRING("col_destroy_collection_with_cb", "Exit.");
2485 }
2486
2487
2488 /* Function that destroys a collection */
col_destroy_collection(struct collection_item * ci)2489 void col_destroy_collection(struct collection_item *ci)
2490 {
2491 TRACE_FLOW_STRING("col_destroy_collection", "Entry.");
2492
2493 col_destroy_collection_with_cb(ci, NULL, NULL);
2494
2495 TRACE_FLOW_STRING("col_destroy_collection", "Exit.");
2496 }
2497
2498 /* COPY */
2499
2500 /* Wrapper around a more advanced function */
col_copy_collection(struct collection_item ** collection_copy,struct collection_item * collection_to_copy,const char * name_to_use,int copy_mode)2501 int col_copy_collection(struct collection_item **collection_copy,
2502 struct collection_item *collection_to_copy,
2503 const char *name_to_use,
2504 int copy_mode)
2505 {
2506 int error = EOK;
2507 TRACE_FLOW_STRING("col_copy_collection", "Entry.");
2508
2509 error = col_copy_collection_with_cb(collection_copy,
2510 collection_to_copy,
2511 name_to_use,
2512 copy_mode,
2513 NULL,
2514 NULL);
2515
2516 TRACE_FLOW_NUMBER("col_copy_collection. Exit. Returning", error);
2517 return error;
2518 }
2519
2520 /* Create a deep copy of the current collection. */
2521 /* Referenced collections of the donor are copied as sub collections. */
col_copy_collection_with_cb(struct collection_item ** collection_copy,struct collection_item * collection_to_copy,const char * name_to_use,int copy_mode,col_copy_cb copy_cb,void * ext_data)2522 int col_copy_collection_with_cb(struct collection_item **collection_copy,
2523 struct collection_item *collection_to_copy,
2524 const char *name_to_use,
2525 int copy_mode,
2526 col_copy_cb copy_cb,
2527 void *ext_data)
2528 {
2529 int error = EOK;
2530 struct collection_item *new_collection = NULL;
2531 const char *name;
2532 struct collection_header *header;
2533 unsigned depth = 0;
2534 struct col_copy traverse_data;
2535 int flags;
2536
2537 TRACE_FLOW_STRING("col_copy_collection_with_cb", "Entry.");
2538
2539 /* Collection is required */
2540 if (collection_to_copy == NULL) {
2541 TRACE_ERROR_NUMBER("No collection to search!", EINVAL);
2542 return EINVAL;
2543 }
2544
2545 /* Storage is required too */
2546 if (collection_copy == NULL) {
2547 TRACE_ERROR_NUMBER("No memory provided to receive collection copy!", EINVAL);
2548 return EINVAL;
2549 }
2550
2551 /* NOTE: Refine this check if adding a new copy mode */
2552 if ((copy_mode < 0) || (copy_mode > COL_COPY_TOP)) {
2553 TRACE_ERROR_NUMBER("Invalid copy mode:", copy_mode);
2554 return EINVAL;
2555 }
2556
2557 /* Determine what name to use */
2558 if (name_to_use != NULL)
2559 name = name_to_use;
2560 else
2561 name = collection_to_copy->property;
2562
2563 header = (struct collection_header *)collection_to_copy->data;
2564
2565 /* Create a new collection */
2566 error = col_create_collection(&new_collection, name, header->cclass);
2567 if (error) {
2568 TRACE_ERROR_NUMBER("col_create_collection failed returning", error);
2569 return error;
2570 }
2571
2572 traverse_data.mode = copy_mode;
2573 traverse_data.current_path = NULL;
2574 traverse_data.given_name = NULL;
2575 traverse_data.given_len = 0;
2576 traverse_data.copy_cb = copy_cb;
2577 traverse_data.ext_data = ext_data;
2578
2579 if (copy_mode == COL_COPY_FLATDOT) flags = COL_TRAVERSE_DEFAULT | COL_TRAVERSE_END;
2580 else if (copy_mode == COL_COPY_FLAT) flags = COL_TRAVERSE_FLAT;
2581 else flags = COL_TRAVERSE_ONELEVEL;
2582
2583 error = col_walk_items(collection_to_copy, flags,
2584 col_copy_traverse_handler, (void *)(&traverse_data),
2585 NULL, new_collection, &depth);
2586
2587 if (!error) *collection_copy = new_collection;
2588 else col_destroy_collection(new_collection);
2589
2590 TRACE_FLOW_NUMBER("col_copy_collection_with_cb returning", error);
2591 return error;
2592
2593 }
2594
2595
2596 /* EXTRACTION */
2597
2598 /* Extract collection */
col_get_collection_reference(struct collection_item * ci,struct collection_item ** acceptor,const char * collection_to_find)2599 int col_get_collection_reference(struct collection_item *ci,
2600 struct collection_item **acceptor,
2601 const char *collection_to_find)
2602 {
2603 struct collection_header *header;
2604 struct collection_item *subcollection = NULL;
2605 int error = EOK;
2606
2607 TRACE_FLOW_STRING("col_get_collection_reference", "Entry.");
2608
2609 if ((ci == NULL) ||
2610 (ci->type != COL_TYPE_COLLECTION) ||
2611 (acceptor == NULL)) {
2612 TRACE_ERROR_NUMBER("Invalid parameter - returning error",EINVAL);
2613 return EINVAL;
2614 }
2615
2616 if (collection_to_find) {
2617 /* Find a sub collection */
2618 TRACE_INFO_STRING("We are given subcollection name - search it:",
2619 collection_to_find);
2620 error = col_find_item_and_do(ci, collection_to_find,
2621 COL_TYPE_COLLECTIONREF,
2622 COL_TRAVERSE_DEFAULT,
2623 col_get_subcollection,
2624 (void *)(&subcollection),
2625 COLLECTION_ACTION_FIND);
2626 if (error) {
2627 TRACE_ERROR_NUMBER("Search failed returning error", error);
2628 return error;
2629 }
2630
2631 if (subcollection == NULL) {
2632 TRACE_ERROR_STRING("Search for subcollection returned NULL pointer", "");
2633 return ENOENT;
2634 }
2635 }
2636 else {
2637 /* Create reference to the same collection */
2638 TRACE_INFO_STRING("Creating reference to the top level collection.", "");
2639 subcollection = ci;
2640 }
2641
2642 header = (struct collection_header *)subcollection->data;
2643 TRACE_INFO_NUMBER("Count:", header->count);
2644 TRACE_INFO_NUMBER("Ref count:", header->reference_count);
2645 header->reference_count++;
2646 TRACE_INFO_NUMBER("Ref count after increment:", header->reference_count);
2647 *acceptor = subcollection;
2648
2649 TRACE_FLOW_STRING("col_get_collection_reference", "Success Exit.");
2650 return EOK;
2651 }
2652
2653 /* Get collection - if current item is a reference get a real collection from it. */
col_get_reference_from_item(struct collection_item * ci,struct collection_item ** acceptor)2654 int col_get_reference_from_item(struct collection_item *ci,
2655 struct collection_item **acceptor)
2656 {
2657 struct collection_header *header;
2658 struct collection_item *subcollection = NULL;
2659
2660 TRACE_FLOW_STRING("get_reference_from_item", "Entry.");
2661
2662 if ((ci == NULL) ||
2663 (ci->type != COL_TYPE_COLLECTIONREF) ||
2664 (acceptor == NULL)) {
2665 TRACE_ERROR_NUMBER("Invalid parameter - returning error",EINVAL);
2666 return EINVAL;
2667 }
2668
2669 subcollection = *((struct collection_item **)ci->data);
2670
2671 header = (struct collection_header *)subcollection->data;
2672 TRACE_INFO_NUMBER("Count:", header->count);
2673 TRACE_INFO_NUMBER("Ref count:", header->reference_count);
2674 header->reference_count++;
2675 TRACE_INFO_NUMBER("Ref count after increment:", header->reference_count);
2676 *acceptor = subcollection;
2677
2678 TRACE_FLOW_STRING("col_get_reference_from_item", "Success Exit.");
2679 return EOK;
2680 }
2681
2682 /* ADDITION */
2683
2684 /* Add collection to collection */
col_add_collection_to_collection(struct collection_item * ci,const char * sub_collection_name,const char * as_property,struct collection_item * collection_to_add,int mode)2685 int col_add_collection_to_collection(struct collection_item *ci,
2686 const char *sub_collection_name,
2687 const char *as_property,
2688 struct collection_item *collection_to_add,
2689 int mode)
2690 {
2691 struct collection_item *acceptor = NULL;
2692 const char *name_to_use;
2693 struct collection_header *header;
2694 struct collection_item *collection_copy;
2695 int error = EOK;
2696 struct col_copy traverse_data;
2697 unsigned depth = 0;
2698
2699
2700 TRACE_FLOW_STRING("col_add_collection_to_collection", "Entry.");
2701
2702 if ((ci == NULL) ||
2703 (ci->type != COL_TYPE_COLLECTION) ||
2704 (collection_to_add == NULL) ||
2705 (collection_to_add->type != COL_TYPE_COLLECTION)) {
2706 /* Need to debug here */
2707 TRACE_ERROR_NUMBER("Missing parameter - returning error", EINVAL);
2708 return EINVAL;
2709 }
2710
2711 if (sub_collection_name != NULL) {
2712 /* Find a sub collection */
2713 TRACE_INFO_STRING("We are given subcollection name - search it:",
2714 sub_collection_name);
2715 error = col_find_item_and_do(ci, sub_collection_name,
2716 COL_TYPE_COLLECTIONREF,
2717 COL_TRAVERSE_DEFAULT,
2718 col_get_subcollection,
2719 (void *)(&acceptor),
2720 COLLECTION_ACTION_FIND);
2721 if (error) {
2722 TRACE_ERROR_NUMBER("Search failed returning error", error);
2723 return error;
2724 }
2725
2726 if (acceptor == NULL) {
2727 TRACE_ERROR_STRING("Search for subcollection returned NULL pointer", "");
2728 return ENOENT;
2729 }
2730
2731 }
2732 else {
2733 acceptor = ci;
2734 }
2735
2736 if (as_property != NULL)
2737 name_to_use = as_property;
2738 else
2739 name_to_use = collection_to_add->property;
2740
2741
2742 TRACE_INFO_STRING("Going to use name:", name_to_use);
2743
2744
2745 switch (mode) {
2746 case COL_ADD_MODE_REFERENCE:
2747 TRACE_INFO_STRING("We are adding a reference.", "");
2748 TRACE_INFO_NUMBER("Type of the header element:",
2749 collection_to_add->type);
2750 TRACE_INFO_STRING("Header name we are adding.",
2751 collection_to_add->property);
2752 /* Create a pointer to external collection */
2753 /* For future thread safety: Transaction start -> */
2754 error = col_insert_property_with_ref_int(acceptor,
2755 NULL,
2756 COL_DSP_END,
2757 NULL,
2758 0,
2759 0,
2760 name_to_use,
2761 COL_TYPE_COLLECTIONREF,
2762 (void *)(&collection_to_add),
2763 sizeof(struct collection_item **),
2764 NULL);
2765
2766 TRACE_INFO_NUMBER("Type of the header element after adding property:",
2767 collection_to_add->type);
2768 TRACE_INFO_STRING("Header name we just added.",
2769 collection_to_add->property);
2770 if (error) {
2771 TRACE_ERROR_NUMBER("Adding property failed with error:", error);
2772 return error;
2773 }
2774 header = (struct collection_header *)collection_to_add->data;
2775 TRACE_INFO_NUMBER("Count:", header->count);
2776 TRACE_INFO_NUMBER("Ref count:", header->reference_count);
2777 header->reference_count++;
2778 TRACE_INFO_NUMBER("Ref count after increment:",
2779 header->reference_count);
2780 /* -> Transaction end */
2781 break;
2782
2783 case COL_ADD_MODE_EMBED:
2784 TRACE_INFO_STRING("We are embedding the collection.", "");
2785 /* First check if the passed in collection is referenced more than once */
2786 TRACE_INFO_NUMBER("Type of the header element we are adding:",
2787 collection_to_add->type);
2788 TRACE_INFO_STRING("Header name we are adding.",
2789 collection_to_add->property);
2790 TRACE_INFO_NUMBER("Type of the header element we are adding to:",
2791 acceptor->type);
2792 TRACE_INFO_STRING("Header name we are adding to.",
2793 acceptor->property);
2794
2795 error = col_insert_property_with_ref_int(acceptor,
2796 NULL,
2797 COL_DSP_END,
2798 NULL,
2799 0,
2800 0,
2801 name_to_use,
2802 COL_TYPE_COLLECTIONREF,
2803 (void *)(&collection_to_add),
2804 sizeof(struct collection_item **),
2805 NULL);
2806
2807
2808 TRACE_INFO_NUMBER("Adding property returned:", error);
2809 break;
2810
2811 case COL_ADD_MODE_CLONE:
2812 TRACE_INFO_STRING("We are cloning the collection.", "");
2813 TRACE_INFO_STRING("Name we will use.", name_to_use);
2814
2815 /* For future thread safety: Transaction start -> */
2816 error = col_copy_collection(&collection_copy,
2817 collection_to_add, name_to_use,
2818 COL_COPY_NORMAL);
2819 if (error) return error;
2820
2821 TRACE_INFO_STRING("We have a collection copy.", collection_copy->property);
2822 TRACE_INFO_NUMBER("Collection type.", collection_copy->type);
2823 TRACE_INFO_STRING("Acceptor collection.", acceptor->property);
2824 TRACE_INFO_NUMBER("Acceptor collection type.", acceptor->type);
2825
2826 error = col_insert_property_with_ref_int(acceptor,
2827 NULL,
2828 COL_DSP_END,
2829 NULL,
2830 0,
2831 0,
2832 name_to_use,
2833 COL_TYPE_COLLECTIONREF,
2834 (void *)(&collection_copy),
2835 sizeof(struct collection_item **),
2836 NULL);
2837
2838 /* -> Transaction end */
2839 TRACE_INFO_NUMBER("Adding property returned:", error);
2840 break;
2841
2842 case COL_ADD_MODE_FLAT:
2843 TRACE_INFO_STRING("We are flattening the collection.", "");
2844
2845 traverse_data.mode = COL_COPY_FLAT;
2846 traverse_data.current_path = NULL;
2847 traverse_data.copy_cb = NULL;
2848 traverse_data.ext_data = NULL;
2849
2850 if ((as_property) && (*as_property)) {
2851 /* The normal assignement generates a warning
2852 * becuase I am assigning const to a non const.
2853 * I can't make the structure member to be const
2854 * since it changes but it changes
2855 * to point to different stings at different time
2856 * This is just an initial sting it will use.
2857 * The logic does not change the content of the string.
2858 * To overcome the issue I use memcpy();
2859 */
2860 memcpy(&(traverse_data.given_name),
2861 &(as_property), sizeof(char *));
2862 traverse_data.given_len = strlen(as_property);
2863 }
2864 else {
2865 traverse_data.given_name = NULL;
2866 traverse_data.given_len = 0;
2867 }
2868
2869 error = col_walk_items(collection_to_add, COL_TRAVERSE_FLAT,
2870 col_copy_traverse_handler, (void *)(&traverse_data),
2871 NULL, acceptor, &depth);
2872
2873 TRACE_INFO_NUMBER("Copy collection flat returned:", error);
2874 break;
2875
2876 case COL_ADD_MODE_FLATDOT:
2877 TRACE_INFO_STRING("We are flattening the collection with dots.", "");
2878
2879 traverse_data.mode = COL_COPY_FLATDOT;
2880 traverse_data.current_path = NULL;
2881 traverse_data.copy_cb = NULL;
2882 traverse_data.ext_data = NULL;
2883
2884 if ((as_property) && (*as_property)) {
2885 /* The normal assignement generates a warning
2886 * becuase I am assigning const to a non const.
2887 * I can't make the structure member to be const
2888 * since it changes but it changes
2889 * to point to different stings at different time
2890 * This is just an initial sting it will use.
2891 * The logic does not change the content of the string.
2892 * To overcome the issue I use memcpy();
2893 */
2894 memcpy(&(traverse_data.given_name),
2895 &(as_property), sizeof(char *));
2896 traverse_data.given_len = strlen(as_property);
2897 }
2898 else {
2899 traverse_data.given_name = NULL;
2900 traverse_data.given_len = 0;
2901 }
2902
2903 error = col_walk_items(collection_to_add, COL_TRAVERSE_DEFAULT | COL_TRAVERSE_END,
2904 col_copy_traverse_handler, (void *)(&traverse_data),
2905 NULL, acceptor, &depth);
2906
2907 TRACE_INFO_NUMBER("Copy collection flatdot returned:", error);
2908 break;
2909
2910 default:
2911 error = EINVAL;
2912 }
2913
2914 TRACE_FLOW_NUMBER("col_add_collection_to_collection returning:", error);
2915 return error;
2916 }
2917
2918 /* TRAVERSING */
2919
2920 /* Function to traverse the entire collection including optionally
2921 * sub collections */
col_traverse_collection(struct collection_item * ci,int mode_flags,col_item_fn item_handler,void * custom_data)2922 int col_traverse_collection(struct collection_item *ci,
2923 int mode_flags,
2924 col_item_fn item_handler,
2925 void *custom_data)
2926 {
2927
2928 int error = EOK;
2929 unsigned depth = 0;
2930
2931 TRACE_FLOW_STRING("col_traverse_collection", "Entry.");
2932
2933 if (ci == NULL) {
2934 TRACE_ERROR_NUMBER("No collection to traverse!", EINVAL);
2935 return EINVAL;
2936 }
2937
2938 error = col_walk_items(ci, mode_flags, col_simple_traverse_handler,
2939 NULL, item_handler, custom_data, &depth);
2940
2941 if ((error != 0) && (error != EINTR_INTERNAL)) {
2942 TRACE_ERROR_NUMBER("Error walking tree", error);
2943 return error;
2944 }
2945
2946 TRACE_FLOW_STRING("col_traverse_collection", "Success exit.");
2947 return EOK;
2948 }
2949
2950 /* CHECK */
2951
2952 /* Convenience function to check if specific property is in the collection */
col_is_item_in_collection(struct collection_item * ci,const char * property_to_find,int type,int mode_flags,int * found)2953 int col_is_item_in_collection(struct collection_item *ci,
2954 const char *property_to_find,
2955 int type,
2956 int mode_flags,
2957 int *found)
2958 {
2959 int error;
2960
2961 TRACE_FLOW_STRING("col_is_item_in_collection","Entry.");
2962
2963 *found = COL_NOMATCH;
2964 error = col_find_item_and_do(ci, property_to_find,
2965 type, mode_flags,
2966 col_is_in_item_handler,
2967 (void *)found,
2968 COLLECTION_ACTION_FIND);
2969
2970 TRACE_FLOW_NUMBER("col_is_item_in_collection returning", error);
2971 return error;
2972 }
2973
2974 /* SEARCH */
2975 /* Search function. Looks up an item in the collection based on the property.
2976 Essentually it is a traverse function with spacial traversing logic.
2977 */
col_get_item_and_do(struct collection_item * ci,const char * property_to_find,int type,int mode_flags,col_item_fn item_handler,void * custom_data)2978 int col_get_item_and_do(struct collection_item *ci,
2979 const char *property_to_find,
2980 int type,
2981 int mode_flags,
2982 col_item_fn item_handler,
2983 void *custom_data)
2984 {
2985 int error = EOK;
2986
2987 TRACE_FLOW_STRING("col_get_item_and_do","Entry.");
2988
2989 error = col_find_item_and_do(ci, property_to_find,
2990 type, mode_flags,
2991 item_handler,
2992 custom_data,
2993 COLLECTION_ACTION_FIND);
2994
2995 TRACE_FLOW_NUMBER("col_get_item_and_do returning", error);
2996 return error;
2997 }
2998
2999
3000 /* Get raw item */
col_get_item(struct collection_item * ci,const char * property_to_find,int type,int mode_flags,struct collection_item ** item)3001 int col_get_item(struct collection_item *ci,
3002 const char *property_to_find,
3003 int type,
3004 int mode_flags,
3005 struct collection_item **item)
3006 {
3007
3008 int error = EOK;
3009
3010 TRACE_FLOW_STRING("col_get_item", "Entry.");
3011
3012 error = col_find_item_and_do(ci, property_to_find,
3013 type, mode_flags,
3014 NULL, (void *)item,
3015 COLLECTION_ACTION_GET);
3016
3017 TRACE_FLOW_NUMBER("col_get_item returning", error);
3018 return error;
3019 }
3020
3021 /* DELETE */
3022 /* Delete property from the collection */
col_delete_property(struct collection_item * ci,const char * property_to_find,int type,int mode_flags)3023 int col_delete_property(struct collection_item *ci,
3024 const char *property_to_find,
3025 int type,
3026 int mode_flags)
3027 {
3028 int error = EOK;
3029 int found;
3030
3031 TRACE_FLOW_STRING("col_delete_property", "Entry.");
3032 found = COL_NOMATCH;
3033
3034 error = col_find_item_and_do(ci, property_to_find,
3035 type, mode_flags,
3036 NULL, (void *)(&found),
3037 COLLECTION_ACTION_DEL);
3038
3039 if ((error == EOK) && (found == COL_NOMATCH))
3040 error = ENOENT;
3041
3042 TRACE_FLOW_NUMBER("col_delete_property returning", error);
3043 return error;
3044 }
3045
3046 /* UPDATE */
3047 /* Update property in the collection */
col_update_property(struct collection_item * ci,const char * property_to_find,int type,void * new_data,int length,int mode_flags)3048 int col_update_property(struct collection_item *ci,
3049 const char *property_to_find,
3050 int type,
3051 void *new_data,
3052 int length,
3053 int mode_flags)
3054 {
3055 int error = EOK;
3056 struct update_property update_data;
3057
3058 TRACE_FLOW_STRING("col_update_property", "Entry.");
3059 update_data.type = type;
3060 update_data.data = new_data;
3061 update_data.length = length;
3062 update_data.found = COL_NOMATCH;
3063
3064 error = col_find_item_and_do(ci, property_to_find,
3065 type, mode_flags,
3066 NULL, (void *)(&update_data),
3067 COLLECTION_ACTION_UPDATE);
3068
3069 if ((error == EOK) && (update_data.found == COL_NOMATCH))
3070 error = ENOENT;
3071
3072 TRACE_FLOW_NUMBER("col_update_property returning", error);
3073 return error;
3074 }
3075
3076
3077 /* Function to modify the item */
col_modify_item(struct collection_item * item,const char * property,int type,const void * data,int length)3078 int col_modify_item(struct collection_item *item,
3079 const char *property,
3080 int type,
3081 const void *data,
3082 int length)
3083 {
3084 TRACE_FLOW_STRING("col_modify_item", "Entry");
3085
3086 /* Allow renameing only */
3087 if ((item == NULL) ||
3088 ((item->type == COL_TYPE_COLLECTION) && (length != 0)) ||
3089 ((item->type == COL_TYPE_COLLECTIONREF) && (length != 0))) {
3090 TRACE_ERROR_NUMBER("Invalid argument or invalid argument type", EINVAL);
3091 return EINVAL;
3092 }
3093
3094 if (property != NULL) {
3095 if (col_validate_property(property)) {
3096 TRACE_ERROR_STRING("Invalid chracters in the property name", property);
3097 return EINVAL;
3098 }
3099 free(item->property);
3100 item->property = strdup(property);
3101 if (item->property == NULL) {
3102 TRACE_ERROR_STRING("Failed to allocate memory", "");
3103 return ENOMEM;
3104 }
3105
3106 /* Update property length and hash if we rename the property */
3107 item->phash = col_make_hash(property, 0, &(item->property_len));
3108 TRACE_INFO_NUMBER("Item hash", item->phash);
3109 TRACE_INFO_NUMBER("Item property length", item->property_len);
3110 TRACE_INFO_NUMBER("Item property strlen", strlen(item->property));
3111
3112 }
3113
3114 /* We need to change data ? */
3115 if(length) {
3116
3117 /* If type is different or same but it is string or binary we need to
3118 * replace the storage */
3119 if ((item->type != type) ||
3120 ((item->type == type) &&
3121 ((item->type == COL_TYPE_STRING) || (item->type == COL_TYPE_BINARY)))) {
3122 TRACE_INFO_STRING("Replacing item data buffer", "");
3123 free(item->data);
3124 item->data = malloc(length);
3125 if (item->data == NULL) {
3126 TRACE_ERROR_STRING("Failed to allocate memory", "");
3127 item->length = 0;
3128 return ENOMEM;
3129 }
3130 item->length = length;
3131 }
3132
3133 TRACE_INFO_STRING("Overwriting item data", "");
3134 memcpy(item->data, data, item->length);
3135 item->type = type;
3136
3137 if (item->type == COL_TYPE_STRING)
3138 ((char *)(item->data))[item->length - 1] = '\0';
3139 }
3140
3141 TRACE_FLOW_STRING("col_modify_item", "Exit");
3142 return EOK;
3143 }
3144
3145
3146 /* Set collection class */
col_set_collection_class(struct collection_item * item,unsigned cclass)3147 int col_set_collection_class(struct collection_item *item,
3148 unsigned cclass)
3149 {
3150 struct collection_header *header;
3151
3152 TRACE_FLOW_STRING("col_set_collection_class", "Entry");
3153
3154 if (item->type != COL_TYPE_COLLECTION) {
3155 TRACE_INFO_NUMBER("Not a collectin object. Type is", item->type);
3156 return EINVAL;
3157 }
3158
3159 header = (struct collection_header *)item->data;
3160 header->cclass = cclass;
3161 TRACE_FLOW_STRING("col_set_collection_class", "Exit");
3162 return EOK;
3163 }
3164
3165 /* Get collection class */
col_get_collection_class(struct collection_item * item,unsigned * cclass)3166 int col_get_collection_class(struct collection_item *item,
3167 unsigned *cclass)
3168 {
3169 struct collection_header *header;
3170
3171 TRACE_FLOW_STRING("col_get_collection_class", "Entry");
3172
3173 if (item->type != COL_TYPE_COLLECTION) {
3174 TRACE_ERROR_NUMBER("Not a collection object. Type is", item->type);
3175 return EINVAL;
3176 }
3177
3178 header = (struct collection_header *)item->data;
3179 *cclass = header->cclass;
3180 TRACE_FLOW_STRING("col_get_collection_class", "Exit");
3181 return EOK;
3182 }
3183
3184 /* Get collection count */
col_get_collection_count(struct collection_item * item,unsigned * count)3185 int col_get_collection_count(struct collection_item *item,
3186 unsigned *count)
3187 {
3188 struct collection_header *header;
3189
3190 TRACE_FLOW_STRING("col_get_collection_count", "Entry");
3191
3192 if (item->type != COL_TYPE_COLLECTION) {
3193 TRACE_ERROR_NUMBER("Not a collectin object. Type is", item->type);
3194 return EINVAL;
3195 }
3196
3197 header = (struct collection_header *)item->data;
3198 *count = header->count;
3199 TRACE_FLOW_STRING("col_get_collection_count", "Exit");
3200 return EOK;
3201
3202 }
3203
3204 /* Convinience function to check if the collection is of the specific class */
3205 /* In case of internal error assumes that collection is not of the right class */
col_is_of_class(struct collection_item * item,unsigned cclass)3206 int col_is_of_class(struct collection_item *item, unsigned cclass)
3207 {
3208 int error = EOK;
3209 unsigned ret_class = 0;
3210
3211 TRACE_FLOW_STRING("col_is_of_class invoked", "");
3212
3213 error = col_get_collection_class(item, &ret_class);
3214 if (error || (ret_class != cclass))
3215 return 0;
3216 else
3217 return 1;
3218 }
3219
3220 /* Get propery */
col_get_item_property(struct collection_item * ci,int * property_len)3221 const char *col_get_item_property(struct collection_item *ci,
3222 int *property_len)
3223 {
3224 if (property_len != NULL) *property_len = ci->property_len;
3225 return ci->property;
3226 }
3227
3228 /* Get type */
col_get_item_type(struct collection_item * ci)3229 int col_get_item_type(struct collection_item *ci)
3230 {
3231 return ci->type;
3232 }
3233
3234 /* Get length */
col_get_item_length(struct collection_item * ci)3235 int col_get_item_length(struct collection_item *ci)
3236 {
3237 return ci->length;
3238 }
3239
3240 /* Get data */
col_get_item_data(struct collection_item * ci)3241 void *col_get_item_data(struct collection_item *ci)
3242 {
3243 return ci->data;
3244 }
3245
3246 /* Get hash */
col_get_item_hash(struct collection_item * ci)3247 uint64_t col_get_item_hash(struct collection_item *ci)
3248 {
3249 return ci->phash;
3250 }
3251
3252 /* Calculates hash of the string using internal hashing
3253 * algorithm. Populates "length" with length
3254 * of the string not counting 0.
3255 * Length argument can be NULL.
3256 */
col_make_hash(const char * string,int sub_len,int * length)3257 uint64_t col_make_hash(const char *string, int sub_len, int *length)
3258 {
3259 uint64_t hash = 0;
3260 int str_len = 0;
3261
3262 TRACE_FLOW_STRING("col_make_hash called for string:", string);
3263
3264 if (string) {
3265 hash = FNV1a_base;
3266 while (string[str_len] != 0) {
3267
3268 /* Check if we need to stop */
3269 if ((sub_len > 0) && (str_len == sub_len)) break;
3270
3271 hash = hash ^ toupper(string[str_len]);
3272 hash *= FNV1a_prime;
3273 str_len++;
3274 }
3275 }
3276
3277 if (length) *length = str_len;
3278
3279 TRACE_FLOW_NUMBER("col_make_hash returning hash:", hash);
3280
3281 return hash;
3282 }
3283