]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-settings: Apply settings in reverse order of filter specificity
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 29 May 2023 21:02:11 +0000 (00:02 +0300)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Wed, 12 Feb 2025 10:34:08 +0000 (12:34 +0200)
Instead of going from least specific filter to most specific, do it in
reverse. This is going to help with variable expansions in a following
commit.

src/lib-settings/settings-parser.c
src/lib-settings/settings-parser.h
src/lib-settings/settings.c

index 0a820fa9a4bff71bfcc4a3e8cf886d1627b373cf..53d4ded72f48311b2723403cc289d2dc2a41b703 100644 (file)
@@ -413,10 +413,14 @@ settings_parse(struct setting_parser_context *ctx,
                unsigned int i, count = str_array_length(list);
                if (!array_is_created(arr))
                        p_array_init(arr, ctx->set_pool, count);
+               unsigned int insert_pos = 0;
                for (i = 0; i < count; i++) {
                        const char *value = p_strdup(ctx->set_pool,
                                settings_section_unescape(list[i]));
-                       array_push_back(arr, &value);
+                       if ((ctx->flags & SETTINGS_PARSER_FLAG_INSERT_FILTERS) != 0)
+                               array_insert(arr, insert_pos++, &value, 1);
+                       else
+                               array_push_back(arr, &value);
                }
                break;
        }
@@ -510,6 +514,27 @@ int settings_parse_keyidx_value_nodup(struct setting_parser_context *ctx,
                              key, value, FALSE);
 }
 
+bool settings_parse_strlist_has_key(struct setting_parser_context *ctx,
+                                   unsigned int key_idx,
+                                   const char *key_suffix)
+{
+       const struct setting_define *def = &ctx->info->defines[key_idx];
+       i_assert(def->type == SET_STRLIST);
+
+       ARRAY_TYPE(const_string) *array =
+               STRUCT_MEMBER_P(ctx->set_struct, def->offset);
+       if (!array_is_created(array))
+               return FALSE;
+
+       unsigned int i, count;
+       const char *const *items = array_get(array, &count);
+       for (i = 0; i < count; i += 2) {
+               if (strcmp(items[i], key_suffix) == 0)
+                       return TRUE;
+       }
+       return FALSE;
+}
+
 const void *
 settings_parse_get_value(struct setting_parser_context *ctx,
                         const char **key, enum setting_type *type_r)
index 055f9f125943abb5a6f70d24b5154bb9f099c52f..4b37615f1b52d7d9d7ff4ac3320351f039dbd19d 100644 (file)
@@ -130,7 +130,10 @@ ARRAY_DEFINE_TYPE(setting_parser_info, struct setting_parser_info);
 
 enum settings_parser_flags {
        SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS        = 0x01,
-       SETTINGS_PARSER_FLAG_TRACK_CHANGES              = 0x02
+       SETTINGS_PARSER_FLAG_TRACK_CHANGES              = 0x02,
+       /* Filters are added in reverse order. New filters are inserted to the
+          beginning of the array. */
+       SETTINGS_PARSER_FLAG_INSERT_FILTERS             = 0x04,
 };
 
 struct setting_parser_context;
