Merge common RAID and LVM logic to an abstract diskfilter.

Add LDM support using the same framework.

	* Makefile.util.def (libgrubkern): Add grub-core/disk/ldm.c,
	grub-core/disk/diskfilter.c and grub-core/partmap/gpt.c.
	(libgrubmods): Remove grub-core/disk/raid.c and
	grub-core/partmap/gpt.c.
	* grub-core/Makefile.core.def (ldm): New module.
	(raid): Renamed to diskfilter. All users updated.
	* grub-core/disk/raid.c: Moved to ...
	* grub-core/disk/diskfilter.c: ... here.
	* grub-core/disk/diskfilter.c: Rename grub_raid_ to grub_diskfilter_.
	(lv_num): New var.
	(find_array): Renamed to ...
	(find_lv): ... this. Support multi-LV. Skip nameless LVs
	(grub_is_array_readable): Renamed to ...
	(grub_is_lv_readable): ... this. Support multinode hierarchy.
	(insert_array): New argument id.
	(is_node_readable): New function.
	(scan_device): Rename to ...
	(scan_disk): .. this. Restrict to one disk.
	(scan_devices): New function.
	(grub_diskfilter_iterate): Support multi-LV.
	Skip invisible and nameless LVs.
	(grub_diskfilter_memberlist): Support multi-LV.
	(grub_diskfilter_read_node): New function.
	(grub_raid_read): Most of logic moved to ...
	(read_segment): ... here
	(read_lv): New function.
	(grub_diskfilter_get_vg_by_uuid): New function.
	(grub_diskfilter_make_raid): Likewise.
	* grub-core/disk/ldm.c: New file.
	* grub-core/disk/lvm.c (vg_list): Removed.
	(lv_count): Likewise.
	(scan_depth): Likewise.
	(is_lv_readable): Likewise.
	(grub_lvm_getvalue): Advance pointer past the number.
	(find_lv): Removed.
	(do_lvm_scan): Refactored into ...
	(grub_lvm_detect): ... this. Support raid.
	(grub_lvm_iterate): Removed.
	(grub_lvm_memberlist): Likewise.
	(grub_lvm_open): Likewise.
	(grub_lvm_close): Likewise.
	(read_lv): Likewise.
	(read_node): Likewise.
	(is_node_readable): Likewise.
	(is_lv_readable): Likewise.
	(grub_lvm_read): Likewise.
	(grub_lvm_write): Likewise.
	(grub_lvm_dev): Use diskfilter
	(GRUB_MOD_INIT): Likewise.
	(GRUB_MOD_FINI): Likewise.
	* grub-core/disk/dmraid_nvidia.c (grub_dmraid_nv_detect): Use
	new interface.
	* grub-core/disk/mdraid1x_linux.c (grub_mdraid_detect): Likewise.
	* grub-core/disk/mdraid_linux.c (grub_mdraid_detect): Likewise.
	* grub-core/disk/raid5_recover.c (grub_raid5_recover): Use
	grub_diskfilter_read_node.
	Fix a bug with xor.
	* grub-core/disk/raid6_recover.c (grub_raid6_recover): Use
	grub_diskfilter_read_node.
	Support GRUB_RAID_LAYOUT_MUL_FROM_POS.
	* grub-core/kern/disk.c (grub_disk_dev_list): Make global.
	(grub_disk_dev_iterate): Move from here...
	* include/grub/disk.h (grub_disk_dev_iterate): ... to here. Inlined.
	* grub-core/kern/emu/hostdisk.c (grub_hostdisk_find_partition_start):
	Make global.
	(grub_hostdisk_find_partition_start): Likewise.
	(grub_hostdisk_os_dev_to_grub_drive): New function.
	(grub_util_biosdisk_get_osdev): Check that disk is biosdisk.
	* grub-core/kern/emu/hostdisk.c (make_device_name): Move to ...
	* util/getroot.c (make_device_name): ... here.
	* grub-core/kern/emu/hostdisk.c (grub_util_get_dm_node_linear_info):
	Move to ...
	* util/getroot.c (grub_util_get_dm_node_linear_info): ...here.
	* grub-core/kern/emu/hostdisk.c
	(convert_system_partition_to_system_disk): Move to ...
	* util/getroot.c (convert_system_partition_to_system_disk): ...here.
	* grub-core/kern/emu/hostdisk.c (device_is_wholedisk): Move to ...
	* util/getroot.c (device_is_wholedisk): ... here.
	* grub-core/kern/emu/hostdisk.c (find_system_device): Move to ...
	* util/getroot.c (find_system_device): ... here.
	* grub-core/kern/emu/hostdisk.c (grub_util_biosdisk_is_present):
	Move to ...
	* util/getroot.c (grub_util_biosdisk_is_present): ...here.
	* grub-core/kern/emu/hostdisk.c (grub_util_biosdisk_get_grub_dev):
	Move to ...
	* util/getroot.c (grub_util_biosdisk_get_grub_dev): ... here.
	Handle LDM.
	* grub-core/kern/emu/hostdisk.c (grub_util_biosdisk_is_floppy):
	Move to ...
	* util/getroot.c (grub_util_biosdisk_is_floppy): ... here.
	* grub-core/partmap/gpt.c (grub_gpt_partition_map_iterate): Made global.
	* include/grub/disk.h (grub_disk_dev_id): Replaced RAID and LVM with
	DISKFILTER.
	* include/grub/raid.h: Renamed to ...
	* include/grub/diskfilter.h: ... this.
	* include/grub/diskfilter.h: Rename grub_raid_* to grub_diskfilter_*
	(GRUB_RAID_LAYOUT_*): Make into array.
	(GRUB_RAID_LAYOUT_MUL_FROM_POS): New value.
	(grub_diskfilter_vg): New struct.
	(grub_diskfilter_pv_id): Likewise.
	(grub_raid_member): Removed.
	(grub_raid_array): Likewise.
	(grub_diskfilter_pv): New struct.
	(grub_diskfilter_lv): Likewise.
	(grub_diskfilter_segment): Likewise.
	(grub_diskfilter_node): Likewise.
	(grub_diskfilter_get_vg_by_uuid): New proto.
	(grub_raid_register): Inline.
	(grub_diskfilter_unregister): Likewise.
	(grub_diskfilter_make_raid): New proto.
	(grub_diskfilter_vg_register): Likewise.
	(grub_diskfilter_read_node): Likewise.
	(grub_diskfilter_get_pv_from_disk) [GRUB_UTIL]: Likewise.
	* include/grub/emu/hostdisk.h (grub_util_get_ldm): New proto.
	(grub_util_is_ldm): Likewise.
	(grub_util_ldm_embed) [GRUB_UTIL]: Likewise.
	(grub_hostdisk_find_partition_start): Likewise.
	(grub_hostdisk_os_dev_to_grub_drive): Likewise.
	* include/grub/gpt_partition.h (GRUB_GPT_PARTITION_TYPE_LDM):
	New definition.
	(grub_gpt_partition_map_iterate): New proto.
	* include/grub/lvm.h (grub_lvm_vg): Removed.
	(grub_lvm_pv): Likewise.
	(grub_lvm_lv): Likewise.
	(grub_lvm_segment): Likewise.
	(grub_lvm_node): Likewise.
	* util/getroot.c [...]
	* util/grub-probe.c (probe_raid_level): Handle diskfilter.
	(probe_abstraction): Likewise.
	* util/grub-setup.c (setup): Remove must_embed. Support LDM.
	(main): Remove dead logic.
