From: Timo Sirainen Date: Wed, 15 Jan 2025 12:54:21 +0000 (+0200) Subject: config: Sort named list filters so more specific ones always override less specific... X-Git-Tag: 2.4.0~46 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c0d0651ae5bb8733dfab631507c8c71c4712cd61;p=thirdparty%2Fdovecot%2Fcore.git config: Sort named list filters so more specific ones always override less specific ones --- diff --git a/src/config/config-filter.h b/src/config/config-filter.h index f2df212741..ca0d37ee47 100644 --- a/src/config/config-filter.h +++ b/src/config/config-filter.h @@ -40,6 +40,12 @@ ARRAY_DEFINE_TYPE(config_include_group, struct config_include_group); /* Each unique config_filter (including its parents in hierarchy) has its own config_filter_parser. */ struct config_filter_parser { + /* Increasing number for every created parser. Used by sorting. */ + unsigned int create_order; + /* Number of filters in this parser and parent parsers that have + filter.filter_name_array=TRUE. */ + unsigned int named_list_filter_count; + /* Filter parser tree. These are used only for doveconf's human output to write the filters in nice nested hierarchies. */ struct config_filter_parser *parent; diff --git a/src/config/config-parser-private.h b/src/config/config-parser-private.h index f1884665ec..3e9729a2ff 100644 --- a/src/config/config-parser-private.h +++ b/src/config/config-parser-private.h @@ -86,6 +86,7 @@ struct config_parser_context { struct config_section_stack *cur_section; struct input_stack *cur_input; uint8_t change_counter; + unsigned int create_order_counter; string_t *value; const char *error; diff --git a/src/config/config-parser.c b/src/config/config-parser.c index ef16c80295..4b4fd476cb 100644 --- a/src/config/config-parser.c +++ b/src/config/config-parser.c @@ -1061,6 +1061,7 @@ config_add_new_parser(struct config_parser_context *ctx, struct config_filter_parser *filter_parser; filter_parser = p_new(ctx->pool, struct config_filter_parser, 1); + filter_parser->create_order = ctx->create_order_counter++; filter_parser->filter = *filter; filter_parser->module_parsers = parent_filter_parser == NULL && !filter->default_settings ? @@ -1075,6 +1076,9 @@ config_add_new_parser(struct config_parser_context *ctx, if (parent_filter_parser != NULL) { filter_parser->parent = parent_filter_parser; + filter_parser->named_list_filter_count = + parent_filter_parser->named_list_filter_count + + (filter->filter_name_array ? 1 : 0); DLLIST2_APPEND(&parent_filter_parser->children_head, &parent_filter_parser->children_tail, filter_parser); @@ -2217,6 +2221,35 @@ config_parse_finish_service_defaults(struct config_parser_context *ctx) config_parser_set_change_counter(ctx, CONFIG_PARSER_CHANGE_EXPLICIT); } +static int config_parser_filter_cmp(struct config_filter_parser *const *f1, + struct config_filter_parser *const *f2) +{ + /* Preserve position for the first two parsers */ + if ((*f1)->create_order <= 1) { + if ((*f2)->create_order <= 1) + return (int)(*f1)->create_order - (int)(*f2)->create_order; + return -1; + } + if ((*f2)->create_order <= 1) + return -1; + + /* Next, order by the number of named list filters, so more specific + filters are applied before less specific ones. (Applying is done in + reverse order from the last filter to the first.) + + Don't include other types of filters in this check, since e.g. + hierarchical named filters may commonly be used to specify defaults + (e.g. layout_fs { sdbox { } }) which shouldn't override even a + smaller number of named list filters (e.g. mailbox foo { .. }). */ + int ret = (int)(*f1)->named_list_filter_count - + (int)(*f2)->named_list_filter_count; + if (ret != 0) + return ret; + + /* Finally, just order them in the order of creation. */ + return (int)(*f1)->create_order - (int)(*f2)->create_order; +} + static int config_parse_finish(struct config_parser_context *ctx, enum config_parse_flags flags, @@ -2239,6 +2272,7 @@ config_parse_finish(struct config_parser_context *ctx, new_config->dovecot_config_version = ctx->dovecot_config_version; p_array_init(&new_config->errors, ctx->pool, 1); + array_sort(&ctx->all_filter_parsers, config_parser_filter_cmp); array_append_zero(&ctx->all_filter_parsers); array_pop_back(&ctx->all_filter_parsers); new_config->filter_parsers = array_front(&ctx->all_filter_parsers);