]> git.proxmox.com Git - grub2.git/commitdiff
Restore grub-mkdevicemap
authorColin Watson <cjwatson@debian.org>
Mon, 13 Jan 2014 12:13:01 +0000 (12:13 +0000)
committerColin Watson <cjwatson@debian.org>
Fri, 16 Mar 2018 10:56:30 +0000 (10:56 +0000)
This is kind of a mess, requiring lots of OS-specific code to iterate
over all possible devices.  However, we use it in a number of scripts to
discover devices and reimplementing those in terms of something else
would be very complicated.

Author: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
Forwarded: no
Last-Update: 2018-03-16

Patch-Name: restore_mkdevicemap.patch

Makefile.util.def
docs/man/grub-mkdevicemap.h2m [new file with mode: 0644]
include/grub/util/deviceiter.h [new file with mode: 0644]
util/deviceiter.c [new file with mode: 0644]
util/devicemap.c [new file with mode: 0644]
util/grub-mkdevicemap.c [new file with mode: 0644]

index f9caccb9780ffe8d4b31c8a19399ba7bbd308e56..c939deb9228f73d4bf2d725142c4856440b2ed2d 100644 (file)
@@ -314,6 +314,23 @@ program = {
   condition = COND_GRUB_MKFONT;
 };
 
+program = {
+  name = grub-mkdevicemap;
+  installdir = sbin;
+  mansection = 8;
+
+  common = util/grub-mkdevicemap.c;
+  common = util/deviceiter.c;
+  common = util/devicemap.c;
+  common = grub-core/osdep/init.c;
+
+  ldadd = libgrubmods.a;
+  ldadd = libgrubgcry.a;
+  ldadd = libgrubkern.a;
+  ldadd = grub-core/gnulib/libgnu.a;
+  ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
+};
+
 program = {
   name = grub-probe;
   installdir = sbin;
diff --git a/docs/man/grub-mkdevicemap.h2m b/docs/man/grub-mkdevicemap.h2m
new file mode 100644 (file)
index 0000000..96cd6ee
--- /dev/null
@@ -0,0 +1,4 @@
+[NAME]
+grub-mkdevicemap \- make a device map file automatically
+[SEE ALSO]
+.BR grub-probe (8)
diff --git a/include/grub/util/deviceiter.h b/include/grub/util/deviceiter.h
new file mode 100644 (file)
index 0000000..8537497
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef GRUB_DEVICEITER_MACHINE_UTIL_HEADER
+#define GRUB_DEVICEITER_MACHINE_UTIL_HEADER    1
+
+#include <config.h>
+
+typedef int (*grub_util_iterate_devices_hook_t) (const char *name,
+                                                int is_floppy, void *data);
+
+void grub_util_iterate_devices (grub_util_iterate_devices_hook_t hook,
+                               void *hook_data, int floppy_disks);
+void grub_util_emit_devicemap_entry (FILE *fp, char *name, int is_floppy,
+                                    int *num_fd, int *num_hd);
+
+#endif /* ! GRUB_DEVICEITER_MACHINE_UTIL_HEADER */
diff --git a/util/deviceiter.c b/util/deviceiter.c
new file mode 100644 (file)
index 0000000..a4971ef
--- /dev/null
@@ -0,0 +1,1021 @@
+/* deviceiter.c - iterate over system devices */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2011 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <dirent.h>
+
+#include <grub/util/misc.h>
+#include <grub/util/deviceiter.h>
+#include <grub/list.h>
+#include <grub/misc.h>
+#include <grub/emu/misc.h>
+
+#ifdef __linux__
+# if !defined(__GLIBC__) || \
+        ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
+/* Maybe libc doesn't have large file support.  */
+#  include <linux/unistd.h>     /* _llseek */
+# endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */
+# 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 FLOPPY_MAJOR
+#  define FLOPPY_MAJOR 2       /* the major number for floppy */
+# endif /* ! FLOPPY_MAJOR */
+# ifndef MAJOR
+#  define MAJOR(dev)   \
+  ({ \
+     unsigned long long __dev = (dev); \
+     (unsigned) ((__dev >> 8) & 0xfff) \
+                 | ((unsigned int) (__dev >> 32) & ~0xfff); \
+  })
+# endif /* ! MAJOR */
+# ifndef MINOR
+#  define MINOR(dev)   \
+  ({ \
+     unsigned long long __dev = (dev); \
+     (unsigned) (__dev & 0xff) | ((unsigned int) (__dev >> 12) & ~0xff); \
+  })
+# endif /* ! MINOR */
+# ifndef CDROM_GET_CAPABILITY
+#  define CDROM_GET_CAPABILITY 0x5331  /* get capabilities */
+# endif /* ! CDROM_GET_CAPABILITY */
+# ifndef BLKGETSIZE
+#  define BLKGETSIZE   _IO(0x12,96)    /* return device size */
+# endif /* ! BLKGETSIZE */
+
+#ifdef HAVE_DEVICE_MAPPER
+# include <libdevmapper.h>
+# pragma GCC diagnostic ignored "-Wcast-align"
+#endif
+#endif /* __linux__ */
+
+/* Use __FreeBSD_kernel__ instead of __FreeBSD__ for compatibility with
+   kFreeBSD-based non-FreeBSD systems (e.g. GNU/kFreeBSD) */
+#if defined(__FreeBSD__) && ! defined(__FreeBSD_kernel__)
+# define __FreeBSD_kernel__
+#endif
+#ifdef __FreeBSD_kernel__
+  /* Obtain version of kFreeBSD headers */
+# include <osreldate.h>
+# ifndef __FreeBSD_kernel_version
+#  define __FreeBSD_kernel_version __FreeBSD_version
+# endif
+
+  /* Runtime detection of kernel */
+# include <sys/utsname.h>
+static int
+get_kfreebsd_version (void)
+{
+  struct utsname uts;
+  int major;
+  int minor;
+  int v[2];
+
+  uname (&uts);
+  sscanf (uts.release, "%d.%d", &major, &minor);
+
+  if (major >= 9)
+    major = 9;
+  if (major >= 5)
+    {
+      v[0] = minor/10; v[1] = minor%10;
+    }
+  else
+    {
+      v[0] = minor%10; v[1] = minor/10;
+    }
+  return major*100000+v[0]*10000+v[1]*1000;
+}
+#endif /* __FreeBSD_kernel__ */
+
+#if defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
+# include <sys/ioctl.h>                /* ioctl */
+# include <sys/disklabel.h>
+# include <sys/cdio.h>         /* CDIOCCLRDEBUG */
+# if defined(__FreeBSD_kernel__)
+#  include <sys/param.h>
+#  if __FreeBSD_kernel_version >= 500040
+#   include <sys/disk.h>
+#  endif
+# endif /* __FreeBSD_kernel__ */
+#endif /* __FreeBSD_kernel__ || __NetBSD__ || __OpenBSD__ */
+
+#ifdef HAVE_OPENDISK
+# include <util.h>
+#endif /* HAVE_OPENDISK */
+
+#ifdef __linux__
+/* Check if we have devfs support.  */
+static int
+have_devfs (void)
+{
+  struct stat st;
+  return stat ("/dev/.devfsd", &st) == 0;
+}
+#endif /* __linux__ */
+
+/* These three functions are quite different among OSes.  */
+static void
+get_floppy_disk_name (char *name, int unit)
+{
+#if defined(__linux__)
+  /* GNU/Linux */
+  if (have_devfs ())
+    sprintf (name, "/dev/floppy/%d", unit);
+  else
+    sprintf (name, "/dev/fd%d", unit);
+#elif defined(__GNU__)
+  /* GNU/Hurd */
+  sprintf (name, "/dev/fd%d", unit);
+#elif defined(__FreeBSD_kernel__)
+  /* kFreeBSD */
+  if (get_kfreebsd_version () >= 400000)
+    sprintf (name, "/dev/fd%d", unit);
+  else
+    sprintf (name, "/dev/rfd%d", unit);
+#elif defined(__NetBSD__)
+  /* NetBSD */
+  /* opendisk() doesn't work for floppies.  */
+  sprintf (name, "/dev/rfd%da", unit);
+#elif defined(__OpenBSD__)
+  /* OpenBSD */
+  sprintf (name, "/dev/rfd%dc", unit);
+#elif defined(__QNXNTO__)
+  /* QNX RTP */
+  sprintf (name, "/dev/fd%d", unit);
+#elif defined(__CYGWIN__)
+  /* Cygwin */
+  sprintf (name, "/dev/fd%d", unit);
+#elif defined(__MINGW32__)
+  (void) unit;
+  *name = 0;
+#else
+# warning "BIOS floppy drives cannot be guessed in your operating system."
+  /* Set NAME to a bogus string.  */
+  *name = 0;
+#endif
+}
+
+static void
+get_ide_disk_name (char *name, int unit)
+{
+#if defined(__linux__)
+  /* GNU/Linux */
+  sprintf (name, "/dev/hd%c", unit + 'a');
+#elif defined(__GNU__)
+  /* GNU/Hurd */
+  sprintf (name, "/dev/hd%d", unit);
+#elif defined(__FreeBSD_kernel__)
+  /* kFreeBSD */
+  if (get_kfreebsd_version () >= 400000)
+    sprintf (name, "/dev/ad%d", unit);
+  else
+    sprintf (name, "/dev/rwd%d", unit);
+#elif defined(__NetBSD__) && defined(HAVE_OPENDISK)
+  /* NetBSD */
+  char shortname[16];
+  int fd;
+
+  sprintf (shortname, "wd%d", unit);
+  fd = opendisk (shortname, O_RDONLY, name,
+                16,    /* length of NAME */
+                0      /* char device */
+                );
+  close (fd);
+#elif defined(__OpenBSD__)
+  /* OpenBSD */
+  sprintf (name, "/dev/rwd%dc", unit);
+#elif defined(__QNXNTO__)
+  /* QNX RTP */
+  /* Actually, QNX RTP doesn't distinguish IDE from SCSI, so this could
+     contain SCSI disks.  */
+  sprintf (name, "/dev/hd%d", unit);
+#elif defined(__CYGWIN__)
+  /* Cygwin emulates all disks as /dev/sdX.  */
+  (void) unit;
+  *name = 0;
+#elif defined(__MINGW32__)
+  sprintf (name, "//./PHYSICALDRIVE%d", unit);
+#else
+# warning "BIOS IDE drives cannot be guessed in your operating system."
+  /* Set NAME to a bogus string.  */
+  *name = 0;
+#endif
+}
+
+static void
+get_scsi_disk_name (char *name, int unit)
+{
+#if defined(__linux__)
+  /* GNU/Linux */
+  sprintf (name, "/dev/sd%c", unit + 'a');
+#elif defined(__GNU__)
+  /* GNU/Hurd */
+  sprintf (name, "/dev/sd%d", unit);
+#elif defined(__FreeBSD_kernel__)
+  /* kFreeBSD */
+  if (get_kfreebsd_version () >= 400000)
+    sprintf (name, "/dev/da%d", unit);
+  else
+    sprintf (name, "/dev/rda%d", unit);
+#elif defined(__NetBSD__) && defined(HAVE_OPENDISK)
+  /* NetBSD */
+  char shortname[16];
+  int fd;
+
+  sprintf (shortname, "sd%d", unit);
+  fd = opendisk (shortname, O_RDONLY, name,
+                16,    /* length of NAME */
+                0      /* char device */
+                );
+  close (fd);
+#elif defined(__OpenBSD__)
+  /* OpenBSD */
+  sprintf (name, "/dev/rsd%dc", unit);
+#elif defined(__QNXNTO__)
+  /* QNX RTP */
+  /* QNX RTP doesn't distinguish SCSI from IDE, so it is better to
+     disable the detection of SCSI disks here.  */
+  *name = 0;
+#elif defined(__CYGWIN__)
+  /* Cygwin emulates all disks as /dev/sdX.  */
+  sprintf (name, "/dev/sd%c", unit + 'a');
+#elif defined(__MINGW32__)
+  (void) unit;
+  *name = 0;
+#else
+# warning "BIOS SCSI drives cannot be guessed in your operating system."
+  /* Set NAME to a bogus string.  */
+  *name = 0;
+#endif
+}
+
+#ifdef __FreeBSD_kernel__
+static void
+get_ada_disk_name (char *name, int unit)
+{
+  sprintf (name, "/dev/ada%d", unit);
+}
+
+static void
+get_ataraid_disk_name (char *name, int unit)
+{
+  sprintf (name, "/dev/ar%d", unit);
+}
+
+static void
+get_mfi_disk_name (char *name, int unit)
+{
+  sprintf (name, "/dev/mfid%d", unit);
+}
+
+static void
+get_virtio_disk_name (char *name, int unit)
+{
+  sprintf (name, "/dev/vtbd%d", unit);
+}
+
+static void
+get_xvd_disk_name (char *name, int unit)
+{
+  sprintf (name, "/dev/xbd%d", unit);
+}
+#endif
+
+#ifdef __linux__
+static void
+get_virtio_disk_name (char *name, int unit)
+{
+#ifdef __sparc__
+  sprintf (name, "/dev/vdisk%c", unit + 'a');
+#else
+  sprintf (name, "/dev/vd%c", unit + 'a');
+#endif
+}
+
+static void
+get_dac960_disk_name (char *name, int controller, int drive)
+{
+  sprintf (name, "/dev/rd/c%dd%d", controller, drive);
+}
+
+static void
+get_acceleraid_disk_name (char *name, int controller, int drive)
+{
+  sprintf (name, "/dev/rs/c%dd%d", controller, drive);
+}
+
+static void
+get_ataraid_disk_name (char *name, int unit)
+{
+  sprintf (name, "/dev/ataraid/d%c", unit + '0');
+}
+
+static void
+get_i2o_disk_name (char *name, char unit)
+{
+  sprintf (name, "/dev/i2o/hd%c", unit);
+}
+
+static void
+get_cciss_disk_name (char *name, int controller, int drive)
+{
+  sprintf (name, "/dev/cciss/c%dd%d", controller, drive);
+}
+
+static void
+get_ida_disk_name (char *name, int controller, int drive)
+{
+  sprintf (name, "/dev/ida/c%dd%d", controller, drive);
+}
+
+static void
+get_mmc_disk_name (char *name, int unit)
+{
+  sprintf (name, "/dev/mmcblk%d", unit);
+}
+
+static void
+get_xvd_disk_name (char *name, int unit)
+{
+  sprintf (name, "/dev/xvd%c", unit + 'a');
+}
+
+static void
+get_nvme_disk_name (char *name, int controller, int namespace)
+{
+  sprintf (name, "/dev/nvme%dn%d", controller, namespace);
+}
+#endif
+
+static struct seen_device
+{
+  struct seen_device *next;
+  struct seen_device **prev;
+  const char *name;
+} *seen;
+
+/* Check if DEVICE can be read.  Skip any DEVICE that we have already seen.
+   If an error occurs, return zero, otherwise return non-zero.  */
+static int
+check_device_readable_unique (const char *device)
+{
+  char *real_device;
+  char buf[512];
+  FILE *fp;
+  struct seen_device *seen_elt;
+
+  /* If DEVICE is empty, just return error.  */
+  if (*device == 0)
+    return 0;
+
+  /* Have we seen this device already?  */
+  real_device = canonicalize_file_name (device);
+  if (! real_device)
+    return 0;
+  if (grub_named_list_find (GRUB_AS_NAMED_LIST (seen), real_device))
+    {
+      grub_dprintf ("deviceiter", "Already seen %s (%s)\n",
+                   device, real_device);
+      goto fail;
+    }
+
+  fp = fopen (device, "r");
+  if (! fp)
+    {
+      switch (errno)
+       {
+#ifdef ENOMEDIUM
+       case ENOMEDIUM:
+# if 0
+         /* At the moment, this finds only CDROMs, which can't be
+            read anyway, so leave it out. Code should be
+            reactivated if `removable disks' and CDROMs are
+            supported.  */
+         /* Accept it, it may be inserted.  */
+         return 1;
+# endif
+         break;
+#endif /* ENOMEDIUM */
+       default:
+         /* Break case and leave.  */
+         break;
+       }
+      /* Error opening the device.  */
+      goto fail;
+    }
+
+  /* Make sure CD-ROMs don't get assigned a BIOS disk number
+     before SCSI disks!  */
+#ifdef __linux__
+# ifdef CDROM_GET_CAPABILITY
+  if (ioctl (fileno (fp), CDROM_GET_CAPABILITY, 0) >= 0)
+    goto fail;
+# else /* ! CDROM_GET_CAPABILITY */
+  /* Check if DEVICE is a CD-ROM drive by the HDIO_GETGEO ioctl.  */
+  {
+    struct hd_geometry hdg;
+    struct stat st;
+
+    if (fstat (fileno (fp), &st))
+      goto fail;
+
+    /* If it is a block device and isn't a floppy, check if HDIO_GETGEO
+       succeeds.  */
+    if (S_ISBLK (st.st_mode)
+       && MAJOR (st.st_rdev) != FLOPPY_MAJOR
+       && ioctl (fileno (fp), HDIO_GETGEO, &hdg))
+      goto fail;
+  }
+# endif /* ! CDROM_GET_CAPABILITY */
+#endif /* __linux__ */
+
+#if defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
+# ifdef CDIOCCLRDEBUG
+  if (ioctl (fileno (fp), CDIOCCLRDEBUG, 0) >= 0)
+    goto fail;
+# endif /* CDIOCCLRDEBUG */
+#endif /* __FreeBSD_kernel__ || __NetBSD__ || __OpenBSD__ */
+
+  /* Attempt to read the first sector.  */
+  if (fread (buf, 1, 512, fp) != 512)
+    {
+      fclose (fp);
+      goto fail;
+    }
+
+  /* Remember that we've seen this device.  */
+  seen_elt = xmalloc (sizeof (*seen_elt));
+  seen_elt->name = real_device; /* steal memory */
+  grub_list_push (GRUB_AS_LIST_P (&seen), GRUB_AS_LIST (seen_elt));
+
+  fclose (fp);
+  return 1;
+
+fail:
+  free (real_device);
+  return 0;
+}
+
+static void
+clear_seen_devices (void)
+{
+  while (seen)
+    {
+      struct seen_device *seen_elt = seen;
+      seen = seen->next;
+      free (seen_elt);
+    }
+  seen = NULL;
+}
+
+#ifdef __linux__
+struct device
+{
+       char *stable;
+       char *kernel;
+};
+
+/* Sort by the kernel name for preference since that most closely matches
+   older device.map files, but sort by stable by-id names as a fallback.
+   This is because /dev/disk/by-id/ usually has a few alternative
+   identifications of devices (e.g. ATA vs. SATA).
+   check_device_readable_unique will ensure that we only get one for any
+   given disk, but sort the list so that the choice of which one we get is
+   stable.  */
+static int
+compare_devices (const void *a, const void *b)
+{
+  const struct device *left = (const struct device *) a;
+  const struct device *right = (const struct device *) b;
+
+  if (left->kernel && right->kernel)
+    {
+      int ret = strcmp (left->kernel, right->kernel);
+      if (ret)
+       return ret;
+    }
+
+  return strcmp (left->stable, right->stable);
+}
+#endif /* __linux__ */
+
+void
+grub_util_iterate_devices (int (*hook) (const char *, int, void *), void *hook_data,
+                          int floppy_disks)
+{
+  int i;
+
+  clear_seen_devices ();
+
+  /* Floppies.  */
+  for (i = 0; i < floppy_disks; i++)
+    {
+      char name[32];
+      struct stat st;
+
+      get_floppy_disk_name (name, i);
+      if (stat (name, &st) < 0)
+       break;
+      /* In floppies, write the map, whether check_device_readable_unique
+         succeeds or not, because the user just may not insert floppies.  */
+      if (hook (name, 1, hook_data))
+       goto out;
+    }
+
+#ifdef __linux__
+  {
+    DIR *dir = opendir ("/dev/disk/by-id");
+
+    if (dir)
+      {
+       struct dirent *entry;
+       struct device *devs;
+       size_t devs_len = 0, devs_max = 1024, dev;
+
+       devs = xmalloc (devs_max * sizeof (*devs));
+
+       /* Dump all the directory entries into names, resizing if
+          necessary.  */
+       for (entry = readdir (dir); entry; entry = readdir (dir))
+         {
+           /* Skip current and parent directory entries.  */
+           if (strcmp (entry->d_name, ".") == 0 ||
+               strcmp (entry->d_name, "..") == 0)
+             continue;
+           /* Skip partition entries.  */
+           if (strstr (entry->d_name, "-part"))
+             continue;
+           /* Skip device-mapper entries; we'll handle the ones we want
+              later.  */
+           if (strncmp (entry->d_name, "dm-", sizeof ("dm-") - 1) == 0)
+             continue;
+           /* Skip RAID entries; they are handled by upper layers.  */
+           if (strncmp (entry->d_name, "md-", sizeof ("md-") - 1) == 0)
+             continue;
+           if (devs_len >= devs_max)
+             {
+               devs_max *= 2;
+               devs = xrealloc (devs, devs_max * sizeof (*devs));
+             }
+           devs[devs_len].stable =
+             xasprintf ("/dev/disk/by-id/%s", entry->d_name);
+           devs[devs_len].kernel =
+             canonicalize_file_name (devs[devs_len].stable);
+           devs_len++;
+         }
+
+       qsort (devs, devs_len, sizeof (*devs), &compare_devices);
+
+       closedir (dir);
+
+       /* Now add all the devices in sorted order.  */
+       for (dev = 0; dev < devs_len; ++dev)
+         {
+           if (check_device_readable_unique (devs[dev].stable))
+             {
+               if (hook (devs[dev].stable, 0, hook_data))
+                 goto out;
+             }
+           free (devs[dev].stable);
+           free (devs[dev].kernel);
+         }
+       free (devs);
+      }
+  }
+
+  if (have_devfs ())
+    {
+      i = 0;
+      while (1)
+       {
+         char discn[32];
+         char name[PATH_MAX];
+         struct stat st;
+
+         /* Linux creates symlinks "/dev/discs/discN" for convenience.
+            The way to number disks is the same as GRUB's.  */
+         sprintf (discn, "/dev/discs/disc%d", i++);
+         if (stat (discn, &st) < 0)
+           break;
+
+         if (realpath (discn, name))
+           {
+             strcat (name, "/disc");
+             if (hook (name, 0, hook_data))
+               goto out;
+           }
+       }
+      goto out;
+    }
+#endif /* __linux__ */
+
+  /* IDE disks.  */
+  for (i = 0; i < 96; i++)
+    {
+      char name[16];
+
+      get_ide_disk_name (name, i);
+      if (check_device_readable_unique (name))
+       {
+         if (hook (name, 0, hook_data))
+           goto out;
+       }
+    }
+
+#ifdef __FreeBSD_kernel__
+  /* IDE disks using ATA Direct Access driver.  */
+  if (get_kfreebsd_version () >= 800000)
+    for (i = 0; i < 96; i++)
+      {
+       char name[16];
+
+       get_ada_disk_name (name, i);
+       if (check_device_readable_unique (name))
+         {
+           if (hook (name, 0, hook_data))
+             goto out;
+         }
+      }
+
+  /* ATARAID disks.  */
+  for (i = 0; i < 8; i++)
+    {
+      char name[20];
+
+      get_ataraid_disk_name (name, i);
+      if (check_device_readable_unique (name))
+       {
+         if (hook (name, 0, hook_data))
+           goto out;
+        }
+    }
+
+  /* LSI MegaRAID SAS.  */
+  for (i = 0; i < 32; i++)
+    {
+      char name[20];
+
+      get_mfi_disk_name (name, i);
+      if (check_device_readable_unique (name))
+       {
+         if (hook (name, 0, hook_data))
+           goto out;
+        }
+    }
+
+  /* Virtio disks.  */
+  for (i = 0; i < 96; i++)
+    {
+      char name[16];
+
+      get_virtio_disk_name (name, i);
+      if (check_device_readable_unique (name))
+       {
+         if (hook (name, 0, hook_data))
+           goto out;
+       }
+    }
+
+  /* Xen virtual block devices.  */
+  for (i = 0; i < 96; i++)
+    {
+      char name[16];
+
+      get_xvd_disk_name (name, i);
+      if (check_device_readable_unique (name))
+       {
+         if (hook (name, 0, hook_data))
+           goto out;
+       }
+    }
+#endif
+
+#ifdef __linux__
+  /* Virtio disks.  */
+  for (i = 0; i < 26; i++)
+    {
+      char name[16];
+
+      get_virtio_disk_name (name, i);
+      if (check_device_readable_unique (name))
+       {
+         if (hook (name, 0, hook_data))
+           goto out;
+       }
+    }
+
+  /* ATARAID disks.  */
+  for (i = 0; i < 8; i++)
+    {
+      char name[20];
+
+      get_ataraid_disk_name (name, i);
+      if (check_device_readable_unique (name))
+       {
+         if (hook (name, 0, hook_data))
+           goto out;
+        }
+    }
+
+  /* Xen virtual block devices.  */
+  for (i = 0; i < 26; i++)
+    {
+      char name[16];
+
+      get_xvd_disk_name (name, i);
+      if (check_device_readable_unique (name))
+       {
+         if (hook (name, 0, hook_data))
+           goto out;
+       }
+    }
+#endif /* __linux__ */
+
+  /* The rest is SCSI disks.  */
+  for (i = 0; i < 48; i++)
+    {
+      char name[16];
+
+      get_scsi_disk_name (name, i);
+      if (check_device_readable_unique (name))
+       {
+         if (hook (name, 0, hook_data))
+           goto out;
+       }
+    }
+
+#ifdef __linux__
+  /* This is for DAC960 - we have
+     /dev/rd/c<controller>d<logical drive>p<partition>.
+
+     DAC960 driver currently supports up to 8 controllers, 32 logical
+     drives, and 7 partitions.  */
+  {
+    int controller, drive;
+
+    for (controller = 0; controller < 8; controller++)
+      {
+       for (drive = 0; drive < 15; drive++)
+         {
+           char name[24];
+
+           get_dac960_disk_name (name, controller, drive);
+           if (check_device_readable_unique (name))
+             {
+               if (hook (name, 0, hook_data))
+                 goto out;
+             }
+         }
+      }
+  }
+
+  /* This is for Mylex Acceleraid - we have
+     /dev/rd/c<controller>d<logical drive>p<partition>.  */
+  {
+    int controller, drive;
+
+    for (controller = 0; controller < 8; controller++)
+      {
+       for (drive = 0; drive < 15; drive++)
+         {
+           char name[24];
+
+           get_acceleraid_disk_name (name, controller, drive);
+           if (check_device_readable_unique (name))
+             {
+               if (hook (name, 0, hook_data))
+                 goto out;
+             }
+         }
+      }
+  }
+
+  /* This is for CCISS - we have
+     /dev/cciss/c<controller>d<logical drive>p<partition>.  */
+  {
+    int controller, drive;
+
+    for (controller = 0; controller < 3; controller++)
+      {
+       for (drive = 0; drive < 16; drive++)
+         {
+           char name[24];
+
+           get_cciss_disk_name (name, controller, drive);
+           if (check_device_readable_unique (name))
+             {
+               if (hook (name, 0, hook_data))
+                 goto out;
+             }
+         }
+      }
+  }
+
+  /* This is for Compaq Intelligent Drive Array - we have
+     /dev/ida/c<controller>d<logical drive>p<partition>.  */
+  {
+    int controller, drive;
+
+    for (controller = 0; controller < 3; controller++)
+      {
+       for (drive = 0; drive < 16; drive++)
+         {
+           char name[24];
+
+           get_ida_disk_name (name, controller, drive);
+           if (check_device_readable_unique (name))
+             {
+               if (hook (name, 0, hook_data))
+                 goto out;
+             }
+         }
+      }
+  }
+
+  /* This is for I2O - we have /dev/i2o/hd<logical drive><partition> */
+  {
+    char unit;
+
+    for (unit = 'a'; unit < 'f'; unit++)
+      {
+       char name[24];
+
+       get_i2o_disk_name (name, unit);
+       if (check_device_readable_unique (name))
+         {
+           if (hook (name, 0, hook_data))
+             goto out;
+         }
+      }
+  }
+
+  /* MultiMediaCard (MMC).  */
+  for (i = 0; i < 10; i++)
+    {
+      char name[16];
+
+      get_mmc_disk_name (name, i);
+      if (check_device_readable_unique (name))
+       {
+         if (hook (name, 0, hook_data))
+           goto out;
+       }
+    }
+
+  /* This is for standard NVMe controllers
+     /dev/nvme<controller>n<namespace>p<partition>. No idea about
+     actual limits of how many controllers a system can have and/or
+     how many namespace that would be, 10 for now. */
+  {
+    int controller, namespace;
+
+    for (controller = 0; controller < 10; controller++)
+      {
+       for (namespace = 0; namespace < 10; namespace++)
+         {
+           char name[16];
+
+           get_nvme_disk_name (name, controller, namespace);
+           if (check_device_readable_unique (name))
+             {
+               if (hook (name, 0, hook_data))
+                 goto out;
+             }
+         }
+      }
+  }
+
+# ifdef HAVE_DEVICE_MAPPER
+#  define dmraid_check(cond, ...) \
+  if (! (cond)) \
+    { \
+      grub_dprintf ("deviceiter", __VA_ARGS__); \
+      goto dmraid_end; \
+    }
+
+  /* DM-RAID.  */
+  if (grub_device_mapper_supported ())
+    {
+      struct dm_tree *tree = NULL;
+      struct dm_task *task = NULL;
+      struct dm_names *names = NULL;
+      unsigned int next = 0;
+      void *top_handle, *second_handle;
+      struct dm_tree_node *root, *top, *second;
+
+      /* Build DM tree for all devices.  */
+      tree = dm_tree_create ();
+      dmraid_check (tree, "dm_tree_create failed\n");
+      task = dm_task_create (DM_DEVICE_LIST);
+      dmraid_check (task, "dm_task_create failed\n");
+      dmraid_check (dm_task_run (task), "dm_task_run failed\n");
+      names = dm_task_get_names (task);
+      dmraid_check (names, "dm_task_get_names failed\n");
+      dmraid_check (names->dev, "No DM devices found\n");
+      do
+       {
+         names = (struct dm_names *) ((char *) names + next);
+         dmraid_check (dm_tree_add_dev (tree, MAJOR (names->dev),
+                                        MINOR (names->dev)),
+                          "dm_tree_add_dev (%s) failed\n", names->name);
+         next = names->next;
+       }
+      while (next);
+
+      /* Walk the second-level children of the inverted tree; that is, devices
+        which are directly composed of non-DM devices such as hard disks.
+        This class includes all DM-RAID disks and excludes all DM-RAID
+        partitions.  */
+      root = dm_tree_find_node (tree, 0, 0);
+      top_handle = NULL;
+      top = dm_tree_next_child (&top_handle, root, 1);
+      while (top)
+       {
+         second_handle = NULL;
+         second = dm_tree_next_child (&second_handle, top, 1);
+         while (second)
+           {
+             const char *node_name, *node_uuid;
+             char *name;
+
+             node_name = dm_tree_node_get_name (second);
+             dmraid_check (node_name, "dm_tree_node_get_name failed\n");
+             node_uuid = dm_tree_node_get_uuid (second);
+             dmraid_check (node_uuid, "dm_tree_node_get_uuid failed\n");
+             if (strstr (node_uuid, "DMRAID-") == 0)
+               {
+                 grub_dprintf ("deviceiter", "%s is not DM-RAID\n", node_name);
+                 goto dmraid_next_child;
+               }
+
+             name = xasprintf ("/dev/mapper/%s", node_name);
+             if (check_device_readable_unique (name))
+               {
+                 if (hook (name, 0, hook_data))
+                   {
+                     free (name);
+                     if (task)
+                       dm_task_destroy (task);
+                     if (tree)
+                       dm_tree_free (tree);
+                     goto out;
+                   }
+               }
+             free (name);
+
+dmraid_next_child:
+             second = dm_tree_next_child (&second_handle, top, 1);
+           }
+         top = dm_tree_next_child (&top_handle, root, 1);
+       }
+
+dmraid_end:
+      if (task)
+       dm_task_destroy (task);
+      if (tree)
+       dm_tree_free (tree);
+    }
+# endif /* HAVE_DEVICE_MAPPER */
+#endif /* __linux__ */
+
+out:
+  clear_seen_devices ();
+}
diff --git a/util/devicemap.c b/util/devicemap.c
new file mode 100644 (file)
index 0000000..c618644
--- /dev/null
@@ -0,0 +1,13 @@
+#include <stdio.h>
+
+#include <grub/util/deviceiter.h>
+
+void
+grub_util_emit_devicemap_entry (FILE *fp, char *name, int is_floppy,
+                               int *num_fd, int *num_hd)
+{
+    if (is_floppy)
+      fprintf (fp, "(fd%d)\t%s\n", (*num_fd)++, name);
+    else
+      fprintf (fp, "(hd%d)\t%s\n", (*num_hd)++, name);
+}
diff --git a/util/grub-mkdevicemap.c b/util/grub-mkdevicemap.c
new file mode 100644 (file)
index 0000000..c4bbdbf
--- /dev/null
@@ -0,0 +1,181 @@
+/* grub-mkdevicemap.c - make a device map file automatically */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/util/misc.h>
+#include <grub/util/deviceiter.h>
+#include <grub/env.h>
+#include <grub/i18n.h>
+
+#define _GNU_SOURCE    1
+#include <getopt.h>
+
+#include "progname.h"
+
+/* Context for make_device_map.  */
+struct make_device_map_ctx
+{
+  FILE *fp;
+  int num_fd;
+  int num_hd;
+};
+
+/* Helper for make_device_map.  */
+static int
+process_device (const char *name, int is_floppy, void *data)
+{
+  struct make_device_map_ctx *ctx = data;
+
+  grub_util_emit_devicemap_entry (ctx->fp, (char *) name,
+                                 is_floppy, &ctx->num_fd, &ctx->num_hd);
+  return 0;
+}
+
+static void
+make_device_map (const char *device_map, int floppy_disks)
+{
+  struct make_device_map_ctx ctx = {
+    .num_fd = 0,
+    .num_hd = 0
+  };
+
+  if (strcmp (device_map, "-") == 0)
+    ctx.fp = stdout;
+  else
+    ctx.fp = fopen (device_map, "w");
+
+  if (! ctx.fp)
+    grub_util_error (_("cannot open %s"), device_map);
+
+  grub_util_iterate_devices (process_device, &ctx, floppy_disks);
+
+  if (ctx.fp != stdout)
+    fclose (ctx.fp);
+}
+
+static struct option options[] =
+  {
+    {"device-map", required_argument, 0, 'm'},
+    {"probe-second-floppy", no_argument, 0, 's'},
+    {"no-floppy", no_argument, 0, 'n'},
+    {"help", no_argument, 0, 'h'},
+    {"version", no_argument, 0, 'V'},
+    {"verbose", no_argument, 0, 'v'},
+    {0, 0, 0, 0}
+  };
+
+static void
+usage (int status)
+{
+  if (status)
+    fprintf (stderr,
+            _("Try `%s --help' for more information.\n"), program_name);
+  else
+    printf (_("\
+Usage: %s [OPTION]...\n\
+\n\
+Generate a device map file automatically.\n\
+\n\
+  -n, --no-floppy           do not probe any floppy drive\n\
+  -s, --probe-second-floppy probe the second floppy drive\n\
+  -m, --device-map=FILE     use FILE as the device map [default=%s]\n\
+  -h, --help                display this message and exit\n\
+  -V, --version             print version information and exit\n\
+  -v, --verbose             print verbose messages\n\
+\n\
+Report bugs to <%s>.\n\
+"), program_name,
+           DEFAULT_DEVICE_MAP, PACKAGE_BUGREPORT);
+
+  exit (status);
+}
+
+int
+main (int argc, char *argv[])
+{
+  char *dev_map = 0;
+  int floppy_disks = 1;
+
+  grub_util_host_init (&argc, &argv);
+
+  /* Check for options.  */
+  while (1)
+    {
+      int c = getopt_long (argc, argv, "snm:r:hVv", options, 0);
+
+      if (c == -1)
+       break;
+      else
+       switch (c)
+         {
+         case 'm':
+           if (dev_map)
+             free (dev_map);
+
+           dev_map = xstrdup (optarg);
+           break;
+
+         case 'n':
+           floppy_disks = 0;
+           break;
+
+         case 's':
+           floppy_disks = 2;
+           break;
+
+         case 'h':
+           usage (0);
+           break;
+
+         case 'V':
+           printf ("%s (%s) %s\n", program_name, PACKAGE_NAME, PACKAGE_VERSION);
+           return 0;
+
+         case 'v':
+           verbosity++;
+           break;
+
+         default:
+           usage (1);
+           break;
+         }
+    }
+
+  if (verbosity > 1)
+    grub_env_set ("debug", "all");
+
+  make_device_map (dev_map ? : DEFAULT_DEVICE_MAP, floppy_disks);
+
+  free (dev_map);
+
+  return 0;
+}