Path: blob/main/crypto/krb5/src/util/profile/prof_tree.c
34889 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/*2* prof_tree.c --- these routines maintain the parse tree of the3* config file.4*5* All of the details of how the tree is stored is abstracted away in6* this file; all of the other profile routines build, access, and7* modify the tree via the accessor functions found in this file.8*9* Each node may represent either a relation or a section header.10*11* A section header must have its value field be null, and may have one12* or more child nodes, pointed to by first_child.13*14* A relation has as its value a pointer to allocated memory15* containing a string. Its first_child pointer must be null.16*17*/181920#include "prof_int.h"2122#include <stdio.h>23#include <string.h>24#ifdef HAVE_STDLIB_H25#include <stdlib.h>26#endif27#include <errno.h>28#include <ctype.h>2930struct profile_node {31errcode_t magic;32char *name;33char *value;34int group_level;35unsigned int final:1; /* Indicate don't search next file */36unsigned int deleted:1;37struct profile_node *first_child;38struct profile_node *parent;39struct profile_node *next, *prev;40};4142#define CHECK_MAGIC(node) \43if ((node)->magic != PROF_MAGIC_NODE) \44return PROF_MAGIC_NODE;4546/*47* Free a node, and any children48*/49void profile_free_node(struct profile_node *node)50{51struct profile_node *child, *next;5253if (node->magic != PROF_MAGIC_NODE)54return;5556if (node->name)57free(node->name);58if (node->value)59free(node->value);6061for (child=node->first_child; child; child = next) {62next = child->next;63profile_free_node(child);64}65node->magic = 0;6667free(node);68}6970#ifndef HAVE_STRDUP71#undef strdup72#define strdup MYstrdup73static char *MYstrdup (const char *s)74{75size_t sz = strlen(s) + 1;76char *p = malloc(sz);77if (p != 0)78memcpy(p, s, sz);79return p;80}81#endif8283/*84* Create a node85*/86errcode_t profile_create_node(const char *name, const char *value,87struct profile_node **ret_node)88{89struct profile_node *new;9091new = malloc(sizeof(struct profile_node));92if (!new)93return ENOMEM;94memset(new, 0, sizeof(struct profile_node));95/* Set magic here so profile_free_node will free memory */96new->magic = PROF_MAGIC_NODE;97new->name = strdup(name);98if (new->name == 0) {99profile_free_node(new);100return ENOMEM;101}102if (value) {103new->value = strdup(value);104if (new->value == 0) {105profile_free_node(new);106return ENOMEM;107}108}109110*ret_node = new;111return 0;112}113114/* Return a copy of oldnode. Recursively copy oldnode's children, but leave115* the parent, next, and prev pointers as null. */116struct profile_node *profile_copy_node(struct profile_node *oldnode)117{118struct profile_node *node = NULL, *p, *q, **nextp, *last;119120if (oldnode->magic != PROF_MAGIC_NODE)121return NULL;122123node = calloc(1, sizeof(*node));124node->magic = PROF_MAGIC_NODE;125node->name = strdup(oldnode->name);126if (node->name == NULL)127goto errout;128if (oldnode->value != NULL) {129node->value = strdup(oldnode->value);130if (oldnode->value == NULL)131goto errout;132}133node->group_level = oldnode->group_level;134node->final = oldnode->final;135node->deleted = oldnode->deleted;136137nextp = &node->first_child;138last = NULL;139for (p = oldnode->first_child; p != NULL; p = p->next) {140q = profile_copy_node(p);141if (q == NULL)142goto errout;143144/* Link in the new child and prepare for the next. */145q->parent = node;146q->prev = last;147last = q;148*nextp = q;149nextp = &q->next;150}151152return node;153154errout:155profile_free_node(node);156return NULL;157}158159/*160* This function verifies that all of the representation invariants of161* the profile are true. If not, we have a programming bug somewhere,162* probably in this file.163*/164errcode_t profile_verify_node(struct profile_node *node)165{166struct profile_node *p, *last;167errcode_t retval;168169CHECK_MAGIC(node);170171if (node->value && node->first_child)172return PROF_SECTION_WITH_VALUE;173174last = 0;175for (p = node->first_child; p; last = p, p = p->next) {176if (p->prev != last)177return PROF_BAD_LINK_LIST;178if (last && (last->next != p))179return PROF_BAD_LINK_LIST;180if (node->group_level+1 != p->group_level)181return PROF_BAD_GROUP_LVL;182if (p->parent != node)183return PROF_BAD_PARENT_PTR;184retval = profile_verify_node(p);185if (retval)186return retval;187}188return 0;189}190191/*192* Add a node to a particular section. If check_final is true, don't add the193* node if we find a final node for the same name.194*/195errcode_t profile_add_node(struct profile_node *section, const char *name,196const char *value, int check_final,197struct profile_node **ret_node)198{199errcode_t retval;200struct profile_node *p, *last, *new;201202CHECK_MAGIC(section);203204if (section->value)205return PROF_ADD_NOT_SECTION;206207/*208* Find the place to insert the new node. If we are adding a subsection209* and already have a subsection with that name, merge them. Otherwise,210* we look for the place *after* the last match of the node name, since211* order matters.212*/213for (p=section->first_child, last = 0; p; last = p, p = p->next) {214int cmp;215cmp = strcmp(p->name, name);216if (cmp > 0) {217break;218} else if (value == NULL && cmp == 0 &&219p->value == NULL && p->deleted != 1) {220/* Found duplicate subsection, so don't make a new one. */221if (ret_node)222*ret_node = p;223return 0;224} else if (check_final && cmp == 0 && p->final) {225/* This key already exists with the final flag and we were asked226* to check it, so don't add this node. */227if (ret_node)228*ret_node = NULL;229return 0;230}231}232retval = profile_create_node(name, value, &new);233if (retval)234return retval;235new->group_level = section->group_level+1;236new->deleted = 0;237new->parent = section;238new->prev = last;239new->next = p;240if (p)241p->prev = new;242if (last)243last->next = new;244else245section->first_child = new;246if (ret_node)247*ret_node = new;248return 0;249}250251/*252* Set the final flag on a particular node.253*/254errcode_t profile_make_node_final(struct profile_node *node)255{256CHECK_MAGIC(node);257258node->final = 1;259return 0;260}261262/*263* Check the final flag on a node264*/265int profile_is_node_final(struct profile_node *node)266{267return (node->final != 0);268}269270/*271* Return the name of a node. (Note: this is for internal functions272* only; if the name needs to be returned from an exported function,273* strdup it first!)274*/275const char *profile_get_node_name(struct profile_node *node)276{277return node->name;278}279280/*281* Return the value of a node. (Note: this is for internal functions282* only; if the name needs to be returned from an exported function,283* strdup it first!)284*/285const char *profile_get_node_value(struct profile_node *node)286{287return node->value;288}289290/*291* Iterate through the section, returning the nodes which match292* the given name. If name is NULL, then iterate through all the293* nodes in the section. If section_flag is non-zero, only return the294* section which matches the name; don't return relations. If value295* is non-NULL, then only return relations which match the requested296* value. (The value argument is ignored if section_flag is non-zero.)297*298* The first time this routine is called, the state pointer must be299* null. When this profile_find_node_relation() returns, if the state300* pointer is non-NULL, then this routine should be called again.301* (This won't happen if section_flag is non-zero, obviously.)302*303*/304errcode_t profile_find_node(struct profile_node *section, const char *name,305const char *value, int section_flag, void **state,306struct profile_node **node)307{308struct profile_node *p;309310CHECK_MAGIC(section);311p = *state;312if (p) {313CHECK_MAGIC(p);314} else315p = section->first_child;316317for (; p; p = p->next) {318if (name && (strcmp(p->name, name)))319continue;320if (section_flag) {321if (p->value)322continue;323} else {324if (!p->value)325continue;326if (value && (strcmp(p->value, value)))327continue;328}329if (p->deleted)330continue;331/* A match! */332if (node)333*node = p;334break;335}336if (p == 0) {337*state = 0;338return section_flag ? PROF_NO_SECTION : PROF_NO_RELATION;339}340/*341* OK, we've found one match; now let's try to find another342* one. This way, if we return a non-zero state pointer,343* there's guaranteed to be another match that's returned.344*/345for (p = p->next; p; p = p->next) {346if (name && (strcmp(p->name, name)))347continue;348if (section_flag) {349if (p->value)350continue;351} else {352if (!p->value)353continue;354if (value && (strcmp(p->value, value)))355continue;356}357if (p->deleted)358continue;359/* A match! */360break;361}362*state = p;363return 0;364}365366367/*368* Iterate through the section, returning the relations which match369* the given name. If name is NULL, then iterate through all the370* relations in the section. The first time this routine is called,371* the state pointer must be null. When this profile_find_node_relation()372* returns, if the state pointer is non-NULL, then this routine should373* be called again.374*375* The returned character string in value points to the stored376* character string in the parse string. Before this string value is377* returned to a calling application (profile_find_node_relation is not an378* exported interface), it should be strdup()'ed.379*/380errcode_t profile_find_node_relation(struct profile_node *section,381const char *name, void **state,382char **ret_name, char **value,383int *ret_final)384{385struct profile_node *p;386errcode_t retval;387388retval = profile_find_node(section, name, 0, 0, state, &p);389if (retval)390return retval;391392if (p) {393if (value)394*value = p->value;395if (ret_name)396*ret_name = p->name;397if (ret_final)398*ret_final = p->final;399}400return 0;401}402403/*404* Iterate through the section, returning the subsections which match405* the given name. If name is NULL, then iterate through all the406* subsections in the section. The first time this routine is called,407* the state pointer must be null. When this profile_find_node_subsection()408* returns, if the state pointer is non-NULL, then this routine should409* be called again.410*411* This is (plus accessor functions for the name and value given a412* profile node) makes this function mostly syntactic sugar for413* profile_find_node.414*/415errcode_t profile_find_node_subsection(struct profile_node *section,416const char *name, void **state,417char **ret_name,418struct profile_node **subsection)419{420struct profile_node *p;421errcode_t retval;422423retval = profile_find_node(section, name, 0, 1, state, &p);424if (retval)425return retval;426427if (p) {428if (subsection)429*subsection = p;430if (ret_name)431*ret_name = p->name;432}433return 0;434}435436/*437* This function returns the parent of a particular node.438*/439errcode_t profile_get_node_parent(struct profile_node *section,440struct profile_node **parent)441{442*parent = section->parent;443return 0;444}445446/*447* This is a general-purpose iterator for returning all nodes that448* match the specified name array.449*/450struct profile_node_iterator {451prf_magic_t magic;452int flags;453const char *const *names;454const char *name;455prf_file_t file;456int file_serial;457int done_idx;458struct profile_node *node;459int num;460};461462errcode_t profile_node_iterator_create(profile_t profile,463const char *const *names, int flags,464void **ret_iter)465{466struct profile_node_iterator *iter;467int done_idx = 0;468469if (profile == 0)470return PROF_NO_PROFILE;471if (profile->magic != PROF_MAGIC_PROFILE)472return PROF_MAGIC_PROFILE;473if (!names)474return PROF_BAD_NAMESET;475if (!(flags & PROFILE_ITER_LIST_SECTION)) {476if (!names[0])477return PROF_BAD_NAMESET;478done_idx = 1;479}480481iter = malloc(sizeof(*iter));482if (iter == NULL)483return ENOMEM;484485iter->magic = PROF_MAGIC_NODE_ITERATOR;486iter->names = names;487iter->flags = flags;488iter->file = profile->first_file;489iter->done_idx = done_idx;490iter->node = 0;491iter->num = 0;492*ret_iter = iter;493return 0;494}495496void profile_node_iterator_free(void **iter_p)497{498struct profile_node_iterator *iter;499500if (!iter_p)501return;502iter = *iter_p;503if (!iter || iter->magic != PROF_MAGIC_NODE_ITERATOR)504return;505free(iter);506*iter_p = 0;507}508509/*510* Note: the returned character strings in ret_name and ret_value511* points to the stored character string in the parse string. Before512* this string value is returned to a calling application513* (profile_node_iterator is not an exported interface), it should be514* strdup()'ed.515*/516errcode_t profile_node_iterator(void **iter_p,517struct profile_node **ret_node,518char **ret_name, char **ret_value)519{520struct profile_node_iterator *iter = *iter_p;521struct profile_node *section, *p;522const char *const *cpp;523errcode_t retval;524int skip_num = 0;525526if (!iter || iter->magic != PROF_MAGIC_NODE_ITERATOR)527return PROF_MAGIC_NODE_ITERATOR;528if (iter->file && iter->file->magic != PROF_MAGIC_FILE)529return PROF_MAGIC_FILE;530if (iter->file && iter->file->data->magic != PROF_MAGIC_FILE_DATA)531return PROF_MAGIC_FILE_DATA;532/*533* If the file has changed, then the node pointer is invalid,534* so we'll have search the file again looking for it.535*/536if (iter->file)537k5_mutex_lock(&iter->file->data->lock);538if (iter->node && (iter->file->data->upd_serial != iter->file_serial)) {539iter->flags &= ~PROFILE_ITER_FINAL_SEEN;540skip_num = iter->num;541iter->node = 0;542}543if (iter->node && iter->node->magic != PROF_MAGIC_NODE) {544if (iter->file)545k5_mutex_unlock(&iter->file->data->lock);546return PROF_MAGIC_NODE;547}548get_new_file:549if (iter->node == 0) {550if (iter->file == 0 ||551(iter->flags & PROFILE_ITER_FINAL_SEEN)) {552if (iter->file)553k5_mutex_unlock(&iter->file->data->lock);554profile_node_iterator_free(iter_p);555if (ret_node)556*ret_node = 0;557if (ret_name)558*ret_name = 0;559if (ret_value)560*ret_value =0;561return 0;562}563if ((retval = profile_update_file_locked(iter->file, NULL))) {564k5_mutex_unlock(&iter->file->data->lock);565if (retval == ENOENT || retval == EACCES) {566/* XXX memory leak? */567iter->file = iter->file->next;568if (iter->file)569k5_mutex_lock(&iter->file->data->lock);570skip_num = 0;571retval = 0;572goto get_new_file;573} else {574profile_node_iterator_free(iter_p);575return retval;576}577}578iter->file_serial = iter->file->data->upd_serial;579/*580* Find the section to list if we are a LIST_SECTION,581* or find the containing section if not.582*/583section = iter->file->data->root;584assert(section != NULL);585for (cpp = iter->names; cpp[iter->done_idx]; cpp++) {586for (p=section->first_child; p; p = p->next) {587if (!strcmp(p->name, *cpp) && !p->value && !p->deleted)588break;589}590if (!p) {591section = 0;592break;593}594section = p;595if (p->final)596iter->flags |= PROFILE_ITER_FINAL_SEEN;597}598if (!section) {599k5_mutex_unlock(&iter->file->data->lock);600iter->file = iter->file->next;601if (iter->file)602k5_mutex_lock(&iter->file->data->lock);603skip_num = 0;604goto get_new_file;605}606iter->name = *cpp;607iter->node = section->first_child;608}609/*610* OK, now we know iter->node is set up correctly. Let's do611* the search.612*/613for (p = iter->node; p; p = p->next) {614if (iter->name && strcmp(p->name, iter->name))615continue;616if ((iter->flags & PROFILE_ITER_SECTIONS_ONLY) &&617p->value)618continue;619if ((iter->flags & PROFILE_ITER_RELATIONS_ONLY) &&620!p->value)621continue;622if (skip_num > 0) {623skip_num--;624continue;625}626if (p->deleted)627continue;628if (p->final)629iter->flags |= PROFILE_ITER_FINAL_SEEN;630break;631}632iter->num++;633if (!p) {634k5_mutex_unlock(&iter->file->data->lock);635iter->file = iter->file->next;636if (iter->file)637k5_mutex_lock(&iter->file->data->lock);638iter->node = 0;639skip_num = 0;640goto get_new_file;641}642k5_mutex_unlock(&iter->file->data->lock);643if ((iter->node = p->next) == NULL)644iter->file = iter->file->next;645if (ret_node)646*ret_node = p;647if (ret_name)648*ret_name = p->name;649if (ret_value)650*ret_value = p->value;651return 0;652}653654/*655* Remove a particular node.656*657* TYT, 2/25/99658*/659errcode_t profile_remove_node(struct profile_node *node)660{661CHECK_MAGIC(node);662663if (node->parent == 0)664return PROF_EINVAL; /* Can't remove the root! */665666node->deleted = 1;667668return 0;669}670671/*672* Set the value of a specific node containing a relation.673*674* TYT, 2/25/99675*/676errcode_t profile_set_relation_value(struct profile_node *node,677const char *new_value)678{679char *cp;680681CHECK_MAGIC(node);682683if (!node->value)684return PROF_SET_SECTION_VALUE;685686cp = strdup(new_value);687if (!cp)688return ENOMEM;689690free(node->value);691node->value = cp;692693return 0;694}695696/*697* Rename a specific node698*699* TYT 2/25/99700*/701errcode_t profile_rename_node(struct profile_node *node, const char *new_name)702{703char *new_string;704struct profile_node *p, *last;705706CHECK_MAGIC(node);707708if (strcmp(new_name, node->name) == 0)709return 0; /* It's the same name, return */710711/*712* Make sure we can allocate memory for the new name, first!713*/714new_string = strdup(new_name);715if (!new_string)716return ENOMEM;717718/*719* Find the place to where the new node should go. We look720* for the place *after* the last match of the node name,721* since order matters.722*/723for (p=node->parent->first_child, last = 0; p; last = p, p = p->next) {724if (strcmp(p->name, new_name) > 0)725break;726}727728/*729* If we need to move the node, do it now.730*/731if ((p != node) && (last != node)) {732/*733* OK, let's detach the node734*/735if (node->prev)736node->prev->next = node->next;737else738node->parent->first_child = node->next;739if (node->next)740node->next->prev = node->prev;741742/*743* Now let's reattach it in the right place.744*/745if (p)746p->prev = node;747if (last)748last->next = node;749else750node->parent->first_child = node;751node->next = p;752node->prev = last;753}754755free(node->name);756node->name = new_string;757return 0;758}759760761