mirror of
				https://git.proxmox.com/git/grub2
				synced 2025-11-04 12:55:16 +00:00 
			
		
		
		
	Author: Colin Watson <cjwatson@debian.org> Forwarded: not-needed Last-Update: 2013-12-25 Patch-Name: grub_legacy_0_based_partitions.patch
		
			
				
	
	
		
			497 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			497 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* getroot.c - Get root device */
 | 
						|
/*
 | 
						|
 *  GRUB  --  GRand Unified Bootloader
 | 
						|
 *  Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010,2011  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/util/misc.h>
 | 
						|
 | 
						|
#include <grub/cryptodisk.h>
 | 
						|
#include <grub/i18n.h>
 | 
						|
 | 
						|
#ifdef __linux__
 | 
						|
#include <sys/ioctl.h>         /* ioctl */
 | 
						|
#include <sys/mount.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <sys/types.h>
 | 
						|
 | 
						|
#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
 | 
						|
# include <grub/util/libzfs.h>
 | 
						|
# include <grub/util/libnvpair.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <grub/mm.h>
 | 
						|
#include <grub/misc.h>
 | 
						|
#include <grub/emu/misc.h>
 | 
						|
#include <grub/emu/hostdisk.h>
 | 
						|
#include <grub/emu/getroot.h>
 | 
						|
 | 
						|
#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
 | 
						|
#include <sys/mount.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(__NetBSD__) || defined(__OpenBSD__)
 | 
						|
# include <sys/ioctl.h>
 | 
						|
# include <sys/disklabel.h>    /* struct disklabel */
 | 
						|
# include <sys/disk.h>    /* struct dkwedge_info */
 | 
						|
#include <sys/param.h>
 | 
						|
#include <sys/mount.h>
 | 
						|
#endif /* defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
 | 
						|
 | 
						|
#if defined(__NetBSD__)
 | 
						|
# include <sys/fdio.h>
 | 
						|
#endif
 | 
						|
 | 
						|
grub_disk_addr_t
 | 
						|
