From: Vladimir 'phcoder' Serbinenko Date: Thu, 7 Jul 2011 21:21:59 +0000 (+0200) Subject: Lazy device scanning. X-Git-Tag: 2.00~1165 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=9bfdcbbc7d152f5ccaa2e2fd130815ba744c3941;p=thirdparty%2Fgrub.git 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. --- 9bfdcbbc7d152f5ccaa2e2fd130815ba744c3941 diff --cc ChangeLog index e6aace254,e6aace254..2eaf669c0 --- a/ChangeLog +++ b/ChangeLog @@@ -1,3 -1,3 +1,51 @@@ ++2011-07-07 Vladimir Serbinenko ++ ++ 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 * po/POTFILES.in: Regenerate. diff --cc grub-core/disk/lvm.c index 563b49b49,06e9f6d6a..7c19f08de --- a/grub-core/disk/lvm.c +++ b/grub-core/disk/lvm.c @@@ -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); /* 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; + unsigned old_count = 0; + if (pull == GRUB_DISK_PULL_RESCAN && scan_depth) + return 0; + + if (pull == GRUB_DISK_PULL_RESCAN) + { + old_count = lv_count; + if (!scan_depth) - { - scan_depth++; - grub_device_iterate (&grub_lvm_scan_device); - scan_depth--; - } ++ 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",