From: Yu Watanabe Date: Fri, 22 Apr 2022 03:33:15 +0000 (+0900) Subject: udev: check stats of .link files and their drop-in files X-Git-Tag: v251-rc2~24 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bdb2d3c6889408c7f26c2eeddbe9021ac53f962c;p=thirdparty%2Fsystemd.git udev: check stats of .link files and their drop-in files Fixes #23128. --- diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 291707cd93d..c0b8e3893c0 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -591,28 +591,6 @@ int network_load(Manager *manager, OrderedHashmap **networks) { return 0; } -static bool stats_by_path_equal(Hashmap *a, Hashmap *b) { - struct stat *st_a, *st_b; - const char *path; - - assert(a); - assert(b); - - if (hashmap_size(a) != hashmap_size(b)) - return false; - - HASHMAP_FOREACH_KEY(st_a, path, a) { - st_b = hashmap_get(b, path); - if (!st_b) - return false; - - if (!stat_inode_unmodified(st_a, st_b)) - return false; - } - - return true; -} - int network_reload(Manager *manager) { OrderedHashmap *new_networks = NULL; Network *n, *old; diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index 6c105e7fd27..a84c47cd26a 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -34,6 +34,7 @@ #include "set.h" #include "signal-util.h" #include "socket-util.h" +#include "stat-util.h" #include "string-util.h" #include "strv.h" #include "syslog-util.h" @@ -556,6 +557,27 @@ int config_parse_many_nulstr( ret_stats_by_path); } +static int config_get_dropin_files( + const char* const* conf_file_dirs, + const char *dropin_dirname, + char ***ret) { + + _cleanup_strv_free_ char **dropin_dirs = NULL; + const char *suffix; + int r; + + assert(conf_file_dirs); + assert(dropin_dirname); + assert(ret); + + suffix = strjoina("/", dropin_dirname); + r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix); + if (r < 0) + return r; + + return conf_files_list_strv(ret, ".conf", NULL, 0, (const char* const*) dropin_dirs); +} + /* Parse each config file in the directories specified as strv. */ int config_parse_many( const char* const* conf_files, @@ -568,21 +590,126 @@ int config_parse_many( void *userdata, Hashmap **ret_stats_by_path) { - _cleanup_strv_free_ char **dropin_dirs = NULL; _cleanup_strv_free_ char **files = NULL; - const char *suffix; int r; - suffix = strjoina("/", dropin_dirname); - r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix); + assert(conf_file_dirs); + assert(dropin_dirname); + assert(sections); + assert(table); + + r = config_get_dropin_files(conf_file_dirs, dropin_dirname, &files); + if (r < 0) + return r; + + return config_parse_many_files(conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path); +} + +static int config_get_stats_by_path_one( + const char* conf_file, + const char* const* conf_file_dirs, + Hashmap *stats_by_path) { + + _cleanup_strv_free_ char **files = NULL; + _cleanup_free_ char *dropin_dirname = NULL; + struct stat st; + int r; + + assert(conf_file); + assert(conf_file_dirs); + assert(stats_by_path); + + /* Unlike config_parse(), this does not support stream. */ + + r = path_extract_filename(conf_file, &dropin_dirname); if (r < 0) return r; + if (r == O_DIRECTORY) + return -EINVAL; + + if (!strextend(&dropin_dirname, ".d")) + return -ENOMEM; - r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs); + r = config_get_dropin_files(conf_file_dirs, dropin_dirname, &files); if (r < 0) return r; - return config_parse_many_files(conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path); + /* First read the main config file. */ + r = RET_NERRNO(stat(conf_file, &st)); + if (r >= 0) { + r = hashmap_put_stats_by_path(&stats_by_path, conf_file, &st); + if (r < 0) + return r; + } else if (r != -ENOENT) + return r; + + /* Then read all the drop-ins. */ + STRV_FOREACH(fn, files) { + if (stat(*fn, &st) < 0) { + if (errno == ENOENT) + continue; + + return -errno; + } + + r = hashmap_put_stats_by_path(&stats_by_path, *fn, &st); + if (r < 0) + return r; + } + + return 0; +} + +int config_get_stats_by_path( + const char *suffix, + const char *root, + unsigned flags, + const char* const* dirs, + Hashmap **ret) { + + _cleanup_hashmap_free_ Hashmap *stats_by_path = NULL; + _cleanup_strv_free_ char **files = NULL; + int r; + + assert(suffix); + assert(dirs); + assert(ret); + + r = conf_files_list_strv(&files, suffix, root, flags, dirs); + if (r < 0) + return r; + + stats_by_path = hashmap_new(&path_hash_ops_free_free); + if (!stats_by_path) + return -ENOMEM; + + STRV_FOREACH(f, files) { + r = config_get_stats_by_path_one(*f, dirs, stats_by_path); + if (r < 0) + return r; + } + + *ret = TAKE_PTR(stats_by_path); + return 0; +} + +bool stats_by_path_equal(Hashmap *a, Hashmap *b) { + struct stat *st_a, *st_b; + const char *path; + + if (hashmap_size(a) != hashmap_size(b)) + return false; + + HASHMAP_FOREACH_KEY(st_a, path, a) { + st_b = hashmap_get(b, path); + if (!st_b) + return false; + + if (!stat_inode_unmodified(st_a, st_b)) + return false; + } + + return true; } static void config_section_hash_func(const ConfigSection *c, struct siphash *state) { diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index 94778af4588..07b6490165a 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -114,6 +114,15 @@ int config_parse_many( void *userdata, Hashmap **ret_stats_by_path); /* possibly NULL */ +int config_get_stats_by_path( + const char *suffix, + const char *root, + unsigned flags, + const char* const* dirs, + Hashmap **ret); + +bool stats_by_path_equal(Hashmap *a, Hashmap *b); + typedef struct ConfigSection { unsigned line; bool invalid; diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 2de172a67a7..42745b6c59b 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -40,6 +40,7 @@ struct LinkConfigContext { LIST_HEAD(LinkConfig, configs); int ethtool_fd; usec_t network_dirs_ts_usec; + Hashmap *stats_by_path; }; static LinkConfig* link_config_free(LinkConfig *config) { @@ -71,6 +72,8 @@ static void link_configs_free(LinkConfigContext *ctx) { if (!ctx) return; + ctx->stats_by_path = hashmap_free(ctx->stats_by_path); + LIST_FOREACH(configs, config, ctx->configs) link_config_free(config); } @@ -207,6 +210,7 @@ static int link_adjust_wol_options(LinkConfig *config) { int link_load_one(LinkConfigContext *ctx, const char *filename) { _cleanup_(link_config_freep) LinkConfig *config = NULL; + _cleanup_hashmap_free_ Hashmap *stats_by_path = NULL; _cleanup_free_ char *name = NULL; const char *dropin_dirname; size_t i; @@ -254,16 +258,23 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) { dropin_dirname = strjoina(basename(filename), ".d"); r = config_parse_many( STRV_MAKE_CONST(filename), - (const char* const*) CONF_PATHS_STRV("systemd/network"), + NETWORK_DIRS, dropin_dirname, "Match\0" "Link\0" "SR-IOV\0", config_item_perf_lookup, link_config_gperf_lookup, - CONFIG_PARSE_WARN, config, NULL); + CONFIG_PARSE_WARN, config, &stats_by_path); if (r < 0) return r; /* config_parse_many() logs internally. */ + if (ctx->stats_by_path) { + r = hashmap_move(ctx->stats_by_path, stats_by_path); + if (r < 0) + log_warning_errno(r, "Failed to save stats of '%s' and its drop-in configs, ignoring: %m", filename); + } else + ctx->stats_by_path = TAKE_PTR(stats_by_path); + if (net_match_is_empty(&config->match) && !config->conditions) { log_warning("%s: No valid settings found in the [Match] section, ignoring file. " "To match all interfaces, add OriginalName=* in the [Match] section.", @@ -316,10 +327,9 @@ int link_config_load(LinkConfigContext *ctx) { _cleanup_strv_free_ char **files = NULL; int r; - link_configs_free(ctx); + assert(ctx); - /* update timestamp */ - paths_check_timestamp(NETWORK_DIRS, &ctx->network_dirs_ts_usec, true); + link_configs_free(ctx); r = conf_files_list_strv(&files, ".link", NULL, 0, NETWORK_DIRS); if (r < 0) @@ -332,7 +342,18 @@ int link_config_load(LinkConfigContext *ctx) { } bool link_config_should_reload(LinkConfigContext *ctx) { - return paths_check_timestamp(NETWORK_DIRS, &ctx->network_dirs_ts_usec, false); + _cleanup_hashmap_free_ Hashmap *stats_by_path = NULL; + int r; + + assert(ctx); + + r = config_get_stats_by_path(".link", NULL, 0, NETWORK_DIRS, &stats_by_path); + if (r < 0) { + log_warning_errno(r, "Failed to get stats of .link files: %m"); + return true; + } + + return !stats_by_path_equal(ctx->stats_by_path, stats_by_path); } Link *link_free(Link *link) {