mirror of
https://github.com/isc-projects/bind9.git
synced 2026-06-13 01:50:00 -04:00
new: dev: 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.
This is pre-require work for MR !11121 !11122 !11123
Merge branch 'colin/effective-config-clone' into 'main'
See merge request isc-projects/bind9!11124
This commit is contained in:
commit
d951cedd02
4 changed files with 274 additions and 14 deletions
|
|
@ -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);
|
||||
/*%<
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
};
|
||||
|
||||
/*%
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -206,12 +206,98 @@ ISC_RUN_TEST_IMPL(cfg_map_nextclause) {
|
|||
} while (name != NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
cfg_clone_copy_dumpconf(void *closure, const char *text, int textlen) {
|
||||
isc_buffer_putmem((isc_buffer_t *)closure, (const unsigned char *)text,
|
||||
textlen);
|
||||
}
|
||||
|
||||
ISC_RUN_TEST_IMPL(cfg_clone_copy) {
|
||||
cfg_obj_t *orig = NULL;
|
||||
cfg_obj_t *clone = NULL;
|
||||
isc_result_t result;
|
||||
isc_buffer_t buf;
|
||||
isc_buffer_t dumpb1;
|
||||
char dumpbdata1[10024];
|
||||
isc_buffer_t dumpb2;
|
||||
char dumpbdata2[10024];
|
||||
|
||||
/*
|
||||
* This is a modified subset of the default conf which contains
|
||||
* all the possible types cloned and copied.
|
||||
*/
|
||||
static char conf[] = "\
|
||||
options {\n\
|
||||
answer-cookie yes;\n\
|
||||
cookie-algorithm siphash24;\n\
|
||||
dump-file \"named_dump.db\";\n\
|
||||
notify-rate 20;\n\
|
||||
allow-recursion {\n\
|
||||
\"localhost\";\n\
|
||||
\"localnets\";\n\
|
||||
};\n\
|
||||
prefetch 2 9;\n\
|
||||
check-dup-records warn;\n\
|
||||
max-ixfr-ratio 100%;\n\
|
||||
};\n\
|
||||
remote-servers \"foo\" {\n\
|
||||
2801:1b8:10::b;\n\
|
||||
192.0.32.132;\n\
|
||||
};\n\
|
||||
view \"_bind\" chaos {\n\
|
||||
zone \"version.bind\" chaos {\n\
|
||||
type primary;\n\
|
||||
database \"_builtin version\";\n\
|
||||
};\n\
|
||||
max-cache-size 2097152;\n\
|
||||
rate-limit {\n\
|
||||
min-table-size 10;\n\
|
||||
slip 0;\n\
|
||||
};\n\
|
||||
};\n";
|
||||
isc_buffer_init(&buf, conf, sizeof(conf));
|
||||
isc_buffer_add(&buf, sizeof(conf) - 1);
|
||||
|
||||
result = cfg_parse_buffer(isc_g_mctx, &buf, "", 0, &cfg_type_namedconf,
|
||||
0, &orig);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
isc_buffer_init(&dumpb1, dumpbdata1, sizeof(dumpbdata1));
|
||||
cfg_printx(orig, 0, cfg_clone_copy_dumpconf, &dumpb1);
|
||||
isc_buffer_putuint8(&dumpb1, 0);
|
||||
|
||||
/*
|
||||
* The point of the test is not really to test the stringify code of the
|
||||
* cfg_obj_t tree, but let's do it as a sanity check first.
|
||||
*/
|
||||
assert_int_equal(strcmp(conf, dumpbdata1), 0);
|
||||
|
||||
/*
|
||||
* The original tree can be freed anytime, it is not connected in any
|
||||
* way to the clone.
|
||||
*/
|
||||
cfg_obj_clone(orig, &clone);
|
||||
cfg_obj_detach(&orig);
|
||||
|
||||
/*
|
||||
* Dumping the clone and comparing its output to the original
|
||||
* dump of the orinal config verify-ish the two assumptions above.
|
||||
*/
|
||||
isc_buffer_init(&dumpb2, dumpbdata2, sizeof(dumpbdata2));
|
||||
cfg_printx(clone, 0, cfg_clone_copy_dumpconf, &dumpb2);
|
||||
|
||||
assert_int_equal(strcmp(dumpbdata1, dumpbdata2), 0);
|
||||
|
||||
cfg_obj_detach(&clone);
|
||||
}
|
||||
|
||||
ISC_TEST_LIST_START
|
||||
|
||||
ISC_TEST_ENTRY(addzoneconf)
|
||||
ISC_TEST_ENTRY(parse_buffer)
|
||||
ISC_TEST_ENTRY(cfg_map_firstclause)
|
||||
ISC_TEST_ENTRY(cfg_map_nextclause)
|
||||
ISC_TEST_ENTRY(cfg_clone_copy)
|
||||
|
||||
ISC_TEST_LIST_END
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue