Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prepare-root: Add ostree.prepare-root.composefs #3277

Merged
merged 3 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions man/ostree-prepare-root.xml
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,18 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
commit must match the target composefs image.</para></listitem>
</varlistentry>
</variablelist>

<para>
The following kernel commandline parameters are also parsed:
</para>
<variablelist>
<varlistentry>
<term><varname>ostree.prepare-root.composefs</varname></term>
<listitem><para>This accepts the same values as <literal>composefs.enabled</literal> above, and overrides the config file (if present).
For example, specifying <literal>ostree.prepare-root.composefs=0</literal> will disable composefs, even if it is enabled by default in the initrd config.</para></listitem>
</varlistentry>
</variablelist>

</refsect1>


Expand Down
2 changes: 1 addition & 1 deletion src/libostree/ostree-sysroot-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ checkout_deployment_tree (OstreeSysroot *sysroot, OstreeRepo *repo, OstreeDeploy
// However, we don't load the keys here, because they may not exist, such
// as in the initial deploy
g_autoptr (ComposefsConfig) composefs_config
= otcore_load_composefs_config (prepare_root_config, FALSE, error);
= otcore_load_composefs_config ("", prepare_root_config, FALSE, error);
if (!composefs_config)
return glnx_prefix_error (error, "Reading composefs config");

Expand Down
25 changes: 24 additions & 1 deletion src/libotcore/otcore-prepare-root.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
// in use, the ostree commit metadata will contain the composefs image digest,
// which can be used to fully verify the target filesystem tree.
#define BINDING_KEYPATH "/etc/ostree/initramfs-root-binding.key"
// The kernel argument to configure composefs
#define CMDLINE_KEY_COMPOSEFS "ostree.prepare-root.composefs"

static bool
proc_cmdline_has_key_starting_with (const char *cmdline, const char *key)
Expand Down Expand Up @@ -161,8 +163,12 @@ otcore_free_composefs_config (ComposefsConfig *config)

// Parse the [composefs] section of the prepare-root.conf.
ComposefsConfig *
otcore_load_composefs_config (GKeyFile *config, gboolean load_keys, GError **error)
otcore_load_composefs_config (const char *cmdline, GKeyFile *config, gboolean load_keys,
GError **error)
{
g_assert (cmdline);
g_assert (config);

GLNX_AUTO_PREFIX_ERROR ("Loading composefs config", error);

g_autoptr (ComposefsConfig) ret = g_new0 (ComposefsConfig, 1);
Expand Down Expand Up @@ -214,5 +220,22 @@ otcore_load_composefs_config (GKeyFile *config, gboolean load_keys, GError **err
return glnx_null_throw (error, "public key file specified, but no public keys found");
}

g_autofree char *ostree_composefs = otcore_find_proc_cmdline_key (cmdline, CMDLINE_KEY_COMPOSEFS);
if (ostree_composefs)
{
if (g_strcmp0 (ostree_composefs, "signed") == 0)
{
ret->enabled = OT_TRISTATE_YES;
ret->is_signed = true;
}
else
{
// The other states force off signatures
ret->is_signed = false;
if (!_ostree_parse_tristate (ostree_composefs, &ret->enabled, error))
return glnx_prefix_error (error, "handling karg " CMDLINE_KEY_COMPOSEFS), NULL;
}
}

return g_steal_pointer (&ret);
}
4 changes: 2 additions & 2 deletions src/libotcore/otcore.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ typedef struct
void otcore_free_composefs_config (ComposefsConfig *config);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ComposefsConfig, otcore_free_composefs_config)

ComposefsConfig *otcore_load_composefs_config (GKeyFile *config, gboolean load_keys,
GError **error);
ComposefsConfig *otcore_load_composefs_config (const char *cmdline, GKeyFile *config,
gboolean load_keys, GError **error);

// Our directory with transient state (eventually /run/ostree-booted should be a link to
// /run/ostree/booted)
Expand Down
63 changes: 46 additions & 17 deletions src/libotutil/ot-keyfile-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,51 @@ ot_keyfile_get_boolean_with_default (GKeyFile *keyfile, const char *section, con
return TRUE;
}

