mirror of
https://github.com/ostreedev/ostree.git
synced 2025-03-19 22:50:35 +03:00
Add (non-atomic) support for GRUB2 + UEFI
We need basic support for UEFI - many newer servers don't support BIOS compatibility mode anymore. However, this patch only implements non-atomic because UEFI is FAT, and we can't do the previous design for OSTree of atomic swap of /boot/loader. The Fedora/RHEL UEFI layout has the kernels on a "real" /boot partition, and /boot/efi/EFI/$vendor just holds the grub2 UEFI binary and grub.cfg. Following this, /boot/loader is still on the OS boot partition, and we still atomically swap it. This potentially paves the way to atomic upgrades in the future. https://bugzilla.gnome.org/show_bug.cgi?id=724246
This commit is contained in:
parent
b43ce2329e
commit
0c89abee6d
@ -35,6 +35,8 @@ struct _OstreeBootloaderGrub2
|
||||
|
||||
OstreeSysroot *sysroot;
|
||||
GFile *config_path_bios;
|
||||
GFile *config_path_efi;
|
||||
gboolean is_efi;
|
||||
};
|
||||
|
||||
typedef GObjectClass OstreeBootloaderGrub2Class;
|
||||
@ -44,11 +46,79 @@ G_DEFINE_TYPE_WITH_CODE (OstreeBootloaderGrub2, _ostree_bootloader_grub2, G_TYPE
|
||||
G_IMPLEMENT_INTERFACE (OSTREE_TYPE_BOOTLOADER, _ostree_bootloader_grub2_bootloader_iface_init));
|
||||
|
||||
static gboolean
|
||||
_ostree_bootloader_grub2_query (OstreeBootloader *bootloader)
|
||||
_ostree_bootloader_grub2_query (OstreeBootloader *bootloader,
|
||||
gboolean *out_is_active,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (bootloader);
|
||||
gs_unref_object GFile* efi_basedir = NULL;
|
||||
gs_unref_object GFileInfo *file_info = NULL;
|
||||
|
||||
return g_file_query_exists (self->config_path_bios, NULL);
|
||||
if (g_file_query_exists (self->config_path_bios, NULL))
|
||||
{
|
||||
*out_is_active = TRUE;
|
||||
ret = TRUE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
efi_basedir = g_file_resolve_relative_path (self->sysroot->path, "boot/efi/EFI");
|
||||
|
||||
g_clear_object (&self->config_path_efi);
|
||||
|
||||
if (g_file_query_exists (efi_basedir, NULL))
|
||||
{
|
||||
gs_unref_object GFileEnumerator *direnum = NULL;
|
||||
|
||||
direnum = g_file_enumerate_children (efi_basedir, OSTREE_GIO_FAST_QUERYINFO,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable, error);
|
||||
if (!direnum)
|
||||
goto out;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
GFileInfo *file_info;
|
||||
const char *fname;
|
||||
gs_free char *subdir_grub_cfg = NULL;
|
||||
|
||||
if (!gs_file_enumerator_iterate (direnum, &file_info, NULL,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
if (file_info == NULL)
|
||||
break;
|
||||
|
||||
fname = g_file_info_get_name (file_info);
|
||||
if (strcmp (fname, "BOOT") == 0)
|
||||
continue;
|
||||
|
||||
if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY)
|
||||
continue;
|
||||
|
||||
subdir_grub_cfg = g_build_filename (gs_file_get_path_cached (efi_basedir), fname, "grub.cfg", NULL);
|
||||
|
||||
if (g_file_test (subdir_grub_cfg, G_FILE_TEST_EXISTS))
|
||||
{
|
||||
self->config_path_efi = g_file_new_for_path (subdir_grub_cfg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (self->config_path_efi)
|
||||
{
|
||||
self->is_efi = TRUE;
|
||||
*out_is_active = TRUE;
|
||||
ret = TRUE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else
|
||||
*out_is_active = FALSE;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *
|
||||
@ -58,7 +128,7 @@ _ostree_bootloader_grub2_get_name (OstreeBootloader *bootloader)
|
||||
}
|
||||
|
||||
gboolean
|
||||
_ostree_bootloader_grub2_generate_config (OstreeBootloaderGrub2 *self,
|
||||
_ostree_bootloader_grub2_generate_config (OstreeSysroot *sysroot,
|
||||
int bootversion,
|
||||
int target_fd,
|
||||
GCancellable *cancellable,
|
||||
@ -70,6 +140,7 @@ _ostree_bootloader_grub2_generate_config (OstreeBootloaderGrub2 *self,
|
||||
gs_unref_ptrarray GPtrArray *loader_configs = NULL;
|
||||
guint i;
|
||||
gsize bytes_written;
|
||||
gboolean is_efi;
|
||||
/* So... yeah. Just going to hardcode these. */
|
||||
static const char hardcoded_video[] = "load_video\n"
|
||||
"set gfxpayload=keep\n";
|
||||
@ -83,9 +154,12 @@ _ostree_bootloader_grub2_generate_config (OstreeBootloaderGrub2 *self,
|
||||
g_assert (grub2_boot_device_id != NULL);
|
||||
g_assert (grub2_prepare_root_cache != NULL);
|
||||
|
||||
/* Passed from the parent */
|
||||
is_efi = g_getenv ("_OSTREE_GRUB2_IS_EFI") != NULL;
|
||||
|
||||
out_stream = g_unix_output_stream_new (target_fd, FALSE);
|
||||
|
||||
if (!_ostree_sysroot_read_boot_loader_configs (self->sysroot, bootversion,
|
||||
if (!_ostree_sysroot_read_boot_loader_configs (sysroot, bootversion,
|
||||
&loader_configs,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
@ -127,7 +201,10 @@ _ostree_bootloader_grub2_generate_config (OstreeBootloaderGrub2 *self,
|
||||
"No \"linux\" key in bootloader config");
|
||||
goto out;
|
||||
}
|
||||
g_string_append (output, "linux16 ");
|
||||
if (is_efi)
|
||||
g_string_append (output, "linuxefi ");
|
||||
else
|
||||
g_string_append (output, "linux16 ");
|
||||
g_string_append (output, kernel);
|
||||
|
||||
options = ostree_bootconfig_parser_get (config, "options");
|
||||
@ -141,7 +218,10 @@ _ostree_bootloader_grub2_generate_config (OstreeBootloaderGrub2 *self,
|
||||
initrd = ostree_bootconfig_parser_get (config, "initrd");
|
||||
if (initrd)
|
||||
{
|
||||
g_string_append (output, "initrd16 ");
|
||||
if (is_efi)
|
||||
g_string_append (output, "initrdefi ");
|
||||
else
|
||||
g_string_append (output, "initrd16 ");
|
||||
g_string_append (output, initrd);
|
||||
g_string_append_c (output, '\n');
|
||||
}
|
||||
@ -168,19 +248,36 @@ _ostree_bootloader_grub2_write_config (OstreeBootloader *bootloader,
|
||||
{
|
||||
OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (bootloader);
|
||||
gboolean ret = FALSE;
|
||||
gs_unref_object GFile *efi_new_config_temp = NULL;
|
||||
gs_unref_object GFile *efi_orig_config = NULL;
|
||||
gs_unref_object GFile *new_config_path = NULL;
|
||||
gs_unref_object GSSubprocessContext *procctx = NULL;
|
||||
gs_unref_object GSSubprocess *proc = NULL;
|
||||
gs_strfreev char **child_env = g_get_environ ();
|
||||
gs_free char *bootversion_str = g_strdup_printf ("%u", (guint)bootversion);
|
||||
gs_unref_object GFile *config_path_efi_dir = NULL;
|
||||
|
||||
new_config_path = ot_gfile_resolve_path_printf (self->sysroot->path, "boot/loader.%d/grub.cfg",
|
||||
bootversion);
|
||||
if (self->is_efi)
|
||||
{
|
||||
config_path_efi_dir = g_file_get_parent (self->config_path_efi);
|
||||
new_config_path = g_file_get_child (config_path_efi_dir, "grub.cfg.new");
|
||||
/* We use grub2-mkconfig to write to a temporary file first */
|
||||
if (!ot_gfile_ensure_unlinked (new_config_path, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_config_path = ot_gfile_resolve_path_printf (self->sysroot->path, "boot/loader.%d/grub.cfg",
|
||||
bootversion);
|
||||
}
|
||||
|
||||
procctx = gs_subprocess_context_newv ("grub2-mkconfig", "-o",
|
||||
gs_file_get_path_cached (new_config_path),
|
||||
NULL);
|
||||
child_env = g_environ_setenv (child_env, "_OSTREE_GRUB2_BOOTVERSION", bootversion_str, TRUE);
|
||||
/* We have to pass our state to the child */
|
||||
if (self->is_efi)
|
||||
child_env = g_environ_setenv (child_env, "_OSTREE_GRUB2_IS_EFI", "1", TRUE);
|
||||
gs_subprocess_context_set_environment (procctx, child_env);
|
||||
gs_subprocess_context_set_stdout_disposition (procctx, GS_SUBPROCESS_STREAM_DISPOSITION_NULL);
|
||||
if (g_getenv ("OSTREE_DEBUG_GRUB2"))
|
||||
@ -206,6 +303,29 @@ rm -f ${grub_cfg}.new
|
||||
/* Now let's fdatasync() for the new file */
|
||||
if (!gs_file_sync_data (new_config_path, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (self->is_efi)
|
||||
{
|
||||
gs_unref_object GFile *config_path_efi_old = g_file_get_child (config_path_efi_dir, "grub.cfg.old");
|
||||
|
||||
/* copy current to old */
|
||||
if (!ot_gfile_ensure_unlinked (config_path_efi_old, cancellable, error))
|
||||
goto out;
|
||||
if (!g_file_copy (self->config_path_efi, config_path_efi_old,
|
||||
G_FILE_COPY_OVERWRITE, cancellable, NULL, NULL, error))
|
||||
goto out;
|
||||
if (!ot_gfile_ensure_unlinked (config_path_efi_old, cancellable, error))
|
||||
goto out;
|
||||
|
||||
/* NOTE: NON-ATOMIC REPLACEMENT; WE can't do anything else on FAT;
|
||||
* see https://bugzilla.gnome.org/show_bug.cgi?id=724246
|
||||
*/
|
||||
if (!ot_gfile_ensure_unlinked (new_config_path, cancellable, error))
|
||||
goto out;
|
||||
if (!gs_file_rename (new_config_path, self->config_path_efi,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
@ -215,7 +335,8 @@ rm -f ${grub_cfg}.new
|
||||
static gboolean
|
||||
_ostree_bootloader_grub2_is_atomic (OstreeBootloader *bootloader)
|
||||
{
|
||||
return TRUE;
|
||||
OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (bootloader);
|
||||
return !self->is_efi;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -225,6 +346,7 @@ _ostree_bootloader_grub2_finalize (GObject *object)
|
||||
|
||||
g_clear_object (&self->sysroot);
|
||||
g_clear_object (&self->config_path_bios);
|
||||
g_clear_object (&self->config_path_efi);
|
||||
|
||||
G_OBJECT_CLASS (_ostree_bootloader_grub2_parent_class)->finalize (object);
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ GType _ostree_bootloader_grub2_get_type (void) G_GNUC_CONST;
|
||||
|
||||
OstreeBootloaderGrub2 * _ostree_bootloader_grub2_new (OstreeSysroot *sysroot);
|
||||
|
||||
gboolean _ostree_bootloader_grub2_generate_config (OstreeBootloaderGrub2 *self, int bootversion, int target_fd, GCancellable *cancellable, GError **error);
|
||||
gboolean _ostree_bootloader_grub2_generate_config (OstreeSysroot *sysroot, int bootversion, int target_fd, GCancellable *cancellable, GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -42,11 +42,15 @@ G_DEFINE_TYPE_WITH_CODE (OstreeBootloaderSyslinux, _ostree_bootloader_syslinux,
|
||||
G_IMPLEMENT_INTERFACE (OSTREE_TYPE_BOOTLOADER, _ostree_bootloader_syslinux_bootloader_iface_init));
|
||||
|
||||
static gboolean
|
||||
_ostree_bootloader_syslinux_query (OstreeBootloader *bootloader)
|
||||
_ostree_bootloader_syslinux_query (OstreeBootloader *bootloader,
|
||||
gboolean *out_is_active,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
OstreeBootloaderSyslinux *self = OSTREE_BOOTLOADER_SYSLINUX (bootloader);
|
||||
|
||||
return g_file_query_file_type (self->config_path, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) == G_FILE_TYPE_SYMBOLIC_LINK;
|
||||
*out_is_active = g_file_query_file_type (self->config_path, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) == G_FILE_TYPE_SYMBOLIC_LINK;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static const char *
|
||||
|
@ -46,11 +46,15 @@ G_DEFINE_TYPE_WITH_CODE (OstreeBootloaderUboot, _ostree_bootloader_uboot, G_TYPE
|
||||
G_IMPLEMENT_INTERFACE (OSTREE_TYPE_BOOTLOADER, _ostree_bootloader_uboot_bootloader_iface_init));
|
||||
|
||||
static gboolean
|
||||
_ostree_bootloader_uboot_query (OstreeBootloader *bootloader)
|
||||
_ostree_bootloader_uboot_query (OstreeBootloader *bootloader,
|
||||
gboolean *out_is_active,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
OstreeBootloaderUboot *self = OSTREE_BOOTLOADER_UBOOT (bootloader);
|
||||
|
||||
return g_file_query_file_type (self->config_path, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) == G_FILE_TYPE_SYMBOLIC_LINK;
|
||||
*out_is_active = g_file_query_file_type (self->config_path, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) == G_FILE_TYPE_SYMBOLIC_LINK;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static const char *
|
||||
|
@ -30,11 +30,14 @@ _ostree_bootloader_default_init (OstreeBootloaderInterface *iface)
|
||||
}
|
||||
|
||||
gboolean
|
||||
_ostree_bootloader_query (OstreeBootloader *self)
|
||||
_ostree_bootloader_query (OstreeBootloader *self,
|
||||
gboolean *out_is_active,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (OSTREE_IS_BOOTLOADER (self), FALSE);
|
||||
|
||||
return OSTREE_BOOTLOADER_GET_IFACE (self)->query (self);
|
||||
return OSTREE_BOOTLOADER_GET_IFACE (self)->query (self, out_is_active, cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,7 +37,10 @@ struct _OstreeBootloaderInterface
|
||||
GTypeInterface g_iface;
|
||||
|
||||
/* virtual functions */
|
||||
gboolean (* query) (OstreeBootloader *self);
|
||||
gboolean (* query) (OstreeBootloader *bootloader,
|
||||
gboolean *out_is_active,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
const char * (* get_name) (OstreeBootloader *self);
|
||||
gboolean (* write_config) (OstreeBootloader *self,
|
||||
int bootversion,
|
||||
@ -48,7 +51,10 @@ struct _OstreeBootloaderInterface
|
||||
|
||||
GType _ostree_bootloader_get_type (void) G_GNUC_CONST;
|
||||
|
||||
gboolean _ostree_bootloader_query (OstreeBootloader *self);
|
||||
gboolean _ostree_bootloader_query (OstreeBootloader *bootloader,
|
||||
gboolean *out_is_active,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
const char *_ostree_bootloader_get_name (OstreeBootloader *self);
|
||||
|
||||
|
@ -29,9 +29,7 @@
|
||||
static gboolean
|
||||
impl_ostree_generate_grub2_config (OstreeSysroot *sysroot, int bootversion, int target_fd, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
gs_unref_object OstreeBootloaderGrub2 *grub2 = _ostree_bootloader_grub2_new (sysroot);
|
||||
|
||||
return _ostree_bootloader_grub2_generate_config (grub2, bootversion, target_fd, cancellable, error);
|
||||
return _ostree_bootloader_grub2_generate_config (sysroot, bootversion, target_fd, cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1598,9 +1598,12 @@ ostree_sysroot_write_deployments (OstreeSysroot *self,
|
||||
else
|
||||
{
|
||||
int new_bootversion = self->bootversion ? 0 : 1;
|
||||
gs_unref_object OstreeBootloader *bootloader = _ostree_sysroot_query_bootloader (self);
|
||||
gs_unref_object OstreeBootloader *bootloader = NULL;
|
||||
gs_unref_object GFile *new_loader_entries_dir = NULL;
|
||||
|
||||
if (!_ostree_sysroot_query_bootloader (self, &bootloader, cancellable, error))
|
||||
goto out;
|
||||
|
||||
new_loader_entries_dir = ot_gfile_resolve_path_printf (self->path, "boot/loader.%d/entries",
|
||||
new_bootversion);
|
||||
if (!gs_shutil_rm_rf (new_loader_entries_dir, cancellable, error))
|
||||
|
@ -75,7 +75,10 @@ _ostree_sysroot_get_devino (GFile *path,
|
||||
|
||||
char *_ostree_sysroot_join_lines (GPtrArray *lines);
|
||||
|
||||
OstreeBootloader *_ostree_sysroot_query_bootloader (OstreeSysroot *sysroot);
|
||||
gboolean _ostree_sysroot_query_bootloader (OstreeSysroot *sysroot,
|
||||
OstreeBootloader **out_bootloader,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -846,29 +846,46 @@ ostree_sysroot_get_repo (OstreeSysroot *self,
|
||||
/**
|
||||
* ostree_sysroot_query_bootloader:
|
||||
* @sysroot: Sysroot
|
||||
*
|
||||
* Returns: (transfer full): Currently active bootloader in @sysroot
|
||||
* @out_bootloader: (out) (transfer full) (allow-none): Return location for bootloader, may be %NULL
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*/
|
||||
OstreeBootloader *
|
||||
_ostree_sysroot_query_bootloader (OstreeSysroot *self)
|
||||
gboolean
|
||||
_ostree_sysroot_query_bootloader (OstreeSysroot *sysroot,
|
||||
OstreeBootloader **out_bootloader,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
OstreeBootloaderSyslinux *syslinux;
|
||||
OstreeBootloaderUboot *uboot;
|
||||
OstreeBootloaderGrub2 *grub2;
|
||||
gboolean ret = FALSE;
|
||||
gboolean is_active;
|
||||
gs_unref_object OstreeBootloader *ret_loader = NULL;
|
||||
|
||||
syslinux = _ostree_bootloader_syslinux_new (self);
|
||||
if (_ostree_bootloader_query ((OstreeBootloader*)syslinux))
|
||||
return (OstreeBootloader*) (syslinux);
|
||||
ret_loader = (OstreeBootloader*)_ostree_bootloader_syslinux_new (sysroot);
|
||||
if (!_ostree_bootloader_query (ret_loader, &is_active,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
if (!is_active)
|
||||
{
|
||||
g_object_unref (ret_loader);
|
||||
ret_loader = (OstreeBootloader*)_ostree_bootloader_grub2_new (sysroot);
|
||||
if (!_ostree_bootloader_query (ret_loader, &is_active,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
if (!is_active)
|
||||
{
|
||||
g_object_unref (ret_loader);
|
||||
ret_loader = (OstreeBootloader*)_ostree_bootloader_uboot_new (sysroot);
|
||||
if (!_ostree_bootloader_query (ret_loader, &is_active, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
if (!is_active)
|
||||
g_clear_object (&ret_loader);
|
||||
|
||||
grub2 = _ostree_bootloader_grub2_new (self);
|
||||
if (_ostree_bootloader_query ((OstreeBootloader*)grub2))
|
||||
return (OstreeBootloader*) (grub2);
|
||||
|
||||
uboot = _ostree_bootloader_uboot_new (self);
|
||||
if (_ostree_bootloader_query ((OstreeBootloader*)uboot))
|
||||
return (OstreeBootloader*) (uboot);
|
||||
|
||||
return NULL;
|
||||
ret = TRUE;
|
||||
gs_transfer_out_value (out_bootloader, &ret_loader);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
|
Loading…
x
Reference in New Issue
Block a user