Beispiel #1
0
static grub_err_t
read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb)
{
  unsigned i;
  grub_err_t err = GRUB_ERR_NONE;
  for (i = 0; i < ARRAY_SIZE (superblock_sectors); i++)
    {
      struct grub_btrfs_superblock sblock;
      err = grub_disk_read (disk, superblock_sectors[i], 0,
			    sizeof (sblock), &sblock);
      if (err == GRUB_ERR_OUT_OF_RANGE)
	break;

      if (grub_memcmp ((char *) sblock.signature, GRUB_BTRFS_SIGNATURE,
		       sizeof (GRUB_BTRFS_SIGNATURE) - 1) != 0)
	break;
      if (i == 0 || grub_le_to_cpu64 (sblock.generation)
	  > grub_le_to_cpu64 (sb->generation))
	grub_memcpy (sb, &sblock, sizeof (sblock));
    }

  if ((err == GRUB_ERR_OUT_OF_RANGE || !err) && i == 0)
    return grub_error (GRUB_ERR_BAD_FS, "not a Btrfs filesystem");

  if (err == GRUB_ERR_OUT_OF_RANGE)
    grub_errno = err = GRUB_ERR_NONE;

  return err;
}
Beispiel #2
0
static int
next (struct grub_btrfs_data *data,
      struct grub_btrfs_leaf_descriptor *desc,
      grub_disk_addr_t *outaddr, grub_size_t *outsize,
      struct grub_btrfs_key *key_out)
{
  grub_err_t err;
  struct grub_btrfs_leaf_node leaf;

  for (; desc->depth > 0; desc->depth--)
    {
      desc->data[desc->depth - 1].iter++;
      if (desc->data[desc->depth - 1].iter
	  < desc->data[desc->depth - 1].maxiter)
	break;
    }
  if (desc->depth == 0)
    return 0;
  while (!desc->data[desc->depth - 1].leaf)
    {
      struct grub_btrfs_internal_node node;
      struct btrfs_header head;

      err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter
				     * sizeof (node)
				     + sizeof (struct btrfs_header)
				     + desc->data[desc->depth - 1].addr, &node,
				     sizeof (node));
      if (err)
	return -err;

      err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr), &head,
				     sizeof (head));
      if (err)
	return -err;

      save_ref (desc, grub_le_to_cpu64 (node.addr), 0, 
		grub_le_to_cpu32 (head.nitems), !head.level);
    }
  err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter
				 * sizeof (leaf)
				 + sizeof (struct btrfs_header)
				 + desc->data[desc->depth - 1].addr, &leaf,
				 sizeof (leaf));
  if (err)
    return -err;
  *outsize = grub_le_to_cpu32 (leaf.size);
  *outaddr = desc->data[desc->depth - 1].addr + sizeof (struct btrfs_header)
    + grub_le_to_cpu32 (leaf.offset);
  *key_out = leaf.key;
  return 1;  
}
Beispiel #3
0
static grub_err_t
lower_bound (struct grub_btrfs_data *data,
	     const struct grub_btrfs_key *key_in, 
	     struct grub_btrfs_key *key_out,
	     grub_disk_addr_t root,
	     grub_disk_addr_t *outaddr, grub_size_t *outsize,
	     struct grub_btrfs_leaf_descriptor *desc)
{
  grub_disk_addr_t addr = root;
  int depth = -1;

  if (desc)
    {
      desc->allocated = 16;
      desc->depth = 0;
      desc->data = grub_malloc (sizeof (desc->data[0]) * desc->allocated);
      if (!desc->data)
	return grub_errno;
    }

  grub_dprintf ("btrfs",
		"retrieving %" PRIxGRUB_UINT64_T
		" %x %" PRIxGRUB_UINT64_T "\n",
		key_in->object_id, key_in->type, key_in->offset);

