grub/grub-core/osdep/unix/platform.c
Paul Menzel 91ac8687a9 unix/platform: Initialize variable to fix grub-install on UEFI system
On a UEFI system, were no boot entry *grub* is present, currently,
`grub-install` fails with an error.

    $ efibootmgr
    BootCurrent: 0000
    Timeout: 0 seconds
    BootOrder: 0001,0006,0003,0004,0005
    Boot0001  Diskette Drive
    Boot0003* USB Storage Device
    Boot0004* CD/DVD/CD-RW Drive
    Boot0005  Onboard NIC
    Boot0006* WDC WD2500AAKX-75U6AA0
    $ sudo grub-install /dev/sda
    Installing for x86_64-efi platform.
    grub-install: error: efibootmgr failed to register the boot entry: Unknown error 22020.

The error code is always different, and the error message (incorrectly)
points to efibootmgr.

But, the error is in GRUB’s function
`grub_install_remove_efi_entries_by_distributor()`, where the variable
`rc` for the return value, is uninitialized and never set, when no boot
entry for the distributor is found.

The content of that uninitialized variable is then returned as the error
code of efibootmgr.

Set the variable to 0, so that success is returned, when no entry needs
to be deleted.

Tested on Dell OptiPlex 7010 with firmware A28.

    $ sudo ./grub-install /dev/sda
    Installing for x86_64-efi platform.
    Installation finished. No error reported.

[1]: https://github.com/rhboot/efibootmgr/issues/100

Signed-off-by: Paul Menzel <pmenzel@molgen.mpg.de>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
2020-09-21 13:46:34 -04:00

241 lines
6.3 KiB
C

/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2013 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/>.
*/
#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)
{
size_t alloced = 4096;
char *ret = xmalloc (alloced);
size_t offset = 0;
int fd;
pid_t pid;
pid = grub_util_exec_pipe ((const char * []){ "ofpathname", dev, NULL }, &fd);
if (!pid)
goto fail;
FILE *fp = fdopen (fd, "r");
if (!fp)
goto fail;
while (!feof (fp))
{
size_t r;
if (alloced == offset)
{
alloced *= 2;
ret = xrealloc (ret, alloced);
}
r = fread (ret + offset, 1, alloced - offset, fp);
offset += r;
}
if (offset > 0 && ret[offset - 1] == '\n')
offset--;
if (offset > 0 && ret[offset - 1] == '\r')
offset--;
if (alloced == offset)
{
alloced++;
ret = xrealloc (ret, alloced);
}
ret[offset] = '\0';
fclose (fp);
return ret;
fail:
grub_util_error (_("couldn't find IEEE1275 device path for %s.\nYou will have to set `boot-device' variable manually"),
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 rc = 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)
rc = grub_util_exec ((const char * []){ "efibootmgr", "-q",
"-b", bootnum, "-B", NULL });
else
rc = grub_util_exec ((const char * []){ "efibootmgr",
"-b", bootnum, "-B", NULL });
}
free (line);
return rc;
}
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 ret;
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 });
#endif
/* Delete old entries from the same distributor. */
ret = grub_install_remove_efi_entries_by_distributor (efi_distributor);
if (ret)
return ret;
char *efidir_part_str = xasprintf ("%d", efidir_part);
if (!verbosity)
ret = grub_util_exec ((const char * []){ "efibootmgr", "-q",
"-c", "-d", efidir_disk,
"-p", efidir_part_str, "-w",
"-L", efi_distributor, "-l",
efifile_path, NULL });
else
ret = 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 ret;
}
void
grub_install_register_ieee1275 (int is_prep, const char *install_device,
int partno, const char *relpath)
{
char *boot_device;
if (grub_util_exec_redirect_null ((const char * []){ "ofpathname", "--version", NULL }))
{
/* TRANSLATORS: This message is shown when required executable `%s'
isn't found. */
grub_util_error (_("%s: not found"), "ofpathname");
}
/* Get the Open Firmware device tree path translation. */
if (!is_prep)
{
char *ptr;
char *ofpath;
const char *iptr;
ofpath = get_ofpathname (install_device);
boot_device = xmalloc (strlen (ofpath) + 1
+ sizeof ("XXXXXXXXXXXXXXXXXXXX")
+ 1 + strlen (relpath) + 1);
ptr = grub_stpcpy (boot_device, ofpath);
*ptr++ = ':';
grub_snprintf (ptr, sizeof ("XXXXXXXXXXXXXXXXXXXX"), "%d",
partno);
ptr += strlen (ptr);
*ptr++ = ',';
for (iptr = relpath; *iptr; iptr++, ptr++)
{
if (*iptr == '/')
*ptr = '\\';
else
*ptr = *iptr;
}
*ptr = '\0';
}
else
boot_device = get_ofpathname (install_device);
if (grub_util_exec ((const char * []){ "nvsetenv", "boot-device",
boot_device, NULL }))
{
char *cmd = xasprintf ("setenv boot-device %s", boot_device);
grub_util_error (_("`nvsetenv' failed. \nYou will have to set `boot-device' variable manually. At the IEEE1275 prompt, type:\n %s\n"),
cmd);
free (cmd);
}
free (boot_device);
}
void
grub_install_sgi_setup (const char *install_device,
const char *imgfile, const char *destname)
{
grub_util_exec ((const char * []){ "dvhtool", "-d",
install_device, "--unix-to-vh",
imgfile, destname, NULL });
grub_util_warn ("%s", _("You will have to set `SystemPartition' and `OSLoader' manually."));
}