]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
* grub-core/kern/emu/hostdisk.c: Disentagle into a series of OS-specific
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sun, 22 Sep 2013 05:36:17 +0000 (07:36 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sun, 22 Sep 2013 05:36:17 +0000 (07:36 +0200)
files rather than one file with loads of #if's.
* util/getroot.c: Likewise.

30 files changed:
ChangeLog
Makefile.util.def
conf/Makefile.extra-dist
grub-core/Makefile.core.def
grub-core/kern/emu/hostdisk.c
grub-core/kern/emu/hostdisk_apple.c [new file with mode: 0644]
grub-core/kern/emu/hostdisk_basic.c [new file with mode: 0644]
grub-core/kern/emu/hostdisk_bsd.c [new file with mode: 0644]
grub-core/kern/emu/hostdisk_cygwin.c [new file with mode: 0644]
grub-core/kern/emu/hostdisk_devmapper.c [new file with mode: 0644]
grub-core/kern/emu/hostdisk_freebsd.c [new file with mode: 0644]
grub-core/kern/emu/hostdisk_hurd.c [new file with mode: 0644]
grub-core/kern/emu/hostdisk_linux.c [new file with mode: 0644]
grub-core/kern/emu/hostdisk_mingw.c [new file with mode: 0644]
grub-core/kern/emu/hostdisk_os.c [new file with mode: 0644]
grub-core/kern/emu/hostdisk_sun.c [new file with mode: 0644]
include/grub/emu/getroot.h
include/grub/emu/hostdisk.h
include/grub/util/lvm.h
util/getroot.c
util/getroot_apple.c [new file with mode: 0644]
util/getroot_basic.c [new file with mode: 0644]
util/getroot_bsd.c [new file with mode: 0644]
util/getroot_cygwin.c [new file with mode: 0644]
util/getroot_devmapper.c [new file with mode: 0644]
util/getroot_freebsd.c [new file with mode: 0644]
util/getroot_hurd.c [new file with mode: 0644]
util/getroot_linux.c [new file with mode: 0644]
util/getroot_os.c [new file with mode: 0644]
util/getroot_sun.c [new file with mode: 0644]

index 9e5d2a3569c49d03a8c34995586afd36ebc4fad7..156d4576878cd9eb5b6fa58d0c4010433738df48 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2013-09-22  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       * grub-core/kern/emu/hostdisk.c: Disentagle into a series of OS-specific
+       files rather than one file with loads of #if's.
+       * util/getroot.c: Likewise.
+
 2013-09-22  Vladimir Serbinenko  <phcoder@gmail.com>
 
        * grub-core/lib/posix_wrap/sys/types.h: Use stddef on *BSD.
index 40781235caa9e287a715cd614711efd2c67c6b2c..38ce370bf91d3bd2cf9297dea4e76842fd619107 100644 (file)
@@ -10,8 +10,12 @@ library = {
   common = grub-core/kern/device.c;
   common = grub-core/kern/disk.c;
   common = util/getroot.c;
+  common = util/getroot_os.c;
+  common = util/getroot_devmapper.c;
   common = util/raid.c;
   common = grub-core/kern/emu/hostdisk.c;
+  common = grub-core/kern/emu/hostdisk_devmapper.c;
+  common = grub-core/kern/emu/hostdisk_os.c;
   common = grub-core/kern/emu/misc.c;
   common = grub-core/kern/emu/mm.c;
   common = grub-core/kern/env.c;
index 4b67d4dade96a9d665b9467366d002f7e64d38d0..b666468e34339b709612a73978e01a940b604fb1 100644 (file)
@@ -61,3 +61,22 @@ EXTRA_DIST += m4/threadlib.m4
 EXTRA_DIST += m4/uintmax_t.m4
 EXTRA_DIST += m4/visibility.m4
 EXTRA_DIST += m4/math_h.m4
+
+EXTRA_DIST += grub-core/kern/emu/hostdisk_apple.c
+EXTRA_DIST += grub-core/kern/emu/hostdisk_basic.c
+EXTRA_DIST += grub-core/kern/emu/hostdisk_bsd.c
+EXTRA_DIST += grub-core/kern/emu/hostdisk_cygwin.c
+EXTRA_DIST += grub-core/kern/emu/hostdisk_freebsd.c
+EXTRA_DIST += grub-core/kern/emu/hostdisk_hurd.c
+EXTRA_DIST += grub-core/kern/emu/hostdisk_linux.c
+EXTRA_DIST += grub-core/kern/emu/hostdisk_mingw.c
+EXTRA_DIST += grub-core/kern/emu/hostdisk_sun.c
+
+EXTRA_DIST += util/getroot_apple.c
+EXTRA_DIST += util/getroot_basic.c
+EXTRA_DIST += util/getroot_bsd.c
+EXTRA_DIST += util/getroot_cygwin.c
+EXTRA_DIST += util/getroot_freebsd.c
+EXTRA_DIST += util/getroot_hurd.c
+EXTRA_DIST += util/getroot_linux.c
+EXTRA_DIST += util/getroot_sun.c
index bf0a6cd26e4ab7cfdd6b09f85cfeb8468db4860e..a4e6862bb5cf4c38613a9b789b8bedbcfe6f26f5 100644 (file)
@@ -241,6 +241,8 @@ kernel = {
   emu = kern/emu/error.c;
   emu = kern/emu/cache_s.S;
   emu = kern/emu/hostdisk.c;
+  emu = kern/emu/hostdisk_devmapper.c;
+  emu = kern/emu/hostdisk_os.c;
   emu = kern/emu/hostfs.c;
   emu = kern/emu/main.c;
   emu = kern/emu/argp_common.c;
index b90a26d48e1f3474d880d4c1cebf8c23835964ab..f1f75705552e2ba4706259f41a6881fd94ae39fb 100644 (file)
 #include <errno.h>
 #include <limits.h>
 
-#ifdef __MINGW32__
-#include <windows.h>
-#include <winioctl.h>
-#include "dirname.h"
-#endif
-
 #ifdef __linux__
 # include <sys/ioctl.h>         /* ioctl */
 # include <sys/mount.h>
 #  define BLKFLSBUF     _IO (0x12,97)   /* flush buffer cache */
 # endif /* ! BLKFLSBUF */
 # include <sys/ioctl.h>                /* ioctl */
-# ifndef HDIO_GETGEO
-#  define HDIO_GETGEO  0x0301  /* get device geometry */
-/* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
-   defined.  */
-struct hd_geometry
-{
-  unsigned char heads;
-  unsigned char sectors;
-  unsigned short cylinders;
-  unsigned long start;
-};
-# endif /* ! HDIO_GETGEO */
-# ifndef BLKGETSIZE64
-#  define BLKGETSIZE64  _IOR(0x12,114,size_t)    /* return device size */
-# endif /* ! BLKGETSIZE64 */
 #endif /* __linux__ */
 
-#ifdef __CYGWIN__
-# include <sys/ioctl.h>
-# include <cygwin/fs.h> /* BLKGETSIZE64 */
-# include <cygwin/hdreg.h> /* HDIO_GETGEO */
-#endif
-
 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-# include <sys/disk.h> /* DIOCGMEDIASIZE */
-# include <sys/param.h>
 # include <sys/sysctl.h>
-# include <sys/mount.h>
-#include <libgeom.h>
-#endif
-
-#if defined (__sun__)
-# include <sys/dkio.h>
-#endif
-
-#if defined(__APPLE__)
-# include <sys/disk.h>
-#endif
-
-#ifdef HAVE_DEVICE_MAPPER
-# include <libdevmapper.h>
 #endif
 
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-# define HAVE_DIOCGDINFO
-# include <sys/ioctl.h>
-# include <sys/disklabel.h>    /* struct disklabel */
-# include <sys/disk.h>    /* struct dkwedge_info */
-#else /* !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) */
-# undef HAVE_DIOCGDINFO
-#endif /* defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
-
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-# ifdef HAVE_GETRAWPARTITION
-#  include <util.h>    /* getrawpartition */
-# endif /* HAVE_GETRAWPARTITION */
-# if defined(__NetBSD__)
-# include <sys/fdio.h>
-# endif
-# if defined(__OpenBSD__)
-# include <sys/dkio.h>
-# endif
-# ifndef RAW_FLOPPY_MAJOR
-#  define RAW_FLOPPY_MAJOR     9
-# endif /* ! RAW_FLOPPY_MAJOR */
-#endif /* defined(__NetBSD__) */
-
 static struct
 {
   char *drive;
@@ -144,49 +77,6 @@ struct grub_util_biosdisk_data
   int device_map;
 };
 
-#ifdef __linux__
-/* Check if we have devfs support.  */
-static int
-have_devfs (void)
-{
-  static int dev_devfsd_exists = -1;
-
-  if (dev_devfsd_exists < 0)
-    {
-      struct stat st;
-
-      dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0;
-    }
-
-  return dev_devfsd_exists;
-}
-#endif /* __linux__ */
-
-#if defined(__NetBSD__)
-/* Adjust device driver parameters.  This function should be called just
-   after successfully opening the device.  For now, it simply prevents the
-   floppy driver from retrying operations on failure, as otherwise the
-   driver takes a while to abort when there is no floppy in the drive.  */
-static void
-configure_device_driver (int fd)
-{
-  struct stat st;
-
-  if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode))
-    return;
-  if (major(st.st_rdev) == RAW_FLOPPY_MAJOR)
-    {
-      int floppy_opts;
-
-      if (ioctl (fd, FDIOCGETOPTS, &floppy_opts) == -1)
-       return;
-      floppy_opts |= FDOPT_NORETRY;
-      if (ioctl (fd, FDIOCSETOPTS, &floppy_opts) == -1)
-       return;
-    }
-}
-#endif /* defined(__NetBSD__) */
-
 static int
 unescape_cmp (const char *a, const char *b_escaped)
 {
@@ -251,171 +141,41 @@ grub_util_biosdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data,
   return 0;
 }
 
-
 #ifdef __MINGW32__
 
 grub_uint64_t
-grub_util_get_fd_size (int fd __attribute__ ((unused)), const char *name_in,
+grub_util_get_fd_size (int fd, const char *name,
                       unsigned *log_secsize)
 {
-  HANDLE hd;
-  grub_int64_t size = -1LL;
-  int log_sector_size = 9;
-  char *name = xstrdup (name_in);
-
-  if (log_secsize)
-    *log_secsize = log_sector_size;
-
-  strip_trailing_slashes(name);
-  hd = CreateFile (name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
-                   0, OPEN_EXISTING, 0, 0);
-
-  if (hd == INVALID_HANDLE_VALUE)
-    {
-      free (name);
-      return size;
-    }
-
-  if (((name[0] == '/') || (name[0] == '\\')) &&
-      ((name[1] == '/') || (name[1] == '\\')) &&
-      (name[2] == '.') &&
-      ((name[3] == '/') || (name[3] == '\\')) &&
-      (! strncasecmp (name + 4, "PHYSICALDRIVE", 13)))
-    {
-      DWORD nr;
-      DISK_GEOMETRY g;
-
-      if (! DeviceIoControl (hd, IOCTL_DISK_GET_DRIVE_GEOMETRY,
-                             0, 0, &g, sizeof (g), &nr, 0))
-        goto fail;
-
-      size = g.Cylinders.QuadPart;
-      size *= g.TracksPerCylinder * g.SectorsPerTrack * g.BytesPerSector;
-
-      for (log_sector_size = 0;
-          (1 << log_sector_size) < g.BytesPerSector;
-          log_sector_size++);
-    }
-  else
-    {
-      ULARGE_INTEGER s;
-
-      s.LowPart = GetFileSize (hd, &s.HighPart);
-      size = s.QuadPart;
-    }
-
-fail:
-
-  if (log_secsize)
-    *log_secsize = log_sector_size;
-
-  free (name);
-
-  CloseHandle (hd);
-
-  return size;
+  return grub_util_get_fd_size_os (fd, name, log_secsize);
 }
 
-#endif
+#else
 
-#if !defined(__MINGW32__)
 grub_uint64_t
 grub_util_get_fd_size (int fd, const char *name, unsigned *log_secsize)
 {
-#if !defined (__GNU__)
-# if defined(__NetBSD__) || defined(__OpenBSD__)
-  struct disklabel label;
-# elif defined (__sun__)
-  struct dk_minfo minfo;
-# else
-  unsigned long long nr;
-# endif
-#endif
-  unsigned sector_size, log_sector_size;
   struct stat st;
+  grub_int64_t ret = -1;
 
   if (fstat (fd, &st) < 0)
     /* TRANSLATORS: "stat" comes from the name of POSIX function.  */
     grub_util_error (_("cannot stat `%s': %s"), name, strerror (errno));
-
-#if defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \
-  defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) \
-  || defined (__sun__) || defined(__OpenBSD__)
-
-# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) || defined (__sun__)  || defined(__OpenBSD__)
-  if (! S_ISCHR (st.st_mode))
-# else
-  if (! S_ISBLK (st.st_mode))
-# endif
-    goto fail;
-
-# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-    if (ioctl (fd, DIOCGMEDIASIZE, &nr))
-# elif defined(__APPLE__)
-    if (ioctl (fd, DKIOCGETBLOCKCOUNT, &nr))
-# elif defined(__NetBSD__) || defined(__OpenBSD__)
-#  if defined(__NetBSD__)
-    configure_device_driver (fd);
-#  endif
-    if (ioctl (fd, DIOCGDINFO, &label) == -1)
-# elif defined (__sun__)
-    if (!ioctl (fd, DKIOCGMEDIAINFO, &minfo))
-# else
-    if (ioctl (fd, BLKGETSIZE64, &nr))
-# endif
-      goto fail;
-
-# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-    if (ioctl (fd, DIOCGSECTORSIZE, &sector_size))
-      goto fail;
-# elif defined(__APPLE__)
-    if (ioctl (fd, DKIOCGETBLOCKSIZE, &sector_size))
-      goto fail;
-# elif defined(__sun__)
-    sector_size = minfo.dki_lbsize;
-# elif defined(__NetBSD__) || defined(__OpenBSD__)
-    sector_size = label.d_secsize;
-# else
-    if (ioctl (fd, BLKSSZGET, &sector_size))
-      goto fail;
-# endif
-    if (sector_size & (sector_size - 1) || !sector_size)
-      goto fail;
-    for (log_sector_size = 0;
-        (1 << log_sector_size) < sector_size;
-        log_sector_size++);
-
-    if (log_secsize)
-      *log_secsize = log_sector_size;
-
-# if defined (__APPLE__)
-    return nr << log_sector_size;
-# elif defined(__NetBSD__) || defined(__OpenBSD__)
-    return (grub_uint64_t) label.d_secperunit << log_sector_size;
-# elif defined (__sun__)
-    return minfo.dki_capacity << log_sector_size;
-# else
-    if (nr & ((1 << log_sector_size) - 1))
-      grub_util_error ("%s", _("unaligned device size"));
-
-    return nr;
-# endif
-
- fail:
-
-  /* In GNU/Hurd, stat() will return the right size.  */
-#elif !defined (__GNU__)
-# warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
+#if GRUB_DISK_DEVS_ARE_CHAR
+  if (S_ISCHR (st.st_mode))
+#else
+  if (S_ISBLK (st.st_mode))
 #endif
-
-  sector_size = 512;
-  log_sector_size = 9;
+    ret = grub_util_get_fd_size_os (fd, name, log_secsize);
+  if (ret != -1LL)
+    return ret;
 
   if (log_secsize)
-   *log_secsize = 9;
+    *log_secsize = 9;
 
   return st.st_size;
 }
+
 #endif
 
 static grub_err_t
@@ -457,7 +217,7 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk)
 
 #if !defined(__MINGW32__)
 
-# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
+# if GRUB_DISK_DEVS_ARE_CHAR
     if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode))
 # else
     if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode))
@@ -474,412 +234,6 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk)
   }
 }
 
-#ifdef HAVE_DEVICE_MAPPER
-static void device_mapper_null_log (int level __attribute__ ((unused)),
-                                   const char *file __attribute__ ((unused)),
-                                   int line __attribute__ ((unused)),
-                                   int dm_errno __attribute__ ((unused)),
-                                   const char *f __attribute__ ((unused)),
-                                   ...)
-{
-}
-
-int
-grub_device_mapper_supported (void)
-{
-  static int supported = -1;
-
-  if (supported == -1)
-    {
-      struct dm_task *dmt;
-
-      /* Suppress annoying log messages.  */
-      dm_log_with_errno_init (&device_mapper_null_log);
-
-      dmt = dm_task_create (DM_DEVICE_VERSION);
-      supported = (dmt != NULL);
-      if (dmt)
-       dm_task_destroy (dmt);
-
-      /* Restore the original logger.  */
-      dm_log_with_errno_init (NULL);
-    }
-
-  return supported;
-}
-#endif /* HAVE_DEVICE_MAPPER */
-
-int
-grub_util_device_is_mapped (const char *dev)
-{
-#ifdef HAVE_DEVICE_MAPPER
-  struct stat st;
-
-  if (!grub_device_mapper_supported ())
-    return 0;
-
-  if (stat (dev, &st) < 0)
-    return 0;
-
-  return dm_is_dm_major (major (st.st_rdev));
-#else
-  return 0;
-#endif /* HAVE_DEVICE_MAPPER */
-}
-
-#ifdef HAVE_DEVICE_MAPPER
-int
-grub_util_get_dm_node_linear_info (const char *dev,
-                                  int *maj, int *min,
-                                  grub_disk_addr_t *st)
-{
-  struct dm_task *dmt;
-  void *next = NULL;
-  uint64_t length, start;
-  char *target, *params;
-  char *ptr;
-  int major = 0, minor = 0;
-  int first = 1;
-  grub_disk_addr_t partstart = 0;
-  const char *node_uuid;
-
-  while (1)
-    {
-      dmt = dm_task_create(DM_DEVICE_TABLE);
-      if (!dmt)
-       break;
-      
-      if (! (first ? dm_task_set_name (dmt, dev)
-            : dm_task_set_major_minor (dmt, major, minor, 0)))
-       {
-         dm_task_destroy (dmt);
-         break;
-       }
-      dm_task_no_open_count(dmt);
-      if (!dm_task_run(dmt))
-       {
-         dm_task_destroy (dmt);
-         break;
-       }
-      node_uuid = dm_task_get_uuid (dmt);
-      if (node_uuid && (strncmp (node_uuid, "LVM-", 4) == 0
-                       || strncmp (node_uuid, "mpath-", 6) == 0
-                       || strncmp (node_uuid, "DMRAID-", 7) == 0))
-       {
-         dm_task_destroy (dmt);
-         break;
-       }
-
-      next = dm_get_next_target(dmt, next, &start, &length,
-                               &target, &params);
-      if (grub_strcmp (target, "linear") != 0)
-       {
-         dm_task_destroy (dmt);
-         break;
-       }
-      major = grub_strtoul (params, &ptr, 10);
-      if (grub_errno)
-       {
-         dm_task_destroy (dmt);
-         grub_errno = GRUB_ERR_NONE;
-         return 0;
-       }
-      if (*ptr != ':')
-       {
-         dm_task_destroy (dmt);
-         return 0;
-       }
-      ptr++;
-      minor = grub_strtoul (ptr, &ptr, 10);
-      if (grub_errno)
-       {
-         grub_errno = GRUB_ERR_NONE;
-         dm_task_destroy (dmt);
-         return 0;
-       }
-
-      if (*ptr != ' ')
-       {
-         dm_task_destroy (dmt);
-         return 0;
-       }
-      ptr++;
-      partstart += grub_strtoull (ptr, &ptr, 10);
-      if (grub_errno)
-       {
-         grub_errno = GRUB_ERR_NONE;
-         dm_task_destroy (dmt);
-         return 0;
-       }
-
-      dm_task_destroy (dmt);
-      first = 0;
-      if (!dm_is_dm_major (major))
-       break;
-    }
-  if (first)
-    return 0;
-  if (maj)
-    *maj = major;
-  if (min)
-    *min = minor;
-  if (st)
-    *st = partstart;
-  return 1;
-}
-#endif
-
-#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
-
-/* 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 error;
-  struct ggeom *geom;
-
-  grub_util_info ("following geom '%s'", name);
-
-  error = geom_gettree (&mesh);
-  if (error != 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, "start") == 0)
-               off += strtoull (config->lg_val, 0, 10);
-           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_hostdisk_find_partition_start (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;
-}
-
-#elif defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) || defined (__sun__)
-grub_disk_addr_t
-grub_hostdisk_find_partition_start (const char *dev)
-{
-  int fd;
-#ifdef __sun__
-  struct extpart_info pinfo;
-# elif !defined(HAVE_DIOCGDINFO)
-  struct hd_geometry hdg;
-# else /* defined(HAVE_DIOCGDINFO) */
-#  if defined(__NetBSD__)
-  struct dkwedge_info dkw;
-#  endif /* defined(__NetBSD__) */
-  struct disklabel label;
-  int p_index;
-# endif /* !defined(HAVE_DIOCGDINFO) */
-
-# ifdef HAVE_DEVICE_MAPPER
-  grub_disk_addr_t partition_start;
-  if (grub_util_device_is_mapped (dev)
-      && grub_util_get_dm_node_linear_info (dev, 0, 0, &partition_start))
-    return partition_start;
-# endif /* HAVE_DEVICE_MAPPER */
-
-  fd = open (dev, O_RDONLY);
-  if (fd == -1)
-    {
-      grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot open `%s': %s"),
-                 dev, strerror (errno));
-      return 0;
-    }
-
-#if defined(__sun__)
-  if (ioctl (fd, DKIOCEXTPARTINFO, &pinfo))
-# elif !defined(HAVE_DIOCGDINFO)
-  if (ioctl (fd, HDIO_GETGEO, &hdg))
-# else /* defined(HAVE_DIOCGDINFO) */
-#  if defined(__NetBSD__)
-  configure_device_driver (fd);
-  /* First handle the case of disk wedges.  */
-  if (ioctl (fd, DIOCGWEDGEINFO, &dkw) == 0)
-    {
-      close (fd);
-      return (grub_disk_addr_t) dkw.dkw_offset;
-    }
-#  endif /* defined(__NetBSD__) */
-  if (ioctl (fd, DIOCGDINFO, &label) == -1)
-# endif /* !defined(HAVE_DIOCGDINFO) */
-    {
-      grub_error (GRUB_ERR_BAD_DEVICE,
-# if !defined(HAVE_DIOCGDINFO)
-                 "cannot get disk geometry of `%s'", dev);
-# else /* defined(HAVE_DIOCGDINFO) */
-                 "cannot get disk label of `%s'", dev);
-# endif /* !defined(HAVE_DIOCGDINFO) */
-      close (fd);
-      return 0;
-    }
-
-  close (fd);
-
-#ifdef __sun__
-  return pinfo.p_start;
-# elif !defined(HAVE_DIOCGDINFO)
-  return hdg.start;
-# else /* defined(HAVE_DIOCGDINFO) */
-  if (dev[0])
-    p_index = dev[strlen(dev) - 1] - 'a';
-  else
-    p_index = -1;
-  
-  if (p_index >= label.d_npartitions || p_index < 0)
-    {
-      grub_error (GRUB_ERR_BAD_DEVICE,
-                 "no disk label entry for `%s'", dev);
-      return 0;
-    }
-  return (grub_disk_addr_t) label.d_partitions[p_index].p_offset;
-# endif /* !defined(HAVE_DIOCGDINFO) */
-}
-#endif /* __linux__ || __CYGWIN__ || HAVE_DIOCGDINFO */
-
-#ifdef __linux__
-/* Cache of partition start sectors for each disk.  */
-struct linux_partition_cache
-{
-  struct linux_partition_cache *next;
-  struct linux_partition_cache **prev;
-  char *dev;
-  unsigned long start;
-  int partno;
-};
-
-struct linux_partition_cache *linux_partition_cache_list;
-
-static int
-linux_find_partition (char *dev, grub_disk_addr_t sector)
-{
-  size_t len = strlen (dev);
-  const char *format;
-  char *p;
-  int i;
-  char real_dev[PATH_MAX];
-  struct linux_partition_cache *cache;
-  int missing = 0;
-
-  strcpy(real_dev, dev);
-
-  if (have_devfs () && strcmp (real_dev + len - 5, "/disc") == 0)
-    {
-      p = real_dev + len - 4;
-      format = "part%d";
-    }
-  else if (strncmp (real_dev, "/dev/disk/by-id/",
-                   sizeof ("/dev/disk/by-id/") - 1) == 0)
-    {
-      p = real_dev + len;
-      format = "-part%d";
-    }
-  else if (real_dev[len - 1] >= '0' && real_dev[len - 1] <= '9')
-    {
-      p = real_dev + len;
-      format = "p%d";
-    }
-  else
-    {
-      p = real_dev + len;
-      format = "%d";
-    }
-
-  for (cache = linux_partition_cache_list; cache; cache = cache->next)
-    {
-      if (strcmp (cache->dev, dev) == 0 && cache->start == sector)
-       {
-         sprintf (p, format, cache->partno);
-         strcpy (dev, real_dev);
-         return 1;
-       }
-    }
-
-  for (i = 1; i < 10000; i++)
-    {
-      int fd;
-      grub_disk_addr_t start;
-
-      sprintf (p, format, i);
-
-      fd = open (real_dev, O_RDONLY);
-      if (fd == -1)
-       {
-         if (missing++ < 10)
-           continue;
-         else
-           return 0;
-       }
-      missing = 0;
-      close (fd);
-
-      start = grub_hostdisk_find_partition_start (real_dev);
-      /* We don't care about errors here.  */
-      grub_errno = GRUB_ERR_NONE;
-
-      if (start == sector)
-       {
-         struct linux_partition_cache *new_cache_item;
-
-         new_cache_item = xmalloc (sizeof *new_cache_item);
-         new_cache_item->dev = xstrdup (dev);
-         new_cache_item->start = start;
-         new_cache_item->partno = i;
-         grub_list_push (GRUB_AS_LIST_P (&linux_partition_cache_list),
-                         GRUB_AS_LIST (new_cache_item));
-
-         strcpy (dev, real_dev);
-         return 1;
-       }
-    }
-
-  return 0;
-}
-#endif /* __linux__ */
-
 #if defined(__linux__) && (!defined(__GLIBC__) || \
         ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
   /* Maybe libc doesn't have large file support.  */
