]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sysext: Implement ephemeral import mode
authorKrzesimir Nowak <knowak@microsoft.com>
Wed, 28 Feb 2024 13:23:22 +0000 (14:23 +0100)
committerKrzesimir Nowak <knowak@microsoft.com>
Mon, 25 Mar 2024 07:30:01 +0000 (08:30 +0100)
To enable it, use "ephemeral-import" either for mutable mode environment
variable or for value of "--mutable=" flag.

This is a combination of "ephemeral" and "import" modes. It results in a
mutable hierarchy that includes contents of the mutable extension data, but the
modifications are thrown away when the hierarchy is unmerged.

src/sysext/sysext.c

index 51333dc6fdec428558d786405aea5f1a9213a424..57f06b267849a2d1aa516d4940d26fc0594228a3 100644 (file)
@@ -57,6 +57,7 @@ typedef enum MutableMode {
         MUTABLE_AUTO,
         MUTABLE_IMPORT,
         MUTABLE_EPHEMERAL,
+        MUTABLE_EPHEMERAL_IMPORT,
         _MUTABLE_MAX,
         _MUTABLE_INVALID = -EINVAL,
 } MutableMode;
@@ -76,6 +77,8 @@ static MutableMode arg_mutable = MUTABLE_NO;
 /* Is set to IMAGE_CONFEXT when systemd is called with the confext functionality instead of the default */
 static ImageClass arg_image_class = IMAGE_SYSEXT;
 