  while (1)
    {
      grub_err_t err;
      struct btrfs_header head;

    reiter:
      depth++;
      /* FIXME: preread few nodes into buffer. */
      err = grub_btrfs_read_logical (data, addr, &head, sizeof (head));
      if (err)
	return err;
      addr += sizeof (head);
      if (head.level)
	{
	  unsigned i;
	  struct grub_btrfs_internal_node node, node_last;
	  int have_last = 0;
	  grub_memset (&node_last, 0, sizeof (node_last));
	  for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++)
	    {
	      err = grub_btrfs_read_logical (data, addr + i * sizeof (node),
					     &node, sizeof (node));
	      if (err)
		return err;

	      grub_dprintf ("btrfs",
			    "internal node (depth %d) %" PRIxGRUB_UINT64_T
			    " %x %" PRIxGRUB_UINT64_T "\n", depth,
			    node.key.object_id, node.key.type, node.key.offset);
	      
	      if (key_cmp (&node.key, key_in) == 0)
		{
		  err = GRUB_ERR_NONE;
		  if (desc)
		    err = save_ref (desc, addr - sizeof (head), i,
				    grub_le_to_cpu32 (head.nitems), 0);
		  if (err)
		    return err;
		  addr = grub_le_to_cpu64 (node.addr);
		  goto reiter;
		}
	      if (key_cmp (&node.key, key_in) > 0)
		break;
	      node_last = node;
	      have_last = 1;
	    }
	  if (have_last)
	    {
	      err = GRUB_ERR_NONE;
	      if (desc)
		err = save_ref (desc, addr - sizeof (head), i - 1,
				 grub_le_to_cpu32 (head.nitems), 0);
	      if (err)
		return err;
	      addr = grub_le_to_cpu64 (node_last.addr);
	      goto reiter;
	    }
	  *outsize = 0;
	  *outaddr = 0;
	  grub_memset (key_out, 0, sizeof (*key_out));
	  if (desc)
	    return save_ref (desc, addr - sizeof (head), -1,
			     grub_le_to_cpu32 (head.nitems), 0);
	  return GRUB_ERR_NONE;
	}
      {
	unsigned i;
	struct grub_btrfs_leaf_node leaf, leaf_last;
	int have_last = 0;
	for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++)
	  {
	    err = grub_btrfs_read_logical (data, addr + i * sizeof (leaf),
					   &leaf, sizeof (leaf));
	    if (err)
	      return err;
	    
	    grub_dprintf ("btrfs",
			  "leaf (depth %d) %" PRIxGRUB_UINT64_T
			  " %x %" PRIxGRUB_UINT64_T "\n", depth,
			  leaf.key.object_id, leaf.key.type, leaf.key.offset);

	    if (key_cmp (&leaf.key, key_in) == 0)
	      {
		grub_memcpy (key_out, &leaf.key, sizeof(*key_out));
		*outsize = grub_le_to_cpu32 (leaf.size);
		*outaddr = addr + grub_le_to_cpu32 (leaf.offset);
		if (desc)
		  return save_ref (desc, addr - sizeof (head), i,
				   grub_le_to_cpu32 (head.nitems), 1);
		return GRUB_ERR_NONE;	      
	      }
	    
	    if (key_cmp (&leaf.key, key_in) > 0)
	      break;
	    
	    have_last = 1;
	    leaf_last = leaf;
	  }