@@ -189,6 +192,11 @@ int settings_parse_keyvalue_nodup(struct setting_parser_context *ctx,
 int settings_parse_keyidx_value_nodup(struct setting_parser_context *ctx,
                                      unsigned int key_idx, const char *key,
                                      const char *value);
+/* Returns TRUE if strlist has the specific key. The key must NOT include the
+   strlist/ prefix. */
+bool settings_parse_strlist_has_key(struct setting_parser_context *ctx,
+                                   unsigned int key_idx,
+                                   const char *key_suffix);
 /* Call all check_func()s and ext_check_func()s to see if currently parsed
    settings are valid. */
 bool settings_parser_check(struct setting_parser_context *ctx, pool_t pool,
index 5549d040e995f64239abee71650acee7969da579..78380bffcde8cf0da40a60a6ae95efab0e0c16c2 100644 (file)
@@ -98,6 +98,7 @@ struct settings_apply_ctx {
        struct setting_parser_context *parser;
        struct settings_mmap_pool *mpool;
        void *set_struct;
+       ARRAY_TYPE(bool) set_seen;
 };
 
 static const char *settings_override_type_names[] = {
@@ -466,6 +467,10 @@ static void settings_mmap_apply_defaults(struct settings_apply_ctx *ctx)
        unsigned int key_idx;
 
        for (key_idx = 0; ctx->info->defines[key_idx].key != NULL; key_idx++) {
+               bool *setp = array_idx_get_space(&ctx->set_seen, key_idx);
+               if (*setp)
+                       continue;
+
                void *set = PTR_OFFSET(ctx->info->defaults,
                                       ctx->info->defines[key_idx].offset);
                if (ctx->info->defines[key_idx].type != SET_STR_VARS)
@@ -506,10 +511,23 @@ settings_mmap_apply_blob(struct settings_apply_ctx *ctx,
                }
                offset += sizeof(key_idx);
 
+               bool set_apply;
                const char *strlist_key = NULL;
                if (ctx->info->defines[key_idx].type == SET_STRLIST) {
                        strlist_key = (const char *)mmap->mmap_base + offset;
                        offset += strlen(strlist_key)+1;
+                       set_apply = !settings_parse_strlist_has_key(ctx->parser,
+                                       key_idx, strlist_key);
+               } else if (ctx->info->defines[key_idx].type == SET_FILTER_ARRAY)
+                       set_apply = TRUE;
+               else {
+                       bool *setp = array_idx_get_space(&ctx->set_seen, key_idx);
+                       if (*setp)
+                               set_apply = FALSE;
+                       else {
+                               *setp = TRUE;
+                               set_apply = TRUE;
+                       }
                }
 
                if (offset >= end_offset) {
@@ -530,7 +548,9 @@ settings_mmap_apply_blob(struct settings_apply_ctx *ctx,
                        return -1;
                }
                int ret;
-               T_BEGIN {
+               if (!set_apply)
+                       ret = 0;
+               else T_BEGIN {
                        ret = settings_mmap_apply_key(ctx, key_idx, strlist_key,
                                                      value, error_r);
                } T_END_PASS_STR_IF(ret < 0, error_r);
@@ -598,16 +618,15 @@ settings_mmap_apply(struct settings_apply_ctx *ctx, const char **error_r)
                block->settings_validated = TRUE;
        }
 
-       if (settings_mmap_apply_blob(ctx, block, block->base_start_offset,
-                                    block->base_end_offset, error_r) < 0)
-               return -1;
-
        const struct failure_context failure_ctx = {
                .type = LOG_TYPE_DEBUG,
        };
 
+       /* go through the filters in reverse sorted order, so we always set the
+          setting just once, never overriding anything. */
        bool seen_filter = FALSE;
-       for (uint32_t i = 0; i < block->filter_count; i++) {
+       for (uint32_t i = block->filter_count; i > 0; ) {
+               i--;
                uint32_t event_filter_idx = be32_to_cpu_unaligned(
                        CONST_PTR_OFFSET(mmap->mmap_base,
                                         block->filter_indexes_start_offset +
@@ -661,6 +680,10 @@ settings_mmap_apply(struct settings_apply_ctx *ctx, const char **error_r)
                                return -1;
                }
        }
+       /* apply the base settings last after all filters */
+       if (settings_mmap_apply_blob(ctx, block, block->base_start_offset,
+                                    block->base_end_offset, error_r) < 0)
+               return -1;
        return seen_filter ? 1 : 0;
 
 }
@@ -872,7 +895,7 @@ settings_var_expand_init(struct event *event,
 static int settings_override_cmp(const struct settings_override *set1,
                                 const struct settings_override *set2)
 {
-       return set1->type - set2->type;
+       return set2->type - set1->type;
 }
 
 static int
@@ -955,6 +978,7 @@ settings_instance_override(struct settings_apply_ctx *ctx,
                array_append_array(&overrides, &ctx->instance->overrides);
        if (array_is_created(&ctx->root->overrides))
                array_append_array(&overrides, &ctx->root->overrides);
+       /* sort overrides so that the most specific ones are first */
        array_sort(&overrides, settings_override_cmp);
 
        const struct failure_context failure_ctx = {
@@ -984,6 +1008,22 @@ settings_instance_override(struct settings_apply_ctx *ctx,
                        /* setting doesn't exist in this info */
                        continue;
                }
+               if (ctx->info->defines[key_idx].type == SET_STRLIST) {
+                       const char *suffix;
+                       if (!str_begins(key, ctx->info->defines[key_idx].key, &suffix) ||
+                           suffix[0] != '/')
+                               i_unreached();
+                       if (settings_parse_strlist_has_key(ctx->parser, key_idx,
+                                                          suffix + 1))
+                               continue;
+               } else if (ctx->info->defines[key_idx].type != SET_FILTER_ARRAY) {
+                       bool *setp = array_idx_get_space(&ctx->set_seen, key_idx);
+                       if (*setp) {
+                               /* already set - skip */
+                               continue;
+                       }
+                       *setp = TRUE;
+               }
 
                if (value != set->value)
                        value = p_strdup(&ctx->mpool->pool, value);
@@ -1045,7 +1085,8 @@ settings_instance_get(struct settings_apply_ctx *ctx,
                                               source_filename, source_linenum);
        pool_t set_pool = &ctx->mpool->pool;
        ctx->parser = settings_parser_init(set_pool, ctx->info,
-                                          SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
+                                          SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS |
+                                          SETTINGS_PARSER_FLAG_INSERT_FILTERS);
 
        /* Set the pool early on before any callbacks are called. */
        ctx->set_struct = settings_parser_get_set(ctx->parser);
@@ -1053,30 +1094,29 @@ settings_instance_get(struct settings_apply_ctx *ctx,
                                    ctx->info->pool_offset1 - 1);
        *pool_p = set_pool;
 
-       settings_mmap_apply_defaults(ctx);
-       if (ctx->instance->mmap != NULL) {
+       i_array_init(&ctx->set_seen, 64);
+
+       settings_parse_set_expanded(ctx->parser, TRUE);
+       ret = settings_instance_override(ctx, error_r);
+       settings_parse_set_expanded(ctx->parser, FALSE);
+       if (ret > 0)
+               seen_filter = TRUE;
+
+       if (ctx->instance->mmap != NULL && ret >= 0) {
                ret = settings_mmap_apply(ctx, &error);
                if (ret < 0) {
                        *error_r = t_strdup_printf(
                                "Failed to parse configuration: %s", error);
-                       pool_unref(&set_pool);
-                       return -1;
                }
                if (ret > 0)
                        seen_filter = TRUE;
        }
-
-       /* if we change any settings afterwards, they're in expanded form.
-          especially all settings from userdb are already expanded. */
-       settings_parse_set_expanded(ctx->parser, TRUE);
-
-       ret = settings_instance_override(ctx, error_r);
+       if (ret >= 0)
+               settings_mmap_apply_defaults(ctx);
        if (ret < 0) {
                pool_unref(&set_pool);
                return -1;
        }
-       if (ret > 0)
-               seen_filter = TRUE;
 
        if (ctx->filter_key != NULL && !seen_filter &&
            ctx->filter_name_required) {
@@ -1204,6 +1244,7 @@ settings_get_full(struct event *event,
        } T_END_PASS_STR_IF(ret < 0, error_r);
        settings_parser_unref(&ctx.parser);
        event_unref(&ctx.event);
+       array_free(&ctx.set_seen);
        return ret;
 }