@@ -911,21 +265,6 @@ grub_util_fd_seek (int fd, const char *name, grub_uint64_t off)
 }
 #endif
 
-static void
-flush_initial_buffer (const char *os_dev __attribute__ ((unused)))
-{
-#ifdef __linux__
-  int fd;
-  struct stat st;
-
-  fd = open (os_dev, O_RDONLY);
-  if (fd >= 0 && fstat (fd, &st) >= 0 && S_ISBLK (st.st_mode))
-    ioctl (fd, BLKFLSBUF, 0);
-  if (fd >= 0)
-    close (fd);
-#endif
-}
-
 const char *
 grub_hostdisk_os_dev_to_grub_drive (const char *os_disk, int add)
 {
@@ -961,7 +300,7 @@ grub_hostdisk_os_dev_to_grub_drive (const char *os_disk, int add)
   strcpy (map[i].drive + sizeof ("hostdisk/") - 1, os_disk);
   map[i].device_map = 0;
 
-  flush_initial_buffer (os_disk);
+  grub_hostdisk_flush_initial_buffer (os_disk);
 
   return map[i].drive;
 }
@@ -1003,7 +342,7 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags,
        && strncmp (map[disk->id].device, "/dev/", 5) == 0)
       {
        if (sector >= part_start)
-         is_partition = linux_find_partition (dev, part_start);
+         is_partition = grub_hostdisk_linux_find_partition (dev, part_start);
        else
          *max = part_start - sector;
       }
@@ -1025,10 +364,8 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags,
            if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY)
              {
                fsync (data->fd);
-#ifdef __linux__
                if (data->is_disk)
                  ioctl (data->fd, BLKFLSBUF, 0);
-#endif
              }
 
            close (data->fd);
@@ -1049,10 +386,8 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags,
        data->access_mode = (flags & O_ACCMODE);
        data->fd = fd;
 
-#ifdef __linux__
        if (data->is_disk)
          ioctl (data->fd, BLKFLSBUF, 0);
-#endif
       }
 
     if (is_partition)
@@ -1106,10 +441,6 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags,
            if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY)
              {
                fsync (data->fd);
-#ifdef __linux__
-               if (data->is_disk)
-                 ioctl (data->fd, BLKFLSBUF, 0);
-#endif
              }
            close (data->fd);
            data->fd = -1;
@@ -1147,9 +478,7 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags,
     }
 #endif /* ! __linux__ */
 
-#if defined(__NetBSD__)
-  configure_device_driver (fd);
-#endif /* defined(__NetBSD__) */
+  grub_hostdisk_configure_device_driver (fd);
 
   if (grub_util_fd_seek (fd, map[disk->id].device,
                         sector << disk->log_sector_size))
@@ -1485,7 +814,7 @@ read_device_map (const char *dev_map)
       grub_util_info ("adding `%s' -> `%s' from device.map", map[drive].drive,
                      map[drive].device);
 
-      flush_initial_buffer (map[drive].device);
+      grub_hostdisk_flush_initial_buffer (map[drive].device);
     }
 
   fclose (fp);
diff --git a/grub-core/kern/emu/hostdisk_apple.c b/grub-core/kern/emu/hostdisk_apple.c
new file mode 100644 (file)
index 0000000..8ba785e
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2004,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 <grub/disk.h>
+#include <grub/partition.h>
+#include <grub/msdos_partition.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/getroot.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+#include <grub/list.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+# include <sys/disk.h>
+
+grub_uint64_t
+grub_util_get_fd_size (int fd, const char *name, unsigned *log_secsize)
+{
+  unsigned long long nr;
+  unsigned sector_size, log_sector_size;
+
+  if (ioctl (fd, DKIOCGETBLOCKCOUNT, &nr))
+    return -1;
+
+  if (ioctl (fd, DKIOCGETBLOCKSIZE, &sector_size))
+    return -1;
+
+  if (sector_size & (sector_size - 1) || !sector_size)
+    return -1;
+  for (log_sector_size = 0;
+       (1 << log_sector_size) < sector_size;
+       log_sector_size++);
+
+  if (log_secsize)
+    *log_secsize = log_sector_size;
+
+  return nr << log_sector_size;
+}
+
+void
+grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused)))
+{
+}
diff --git a/grub-core/kern/emu/hostdisk_basic.c b/grub-core/kern/emu/hostdisk_basic.c
new file mode 100644 (file)
index 0000000..a9342d0
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2004,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 <grub/disk.h>
+#include <grub/partition.h>
+#include <grub/msdos_partition.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/getroot.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+#include <grub/list.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+grub_int64_t
+grub_util_get_fd_size_os (int fd __attribute__ ((unused)),
+                      const char *name __attribute__ ((unused)),
+                      unsigned *log_secsize __attribute__ ((unused)))
+{
+# warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
+
+  return -1;
+}
+
+void
+grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused)))
+{
+}
+
+void
+grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused)))
+{
+}
diff --git a/grub-core/kern/emu/hostdisk_bsd.c b/grub-core/kern/emu/hostdisk_bsd.c
new file mode 100644 (file)
index 0000000..b986f03
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2004,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 <grub/disk.h>
+#include <grub/partition.h>
+#include <grub/msdos_partition.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/getroot.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+#include <grub/list.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+# include <sys/ioctl.h>
+# include <sys/disklabel.h>    /* struct disklabel */
+# include <sys/disk.h>    /* struct dkwedge_info */
+# ifdef HAVE_GETRAWPARTITION
+#  include <util.h>    /* getrawpartition */
+# endif /* HAVE_GETRAWPARTITION */
+# if defined(__NetBSD__)
+# include <sys/fdio.h>
+# endif
+# if defined(__OpenBSD__)
+# include <sys/dkio.h>
+# endif
+
+#if defined(__NetBSD__)
+/* Adjust device driver parameters.  This function should be called just
+   after successfully opening the device.  For now, it simply prevents the
+   floppy driver from retrying operations on failure, as otherwise the
+   driver takes a while to abort when there is no floppy in the drive.  */
+void
+grub_hostdisk_configure_device_driver (int fd)
+{
+  struct stat st;
+
+  if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode))
+    return;
+  if (major(st.st_rdev) == RAW_FLOPPY_MAJOR)
+    {
+      int floppy_opts;
+
+      if (ioctl (fd, FDIOCGETOPTS, &floppy_opts) == -1)
+       return;
+      floppy_opts |= FDOPT_NORETRY;
+      if (ioctl (fd, FDIOCSETOPTS, &floppy_opts) == -1)
+       return;
+    }
+}
+#else
+void
+grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused)))
+{
+}
+#endif
+
+grub_int64_t
+grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize)
+{
+  struct disklabel label;
+  unsigned sector_size, log_sector_size;
+
+  grub_hostdisk_configure_device_driver (fd);
+  
+  if (ioctl (fd, DIOCGDINFO, &label) == -1)
+    return -1;
+
+  sector_size = label.d_secsize;
+  if (sector_size & (sector_size - 1) || !sector_size)
+    return -1;
+  for (log_sector_size = 0;
+       (1 << log_sector_size) < sector_size;
+       log_sector_size++);
+
+  if (log_secsize)
+    *log_secsize = log_sector_size;
+
+  return (grub_uint64_t) label.d_secperunit << log_sector_size;
+}
+
+void
+grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused)))
+{
+}
diff --git a/grub-core/kern/emu/hostdisk_cygwin.c b/grub-core/kern/emu/hostdisk_cygwin.c
new file mode 100644 (file)
index 0000000..c1e4bd5
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2004,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 <grub/disk.h>
+#include <grub/partition.h>
+#include <grub/msdos_partition.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/getroot.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+#include <grub/list.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+# include <sys/ioctl.h>
+# include <cygwin/fs.h> /* BLKGETSIZE64 */
+# include <cygwin/hdreg.h> /* HDIO_GETGEO */
+
+grub_int64_t
+grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize)
+{
+  unsigned long long nr;
+  unsigned sector_size, log_sector_size;
+
+  if (ioctl (fd, BLKGETSIZE64, &nr))
+    return -1;
+
+  if (ioctl (fd, BLKSSZGET, &sector_size))
+    return -1;
+
+  if (sector_size & (sector_size - 1) || !sector_size)
+    return -1;
+  for (log_sector_size = 0;
+       (1 << log_sector_size) < sector_size;
+       log_sector_size++);
+
+  if (log_secsize)
+    *log_secsize = log_sector_size;
+
+  if (nr & ((1 << log_sector_size) - 1))
+    grub_util_error ("%s", _("unaligned device size"));
+
+  return nr;
+}
+
+void
+grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused)))
+{
+}
+
+void
+grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused)))
+{
+}
diff --git a/grub-core/kern/emu/hostdisk_devmapper.c b/grub-core/kern/emu/hostdisk_devmapper.c
new file mode 100644 (file)
index 0000000..347813e
--- /dev/null
@@ -0,0 +1,194 @@
+#include <config-util.h>
+
+#include <grub/disk.h>
+#include <grub/partition.h>
+#include <grub/msdos_partition.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/getroot.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+#include <grub/list.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+
+#ifdef HAVE_DEVICE_MAPPER
+# include <libdevmapper.h>
+
+static void device_mapper_null_log (int level __attribute__ ((unused)),
+                                   const char *file __attribute__ ((unused)),
+                                   int line __attribute__ ((unused)),
+                                   int dm_errno __attribute__ ((unused)),
+                                   const char *f __attribute__ ((unused)),
+                                   ...)
+{
+}
+
+int
+grub_device_mapper_supported (void)
+{
+  static int supported = -1;
+
+  if (supported == -1)
+    {
+      struct dm_task *dmt;
+
+      /* Suppress annoying log messages.  */
+      dm_log_with_errno_init (&device_mapper_null_log);
+
+      dmt = dm_task_create (DM_DEVICE_VERSION);
+      supported = (dmt != NULL);
+      if (dmt)
+       dm_task_destroy (dmt);
+
+      /* Restore the original logger.  */
+      dm_log_with_errno_init (NULL);
+    }
+
+  return supported;
+}
+
+int
+grub_util_device_is_mapped (const char *dev)
+{
+  struct stat st;
+
+  if (!grub_device_mapper_supported ())
+    return 0;
+
+  if (stat (dev, &st) < 0)
+    return 0;
+
+  return dm_is_dm_major (major (st.st_rdev));
+}
+
+int
+grub_util_get_dm_node_linear_info (const char *dev,
+                                  int *maj, int *min,
+                                  grub_disk_addr_t *st)
+{
+  struct dm_task *dmt;
+  void *next = NULL;
+  uint64_t length, start;
+  char *target, *params;
+  char *ptr;
+  int major = 0, minor = 0;
+  int first = 1;
+  grub_disk_addr_t partstart = 0;
+  const char *node_uuid;
+
+  while (1)
+    {
+      dmt = dm_task_create(DM_DEVICE_TABLE);
+      if (!dmt)
+       break;
+      
+      if (! (first ? dm_task_set_name (dmt, dev)
+            : dm_task_set_major_minor (dmt, major, minor, 0)))
+       {
+         dm_task_destroy (dmt);
+         break;
+       }
+      dm_task_no_open_count(dmt);
+      if (!dm_task_run(dmt))
+       {
+         dm_task_destroy (dmt);
+         break;
+       }
+      node_uuid = dm_task_get_uuid (dmt);
+      if (node_uuid && (strncmp (node_uuid, "LVM-", 4) == 0
+                       || strncmp (node_uuid, "mpath-", 6) == 0
+                       || strncmp (node_uuid, "DMRAID-", 7) == 0))
+       {
+         dm_task_destroy (dmt);
+         break;
+       }
+
+      next = dm_get_next_target(dmt, next, &start, &length,
+                               &target, &params);
+      if (grub_strcmp (target, "linear") != 0)
+       {
+         dm_task_destroy (dmt);
+         break;
+       }
+      major = grub_strtoul (params, &ptr, 10);
+      if (grub_errno)
+       {
+         dm_task_destroy (dmt);
+         grub_errno = GRUB_ERR_NONE;
+         return 0;
+       }
+      if (*ptr != ':')
+       {
+         dm_task_destroy (dmt);
+         return 0;
+       }
+      ptr++;
+      minor = grub_strtoul (ptr, &ptr, 10);
+      if (grub_errno)
+       {
+         grub_errno = GRUB_ERR_NONE;
+         dm_task_destroy (dmt);
+         return 0;
+       }
+
+      if (*ptr != ' ')
+       {
+         dm_task_destroy (dmt);
+         return 0;
+       }
+      ptr++;
+      partstart += grub_strtoull (ptr, &ptr, 10);
+      if (grub_errno)
+       {
+         grub_errno = GRUB_ERR_NONE;
+         dm_task_destroy (dmt);
+         return 0;
+       }
+
+      dm_task_destroy (dmt);
+      first = 0;
+      if (!dm_is_dm_major (major))
+       break;
+    }
+  if (first)
+    return 0;
+  if (maj)
+    *maj = major;
+  if (min)
+    *min = minor;
+  if (st)
+    *st = partstart;
+  return 1;
+}
+#else
+
+int
+grub_util_device_is_mapped (const char *dev __attribute__ ((unused)))
+{
+  return 0;
+}
+
+int
+grub_util_get_dm_node_linear_info (const char *dev __attribute__ ((unused)),
+                                  int *maj __attribute__ ((unused)),
+                                  int *min __attribute__ ((unused)),
+                                  grub_disk_addr_t *st __attribute__ ((unused)))
+{
+  return 0;
+}
+
+#endif
diff --git a/grub-core/kern/emu/hostdisk_freebsd.c b/grub-core/kern/emu/hostdisk_freebsd.c
new file mode 100644 (file)
index 0000000..72ff591
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2004,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 <grub/disk.h>
+#include <grub/partition.h>
+#include <grub/msdos_partition.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/getroot.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+#include <grub/list.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+# include <sys/disk.h> /* DIOCGMEDIASIZE */
+# include <sys/param.h>
+# include <sys/sysctl.h>
+# include <sys/mount.h>
+# include <libgeom.h>
+
+void
+grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused)))
+{
+}
+
+grub_int64_t
+grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize)
+{
+  unsigned long long nr;
+  unsigned sector_size, log_sector_size;
+
+  if (ioctl (fd, DIOCGMEDIASIZE, &nr))
+    return -1;
+
+  if (ioctl (fd, DIOCGSECTORSIZE, &sector_size))
+    return -1;
+  if (sector_size & (sector_size - 1) || !sector_size)
+    return -1;
+  for (log_sector_size = 0;
+       (1 << log_sector_size) < sector_size;
+       log_sector_size++);
+
+  if (log_secsize)
+    *log_secsize = log_sector_size;
+
+  if (nr & (sector_size - 1))
+    grub_util_error ("%s", _("unaligned device size"));
+
+  return nr;
+}
+
+void
+grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused)))
+{
+}
diff --git a/grub-core/kern/emu/hostdisk_hurd.c b/grub-core/kern/emu/hostdisk_hurd.c
new file mode 100644 (file)
index 0000000..7a364ed
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2004,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 <grub/disk.h>
+#include <grub/partition.h>
+#include <grub/msdos_partition.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/getroot.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+#include <grub/list.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <hurd.h>
+#include <hurd/lookup.h>
+#include <hurd/fs.h>
+#include <sys/mman.h>
+
+void
+grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused)))
+{
+}
+
+int
+grub_util_hurd_get_disk_info (const char *dev, grub_uint32_t *secsize, grub_disk_addr_t *offset,
+                             grub_disk_addr_t *size, char **parent)
+{
+  file_t file;
+  mach_port_t *ports;
+  int *ints;
+  loff_t *offsets;
+  char *data;
+  error_t err;
+  mach_msg_type_number_t num_ports = 0, num_ints = 0, num_offsets = 0, data_len = 0;
+
+  file = file_name_lookup (dev, 0, 0);
+  if (file == MACH_PORT_NULL)
+    return 0;
+
+  err = file_get_storage_info (file,
+                              &ports, &num_ports,
+                              &ints, &num_ints,
+                              &offsets, &num_offsets,
+                              &data, &data_len);
+
+  if (num_ints < 1)
+    grub_util_error (_("Storage info for `%s' does not include type"), dev);
+  if (ints[0] != STORAGE_DEVICE)
+    grub_util_error (_("`%s' is not a local disk"), dev);
+
+  if (num_offsets != 2)
+    grub_util_error (_("Storage info for `%s' does indicate neither plain partition nor plain disk"), dev);
+  if (parent)
+    {
+      *parent = NULL;
+      if (num_ints >= 5)
+       {
+         size_t len = ints[4];
+         if (len > data_len)
+           len = data_len;
+         *parent = xmalloc (len+1);
+         memcpy (*parent, data, len);
+         (*parent)[len] = '\0';
+       }
+    }
+  if (offset)
+    *offset = offsets[0];
+  if (size)
+    *size = offsets[1];
+  if (secsize)
+    *secsize = ints[2];
+  if (ports && num_ports > 0)
+    {
+      mach_msg_type_number_t i;
+      for (i = 0; i < num_ports; i++)
+        {
+         mach_port_t port = ports[i];
+         if (port != MACH_PORT_NULL)
+           mach_port_deallocate (mach_task_self(), port);
+        }
+      munmap ((caddr_t) ports, num_ports * sizeof (*ports));
+    }
+
+  if (ints && num_ints > 0)
+    munmap ((caddr_t) ints, num_ints * sizeof (*ints));
+  if (offsets && num_offsets > 0)
+    munmap ((caddr_t) offsets, num_offsets * sizeof (*offsets));
+  if (data && data_len > 0)
+    munmap (data, data_len);
+  mach_port_deallocate (mach_task_self (), file);
+
+  return 1;
+}
+
+grub_int64_t
+grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize)
+{
+  grub_uint32_t sector_size;
+  grub_disk_addr_t size;
+  unsigned log_sector_size;
+
+  if (!grub_util_hurd_get_disk_info (name, &sector_size, NULL, &size, NULL))
+    return -1;
+
+  if (sector_size & (sector_size - 1) || !sector_size)
+    return -1;
+  for (log_sector_size = 0;
+       (1 << log_sector_size) < sector_size;
+       log_sector_size++);
+
+  if (log_secsize)
+    *log_secsize = log_sector_size;
+
+  return size << log_sector_size;
+}
+
+void
+grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused)))
+{
+}
diff --git a/grub-core/kern/emu/hostdisk_linux.c b/grub-core/kern/emu/hostdisk_linux.c
new file mode 100644 (file)
index 0000000..eef89ad
--- /dev/null
@@ -0,0 +1,255 @@
+/* hostdisk.c - emulate biosdisk */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2004,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 <grub/disk.h>
+#include <grub/partition.h>
+#include <grub/msdos_partition.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/getroot.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+#include <grub/list.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+# include <sys/ioctl.h>         /* ioctl */
+# include <sys/mount.h>
+# ifndef BLKFLSBUF
+#  define BLKFLSBUF     _IO (0x12,97)   /* flush buffer cache */
+# endif /* ! BLKFLSBUF */
+# include <sys/ioctl.h>                /* ioctl */
+# ifndef HDIO_GETGEO
+#  define HDIO_GETGEO  0x0301  /* get device geometry */
+/* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
+   defined.  */
+struct hd_geometry
+{
+  unsigned char heads;
+  unsigned char sectors;
+  unsigned short cylinders;
+  unsigned long start;
+};
+# endif /* ! HDIO_GETGEO */
+# ifndef BLKGETSIZE64
+#  define BLKGETSIZE64  _IOR(0x12,114,size_t)    /* return device size */
+# endif /* ! BLKGETSIZE64 */
+
+
+grub_int64_t
+grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize)
+{
+  unsigned long long nr;
+  unsigned sector_size, log_sector_size;
+
+  if (ioctl (fd, BLKGETSIZE64, &nr))
+    return -1;
+
+  if (ioctl (fd, BLKSSZGET, &sector_size))
+    return -1;
+
+  if (sector_size & (sector_size - 1) || !sector_size)
+    return -1;
+  for (log_sector_size = 0;
+       (1 << log_sector_size) < sector_size;
+       log_sector_size++);
+
+  if (log_secsize)
+    *log_secsize = log_sector_size;
+
+  if (nr & ((1 << log_sector_size) - 1))
+    grub_util_error ("%s", _("unaligned device size"));
+
+  return nr;
+}
+
+grub_disk_addr_t
+grub_util_find_partition_start_os (const char *dev)
+{
+  int fd;
+  struct hd_geometry hdg;
+
+  fd = open (dev, O_RDONLY);
+  if (fd == -1)
+    {
+      grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot open `%s': %s"),
+                 dev, strerror (errno));
+      return 0;
+    }
+
+  if (ioctl (fd, HDIO_GETGEO, &hdg))
+    {
+      grub_error (GRUB_ERR_BAD_DEVICE,
+                 "cannot get disk geometry of `%s'", dev);
+      close (fd);
+      return 0;
+    }
+
+  close (fd);
+
+  return hdg.start;
+}
+
+/* Cache of partition start sectors for each disk.  */
+struct linux_partition_cache
+{
+  struct linux_partition_cache *next;
+  struct linux_partition_cache **prev;
+  char *dev;
+  unsigned long start;
+  int partno;
+};
+
+struct linux_partition_cache *linux_partition_cache_list;
+
+/* Check if we have devfs support.  */
+static int
+have_devfs (void)
+{
+  static int dev_devfsd_exists = -1;
+
+  if (dev_devfsd_exists < 0)
+    {
+      struct stat st;
+
+      dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0;
+    }
+
+  return dev_devfsd_exists;
+}
+
+int
+grub_hostdisk_linux_find_partition (char *dev, grub_disk_addr_t sector)
+{
+  size_t len = strlen (dev);
+  const char *format;
+  char *p;
+  int i;
+  char real_dev[PATH_MAX];
+  struct linux_partition_cache *cache;
+  int missing = 0;
+
+  strcpy(real_dev, dev);
+
+  if (have_devfs () && strcmp (real_dev + len - 5, "/disc") == 0)
+    {
+      p = real_dev + len - 4;
+      format = "part%d";
+    }
+  else if (strncmp (real_dev, "/dev/disk/by-id/",
+                   sizeof ("/dev/disk/by-id/") - 1) == 0)
+    {
+      p = real_dev + len;
+      format = "-part%d";
+    }
+  else if (real_dev[len - 1] >= '0' && real_dev[len - 1] <= '9')
+    {
+      p = real_dev + len;
+      format = "p%d";
+    }
+  else
+    {
+      p = real_dev + len;
+      format = "%d";
+    }
+
+  for (cache = linux_partition_cache_list; cache; cache = cache->next)
+    {
+      if (strcmp (cache->dev, dev) == 0 && cache->start == sector)
+       {
+         sprintf (p, format, cache->partno);
+         strcpy (dev, real_dev);
+         return 1;
+       }
+    }
+
+  for (i = 1; i < 10000; i++)
+    {
+      int fd;
+      grub_disk_addr_t start;
+
+      sprintf (p, format, i);
+
+      fd = open (real_dev, O_RDONLY);
+      if (fd == -1)
+       {
+         if (missing++ < 10)
+           continue;
+         else
+           return 0;
+       }
+      missing = 0;
+      close (fd);
+
+      if (!grub_util_device_is_mapped (real_dev)
+         || !grub_util_get_dm_node_linear_info (real_dev, 0, 0, &start))
+       start = grub_util_find_partition_start_os (real_dev);
+      /* We don't care about errors here.  */
+      grub_errno = GRUB_ERR_NONE;
+
+      if (start == sector)
+       {
+         struct linux_partition_cache *new_cache_item;
+
+         new_cache_item = xmalloc (sizeof *new_cache_item);
+         new_cache_item->dev = xstrdup (dev);
+         new_cache_item->start = start;
+         new_cache_item->partno = i;
+         grub_list_push (GRUB_AS_LIST_P (&linux_partition_cache_list),
+                         GRUB_AS_LIST (new_cache_item));
+
+         strcpy (dev, real_dev);
+         return 1;
+       }
+    }
+
+  return 0;
+}
+
+void
+grub_hostdisk_flush_initial_buffer (const char *os_dev)
+{
+  int fd;
+  struct stat st;
+
+  fd = open (os_dev, O_RDONLY);
+  if (fd >= 0 && fstat (fd, &st) >= 0 && S_ISBLK (st.st_mode))
+    ioctl (fd, BLKFLSBUF, 0);
+  if (fd >= 0)
+    close (fd);
+}
+
+void
+grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused)))
+{
+}
diff --git a/grub-core/kern/emu/hostdisk_mingw.c b/grub-core/kern/emu/hostdisk_mingw.c
new file mode 100644 (file)
index 0000000..eceb5d4
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2004,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 <grub/disk.h>
+#include <grub/partition.h>
+#include <grub/msdos_partition.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/getroot.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+#include <grub/list.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <windows.h>
+#include <winioctl.h>
+#include "dirname.h"
+
+grub_int64_t
+grub_util_get_fd_size_os (int fd __attribute__ ((unused)),
+                         const char *name_in,
+                         unsigned *log_secsize)
+{
+  HANDLE hd;
+  grub_int64_t size = -1LL;
+  int log_sector_size = 9;
+  char *name = xstrdup (name_in);
+
+  if (log_secsize)
+    *log_secsize = log_sector_size;
+
+  strip_trailing_slashes(name);
+  hd = CreateFile (name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                   0, OPEN_EXISTING, 0, 0);
+
+  if (hd == INVALID_HANDLE_VALUE)
+    {
+      free (name);
+      return size;
+    }
+
+  if (((name[0] == '/') || (name[0] == '\\')) &&
+      ((name[1] == '/') || (name[1] == '\\')) &&
+      (name[2] == '.') &&
+      ((name[3] == '/') || (name[3] == '\\')) &&
+      (! strncasecmp (name + 4, "PHYSICALDRIVE", 13)))
+    {
+      DWORD nr;
+      DISK_GEOMETRY g;
+
+      if (! DeviceIoControl (hd, IOCTL_DISK_GET_DRIVE_GEOMETRY,
+                             0, 0, &g, sizeof (g), &nr, 0))
+        goto fail;
+
+      size = g.Cylinders.QuadPart;
+      size *= g.TracksPerCylinder * g.SectorsPerTrack * g.BytesPerSector;
+
+      for (log_sector_size = 0;
+          (1 << log_sector_size) < g.BytesPerSector;
+          log_sector_size++);
+    }
+  else
+    {
+      ULARGE_INTEGER s;
+
+      s.LowPart = GetFileSize (hd, &s.HighPart);
+      size = s.QuadPart;
+    }
+
+ fail:
+
+  if (log_secsize)
+    *log_secsize = log_sector_size;
+
+  free (name);
+
+  CloseHandle (hd);
+
+  return size;
+}
+
+void
+grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused)))
+{
+}
+
+void
+grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused)))
+{
+}
diff --git a/grub-core/kern/emu/hostdisk_os.c b/grub-core/kern/emu/hostdisk_os.c
new file mode 100644 (file)
index 0000000..c21b764
--- /dev/null
@@ -0,0 +1,20 @@
+#ifdef __linux__
+#include "hostdisk_linux.c"
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include "hostdisk_freebsd.c"
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+#include "hostdisk_bsd.c"
+#elif defined(__APPLE__)
+#include "hostdisk_apple.c"
+#elif defined(__sun__)
+#include "hostdisk_sun.c"
+#elif defined(__GNU__)
+#include "hostdisk_hurd.c"
+#elif defined(__CYGWIN__)
+#include "hostdisk_cygwin.c"
+#elif defined(__MINGW32__)
+#include "hostdisk_mingw.c"
+#else
+# warning "No hostdisk OS-specific functions is available for your system. Device detection may not work properly."
+#include "hostdisk_basic.c"
+#endif
diff --git a/grub-core/kern/emu/hostdisk_sun.c b/grub-core/kern/emu/hostdisk_sun.c
new file mode 100644 (file)
index 0000000..0232fa2
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2004,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 <grub/disk.h>
+#include <grub/partition.h>
+#include <grub/msdos_partition.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/getroot.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+#include <grub/list.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+# include <sys/dkio.h>
+
+grub_int64_t
+grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize)
+{
+  struct dk_minfo minfo;
+  unsigned sector_size, log_sector_size;
+
+  if (!ioctl (fd, DKIOCGMEDIAINFO, &minfo))
+    return -1;
+
+  sector_size = minfo.dki_lbsize;
+
+  if (sector_size & (sector_size - 1) || !sector_size)
+    return -1;
+  for (log_sector_size = 0;
+       (1 << log_sector_size) < sector_size;
+       log_sector_size++);
+
+  if (log_secsize)
+    *log_secsize = log_sector_size;
+
+  return minfo.dki_capacity << log_sector_size;
+}
+
+void
+grub_hostdisk_configure_device_driver (int fd __attribute__ ((unused)))
+{
+}
+
+void
+grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused)))
+{
+}
index 83455dddac4bb4175863341c087dc91b16038149..e4b903fa6816528086f5fa973ef4c5cbe8100908 100644 (file)
@@ -19,6 +19,8 @@
 #ifndef GRUB_UTIL_GETROOT_HEADER
 #define GRUB_UTIL_GETROOT_HEADER       1
 
+#include <grub/types.h>
+
 #include <sys/types.h>
 
 enum grub_dev_abstraction_types {
@@ -30,12 +32,13 @@ enum grub_dev_abstraction_types {
 };
 
 char *grub_find_device (const char *dir, dev_t dev);
+void grub_util_pull_device (const char *osname);
 char **grub_guess_root_devices (const char *dir);
 int grub_util_get_dev_abstraction (const char *os_dev);
-char *grub_util_get_grub_dev (const char *os_dev);
 char *grub_make_system_path_relative_to_its_root (const char *path);
 const char *grub_util_check_block_device (const char *blk_dev);
 const char *grub_util_check_char_device (const char *blk_dev);
+char *grub_util_get_grub_dev (const char *os_dev);
 #ifdef __linux__
 char **grub_util_raid_getmembers (const char *name, int bootable);
 #endif
@@ -44,4 +47,57 @@ void grub_util_follow_gpart_up (const char *name, grub_disk_addr_t *off_out,
                                char **name_out);
 #endif
 
+#include <sys/stat.h>
+
+#ifdef __linux__
+char **
+grub_find_root_devices_from_mountinfo (const char *dir, char **relroot);
+#endif
+#if defined (__GNU__)
+char *
+grub_util_find_hurd_root_device (const char *path);
+#endif
+
+/* Devmapper functions provided by getroot_devmapper.c.  */
+void
+grub_util_pull_devmapper (const char *os_dev);
+int
+grub_util_device_is_mapped_stat (struct stat *st);
+void grub_util_devmapper_cleanup (void);
+enum grub_dev_abstraction_types
+grub_util_get_dm_abstraction (const char *os_dev);
+char *
+grub_util_get_vg_uuid (const char *os_dev);
+char *
+grub_util_devmapper_part_to_disk (struct stat *st,
+                                 int *is_part, const char *os_dev);
+char *
+grub_util_get_devmapper_grub_dev (const char *os_dev);
+
+/* Functions provided by getroot.c.  */
+#if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__)
+#include <sys/types.h>
+pid_t
+grub_util_exec_pipe (char **argv, int *fd);
+#endif
+char **
+grub_util_find_root_devices_from_poolname (char *poolname);
+
+grub_disk_addr_t
+grub_util_find_partition_start (const char *dev);
+
+/* OS-specific functions provided by getroot_*.c.  */
+enum grub_dev_abstraction_types
+grub_util_get_dev_abstraction_os (const char *os_dev);
+char *
+grub_util_part_to_disk (const char *os_dev, struct stat *st,
+                       int *is_part);
+int
+grub_util_pull_device_os (const char *osname,
+                         enum grub_dev_abstraction_types ab);
+char *
+grub_util_get_grub_dev_os (const char *os_dev);
+grub_disk_addr_t
+grub_util_find_partition_start_os (const char *dev);
+
 #endif /* ! GRUB_UTIL_GETROOT_HEADER */
index a4e7d14347f7e160ab5401edf953954aef59bc06..fd882dfad0bf1f395d8a2697f18c1cca8b0d328b 100644 (file)
@@ -33,7 +33,6 @@ int grub_util_biosdisk_is_floppy (grub_disk_t disk);
 const char *
 grub_util_biosdisk_get_compatibility_hint (grub_disk_t disk);
 grub_err_t grub_util_biosdisk_flush (struct grub_disk *disk);
-void grub_util_pull_device (const char *osname);
 grub_err_t
 grub_util_fd_seek (int fd, const char *name, grub_uint64_t sector);
 ssize_t grub_util_fd_read (int fd, char *buf, size_t len);
@@ -52,8 +51,6 @@ grub_util_ldm_embed (struct grub_disk *disk, unsigned int *nsectors,
                     grub_embed_type_t embed_type,
                     grub_disk_addr_t **sectors);
 #endif
-grub_disk_addr_t
-grub_hostdisk_find_partition_start (const char *dev);
 const char *
 grub_hostdisk_os_dev_to_grub_drive (const char *os_dev, int add);
 
@@ -63,11 +60,45 @@ grub_util_get_fd_size (int fd, const char *name, unsigned *log_secsize);
 char *
 grub_util_get_os_disk (const char *os_dev);
 
-#ifdef HAVE_DEVICE_MAPPER
+#ifdef __linux__
+int
+grub_hostdisk_linux_find_partition (char *dev, grub_disk_addr_t sector);
+#endif
+
 int
 grub_util_get_dm_node_linear_info (const char *dev,
                                   int *maj, int *min,
                                   grub_disk_addr_t *st);
+
+
+/* Supplied by hostdisk_*.c.  */
+grub_int64_t
+grub_util_get_fd_size_os (int fd, const char *name, unsigned *log_secsize);
+/* REturns partition offset in 512B blocks.  */
+grub_disk_addr_t
+grub_hostdisk_find_partition_start_os (const char *dev);
+/* Adjust device driver parameters.  This function should be called just
+   after successfully opening the device.  For now, it simply prevents the
+   floppy driver from retrying operations on failure, as otherwise the
+   driver takes a while to abort when there is no floppy in the drive.
+   For now it's non-nop only on NetBSD.
+*/
+void
+grub_hostdisk_configure_device_driver (int fd);
+void
+grub_hostdisk_flush_initial_buffer (const char *os_dev);
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) || defined (__sun__) || defined(__OpenBSD__)
+#define GRUB_DISK_DEVS_ARE_CHAR 1
+#else
+#define GRUB_DISK_DEVS_ARE_CHAR 0
+#endif
+
+#ifdef __GNU__
+int
+grub_util_hurd_get_disk_info (const char *dev, grub_uint32_t *secsize,
+                             grub_disk_addr_t *offset,
+                             grub_disk_addr_t *size, char **parent);
 #endif
 
 #endif /* ! GRUB_BIOSDISK_MACHINE_UTIL_HEADER */
index 59944333f16f5e631a7d6b2b21abfdd6a0da0e20..f4577d56f01b77d9066e7ff58dfd6e7212d3c6a3 100644 (file)
 #ifndef GRUB_LVM_UTIL_HEADER
 #define GRUB_LVM_UTIL_HEADER   1
 
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-
-#ifdef __linux__
-#define LVM_DEV_MAPPER_STRING "/dev/mapper/"
-#else
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 #define LVM_DEV_MAPPER_STRING "/dev/linux_lvm/"
-#endif
-
+#else
+#define LVM_DEV_MAPPER_STRING "/dev/mapper/"
 #endif
 
 #endif /* ! GRUB_RAID_UTIL_HEADER */
index 69e04dc907d5b739c37397206981729f2bf7c702..eab6f24df220db61b6f914db8d14ad07f7fd946b 100644 (file)
 #include <grub/cryptodisk.h>
 #include <grub/i18n.h>
 
-#ifdef HAVE_DEVICE_MAPPER
-# include <libdevmapper.h>
+#ifdef __linux__
+#include <sys/ioctl.h>         /* ioctl */
+#include <sys/mount.h>
+#ifndef MAJOR
+# ifndef MINORBITS
+#  define MINORBITS    8
+# endif /* ! MINORBITS */
+# define MAJOR(dev)    ((unsigned) ((dev) >> MINORBITS))
+#endif /* ! MAJOR */
+#ifndef FLOPPY_MAJOR
+# define FLOPPY_MAJOR  2
+#endif /* ! FLOPPY_MAJOR */
 #endif
 
 #ifdef __GNU__
 
 #include <sys/types.h>
 
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)
-# include <sys/param.h>
-# include <sys/mount.h>
-#endif
-
 #if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
 # include <grub/util/libzfs.h>
 # include <grub/util/libnvpair.h>
 #endif
 
-#ifdef __sun__
-# include <sys/types.h>
-# include <sys/mkdev.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>
 
-#ifdef __linux__
-# include <sys/ioctl.h>         /* ioctl */
-# include <sys/mount.h>
-# ifndef MAJOR
-#  ifndef MINORBITS
-#   define MINORBITS   8
-#  endif /* ! MINORBITS */
-#  define MAJOR(dev)   ((unsigned) ((dev) >> MINORBITS))
-# endif /* ! MAJOR */
-# ifndef FLOPPY_MAJOR
-#  define FLOPPY_MAJOR 2
-# endif /* ! FLOPPY_MAJOR */
-#endif
-
 #ifdef __CYGWIN__
 # include <sys/ioctl.h>
 # include <cygwin/fs.h> /* BLKGETSIZE64 */
 #endif
 
 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-# include <sys/disk.h> /* DIOCGMEDIASIZE */
-# include <sys/param.h>
-# include <sys/sysctl.h>
-# include <sys/mount.h>
-#include <libgeom.h>
 # define MAJOR(dev) major(dev)
 # define FLOPPY_MAJOR  2
 #endif
 
-#if defined (__sun__)
-# include <sys/dkio.h>
-#endif
-
-#if defined(__APPLE__)
-# include <sys/disk.h>
-# include <sys/param.h>
-# include <sys/sysctl.h>
-# include <sys/mount.h>
-#endif
-
-#ifdef HAVE_DEVICE_MAPPER
-# include <libdevmapper.h>
+#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
+#include <sys/mount.h>
 #endif
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
-# define HAVE_DIOCGDINFO
 # include <sys/ioctl.h>
 # include <sys/disklabel.h>    /* struct disklabel */
 # include <sys/disk.h>    /* struct dkwedge_info */
-#else /* !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) */
-# undef HAVE_DIOCGDINFO
+#include <sys/param.h>
+#include <sys/mount.h>
 #endif /* defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
 
-#if defined(__NetBSD__)  || defined(__OpenBSD__)
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+# define MAJOR(dev) major(dev)
 # ifdef HAVE_GETRAWPARTITION
 #  include <util.h>    /* getrawpartition */
 # endif /* HAVE_GETRAWPARTITION */
 # endif /* ! RAW_FLOPPY_MAJOR */
 #endif /* defined(__NetBSD__) */
 
