diff --git a/Makefile-libostree-defines.am b/Makefile-libostree-defines.am index 43e09281..4d290a88 100644 --- a/Makefile-libostree-defines.am +++ b/Makefile-libostree-defines.am @@ -28,6 +28,7 @@ libostree_public_headers = \ src/libostree/ostree-dummy-enumtypes.h \ src/libostree/ostree-mutable-tree.h \ src/libostree/ostree-repo.h \ + src/libostree/ostree-repo-os.h \ src/libostree/ostree-types.h \ src/libostree/ostree-repo-file.h \ src/libostree/ostree-diff.h \ diff --git a/Makefile-libostree.am b/Makefile-libostree.am index 7f9d7e67..98fbb289 100644 --- a/Makefile-libostree.am +++ b/Makefile-libostree.am @@ -92,6 +92,7 @@ libostree_1_la_SOURCES = \ src/libostree/ostree-ref.c \ src/libostree/ostree-remote.c \ src/libostree/ostree-remote-private.h \ + src/libostree/ostree-repo-os.c \ src/libostree/ostree-repo.c \ src/libostree/ostree-repo-checkout.c \ src/libostree/ostree-repo-commit.c \ @@ -186,10 +187,10 @@ endif # USE_GPGME symbol_files = $(top_srcdir)/src/libostree/libostree-released.sym -## Uncomment this include when adding new development symbols. -#if BUILDOPT_IS_DEVEL_BUILD -#symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym -#endif +# Uncomment this include when adding new development symbols. +if BUILDOPT_IS_DEVEL_BUILD +symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym +endif # http://blog.jgc.org/2007/06/escaping-comma-and-space-in-gnu-make.html wl_versionscript_arg = -Wl,--version-script= diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index 64bc68d2..400eb53a 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -152,6 +152,7 @@ ostree_validate_structureof_dirtree ostree_validate_structureof_dirmeta ostree_commit_get_parent ostree_commit_get_timestamp +ostree_commit_metadata_for_bootable ostree_commit_get_content_checksum ostree_commit_get_object_sizes OstreeCommitSizesEntry diff --git a/bash/ostree b/bash/ostree index 3cc2e04a..d1de8530 100644 --- a/bash/ostree +++ b/bash/ostree @@ -321,6 +321,7 @@ _ostree_commit() { --canonical-permissions --editor -e --generate-sizes + --bootable --link-checkout-speedup --no-xattrs --orphan diff --git a/man/ostree-commit.xml b/man/ostree-commit.xml index ab5d3415..81af7bf2 100644 --- a/man/ostree-commit.xml +++ b/man/ostree-commit.xml @@ -186,6 +186,13 @@ Boston, MA 02111-1307, USA. + + + + Inject standard metadata for a bootable Linux filesystem tree. + + + diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index e2d6efc4..541caaa2 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -22,6 +22,11 @@ - uncomment the include in Makefile-libostree.am */ +LIBOSTREE_2021.1 { +global: + ostree_commit_metadata_for_bootable; +} LIBOSTREE_2020.8; + /* Stub section for the stable release *after* this development one; don't * edit this other than to update the year. This is just a copy/paste * source. Replace $LASTSTABLE with the last stable version, and $NEWVERSION diff --git a/src/libostree/ostree-repo-os.c b/src/libostree/ostree-repo-os.c new file mode 100644 index 00000000..96beddda --- /dev/null +++ b/src/libostree/ostree-repo-os.c @@ -0,0 +1,83 @@ +/* + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include "libglnx.h" +#include "ostree.h" +#include "ostree-core-private.h" +#include "ostree-repo-os.h" +#include "otutil.h" + +/** + * ostree_commit_metadata_for_bootable: + * @root: Root filesystem to be committed + * @dict: Dictionary to update + * + * Update provided @dict with standard metadata for bootable OSTree commits. + * Since: 2021.1 + */ +_OSTREE_PUBLIC +gboolean +ostree_commit_metadata_for_bootable (GFile *root, GVariantDict *dict, GCancellable *cancellable, GError **error) +{ + g_autoptr(GFile) modules = g_file_resolve_relative_path (root, "usr/lib/modules"); + g_autoptr(GFileEnumerator) dir_enum + = g_file_enumerate_children (modules, OSTREE_GIO_FAST_QUERYINFO, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, error); + if (!dir_enum) + return glnx_prefix_error (error, "Opening usr/lib/modules"); + + g_autofree char *linux_release = NULL; + while (TRUE) + { + GFileInfo *child_info; + GFile *child_path; + if (!g_file_enumerator_iterate (dir_enum, &child_info, &child_path, + cancellable, error)) + return FALSE; + if (child_info == NULL) + break; + if (g_file_info_get_file_type (child_info) != G_FILE_TYPE_DIRECTORY) + continue; + + g_autoptr(GFile) kernel_path = g_file_resolve_relative_path (child_path, "vmlinuz"); + if (!g_file_query_exists (kernel_path, NULL)) + continue; + + if (linux_release != NULL) + return glnx_throw (error, "Multiple kernels found in /usr/lib/modules"); + + linux_release = g_strdup (g_file_info_get_name (child_info)); + } + + if (linux_release) + { + g_variant_dict_insert (dict, OSTREE_METADATA_KEY_BOOTABLE, "b", TRUE); + g_variant_dict_insert (dict, OSTREE_METADATA_KEY_LINUX, "s", linux_release); + return TRUE; + } + return glnx_throw (error, "No kernel found in /usr/lib/modules"); +} diff --git a/src/libostree/ostree-repo-os.h b/src/libostree/ostree-repo-os.h new file mode 100644 index 00000000..b2443768 --- /dev/null +++ b/src/libostree/ostree-repo-os.h @@ -0,0 +1,47 @@ +/* + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include +#include +#include + +G_BEGIN_DECLS + +/** + * OSTREE_METADATA_KEY_BOOTABLE: + * + * GVariant type `b`: Set if this commit is intended to be bootable + * Since: 2021.1 + */ +#define OSTREE_METADATA_KEY_BOOTABLE "ostree.bootable" +/** + * OSTREE_METADATA_KEY_LINUX: + * + * GVariant type `s`: Contains the Linux kernel release (i.e. `uname -r`) + * Since: 2021.1 + */ +#define OSTREE_METADATA_KEY_LINUX "ostree.linux" + +_OSTREE_PUBLIC +gboolean +ostree_commit_metadata_for_bootable (GFile *root, GVariantDict *dict, GCancellable *cancellable, GError **error); + +G_END_DECLS diff --git a/src/libostree/ostree.h b/src/libostree/ostree.h index 0308d0ed..e4847dbe 100644 --- a/src/libostree/ostree.h +++ b/src/libostree/ostree.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/src/ostree/ot-builtin-commit.c b/src/ostree/ot-builtin-commit.c index 48fa2928..7a23741e 100644 --- a/src/ostree/ot-builtin-commit.c +++ b/src/ostree/ot-builtin-commit.c @@ -35,6 +35,7 @@ static char *opt_subject; static char *opt_body; +static char *opt_bootable; static char *opt_body_file; static gboolean opt_editor; static char *opt_parent; @@ -112,6 +113,7 @@ static GOptionEntry options[] = { { "owner-uid", 0, 0, G_OPTION_ARG_INT, &opt_owner_uid, "Set file ownership user id", "UID" }, { "owner-gid", 0, 0, G_OPTION_ARG_INT, &opt_owner_gid, "Set file ownership group id", "GID" }, { "canonical-permissions", 0, 0, G_OPTION_ARG_NONE, &opt_canonical_permissions, "Canonicalize permissions in the same way bare-user does for hardlinked files", NULL }, + { "bootable", 0, 0, G_OPTION_ARG_NONE, &opt_bootable, "Flag this commit as a bootable OSTree (e.g. contains a Linux kernel)", NULL }, { "mode-ro-executables", 0, 0, G_OPTION_ARG_NONE, &opt_ro_executables, "Ensure executable files are not writable", NULL }, { "no-xattrs", 0, 0, G_OPTION_ARG_NONE, &opt_no_xattrs, "Do not import extended attributes", NULL }, { "selinux-policy", 0, 0, G_OPTION_ARG_FILENAME, &opt_selinux_policy, "Set SELinux labels based on policy in root filesystem PATH (may be /)", "PATH" }, @@ -501,7 +503,7 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio goto out; } - if (opt_metadata_strings || opt_metadata_variants || opt_metadata_keep) + if (opt_metadata_strings || opt_metadata_variants || opt_metadata_keep || opt_bootable) { g_autoptr(GVariantBuilder) builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); @@ -841,6 +843,17 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio fill_bindings (repo, old_metadata, &metadata); } + if (opt_bootable) + { + g_autoptr(GVariant) old_metadata = g_steal_pointer (&metadata); + g_auto(GVariantDict) bootmeta; + g_variant_dict_init (&bootmeta, old_metadata); + if (!ostree_commit_metadata_for_bootable (root, &bootmeta, cancellable, error)) + goto out; + + metadata = g_variant_ref_sink (g_variant_dict_end (&bootmeta)); + } + if (!opt_timestamp) { if (!ostree_repo_write_commit (repo, parent, opt_subject, commit_body, metadata, diff --git a/tests/admin-test.sh b/tests/admin-test.sh index 3aab74cc..b05893ae 100644 --- a/tests/admin-test.sh +++ b/tests/admin-test.sh @@ -65,6 +65,12 @@ assert_not_file_has_content status.txt "pending" assert_not_file_has_content status.txt "rollback" validate_bootloader +# Test the bootable and linux keys +${CMD_PREFIX} ostree --repo=sysroot/ostree/repo --print-metadata-key=ostree.linux show testos:testos/buildmaster/x86_64-runtime >out.txt +assert_file_has_content_literal out.txt 3.6.0 +${CMD_PREFIX} ostree --repo=sysroot/ostree/repo --print-metadata-key=ostree.bootable show testos:testos/buildmaster/x86_64-runtime >out.txt +assert_file_has_content_literal out.txt true + echo "ok deploy command" ${CMD_PREFIX} ostree admin --print-current-dir > curdir diff --git a/tests/basic-test.sh b/tests/basic-test.sh index 9227b0cd..75333f4d 100644 --- a/tests/basic-test.sh +++ b/tests/basic-test.sh @@ -21,7 +21,7 @@ set -euo pipefail -echo "1..$((86 + ${extra_basic_tests:-0}))" +echo "1..$((87 + ${extra_basic_tests:-0}))" CHECKOUT_U_ARG="" CHECKOUT_H_ARGS="-H" @@ -226,6 +226,13 @@ $OSTREE commit ${COMMIT_ARGS} -b test2-no-parent -s '' --parent=none $test_tmpdi assert_streq $($OSTREE log test2-no-parent |grep '^commit' | wc -l) "1" echo "ok commit no parent" +cd ${test_tmpdir} +if $OSTREE commit ${COMMIT_ARGS} -b test-bootable --bootable $test_tmpdir/checkout-test2-4 2>err.txt; then + fatal "committed non-bootable tree" +fi +assert_file_has_content err.txt "error: .*No such file or directory" +echo "ok commit fails bootable if no kernel" + cd ${test_tmpdir} # Do the --bind-ref=, so we store both branches sorted # in metadata and thus the checksums remain the same. diff --git a/tests/libtest.sh b/tests/libtest.sh index 7b654c52..ffb05c7b 100755 --- a/tests/libtest.sh +++ b/tests/libtest.sh @@ -415,11 +415,14 @@ setup_os_repository () { echo "an hmac file" > ${hmac_path} bootcsum=$(cat ${kernel_path} ${initramfs_path} | sha256sum | cut -f 1 -d ' ') export bootcsum + bootable_flag="" # Add the checksum for legacy dirs (/boot, /usr/lib/ostree-boot), but not # /usr/lib/modules. if [[ $bootdir != usr/lib/modules/* ]]; then mv ${kernel_path}{,-${bootcsum}} mv ${initramfs_path}{,-${bootcsum}} + else + bootable_flag="--bootable" fi echo "an executable" > usr/bin/sh @@ -439,12 +442,12 @@ EOF mkdir -p usr/etc/testdirectory echo "a default daemon file" > usr/etc/testdirectory/test - ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit --add-metadata-string version=1.0.9 -b testos/buildmaster/x86_64-runtime -s "Build" + ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit ${bootable_flag} --add-metadata-string version=1.0.9 -b testos/buildmaster/x86_64-runtime -s "Build" # Ensure these commits have distinct second timestamps sleep 2 echo "a new executable" > usr/bin/sh - ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit --add-metadata-string version=1.0.10 -b testos/buildmaster/x86_64-runtime -s "Build" + ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit ${bootable_flag} --add-metadata-string version=1.0.10 -b testos/buildmaster/x86_64-runtime -s "Build" cd ${test_tmpdir} rm -rf osdata-devel @@ -453,7 +456,7 @@ EOF cd osdata-devel mkdir -p usr/include echo "a development header" > usr/include/foo.h - ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit --add-metadata-string version=1.0.9 -b testos/buildmaster/x86_64-devel -s "Build" + ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit ${bootable_flag} --add-metadata-string version=1.0.9 -b testos/buildmaster/x86_64-devel -s "Build" ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo fsck -q