prepare-root: Support using composefs as root filesystem
authorAlexander Larsson <alexl@redhat.com>
Thu, 30 Jun 2022 12:53:13 +0000 (14:53 +0200)
committerAlexander Larsson <alexl@redhat.com>
Wed, 31 May 2023 08:57:37 +0000 (10:57 +0200)
This changes ostree-prepare-root to use the .ostree.cfs image as a
composefs filesystem, instead of the checkout.

By default, composefs is used if support is built in and the .ostree.cfs
file exists in the deploy dir, otherwise we fall back to the old
method. However, if the ot-composefs kernel option is specified this
can be tweaked as per:
 * off: Never use composefsz
 * maybe: Use if possible
 * on: Fail if not possible
 * signed: Fail if the cfs image is not fs-verity signed with
   a key in the keyring.
 * digest=....: Fail if the cfs image does not match the specified
   digest.

The final layout when composefs is active is:

 /        ro overlayfs mount for composefs
 /sysroot "real" root
 /etc     rw bind mount to $deploydir/etc
 /var     rw bind mount to $vardir

We also specify the $deploydir/.ostree-mnt directory as the (internal)
mountpoint for the erofs mount for composefs. This can be used to map
the root fs back to the deploy id/dir in use,

A further note: I didn't test the .usr-ovl-work overlayfs case, but a
comment mentions that you can't mount overlayfs on top of a readonly
mount. That seems incompatible with composefs. If this is needed we
have to merge that with the overlayfs that composefs itself sets up,
which is possible with the libcomposefs APIs.

Makefile-switchroot.am
src/switchroot/ostree-prepare-root.c

index 104ec0cdf338e4e9cf1acc318f4a2d2122ead863..8c486e4935b0a01e3228a57c08182312d3c56422 100644 (file)
@@ -27,7 +27,9 @@ ostree_prepare_root_SOURCES = \
     src/switchroot/ostree-mount-util.h \
     src/switchroot/ostree-prepare-root.c \
     $(NULL)
+ostree_prepare_root_CFLAGS =
 ostree_prepare_root_CPPFLAGS = $(AM_CPPFLAGS)
+ostree_prepare_root_LDADD =
 
 if BUILDOPT_USE_STATIC_COMPILER
 # ostree-prepare-root can be used as init in a system without a populated /lib.
@@ -43,10 +45,10 @@ if BUILDOPT_USE_STATIC_COMPILER
 ostree_boot_SCRIPTS += ostree-prepare-root
 
 ostree-prepare-root : $(ostree_prepare_root_SOURCES)
-       $(STATIC_COMPILER) -o $@ -static $(top_srcdir)/src/switchroot/ostree-prepare-root.c $(ostree_prepare_root_CPPFLAGS) $(AM_CFLAGS) $(DEFAULT_INCLUDES) -DOSTREE_PREPARE_ROOT_STATIC=1
+       $(STATIC_COMPILER) -o $@ -static $(top_srcdir)/src/switchroot/ostree-prepare-root.c $(ostree_prepare_root_CPPFLAGS) $(AM_CFLAGS) $(DEFAULT_INCLUDES) $(OT_DEP_COMPOSEFS_CFLAGS) $(OT_DEP_COMPOSEFS_LIBS) -DOSTREE_PREPARE_ROOT_STATIC=1
 else
 ostree_boot_PROGRAMS += ostree-prepare-root
-ostree_prepare_root_CFLAGS = $(AM_CFLAGS) -Isrc/switchroot
+ostree_prepare_root_CFLAGS += $(AM_CFLAGS) -Isrc/switchroot
 endif
 
 ostree_remount_SOURCES = \
@@ -56,9 +58,14 @@ ostree_remount_SOURCES = \
 ostree_remount_CPPFLAGS = $(AM_CPPFLAGS) $(OT_INTERNAL_GIO_UNIX_CFLAGS) -Isrc/switchroot -I$(srcdir)/libglnx
 ostree_remount_LDADD = $(AM_LDFLAGS) $(OT_INTERNAL_GIO_UNIX_LIBS) libglnx.la
 
+if USE_COMPOSEFS
+ostree_prepare_root_CFLAGS += $(OT_DEP_COMPOSEFS_CFLAGS)
+ostree_prepare_root_LDADD += $(OT_DEP_COMPOSEFS_LIBS)
+endif
+
 if BUILDOPT_SYSTEMD
 ostree_prepare_root_CPPFLAGS += -DHAVE_SYSTEMD=1
-ostree_prepare_root_LDADD = $(AM_LDFLAGS) $(LIBSYSTEMD_LIBS)
+ostree_prepare_root_LDADD += $(AM_LDFLAGS) $(LIBSYSTEMD_LIBS)
 endif
 
 # This is the "new mode" of using a generator for /var; see
index 7f1489593e120b7933b6b6808ac97425d146d625..3dbca634a9e1826a09c4e54eb60e06b06061d493 100644 (file)
 // A temporary mount point
 #define TMP_SYSROOT "/sysroot.tmp"
 
