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 * make_vg (grub_disk_t disk, const struct grub_ldm_label *label) { grub_disk_addr_t startsec, endsec, cursec; struct grub_diskfilter_vg *vg; grub_err_t err; /* First time we see this volume group. We've to create the whole volume group structure. */ vg = grub_malloc (sizeof (*vg)); if (! vg) return NULL; vg->extent_size = 1; vg->name = grub_malloc (LDM_NAME_STRLEN + 1); vg->uuid = grub_malloc (LDM_GUID_STRLEN + 1); if (! vg->uuid || !vg->name) { grub_free (vg->uuid); grub_free (vg->name); return NULL; } grub_memcpy (vg->uuid, label->group_guid, LDM_GUID_STRLEN); grub_memcpy (vg->name, label->group_name, LDM_NAME_STRLEN); vg->name[LDM_NAME_STRLEN] = 0; vg->uuid[LDM_GUID_STRLEN] = 0; vg->uuid_len = grub_strlen (vg->uuid); vg->lvs = NULL; vg->pvs = NULL; startsec = grub_be_to_cpu64 (label->config_start); endsec = startsec + grub_be_to_cpu64 (label->config_size); /* First find disks. */ for (cursec = startsec + 0x12; cursec < endsec; cursec++) { struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_ldm_vblk)]; unsigned i; err = grub_disk_read (disk, cursec, 0, sizeof(vblk), &vblk); if (err) goto fail2; for (i = 0; i < ARRAY_SIZE (vblk); i++) { struct grub_diskfilter_pv *pv; grub_uint8_t *ptr; if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC, sizeof (vblk[i].magic)) != 0) continue; if (grub_be_to_cpu16 (vblk[i].update_status) != STATUS_CONSISTENT && grub_be_to_cpu16 (vblk[i].update_status) != STATUS_STILL_ACTIVE) continue; if (vblk[i].type != ENTRY_DISK) continue; pv = grub_zalloc (sizeof (*pv)); if (!pv) goto fail2; pv->disk = 0; ptr = vblk[i].dynamic; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (pv); goto fail2; } pv->internal_id = grub_malloc (ptr[0] + 2); if (!pv->internal_id) { grub_free (pv); goto fail2; } grub_memcpy (pv->internal_id, ptr, (grub_size_t) ptr[0] + 1); pv->internal_id[(grub_size_t) ptr[0] + 1] = 0; ptr += *ptr + 1; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (pv); goto fail2; } /* ptr = name. */ ptr += *ptr + 1; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (pv); goto fail2; } pv->id.uuidlen = *ptr; pv->id.uuid = grub_malloc (pv->id.uuidlen + 1); grub_memcpy (pv->id.uuid, ptr + 1, pv->id.uuidlen); pv->id.uuid[pv->id.uuidlen] = 0; pv->next = vg->pvs; vg->pvs = pv; } } /* Then find LVs. */ for (cursec = startsec + 0x12; cursec < endsec; cursec++) { struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_ldm_vblk)]; unsigned i; err = grub_disk_read (disk, cursec, 0, sizeof(vblk), &vblk); if (err) goto fail2; for (i = 0; i < ARRAY_SIZE (vblk); i++) { struct grub_diskfilter_lv *lv; grub_uint8_t *ptr; if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC, sizeof (vblk[i].magic)) != 0) continue; if (grub_be_to_cpu16 (vblk[i].update_status) != STATUS_CONSISTENT && grub_be_to_cpu16 (vblk[i].update_status) != STATUS_STILL_ACTIVE) continue; if (vblk[i].type != ENTRY_VOLUME) continue; lv = grub_zalloc (sizeof (*lv)); if (!lv) goto fail2; lv->vg = vg; lv->segment_count = 1; lv->segment_alloc = 1; lv->visible = 1; lv->segments = grub_zalloc (sizeof (*lv->segments)); if (!lv->segments) goto fail2; lv->segments->start_extent = 0; lv->segments->type = GRUB_DISKFILTER_MIRROR; lv->segments->node_count = 0; lv->segments->node_alloc = 8; lv->segments->nodes = grub_zalloc (sizeof (*lv->segments->nodes) * lv->segments->node_alloc); if (!lv->segments->nodes) goto fail2; ptr = vblk[i].dynamic; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (lv); goto fail2; } lv->internal_id = grub_malloc ((grub_size_t) ptr[0] + 2); if (!lv->internal_id) { grub_free (lv); goto fail2; } grub_memcpy (lv->internal_id, ptr, ptr[0] + 1); lv->internal_id[ptr[0] + 1] = 0; ptr += *ptr + 1; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (lv); goto fail2; } lv->name = grub_malloc (*ptr + 1); if (!lv->name) { grub_free (lv->internal_id); grub_free (lv); goto fail2; } grub_memcpy (lv->name, ptr + 1, *ptr); lv->name[*ptr] = 0; lv->fullname = grub_xasprintf ("ldm/%s/%s", vg->uuid, lv->name); if (!lv->fullname) { grub_free (lv->internal_id); grub_free (lv->name); grub_free (lv); goto fail2; } ptr += *ptr + 1; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (lv->internal_id); grub_free (lv->name); grub_free (lv); goto fail2; } /* ptr = volume type. */ ptr += *ptr + 1; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (lv->internal_id); grub_free (lv->name); grub_free (lv); goto fail2; } /* ptr = flags. */ ptr += *ptr + 1; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (lv->internal_id); grub_free (lv->name); grub_free (lv); goto fail2; } /* Skip state, type, unknown, volume number, zeros, flags. */ ptr += 14 + 1 + 1 + 1 + 3 + 1; /* ptr = number of children. */ if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (lv->internal_id); grub_free (lv->name); grub_free (lv); goto fail2; } ptr += *ptr + 1; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (lv->internal_id); grub_free (lv->name); grub_free (lv); goto fail2; } /* Skip 2 more fields. */ ptr += 8 + 8; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic) || ptr + *ptr + 1>= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (lv->internal_id); grub_free (lv->name); grub_free (lv); goto fail2; } lv->size = read_int (ptr + 1, *ptr); lv->segments->extent_count = lv->size; lv->next = vg->lvs; vg->lvs = lv; } } /* Now the components. */ for (cursec = startsec + 0x12; cursec < endsec; cursec++) { struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_ldm_vblk)]; unsigned i; err = grub_disk_read (disk, cursec, 0, sizeof(vblk), &vblk); if (err) goto fail2; for (i = 0; i < ARRAY_SIZE (vblk); i++) { struct grub_diskfilter_lv *comp; struct grub_diskfilter_lv *lv; grub_uint8_t type; grub_uint8_t *ptr; if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC, sizeof (vblk[i].magic)) != 0) continue; if (grub_be_to_cpu16 (vblk[i].update_status) != STATUS_CONSISTENT && grub_be_to_cpu16 (vblk[i].update_status) != STATUS_STILL_ACTIVE) continue; if (vblk[i].type != ENTRY_COMPONENT) continue; comp = grub_zalloc (sizeof (*comp)); if (!comp) goto fail2; comp->visible = 0; comp->name = 0; comp->fullname = 0; ptr = vblk[i].dynamic; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { goto fail2; } comp->internal_id = grub_malloc ((grub_size_t) ptr[0] + 2); if (!comp->internal_id) { grub_free (comp); goto fail2; } grub_memcpy (comp->internal_id, ptr, ptr[0] + 1); comp->internal_id[ptr[0] + 1] = 0; ptr += *ptr + 1; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (comp->internal_id); grub_free (comp); goto fail2; } /* ptr = name. */ ptr += *ptr + 1; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (comp->internal_id); grub_free (comp); goto fail2; } /* ptr = state. */ ptr += *ptr + 1; type = *ptr++; /* skip zeros. */ ptr += 4; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (comp->internal_id); grub_free (comp); goto fail2; } /* ptr = number of children. */ ptr += *ptr + 1; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (comp->internal_id); grub_free (comp); goto fail2; } ptr += 8 + 8; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (comp->internal_id); grub_free (comp); goto fail2; } for (lv = vg->lvs; lv; lv = lv->next) { if (lv->internal_id[0] == ptr[0] && grub_memcmp (lv->internal_id + 1, ptr + 1, ptr[0]) == 0) break; } if (!lv) { grub_free (comp->internal_id); grub_free (comp); continue; } comp->size = lv->size; if (type == SPANNED) { comp->segment_alloc = 8; comp->segment_count = 0; comp->segments = grub_malloc (sizeof (*comp->segments) * comp->segment_alloc); if (!comp->segments) goto fail2; } else { comp->segment_alloc = 1; comp->segment_count = 1; comp->segments = grub_malloc (sizeof (*comp->segments)); if (!comp->segments) goto fail2; comp->segments->start_extent = 0; comp->segments->extent_count = lv->size; comp->segments->layout = 0; if (type == STRIPE) comp->segments->type = GRUB_DISKFILTER_STRIPED; else if (type == RAID5) { comp->segments->type = GRUB_DISKFILTER_RAID5; comp->segments->layout = GRUB_RAID_LAYOUT_SYMMETRIC_MASK; } else goto fail2; ptr += *ptr + 1; ptr++; if (!(vblk[i].flags & 0x10)) goto fail2; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic) || ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (comp->internal_id); grub_free (comp); goto fail2; } comp->segments->stripe_size = read_int (ptr + 1, *ptr); ptr += *ptr + 1; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { grub_free (comp->internal_id); grub_free (comp); goto fail2; } comp->segments->node_count = read_int (ptr + 1, *ptr); comp->segments->node_alloc = comp->segments->node_count; comp->segments->nodes = grub_zalloc (sizeof (*comp->segments->nodes) * comp->segments->node_alloc); if (!lv->segments->nodes) goto fail2; } if (lv->segments->node_alloc == lv->segments->node_count) { void *t; lv->segments->node_alloc *= 2; t = grub_realloc (lv->segments->nodes, sizeof (*lv->segments->nodes) * lv->segments->node_alloc); if (!t) goto fail2; lv->segments->nodes = t; } lv->segments->nodes[lv->segments->node_count].pv = 0; lv->segments->nodes[lv->segments->node_count].start = 0; lv->segments->nodes[lv->segments->node_count++].lv = comp; comp->next = vg->lvs; vg->lvs = comp; } } /* Partitions. */ for (cursec = startsec + 0x12; cursec < endsec; cursec++) { struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_ldm_vblk)]; unsigned i; err = grub_disk_read (disk, cursec, 0, sizeof(vblk), &vblk); if (err) goto fail2; for (i = 0; i < ARRAY_SIZE (vblk); i++) { struct grub_diskfilter_lv *comp; struct grub_diskfilter_node part; grub_disk_addr_t start, size; grub_uint8_t *ptr; part.name = 0; if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC, sizeof (vblk[i].magic)) != 0) continue; if (grub_be_to_cpu16 (vblk[i].update_status) != STATUS_CONSISTENT && grub_be_to_cpu16 (vblk[i].update_status) != STATUS_STILL_ACTIVE) continue; if (vblk[i].type != ENTRY_PARTITION) continue; part.lv = 0; part.pv = 0; ptr = vblk[i].dynamic; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { goto fail2; } /* ID */ ptr += *ptr + 1; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { goto fail2; } /* ptr = name. */ ptr += *ptr + 1; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { goto fail2; } /* skip zeros and logcommit id. */ ptr += 4 + 8; if (ptr + 16 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { goto fail2; } part.start = read_int (ptr, 8); start = read_int (ptr + 8, 8); ptr += 16; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic) || ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { goto fail2; } size = read_int (ptr + 1, *ptr); ptr += *ptr + 1; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic) || ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { goto fail2; } for (comp = vg->lvs; comp; comp = comp->next) if (comp->internal_id[0] == ptr[0] && grub_memcmp (ptr + 1, comp->internal_id + 1, comp->internal_id[0]) == 0) goto out; continue; out: if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic) || ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { goto fail2; } ptr += *ptr + 1; struct grub_diskfilter_pv *pv; for (pv = vg->pvs; pv; pv = pv->next) if (pv->internal_id[0] == ptr[0] && grub_memcmp (pv->internal_id + 1, ptr + 1, ptr[0]) == 0) part.pv = pv; if (comp->segment_alloc == 1) { unsigned node_index; ptr += *ptr + 1; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { goto fail2; } node_index = read_int (ptr + 1, *ptr); if (node_index < comp->segments->node_count) comp->segments->nodes[node_index] = part; } else { if (comp->segment_alloc == comp->segment_count) { void *t; comp->segment_alloc *= 2; t = grub_realloc (comp->segments, comp->segment_alloc * sizeof (*comp->segments)); if (!t) goto fail2; comp->segments = t; } comp->segments[comp->segment_count].start_extent = start; comp->segments[comp->segment_count].extent_count = size; comp->segments[comp->segment_count].type = GRUB_DISKFILTER_STRIPED; comp->segments[comp->segment_count].node_count = 1; comp->segments[comp->segment_count].node_alloc = 1; comp->segments[comp->segment_count].nodes = grub_malloc (sizeof (*comp->segments[comp->segment_count].nodes)); if (!comp->segments[comp->segment_count].nodes) goto fail2; comp->segments[comp->segment_count].nodes[0] = part; comp->segment_count++; } } } if (grub_diskfilter_vg_register (vg)) goto fail2; return vg; fail2: { struct grub_diskfilter_lv *lv, *next_lv; struct grub_diskfilter_pv *pv, *next_pv; for (lv = vg->lvs; lv; lv = next_lv) { unsigned i; for (i = 0; i < lv->segment_count; i++) grub_free (lv->segments[i].nodes); next_lv = lv->next; grub_free (lv->segments); grub_free (lv->internal_id); grub_free (lv->name); grub_free (lv->fullname); grub_free (lv); } for (pv = vg->pvs; pv; pv = next_pv) { next_pv = pv->next; grub_free (pv->id.uuid); grub_free (pv); } } grub_free (vg->uuid); grub_free (vg); return NULL; }