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);
\f
/* 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
}
}
++static struct grub_lvm_lv *
++find_lv (const char *name)
++{
++ struct grub_lvm_vg *vg;
++ struct grub_lvm_lv *lv = NULL;
++ for (vg = vg_list; vg; vg = vg->next)
++ {
++ if (vg->lvs)
++ for (lv = vg->lvs; lv; lv = lv->next)
++ if ((grub_strcmp (lv->fullname, name) == 0
++ || grub_strcmp (lv->compatname, name) == 0)
++ && is_lv_readable (lv))
++ return lv;
++ }
++ return NULL;
++}
++
++static void
++do_lvm_scan (const char *scan_for)
++{
++ auto int grub_lvm_scan_device (const char *name);
++ int grub_lvm_scan_device (const char *name)
++ {
++ grub_err_t err;
++ grub_disk_t disk;
++ grub_uint64_t mda_offset, mda_size;
++ char buf[GRUB_LVM_LABEL_SIZE];
++ char vg_id[GRUB_LVM_ID_STRLEN+1];
++ char pv_id[GRUB_LVM_ID_STRLEN+1];
++ char *metadatabuf, *p, *q, *vgname;
++ struct grub_lvm_label_header *lh = (struct grub_lvm_label_header *) buf;
++ struct grub_lvm_pv_header *pvh;
++ struct grub_lvm_disk_locn *dlocn;
++ struct grub_lvm_mda_header *mdah;
++ struct grub_lvm_raw_locn *rlocn;
++ unsigned int i, j, vgname_len;
++ struct grub_lvm_vg *vg;
++ struct grub_lvm_pv *pv;
++
++#ifdef GRUB_UTIL
++ grub_util_info ("scanning %s for LVM", name);
++#endif
++
++ disk = grub_disk_open (name);
++ if (!disk)
++ {
++ if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
++ grub_errno = GRUB_ERR_NONE;
++ 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++)
++ {
++ err = grub_disk_read (disk, i, 0, sizeof(buf), buf);
++ if (err)
++ goto fail;
++
++ if ((! grub_strncmp ((char *)lh->id, GRUB_LVM_LABEL_ID,
++ sizeof (lh->id)))
++ && (! grub_strncmp ((char *)lh->type, GRUB_LVM_LVM2_LABEL,
++ sizeof (lh->type))))
++ break;
++ }
++
++ /* Return if we didn't find a label. */
++ if (i == GRUB_LVM_LABEL_SCAN_SECTORS)
++ {
++#ifdef GRUB_UTIL
++ grub_util_info ("no LVM signature found");
++#endif
++ goto fail;
++ }
++
++ pvh = (struct grub_lvm_pv_header *) (buf + grub_le_to_cpu32(lh->offset_xl));
++
++ for (i = 0, j = 0; i < GRUB_LVM_ID_LEN; i++)
++ {
++ pv_id[j++] = pvh->pv_uuid[i];
++ if ((i != 1) && (i != 29) && (i % 4 == 1))
++ pv_id[j++] = '-';
++ }
++ pv_id[j] = '\0';
++
++ dlocn = pvh->disk_areas_xl;
++
++ dlocn++;
++ /* Is it possible to have multiple data/metadata areas? I haven't
++ seen devices that have it. */
++ if (dlocn->offset)
++ {
++ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
++ "we don't support multiple LVM data areas");
++
++#ifdef GRUB_UTIL
++ grub_util_info ("we don't support multiple LVM data areas\n");
++#endif
++ goto fail;
++ }
++
++ dlocn++;
++ mda_offset = grub_le_to_cpu64 (dlocn->offset);
++ mda_size = grub_le_to_cpu64 (dlocn->size);
++
++ /* It's possible to have multiple copies of metadata areas, we just use the
++ first one. */
++
++ /* Allocate buffer space for the circular worst-case scenario. */
++ metadatabuf = grub_malloc (2 * mda_size);
++ if (! metadatabuf)
++ goto fail;
++
++ err = grub_disk_read (disk, 0, mda_offset, mda_size, metadatabuf);
++ if (err)
++ goto fail2;
++
++ mdah = (struct grub_lvm_mda_header *) metadatabuf;
++ if ((grub_strncmp ((char *)mdah->magic, GRUB_LVM_FMTT_MAGIC,
++ sizeof (mdah->magic)))
++ || (grub_le_to_cpu32 (mdah->version) != GRUB_LVM_FMTT_VERSION))
++ {
++ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
++ "unknown LVM metadata header");
++#ifdef GRUB_UTIL
++ grub_util_info ("unknown LVM metadata header\n");
++#endif
++ goto fail2;
++ }
++
++ rlocn = mdah->raw_locns;
++ if (grub_le_to_cpu64 (rlocn->offset) + grub_le_to_cpu64 (rlocn->size) >
++ grub_le_to_cpu64 (mdah->size))
++ {
++ /* Metadata is circular. Copy the wrap in place. */
++ grub_memcpy (metadatabuf + mda_size,
++ metadatabuf + GRUB_LVM_MDA_HEADER_SIZE,
++ grub_le_to_cpu64 (rlocn->offset) +
++ grub_le_to_cpu64 (rlocn->size) -
++ grub_le_to_cpu64 (mdah->size));
++ }
++ p = q = metadatabuf + grub_le_to_cpu64 (rlocn->offset);
++
++ while (*q != ' ' && q < metadatabuf + mda_size)
++ q++;
++
++ if (q == metadatabuf + mda_size)
++ {
++#ifdef GRUB_UTIL
++ grub_util_info ("error parsing metadata\n");
++#endif
++ goto fail2;
++ }
++
++ vgname_len = q - p;
++ vgname = grub_malloc (vgname_len + 1);
++ if (!vgname)
++ goto fail2;
++
++ grub_memcpy (vgname, p, vgname_len);
++ vgname[vgname_len] = '\0';
++
++ p = grub_strstr (q, "id = \"");
++ if (p == NULL)
++ {
++#ifdef GRUB_UTIL
++ grub_util_info ("couldn't find ID\n");
++#endif
++ goto fail3;
++ }
++ p += sizeof ("id = \"") - 1;
++ grub_memcpy (vg_id, p, GRUB_LVM_ID_STRLEN);
++ vg_id[GRUB_LVM_ID_STRLEN] = '\0';
++
++ for (vg = vg_list; vg; vg = vg->next)
++ {
++ if (! grub_memcmp(vg_id, vg->id, GRUB_LVM_ID_STRLEN))
++ break;
++ }
++
++ if (! vg)
++ {
++ /* First time we see this volume group. We've to create the
++ whole volume group structure. */
++ vg = grub_malloc (sizeof (*vg));
++ if (! vg)
++ goto fail3;
++ vg->name = vgname;
++ grub_memcpy (vg->id, vg_id, GRUB_LVM_ID_STRLEN+1);
++
++ vg->extent_size = grub_lvm_getvalue (&p, "extent_size = ");
++ if (p == NULL)
++ {
++#ifdef GRUB_UTIL
++ grub_util_info ("unknown extent size\n");
++#endif
++ goto fail4;
++ }
++
++ vg->lvs = NULL;
++ vg->pvs = NULL;
++
++ p = grub_strstr (p, "physical_volumes {");
++ if (p)
++ {
++ p += sizeof ("physical_volumes {") - 1;
++
++ /* Add all the pvs to the volume group. */
++ while (1)
++ {
++ int s;
++ while (grub_isspace (*p))
++ p++;
++
++ if (*p == '}')
++ break;
++
++ pv = grub_malloc (sizeof (*pv));
++ q = p;
++ while (*q != ' ')
++ q++;
++
++ s = q - p;
++ pv->name = grub_malloc (s + 1);
++ grub_memcpy (pv->name, p, s);
++ pv->name[s] = '\0';
++
++ p = grub_strstr (p, "id = \"");
++ if (p == NULL)
++ goto pvs_fail;
++ p += sizeof("id = \"") - 1;
++
++ grub_memcpy (pv->id, p, GRUB_LVM_ID_STRLEN);
++ pv->id[GRUB_LVM_ID_STRLEN] = '\0';
++
++ pv->start = grub_lvm_getvalue (&p, "pe_start = ");
++ if (p == NULL)
++ {
++#ifdef GRUB_UTIL
++ grub_util_info ("unknown pe_start\n");
++#endif
++ goto pvs_fail;
++ }
++
++ p = grub_strchr (p, '}');
++ if (p == NULL)
++ {
++#ifdef GRUB_UTIL
++ grub_util_info ("error parsing pe_start\n");
++#endif
++ goto pvs_fail;
++ }
++ p++;
++
++ pv->disk = NULL;
++ pv->next = vg->pvs;
++ vg->pvs = pv;
++
++ continue;
++ pvs_fail:
++ grub_free (pv->name);
++ grub_free (pv);
++ goto fail4;
++ }
++ }
++
++ p = grub_strstr (p, "logical_volumes");
++ if (p)
++ {
++ p += 18;
++
++ /* And add all the lvs to the volume group. */
++ while (1)
++ {
++ int s;
++ int skip_lv = 0;
++ struct grub_lvm_lv *lv;
++ struct grub_lvm_segment *seg;
++ int is_pvmove;
++
++ while (grub_isspace (*p))
++ p++;
++
++ if (*p == '}')
++ break;
++
++ lv = grub_malloc (sizeof (*lv));
++
++ q = p;
++ while (*q != ' ')
++ q++;
++
++ s = q - p;
++ lv->name = grub_strndup (p, s);
++ if (!lv->name)
++ goto lvs_fail;
++ lv->compatname = grub_malloc (vgname_len + 1 + s + 1);
++ if (!lv->compatname)
++ goto lvs_fail;
++ grub_memcpy (lv->compatname, vgname, vgname_len);
++ lv->compatname[vgname_len] = '-';
++ grub_memcpy (lv->compatname + vgname_len + 1, p, s);
++ lv->compatname[vgname_len + 1 + s] = '\0';
++
++ {
++ const char *iptr;
++ char *optr;
++ lv->fullname = grub_malloc (sizeof("lvm/") + 2 * vgname_len
++ + 1 + 2 * s + 1);
++ if (!lv->fullname)
++ goto lvs_fail;
++
++ optr = lv->fullname;
++ grub_memcpy (optr, "lvm/", sizeof ("lvm/") - 1);
++ optr += sizeof ("lvm/") - 1;
++ for (iptr = vgname; iptr < vgname + vgname_len; iptr++)
++ {
++ *optr++ = *iptr;
++ if (*iptr == '-')
++ *optr++ = '-';
++ }
++ *optr++ = '-';
++ for (iptr = p; iptr < p + s; iptr++)
++ {
++ *optr++ = *iptr;
++ if (*iptr == '-')
++ *optr++ = '-';
++ }
++ *optr++ = 0;
++ }
++
++ lv->size = 0;
++
++ lv->visible = grub_lvm_check_flag (p, "status", "VISIBLE");
++ is_pvmove = grub_lvm_check_flag (p, "status", "PVMOVE");
++
++ lv->segment_count = grub_lvm_getvalue (&p, "segment_count = ");
++ if (p == NULL)
++ {
++#ifdef GRUB_UTIL
++ grub_util_info ("unknown segment_count\n");
++#endif
++ goto lvs_fail;
++ }
++ lv->segments = grub_malloc (sizeof (*seg) * lv->segment_count);
++ seg = lv->segments;
++
++ for (i = 0; i < lv->segment_count; i++)
++ {
++
++ p = grub_strstr (p, "segment");
++ if (p == NULL)
++ {
++#ifdef GRUB_UTIL
++ grub_util_info ("unknown segment\n");
++#endif
++ goto lvs_segment_fail;
++ }
++
++ seg->start_extent = grub_lvm_getvalue (&p, "start_extent = ");
++ if (p == NULL)
++ {
++#ifdef GRUB_UTIL
++ grub_util_info ("unknown start_extent\n");
++#endif
++ goto lvs_segment_fail;
++ }
++ seg->extent_count = grub_lvm_getvalue (&p, "extent_count = ");
++ if (p == NULL)
++ {
++#ifdef GRUB_UTIL
++ grub_util_info ("unknown extent_count\n");
++#endif
++ goto lvs_segment_fail;
++ }
++
++ p = grub_strstr (p, "type = \"");
++ if (p == NULL)
++ goto lvs_segment_fail;
++ p += sizeof("type = \"") - 1;
++
++ lv->size += seg->extent_count * vg->extent_size;
++
++ if (grub_memcmp (p, "striped\"",
++ sizeof ("striped\"") - 1) == 0)
++ {
++ struct grub_lvm_node *stripe;
++
++ seg->type = GRUB_LVM_STRIPED;
++ seg->node_count = grub_lvm_getvalue (&p, "stripe_count = ");
++ if (p == NULL)
++ {
++#ifdef GRUB_UTIL
++ grub_util_info ("unknown stripe_count\n");
++#endif
++ goto lvs_segment_fail;
++ }
++
++ if (seg->node_count != 1)
++ seg->stripe_size = grub_lvm_getvalue (&p, "stripe_size = ");
++
++ seg->nodes = grub_zalloc (sizeof (*stripe)
++ * seg->node_count);
++ stripe = seg->nodes;
++
++ p = grub_strstr (p, "stripes = [");
++ if (p == NULL)
++ {
++#ifdef GRUB_UTIL
++ grub_util_info ("unknown stripes\n");
++#endif
++ goto lvs_segment_fail2;
++ }
++ p += sizeof("stripes = [") - 1;
++
++ for (j = 0; j < seg->node_count; j++)
++ {
++ p = grub_strchr (p, '"');
++ if (p == NULL)
++ continue;
++ q = ++p;
++ while (*q != '"')
++ q++;
++
++ s = q - p;
++
++ stripe->name = grub_malloc (s + 1);
++ if (stripe->name == NULL)
++ goto lvs_segment_fail2;
++
++ grub_memcpy (stripe->name, p, s);
++ stripe->name[s] = '\0';
++
++ stripe->start = grub_lvm_getvalue (&p, ",");
++ if (p == NULL)
++ continue;
++
++ stripe++;
++ }
++ }
++ else if (grub_memcmp (p, "mirror\"", sizeof ("mirror\"") - 1)
++ == 0)
++ {
++ seg->type = GRUB_LVM_MIRROR;
++ seg->node_count = grub_lvm_getvalue (&p, "mirror_count = ");
++ if (p == NULL)
++ {
++#ifdef GRUB_UTIL
++ grub_util_info ("unknown mirror_count\n");
++#endif
++ goto lvs_segment_fail;
++ }
++
++ seg->nodes = grub_zalloc (sizeof (seg->nodes[0])
++ * seg->node_count);
++
++ p = grub_strstr (p, "mirrors = [");
++ if (p == NULL)
++ {
++#ifdef GRUB_UTIL
++ grub_util_info ("unknown mirrors\n");
++#endif
++ goto lvs_segment_fail2;
++ }
++ p += sizeof("mirrors = [") - 1;
++
++ for (j = 0; j < seg->node_count; j++)
++ {
++ char *lvname;
++
++ p = grub_strchr (p, '"');
++ if (p == NULL)
++ continue;
++ q = ++p;
++ while (*q != '"')
++ q++;
++
++ s = q - p;
++
++ lvname = grub_malloc (s + 1);
++ if (lvname == NULL)
++ goto lvs_segment_fail2;
++
++ grub_memcpy (lvname, p, s);
++ lvname[s] = '\0';
++ seg->nodes[j].name = lvname;
++ p = q + 1;
++ }
++ /* Only first (original) is ok with in progress pvmove. */
++ if (is_pvmove)
++ seg->node_count = 1;
++ }
++ else
++ {
++#ifdef GRUB_UTIL
++ char *p2;
++ p2 = grub_strchr (p, '"');
++ if (p2)
++ *p2 = 0;
++ grub_util_info ("unknown LVM type %s\n", p);
++ if (p2)
++ *p2 ='"';
++#endif
++ /* Found a non-supported type, give up and move on. */
++ skip_lv = 1;
++ break;
++ }
++
++ seg++;
++
++ continue;
++ lvs_segment_fail2:
++ grub_free (seg->nodes);
++ lvs_segment_fail:
++ goto fail4;
++ }
++
++ if (p != NULL)
++ p = grub_strchr (p, '}');
++ if (p == NULL)
++ goto lvs_fail;
++ p += 3;
++
++ if (skip_lv)
++ {
++ grub_free (lv->name);
++ grub_free (lv);
++ continue;
++ }
++
++ lv->number = lv_count++;
++ lv->vg = vg;
++ lv->next = vg->lvs;
++ vg->lvs = lv;
++
++ continue;
++ lvs_fail:
++ grub_free (lv->name);
++ grub_free (lv);
++ goto fail4;
++ }
++ }
++
++ /* Match lvs. */
++ {
++ struct grub_lvm_lv *lv1;
++ struct grub_lvm_lv *lv2;
++ for (lv1 = vg->lvs; lv1; lv1 = lv1->next)
++ for (i = 0; i < lv1->segment_count; i++)
++ for (j = 0; j < lv1->segments[i].node_count; j++)
++ {
++ if (vg->pvs)
++ for (pv = vg->pvs; pv; pv = pv->next)
++ {
++ if (! grub_strcmp (pv->name,
++ lv1->segments[i].nodes[j].name))
++ {
++ lv1->segments[i].nodes[j].pv = pv;
++ break;
++ }
++ }
++ if (lv1->segments[i].nodes[j].pv == NULL)
++ for (lv2 = vg->lvs; lv2; lv2 = lv2->next)
++ if (grub_strcmp (lv2->name + grub_strlen (vg->name) + 1,
++ lv1->segments[i].nodes[j].name) == 0)
++ lv1->segments[i].nodes[j].lv = lv2;
++ }
++
++ }
++
++ vg->next = vg_list;
++ vg_list = vg;
++ }
++ else
++ {
++ grub_free (vgname);
++ }
++
++ /* Match the device we are currently reading from with the right
++ PV. */
++ if (vg->pvs)
++ for (pv = vg->pvs; pv; pv = pv->next)
++ {
++ if (! grub_memcmp (pv->id, pv_id, GRUB_LVM_ID_STRLEN))
++ {
++ /* This could happen to LVM on RAID, pv->disk points to the
++ raid device, we shouldn't change it. */
++ if (! pv->disk)
++ pv->disk = grub_disk_open (name);
++ break;
++ }
++ }
++
++ goto fail2;
++
++ /* Failure path. */
++ fail4:
++ grub_free (vg);
++ fail3:
++ grub_free (vgname);
++
++ /* Normal exit path. */
++ fail2:
++ grub_free (metadatabuf);
++ fail:
++ grub_disk_close (disk);
++ 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;
++ }
++
++ scan_depth++;
++ grub_device_iterate (&grub_lvm_scan_device);
++ scan_depth--;
++}
++
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;
- {
- scan_depth++;
- grub_device_iterate (&grub_lvm_scan_device);
- scan_depth--;
- }
+ 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)
++ do_lvm_scan (NULL);
+ }
+ 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;
static grub_err_t
grub_lvm_open (const char *name, grub_disk_t disk)
{
- struct grub_lvm_vg *vg;
struct grub_lvm_lv *lv = NULL;
- for (vg = vg_list; vg; vg = vg->next)
- {
- if (vg->lvs)
- for (lv = vg->lvs; lv; lv = lv->next)
- if (! grub_strcmp (lv->name, name))
- break;
+ int explicit = 0;
- if (lv)
- break;
+ if (grub_memcmp (name, "lvm/", sizeof ("lvm/") - 1) == 0)
+ explicit = 1;
+
+ lv = find_lv (name);
+
+ if (! lv && !scan_depth && explicit)
+ {
- scan_for = name;
- scan_depth++;
- grub_device_iterate (&grub_lvm_scan_device);
- scan_depth--;
- scan_for = NULL;
++ do_lvm_scan (name);
+ if (grub_errno)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+ }
+ lv = find_lv (name);
}
if (! lv)
return GRUB_ERR_NOT_IMPLEMENTED_YET;
}
--static int
--grub_lvm_scan_device (const char *name)
--{
-- grub_err_t err;
-- grub_disk_t disk;
-- grub_uint64_t mda_offset, mda_size;
-- char buf[GRUB_LVM_LABEL_SIZE];
-- char vg_id[GRUB_LVM_ID_STRLEN+1];
-- char pv_id[GRUB_LVM_ID_STRLEN+1];
-- char *metadatabuf, *p, *q, *vgname;
-- struct grub_lvm_label_header *lh = (struct grub_lvm_label_header *) buf;
-- struct grub_lvm_pv_header *pvh;
-- struct grub_lvm_disk_locn *dlocn;
-- struct grub_lvm_mda_header *mdah;
-- struct grub_lvm_raw_locn *rlocn;
-- unsigned int i, j, vgname_len;
-- struct grub_lvm_vg *vg;
-- struct grub_lvm_pv *pv;
--
--#ifdef GRUB_UTIL
-- grub_util_info ("scanning %s for LVM", name);
--#endif
--
-- disk = grub_disk_open (name);
-- if (!disk)
-- {
-- if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
-- grub_errno = GRUB_ERR_NONE;
-- 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++)
-- {
-- err = grub_disk_read (disk, i, 0, sizeof(buf), buf);
-- if (err)
-- goto fail;
--
-- if ((! grub_strncmp ((char *)lh->id, GRUB_LVM_LABEL_ID,
-- sizeof (lh->id)))
-- && (! grub_strncmp ((char *)lh->type, GRUB_LVM_LVM2_LABEL,
-- sizeof (lh->type))))
-- break;
-- }
--
-- /* Return if we didn't find a label. */
-- if (i == GRUB_LVM_LABEL_SCAN_SECTORS)
-- {
--#ifdef GRUB_UTIL
-- grub_util_info ("no LVM signature found");
--#endif
-- goto fail;
-- }
--
-- pvh = (struct grub_lvm_pv_header *) (buf + grub_le_to_cpu32(lh->offset_xl));
--
-- for (i = 0, j = 0; i < GRUB_LVM_ID_LEN; i++)
-- {
-- pv_id[j++] = pvh->pv_uuid[i];
-- if ((i != 1) && (i != 29) && (i % 4 == 1))
-- pv_id[j++] = '-';
-- }
-- pv_id[j] = '\0';
--
-- dlocn = pvh->disk_areas_xl;
--
-- dlocn++;
-- /* Is it possible to have multiple data/metadata areas? I haven't
-- seen devices that have it. */
-- if (dlocn->offset)
-- {
-- grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-- "we don't support multiple LVM data areas");
--
--#ifdef GRUB_UTIL
-- grub_util_info ("we don't support multiple LVM data areas\n");
--#endif
-- goto fail;
-- }
--
-- dlocn++;
-- mda_offset = grub_le_to_cpu64 (dlocn->offset);
-- mda_size = grub_le_to_cpu64 (dlocn->size);
--
-- /* It's possible to have multiple copies of metadata areas, we just use the
-- first one. */
--
-- /* Allocate buffer space for the circular worst-case scenario. */
-- metadatabuf = grub_malloc (2 * mda_size);
-- if (! metadatabuf)
-- goto fail;
--
-- err = grub_disk_read (disk, 0, mda_offset, mda_size, metadatabuf);
-- if (err)
-- goto fail2;
--
-- mdah = (struct grub_lvm_mda_header *) metadatabuf;
-- if ((grub_strncmp ((char *)mdah->magic, GRUB_LVM_FMTT_MAGIC,
-- sizeof (mdah->magic)))
-- || (grub_le_to_cpu32 (mdah->version) != GRUB_LVM_FMTT_VERSION))
-- {
-- grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-- "unknown LVM metadata header");
--#ifdef GRUB_UTIL
-- grub_util_info ("unknown LVM metadata header\n");
--#endif
-- goto fail2;
-- }
--
-- rlocn = mdah->raw_locns;
-- if (grub_le_to_cpu64 (rlocn->offset) + grub_le_to_cpu64 (rlocn->size) >
-- grub_le_to_cpu64 (mdah->size))
-- {
-- /* Metadata is circular. Copy the wrap in place. */
-- grub_memcpy (metadatabuf + mda_size,
-- metadatabuf + GRUB_LVM_MDA_HEADER_SIZE,
-- grub_le_to_cpu64 (rlocn->offset) +
-- grub_le_to_cpu64 (rlocn->size) -
-- grub_le_to_cpu64 (mdah->size));
-- }
-- p = q = metadatabuf + grub_le_to_cpu64 (rlocn->offset);
--
-- while (*q != ' ' && q < metadatabuf + mda_size)
-- q++;
--
-- if (q == metadatabuf + mda_size)
-- {
--#ifdef GRUB_UTIL
-- grub_util_info ("error parsing metadata\n");
--#endif
-- goto fail2;
-- }
--
-- vgname_len = q - p;
-- vgname = grub_malloc (vgname_len + 1);
-- if (!vgname)
-- goto fail2;
--
-- grub_memcpy (vgname, p, vgname_len);
-- vgname[vgname_len] = '\0';
--
-- p = grub_strstr (q, "id = \"");
-- if (p == NULL)
-- {
--#ifdef GRUB_UTIL
-- grub_util_info ("couldn't find ID\n");
--#endif
-- goto fail3;
-- }
-- p += sizeof ("id = \"") - 1;
-- grub_memcpy (vg_id, p, GRUB_LVM_ID_STRLEN);
-- vg_id[GRUB_LVM_ID_STRLEN] = '\0';
--
-- for (vg = vg_list; vg; vg = vg->next)
-- {
-- if (! grub_memcmp(vg_id, vg->id, GRUB_LVM_ID_STRLEN))
-- break;
-- }
--
-- if (! vg)
-- {
-- /* First time we see this volume group. We've to create the
-- whole volume group structure. */
-- vg = grub_malloc (sizeof (*vg));
-- if (! vg)
-- goto fail3;
-- vg->name = vgname;
-- grub_memcpy (vg->id, vg_id, GRUB_LVM_ID_STRLEN+1);
--
-- vg->extent_size = grub_lvm_getvalue (&p, "extent_size = ");
-- if (p == NULL)
-- {
--#ifdef GRUB_UTIL
-- grub_util_info ("unknown extent size\n");
--#endif
-- goto fail4;
-- }
--
-- vg->lvs = NULL;
-- vg->pvs = NULL;
--
-- p = grub_strstr (p, "physical_volumes {");
-- if (p)
-- {
-- p += sizeof ("physical_volumes {") - 1;
--
-- /* Add all the pvs to the volume group. */
-- while (1)
-- {
-- int s;
-- while (grub_isspace (*p))
-- p++;
--
-- if (*p == '}')
-- break;
--
-- pv = grub_malloc (sizeof (*pv));
-- q = p;
-- while (*q != ' ')
-- q++;
--
-- s = q - p;
-- pv->name = grub_malloc (s + 1);
-- grub_memcpy (pv->name, p, s);
-- pv->name[s] = '\0';
--
-- p = grub_strstr (p, "id = \"");
-- if (p == NULL)
-- goto pvs_fail;
-- p += sizeof("id = \"") - 1;
--
-- grub_memcpy (pv->id, p, GRUB_LVM_ID_STRLEN);
-- pv->id[GRUB_LVM_ID_STRLEN] = '\0';
--
-- pv->start = grub_lvm_getvalue (&p, "pe_start = ");
-- if (p == NULL)
-- {
--#ifdef GRUB_UTIL
-- grub_util_info ("unknown pe_start\n");
--#endif
-- goto pvs_fail;
-- }
--
-- p = grub_strchr (p, '}');
-- if (p == NULL)
-- {
--#ifdef GRUB_UTIL
-- grub_util_info ("error parsing pe_start\n");
--#endif
-- goto pvs_fail;
-- }
-- p++;
--
-- pv->disk = NULL;
-- pv->next = vg->pvs;
-- vg->pvs = pv;
--
-- continue;
-- pvs_fail:
-- grub_free (pv->name);
-- grub_free (pv);
-- goto fail4;
-- }
-- }
--
-- p = grub_strstr (p, "logical_volumes");
-- if (p)
-- {
-- p += 18;
--
-- /* And add all the lvs to the volume group. */
-- while (1)
-- {
-- int s;
-- int skip_lv = 0;
-- struct grub_lvm_lv *lv;
-- struct grub_lvm_segment *seg;
-- int is_pvmove;
--
-- while (grub_isspace (*p))
-- p++;
--
-- if (*p == '}')
-- break;
--
-- lv = grub_malloc (sizeof (*lv));
--
-- q = p;
-- while (*q != ' ')
-- q++;
--
-- s = q - p;
- lv->name = grub_malloc (vgname_len + 1 + s + 1);
- grub_memcpy (lv->name, vgname, vgname_len);
- lv->name[vgname_len] = '-';
- grub_memcpy (lv->name + vgname_len + 1, p, s);
- lv->name[vgname_len + 1 + s] = '\0';
- lv->name = grub_strndup (p, s);
- if (!lv->name)
- goto lvs_fail;
- lv->compatname = grub_malloc (vgname_len + 1 + s + 1);
- if (!lv->compatname)
- goto lvs_fail;
- grub_memcpy (lv->compatname, vgname, vgname_len);
- lv->compatname[vgname_len] = '-';
- grub_memcpy (lv->compatname + vgname_len + 1, p, s);
- lv->compatname[vgname_len + 1 + s] = '\0';
-
- {
- const char *iptr;
- char *optr;
- lv->fullname = grub_malloc (sizeof("lvm/") + 2 * vgname_len
- + 1 + 2 * s + 1);
- if (!lv->fullname)
- goto lvs_fail;
-
- optr = lv->fullname;
- grub_memcpy (optr, "lvm/", sizeof ("lvm/") - 1);
- optr += sizeof ("lvm/") - 1;
- for (iptr = vgname; iptr < vgname + vgname_len; iptr++)
- {
- *optr++ = *iptr;
- if (*iptr == '-')
- *optr++ = '-';
- }
- *optr++ = '-';
- for (iptr = p; iptr < p + s; iptr++)
- {
- *optr++ = *iptr;
- if (*iptr == '-')
- *optr++ = '-';
- }
- *optr++ = 0;
- }
--
-- lv->size = 0;
--
-- lv->visible = grub_lvm_check_flag (p, "status", "VISIBLE");
-- is_pvmove = grub_lvm_check_flag (p, "status", "PVMOVE");
--
-- lv->segment_count = grub_lvm_getvalue (&p, "segment_count = ");
-- if (p == NULL)
-- {
--#ifdef GRUB_UTIL
-- grub_util_info ("unknown segment_count\n");
--#endif
-- goto lvs_fail;
-- }
-- lv->segments = grub_malloc (sizeof (*seg) * lv->segment_count);
-- seg = lv->segments;
--
-- for (i = 0; i < lv->segment_count; i++)
-- {
--
-- p = grub_strstr (p, "segment");
-- if (p == NULL)
-- {
--#ifdef GRUB_UTIL
-- grub_util_info ("unknown segment\n");
--#endif
-- goto lvs_segment_fail;
-- }
--
-- seg->start_extent = grub_lvm_getvalue (&p, "start_extent = ");
-- if (p == NULL)
-- {
--#ifdef GRUB_UTIL
-- grub_util_info ("unknown start_extent\n");
--#endif
-- goto lvs_segment_fail;
-- }
-- seg->extent_count = grub_lvm_getvalue (&p, "extent_count = ");
-- if (p == NULL)
-- {
--#ifdef GRUB_UTIL
-- grub_util_info ("unknown extent_count\n");
--#endif
-- goto lvs_segment_fail;
-- }
--
-- p = grub_strstr (p, "type = \"");
-- if (p == NULL)
-- goto lvs_segment_fail;
-- p += sizeof("type = \"") - 1;
--
-- lv->size += seg->extent_count * vg->extent_size;
--
-- if (grub_memcmp (p, "striped\"",
-- sizeof ("striped\"") - 1) == 0)
-- {
-- struct grub_lvm_node *stripe;
--
-- seg->type = GRUB_LVM_STRIPED;
-- seg->node_count = grub_lvm_getvalue (&p, "stripe_count = ");
-- if (p == NULL)
-- {
--#ifdef GRUB_UTIL
-- grub_util_info ("unknown stripe_count\n");
--#endif
-- goto lvs_segment_fail;
-- }
--
-- if (seg->node_count != 1)
-- seg->stripe_size = grub_lvm_getvalue (&p, "stripe_size = ");
--
-- seg->nodes = grub_zalloc (sizeof (*stripe)
-- * seg->node_count);
-- stripe = seg->nodes;
--
-- p = grub_strstr (p, "stripes = [");
-- if (p == NULL)
-- {
--#ifdef GRUB_UTIL
-- grub_util_info ("unknown stripes\n");
--#endif
-- goto lvs_segment_fail2;
-- }
-- p += sizeof("stripes = [") - 1;
--
-- for (j = 0; j < seg->node_count; j++)
-- {
-- p = grub_strchr (p, '"');
-- if (p == NULL)
-- continue;
-- q = ++p;
-- while (*q != '"')
-- q++;
--
-- s = q - p;
--
-- stripe->name = grub_malloc (s + 1);
-- if (stripe->name == NULL)
-- goto lvs_segment_fail2;
--
-- grub_memcpy (stripe->name, p, s);
-- stripe->name[s] = '\0';
--
-- stripe->start = grub_lvm_getvalue (&p, ",");
-- if (p == NULL)
-- continue;
--
-- stripe++;
-- }
-- }
-- else if (grub_memcmp (p, "mirror\"", sizeof ("mirror\"") - 1)
-- == 0)
-- {
-- seg->type = GRUB_LVM_MIRROR;
-- seg->node_count = grub_lvm_getvalue (&p, "mirror_count = ");
-- if (p == NULL)
-- {
--#ifdef GRUB_UTIL
-- grub_util_info ("unknown mirror_count\n");
--#endif
-- goto lvs_segment_fail;
-- }
--
-- seg->nodes = grub_zalloc (sizeof (seg->nodes[0])
-- * seg->node_count);
--
-- p = grub_strstr (p, "mirrors = [");
-- if (p == NULL)
-- {
--#ifdef GRUB_UTIL
-- grub_util_info ("unknown mirrors\n");
--#endif
-- goto lvs_segment_fail2;
-- }
-- p += sizeof("mirrors = [") - 1;
--
-- for (j = 0; j < seg->node_count; j++)
-- {
-- char *lvname;
--
-- p = grub_strchr (p, '"');
-- if (p == NULL)
-- continue;
-- q = ++p;
-- while (*q != '"')
-- q++;
--
-- s = q - p;
--
-- lvname = grub_malloc (s + 1);
-- if (lvname == NULL)
-- goto lvs_segment_fail2;
--
-- grub_memcpy (lvname, p, s);
-- lvname[s] = '\0';
-- seg->nodes[j].name = lvname;
-- p = q + 1;
-- }
-- /* Only first (original) is ok with in progress pvmove. */
-- if (is_pvmove)
-- seg->node_count = 1;
-- }
-- else
-- {
--#ifdef GRUB_UTIL
-- char *p2;
-- p2 = grub_strchr (p, '"');
-- if (p2)
-- *p2 = 0;
-- grub_util_info ("unknown LVM type %s\n", p);
-- if (p2)
-- *p2 ='"';
--#endif
-- /* Found a non-supported type, give up and move on. */
-- skip_lv = 1;
-- break;
-- }
--
-- seg++;
--
-- continue;
-- lvs_segment_fail2:
-- grub_free (seg->nodes);
-- lvs_segment_fail:
-- goto fail4;
-- }
--
-- if (p != NULL)
-- p = grub_strchr (p, '}');
-- if (p == NULL)
-- goto lvs_fail;
-- p += 3;
--
-- if (skip_lv)
-- {
-- grub_free (lv->name);
-- grub_free (lv);
-- continue;
-- }
--
-- lv->number = lv_count++;
-- lv->vg = vg;
-- lv->next = vg->lvs;
-- vg->lvs = lv;
--
-- continue;
-- lvs_fail:
-- grub_free (lv->name);
-- grub_free (lv);
-- goto fail4;
-- }
-- }
--
-- /* Match lvs. */
-- {
-- struct grub_lvm_lv *lv1;
-- struct grub_lvm_lv *lv2;
-- for (lv1 = vg->lvs; lv1; lv1 = lv1->next)
-- for (i = 0; i < lv1->segment_count; i++)
-- for (j = 0; j < lv1->segments[i].node_count; j++)
-- {
-- if (vg->pvs)
-- for (pv = vg->pvs; pv; pv = pv->next)
-- {
-- if (! grub_strcmp (pv->name,
-- lv1->segments[i].nodes[j].name))
-- {
-- lv1->segments[i].nodes[j].pv = pv;
-- break;
-- }
-- }
-- if (lv1->segments[i].nodes[j].pv == NULL)
-- for (lv2 = vg->lvs; lv2; lv2 = lv2->next)
-- if (grub_strcmp (lv2->name + grub_strlen (vg->name) + 1,
-- lv1->segments[i].nodes[j].name) == 0)
-- lv1->segments[i].nodes[j].lv = lv2;
-- }
--
-- }
--
-- vg->next = vg_list;
-- vg_list = vg;
-- }
-- else
-- {
-- grub_free (vgname);
-- }
--
-- /* Match the device we are currently reading from with the right
-- PV. */
-- if (vg->pvs)
-- for (pv = vg->pvs; pv; pv = pv->next)
-- {
-- if (! grub_memcmp (pv->id, pv_id, GRUB_LVM_ID_STRLEN))
-- {
-- /* This could happen to LVM on RAID, pv->disk points to the
-- raid device, we shouldn't change it. */
-- if (! pv->disk)
-- pv->disk = grub_disk_open (name);
-- break;
-- }
-- }
--
-- goto fail2;
--
-- /* Failure path. */
-- fail4:
-- grub_free (vg);
-- fail3:
-- grub_free (vgname);
--
-- /* Normal exit path. */
-- fail2:
-- grub_free (metadatabuf);
-- fail:
-- grub_disk_close (disk);
-- 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;
--}
--
static struct grub_disk_dev grub_lvm_dev =
{
.name = "lvm",