+#ifdef HAVE_COMPOSEFS
+#include <libcomposefs/lcfs-mount.h>
+#endif
+
 #include "ostree-mount-util.h"
 
+typedef enum
+{
+  OSTREE_COMPOSEFS_MODE_OFF,    /* Never use composefs */
+  OSTREE_COMPOSEFS_MODE_MAYBE,  /* Use if supported and image exists in deploy */
+  OSTREE_COMPOSEFS_MODE_ON,     /* Always use (and fail if not working) */
+  OSTREE_COMPOSEFS_MODE_SIGNED, /* Always use and require it to be signed */
+  OSTREE_COMPOSEFS_MODE_DIGEST, /* Always use and require specific digest */
+} OstreeComposefsMode;
+
 static inline bool
 sysroot_is_configured_ro (const char *sysroot)
 {
@@ -227,6 +240,34 @@ main (int argc, char *argv[])
         err (EXIT_FAILURE, "failed to umount proc from /proc");
     }
 
+  OstreeComposefsMode composefs_mode = OSTREE_COMPOSEFS_MODE_MAYBE;
+  char *ot_composefs = read_proc_cmdline_key ("ot-composefs");
+  char *composefs_digest = NULL;
+  if (ot_composefs)
+    {
+      if (strcmp (ot_composefs, "off") == 0)
+        composefs_mode = OSTREE_COMPOSEFS_MODE_OFF;
+      else if (strcmp (ot_composefs, "maybe") == 0)
+        composefs_mode = OSTREE_COMPOSEFS_MODE_MAYBE;
+      else if (strcmp (ot_composefs, "on") == 0)
+        composefs_mode = OSTREE_COMPOSEFS_MODE_ON;
+      else if (strcmp (ot_composefs, "signed") == 0)
+        composefs_mode = OSTREE_COMPOSEFS_MODE_SIGNED;
+      else if (strncmp (ot_composefs, "digest=", strlen ("digest=")) == 0)
+        {
+          composefs_mode = OSTREE_COMPOSEFS_MODE_DIGEST;
+          composefs_digest = ot_composefs + strlen ("digest=");
+        }
+      else
+        err (EXIT_FAILURE, "Unsupported ot-composefs option: '%s'", ot_composefs);
+    }
+
+#ifndef HAVE_COMPOSEFS
+  if (composefs_mode == OSTREE_COMPOSEFS_MODE_MAYBE)
+    composefs_mode = OSTREE_COMPOSEFS_MODE_OFF;
+  (void)composefs_digest;
+#endif
+
   /* Query the repository configuration - this is an operating system builder
    * choice.  More info: https://github.com/ostreedev/ostree/pull/1767
    */
@@ -254,9 +295,47 @@ main (int argc, char *argv[])
   if (chdir (deploy_path) < 0)
     err (EXIT_FAILURE, "failed to chdir to deploy_path");
 
-  /* Currently always false */
   bool using_composefs = false;
 
+  /* We construct the new sysroot in /sysroot.tmp, which is either the composfs
+     mount or a bind mount of the deploy-dir */
+  if (composefs_mode != OSTREE_COMPOSEFS_MODE_OFF)
+    {
+#ifdef HAVE_COMPOSEFS
+      const char *objdirs[] = { "/sysroot/ostree/repo/objects" };
+      struct lcfs_mount_options_s cfs_options = {
+        objdirs,
+        1,
+      };
+
+      cfs_options.flags = LCFS_MOUNT_FLAGS_READONLY;
+
+      if (snprintf (srcpath, sizeof (srcpath), "%s/.ostree.mnt", deploy_path) < 0)
+        err (EXIT_FAILURE, "failed to assemble /boot/loader path");
+      cfs_options.image_mountdir = srcpath;
+
+      if (composefs_mode == OSTREE_COMPOSEFS_MODE_SIGNED)
+        {
+          cfs_options.flags |= LCFS_MOUNT_FLAGS_REQUIRE_SIGNATURE | LCFS_MOUNT_FLAGS_REQUIRE_VERITY;
+        }
+      else if (composefs_mode == OSTREE_COMPOSEFS_MODE_DIGEST)
+        {
+          cfs_options.flags |= LCFS_MOUNT_FLAGS_REQUIRE_VERITY;
+          cfs_options.expected_digest = composefs_digest;
+        }
+
+      if (lcfs_mount_image (".ostree.cfs", "/sysroot.tmp", &cfs_options) < 0)
+        {
+          if (composefs_mode > OSTREE_COMPOSEFS_MODE_MAYBE)
+            err (EXIT_FAILURE, "Failed to mount composefs");
+        }
+      else
+        using_composefs = 1;
+#else
+      err (EXIT_FAILURE, "Composefs not supported");
+#endif
+    }
+
   if (!using_composefs)
     {
       /* The deploy root starts out bind mounted to sysroot.tmp */