]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
firstboot: Modernize path handling 26707/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 22 Dec 2022 13:29:13 +0000 (14:29 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 29 Mar 2023 07:25:38 +0000 (09:25 +0200)
Let's open a file descriptor to the root directory and perform all
path operations using that file descriptor. On top of that, let's
make sure we pin the directory containing the file we want to work
on and use atomic copies and writes everywhere we can..

src/firstboot/firstboot.c

index 3973de893187624af1f5d18597236ceb6a391e54..c1b739dd4c78d206d05a685e57820ba04857f7d9 100644 (file)
@@ -23,6 +23,7 @@
 #include "kbd-util.h"
 #include "libcrypt-util.h"
 #include "locale-util.h"
+#include "lock-util.h"
 #include "main-func.h"
 #include "memory-util.h"
 #include "mkdir.h"
@@ -228,6 +229,19 @@ static int prompt_loop(const char *text, char **l, unsigned percentage, bool (*i
         }
 }
 
+static int should_configure(int dir_fd, const char *filename) {
+        int r;
+
+        assert(dir_fd >= 0);
+        assert(filename);
+
+        r = faccessat(dir_fd, filename, F_OK, AT_SYMLINK_NOFOLLOW);
+        if (r < 0 && errno != ENOENT)
+                return log_error_errno(errno, "Failed to access %s: %m", filename);
+
+        return r < 0 || arg_force;
+}
+
 static bool locale_is_ok(const char *name) {
 
         if (arg_root)
@@ -309,28 +323,34 @@ static int prompt_locale(void) {
         return 0;
 }
 
-static int process_locale(void) {
-        const char *etc_localeconf;
+static int process_locale(int rfd) {
+        _cleanup_close_ int pfd = -EBADF;
+        _cleanup_free_ char *f = NULL;
         char* locales[3];
         unsigned i = 0;
         int r;
 
-        etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf");
-        if (laccess(etc_localeconf, F_OK) >= 0 && !arg_force) {
-                log_debug("Found %s, assuming locale information has been configured.",
-                          etc_localeconf);
-                return 0;
-        }
+        assert(rfd >= 0);
+
+        pfd = chase_and_open_parent_at(rfd, "/etc/locale.conf",
+                                       CHASE_AT_RESOLVE_IN_ROOT|CHASE_MKDIR_0755|CHASE_WARN,
+                                       &f);
+        if (pfd < 0)
+                return log_error_errno(pfd, "Failed to chase /etc/locale.conf: %m");
 
-        if (arg_copy_locale && arg_root) {
+        r = should_configure(pfd, f);
+        if (r == 0)
+                log_debug("Found /etc/locale.conf, assuming locale information has been configured.");
+        if (r <= 0)
+                return r;
 
-                (void) mkdir_parents(etc_localeconf, 0755);
-                r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, COPY_REFLINK);
+        if (arg_copy_locale && !dir_fd_is_root(rfd)) {
+                r = copy_file_atomic_at(AT_FDCWD, "/etc/locale.conf", pfd, f, 0644, COPY_REFLINK);
                 if (r != -ENOENT) {
                         if (r < 0)
-                                return log_error_errno(r, "Failed to copy %s: %m", etc_localeconf);
+                                return log_error_errno(r, "Failed to copy host's /etc/locale.conf: %m");
 
-                        log_info("%s copied.", etc_localeconf);
+                        log_info("Copied host's /etc/locale.conf.");
                         return 0;
                 }
         }
@@ -349,12 +369,11 @@ static int process_locale(void) {
 
         locales[i] = NULL;
 
-        (void) mkdir_parents(etc_localeconf, 0755);
-        r = write_env_file(etc_localeconf, locales);
+        r = write_env_file_at(pfd, f, locales);
         if (r < 0)
-                return log_error_errno(r, "Failed to write %s: %m", etc_localeconf);
+                return log_error_errno(r, "Failed to write /etc/locale.conf: %m");
 
-        log_info("%s written.", etc_localeconf);
+        log_info("/etc/locale.conf written.");
         return 0;
 }
 
@@ -390,27 +409,33 @@ static int prompt_keymap(void) {
                            kmaps, 60, keymap_is_valid, &arg_keymap);
 }
 
-static int process_keymap(void) {
-        const char *etc_vconsoleconf;
+static int process_keymap(int rfd) {
+        _cleanup_close_ int pfd = -EBADF;
+        _cleanup_free_ char *f = NULL;
         char **keymap;
         int r;
 
-        etc_vconsoleconf = prefix_roota(arg_root, "/etc/vconsole.conf");
-        if (laccess(etc_vconsoleconf, F_OK) >= 0 && !arg_force) {
-                log_debug("Found %s, assuming console has been configured.",
-                          etc_vconsoleconf);
-                return 0;
-        }
+        assert(rfd >= 0);
 
-        if (arg_copy_keymap && arg_root) {
+        pfd = chase_and_open_parent_at(rfd, "/etc/vconsole.conf",
+                                       CHASE_AT_RESOLVE_IN_ROOT|CHASE_MKDIR_0755|CHASE_WARN,
+                                       &f);
+        if (pfd < 0)
+                return log_error_errno(pfd, "Failed to chase /etc/vconsole.conf: %m");
 
-                (void) mkdir_parents(etc_vconsoleconf, 0755);
-                r = copy_file("/etc/vconsole.conf", etc_vconsoleconf, 0, 0644, COPY_REFLINK);
+        r = should_configure(pfd, f);
+        if (r == 0)
+                log_debug("Found /etc/vconsole.conf, assuming console has been configured.");
+        if (r <= 0)
+                return r;
+
+        if (arg_copy_keymap && !dir_fd_is_root(rfd)) {
+                r = copy_file_atomic_at(AT_FDCWD, "/etc/vconsole.conf", pfd, f, 0644, COPY_REFLINK);
                 if (r != -ENOENT) {
                         if (r < 0)
-                                return log_error_errno(r, "Failed to copy %s: %m", etc_vconsoleconf);
+                                return log_error_errno(r, "Failed to copy host's /etc/vconsole.conf: %m");
 
-                        log_info("%s copied.", etc_vconsoleconf);
+                        log_info("Copied host's /etc/vconsole.conf.");
                         return 0;
                 }
         }
@@ -426,15 +451,11 @@ static int process_keymap(void) {
 
         keymap = STRV_MAKE(strjoina("KEYMAP=", arg_keymap));
 
-        r = mkdir_parents(etc_vconsoleconf, 0755);
-        if (r < 0)
-                return log_error_errno(r, "Failed to create the parent directory of %s: %m", etc_vconsoleconf);
-
-        r = write_env_file(etc_vconsoleconf, keymap);
+        r = write_env_file_at(pfd, f, keymap);
         if (r < 0)
-                return log_error_errno(r, "Failed to write %s: %m", etc_vconsoleconf);
+                return log_error_errno(r, "Failed to write /etc/vconsole.conf: %m");
 
-        log_info("%s written.", etc_vconsoleconf);
+        log_info("/etc/vconsole.conf written.");
         return 0;
 }
 
@@ -476,31 +497,39 @@ static int prompt_timezone(void) {
         return 0;
 }
 
-static int process_timezone(void) {
-        const char *etc_localtime, *e;
+static int process_timezone(int rfd) {
+        _cleanup_close_ int pfd = -EBADF;
+        _cleanup_free_ char *f = NULL;
+        const char *e;
         int r;
 
-        etc_localtime = prefix_roota(arg_root, "/etc/localtime");
-        if (laccess(etc_localtime, F_OK) >= 0 && !arg_force) {
-                log_debug("Found %s, assuming timezone has been configured.",
-                          etc_localtime);
-                return 0;
-        }
+        assert(rfd >= 0);
 
-        if (arg_copy_timezone && arg_root) {
-                _cleanup_free_ char *p = NULL;
+        pfd = chase_and_open_parent_at(rfd, "/etc/localtime",
+                                       CHASE_AT_RESOLVE_IN_ROOT|CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
+                                       &f);
+        if (pfd < 0)
+                return log_error_errno(pfd, "Failed to chase /etc/localtime: %m");
 
-                r = readlink_malloc("/etc/localtime", &p);
+        r = should_configure(pfd, f);
+        if (r == 0)
+                log_debug("Found /etc/localtime, assuming timezone has been configured.");
+        if (r <= 0)
+                return r;
+
+        if (arg_copy_timezone && !dir_fd_is_root(rfd)) {
+                _cleanup_free_ char *s = NULL;
+
+                r = readlink_malloc("/etc/localtime", &s);
                 if (r != -ENOENT) {
                         if (r < 0)
-                                return log_error_errno(r, "Failed to read host timezone: %m");
+                                return log_error_errno(r, "Failed to read host's /etc/localtime: %m");
 
-                        (void) mkdir_parents(etc_localtime, 0755);
-                        r = symlink_atomic(p, etc_localtime);
+                        r = symlinkat_atomic_full(s, pfd, f, /* make_relative= */ false);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to create %s symlink: %m", etc_localtime);
+                                return log_error_errno(r, "Failed to create /etc/localtime symlink: %m");
 
-                        log_info("%s copied.", etc_localtime);
+                        log_info("Copied host's /etc/localtime.");
                         return 0;
                 }
         }
@@ -514,12 +543,11 @@ static int process_timezone(void) {
 
         e = strjoina("../usr/share/zoneinfo/", arg_timezone);
 
-        (void) mkdir_parents(etc_localtime, 0755);
-        r = symlink_atomic(e, etc_localtime);
+        r = symlinkat_atomic_full(e, pfd, f, /* make_relative= */ false);
         if (r < 0)
-                return log_error_errno(r, "Failed to create %s symlink: %m", etc_localtime);
+                return log_error_errno(r, "Failed to create /etc/localtime symlink: %m");
 
-        log_info("%s written", etc_localtime);
+        log_info("/etc/localtime written");
         return 0;
 }
 
@@ -563,16 +591,24 @@ static int prompt_hostname(void) {
         return 0;
 }
 
-static int process_hostname(void) {
-        const char *etc_hostname;
+static int process_hostname(int rfd) {
+        _cleanup_close_ int pfd = -EBADF;
+        _cleanup_free_ char *f = NULL;
         int r;
 
-        etc_hostname = prefix_roota(arg_root, "/etc/hostname");
-        if (laccess(etc_hostname, F_OK) >= 0 && !arg_force) {
-                log_debug("Found %s, assuming hostname has been configured.",
-                          etc_hostname);
-                return 0;
-        }
+        assert(rfd >= 0);
+
+        pfd = chase_and_open_parent_at(rfd, "/etc/hostname",
+                                       CHASE_AT_RESOLVE_IN_ROOT|CHASE_MKDIR_0755|CHASE_WARN,
+                                       &f);
+        if (pfd < 0)
+                return log_error_errno(pfd, "Failed to chase /etc/hostname: %m");
+
+        r = should_configure(pfd, f);
+        if (r == 0)
+                log_debug("Found /etc/hostname, assuming hostname has been configured.");
+        if (r <= 0)
+                return r;
 
         r = prompt_hostname();
         if (r < 0)
@@ -581,39 +617,45 @@ static int process_hostname(void) {
         if (isempty(arg_hostname))
                 return 0;
 
-        r = write_string_file(etc_hostname, arg_hostname,
-                              WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 |
-                              (arg_force ? WRITE_STRING_FILE_ATOMIC : 0));
+        r = write_string_file_at(pfd, f, arg_hostname,
+                                 WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC);
         if (r < 0)
-                return log_error_errno(r, "Failed to write %s: %m", etc_hostname);
+                return log_error_errno(r, "Failed to write /etc/hostname: %m");
 
-        log_info("%s written.", etc_hostname);
+        log_info("/etc/hostname written.");
         return 0;
 }
 
-static int process_machine_id(void) {
-        const char *etc_machine_id;
+static int process_machine_id(int rfd) {
+        _cleanup_close_ int pfd = -EBADF;
+        _cleanup_free_ char *f = NULL;
         int r;
 
-        etc_machine_id = prefix_roota(arg_root, "/etc/machine-id");
-        if (laccess(etc_machine_id, F_OK) >= 0 && !arg_force) {
-                log_debug("Found %s, assuming machine-id has been configured.",
-                          etc_machine_id);
-                return 0;
-        }
+        assert(rfd >= 0);
+
+        pfd = chase_and_open_parent_at(rfd, "/etc/machine-id",
+                                       CHASE_AT_RESOLVE_IN_ROOT|CHASE_MKDIR_0755|CHASE_WARN,
+                                       &f);
+        if (pfd < 0)
+                return log_error_errno(pfd, "Failed to chase /etc/machine-id: %m");
+
+        r = should_configure(pfd, f);
+        if (r == 0)
+                log_debug("Found /etc/machine-id, assuming machine-id has been configured.");
+        if (r <= 0)
+                return r;
 
         if (sd_id128_is_null(arg_machine_id)) {
                 log_debug("Initialization of machine-id was not requested, skipping.");
                 return 0;
         }
 
-        r = write_string_file(etc_machine_id, SD_ID128_TO_STRING(arg_machine_id),
-                              WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 |
-                              (arg_force ? WRITE_STRING_FILE_ATOMIC : 0));
+        r = write_string_file_at(pfd, "machine-id", SD_ID128_TO_STRING(arg_machine_id),
+                                 WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC);
         if (r < 0)
-                return log_error_errno(r, "Failed to write machine id: %m");
+                return log_error_errno(r, "Failed to write /etc/machine id: %m");
 
-        log_info("%s written.", etc_machine_id);
+        log_info("/etc/machine-id written.");
         return 0;
 }
 
@@ -681,7 +723,7 @@ static int prompt_root_password(void) {
         return 0;
 }
 
-static int find_shell(const char *path, const char *root) {
+static int find_shell(int rfd, const char *path) {
         int r;
 
         assert(path);
@@ -689,17 +731,14 @@ static int find_shell(const char *path, const char *root) {
         if (!valid_shell(path))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "%s is not a valid shell", path);
 
-        r = chase(path, root, CHASE_PREFIX_ROOT, NULL, NULL);
-        if (r < 0) {
-                const char *p;
-                p = prefix_roota(root, path);
-                return log_error_errno(r, "Failed to resolve shell %s: %m", p);
-        }
+        r = chaseat(rfd, path, CHASE_AT_RESOLVE_IN_ROOT, NULL, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to resolve shell %s: %m", path);
 
         return 0;
 }
 
-static int prompt_root_shell(void) {
+static int prompt_root_shell(int rfd) {
         int r;
 
         if (arg_root_shell)
@@ -733,7 +772,7 @@ static int prompt_root_shell(void) {
                         break;
                 }
 
-                r = find_shell(s, arg_root);
+                r = find_shell(rfd, s);
                 if (r < 0)
                         continue;
 
@@ -744,18 +783,21 @@ static int prompt_root_shell(void) {
         return 0;
 }
 
-static int write_root_passwd(const char *passwd_path, const char *password, const char *shell) {
+static int write_root_passwd(int etc_fd, const char *password, const char *shell) {
         _cleanup_fclose_ FILE *original = NULL, *passwd = NULL;
         _cleanup_(unlink_and_freep) char *passwd_tmp = NULL;
         int r;
 
         assert(password);
 
-        r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
+        r = fopen_temporary_at_label(etc_fd, "passwd", "passwd", &passwd, &passwd_tmp);
         if (r < 0)
                 return r;
 
-        original = fopen(passwd_path, "re");
+        r = xfopenat(etc_fd, "passwd", "re", O_NOFOLLOW, &original);
+        if (r < 0 && r != -ENOENT)
+                return r;
+
         if (original) {
                 struct passwd *i;
 
@@ -805,25 +847,28 @@ static int write_root_passwd(const char *passwd_path, const char *password, cons
         if (r < 0)
                 return r;
 
-        r = rename_and_apply_smack_floor_label(passwd_tmp, passwd_path);
+        r = renameat_and_apply_smack_floor_label(etc_fd, passwd_tmp, etc_fd, "passwd");
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-static int write_root_shadow(const char *shadow_path, const char *hashed_password) {
+static int write_root_shadow(int etc_fd, const char *hashed_password) {
         _cleanup_fclose_ FILE *original = NULL, *shadow = NULL;
         _cleanup_(unlink_and_freep) char *shadow_tmp = NULL;
         int r;
 
         assert(hashed_password);
 
-        r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
+        r = fopen_temporary_at_label(etc_fd, "shadow", "shadow", &shadow, &shadow_tmp);
         if (r < 0)
                 return r;
 
-        original = fopen(shadow_path, "re");
+        r = xfopenat(etc_fd, "shadow", "re", O_NOFOLLOW, &original);
+        if (r < 0 && r != -ENOENT)
+                return r;
+
         if (original) {
                 struct spwd *i;
 
@@ -874,26 +919,46 @@ static int write_root_shadow(const char *shadow_path, const char *hashed_passwor
         if (r < 0)
                 return r;
 
-        r = rename_and_apply_smack_floor_label(shadow_tmp, shadow_path);
+        r = renameat_and_apply_smack_floor_label(etc_fd, shadow_tmp, etc_fd, "shadow");
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-static int process_root_account(void) {
-        _cleanup_close_ int lock = -EBADF;
+static int process_root_account(int rfd) {
+        _cleanup_close_ int pfd = -EBADF;
+        _cleanup_(release_lock_file) LockFile lock = LOCK_FILE_INIT;
         _cleanup_(erase_and_freep) char *_hashed_password = NULL;
         const char *password, *hashed_password;
-        const char *etc_passwd, *etc_shadow;
-        int r;
+        int k = 0, r;
+
+        assert(rfd >= 0);
+
+        pfd = chase_and_open_parent_at(rfd, "/etc/passwd",
+                                       CHASE_AT_RESOLVE_IN_ROOT|CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
+                                       NULL);
+        if (pfd < 0)
+                return log_error_errno(pfd, "Failed to chase /etc/passwd: %m");
+
+        /* Ensure that passwd and shadow are in the same directory and are not symlinks. */
+
+        FOREACH_STRING(s, "passwd", "shadow") {
+                r = verify_regular_at(pfd, s, /* follow = */ false);
+                if (IN_SET(r, -EISDIR, -ELOOP, -EBADFD))
+                        return log_error_errno(r, "/etc/%s is not a regular file", s);
+                if (r < 0 && r != -ENOENT)
+                        return log_error_errno(r, "Failed to check whether /etc/%s is a regular file: %m", s);
+
+                r = should_configure(pfd, s);
+                if (r < 0)
+                        return r;
 
-        etc_passwd = prefix_roota(arg_root, "/etc/passwd");
-        etc_shadow = prefix_roota(arg_root, "/etc/shadow");
+                k += r;
+        }
 
-        if (laccess(etc_passwd, F_OK) >= 0 && laccess(etc_shadow, F_OK) >= 0 && !arg_force) {
-                log_debug("Found %s and %s, assuming root account has been initialized.",
-                          etc_passwd, etc_shadow);
+        if (k == 0) {
+                log_debug("Found /etc/passwd and /etc/shadow, assuming root account has been initialized.");
                 return 0;
         }
 
@@ -904,11 +969,11 @@ static int process_root_account(void) {
                 return 0;
         }
 
-        lock = take_etc_passwd_lock(arg_root);
-        if (lock < 0)
-                return log_error_errno(lock, "Failed to take a lock on %s: %m", etc_passwd);
+        r = make_lock_file_at(pfd, ETC_PASSWD_LOCK_NAME, LOCK_EX, &lock);
+        if (r < 0)
+                return log_error_errno(r, "Failed to take a lock on /etc/passwd: %m");
 
-        if (arg_copy_root_shell && arg_root) {
+        if (arg_copy_root_shell && !dir_fd_is_root(rfd)) {
                 struct passwd *p;
 
                 errno = 0;
@@ -921,11 +986,11 @@ static int process_root_account(void) {
                         return log_oom();
         }
 
-        r = prompt_root_shell();
+        r = prompt_root_shell(rfd);
         if (r < 0)
                 return r;
 
-        if (arg_copy_root_password && arg_root) {
+        if (arg_copy_root_password && !dir_fd_is_root(rfd)) {
                 struct spwd *p;
 
                 errno = 0;
@@ -960,43 +1025,50 @@ static int process_root_account(void) {
         else
                 password = hashed_password = PASSWORD_LOCKED_AND_INVALID;
 
-        r = write_root_passwd(etc_passwd, password, arg_root_shell);
+        r = write_root_passwd(pfd, password, arg_root_shell);
         if (r < 0)
-                return log_error_errno(r, "Failed to write %s: %m", etc_passwd);
+                return log_error_errno(r, "Failed to write /etc/passwd: %m");
 
-        log_info("%s written", etc_passwd);
+        log_info("/etc/passwd written.");
 
-        r = write_root_shadow(etc_shadow, hashed_password);
+        r = write_root_shadow(pfd, hashed_password);
         if (r < 0)
-                return log_error_errno(r, "Failed to write %s: %m", etc_shadow);
+                return log_error_errno(r, "Failed to write /etc/shadow: %m");
 
-        log_info("%s written.", etc_shadow);
+        log_info("/etc/shadow written.");
         return 0;
 }
 
-static int process_kernel_cmdline(void) {
-        const char *etc_kernel_cmdline;
+static int process_kernel_cmdline(int rfd) {
+        _cleanup_close_ int pfd = -EBADF;
+        _cleanup_free_ char *f = NULL;
         int r;
 
-        etc_kernel_cmdline = prefix_roota(arg_root, "/etc/kernel/cmdline");
-        if (laccess(etc_kernel_cmdline, F_OK) >= 0 && !arg_force) {
-                log_debug("Found %s, assuming kernel has been configured.",
-                          etc_kernel_cmdline);
-                return 0;
-        }
+        assert(rfd >= 0);
+
+        pfd = chase_and_open_parent_at(rfd, "/etc/kernel/cmdline",
+                                       CHASE_AT_RESOLVE_IN_ROOT|CHASE_MKDIR_0755|CHASE_WARN,
+                                       &f);
+        if (pfd < 0)
+                return log_error_errno(pfd, "Failed to chase /etc/kernel/cmdline: %m");
+
+        r = should_configure(pfd, f);
+        if (r == 0)
+                log_debug("Found /etc/kernel/cmdline, assuming kernel command line has been configured.");
+        if (r <= 0)
+                return r;
 
         if (!arg_kernel_cmdline) {
                 log_debug("Creation of /etc/kernel/cmdline was not requested, skipping.");
                 return 0;
         }
 
-        r = write_string_file(etc_kernel_cmdline, arg_kernel_cmdline,
-                              WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 |
-                              (arg_force ? WRITE_STRING_FILE_ATOMIC : 0));
+        r = write_string_file_at(pfd, "cmdline", arg_kernel_cmdline,
+                                 WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC);
         if (r < 0)
-                return log_error_errno(r, "Failed to write %s: %m", etc_kernel_cmdline);
+                return log_error_errno(r, "Failed to write /etc/kernel/cmdline: %m");
 
-        log_info("%s written.", etc_kernel_cmdline);
+        log_info("/etc/kernel/cmdline written.");
         return 0;
 }
 
@@ -1210,10 +1282,6 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_ROOT_SHELL:
-                        r = find_shell(optarg, arg_root);
-                        if (r < 0)
-                                return r;
-
                         r = free_and_strdup(&arg_root_shell, optarg);
                         if (r < 0)
                                 return log_oom();
@@ -1351,6 +1419,7 @@ static int parse_argv(int argc, char *argv[]) {
 static int run(int argc, char *argv[]) {
         _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
         _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
+        _cleanup_close_ int rfd = -EBADF;
         int r;
 
         r = parse_argv(argc, argv);
@@ -1389,7 +1458,7 @@ static int run(int argc, char *argv[]) {
                                 DISSECT_IMAGE_FSCK |
                                 DISSECT_IMAGE_GROWFS,
                                 &unlink_dir,
-                                /* ret_dir_fd= */ NULL,
+                                &rfd,
                                 &loop_device);
                 if (r < 0)
                         return r;
@@ -1397,33 +1466,45 @@ static int run(int argc, char *argv[]) {
                 arg_root = strdup(unlink_dir);
                 if (!arg_root)
                         return log_oom();
+        } else {
+                rfd = open(empty_to_root(arg_root), O_DIRECTORY|O_CLOEXEC);
+                if (rfd < 0)
+                        return log_error_errno(errno, "Failed to open %s: %m", empty_to_root(arg_root));
+        }
+
+        LOG_SET_PREFIX(arg_image ?: arg_root);
+
+        if (arg_root_shell) {
+                r = find_shell(rfd, arg_root_shell);
+                if (r < 0)
+                        return r;
         }
 
-        r = process_locale();
+        r = process_locale(rfd);
         if (r < 0)
                 return r;
 
-        r = process_keymap();
+        r = process_keymap(rfd);
         if (r < 0)
                 return r;
 
-        r = process_timezone();
+        r = process_timezone(rfd);
         if (r < 0)
                 return r;
 
-        r = process_hostname();
+        r = process_hostname(rfd);
         if (r < 0)
                 return r;
 
-        r = process_machine_id();
+        r = process_machine_id(rfd);
         if (r < 0)
                 return r;
 
-        r = process_root_account();
+        r = process_root_account(rfd);
         if (r < 0)
                 return r;
 
-        r = process_kernel_cmdline();
+        r = process_kernel_cmdline(rfd);
         if (r < 0)
                 return r;