	if (have_last)
	  {
	    grub_memcpy (key_out, &leaf_last.key, sizeof(*key_out));
	    *outsize = grub_le_to_cpu32 (leaf_last.size);
	    *outaddr = addr + grub_le_to_cpu32 (leaf_last.offset);
	    if (desc)
	      return save_ref (desc, addr - sizeof (head), i - 1,
			       grub_le_to_cpu32 (head.nitems), 1);
	    return GRUB_ERR_NONE;	      
	  }
	*outsize = 0;
	*outaddr = 0;
	grub_memset (key_out, 0, sizeof (*key_out));
	if (desc)
	  return save_ref (desc, addr - sizeof (head), -1,
			   grub_le_to_cpu32 (head.nitems), 1);
	return GRUB_ERR_NONE;
      }
    }
}
Beispiel #4
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;
}
static struct grub_diskfilter_vg *
grub_mdraid_detect (grub_disk_t disk,
		    struct grub_diskfilter_pv_id *id,
		    grub_disk_addr_t *start_sector)
{
  grub_uint64_t size;
  grub_uint8_t minor_version;

  size = grub_disk_get_size (disk);

  /* Check for an 1.x superblock.
   * It's always aligned to a 4K boundary
   * and depending on the minor version it can be:
   * 0: At least 8K, but less than 12K, from end of device
   * 1: At start of device
   * 2: 4K from start of device.
   */

  for (minor_version = 0; minor_version < 3; ++minor_version)
    {
      grub_disk_addr_t sector = 0;
      struct grub_raid_super_1x sb;
      grub_uint16_t role;
      grub_uint32_t level;
      struct grub_diskfilter_vg *array;
      char *uuid;
	
      if (size == GRUB_DISK_SIZE_UNKNOWN && minor_version == 0)
	continue;
	
      switch (minor_version)
	{
	case 0:
	  sector = (size - 8 * 2) & ~(4 * 2 - 1);
	  break;
	case 1:
	  sector = 0;
	  break;
	case 2:
	  sector = 4 * 2;
	  break;
	}

      if (grub_disk_read (disk, sector, 0, sizeof (struct grub_raid_super_1x),
			  &sb))
	return NULL;

      if (sb.magic != grub_cpu_to_le32_compile_time (SB_MAGIC)
	  || grub_le_to_cpu64 (sb.super_offset) != sector)
	continue;

      if (sb.major_version != grub_cpu_to_le32_compile_time (1))
	/* Unsupported version.  */
	return NULL;

      level = grub_le_to_cpu32 (sb.level);

      /* Multipath.  */
      if ((int) level == -4)
	level = 1;

      if (level != 0 && level != 1 && level != 4 &&
	  level != 5 && level != 6 && level != 10)
	{
	  grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
		      "Unsupported RAID level: %d", sb.level);
	  return NULL;
	}

      if (grub_le_to_cpu32 (sb.dev_number) >=
	  grub_le_to_cpu32 (sb.max_dev))
	/* Spares aren't implemented.  */
	return NULL;

      if (grub_disk_read (disk, sector, 
			  (char *) &sb.dev_roles[grub_le_to_cpu32 (sb.dev_number)]
			  - (char *) &sb,
			  sizeof (role), &role))
	return NULL;

      if (grub_le_to_cpu16 (role)
	  >= grub_le_to_cpu32 (sb.raid_disks))
	/* Spares aren't implemented.  */
	return NULL;

      id->uuidlen = 0;
      id->id = grub_le_to_cpu16 (role);

      uuid = grub_malloc (16);
      if (!uuid)
	return NULL;

      grub_memcpy (uuid, sb.set_uuid, 16);

      *start_sector = grub_le_to_cpu64 (sb.data_offset);

      array = grub_diskfilter_make_raid (16, uuid,
					 grub_le_to_cpu32 (sb.raid_disks),
					 sb.set_name,
					 (sb.size)
					 ? grub_le_to_cpu64 (sb.size) 
					 : grub_le_to_cpu64 (sb.data_size),
					 grub_le_to_cpu32 (sb.chunksize),
					 grub_le_to_cpu32 (sb.layout),
					 grub_le_to_cpu32 (sb.level));

      return array;
    }

  /* not 1.x raid.  */
  return NULL;
}
static grub_err_t
grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array,
		    grub_disk_addr_t *start_sector)
{
  grub_disk_addr_t sector = 0;
  grub_uint64_t size;
  struct grub_raid_super_1x sb;
  grub_uint8_t minor_version;

  /* The sector where the mdraid 0.90 superblock is stored, if available.  */
  size = grub_disk_get_size (disk);

  /* Check for an 1.x superblock.
   * It's always aligned to a 4K boundary
   * and depending on the minor version it can be:
   * 0: At least 8K, but less than 12K, from end of device
   * 1: At start of device
   * 2: 4K from start of device.
   */

  for (minor_version = 0; minor_version < 3; ++minor_version)
    {
      if (size == GRUB_DISK_SIZE_UNKNOWN && minor_version == 0)
	continue;
	
      switch (minor_version)
	{
	case 0:
	  sector = (size - 8 * 2) & ~(4 * 2 - 1);
	  break;
	case 1:
	  sector = 0;
	  break;
	case 2:
	  sector = 4 * 2;
	  break;
	}

      if (grub_disk_read (disk, sector, 0, sizeof (struct grub_raid_super_1x),
			  &sb))
	return grub_errno;

      if (grub_le_to_cpu32 (sb.magic) != SB_MAGIC
	  || grub_le_to_cpu64 (sb.super_offset) != sector)
	continue;

      {
	grub_uint64_t sb_size;
	struct grub_raid_super_1x *real_sb;
	grub_uint32_t level;

	if (grub_le_to_cpu32 (sb.major_version) != 1)
	  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
			     "Unsupported RAID version: %d",
			     grub_le_to_cpu32 (sb.major_version));

	level = grub_le_to_cpu32 (sb.level);

	/* Multipath.  */
	if ((int) level == -4)
	  level = 1;

	if (level != 0 && level != 1 && level != 4 &&
	    level != 5 && level != 6 && level != 10)
	  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
			     "Unsupported RAID level: %d", sb.level);

	/* 1.x superblocks don't have a fixed size on disk.  So we have to
	   read it again now that we now the max device count.  */
	sb_size = sizeof (struct grub_raid_super_1x) 
	  + 2 * grub_le_to_cpu32 (sb.max_dev);
	real_sb = grub_malloc (sb_size);
	if (! real_sb)
	  return grub_errno;

	if (grub_disk_read (disk, sector, 0, sb_size, real_sb))
	  {
	    grub_free (real_sb);
	    return grub_errno;
	  }

	array->name = grub_strdup (real_sb->set_name);
	if (! array->name)
	  {
	    grub_free (real_sb);
	    return grub_errno;
	  }

	array->number = 0;
	array->level = grub_le_to_cpu32 (real_sb->level);
	array->layout = grub_le_to_cpu32 (real_sb->layout);
	array->total_devs = grub_le_to_cpu32 (real_sb->raid_disks);
	array->disk_size = grub_le_to_cpu64 (real_sb->size);
	array->chunk_size = grub_le_to_cpu32 (real_sb->chunksize);

	if (grub_le_to_cpu32 (real_sb->dev_number) >=
	    grub_le_to_cpu32 (real_sb->max_dev))
	  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
			     "spares aren't implemented");

	array->index = grub_le_to_cpu16
	  (real_sb->dev_roles[grub_le_to_cpu32 (real_sb->dev_number)]);
	array->uuid_len = 16;
	array->uuid = grub_malloc (16);
	if (!array->uuid)
	  {
	    grub_free (real_sb);
	    return grub_errno;
	  }

	grub_memcpy (array->uuid, real_sb->set_uuid, 16);
	
	*start_sector = grub_le_to_cpu64 (real_sb->data_offset);

	grub_free (real_sb);
	return 0;
      }
    }

  return grub_error (GRUB_ERR_OUT_OF_RANGE, "not 1.x raid");
}
Beispiel #7
0
static char *
get_btrfs_fs_prefix (const char *mount_path)
{
    struct btrfs_ioctl_ino_lookup_args args;
    struct stat st;
    int fd;
    grub_uint64_t tree_id, inode_id;
    char *ret = NULL;

    fd = open (mount_path, O_RDONLY);

    if (fd < 0)
        return NULL;
    memset (&args, 0, sizeof(args));
    args.objectid = GRUB_BTRFS_TREE_ROOT_OBJECTID;

    if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
        goto fail;
    tree_id = args.treeid;

    if (fstat (fd, &st) < 0)
        goto fail;
    inode_id = st.st_ino;

    while (tree_id != GRUB_BTRFS_ROOT_VOL_OBJECTID
            || inode_id != GRUB_BTRFS_TREE_ROOT_OBJECTID)
    {
        const char *name;
        size_t namelen;
        struct btrfs_ioctl_search_args sargs;
        char *old;

        memset (&sargs, 0, sizeof(sargs));

        if (inode_id == GRUB_BTRFS_TREE_ROOT_OBJECTID)
        {
            struct grub_btrfs_root_backref *br;

            sargs.key.tree_id = 1;
            sargs.key.min_objectid = tree_id;
            sargs.key.max_objectid = tree_id;

            sargs.key.min_offset = 0;
            sargs.key.max_offset = ~0ULL;
            sargs.key.min_transid = 0;
            sargs.key.max_transid = ~0ULL;
            sargs.key.min_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF;
            sargs.key.max_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF;

            sargs.key.nr_items = 1;

            if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0)
                goto fail;

            if (sargs.key.nr_items == 0)
                goto fail;

            tree_id = sargs.buf[2];
            br = (struct grub_btrfs_root_backref *) (sargs.buf + 4);
            inode_id = grub_le_to_cpu64 (br->inode_id);
            name = br->name;
            namelen = grub_le_to_cpu16 (br->n);
        }
        else
        {
            struct grub_btrfs_inode_ref *ir;

            sargs.key.tree_id = tree_id;
            sargs.key.min_objectid = inode_id;
            sargs.key.max_objectid = inode_id;

            sargs.key.min_offset = 0;
            sargs.key.max_offset = ~0ULL;
            sargs.key.min_transid = 0;
            sargs.key.max_transid = ~0ULL;
            sargs.key.min_type = GRUB_BTRFS_ITEM_TYPE_INODE_REF;
            sargs.key.max_type = GRUB_BTRFS_ITEM_TYPE_INODE_REF;

            if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0)
                goto fail;

            if (sargs.key.nr_items == 0)
                goto fail;

            inode_id = sargs.buf[2];

            ir = (struct grub_btrfs_inode_ref *) (sargs.buf + 4);
            name = ir->name;
            namelen = grub_le_to_cpu16 (ir->n);
        }
        old = ret;
        ret = xmalloc (namelen + (old ? strlen (old) : 0) + 2);
        ret[0] = '/';
        memcpy (ret + 1, name, namelen);
        if (old)
        {
            strcpy (ret + 1 + namelen, old);
            free (old);
        }
        else
            ret[1+namelen] = '\0';
    }
    if (!ret)
        ret = xstrdup ("/");
    close (fd);
    return ret;

fail:
    free (ret);
    close (fd);
    return NULL;
}