// Keep this in sync with
// https://gitlab.gnome.org/GNOME/glib/-/blob/4a73fbda8be6f80f14b05983eb575c1eb1329c2c/glib/gkeyfile.c?page=5#L4585
// Except for some reason at some point we added "yes" or "no" as possible values too...
gboolean
_ostree_parse_boolean (const char *s, gboolean *out_val, GError **error)
{
g_assert (s);
g_assert (out_val);

if (g_str_equal (s, "yes") || g_str_equal (s, "true") || g_str_equal (s, "1"))
{
*out_val = TRUE;
return TRUE;
}
else if (g_str_equal (s, "no") || g_str_equal (s, "false") || g_str_equal (s, "0"))
{
*out_val = FALSE;
return TRUE;
}
return glnx_throw (error, "Invalid boolean: %s", s);
}

gboolean
_ostree_parse_tristate (const char *s, OtTristate *out_tri, GError **error)
{
if (strcmp (s, "maybe") == 0)
{
*out_tri = OT_TRISTATE_MAYBE;
return TRUE;
}

gboolean bool_value = FALSE;
// Discard the error here, just check if it's valid
if (_ostree_parse_boolean (s, &bool_value, NULL))
{
if (bool_value)
*out_tri = OT_TRISTATE_YES;
else
*out_tri = OT_TRISTATE_NO;
return TRUE;
}
// If it's invalid, be clear a tristate was expected.
return glnx_throw (error, "Invalid tri-state value: %s", s);
}

gboolean
ot_keyfile_get_tristate_with_default (GKeyFile *keyfile, const char *section, const char *value,
OtTristate default_value, OtTristate *out_tri, GError **error)
Expand All @@ -85,23 +130,7 @@ ot_keyfile_get_tristate_with_default (GKeyFile *keyfile, const char *section, co
}

ret_value = g_strstrip (ret_value);

if (strcmp (ret_value, "yes") == 0 || strcmp (ret_value, "true") == 0
|| strcmp (ret_value, "1") == 0)
*out_tri = OT_TRISTATE_YES;
else if (strcmp (ret_value, "no") == 0 || strcmp (ret_value, "false") == 0
|| strcmp (ret_value, "0") == 0)
*out_tri = OT_TRISTATE_NO;
else if (strcmp (ret_value, "maybe") == 0)
*out_tri = OT_TRISTATE_MAYBE;
else
{
g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
"Invalid tri-state value: %s", ret_value);
return FALSE;
}

return TRUE;
return _ostree_parse_tristate (ret_value, out_tri, error);
}

gboolean
Expand Down
3 changes: 3 additions & 0 deletions src/libotutil/ot-keyfile-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ typedef enum

G_BEGIN_DECLS

gboolean _ostree_parse_boolean (const char *s, gboolean *out_val, GError **error);
gboolean _ostree_parse_tristate (const char *s, OtTristate *out_tri, GError **error);

gboolean ot_keyfile_get_boolean_with_default (GKeyFile *keyfile, const char *section,
const char *value, gboolean default_value,
gboolean *out_bool, GError **error);
Expand Down
13 changes: 7 additions & 6 deletions src/switchroot/ostree-prepare-root.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,11 @@ sysroot_is_configured_ro (const char *sysroot)
}

static char *
resolve_deploy_path (const char *root_mountpoint)
resolve_deploy_path (const char *kernel_cmdline, const char *root_mountpoint)
{
char destpath[PATH_MAX];
struct stat stbuf;
char *deploy_path;
g_autofree char *kernel_cmdline = read_proc_cmdline ();
if (!kernel_cmdline)
errx (EXIT_FAILURE, "Failed to read kernel cmdline");

g_autoptr (GError) error = NULL;
g_autofree char *ostree_target = NULL;
Expand Down Expand Up @@ -268,6 +265,10 @@ main (int argc, char *argv[])
err (EXIT_FAILURE, "usage: ostree-prepare-root SYSROOT");
const char *root_arg = argv[1];

g_autofree char *kernel_cmdline = read_proc_cmdline ();
if (!kernel_cmdline)
errx (EXIT_FAILURE, "Failed to read kernel cmdline");

// Since several APIs want to operate in terms of file descriptors, let's
// open the initramfs now. Currently this is just used for the config parser.
glnx_autofd int initramfs_rootfs_fd = -1;
Expand All @@ -289,7 +290,7 @@ main (int argc, char *argv[])
// We always parse the composefs config, because we want to detect and error
// out if it's enabled, but not supported at compile time.
g_autoptr (ComposefsConfig) composefs_config
= otcore_load_composefs_config (config, TRUE, &error);
= otcore_load_composefs_config (kernel_cmdline, config, TRUE, &error);
if (!composefs_config)
errx (EXIT_FAILURE, "%s", error->message);

Expand All @@ -308,7 +309,7 @@ main (int argc, char *argv[])
const char *root_mountpoint = realpath (root_arg, NULL);
if (root_mountpoint == NULL)
err (EXIT_FAILURE, "realpath(\"%s\")", root_arg);
g_autofree char *deploy_path = resolve_deploy_path (root_mountpoint);
g_autofree char *deploy_path = resolve_deploy_path (kernel_cmdline, root_mountpoint);
const char *deploy_directory_name = glnx_basename (deploy_path);
// Note that realpath() should have stripped any trailing `/` which shouldn't
// be in the karg to start with, but we assert here to be sure we have a non-empty
Expand Down
24 changes: 23 additions & 1 deletion tests/inst/src/composefs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,19 @@ fn verify_composefs_signed(sh: &xshell::Shell, metadata: &glib::VariantDict) ->
Ok(())
}