-#ifdef __linux__
-/* Defines taken from btrfs/ioctl.h.  */
-
-struct btrfs_ioctl_dev_info_args
-{
-  grub_uint64_t devid;
-  grub_uint8_t uuid[16];
-  grub_uint64_t bytes_used;
-  grub_uint64_t total_bytes;
-  grub_uint64_t unused[379];
-  grub_uint8_t path[1024];
-};
-
-struct btrfs_ioctl_fs_info_args
-{
-  grub_uint64_t max_id;
-  grub_uint64_t num_devices;
-  grub_uint8_t fsid[16];
-  grub_uint64_t reserved[124];
-};
-
-#define BTRFS_IOC_DEV_INFO _IOWR(0x94, 30, \
-                                 struct btrfs_ioctl_dev_info_args)
-#define BTRFS_IOC_FS_INFO _IOR(0x94, 31, \
-                               struct btrfs_ioctl_fs_info_args)
-#endif
+#if !defined (__MINGW32__) && !defined (__CYGWIN__)
 
-#ifdef __linux__
-static int
-grub_util_is_imsm (const char *os_dev);
+static void
+pull_lvm_by_command (const char *os_dev);
 #endif
 
-#if ! defined(__CYGWIN__) && !defined(__GNU__)
+#if ! defined(__CYGWIN__)
 
 static void
 strip_extra_slashes (char *dir)
@@ -222,13 +166,13 @@ xgetcwd (void)
 
 #endif
 
-#if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__)
+#if !defined (__MINGW32__) && !defined (__CYGWIN__)
 
 #include <sys/types.h>
 #include <sys/wait.h>
 
-static pid_t
-exec_pipe (char **argv, int *fd)
+pid_t
+grub_util_exec_pipe (char **argv, int *fd)
 {
   int mdadm_pipe[2];
   pid_t mdadm_pid;
@@ -249,9 +193,7 @@ exec_pipe (char **argv, int *fd)
       /* Child.  */
 
       /* Close fd's.  */
-#ifdef HAVE_DEVICE_MAPPER
-      dm_lib_release ();
-#endif
+      grub_util_devmapper_cleanup ();
       grub_diskfilter_fini ();
 
       /* Ensure child is not localised.  */
@@ -272,8 +214,11 @@ exec_pipe (char **argv, int *fd)
     }
 }
 
-static char **
-find_root_devices_from_poolname (char *poolname)
+#endif
+
+#if !defined (__CYGWIN__) && !defined(__MINGW32__) && !defined (__GNU__)
+char **
+grub_util_find_root_devices_from_poolname (char *poolname)
 {
   char **devices = 0;
   size_t ndevices = 0;
@@ -361,7 +306,7 @@ find_root_devices_from_poolname (char *poolname)
   argv[2] = (char *) poolname;
   argv[3] = NULL;
 
-  pid = exec_pipe (argv, &fd);
+  pid = grub_util_exec_pipe (argv, &fd);
   if (!pid)
     return NULL;
 
@@ -451,258 +396,6 @@ find_root_devices_from_poolname (char *poolname)
 
 #endif
 
-#ifdef __linux__
-
-#define ESCAPED_PATH_MAX (4 * PATH_MAX)
-struct mountinfo_entry
-{
-  int id;
-  int major, minor;
-  char enc_root[ESCAPED_PATH_MAX + 1], enc_path[ESCAPED_PATH_MAX + 1];
-  char fstype[ESCAPED_PATH_MAX + 1], device[ESCAPED_PATH_MAX + 1];
-};
-
-/* Statting something on a btrfs filesystem always returns a virtual device
-   major/minor pair rather than the real underlying device, because btrfs
-   can span multiple underlying devices (and even if it's currently only
-   using a single device it can be dynamically extended onto another).  We
-   can't deal with the multiple-device case yet, but in the meantime, we can
-   at least cope with the single-device case by scanning
-   /proc/self/mountinfo.  */
-static void
-unescape (char *str)
-{
-  char *optr;
-  const char *iptr;
-  for (iptr = optr = str; *iptr; optr++)
-    {
-      if (iptr[0] == '\\' && iptr[1] >= '0' && iptr[1] < '8'
-         && iptr[2] >= '0' && iptr[2] < '8'
-         && iptr[3] >= '0' && iptr[3] < '8')
-       {
-         *optr = (((iptr[1] - '0') << 6) | ((iptr[2] - '0') << 3)
-                  | (iptr[3] - '0'));
-         iptr += 4;
-       }
-      else
-       *optr = *iptr++;
-    }
-  *optr = 0;
-}
-
-static char **
-grub_find_root_devices_from_btrfs (const char *dir)
-{
-  int fd;
-  struct btrfs_ioctl_fs_info_args fsi;
-  int i, j = 0;
-  char **ret;
-
-  fd = open (dir, 0);
-  if (!fd)
-    return NULL;
-
-  if (ioctl (fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
-    {
-      close (fd);
-      return NULL;
-    }
-
-  ret = xmalloc ((fsi.num_devices + 1) * sizeof (ret[0]));
-
-  for (i = 1; i <= fsi.max_id && j < fsi.num_devices; i++)
-    {
-      struct btrfs_ioctl_dev_info_args devi;
-      memset (&devi, 0, sizeof (devi));
-      devi.devid = i;
-      if (ioctl (fd, BTRFS_IOC_DEV_INFO, &devi) < 0)
-       {
-         close (fd);
-         free (ret);
-         return NULL;
-       }
-      ret[j++] = xstrdup ((char *) devi.path);
-      if (j >= fsi.num_devices)
-       break;
-    }
-  close (fd);
-  ret[j] = 0;
-  return ret;
-}
-
-static char **
-grub_find_root_devices_from_mountinfo (const char *dir, char **relroot)
-{
-  FILE *fp;
-  char *buf = NULL;
-  size_t len = 0;
-  grub_size_t entry_len = 0, entry_max = 4;
-  struct mountinfo_entry *entries;
-  struct mountinfo_entry parent_entry = { 0, 0, 0, "", "", "", "" };
-  int i;
-
-  if (! *dir)
-    dir = "/";
-  if (relroot)
-    *relroot = NULL;
-
-  fp = fopen ("/proc/self/mountinfo", "r");
-  if (! fp)
-    return NULL; /* fall through to other methods */
-
-  entries = xmalloc (entry_max * sizeof (*entries));
-
-  /* First, build a list of relevant visible mounts.  */
-  while (getline (&buf, &len, fp) > 0)
-    {
-      struct mountinfo_entry entry;
-      int count;
-      size_t enc_path_len;
-      const char *sep;
-
-      if (sscanf (buf, "%d %d %u:%u %s %s%n",
-                 &entry.id, &parent_entry.id, &entry.major, &entry.minor,
-                 entry.enc_root, entry.enc_path, &count) < 6)
-       continue;
-
-      unescape (entry.enc_root);
-      unescape (entry.enc_path);
-
-      enc_path_len = strlen (entry.enc_path);
-      /* Check that enc_path is a prefix of dir.  The prefix must either be
-         the entire string, or end with a slash, or be immediately followed
-         by a slash.  */
-      if (strncmp (dir, entry.enc_path, enc_path_len) != 0 ||
-         (enc_path_len && dir[enc_path_len - 1] != '/' &&
-          dir[enc_path_len] && dir[enc_path_len] != '/'))
-       continue;
-
-      sep = strstr (buf + count, " - ");
-      if (!sep)
-       continue;
-
-      sep += sizeof (" - ") - 1;
-      if (sscanf (sep, "%s %s", entry.fstype, entry.device) != 2)
-       continue;
-
-      unescape (entry.device);
-
-      /* Using the mount IDs, find out where this fits in the list of
-        visible mount entries we've seen so far.  There are three
-        interesting cases.  Firstly, it may be inserted at the end: this is
-        the usual case of /foo/bar being mounted after /foo.  Secondly, it
-        may be inserted at the start: for example, this can happen for
-        filesystems that are mounted before / and later moved under it.
-        Thirdly, it may occlude part or all of the existing filesystem
-        tree, in which case the end of the list needs to be pruned and this
-        new entry will be inserted at the end.  */
-      if (entry_len >= entry_max)
-       {
-         entry_max <<= 1;
-         entries = xrealloc (entries, entry_max * sizeof (*entries));
-       }
-
-      if (!entry_len)
-       {
-         /* Initialise list.  */
-         entry_len = 2;
-         entries[0] = parent_entry;
-         entries[1] = entry;
-       }
-      else
-       {
-         for (i = entry_len - 1; i >= 0; i--)
-           {
-             if (entries[i].id == parent_entry.id)
-               {
-                 /* Insert at end, pruning anything previously above this.  */
-                 entry_len = i + 2;
-                 entries[i + 1] = entry;
-                 break;
-               }
-             else if (i == 0 && entries[i].id == entry.id)
-               {
-                 /* Insert at start.  */
-                 entry_len++;
-                 memmove (entries + 1, entries,
-                          (entry_len - 1) * sizeof (*entries));
-                 entries[0] = parent_entry;
-                 entries[1] = entry;
-                 break;
-               }
-           }
-       }
-    }
-
-  /* Now scan visible mounts for the ones we're interested in.  */
-  for (i = entry_len - 1; i >= 0; i--)
-    {
-      char **ret = NULL;
-      if (!*entries[i].device)
-       continue;
-
-      if (grub_strcmp (entries[i].fstype, "fuse.zfs") == 0
-         || grub_strcmp (entries[i].fstype, "zfs") == 0)
-       {
-         char *slash;
-         slash = strchr (entries[i].device, '/');
-         if (slash)
-           *slash = 0;
-         ret = find_root_devices_from_poolname (entries[i].device);
-         if (slash)
-           *slash = '/';
-         if (relroot)
-           {
-             if (!slash)
-               *relroot = xasprintf ("/@%s", entries[i].enc_root);
-             else if (strchr (slash + 1, '@'))
-               *relroot = xasprintf ("/%s%s", slash + 1, entries[i].enc_root);
-             else
-               *relroot = xasprintf ("/%s@%s", slash + 1, entries[i].enc_root);
-           }
-       }
-      else if (grub_strcmp (entries[i].fstype, "btrfs") == 0)
-       {
-         ret = grub_find_root_devices_from_btrfs (dir);
-         if (relroot)
-           {
-             char *ptr;
-             *relroot = xmalloc (strlen (entries[i].enc_root) +
-                                 2 + strlen (dir));
-             ptr = grub_stpcpy (*relroot, entries[i].enc_root);
-             if (strlen (dir) > strlen (entries[i].enc_path))
-               {
-                 while (ptr > *relroot && *(ptr - 1) == '/')
-                   ptr--;
-                 if (dir[strlen (entries[i].enc_path)] != '/')
-                   *ptr++ = '/';
-                 ptr = grub_stpcpy (ptr, dir + strlen (entries[i].enc_path));
-               }
-             *ptr = 0;
-           }
-       }
-      if (!ret)
-       {
-         ret = xmalloc (2 * sizeof (ret[0]));
-         ret[0] = strdup (entries[i].device);
-         ret[1] = 0;
-         if (relroot)
-           *relroot = strdup (entries[i].enc_root);
-       }
-       free (buf);
-       free (entries);
-       fclose (fp);
-       return ret;
-    }
-
-  free (buf);
-  free (entries);
-  fclose (fp);
-  return NULL;
-}
-
-#endif /* __linux__ */
-
 #if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__)
 
 static char **
@@ -716,7 +409,7 @@ find_root_devices_from_libzfs (const char *dir)
   if (! poolname)
     return NULL;
 
-  devices = find_root_devices_from_poolname (poolname);
+  devices = grub_util_find_root_devices_from_poolname (poolname);
 
   free (poolname);
   if (poolfs)
@@ -736,83 +429,6 @@ grub_find_device (const char *dir __attribute__ ((unused)),
   return 0;
 }
 
-#elif defined (__GNU__)
-
-static char *
-find_hurd_root_device (const char *path)
-{
-  file_t file;
-  error_t err;
-  char *argz = NULL, *name = NULL, *ret;
-  size_t argz_len = 0;
-  int i;
-
-  file = file_name_lookup (path, 0, 0);
-  if (file == MACH_PORT_NULL)
-    /* TRANSLATORS: The first %s is the file being looked at, the second %s is
-       the error message.  */
-    grub_util_error (_("cannot open `%s': %s"), path, strerror (errno));
-
-  /* This returns catenated 0-terminated strings.  */
-  err = file_get_fs_options (file, &argz, &argz_len);
-  if (err)
-    /* TRANSLATORS: On GNU/Hurd, a "translator" is similar to a filesystem
-       mount, but handled by a userland daemon, whose invocation command line
-       is being fetched here.  First %s is the file being looked at (for which
-       we are fetching the "translator" command line), second %s is the error
-       message.
-       */
-    grub_util_error (_("cannot get translator command line "
-                       "for path `%s': %s"), path, strerror(err));
-  if (argz_len == 0)
-    grub_util_error (_("translator command line is empty for path `%s'"), path);
-
-  /* Make sure the string is terminated.  */
-  argz[argz_len-1] = 0;
-
-  /* Skip first word (translator path) and options.  */
-  for (i = strlen (argz) + 1; i < argz_len; i += strlen (argz + i) + 1)
-    {
-      if (argz[i] != '-')
-        {
-          /* Non-option.  Only accept one, assumed to be the FS path.  */
-          /* XXX: this should be replaced by an RPC to the translator.  */
-          if (name)
-            /* TRANSLATORS: we expect to get something like
-               /hurd/foobar --option1 --option2=baz /dev/something
-             */
-            grub_util_error (_("translator `%s' for path `%s' has several "
-                               "non-option words, at least `%s' and `%s'"),
-                               argz, path, name, argz + i);
-          name = argz + i;
-        }
-    }
-
-  if (!name)
-    /* TRANSLATORS: we expect to get something like
-       /hurd/foobar --option1 --option2=baz /dev/something
-     */
-    grub_util_error (_("translator `%s' for path `%s' is given only options, "
-                       "cannot find device part"), argz, path);
-
-  if (strncmp (name, "device:", sizeof ("device:") - 1) == 0)
-    {
-      char *dev_name = name + sizeof ("device:") - 1;
-      size_t size = sizeof ("/dev/") - 1 + strlen (dev_name) + 1;
-      char *next;
-      ret = malloc (size);
-      next = stpncpy (ret, "/dev/", size);
-      stpncpy (next, dev_name, size - (next - ret));
-    }
-  else if (!strncmp (name, "file:", sizeof ("file:") - 1))
-    ret = strdup (name + sizeof ("file:") - 1);
-  else
-    ret = strdup (name);
-
-  munmap (argz, argz_len);
-  return ret;
-}
-
 #elif ! defined(__CYGWIN__)
 
 char *
@@ -913,7 +529,7 @@ grub_find_device (const char *dir, dev_t dev)
          /* Found!  */
          char *res;
          char *cwd;
-#if defined(__NetBSD__)
+#if defined(__NetBSD__) || defined(__OpenBSD__)
          /* Convert this block device to its character (raw) device.  */
          const char *template = "%s/r%s";
 #else
@@ -1110,7 +726,7 @@ grub_guess_root_devices (const char *dir)
 
 #elif defined __GNU__
   /* GNU/Hurd specific function.  */
-  os_dev[0] = find_hurd_root_device (dir);
+  os_dev[0] = grub_util_find_hurd_root_device (dir);
 
 #else
 
@@ -1128,171 +744,67 @@ grub_guess_root_devices (const char *dir)
   return os_dev;
 }
 
-#ifdef HAVE_DEVICE_MAPPER
-
-static int
-grub_util_open_dm (const char *os_dev, struct dm_tree **tree,
-                  struct dm_tree_node **node)
+grub_disk_addr_t
+grub_util_find_partition_start (const char *dev)
 {
-  uint32_t maj, min;
-  struct stat st;
-
-  *node = NULL;
-  *tree = NULL;
-
-  if (stat (os_dev, &st) < 0)
-    return 0;
+  grub_disk_addr_t partition_start;
+  if (grub_util_device_is_mapped (dev)
+      && grub_util_get_dm_node_linear_info (dev, 0, 0, &partition_start))
+    return partition_start;
 
-  maj = major (st.st_rdev);
-  min = minor (st.st_rdev);
-
-  if (!dm_is_dm_major (maj))
-    return 0;
-
-  *tree = dm_tree_create ();
-  if (! *tree)
-    {
-      grub_puts_ (N_("Failed to create `device-mapper' tree"));
-      grub_dprintf ("hostdisk", "dm_tree_create failed\n");
-      return 0;
-    }
-
-  if (! dm_tree_add_dev (*tree, maj, min))
-    {
-      grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n");
-      dm_tree_free (*tree);
-      *tree = NULL;
-      return 0;
-    }
-
-  *node = dm_tree_find_node (*tree, maj, min);
-  if (! *node)
-    {
-      grub_dprintf ("hostdisk", "dm_tree_find_node failed\n");
-      dm_tree_free (*tree);
-      *tree = NULL;
-      return 0;
-    }
-  return 1;
+  return grub_util_find_partition_start_os (dev);
 }
 
-#endif
-
-#ifdef HAVE_DEVICE_MAPPER
-static char *
-get_dm_uuid (const char *os_dev)
+void
+grub_util_pull_device (const char *os_dev)
 {
-  struct dm_tree *tree;
-  struct dm_tree_node *node;
-  const char *node_uuid;
-  char *ret;
-
-  if (!grub_util_open_dm (os_dev, &tree, &node))
-    return NULL;
-
-  node_uuid = dm_tree_node_get_uuid (node);
-  if (! node_uuid)
+  enum grub_dev_abstraction_types ab;
+  ab = grub_util_get_dev_abstraction (os_dev);
+  switch (ab)
     {
-      grub_dprintf ("hostdisk", "%s has no DM uuid\n", os_dev);
-      dm_tree_free (tree);
-      return NULL;
-    }
-
-  ret = grub_strdup (node_uuid);
-
-  dm_tree_free (tree);
-
-  return ret;
-}
+    case GRUB_DEV_ABSTRACTION_LVM:
+#if !defined (__MINGW32__) && !defined (__CYGWIN__)
+      pull_lvm_by_command (os_dev);
 #endif
+      /* Fallthrough in case that lvm-tools are unavailable.  */
+    case GRUB_DEV_ABSTRACTION_LUKS:
+      grub_util_pull_devmapper (os_dev);
+      return;
 
-#ifdef __linux__
-
-static enum grub_dev_abstraction_types
-grub_util_get_dm_abstraction (const char *os_dev)
-{
-#ifdef HAVE_DEVICE_MAPPER
-  char *uuid;
-
-  uuid = get_dm_uuid (os_dev);
-
-  if (uuid == NULL)
-    return GRUB_DEV_ABSTRACTION_NONE;
-
-  if (strncmp (uuid, "LVM-", 4) == 0)
-    {
-      grub_free (uuid);
-      return GRUB_DEV_ABSTRACTION_LVM;
-    }
-  if (strncmp (uuid, "CRYPT-LUKS1-", 4) == 0)
-    {
-      grub_free (uuid);
-      return GRUB_DEV_ABSTRACTION_LUKS;
+    default:
+      if (grub_util_pull_device_os (os_dev, ab))
+       return;
+    case GRUB_DEV_ABSTRACTION_NONE:
+      free (grub_util_biosdisk_get_grub_dev (os_dev));
+      return;
     }
-
-  grub_free (uuid);
-  return GRUB_DEV_ABSTRACTION_NONE;
-#else
-  if ((strncmp ("/dev/mapper/", os_dev, 12) != 0))
-    return GRUB_DEV_ABSTRACTION_NONE;
-  return GRUB_DEV_ABSTRACTION_LVM;  
-#endif
 }
 
-#endif
-
-#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
-#include <libgeom.h>
-
-static const char *
-grub_util_get_geom_abstraction (const char *dev)
+char *
+grub_util_get_grub_dev (const char *os_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);
+  char *ret;
 
-  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"));
+  grub_util_pull_device (os_dev);
 
-  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;
+  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);
 }
-#endif
+
 
 int
 grub_util_get_dev_abstraction (const char *os_dev)
 {
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+  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;
-#endif
-
-#ifdef __linux__
-  enum grub_dev_abstraction_types ret;
 
   /* Check for LVM and LUKS.  */
   ret = grub_util_get_dm_abstraction (os_dev);
@@ -1300,29 +812,10 @@ grub_util_get_dev_abstraction (const char *os_dev)
   if (ret != GRUB_DEV_ABSTRACTION_NONE)
     return ret;
 
-  /* Check for RAID.  */
-  if (!strncmp (os_dev, "/dev/md", 7) && ! grub_util_device_is_mapped (os_dev)
-      && !grub_util_is_imsm (os_dev))
-    return GRUB_DEV_ABSTRACTION_RAID;
-#endif
-
-#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
-  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;
-#endif
-
-  /* No abstraction found.  */
-  return GRUB_DEV_ABSTRACTION_NONE;
+  return grub_util_get_dev_abstraction_os (os_dev);
 }
 
-#if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__)
+#if !defined (__MINGW32__) && !defined (__CYGWIN__)
 
 static void
 pull_lvm_by_command (const char *os_dev)
@@ -1339,28 +832,9 @@ pull_lvm_by_command (const char *os_dev)
   char *vgid = NULL;
   grub_size_t vgidlen = 0;
 
