From accb1f0602ffcfb977030380516cee64a29285f0 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 13 Nov 2023 17:33:57 +0100 Subject: [PATCH] Add `ostree admin post-copy` command This command will apply fs-verity on all objects that need it and needs to be called when an ostree deployment has been copied on a file-by-file basis, which would loose information such as fs-verity. This is needed by osbuild which works by creating the final image in a rootfs, and then separately copying that rootfs file-by-file to a loopback mounted filesystem image. --- Makefile-libostree.am | 6 +- Makefile-man.am | 2 +- Makefile-ostree.am | 1 + apidoc/ostree-sections.txt | 1 + man/index.xml | 4 ++ man/ostree-admin-post-copy.xml | 79 +++++++++++++++++++++++++ man/ostree-admin.xml | 1 + src/libostree/libostree-devel.sym | 5 ++ src/libostree/ostree-repo-private.h | 3 + src/libostree/ostree-repo-verity.c | 77 ++++++++++++++++++------ src/libostree/ostree-sysroot-cleanup.c | 67 +++++++++++++++++++++ src/libostree/ostree-sysroot.h | 4 ++ src/ostree/ot-admin-builtin-post-copy.c | 50 ++++++++++++++++ src/ostree/ot-admin-builtins.h | 1 + src/ostree/ot-builtin-admin.c | 2 + 15 files changed, 280 insertions(+), 23 deletions(-) create mode 100644 man/ostree-admin-post-copy.xml create mode 100644 src/ostree/ot-admin-builtin-post-copy.c diff --git a/Makefile-libostree.am b/Makefile-libostree.am index 9611473bc5..46f0999b6b 100644 --- a/Makefile-libostree.am +++ b/Makefile-libostree.am @@ -175,9 +175,9 @@ 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 +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/Makefile-man.am b/Makefile-man.am index da229bc7cb..096560b1ad 100644 --- a/Makefile-man.am +++ b/Makefile-man.am @@ -29,7 +29,7 @@ ostree-admin-config-diff.1 ostree-admin-deploy.1 \ ostree-admin-init-fs.1 ostree-admin-instutil.1 ostree-admin-stateroot-init.1 ostree-admin-os-init.1 \ ostree-admin-status.1 ostree-admin-set-origin.1 ostree-admin-switch.1 \ ostree-admin-undeploy.1 ostree-admin-upgrade.1 ostree-admin-unlock.1 \ -ostree-admin-pin.1 ostree-admin-set-default.1 \ +ostree-admin-pin.1 ostree-admin-post-copy.1 ostree-admin-set-default.1 \ ostree-admin.1 ostree-cat.1 ostree-checkout.1 ostree-checksum.1 \ ostree-commit.1 ostree-create-usb.1 ostree-export.1 \ ostree-config.1 ostree-diff.1 ostree-find-remotes.1 ostree-fsck.1 \ diff --git a/Makefile-ostree.am b/Makefile-ostree.am index 16bb04d02b..8d15a55f58 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -81,6 +81,7 @@ ostree_SOURCES += \ src/ostree/ot-admin-builtin-status.c \ src/ostree/ot-admin-builtin-switch.c \ src/ostree/ot-admin-builtin-pin.c \ + src/ostree/ot-admin-builtin-post-copy.c \ src/ostree/ot-admin-builtin-upgrade.c \ src/ostree/ot-admin-builtin-unlock.c \ src/ostree/ot-admin-builtins.h \ diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index af57d7c9d1..7a8966421f 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -560,6 +560,7 @@ ostree_sysroot_lock_async ostree_sysroot_lock_finish ostree_sysroot_unlock ostree_sysroot_unload +ostree_sysroot_update_post_copy ostree_sysroot_set_mount_namespace_in_use ostree_sysroot_is_booted ostree_sysroot_get_fd diff --git a/man/index.xml b/man/index.xml index 9f52cf78b7..63ae6e5bd6 100644 --- a/man/index.xml +++ b/man/index.xml @@ -56,6 +56,10 @@ License along with this library. If not, see . ostree-admin-pin1 + + ostree-admin-post-copy1 + + ostree-admin-set-origin1 diff --git a/man/ostree-admin-post-copy.xml b/man/ostree-admin-post-copy.xml new file mode 100644 index 0000000000..980e7cd0e7 --- /dev/null +++ b/man/ostree-admin-post-copy.xml @@ -0,0 +1,79 @@ + + + + + + + + + ostree admin post-copy + OSTree + + + + Developer + Colin + Walters + walters@verbum.org + + + + + + ostree admin post-copy + 1 + + + + ostree-admin-post-copy + Fix up sysroot after a (file based) copy + + + + + ostree admin post-copy OPTIONS + + + + + Description + + + Applies any fixes to a sysroot that are needed after having copyed it file by file. + This includes enabling fs-verity to any files that lack it, which can happen if + you copy a file. + + + + + Options + + + + ="PATH" + + + Path to the system to use rather than the current one. + + + + + diff --git a/man/ostree-admin.xml b/man/ostree-admin.xml index 85f4347d22..ba1885916d 100644 --- a/man/ostree-admin.xml +++ b/man/ostree-admin.xml @@ -72,6 +72,7 @@ License along with this library. If not, see . instutil os-init pin + post-copy set-origin status switch diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index 9168db734a..28d6154f3d 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -29,3 +29,8 @@ global: someostree_symbol_deleteme; } LIBOSTREE_2021.$LASTSTABLE; */ + +LIBOSTREE_2023.11 { +global: + ostree_sysroot_update_post_copy; +} LIBOSTREE_2023.4; diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h index ad6457ec85..e6b26ce50e 100644 --- a/src/libostree/ostree-repo-private.h +++ b/src/libostree/ostree-repo-private.h @@ -412,6 +412,9 @@ gboolean _ostree_tmpf_fsverity_core (GLnxTmpfile *tmpf, _OstreeFeatureSupport fs gboolean _ostree_tmpf_fsverity (OstreeRepo *self, GLnxTmpfile *tmpf, GBytes *signature, GError **error); +gboolean _ostree_ensure_fsverity (OstreeRepo *self, gboolean allow_enoent, int dirfd, + const char *path, gboolean *supported, GError **error); + gboolean _ostree_repo_verify_bindings (const char *collection_id, const char *ref_name, GVariant *commit, GError **error); diff --git a/src/libostree/ostree-repo-verity.c b/src/libostree/ostree-repo-verity.c index 6a7130c8a2..0dfd8607a4 100644 --- a/src/libostree/ostree-repo-verity.c +++ b/src/libostree/ostree-repo-verity.c @@ -102,28 +102,14 @@ _ostree_repo_parse_fsverity_config (OstreeRepo *self, GError **error) return TRUE; } -/* Wrapper around the fsverity ioctl, compressing the result to - * "success, unsupported or error". This is used for /boot where - * we enable verity if supported. - * */ -gboolean -_ostree_tmpf_fsverity_core (GLnxTmpfile *tmpf, _OstreeFeatureSupport fsverity_requested, - GBytes *signature, gboolean *supported, GError **error) +static gboolean +_ostree_fsverity_enable (int fd, gboolean allow_existing, gboolean *supported, GBytes *signature, + GError **error) { - /* Set this by default to simplify the code below */ if (supported) *supported = FALSE; - if (fsverity_requested == _OSTREE_FEATURE_NO) - return TRUE; - #ifdef HAVE_LINUX_FSVERITY_H - GLNX_AUTO_PREFIX_ERROR ("fsverity", error); - - /* fs-verity requires a read-only file descriptor */ - if (!glnx_tmpfile_reopen_rdonly (tmpf, error)) - return FALSE; - struct fsverity_enable_arg arg = { 0, }; @@ -135,7 +121,7 @@ _ostree_tmpf_fsverity_core (GLnxTmpfile *tmpf, _OstreeFeatureSupport fsverity_re arg.sig_size = signature ? g_bytes_get_size (signature) : 0; arg.sig_ptr = signature ? (guint64)g_bytes_get_data (signature, NULL) : 0; - if (ioctl (tmpf->fd, FS_IOC_ENABLE_VERITY, &arg) < 0) + if (ioctl (fd, FS_IOC_ENABLE_VERITY, &arg) < 0) { switch (errno) { @@ -143,13 +129,42 @@ _ostree_tmpf_fsverity_core (GLnxTmpfile *tmpf, _OstreeFeatureSupport fsverity_re case EOPNOTSUPP: return TRUE; default: - return glnx_throw_errno_prefix (error, "ioctl(FS_IOC_ENABLE_VERITY)"); + if (errno != EEXIST || !allow_existing) + return glnx_throw_errno_prefix (error, "ioctl(FS_IOC_ENABLE_VERITY)"); } } if (supported) *supported = TRUE; #endif + + return TRUE; +} + +/* Wrapper around the fsverity ioctl, compressing the result to + * "success, unsupported or error". This is used for /boot where + * we enable verity if supported. + * */ +gboolean +_ostree_tmpf_fsverity_core (GLnxTmpfile *tmpf, _OstreeFeatureSupport fsverity_requested, + GBytes *signature, gboolean *supported, GError **error) +{ + if (fsverity_requested == _OSTREE_FEATURE_NO) + { + if (supported) + *supported = FALSE; + return TRUE; + } + + GLNX_AUTO_PREFIX_ERROR ("fsverity", error); + + /* fs-verity requires a read-only file descriptor */ + if (!glnx_tmpfile_reopen_rdonly (tmpf, error)) + return FALSE; + + if (!_ostree_fsverity_enable (tmpf->fd, FALSE, supported, signature, error)) + return FALSE; + return TRUE; } @@ -206,3 +221,27 @@ _ostree_tmpf_fsverity (OstreeRepo *self, GLnxTmpfile *tmpf, GBytes *signature, G #endif return TRUE; } + +gboolean +_ostree_ensure_fsverity (OstreeRepo *self, gboolean allow_enoent, int dirfd, const char *path, + gboolean *supported, GError **error) +{ + glnx_autofd int fd = -1; + + if (!ot_openat_ignore_enoent (dirfd, path, &fd, error)) + return FALSE; + + if (fd == -1 && !allow_enoent) + return glnx_throw (error, "Unexpectedly missing file '%s', can't enable fs-verity", path); + + if (fd != -1) + { + if (!_ostree_fsverity_enable (fd, TRUE, supported, NULL, error)) + return FALSE; + + if (!supported && self->fs_verity_wanted == _OSTREE_FEATURE_YES) + return glnx_throw (error, "fsverity required but filesystem does not support it"); + } + + return TRUE; +} diff --git a/src/libostree/ostree-sysroot-cleanup.c b/src/libostree/ostree-sysroot-cleanup.c index 090b004484..ba9b3bacda 100644 --- a/src/libostree/ostree-sysroot-cleanup.c +++ b/src/libostree/ostree-sysroot-cleanup.c @@ -19,8 +19,10 @@ #include "config.h" +#include "ostree-core-private.h" #include "ostree-linuxfsutil.h" #include "ostree-repo-private.h" +#include "otcore.h" #include "otutil.h" #include "ostree-sysroot-private.h" @@ -573,3 +575,68 @@ _ostree_sysroot_cleanup_internal (OstreeSysroot *self, gboolean do_prune_repo, return TRUE; } + +/** + * ostree_sysroot_update_post_copy: + * @self: Sysroot + * @error: Error + * + * Update a sysroot as needed after having copied it into place using file-level + * operations. This enables options like fs-verity on the required files that may + * have been lost during the copy. + * + * Since: 2023.11 + */ +gboolean +ostree_sysroot_update_post_copy (OstreeSysroot *self, GCancellable *cancellable, GError **error) +{ + OstreeRepo *repo = ostree_sysroot_repo (self); + + if (repo->fs_verity_wanted == _OSTREE_FEATURE_NO) + return TRUE; + + g_autoptr (GHashTable) objects + = ostree_repo_list_objects_set (repo, OSTREE_REPO_LIST_OBJECTS_LOOSE, cancellable, error); + if (objects == NULL) + return FALSE; + + GLNX_HASH_TABLE_FOREACH (objects, GVariant *, key) + { + const char *checksum; + OstreeObjectType objtype; + + ostree_object_name_deserialize (key, &checksum, &objtype); + + char loose_path_buf[_OSTREE_LOOSE_PATH_MAX]; + _ostree_loose_path (loose_path_buf, checksum, objtype, repo->mode); + + gboolean supported; + if (!_ostree_ensure_fsverity (repo, FALSE, repo->objects_dir_fd, loose_path_buf, &supported, + error)) + return FALSE; + + if (!supported) + break; /* If not supported, skip rest */ + } + + g_autoptr (GPtrArray) all_deployment_dirs = NULL; + if (!list_all_deployment_directories (self, &all_deployment_dirs, cancellable, error)) + return FALSE; + g_assert (all_deployment_dirs); /* Pacify static analysis */ + for (guint i = 0; i < all_deployment_dirs->len; i++) + { + OstreeDeployment *deployment = all_deployment_dirs->pdata[i]; + g_autofree char *deployment_path = ostree_sysroot_get_deployment_dirpath (self, deployment); + + g_autofree char *cfs_file = g_build_filename (deployment_path, OSTREE_COMPOSEFS_NAME, NULL); + + gboolean supported; + if (!_ostree_ensure_fsverity (repo, TRUE, self->sysroot_fd, cfs_file, &supported, error)) + return FALSE; + + if (!supported) + break; /* If not supported, skip rest */ + } + + return TRUE; +} diff --git a/src/libostree/ostree-sysroot.h b/src/libostree/ostree-sysroot.h index e84007a918..b7ba6ac97b 100644 --- a/src/libostree/ostree-sysroot.h +++ b/src/libostree/ostree-sysroot.h @@ -237,6 +237,10 @@ void ostree_sysroot_query_deployments_for (OstreeSysroot *self, const char *osna _OSTREE_PUBLIC OstreeDeployment *ostree_sysroot_get_merge_deployment (OstreeSysroot *self, const char *osname); +_OSTREE_PUBLIC +gboolean ostree_sysroot_update_post_copy (OstreeSysroot *self, GCancellable *cancellable, + GError **error); + _OSTREE_PUBLIC GKeyFile *ostree_sysroot_origin_new_from_refspec (OstreeSysroot *self, const char *refspec); diff --git a/src/ostree/ot-admin-builtin-post-copy.c b/src/ostree/ot-admin-builtin-post-copy.c new file mode 100644 index 0000000000..3b96812d04 --- /dev/null +++ b/src/ostree/ot-admin-builtin-post-copy.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 Alexander Larsson + * + * 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 . + */ + +#include "config.h" + +#include "ostree-sysroot-private.h" + +#include "ostree.h" +#include "ot-admin-builtins.h" +#include "ot-admin-functions.h" +#include "ot-main.h" +#include "otutil.h" + +#include + +static GOptionEntry options[] = { { NULL } }; + +gboolean +ot_admin_builtin_post_copy (int argc, char **argv, OstreeCommandInvocation *invocation, + GCancellable *cancellable, GError **error) +{ + g_autoptr (GOptionContext) context = g_option_context_new (""); + + g_autoptr (OstreeSysroot) sysroot = NULL; + if (!ostree_admin_option_context_parse (context, options, &argc, &argv, + OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER, invocation, &sysroot, + cancellable, error)) + return FALSE; + + if (!ostree_sysroot_update_post_copy (sysroot, cancellable, error)) + return FALSE; + + return TRUE; +} diff --git a/src/ostree/ot-admin-builtins.h b/src/ostree/ot-admin-builtins.h index 0464a81860..1f94e414d9 100644 --- a/src/ostree/ot-admin-builtins.h +++ b/src/ostree/ot-admin-builtins.h @@ -48,6 +48,7 @@ BUILTINPROTO (diff); BUILTINPROTO (switch); BUILTINPROTO (upgrade); BUILTINPROTO (kargs); +BUILTINPROTO (post_copy); #undef BUILTINPROTO diff --git a/src/ostree/ot-builtin-admin.c b/src/ostree/ot-builtin-admin.c index e48f91e4c5..3de552380f 100644 --- a/src/ostree/ot-builtin-admin.c +++ b/src/ostree/ot-builtin-admin.c @@ -51,6 +51,8 @@ static OstreeCommand admin_subcommands[] = { "Initialize empty state for given operating system" }, { "pin", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_pin, "Change the \"pinning\" state of a deployment" }, + { "post-copy", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_post_copy, + "Update the repo and deployments as needed after a copy" }, { "set-origin", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_set_origin, "Set Origin and create a new origin file" }, { "status", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_status, "List deployments" },