fn verify_disable_composefs(sh: &xshell::Shell, metadata: &glib::VariantDict) -> Result<()> {
assert_eq!(
metadata
.lookup::<bool>("composefs")
.unwrap()
.unwrap_or_default(),
false
);
let fstype = cmd!(sh, "findmnt -n -o FSTYPE /").read()?;
assert_ne!(fstype.as_str(), "overlay");
Ok(())
}

pub(crate) fn itest_composefs() -> Result<()> {
let sh = &xshell::Shell::new()?;
let mark = match crate::test::get_reboot_mark()? {
Expand Down Expand Up @@ -165,7 +178,16 @@ pub(crate) fn itest_composefs() -> Result<()> {
Err(reboot("2"))?;
Ok(())
}
"2" => verify_composefs_signed(sh, &metadata),
"2" => {
verify_composefs_signed(sh, &metadata)?;
cmd!(
sh,
"rpm-ostree kargs --append=ostree.prepare-root.composefs=0"
)
.run()?;
Err(reboot("3"))
}
"3" => verify_disable_composefs(sh, &metadata),
o => anyhow::bail!("Unrecognized reboot mark {o}"),
}
}
Expand Down
37 changes: 37 additions & 0 deletions tests/test-keyfile-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,42 @@ fill_keyfile (GKeyFile *file)
g_key_file_set_value (file, "section", "value_bar", "bar");
}

static void
test_parse_tristate (void)
{
g_autoptr (GError) error = NULL;

OtTristate t = OT_TRISTATE_NO;
// Verify maybe
(void)_ostree_parse_tristate ("maybe", &t, &error);
g_assert_no_error (error);
g_assert_cmpint (t, ==, OT_TRISTATE_MAYBE);

// Alternate yes and no
(void)_ostree_parse_tristate ("yes", &t, &error);
g_assert_no_error (error);
g_assert_cmpint (t, ==, OT_TRISTATE_YES);
(void)_ostree_parse_tristate ("no", &t, &error);
g_assert_no_error (error);
g_assert_cmpint (t, ==, OT_TRISTATE_NO);
(void)_ostree_parse_tristate ("1", &t, &error);
g_assert_no_error (error);
g_assert_cmpint (t, ==, OT_TRISTATE_YES);
(void)_ostree_parse_tristate ("0", &t, &error);
g_assert_no_error (error);
g_assert_cmpint (t, ==, OT_TRISTATE_NO);
(void)_ostree_parse_tristate ("true", &t, &error);
g_assert_no_error (error);
g_assert_cmpint (t, ==, OT_TRISTATE_YES);
(void)_ostree_parse_tristate ("false", &t, &error);
g_assert_no_error (error);
g_assert_cmpint (t, ==, OT_TRISTATE_NO);

// And an error case
(void)_ostree_parse_tristate ("foobar", &t, &error);
g_assert (error != NULL);
}

int
main (int argc, char **argv)
{
Expand All @@ -186,6 +222,7 @@ main (int argc, char **argv)
g_test_add_func ("/keyfile-utils/get-value-with-default-group-optional",
test_get_value_with_default_group_optional);
g_test_add_func ("/keyfile-utils/copy-group", test_copy_group);
g_test_add_func ("/keyfile-utils/parse-tristate", test_parse_tristate);

ret = g_test_run ();

Expand Down
Loading