]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Lazy device scanning.
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Thu, 7 Jul 2011 21:21:59 +0000 (23:21 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Thu, 7 Jul 2011 21:21:59 +0000 (23:21 +0200)
* Makefile.util.def (libgrubkern.a): Add grub-core/kern/emu/raid.c.
(grub-setup): Remove util/raid.c.
* grub-core/Makefile.core.def (kernel): Add kern/emu/raid.c on emu.
* grub-core/disk/lvm.c (scan_depth): New variable.
(grub_lvm_iterate): Rescan if necessary.
(find_lv): New function based on grub_lvm_open.
(grub_lvm_open): Use find_lv. Rescan on error.
(is_node_readable): New function.
(is_lv_readable): Likewise.
(grub_lvm_scan_device): Skip already found disks.
(do_lvm_scan): New function. Move grub_lvm_scan_device inside of it.
Stop if searched device is found and readable.
* grub-core/disk/raid.c (inscnt): New variable.
(scan_depth): Likewise.
(scan_devices): New function based on grub_raid_register. Abort if
looked for device is found.
(grub_raid_iterate): Rescan if needed.
(find_array): NEw function based on -grub_raid_open.
(grub_raid_open): Use find_array and rescan.
(insert_array): Set became_readable_at.
* grub-core/kern/disk.c (grub_disk_dev_iterate): Iterate though "pull.
* grub-core/kern/emu/getroot.c (grub_util_open_dm) [HAVE_DEVICE_MAPPER]:
New function.
(grub_util_is_lvm) [HAVE_DEVICE_MAPPER]: Use grub_util_open_dm.
(grub_util_pull_device): New function.
(grub_util_get_grub_dev): Call grub_util_pull_device.
* util/raid.c: Moved to ..
* grub-core/kern/emu/raid.c: ... here.
(grub_util_raid_getmembers): New parameter "bootable".
All users updated. Support 1.x.
* include/grub/ata.h (grub_ata_dev): Change iterate prototype.
All users updated.
* include/grub/disk.h (grub_disk_pull_t): New enum.
(grub_disk_dev): Change iterate prototype.
All users updated.
* include/grub/emu/getroot.h (grub_util_raid_getmembers) [__linux__]:
New proto.
* include/grub/emu/hostdisk.h (grub_util_pull_device): Likewise.
* include/grub/lvm.h (grub_lvm_lv): New members fullname and compatname.
* include/grub/raid.h (grub_raid_array): New member became_readable_at.
* include/grub/scsi.h (grub_scsi_dev): Change iterate prototype.
All users updated.
* include/grub/util/raid.h: Removed.

1  2 
ChangeLog
grub-core/disk/lvm.c

diff --cc ChangeLog
index e6aace254ed158b8daa9278d4ea9c86cfd6183bf,e6aace254ed158b8daa9278d4ea9c86cfd6183bf..2eaf669c07e7075ae34faae3090fc172f8b9aa40
+++ b/ChangeLog
@@@ -1,3 -1,3 +1,51 @@@
++2011-07-07  Vladimir Serbinenko  <phcoder@gmail.com>
++
++      Lazy device scanning.
++
++      * Makefile.util.def (libgrubkern.a): Add grub-core/kern/emu/raid.c.
++      (grub-setup): Remove util/raid.c.
++      * grub-core/Makefile.core.def (kernel): Add kern/emu/raid.c on emu.
++      * grub-core/disk/lvm.c (scan_depth): New variable.
++      (grub_lvm_iterate): Rescan if necessary.
++      (find_lv): New function based on grub_lvm_open.
++      (grub_lvm_open): Use find_lv. Rescan on error.
++      (is_node_readable): New function.
++      (is_lv_readable): Likewise.
++      (grub_lvm_scan_device): Skip already found disks.
++      (do_lvm_scan): New function. Move grub_lvm_scan_device inside of it.
++      Stop if searched device is found and readable.
++      * grub-core/disk/raid.c (inscnt): New variable.
++      (scan_depth): Likewise.
++      (scan_devices): New function based on grub_raid_register. Abort if
++      looked for device is found.
++      (grub_raid_iterate): Rescan if needed.
++      (find_array): NEw function based on -grub_raid_open.
++      (grub_raid_open): Use find_array and rescan.
++      (insert_array): Set became_readable_at.
++      * grub-core/kern/disk.c (grub_disk_dev_iterate): Iterate though "pull.
++      * grub-core/kern/emu/getroot.c (grub_util_open_dm) [HAVE_DEVICE_MAPPER]:
++      New function.
++      (grub_util_is_lvm) [HAVE_DEVICE_MAPPER]: Use grub_util_open_dm.
++      (grub_util_pull_device): New function.
++      (grub_util_get_grub_dev): Call grub_util_pull_device.
++      * util/raid.c: Moved to ..
++      * grub-core/kern/emu/raid.c: ... here.
++      (grub_util_raid_getmembers): New parameter "bootable".
++      All users updated. Support 1.x.
++      * include/grub/ata.h (grub_ata_dev): Change iterate prototype.
++      All users updated.
++      * include/grub/disk.h (grub_disk_pull_t): New enum.
++      (grub_disk_dev): Change iterate prototype.
++      All users updated.
++      * include/grub/emu/getroot.h (grub_util_raid_getmembers) [__linux__]:
++      New proto.
++      * include/grub/emu/hostdisk.h (grub_util_pull_device): Likewise.
++      * include/grub/lvm.h (grub_lvm_lv): New members fullname and compatname.
++      * include/grub/raid.h (grub_raid_array): New member became_readable_at.
++      * include/grub/scsi.h (grub_scsi_dev): Change iterate prototype.
++      All users updated.
++      * include/grub/util/raid.h: Removed.
++
  2011-07-06  Vladimir Serbinenko  <phcoder@gmail.com>
  
        * po/POTFILES.in: Regenerate.
index 563b49b49f32b452efc46df7b9f7bfcbcf3f3b2c,06e9f6d6a721773bea1475dd3fad8c65187ec073..7c19f08de1d2e2495c6b04209b0bcca804292b77
@@@ -32,7 -33,12 +33,10 @@@ 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);
  \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
@@@ -95,10 -101,27 +99,650 @@@ grub_lvm_check_flag (char *p, char *str
      }
  }
  
++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;
@@@ -138,17 -164,46 +785,23 @@@ grub_lvm_memberlist (grub_disk_t disk
  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)
@@@ -301,565 -399,608 +997,6 @@@ grub_lvm_write (grub_disk_t disk __attr
    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",