This commit is contained in:
Vladimir 'phcoder' Serbinenko 2012-01-29 14:28:01 +01:00
parent 8a7f9b9c50
commit 076e7c0fda
27 changed files with 4249 additions and 3052 deletions

View file

@ -76,6 +76,71 @@
#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 */
# include <cygwin/hdreg.h> /* HDIO_GETGEO */
# define MAJOR(dev) ((unsigned) ((dev) >> 16))
# define FLOPPY_MAJOR 2
#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>
#endif
#ifdef HAVE_DEVICE_MAPPER
# include <libdevmapper.h>
#endif
#if defined(__NetBSD__)
# define HAVE_DIOCGDINFO
# include <sys/ioctl.h>
# include <sys/disklabel.h> /* struct disklabel */
#else /* !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) */
# undef HAVE_DIOCGDINFO
#endif /* defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
#if defined(__NetBSD__)
# ifdef HAVE_GETRAWPARTITION
# include <util.h> /* getrawpartition */
# endif /* HAVE_GETRAWPARTITION */
# include <sys/fdio.h>
# ifndef FLOPPY_MAJOR
# define FLOPPY_MAJOR 2
# endif /* ! FLOPPY_MAJOR */
# ifndef RAW_FLOPPY_MAJOR
# define RAW_FLOPPY_MAJOR 9
# endif /* ! RAW_FLOPPY_MAJOR */
#endif /* defined(__NetBSD__) */
static void
strip_extra_slashes (char *dir)
{
@ -1164,6 +1229,773 @@ grub_util_pull_device (const char *os_dev)
}
}
#ifdef HAVE_DEVICE_MAPPER
static int
grub_util_get_dm_node_linear_info (const char *dev,
int *maj, int *min)
{
struct dm_task *dmt;
void *next = NULL;
uint64_t length, start;
char *target, *params;
char *ptr;
int major, minor;
dmt = dm_task_create(DM_DEVICE_TABLE);
if (!dmt)
return 0;
if (!dm_task_set_name(dmt, dev))
return 0;
dm_task_no_open_count(dmt);
if (!dm_task_run(dmt))
return 0;
next = dm_get_next_target(dmt, next, &start, &length,
&target, &params);
if (grub_strcmp (target, "linear") != 0)
return 0;
major = grub_strtoul (params, &ptr, 10);
if (grub_errno)
{
grub_errno = GRUB_ERR_NONE;
return 0;
}
if (*ptr != ':')
return 0;
ptr++;
minor = grub_strtoul (ptr, 0, 10);
if (grub_errno)
{
grub_errno = GRUB_ERR_NONE;
return 0;
}
if (maj)
*maj = major;
if (min)
*min = minor;
return 1;
}
#endif
int
grub_util_biosdisk_is_floppy (grub_disk_t disk)
{
struct stat st;
int fd;
const char *dname;
dname = grub_util_biosdisk_get_osdev (disk);
if (!dname)
return 0;
fd = open (dname, O_RDONLY);
/* Shouldn't happen. */
if (fd == -1)
return 0;
/* Shouldn't happen either. */
if (fstat (fd, &st) < 0)
{
close (fd);
return 0;
}
close (fd);
#if defined(__NetBSD__)
if (major(st.st_rdev) == RAW_FLOPPY_MAJOR)
return 1;
#endif
#if defined(FLOPPY_MAJOR)
if (major(st.st_rdev) == FLOPPY_MAJOR)
#else
/* Some kernels (e.g. kFreeBSD) don't have a static major number
for floppies, but they still use a "fd[0-9]" pathname. */
if (map[disk->id].device[5] == 'f'
&& map[disk->id].device[6] == 'd'
&& map[disk->id].device[7] >= '0'
&& map[disk->id].device[7] <= '9')
#endif
return 1;
return 0;
}
static char *
convert_system_partition_to_system_disk (const char *os_dev, struct stat *st)
{
#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)
strcpy (p, "disc");
return path;
}
/* If this is a SCSI disk. */
if (strncmp ("scsi/", p, 5) == 0)
{
p = strstr (p, "part");
if (p)
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)
*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)
*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)
*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)
*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]+)? */
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)
*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++;
*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]* */
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++;
/* /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++;
/* /dev/xvd[a-z]+[0-9]* */
*pp = '\0';
return path;
}
#ifdef HAVE_DEVICE_MAPPER
/* If this is a DM-RAID device.
Compare os_dev rather than path here, since nodes under
/dev/mapper/ are often symlinks. */
if ((strncmp ("/dev/mapper/", os_dev, 12) == 0))
{
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_dprintf ("hostdisk", "dm_tree_create failed\n");
goto devmapper_out;
}
maj = major (st->st_rdev);
min = minor (st->st_rdev);
if (! dm_tree_add_dev (tree, maj, min))
{
grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n");
goto devmapper_out;
}
node = dm_tree_find_node (tree, maj, min);
if (! node)
{
grub_dprintf ("hostdisk", "dm_tree_find_node failed\n");
goto devmapper_out;
}
node_uuid = dm_tree_node_get_uuid (node);
if (! node_uuid)
{
grub_dprintf ("hostdisk", "%s has no DM uuid\n", path);
node = NULL;
goto devmapper_out;
}
if (strncmp (node_uuid, "LVM-", 4) == 0)
{
grub_dprintf ("hostdisk", "%s is an LVM\n", path);
node = NULL;
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_dprintf ("hostdisk", "%s is a multipath disk\n", path);
mapper_name = dm_tree_node_get_name (node);
goto devmapper_out;
}
if (strncmp (node_uuid, "DMRAID-", 7) != 0)
{
int major, minor;
const char *node_name;
grub_dprintf ("hostdisk", "%s is not DM-RAID\n", path);
if ((node_name = dm_tree_node_get_name (node))
&& grub_util_get_dm_node_linear_info (node_name,
&major, &minor))
{
if (tree)
dm_tree_free (tree);
free (path);
char *ret = grub_find_device ("/dev",
(major << 8) | minor);
return ret;
}
node = NULL;
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_dprintf ("hostdisk", "%s has no DM children\n", path);
goto devmapper_out;
}
child_uuid = dm_tree_node_get_uuid (child);
if (! child_uuid)
{
grub_dprintf ("hostdisk", "%s child has no DM uuid\n", path);
goto devmapper_out;
}
else if (strncmp (child_uuid, "DMRAID-", 7) != 0)
{
grub_dprintf ("hostdisk", "%s child is not DM-RAID\n", path);
goto devmapper_out;
}
child_name = dm_tree_node_get_name (child);
if (! child_name)
{
grub_dprintf ("hostdisk", "%s child has no DM name\n", path);
goto devmapper_out;
}
mapper_name = child_name;
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_dprintf ("hostdisk", "%s has no DM name\n", path);
}
if (tree)
dm_tree_free (tree);
free (path);
if (mapper_name)
return xasprintf ("/dev/mapper/%s", mapper_name);
else
return NULL;
}
#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)
*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] = 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);
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)
*p = '\0';
break;
}
}
return path;
#elif defined(__NetBSD__)
/* NetBSD uses "/dev/r[a-z]+[0-9][a-z]". */
char *path = xstrdup (os_dev);
if (strncmp ("/dev/r", path, sizeof("/dev/r") - 1) == 0 &&
(path[sizeof("/dev/r") - 1] >= 'a' && path[sizeof("/dev/r") - 1] <= 'z') &&
strncmp ("fd", path + sizeof("/dev/r") - 1, sizeof("fd") - 1) != 0) /* not a floppy device name */
{
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'))
{
/* path matches the required regular expression and
p points to its last character. */
int rawpart = -1;
# ifdef HAVE_GETRAWPARTITION
rawpart = getrawpartition();
# endif /* HAVE_GETRAWPARTITION */
if (rawpart >= 0)
*p = 'a' + rawpart;
}
}
}
return path;
#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"));
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);
#else
# warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
return xstrdup (os_dev);
#endif
}
static const char *
find_system_device (const char *os_dev, struct stat *st, int convert, int add)
{
unsigned int i;
char *os_disk;
const char *drive;
if (convert)
os_disk = convert_system_partition_to_system_disk (os_dev, st);
else
os_disk = xstrdup (os_dev);
if (! os_disk)
return NULL;
drive = grub_hostdisk_os_dev_to_grub_drive (os_disk, add);
free (os_disk);
return drive;
}
/*
* Note: we do not use the new partition naming scheme as dos_part does not
* necessarily correspond to an msdos partition.
*/
static char *
make_device_name (const char *drive, int dos_part, int bsd_part)
{
char *ret, *ptr, *end;
const char *iptr;
ret = xmalloc (strlen (drive) * 2
+ sizeof (",XXXXXXXXXXXXXXXXXXXXXXXXXX"
",XXXXXXXXXXXXXXXXXXXXXXXXXX"));
end = (ret + strlen (drive) * 2
+ sizeof (",XXXXXXXXXXXXXXXXXXXXXXXXXX"
",XXXXXXXXXXXXXXXXXXXXXXXXXX"));
ptr = ret;
for (iptr = drive; *iptr; iptr++)
{
if (*iptr == ',')
*ptr++ = '\\';
*ptr++ = *iptr;
}
*ptr = 0;
if (dos_part >= 0)
snprintf (ptr, end - ptr, ",%d", dos_part + 1);
ptr += strlen (ptr);
if (bsd_part >= 0)
snprintf (ptr, end - ptr, ",%d", bsd_part + 1);
return ret;
}
#if defined(__sun__)
static int
device_is_wholedisk (const char *os_dev)
{
if (grub_memcmp (os_dev, "/devices/", sizeof ("/devices/") - 1) != 0)
return 1;
if (grub_memcmp (os_dev + strlen (os_dev) - (sizeof (":q,raw") - 1),
":q,raw", (sizeof (":q,raw") - 1)) == 0)
return 1;
return 0;
}
#endif
#if defined(__linux__) || defined(__CYGWIN__)
static int
device_is_wholedisk (const char *os_dev)
{
int len = strlen (os_dev);
if (os_dev[len - 1] < '0' || os_dev[len - 1] > '9')
return 1;
return 0;
}
#endif
#if defined(__NetBSD__)
/* Try to determine whether a given device name corresponds to a whole disk.
This function should give in most cases a definite answer, but it may
actually give an approximate one in the following sense: if the return
value is 0 then the device name does not correspond to a whole disk. */
static int
device_is_wholedisk (const char *os_dev)
{
int len = strlen (os_dev);
int rawpart = -1;
# ifdef HAVE_GETRAWPARTITION
rawpart = getrawpartition();
# endif /* HAVE_GETRAWPARTITION */
if (rawpart < 0)
return 1;
return (os_dev[len - 1] == ('a' + rawpart));
}
#endif /* defined(__NetBSD__) */
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
static int
device_is_wholedisk (const char *os_dev)
{
const char *p;
if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
return 0;
for (p = os_dev + sizeof ("/dev/") - 1; *p; ++p)
if (grub_isdigit (*p))
{
if (strchr (p, 's'))
return 0;
break;
}
return 1;
}
#endif /* defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
char *
grub_util_biosdisk_get_grub_dev (const char *os_dev)
{
struct stat st;
const char *drive;
char *sys_disk;
grub_util_info ("Looking for %s", os_dev);
if (stat (os_dev, &st) < 0)
{
grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", os_dev);
grub_util_info ("cannot stat `%s'", os_dev);
return 0;
}
drive = find_system_device (os_dev, &st, 1, 1);
if (!drive)
{
grub_error (GRUB_ERR_UNKNOWN_DEVICE,
"no mapping exists for `%s'", os_dev);
grub_util_info ("no mapping exists for `%s'", os_dev);
return 0;
}
sys_disk = convert_system_partition_to_system_disk (os_dev, &st);
if (grub_strcmp (os_dev, sys_disk) == 0)
{
free (sys_disk);
return make_device_name (drive, -1, -1);
}
free (sys_disk);
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) || defined (__sun__)
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__)
/* Linux counts partitions uniformly, whether a BSD partition or a DOS
partition, so mapping them to GRUB devices is not trivial.
Here, get the start sector of a partition by HDIO_GETGEO, and
compare it with each partition GRUB recognizes.
Cygwin /dev/sdXN emulation uses Windows partition mapping. It
does not count the extended partition and missing primary
partitions. Use same method as on Linux here.
For NetBSD and FreeBSD, proceed as for Linux, except that the start
sector is obtained from the disk label. */
{
char *name, *partname;
grub_disk_t disk;
grub_disk_addr_t start;
auto int find_partition (grub_disk_t dsk,
const grub_partition_t partition);
int find_partition (grub_disk_t dsk __attribute__ ((unused)),
const grub_partition_t partition)
{
grub_disk_addr_t part_start = 0;
grub_util_info ("Partition %d starts from %lu",
partition->number, partition->start);
part_start = grub_partition_get_start (partition);
if (start == part_start)
{
partname = grub_partition_get_name (partition);
return 1;
}
return 0;
}
name = make_device_name (drive, -1, -1);
# if !defined(HAVE_DIOCGDINFO) && !defined(__sun__)
if (MAJOR (st.st_rdev) == FLOPPY_MAJOR)
return name;
# else /* defined(HAVE_DIOCGDINFO) */
/* 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) */
start = grub_hostdisk_find_partition_start (os_dev);
if (grub_errno != GRUB_ERR_NONE)
{
free (name);
return 0;
}
grub_util_info ("%s starts from %lu", os_dev, start);
if (start == 0 && device_is_wholedisk (os_dev))
return name;
grub_util_info ("opening the device %s", name);
disk = grub_disk_open (name);
free (name);
if (! disk)
{
/* We already know that the partition exists. Given that we already
checked the device map above, we can only get
GRUB_ERR_UNKNOWN_DEVICE at this point if the disk does not exist.
This can happen on Xen, where disk images in the host can be
assigned to devices that have partition-like names in the guest
but are really more like disks. */
if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
{
grub_util_warn
("disk does not exist, so falling back to partition device %s",
os_dev);
drive = find_system_device (os_dev, &st, 0, 1);
if (!drive)
{
grub_error (GRUB_ERR_UNKNOWN_DEVICE,
"no mapping exists for `%s'", os_dev);
return 0;
}
return make_device_name (drive, -1, -1);
}
else
return 0;
}
name = grub_util_get_ldm (disk, start);
if (name)
return name;
partname = NULL;
grub_partition_iterate (disk, find_partition);
if (grub_errno != GRUB_ERR_NONE)
{
grub_disk_close (disk);
return 0;
}
if (partname == NULL)
{
grub_disk_close (disk);
grub_util_info ("cannot find the partition of `%s'", os_dev);
grub_error (GRUB_ERR_BAD_DEVICE,
"cannot find the partition of `%s'", os_dev);
return 0;
}
name = grub_xasprintf ("%s,%s", disk->name, partname);
free (partname);
grub_disk_close (disk);
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);
}
#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
}
int
grub_util_biosdisk_is_present (const char *os_dev)
{
struct stat st;
if (stat (os_dev, &st) < 0)
return 0;
return find_system_device (os_dev, &st, 1, 0) != NULL;
}
char *
grub_util_get_grub_dev (const char *os_dev)
{