grub/grub-core/osdep/freebsd/getroot.c
James Clarke 0726ab2d54 osdep/freebsd: Fix partition calculation for EBR entries
For EBR partitions, "start" is the relative starting sector of the EBR
header itself, whereas "offset" is the relative starting byte of the
partition's contents, excluding the EBR header and any padding. Thus we
must use "offset", and divide by the sector size to convert to sectors.

Fixes Debian bug #923253.

Signed-off-by: James Clarke <jrtc27@jrtc27.com>
Reviewed-by: Colin Watson <cjwatson@ubuntu.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
2020-09-21 16:43:55 -04:00

364 lines
9.4 KiB
C

/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010,2011,2012,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-util.h>
#include <config.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <error.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include <grub/types.h>
# include <sys/param.h>
# include <sys/mount.h>
# include <sys/disk.h> /* DIOCGMEDIASIZE */
# include <sys/param.h>
# include <sys/sysctl.h>
#include <libgeom.h>
#include <grub/util/misc.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/emu/misc.h>
#include <grub/emu/hostdisk.h>
#include <grub/emu/getroot.h>
#include <grub/cryptodisk.h>
#include <sys/wait.h>
#include <libgeom.h>
#define LVM_DEV_MAPPER_STRING "/dev/linux_lvm/"
static const char *
grub_util_get_geom_abstraction (const char *dev)
{
char *whole;
struct gmesh mesh;
struct gclass *class;
const char *name;
int err;
if (strncmp (dev, "/dev/", sizeof ("/dev/") - 1) != 0)
return 0;
name = dev + sizeof ("/dev/") - 1;
grub_util_follow_gpart_up (name, NULL, &whole);
grub_util_info ("following geom '%s'", name);
err = geom_gettree (&mesh);
if (err != 0)
/* TRANSLATORS: geom is the name of (k)FreeBSD device framework.
Usually left untranslated.
*/
grub_util_error ("%s", _("couldn't open geom"));
LIST_FOREACH (class, &mesh.lg_class, lg_class)
{
struct ggeom *geom;
LIST_FOREACH (geom, &class->lg_geom, lg_geom)
{
struct gprovider *provider;
LIST_FOREACH (provider, &geom->lg_provider, lg_provider)
if (strcmp (provider->lg_name, name) == 0)
return class->lg_name;
}
}
return NULL;
}
enum grub_dev_abstraction_types
grub_util_get_dev_abstraction_os (const char *os_dev)
{
const char *abstrac;
abstrac = grub_util_get_geom_abstraction (os_dev);
grub_util_info ("abstraction of %s is %s", os_dev, abstrac);
if (abstrac && grub_strcasecmp (abstrac, "eli") == 0)
return GRUB_DEV_ABSTRACTION_GELI;
/* Check for LVM. */
if (!strncmp (os_dev, LVM_DEV_MAPPER_STRING, sizeof(LVM_DEV_MAPPER_STRING)-1))
return GRUB_DEV_ABSTRACTION_LVM;
return GRUB_DEV_ABSTRACTION_NONE;
}
char *
grub_util_part_to_disk (const char *os_dev, struct stat *st,
int *is_part)
{
char *out, *out2;
if (! S_ISCHR (st->st_mode))
{
*is_part = 0;
return xstrdup (os_dev);
}
if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
return xstrdup (os_dev);
grub_util_follow_gpart_up (os_dev + sizeof ("/dev/") - 1, NULL, &out);
if (grub_strcmp (os_dev + sizeof ("/dev/") - 1, out) != 0)
*is_part = 1;
out2 = xasprintf ("/dev/%s", out);
free (out);
return out2;
}
int
grub_util_pull_device_os (const char *os_dev,
enum grub_dev_abstraction_types ab)
{
switch (ab)
{
case GRUB_DEV_ABSTRACTION_GELI:
{
char *whole;
struct gmesh mesh;
struct gclass *class;
const char *name;
int err;
char *lastsubdev = NULL;
if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
return 1;
name = os_dev + sizeof ("/dev/") - 1;
grub_util_follow_gpart_up (name, NULL, &whole);
grub_util_info ("following geom '%s'", name);
err = geom_gettree (&mesh);
if (err != 0)
/* TRANSLATORS: geom is the name of (k)FreeBSD device framework.
Usually left untranslated.
*/
grub_util_error ("%s", _("couldn't open geom"));
LIST_FOREACH (class, &mesh.lg_class, lg_class)
{
struct ggeom *geom;
LIST_FOREACH (geom, &class->lg_geom, lg_geom)
{
struct gprovider *provider;
LIST_FOREACH (provider, &geom->lg_provider, lg_provider)
if (strcmp (provider->lg_name, name) == 0)
{
struct gconsumer *consumer;
char *fname;
LIST_FOREACH (consumer, &geom->lg_consumer, lg_consumer)
break;
if (!consumer)
grub_util_error ("%s",
_("couldn't find geli consumer"));
fname = xasprintf ("/dev/%s", consumer->lg_provider->lg_name);
grub_util_info ("consumer %s", consumer->lg_provider->lg_name);
lastsubdev = consumer->lg_provider->lg_name;
grub_util_pull_device (fname);
free (fname);
}
}
}
if (ab == GRUB_DEV_ABSTRACTION_GELI && lastsubdev)
{
char *fname = xasprintf ("/dev/%s", lastsubdev);
char *grdev = grub_util_get_grub_dev (fname);
free (fname);
if (grdev)
{
grub_err_t gr_err;
gr_err = grub_cryptodisk_cheat_mount (grdev, os_dev);
if (gr_err)
grub_util_error (_("can't mount encrypted volume `%s': %s"),
lastsubdev, grub_errmsg);
}
grub_free (grdev);
}
}
return 1;
default:
return 0;
}
}
char *
grub_util_get_grub_dev_os (const char *os_dev)
{
char *grub_dev = NULL;
switch (grub_util_get_dev_abstraction (os_dev))
{
/* Fallback for non-devmapper build. In devmapper-builds LVM is handled
in rub_util_get_devmapper_grub_dev and this point isn't reached.
*/
case GRUB_DEV_ABSTRACTION_LVM:
{
unsigned short len;
grub_size_t offset = sizeof (LVM_DEV_MAPPER_STRING) - 1;
len = strlen (os_dev) - offset + 1;
grub_dev = xmalloc (len + sizeof ("lvm/"));
grub_memcpy (grub_dev, "lvm/", sizeof ("lvm/") - 1);
grub_memcpy (grub_dev + sizeof ("lvm/") - 1, os_dev + offset, len);
}
break;
case GRUB_DEV_ABSTRACTION_GELI:
{
char *whole;
struct gmesh mesh;
struct gclass *class;
const char *name;
int err;
if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
return 0;
name = os_dev + sizeof ("/dev/") - 1;
grub_util_follow_gpart_up (name, NULL, &whole);
grub_util_info ("following geom '%s'", name);
err = geom_gettree (&mesh);
if (err != 0)
/* TRANSLATORS: geom is the name of (k)FreeBSD device framework.
Usually left untranslated.
*/
grub_util_error ("%s", _("couldn't open geom"));
LIST_FOREACH (class, &mesh.lg_class, lg_class)
{
struct ggeom *geom;
LIST_FOREACH (geom, &class->lg_geom, lg_geom)
{
struct gprovider *provider;
LIST_FOREACH (provider, &geom->lg_provider, lg_provider)
if (strcmp (provider->lg_name, name) == 0)
{
struct gconsumer *consumer;
char *fname;
char *uuid;
LIST_FOREACH (consumer, &geom->lg_consumer, lg_consumer)
break;
if (!consumer)
grub_util_error ("%s",
_("couldn't find geli consumer"));
fname = xasprintf ("/dev/%s", consumer->lg_provider->lg_name);
uuid = grub_util_get_geli_uuid (fname);
if (!uuid)
grub_util_error ("%s",
_("couldn't retrieve geli UUID"));
grub_dev = xasprintf ("cryptouuid/%s", uuid);
free (fname);
free (uuid);
}
}
}
}
break;
default:
break;
}
return grub_dev;
}
/* FIXME: geom actually gives us the whole container hierarchy.
It can be used more efficiently than this. */
void
grub_util_follow_gpart_up (const char *name, grub_disk_addr_t *off_out, char **name_out)
{
struct gmesh mesh;
struct gclass *class;
int err;
struct ggeom *geom;
grub_util_info ("following geom '%s'", name);
err = geom_gettree (&mesh);
if (err != 0)
/* TRANSLATORS: geom is the name of (k)FreeBSD device framework.
Usually left untranslated.
*/
grub_util_error ("%s", _("couldn't open geom"));
LIST_FOREACH (class, &mesh.lg_class, lg_class)
if (strcasecmp (class->lg_name, "part") == 0)
break;
if (!class)
/* TRANSLATORS: geom is the name of (k)FreeBSD device framework.
Usually left untranslated. "part" is the identifier of one of its
classes. */
grub_util_error ("%s", _("couldn't find geom `part' class"));
LIST_FOREACH (geom, &class->lg_geom, lg_geom)
{
struct gprovider *provider;
LIST_FOREACH (provider, &geom->lg_provider, lg_provider)
if (strcmp (provider->lg_name, name) == 0)
{
char *name_tmp = xstrdup (geom->lg_name);
grub_disk_addr_t off = 0;
struct gconfig *config;
grub_util_info ("geom '%s' has parent '%s'", name, geom->lg_name);
grub_util_follow_gpart_up (name_tmp, &off, name_out);
free (name_tmp);
LIST_FOREACH (config, &provider->lg_config, lg_config)
if (strcasecmp (config->lg_name, "offset") == 0)
off += strtoull (config->lg_val, 0, 10) / provider->lg_sectorsize;
if (off_out)
*off_out = off;
return;
}
}
grub_util_info ("geom '%s' has no parent", name);
if (name_out)
*name_out = xstrdup (name);
if (off_out)
*off_out = 0;
}
grub_disk_addr_t
grub_util_find_partition_start_os (const char *dev)
{
grub_disk_addr_t out;
if (strncmp (dev, "/dev/", sizeof ("/dev/") - 1) != 0)
return 0;
grub_util_follow_gpart_up (dev + sizeof ("/dev/") - 1, &out, NULL);
return out;
}