Package: release.debian.org Severity: normal User: release.debian....@packages.debian.org Usertags: unblock
Please review and unblock grub2 2.02+dfsg1-15 (just uploaded, so not quite in the archive yet). I still have some more RC-bug-fixing to do, but the EFI variable storage changes here are probably going to be the most complicated remaining change for buster, so I want to maximise the time available for finding bugs in it. unblock grub2/2.02+dfsg1-15 Thanks, -- Colin Watson [cjwat...@debian.org]
diff -Nru grub2-2.02+dfsg1/debian/.git-dpm grub2-2.02+dfsg1/debian/.git-dpm --- grub2-2.02+dfsg1/debian/.git-dpm 2019-02-28 09:35:09.000000000 +0000 +++ grub2-2.02+dfsg1/debian/.git-dpm 2019-03-23 00:39:00.000000000 +0000 @@ -1,6 +1,6 @@ # see git-dpm(1) from git-dpm package -0cc1bd74c82c94ad93049a7298987c8f155cd0d2 -0cc1bd74c82c94ad93049a7298987c8f155cd0d2 +649e5a39cc5ddf42f6853a0bf818685a625f7cab +649e5a39cc5ddf42f6853a0bf818685a625f7cab 59aeb1cfaa3d5bfd7bbeeee0f0d37f6d9eed51fe 59aeb1cfaa3d5bfd7bbeeee0f0d37f6d9eed51fe grub2_2.02+dfsg1.orig.tar.xz diff -Nru grub2-2.02+dfsg1/debian/changelog grub2-2.02+dfsg1/debian/changelog --- grub2-2.02+dfsg1/debian/changelog 2019-03-14 10:33:24.000000000 +0000 +++ grub2-2.02+dfsg1/debian/changelog 2019-03-23 09:56:35.000000000 +0000 @@ -1,3 +1,21 @@ +grub2 (2.02+dfsg1-15) unstable; urgency=medium + + * Build-depend on libefiboot-dev and libefivar-dev, for EFI variable + storage changes. + * Drop now-unnecessary dependencies on efibootmgr. + + -- Colin Watson <cjwat...@debian.org> Sat, 23 Mar 2019 09:56:35 +0000 + +grub2 (2.02+dfsg1-14) unstable; urgency=medium + + * Make signed packages depend on a matching version of grub-common, in an + attempt to prevent incorrect testing migrations (closes: #924814). + * Cherry-pick from upstream: + - xfs: Accept filesystem with sparse inodes (closes: #924760). + * Minimise writes to EFI variable storage (closes: #891434). + + -- Colin Watson <cjwat...@debian.org> Sat, 23 Mar 2019 09:47:10 +0000 + grub2 (2.02+dfsg1-13) unstable; urgency=medium * Add regexp module to signed UEFI images. diff -Nru grub2-2.02+dfsg1/debian/control grub2-2.02+dfsg1/debian/control --- grub2-2.02+dfsg1/debian/control 2019-02-28 09:35:06.000000000 +0000 +++ grub2-2.02+dfsg1/debian/control 2019-03-23 09:56:03.000000000 +0000 @@ -32,6 +32,8 @@ libparted-dev [any-powerpc any-ppc64 any-ppc64el], pkg-config, bash-completion, + libefiboot-dev [any-i386 any-amd64 any-ia64 any-arm any-arm64], + libefivar-dev [any-i386 any-amd64 any-ia64 any-arm any-arm64], Build-Conflicts: autoconf2.13, libzfs-dev, libnvpair-dev Standards-Version: 3.9.6 Homepage: https://www.gnu.org/software/grub/ @@ -244,7 +246,7 @@ Package: grub-efi-ia32-bin Architecture: any-i386 any-amd64 -Depends: ${shlibs:Depends}, ${misc:Depends}, grub-common (= ${binary:Version}), efibootmgr [linux-any] +Depends: ${shlibs:Depends}, ${misc:Depends}, grub-common (= ${binary:Version}) Recommends: grub-efi-ia32-signed, Replaces: grub2 (<< ${source:Version}), grub-common (<= 1.97~beta2-1), grub-efi, grub-efi-ia32 (<< 1.99-1) Multi-Arch: foreign @@ -305,7 +307,7 @@ Package: grub-efi-amd64-bin Architecture: i386 kopensolaris-i386 any-amd64 -Depends: ${shlibs:Depends}, ${misc:Depends}, grub-common (= ${binary:Version}), efibootmgr [linux-any] +Depends: ${shlibs:Depends}, ${misc:Depends}, grub-common (= ${binary:Version}) Recommends: grub-efi-amd64-signed, Replaces: grub2 (<< ${source:Version}), grub-common (<= 1.97~beta2-1), grub-efi-amd64 (<< 1.99-1) Multi-Arch: foreign @@ -415,7 +417,7 @@ Package: grub-efi-arm-bin Architecture: any-arm -Depends: ${shlibs:Depends}, ${misc:Depends}, grub-common (= ${binary:Version}), efibootmgr [linux-any] +Depends: ${shlibs:Depends}, ${misc:Depends}, grub-common (= ${binary:Version}) Multi-Arch: foreign XB-Efi-Vendor: ${efi:Vendor} Description: GRand Unified Bootloader, version 2 (ARM UEFI modules) @@ -465,7 +467,7 @@ Package: grub-efi-arm64-bin Architecture: any-arm64 -Depends: ${shlibs:Depends}, ${misc:Depends}, grub-common (= ${binary:Version}), efibootmgr [linux-any] +Depends: ${shlibs:Depends}, ${misc:Depends}, grub-common (= ${binary:Version}) Recommends: grub-efi-arm64-signed, Multi-Arch: foreign XB-Efi-Vendor: ${efi:Vendor} diff -Nru grub2-2.02+dfsg1/debian/patches/efi-variable-storage-minimise-writes.patch grub2-2.02+dfsg1/debian/patches/efi-variable-storage-minimise-writes.patch --- grub2-2.02+dfsg1/debian/patches/efi-variable-storage-minimise-writes.patch 1970-01-01 01:00:00.000000000 +0100 +++ grub2-2.02+dfsg1/debian/patches/efi-variable-storage-minimise-writes.patch 2019-03-23 00:39:00.000000000 +0000 @@ -0,0 +1,890 @@ +From 649e5a39cc5ddf42f6853a0bf818685a625f7cab Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwat...@ubuntu.com> +Date: Mon, 11 Mar 2019 11:17:43 +0000 +Subject: Minimise writes to EFI variable storage + +Some UEFI firmware is easily provoked into running out of space in its +variable storage. This is usually due to certain kernel drivers (e.g. +pstore), but regardless of the cause it can cause grub-install to fail +because it currently asks efibootmgr to delete and re-add entries, and +the deletion often doesn't result in an immediate garbage collection. +Writing variables frequently also increases wear on the NVRAM which may +have limited write cycles. For these reasons, it's desirable to find a +way to minimise writes while still allowing grub-install to ensure that +a suitable boot entry exists. + +Unfortunately, efibootmgr doesn't offer an interface that would let +grub-install do this. It doesn't in general make very much effort to +minimise writes; it doesn't allow modifying an existing Boot* variable +entry, except in certain limited ways; and current versions don't have a +way to export the expected variable data so that grub-install can +compare it to the current data. While it would be possible (and perhaps +desirable?) to add at least some of this to efibootmgr, that would still +leave the problem that there isn't a good upstreamable way for +grub-install to guarantee that it has a new enough version of +efibootmgr. In any case, it's cumbersome and slow for grub-install to +have to fork efibootmgr to get things done. + +Fortunately, a few years ago Peter Jones helpfully factored out a +substantial part of efibootmgr to the efivar and efiboot libraries, and +so it's now possible to have grub-install use those directly. We still +have to use some code from efibootmgr, but much less than would +previously have been necessary. + +grub-install now reuses existing boot entries where possible, and avoids +writing to variables when the new contents are the same as the old +contents. In the common upgrade case where nothing needs to change, it +no longer writes to NVRAM at all. It's also now slightly faster, since +using libefivar is faster than forking efibootmgr. + +Fixes Debian bug #891434. + +Signed-off-by: Colin Watson <cjwat...@ubuntu.com> + +Bug-Debian: https://bugs.debian.org/891434 +Forwarded: https://lists.gnu.org/archive/html/grub-devel/2019-03/msg00119.html +Last-Update: 2019-03-23 + +Patch-Name: efi-variable-storage-minimise-writes.patch +--- + INSTALL | 5 + + Makefile.util.def | 20 ++ + configure.ac | 12 + + grub-core/osdep/efivar.c | 3 + + grub-core/osdep/unix/efivar.c | 503 ++++++++++++++++++++++++++++++++ + grub-core/osdep/unix/platform.c | 100 +------ + include/grub/util/install.h | 5 + + util/grub-install.c | 4 +- + 8 files changed, 557 insertions(+), 95 deletions(-) + create mode 100644 grub-core/osdep/efivar.c + create mode 100644 grub-core/osdep/unix/efivar.c + +diff --git a/INSTALL b/INSTALL +index b370d7753..5862e458a 100644 +--- a/INSTALL ++++ b/INSTALL +@@ -41,6 +41,11 @@ configuring the GRUB. + * Other standard GNU/Unix tools + * a libc with large file support (e.g. glibc 2.1 or later) + ++On Unix-based systems, you also need: ++ ++* libefivar (recommended) ++* libefiboot (recommended; your OS may ship this together with libefivar) ++ + On GNU/Linux, you also need: + + * libdevmapper 1.02.34 or later (recommended) +diff --git a/Makefile.util.def b/Makefile.util.def +index fa39d8bd1..58f8284cb 100644 +--- a/Makefile.util.def ++++ b/Makefile.util.def +@@ -548,6 +548,8 @@ program = { + common = grub-core/osdep/compress.c; + extra_dist = grub-core/osdep/unix/compress.c; + extra_dist = grub-core/osdep/basic/compress.c; ++ common = grub-core/osdep/efivar.c; ++ extra_dist = grub-core/osdep/unix/efivar.c; + common = util/editenv.c; + common = grub-core/osdep/blocklist.c; + common = grub-core/osdep/config.c; +@@ -561,12 +563,15 @@ program = { + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + ++ cflags = '$(EFIVAR_CFLAGS)'; ++ + ldadd = '$(LIBLZMA)'; + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; ++ ldadd = '$(EFIVAR_LIBS)'; + + condition = COND_HAVE_EXEC; + }; +@@ -595,6 +600,8 @@ program = { + extra_dist = grub-core/osdep/basic/no_platform.c; + extra_dist = grub-core/osdep/unix/platform.c; + common = grub-core/osdep/compress.c; ++ common = grub-core/osdep/efivar.c; ++ extra_dist = grub-core/osdep/unix/efivar.c; + common = util/editenv.c; + common = grub-core/osdep/blocklist.c; + common = grub-core/osdep/config.c; +@@ -608,12 +615,15 @@ program = { + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + ++ cflags = '$(EFIVAR_CFLAGS)'; ++ + ldadd = '$(LIBLZMA)'; + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; ++ ldadd = '$(EFIVAR_LIBS)'; + }; + + program = { +@@ -635,6 +645,8 @@ program = { + common = grub-core/osdep/platform.c; + common = grub-core/osdep/platform_unix.c; + common = grub-core/osdep/compress.c; ++ common = grub-core/osdep/efivar.c; ++ extra_dist = grub-core/osdep/unix/efivar.c; + common = util/editenv.c; + common = grub-core/osdep/blocklist.c; + common = grub-core/osdep/config.c; +@@ -647,12 +659,15 @@ program = { + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + ++ cflags = '$(EFIVAR_CFLAGS)'; ++ + ldadd = '$(LIBLZMA)'; + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; ++ ldadd = '$(EFIVAR_LIBS)'; + }; + + program = { +@@ -674,6 +689,8 @@ program = { + common = grub-core/osdep/platform.c; + common = grub-core/osdep/platform_unix.c; + common = grub-core/osdep/compress.c; ++ common = grub-core/osdep/efivar.c; ++ extra_dist = grub-core/osdep/unix/efivar.c; + common = util/editenv.c; + common = grub-core/osdep/blocklist.c; + common = grub-core/osdep/config.c; +@@ -683,12 +700,15 @@ program = { + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + ++ cflags = '$(EFIVAR_CFLAGS)'; ++ + ldadd = '$(LIBLZMA)'; + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; ++ ldadd = '$(EFIVAR_LIBS)'; + }; + + script = { +diff --git a/configure.ac b/configure.ac +index 5b7423be3..3b97f2ba1 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -426,6 +426,18 @@ AC_CHECK_HEADER([util.h], [ + ]) + AC_SUBST([LIBUTIL]) + ++case "$host_os" in ++ cygwin | windows* | mingw32* | aros*) ++ ;; ++ *) ++ # For setting EFI variables in grub-install. ++ PKG_CHECK_MODULES([EFIVAR], [efivar efiboot], [ ++ AC_DEFINE([HAVE_EFIVAR], [1], ++ [Define to 1 if you have the efivar and efiboot libraries.]) ++ ], [:]) ++ ;; ++esac ++ + AC_CACHE_CHECK([whether -Wtrampolines work], [grub_cv_host_cc_wtrampolines], [ + SAVED_CFLAGS="$CFLAGS" + CFLAGS="$HOST_CFLAGS -Wtrampolines -Werror" +diff --git a/grub-core/osdep/efivar.c b/grub-core/osdep/efivar.c +new file mode 100644 +index 000000000..d2750e252 +--- /dev/null ++++ b/grub-core/osdep/efivar.c +@@ -0,0 +1,3 @@ ++#if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__AROS__) ++#include "unix/efivar.c" ++#endif +diff --git a/grub-core/osdep/unix/efivar.c b/grub-core/osdep/unix/efivar.c +new file mode 100644 +index 000000000..2991c71db +--- /dev/null ++++ b/grub-core/osdep/unix/efivar.c +@@ -0,0 +1,503 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2013,2019 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB 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 General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++/* Contains portions derived from efibootmgr, licensed as follows: ++ * ++ * Copyright (C) 2001-2004 Dell, Inc. <matt_dom...@dell.com> ++ * Copyright 2015-2016 Red Hat, Inc. <pjo...@redhat.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include <config.h> ++ ++#ifdef HAVE_EFIVAR ++ ++#include <grub/util/install.h> ++#include <grub/emu/hostdisk.h> ++#include <grub/util/misc.h> ++#include <grub/list.h> ++#include <grub/misc.h> ++#include <grub/emu/exec.h> ++#include <sys/types.h> ++#include <ctype.h> ++#include <errno.h> ++#include <stdlib.h> ++#include <string.h> ++ ++#include <efiboot.h> ++#include <efivar.h> ++ ++struct efi_variable { ++ struct efi_variable *next; ++ struct efi_variable **prev; ++ char *name; ++ efi_guid_t guid; ++ uint8_t *data; ++ size_t data_size; ++ uint32_t attributes; ++ int num; ++}; ++ ++/* Boot option attributes. */ ++#define LOAD_OPTION_ACTIVE 0x00000001 ++ ++/* GUIDs. */ ++#define BLKX_UNKNOWN_GUID \ ++ EFI_GUID (0x47c7b225, 0xc42a, 0x11d2, 0x8e57, 0x00, 0xa0, 0xc9, 0x69, \ ++ 0x72, 0x3b) ++ ++/* Log all errors recorded by libefivar/libefiboot. */ ++static void ++show_efi_errors (void) ++{ ++ int i; ++ int saved_errno = errno; ++ ++ for (i = 0; ; ++i) ++ { ++ char *filename, *function, *message = NULL; ++ int line, error = 0, rc; ++ ++ rc = efi_error_get (i, &filename, &function, &line, &message, &error); ++ if (rc < 0) ++ /* Give up. The caller is going to log an error anyway. */ ++ break; ++ if (rc == 0) ++ /* No more errors. */ ++ break; ++ grub_util_warn ("%s: %s: %s", function, message, strerror (error)); ++ } ++ ++ efi_error_clear (); ++ errno = saved_errno; ++} ++ ++static struct efi_variable * ++new_efi_variable (void) ++{ ++ struct efi_variable *new = xmalloc (sizeof (*new)); ++ memset (new, 0, sizeof (*new)); ++ return new; ++} ++ ++static struct efi_variable * ++new_boot_variable (void) ++{ ++ struct efi_variable *new = new_efi_variable (); ++ new->guid = EFI_GLOBAL_GUID; ++ new->attributes = EFI_VARIABLE_NON_VOLATILE | ++ EFI_VARIABLE_BOOTSERVICE_ACCESS | ++ EFI_VARIABLE_RUNTIME_ACCESS; ++ return new; ++} ++ ++static void ++free_efi_variable (struct efi_variable *entry) ++{ ++ if (entry) ++ { ++ free (entry->name); ++ free (entry->data); ++ free (entry); ++ } ++} ++ ++static int ++read_efi_variable (const char *name, struct efi_variable **entry) ++{ ++ struct efi_variable *new = new_efi_variable (); ++ int rc; ++ ++ rc = efi_get_variable (EFI_GLOBAL_GUID, name, ++ &new->data, &new->data_size, &new->attributes); ++ if (rc < 0) ++ { ++ free_efi_variable (new); ++ new = NULL; ++ } ++ ++ if (new) ++ { ++ /* Latest Apple firmware sets the high bit which appears invalid ++ to the Linux kernel if we write it back, so let's zero it out if it ++ is set since it would be invalid to set it anyway. */ ++ new->attributes = new->attributes & ~(1 << 31); ++ ++ new->name = xstrdup (name); ++ new->guid = EFI_GLOBAL_GUID; ++ } ++ ++ *entry = new; ++ return rc; ++} ++ ++/* Set an EFI variable, but only if it differs from the current value. ++ Some firmware implementations are liable to fill up flash space if we set ++ variables unnecessarily, so try to keep write activity to a minimum. */ ++static int ++set_efi_variable (const char *name, struct efi_variable *entry) ++{ ++ struct efi_variable *old = NULL; ++ int rc = 0; ++ ++ read_efi_variable (name, &old); ++ efi_error_clear (); ++ if (old && old->attributes == entry->attributes && ++ old->data_size == entry->data_size && ++ memcmp (old->data, entry->data, entry->data_size) == 0) ++ grub_util_info ("skipping unnecessary update of EFI variable %s", name); ++ else ++ { ++ rc = efi_set_variable (EFI_GLOBAL_GUID, name, ++ entry->data, entry->data_size, entry->attributes, ++ 0644); ++ if (rc < 0) ++ grub_util_warn (_("Cannot set EFI variable %s"), name); ++ } ++ free_efi_variable (old); ++ return rc; ++} ++ ++static int ++cmpvarbyname (const void *p1, const void *p2) ++{ ++ const struct efi_variable *var1 = *(const struct efi_variable **)p1; ++ const struct efi_variable *var2 = *(const struct efi_variable **)p2; ++ return strcmp (var1->name, var2->name); ++} ++ ++static int ++read_boot_variables (struct efi_variable **varlist) ++{ ++ int rc; ++ efi_guid_t *guid = NULL; ++ char *name = NULL; ++ struct efi_variable **newlist = NULL; ++ int nentries = 0; ++ int i; ++ ++ while ((rc = efi_get_next_variable_name (&guid, &name)) > 0) ++ { ++ const char *snum = name + sizeof ("Boot") - 1; ++ struct efi_variable *var = NULL; ++ unsigned int num; ++ ++ if (memcmp (guid, &efi_guid_global, sizeof (efi_guid_global)) != 0 || ++ strncmp (name, "Boot", sizeof ("Boot") - 1) != 0 || ++ !grub_isxdigit (snum[0]) || !grub_isxdigit (snum[1]) || ++ !grub_isxdigit (snum[2]) || !grub_isxdigit (snum[3])) ++ continue; ++ ++ rc = read_efi_variable (name, &var); ++ if (rc < 0) ++ break; ++ ++ if (sscanf (var->name, "Boot%04X-%*s", &num) == 1 && num < 65536) ++ var->num = num; ++ ++ newlist = xrealloc (newlist, (++nentries) * sizeof (*newlist)); ++ newlist[nentries - 1] = var; ++ } ++ if (rc == 0 && newlist) ++ { ++ qsort (newlist, nentries, sizeof (*newlist), cmpvarbyname); ++ for (i = nentries - 1; i >= 0; --i) ++ grub_list_push (GRUB_AS_LIST_P (varlist), GRUB_AS_LIST (newlist[i])); ++ } ++ else if (newlist) ++ { ++ for (i = 0; i < nentries; ++i) ++ free (newlist[i]); ++ free (newlist); ++ } ++ return rc; ++} ++ ++static void ++remove_from_boot_order (struct efi_variable *order, uint16_t num) ++{ ++ uint16_t *data; ++ unsigned int old_i, new_i; ++ ++ /* We've got an array (in order->data) of the order. Squeeze out any ++ instance of the entry we're deleting by shifting the remainder down. */ ++ data = (uint16_t *) order->data; ++ ++ for (old_i = 0, new_i = 0; ++ old_i < order->data_size / sizeof (uint16_t); ++ ++old_i) ++ { ++ if (data[old_i] != num) { ++ if (new_i != old_i) ++ data[new_i] = data[old_i]; ++ new_i++; ++ } ++ } ++ ++ order->data_size = sizeof (data[0]) * new_i; ++} ++ ++static void ++add_to_boot_order (struct efi_variable *order, uint16_t num) ++{ ++ uint16_t *data; ++ int i; ++ size_t new_data_size; ++ uint16_t *new_data; ++ ++ /* Check whether this entry is already in the boot order. If it is, leave ++ it alone. */ ++ data = (uint16_t *) order->data; ++ for (i = 0; i < order->data_size / sizeof (uint16_t); ++i) ++ if (data[i] == num) ++ return; ++ ++ new_data_size = order->data_size + sizeof (uint16_t); ++ new_data = xmalloc (new_data_size); ++ new_data[0] = num; ++ memcpy (new_data + 1, order->data, order->data_size); ++ free (order->data); ++ order->data = (uint8_t *) new_data; ++ order->data_size = new_data_size; ++} ++ ++static int ++find_free_boot_num (struct efi_variable *entries) ++{ ++ int num_vars = 0, i; ++ struct efi_variable *entry; ++ ++ FOR_LIST_ELEMENTS (entry, entries) ++ ++num_vars; ++ ++ if (num_vars == 0) ++ return 0; ++ ++ /* O(n^2), but n is small and this is easy. */ ++ for (i = 0; i < num_vars; ++i) ++ { ++ int found = 0; ++ FOR_LIST_ELEMENTS (entry, entries) ++ { ++ if (entry->num == i) ++ { ++ found = 1; ++ break; ++ } ++ } ++ if (!found) ++ return i; ++ } ++ ++ return i; ++} ++ ++static int ++get_edd_version (void) ++{ ++ efi_guid_t blkx_guid = BLKX_UNKNOWN_GUID; ++ uint8_t *data = NULL; ++ size_t data_size = 0; ++ uint32_t attributes; ++ efidp_header *path; ++ int rc; ++ ++ rc = efi_get_variable (blkx_guid, "blk0", &data, &data_size, &attributes); ++ if (rc < 0) ++ return rc; ++ ++ path = (efidp_header *) data; ++ if (path->type == 2 && path->subtype == 1) ++ return 3; ++ return 1; ++} ++ ++static struct efi_variable * ++make_boot_variable (int num, const char *disk, int part, const char *loader, ++ const char *label) ++{ ++ struct efi_variable *entry = new_boot_variable (); ++ uint32_t options; ++ uint32_t edd10_devicenum; ++ ssize_t dp_needed, loadopt_needed; ++ efidp dp = NULL; ++ ++ options = EFIBOOT_ABBREV_HD; ++ switch (get_edd_version ()) { ++ case 1: ++ options = EFIBOOT_ABBREV_EDD10; ++ break; ++ case 3: ++ options = EFIBOOT_ABBREV_NONE; ++ break; ++ } ++ ++ /* This may not be the right disk; but it's probably only an issue on very ++ old hardware anyway. */ ++ edd10_devicenum = 0x80; ++ ++ dp_needed = efi_generate_file_device_path_from_esp (NULL, 0, disk, part, ++ loader, options, ++ edd10_devicenum); ++ if (dp_needed < 0) ++ goto err; ++ ++ dp = xmalloc (dp_needed); ++ dp_needed = efi_generate_file_device_path_from_esp ((uint8_t *) dp, ++ dp_needed, disk, part, ++ loader, options, ++ edd10_devicenum); ++ if (dp_needed < 0) ++ goto err; ++ ++ loadopt_needed = efi_loadopt_create (NULL, 0, LOAD_OPTION_ACTIVE, ++ dp, dp_needed, (unsigned char *) label, ++ NULL, 0); ++ if (loadopt_needed < 0) ++ goto err; ++ entry->data_size = loadopt_needed; ++ entry->data = xmalloc (entry->data_size); ++ loadopt_needed = efi_loadopt_create (entry->data, entry->data_size, ++ LOAD_OPTION_ACTIVE, dp, dp_needed, ++ (unsigned char *) label, NULL, 0); ++ if (loadopt_needed < 0) ++ goto err; ++ ++ entry->name = xasprintf ("Boot%04X", num); ++ entry->num = num; ++ ++ return entry; ++ ++err: ++ free_efi_variable (entry); ++ free (dp); ++ return NULL; ++} ++ ++int ++grub_install_efivar_register_efi (grub_device_t efidir_grub_dev, ++ const char *efifile_path, ++ const char *efi_distributor) ++{ ++ const char *efidir_disk; ++ int efidir_part; ++ struct efi_variable *entries = NULL, *entry; ++ struct efi_variable *order; ++ int entry_num = -1; ++ int rc; ++ ++ efidir_disk = grub_util_biosdisk_get_osdev (efidir_grub_dev->disk); ++ efidir_part = efidir_grub_dev->disk->partition ? efidir_grub_dev->disk->partition->number + 1 : 1; ++ ++#ifdef __linux__ ++ /* ++ * Linux uses efivarfs (mounted on /sys/firmware/efi/efivars) to access the ++ * EFI variable store. Some legacy systems may still use the deprecated ++ * efivars interface (accessed through /sys/firmware/efi/vars). Where both ++ * are present, libefivar will use the former in preference, so attempting ++ * to load efivars will not interfere with later operations. ++ */ ++ grub_util_exec_redirect_all ((const char * []){ "modprobe", "efivars", NULL }, ++ NULL, NULL, "/dev/null"); ++#endif ++ ++ if (!efi_variables_supported ()) ++ { ++ grub_util_warn ("%s", ++ _("EFI variables are not supported on this system.")); ++ /* Let the user continue. Perhaps they can still arrange to boot GRUB ++ manually. */ ++ return 0; ++ } ++ ++ rc = read_boot_variables (&entries); ++ if (rc < 0) ++ { ++ grub_util_warn ("%s", _("Cannot read EFI Boot* variables")); ++ goto err; ++ } ++ rc = read_efi_variable ("BootOrder", &order); ++ if (rc < 0) ++ { ++ order = new_boot_variable (); ++ order->name = xstrdup ("BootOrder"); ++ efi_error_clear (); ++ } ++ ++ /* Delete old entries from the same distributor. */ ++ FOR_LIST_ELEMENTS (entry, entries) ++ { ++ efi_load_option *load_option = (efi_load_option *) entry->data; ++ const char *label; ++ ++ if (entry->num < 0) ++ continue; ++ label = (const char *) efi_loadopt_desc (load_option, entry->data_size); ++ if (strcasecmp (label, efi_distributor) != 0) ++ continue; ++ ++ /* To avoid problems with some firmware implementations, reuse the first ++ matching variable we find rather than deleting and recreating it. */ ++ if (entry_num == -1) ++ entry_num = entry->num; ++ else ++ { ++ grub_util_info ("deleting superfluous EFI variable %s (%s)", ++ entry->name, label); ++ rc = efi_del_variable (EFI_GLOBAL_GUID, entry->name); ++ if (rc < 0) ++ { ++ grub_util_warn (_("Cannot delete EFI variable %s"), entry->name); ++ goto err; ++ } ++ } ++ ++ remove_from_boot_order (order, (uint16_t) entry->num); ++ } ++ ++ if (entry_num == -1) ++ entry_num = find_free_boot_num (entries); ++ entry = make_boot_variable (entry_num, efidir_disk, efidir_part, ++ efifile_path, efi_distributor); ++ if (!entry) ++ goto err; ++ ++ grub_util_info ("setting EFI variable %s", entry->name); ++ rc = set_efi_variable (entry->name, entry); ++ if (rc < 0) ++ goto err; ++ ++ add_to_boot_order (order, (uint16_t) entry_num); ++ ++ grub_util_info ("setting EFI variable BootOrder"); ++ rc = set_efi_variable ("BootOrder", order); ++ if (rc < 0) ++ goto err; ++ ++ return 0; ++ ++err: ++ show_efi_errors (); ++ return errno; ++} ++ ++#endif /* HAVE_EFIVAR */ +diff --git a/grub-core/osdep/unix/platform.c b/grub-core/osdep/unix/platform.c +index f9c376c35..b561174ea 100644 +--- a/grub-core/osdep/unix/platform.c ++++ b/grub-core/osdep/unix/platform.c +@@ -19,15 +19,12 @@ + #include <config.h> + + #include <grub/util/install.h> +-#include <grub/emu/hostdisk.h> + #include <grub/util/misc.h> + #include <grub/misc.h> + #include <grub/i18n.h> + #include <grub/emu/exec.h> + #include <sys/types.h> +-#include <dirent.h> + #include <string.h> +-#include <errno.h> + + static char * + get_ofpathname (const char *dev) +@@ -78,102 +75,19 @@ get_ofpathname (const char *dev) + dev); + } + +-static int +-grub_install_remove_efi_entries_by_distributor (const char *efi_distributor) +-{ +- int fd; +- pid_t pid = grub_util_exec_pipe ((const char * []){ "efibootmgr", NULL }, &fd); +- char *line = NULL; +- size_t len = 0; +- int error = 0; +- +- if (!pid) +- { +- grub_util_warn (_("Unable to open stream from %s: %s"), +- "efibootmgr", strerror (errno)); +- return errno; +- } +- +- FILE *fp = fdopen (fd, "r"); +- if (!fp) +- { +- grub_util_warn (_("Unable to open stream from %s: %s"), +- "efibootmgr", strerror (errno)); +- return errno; +- } +- +- line = xmalloc (80); +- len = 80; +- while (1) +- { +- int ret; +- char *bootnum; +- ret = getline (&line, &len, fp); +- if (ret == -1) +- break; +- if (grub_memcmp (line, "Boot", sizeof ("Boot") - 1) != 0 +- || line[sizeof ("Boot") - 1] < '0' +- || line[sizeof ("Boot") - 1] > '9') +- continue; +- if (!strcasestr (line, efi_distributor)) +- continue; +- bootnum = line + sizeof ("Boot") - 1; +- bootnum[4] = '\0'; +- if (!verbosity) +- error = grub_util_exec ((const char * []){ "efibootmgr", "-q", +- "-b", bootnum, "-B", NULL }); +- else +- error = grub_util_exec ((const char * []){ "efibootmgr", +- "-b", bootnum, "-B", NULL }); +- } +- +- free (line); +- return error; +-} +- + int + grub_install_register_efi (grub_device_t efidir_grub_dev, + const char *efifile_path, + const char *efi_distributor) + { +- const char * efidir_disk; +- int efidir_part; +- int error = 0; +- efidir_disk = grub_util_biosdisk_get_osdev (efidir_grub_dev->disk); +- efidir_part = efidir_grub_dev->disk->partition ? efidir_grub_dev->disk->partition->number + 1 : 1; +- +- if (grub_util_exec_redirect_null ((const char * []){ "efibootmgr", "--version", NULL })) +- { +- /* TRANSLATORS: This message is shown when required executable `%s' +- isn't found. */ +- grub_util_error (_("%s: not found"), "efibootmgr"); +- } +- +- /* On Linux, we need the efivars kernel modules. */ +-#ifdef __linux__ +- grub_util_exec ((const char * []){ "modprobe", "-q", "efivars", NULL }); ++#ifdef HAVE_EFIVAR ++ return grub_install_efivar_register_efi (efidir_grub_dev, efifile_path, ++ efi_distributor); ++#else ++ grub_util_error ("%s", ++ _("GRUB was not built with efivar support; " ++ "cannot register EFI boot entry")); + #endif +- /* Delete old entries from the same distributor. */ +- error = grub_install_remove_efi_entries_by_distributor (efi_distributor); +- if (error) +- return error; +- +- char *efidir_part_str = xasprintf ("%d", efidir_part); +- +- if (!verbosity) +- error = grub_util_exec ((const char * []){ "efibootmgr", "-q", +- "-c", "-d", efidir_disk, +- "-p", efidir_part_str, "-w", +- "-L", efi_distributor, "-l", +- efifile_path, NULL }); +- else +- error = grub_util_exec ((const char * []){ "efibootmgr", +- "-c", "-d", efidir_disk, +- "-p", efidir_part_str, "-w", +- "-L", efi_distributor, "-l", +- efifile_path, NULL }); +- free (efidir_part_str); +- return error; + } + + void +diff --git a/include/grub/util/install.h b/include/grub/util/install.h +index 5783cc4bc..961a2613b 100644 +--- a/include/grub/util/install.h ++++ b/include/grub/util/install.h +@@ -213,6 +213,11 @@ grub_install_get_default_x86_platform (void); + const char * + grub_install_get_default_powerpc_machtype (void); + ++int ++grub_install_efivar_register_efi (grub_device_t efidir_grub_dev, ++ const char *efifile_path, ++ const char *efi_distributor); ++ + int + grub_install_register_efi (grub_device_t efidir_grub_dev, + const char *efifile_path, +diff --git a/util/grub-install.c b/util/grub-install.c +index 55491589f..04d8250c9 100644 +--- a/util/grub-install.c ++++ b/util/grub-install.c +@@ -2031,7 +2031,7 @@ main (int argc, char *argv[]) + "\\System\\Library\\CoreServices", + efi_distributor); + if (error) +- grub_util_error (_("efibootmgr failed to register the boot entry: %s"), ++ grub_util_error (_("failed to register the EFI boot entry: %s"), + strerror (error)); + } + +@@ -2143,7 +2143,7 @@ main (int argc, char *argv[]) + error = grub_install_register_efi (efidir_grub_dev, + efifile_path, efi_distributor); + if (error) +- grub_util_error (_("efibootmgr failed to register the boot entry: %s"), ++ grub_util_error (_("failed to register the EFI boot entry: %s"), + strerror (error)); + } + break; diff -Nru grub2-2.02+dfsg1/debian/patches/series grub2-2.02+dfsg1/debian/patches/series --- grub2-2.02+dfsg1/debian/patches/series 2019-02-28 09:35:09.000000000 +0000 +++ grub2-2.02+dfsg1/debian/patches/series 2019-03-23 00:39:00.000000000 +0000 @@ -131,3 +131,6 @@ install_efi_fallback_arm.patch freebsd-ebr-partition-offset.patch util-check-errors.patch +xfs-sparse-inodes.patch +vsnprintf-upper-case-hex.patch +efi-variable-storage-minimise-writes.patch diff -Nru grub2-2.02+dfsg1/debian/patches/vsnprintf-upper-case-hex.patch grub2-2.02+dfsg1/debian/patches/vsnprintf-upper-case-hex.patch --- grub2-2.02+dfsg1/debian/patches/vsnprintf-upper-case-hex.patch 1970-01-01 01:00:00.000000000 +0100 +++ grub2-2.02+dfsg1/debian/patches/vsnprintf-upper-case-hex.patch 2019-03-23 00:39:00.000000000 +0000 @@ -0,0 +1,65 @@ +From 27f1a9c448678f5d354957e0ebad0a2c310f53d1 Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwat...@ubuntu.com> +Date: Mon, 11 Mar 2019 11:15:12 +0000 +Subject: Add %X to grub_vsnprintf_real and friends + +This is needed for UEFI Boot* variables, which the standard says are +named using upper-case hexadecimal. + +Signed-off-by: Colin Watson <cjwat...@ubuntu.com> + +Bug-Debian: https://bugs.debian.org/891434 +Forwarded: https://lists.gnu.org/archive/html/grub-devel/2019-03/msg00121.html +Last-Update: 2019-03-23 + +Patch-Name: vsnprintf-upper-case-hex.patch +--- + grub-core/kern/misc.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c +index 3a14d679e..1e5bd2411 100644 +--- a/grub-core/kern/misc.c ++++ b/grub-core/kern/misc.c +@@ -591,7 +591,7 @@ grub_divmod64 (grub_uint64_t n, grub_uint64_t d, grub_uint64_t *r) + static inline char * + grub_lltoa (char *str, int c, unsigned long long n) + { +- unsigned base = (c == 'x') ? 16 : 10; ++ unsigned base = (c == 'x' || c == 'X') ? 16 : 10; + char *p; + + if ((long long) n < 0 && c == 'd') +@@ -606,7 +606,7 @@ grub_lltoa (char *str, int c, unsigned long long n) + do + { + unsigned d = (unsigned) (n & 0xf); +- *p++ = (d > 9) ? d + 'a' - 10 : d + '0'; ++ *p++ = (d > 9) ? d + ((c == 'x') ? 'a' : 'A') - 10 : d + '0'; + } + while (n >>= 4); + else +@@ -679,6 +679,7 @@ parse_printf_args (const char *fmt0, struct printf_args *args, + { + case 'p': + case 'x': ++ case 'X': + case 'u': + case 'd': + case 'c': +@@ -765,6 +766,7 @@ parse_printf_args (const char *fmt0, struct printf_args *args, + switch (c) + { + case 'x': ++ case 'X': + case 'u': + args->ptr[curn].type = UNSIGNED_INT + longfmt; + break; +@@ -903,6 +905,7 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, + c = 'x'; + /* Fall through. */ + case 'x': ++ case 'X': + case 'u': + case 'd': + { diff -Nru grub2-2.02+dfsg1/debian/patches/xfs-sparse-inodes.patch grub2-2.02+dfsg1/debian/patches/xfs-sparse-inodes.patch --- grub2-2.02+dfsg1/debian/patches/xfs-sparse-inodes.patch 1970-01-01 01:00:00.000000000 +0100 +++ grub2-2.02+dfsg1/debian/patches/xfs-sparse-inodes.patch 2019-03-23 00:38:57.000000000 +0000 @@ -0,0 +1,63 @@ +From ec64736811599e752ed4b4dff04a3029179e9f56 Mon Sep 17 00:00:00 2001 +From: Daniel Kiper <daniel.ki...@oracle.com> +Date: Tue, 29 May 2018 16:16:02 +0200 +Subject: xfs: Accept filesystem with sparse inodes + +The sparse inode metadata format became a mkfs.xfs default in +xfsprogs-4.16.0, and such filesystems are now rejected by grub as +containing an incompatible feature. + +In essence, this feature allows xfs to allocate inodes into fragmented +freespace. (Without this feature, if xfs could not allocate contiguous +space for 64 new inodes, inode creation would fail.) + +In practice, the disk format change is restricted to the inode btree, +which as far as I can tell is not used by grub. If all you're doing +today is parsing a directory, reading an inode number, and converting +that inode number to a disk location, then ignoring this feature +should be fine, so I've added it to XFS_SB_FEAT_INCOMPAT_SUPPORTED + +I did some brief testing of this patch by hacking up the regression +tests to completely fragment freespace on the test xfs filesystem, and +then write a large-ish number of inodes to consume any existing +contiguous 64-inode chunk. This way any files the grub tests add and +traverse would be in such a fragmented inode allocation. Tests passed, +but I'm not sure how to cleanly integrate that into the test harness. + +Signed-off-by: Eric Sandeen <sand...@redhat.com> +Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> +Tested-by: Chris Murphy <li...@colorremedies.com> + +Origin: upstream, https://git.savannah.gnu.org/cgit/grub.git/commit/?id=cda0a857dd7a27cd5d621747464bfe71e8727fff +Bug-Debian: https://bugs.debian.org/924760 +Last-Update: 2019-03-20 + +Patch-Name: xfs-sparse-inodes.patch +--- + grub-core/fs/xfs.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c +index 9f66dd6e4..231f5b886 100644 +--- a/grub-core/fs/xfs.c ++++ b/grub-core/fs/xfs.c +@@ -79,9 +79,18 @@ GRUB_MOD_LICENSE ("GPLv3+"); + #define XFS_SB_FEAT_INCOMPAT_SPINODES (1 << 1) /* sparse inode chunks */ + #define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */ + +-/* We do not currently verify metadata UUID so it is safe to read such filesystem */ ++/* ++ * Directory entries with ftype are explicitly handled by GRUB code. ++ * ++ * We do not currently read the inode btrees, so it is safe to read filesystems ++ * with the XFS_SB_FEAT_INCOMPAT_SPINODES feature. ++ * ++ * We do not currently verify metadata UUID, so it is safe to read filesystems ++ * with the XFS_SB_FEAT_INCOMPAT_META_UUID feature. ++ */ + #define XFS_SB_FEAT_INCOMPAT_SUPPORTED \ + (XFS_SB_FEAT_INCOMPAT_FTYPE | \ ++ XFS_SB_FEAT_INCOMPAT_SPINODES | \ + XFS_SB_FEAT_INCOMPAT_META_UUID) + + struct grub_xfs_sblock diff -Nru grub2-2.02+dfsg1/debian/signing-template/control.in grub2-2.02+dfsg1/debian/signing-template/control.in --- grub2-2.02+dfsg1/debian/signing-template/control.in 2019-02-28 09:35:06.000000000 +0000 +++ grub2-2.02+dfsg1/debian/signing-template/control.in 2019-03-23 00:38:57.000000000 +0000 @@ -11,6 +11,7 @@ Package: @pkg_signed@ Architecture: @arch@ +Depends: grub-common (= @version_binary@) Recommends: shim-signed [amd64] Built-Using: grub2 (= @version_binary@) Description: GRand Unified Bootloader, version 2 (@arch@ UEFI signed by Debian)
signature.asc
Description: PGP signature