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