From a7b960126fbc0d4541babe5553546e5f9530ac48 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:01 +0000 Subject: Restore grub-mkdevicemap 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 Forwarded: no Last-Update: 2021-09-24 Patch-Name: restore-mkdevicemap.patch --- Makefile.util.def | 17 + docs/man/grub-mkdevicemap.h2m | 4 + include/grub/util/deviceiter.h | 14 + util/deviceiter.c | 1027 ++++++++++++++++++++++++++++++++ util/devicemap.c | 13 + util/grub-mkdevicemap.c | 181 ++++++ 6 files changed, 1256 insertions(+) create mode 100644 docs/man/grub-mkdevicemap.h2m create mode 100644 include/grub/util/deviceiter.h create mode 100644 util/deviceiter.c create mode 100644 util/devicemap.c create mode 100644 util/grub-mkdevicemap.c diff --git a/Makefile.util.def b/Makefile.util.def index f8b356cc1..27f948291 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -336,6 +336,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/lib/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 index 000000000..96cd6ee72 --- /dev/null +++ b/docs/man/grub-mkdevicemap.h2m @@ -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 index 000000000..85374978c --- /dev/null +++ b/include/grub/util/deviceiter.h @@ -0,0 +1,14 @@ +#ifndef GRUB_DEVICEITER_MACHINE_UTIL_HEADER +#define GRUB_DEVICEITER_MACHINE_UTIL_HEADER 1 + +#include + +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 index 000000000..b5b9ac6ab --- /dev/null +++ b/util/deviceiter.c @@ -0,0 +1,1027 @@ +/* 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 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +# if !defined(__GLIBC__) || \ + ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))) +/* Maybe libc doesn't have large file support. */ +# include /* _llseek */ +# endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */ +# include /* 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 +# 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 +# ifndef __FreeBSD_kernel_version +# define __FreeBSD_kernel_version __FreeBSD_version +# endif + + /* Runtime detection of kernel */ +# include +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 /* ioctl */ +# include +# include /* CDIOCCLRDEBUG */ +# if defined(__FreeBSD_kernel__) +# include +# if __FreeBSD_kernel_version >= 500040 +# include +# endif +# endif /* __FreeBSD_kernel__ */ +#endif /* __FreeBSD_kernel__ || __NetBSD__ || __OpenBSD__ */ + +#ifdef HAVE_OPENDISK +# include +#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 = xcalloc (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) + { + size_t sz; + + devs_max *= 2; + sz = devs_max; + if (grub_mul (sz, sizeof (*devs), &sz)) + grub_util_error ("%s", _("overflow is detected")); + devs = xrealloc (devs, sz); + } + 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/cdp. + + 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/cdp. */ + { + 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/cdp. */ + { + 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/cdp. */ + { + 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 */ + { + 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/nvmenp. 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 index 000000000..c61864420 --- /dev/null +++ b/util/devicemap.c @@ -0,0 +1,13 @@ +#include + +#include + +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 index 000000000..c4bbdbf69 --- /dev/null +++ b/util/grub-mkdevicemap.c @@ -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 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define _GNU_SOURCE 1 +#include + +#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; +}