diff --git a/Makefile-man.am b/Makefile-man.am index 8ccbba8c6e..fee5b89721 100644 --- a/Makefile-man.am +++ b/Makefile-man.am @@ -26,7 +26,7 @@ ostree-admin-config-diff.1 ostree-admin-deploy.1 \ ostree-admin-init-fs.1 ostree-admin-instutil.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-pin.1 ostree-admin-esp-upgrade.1 \ ostree-admin.1 ostree-cat.1 ostree-checkout.1 ostree-checksum.1 \ ostree-commit.1 ostree-create-usb.1 ostree-export.1 ostree-gpg-sign.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 8d352e38fd..7ae761eb3b 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -79,6 +79,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-esp-upgrade.c \ src/ostree/ot-admin-builtin-upgrade.c \ src/ostree/ot-admin-builtin-unlock.c \ src/ostree/ot-admin-builtins.h \ diff --git a/Makefile-tests.am b/Makefile-tests.am index 2c0916f620..9a2bdc8f46 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -100,6 +100,7 @@ _installed_or_uninstalled_test_scripts = \ tests/test-admin-deploy-karg.sh \ tests/test-admin-deploy-switch.sh \ tests/test-admin-deploy-etcmerge-cornercases.sh \ + tests/test-admin-esp-upgrade.sh \ tests/test-admin-deploy-uboot.sh \ tests/test-admin-deploy-grub2.sh \ tests/test-admin-deploy-none.sh \ diff --git a/man/ostree-admin-esp-upgrade.xml b/man/ostree-admin-esp-upgrade.xml new file mode 100644 index 0000000000..48158debb6 --- /dev/null +++ b/man/ostree-admin-esp-upgrade.xml @@ -0,0 +1,70 @@ + + + + + + + + + ostree admin esp-upgrade + OSTree + + + + Developer + Javier + Martinez Canillas + javierm@redhat.com + + + + + + ostree admin esp-upgrade + 1 + + + + ostree-admin-esp-upgrade + Upgrade the EFI System Partition (ESP) with files from the current deployment + + + + + ostree admin esp-upgrade + + + + + Description + + + Upgrade the EFI System Partition (ESP) with the files in the /usr/lib/ostree-boot/efi directory of the current deployment. + + + + + Example + $ ostree admin esp-upgrade + + diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index 1096b0b071..e807f4cd12 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -25,16 +25,12 @@ #include #include #include -#include #include #include #include #include #include -#ifdef HAVE_LIBMOUNT -#include -#endif #ifdef HAVE_LIBSYSTEMD #include #endif @@ -86,15 +82,6 @@ symlink_at_replace (const char *oldpath, return TRUE; } -static GLnxFileCopyFlags -sysroot_flags_to_copy_flags (GLnxFileCopyFlags defaults, - OstreeSysrootDebugFlags sysrootflags) -{ - if (sysrootflags & OSTREE_SYSROOT_DEBUG_NO_XATTRS) - defaults |= GLNX_FILE_COPY_NOXATTRS; - return defaults; -} - /* Try a hardlink if we can, otherwise fall back to copying. Used * right now for kernels/initramfs/device trees in /boot, where we can just * hardlink if we're on the same partition. @@ -139,101 +126,6 @@ install_into_boot (OstreeSePolicy *sepolicy, return TRUE; } -/* Copy ownership, mode, and xattrs from source directory to destination */ -static gboolean -dirfd_copy_attributes_and_xattrs (int src_parent_dfd, - const char *src_name, - int src_dfd, - int dest_dfd, - OstreeSysrootDebugFlags flags, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GVariant) xattrs = NULL; - - /* Clone all xattrs first, so we get the SELinux security context - * right. This will allow other users access if they have ACLs, but - * oh well. - */ - if (!(flags & OSTREE_SYSROOT_DEBUG_NO_XATTRS)) - { - if (!glnx_dfd_name_get_all_xattrs (src_parent_dfd, src_name, - &xattrs, cancellable, error)) - return FALSE; - if (!glnx_fd_set_all_xattrs (dest_dfd, xattrs, - cancellable, error)) - return FALSE; - } - - struct stat src_stbuf; - if (!glnx_fstat (src_dfd, &src_stbuf, error)) - return FALSE; - if (fchown (dest_dfd, src_stbuf.st_uid, src_stbuf.st_gid) != 0) - return glnx_throw_errno_prefix (error, "fchown"); - if (fchmod (dest_dfd, src_stbuf.st_mode) != 0) - return glnx_throw_errno_prefix (error, "fchmod"); - - return TRUE; -} - -static gboolean -copy_dir_recurse (int src_parent_dfd, - int dest_parent_dfd, - const char *name, - OstreeSysrootDebugFlags flags, - GCancellable *cancellable, - GError **error) -{ - g_auto(GLnxDirFdIterator) src_dfd_iter = { 0, }; - glnx_autofd int dest_dfd = -1; - struct dirent *dent; - - if (!glnx_dirfd_iterator_init_at (src_parent_dfd, name, TRUE, &src_dfd_iter, error)) - return FALSE; - - /* Create with mode 0700, we'll fchmod/fchown later */ - if (!glnx_ensure_dir (dest_parent_dfd, name, 0700, error)) - return FALSE; - - if (!glnx_opendirat (dest_parent_dfd, name, TRUE, &dest_dfd, error)) - return FALSE; - - if (!dirfd_copy_attributes_and_xattrs (src_parent_dfd, name, src_dfd_iter.fd, dest_dfd, - flags, cancellable, error)) - return FALSE; - - while (TRUE) - { - struct stat child_stbuf; - - if (!glnx_dirfd_iterator_next_dent (&src_dfd_iter, &dent, cancellable, error)) - return FALSE; - if (dent == NULL) - break; - - if (!glnx_fstatat (src_dfd_iter.fd, dent->d_name, &child_stbuf, - AT_SYMLINK_NOFOLLOW, error)) - return FALSE; - - if (S_ISDIR (child_stbuf.st_mode)) - { - if (!copy_dir_recurse (src_dfd_iter.fd, dest_dfd, dent->d_name, - flags, cancellable, error)) - return FALSE; - } - else - { - if (!glnx_file_copy_at (src_dfd_iter.fd, dent->d_name, &child_stbuf, - dest_dfd, dent->d_name, - sysroot_flags_to_copy_flags (GLNX_FILE_COPY_OVERWRITE, flags), - cancellable, error)) - return FALSE; - } - } - - return TRUE; -} - /* If a chain of directories is added, this function will ensure * they're created. */ @@ -290,8 +182,8 @@ ensure_directory_from_template (int orig_etc_fd, if (!glnx_opendirat (new_etc_fd, path, TRUE, &target_dfd, error)) return FALSE; - if (!dirfd_copy_attributes_and_xattrs (modified_etc_fd, path, src_dfd, target_dfd, - flags, cancellable, error)) + if (!ot_dirfd_copy_attributes_and_xattrs (modified_etc_fd, path, src_dfd, target_dfd, + flags, cancellable, error)) return FALSE; if (out_dfd) @@ -365,15 +257,15 @@ copy_modified_config_file (int orig_etc_fd, if (S_ISDIR (modified_stbuf.st_mode)) { - if (!copy_dir_recurse (modified_etc_fd, new_etc_fd, path, flags, - cancellable, error)) + if (!ot_copy_dir_recurse (modified_etc_fd, new_etc_fd, path, flags, + cancellable, error)) return FALSE; } else if (S_ISLNK (modified_stbuf.st_mode) || S_ISREG (modified_stbuf.st_mode)) { if (!glnx_file_copy_at (modified_etc_fd, path, &modified_stbuf, new_etc_fd, path, - sysroot_flags_to_copy_flags (GLNX_FILE_COPY_OVERWRITE, flags), + ot_sysroot_flags_to_copy_flags (GLNX_FILE_COPY_OVERWRITE, flags), cancellable, error)) return FALSE; } @@ -1979,57 +1871,6 @@ cleanup_legacy_current_symlinks (OstreeSysroot *self, return TRUE; } -/* Detect whether or not @path refers to a read-only mountpoint. This is - * currently just used to handle a potentially read-only /boot by transiently - * remounting it read-write. In the future we might also do this for e.g. - * /sysroot. - */ -static gboolean -is_ro_mount (const char *path) -{ -#ifdef HAVE_LIBMOUNT - /* Dragging in all of this crud is apparently necessary just to determine - * whether something is a mount point. - * - * Systemd has a totally different implementation in - * src/basic/mount-util.c. - */ - struct libmnt_table *tb = mnt_new_table_from_file ("/proc/self/mountinfo"); - struct libmnt_fs *fs; - struct libmnt_cache *cache; - gboolean is_mount = FALSE; - struct statvfs stvfsbuf; - - if (!tb) - return FALSE; - - /* to canonicalize all necessary paths */ - cache = mnt_new_cache (); - mnt_table_set_cache (tb, cache); - - fs = mnt_table_find_target(tb, path, MNT_ITER_BACKWARD); - is_mount = fs && mnt_fs_get_target (fs); -#ifdef HAVE_MNT_UNREF_CACHE - mnt_unref_table (tb); - mnt_unref_cache (cache); -#else - mnt_free_table (tb); - mnt_free_cache (cache); -#endif - - if (!is_mount) - return FALSE; - - /* We *could* parse the options, but it seems more reliable to - * introspect the actual mount at runtime. - */ - if (statvfs (path, &stvfsbuf) == 0) - return (stvfsbuf.f_flag & ST_RDONLY) != 0; - -#endif - return FALSE; -} - /** * ostree_sysroot_write_deployments: * @self: Sysroot @@ -2331,7 +2172,7 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self, { gboolean boot_was_ro_mount = FALSE; if (self->booted_deployment) - boot_was_ro_mount = is_ro_mount ("/boot"); + boot_was_ro_mount = ot_is_ro_mount ("/boot"); g_debug ("boot is ro: %s", boot_was_ro_mount ? "yes" : "no"); diff --git a/src/libostree/ostree-sysroot-private.h b/src/libostree/ostree-sysroot-private.h index e4b2039e2f..eef50160ac 100644 --- a/src/libostree/ostree-sysroot-private.h +++ b/src/libostree/ostree-sysroot-private.h @@ -28,19 +28,6 @@ G_BEGIN_DECLS -typedef enum { - - /* Don't flag deployments as immutable. */ - OSTREE_SYSROOT_DEBUG_MUTABLE_DEPLOYMENTS = 1 << 0, - /* See https://github.com/ostreedev/ostree/pull/759 */ - OSTREE_SYSROOT_DEBUG_NO_XATTRS = 1 << 1, - /* https://github.com/ostreedev/ostree/pull/1049 */ - OSTREE_SYSROOT_DEBUG_TEST_FIFREEZE = 1 << 2, - /* This is a temporary flag until we fully drop the explicit `systemctl start - * ostree-finalize-staged.service` so that tests can exercise the new path unit. */ - OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH = 1 << 3, -} OstreeSysrootDebugFlags; - /** * OstreeSysroot: * Internal struct diff --git a/src/libotutil/ot-fs-utils.c b/src/libotutil/ot-fs-utils.c index c4fcd56f11..a9948a3eea 100644 --- a/src/libotutil/ot-fs-utils.c +++ b/src/libotutil/ot-fs-utils.c @@ -23,11 +23,17 @@ #include "ot-fs-utils.h" #include "libglnx.h" +#include +#include #include #include #include #include +#ifdef HAVE_LIBMOUNT +#include +#endif + /* Convert a fd-relative path to a GFile* - use * for legacy code. */ @@ -247,3 +253,172 @@ ot_parse_file_by_line (const char *path, return TRUE; } + +/* Copy ownership, mode, and xattrs from source directory to destination */ +gboolean +ot_dirfd_copy_attributes_and_xattrs (int src_parent_dfd, + const char *src_name, + int src_dfd, + int dest_dfd, + OstreeSysrootDebugFlags flags, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GVariant) xattrs = NULL; + + /* Clone all xattrs first, so we get the SELinux security context + * right. This will allow other users access if they have ACLs, but + * oh well. + */ + if (!(flags & OSTREE_SYSROOT_DEBUG_NO_XATTRS)) + { + if (!glnx_dfd_name_get_all_xattrs (src_parent_dfd, src_name, + &xattrs, cancellable, error)) + return FALSE; + if (!glnx_fd_set_all_xattrs (dest_dfd, xattrs, + cancellable, error)) + return FALSE; + } + + struct stat src_stbuf; + if (!glnx_fstat (src_dfd, &src_stbuf, error)) + return FALSE; + if (fchown (dest_dfd, src_stbuf.st_uid, src_stbuf.st_gid) != 0) + return glnx_throw_errno_prefix (error, "fchown"); + if (fchmod (dest_dfd, src_stbuf.st_mode) != 0) + return glnx_throw_errno_prefix (error, "fchmod"); + + return TRUE; +} + +gboolean +ot_copy_dir_recurse (int src_parent_dfd, + int dest_parent_dfd, + const char *name, + OstreeSysrootDebugFlags flags, + GCancellable *cancellable, + GError **error) +{ + g_auto(GLnxDirFdIterator) src_dfd_iter = { 0, }; + glnx_autofd int dest_dfd = -1; + struct dirent *dent; + + if (!glnx_dirfd_iterator_init_at (src_parent_dfd, name, TRUE, &src_dfd_iter, error)) + return FALSE; + + /* Create with mode 0700, we'll fchmod/fchown later */ + if (!glnx_ensure_dir (dest_parent_dfd, name, 0700, error)) + return FALSE; + + if (!glnx_opendirat (dest_parent_dfd, name, TRUE, &dest_dfd, error)) + return FALSE; + + if (!ot_dirfd_copy_attributes_and_xattrs (src_parent_dfd, name, src_dfd_iter.fd, dest_dfd, + flags, cancellable, error)) + return FALSE; + + while (TRUE) + { + struct stat child_stbuf; + + if (!glnx_dirfd_iterator_next_dent (&src_dfd_iter, &dent, cancellable, error)) + return FALSE; + if (dent == NULL) + break; + + if (!glnx_fstatat (src_dfd_iter.fd, dent->d_name, &child_stbuf, + AT_SYMLINK_NOFOLLOW, error)) + return FALSE; + + if (S_ISDIR (child_stbuf.st_mode)) + { + if (!ot_copy_dir_recurse (src_dfd_iter.fd, dest_dfd, dent->d_name, + flags, cancellable, error)) + return FALSE; + } + else + { + if (!glnx_file_copy_at (src_dfd_iter.fd, dent->d_name, &child_stbuf, + dest_dfd, dent->d_name, + ot_sysroot_flags_to_copy_flags (GLNX_FILE_COPY_OVERWRITE, flags), + cancellable, error)) + return FALSE; + } + } + + return TRUE; +} + +/* Detect whether or not @path refers to a mountpoint. If is a mountpoint + * the struct statvfs .f_flag is returned in @flag to get the mount flags. + */ +static gboolean +is_mount(const char *path, unsigned long *flag) +{ +#ifdef HAVE_LIBMOUNT + /* Dragging in all of this crud is apparently necessary just to determine + * whether something is a mount point. + * + * Systemd has a totally different implementation in + * src/basic/mount-util.c. + */ + struct libmnt_table *tb = mnt_new_table_from_file ("/proc/self/mountinfo"); + struct libmnt_fs *fs; + struct libmnt_cache *cache; + gboolean is_mount = FALSE; + struct statvfs stvfsbuf; + + if (!tb) + return FALSE; + + /* to canonicalize all necessary paths */ + cache = mnt_new_cache (); + mnt_table_set_cache (tb, cache); + + fs = mnt_table_find_target(tb, path, MNT_ITER_BACKWARD); + is_mount = fs && mnt_fs_get_target (fs); +#ifdef HAVE_MNT_UNREF_CACHE + mnt_unref_table (tb); + mnt_unref_cache (cache); +#else + mnt_free_table (tb); + mnt_free_cache (cache); +#endif + + if (!is_mount) + return FALSE; + + /* We *could* parse the options, but it seems more reliable to + * introspect the actual mount at runtime. + */ + if (statvfs (path, &stvfsbuf) == 0) + { + *flag = stvfsbuf.f_flag; + return TRUE; + } + +#endif + return FALSE; +} + +/* Detect whether or not @path refers to a read-only mountpoint. This is + * currently just used to handle a potentially read-only /boot by transiently + * remounting it read-write. In the future we might also do this for e.g. + * /sysroot. + */ +gboolean +ot_is_ro_mount (const char *path) +{ + unsigned long flag; + return is_mount (path, &flag) && (flag & ST_RDONLY) != 0; +} + +/* Detect whether or not @path refers to a mountpoint that is not read-only. + * This is currently used to check if /boot/efi is a read-write mountpoint. + */ +gboolean +ot_is_rw_mount (const char *path) +{ + unsigned long flag; + return is_mount (path, &flag) && (flag & ST_RDONLY) == 0; +} diff --git a/src/libotutil/ot-fs-utils.h b/src/libotutil/ot-fs-utils.h index 74a0fed6d8..ade48788da 100644 --- a/src/libotutil/ot-fs-utils.h +++ b/src/libotutil/ot-fs-utils.h @@ -26,6 +26,19 @@ G_BEGIN_DECLS +typedef enum { + + /* Don't flag deployments as immutable. */ + OSTREE_SYSROOT_DEBUG_MUTABLE_DEPLOYMENTS = 1 << 0, + /* See https://github.com/ostreedev/ostree/pull/759 */ + OSTREE_SYSROOT_DEBUG_NO_XATTRS = 1 << 1, + /* https://github.com/ostreedev/ostree/pull/1049 */ + OSTREE_SYSROOT_DEBUG_TEST_FIFREEZE = 1 << 2, + /* This is a temporary flag until we fully drop the explicit `systemctl start + * ostree-finalize-staged.service` so that tests can exercise the new path unit. */ + OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH = 1 << 3, +} OstreeSysrootDebugFlags; + /* A little helper to call unlinkat() as a cleanup * function. Mostly only necessary to handle * deletion of temporary symlinks. @@ -52,6 +65,15 @@ ot_cleanup_unlinkat (OtCleanupUnlinkat *cleanup) } G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(OtCleanupUnlinkat, ot_cleanup_unlinkat) +static inline GLnxFileCopyFlags +ot_sysroot_flags_to_copy_flags (GLnxFileCopyFlags defaults, + OstreeSysrootDebugFlags sysrootflags) +{ + if (sysrootflags & OSTREE_SYSROOT_DEBUG_NO_XATTRS) + defaults |= GLNX_FILE_COPY_NOXATTRS; + return defaults; +} + GFile * ot_fdrel_to_gfile (int dfd, const char *path); gboolean ot_readlinkat_gfile_info (int dfd, @@ -97,4 +119,27 @@ ot_parse_file_by_line (const char *path, GCancellable *cancellable, GError **error); +gboolean +ot_dirfd_copy_attributes_and_xattrs (int src_parent_dfd, + const char *src_name, + int src_dfd, + int dest_dfd, + OstreeSysrootDebugFlags flags, + GCancellable *cancellable, + GError **error); + +gboolean +ot_copy_dir_recurse (int src_parent_dfd, + int dest_parent_dfd, + const char *name, + OstreeSysrootDebugFlags flags, + GCancellable *cancellable, + GError **error); + +gboolean +ot_is_ro_mount (const char *path); + +gboolean +ot_is_rw_mount (const char *path); + G_END_DECLS diff --git a/src/ostree/ot-admin-builtin-esp-upgrade.c b/src/ostree/ot-admin-builtin-esp-upgrade.c new file mode 100644 index 0000000000..681e16df45 --- /dev/null +++ b/src/ostree/ot-admin-builtin-esp-upgrade.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2019 Red Hat, Inc. + * + * 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. + * + * Author: Javier Martinez Canillas + */ + +#include "config.h" + +#include "ostree-sysroot-private.h" +#include "ot-main.h" +#include "ot-admin-builtins.h" +#include "ot-admin-functions.h" +#include "otutil.h" + +static GOptionEntry options[] = { + { NULL } +}; + +gboolean +ot_admin_builtin_esp_upgrade (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_UNLOCKED, + invocation, &sysroot, cancellable, error)) + return FALSE; + + g_autoptr(OstreeRepo) repo = NULL; + if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) + return FALSE; + + g_autoptr(GPtrArray) deployments = ostree_sysroot_get_deployments (sysroot); + + if (deployments->len == 0) + { + g_print ("No deployments.\n"); + return TRUE; + } + + OstreeDeployment *deployment = ostree_sysroot_get_booted_deployment (sysroot); + + if (!deployment) + deployment = ot_admin_get_indexed_deployment (sysroot, 0, error); + + struct stat stbuf; + + if (!glnx_fstatat_allow_noent (sysroot->sysroot_fd, "sys/firmware/efi", &stbuf, AT_SYMLINK_NOFOLLOW, error)) + return FALSE; + + if (errno == ENOENT) + { + g_print ("Not an EFI system.\n"); + return TRUE; + } + + if (!ot_is_rw_mount ("/boot/efi")) + { + if (ot_is_ro_mount ("/boot/efi")) + g_print ("The ESP can't be updated because /boot/efi is a read-only mountpoint.\n"); + else + g_print ("Only ESP mounted in /boot/efi is supported.\n"); + return TRUE; + } + + g_autofree char *deployment_path = ostree_sysroot_get_deployment_dirpath (sysroot, deployment); + + g_autofree char *new_esp_path = g_strdup_printf ("%s/usr/lib/ostree-boot", deployment_path); + + GLNX_AUTO_PREFIX_ERROR ("During copy files to the ESP", error); + glnx_autofd int old_esp_fd = -1; + if (!glnx_opendirat (sysroot->sysroot_fd, "boot", TRUE, &old_esp_fd, error)) + return FALSE; + + glnx_autofd int new_esp_fd = -1; + if (!glnx_opendirat (sysroot->sysroot_fd, new_esp_path, TRUE, &new_esp_fd, error)) + return FALSE; + + /* The ESP filesystem is vfat so don't attempt to copy ownership, mode, and xattrs */ + const OstreeSysrootDebugFlags flags = sysroot->debug_flags | OSTREE_SYSROOT_DEBUG_NO_XATTRS; + + if (!ot_copy_dir_recurse (new_esp_fd, old_esp_fd, "efi", flags , cancellable, error)) + return FALSE; + + return TRUE; +} diff --git a/src/ostree/ot-admin-builtins.h b/src/ostree/ot-admin-builtins.h index d88fc0b907..5efb7101b0 100644 --- a/src/ostree/ot-admin-builtins.h +++ b/src/ostree/ot-admin-builtins.h @@ -40,6 +40,7 @@ BUILTINPROTO(undeploy); BUILTINPROTO(deploy); BUILTINPROTO(cleanup); BUILTINPROTO(pin); +BUILTINPROTO(esp_upgrade); BUILTINPROTO(finalize_staged); BUILTINPROTO(unlock); BUILTINPROTO(status); diff --git a/src/ostree/ot-builtin-admin.c b/src/ostree/ot-builtin-admin.c index 9f1a61562a..531a40da47 100644 --- a/src/ostree/ot-builtin-admin.c +++ b/src/ostree/ot-builtin-admin.c @@ -57,6 +57,9 @@ static OstreeCommand admin_subcommands[] = { { "pin", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_pin, "Change the \"pinning\" state of a deployment" }, + { "esp-upgrade", OSTREE_BUILTIN_FLAG_NO_REPO, + ot_admin_builtin_esp_upgrade, + "Upgrade the ESP with files from the current deployment" }, { "set-origin", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_set_origin, "Set Origin and create a new origin file" }, diff --git a/tests/test-admin-esp-upgrade.sh b/tests/test-admin-esp-upgrade.sh new file mode 100755 index 0000000000..ba5bcf60cb --- /dev/null +++ b/tests/test-admin-esp-upgrade.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# +# Copyright (C) 2019 Red Hat, Inc +# +# 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. + +set -euo pipefail + +. $(dirname $0)/libtest.sh + +id=$(id -u) + +if test ${id} != 0; then + skip "this test needs to set up mount namespaces, rerun as root" +fi + +# Exports OSTREE_SYSROOT so --sysroot not needed. +setup_os_repository "archive" "sysroot.bootloader none" + +echo "1..1" + +${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime +rev=$(${CMD_PREFIX} ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime) +export rev +echo "rev=${rev}" +${CMD_PREFIX} ostree admin deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime +assert_has_dir sysroot/boot/ostree/testos-${bootcsum} + +usr=sysroot/ostree/deploy/testos/deploy/${rev}.0/usr + +# Create /usr/lib/ostree-boot/efi dir and some test files +mkdir -p ${usr}/lib/ostree-boot/efi/EFI +touch ${usr}/lib/ostree-boot/efi/file-a +touch ${usr}/lib/ostree-boot/efi/EFI/file-b + +cd ${test_tmpdir} + +# ostree-admin-esp-upgrade checks if /sys/firmware/efi exists +# and /boot/efi is a mountpoint. +mkdir -p sysroot/sys/firmware/efi +mkdir -p sysroot/boot/efi +mount --bind sysroot/boot/efi /boot/efi + +${CMD_PREFIX} ostree admin esp-upgrade + +assert_has_file sysroot/boot/efi/file-a +assert_has_file sysroot/boot/efi/EFI/file-b + +umount /boot/efi + +echo "ok"