-#ifdef HAVE_DEVICE_MAPPER
-  char *uuid;
-
-  uuid = get_dm_uuid (os_dev);
-  if (uuid)
-    {
-      int dashes[] = { 0, 6, 10, 14, 18, 22, 26, 32};
-      unsigned i;
-      vgid = xmalloc (grub_strlen (uuid));
-      optr = vgid;
-      for (i = 0; i < ARRAY_SIZE (dashes) - 1; i++)
-         {
-           memcpy (optr, uuid + sizeof ("LVM-") - 1 + dashes[i],
-                   dashes[i+1] - dashes[i]);
-           optr += dashes[i+1] - dashes[i];
-           *optr++ = '-';
-         }
-      optr--;
-      *optr = '\0';
-      vgidlen = optr - vgid;
-    }
-#endif
+  vgid = grub_util_get_vg_uuid (os_dev);
+  if (vgid)
+    vgidlen = grub_strlen (vgid);
 
   if (!vgid)
     {
@@ -1400,7 +874,7 @@ pull_lvm_by_command (const char *os_dev)
   argv[6] = vgname;
   argv[7] = NULL;
 
-  pid = exec_pipe (argv, &fd);
+  pid = grub_util_exec_pipe (argv, &fd);
   free (vgname);
 
   if (!pid)
@@ -1440,299 +914,6 @@ out:
 
 #endif
 
-#ifdef __linux__
-static char *
-get_mdadm_uuid (const char *os_dev)
-{
-  char *argv[5];
-  int fd;
-  pid_t pid;
-  FILE *mdadm;
-  char *buf = NULL;
-  size_t len = 0;
-  char *name = NULL;
-
-  /* execvp has inconvenient types, hence the casts.  None of these
-     strings will actually be modified.  */
-  argv[0] = (char *) "mdadm";
-  argv[1] = (char *) "--detail";
-  argv[2] = (char *) "--export";
-  argv[3] = (char *) os_dev;
-  argv[4] = NULL;
-
-  pid = exec_pipe (argv, &fd);
-
-  if (!pid)
-    return NULL;
-
-  /* Parent.  Read mdadm's output.  */
-  mdadm = fdopen (fd, "r");
-  if (! mdadm)
-    {
-      grub_util_warn (_("Unable to open stream from %s: %s"),
-                     "mdadm", strerror (errno));
-      goto out;
-    }
-
-  while (getline (&buf, &len, mdadm) > 0)
-    {
-      if (strncmp (buf, "MD_UUID=", sizeof ("MD_UUID=") - 1) == 0)
-       {
-         char *name_start, *ptri, *ptro;
-         
-         free (name);
-         name_start = buf + sizeof ("MD_UUID=") - 1;
-         ptro = name = xmalloc (strlen (name_start) + 1);
-         for (ptri = name_start; *ptri && *ptri != '\n' && *ptri != '\r';
-              ptri++)
-           if ((*ptri >= '0' && *ptri <= '9')
-               || (*ptri >= 'a' && *ptri <= 'f')
-               || (*ptri >= 'A' && *ptri <= 'F'))
-             *ptro++ = *ptri;
-         *ptro = 0;
-       }
-    }
-
-out:
-  close (fd);
-  waitpid (pid, NULL, 0);
-  free (buf);
-
-  return name;
-}
-
-static int
-grub_util_is_imsm (const char *os_dev)
-{
-  int retry;
-  int is_imsm = 0;
-  int container_seen = 0;
-  const char *dev = os_dev;
-
-  do
-    {
-      char *argv[5];
-      int fd;
-      pid_t pid;
-      FILE *mdadm;
-      char *buf = NULL;
-      size_t len = 0;
-
-      retry = 0; /* We'll do one more pass if device is part of container */
-
-      /* execvp has inconvenient types, hence the casts.  None of these
-        strings will actually be modified.  */
-      argv[0] = (char *) "mdadm";
-      argv[1] = (char *) "--detail";
-      argv[2] = (char *) "--export";
-      argv[3] = (char *) dev;
-      argv[4] = NULL;
-
-      pid = exec_pipe (argv, &fd);
-
-      if (!pid)
-       {
-         if (dev != os_dev)
-           free ((void *) dev);
-         return 0;
-       }
-
-      /* Parent.  Read mdadm's output.  */
-      mdadm = fdopen (fd, "r");
-      if (! mdadm)
-       {
-         grub_util_warn (_("Unable to open stream from %s: %s"),
-                         "mdadm", strerror (errno));
-         close (fd);
-         waitpid (pid, NULL, 0);
-         if (dev != os_dev)
-           free ((void *) dev);
-         return 0;
-       }
-
-      while (getline (&buf, &len, mdadm) > 0)
-       {
-         if (strncmp (buf, "MD_CONTAINER=", sizeof ("MD_CONTAINER=") - 1) == 0
-             && !container_seen)
-           {
-             char *newdev, *ptr;
-             newdev = xstrdup (buf + sizeof ("MD_CONTAINER=") - 1);
-             ptr = newdev + strlen (newdev) - 1;
-             for (; ptr >= newdev && (*ptr == '\n' || *ptr == '\r'); ptr--);
-             ptr[1] = 0;
-             grub_util_info ("Container of %s is %s", dev, newdev);
-             dev = newdev;
-             container_seen = retry = 1;
-             break;
-           }
-         if (strncmp (buf, "MD_METADATA=imsm",
-                      sizeof ("MD_METADATA=imsm") - 1) == 0)
-           {
-             is_imsm = 1;
-             grub_util_info ("%s is imsm", dev);             
-             break;
-           }
-       }
-
-      free (buf);
-      close (fd);
-      waitpid (pid, NULL, 0);
-    }
-  while (retry);
-
-  if (dev != os_dev)
-    free ((void *) dev);
-  return is_imsm;
-}
-#endif /* __linux__ */
-
-void
-grub_util_pull_device (const char *os_dev)
-{
-  int ab;
-  ab = grub_util_get_dev_abstraction (os_dev);
-  switch (ab)
-    {
-    case GRUB_DEV_ABSTRACTION_GELI:
-#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
-      {
-       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;
-       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);
-         }
-      }
-#endif
-      break;
-
-    case GRUB_DEV_ABSTRACTION_LVM:
-#if !defined (__MINGW32__) && !defined (__CYGWIN__) && !defined (__GNU__)
-      pull_lvm_by_command (os_dev);
-#endif
-      /* Fallthrough in case that lvm-tools are unavailable.  */
-    case GRUB_DEV_ABSTRACTION_LUKS:
-#ifdef HAVE_DEVICE_MAPPER
-      {
-       struct dm_tree *tree;
-       struct dm_tree_node *node;
-       struct dm_tree_node *child;
-       void *handle = NULL;
-       char *lastsubdev = NULL;
-
-       if (!grub_util_open_dm (os_dev, &tree, &node))
-         return;
-
-       while ((child = dm_tree_next_child (&handle, node, 0)))
-         {
-           const struct dm_info *dm = dm_tree_node_get_info (child);
-           char *subdev;
-           if (!dm)
-             continue;
-           subdev = grub_find_device ("/dev", makedev (dm->major, dm->minor));
-           if (subdev)
-             {
-               lastsubdev = subdev;
-               grub_util_pull_device (subdev);
-             }
-         }
-       if (ab == GRUB_DEV_ABSTRACTION_LUKS && lastsubdev)
-         {
-           char *grdev = grub_util_get_grub_dev (lastsubdev);
-           dm_tree_free (tree);
-           if (grdev)
-             {
-               grub_err_t err;
-               err = grub_cryptodisk_cheat_mount (grdev, os_dev);
-               if (err)
-                 grub_util_error (_("can't mount encrypted volume `%s': %s"),
-                                  lastsubdev, grub_errmsg);
-             }
-           grub_free (grdev);
-         }
-       else
-         dm_tree_free (tree);
-      }
-#endif
-      return;
-    case GRUB_DEV_ABSTRACTION_RAID:
-#ifdef __linux__
-      {
-       char **devicelist = grub_util_raid_getmembers (os_dev, 0);
-       int i;
-       for (i = 0; devicelist[i];i++)
-         {
-           grub_util_pull_device (devicelist[i]);
-           free (devicelist[i]);
-         }
-       free (devicelist);
-      }
-#endif
-      return;
-
-    default:  /* GRUB_DEV_ABSTRACTION_NONE */
-      free (grub_util_biosdisk_get_grub_dev (os_dev));
-      return;
-    }
-}
-
 int
 grub_util_biosdisk_is_floppy (grub_disk_t disk)
 {
@@ -1785,476 +966,12 @@ convert_system_partition_to_system_disk (const char *os_dev, struct stat *st,
 {
   *is_part = 0;
 
-#if defined(__linux__)
-  char *path = xmalloc (PATH_MAX);
-
-  if (! realpath (os_dev, path))
-    return NULL;
-
-  if (strncmp ("/dev/", path, 5) == 0)
-    {
-      char *p = path + 5;
-
-      /* If this is an IDE disk.  */
-      if (strncmp ("ide/", p, 4) == 0)
-       {
-         p = strstr (p, "part");
-         if (p)
-           {
-             *is_part = 1;
-             strcpy (p, "disc");
-           }
-
-         return path;
-       }
-
-      /* If this is a SCSI disk.  */
-      if (strncmp ("scsi/", p, 5) == 0)
-       {
-         p = strstr (p, "part");
-         if (p)
-           {
-             *is_part = 1;
-             strcpy (p, "disc");
-           }
-
-         return path;
-       }
-
-      /* If this is a DAC960 disk.  */
-      if (strncmp ("rd/c", p, 4) == 0)
-       {
-         /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
-         p = strchr (p, 'p');
-         if (p)
-           {
-             *is_part = 1;
-             *p = '\0';
-           }
-
-         return path;
-       }
-
-      /* If this is a Mylex AcceleRAID Array.  */
-      if (strncmp ("rs/c", p, 4) == 0)
-       {
-         /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
-         p = strchr (p, 'p');
-         if (p)
-           {
-             *is_part = 1;
-             *p = '\0';
-           }
-
-         return path;
-       }
-      /* If this is a CCISS disk.  */
-      if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0)
-       {
-         /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
-         p = strchr (p, 'p');
-         if (p)
-           {
-             *is_part = 1;
-             *p = '\0';
-           }
-
-         return path;
-       }
-
-      /* If this is an AOE disk.  */
-      if (strncmp ("etherd/e", p, sizeof ("etherd/e") - 1) == 0)
-       {
-         /* /dev/etherd/e[0-9]+\.[0-9]+(p[0-9]+)? */
-         p = strchr (p, 'p');
-         if (p)
-           {
-             *is_part = 1;
-             *p = '\0';
-           }
-
-         return path;
-       }
-
-      /* If this is a Compaq Intelligent Drive Array.  */
-      if (strncmp ("ida/c", p, sizeof ("ida/c") - 1) == 0)
-       {
-         /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
-         p = strchr (p, 'p');
-         if (p)
-           {
-             *is_part = 1;
-             *p = '\0';
-           }
-
-         return path;
-       }
-
-      /* If this is an I2O disk.  */
-      if (strncmp ("i2o/hd", p, sizeof ("i2o/hd") - 1) == 0)
-       {
-         /* /dev/i2o/hd[a-z]([0-9]+)? */
-         if (p[sizeof ("i2o/hda") - 1])
-           *is_part = 1;
-         p[sizeof ("i2o/hda") - 1] = '\0';
-         return path;
-       }
-
-      /* If this is a MultiMediaCard (MMC).  */
-      if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0)
-       {
-         /* /dev/mmcblk[0-9]+(p[0-9]+)? */
-         p = strchr (p, 'p');
-         if (p)
-           {
-             *is_part = 1;
-             *p = '\0';
-           }
-
-         return path;
-       }
-
-      if (strncmp ("md", p, 2) == 0
-         && p[2] >= '0' && p[2] <= '9')
-       {
-         char *ptr = p + 2;
-         while (*ptr >= '0' && *ptr <= '9')
-           ptr++;
-         if (*ptr)
-           *is_part = 1;
-         *ptr = 0;
-         return path;
-       }
-
-      if (strncmp ("nbd", p, 3) == 0
-         && p[3] >= '0' && p[3] <= '9')
-       {
-         char *ptr = p + 3;
-         while (*ptr >= '0' && *ptr <= '9')
-           ptr++;
-         if (*ptr)
-           *is_part = 1;
-         *ptr = 0;
-         return path;
-       }
-
-      /* If this is an IDE, SCSI or Virtio disk.  */
-      if (strncmp ("vdisk", p, 5) == 0
-         && p[5] >= 'a' && p[5] <= 'z')
-       {
-         /* /dev/vdisk[a-z][0-9]* */
-         if (p[6])
-           *is_part = 1;
-         p[6] = '\0';
-         return path;
-       }
-      if ((strncmp ("hd", p, 2) == 0
-          || strncmp ("vd", p, 2) == 0
-          || strncmp ("sd", p, 2) == 0)
-         && p[2] >= 'a' && p[2] <= 'z')
-       {
-         char *pp = p + 2;
-         while (*pp >= 'a' && *pp <= 'z')
-           pp++;
-         if (*pp)
-           *is_part = 1;
-         /* /dev/[hsv]d[a-z]+[0-9]* */
-         *pp = '\0';
-         return path;
-       }
-
-      /* If this is a Xen virtual block device.  */
-      if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z')
-       {
-         char *pp = p + 3;
-         while (*pp >= 'a' && *pp <= 'z')
-           pp++;
-         if (*pp)
-           *is_part = 1;
-         /* /dev/xvd[a-z]+[0-9]* */
-         *pp = '\0';
-         return path;
-       }
-
-#ifdef HAVE_DEVICE_MAPPER
-      if (dm_is_dm_major (major (st->st_rdev)))
-       {
-         struct dm_tree *tree;
-         uint32_t maj, min;
-         struct dm_tree_node *node = NULL, *child;
-         void *handle;
-         const char *node_uuid, *mapper_name = NULL, *child_uuid, *child_name;
-
-         tree = dm_tree_create ();
-         if (! tree)
-           {
-             grub_util_info ("dm_tree_create failed");
-             goto devmapper_out;
-           }
-
-         maj = major (st->st_rdev);
-         min = minor (st->st_rdev);
-         if (! dm_tree_add_dev (tree, maj, min))
-           {
-             grub_util_info ("dm_tree_add_dev failed");
-             goto devmapper_out;
-           }
-
-         node = dm_tree_find_node (tree, maj, min);
-         if (! node)
-           {
-             grub_util_info ("dm_tree_find_node failed");
-             goto devmapper_out;
-           }
-       reiterate:
-         node_uuid = dm_tree_node_get_uuid (node);
-         if (! node_uuid)
-           {
-             grub_util_info ("%s has no DM uuid", path);
-             goto devmapper_out;
-           }
-         if (strncmp (node_uuid, "LVM-", 4) == 0)
-           {
-             grub_util_info ("%s is an LVM", path);
-             goto devmapper_out;
-           }
-         if (strncmp (node_uuid, "mpath-", 6) == 0)
-           {
-             /* Multipath partitions have partN-mpath-* UUIDs, and are
-                linear mappings so are handled by
-                grub_util_get_dm_node_linear_info.  Multipath disks are not
-                linear mappings and must be handled specially.  */
-             grub_util_info ("%s is a multipath disk", path);
-             goto devmapper_out;
-           }
-         if (strncmp (node_uuid, "DMRAID-", 7) != 0)
-           {
-             int major, minor;
-             const char *node_name;
-             grub_util_info ("%s is not DM-RAID", path);
-
-             if ((node_name = dm_tree_node_get_name (node))
-                 && grub_util_get_dm_node_linear_info (node_name,
-                                                       &major, &minor, 0))
-               {
-                 *is_part = 1;
-                 if (tree)
-                   dm_tree_free (tree);
-                 free (path);
-                 char *ret = grub_find_device ("/dev",
-                                               (major << 8) | minor);
-                 return ret;
-               }
-
-             goto devmapper_out;
-           }
-
-         handle = NULL;
-         /* Counter-intuitively, device-mapper refers to the disk-like
-            device containing a DM-RAID partition device as a "child" of
-            the partition device.  */
-         child = dm_tree_next_child (&handle, node, 0);
-         if (! child)
-           {
-             grub_util_info ("%s has no DM children", path);
-             goto devmapper_out;
-           }
-         child_uuid = dm_tree_node_get_uuid (child);
-         if (! child_uuid)
-           {
-             grub_util_info ("%s child has no DM uuid", path);
-             goto devmapper_out;
-           }
-         else if (strncmp (child_uuid, "DMRAID-", 7) != 0)
-           {
-             grub_util_info ("%s child is not DM-RAID", path);
-             goto devmapper_out;
-           }
-         child_name = dm_tree_node_get_name (child);
-         if (! child_name)
-           {
-             grub_util_info ("%s child has no DM name", path);
-             goto devmapper_out;
-           }
-         mapper_name = child_name;
-         *is_part = 1;
-         node = child;
-         goto reiterate;
+  if (grub_util_device_is_mapped_stat (st))
+    return grub_util_devmapper_part_to_disk (st, is_part, os_dev);
 
-devmapper_out:
-         if (! mapper_name && node)
-           {
-             /* This is a DM-RAID disk, not a partition.  */
-             mapper_name = dm_tree_node_get_name (node);
-             if (! mapper_name)
-               grub_util_info ("%s has no DM name", path);
-           }
-         char *ret;
-         if (mapper_name)
-           ret = xasprintf ("/dev/mapper/%s", mapper_name);
-         else
-           ret = NULL;
-
-         if (tree)
-           dm_tree_free (tree);
-         free (path);
-         return ret;
-       }
-#endif /* HAVE_DEVICE_MAPPER */
-    }
-
-  return path;
-
-#elif defined(__GNU__)
-  char *path = xstrdup (os_dev);
-  if (strncmp ("/dev/sd", path, 7) == 0 || strncmp ("/dev/hd", path, 7) == 0)
-    {
-      char *p = strchr (path + 7, 's');
-      if (p)
-       {
-         *is_part = 1;
-         *p = '\0';
-       }
-    }
-  return path;
-
-#elif defined(__CYGWIN__)
-  char *path = xstrdup (os_dev);
-  if (strncmp ("/dev/sd", path, 7) == 0 && 'a' <= path[7] && path[7] <= 'z'
-      && path[8])
-    {
-      *is_part = 1;
-      path[8] = 0;
-    }
-  return path;
-
-#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-  char *out, *out2;
-  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;
-#elif defined(__APPLE__)
-  char *path = xstrdup (os_dev);
-  if (strncmp ("/dev/", path, 5) == 0)
-    {
-      char *p;
-      for (p = path + 5; *p; ++p)
-        if (grub_isdigit(*p))
-          {
-            p = strpbrk (p, "sp");
-            if (p)
-             {
-               *is_part = 1;
-               *p = '\0';
-             }
-            break;
-          }
-    }
-  return path;
-
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
-  int rawpart = -1;
-# ifdef HAVE_GETRAWPARTITION
-  rawpart = getrawpartition();
-# endif /* HAVE_GETRAWPARTITION */
-  if (rawpart < 0)
-    return xstrdup (os_dev);
-
-#if defined(__NetBSD__)
-  /* NetBSD disk wedges are of the form "/dev/rdk.*".  */
-  if (strncmp ("/dev/rdk", os_dev, sizeof("/dev/rdk") - 1) == 0)
-    {
-      struct dkwedge_info dkw;
-      int fd;
-
-      fd = open (os_dev, O_RDONLY);
-      if (fd == -1)
-       {
-         grub_error (GRUB_ERR_BAD_DEVICE,
-                     N_("cannot open `%s': %s"), os_dev,
-                     strerror (errno));
-         return xstrdup (os_dev);
-       }
-      /* We don't call configure_device_driver since this isn't a floppy device name.  */
-      if (ioctl (fd, DIOCGWEDGEINFO, &dkw) == -1)
-       {
-         grub_error (GRUB_ERR_BAD_DEVICE,
-                     "cannot get disk wedge info of `%s'", os_dev);
-         close (fd);
-         return xstrdup (os_dev);
-       }
-      *is_part = (dkw.dkw_offset != 0);
-      close (fd);
-      return xasprintf ("/dev/r%s%c", dkw.dkw_parent, 'a' + rawpart);
-    }
-#endif
-
-  /* NetBSD (disk label) partitions are of the form "/dev/r[a-z]+[0-9][a-z]".  */
-  if (strncmp ("/dev/r", os_dev, sizeof("/dev/r") - 1) == 0 &&
-      (os_dev[sizeof("/dev/r") - 1] >= 'a' && os_dev[sizeof("/dev/r") - 1] <= 'z') &&
-      strncmp ("fd", os_dev + sizeof("/dev/r") - 1, sizeof("fd") - 1) != 0)    /* not a floppy device name */
-    {
-      char *path = xstrdup (os_dev);
-      char *p;
-      for (p = path + sizeof("/dev/r"); *p >= 'a' && *p <= 'z'; p++);
-      if (grub_isdigit(*p))
-       {
-         p++;
-         if ((*p >= 'a' && *p <= 'z') && (*(p+1) == '\0'))
-           {
-             if (*p != 'a' + rawpart)
-               *is_part = 1;
-             /* path matches the required regular expression and
-                p points to its last character.  */
-             *p = 'a' + rawpart;
-           }
-       }
-      return path;
-    }
-
-  return xstrdup (os_dev);
+  *is_part = 0;
 
-#elif defined (__sun__)
-  char *colon = grub_strrchr (os_dev, ':');
-  if (grub_memcmp (os_dev, "/devices", sizeof ("/devices") - 1) == 0
-      && colon)
-    {
-      char *ret = xmalloc (colon - os_dev + sizeof (":q,raw"));
-      if (grub_strcmp (colon, ":q,raw") != 0)
-       *is_part = 1;
-      grub_memcpy (ret, os_dev, colon - os_dev);
-      grub_memcpy (ret + (colon - os_dev), ":q,raw", sizeof (":q,raw"));
-      return ret;
-    }
-  else
-    return xstrdup (os_dev);
-#elif defined (__APPLE__)
-  char *ptr;
-  char *ret = xstrdup (os_dev);
-  int disk = grub_memcmp (ret, "/dev/disk", sizeof ("/dev/disk") - 1) == 0;
-  int rdisk = grub_memcmp (ret, "/dev/rdisk", sizeof ("/dev/rdisk") - 1) == 0;
-  if (!disk && !rdisk)
-    return ret;
-  ptr = ret + sizeof ("/dev/disk") + rdisk - 1;
-  while (*ptr >= '0' && *ptr <= '9')
-    ptr++;
-  if (*ptr)
-    {
-      *is_part = 1;
-      *ptr = 0;
-    }
-  return ret;
-#else
-# warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
-  return xstrdup (os_dev);
-#endif
+  return grub_util_part_to_disk (os_dev, st, is_part);
 }
 
 static const char *
