static grub_err_t read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb) { unsigned i; grub_err_t err = GRUB_ERR_NONE; for (i = 0; i < ARRAY_SIZE (superblock_sectors); i++) { struct grub_btrfs_superblock sblock; err = grub_disk_read (disk, superblock_sectors[i], 0, sizeof (sblock), &sblock); if (err == GRUB_ERR_OUT_OF_RANGE) break; if (grub_memcmp ((char *) sblock.signature, GRUB_BTRFS_SIGNATURE, sizeof (GRUB_BTRFS_SIGNATURE) - 1) != 0) break; if (i == 0 || grub_le_to_cpu64 (sblock.generation) > grub_le_to_cpu64 (sb->generation)) grub_memcpy (sb, &sblock, sizeof (sblock)); } if ((err == GRUB_ERR_OUT_OF_RANGE || !err) && i == 0) return grub_error (GRUB_ERR_BAD_FS, "not a Btrfs filesystem"); if (err == GRUB_ERR_OUT_OF_RANGE) grub_errno = err = GRUB_ERR_NONE; return err; }
static int next (struct grub_btrfs_data *data, struct grub_btrfs_leaf_descriptor *desc, grub_disk_addr_t *outaddr, grub_size_t *outsize, struct grub_btrfs_key *key_out) { grub_err_t err; struct grub_btrfs_leaf_node leaf; for (; desc->depth > 0; desc->depth--) { desc->data[desc->depth - 1].iter++; if (desc->data[desc->depth - 1].iter < desc->data[desc->depth - 1].maxiter) break; } if (desc->depth == 0) return 0; while (!desc->data[desc->depth - 1].leaf) { struct grub_btrfs_internal_node node; struct btrfs_header head; err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter * sizeof (node) + sizeof (struct btrfs_header) + desc->data[desc->depth - 1].addr, &node, sizeof (node)); if (err) return -err; err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr), &head, sizeof (head)); if (err) return -err; save_ref (desc, grub_le_to_cpu64 (node.addr), 0, grub_le_to_cpu32 (head.nitems), !head.level); } err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter * sizeof (leaf) + sizeof (struct btrfs_header) + desc->data[desc->depth - 1].addr, &leaf, sizeof (leaf)); if (err) return -err; *outsize = grub_le_to_cpu32 (leaf.size); *outaddr = desc->data[desc->depth - 1].addr + sizeof (struct btrfs_header) + grub_le_to_cpu32 (leaf.offset); *key_out = leaf.key; return 1; }
static grub_err_t lower_bound (struct grub_btrfs_data *data, const struct grub_btrfs_key *key_in, struct grub_btrfs_key *key_out, grub_disk_addr_t root, grub_disk_addr_t *outaddr, grub_size_t *outsize, struct grub_btrfs_leaf_descriptor *desc) { grub_disk_addr_t addr = root; int depth = -1; if (desc) { desc->allocated = 16; desc->depth = 0; desc->data = grub_malloc (sizeof (desc->data[0]) * desc->allocated); if (!desc->data) return grub_errno; } grub_dprintf ("btrfs", "retrieving %" PRIxGRUB_UINT64_T " %x %" PRIxGRUB_UINT64_T "\n", key_in->object_id, key_in->type, key_in->offset); while (1) { grub_err_t err; struct btrfs_header head; reiter: depth++; /* FIXME: preread few nodes into buffer. */ err = grub_btrfs_read_logical (data, addr, &head, sizeof (head)); if (err) return err; addr += sizeof (head); if (head.level) { unsigned i; struct grub_btrfs_internal_node node, node_last; int have_last = 0; grub_memset (&node_last, 0, sizeof (node_last)); for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++) { err = grub_btrfs_read_logical (data, addr + i * sizeof (node), &node, sizeof (node)); if (err) return err; grub_dprintf ("btrfs", "internal node (depth %d) %" PRIxGRUB_UINT64_T " %x %" PRIxGRUB_UINT64_T "\n", depth, node.key.object_id, node.key.type, node.key.offset); if (key_cmp (&node.key, key_in) == 0) { err = GRUB_ERR_NONE; if (desc) err = save_ref (desc, addr - sizeof (head), i, grub_le_to_cpu32 (head.nitems), 0); if (err) return err; addr = grub_le_to_cpu64 (node.addr); goto reiter; } if (key_cmp (&node.key, key_in) > 0) break; node_last = node; have_last = 1; } if (have_last) { err = GRUB_ERR_NONE; if (desc) err = save_ref (desc, addr - sizeof (head), i - 1, grub_le_to_cpu32 (head.nitems), 0); if (err) return err; addr = grub_le_to_cpu64 (node_last.addr); goto reiter; } *outsize = 0; *outaddr = 0; grub_memset (key_out, 0, sizeof (*key_out)); if (desc) return save_ref (desc, addr - sizeof (head), -1, grub_le_to_cpu32 (head.nitems), 0); return GRUB_ERR_NONE; } { unsigned i; struct grub_btrfs_leaf_node leaf, leaf_last; int have_last = 0; for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++) { err = grub_btrfs_read_logical (data, addr + i * sizeof (leaf), &leaf, sizeof (leaf)); if (err) return err; grub_dprintf ("btrfs", "leaf (depth %d) %" PRIxGRUB_UINT64_T " %x %" PRIxGRUB_UINT64_T "\n", depth, leaf.key.object_id, leaf.key.type, leaf.key.offset); if (key_cmp (&leaf.key, key_in) == 0) { grub_memcpy (key_out, &leaf.key, sizeof(*key_out)); *outsize = grub_le_to_cpu32 (leaf.size); *outaddr = addr + grub_le_to_cpu32 (leaf.offset); if (desc) return save_ref (desc, addr - sizeof (head), i, grub_le_to_cpu32 (head.nitems), 1); return GRUB_ERR_NONE; } if (key_cmp (&leaf.key, key_in) > 0) break; have_last = 1; leaf_last = leaf; } if (have_last) { grub_memcpy (key_out, &leaf_last.key, sizeof(*key_out)); *outsize = grub_le_to_cpu32 (leaf_last.size); *outaddr = addr + grub_le_to_cpu32 (leaf_last.offset); if (desc) return save_ref (desc, addr - sizeof (head), i - 1, grub_le_to_cpu32 (head.nitems), 1); return GRUB_ERR_NONE; } *outsize = 0; *outaddr = 0; grub_memset (key_out, 0, sizeof (*key_out)); if (desc) return save_ref (desc, addr - sizeof (head), -1, grub_le_to_cpu32 (head.nitems), 1); return GRUB_ERR_NONE; } } }
static struct grub_diskfilter_vg * grub_lvm_detect (grub_disk_t disk, struct grub_diskfilter_pv_id *id, grub_disk_addr_t *start_sector) { grub_err_t err; grub_uint64_t mda_offset, mda_size; char buf[GRUB_LVM_LABEL_SIZE]; char vg_id[GRUB_LVM_ID_STRLEN+1]; char pv_id[GRUB_LVM_ID_STRLEN+1]; char *metadatabuf, *p, *q, *vgname; struct grub_lvm_label_header *lh = (struct grub_lvm_label_header *) buf; struct grub_lvm_pv_header *pvh; struct grub_lvm_disk_locn *dlocn; struct grub_lvm_mda_header *mdah; struct grub_lvm_raw_locn *rlocn; unsigned int i, j, vgname_len; struct grub_diskfilter_vg *vg; struct grub_diskfilter_pv *pv; /* Search for label. */ for (i = 0; i < GRUB_LVM_LABEL_SCAN_SECTORS; i++) { err = grub_disk_read (disk, i, 0, sizeof(buf), buf); if (err) goto fail; if ((! grub_strncmp ((char *)lh->id, GRUB_LVM_LABEL_ID, sizeof (lh->id))) && (! grub_strncmp ((char *)lh->type, GRUB_LVM_LVM2_LABEL, sizeof (lh->type)))) break; } /* Return if we didn't find a label. */ if (i == GRUB_LVM_LABEL_SCAN_SECTORS) { #ifdef GRUB_UTIL grub_util_info ("no LVM signature found"); #endif goto fail; } pvh = (struct grub_lvm_pv_header *) (buf + grub_le_to_cpu32(lh->offset_xl)); for (i = 0, j = 0; i < GRUB_LVM_ID_LEN; i++) { pv_id[j++] = pvh->pv_uuid[i]; if ((i != 1) && (i != 29) && (i % 4 == 1)) pv_id[j++] = '-'; } pv_id[j] = '\0'; dlocn = pvh->disk_areas_xl; dlocn++; /* Is it possible to have multiple data/metadata areas? I haven't seen devices that have it. */ if (dlocn->offset) { grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "we don't support multiple LVM data areas"); #ifdef GRUB_UTIL grub_util_info ("we don't support multiple LVM data areas\n"); #endif goto fail; } dlocn++; mda_offset = grub_le_to_cpu64 (dlocn->offset); mda_size = grub_le_to_cpu64 (dlocn->size); /* It's possible to have multiple copies of metadata areas, we just use the first one. */ /* Allocate buffer space for the circular worst-case scenario. */ metadatabuf = grub_malloc (2 * mda_size); if (! metadatabuf) goto fail; err = grub_disk_read (disk, 0, mda_offset, mda_size, metadatabuf); if (err) goto fail2; mdah = (struct grub_lvm_mda_header *) metadatabuf; if ((grub_strncmp ((char *)mdah->magic, GRUB_LVM_FMTT_MAGIC, sizeof (mdah->magic))) || (grub_le_to_cpu32 (mdah->version) != GRUB_LVM_FMTT_VERSION)) { grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "unknown LVM metadata header"); #ifdef GRUB_UTIL grub_util_info ("unknown LVM metadata header\n"); #endif goto fail2; } rlocn = mdah->raw_locns; if (grub_le_to_cpu64 (rlocn->offset) + grub_le_to_cpu64 (rlocn->size) > grub_le_to_cpu64 (mdah->size)) { /* Metadata is circular. Copy the wrap in place. */ grub_memcpy (metadatabuf + mda_size, metadatabuf + GRUB_LVM_MDA_HEADER_SIZE, grub_le_to_cpu64 (rlocn->offset) + grub_le_to_cpu64 (rlocn->size) - grub_le_to_cpu64 (mdah->size)); } p = q = metadatabuf + grub_le_to_cpu64 (rlocn->offset); while (*q != ' ' && q < metadatabuf + mda_size) q++; if (q == metadatabuf + mda_size) { #ifdef GRUB_UTIL grub_util_info ("error parsing metadata\n"); #endif goto fail2; } vgname_len = q - p; vgname = grub_malloc (vgname_len + 1); if (!vgname) goto fail2; grub_memcpy (vgname, p, vgname_len); vgname[vgname_len] = '\0'; p = grub_strstr (q, "id = \""); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("couldn't find ID\n"); #endif goto fail3; } p += sizeof ("id = \"") - 1; grub_memcpy (vg_id, p, GRUB_LVM_ID_STRLEN); vg_id[GRUB_LVM_ID_STRLEN] = '\0'; vg = grub_diskfilter_get_vg_by_uuid (GRUB_LVM_ID_STRLEN, vg_id); if (! vg) { /* First time we see this volume group. We've to create the whole volume group structure. */ vg = grub_malloc (sizeof (*vg)); if (! vg) goto fail3; vg->name = vgname; vg->uuid = grub_malloc (GRUB_LVM_ID_STRLEN); if (! vg->uuid) goto fail3; grub_memcpy (vg->uuid, vg_id, GRUB_LVM_ID_STRLEN); vg->uuid_len = GRUB_LVM_ID_STRLEN; vg->extent_size = grub_lvm_getvalue (&p, "extent_size = "); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("unknown extent size\n"); #endif goto fail4; } vg->lvs = NULL; vg->pvs = NULL; p = grub_strstr (p, "physical_volumes {"); if (p) { p += sizeof ("physical_volumes {") - 1; /* Add all the pvs to the volume group. */ while (1) { int s; while (grub_isspace (*p)) p++; if (*p == '}') break; pv = grub_zalloc (sizeof (*pv)); q = p; while (*q != ' ') q++; s = q - p; pv->name = grub_malloc (s + 1); grub_memcpy (pv->name, p, s); pv->name[s] = '\0'; p = grub_strstr (p, "id = \""); if (p == NULL) goto pvs_fail; p += sizeof("id = \"") - 1; pv->id.uuid = grub_malloc (GRUB_LVM_ID_STRLEN); if (!pv->id.uuid) goto pvs_fail; grub_memcpy (pv->id.uuid, p, GRUB_LVM_ID_STRLEN); pv->id.uuidlen = GRUB_LVM_ID_STRLEN; pv->start_sector = grub_lvm_getvalue (&p, "pe_start = "); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("unknown pe_start\n"); #endif goto pvs_fail; } p = grub_strchr (p, '}'); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("error parsing pe_start\n"); #endif goto pvs_fail; } p++; pv->disk = NULL; pv->next = vg->pvs; vg->pvs = pv; continue; pvs_fail: grub_free (pv->name); grub_free (pv); goto fail4; } } p = grub_strstr (p, "logical_volumes"); if (p) { p += sizeof ("logical_volumes = ") - 1; /* And add all the lvs to the volume group. */ while (1) { int s; int skip_lv = 0; struct grub_diskfilter_lv *lv; struct grub_diskfilter_segment *seg; int is_pvmove; while (grub_isspace (*p)) p++; if (*p == '}') break; lv = grub_zalloc (sizeof (*lv)); q = p; while (*q != ' ') q++; s = q - p; lv->name = grub_strndup (p, s); if (!lv->name) goto lvs_fail; { const char *iptr; char *optr; lv->fullname = grub_malloc (sizeof ("lvm/") - 1 + 2 * vgname_len + 1 + 2 * s + 1); if (!lv->fullname) goto lvs_fail; grub_memcpy (lv->fullname, "lvm/", sizeof ("lvm/") - 1); optr = lv->fullname + sizeof ("lvm/") - 1; for (iptr = vgname; iptr < vgname + vgname_len; iptr++) { *optr++ = *iptr; if (*iptr == '-') *optr++ = '-'; } *optr++ = '-'; for (iptr = p; iptr < p + s; iptr++) { *optr++ = *iptr; if (*iptr == '-') *optr++ = '-'; } *optr++ = 0; lv->idname = grub_malloc (sizeof ("lvmid/") + 2 * GRUB_LVM_ID_STRLEN + 1); if (!lv->idname) goto lvs_fail; grub_memcpy (lv->idname, "lvmid/", sizeof ("lvmid/") - 1); grub_memcpy (lv->idname + sizeof ("lvmid/") - 1, vg_id, GRUB_LVM_ID_STRLEN); lv->idname[sizeof ("lvmid/") - 1 + GRUB_LVM_ID_STRLEN] = '/'; p = grub_strstr (q, "id = \""); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("couldn't find ID\n"); #endif goto lvs_fail; } p += sizeof ("id = \"") - 1; grub_memcpy (lv->idname + sizeof ("lvmid/") - 1 + GRUB_LVM_ID_STRLEN + 1, p, GRUB_LVM_ID_STRLEN); lv->idname[sizeof ("lvmid/") - 1 + 2 * GRUB_LVM_ID_STRLEN + 1] = '\0'; } lv->size = 0; lv->visible = grub_lvm_check_flag (p, "status", "VISIBLE"); is_pvmove = grub_lvm_check_flag (p, "status", "PVMOVE"); lv->segment_count = grub_lvm_getvalue (&p, "segment_count = "); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("unknown segment_count\n"); #endif goto lvs_fail; } lv->segments = grub_malloc (sizeof (*seg) * lv->segment_count); seg = lv->segments; for (i = 0; i < lv->segment_count; i++) { p = grub_strstr (p, "segment"); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("unknown segment\n"); #endif goto lvs_segment_fail; } seg->start_extent = grub_lvm_getvalue (&p, "start_extent = "); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("unknown start_extent\n"); #endif goto lvs_segment_fail; } seg->extent_count = grub_lvm_getvalue (&p, "extent_count = "); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("unknown extent_count\n"); #endif goto lvs_segment_fail; } p = grub_strstr (p, "type = \""); if (p == NULL) goto lvs_segment_fail; p += sizeof("type = \"") - 1; lv->size += seg->extent_count * vg->extent_size; if (grub_memcmp (p, "striped\"", sizeof ("striped\"") - 1) == 0) { struct grub_diskfilter_node *stripe; seg->type = GRUB_DISKFILTER_STRIPED; seg->node_count = grub_lvm_getvalue (&p, "stripe_count = "); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("unknown stripe_count\n"); #endif goto lvs_segment_fail; } if (seg->node_count != 1) seg->stripe_size = grub_lvm_getvalue (&p, "stripe_size = "); seg->nodes = grub_zalloc (sizeof (*stripe) * seg->node_count); stripe = seg->nodes; p = grub_strstr (p, "stripes = ["); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("unknown stripes\n"); #endif goto lvs_segment_fail2; } p += sizeof("stripes = [") - 1; for (j = 0; j < seg->node_count; j++) { p = grub_strchr (p, '"'); if (p == NULL) continue; q = ++p; while (*q != '"') q++; s = q - p; stripe->name = grub_malloc (s + 1); if (stripe->name == NULL) goto lvs_segment_fail2; grub_memcpy (stripe->name, p, s); stripe->name[s] = '\0'; p = q + 1; stripe->start = grub_lvm_getvalue (&p, ",") * vg->extent_size; if (p == NULL) continue; stripe++; } } else if (grub_memcmp (p, "mirror\"", sizeof ("mirror\"") - 1) == 0) { seg->type = GRUB_DISKFILTER_MIRROR; seg->node_count = grub_lvm_getvalue (&p, "mirror_count = "); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("unknown mirror_count\n"); #endif goto lvs_segment_fail; } seg->nodes = grub_zalloc (sizeof (seg->nodes[0]) * seg->node_count); p = grub_strstr (p, "mirrors = ["); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("unknown mirrors\n"); #endif goto lvs_segment_fail2; } p += sizeof("mirrors = [") - 1; for (j = 0; j < seg->node_count; j++) { char *lvname; p = grub_strchr (p, '"'); if (p == NULL) continue; q = ++p; while (*q != '"') q++; s = q - p; lvname = grub_malloc (s + 1); if (lvname == NULL) goto lvs_segment_fail2; grub_memcpy (lvname, p, s); lvname[s] = '\0'; seg->nodes[j].name = lvname; p = q + 1; } /* Only first (original) is ok with in progress pvmove. */ if (is_pvmove) seg->node_count = 1; } else if (grub_memcmp (p, "raid", sizeof ("raid") - 1) == 0 && (p[sizeof ("raid") - 1] >= '4' && p[sizeof ("raid") - 1] <= '6') && p[sizeof ("raidX") - 1] == '"') { switch (p[sizeof ("raid") - 1]) { case '4': seg->type = GRUB_DISKFILTER_RAID4; seg->layout = GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC; break; case '5': seg->type = GRUB_DISKFILTER_RAID5; seg->layout = GRUB_RAID_LAYOUT_LEFT_SYMMETRIC; break; case '6': seg->type = GRUB_DISKFILTER_RAID6; seg->layout = (GRUB_RAID_LAYOUT_RIGHT_ASYMMETRIC | GRUB_RAID_LAYOUT_MUL_FROM_POS); break; } seg->node_count = grub_lvm_getvalue (&p, "device_count = "); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("unknown device_count\n"); #endif goto lvs_segment_fail; } seg->stripe_size = grub_lvm_getvalue (&p, "stripe_size = "); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("unknown stripe_size\n"); #endif goto lvs_segment_fail; } seg->nodes = grub_zalloc (sizeof (seg->nodes[0]) * seg->node_count); p = grub_strstr (p, "raids = ["); if (p == NULL) { #ifdef GRUB_UTIL grub_util_info ("unknown mirrors\n"); #endif goto lvs_segment_fail2; } p += sizeof("raids = [") - 1; for (j = 0; j < seg->node_count; j++) { char *lvname; p = grub_strchr (p, '"'); p = p ? grub_strchr (p + 1, '"') : 0; p = p ? grub_strchr (p + 1, '"') : 0; if (p == NULL) continue; q = ++p; while (*q != '"') q++; s = q - p; lvname = grub_malloc (s + 1); if (lvname == NULL) goto lvs_segment_fail2; grub_memcpy (lvname, p, s); lvname[s] = '\0'; seg->nodes[j].name = lvname; p = q + 1; } if (seg->type == GRUB_DISKFILTER_RAID4) { char *tmp; tmp = seg->nodes[0].name; grub_memmove (seg->nodes, seg->nodes + 1, sizeof (seg->nodes[0]) * (seg->node_count - 1)); seg->nodes[seg->node_count - 1].name = tmp; } } else { #ifdef GRUB_UTIL char *p2; p2 = grub_strchr (p, '"'); if (p2) *p2 = 0; grub_util_info ("unknown LVM type %s\n", p); if (p2) *p2 ='"'; #endif /* Found a non-supported type, give up and move on. */ skip_lv = 1; break; } seg++; continue; lvs_segment_fail2: grub_free (seg->nodes); lvs_segment_fail: goto fail4; } if (p != NULL) p = grub_strchr (p, '}'); if (p == NULL) goto lvs_fail; p += 3; if (skip_lv) { grub_free (lv->name); grub_free (lv); continue; } lv->vg = vg; lv->next = vg->lvs; vg->lvs = lv; continue; lvs_fail: grub_free (lv->name); grub_free (lv); goto fail4; } } /* Match lvs. */ { struct grub_diskfilter_lv *lv1; struct grub_diskfilter_lv *lv2; for (lv1 = vg->lvs; lv1; lv1 = lv1->next) for (i = 0; i < lv1->segment_count; i++) for (j = 0; j < lv1->segments[i].node_count; j++) { if (vg->pvs) for (pv = vg->pvs; pv; pv = pv->next) { if (! grub_strcmp (pv->name, lv1->segments[i].nodes[j].name)) { lv1->segments[i].nodes[j].pv = pv; break; } } if (lv1->segments[i].nodes[j].pv == NULL) for (lv2 = vg->lvs; lv2; lv2 = lv2->next) if (grub_strcmp (lv2->name, lv1->segments[i].nodes[j].name) == 0) lv1->segments[i].nodes[j].lv = lv2; } } if (grub_diskfilter_vg_register (vg)) goto fail4; } else { grub_free (vgname); } id->uuid = grub_malloc (GRUB_LVM_ID_STRLEN); if (!id->uuid) goto fail4; grub_memcpy (id->uuid, pv_id, GRUB_LVM_ID_STRLEN); id->uuidlen = GRUB_LVM_ID_STRLEN; grub_free (metadatabuf); *start_sector = -1; return vg; /* Failure path. */ fail4: grub_free (vg); fail3: grub_free (vgname); fail2: grub_free (metadatabuf); fail: return NULL; }
static struct grub_diskfilter_vg * grub_mdraid_detect (grub_disk_t disk, struct grub_diskfilter_pv_id *id, grub_disk_addr_t *start_sector) { grub_uint64_t size; grub_uint8_t minor_version; size = grub_disk_get_size (disk); /* Check for an 1.x superblock. * It's always aligned to a 4K boundary * and depending on the minor version it can be: * 0: At least 8K, but less than 12K, from end of device * 1: At start of device * 2: 4K from start of device. */ for (minor_version = 0; minor_version < 3; ++minor_version) { grub_disk_addr_t sector = 0; struct grub_raid_super_1x sb; grub_uint16_t role; grub_uint32_t level; struct grub_diskfilter_vg *array; char *uuid; if (size == GRUB_DISK_SIZE_UNKNOWN && minor_version == 0) continue; switch (minor_version) { case 0: sector = (size - 8 * 2) & ~(4 * 2 - 1); break; case 1: sector = 0; break; case 2: sector = 4 * 2; break; } if (grub_disk_read (disk, sector, 0, sizeof (struct grub_raid_super_1x), &sb)) return NULL; if (sb.magic != grub_cpu_to_le32_compile_time (SB_MAGIC) || grub_le_to_cpu64 (sb.super_offset) != sector) continue; if (sb.major_version != grub_cpu_to_le32_compile_time (1)) /* Unsupported version. */ return NULL; level = grub_le_to_cpu32 (sb.level); /* Multipath. */ if ((int) level == -4) level = 1; if (level != 0 && level != 1 && level != 4 && level != 5 && level != 6 && level != 10) { grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "Unsupported RAID level: %d", sb.level); return NULL; } if (grub_le_to_cpu32 (sb.dev_number) >= grub_le_to_cpu32 (sb.max_dev)) /* Spares aren't implemented. */ return NULL; if (grub_disk_read (disk, sector, (char *) &sb.dev_roles[grub_le_to_cpu32 (sb.dev_number)] - (char *) &sb, sizeof (role), &role)) return NULL; if (grub_le_to_cpu16 (role) >= grub_le_to_cpu32 (sb.raid_disks)) /* Spares aren't implemented. */ return NULL; id->uuidlen = 0; id->id = grub_le_to_cpu16 (role); uuid = grub_malloc (16); if (!uuid) return NULL; grub_memcpy (uuid, sb.set_uuid, 16); *start_sector = grub_le_to_cpu64 (sb.data_offset); array = grub_diskfilter_make_raid (16, uuid, grub_le_to_cpu32 (sb.raid_disks), sb.set_name, (sb.size) ? grub_le_to_cpu64 (sb.size) : grub_le_to_cpu64 (sb.data_size), grub_le_to_cpu32 (sb.chunksize), grub_le_to_cpu32 (sb.layout), grub_le_to_cpu32 (sb.level)); return array; } /* not 1.x raid. */ return NULL; }
static grub_err_t grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array, grub_disk_addr_t *start_sector) { grub_disk_addr_t sector = 0; grub_uint64_t size; struct grub_raid_super_1x sb; grub_uint8_t minor_version; /* The sector where the mdraid 0.90 superblock is stored, if available. */ size = grub_disk_get_size (disk); /* Check for an 1.x superblock. * It's always aligned to a 4K boundary * and depending on the minor version it can be: * 0: At least 8K, but less than 12K, from end of device * 1: At start of device * 2: 4K from start of device. */ for (minor_version = 0; minor_version < 3; ++minor_version) { if (size == GRUB_DISK_SIZE_UNKNOWN && minor_version == 0) continue; switch (minor_version) { case 0: sector = (size - 8 * 2) & ~(4 * 2 - 1); break; case 1: sector = 0; break; case 2: sector = 4 * 2; break; } if (grub_disk_read (disk, sector, 0, sizeof (struct grub_raid_super_1x), &sb)) return grub_errno; if (grub_le_to_cpu32 (sb.magic) != SB_MAGIC || grub_le_to_cpu64 (sb.super_offset) != sector) continue; { grub_uint64_t sb_size; struct grub_raid_super_1x *real_sb; grub_uint32_t level; if (grub_le_to_cpu32 (sb.major_version) != 1) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "Unsupported RAID version: %d", grub_le_to_cpu32 (sb.major_version)); level = grub_le_to_cpu32 (sb.level); /* Multipath. */ if ((int) level == -4) level = 1; if (level != 0 && level != 1 && level != 4 && level != 5 && level != 6 && level != 10) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "Unsupported RAID level: %d", sb.level); /* 1.x superblocks don't have a fixed size on disk. So we have to read it again now that we now the max device count. */ sb_size = sizeof (struct grub_raid_super_1x) + 2 * grub_le_to_cpu32 (sb.max_dev); real_sb = grub_malloc (sb_size); if (! real_sb) return grub_errno; if (grub_disk_read (disk, sector, 0, sb_size, real_sb)) { grub_free (real_sb); return grub_errno; } array->name = grub_strdup (real_sb->set_name); if (! array->name) { grub_free (real_sb); return grub_errno; } array->number = 0; array->level = grub_le_to_cpu32 (real_sb->level); array->layout = grub_le_to_cpu32 (real_sb->layout); array->total_devs = grub_le_to_cpu32 (real_sb->raid_disks); array->disk_size = grub_le_to_cpu64 (real_sb->size); array->chunk_size = grub_le_to_cpu32 (real_sb->chunksize); if (grub_le_to_cpu32 (real_sb->dev_number) >= grub_le_to_cpu32 (real_sb->max_dev)) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "spares aren't implemented"); array->index = grub_le_to_cpu16 (real_sb->dev_roles[grub_le_to_cpu32 (real_sb->dev_number)]); array->uuid_len = 16; array->uuid = grub_malloc (16); if (!array->uuid) { grub_free (real_sb); return grub_errno; } grub_memcpy (array->uuid, real_sb->set_uuid, 16); *start_sector = grub_le_to_cpu64 (real_sb->data_offset); grub_free (real_sb); return 0; } } return grub_error (GRUB_ERR_OUT_OF_RANGE, "not 1.x raid"); }
static char * get_btrfs_fs_prefix (const char *mount_path) { struct btrfs_ioctl_ino_lookup_args args; struct stat st; int fd; grub_uint64_t tree_id, inode_id; char *ret = NULL; fd = open (mount_path, O_RDONLY); if (fd < 0) return NULL; memset (&args, 0, sizeof(args)); args.objectid = GRUB_BTRFS_TREE_ROOT_OBJECTID; if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0) goto fail; tree_id = args.treeid; if (fstat (fd, &st) < 0) goto fail; inode_id = st.st_ino; while (tree_id != GRUB_BTRFS_ROOT_VOL_OBJECTID || inode_id != GRUB_BTRFS_TREE_ROOT_OBJECTID) { const char *name; size_t namelen; struct btrfs_ioctl_search_args sargs; char *old; memset (&sargs, 0, sizeof(sargs)); if (inode_id == GRUB_BTRFS_TREE_ROOT_OBJECTID) { struct grub_btrfs_root_backref *br; sargs.key.tree_id = 1; sargs.key.min_objectid = tree_id; sargs.key.max_objectid = tree_id; sargs.key.min_offset = 0; sargs.key.max_offset = ~0ULL; sargs.key.min_transid = 0; sargs.key.max_transid = ~0ULL; sargs.key.min_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF; sargs.key.max_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF; sargs.key.nr_items = 1; if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0) goto fail; if (sargs.key.nr_items == 0) goto fail; tree_id = sargs.buf[2]; br = (struct grub_btrfs_root_backref *) (sargs.buf + 4); inode_id = grub_le_to_cpu64 (br->inode_id); name = br->name; namelen = grub_le_to_cpu16 (br->n); } else { struct grub_btrfs_inode_ref *ir; sargs.key.tree_id = tree_id; sargs.key.min_objectid = inode_id; sargs.key.max_objectid = inode_id; sargs.key.min_offset = 0; sargs.key.max_offset = ~0ULL; sargs.key.min_transid = 0; sargs.key.max_transid = ~0ULL; sargs.key.min_type = GRUB_BTRFS_ITEM_TYPE_INODE_REF; sargs.key.max_type = GRUB_BTRFS_ITEM_TYPE_INODE_REF; if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0) goto fail; if (sargs.key.nr_items == 0) goto fail; inode_id = sargs.buf[2]; ir = (struct grub_btrfs_inode_ref *) (sargs.buf + 4); name = ir->name; namelen = grub_le_to_cpu16 (ir->n); } old = ret; ret = xmalloc (namelen + (old ? strlen (old) : 0) + 2); ret[0] = '/'; memcpy (ret + 1, name, namelen); if (old) { strcpy (ret + 1 + namelen, old); free (old); } else ret[1+namelen] = '\0'; } if (!ret) ret = xstrdup ("/"); close (fd); return ret; fail: free (ret); close (fd); return NULL; }