]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sysext: Create mutable directory with the right mode
authorKai Lueke <kailuke@microsoft.com>
Tue, 2 Dec 2025 15:02:32 +0000 (00:02 +0900)
committerKai Lüke <kailueke@riseup.net>
Tue, 3 Feb 2026 22:55:36 +0000 (23:55 +0100)
When the mutable directory didn't exist but gets created with
--mutable=yes then it used to get mode 700 and later it got patched by
a chmod because it is the top layer and must match the target hierarchy.
This meant one could not call the function to resolve the mutable
directory twice before the mount because it has a check for a proper
mode when the directory exists which is the case for the second call.
Also, this resulted in /var/lib/extensions.mutable getting created with
mode 700 which is not really required.

Don't rely on the chmod for the upper dir but directly create the
directory with the right mode by first creating all missing directories
with 755 as a sane default and then changing the mode as needed for the
mutable directory.

src/sysext/sysext.c

index ea3ebceab53db31ed377041eb22b76f63834abdc..5374a89a659d3880a78a63c50646ce04ef806b9b 100644 (file)
@@ -1037,23 +1037,25 @@ static int resolve_mutable_directory(
         }
 
         if (IN_SET(arg_mutable, MUTABLE_YES, MUTABLE_EPHEMERAL, MUTABLE_EPHEMERAL_IMPORT)) {
-                _cleanup_free_ char *path_in_root = NULL;
+                _cleanup_close_ int path_fd = -EBADF, chmod_fd = -EBADF;
 
-                path_in_root = path_join(root, path);
-                if (!path_in_root)
-                        return log_oom();
-
-                r = mkdir_p(path_in_root, 0700);
+                /* This also creates, e.g., /var/lib/extensions.mutable/usr if needed and all parent
+                 * directories plus it also works when the last part is a symlink to the real /usr but we
+                 * can't use chase_and_open here because it does not behave the same. */
+                r = chase(path, root, CHASE_AT_RESOLVE_IN_ROOT|CHASE_MKDIR_0755|CHASE_MUST_BE_DIRECTORY|CHASE_PREFIX_ROOT, /* ret_path */ NULL, &path_fd);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to create a directory '%s': %m", path_in_root);
+                        return log_error_errno(r, "Failed to chase/create base directory '%s/%s': %m", strempty(root), skip_leading_slash(path));
+
+                chmod_fd = fd_reopen(path_fd, O_CLOEXEC|O_DIRECTORY);
+                if (chmod_fd < 0)
+                        return log_error_errno(chmod_fd, "Failed to reopen '%s/%s': %m", strempty(root), skip_leading_slash(path));
 
-                _cleanup_close_ int atfd = open(path_in_root, O_DIRECTORY|O_CLOEXEC);
-                if (atfd < 0)
-                        return log_error_errno(errno, "Failed to open directory '%s': %m", path_in_root);
+                if (fchmod(chmod_fd, hierarchy_mode) < 0)
+                        return log_error_errno(errno, "Failed to chmod directory '%s/%s': %m", strempty(root), skip_leading_slash(path));
 
-                r = mac_selinux_fix_full(atfd, /* inode_path= */ NULL, hierarchy, /* flags= */ 0);
+                r = mac_selinux_fix_full(chmod_fd, /* inode_path= */ NULL, hierarchy, /* flags= */ 0);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to fix SELinux label for '%s': %m", path_in_root);
+                        return log_error_errno(r, "Failed to fix SELinux label for '%s/%s': %m", strempty(root), skip_leading_slash(path));
         }
 
         r = chase(path, root, CHASE_PREFIX_ROOT, &resolved_path, NULL);