#include "safe-mkstemp.h"
#include "ostream.h"
#include "settings.h"
+#include "master-service-settings.h"
#include "config-parser.h"
#include "config-request.h"
#include "config-filter.h"
"DOVECOT-CONFIG\t1.0\n"
<64bit big-endian: settings full size>
+ <32bit big-endian: number of paths used for caching>
+ Repeat for "number of paths":
+ <NUL-terminated string: path>
+ <64bit big-endian: inode>
+ <64bit big-endian: size>
+ <32bit big-endian: mtime UNIX timestamp>
+ <32bit big-endian: mtime nsecs>
+ <32bit big-endian: ctime UNIX timestamp>
+ <32bit big-endian: ctime nsecs>
+
<32bit big-endian: event filter strings count>
Repeat for "event filter strings count":
<NUL-terminated string: event filter string>
return 0;
}
+static const char *config_get_cache_dir(enum config_dump_flags flags)
+{
+ if ((flags & CONFIG_DUMP_FLAG_WRITE_BINARY_CACHE) == 0)
+ return NULL;
+ return getenv("DOVECOT_CONFIG_CACHE");
+}
+
+static void
+config_dump_full_write_cache_paths(struct ostream *output,
+ struct config_parsed *config,
+ enum config_dump_flags flags,
+ const char **cache_path_r)
+{
+ const struct config_path *path;
+ const char *cache_dir = config_get_cache_dir(flags);
+ uint32_t num32 = 0;
+
+ if (cache_dir == NULL) {
+ /* no config caching - nobody cares about the paths */
+ o_stream_nsend(output, &num32, sizeof(num32));
+ *cache_path_r = NULL;
+ return;
+ }
+
+ num32 = cpu32_to_be(array_count(config_parsed_get_paths(config)));
+ o_stream_nsend(output, &num32, sizeof(num32));
+ array_foreach(config_parsed_get_paths(config), path) {
+ o_stream_nsend(output, path->path, strlen(path->path) + 1);
+ uint64_t num64 = cpu32_to_be(path->st.st_ino);
+ o_stream_nsend(output, &num64, sizeof(num64));
+ num64 = cpu32_to_be(path->st.st_size);
+ o_stream_nsend(output, &num64, sizeof(num64));
+ num32 = cpu32_to_be(path->st.st_mtime);
+ o_stream_nsend(output, &num32, sizeof(num32));
+ num32 = cpu32_to_be(ST_MTIME_NSEC(path->st));
+ o_stream_nsend(output, &num32, sizeof(num32));
+ num32 = cpu32_to_be(path->st.st_ctime);
+ o_stream_nsend(output, &num32, sizeof(num32));
+ num32 = cpu32_to_be(ST_CTIME_NSEC(path->st));
+ o_stream_nsend(output, &num32, sizeof(num32));
+ }
+
+ const struct config_path *main_path =
+ array_front(config_parsed_get_paths(config));
+ *cache_path_r = master_service_get_binary_config_cache_path(cache_dir,
+ main_path->path);
+}
+
static void
config_dump_full_append_filter_query(string_t *str,
const struct config_filter *filter,
str_free(&dump_ctx.delayed_output);
return -1;
}
- if (dest == CONFIG_DUMP_FULL_DEST_TEMPDIR)
+ if (dest == CONFIG_DUMP_FULL_DEST_TEMPDIR &&
+ config_get_cache_dir(flags) == NULL)
i_unlink(str_c(path));
dump_ctx.output = o_stream_create_fd_autoclose(&fd, IO_BLOCK_SIZE);
o_stream_set_name(dump_ctx.output, str_c(path));
settings_full_size_offset = output->offset;
o_stream_nsend(output, &blob_size, sizeof(blob_size));
+ const char *cache_path;
+ config_dump_full_write_cache_paths(output, config, flags,
+ &cache_path);
+ if (cache_path != NULL)
+ final_path = cache_path;
config_dump_full_write_filters(output, config);
}
pool_t pool;
const char *path;
+ ARRAY_TYPE(config_path) seen_paths;
HASH_TABLE(const char *, struct config_parser_key *) all_keys;
ARRAY(struct config_filter_parser *) all_filter_parsers;
HASH_TABLE(struct config_filter *,
const char *dovecot_config_version;
struct config_filter_parser *const *filter_parsers;
struct config_module_parser *module_parsers;
+ ARRAY_TYPE(config_path) seen_paths;
ARRAY_TYPE(const_string) errors;
HASH_TABLE(const char *, const struct setting_define *) key_hash;
HASH_TABLE(const char *, struct config_include_group_filters *) include_groups;
ctx->change_counter = change_counter;
}
+static void
+config_parser_add_seen_file(struct config_parser_context *ctx,
+ const struct stat *st, const char *path)
+{
+ struct config_path *seen_path = array_append_space(&ctx->seen_paths);
+ seen_path->path = p_strdup(ctx->pool, path);
+ seen_path->st = *st;
+}
+
+static int
+config_parser_add_seen_file_fd(struct config_parser_context *ctx,
+ int fd, const char *path, const char **error_r)
+{
+ struct stat st;
+
+ if (fstat(fd, &st) < 0) {
+ *error_r = t_strdup_printf("fstat(%s) failed: %m", path);
+ return -1;
+ }
+ config_parser_add_seen_file(ctx, &st, path);
+ return 0;
+}
+
static struct config_section_stack *
config_parser_add_filter_array(struct config_parser_context *ctx,
const char *filter_key, const char *name)
const struct config_line *line,
const char *path, const char **output_r)
{
+ struct stat st;
const char *full_path, *error;
if (path[0] == '\0') {
/* preserve original relative path in doveconf output */
if (full_path != path && ctx->expand_values)
path = full_path;
- if (settings_parse_read_file(full_path, path, ctx->pool, NULL,
+ if (settings_parse_read_file(full_path, path, ctx->pool, &st,
output_r, &error) < 0) {
ctx->error = p_strdup(ctx->pool, error);
if (config_apply_error(ctx, line->key) < 0)
return -1;
/* delayed error */
*output_r = "";
+ } else {
+ config_parser_add_seen_file(ctx, &st, full_path);
}
return 0;
}
}
static int
-str_append_file(string_t *str, const char *key, const char *path,
- const char **error_r)
+str_append_file(struct config_parser_context *ctx, string_t *str,
+ const char *key, const char *path, const char **error_r)
{
unsigned char buf[1024];
int fd;
key, path);
return -1;
}
+ if (config_parser_add_seen_file_fd(ctx, fd, path, error_r) < 0)
+ return -1;
while ((ret = read(fd, buf, sizeof(buf))) > 0)
str_append_data(str, buf, ret);
if (ret < 0) {
path);
return -1;
}
+ if (config_parser_add_seen_file_fd(ctx, fd, path, error_r) < 0)
+ return -1;
new_input = p_new(ctx->pool, struct input_stack, 1);
new_input->prev = ctx->cur_input;
pool_ref(new_config->pool);
new_config->dovecot_config_version = ctx->dovecot_config_version;
p_array_init(&new_config->errors, ctx->pool, 1);
+ new_config->seen_paths = ctx->seen_paths;
array_sort(&ctx->all_filter_parsers, config_parser_filter_cmp);
array_append_zero(&ctx->all_filter_parsers);
line->key) :
line->key;
path = fix_relative_path(line->value, ctx->cur_input);
- if (str_append_file(ctx->value, key_with_path, path,
+ if (str_append_file(ctx, ctx->value, key_with_path, path,
&error) < 0) {
/* file reading failed */
ctx->error = p_strdup(ctx->pool, error);
ctx.delay_errors = (flags & CONFIG_PARSE_FLAG_DELAY_ERRORS) != 0;
ctx.ignore_unknown = (flags & CONFIG_PARSE_FLAG_IGNORE_UNKNOWN) != 0;
hash_table_create(&ctx.all_keys, ctx.pool, 500, str_hash, strcmp);
+ p_array_init(&ctx.seen_paths, ctx.pool, 8);
+ if (fd != -1) {
+ if (config_parser_add_seen_file_fd(&ctx, fd, path, error_r) < 0)
+ return -1;
+ }
for (count = 0; all_infos[count] != NULL; count++) ;
config_parser_set_change_counter(&ctx, CONFIG_PARSER_CHANGE_DEFAULTS);
return array_count(groups) > 0;
}
+const ARRAY_TYPE(config_path) *
+config_parsed_get_paths(struct config_parsed *config)
+{
+ return &config->seen_paths;
+}
+
void config_parsed_free(struct config_parsed **_config)
{
struct config_parsed *config = *_config;
#include "config-filter.h"
+#include <sys/stat.h>
+
#define CONFIG_MODULE_DIR MODULEDIR"/settings"
/* change_counter used for default settings created internally */
};
ARRAY_DEFINE_TYPE(config_module_parsers, struct config_module_parser *);
+struct config_path {
+ const char *path;
+ struct stat st;
+};
+ARRAY_DEFINE_TYPE(config_path, struct config_path);
+
extern struct module *modules;
int config_parse_net(const char *value, struct ip_addr *ip_r,
const struct config_filter_parser *filter,
unsigned int parser_idx,
ARRAY_TYPE(config_include_group) *groups);
-
+/* Get all paths used for generating the config */
+const ARRAY_TYPE(config_path) *
+config_parsed_get_paths(struct config_parsed *config);
void config_parsed_free(struct config_parsed **config);
void config_parse_load_modules(bool dump_config_import);
};
enum config_dump_flags {
+ /* Write config binary cache if it's enabled. */
+ CONFIG_DUMP_FLAG_WRITE_BINARY_CACHE = 0x01,
CONFIG_DUMP_FLAG_DEDUPLICATE_KEYS = 0x08,
};
if (dump_full && exec_args != NULL) {
int temp_fd = config_dump_full(config,
CONFIG_DUMP_FULL_DEST_TEMPDIR,
- 0, &import_environment);
+ CONFIG_DUMP_FLAG_WRITE_BINARY_CACHE,
+ &import_environment);
if (getenv(DOVECOT_PRESERVE_ENVS_ENV) != NULL) {
/* Standalone binary is getting its configuration via
doveconf. Clean the environment before calling it.
#include "fdpass.h"
#include "write-full.h"
#include "str.h"
+#include "sha2.h"
+#include "hex-binary.h"
+#include "restrict-access.h"
#include "syslog-util.h"
#include "eacces-error.h"
#include "env-util.h"
}
return str_c(keyvals);
}
+
+const char *
+master_service_get_binary_config_cache_path(const char *cache_dir,
+ const char *main_path)
+{
+ struct sha256_ctx hash;
+ sha256_init(&hash);
+ sha256_loop(&hash, main_path, strlen(main_path) + 1);
+ uid_t euid = geteuid();
+ sha256_loop(&hash, &euid, sizeof(euid));
+ gid_t egid = getegid();
+ sha256_loop(&hash, &egid, sizeof(egid));
+ unsigned int groups_count;
+ const gid_t *groups = restrict_get_groups_list(&groups_count);
+ sha256_loop(&hash, groups, sizeof(*groups) * groups_count);
+ unsigned char digest[SHA256_RESULTLEN];
+ sha256_result(&hash, digest);
+
+ string_t *cache_path = t_str_new(128);
+ str_append(cache_path, cache_dir);
+ str_append(cache_path, "/binary.conf.");
+ binary_to_hex_append(cache_path, digest, sizeof(digest));
+ return str_c(cache_path);
+}
const char *
master_service_get_import_environment_keyvals(struct master_service *service);
+const char *
+master_service_get_binary_config_cache_path(const char *cache_dir,
+ const char *main_path);
+
#endif
"\x00\x00\x00\x00\x00\x00\x00\x01"), // full size
"Full size mismatch" },
- /* event filter count is truncated */
+ /* cache path count is truncated */
{ DATA("DOVECOT-CONFIG\t1.0\n"
"\x00\x00\x00\x00\x00\x00\x00\x04" // full size
+ "\x00\x00\x00"), // cache path count
+ "Full size mismatch" },
+
+ /* event filter count is truncated */
+ { DATA("DOVECOT-CONFIG\t1.0\n"
+ "\x00\x00\x00\x00\x00\x00\x00\x08" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00"), // event filter count
"Full size mismatch" },
/* event filter strings are truncated */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x04" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x08" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x10\x00"), // event filter count
"'filter string' points outside area" },
/* full file size is 7 bytes, which makes the first block size
truncated, since it needs 8 bytes */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x0D" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x11" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
"Area too small when reading size of 'block size'" },
/* first block size is 0, which is too small */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x0E" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x12" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
"'block name' points outside area" },
/* first block size is 1, but full file size is too small */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x0E" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x12" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
"'block size' points outside are" },
/* block name is not NUL-terminated */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x10" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x14" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
/* settings count is truncated */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x13" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x17" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
/* settings keys are truncated */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x14" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x18" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
/* filter count is truncated */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x19" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x1D" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
/* filter settings size is truncated */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x21" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x25" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
/* filter settings is truncated */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x22" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x26" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
"'filter settings size' points outside area" },
/* filter error is missing */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x2F" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x33" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
"'filter error string' points outside area" },
/* filter error is not NUL-terminated */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x3D" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x41" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
"'filter error string' points outside area" },
/* include group count is truncated */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x40" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x44" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
"Area too small when reading uint of 'include group count'" },
/* include group count is too large */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x41" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x45" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
"'group label string' points outside area" },
/* group label not NUL-terminated */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x42" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x46" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
"'group label string' points outside area" },
/* group name not NUL-terminated */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x44" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x48" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
"'group name string' points outside area" },
/* invalid filter string */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x36" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x3A" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"F\x00" // event filter[0]
"F\x00" // override event filter[0]
/* Duplicate block name */
{ DATA("DOVECOT-CONFIG\t1.0\n"
- "\x00\x00\x00\x00\x00\x00\x00\x3E" // full size
+ "\x00\x00\x00\x00\x00\x00\x00\x42" // full size
+ "\x00\x00\x00\x00" // cache path count
"\x00\x00\x00\x01" // event filter count
"\x00" // event filter[0]
"\x00" // override event filter[0]
#include "var-expand.h"
#include <ctype.h>
+#include <sys/stat.h>
enum set_seen_type {
/* Setting has not been changed */
event_unref(&override->filter_event);
}
+static int
+settings_block_read_uint64(struct settings_mmap *mmap,
+ size_t *offset, size_t end_offset,
+ const char *name, uint64_t *num_r,
+ const char **error_r)
+{
+ if (*offset + sizeof(*num_r) > end_offset) {
+ *error_r = t_strdup_printf(
+ "Area too small when reading uint of '%s' "
+ "(offset=%zu, end_offset=%zu, file_size=%zu)", name,
+ *offset, end_offset, mmap->mmap_size);
+ return -1;
+ }
+ *num_r = be64_to_cpu_unaligned(CONST_PTR_OFFSET(mmap->mmap_base, *offset));
+ *offset += sizeof(*num_r);
+ return 0;
+}
+
static int
settings_block_read_uint32(struct settings_mmap *mmap,
size_t *offset, size_t end_offset,
return settings_event_filter_node_name_find(node, filter_name, FALSE);
}
+static int
+settings_read_config_paths(struct settings_mmap *mmap,
+ enum settings_read_flags flags,
+ size_t *offset, const char **error_r)
+{
+ uint32_t count;
+ if (settings_block_read_uint32(mmap, offset, mmap->mmap_size,
+ "config paths count", &count,
+ error_r) < 0)
+ return -1;
+
+ for (uint32_t i = 0; i < count; i++) {
+ const char *config_path;
+ if (settings_block_read_str(mmap, offset, mmap->mmap_size,
+ "config path", &config_path,
+ error_r) < 0)
+ return -1;
+ uint64_t inode, size;
+ if (settings_block_read_uint64(mmap, offset, mmap->mmap_size,
+ "config path size",
+ &size, error_r) < 0)
+ return -1;
+ if (settings_block_read_uint64(mmap, offset, mmap->mmap_size,
+ "config path inode",
+ &inode, error_r) < 0)
+ return -1;
+ uint32_t mtime_sec, mtime_nsec, ctime_sec, ctime_nsec;
+ if (settings_block_read_uint32(mmap, offset, mmap->mmap_size,
+ "config path mtime sec",
+ &mtime_sec, error_r) < 0)
+ return -1;
+ if (settings_block_read_uint32(mmap, offset, mmap->mmap_size,
+ "config path mtime nsec",
+ &mtime_nsec, error_r) < 0)
+ return -1;
+
+ if (settings_block_read_uint32(mmap, offset, mmap->mmap_size,
+ "config path ctime sec",
+ &ctime_sec, error_r) < 0)
+ return -1;
+ if (settings_block_read_uint32(mmap, offset, mmap->mmap_size,
+ "config path ctime nsec",
+ &ctime_nsec, error_r) < 0)
+ return -1;
+
+ if ((flags & SETTINGS_READ_CHECK_CACHE_TIMESTAMPS) == 0)
+ continue;
+
+ struct stat st;
+ if (stat(config_path, &st) < 0) {
+ if (errno == ENOENT || ENOACCESS(errno))
+ return 0;
+ *error_r = t_strdup_printf("stat(%s) failed: %m",
+ config_path);
+ return -1;
+ }
+ if (st.st_ino != inode || (uoff_t)st.st_size != size ||
+ st.st_mtime != mtime_sec || ST_MTIME_NSEC(st) != mtime_nsec ||
+ st.st_ctime != ctime_sec || ST_CTIME_NSEC(st) != ctime_nsec)
+ return 0;
+ }
+ return 1;
+}
+
static int
settings_read_filters(struct settings_mmap *mmap, const char *service_name,
enum settings_read_flags flags, size_t *offset,
}
size_t offset = full_size_offset + sizeof(settings_full_size);
+ int ret;
+ if ((ret = settings_read_config_paths(mmap, flags, &offset, error_r)) <= 0)
+ return ret;
if (settings_read_filters(mmap, service_name, flags, &offset,
&protocols, error_r) < 0)
return -1;
} else {
*specific_protocols_r = NULL;
}
+ if ((flags & SETTINGS_READ_CHECK_CACHE_TIMESTAMPS) != 0)
+ return 1;
return 0;
}
root->mmap = mmap;
hash_table_create(&mmap->blocks, mmap->pool, 0, str_hash, strcmp);
- return settings_mmap_parse(root->mmap, service_name, flags,
- specific_protocols_r, error_r);
+ int ret = settings_mmap_parse(root->mmap, service_name, flags,
+ specific_protocols_r, error_r);
+ if (ret < 0 ||
+ (ret == 0 && (flags & SETTINGS_READ_CHECK_CACHE_TIMESTAMPS) != 0))
+ settings_mmap_unref(&root->mmap);
+ return ret;
}
bool settings_has_mmap(struct settings_root *root)
enum settings_read_flags {
/* Don't drop filters that contain a mismatching protocol */
SETTINGS_READ_NO_PROTOCOL_FILTER = BIT(0),
+ /* Check that all the paths referenced by the binary config still have
+ the same mtime and ctime. If not, fail the config reading.
+ Changes settings_read() return value to return 1 on success, or 0
+ if timestamps were obsolete. */
+ SETTINGS_READ_CHECK_CACHE_TIMESTAMPS = BIT(1),
};
enum settings_get_flags {