@@ -2329,7 +1046,7 @@ grub_util_get_os_disk (const char *os_dev)
   return convert_system_partition_to_system_disk (os_dev, &st, &is_part);
 }
 
-#if defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined (__sun__)
+#if !defined(__APPLE__)
 /* Context for grub_util_biosdisk_get_grub_dev.  */
 struct grub_util_biosdisk_get_grub_dev_ctx
 {
@@ -2390,14 +1107,36 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev)
     }
   free (sys_disk);
 
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) || defined (__sun__)
+#if GRUB_DISK_DEVS_ARE_CHAR
   if (! S_ISCHR (st.st_mode))
 #else
   if (! S_ISBLK (st.st_mode))
 #endif
     return make_device_name (drive, -1, -1);
 
-#if defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined (__sun__) || defined(__OpenBSD__)
+#if defined(__APPLE__)
+  /* Apple uses "/dev/r?disk[0-9]+(s[0-9]+)?".  */
+  {
+    const char *p;
+    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);
+    if (!disk && !rdisk)
+      return make_device_name (drive, -1, -1);
+
+    p = os_dev + sizeof ("/dev/disk") + rdisk - 1;
+    while (*p >= '0' && *p <= '9')
+      p++;
+    if (*p != 's')
+      return make_device_name (drive, -1, -1);
+    p++;
+
+    return make_device_name (drive, strtol (p, NULL, 10) - 1, -1);
+  }
+
+#else
 
   /* Linux counts partitions uniformly, whether a BSD partition or a DOS
      partition, so mapping them to GRUB devices is not trivial.
@@ -2417,21 +1156,52 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev)
 
     name = make_device_name (drive, -1, -1);
 
-# if !defined(HAVE_DIOCGDINFO) && !defined(__sun__)
+# ifdef FLOPPY_MAJOR
     if (MAJOR (st.st_rdev) == FLOPPY_MAJOR)
       return name;
-# else /* defined(HAVE_DIOCGDINFO) */
+# else
     /* Since os_dev and convert_system_partition_to_system_disk (os_dev) are
      * different, we know that os_dev cannot be a floppy device.  */
-# endif /* !defined(HAVE_DIOCGDINFO) */
+# endif
 
-    ctx.start = grub_hostdisk_find_partition_start (os_dev);
+    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]?)?".  */
+    if (ctx.start == (grub_disk_addr_t) -1)
+      {
+       char *p;
+       int dos_part = -1;
+       int bsd_part = -1;
+
+       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 != GRUB_LONG_MIN && n != GRUB_LONG_MAX)
+             {
+               dos_part = (int) n - 1;
+
+               if (*q >= 'a' && *q <= 'g')
+                 bsd_part = *q - 'a';
+             }
+         }
+
+       return make_device_name (drive, dos_part, bsd_part);
+      }
+#endif
+
     grub_util_info ("%s starts from %" PRIuGRUB_UINT64_T, os_dev, ctx.start);
 
     if (ctx.start == 0 && !is_part)
@@ -2495,57 +1265,6 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev)
     return name;
   }
 
-#elif defined(__GNU__)
-  /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?".  */
-  {
-    char *p;
-    int dos_part = -1;
-    int bsd_part = -1;
-
-    p = strrchr (os_dev, 's');
-    if (p)
-      {
-       long int n;
-       char *q;
-
-       p++;
-       n = strtol (p, &q, 10);
-       if (p != q && n != GRUB_LONG_MIN && n != GRUB_LONG_MAX)
-         {
-           dos_part = (int) n - 1;
-
-           if (*q >= 'a' && *q <= 'g')
-             bsd_part = *q - 'a';
-         }
-      }
-
-    return make_device_name (drive, dos_part, bsd_part);
-  }
-
-#elif defined(__APPLE__)
-  /* Apple uses "/dev/r?disk[0-9]+(s[0-9]+)?".  */
-  {
-    const char *p;
-    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);
-    if (!disk && !rdisk)
-      return make_device_name (drive, -1, -1);
-
-    p = os_dev + sizeof ("/dev/disk") + rdisk - 1;
-    while (*p >= '0' && *p <= '9')
-      p++;
-    if (*p != 's')
-      return make_device_name (drive, -1, -1);
-    p++;
-
-    return make_device_name (drive, strtol (p, NULL, 10) - 1, -1);
-  }
-#else
-# warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
-  return make_device_name (drive, -1, -1);
 #endif
 }
 
@@ -2563,239 +1282,6 @@ grub_util_biosdisk_is_present (const char *os_dev)
   return ret;
 }
 