+static const char *mutable_extensions_base_dir = "/var/lib/extensions.mutable";
+
 STATIC_DESTRUCTOR_REGISTER(arg_hierarchies, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
@@ -137,6 +140,9 @@ static int parse_mutable_mode(const char *p) {
         if (streq(p, "ephemeral"))
                 return MUTABLE_EPHEMERAL;
 
+        if (streq(p, "ephemeral-import"))
+                return MUTABLE_EPHEMERAL_IMPORT;
+
         r = parse_boolean(p);
         if (r < 0)
                 return r;
@@ -768,7 +774,7 @@ static int resolve_mutable_directory(
                 char **ret_resolved_mutable_directory) {
 
         _cleanup_free_ char *path = NULL, *resolved_path = NULL, *dir_name = NULL;
-        const char *root = arg_root, *base = "/var/lib/extensions.mutable";
+        const char *root = arg_root, *base = mutable_extensions_base_dir;
         int r;
 
         assert(hierarchy);
@@ -780,7 +786,7 @@ static int resolve_mutable_directory(
                 return 0;
         }
 
-        if (arg_mutable == MUTABLE_EPHEMERAL) {
+        if (IN_SET(arg_mutable, MUTABLE_EPHEMERAL, MUTABLE_EPHEMERAL_IMPORT)) {
                 /* We create mutable directory inside the temporary tmpfs workspace, which is a fixed
                  * location that ignores arg_root. */
                 root = NULL;
@@ -795,7 +801,7 @@ static int resolve_mutable_directory(
         if (!path)
                 return log_oom();
 
-        if (IN_SET(arg_mutable, MUTABLE_YES, MUTABLE_EPHEMERAL)) {
+        if (IN_SET(arg_mutable, MUTABLE_YES, MUTABLE_EPHEMERAL, MUTABLE_EPHEMERAL_IMPORT)) {
                 _cleanup_free_ char *path_in_root = NULL;
 
                 path_in_root = path_join(root, path);
@@ -848,6 +854,83 @@ static int overlayfs_paths_new(const char *hierarchy, const char *workspace_path
         return 0;
 }
 
+static int resolved_paths_equal(const char *resolved_a, const char *resolved_b) {
+        /* Returns true if paths are of the same entry, false if not, <0 on error. */
+
+        if (path_equal(resolved_a, resolved_b))
+                return 1;
+
+        if (!resolved_a || !resolved_b)
+                return 0;
+
+        return inode_same(resolved_a, resolved_b, 0);
+}
+
+static int maybe_import_mutable_directory(OverlayFSPaths *op) {
+        int r;
+
+        assert(op);
+
+        /* If importing mutable layer and it actually exists and is not a hierarchy itself, add it just below
+         * the meta path */
+
+        if (arg_mutable != MUTABLE_IMPORT || !op->resolved_mutable_directory)
+                return 0;
+
+        r = resolved_paths_equal(op->resolved_hierarchy, op->resolved_mutable_directory);
+        if (r < 0)
+                return log_error_errno(r, "Failed to check equality of hierarchy %s and its mutable directory %s: %m", op->resolved_hierarchy, op->resolved_mutable_directory);
+        if (r > 0) {
+                log_debug("Not importing mutable directory for hierarchy %s as a lower dir, because it points to the hierarchy itself", op->hierarchy);
+                return 0;
+        }
+
+        r = strv_extend(&op->lower_dirs, op->resolved_mutable_directory);
+        if (r < 0)
+                return log_oom();
+
+        return 0;
+}
+
+static int maybe_import_ignored_mutable_directory(OverlayFSPaths *op) {
+        _cleanup_free_ char *dir_name = NULL, *path = NULL, *resolved_path = NULL;
+        int r;
+
+        assert(op);
+
+        /* If importing the ignored mutable layer and it actually exists and is not a hierarchy itself, add
+         * it just below the meta path */
+        if (arg_mutable != MUTABLE_EPHEMERAL_IMPORT)
+                return 0;
+
+        dir_name = hierarchy_as_single_path_component(op->hierarchy);
+        if (!dir_name)
+                return log_oom();
+
+        path = path_join(mutable_extensions_base_dir, dir_name);
+        if (!path)
+                return log_oom();
+
+        r = chase(path, arg_root, CHASE_PREFIX_ROOT, &resolved_path, NULL);
+        if (r < 0 && r != -ENOENT)
+                return log_error_errno(r, "Failed to resolve mutable directory '%s': %m", path);
+
+        r = resolved_paths_equal(op->resolved_hierarchy, resolved_path);
+        if (r < 0)
+                return log_error_errno(r, "Failed to check equality of hierarchy %s and its mutable directory %s: %m", op->resolved_hierarchy, op->resolved_mutable_directory);
+
+        if (r > 0) {
+                log_debug("Not importing mutable directory for hierarchy %s as a lower dir, because it points to the hierarchy itself", op->hierarchy);
+                return 0;
+        }
+
+        r = strv_consume(&op->lower_dirs, TAKE_PTR(resolved_path));
+        if (r < 0)
+                return log_oom();
+
+        return 0;
+}
+
 static int determine_top_lower_dirs(OverlayFSPaths *op, const char *meta_path) {
         int r;
 
@@ -859,12 +942,13 @@ static int determine_top_lower_dirs(OverlayFSPaths *op, const char *meta_path) {
         if (r < 0)
                 return log_oom();
 
-        /* If importing mutable layer and it actually exists, add it just below the meta path */
-        if (arg_mutable == MUTABLE_IMPORT && op->resolved_mutable_directory) {
-                r = strv_extend(&op->lower_dirs, op->resolved_mutable_directory);
-                if (r < 0)
-                        return r;
-        }
+        r = maybe_import_mutable_directory(op);
+        if (r < 0)
+                return r;
+
+        r = maybe_import_ignored_mutable_directory(op);
+        if (r < 0)
+                return r;
 
         return 0;
 }
@@ -928,7 +1012,12 @@ static int hierarchy_as_lower_dir(OverlayFSPaths *op) {
         }
 
         if (arg_mutable == MUTABLE_IMPORT) {
-                log_debug("Mutability for host hierarchy '%s' is disabled, so it will be a lowerdir", op->resolved_hierarchy);
+                log_debug("Mutability for host hierarchy '%s' is disabled, so host hierarchy will be a lowerdir", op->resolved_hierarchy);
+                return 0;
+        }
+
+        if (arg_mutable == MUTABLE_EPHEMERAL_IMPORT) {
+                log_debug("Mutability for host hierarchy '%s' is ephemeral, so host hierarchy will be a lowerdir", op->resolved_hierarchy);
                 return 0;
         }
 
@@ -937,13 +1026,9 @@ static int hierarchy_as_lower_dir(OverlayFSPaths *op) {
                 return 0;
         }
 
-        if (path_equal(op->resolved_hierarchy, op->resolved_mutable_directory)) {
-                log_debug("Host hierarchy '%s' will serve as upperdir.", op->resolved_hierarchy);
-                return 1;
-        }
-        r = inode_same(op->resolved_hierarchy, op->resolved_mutable_directory, 0);
+        r = resolved_paths_equal(op->resolved_hierarchy, op->resolved_mutable_directory);
         if (r < 0)
-                return log_error_errno(r, "Failed to check inode equality of hierarchy %s and its mutable directory %s: %m", op->resolved_hierarchy, op->resolved_mutable_directory);
+                return log_error_errno(r, "Failed to check equality of hierarchy %s and its mutable directory %s: %m", op->resolved_hierarchy, op->resolved_mutable_directory);
         if (r > 0) {
                 log_debug("Host hierarchy '%s' will serve as upperdir.", op->resolved_hierarchy);
                 return 1;
@@ -963,7 +1048,7 @@ static int determine_bottom_lower_dirs(OverlayFSPaths *op) {
         if (!r) {
                 r = strv_extend(&op->lower_dirs, op->resolved_hierarchy);
                 if (r < 0)
-                        return r;
+                        return log_oom();
         }
 
         return 0;
@@ -1147,7 +1232,7 @@ static int write_work_dir_file(ImageClass image_class, const char *meta_path, co
                 return 0;
 
         /* Do not store work dir path for ephemeral mode, it will be gone once this process is done. */
-        if (arg_mutable == MUTABLE_EPHEMERAL)
+        if (IN_SET(arg_mutable, MUTABLE_EPHEMERAL, MUTABLE_EPHEMERAL_IMPORT))
                 return 0;
 
         work_dir_in_root = path_startswith(work_dir, empty_to_root(arg_root));
@@ -2038,7 +2123,7 @@ static int verb_help(int argc, char **argv, void *userdata) {
                "  -h --help               Show this help\n"
                "     --version            Show package version\n"
                "\n%3$sOptions:%4$s\n"
-               "     --mutable=yes|no|auto|import|ephemeral\n"
+               "     --mutable=yes|no|auto|import|ephemeral|ephemeral-import\n"
                "                          Specify a mutability mode of the merged hierarchy\n"
                "     --no-pager           Do not pipe output into a pager\n"
                "     --no-legend          Do not show the headers and footers\n"