From 17ade5b2af38f318a3fc9695e2b0a936b4f3523a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 20 Jan 2026 22:34:07 +0100 Subject: [PATCH 1/6] sd-id128-util: allow XAT_FDROOT pass through in id128_get_machine_at() --- src/libsystemd/sd-id128/sd-id128.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c index e08f0b6412..f2a7209a25 100644 --- a/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libsystemd/sd-id128/sd-id128.c @@ -138,7 +138,7 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) { int id128_get_machine_at(int rfd, sd_id128_t *ret) { int r; - assert(rfd >= 0 || rfd == AT_FDCWD); + assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT)); r = dir_fd_is_root_or_cwd(rfd); if (r < 0) From 6ecfa12af05ca181a12e1ecde6ba5e35e139a16c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 5 Sep 2025 14:21:53 +0200 Subject: [PATCH 2/6] conf-files: add support for root_fd based operation Let's make sure we can safely load configuration files based on a root fd, including XAT_FDROOT. (While we are at it, always pass path to root fs before fd to root fs, following our recently updated CODING_STYLE) --- src/basic/conf-files.c | 91 ++++++++++++++++----------- src/basic/conf-files.h | 3 +- src/core/main.c | 3 +- src/kernel-install/kernel-install.c | 16 ++--- src/network/netdev/netdev.c | 22 ++++--- src/network/networkctl-config-file.c | 9 ++- src/network/networkd-network.c | 11 +++- src/repart/repart.c | 5 +- src/resolve/resolved-dns-delegate.c | 5 +- src/resolve/resolved-dnssd.c | 8 +-- src/shared/conf-parser.c | 94 ++++++++++++++++++++++------ src/shared/conf-parser.h | 32 +++++++++- src/shared/kernel-config.c | 12 +++- src/shared/kernel-config.h | 16 ++++- src/sysext/sysext.c | 1 + src/sysupdate/sysupdate-feature.c | 3 +- src/sysupdate/sysupdate-transfer.c | 3 +- src/test/test-conf-parser.c | 8 ++- src/udev/net/link-config.c | 7 ++- 19 files changed, 249 insertions(+), 100 deletions(-) diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index 76ff20f07b..4d4cf27952 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -44,13 +44,20 @@ static int conf_files_log_level(ConfFilesFlags flags) { return FLAGS_SET(flags, CONF_FILES_WARN) ? LOG_WARNING : LOG_DEBUG; } -static int prepare_dirs(const char *root, ConfFilesFlags flags, char * const *dirs, int *ret_rfd, char **ret_root, char ***ret_dirs) { +static int prepare_dirs( + const char *root, + ConfFilesFlags flags, + char * const *dirs, + char **ret_root, + int *ret_rfd, + char ***ret_dirs) { + _cleanup_free_ char *root_abs = NULL; _cleanup_strv_free_ char **dirs_abs = NULL; int r; - assert(ret_rfd); assert(ret_root); + assert(ret_rfd); assert(ret_dirs || strv_isempty(dirs)); int log_level = conf_files_log_level(flags); @@ -65,6 +72,7 @@ static int prepare_dirs(const char *root, ConfFilesFlags flags, char * const *di return log_oom_full(log_level); } + _cleanup_close_ int rfd = XAT_FDROOT; if (root) { /* When a non-trivial root is specified, we will prefix the result later. Hence, it is not * necessary to modify each config directories here. but needs to normalize the root directory. */ @@ -73,6 +81,11 @@ static int prepare_dirs(const char *root, ConfFilesFlags flags, char * const *di return log_full_errno(log_level, r, "Failed to make '%s' absolute: %m", root); path_simplify(root_abs); + + rfd = open(root, O_CLOEXEC|O_DIRECTORY|O_PATH); + if (rfd < 0) + return log_full_errno(log_level, errno, "Failed to open '%s': %m", root_abs); + } else if (ret_dirs) { /* When an empty root or "/" is specified, we will open "/" below, hence we need to make * each config directory absolute if relative. */ @@ -81,12 +94,8 @@ static int prepare_dirs(const char *root, ConfFilesFlags flags, char * const *di return log_full_errno(log_level, r, "Failed to make directories absolute: %m"); } - _cleanup_close_ int rfd = open(empty_to_root(root_abs), O_CLOEXEC|O_DIRECTORY|O_PATH); - if (rfd < 0) - return log_full_errno(log_level, errno, "Failed to open '%s': %m", empty_to_root(root_abs)); - - *ret_rfd = TAKE_FD(rfd); *ret_root = TAKE_PTR(root_abs); + *ret_rfd = TAKE_FD(rfd); if (ret_dirs) *ret_dirs = TAKE_PTR(dirs_abs); return 0; @@ -135,8 +144,8 @@ static ChaseFlags conf_files_chase_flags(ConfFilesFlags flags) { } static int conf_file_chase_and_verify( - int rfd, const char *root, /* for logging, can be NULL */ + int rfd, const char *original_path, /* for logging */ const char *path, const char *name, @@ -151,7 +160,7 @@ static int conf_file_chase_and_verify( struct stat st = {}; int r; - assert(rfd >= 0 || rfd == AT_FDCWD); + assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT)); assert(original_path); assert(path); assert(name); @@ -261,18 +270,25 @@ static int conf_file_chase_and_verify( return 0; } -int conf_file_new_at(const char *path, int rfd, ConfFilesFlags flags, ConfFile **ret) { +int conf_file_new_at( + const char *path, + const char *root, + int rfd, + ConfFilesFlags flags, + ConfFile **ret) { int r; assert(path); - assert(rfd >= 0 || rfd == AT_FDCWD); + assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT)); assert(ret); int log_level = conf_files_log_level(flags); - _cleanup_free_ char *root = NULL; - if (rfd >= 0) - (void) fd_get_path(rfd, &root); + _cleanup_free_ char *_root = NULL; + if (DEBUG_LOGGING && !root) { + (void) fd_get_path(rfd, &_root); + root = _root; + } _cleanup_(conf_file_freep) ConfFile *c = new(ConfFile, 1); if (!c) @@ -307,8 +323,8 @@ int conf_file_new_at(const char *path, int rfd, ConfFilesFlags flags, ConfFile * return log_oom_full(log_level); r = conf_file_chase_and_verify( - rfd, root, + rfd, c->original_path, c->result, c->name, @@ -332,7 +348,7 @@ int conf_file_new(const char *path, const char *root, ConfFilesFlags flags, Conf _cleanup_free_ char *root_abs = NULL; _cleanup_close_ int rfd = -EBADF; - r = prepare_dirs(root, flags, /* dirs= */ NULL, &rfd, &root_abs, /* ret_dirs= */ NULL); + r = prepare_dirs(root, flags, /* dirs= */ NULL, &root_abs, &rfd, /* ret_dirs= */ NULL); if (r < 0) return r; @@ -346,7 +362,7 @@ int conf_file_new(const char *path, const char *root, ConfFilesFlags flags, Conf } _cleanup_(conf_file_freep) ConfFile *c = NULL; - r = conf_file_new_at(path, rfd, flags, &c); + r = conf_file_new_at(path, root_abs, rfd, flags, &c); if (r < 0) return r; @@ -367,8 +383,8 @@ static int files_add( DIR *dir, const char *original_dirpath, const char *resolved_dirpath, - int rfd, const char *root, /* for logging, can be NULL */ + int rfd, Hashmap **files, Set **masked, const char *suffix, @@ -379,7 +395,7 @@ static int files_add( assert(dir); assert(original_dirpath); assert(resolved_dirpath); - assert(rfd >= 0 || rfd == AT_FDCWD); + assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT)); assert(files); assert(masked); @@ -420,8 +436,8 @@ static int files_add( _cleanup_close_ int fd = -EBADF; struct stat st; r = conf_file_chase_and_verify( - rfd, root, + rfd, original_path, p, de->d_name, @@ -597,8 +613,8 @@ static int insert_replacement(Hashmap **fh, ConfFile *replacement, ConfFilesFlag static int conf_files_list_impl( const char *suffix, - int rfd, const char *root, /* for logging, can be NULL */ + int rfd, ConfFilesFlags flags, const char * const *dirs, const char *replacement, @@ -611,13 +627,13 @@ static int conf_files_list_impl( const ConfFile *inserted = NULL; int r; - assert(rfd >= 0 || rfd == AT_FDCWD); + assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT)); assert(ret); root = empty_to_root(root); if (replacement) { - r = conf_file_new_at(replacement, rfd, flags & CONF_FILES_WARN, &c); + r = conf_file_new_at(replacement, root, rfd, flags & CONF_FILES_WARN, &c); if (r < 0) return r; } @@ -641,7 +657,7 @@ static int conf_files_list_impl( return r; } - r = files_add(dir, *p, path, rfd, root, &fh, &masked, suffix, flags); + r = files_add(dir, *p, path, root, rfd, &fh, &masked, suffix, flags); if (r == -ENOMEM) return r; } @@ -673,11 +689,11 @@ int conf_files_list_strv( assert(ret); - r = prepare_dirs(root, flags, (char**) dirs, &rfd, &root_abs, &dirs_abs); + r = prepare_dirs(root, flags, (char**) dirs, &root_abs, &rfd, &dirs_abs); if (r < 0) return r; - r = conf_files_list_impl(suffix, rfd, root_abs, flags, (const char * const *) dirs_abs, + r = conf_files_list_impl(suffix, root_abs, rfd, flags, (const char * const *) dirs_abs, /* replacement= */ NULL, &fh, /* ret_inserted= */ NULL); if (r < 0) return r; @@ -702,11 +718,11 @@ int conf_files_list_strv_full( assert(ret_files); assert(ret_n_files); - r = prepare_dirs(root, flags, (char**) dirs, &rfd, &root_abs, &dirs_abs); + r = prepare_dirs(root, flags, (char**) dirs, &root_abs, &rfd, &dirs_abs); if (r < 0) return r; - r = conf_files_list_impl(suffix, rfd, root_abs, flags, (const char * const *) dirs_abs, + r = conf_files_list_impl(suffix, root_abs, rfd, flags, (const char * const *) dirs_abs, /* replacement= */ NULL, &fh, /* ret_inserted= */ NULL); if (r < 0) return r; @@ -725,13 +741,13 @@ int conf_files_list_strv_at( _cleanup_free_ char *root = NULL; int r; - assert(rfd >= 0 || rfd == AT_FDCWD); + assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT)); assert(ret); - if (rfd >= 0) + if (DEBUG_LOGGING) (void) fd_get_path(rfd, &root); /* for logging */ - r = conf_files_list_impl(suffix, rfd, root, flags, dirs, /* replacement= */ NULL, &fh, /* ret_inserted= */ NULL); + r = conf_files_list_impl(suffix, root, rfd, flags, dirs, /* replacement= */ NULL, &fh, /* ret_inserted= */ NULL); if (r < 0) return r; @@ -750,14 +766,14 @@ int conf_files_list_strv_at_full( _cleanup_free_ char *root = NULL; int r; - assert(rfd >= 0 || rfd == AT_FDCWD); + assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT)); assert(ret_files); assert(ret_n_files); - if (rfd >= 0) + if (DEBUG_LOGGING) (void) fd_get_path(rfd, &root); /* for logging */ - r = conf_files_list_impl(suffix, rfd, root, flags, dirs, /* replacement= */ NULL, &fh, /* ret_inserted= */ NULL); + r = conf_files_list_impl(suffix, root, rfd, flags, dirs, /* replacement= */ NULL, &fh, /* ret_inserted= */ NULL); if (r < 0) return r; @@ -848,11 +864,11 @@ int conf_files_list_with_replacement( assert(ret_files); - r = prepare_dirs(root, flags, config_dirs, &rfd, &root_abs, &dirs_abs); + r = prepare_dirs(root, flags, config_dirs, &root_abs, &rfd, &dirs_abs); if (r < 0) return r; - r = conf_files_list_impl(".conf", rfd, root_abs, flags, (const char * const *) dirs_abs, + r = conf_files_list_impl(".conf", root_abs, rfd, flags, (const char * const *) dirs_abs, replacement, &fh, ret_inserted ? &c : NULL); if (r < 0) return r; @@ -878,6 +894,7 @@ int conf_files_list_dropins( char ***ret, const char *dropin_dirname, const char *root, + int root_fd, ConfFilesFlags flags, const char * const *dirs) { @@ -894,7 +911,7 @@ int conf_files_list_dropins( if (r < 0) return log_oom_full(conf_files_log_level(flags)); - return conf_files_list_strv(ret, ".conf", root, flags, (const char* const*) dropin_dirs); + return conf_files_list_strv_at(ret, ".conf", root_fd, flags, (const char* const*) dropin_dirs); } /** diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h index 90e5b1bec5..444627b8d0 100644 --- a/src/basic/conf-files.h +++ b/src/basic/conf-files.h @@ -30,7 +30,7 @@ ConfFile* conf_file_free(ConfFile *c); DEFINE_TRIVIAL_CLEANUP_FUNC(ConfFile*, conf_file_free); void conf_file_free_many(ConfFile **array, size_t n); -int conf_file_new_at(const char *path, int rfd, ConfFilesFlags flags, ConfFile **ret); +int conf_file_new_at(const char *path, const char *root, int rfd, ConfFilesFlags flags, ConfFile **ret); int conf_file_new(const char *path, const char *root, ConfFilesFlags flags, ConfFile **ret); int conf_files_list(char ***ret, const char *suffix, const char *root, ConfFilesFlags flags, const char *dir); @@ -57,6 +57,7 @@ int conf_files_list_dropins( char ***ret, const char *dropin_dirname, const char *root, + int root_fd, ConfFilesFlags flags, const char * const *dirs); diff --git a/src/core/main.c b/src/core/main.c index 687b397e62..3a6284b456 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -842,11 +842,10 @@ static int parse_config_file(void) { (const char* const*) files, (const char* const*) dirs, "user.conf.d", - /* root= */ NULL, "Manager\0", config_item_table_lookup, items, CONFIG_PARSE_WARN, - NULL, NULL, NULL); + /* userdata= */ NULL); } /* Traditionally "0" was used to turn off the default unit timeouts. Fix this up so that we use diff --git a/src/kernel-install/kernel-install.c b/src/kernel-install/kernel-install.c index b80f8d50ee..b79e830844 100644 --- a/src/kernel-install/kernel-install.c +++ b/src/kernel-install/kernel-install.c @@ -460,13 +460,15 @@ static int context_load_install_conf(Context *c) { assert(c); - r = load_kernel_install_conf(arg_root, - c->conf_root, - &machine_id, - &boot_root, - &layout, - &initrd_generator, - &uki_generator); + r = load_kernel_install_conf_at( + c->conf_root ? NULL : arg_root, + c->conf_root ? XAT_FDROOT : c->rfd, + c->conf_root, + &machine_id, + &boot_root, + &layout, + &initrd_generator, + &uki_generator); if (r <= 0) return r; diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index be35c59fd9..393114aa7b 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -1031,13 +1031,14 @@ int netdev_load_one(Manager *manager, const char *filename, NetDev **ret) { dropin_dirname = strjoina(file_basename, ".d"); r = config_parse_many( - STRV_MAKE_CONST(filename), NETWORK_DIRS, dropin_dirname, /* root= */ NULL, + STRV_MAKE_CONST(filename), + NETWORK_DIRS, + dropin_dirname, NETDEV_COMMON_SECTIONS NETDEV_OTHER_SECTIONS, - config_item_perf_lookup, network_netdev_gperf_lookup, + config_item_perf_lookup, + network_netdev_gperf_lookup, CONFIG_PARSE_WARN, - netdev_raw, - NULL, - NULL); + netdev_raw); if (r < 0) return r; /* config_parse_many() logs internally. */ @@ -1064,10 +1065,15 @@ int netdev_load_one(Manager *manager, const char *filename, NetDev **ret) { if (NETDEV_VTABLE(netdev)->init) NETDEV_VTABLE(netdev)->init(netdev); - r = config_parse_many( - STRV_MAKE_CONST(filename), NETWORK_DIRS, dropin_dirname, /* root= */ NULL, + r = config_parse_many_full( + STRV_MAKE_CONST(filename), + NETWORK_DIRS, + dropin_dirname, + /* root= */ NULL, + /* root_fd= */ -EBADF, NETDEV_VTABLE(netdev)->sections, - config_item_perf_lookup, network_netdev_gperf_lookup, + config_item_perf_lookup, + network_netdev_gperf_lookup, CONFIG_PARSE_WARN, netdev, &netdev->stats_by_path, diff --git a/src/network/networkctl-config-file.c b/src/network/networkctl-config-file.c index 36c6c0e8e1..9f0236b540 100644 --- a/src/network/networkctl-config-file.c +++ b/src/network/networkctl-config-file.c @@ -17,6 +17,7 @@ #include "edit-util.h" #include "errno-util.h" #include "extract-word.h" +#include "fd-util.h" #include "log.h" #include "mkdir-label.h" #include "netlink-util.h" @@ -106,7 +107,13 @@ static int get_config_files_by_name( if (!dropin_dirname) return -ENOMEM; - r = conf_files_list_dropins(ret_dropins, dropin_dirname, /* root= */ NULL, CONF_FILES_WARN, NETWORK_DIRS); + r = conf_files_list_dropins( + ret_dropins, + dropin_dirname, + /* root= */ NULL, + /* root_fd= */ XAT_FDROOT, + CONF_FILES_WARN, + NETWORK_DIRS); if (r < 0) return r; } diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 4d1ed4ccca..45456d677d 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -514,8 +514,12 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .ipoib_umcast = -1, }; - r = config_parse_many( - STRV_MAKE_CONST(filename), NETWORK_DIRS, dropin_dirname, /* root= */ NULL, + r = config_parse_many_full( + STRV_MAKE_CONST(filename), + NETWORK_DIRS, + dropin_dirname, + /* root= */ NULL, + /* root_fd= */ -EBADF, "Match\0" "Link\0" "SR-IOV\0" @@ -574,7 +578,8 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi "StochasticFairnessQueueing\0" "TokenBucketFilter\0" "TrivialLinkEqualizer\0", - config_item_perf_lookup, network_network_gperf_lookup, + config_item_perf_lookup, + network_network_gperf_lookup, CONFIG_PARSE_WARN, network, &network->stats_by_path, diff --git a/src/repart/repart.c b/src/repart/repart.c index 100e03aab5..97cc60c95b 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -2871,16 +2871,17 @@ static int partition_read_definition( dropin_dirname = strjoina(filename, ".d"); - r = config_parse_many( + r = config_parse_many_full( STRV_MAKE_CONST(path), conf_file_dirs, dropin_dirname, c->definitions ? NULL : arg_root, + /* root_fd= */ -EBADF, "Partition\0", config_item_table_lookup, table, CONFIG_PARSE_WARN, p, - NULL, + /* ret_stats_by_path= */ NULL, &p->drop_in_files); if (r < 0) return r; diff --git a/src/resolve/resolved-dns-delegate.c b/src/resolve/resolved-dns-delegate.c index b866a5838d..eee2daab94 100644 --- a/src/resolve/resolved-dns-delegate.c +++ b/src/resolve/resolved-dns-delegate.c @@ -188,14 +188,11 @@ static int dns_delegate_load(Manager *m, const char *path) { STRV_MAKE_CONST(path), DNS_DELEGATE_SEARCH_DIRS, dropin_dirname, - /* root= */ NULL, "Delegate\0", config_item_perf_lookup, resolved_dns_delegate_gperf_lookup, /* flags= */ 0, - d, - /* ret_stats_by_path= */ NULL, - /* ret_drop_in_files= */ NULL); + d); if (r < 0) return r; diff --git a/src/resolve/resolved-dnssd.c b/src/resolve/resolved-dnssd.c index 977f786913..6cc0f86796 100644 --- a/src/resolve/resolved-dnssd.c +++ b/src/resolve/resolved-dnssd.c @@ -125,13 +125,13 @@ static int dnssd_registered_service_load(Manager *manager, const char *path) { return log_oom(); r = config_parse_many( - STRV_MAKE_CONST(path), DNSSD_SERVICE_DIRS, dropin_dirname, /* root= */ NULL, + STRV_MAKE_CONST(path), + DNSSD_SERVICE_DIRS, + dropin_dirname, "Service\0", config_item_perf_lookup, resolved_dnssd_gperf_lookup, CONFIG_PARSE_WARN, - service, - NULL, - NULL); + service); if (r < 0) return r; diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index e5e1855892..1d1d8a10cc 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -481,6 +481,7 @@ int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const s static int config_parse_many_files( const char *root, + int root_fd, const char* const* conf_files, char **files, const char *sections, @@ -502,17 +503,17 @@ static int config_parse_many_files( return log_oom_full(level); } + /* Pin and stat() all dropins */ STRV_FOREACH(fn, files) { _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *fname = NULL; - - r = chase_and_fopen_unlocked(*fn, root, CHASE_AT_RESOLVE_IN_ROOT, "re", &fname, &f); + r = chase_and_fopenat_unlocked(root_fd, *fn, CHASE_AT_RESOLVE_IN_ROOT|CHASE_MUST_BE_REGULAR, "re", /* ret_path= */ NULL, &f); if (r == -ENOENT) continue; if (r < 0) return log_full_errno(level, r, "Failed to open %s: %m", *fn); int fd = fileno(f); + assert(fd >= 0); r = ordered_hashmap_ensure_put(&dropins, &config_file_hash_ops_fclose, *fn, f); if (r < 0) { @@ -537,11 +538,10 @@ static int config_parse_many_files( return log_oom_full(level); } - /* First read the first found main config file. */ + /* First process the first found main config file. */ STRV_FOREACH(fn, conf_files) { _cleanup_fclose_ FILE *f = NULL; - - r = chase_and_fopen_unlocked(*fn, root, CHASE_AT_RESOLVE_IN_ROOT, "re", NULL, &f); + r = chase_and_fopenat_unlocked(root_fd, *fn, CHASE_AT_RESOLVE_IN_ROOT|CHASE_MUST_BE_REGULAR, "re", /* ret_path= */ NULL, &f); if (r == -ENOENT) continue; if (r < 0) @@ -572,7 +572,6 @@ static int config_parse_many_files( } /* Then read all the drop-ins. */ - const char *path_dropin; FILE *f_dropin; ORDERED_HASHMAP_FOREACH_KEY(f_dropin, path_dropin, dropins) { @@ -594,12 +593,39 @@ static int config_parse_many_files( return 0; } +static int normalize_root_fd(const char *root, int *root_fd, int *ret_opened_fd) { + assert(root_fd); + assert(ret_opened_fd); + + /* Normalizes a root dir specification: if root_fd is already valid, keep it. Otherwise, we open the + * specified dir */ + + if (*root_fd >= 0 || IN_SET(*root_fd, AT_FDCWD, XAT_FDROOT)) { + *ret_opened_fd = -EBADF; + return 0; + } + + if (empty_or_root(root)) { + *root_fd = XAT_FDROOT; + *ret_opened_fd = -EBADF; + return 0; + } + + int fd = open(root, O_CLOEXEC|O_PATH|O_DIRECTORY); + if (fd < 0) + return log_error_errno(errno, "Failed to open root directory '%s': %m", root); + + *ret_opened_fd = *root_fd = fd; + return 0; +} + /* Parse each config file in the directories specified as strv. */ -int config_parse_many( +int config_parse_many_full( const char* const* conf_files, const char* const* conf_file_dirs, const char *dropin_dirname, const char *root, + int root_fd, const char *sections, ConfigItemLookup lookup, const void *table, @@ -615,14 +641,33 @@ int config_parse_many( assert(dropin_dirname); assert(table); - r = conf_files_list_dropins(&files, dropin_dirname, root, - FLAGS_SET(flags, CONFIG_PARSE_WARN) ? CONF_FILES_WARN : 0, - conf_file_dirs); + _cleanup_close_ int opened_root_fd = -EBADF; + r = normalize_root_fd(root, &root_fd, &opened_root_fd); + if (r < 0) + return r; + + r = conf_files_list_dropins( + &files, + dropin_dirname, + root, + root_fd, + FLAGS_SET(flags, CONFIG_PARSE_WARN) ? CONF_FILES_WARN : 0, + conf_file_dirs); if (r < 0) return log_full_errno(FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG, r, "Failed to list drop-ins in %s: %m", dropin_dirname); - r = config_parse_many_files(root, conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path); + r = config_parse_many_files( + root, + root_fd, + conf_files, + files, + sections, + lookup, + table, + flags, + userdata, + ret_stats_by_path); if (r < 0) return r; /* config_parse_many_files() logs internally. */ @@ -634,6 +679,7 @@ int config_parse_many( int config_parse_standard_file_with_dropins_full( const char *root, + int root_fd, const char *main_file, /* A path like "systemd/frobnicator.conf" */ const char *sections, ConfigItemLookup lookup, @@ -643,12 +689,15 @@ int config_parse_standard_file_with_dropins_full( Hashmap **ret_stats_by_path, char ***ret_dropin_files) { - const char* const *conf_paths = (const char* const*) CONF_PATHS_STRV(""); - _cleanup_strv_free_ char **configs = NULL; + const char* const *conf_file_dirs = (const char* const*) CONF_PATHS_STRV(""); + _cleanup_strv_free_ char **conf_files = NULL; int r, level = FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG; /* Build the list of main config files */ - r = strv_extend_strv_biconcat(&configs, root, conf_paths, main_file); + r = strv_extend_strv_concat( + &conf_files, + conf_file_dirs, + main_file); if (r < 0) return log_oom_full(level); @@ -656,11 +705,12 @@ int config_parse_standard_file_with_dropins_full( if (!dropin_dirname) return log_oom_full(level); - return config_parse_many( - (const char* const*) configs, - conf_paths, + return config_parse_many_full( + (const char* const*) conf_files, + conf_file_dirs, dropin_dirname, root, + root_fd, sections, lookup, table, @@ -692,7 +742,13 @@ static int dropins_get_stats_by_path( if (!strextend(&dropin_dirname, ".d")) return -ENOMEM; - r = conf_files_list_dropins(&files, dropin_dirname, /* root= */ NULL, /* flags= */ 0, conf_file_dirs); + r = conf_files_list_dropins( + &files, + dropin_dirname, + /* root= */ NULL, + /* root_fd= */ XAT_FDROOT, + /* flags= */ 0, + conf_file_dirs); if (r < 0) return r; diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index b394131b64..0d1324b67d 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -5,7 +5,7 @@ #include "alloc-util.h" #include "conf-parser-forward.h" -#include "shared-forward.h" +#include "fd-util.h" #include "log.h" /* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */ @@ -66,11 +66,12 @@ int config_parse( void *userdata, struct stat *ret_stat); /* possibly NULL */ -int config_parse_many( +int config_parse_many_full( const char* const* conf_files, /* possibly empty */ const char* const* conf_file_dirs, const char *dropin_dirname, const char *root, + int root_fd, const char *sections, /* nulstr */ ConfigItemLookup lookup, const void *table, @@ -79,8 +80,34 @@ int config_parse_many( Hashmap **ret_stats_by_path, /* possibly NULL */ char ***ret_dropin_files); /* possibly NULL */ +static inline int config_parse_many( + const char* const* conf_files, /* possibly empty */ + const char* const* conf_file_dirs, + const char *dropin_dirname, + const char *sections, /* nulstr */ + ConfigItemLookup lookup, + const void *table, + ConfigParseFlags flags, + void *userdata) { + + return config_parse_many_full( + conf_files, + conf_file_dirs, + dropin_dirname, + /* root= */ NULL, + /* root_fd= */ XAT_FDROOT, + sections, + lookup, + table, + flags, + userdata, + /* ret_stats_by_path= */ NULL, + /* ret_drop_in_files= */ NULL); +} + int config_parse_standard_file_with_dropins_full( const char *root, + int root_fd, const char *main_file, /* A path like "systemd/frobnicator.conf" */ const char *sections, ConfigItemLookup lookup, @@ -99,6 +126,7 @@ static inline int config_parse_standard_file_with_dropins( void *userdata) { return config_parse_standard_file_with_dropins_full( /* root= */ NULL, + /* root_fd= */ XAT_FDROOT, main_file, sections, lookup, diff --git a/src/shared/kernel-config.c b/src/shared/kernel-config.c index 9bc984e919..415730bf1a 100644 --- a/src/shared/kernel-config.c +++ b/src/shared/kernel-config.c @@ -2,11 +2,13 @@ #include "alloc-util.h" #include "conf-parser.h" +#include "fd-util.h" #include "kernel-config.h" #include "path-util.h" -int load_kernel_install_conf( +int load_kernel_install_conf_at( const char *root, + int root_fd, const char *conf_root, char **ret_machine_id, char **ret_boot_root, @@ -26,16 +28,19 @@ int load_kernel_install_conf( }; int r; + assert(root_fd >= 0 || IN_SET(root_fd, AT_FDCWD, XAT_FDROOT)); + if (conf_root) { _cleanup_free_ char *conf = path_join(conf_root, "install.conf"); if (!conf) return log_oom(); - r = config_parse_many( + r = config_parse_many_full( STRV_MAKE_CONST(conf), STRV_MAKE_CONST(conf_root), "install.conf.d", - /* root= */ NULL, /* $KERNEL_INSTALL_CONF_ROOT and --root are independent */ + root, + root_fd, /* sections= */ NULL, config_item_table_lookup, items, CONFIG_PARSE_WARN, @@ -45,6 +50,7 @@ int load_kernel_install_conf( } else r = config_parse_standard_file_with_dropins_full( root, + root_fd, "kernel/install.conf", /* sections= */ NULL, config_item_table_lookup, items, diff --git a/src/shared/kernel-config.h b/src/shared/kernel-config.h index 5d0f00c929..eab3fe4641 100644 --- a/src/shared/kernel-config.h +++ b/src/shared/kernel-config.h @@ -1,13 +1,27 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include "fd-util.h" #include "shared-forward.h" -int load_kernel_install_conf( +int load_kernel_install_conf_at( const char *root, + int root_fd, const char *conf_root, char **ret_machine_id, char **ret_boot_root, char **ret_layout, char **ret_initrd_generator, char **ret_uki_generator); + +static inline int load_kernel_install_conf( + const char *root, + const char *conf_root, + char **ret_machine_id, + char **ret_boot_root, + char **ret_layout, + char **ret_initrd_generator, + char **ret_uki_generator) { + + return load_kernel_install_conf_at(root, XAT_FDROOT, conf_root, ret_machine_id, ret_boot_root, ret_layout, ret_initrd_generator, ret_uki_generator); +} diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 072181ea46..340d762b13 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -191,6 +191,7 @@ static int parse_config_file(ImageClass image_class) { r = config_parse_standard_file_with_dropins_full( arg_root, + /* root_fd= */ -EBADF, config_file, sections, config_item_table_lookup, items, diff --git a/src/sysupdate/sysupdate-feature.c b/src/sysupdate/sysupdate-feature.c index 2771b58a8a..795d4ef90a 100644 --- a/src/sysupdate/sysupdate-feature.c +++ b/src/sysupdate/sysupdate-feature.c @@ -101,11 +101,12 @@ int feature_read_definition(Feature *f, const char *path, const char *const *dir if (r < 0) return log_error_errno(r, "Failed to extract filename from path '%s': %m", path); - r = config_parse_many( + r = config_parse_many_full( STRV_MAKE_CONST(path), dirs, strjoina(filename, ".d"), arg_root, + /* root_fd= */ -EBADF, "Feature\0", config_item_table_lookup, table, CONFIG_PARSE_WARN, diff --git a/src/sysupdate/sysupdate-transfer.c b/src/sysupdate/sysupdate-transfer.c index d6009d5e4c..51ae49f407 100644 --- a/src/sysupdate/sysupdate-transfer.c +++ b/src/sysupdate/sysupdate-transfer.c @@ -543,11 +543,12 @@ int transfer_read_definition(Transfer *t, const char *path, const char **dirs, H if (r < 0) return log_error_errno(r, "Failed to extract filename from path '%s': %m", path); - r = config_parse_many( + r = config_parse_many_full( STRV_MAKE_CONST(path), dirs, strjoina(filename, ".d"), arg_root, + /* root_fd= */ -EBADF, "Transfer\0" "Source\0" "Target\0", diff --git a/src/test/test-conf-parser.c b/src/test/test-conf-parser.c index e2f9f28792..5cae59ce71 100644 --- a/src/test/test-conf-parser.c +++ b/src/test/test-conf-parser.c @@ -444,7 +444,9 @@ TEST(config_parse_standard_file_with_dropins_full) { }; r = config_parse_standard_file_with_dropins_full( - root, "kernel/install.conf", + root, + /* root_fd= */ -EBADF, + "kernel/install.conf", /* sections= */ NULL, config_item_table_lookup, items, CONFIG_PARSE_WARN, @@ -483,7 +485,9 @@ TEST(config_parse_standard_file_with_dropins_full) { assert_se(symlinkat("/etc/kernel/install.conf.d/drop4.conf", rfd, "etc/kernel/install2.conf.d/drop4.conf") == 0); r = config_parse_standard_file_with_dropins_full( - root, "kernel/install2.conf", + root, + /* root_fd= */ -EBADF, + "kernel/install2.conf", /* sections= */ NULL, config_item_table_lookup, items, CONFIG_PARSE_WARN, diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 378e909b9b..eefa95dc5f 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -280,17 +280,20 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) { return log_error_errno(r, "Failed to extract file name of '%s': %m", filename); dropin_dirname = strjoina(file_basename, ".d"); - r = config_parse_many( + r = config_parse_many_full( STRV_MAKE_CONST(filename), NETWORK_DIRS, dropin_dirname, /* root= */ NULL, + /* root_fd= */ -EBADF, "Match\0" "Link\0" "SR-IOV\0" "EnergyEfficientEthernet\0", config_item_perf_lookup, link_config_gperf_lookup, - CONFIG_PARSE_WARN, config, &stats_by_path, + CONFIG_PARSE_WARN, + config, + &stats_by_path, &config->dropins); if (r < 0) return r; /* config_parse_many() logs internally. */ From 64ef947022d3772765139b1d22a224ceca963caa Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 23 Jan 2026 11:24:07 +0100 Subject: [PATCH 3/6] os-util: support XAT_FDROOT everywhere --- src/basic/os-util.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/basic/os-util.c b/src/basic/os-util.c index c04f86893c..66bab1bcee 100644 --- a/src/basic/os-util.c +++ b/src/basic/os-util.c @@ -168,7 +168,7 @@ int open_os_release_at(int rfd, char **ret_path, int *ret_fd) { const char *e; int r; - assert(rfd >= 0 || rfd == AT_FDCWD); + assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT)); e = secure_getenv("SYSTEMD_OS_RELEASE"); if (e) @@ -184,13 +184,15 @@ int open_os_release_at(int rfd, char **ret_path, int *ret_fd) { } int open_os_release(const char *root, char **ret_path, int *ret_fd) { - _cleanup_close_ int rfd = -EBADF, fd = -EBADF; + _cleanup_close_ int rfd = XAT_FDROOT, fd = -EBADF; _cleanup_free_ char *p = NULL; int r; - rfd = open(empty_to_root(root), O_CLOEXEC | O_DIRECTORY | O_PATH); - if (rfd < 0) - return -errno; + if (!empty_or_root(root)) { + rfd = open(root, O_CLOEXEC | O_DIRECTORY | O_PATH); + if (rfd < 0) + return -errno; + } r = open_os_release_at(rfd, ret_path ? &p : NULL, ret_fd ? &fd : NULL); if (r < 0) @@ -223,7 +225,7 @@ int open_extension_release_at( const char *p; int r; - assert(rfd >= 0 || rfd == AT_FDCWD); + assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT)); assert(!extension || (image_class >= 0 && image_class < _IMAGE_CLASS_MAX)); if (!extension) @@ -330,13 +332,15 @@ int open_extension_release( char **ret_path, int *ret_fd) { - _cleanup_close_ int rfd = -EBADF, fd = -EBADF; + _cleanup_close_ int rfd = XAT_FDROOT, fd = -EBADF; _cleanup_free_ char *p = NULL; int r; - rfd = open(empty_to_root(root), O_CLOEXEC | O_DIRECTORY | O_PATH); - if (rfd < 0) - return -errno; + if (!empty_or_root(root)) { + rfd = open(root, O_CLOEXEC | O_DIRECTORY | O_PATH); + if (rfd < 0) + return -errno; + } r = open_extension_release_at(rfd, image_class, extension, relax_extension_release_check, ret_path ? &p : NULL, ret_fd ? &fd : NULL); @@ -366,7 +370,7 @@ static int parse_extension_release_atv( _cleanup_free_ char *p = NULL; int r; - assert(rfd >= 0 || rfd == AT_FDCWD); + assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT)); r = open_extension_release_at(rfd, image_class, extension, relax_extension_release_check, &p, &fd); if (r < 0) @@ -385,7 +389,7 @@ int parse_extension_release_at_sentinel( va_list ap; int r; - assert(rfd >= 0 || rfd == AT_FDCWD); + assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT)); va_start(ap, extension); r = parse_extension_release_atv(rfd, image_class, extension, relax_extension_release_check, ap); @@ -400,17 +404,20 @@ int parse_extension_release_sentinel( const char *extension, ...) { - _cleanup_close_ int rfd = -EBADF; + _cleanup_close_ int rfd = XAT_FDROOT; va_list ap; int r; - rfd = open(empty_to_root(root), O_CLOEXEC | O_DIRECTORY | O_PATH); - if (rfd < 0) - return -errno; + if (!empty_or_root(root)) { + rfd = open(root, O_CLOEXEC | O_DIRECTORY | O_PATH); + if (rfd < 0) + return -errno; + } va_start(ap, extension); r = parse_extension_release_atv(rfd, image_class, extension, relax_extension_release_check, ap); va_end(ap); + return r; } From 9b116a0871a2d1a16c6d458be1a6bc601f92177c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Sep 2025 10:39:49 +0200 Subject: [PATCH 4/6] boot-entry: plug through XAT_FDROOT --- src/shared/boot-entry.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/shared/boot-entry.c b/src/shared/boot-entry.c index 3288d0de59..2885d3f92e 100644 --- a/src/shared/boot-entry.c +++ b/src/shared/boot-entry.c @@ -22,7 +22,7 @@ static int entry_token_load_one(int rfd, const char *dir, BootEntryTokenType *ty _cleanup_fclose_ FILE *f = NULL; int r; - assert(rfd >= 0 || rfd == AT_FDCWD); + assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT)); assert(dir); assert(type); assert(*type == BOOT_ENTRY_TOKEN_AUTO); @@ -58,7 +58,7 @@ static int entry_token_load_one(int rfd, const char *dir, BootEntryTokenType *ty static int entry_token_load(int rfd, const char *conf_root, BootEntryTokenType *type, char **token) { int r; - assert(rfd >= 0 || rfd == AT_FDCWD); + assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT)); assert(type); assert(*type == BOOT_ENTRY_TOKEN_AUTO); assert(token); @@ -98,7 +98,7 @@ static int entry_token_from_os_release(int rfd, BootEntryTokenType *type, char * _cleanup_free_ char *id = NULL, *image_id = NULL; int r; - assert(rfd >= 0 || rfd == AT_FDCWD); + assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT)); assert(type); assert(IN_SET(*type, BOOT_ENTRY_TOKEN_AUTO, BOOT_ENTRY_TOKEN_OS_IMAGE_ID, BOOT_ENTRY_TOKEN_OS_ID)); assert(token); @@ -151,7 +151,7 @@ int boot_entry_token_ensure_at( int r; - assert(rfd >= 0 || rfd == AT_FDCWD); + assert(rfd >= 0 || IN_SET(rfd, AT_FDCWD, XAT_FDROOT)); assert(type); assert(token); @@ -229,11 +229,12 @@ int boot_entry_token_ensure( if (*token) return 0; /* Already set. */ - _cleanup_close_ int rfd = -EBADF; - - rfd = open(empty_to_root(root), O_CLOEXEC | O_DIRECTORY | O_PATH); - if (rfd < 0) - return -errno; + _cleanup_close_ int rfd = XAT_FDROOT; + if (!empty_or_root(root)) { + rfd = open(root, O_CLOEXEC | O_DIRECTORY | O_PATH); + if (rfd < 0) + return -errno; + } return boot_entry_token_ensure_at(rfd, conf_root, machine_id, machine_id_is_random, type, token); } From 8a4f8569772bca5c7883abe3a91a215de535b301 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 15 Sep 2025 14:45:08 +0200 Subject: [PATCH 5/6] conf-files: rename .name field to .filename to make clearer what precisely it is the name of --- src/basic/conf-files.c | 28 ++++++++++++++-------------- src/basic/conf-files.h | 10 +++++----- src/sysupdate/sysupdate.c | 4 ++-- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index 4d4cf27952..ff40eb617b 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -24,7 +24,7 @@ ConfFile* conf_file_free(ConfFile *c) { if (!c) return NULL; - free(c->name); + free(c->filename); free(c->result); free(c->original_path); free(c->resolved_path); @@ -302,7 +302,7 @@ int conf_file_new_at( if (!c->original_path) return log_oom_full(log_level); - r = path_extract_filename(path, &c->name); + r = path_extract_filename(path, &c->filename); if (r < 0) return log_full_errno(log_level, r, "Failed to extract filename from '%s': %m", path); @@ -318,7 +318,7 @@ int conf_file_new_at( return log_full_errno(log_level, r, "Failed to chase '%s%s': %m", empty_to_root(root), skip_leading_slash(dirpath)); } - c->result = path_join(resolved_dirpath, c->name); + c->result = path_join(resolved_dirpath, c->filename); if (!c->result) return log_oom_full(log_level); @@ -327,7 +327,7 @@ int conf_file_new_at( rfd, c->original_path, c->result, - c->name, + c->filename, /* masked= */ NULL, flags, &c->resolved_path, @@ -456,7 +456,7 @@ static int files_add( return log_oom_full(log_level); *c = (ConfFile) { - .name = strdup(de->d_name), + .filename = strdup(de->d_name), .result = TAKE_PTR(p), .original_path = TAKE_PTR(original_path), .resolved_path = TAKE_PTR(resolved_path), @@ -464,10 +464,10 @@ static int files_add( .st = st, }; - if (!c->name) + if (!c->filename) return log_oom_full(log_level); - r = hashmap_ensure_put(files, &conf_file_hash_ops, c->name, c); + r = hashmap_ensure_put(files, &conf_file_hash_ops, c->filename, c); if (r < 0) { assert(r == -ENOMEM); return log_oom_full(log_level); @@ -497,7 +497,7 @@ static int dump_files(Hashmap *fh, const char *root, ConfFilesFlags flags, ConfF /* Hence, we need to remove them from the hashmap. */ FOREACH_ARRAY(i, files, n_files) - assert_se(hashmap_remove(fh, (*i)->name) == *i); + assert_se(hashmap_remove(fh, (*i)->filename) == *i); if (root) FOREACH_ARRAY(i, files, n_files) { @@ -538,7 +538,7 @@ static int copy_and_sort_files_from_hashmap( const char *add = NULL; if (FLAGS_SET(flags, CONF_FILES_BASENAME)) - add = c->name; + add = c->filename; else if (root) { _cleanup_free_ char *p = NULL; @@ -590,22 +590,22 @@ static int insert_replacement(Hashmap **fh, ConfFile *replacement, ConfFilesFlag /* This consumes the input ConfFile. */ - ConfFile *existing = hashmap_get(*fh, c->name); + ConfFile *existing = hashmap_get(*fh, c->filename); if (existing) { log_debug("An entry with higher priority '%s' -> '%s' already exists, ignoring the replacement: %s", - existing->name, existing->result, c->original_path); + existing->filename, existing->result, c->original_path); *ret = NULL; return 0; } - r = hashmap_ensure_put(fh, &conf_file_hash_ops, c->name, c); + r = hashmap_ensure_put(fh, &conf_file_hash_ops, c->filename, c); if (r < 0) { assert(r == -ENOMEM); return log_oom_full(conf_files_log_level(flags)); } assert(r > 0); - log_debug("Inserted replacement: '%s' -> '%s'", c->name, c->result); + log_debug("Inserted replacement: '%s' -> '%s'", c->filename, c->result); *ret = TAKE_PTR(c); return 0; @@ -651,7 +651,7 @@ static int conf_files_list_impl( continue; } - if (c && streq_ptr(path_startswith(c->result, path), c->name)) { + if (c && streq_ptr(path_startswith(c->result, path), c->filename)) { r = insert_replacement(&fh, TAKE_PTR(c), flags, &inserted); if (r < 0) return r; diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h index 444627b8d0..fa460b9e91 100644 --- a/src/basic/conf-files.h +++ b/src/basic/conf-files.h @@ -18,11 +18,11 @@ typedef enum ConfFilesFlags { } ConfFilesFlags; typedef struct ConfFile { - char *name; /* name of a file found in config directories */ - char *result; /* resolved config directory with the original file name found in the directory */ - char *original_path; /* original config directory with the original file name found in the directory */ - char *resolved_path; /* fully resolved path, where the filename part of the path may be different from the original name */ - int fd; /* O_PATH fd to resolved_path, -EBADF if the resolved_path does not exist */ + char *filename; /* filename of the discovered file (i.e. without any directory prefix) */ + char *result; /* full path to the file (with the directory prefix fully resolved, but the filename part left as is) */ + char *original_path; /* full path to the file (original – non-resolved – directory prefix + filename part left as is) */ + char *resolved_path; /* fully resolved path to the file with both directory prefix and filename fully resolved */ + int fd; /* O_PATH fd to resolved_path, -EBADF if resolved_path does not exist */ struct stat st; /* stat of the file. */ } ConfFile; diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c index 6702358ccc..05163754af 100644 --- a/src/sysupdate/sysupdate.c +++ b/src/sysupdate/sysupdate.c @@ -1568,12 +1568,12 @@ static int verb_components(int argc, char **argv, void *userdata) { FOREACH_ARRAY(i, directories, n_directories) { ConfFile *e = *i; - if (streq(e->name, "sysupdate.d")) { + if (streq(e->filename, "sysupdate.d")) { has_default_component = true; continue; } - const char *s = startswith(e->name, "sysupdate."); + const char *s = startswith(e->filename, "sysupdate."); if (!s) continue; From 65a6da9c96888cc0ed2af836d69b38bb20003cca Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 15 Sep 2025 15:12:40 +0200 Subject: [PATCH 6/6] conf-files: add flag so that we don't always prefix returned paths with the root dir path used This is useful in tools such as system-repart where we show the definition file paths a lot in our output, but if prefixed with the root path we'd show a temporary mount dir when operating on a image file. Hence, let's drop the prefix here, and show only the path within the image. --- src/basic/conf-files.c | 2 +- src/basic/conf-files.h | 1 + src/repart/repart.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index ff40eb617b..cd91c5bc1a 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -539,7 +539,7 @@ static int copy_and_sort_files_from_hashmap( if (FLAGS_SET(flags, CONF_FILES_BASENAME)) add = c->filename; - else if (root) { + else if (root && !FLAGS_SET(flags, CONF_FILES_DONT_PREFIX_ROOT)) { _cleanup_free_ char *p = NULL; r = chaseat_prefix_root(c->result, root, &p); diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h index fa460b9e91..031463172a 100644 --- a/src/basic/conf-files.h +++ b/src/basic/conf-files.h @@ -15,6 +15,7 @@ typedef enum ConfFilesFlags { CONF_FILES_FILTER_MASKED = CONF_FILES_FILTER_MASKED_BY_SYMLINK | CONF_FILES_FILTER_MASKED_BY_EMPTY, CONF_FILES_TRUNCATE_SUFFIX = 1 << 6, /* truncate specified suffix from return filename or path */ CONF_FILES_WARN = 1 << 7, /* warn on some errors */ + CONF_FILES_DONT_PREFIX_ROOT = 1 << 8, /* don't prefix the specified root path to the resulting paths */ } ConfFilesFlags; typedef struct ConfFile { diff --git a/src/repart/repart.c b/src/repart/repart.c index 97cc60c95b..3b5a541368 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -3397,7 +3397,7 @@ static int context_read_definitions(Context *context) { &files, ".conf", context->definitions ? NULL : arg_root, - CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED|CONF_FILES_WARN, + CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED|CONF_FILES_WARN|CONF_FILES_DONT_PREFIX_ROOT, dirs); if (r < 0) return log_error_errno(r, "Failed to enumerate *.conf files: %m");