grub_util_find_partition_start (const char *dev)
 | 
						|
{
 | 
						|
#if GRUB_UTIL_FD_STAT_IS_FUNCTIONAL
 | 
						|
  struct stat st;
 | 
						|
  grub_disk_addr_t partition_start;
 | 
						|
 | 
						|
  if (stat (dev, &st) >= 0
 | 
						|
      && grub_util_device_is_mapped_stat (&st)
 | 
						|
      && grub_util_get_dm_node_linear_info (st.st_rdev, 0, 0, &partition_start))
 | 
						|
    return partition_start;
 | 
						|
#endif
 | 
						|
 | 
						|
  return grub_util_find_partition_start_os (dev);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grub_util_pull_device (const char *os_dev)
 | 
						|
{
 | 
						|
  enum grub_dev_abstraction_types ab;
 | 
						|
  ab = grub_util_get_dev_abstraction (os_dev);
 | 
						|
  switch (ab)
 | 
						|
    {
 | 
						|
    case GRUB_DEV_ABSTRACTION_LVM:
 | 
						|
      grub_util_pull_lvm_by_command (os_dev);
 | 
						|
      /* Fallthrough - in case that lvm-tools are unavailable.  */
 | 
						|
    case GRUB_DEV_ABSTRACTION_LUKS:
 | 
						|
      grub_util_pull_devmapper (os_dev);
 | 
						|
      return;
 | 
						|
 | 
						|
    default:
 | 
						|
      if (grub_util_pull_device_os (os_dev, ab))
 | 
						|
	return;
 | 
						|
      /* Fallthrough.  */
 | 
						|
    case GRUB_DEV_ABSTRACTION_NONE:
 | 
						|
      free (grub_util_biosdisk_get_grub_dev (os_dev));
 | 
						|
      return;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
char *
 | 
						|
grub_util_get_grub_dev (const char *os_dev)
 | 
						|
{
 | 
						|
  char *ret;
 | 
						|
 | 
						|
  grub_util_pull_device (os_dev);
 | 
						|
 | 
						|
  ret = grub_util_get_devmapper_grub_dev (os_dev);
 | 
						|
  if (ret)
 | 
						|
    return ret;
 | 
						|
  ret = grub_util_get_grub_dev_os (os_dev);
 | 
						|
  if (ret)
 | 
						|
    return ret;
 | 
						|
  return grub_util_biosdisk_get_grub_dev (os_dev);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
grub_util_get_dev_abstraction (const char *os_dev)
 | 
						|
{
 | 
						|
  enum grub_dev_abstraction_types ret;
 | 
						|
 | 
						|
  /* User explicitly claims that this drive is visible by BIOS.  */
 | 
						|
  if (grub_util_biosdisk_is_present (os_dev))
 | 
						|
    return GRUB_DEV_ABSTRACTION_NONE;
 | 
						|
 | 
						|
  /* Check for LVM and LUKS.  */
 | 
						|
  ret = grub_util_get_dm_abstraction (os_dev);
 | 
						|
 | 
						|
  if (ret != GRUB_DEV_ABSTRACTION_NONE)
 | 
						|
    return ret;
 | 
						|
 | 
						|
  return grub_util_get_dev_abstraction_os (os_dev);
 | 
						|
}
 | 
						|
 | 
						|
static char *
 | 
						|
convert_system_partition_to_system_disk (const char *os_dev, int *is_part)
 | 
						|
{
 | 
						|
#if GRUB_UTIL_FD_STAT_IS_FUNCTIONAL
 | 
						|
  struct stat st;
 | 
						|
 | 
						|
  if (stat (os_dev, &st) < 0)
 | 
						|
    {
 | 
						|
      const char *errstr = strerror (errno); 
 | 
						|
      grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot stat `%s': %s"),
 | 
						|
		  os_dev, errstr);
 | 
						|
      grub_util_info (_("cannot stat `%s': %s"), os_dev, errstr);
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
  *is_part = 0;
 | 
						|
 | 
						|
  if (grub_util_device_is_mapped_stat (&st))
 | 
						|
    return grub_util_devmapper_part_to_disk (&st, is_part, os_dev);
 | 
						|
 | 
						|
  *is_part = 0;
 | 
						|
 | 
						|
  return grub_util_part_to_disk (os_dev, &st, is_part);
 | 
						|
#else
 | 
						|
  *is_part = 0;
 | 
						|
 | 
						|
  return grub_util_part_to_disk (os_dev, NULL, is_part);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
static const char *
 | 
						|
find_system_device (const char *os_dev)
 | 
						|
{
 | 
						|
  char *os_disk;
 | 
						|
  const char *drive;
 | 
						|
  int is_part;
 | 
						|
 | 
						|
  os_disk = convert_system_partition_to_system_disk (os_dev, &is_part);
 | 
						|
  if (! os_disk)
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  drive = grub_hostdisk_os_dev_to_grub_drive (os_disk, 0);
 | 
						|
  free (os_disk);
 | 
						|
  return drive;
 | 
						|
}
 | 
						|
 | 
						|
static char *
 | 
						|
make_device_name (const char *drive)
 | 
						|
{
 | 
						|
  char *ret, *ptr;
 | 
						|
  const char *iptr;
 | 
						|
 | 
						|
  ret = xmalloc (strlen (drive) * 2);
 | 
						|
  ptr = ret;
 | 
						|
  for (iptr = drive; *iptr; iptr++)
 | 
						|
    {
 | 
						|
      if (*iptr == ',' || *iptr == '\\')
 | 
						|
	*ptr++ = '\\';
 | 
						|
      *ptr++ = *iptr;
 | 
						|
    }
 | 
						|
  *ptr = 0;
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
char *
 | 
						|
grub_util_get_os_disk (const char *os_dev)
 | 
						|
{
 | 
						|
  int is_part;
 | 
						|
 | 
						|
  grub_util_info ("Looking for %s", os_dev);
 | 
						|
 | 
						|
  return convert_system_partition_to_system_disk (os_dev, &is_part);
 | 
						|
}
 | 
						|
 | 
						|
#if !defined(__APPLE__)
 | 
						|
/* Context for grub_util_biosdisk_get_grub_dev.  */
 | 
						|
struct grub_util_biosdisk_get_grub_dev_ctx
 | 
						|
{
 | 
						|
  char *partname;
 | 
						|
  grub_disk_addr_t start;
 | 
						|
};
 | 
						|
 | 
						|
/* Helper for grub_util_biosdisk_get_grub_dev.  */
 | 
						|
static int
 | 
						|
find_partition (grub_disk_t dsk __attribute__ ((unused)),
 | 
						|
		const grub_partition_t partition, void *data)
 | 
						|
{
 | 
						|
  struct grub_util_biosdisk_get_grub_dev_ctx *ctx = data;
 | 
						|
  grub_disk_addr_t part_start = 0;
 | 
						|
  grub_util_info ("Partition %d starts from %" GRUB_HOST_PRIuLONG_LONG,
 | 
						|
		  partition->number, (unsigned long long) partition->start);
 | 
						|
 | 
						|
  part_start = grub_partition_get_start (partition);
 | 
						|
 | 
						|
  if (ctx->start == part_start)
 | 
						|
    {
 | 
						|
      /* This is dreadfully hardcoded, but there's a limit to what GRUB
 | 
						|
         Legacy was able to deal with anyway.  */
 | 
						|
      if (getenv ("GRUB_LEGACY_0_BASED_PARTITIONS"))
 | 
						|
	{
 | 
						|
	  if (partition->parent)
 | 
						|
	    /* Probably a BSD slice.  */
 | 
						|
	    ctx->partname = xasprintf ("%d,%d", partition->parent->number,
 | 
						|
				       partition->number + 1);
 | 
						|
	  else
 | 
						|
	    ctx->partname = xasprintf ("%d", partition->number);
 | 
						|
 | 
						|
	  return 1;
 | 
						|
	}
 | 
						|
 | 
						|
      ctx->partname = grub_partition_get_name (partition);
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
char *
 | 
						|
grub_util_biosdisk_get_grub_dev (const char *os_dev)
 | 
						|
{
 | 
						|
  const char *drive;
 | 
						|
  char *sys_disk;
 | 
						|
  int is_part;
 | 
						|
 | 
						|
  grub_util_info ("Looking for %s", os_dev);
 | 
						|
 | 
						|
  sys_disk = convert_system_partition_to_system_disk (os_dev, &is_part);
 | 
						|
 | 
						|
  if (!sys_disk)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  drive = grub_hostdisk_os_dev_to_grub_drive (sys_disk, 1);
 | 
						|
 | 
						|
  grub_util_info ("%s is a parent of %s", sys_disk, os_dev);
 | 
						|
  if (!is_part)
 | 
						|
    {
 | 
						|
      free (sys_disk);
 | 
						|
      return make_device_name (drive);
 | 
						|
    }
 | 
						|
  free (sys_disk);
 | 
						|
 | 
						|
#if defined(__APPLE__)
 | 
						|
  /* Apple uses "/dev/r?disk[0-9]+(s[0-9]+)?".  */
 | 
						|
  /*
 | 
						|
   * Note: we do not use the new partition naming scheme as dos_part does not
 | 
						|
   * necessarily correspond to an msdos partition.
 | 
						|
   */
 | 
						|
  {
 | 
						|
    const char *p;
 | 
						|
    char *dri, *ret;
 | 
						|
    int part;
 | 
						|
    int disk = (grub_memcmp (os_dev, "/dev/disk", sizeof ("/dev/disk") - 1)
 | 
						|
		 == 0);
 | 
						|
    int rdisk = (grub_memcmp (os_dev, "/dev/rdisk", sizeof ("/dev/rdisk") - 1)
 | 
						|
		 == 0);
 | 
						|
 | 
						|
    dri = make_device_name (drive);
 | 
						|
 
 | 
						|
    if (!disk && !rdisk)
 | 
						|
      return dri;
 | 
						|
 | 
						|
    p = os_dev + sizeof ("/dev/disk") + rdisk - 1;
 | 
						|
    while (*p >= '0' && *p <= '9')
 | 
						|
      p++;
 | 
						|
    if (*p != 's')
 | 
						|
      return dri;
 | 
						|
    p++;
 | 
						|
 | 
						|
    part = strtol (p, NULL, 10);
 | 
						|
    if (part == 0)
 | 
						|
      return dri;
 | 
						|
 | 
						|
    ret = xasprintf ("%s,%d", dri, part);
 | 
						|
    free (dri);
 | 
						|
 | 
						|
    return ret;
 | 
						|
  }
 | 
						|
 | 
						|
#else
 | 
						|
 | 
						|
  /* Linux counts partitions uniformly, whether a BSD partition or a DOS
 | 
						|
     partition, so mapping them to GRUB devices is not trivial.
 | 
						|
     Here, get the start sector of a partition by HDIO_GETGEO, and
 | 
						|
     compare it with each partition GRUB recognizes.
 | 
						|
 | 
						|
     Cygwin /dev/sdXN emulation uses Windows partition mapping. It
 | 
						|
     does not count the extended partition and missing primary
 | 
						|
     partitions.  Use same method as on Linux here.
 | 
						|
 | 
						|
     For NetBSD and FreeBSD, proceed as for Linux, except that the start
 | 
						|
     sector is obtained from the disk label.  */
 | 
						|
  {
 | 
						|
    char *name;
 | 
						|
    grub_disk_t disk;
 | 
						|
    struct grub_util_biosdisk_get_grub_dev_ctx ctx;
 | 
						|
 | 
						|
    name = make_device_name (drive);
 | 
						|
 | 
						|
    ctx.start = grub_util_find_partition_start (os_dev);
 | 
						|
    if (grub_errno != GRUB_ERR_NONE)
 | 
						|
      {
 | 
						|
	free (name);
 | 
						|
	return 0;
 | 
						|
      }
 | 
						|
 | 
						|
#if defined(__GNU__)
 | 
						|
    /* Some versions of Hurd use badly glued Linux code to handle partitions
 | 
						|
       resulting in partitions being promoted to disks.  */
 | 
						|
    /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?".  */
 | 
						|
    /*
 | 
						|
     * Note: we do not use the new partition naming scheme as dos_part does not
 | 
						|
     * necessarily correspond to an msdos partition.
 | 
						|
     */
 | 
						|
    if (ctx.start == (grub_disk_addr_t) -1)
 | 
						|
      {
 | 
						|
	char *p;
 | 
						|
	char *dri;
 | 
						|
 | 
						|
	dri = make_device_name (drive);
 | 
						|
 | 
						|
	p = strrchr (os_dev + sizeof ("/dev/hd") - 1, 's');
 | 
						|
	if (p)
 | 
						|
	  {
 | 
						|
	    long int n;
 | 
						|
	    char *q;
 | 
						|
 | 
						|
	    p++;
 | 
						|
	    n = strtol (p, &q, 10);
 | 
						|
	    if (p != q && n > 0 && n != GRUB_LONG_MAX)
 | 
						|
	      {
 | 
						|
		char *t;
 | 
						|
		t = dri;
 | 
						|
		if (*q >= 'a' && *q <= 'g')
 | 
						|
		  dri = xasprintf ("%s,%ld,%d", t, n, *q - 'a' + 1);
 | 
						|
		else
 | 
						|
		  dri = xasprintf ("%s,%ld", t, n);
 | 
						|
		free (t);
 | 
						|
	      }
 | 
						|
	  }
 | 
						|
 | 
						|
	return dri;
 | 
						|
      }
 | 
						|
#endif
 | 
						|
 | 
						|
    grub_util_info ("%s starts from %" GRUB_HOST_PRIuLONG_LONG,
 | 
						|
		    os_dev, (unsigned long long) ctx.start);
 | 
						|
 | 
						|
    grub_util_info ("opening the device %s", name);
 | 
						|
    disk = grub_disk_open (name);
 | 
						|
    free (name);
 | 
						|
 | 
						|
    if (! disk)
 | 
						|
      {
 | 
						|
	/* We already know that the partition exists.  Given that we already
 | 
						|
	   checked the device map above, we can only get
 | 
						|
	   GRUB_ERR_UNKNOWN_DEVICE at this point if the disk does not exist.
 | 
						|
	   This can happen on Xen, where disk images in the host can be
 | 
						|
	   assigned to devices that have partition-like names in the guest
 | 
						|
	   but are really more like disks.  */
 | 
						|
	if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
 | 
						|
	  {
 | 
						|
	    char *canon;
 | 
						|
	    grub_util_warn
 | 
						|
	      (_("disk does not exist, so falling back to partition device %s"),
 | 
						|
	       os_dev);
 | 
						|
	    grub_errno = GRUB_ERR_NONE;
 | 
						|
 | 
						|
	    canon = grub_canonicalize_file_name (os_dev);
 | 
						|
	    drive = grub_hostdisk_os_dev_to_grub_drive (canon ? : os_dev, 1);
 | 
						|
	    if (canon)
 | 
						|
	      free (canon);
 | 
						|
	    return make_device_name (drive);
 | 
						|
	  }
 | 
						|
	else
 | 
						|
	  return 0;
 | 
						|
      }
 | 
						|
 | 
						|
    name = grub_util_get_ldm (disk, ctx.start);
 | 
						|
    if (name)
 | 
						|
      {
 | 
						|
	grub_disk_close (disk);
 | 
						|
	return name;
 | 
						|
      }
 | 
						|
 | 
						|
    ctx.partname = NULL;
 | 
						|
 | 
						|
    grub_partition_iterate (disk, find_partition, &ctx);
 | 
						|
    if (grub_errno != GRUB_ERR_NONE)
 | 
						|
      {
 | 
						|
	grub_disk_close (disk);
 | 
						|
	return 0;
 | 
						|
      }
 | 
						|
 | 
						|
    if (ctx.partname == NULL)
 | 
						|
      {
 | 
						|
	grub_disk_close (disk);
 | 
						|
	grub_util_info ("cannot find the partition of `%s'", os_dev);
 | 
						|
	grub_error (GRUB_ERR_BAD_DEVICE,
 | 
						|
		    "cannot find the partition of `%s'", os_dev);
 | 
						|
	return 0;
 | 
						|
      }
 | 
						|
 | 
						|
    name = grub_xasprintf ("%s,%s", disk->name, ctx.partname);
 | 
						|
    free (ctx.partname);
 | 
						|
    grub_disk_close (disk);
 | 
						|
    return name;
 | 
						|
  }
 | 
						|
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
grub_util_biosdisk_is_present (const char *os_dev)
 | 
						|
{
 | 
						|
  int ret = (find_system_device (os_dev) != NULL);
 | 
						|
  grub_util_info ((ret ? "%s is present" : "%s is not present"), 
 | 
						|
		  os_dev);
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef HAVE_LIBZFS
 | 
						|
static libzfs_handle_t *__libzfs_handle;
 | 
						|
 | 
						|
static void
 | 
						|
fini_libzfs (void)
 | 
						|
{
 | 
						|
  libzfs_fini (__libzfs_handle);
 | 
						|
}
 | 
						|
 | 
						|
libzfs_handle_t *
 | 
						|
grub_get_libzfs_handle (void)
 | 
						|
{
 | 
						|
  if (! __libzfs_handle)
 | 
						|
    {
 | 
						|
      __libzfs_handle = libzfs_init ();
 | 
						|
 | 
						|
      if (__libzfs_handle)
 | 
						|
	atexit (fini_libzfs);
 | 
						|
    }
 | 
						|
 | 
						|
  return __libzfs_handle;
 | 
						|
}
 | 
						|
#endif /* HAVE_LIBZFS */
 | 
						|
 |