xref: /freebsd/crypto/krb5/src/util/profile/prof_tree.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * prof_tree.c --- these routines maintain the parse tree of the
4  *      config file.
5  *
6  * All of the details of how the tree is stored is abstracted away in
7  * this file; all of the other profile routines build, access, and
8  * modify the tree via the accessor functions found in this file.
9  *
10  * Each node may represent either a relation or a section header.
11  *
12  * A section header must have its value field be null, and may have one
13  * or more child nodes, pointed to by first_child.
14  *
15  * A relation has as its value a pointer to allocated memory
16  * containing a string.  Its first_child pointer must be null.
17  *
18  */
19 
20 
21 #include "prof_int.h"
22 
23 #include <stdio.h>
24 #include <string.h>
25 #ifdef HAVE_STDLIB_H
26 #include <stdlib.h>
27 #endif
28 #include <errno.h>
29 #include <ctype.h>
30 
31 struct profile_node {
32     errcode_t       magic;
33     char *name;
34     char *value;
35     int group_level;
36     unsigned int final:1;           /* Indicate don't search next file */
37     unsigned int deleted:1;
38     struct profile_node *first_child;
39     struct profile_node *parent;
40     struct profile_node *next, *prev;
41 };
42 
43 #define CHECK_MAGIC(node)                       \
44     if ((node)->magic != PROF_MAGIC_NODE)       \
45         return PROF_MAGIC_NODE;
46 
47 /*
48  * Free a node, and any children
49  */
profile_free_node(struct profile_node * node)50 void profile_free_node(struct profile_node *node)
51 {
52     struct profile_node *child, *next;
53 
54     if (node->magic != PROF_MAGIC_NODE)
55         return;
56 
57     if (node->name)
58         free(node->name);
59     if (node->value)
60         free(node->value);
61 
62     for (child=node->first_child; child; child = next) {
63         next = child->next;
64         profile_free_node(child);
65     }
66     node->magic = 0;
67 
68     free(node);
69 }
70 
71 #ifndef HAVE_STRDUP
72 #undef strdup
73 #define strdup MYstrdup
MYstrdup(const char * s)74 static char *MYstrdup (const char *s)
75 {
76     size_t sz = strlen(s) + 1;
77     char *p = malloc(sz);
78     if (p != 0)
79         memcpy(p, s, sz);
80     return p;
81 }
82 #endif
83 
84 /*
85  * Create a node
86  */
profile_create_node(const char * name,const char * value,struct profile_node ** ret_node)87 errcode_t profile_create_node(const char *name, const char *value,
88                               struct profile_node **ret_node)
89 {
90     struct profile_node *new;
91 
92     new = malloc(sizeof(struct profile_node));
93     if (!new)
94         return ENOMEM;
95     memset(new, 0, sizeof(struct profile_node));
96     /* Set magic here so profile_free_node will free memory */
97     new->magic = PROF_MAGIC_NODE;
98     new->name = strdup(name);
99     if (new->name == 0) {
100         profile_free_node(new);
101         return ENOMEM;
102     }
103     if (value) {
104         new->value = strdup(value);
105         if (new->value == 0) {
106             profile_free_node(new);
107             return ENOMEM;
108         }
109     }
110 
111     *ret_node = new;
112     return 0;
113 }
114 
115 /*
116  * This function verifies that all of the representation invariants of
117  * the profile are true.  If not, we have a programming bug somewhere,
118  * probably in this file.
119  */
profile_verify_node(struct profile_node * node)120 errcode_t profile_verify_node(struct profile_node *node)
121 {
122     struct profile_node *p, *last;
123     errcode_t       retval;
124 
125     CHECK_MAGIC(node);
126 
127     if (node->value && node->first_child)
128         return PROF_SECTION_WITH_VALUE;
129 
130     last = 0;
131     for (p = node->first_child; p; last = p, p = p->next) {
132         if (p->prev != last)
133             return PROF_BAD_LINK_LIST;
134         if (last && (last->next != p))
135             return PROF_BAD_LINK_LIST;
136         if (node->group_level+1 != p->group_level)
137             return PROF_BAD_GROUP_LVL;
138         if (p->parent != node)
139             return PROF_BAD_PARENT_PTR;
140         retval = profile_verify_node(p);
141         if (retval)
142             return retval;
143     }
144     return 0;
145 }
146 
147 /*
148  * Add a node to a particular section
149  */
profile_add_node(struct profile_node * section,const char * name,const char * value,struct profile_node ** ret_node)150 errcode_t profile_add_node(struct profile_node *section, const char *name,
151                            const char *value, struct profile_node **ret_node)
152 {
153     errcode_t retval;
154     struct profile_node *p, *last, *new;
155 
156     CHECK_MAGIC(section);
157 
158     if (section->value)
159         return PROF_ADD_NOT_SECTION;
160 
161     /*
162      * Find the place to insert the new node.  If we are adding a subsection
163      * and already have a subsection with that name, merge them.  Otherwise,
164      * we look for the place *after* the last match of the node name, since
165      * order matters.
166      */
167     for (p=section->first_child, last = 0; p; last = p, p = p->next) {
168         int cmp;
169         cmp = strcmp(p->name, name);
170         if (cmp > 0) {
171             break;
172         } else if (value == NULL && cmp == 0 &&
173                    p->value == NULL && p->deleted != 1) {
174             /* Found duplicate subsection, so don't make a new one. */
175             *ret_node = p;
176             return 0;
177         }
178     }
179     retval = profile_create_node(name, value, &new);
180     if (retval)
181         return retval;
182     new->group_level = section->group_level+1;
183     new->deleted = 0;
184     new->parent = section;
185     new->prev = last;
186     new->next = p;
187     if (p)
188         p->prev = new;
189     if (last)
190         last->next = new;
191     else
192         section->first_child = new;
193     if (ret_node)
194         *ret_node = new;
195     return 0;
196 }
197 
198 /*
199  * Set the final flag on a particular node.
200  */
profile_make_node_final(struct profile_node * node)201 errcode_t profile_make_node_final(struct profile_node *node)
202 {
203     CHECK_MAGIC(node);
204 
205     node->final = 1;
206     return 0;
207 }
208 
209 /*
210  * Check the final flag on a node
211  */
profile_is_node_final(struct profile_node * node)212 int profile_is_node_final(struct profile_node *node)
213 {
214     return (node->final != 0);
215 }
216 
217 /*
218  * Return the name of a node.  (Note: this is for internal functions
219  * only; if the name needs to be returned from an exported function,
220  * strdup it first!)
221  */
profile_get_node_name(struct profile_node * node)222 const char *profile_get_node_name(struct profile_node *node)
223 {
224     return node->name;
225 }
226 
227 /*
228  * Return the value of a node.  (Note: this is for internal functions
229  * only; if the name needs to be returned from an exported function,
230  * strdup it first!)
231  */
profile_get_node_value(struct profile_node * node)232 const char *profile_get_node_value(struct profile_node *node)
233 {
234     return node->value;
235 }
236 
237 /*
238  * Iterate through the section, returning the nodes which match
239  * the given name.  If name is NULL, then iterate through all the
240  * nodes in the section.  If section_flag is non-zero, only return the
241  * section which matches the name; don't return relations.  If value
242  * is non-NULL, then only return relations which match the requested
243  * value.  (The value argument is ignored if section_flag is non-zero.)
244  *
245  * The first time this routine is called, the state pointer must be
246  * null.  When this profile_find_node_relation() returns, if the state
247  * pointer is non-NULL, then this routine should be called again.
248  * (This won't happen if section_flag is non-zero, obviously.)
249  *
250  */
profile_find_node(struct profile_node * section,const char * name,const char * value,int section_flag,void ** state,struct profile_node ** node)251 errcode_t profile_find_node(struct profile_node *section, const char *name,
252                             const char *value, int section_flag, void **state,
253                             struct profile_node **node)
254 {
255     struct profile_node *p;
256 
257     CHECK_MAGIC(section);
258     p = *state;
259     if (p) {
260         CHECK_MAGIC(p);
261     } else
262         p = section->first_child;
263 
264     for (; p; p = p->next) {
265         if (name && (strcmp(p->name, name)))
266             continue;
267         if (section_flag) {
268             if (p->value)
269                 continue;
270         } else {
271             if (!p->value)
272                 continue;
273             if (value && (strcmp(p->value, value)))
274                 continue;
275         }
276         if (p->deleted)
277             continue;
278         /* A match! */
279         if (node)
280             *node = p;
281         break;
282     }
283     if (p == 0) {
284         *state = 0;
285         return section_flag ? PROF_NO_SECTION : PROF_NO_RELATION;
286     }
287     /*
288      * OK, we've found one match; now let's try to find another
289      * one.  This way, if we return a non-zero state pointer,
290      * there's guaranteed to be another match that's returned.
291      */
292     for (p = p->next; p; p = p->next) {
293         if (name && (strcmp(p->name, name)))
294             continue;
295         if (section_flag) {
296             if (p->value)
297                 continue;
298         } else {
299             if (!p->value)
300                 continue;
301             if (value && (strcmp(p->value, value)))
302                 continue;
303         }
304         if (p->deleted)
305             continue;
306         /* A match! */
307         break;
308     }
309     *state = p;
310     return 0;
311 }
312 
313 
314 /*
315  * Iterate through the section, returning the relations which match
316  * the given name.  If name is NULL, then iterate through all the
317  * relations in the section.  The first time this routine is called,
318  * the state pointer must be null.  When this profile_find_node_relation()
319  * returns, if the state pointer is non-NULL, then this routine should
320  * be called again.
321  *
322  * The returned character string in value points to the stored
323  * character string in the parse string.  Before this string value is
324  * returned to a calling application (profile_find_node_relation is not an
325  * exported interface), it should be strdup()'ed.
326  */
profile_find_node_relation(struct profile_node * section,const char * name,void ** state,char ** ret_name,char ** value)327 errcode_t profile_find_node_relation(struct profile_node *section,
328                                      const char *name, void **state,
329                                      char **ret_name, char **value)
330 {
331     struct profile_node *p;
332     errcode_t       retval;
333 
334     retval = profile_find_node(section, name, 0, 0, state, &p);
335     if (retval)
336         return retval;
337 
338     if (p) {
339         if (value)
340             *value = p->value;
341         if (ret_name)
342             *ret_name = p->name;
343     }
344     return 0;
345 }
346 
347 /*
348  * Iterate through the section, returning the subsections which match
349  * the given name.  If name is NULL, then iterate through all the
350  * subsections in the section.  The first time this routine is called,
351  * the state pointer must be null.  When this profile_find_node_subsection()
352  * returns, if the state pointer is non-NULL, then this routine should
353  * be called again.
354  *
355  * This is (plus accessor functions for the name and value given a
356  * profile node) makes this function mostly syntactic sugar for
357  * profile_find_node.
358  */
profile_find_node_subsection(struct profile_node * section,const char * name,void ** state,char ** ret_name,struct profile_node ** subsection)359 errcode_t profile_find_node_subsection(struct profile_node *section,
360                                        const char *name, void **state,
361                                        char **ret_name,
362                                        struct profile_node **subsection)
363 {
364     struct profile_node *p;
365     errcode_t       retval;
366 
367     retval = profile_find_node(section, name, 0, 1, state, &p);
368     if (retval)
369         return retval;
370 
371     if (p) {
372         if (subsection)
373             *subsection = p;
374         if (ret_name)
375             *ret_name = p->name;
376     }
377     return 0;
378 }
379 
380 /*
381  * This function returns the parent of a particular node.
382  */
profile_get_node_parent(struct profile_node * section,struct profile_node ** parent)383 errcode_t profile_get_node_parent(struct profile_node *section,
384                                   struct profile_node **parent)
385 {
386     *parent = section->parent;
387     return 0;
388 }
389 
390 /*
391  * This is a general-purpose iterator for returning all nodes that
392  * match the specified name array.
393  */
394 struct profile_node_iterator {
395     prf_magic_t             magic;
396     int                     flags;
397     const char              *const *names;
398     const char              *name;
399     prf_file_t              file;
400     int                     file_serial;
401     int                     done_idx;
402     struct profile_node     *node;
403     int                     num;
404 };
405 
profile_node_iterator_create(profile_t profile,const char * const * names,int flags,void ** ret_iter)406 errcode_t profile_node_iterator_create(profile_t profile,
407                                        const char *const *names, int flags,
408                                        void **ret_iter)
409 {
410     struct profile_node_iterator *iter;
411     int     done_idx = 0;
412 
413     if (profile == 0)
414         return PROF_NO_PROFILE;
415     if (profile->magic != PROF_MAGIC_PROFILE)
416         return PROF_MAGIC_PROFILE;
417     if (!names)
418         return PROF_BAD_NAMESET;
419     if (!(flags & PROFILE_ITER_LIST_SECTION)) {
420         if (!names[0])
421             return PROF_BAD_NAMESET;
422         done_idx = 1;
423     }
424 
425     iter = malloc(sizeof(*iter));
426     if (iter == NULL)
427         return ENOMEM;
428 
429     iter->magic = PROF_MAGIC_NODE_ITERATOR;
430     iter->names = names;
431     iter->flags = flags;
432     iter->file = profile->first_file;
433     iter->done_idx = done_idx;
434     iter->node = 0;
435     iter->num = 0;
436     *ret_iter = iter;
437     return 0;
438 }
439 
profile_node_iterator_free(void ** iter_p)440 void profile_node_iterator_free(void **iter_p)
441 {
442     struct profile_node_iterator *iter;
443 
444     if (!iter_p)
445         return;
446     iter = *iter_p;
447     if (!iter || iter->magic != PROF_MAGIC_NODE_ITERATOR)
448         return;
449     free(iter);
450     *iter_p = 0;
451 }
452 
453 /*
454  * Note: the returned character strings in ret_name and ret_value
455  * points to the stored character string in the parse string.  Before
456  * this string value is returned to a calling application
457  * (profile_node_iterator is not an exported interface), it should be
458  * strdup()'ed.
459  */
profile_node_iterator(void ** iter_p,struct profile_node ** ret_node,char ** ret_name,char ** ret_value)460 errcode_t profile_node_iterator(void **iter_p,
461                                 struct profile_node **ret_node,
462                                 char **ret_name, char **ret_value)
463 {
464     struct profile_node_iterator    *iter = *iter_p;
465     struct profile_node             *section, *p;
466     const char                      *const *cpp;
467     errcode_t                       retval;
468     int                             skip_num = 0;
469 
470     if (!iter || iter->magic != PROF_MAGIC_NODE_ITERATOR)
471         return PROF_MAGIC_NODE_ITERATOR;
472     if (iter->file && iter->file->magic != PROF_MAGIC_FILE)
473         return PROF_MAGIC_FILE;
474     if (iter->file && iter->file->data->magic != PROF_MAGIC_FILE_DATA)
475         return PROF_MAGIC_FILE_DATA;
476     /*
477      * If the file has changed, then the node pointer is invalid,
478      * so we'll have search the file again looking for it.
479      */
480     if (iter->file)
481         k5_mutex_lock(&iter->file->data->lock);
482     if (iter->node && (iter->file->data->upd_serial != iter->file_serial)) {
483         iter->flags &= ~PROFILE_ITER_FINAL_SEEN;
484         skip_num = iter->num;
485         iter->node = 0;
486     }
487     if (iter->node && iter->node->magic != PROF_MAGIC_NODE) {
488         if (iter->file)
489             k5_mutex_unlock(&iter->file->data->lock);
490         return PROF_MAGIC_NODE;
491     }
492 get_new_file:
493     if (iter->node == 0) {
494         if (iter->file == 0 ||
495             (iter->flags & PROFILE_ITER_FINAL_SEEN)) {
496             if (iter->file)
497                 k5_mutex_unlock(&iter->file->data->lock);
498             profile_node_iterator_free(iter_p);
499             if (ret_node)
500                 *ret_node = 0;
501             if (ret_name)
502                 *ret_name = 0;
503             if (ret_value)
504                 *ret_value =0;
505             return 0;
506         }
507         if ((retval = profile_update_file_locked(iter->file, NULL))) {
508             k5_mutex_unlock(&iter->file->data->lock);
509             if (retval == ENOENT || retval == EACCES) {
510                 /* XXX memory leak? */
511                 iter->file = iter->file->next;
512                 if (iter->file)
513                     k5_mutex_lock(&iter->file->data->lock);
514                 skip_num = 0;
515                 retval = 0;
516                 goto get_new_file;
517             } else {
518                 profile_node_iterator_free(iter_p);
519                 return retval;
520             }
521         }
522         iter->file_serial = iter->file->data->upd_serial;
523         /*
524          * Find the section to list if we are a LIST_SECTION,
525          * or find the containing section if not.
526          */
527         section = iter->file->data->root;
528         assert(section != NULL);
529         for (cpp = iter->names; cpp[iter->done_idx]; cpp++) {
530             for (p=section->first_child; p; p = p->next) {
531                 if (!strcmp(p->name, *cpp) && !p->value && !p->deleted)
532                     break;
533             }
534             if (!p) {
535                 section = 0;
536                 break;
537             }
538             section = p;
539             if (p->final)
540                 iter->flags |= PROFILE_ITER_FINAL_SEEN;
541         }
542         if (!section) {
543             k5_mutex_unlock(&iter->file->data->lock);
544             iter->file = iter->file->next;
545             if (iter->file)
546                 k5_mutex_lock(&iter->file->data->lock);
547             skip_num = 0;
548             goto get_new_file;
549         }
550         iter->name = *cpp;
551         iter->node = section->first_child;
552     }
553     /*
554      * OK, now we know iter->node is set up correctly.  Let's do
555      * the search.
556      */
557     for (p = iter->node; p; p = p->next) {
558         if (iter->name && strcmp(p->name, iter->name))
559             continue;
560         if ((iter->flags & PROFILE_ITER_SECTIONS_ONLY) &&
561             p->value)
562             continue;
563         if ((iter->flags & PROFILE_ITER_RELATIONS_ONLY) &&
564             !p->value)
565             continue;
566         if (skip_num > 0) {
567             skip_num--;
568             continue;
569         }
570         if (p->deleted)
571             continue;
572         break;
573     }
574     iter->num++;
575     if (!p) {
576         k5_mutex_unlock(&iter->file->data->lock);
577         iter->file = iter->file->next;
578         if (iter->file)
579             k5_mutex_lock(&iter->file->data->lock);
580         iter->node = 0;
581         skip_num = 0;
582         goto get_new_file;
583     }
584     k5_mutex_unlock(&iter->file->data->lock);
585     if ((iter->node = p->next) == NULL)
586         iter->file = iter->file->next;
587     if (ret_node)
588         *ret_node = p;
589     if (ret_name)
590         *ret_name = p->name;
591     if (ret_value)
592         *ret_value = p->value;
593     return 0;
594 }
595 
596 /*
597  * Remove a particular node.
598  *
599  * TYT, 2/25/99
600  */
profile_remove_node(struct profile_node * node)601 errcode_t profile_remove_node(struct profile_node *node)
602 {
603     CHECK_MAGIC(node);
604 
605     if (node->parent == 0)
606         return PROF_EINVAL; /* Can't remove the root! */
607 
608     node->deleted = 1;
609 
610     return 0;
611 }
612 
613 /*
614  * Set the value of a specific node containing a relation.
615  *
616  * TYT, 2/25/99
617  */
profile_set_relation_value(struct profile_node * node,const char * new_value)618 errcode_t profile_set_relation_value(struct profile_node *node,
619                                      const char *new_value)
620 {
621     char    *cp;
622 
623     CHECK_MAGIC(node);
624 
625     if (!node->value)
626         return PROF_SET_SECTION_VALUE;
627 
628     cp = strdup(new_value);
629     if (!cp)
630         return ENOMEM;
631 
632     free(node->value);
633     node->value = cp;
634 
635     return 0;
636 }
637 
638 /*
639  * Rename a specific node
640  *
641  * TYT 2/25/99
642  */
profile_rename_node(struct profile_node * node,const char * new_name)643 errcode_t profile_rename_node(struct profile_node *node, const char *new_name)
644 {
645     char                    *new_string;
646     struct profile_node     *p, *last;
647 
648     CHECK_MAGIC(node);
649 
650     if (strcmp(new_name, node->name) == 0)
651         return 0;       /* It's the same name, return */
652 
653     /*
654      * Make sure we can allocate memory for the new name, first!
655      */
656     new_string = strdup(new_name);
657     if (!new_string)
658         return ENOMEM;
659 
660     /*
661      * Find the place to where the new node should go.  We look
662      * for the place *after* the last match of the node name,
663      * since order matters.
664      */
665     for (p=node->parent->first_child, last = 0; p; last = p, p = p->next) {
666         if (strcmp(p->name, new_name) > 0)
667             break;
668     }
669 
670     /*
671      * If we need to move the node, do it now.
672      */
673     if ((p != node) && (last != node)) {
674         /*
675          * OK, let's detach the node
676          */
677         if (node->prev)
678             node->prev->next = node->next;
679         else
680             node->parent->first_child = node->next;
681         if (node->next)
682             node->next->prev = node->prev;
683 
684         /*
685          * Now let's reattach it in the right place.
686          */
687         if (p)
688             p->prev = node;
689         if (last)
690             last->next = node;
691         else
692             node->parent->first_child = node;
693         node->next = p;
694         node->prev = last;
695     }
696 
697     free(node->name);
698     node->name = new_string;
699     return 0;
700 }
701