From: Colin Vidal Date: Tue, 30 Sep 2025 10:41:32 +0000 (+0200) Subject: introduce cfg_obj_clone to clone a config tree X-Git-Tag: v9.21.15~30^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cc91a5d1ec78e6d9c707b3a12bda7496d5b3a877;p=thirdparty%2Fbind9.git introduce cfg_obj_clone to clone a config tree Introduce `cfg_obj_clone` which takes a `cfg_obj_t` node and clones it. it allocates a new node, copies its scalar values and recursively allocates child nodes, copying their scalar values as well and so on. Internally, a new method `cfg_copyfunc_t` copy is added in `cfg_rep_t`, which enables implementing a copy function specific for each representation type a node can hold. --- diff --git a/lib/isccfg/include/isccfg/cfg.h b/lib/isccfg/include/isccfg/cfg.h index 90c94c1b73e..adf83d3b888 100644 --- a/lib/isccfg/include/isccfg/cfg.h +++ b/lib/isccfg/include/isccfg/cfg.h @@ -131,6 +131,23 @@ cfg_parser_currentfile(cfg_parser_t *pctx); * existent. */ +void +cfg_obj_clone(const cfg_obj_t *source, cfg_obj_t **target); +/*%< + * Allocate a new configuration object and copy the value from the `source` + * object into the newly allocated object. The copy is a "deep" copy, i.e. if + * `source` is a list, map, tuple, etc, it recursively clones the children + * and copies their values as well. The cloned node is attached to the + * memory context of the source node. + * + * Require: + * \li 'source' is a valid cfg_obj_t with copy function set. + * \li 'target' is non-NULL and '*target' is NULL. + * + * Ensures: + * \li 'target' contains the cloned object. + */ + bool cfg_obj_isvoid(const cfg_obj_t *obj); /*%< diff --git a/lib/isccfg/include/isccfg/grammar.h b/lib/isccfg/include/isccfg/grammar.h index b91c0901309..0ea711a9041 100644 --- a/lib/isccfg/include/isccfg/grammar.h +++ b/lib/isccfg/include/isccfg/grammar.h @@ -100,6 +100,7 @@ typedef isc_result_t (*cfg_parsefunc_t)(cfg_parser_t *, const cfg_type_t *type, typedef void (*cfg_printfunc_t)(cfg_printer_t *, const cfg_obj_t *); typedef void (*cfg_docfunc_t)(cfg_printer_t *, const cfg_type_t *); typedef void (*cfg_freefunc_t)(cfg_obj_t *); +typedef void (*cfg_copyfunc_t)(cfg_obj_t *to, const cfg_obj_t *from); /* * Structure definitions @@ -169,6 +170,7 @@ struct cfg_netprefix { struct cfg_rep { const char *name; /*%< For debugging only */ cfg_freefunc_t free; /*%< How to free this kind of data. */ + cfg_copyfunc_t copy; /*%< Deep copy of the node. */ }; /*% diff --git a/lib/isccfg/parser.c b/lib/isccfg/parser.c index 011d8305137..e585557657c 100644 --- a/lib/isccfg/parser.c +++ b/lib/isccfg/parser.c @@ -160,26 +160,181 @@ static void doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type); #endif /* HAVE_GEOIP2 */ +void +cfg_obj_clone(const cfg_obj_t *source, cfg_obj_t **target) { + REQUIRE(source != NULL); + REQUIRE(source->type != NULL); + REQUIRE(source->type->rep != NULL); + REQUIRE(source->type->rep->copy != NULL); + REQUIRE(target != NULL && *target == NULL); + + cfg_obj_create(source->mctx, source->file, source->line, source->type, + target); + source->type->rep->copy(*target, source); +} + +static void +copy_uint32(cfg_obj_t *to, const cfg_obj_t *from) { + to->value.uint32 = from->value.uint32; +} + +static void +copy_uint64(cfg_obj_t *to, const cfg_obj_t *from) { + to->value.uint64 = from->value.uint64; +} + +static void +copy_boolean(cfg_obj_t *to, const cfg_obj_t *from) { + to->value.boolean = from->value.boolean; +} + +static void +copy_sockaddr(cfg_obj_t *to, const cfg_obj_t *from) { + to->value.sockaddr = from->value.sockaddr; +} + +static void +copy_sockaddrtls(cfg_obj_t *to, const cfg_obj_t *from) { + to->value.sockaddrtls.sockaddr = from->value.sockaddrtls.sockaddr; + + if (from->value.sockaddrtls.tls.base != NULL) { + size_t len = from->value.sockaddrtls.tls.length; + + to->value.sockaddrtls.tls.base = isc_mem_get(to->mctx, len + 1); + to->value.sockaddrtls.tls.length = len; + memmove(to->value.sockaddrtls.tls.base, + from->value.sockaddrtls.tls.base, len + 1); + } +} + +static void +copy_netprefix(cfg_obj_t *to, const cfg_obj_t *from) { + to->value.netprefix = from->value.netprefix; +} + +static void +copy_duration(cfg_obj_t *to, const cfg_obj_t *from) { + to->value.duration = from->value.duration; +} + +static void +copy_string(cfg_obj_t *to, const cfg_obj_t *from) { + to->value.string.length = from->value.string.length; + to->value.string.base = isc_mem_get(to->mctx, + to->value.string.length + 1); + memmove(to->value.string.base, from->value.string.base, + to->value.string.length + 1); +} + +static void +copy_map_destroy(char *key, unsigned int type, isc_symvalue_t symval, + void *arg) { + cfg_obj_t *obj = symval.as_pointer; + + UNUSED(key); + UNUSED(type); + UNUSED(arg); + + cfg_obj_detach(&obj); +} + +static bool +copy_map_add(char *key, unsigned int type, isc_symvalue_t value, void *arg) { + cfg_obj_t *to = arg; + cfg_obj_t *toelt = NULL; + + /* + * Only `as_pointer` is used to store the cfg_obj_t object (see + * cfg_map_parsebody) + */ + cfg_obj_clone(value.as_pointer, &toelt); + value.as_pointer = toelt; + + INSIST(isc_symtab_define(to->value.map.symtab, key, type, value, + isc_symexists_reject) == ISC_R_SUCCESS); + + /* + * Do not delete the existing element from `from` table. + */ + return false; +} + +static void +copy_map(cfg_obj_t *to, const cfg_obj_t *from) { + if (from->value.map.id != NULL) { + cfg_obj_clone(from->value.map.id, &to->value.map.id); + } + isc_symtab_create(to->mctx, copy_map_destroy, NULL, false, + &to->value.map.symtab); + isc_symtab_foreach(from->value.map.symtab, copy_map_add, to); + + /* + * clausesets are statically defined + */ + to->value.map.clausesets = from->value.map.clausesets; +} + +static void +copy_list(cfg_obj_t *to, const cfg_obj_t *from) { + const cfg_listelt_t *fromelt = cfg_list_first(from); + + ISC_LIST_INIT(to->value.list); + while (fromelt != NULL) { + cfg_listelt_t *toelt = isc_mem_get(to->mctx, sizeof(*toelt)); + + *toelt = (cfg_listelt_t){ .link = ISC_LINK_INITIALIZER }; + cfg_obj_clone(fromelt->obj, &toelt->obj); + + ISC_LIST_APPEND(to->value.list, toelt, link); + + fromelt = cfg_list_next(fromelt); + } +} + +static void +copy_tuple(cfg_obj_t *to, const cfg_obj_t *from) { + const cfg_tuplefielddef_t *fields = from->type->of; + const cfg_tuplefielddef_t *field; + size_t size = 0; + + for (field = fields; field->name != NULL; field++) { + size++; + } + + to->value.tuple = isc_mem_cget(to->mctx, size, sizeof(cfg_obj_t *)); + + for (size_t j = 0; j < size; j++) { + cfg_obj_clone(from->value.tuple[j], &to->value.tuple[j]); + } +} + +static void +copy_noop(cfg_obj_t *to, const cfg_obj_t *from) { + UNUSED(to); + UNUSED(from); +} + /* * Data representations. These correspond to members of the * "value" union in struct cfg_obj (except "void", which does * not need a union member). */ -cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop }; -cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop }; -cfg_rep_t cfg_rep_string = { "string", free_string }; -cfg_rep_t cfg_rep_boolean = { "boolean", free_noop }; -cfg_rep_t cfg_rep_map = { "map", free_map }; -cfg_rep_t cfg_rep_list = { "list", free_list }; -cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple }; -cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop }; -cfg_rep_t cfg_rep_sockaddrtls = { "sockaddrtls", free_sockaddrtls }; -cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop }; -cfg_rep_t cfg_rep_void = { "void", free_noop }; -cfg_rep_t cfg_rep_fixedpoint = { "fixedpoint", free_noop }; -cfg_rep_t cfg_rep_percentage = { "percentage", free_noop }; -cfg_rep_t cfg_rep_duration = { "duration", free_noop }; +cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop, copy_uint32 }; +cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop, copy_uint64 }; +cfg_rep_t cfg_rep_string = { "string", free_string, copy_string }; +cfg_rep_t cfg_rep_boolean = { "boolean", free_noop, copy_boolean }; +cfg_rep_t cfg_rep_map = { "map", free_map, copy_map }; +cfg_rep_t cfg_rep_list = { "list", free_list, copy_list }; +cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple, copy_tuple }; +cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop, copy_sockaddr }; +cfg_rep_t cfg_rep_sockaddrtls = { "sockaddrtls", free_sockaddrtls, + copy_sockaddrtls }; +cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop, copy_netprefix }; +cfg_rep_t cfg_rep_void = { "void", free_noop, copy_noop }; +cfg_rep_t cfg_rep_fixedpoint = { "fixedpoint", free_noop, copy_uint32 }; +cfg_rep_t cfg_rep_percentage = { "percentage", free_noop, copy_uint32 }; +cfg_rep_t cfg_rep_duration = { "duration", free_noop, copy_duration }; /* * Configuration type definitions.