libotcore_la_SOURCES = \
src/libotcore/otcore.h \
src/libotcore/otcore-ed25519-verify.c \
+ src/libotcore/otcore-prepare-root.c \
$(NULL)
libotcore_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/libglnx -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(OT_INTERNAL_GIO_UNIX_CFLAGS) $(OT_INTERNAL_GPGME_CFLAGS) $(OT_DEP_CRYPTO_LIBS) $(LIBSYSTEMD_CFLAGS)
#include "ostree-core-private.h"
#include "ostree-mount-util.h"
#include "ostree-sysroot-private.h"
-#include "ostree.h"
+#include "otcore.h"
#ifdef HAVE_LIBMOUNT
typedef FILE OtLibMountFile;
* exit so that we don't error, but at the same time work where switchroot
* is PID 1 (and so hasn't created /run/ostree-booted).
*/
- g_autofree char *ostree_cmdline = find_proc_cmdline_key (cmdline, "ostree");
+ g_autofree char *ostree_cmdline = otcore_find_proc_cmdline_key (cmdline, "ostree");
if (!ostree_cmdline)
return TRUE;
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.0+
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "otcore.h"
+
+static bool
+proc_cmdline_has_key_starting_with (const char *cmdline, const char *key)
+{
+ for (const char *iter = cmdline; iter;)
+ {
+ if (g_str_has_prefix (iter, key))
+ return true;
+
+ iter = strchr (iter, ' ');
+ if (iter == NULL)
+ return false;
+
+ iter += strspn (iter, " ");
+ }
+
+ return false;
+}
+
+// Parse a kernel cmdline to find the provided key.
+// TODO: Deduplicate this with the kernel argument code from libostree.so
+char *
+otcore_find_proc_cmdline_key (const char *cmdline, const char *key)
+{
+ const size_t key_len = strlen (key);
+ for (const char *iter = cmdline; iter;)
+ {
+ const char *next = strchr (iter, ' ');
+ if (strncmp (iter, key, key_len) == 0 && iter[key_len] == '=')
+ {
+ const char *start = iter + key_len + 1;
+ if (next)
+ return strndup (start, next - start);
+
+ return strdup (start);
+ }
+
+ if (next)
+ next += strspn (next, " ");
+
+ iter = next;
+ }
+
+ return NULL;
+}
+
+// Find the target OSTree root filesystem from parsing the provided kernel commandline.
+// If none is found, @out_target will be set to NULL, and the function will return successfully.
+//
+// If invalid data is found, @error will be set.
+gboolean
+otcore_get_ostree_target (const char *cmdline, char **out_target, GError **error)
+{
+ g_assert (cmdline);
+ g_assert (out_target && *out_target == NULL);
+ static const char slot_a[] = "/ostree/root.a";
+ static const char slot_b[] = "/ostree/root.b";
+
+ // First, handle the Android boot case
+ g_autofree char *slot_suffix = otcore_find_proc_cmdline_key (cmdline, "androidboot.slot_suffix");
+ if (slot_suffix)
+ {
+ if (strcmp (slot_suffix, "_a") == 0)
+ {
+ *out_target = strdup (slot_a);
+ return TRUE;
+ }
+ else if (strcmp (slot_suffix, "_b") == 0)
+ {
+ *out_target = strdup (slot_b);
+ return TRUE;
+ }
+ return glnx_throw (error, "androidboot.slot_suffix invalid: %s", slot_suffix);
+ }
+
+ /* Non-A/B androidboot:
+ * https://source.android.com/docs/core/ota/nonab
+ */
+ if (proc_cmdline_has_key_starting_with (cmdline, "androidboot."))
+ {
+ *out_target = strdup (slot_a);
+ return TRUE;
+ }
+
+ // Otherwise, fall back to the default `ostree=` kernel cmdline
+ *out_target = otcore_find_proc_cmdline_key (cmdline, "ostree");
+ return TRUE;
+}
gboolean otcore_validate_ed25519_signature (GBytes *data, GBytes *pubkey, GBytes *signature,
bool *out_valid, GError **error);
+char *otcore_find_proc_cmdline_key (const char *cmdline, const char *key);
+gboolean otcore_get_ostree_target (const char *cmdline, char **out_target, GError **error);
+
// Our directory with transient state (eventually /run/ostree-booted should be a link to
// /run/ostree/booted)
#define OTCORE_RUN_OSTREE "/run/ostree"
return g_key_file_get_boolean (repo_config, SYSROOT_KEY, READONLY_KEY, NULL);
}
-static inline char *
-get_aboot_root_slot (const char *slot_suffix)
-{
- if (strcmp (slot_suffix, "_a") == 0)
- return strdup ("/ostree/root.a");
- else if (strcmp (slot_suffix, "_b") == 0)
- return strdup ("/ostree/root.b");
-
- errx (EXIT_FAILURE, "androidboot.slot_suffix invalid: %s", slot_suffix);
-
- return NULL;
-}
-
-static bool
-proc_cmdline_has_key_starting_with (const char *cmdline, const char *key)
-{
- for (const char *iter = cmdline; iter;)
- {
- if (g_str_has_prefix (iter, key))
- return true;
-
- iter = strchr (iter, ' ');
- if (iter == NULL)
- return false;
-
- iter += strspn (iter, " ");
- }
-
- return false;
-}
-
-static inline char *
-get_ostree_target (const char *cmdline)
-{
- autofree char *slot_suffix = find_proc_cmdline_key (cmdline, "androidboot.slot_suffix");
- if (slot_suffix)
- return get_aboot_root_slot (slot_suffix);
-
- /* Non-A/B androidboot:
- * https://source.android.com/docs/core/ota/nonab
- */
- if (proc_cmdline_has_key_starting_with (cmdline, "androidboot."))
- return strdup ("/ostree/root.a");
-
- return find_proc_cmdline_key (cmdline, "ostree");
-}
-
static char *
resolve_deploy_path (const char *root_mountpoint)
{
if (!kernel_cmdline)
errx (EXIT_FAILURE, "Failed to read kernel cmdline");
- g_autofree char *ostree_target = get_ostree_target (kernel_cmdline);
+ g_autoptr (GError) error = NULL;
+ g_autofree char *ostree_target = NULL;
+ if (!otcore_get_ostree_target (kernel_cmdline, &ostree_target, &error))
+ errx (EXIT_FAILURE, "Failed to determine ostree target: %s", error->message);
if (!ostree_target)
- errx (EXIT_FAILURE, "No ostree target");
+ errx (EXIT_FAILURE, "No ostree target found");
if (snprintf (destpath, sizeof (destpath), "%s/%s", root_mountpoint, ostree_target) < 0)
err (EXIT_FAILURE, "failed to assemble ostree target path");
g_clear_error (&error);
}
+static void
+test_prepare_root_cmdline (void)
+{
+ g_autoptr (GError) error = NULL;
+ g_autofree char *target = NULL;
+
+ static const char *notfound_cases[]
+ = { "", "foo", "foo=bar baz sometest", "xostree foo", "xostree=blah bar", NULL };
+ for (const char **iter = notfound_cases; iter && *iter; iter++)
+ {
+ const char *tcase = *iter;
+ g_assert (otcore_get_ostree_target (tcase, &target, &error));
+ g_assert_no_error (error);
+ g_assert (target == NULL);
+ }
+
+ // Test the default ostree=
+ g_assert (otcore_get_ostree_target ("blah baz=blah ostree=/foo/bar somearg", &target, &error));
+ g_assert_no_error (error);
+ g_assert_cmpstr (target, ==, "/foo/bar");
+ free (g_steal_pointer (&target));
+
+ // Test android boot
+ g_assert (otcore_get_ostree_target ("blah baz=blah androidboot.slot_suffix=_b somearg", &target,
+ &error));
+ g_assert_no_error (error);
+ g_assert_cmpstr (target, ==, "/ostree/root.b");
+ free (g_steal_pointer (&target));
+
+ g_assert (otcore_get_ostree_target ("blah baz=blah androidboot.slot_suffix=_a somearg", &target,
+ &error));
+ g_assert_no_error (error);
+ g_assert_cmpstr (target, ==, "/ostree/root.a");
+ free (g_steal_pointer (&target));
+
+ // And an expected failure to parse a "c" suffix
+ g_assert (!otcore_get_ostree_target ("blah baz=blah androidboot.slot_suffix=_c somearg", &target,
+ &error));
+ g_assert (error);
+ g_assert (target == NULL);
+ g_clear_error (&error);
+
+ // And non-A/B androidboot
+ g_assert (otcore_get_ostree_target ("blah baz=blah androidboot.somethingelse somearg", &target,
+ &error));
+ g_assert_no_error (error);
+ g_assert_cmpstr (target, ==, "/ostree/root.a");
+ free (g_steal_pointer (&target));
+}
+
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
otcore_ed25519_init ();
g_test_add_func ("/ed25519", test_ed25519);
+ g_test_add_func ("/prepare-root-cmdline", test_prepare_root_cmdline);
return g_test_run ();
}