Example #1
0
static struct grub_diskfilter_vg * 
grub_lvm_detect (grub_disk_t disk,
		 struct grub_diskfilter_pv_id *id,
		 grub_disk_addr_t *start_sector)
{
  grub_err_t err;
  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_diskfilter_vg *vg;
  struct grub_diskfilter_pv *pv;

  /* 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';

  vg = grub_diskfilter_get_vg_by_uuid (GRUB_LVM_ID_STRLEN, vg_id);

  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;
      vg->uuid = grub_malloc (GRUB_LVM_ID_STRLEN);
      if (! vg->uuid)
	goto fail3;
      grub_memcpy (vg->uuid, vg_id, GRUB_LVM_ID_STRLEN);
      vg->uuid_len = GRUB_LVM_ID_STRLEN;

      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_zalloc (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;

	      pv->id.uuid = grub_malloc (GRUB_LVM_ID_STRLEN);
	      if (!pv->id.uuid)
		goto pvs_fail;
	      grub_memcpy (pv->id.uuid, p, GRUB_LVM_ID_STRLEN);
	      pv->id.uuidlen = GRUB_LVM_ID_STRLEN;

	      pv->start_sector = 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 += sizeof ("logical_volumes = ") - 1;

	  /* And add all the lvs to the volume group. */
	  while (1)
	    {
	      int s;
	      int skip_lv = 0;
	      struct grub_diskfilter_lv *lv;
	      struct grub_diskfilter_segment *seg;
	      int is_pvmove;

	      while (grub_isspace (*p))
		p++;

	      if (*p == '}')
		break;

	      lv = grub_zalloc (sizeof (*lv));

	      q = p;
	      while (*q != ' ')
		q++;

	      s = q - p;
	      lv->name = grub_strndup (p, s);
	      if (!lv->name)
		goto lvs_fail;

	      {
		const char *iptr;
		char *optr;
		lv->fullname = grub_malloc (sizeof ("lvm/") - 1 + 2 * vgname_len
					    + 1 + 2 * s + 1);
		if (!lv->fullname)
		  goto lvs_fail;

		grub_memcpy (lv->fullname, "lvm/", sizeof ("lvm/") - 1);
		optr = lv->fullname + 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->idname = grub_malloc (sizeof ("lvmid/")
					  + 2 * GRUB_LVM_ID_STRLEN + 1);
		if (!lv->idname)
		  goto lvs_fail;
		grub_memcpy (lv->idname, "lvmid/",
			     sizeof ("lvmid/") - 1);
		grub_memcpy (lv->idname + sizeof ("lvmid/") - 1,
			     vg_id, GRUB_LVM_ID_STRLEN);
		lv->idname[sizeof ("lvmid/") - 1 + GRUB_LVM_ID_STRLEN] = '/';

		p = grub_strstr (q, "id = \"");
		if (p == NULL)
		  {
#ifdef GRUB_UTIL
		    grub_util_info ("couldn't find ID\n");
#endif
		    goto lvs_fail;
		  }
		p += sizeof ("id = \"") - 1;
		grub_memcpy (lv->idname + sizeof ("lvmid/") - 1
			     + GRUB_LVM_ID_STRLEN + 1,
			     p, GRUB_LVM_ID_STRLEN);
		lv->idname[sizeof ("lvmid/") - 1 + 2 * GRUB_LVM_ID_STRLEN + 1] = '\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_diskfilter_node *stripe;

		      seg->type = GRUB_DISKFILTER_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';

			  p = q + 1;

			  stripe->start = grub_lvm_getvalue (&p, ",")
			    * vg->extent_size;
			  if (p == NULL)
			    continue;

			  stripe++;
			}
		    }
		  else if (grub_memcmp (p, "mirror\"", sizeof ("mirror\"") - 1)
			   == 0)
		    {
		      seg->type = GRUB_DISKFILTER_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 if (grub_memcmp (p, "raid", sizeof ("raid") - 1)
			   == 0 && (p[sizeof ("raid") - 1] >= '4'
				    && p[sizeof ("raid") - 1] <= '6')
			   && p[sizeof ("raidX") - 1] == '"')
		    {
		      switch (p[sizeof ("raid") - 1])
			{
			case '4':
			  seg->type = GRUB_DISKFILTER_RAID4;
			  seg->layout = GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC;
			  break;
			case '5':
			  seg->type = GRUB_DISKFILTER_RAID5;
			  seg->layout = GRUB_RAID_LAYOUT_LEFT_SYMMETRIC;
			  break;
			case '6':
			  seg->type = GRUB_DISKFILTER_RAID6;
			  seg->layout = (GRUB_RAID_LAYOUT_RIGHT_ASYMMETRIC
					 | GRUB_RAID_LAYOUT_MUL_FROM_POS);
			  break;
			}
		      seg->node_count = grub_lvm_getvalue (&p, "device_count = ");

		      if (p == NULL)
			{
#ifdef GRUB_UTIL
			  grub_util_info ("unknown device_count\n");
#endif
			  goto lvs_segment_fail;
			}

		      seg->stripe_size = grub_lvm_getvalue (&p, "stripe_size = ");
		      if (p == NULL)
			{
#ifdef GRUB_UTIL
			  grub_util_info ("unknown stripe_size\n");
#endif
			  goto lvs_segment_fail;
			}


		      seg->nodes = grub_zalloc (sizeof (seg->nodes[0])
						* seg->node_count);

		      p = grub_strstr (p, "raids = [");
		      if (p == NULL)
			{
#ifdef GRUB_UTIL
			  grub_util_info ("unknown mirrors\n");
#endif
			  goto lvs_segment_fail2;
			}
		      p += sizeof("raids = [") - 1;

		      for (j = 0; j < seg->node_count; j++)
			{
			  char *lvname;

			  p = grub_strchr (p, '"');
			  p = p ? grub_strchr (p + 1, '"') : 0;
			  p = p ? grub_strchr (p + 1, '"') : 0;
			  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;
			}
		      if (seg->type == GRUB_DISKFILTER_RAID4)
			{
			  char *tmp;
			  tmp = seg->nodes[0].name;
			  grub_memmove (seg->nodes, seg->nodes + 1,
					sizeof (seg->nodes[0])
					* (seg->node_count - 1));
			  seg->nodes[seg->node_count - 1].name = tmp;
			}
		    }
		  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->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_diskfilter_lv *lv1;
	struct grub_diskfilter_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,
				     lv1->segments[i].nodes[j].name) == 0)
		      lv1->segments[i].nodes[j].lv = lv2;
	      }
	
      }
      if (grub_diskfilter_vg_register (vg))
	goto fail4;
    }
  else
    {
      grub_free (vgname);
    }

  id->uuid = grub_malloc (GRUB_LVM_ID_STRLEN);
  if (!id->uuid)
    goto fail4;
  grub_memcpy (id->uuid, pv_id, GRUB_LVM_ID_STRLEN);
  id->uuidlen = GRUB_LVM_ID_STRLEN;
  grub_free (metadatabuf);
  *start_sector = -1;
  return vg;

  /* Failure path.  */
 fail4:
  grub_free (vg);
 fail3:
  grub_free (vgname);

 fail2:
  grub_free (metadatabuf);
 fail:
  return NULL;
}
Example #2
0
File: ldm.c Project: flihp/grub2
static struct grub_diskfilter_vg *
make_vg (grub_disk_t disk,
	 const struct grub_ldm_label *label)
{
  grub_disk_addr_t startsec, endsec, cursec;
  struct grub_diskfilter_vg *vg;
  grub_err_t err;

  /* First time we see this volume group. We've to create the
     whole volume group structure. */
  vg = grub_malloc (sizeof (*vg));
  if (! vg)
    return NULL;
  vg->extent_size = 1;
  vg->name = grub_malloc (LDM_NAME_STRLEN + 1);
  vg->uuid = grub_malloc (LDM_GUID_STRLEN + 1);
  if (! vg->uuid || !vg->name)
    {
      grub_free (vg->uuid);
      grub_free (vg->name);
      return NULL;
    }
  grub_memcpy (vg->uuid, label->group_guid, LDM_GUID_STRLEN);
  grub_memcpy (vg->name, label->group_name, LDM_NAME_STRLEN);
  vg->name[LDM_NAME_STRLEN] = 0;
  vg->uuid[LDM_GUID_STRLEN] = 0;
  vg->uuid_len = grub_strlen (vg->uuid);

  vg->lvs = NULL;
  vg->pvs = NULL;

  startsec = grub_be_to_cpu64 (label->config_start);
  endsec = startsec + grub_be_to_cpu64 (label->config_size);

  /* First find disks.  */
  for (cursec = startsec + 0x12; cursec < endsec; cursec++)
    {
      struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE
				/ sizeof (struct grub_ldm_vblk)];
      unsigned i;
      err = grub_disk_read (disk, cursec, 0,
			    sizeof(vblk), &vblk);
      if (err)
	goto fail2;

      for (i = 0; i < ARRAY_SIZE (vblk); i++)
	{
	  struct grub_diskfilter_pv *pv;
	  grub_uint8_t *ptr;
	  if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC,
			   sizeof (vblk[i].magic)) != 0)
	    continue;
	  if (grub_be_to_cpu16 (vblk[i].update_status)
	      != STATUS_CONSISTENT
	      && grub_be_to_cpu16 (vblk[i].update_status)
	      != STATUS_STILL_ACTIVE)
	    continue;
	  if (vblk[i].type != ENTRY_DISK)
	    continue;
	  pv = grub_zalloc (sizeof (*pv));
	  if (!pv)
	    goto fail2;

	  pv->disk = 0;
	  ptr = vblk[i].dynamic;
	  if (ptr + *ptr + 1 >= vblk[i].dynamic
	      + sizeof (vblk[i].dynamic))
	    {
	      grub_free (pv);
	      goto fail2;
	    }
	  pv->internal_id = grub_malloc (ptr[0] + 2);
	  if (!pv->internal_id)
	    {
	      grub_free (pv);
	      goto fail2;
	    }
	  grub_memcpy (pv->internal_id, ptr, (grub_size_t) ptr[0] + 1);
	  pv->internal_id[(grub_size_t) ptr[0] + 1] = 0;
	  
	  ptr += *ptr + 1;
	  if (ptr + *ptr + 1 >= vblk[i].dynamic
	      + sizeof (vblk[i].dynamic))
	    {
	      grub_free (pv);
	      goto fail2;
	    }
	  /* ptr = name.  */
	  ptr += *ptr + 1;
	  if (ptr + *ptr + 1
	      >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      grub_free (pv);
	      goto fail2;
	    }
	  pv->id.uuidlen = *ptr;
	  pv->id.uuid = grub_malloc (pv->id.uuidlen + 1);
	  grub_memcpy (pv->id.uuid, ptr + 1, pv->id.uuidlen);
	  pv->id.uuid[pv->id.uuidlen] = 0;

	  pv->next = vg->pvs;
	  vg->pvs = pv;
	}
    }

  /* Then find LVs.  */
  for (cursec = startsec + 0x12; cursec < endsec; cursec++)
    {
      struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE
				/ sizeof (struct grub_ldm_vblk)];
      unsigned i;
      err = grub_disk_read (disk, cursec, 0,
			    sizeof(vblk), &vblk);
      if (err)
	goto fail2;

      for (i = 0; i < ARRAY_SIZE (vblk); i++)
	{
	  struct grub_diskfilter_lv *lv;
	  grub_uint8_t *ptr;
	  if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC,
			   sizeof (vblk[i].magic)) != 0)
	    continue;
	  if (grub_be_to_cpu16 (vblk[i].update_status)
	      != STATUS_CONSISTENT
	      && grub_be_to_cpu16 (vblk[i].update_status)
	      != STATUS_STILL_ACTIVE)
	    continue;
	  if (vblk[i].type != ENTRY_VOLUME)
	    continue;
	  lv = grub_zalloc (sizeof (*lv));
	  if (!lv)
	    goto fail2;

	  lv->vg = vg;
	  lv->segment_count = 1;
	  lv->segment_alloc = 1;
	  lv->visible = 1;
	  lv->segments = grub_zalloc (sizeof (*lv->segments));
	  if (!lv->segments)
	    goto fail2;
	  lv->segments->start_extent = 0;
	  lv->segments->type = GRUB_DISKFILTER_MIRROR;
	  lv->segments->node_count = 0;
	  lv->segments->node_alloc = 8;
	  lv->segments->nodes = grub_zalloc (sizeof (*lv->segments->nodes)
					     * lv->segments->node_alloc);
	  if (!lv->segments->nodes)
	    goto fail2;
	  ptr = vblk[i].dynamic;
	  if (ptr + *ptr + 1 >= vblk[i].dynamic
	      + sizeof (vblk[i].dynamic))
	    {
	      grub_free (lv);
	      goto fail2;
	    }
	  lv->internal_id = grub_malloc ((grub_size_t) ptr[0] + 2);
	  if (!lv->internal_id)
	    {
	      grub_free (lv);
	      goto fail2;
	    }
	  grub_memcpy (lv->internal_id, ptr, ptr[0] + 1);
	  lv->internal_id[ptr[0] + 1] = 0;

	  ptr += *ptr + 1;
	  if (ptr + *ptr + 1 >= vblk[i].dynamic
	      + sizeof (vblk[i].dynamic))
	    {
	      grub_free (lv);
	      goto fail2;
	    }
	  lv->name = grub_malloc (*ptr + 1);
	  if (!lv->name)
	    {
	      grub_free (lv->internal_id);
	      grub_free (lv);
	      goto fail2;
	    }
	  grub_memcpy (lv->name, ptr + 1, *ptr);
	  lv->name[*ptr] = 0;
	  lv->fullname = grub_xasprintf ("ldm/%s/%s",
					 vg->uuid, lv->name);
	  if (!lv->fullname)
	    {
	      grub_free (lv->internal_id);
	      grub_free (lv->name);
	      grub_free (lv);
	      goto fail2;
	    }
	  ptr += *ptr + 1;
	  if (ptr + *ptr + 1
	      >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      grub_free (lv->internal_id);
	      grub_free (lv->name);
	      grub_free (lv);
	      goto fail2;
	    }
	  /* ptr = volume type.  */
	  ptr += *ptr + 1;
	  if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      grub_free (lv->internal_id);
	      grub_free (lv->name);
	      grub_free (lv);
	      goto fail2;
	    }
	  /* ptr = flags.  */
	  ptr += *ptr + 1;
	  if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      grub_free (lv->internal_id);
	      grub_free (lv->name);
	      grub_free (lv);
	      goto fail2;
	    }

	  /* Skip state, type, unknown, volume number, zeros, flags. */
	  ptr += 14 + 1 + 1 + 1 + 3 + 1;
	  /* ptr = number of children.  */
	  if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      grub_free (lv->internal_id);
	      grub_free (lv->name);
	      grub_free (lv);
	      goto fail2;
	    }
	  ptr += *ptr + 1;
	  if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      grub_free (lv->internal_id);
	      grub_free (lv->name);
	      grub_free (lv);
	      goto fail2;
	    }

	  /* Skip 2 more fields.  */
	  ptr += 8 + 8;
	  if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)
	      || ptr + *ptr + 1>= vblk[i].dynamic
	      + sizeof (vblk[i].dynamic))
	    {
	      grub_free (lv->internal_id);
	      grub_free (lv->name);
	      grub_free (lv);
	      goto fail2;
	    }
	  lv->size = read_int (ptr + 1, *ptr);
	  lv->segments->extent_count = lv->size;

	  lv->next = vg->lvs;
	  vg->lvs = lv;
	}
    }

  /* Now the components.  */
  for (cursec = startsec + 0x12; cursec < endsec; cursec++)
    {
      struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE
				/ sizeof (struct grub_ldm_vblk)];
      unsigned i;
      err = grub_disk_read (disk, cursec, 0,
			    sizeof(vblk), &vblk);
      if (err)
	goto fail2;

      for (i = 0; i < ARRAY_SIZE (vblk); i++)
	{
	  struct grub_diskfilter_lv *comp;
	  struct grub_diskfilter_lv *lv;
	  grub_uint8_t type;

	  grub_uint8_t *ptr;
	  if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC,
			   sizeof (vblk[i].magic)) != 0)
	    continue;
	  if (grub_be_to_cpu16 (vblk[i].update_status)
	      != STATUS_CONSISTENT
	      && grub_be_to_cpu16 (vblk[i].update_status)
	      != STATUS_STILL_ACTIVE)
	    continue;
	  if (vblk[i].type != ENTRY_COMPONENT)
	    continue;
	  comp = grub_zalloc (sizeof (*comp));
	  if (!comp)
	    goto fail2;
	  comp->visible = 0;
	  comp->name = 0;
	  comp->fullname = 0;

	  ptr = vblk[i].dynamic;
	  if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      goto fail2;
	    }
	  comp->internal_id = grub_malloc ((grub_size_t) ptr[0] + 2);
	  if (!comp->internal_id)
	    {
	      grub_free (comp);
	      goto fail2;
	    }
	  grub_memcpy (comp->internal_id, ptr, ptr[0] + 1);
	  comp->internal_id[ptr[0] + 1] = 0;

	  ptr += *ptr + 1;
	  if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      grub_free (comp->internal_id);
	      grub_free (comp);
	      goto fail2;
	    }
	  /* ptr = name.  */
	  ptr += *ptr + 1;
	  if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      grub_free (comp->internal_id);
	      grub_free (comp);
	      goto fail2;
	    }
	  /* ptr = state.  */
	  ptr += *ptr + 1;
	  type = *ptr++;
	  /* skip zeros.  */
	  ptr += 4;
	  if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      grub_free (comp->internal_id);
	      grub_free (comp);
	      goto fail2;
	    }

	  /* ptr = number of children. */
	  ptr += *ptr + 1;
	  if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      grub_free (comp->internal_id);
	      grub_free (comp);
	      goto fail2;
	    }
	  ptr += 8 + 8;
	  if (ptr + *ptr + 1 >= vblk[i].dynamic
	      + sizeof (vblk[i].dynamic))
	    {
	      grub_free (comp->internal_id);
	      grub_free (comp);
	      goto fail2;
	    }
	  for (lv = vg->lvs; lv; lv = lv->next)
	    {
	      if (lv->internal_id[0] == ptr[0]
		  && grub_memcmp (lv->internal_id + 1, ptr + 1, ptr[0]) == 0)
		break;
	    }
	  if (!lv)
	    {
	      grub_free (comp->internal_id);
	      grub_free (comp);
	      continue;
	    }
	  comp->size = lv->size;
	  if (type == SPANNED)
	    {
	      comp->segment_alloc = 8;
	      comp->segment_count = 0;
	      comp->segments = grub_malloc (sizeof (*comp->segments)
					    * comp->segment_alloc);
	      if (!comp->segments)
		goto fail2;
	    }
	  else
	    {
	      comp->segment_alloc = 1;
	      comp->segment_count = 1;
	      comp->segments = grub_malloc (sizeof (*comp->segments));
	      if (!comp->segments)
		goto fail2;
	      comp->segments->start_extent = 0;
	      comp->segments->extent_count = lv->size;
	      comp->segments->layout = 0;
	      if (type == STRIPE)
		comp->segments->type = GRUB_DISKFILTER_STRIPED;
	      else if (type == RAID5)
		{
		  comp->segments->type = GRUB_DISKFILTER_RAID5;
		  comp->segments->layout = GRUB_RAID_LAYOUT_SYMMETRIC_MASK;
		}
	      else
		goto fail2;
	      ptr += *ptr + 1;
	      ptr++;
	      if (!(vblk[i].flags & 0x10))
		goto fail2;
	      if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)
		  || ptr + *ptr + 1 >= vblk[i].dynamic
		  + sizeof (vblk[i].dynamic))
		{
		  grub_free (comp->internal_id);
		  grub_free (comp);
		  goto fail2;
		}
	      comp->segments->stripe_size = read_int (ptr + 1, *ptr);
	      ptr += *ptr + 1;
	      if (ptr + *ptr + 1 >= vblk[i].dynamic
		  + sizeof (vblk[i].dynamic))
		{
		  grub_free (comp->internal_id);
		  grub_free (comp);
		  goto fail2;
		}
	      comp->segments->node_count = read_int (ptr + 1, *ptr);
	      comp->segments->node_alloc = comp->segments->node_count;
	      comp->segments->nodes = grub_zalloc (sizeof (*comp->segments->nodes)
						   * comp->segments->node_alloc);
	      if (!lv->segments->nodes)
		goto fail2;
	    }

	  if (lv->segments->node_alloc == lv->segments->node_count)
	    {
	      void *t;
	      lv->segments->node_alloc *= 2; 
	      t = grub_realloc (lv->segments->nodes,
				sizeof (*lv->segments->nodes)
				* lv->segments->node_alloc);
	      if (!t)
		goto fail2;
	      lv->segments->nodes = t;
	    }
	  lv->segments->nodes[lv->segments->node_count].pv = 0;
	  lv->segments->nodes[lv->segments->node_count].start = 0;
	  lv->segments->nodes[lv->segments->node_count++].lv = comp;
	  comp->next = vg->lvs;
	  vg->lvs = comp;
	}
    }
  /* Partitions.  */
  for (cursec = startsec + 0x12; cursec < endsec; cursec++)
    {
      struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE
				/ sizeof (struct grub_ldm_vblk)];
      unsigned i;
      err = grub_disk_read (disk, cursec, 0,
			    sizeof(vblk), &vblk);
      if (err)
	goto fail2;

      for (i = 0; i < ARRAY_SIZE (vblk); i++)
	{
	  struct grub_diskfilter_lv *comp;
	  struct grub_diskfilter_node part;
	  grub_disk_addr_t start, size;

	  grub_uint8_t *ptr;
	  part.name = 0;
	  if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC,
			   sizeof (vblk[i].magic)) != 0)
	    continue;
	  if (grub_be_to_cpu16 (vblk[i].update_status)
	      != STATUS_CONSISTENT
	      && grub_be_to_cpu16 (vblk[i].update_status)
	      != STATUS_STILL_ACTIVE)
	    continue;
	  if (vblk[i].type != ENTRY_PARTITION)
	    continue;
	  part.lv = 0;
	  part.pv = 0;

	  ptr = vblk[i].dynamic;
	  if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      goto fail2;
	    }
	  /* ID */
	  ptr += *ptr + 1;
	  if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      goto fail2;
	    }
	  /* ptr = name.  */
	  ptr += *ptr + 1;
	  if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      goto fail2;
	    }

	  /* skip zeros and logcommit id.  */
	  ptr += 4 + 8;
	  if (ptr + 16 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      goto fail2;
	    }
	  part.start = read_int (ptr, 8);
	  start = read_int (ptr + 8, 8);
	  ptr += 16;
	  if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)
	      || ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      goto fail2;
	    }
	  size = read_int (ptr + 1, *ptr);
	  ptr += *ptr + 1;
	  if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)
	      || ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      goto fail2;
	    }

	  for (comp = vg->lvs; comp; comp = comp->next)
	    if (comp->internal_id[0] == ptr[0]
		&& grub_memcmp (ptr + 1, comp->internal_id + 1,
				comp->internal_id[0]) == 0)
	      goto out;
	  continue;
	out:
	  if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)
	      || ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
	    {
	      goto fail2;
	    }
	  ptr += *ptr + 1;
	  struct grub_diskfilter_pv *pv;
	  for (pv = vg->pvs; pv; pv = pv->next)
	    if (pv->internal_id[0] == ptr[0]
		&& grub_memcmp (pv->internal_id + 1, ptr + 1, ptr[0]) == 0)
	      part.pv = pv;

	  if (comp->segment_alloc == 1)
	    {
	      unsigned node_index;
	      ptr += *ptr + 1;
	      if (ptr + *ptr + 1 >= vblk[i].dynamic
		  + sizeof (vblk[i].dynamic))
		{
		  goto fail2;
		}
	      node_index = read_int (ptr + 1, *ptr);
	      if (node_index < comp->segments->node_count)
		comp->segments->nodes[node_index] = part;
	    }
	  else
	    {
	      if (comp->segment_alloc == comp->segment_count)
		{
		  void *t;
		  comp->segment_alloc *= 2;
		  t = grub_realloc (comp->segments,
				    comp->segment_alloc
				    * sizeof (*comp->segments));
		  if (!t)
		    goto fail2;
		  comp->segments = t;
		}
	      comp->segments[comp->segment_count].start_extent = start;
	      comp->segments[comp->segment_count].extent_count = size;
	      comp->segments[comp->segment_count].type = GRUB_DISKFILTER_STRIPED;
	      comp->segments[comp->segment_count].node_count = 1;
	      comp->segments[comp->segment_count].node_alloc = 1;
	      comp->segments[comp->segment_count].nodes
		= grub_malloc (sizeof (*comp->segments[comp->segment_count].nodes));
	      if (!comp->segments[comp->segment_count].nodes)
		goto fail2;
	      comp->segments[comp->segment_count].nodes[0] = part;
	      comp->segment_count++;
	    }
	}
    }
  if (grub_diskfilter_vg_register (vg))
    goto fail2;
  return vg;
 fail2:
  {
    struct grub_diskfilter_lv *lv, *next_lv;
    struct grub_diskfilter_pv *pv, *next_pv;
    for (lv = vg->lvs; lv; lv = next_lv)
      {
	unsigned i;
	for (i = 0; i < lv->segment_count; i++)
	  grub_free (lv->segments[i].nodes);

	next_lv = lv->next;
	grub_free (lv->segments);
	grub_free (lv->internal_id);
	grub_free (lv->name);
	grub_free (lv->fullname);
	grub_free (lv);
      }
    for (pv = vg->pvs; pv; pv = next_pv)
      {
	next_pv = pv->next;
	grub_free (pv->id.uuid);
	grub_free (pv);
      }
  }
  grub_free (vg->uuid);
  grub_free (vg);
  return NULL;
}