From: Peter Krempa <pkrempa@redhat.com> While 'virTypedParamsValidate', which uses varargs to pass the template to validate parameters agains, is convenient for single uses we have multiple occasions where we want to validate the same list of parameters in muliple places. We use either a macro which expands to the parameter list in place or a function which encapsulates the validation. For introspection of input typed parameters we'll need to have the list of supported typed parameters in each function which uses them as input and either of the approaches is inconvenient for generating the introspection parts. Refactor 'virTypedParamsValidate', to split the actual validation internals into ''virTypedParamsValidateInternal' and create two wrappers: - 'virTypedParamsValidate' which uses varargs - 'virTypedParamsValidateTemplate' which uses an array of structs containing the template. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/libvirt_private.syms | 1 + src/util/virtypedparam.c | 134 +++++++++++++++++++++++++-------------- src/util/virtypedparam.h | 11 ++++ 3 files changed, 100 insertions(+), 46 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 706079b87f..20507af7f7 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3684,6 +3684,7 @@ virTypedParamsRemoteFree; virTypedParamsReplaceString; virTypedParamsSerialize; virTypedParamsValidate; +virTypedParamsValidateTemplate; # util/viruri.h diff --git a/src/util/virtypedparam.c b/src/util/virtypedparam.c index ec8046b998..92f25cea39 100644 --- a/src/util/virtypedparam.c +++ b/src/util/virtypedparam.c @@ -94,75 +94,55 @@ virTypedParamsSortName(const void *left, return strcmp(param_left->field, param_right->field); } +static int +virTypedParamsSortTemplate(const void *left, + const void *right, + void *opaque G_GNUC_UNUSED) +{ + const virTypedParamValidationTemplate *param_left = left; + const virTypedParamValidationTemplate *param_right = right; + return strcmp(param_left->name, param_right->name); +} + /* Validate that PARAMS contains only recognized parameter names with * correct types, and with no duplicates except for parameters * specified with VIR_TYPED_PARAM_MULTIPLE flag in type. * Pass in as many name/type pairs as appropriate, and pass NULL to end * the list of accepted parameters. Return 0 on success, -1 on failure * with error message already issued. */ -int -virTypedParamsValidate(virTypedParameterPtr params, int nparams, ...) +static int +virTypedParamsValidateInternal(virTypedParameterPtr params, + size_t nparams, + virTypedParamValidationTemplate *templates, + size_t ntemplates) { - va_list ap; size_t i; size_t j; - const char *name; const char *last_name = NULL; - size_t nkeys = 0; - size_t nkeysalloc = 0; - g_autofree virTypedParameterPtr sorted = NULL; - g_autofree virTypedParameterPtr keys = NULL; + g_autofree virTypedParameterPtr sorted = g_new0(virTypedParameter, nparams); - if (!nparams) { - return 0; - } - - va_start(ap, nparams); - - sorted = g_new0(virTypedParameter, nparams); - - /* Here we intentionally don't copy values */ memcpy(sorted, params, sizeof(*params) * nparams); g_qsort_with_data(sorted, nparams, sizeof(*sorted), virTypedParamsSortName, NULL); - name = va_arg(ap, const char *); - while (name) { - int type = va_arg(ap, int); - VIR_RESIZE_N(keys, nkeysalloc, nkeys, 1); - - if (virStrcpyStatic(keys[nkeys].field, name) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Field name '%1$s' too long"), name); - va_end(ap); - return -1; - } - - keys[nkeys].type = type & ~VIR_TYPED_PARAM_MULTIPLE; - /* Value is not used anyway */ - keys[nkeys].value.i = type & VIR_TYPED_PARAM_MULTIPLE; - - nkeys++; - name = va_arg(ap, const char *); - } - - va_end(ap); - - g_qsort_with_data(keys, nkeys, sizeof(*keys), virTypedParamsSortName, NULL); + g_qsort_with_data(templates, ntemplates, + sizeof(*templates), virTypedParamsSortTemplate, NULL); - for (i = 0, j = 0; i < nparams && j < nkeys;) { - if (STRNEQ(sorted[i].field, keys[j].field)) { + for (i = 0, j = 0; i < nparams && j < ntemplates;) { + if (STRNEQ(sorted[i].field, templates[j].name)) { j++; } else { - if (STREQ_NULLABLE(last_name, sorted[i].field) && - !(keys[j].value.i & VIR_TYPED_PARAM_MULTIPLE)) { + unsigned int expected_type = templates[j].typeflags & ~VIR_TYPED_PARAM_MULTIPLE; + bool multiple = templates[j].typeflags & VIR_TYPED_PARAM_MULTIPLE; + + if (STREQ_NULLABLE(last_name, sorted[i].field) && !multiple) { virReportError(VIR_ERR_INVALID_ARG, _("parameter '%1$s' occurs multiple times"), sorted[i].field); return -1; } - if (virTypedParamValidateType(sorted + i, keys[j].type) < 0) + if (virTypedParamValidateType(sorted + i, expected_type) < 0) return -1; last_name = sorted[i].field; @@ -170,7 +150,7 @@ virTypedParamsValidate(virTypedParameterPtr params, int nparams, ...) } } - if (j == nkeys && i != nparams) { + if (j == ntemplates && i != nparams) { virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, _("parameter '%1$s' not supported"), sorted[i].field); @@ -181,6 +161,68 @@ virTypedParamsValidate(virTypedParameterPtr params, int nparams, ...) } +/* Validate that PARAMS contains only recognized parameter names with + * correct types, and with no duplicates except for parameters + * specified with VIR_TYPED_PARAM_MULTIPLE flag in type. + * Pass in as many name/type pairs as appropriate, and pass NULL to end + * the list of accepted parameters. Return 0 on success, -1 on failure + * with error message already issued. */ +int +virTypedParamsValidate(virTypedParameterPtr params, + int nparams, + ...) +{ + va_list ap; + const char *name; + g_autofree virTypedParamValidationTemplate *templates = NULL; + size_t ntemplates = 0; + size_t ntemplatesalloc = 0; + + if (nparams == 0) + return 0; + + va_start(ap, nparams); + + for (name = va_arg(ap, const char *); name; name = va_arg(ap, const char *)) { + VIR_RESIZE_N(templates, ntemplatesalloc, ntemplates, 1); + + if (virStrcpy((char *)templates[ntemplates].name, name, VIR_TYPED_PARAM_FIELD_LENGTH) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Field name '%1$s' too long"), name); + va_end(ap); + return -1; + } + + templates[ntemplates].typeflags = va_arg(ap, unsigned int); + ntemplates++; + } + + va_end(ap); + + return virTypedParamsValidateInternal(params, nparams, templates, ntemplates); +} + + +int +virTypedParamsValidateTemplate(virTypedParameterPtr params, + int nparams, + const virTypedParamValidationTemplate *templates) +{ + size_t ntemplates = 0; + g_autofree virTypedParamValidationTemplate *templ_copy = NULL; + + /* we need to copy the list of templates because + * 'virTypedParamsValidateInternal' will need to sort it */ + while (*templates[ntemplates++].name == '\0') + ; + + templ_copy = g_new0(virTypedParamValidationTemplate, ntemplates); + memcpy(templ_copy, templates, sizeof(*templates) * ntemplates); + + return virTypedParamsValidateInternal(params, nparams, templ_copy, ntemplates); +} + + /* Check if params contains only specified parameter names. Return true if * only specified names are present in params, false if params contains any * unspecified parameter name. */ diff --git a/src/util/virtypedparam.h b/src/util/virtypedparam.h index 819166ff1b..bee3ecc14d 100644 --- a/src/util/virtypedparam.h +++ b/src/util/virtypedparam.h @@ -68,6 +68,17 @@ virTypedParamValidateType(virTypedParameterPtr param, unsigned int expected_type) G_GNUC_WARN_UNUSED_RESULT; +struct _virTypedParamValidationTemplate { + const char name[VIR_TYPED_PARAM_FIELD_LENGTH]; /* parameter name */ + unsigned int typeflags; +}; +typedef struct _virTypedParamValidationTemplate virTypedParamValidationTemplate; + +int +virTypedParamsValidateTemplate(virTypedParameterPtr params, + int nparams, + const virTypedParamValidationTemplate *templates) + G_GNUC_WARN_UNUSED_RESULT; int virTypedParamsValidate(virTypedParameterPtr params, int nparams, -- 2.54.0