]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
conf-files: add support for root_fd based operation
authorLennart Poettering <lennart@poettering.net>
Fri, 5 Sep 2025 12:21:53 +0000 (14:21 +0200)
committerLennart Poettering <lennart@poettering.net>
Sun, 25 Jan 2026 19:51:50 +0000 (20:51 +0100)
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)

19 files changed:
src/basic/conf-files.c
src/basic/conf-files.h
src/core/main.c
src/kernel-install/kernel-install.c
src/network/netdev/netdev.c
src/network/networkctl-config-file.c
src/network/networkd-network.c
src/repart/repart.c
src/resolve/resolved-dns-delegate.c
src/resolve/resolved-dnssd.c
src/shared/conf-parser.c
src/shared/conf-parser.h
src/shared/kernel-config.c
src/shared/kernel-config.h
src/sysext/sysext.c
src/sysupdate/sysupdate-feature.c
src/sysupdate/sysupdate-transfer.c
src/test/test-conf-parser.c
src/udev/net/link-config.c

index 76ff20f07b9f39a11e3b9c7e3a751eeb92284221..4d4cf27952f43126e1f36004e8fdb1489670934d 100644 (file)
@@ -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);
 }
 
 /**
index 90e5b1bec52f32f40659462caa5dc0945b60b7aa..444627b8d0410a5d6460638a323bfd248bbd95e8 100644 (file)
@@ -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);
 
index 687b397e62138758c486ba3f7daa14b9d650b550..3a6284b456bc35cdffde2c7cae4dc07ca0a76245 100644 (file)
@@ -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
index b80f8d50ee04c8e427a75cd5951c4f08ceb273a4..b79e830844e4a245cc3b5a458694074a30c12e20 100644 (file)
@@ -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;
 
index be35c59fd9c11447b141ee500368a6c14fb355e8..393114aa7bae4491146396bfd9a9c63b7de596a4 100644 (file)
@@ -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,
index 36c6c0e8e1c8ce58f57c04640c4c02cde93af5e7..9f0236b5406a6ac1a81cf28b65002c0d272ddb6c 100644 (file)
@@ -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;
         }
index 4d1ed4ccca98691341e4a9aa48ab583ccc07587a..45456d677d2d8c7de46e898c7b025d52b1fa9bd4 100644 (file)
@@ -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,
index 100e03aab556496d5025adcd1e5b06f8d7600501..97cc60c95b2fbb226327db7e7a028ee2d240f64f 100644 (file)
@@ -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;
index b866a5838db2fe37a571640ac5bdf5962019fd70..eee2daab94b94557c28964b6a3e0cb97e1d64fec 100644 (file)
@@ -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;
 
index 977f786913233387f7a13c67608fbc9fb513dc1c..6cc0f86796a5273c85157d345e10212bc75286de 100644 (file)
@@ -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;
 
index e5e18558927495cad3e211d0ac4bcb267b922c7e..1d1d8a10ccd0b3d6fa10c7b89248c5a01c575643 100644 (file)
@@ -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;
 
index b394131b6496d51961608883cbb650d2b69dd838..0d1324b67d8a2462e51c51a2e7fff0017a7cb84d 100644 (file)
@@ -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,
index 9bc984e91903bc04c8664d4679e14105f3bd44c1..415730bf1a39f59fda8bd4206f13252f1f0ebdfb 100644 (file)
@@ -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,
index 5d0f00c929053d8dba8698634f23c2903892f43a..eab3fe46410ebe0d2005bef8c56fb88f68b8c333 100644 (file)
@@ -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);
+}
index 072181ea46f389348c84176ecf167d7bfe07feb7..340d762b13fddc4f67d3f03b105f87c0c6fbbf3a 100644 (file)
@@ -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,
index 2771b58a8ad59b6fb025f2abd7fa66be8bba5a04..795d4ef90a35dc40a9becf30953a74e8a852019f 100644 (file)
@@ -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,
index d6009d5e4c17df5867d043fa29da48b059ab7006..51ae49f4076f4d639dde6063e1336e3427134e93 100644 (file)
@@ -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",
index e2f9f28792e21b5408167bec091f841aa169b843..5cae59ce717b15a176298ffb2ea2d0faa9ddc592 100644 (file)
@@ -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,
index 378e909b9be3b6ce31686eede8c3165a459ab916..eefa95dc5f68dae684b4fd0cfad0f1d9fe6b3190 100644 (file)
@@ -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. */