Lazy LVM and RAID assembly

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2011-04-22 00:09:07 +02:00
parent 9b710a888e
commit 24b905a11c
13 changed files with 365 additions and 114 deletions

View file

@ -32,7 +32,12 @@ GRUB_MOD_LICENSE ("GPLv3+");
static struct grub_lvm_vg *vg_list;
static int lv_count;
static int scan_depth = 0;
static int is_lv_readable (struct grub_lvm_lv *lv);
static int
grub_lvm_scan_device (const char *name);
/* Go the string STR and return the number after STR. *P will point
at the number. In case STR is not found, *P will be NULL and the
@ -96,16 +101,39 @@ grub_lvm_check_flag (char *p, char *str, char *flag)
}
static int
grub_lvm_iterate (int (*hook) (const char *name))
grub_lvm_iterate (int (*hook) (const char *name),
grub_disk_pull_t pull)
{
struct grub_lvm_vg *vg;
unsigned old_count = 0;
if (pull == GRUB_DISK_PULL_RESCAN && scan_depth)
return 0;
if (pull == GRUB_DISK_PULL_RESCAN)
{
old_count = lv_count;
if (!scan_depth)
{
scan_depth++;
grub_device_iterate (&grub_lvm_scan_device);
scan_depth--;
}
}
if (pull != GRUB_DISK_PULL_RESCAN && pull != GRUB_DISK_PULL_NONE)
return GRUB_ERR_NONE;
for (vg = vg_list; vg; vg = vg->next)
{
struct grub_lvm_lv *lv;
if (vg->lvs)
for (lv = vg->lvs; lv; lv = lv->next)
if (lv->visible && hook (lv->name))
return 1;
if (lv->visible && lv->number >= old_count)
{
char lvname[sizeof ("lvm/") + grub_strlen (lv->name)];
grub_memcpy (lvname, "lvm/", sizeof ("lvm/") - 1);
grub_strcpy (lvname + sizeof ("lvm/") - 1, lv->name);
if (hook (lvname))
return 1;
}
}
return 0;
@ -135,8 +163,8 @@ grub_lvm_memberlist (grub_disk_t disk)
}
#endif
static grub_err_t
grub_lvm_open (const char *name, grub_disk_t disk)
static struct grub_lvm_lv *
find_lv (const char *name)
{
struct grub_lvm_vg *vg;
struct grub_lvm_lv *lv = NULL;
@ -144,11 +172,43 @@ grub_lvm_open (const char *name, grub_disk_t disk)
{
if (vg->lvs)
for (lv = vg->lvs; lv; lv = lv->next)
if (! grub_strcmp (lv->name, name))
break;
if (! grub_strcmp (lv->name, name) && is_lv_readable (lv))
return lv;
}
return NULL;
}
if (lv)
break;
static const char *scan_for = NULL;
static grub_err_t
grub_lvm_open (const char *name, grub_disk_t disk,
grub_disk_pull_t pull)
{
struct grub_lvm_lv *lv = NULL;
int explicit = 0;
if (grub_memcmp (name, "lvm/", sizeof ("lvm/") - 1) == 0)
{
name += sizeof ("lvm/") - 1;
explicit = 1;
}
lv = find_lv (name);
if (! lv && !scan_depth &&
pull == (explicit ? GRUB_DISK_PULL_RESCAN : GRUB_DISK_PULL_RESCAN_UNTYPED))
{
scan_for = name;
scan_depth++;
grub_device_iterate (&grub_lvm_scan_device);
scan_depth--;
scan_for = NULL;
if (grub_errno)
{
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
}
lv = find_lv (name);
}
if (! lv)
@ -285,6 +345,49 @@ read_lv (struct grub_lvm_lv *lv, grub_disk_addr_t sector,
return grub_error (GRUB_ERR_IO, "unknown LVM segment");
}
static grub_err_t
is_node_readable (const struct grub_lvm_node *node)
{
/* Check whether we actually know the physical volume we want to
read from. */
if (node->pv)
return !!(node->pv->disk);
if (node->lv)
return is_lv_readable (node->lv);
return 0;
}
static int
is_lv_readable (struct grub_lvm_lv *lv)
{
unsigned int i, j;
if (!lv)
return 0;
/* Find the right segment. */
for (i = 0; i < lv->segment_count; i++)
switch (lv->segments[i].type)
{
case GRUB_LVM_STRIPED:
for (j = 0; j < lv->segments[i].node_count; j++)
if (!is_node_readable (lv->segments[i].nodes + j))
return 0;
break;
case GRUB_LVM_MIRROR:
for (j = 0; j < lv->segments[i].node_count; j++)
if (is_node_readable (lv->segments[i].nodes + j))
break;
if (j == lv->segments[i].node_count)
return 0;
default:
return 0;
}
return 1;
}
static grub_err_t
grub_lvm_read (grub_disk_t disk, grub_disk_addr_t sector,
grub_size_t size, char *buf)
@ -332,6 +435,15 @@ grub_lvm_scan_device (const char *name)
return 0;
}
for (vg = vg_list; vg; vg = vg->next)
for (pv = vg->pvs; pv; pv = pv->next)
if (pv->disk && pv->disk->id == disk->id
&& pv->disk->dev->id == disk->dev->id)
{
grub_disk_close (disk);
return 0;
}
/* Search for label. */
for (i = 0; i < GRUB_LVM_LABEL_SCAN_SECTORS; i++)
{
@ -857,6 +969,8 @@ grub_lvm_scan_device (const char *name)
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
grub_errno = GRUB_ERR_NONE;
grub_print_error ();
if (scan_for && find_lv (scan_for))
return 1;
return 0;
}
@ -878,13 +992,6 @@ static struct grub_disk_dev grub_lvm_dev =
GRUB_MOD_INIT(lvm)
{
grub_device_iterate (&grub_lvm_scan_device);
if (grub_errno)
{
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
}
grub_disk_dev_register (&grub_lvm_dev);
}