-char *
-grub_util_get_grub_dev (const char *os_dev)
-{
-  char *grub_dev = NULL;
-
-  grub_util_pull_device (os_dev);
-
-  switch (grub_util_get_dev_abstraction (os_dev))
-    {
-#ifdef HAVE_DEVICE_MAPPER
-    case GRUB_DEV_ABSTRACTION_LVM:
-      {
-       char *uuid, *optr;
-       unsigned i;
-       int dashes[] = { 0, 6, 10, 14, 18, 22, 26, 32, 38, 42, 46, 50, 54, 58};
-       uuid = get_dm_uuid (os_dev);
-       if (!uuid)
-         break;
-       grub_dev = xmalloc (grub_strlen (uuid) + 40);
-       optr = grub_stpcpy (grub_dev, "lvmid/");
-       for (i = 0; i < ARRAY_SIZE (dashes) - 1; i++)
-         {
-           memcpy (optr, uuid + sizeof ("LVM-") - 1 + dashes[i],
-                   dashes[i+1] - dashes[i]);
-           optr += dashes[i+1] - dashes[i];
-           *optr++ = '-';
-         }
-       optr = stpcpy (optr, uuid + sizeof ("LVM-") - 1 + dashes[i]);
-       *optr = '\0';
-       grub_dev[sizeof("lvmid/xxxxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxxxx") - 1]
-         = '/';
-       free (uuid);
-      }
-      break;
-#elif defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-    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;
-#endif
-
-    case GRUB_DEV_ABSTRACTION_LUKS:
-#ifdef HAVE_DEVICE_MAPPER
-      {
-       char *uuid, *dash;
-       uuid = get_dm_uuid (os_dev);
-       if (!uuid)
-         break;
-       dash = grub_strchr (uuid + sizeof ("CRYPT-LUKS1-") - 1, '-');
-       if (dash)
-         *dash = 0;
-       grub_dev = grub_xasprintf ("cryptouuid/%s",
-                                  uuid + sizeof ("CRYPT-LUKS1-") - 1);
-       grub_free (uuid);
-      }
-#endif
-      break;
-
-#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
-    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;
-#endif
-
-#ifdef __linux__
-    case GRUB_DEV_ABSTRACTION_RAID:
-
-      if (os_dev[7] == '_' && os_dev[8] == 'd')
-       {
-         /* This a partitionable RAID device of the form /dev/md_dNNpMM. */
-
-         char *p, *q;
-
-         p = strdup (os_dev + sizeof ("/dev/md_d") - 1);
-
-         q = strchr (p, 'p');
-         if (q)
-           *q = ',';
-
-         grub_dev = xasprintf ("md%s", p);
-         free (p);
-       }
-      else if (os_dev[7] == '/' && os_dev[8] == 'd')
-       {
-         /* This a partitionable RAID device of the form /dev/md/dNNpMM. */
-
-         char *p, *q;
-
-         p = strdup (os_dev + sizeof ("/dev/md/d") - 1);
-
-         q = strchr (p, 'p');
-         if (q)
-           *q = ',';
-
-         grub_dev = xasprintf ("md%s", p);
-         free (p);
-       }
-      else if (os_dev[7] >= '0' && os_dev[7] <= '9')
-       {
-         char *p , *q;
-
-         p = strdup (os_dev + sizeof ("/dev/md") - 1);
-
-         q = strchr (p, 'p');
-         if (q)
-           *q = ',';
-
-         grub_dev = xasprintf ("md%s", p);
-         free (p);
-       }
-      else if (os_dev[7] == '/' && os_dev[8] >= '0' && os_dev[8] <= '9')
-       {
-         char *p , *q;
-
-         p = strdup (os_dev + sizeof ("/dev/md/") - 1);
-
-         q = strchr (p, 'p');
-         if (q)
-           *q = ',';
-
-         grub_dev = xasprintf ("md%s", p);
-         free (p);
-       }
-      else if (os_dev[7] == '/')
-       {
-         /* mdraid 1.x with a free name.  */
-         char *p , *q;
-
-         p = strdup (os_dev + sizeof ("/dev/md/") - 1);
-
-         q = strchr (p, 'p');
-         if (q)
-           *q = ',';
-
-         grub_dev = xasprintf ("md/%s", p);
-         free (p);
-       }
-      else
-       grub_util_error (_("unknown kind of RAID device `%s'"), os_dev);
-
-      {
-       char *mdadm_name = get_mdadm_uuid (os_dev);
-
-       if (mdadm_name)
-         {
-           const char *q;
-
-           for (q = os_dev + strlen (os_dev) - 1; q >= os_dev
-                  && grub_isdigit (*q); q--);
-
-           if (q >= os_dev && *q == 'p')
-             {
-               free (grub_dev);
-               grub_dev = xasprintf ("mduuid/%s,%s", mdadm_name, q + 1);
-               goto done;
-             }
-           free (grub_dev);
-           grub_dev = xasprintf ("mduuid/%s", mdadm_name);
-
-         done:
-           free (mdadm_name);
-         }
-      }
-      break;
-#endif /* __linux__ */
-
-    default:  /* GRUB_DEV_ABSTRACTION_NONE */
-      grub_dev = grub_util_biosdisk_get_grub_dev (os_dev);
-    }
-
-  return grub_dev;
-}
-
 const char *
 grub_util_check_block_device (const char *blk_dev)
 {
diff --git a/util/getroot_apple.c b/util/getroot_apple.c
new file mode 100644 (file)
index 0000000..9ca8afc
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ *  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 <grub/util/misc.h>
+#include <grub/util/lvm.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 <sys/wait.h>
+
+# include <sys/disk.h>
+# include <sys/param.h>
+# include <sys/sysctl.h>
+# include <sys/mount.h>
+
+char *
+grub_util_part_to_disk (const char *os_dev, struct stat *st,
+                       int *is_part)
+{
+  char *path = xstrdup (os_dev);
+  if (strncmp ("/dev/", path, 5) == 0)
+    {
+      char *p;
+      for (p = path + 5; *p; ++p)
+        if (grub_isdigit(*p))
+          {
+            p = strpbrk (p, "sp");
+            if (p)
+             {
+               *is_part = 1;
+               *p = '\0';
+             }
+            break;
+          }
+    }
+  return path;
+}
+
+enum grub_dev_abstraction_types
+grub_util_get_dev_abstraction_os (const char *os_dev __attribute__((unused)))
+{
+  return GRUB_DEV_ABSTRACTION_NONE;
+}
+
+int
+grub_util_pull_device_os (const char *os_dev __attribute__ ((unused)),
+                         enum grub_dev_abstraction_types ab __attribute__ ((unused)))
+{
+  return 0;
+}
+
+char *
+grub_util_get_grub_dev_os (const char *os_dev __attribute__ ((unused)))
+{
+  return NULL;
+}
+
+
+grub_disk_addr_t
+grub_util_find_partition_start_os (const char *dev __attribute__ ((unused)))
+{
+  return 0;
+}
diff --git a/util/getroot_basic.c b/util/getroot_basic.c
new file mode 100644 (file)
index 0000000..0242c9c
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ *  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 <grub/util/misc.h>
+#include <grub/util/lvm.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/getroot.h>
+
+
+char *
+grub_util_part_to_disk (const char *os_dev,
+                       struct stat *st __attribute__ ((unused)),
+                       int *is_part)
+{
+  *is_part = 0;
+  return xstrdup (os_dev);
+}
+
+enum grub_dev_abstraction_types
+grub_util_get_dev_abstraction_os (const char *os_dev __attribute__((unused)))
+{
+  return GRUB_DEV_ABSTRACTION_NONE;
+}
+
+int
+grub_util_pull_device_os (const char *os_dev __attribute__ ((unused)),
+                         enum grub_dev_abstraction_types ab __attribute__ ((unused)))
+{
+  return 0;
+}
+
+char *
+grub_util_get_grub_dev_os (const char *os_dev __attribute__ ((unused)))
+{
+  return NULL;
+}
+
+
+grub_disk_addr_t
+grub_util_find_partition_start_os (const char *dev __attribute__ ((unused)))
+{
+  return 0;
+}
diff --git a/util/getroot_bsd.c b/util/getroot_bsd.c
new file mode 100644 (file)
index 0000000..062bce2
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ *  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 <grub/util/misc.h>
+#include <grub/util/lvm.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 <sys/wait.h>
+
+# include <sys/ioctl.h>
+# include <sys/disklabel.h>    /* struct disklabel */
+# include <sys/disk.h>    /* struct dkwedge_info */
+# ifdef HAVE_GETRAWPARTITION
+#  include <util.h>    /* getrawpartition */
+# endif /* HAVE_GETRAWPARTITION */
+#if defined(__NetBSD__)
+# include <sys/fdio.h>
+#endif
+#if defined(__OpenBSD__)
+# include <sys/dkio.h>
+#endif
+
+char *
+grub_util_part_to_disk (const char *os_dev, struct stat *st,
+                       int *is_part)
+{
+  int rawpart = -1;
+# ifdef HAVE_GETRAWPARTITION
+  rawpart = getrawpartition();
+# endif /* HAVE_GETRAWPARTITION */
+  if (rawpart < 0)
+    return xstrdup (os_dev);
+
+#if defined(__NetBSD__)
+  /* NetBSD disk wedges are of the form "/dev/rdk.*".  */
+  if (strncmp ("/dev/rdk", os_dev, sizeof("/dev/rdk") - 1) == 0)
+    {
+      struct dkwedge_info dkw;
+      int fd;
+
+      fd = open (os_dev, O_RDONLY);
+      if (fd == -1)
+       {
+         grub_error (GRUB_ERR_BAD_DEVICE,
+                     N_("cannot open `%s': %s"), os_dev,
+                     strerror (errno));
+         return xstrdup (os_dev);
+       }
+      /* We don't call configure_device_driver since this isn't a floppy device name.  */
+      if (ioctl (fd, DIOCGWEDGEINFO, &dkw) == -1)
+       {
+         grub_error (GRUB_ERR_BAD_DEVICE,
+                     "cannot get disk wedge info of `%s'", os_dev);
+         close (fd);
+         return xstrdup (os_dev);
+       }
+      *is_part = (dkw.dkw_offset != 0);
+      close (fd);
+      return xasprintf ("/dev/r%s%c", dkw.dkw_parent, 'a' + rawpart);
+    }
+#endif
+
+  /* NetBSD (disk label) partitions are of the form "/dev/r[a-z]+[0-9][a-z]".  */
+  if (strncmp ("/dev/r", os_dev, sizeof("/dev/r") - 1) == 0 &&
+      (os_dev[sizeof("/dev/r") - 1] >= 'a' && os_dev[sizeof("/dev/r") - 1] <= 'z') &&
+      strncmp ("fd", os_dev + sizeof("/dev/r") - 1, sizeof("fd") - 1) != 0)    /* not a floppy device name */
+    {
+      char *path = xstrdup (os_dev);
+      char *p;
+      for (p = path + sizeof("/dev/r"); *p >= 'a' && *p <= 'z'; p++);
+      if (grub_isdigit(*p))
+       {
+         p++;
+         if ((*p >= 'a' && *p <= 'z') && (*(p+1) == '\0'))
+           {
+             if (*p != 'a' + rawpart)
+               *is_part = 1;
+             /* path matches the required regular expression and
+                p points to its last character.  */
+             *p = 'a' + rawpart;
+           }
+       }
+      return path;
+    }
+
+  return xstrdup (os_dev);
+}
+
+enum grub_dev_abstraction_types
+grub_util_get_dev_abstraction_os (const char *os_dev __attribute__((unused)))
+{
+  return GRUB_DEV_ABSTRACTION_NONE;
+}
+
+int
+grub_util_pull_device_os (const char *os_dev __attribute__ ((unused)),
+                         enum grub_dev_abstraction_types ab __attribute__ ((unused)))
+{
+  return 0;
+}
+
+char *
+grub_util_get_grub_dev_os (const char *os_dev __attribute__ ((unused)))
+{
+  return NULL;
+}
+
+
+grub_disk_addr_t
+grub_util_find_partition_start_os (const char *dev)
+{
+  int fd;
+#  if defined(__NetBSD__)
+  struct dkwedge_info dkw;
+#  endif /* defined(__NetBSD__) */
+  struct disklabel label;
+  int p_index;
+
+  fd = open (dev, O_RDONLY);
+  if (fd == -1)
+    {
+      grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot open `%s': %s"),
+                 dev, strerror (errno));
+      return 0;
+    }
+
+#  if defined(__NetBSD__)
+  configure_device_driver (fd);
+  /* First handle the case of disk wedges.  */
+  if (ioctl (fd, DIOCGWEDGEINFO, &dkw) == 0)
+    {
+      close (fd);
+      return (grub_disk_addr_t) dkw.dkw_offset;
+    }
+#  endif /* defined(__NetBSD__) */
+
+  if (ioctl (fd, DIOCGDINFO, &label) == -1)
+    {
+      grub_error (GRUB_ERR_BAD_DEVICE,
+                 "cannot get disk label of `%s'", dev);
+      close (fd);
+      return 0;
+    }
+
+  close (fd);
+
+  if (dev[0])
+    p_index = dev[strlen(dev) - 1] - 'a';
+  else
+    p_index = -1;
+  
+  if (p_index >= label.d_npartitions || p_index < 0)
+    {
+      grub_error (GRUB_ERR_BAD_DEVICE,
+                 "no disk label entry for `%s'", dev);
+      return 0;
+    }
+  return (grub_disk_addr_t) label.d_partitions[p_index].p_offset;
+}
diff --git a/util/getroot_cygwin.c b/util/getroot_cygwin.c
new file mode 100644 (file)
index 0000000..6407d2c
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ *  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 <grub/util/misc.h>
+#include <grub/util/lvm.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 <sys/wait.h>
+
+# include <sys/ioctl.h>
+# include <cygwin/fs.h> /* BLKGETSIZE64 */
+# include <cygwin/hdreg.h> /* HDIO_GETGEO */
+# include <sys/cygwin.h>
+
+
+char *
+grub_util_part_to_disk (const char *os_dev,
+                       struct stat *st __attribute__ ((unused)),
+                       int *is_part)
+{
+  char *path = xstrdup (os_dev);
+  if (strncmp ("/dev/sd", path, 7) == 0 && 'a' <= path[7] && path[7] <= 'z'
+      && path[8])
+    {
+      *is_part = 1;
+      path[8] = 0;
+    }
+  return path;
+}
+
+enum grub_dev_abstraction_types
+grub_util_get_dev_abstraction_os (const char *os_dev __attribute__((unused)))
+{
+  return GRUB_DEV_ABSTRACTION_NONE;
+}
+
+int
+grub_util_pull_device_os (const char *os_dev __attribute__ ((unused)),
+                         enum grub_dev_abstraction_types ab __attribute__ ((unused)))
+{
+  return 0;
+}
+
+char *
+grub_util_get_grub_dev_os (const char *os_dev __attribute__ ((unused)))
+{
+  return NULL;
+}
+
+grub_disk_addr_t
+grub_util_find_partition_start_os (const char *dev)
+{
+  int fd;
+  struct hd_geometry hdg;
+
+  fd = open (dev, O_RDONLY);
+  if (fd == -1)
+    {
+      grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot open `%s': %s"),
+                 dev, strerror (errno));
+      return 0;
+    }
+
+  if (ioctl (fd, HDIO_GETGEO, &hdg))
+    {
+      grub_error (GRUB_ERR_BAD_DEVICE,
+                 "cannot get disk geometry of `%s'", dev);
+      close (fd);
+      return 0;
+    }
+
+  close (fd);
+
+  return hdg.start;
+}
diff --git a/util/getroot_devmapper.c b/util/getroot_devmapper.c
new file mode 100644 (file)
index 0000000..d919a40
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+ *  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 <grub/emu/getroot.h>
+#include <grub/mm.h>
+
+#ifdef HAVE_DEVICE_MAPPER
+
+#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 <libdevmapper.h>
+
+#include <grub/types.h>
+#include <grub/util/misc.h>
+#include <grub/util/lvm.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+
+static int
+grub_util_open_dm (const char *os_dev, struct dm_tree **tree,
+                  struct dm_tree_node **node)
+{
+  uint32_t maj, min;
+  struct stat st;
+
+  *node = NULL;
+  *tree = NULL;
+
+  if (stat (os_dev, &st) < 0)
+    return 0;
+
+  maj = major (st.st_rdev);
+  min = minor (st.st_rdev);
+
+  if (!dm_is_dm_major (maj))
+    return 0;
+
+  *tree = dm_tree_create ();
+  if (! *tree)
+    {
+      grub_puts_ (N_("Failed to create `device-mapper' tree"));
+      grub_dprintf ("hostdisk", "dm_tree_create failed\n");
+      return 0;
+    }
+
+  if (! dm_tree_add_dev (*tree, maj, min))
+    {
+      grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n");
+      dm_tree_free (*tree);
+      *tree = NULL;
+      return 0;
+    }
+
+  *node = dm_tree_find_node (*tree, maj, min);
+  if (! *node)
+    {
+      grub_dprintf ("hostdisk", "dm_tree_find_node failed\n");
+      dm_tree_free (*tree);
+      *tree = NULL;
+      return 0;
+    }
+  return 1;
+}
+
+static char *
+get_dm_uuid (const char *os_dev)
+{
+  struct dm_tree *tree;
+  struct dm_tree_node *node;
+  const char *node_uuid;
+  char *ret;
+
+  if (!grub_util_open_dm (os_dev, &tree, &node))
+    return NULL;
+
+  node_uuid = dm_tree_node_get_uuid (node);
+  if (! node_uuid)
+    {
+      grub_dprintf ("hostdisk", "%s has no DM uuid\n", os_dev);
+      dm_tree_free (tree);
+      return NULL;
+    }
+
+  ret = grub_strdup (node_uuid);
+
+  dm_tree_free (tree);
+
+  return ret;
+}
+
+enum grub_dev_abstraction_types
+grub_util_get_dm_abstraction (const char *os_dev)
+{
+  char *uuid;
+
+  uuid = get_dm_uuid (os_dev);
+
+  if (uuid == NULL)
+    return GRUB_DEV_ABSTRACTION_NONE;
+
+  if (strncmp (uuid, "LVM-", 4) == 0)
+    {
+      grub_free (uuid);
+      return GRUB_DEV_ABSTRACTION_LVM;
+    }
+  if (strncmp (uuid, "CRYPT-LUKS1-", 4) == 0)
+    {
+      grub_free (uuid);
+      return GRUB_DEV_ABSTRACTION_LUKS;
+    }
+
+  grub_free (uuid);
+  return GRUB_DEV_ABSTRACTION_NONE;
+}
+
+void
+grub_util_pull_devmapper (const char *os_dev)
+{
+  struct dm_tree *tree;
+  struct dm_tree_node *node;
+  struct dm_tree_node *child;
+  void *handle = NULL;
+  char *lastsubdev = NULL;
+  char *uuid;
+
+  uuid = get_dm_uuid (os_dev);
+
+  if (!grub_util_open_dm (os_dev, &tree, &node))
+    return;
+
+  while ((child = dm_tree_next_child (&handle, node, 0)))
+    {
+      const struct dm_info *dm = dm_tree_node_get_info (child);
+      char *subdev;
+      if (!dm)
+       continue;
+      subdev = grub_find_device ("/dev", makedev (dm->major, dm->minor));
+      if (subdev)
+       {
+         lastsubdev = subdev;
+         grub_util_pull_device (subdev);
+       }
+    }
+  if (uuid && strncmp (uuid, "CRYPT-LUKS1-", sizeof ("CRYPT-LUKS1-") - 1) == 0
+      && lastsubdev)
+    {
+      char *grdev = grub_util_get_grub_dev (lastsubdev);
+      dm_tree_free (tree);
+      if (grdev)
+       {
+         grub_err_t err;
+         err = grub_cryptodisk_cheat_mount (grdev, os_dev);
+         if (err)
+           grub_util_error (_("can't mount encrypted volume `%s': %s"),
+                            lastsubdev, grub_errmsg);
+       }
+      grub_free (grdev);
+    }
+  else
+    dm_tree_free (tree);
+}
+
+char *
+grub_util_devmapper_part_to_disk (struct stat *st,
+                                 int *is_part, const char *path)
+{
+  struct dm_tree *tree;
+  uint32_t maj, min;
+  struct dm_tree_node *node = NULL, *child;
+  void *handle;
+  const char *node_uuid, *mapper_name = NULL, *child_uuid, *child_name;
+
+  tree = dm_tree_create ();
+  if (! tree)
+    {
+      grub_util_info ("dm_tree_create failed");
+      goto devmapper_out;
+    }
+
+  maj = major (st->st_rdev);
+  min = minor (st->st_rdev);
+  if (! dm_tree_add_dev (tree, maj, min))
+    {
+      grub_util_info ("dm_tree_add_dev failed");
+      goto devmapper_out;
+    }
+
+  node = dm_tree_find_node (tree, maj, min);
+  if (! node)
+    {
+      grub_util_info ("dm_tree_find_node failed");
+      goto devmapper_out;
+    }
+ reiterate:
+  node_uuid = dm_tree_node_get_uuid (node);
+  if (! node_uuid)
+    {
+      grub_util_info ("%s has no DM uuid", path);
+      goto devmapper_out;
+    }
+  if (strncmp (node_uuid, "LVM-", 4) == 0)
+    {
+      grub_util_info ("%s is an LVM", path);
+      goto devmapper_out;
+    }
+  if (strncmp (node_uuid, "mpath-", 6) == 0)
+    {
+      /* Multipath partitions have partN-mpath-* UUIDs, and are
+        linear mappings so are handled by
+        grub_util_get_dm_node_linear_info.  Multipath disks are not
+        linear mappings and must be handled specially.  */
+      grub_util_info ("%s is a multipath disk", path);
+      goto devmapper_out;
+    }
+  if (strncmp (node_uuid, "DMRAID-", 7) != 0)
+    {
+      int major, minor;
+      const char *node_name;
+      grub_util_info ("%s is not DM-RAID", path);
+
+      if ((node_name = dm_tree_node_get_name (node))
+         && grub_util_get_dm_node_linear_info (node_name,
+                                               &major, &minor, 0))
+       {
+         *is_part = 1;
+         if (tree)
+           dm_tree_free (tree);
+         char *ret = grub_find_device ("/dev",
+                                       (major << 8) | minor);
+         return ret;
+       }
+
+      goto devmapper_out;
+    }
+
+  handle = NULL;
+  /* Counter-intuitively, device-mapper refers to the disk-like
+     device containing a DM-RAID partition device as a "child" of
+     the partition device.  */
+  child = dm_tree_next_child (&handle, node, 0);
+  if (! child)
+    {
+      grub_util_info ("%s has no DM children", path);
+      goto devmapper_out;
+    }
+  child_uuid = dm_tree_node_get_uuid (child);
+  if (! child_uuid)
+    {
+      grub_util_info ("%s child has no DM uuid", path);
+      goto devmapper_out;
+    }
+  else if (strncmp (child_uuid, "DMRAID-", 7) != 0)
+    {
+      grub_util_info ("%s child is not DM-RAID", path);
+      goto devmapper_out;
+    }
+  child_name = dm_tree_node_get_name (child);
+  if (! child_name)
+    {
+      grub_util_info ("%s child has no DM name", path);
+      goto devmapper_out;
+    }
+  mapper_name = child_name;
+  *is_part = 1;
+  node = child;
+  goto reiterate;
+
+ devmapper_out:
+  if (! mapper_name && node)
+    {
+      /* This is a DM-RAID disk, not a partition.  */
+      mapper_name = dm_tree_node_get_name (node);
+      if (! mapper_name)
+       grub_util_info ("%s has no DM name", path);
+    }
+  char *ret;
+  if (mapper_name)
+    ret = xasprintf ("/dev/mapper/%s", mapper_name);
+  else
+    ret = NULL;
+
+  if (tree)
+    dm_tree_free (tree);
+  return ret;
+}
+
+int
+grub_util_device_is_mapped_stat (struct stat *st)
+{
+  if (!grub_device_mapper_supported ())
+    return 0;
+
+  return dm_is_dm_major (major (st->st_rdev));
+}
+
+char *
+grub_util_get_devmapper_grub_dev (const char *os_dev)
+{
+  char *uuid, *optr;
+  char *grub_dev;
+
+  uuid = get_dm_uuid (os_dev);
+  if (!uuid)
+    return NULL;
+  
+  if (strncmp (uuid, "LVM-", sizeof ("LVM-") - 1) == 0)
+    {
+       unsigned i;
+       int dashes[] = { 0, 6, 10, 14, 18, 22, 26, 32, 38, 42, 46, 50, 54, 58};
+       grub_dev = xmalloc (grub_strlen (uuid) + 40);
+       optr = grub_stpcpy (grub_dev, "lvmid/");
+       for (i = 0; i < ARRAY_SIZE (dashes) - 1; i++)
+         {
+           memcpy (optr, uuid + sizeof ("LVM-") - 1 + dashes[i],
+                   dashes[i+1] - dashes[i]);
+           optr += dashes[i+1] - dashes[i];
+           *optr++ = '-';
+         }
+       optr = stpcpy (optr, uuid + sizeof ("LVM-") - 1 + dashes[i]);
+       *optr = '\0';
+       grub_dev[sizeof("lvmid/xxxxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxxxx") - 1]
+         = '/';
+       free (uuid);
+       return grub_dev;
+      }
+
+  if (strncmp (uuid, "CRYPT-LUKS1-", sizeof ("CRYPT-LUKS1-") - 1) == 0)
+    {
+      char *dash;
+      dash = grub_strchr (uuid + sizeof ("CRYPT-LUKS1-") - 1, '-');
+      if (dash)
+       *dash = 0;
+      grub_dev = grub_xasprintf ("cryptouuid/%s",
+                                uuid + sizeof ("CRYPT-LUKS1-") - 1);
+      grub_free (uuid);
+      return grub_dev;
+    }
+  return NULL;
+}
+
+char *
+grub_util_get_vg_uuid (const char *os_dev)
+{
+  char *uuid, *vgid;
+  int dashes[] = { 0, 6, 10, 14, 18, 22, 26, 32};
+  unsigned i;
+  char *optr;
+
+  uuid = get_dm_uuid (os_dev);
+  if (!uuid)
+    return NULL;
+
+  vgid = xmalloc (grub_strlen (uuid));
+  optr = vgid;
+  for (i = 0; i < ARRAY_SIZE (dashes) - 1; i++)
+    {
+      memcpy (optr, uuid + sizeof ("LVM-") - 1 + dashes[i],
+             dashes[i+1] - dashes[i]);
+      optr += dashes[i+1] - dashes[i];
+      *optr++ = '-';
+    }
+  optr--;
+  *optr = '\0';
+  return vgid;
+}
+
+void
+grub_util_devmapper_cleanup (void)
+{
+  dm_lib_release ();
+}
+
+#else
+void
+grub_util_pull_devmapper (const char *os_dev __attribute__ ((unused)))
+{
+  return;
+}
+
+int
+grub_util_device_is_mapped_stat (struct stat *st __attribute__ ((unused)))
+{
+  return 0;
+}
+
+void
+grub_util_devmapper_cleanup (void)
+{
+}
+
+enum grub_dev_abstraction_types
+grub_util_get_dm_abstraction (const char *os_dev __attribute__ ((unused)))
+{
+  return GRUB_DEV_ABSTRACTION_NONE;
+}
+
+char *
+grub_util_get_vg_uuid (const char *os_dev __attribute__ ((unused)))
+{
+  return NULL;
+}
+
+char *
+grub_util_devmapper_part_to_disk (struct stat *st __attribute__ ((unused)),
+                                 int *is_part __attribute__ ((unused)),
+                                 const char *os_dev __attribute__ ((unused)))
+{
+  return NULL;
+}
+
+char *
+grub_util_get_devmapper_grub_dev (const char *os_dev __attribute__ ((unused)))
+{
+  return NULL;
+}
+
+#endif
diff --git a/util/getroot_freebsd.c b/util/getroot_freebsd.c
new file mode 100644 (file)
index 0000000..0247433
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ *  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/util/lvm.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>
+
+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 (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, "start") == 0)
+               off += strtoull (config->lg_val, 0, 10);
+           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;
+}
diff --git a/util/getroot_hurd.c b/util/getroot_hurd.c
new file mode 100644 (file)
index 0000000..f63f4de
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ *  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 <grub/util/misc.h>
+#include <grub/util/lvm.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 <sys/wait.h>
+
+#include <hurd.h>
+#include <hurd/lookup.h>
+#include <hurd/fs.h>
+#include <sys/mman.h>
+
+
+char *
+grub_util_find_hurd_root_device (const char *path)
+{
+  file_t file;
+  error_t err;
+  char *argz = NULL, *name = NULL, *ret;
+  size_t argz_len = 0;
+  int i;
+
+  file = file_name_lookup (path, 0, 0);
+  if (file == MACH_PORT_NULL)
+    /* TRANSLATORS: The first %s is the file being looked at, the second %s is
+       the error message.  */
+    grub_util_error (_("cannot open `%s': %s"), path, strerror (errno));
+
+  /* This returns catenated 0-terminated strings.  */
+  err = file_get_fs_options (file, &argz, &argz_len);
+  if (err)
+    /* TRANSLATORS: On GNU/Hurd, a "translator" is similar to a filesystem
+       mount, but handled by a userland daemon, whose invocation command line
+       is being fetched here.  First %s is the file being looked at (for which
+       we are fetching the "translator" command line), second %s is the error
+       message.
+       */
+    grub_util_error (_("cannot get translator command line "
+                       "for path `%s': %s"), path, strerror(err));
+  if (argz_len == 0)
+    grub_util_error (_("translator command line is empty for path `%s'"), path);
+
+  /* Make sure the string is terminated.  */
+  argz[argz_len-1] = 0;
+
+  /* Skip first word (translator path) and options.  */
+  for (i = strlen (argz) + 1; i < argz_len; i += strlen (argz + i) + 1)
+    {
+      if (argz[i] != '-')
+        {
+          /* Non-option.  Only accept one, assumed to be the FS path.  */
+          /* XXX: this should be replaced by an RPC to the translator.  */
+          if (name)
+            /* TRANSLATORS: we expect to get something like
+               /hurd/foobar --option1 --option2=baz /dev/something
+             */
+            grub_util_error (_("translator `%s' for path `%s' has several "
+                               "non-option words, at least `%s' and `%s'"),
+                               argz, path, name, argz + i);
+          name = argz + i;
+        }
+    }
+
+  if (!name)
+    /* TRANSLATORS: we expect to get something like
+       /hurd/foobar --option1 --option2=baz /dev/something
+     */
+    grub_util_error (_("translator `%s' for path `%s' is given only options, "
+                       "cannot find device part"), argz, path);
+
+  if (strncmp (name, "device:", sizeof ("device:") - 1) == 0)
+    {
+      char *dev_name = name + sizeof ("device:") - 1;
+      size_t size = sizeof ("/dev/") - 1 + strlen (dev_name) + 1;
+      char *next;
+      ret = malloc (size);
+      next = stpncpy (ret, "/dev/", size);
+      stpncpy (next, dev_name, size - (next - ret));
+    }
+  else if (!strncmp (name, "file:", sizeof ("file:") - 1))
+    ret = strdup (name + sizeof ("file:") - 1);
+  else
+    ret = strdup (name);
+
+  munmap (argz, argz_len);
+  return ret;
+}
+
+static int
+is_fulldisk (const char *child, const char *parent)
+{
+  if (strcmp (parent, child) == 0)
+    return 1;
+  if (strncmp (parent, "/dev/", sizeof ("/dev/") - 1) == 0
+      && child[0] !=0 && strcmp (parent + sizeof ("/dev/") - 1, child) == 0)
+    return 1;
+  if (strncmp (child, "/dev/", sizeof ("/dev/") - 1) == 0
+      && parent[0] != 0 && strcmp (child + sizeof ("/dev/") - 1, parent) == 0)
+    return 1;
+  return 0;
+}
+
+char *
+grub_util_part_to_disk (const char *os_dev,
+                       struct stat *st __attribute__ ((unused)),
+                       int *is_part)
+{
+  char *path;
+  grub_disk_addr_t offset;
+  char *p;
+
+  if (!grub_util_hurd_get_disk_info (os_dev, NULL, &offset, NULL, &path))
+    return xstrdup (os_dev);
+
+  /* Some versions of Hurd use badly glued Linux code to handle partitions
+     resulting in partitions being promoted to disks.  */
+  if (path && !(offset == 0 && is_fulldisk (path, os_dev)
+               && (strncmp ("/dev/sd", os_dev, 7) == 0
+                   || strncmp ("/dev/hd", os_dev, 7) == 0)))
+    {
+      *is_part = !is_fulldisk (path, os_dev);
+      if (path[0] != '/')
+       {
+         char *n = xasprintf ("/dev/%s", path);
+         free (path);
+         path = n;
+       }
+      return path;
+    }
+  free (path);
+
+  path = xstrdup (os_dev);
+
+  p = strchr (path + 7, 's');
+  if (p)
+    {
+      *is_part = 1;
+      *p = '\0';
+    }
+  return path;
+}
+
+enum grub_dev_abstraction_types
+grub_util_get_dev_abstraction_os (const char *os_dev __attribute__((unused)))
+{
+  return GRUB_DEV_ABSTRACTION_NONE;
+}
+
+int
+grub_util_pull_device_os (const char *os_dev __attribute__ ((unused)),
+                         enum grub_dev_abstraction_types ab __attribute__ ((unused)))
+{
+  return 0;
+}
+
+char *
+grub_util_get_grub_dev_os (const char *os_dev __attribute__ ((unused)))
+{
+  return NULL;
+}
+
+grub_disk_addr_t
+grub_util_find_partition_start_os (const char *dev)
+{
+  grub_uint32_t secsize;
+  grub_disk_addr_t offset;
+  char *path;
+  if (!grub_util_hurd_get_disk_info (dev, &secsize, &offset, NULL, &path))
+    return 0;
+  if (path && !(offset == 0 && is_fulldisk (path, dev)
+               && (strncmp ("/dev/sd", dev, 7) == 0
+                   || strncmp ("/dev/hd", dev, 7) == 0)))
+    {
+      free (path);
+      return (secsize / 512) * offset;
+    }
+  free (path);
+  return -1;
+}
diff --git a/util/getroot_linux.c b/util/getroot_linux.c
new file mode 100644 (file)
index 0000000..13b78e5
--- /dev/null
@@ -0,0 +1,845 @@
+/*
+ *  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/ioctl.h>         /* ioctl */
+#include <sys/mount.h>
+
+#include <grub/util/misc.h>
+#include <grub/util/lvm.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 <sys/wait.h>
+
+
+/* Defines taken from btrfs/ioctl.h.  */
+
+struct btrfs_ioctl_dev_info_args
+{
+  grub_uint64_t devid;
+  grub_uint8_t uuid[16];
+  grub_uint64_t bytes_used;
+  grub_uint64_t total_bytes;
+  grub_uint64_t unused[379];
+  grub_uint8_t path[1024];
+};
+
+struct btrfs_ioctl_fs_info_args
+{
+  grub_uint64_t max_id;
+  grub_uint64_t num_devices;
+  grub_uint8_t fsid[16];
+  grub_uint64_t reserved[124];
+};
+
+#define BTRFS_IOC_DEV_INFO _IOWR(0x94, 30, \
+                                 struct btrfs_ioctl_dev_info_args)
+#define BTRFS_IOC_FS_INFO _IOR(0x94, 31, \
+                               struct btrfs_ioctl_fs_info_args)
+
+static int
+grub_util_is_imsm (const char *os_dev);
+
+
+#define ESCAPED_PATH_MAX (4 * PATH_MAX)
+struct mountinfo_entry
+{
+  int id;
+  int major, minor;
+  char enc_root[ESCAPED_PATH_MAX + 1], enc_path[ESCAPED_PATH_MAX + 1];
+  char fstype[ESCAPED_PATH_MAX + 1], device[ESCAPED_PATH_MAX + 1];
+};
+
+/* Statting something on a btrfs filesystem always returns a virtual device
+   major/minor pair rather than the real underlying device, because btrfs
+   can span multiple underlying devices (and even if it's currently only
+   using a single device it can be dynamically extended onto another).  We
+   can't deal with the multiple-device case yet, but in the meantime, we can
+   at least cope with the single-device case by scanning
+   /proc/self/mountinfo.  */
+static void
+unescape (char *str)
+{
+  char *optr;
+  const char *iptr;
+  for (iptr = optr = str; *iptr; optr++)
+    {
+      if (iptr[0] == '\\' && iptr[1] >= '0' && iptr[1] < '8'
+         && iptr[2] >= '0' && iptr[2] < '8'
+         && iptr[3] >= '0' && iptr[3] < '8')
+       {
+         *optr = (((iptr[1] - '0') << 6) | ((iptr[2] - '0') << 3)
+                  | (iptr[3] - '0'));
+         iptr += 4;
+       }
+      else
+       *optr = *iptr++;
+    }
+  *optr = 0;
+}
+
+static char **
+grub_find_root_devices_from_btrfs (const char *dir)
+{
+  int fd;
+  struct btrfs_ioctl_fs_info_args fsi;
+  int i, j = 0;
+  char **ret;
+
+  fd = open (dir, 0);
+  if (!fd)
+    return NULL;
+
+  if (ioctl (fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
+    {
+      close (fd);
+      return NULL;
+    }
+
+  ret = xmalloc ((fsi.num_devices + 1) * sizeof (ret[0]));
+
+  for (i = 1; i <= fsi.max_id && j < fsi.num_devices; i++)
+    {
+      struct btrfs_ioctl_dev_info_args devi;
+      memset (&devi, 0, sizeof (devi));
+      devi.devid = i;
+      if (ioctl (fd, BTRFS_IOC_DEV_INFO, &devi) < 0)
+       {
+         close (fd);
+         free (ret);
+         return NULL;
+       }
+      ret[j++] = xstrdup ((char *) devi.path);
+      if (j >= fsi.num_devices)
+       break;
+    }
+  close (fd);
+  ret[j] = 0;
+  return ret;
+}
+
+char **
+grub_find_root_devices_from_mountinfo (const char *dir, char **relroot)
+{
+  FILE *fp;
+  char *buf = NULL;
+  size_t len = 0;
+  grub_size_t entry_len = 0, entry_max = 4;
+  struct mountinfo_entry *entries;
+  struct mountinfo_entry parent_entry = { 0, 0, 0, "", "", "", "" };
+  int i;
+
+  if (! *dir)
+    dir = "/";
+  if (relroot)
+    *relroot = NULL;
+
+  fp = fopen ("/proc/self/mountinfo", "r");
+  if (! fp)
+    return NULL; /* fall through to other methods */
+
+  entries = xmalloc (entry_max * sizeof (*entries));
+
+  /* First, build a list of relevant visible mounts.  */
+  while (getline (&buf, &len, fp) > 0)
+    {
+      struct mountinfo_entry entry;
+      int count;
+      size_t enc_path_len;
+      const char *sep;
+
+      if (sscanf (buf, "%d %d %u:%u %s %s%n",
+                 &entry.id, &parent_entry.id, &entry.major, &entry.minor,
+                 entry.enc_root, entry.enc_path, &count) < 6)
+       continue;
+
+      unescape (entry.enc_root);
+      unescape (entry.enc_path);
+
+      enc_path_len = strlen (entry.enc_path);
+      /* Check that enc_path is a prefix of dir.  The prefix must either be
+         the entire string, or end with a slash, or be immediately followed
+         by a slash.  */
+      if (strncmp (dir, entry.enc_path, enc_path_len) != 0 ||
+         (enc_path_len && dir[enc_path_len - 1] != '/' &&
+          dir[enc_path_len] && dir[enc_path_len] != '/'))
+       continue;
+
+      sep = strstr (buf + count, " - ");
+      if (!sep)
+       continue;
+
+      sep += sizeof (" - ") - 1;
+      if (sscanf (sep, "%s %s", entry.fstype, entry.device) != 2)
+       continue;
+
+      unescape (entry.device);
+
+      /* Using the mount IDs, find out where this fits in the list of
+        visible mount entries we've seen so far.  There are three
+        interesting cases.  Firstly, it may be inserted at the end: this is
+        the usual case of /foo/bar being mounted after /foo.  Secondly, it
+        may be inserted at the start: for example, this can happen for
+        filesystems that are mounted before / and later moved under it.
+        Thirdly, it may occlude part or all of the existing filesystem
+        tree, in which case the end of the list needs to be pruned and this
+        new entry will be inserted at the end.  */
+      if (entry_len >= entry_max)
+       {
+         entry_max <<= 1;
+         entries = xrealloc (entries, entry_max * sizeof (*entries));
+       }
+
+      if (!entry_len)
+       {
+         /* Initialise list.  */
+         entry_len = 2;
+         entries[0] = parent_entry;
+         entries[1] = entry;
+       }
+      else
+       {
+         for (i = entry_len - 1; i >= 0; i--)
+           {
+             if (entries[i].id == parent_entry.id)
+               {
+                 /* Insert at end, pruning anything previously above this.  */
+                 entry_len = i + 2;
+                 entries[i + 1] = entry;
+                 break;
+               }
+             else if (i == 0 && entries[i].id == entry.id)
+               {
+                 /* Insert at start.  */
+                 entry_len++;
+                 memmove (entries + 1, entries,
+                          (entry_len - 1) * sizeof (*entries));
+                 entries[0] = parent_entry;
+                 entries[1] = entry;
+                 break;
+               }
+           }
+       }
+    }
+
+  /* Now scan visible mounts for the ones we're interested in.  */
+  for (i = entry_len - 1; i >= 0; i--)
+    {
+      char **ret = NULL;
+      if (!*entries[i].device)
+       continue;
+
+      if (grub_strcmp (entries[i].fstype, "fuse.zfs") == 0
+         || grub_strcmp (entries[i].fstype, "zfs") == 0)
+       {
+         char *slash;
+         slash = strchr (entries[i].device, '/');
+         if (slash)
+           *slash = 0;
+         ret = grub_util_find_root_devices_from_poolname (entries[i].device);
+         if (slash)
+           *slash = '/';
+         if (relroot)
+           {
+             if (!slash)
+               *relroot = xasprintf ("/@%s", entries[i].enc_root);
+             else if (strchr (slash + 1, '@'))
+               *relroot = xasprintf ("/%s%s", slash + 1, entries[i].enc_root);
+             else
+               *relroot = xasprintf ("/%s@%s", slash + 1, entries[i].enc_root);
+           }
+       }
+      else if (grub_strcmp (entries[i].fstype, "btrfs") == 0)
+       {
+         ret = grub_find_root_devices_from_btrfs (dir);
+         if (relroot)
+           {
+             char *ptr;
+             *relroot = xmalloc (strlen (entries[i].enc_root) +
+                                 2 + strlen (dir));
+             ptr = grub_stpcpy (*relroot, entries[i].enc_root);
+             if (strlen (dir) > strlen (entries[i].enc_path))
+               {
+                 while (ptr > *relroot && *(ptr - 1) == '/')
+                   ptr--;
+                 if (dir[strlen (entries[i].enc_path)] != '/')
+                   *ptr++ = '/';
+                 ptr = grub_stpcpy (ptr, dir + strlen (entries[i].enc_path));
+               }
+             *ptr = 0;
+           }
+       }
+      if (!ret)
+       {
+         ret = xmalloc (2 * sizeof (ret[0]));
+         ret[0] = strdup (entries[i].device);
+         ret[1] = 0;
+         if (relroot)
+           *relroot = strdup (entries[i].enc_root);
+       }
+       free (buf);
+       free (entries);
+       fclose (fp);
+       return ret;
+    }
+
+  free (buf);
+  free (entries);
+  fclose (fp);
+  return NULL;
+}
+
+static char *
+get_mdadm_uuid (const char *os_dev)
+{
+  char *argv[5];
+  int fd;
+  pid_t pid;
+  FILE *mdadm;
+  char *buf = NULL;
+  size_t len = 0;
+  char *name = NULL;
+
+  /* execvp has inconvenient types, hence the casts.  None of these
+     strings will actually be modified.  */
+  argv[0] = (char *) "mdadm";
+  argv[1] = (char *) "--detail";
+  argv[2] = (char *) "--export";
+  argv[3] = (char *) os_dev;
+  argv[4] = NULL;
+
+  pid = grub_util_exec_pipe (argv, &fd);
+
+  if (!pid)
+    return NULL;
+
+  /* Parent.  Read mdadm's output.  */
+  mdadm = fdopen (fd, "r");
+  if (! mdadm)
+    {
+      grub_util_warn (_("Unable to open stream from %s: %s"),
+                     "mdadm", strerror (errno));
+      goto out;
+    }
+
+  while (getline (&buf, &len, mdadm) > 0)
+    {
+      if (strncmp (buf, "MD_UUID=", sizeof ("MD_UUID=") - 1) == 0)
+       {
+         char *name_start, *ptri, *ptro;
+         
+         free (name);
+         name_start = buf + sizeof ("MD_UUID=") - 1;
+         ptro = name = xmalloc (strlen (name_start) + 1);
+         for (ptri = name_start; *ptri && *ptri != '\n' && *ptri != '\r';
+              ptri++)
+           if ((*ptri >= '0' && *ptri <= '9')
+               || (*ptri >= 'a' && *ptri <= 'f')
+               || (*ptri >= 'A' && *ptri <= 'F'))
+             *ptro++ = *ptri;
+         *ptro = 0;
+       }
+    }
+
+out:
+  close (fd);
+  waitpid (pid, NULL, 0);
+  free (buf);
+
+  return name;
+}
+
+static int
+grub_util_is_imsm (const char *os_dev)
+{
+  int retry;
+  int is_imsm = 0;
+  int container_seen = 0;
+  const char *dev = os_dev;
+
+  do
+    {
+      char *argv[5];
+      int fd;
+      pid_t pid;
+      FILE *mdadm;
+      char *buf = NULL;
+      size_t len = 0;
+
+      retry = 0; /* We'll do one more pass if device is part of container */
+
+      /* execvp has inconvenient types, hence the casts.  None of these
+        strings will actually be modified.  */
+      argv[0] = (char *) "mdadm";
+      argv[1] = (char *) "--detail";
+      argv[2] = (char *) "--export";
+      argv[3] = (char *) dev;
+      argv[4] = NULL;
+
+      pid = grub_util_exec_pipe (argv, &fd);
+
+      if (!pid)
+       {
+         if (dev != os_dev)
+           free ((void *) dev);
+         return 0;
+       }
+
+      /* Parent.  Read mdadm's output.  */
+      mdadm = fdopen (fd, "r");
+      if (! mdadm)
+       {
+         grub_util_warn (_("Unable to open stream from %s: %s"),
+                         "mdadm", strerror (errno));
+         close (fd);
+         waitpid (pid, NULL, 0);
+         if (dev != os_dev)
+           free ((void *) dev);
+         return 0;
+       }
+
+      while (getline (&buf, &len, mdadm) > 0)
+       {
+         if (strncmp (buf, "MD_CONTAINER=", sizeof ("MD_CONTAINER=") - 1) == 0
+             && !container_seen)
+           {
+             char *newdev, *ptr;
+             newdev = xstrdup (buf + sizeof ("MD_CONTAINER=") - 1);
+             ptr = newdev + strlen (newdev) - 1;
+             for (; ptr >= newdev && (*ptr == '\n' || *ptr == '\r'); ptr--);
+             ptr[1] = 0;
+             grub_util_info ("Container of %s is %s", dev, newdev);
+             dev = newdev;
+             container_seen = retry = 1;
+             break;
+           }
+         if (strncmp (buf, "MD_METADATA=imsm",
+                      sizeof ("MD_METADATA=imsm") - 1) == 0)
+           {
+             is_imsm = 1;
+             grub_util_info ("%s is imsm", dev);             
+             break;
+           }
+       }
+
+      free (buf);
+      close (fd);
+      waitpid (pid, NULL, 0);
+    }
+  while (retry);
+
+  if (dev != os_dev)
+    free ((void *) dev);
+  return is_imsm;
+}
+
+char *
+grub_util_part_to_disk (const char *os_dev, struct stat *st,
+                       int *is_part)
+{
+  char *path = xmalloc (PATH_MAX);
+
+  if (! realpath (os_dev, path))
+    return NULL;
+
+  if (strncmp ("/dev/", path, 5) == 0)
+    {
+      char *p = path + 5;
+
+      /* If this is an IDE disk.  */
+      if (strncmp ("ide/", p, 4) == 0)
+       {
+         p = strstr (p, "part");
+         if (p)
+           {
+             *is_part = 1;
+             strcpy (p, "disc");
+           }
+
+         return path;
+       }
+
+      /* If this is a SCSI disk.  */
+      if (strncmp ("scsi/", p, 5) == 0)
+       {
+         p = strstr (p, "part");
+         if (p)
+           {
+             *is_part = 1;
+             strcpy (p, "disc");
+           }
+
+         return path;
+       }
+
+      /* If this is a DAC960 disk.  */
+      if (strncmp ("rd/c", p, 4) == 0)
+       {
+         /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
+         p = strchr (p, 'p');
+         if (p)
+           {
+             *is_part = 1;
+             *p = '\0';
+           }
+
+         return path;
+       }
+
+      /* If this is a Mylex AcceleRAID Array.  */
+      if (strncmp ("rs/c", p, 4) == 0)
+       {
+         /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
+         p = strchr (p, 'p');
+         if (p)
+           {
+             *is_part = 1;
+             *p = '\0';
+           }
+
+         return path;
+       }
+      /* If this is a CCISS disk.  */
+      if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0)
+       {
+         /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
+         p = strchr (p, 'p');
+         if (p)
+           {
+             *is_part = 1;
+             *p = '\0';
+           }
+
+         return path;
+       }
+
+      /* If this is an AOE disk.  */
+      if (strncmp ("etherd/e", p, sizeof ("etherd/e") - 1) == 0)
+       {
+         /* /dev/etherd/e[0-9]+\.[0-9]+(p[0-9]+)? */
+         p = strchr (p, 'p');
+         if (p)
+           {
+             *is_part = 1;
+             *p = '\0';
+           }
+
+         return path;
+       }
+
+      /* If this is a Compaq Intelligent Drive Array.  */
+      if (strncmp ("ida/c", p, sizeof ("ida/c") - 1) == 0)
+       {
+         /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
+         p = strchr (p, 'p');
+         if (p)
+           {
+             *is_part = 1;
+             *p = '\0';
+           }
+
+         return path;
+       }
+
+      /* If this is an I2O disk.  */
+      if (strncmp ("i2o/hd", p, sizeof ("i2o/hd") - 1) == 0)
+       {
+         /* /dev/i2o/hd[a-z]([0-9]+)? */
+         if (p[sizeof ("i2o/hda") - 1])
+           *is_part = 1;
+         p[sizeof ("i2o/hda") - 1] = '\0';
+         return path;
+       }
+
+      /* If this is a MultiMediaCard (MMC).  */
+      if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0)
+       {
+         /* /dev/mmcblk[0-9]+(p[0-9]+)? */
+         p = strchr (p, 'p');
+         if (p)
+           {
+             *is_part = 1;
+             *p = '\0';
+           }
+
+         return path;
+       }
+
+      if (strncmp ("md", p, 2) == 0
+         && p[2] >= '0' && p[2] <= '9')
+       {
+         char *ptr = p + 2;
+         while (*ptr >= '0' && *ptr <= '9')
+           ptr++;
+         if (*ptr)
+           *is_part = 1;
+         *ptr = 0;
+         return path;
+       }
+
+      if (strncmp ("nbd", p, 3) == 0
+         && p[3] >= '0' && p[3] <= '9')
+       {
+         char *ptr = p + 3;
+         while (*ptr >= '0' && *ptr <= '9')
+           ptr++;
+         if (*ptr)
+           *is_part = 1;
+         *ptr = 0;
+         return path;
+       }
+
+      /* If this is an IDE, SCSI or Virtio disk.  */
+      if (strncmp ("vdisk", p, 5) == 0
+         && p[5] >= 'a' && p[5] <= 'z')
+       {
+         /* /dev/vdisk[a-z][0-9]* */
+         if (p[6])
+           *is_part = 1;
+         p[6] = '\0';
+         return path;
+       }
+      if ((strncmp ("hd", p, 2) == 0
+          || strncmp ("vd", p, 2) == 0
+          || strncmp ("sd", p, 2) == 0)
+         && p[2] >= 'a' && p[2] <= 'z')
+       {
+         char *pp = p + 2;
+         while (*pp >= 'a' && *pp <= 'z')
+           pp++;
+         if (*pp)
+           *is_part = 1;
+         /* /dev/[hsv]d[a-z]+[0-9]* */
+         *pp = '\0';
+         return path;
+       }
+
+      /* If this is a Xen virtual block device.  */
+      if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z')
+       {
+         char *pp = p + 3;
+         while (*pp >= 'a' && *pp <= 'z')
+           pp++;
+         if (*pp)
+           *is_part = 1;
+         /* /dev/xvd[a-z]+[0-9]* */
+         *pp = '\0';
+         return path;
+       }
+    }
+
+  return path;
+}
+
+char *
+grub_util_get_raid_grub_dev (const char *os_dev)
+{
+  char *grub_dev = NULL;
+  if (os_dev[7] == '_' && os_dev[8] == 'd')
+    {
+      /* This a partitionable RAID device of the form /dev/md_dNNpMM. */
+
+      char *p, *q;
+
+      p = strdup (os_dev + sizeof ("/dev/md_d") - 1);
+
+      q = strchr (p, 'p');
+      if (q)
+       *q = ',';
+
+      grub_dev = xasprintf ("md%s", p);
+      free (p);
+    }
+  else if (os_dev[7] == '/' && os_dev[8] == 'd')
+    {
+      /* This a partitionable RAID device of the form /dev/md/dNNpMM. */
+
+      char *p, *q;
+
+      p = strdup (os_dev + sizeof ("/dev/md/d") - 1);
+
+      q = strchr (p, 'p');
+      if (q)
+       *q = ',';
+
+      grub_dev = xasprintf ("md%s", p);
+      free (p);
+    }
+  else if (os_dev[7] >= '0' && os_dev[7] <= '9')
+    {
+      char *p , *q;
+
+      p = strdup (os_dev + sizeof ("/dev/md") - 1);
+
+      q = strchr (p, 'p');
+      if (q)
+       *q = ',';
+
+      grub_dev = xasprintf ("md%s", p);
+      free (p);
+    }
+  else if (os_dev[7] == '/' && os_dev[8] >= '0' && os_dev[8] <= '9')
+    {
+      char *p , *q;
+
+      p = strdup (os_dev + sizeof ("/dev/md/") - 1);
+
+      q = strchr (p, 'p');
+      if (q)
+       *q = ',';
+
+      grub_dev = xasprintf ("md%s", p);
+      free (p);
+    }
+  else if (os_dev[7] == '/')
+    {
+      /* mdraid 1.x with a free name.  */
+      char *p , *q;
+
+      p = strdup (os_dev + sizeof ("/dev/md/") - 1);
+
+      q = strchr (p, 'p');
+      if (q)
+       *q = ',';
+
+      grub_dev = xasprintf ("md/%s", p);
+      free (p);
+    }
+  else
+    grub_util_error (_("unknown kind of RAID device `%s'"), os_dev);
+
+  {
+    char *mdadm_name = get_mdadm_uuid (os_dev);
+
+    if (mdadm_name)
+      {
+       const char *q;
+
+       for (q = os_dev + strlen (os_dev) - 1; q >= os_dev
+              && grub_isdigit (*q); q--);
+
+       if (q >= os_dev && *q == 'p')
+         {
+           free (grub_dev);
+           grub_dev = xasprintf ("mduuid/%s,%s", mdadm_name, q + 1);
+           goto done;
+         }
+       free (grub_dev);
+       grub_dev = xasprintf ("mduuid/%s", mdadm_name);
+
+      done:
+       free (mdadm_name);
+      }
+  }
+  return grub_dev;
+}
+
+enum grub_dev_abstraction_types
+grub_util_get_dev_abstraction_os (const char *os_dev)
+{
+#ifndef HAVE_DEVICE_MAPPER
+  if ((strncmp ("/dev/mapper/", os_dev, 12) == 0))
+    return GRUB_DEV_ABSTRACTION_LVM;
+#endif
+
+  /* Check for RAID.  */
+  if (!strncmp (os_dev, "/dev/md", 7) && ! grub_util_device_is_mapped (os_dev)
+      && !grub_util_is_imsm (os_dev))
+    return GRUB_DEV_ABSTRACTION_RAID;
+  return GRUB_DEV_ABSTRACTION_NONE;
+}
+
+int
+grub_util_pull_device_os (const char *os_dev,
+                         enum grub_dev_abstraction_types ab)
+{
+  switch (ab)
+    {
+    case GRUB_DEV_ABSTRACTION_RAID:
+      {
+       char **devicelist = grub_util_raid_getmembers (os_dev, 0);
+       int i;
+       for (i = 0; devicelist[i];i++)
+         {
+           grub_util_pull_device (devicelist[i]);
+           free (devicelist[i]);
+         }
+       free (devicelist);
+      }
+      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_RAID:
+      grub_dev = grub_util_get_raid_grub_dev (os_dev);
+      break;
+
+    default:  /* GRUB_DEV_ABSTRACTION_NONE */
+      break;
+    }
+
+  return grub_dev;
+}
diff --git a/util/getroot_os.c b/util/getroot_os.c
new file mode 100644 (file)
index 0000000..fee9f21
--- /dev/null
@@ -0,0 +1,18 @@
+#ifdef __linux__
+#include "getroot_linux.c"
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include "getroot_freebsd.c"
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+#include "getroot_bsd.c"
+#elif defined(__APPLE__)
+#include "getroot_apple.c"
+#elif defined(__sun__)
+#include "getroot_sun.c"
+#elif defined(__GNU__)
+#include "getroot_hurd.c"
+#elif defined(__CYGWIN__)
+#include "getroot_cygwin.c"
+#else
+# warning "No getroot OS-specific functions is available for your system. Device detection may not work properly."
+#include "getroot_basic.c"
+#endif
diff --git a/util/getroot_sun.c b/util/getroot_sun.c
new file mode 100644 (file)
index 0000000..d63f5a6
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ *  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 <grub/util/misc.h>
+#include <grub/util/lvm.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 <sys/wait.h>
+
+# include <sys/types.h>
+# include <sys/mkdev.h>
+# include <sys/dkio.h>
+
+
+char *
+grub_util_part_to_disk (const char *os_dev,
+                       struct stat *st __attribute__ ((unused)),
+                       int *is_part)
+{
+  char *colon = grub_strrchr (os_dev, ':');
+  if (grub_memcmp (os_dev, "/devices", sizeof ("/devices") - 1) == 0
+      && colon)
+    {
+      char *ret = xmalloc (colon - os_dev + sizeof (":q,raw"));
+      if (grub_strcmp (colon, ":q,raw") != 0)
+       *is_part = 1;
+      grub_memcpy (ret, os_dev, colon - os_dev);
+      grub_memcpy (ret + (colon - os_dev), ":q,raw", sizeof (":q,raw"));
+      return ret;
+    }
+  else
+    return xstrdup (os_dev);
+}
+
+enum grub_dev_abstraction_types
+grub_util_get_dev_abstraction_os (const char *os_dev __attribute__((unused)))
+{
+  return GRUB_DEV_ABSTRACTION_NONE;
+}
+
+int
+grub_util_pull_device_os (const char *os_dev __attribute__ ((unused)),
+                         enum grub_dev_abstraction_types ab __attribute__ ((unused)))
+{
+  return 0;
+}
+
+char *
+grub_util_get_grub_dev_os (const char *os_dev __attribute__ ((unused)))
+{
+  return NULL;
+}
+
+grub_disk_addr_t
+grub_util_find_partition_start_os (const char *dev)
+{
+  int fd;
+  struct extpart_info pinfo;
+
+  fd = open (dev, O_RDONLY);
+  if (fd == -1)
+    {
+      grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot open `%s': %s"),
+                 dev, strerror (errno));
+      return 0;
+    }
+
+  if (ioctl (fd, DKIOCEXTPARTINFO, &pinfo))
+    {
+      grub_error (GRUB_ERR_BAD_DEVICE,
+                 "cannot get disk geometry of `%s'", dev);
+      close (fd);
+      return 0;
+    }
+
+  close (fd);
+
+  return pinfo.p_start;
+}