grub/grub-core/disk/ldm.c
Nicholas Vinson e6cc6f328a disk: Update grub_gpt_partentry
Rename grub_gpt_part_type to grub_gpt_part_guid and update grub_gpt_partentry
to use this type for both the partition type GUID string and the partition GUID
string entries.  This change ensures that the two GUID fields are handled more
consistently and helps to simplify the changes needed to add Linux partition
GUID support.

Signed-off-by: Nicholas Vinson <nvinson234@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
2020-09-21 13:19:10 -04:00

1052 lines
26 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2006,2007,2008,2009,2011 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/dl.h>
#include <grub/disk.h>
#include <grub/mm.h>
#include <grub/err.h>
#include <grub/misc.h>
#include <grub/diskfilter.h>
#include <grub/msdos_partition.h>
#include <grub/gpt_partition.h>
#include <grub/i18n.h>
#ifdef GRUB_UTIL
#include <grub/emu/misc.h>
#include <grub/emu/hostdisk.h>
#endif
GRUB_MOD_LICENSE ("GPLv3+");
#define LDM_GUID_STRLEN 64
#define LDM_NAME_STRLEN 32
typedef grub_uint8_t *grub_ldm_id_t;
enum { STRIPE = 1, SPANNED = 2, RAID5 = 3 };
#define LDM_LABEL_SECTOR 6
struct grub_ldm_vblk {
char magic[4];
grub_uint8_t unused1[12];
grub_uint16_t update_status;
grub_uint8_t flags;
grub_uint8_t type;
grub_uint32_t unused2;
grub_uint8_t dynamic[104];
} GRUB_PACKED;
#define LDM_VBLK_MAGIC "VBLK"
enum
{
STATUS_CONSISTENT = 0,
STATUS_STILL_ACTIVE = 1,
STATUS_NOT_ACTIVE_YET = 2
};
enum
{
ENTRY_COMPONENT = 0x32,
ENTRY_PARTITION = 0x33,
ENTRY_DISK = 0x34,
ENTRY_VOLUME = 0x51,
};
struct grub_ldm_label
{
char magic[8];
grub_uint32_t unused1;
grub_uint16_t ver_major;
grub_uint16_t ver_minor;
grub_uint8_t unused2[32];
char disk_guid[LDM_GUID_STRLEN];
char host_guid[LDM_GUID_STRLEN];
char group_guid[LDM_GUID_STRLEN];
char group_name[LDM_NAME_STRLEN];
grub_uint8_t unused3[11];
grub_uint64_t pv_start;
grub_uint64_t pv_size;
grub_uint64_t config_start;
grub_uint64_t config_size;
} GRUB_PACKED;
#define LDM_MAGIC "PRIVHEAD"
static inline grub_uint64_t
read_int (grub_uint8_t *in, grub_size_t s)
{
grub_uint8_t *ptr2;
grub_uint64_t ret;
ret = 0;
for (ptr2 = in; ptr2 < in + s; ptr2++)
{
ret <<= 8;
ret |= *ptr2;
}
return ret;
}
static int
check_ldm_partition (grub_disk_t disk __attribute__ ((unused)), const grub_partition_t p, void *data)
{
int *has_ldm = data;
if (p->number >= 4)
return 1;
if (p->msdostype == GRUB_PC_PARTITION_TYPE_LDM)
{
*has_ldm = 1;
return 1;
}
return 0;
}
static int
msdos_has_ldm_partition (grub_disk_t dsk)
{
grub_err_t err;
int has_ldm = 0;
err = grub_partition_msdos_iterate (dsk, check_ldm_partition, &has_ldm);
if (err)
{
grub_errno = GRUB_ERR_NONE;
return 0;
}
return has_ldm;
}
static const grub_gpt_part_guid_t ldm_type = GRUB_GPT_PARTITION_TYPE_LDM;
/* Helper for gpt_ldm_sector. */
static int
gpt_ldm_sector_iter (grub_disk_t disk, const grub_partition_t p, void *data)
{
grub_disk_addr_t *sector = data;
struct grub_gpt_partentry gptdata;
grub_partition_t p2;
p2 = disk->partition;
disk->partition = p->parent;
if (grub_disk_read (disk, p->offset, p->index,
sizeof (gptdata), &gptdata))
{
disk->partition = p2;
return 0;
}
disk->partition = p2;
if (! grub_memcmp (&gptdata.type, &ldm_type, 16))
{
*sector = p->start + p->len - 1;
return 1;
}
return 0;
}
static grub_disk_addr_t
gpt_ldm_sector (grub_disk_t dsk)
{
grub_disk_addr_t sector = 0;
grub_err_t err;
err = grub_gpt_partition_map_iterate (dsk, gpt_ldm_sector_iter, &sector);
if (err)
{
grub_errno = GRUB_ERR_NONE;
return 0;
}
return sector;
}
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;
}
static struct grub_diskfilter_vg *
grub_ldm_detect (grub_disk_t disk,
struct grub_diskfilter_pv_id *id,
grub_disk_addr_t *start_sector)
{
grub_err_t err;
struct grub_ldm_label label;
struct grub_diskfilter_vg *vg;
#ifdef GRUB_UTIL
grub_util_info ("scanning %s for LDM", disk->name);
#endif
{
int i;
int has_ldm = msdos_has_ldm_partition (disk);
for (i = 0; i < 3; i++)
{
grub_disk_addr_t sector = LDM_LABEL_SECTOR;
switch (i)
{
case 0:
if (!has_ldm)
continue;
sector = LDM_LABEL_SECTOR;
break;
case 1:
/* LDM is never inside a partition. */
if (!has_ldm || disk->partition)
continue;
sector = grub_disk_get_size (disk);
if (sector == GRUB_DISK_SIZE_UNKNOWN)
continue;
sector--;
break;
/* FIXME: try the third copy. */
case 2:
sector = gpt_ldm_sector (disk);
if (!sector)
continue;
break;
}
err = grub_disk_read (disk, sector, 0,
sizeof(label), &label);
if (err)
return NULL;
if (grub_memcmp (label.magic, LDM_MAGIC, sizeof (label.magic)) == 0
&& grub_be_to_cpu16 (label.ver_major) == 0x02
&& grub_be_to_cpu16 (label.ver_minor) >= 0x0b
&& grub_be_to_cpu16 (label.ver_minor) <= 0x0c)
break;
}
/* Return if we didn't find a label. */
if (i == 3)
{
#ifdef GRUB_UTIL
grub_util_info ("no LDM signature found");
#endif
return NULL;
}
}
id->uuid = grub_malloc (LDM_GUID_STRLEN + 1);
if (!id->uuid)
return NULL;
grub_memcpy (id->uuid, label.disk_guid, LDM_GUID_STRLEN);
id->uuid[LDM_GUID_STRLEN] = 0;
id->uuidlen = grub_strlen ((char *) id->uuid);
*start_sector = grub_be_to_cpu64 (label.pv_start);
{
grub_size_t s;
for (s = 0; s < LDM_GUID_STRLEN && label.group_guid[s]; s++);
vg = grub_diskfilter_get_vg_by_uuid (s, label.group_guid);
if (! vg)
vg = make_vg (disk, &label);
}
if (!vg)
{
grub_free (id->uuid);
return NULL;
}
return vg;
}
#ifdef GRUB_UTIL
char *
grub_util_get_ldm (grub_disk_t disk, grub_disk_addr_t start)
{
struct grub_diskfilter_pv *pv = NULL;
struct grub_diskfilter_vg *vg = NULL;
struct grub_diskfilter_lv *res = 0, *lv, *res_lv = 0;
pv = grub_diskfilter_get_pv_from_disk (disk, &vg);
if (!pv)
return NULL;
for (lv = vg->lvs; lv; lv = lv->next)
if (lv->segment_count == 1 && lv->segments->node_count == 1
&& lv->segments->type == GRUB_DISKFILTER_STRIPED
&& lv->segments->nodes->pv == pv
&& lv->segments->nodes->start + pv->start_sector == start)
{
res_lv = lv;
break;
}
if (!res_lv)
return NULL;
for (lv = vg->lvs; lv; lv = lv->next)
if (lv->segment_count == 1 && lv->segments->node_count == 1
&& lv->segments->type == GRUB_DISKFILTER_MIRROR
&& lv->segments->nodes->lv == res_lv)
{
res = lv;
break;
}
if (res && res->fullname)
return grub_strdup (res->fullname);
return NULL;
}
int
grub_util_is_ldm (grub_disk_t disk)
{
int i;
int has_ldm = msdos_has_ldm_partition (disk);
for (i = 0; i < 3; i++)
{
grub_disk_addr_t sector = LDM_LABEL_SECTOR;
grub_err_t err;
struct grub_ldm_label label;
switch (i)
{
case 0:
if (!has_ldm)
continue;
sector = LDM_LABEL_SECTOR;
break;
case 1:
/* LDM is never inside a partition. */
if (!has_ldm || disk->partition)
continue;
sector = grub_disk_get_size (disk);
if (sector == GRUB_DISK_SIZE_UNKNOWN)
continue;
sector--;
break;
/* FIXME: try the third copy. */
case 2:
sector = gpt_ldm_sector (disk);
if (!sector)
continue;
break;
}
err = grub_disk_read (disk, sector, 0, sizeof(label), &label);
if (err)
{
grub_errno = GRUB_ERR_NONE;
return 0;
}
/* This check is more relaxed on purpose. */
if (grub_memcmp (label.magic, LDM_MAGIC, sizeof (label.magic)) == 0)
return 1;
}
return 0;
}
grub_err_t
grub_util_ldm_embed (struct grub_disk *disk, unsigned int *nsectors,
unsigned int max_nsectors,
grub_embed_type_t embed_type,
grub_disk_addr_t **sectors)
{
struct grub_diskfilter_pv *pv = NULL;
struct grub_diskfilter_vg *vg;
struct grub_diskfilter_lv *lv;
unsigned i;
if (embed_type != GRUB_EMBED_PCBIOS)
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"LDM currently supports only PC-BIOS embedding");
if (disk->partition)
return grub_error (GRUB_ERR_BUG, "disk isn't LDM");
pv = grub_diskfilter_get_pv_from_disk (disk, &vg);
if (!pv)
return grub_error (GRUB_ERR_BUG, "disk isn't LDM");
for (lv = vg->lvs; lv; lv = lv->next)
{
struct grub_diskfilter_lv *comp;
if (!lv->visible || !lv->fullname)
continue;
if (lv->segment_count != 1)
continue;
if (lv->segments->type != GRUB_DISKFILTER_MIRROR
|| lv->segments->node_count != 1
|| lv->segments->start_extent != 0
|| lv->segments->extent_count != lv->size)
continue;
comp = lv->segments->nodes->lv;
if (!comp)
continue;
if (comp->segment_count != 1 || comp->size != lv->size)
continue;
if (comp->segments->type != GRUB_DISKFILTER_STRIPED
|| comp->segments->node_count != 1
|| comp->segments->start_extent != 0
|| comp->segments->extent_count != lv->size)
continue;
/* How to implement proper check is to be discussed. */
#if 1
if (1)
continue;
#else
if (grub_strcmp (lv->name, "Volume5") != 0)
continue;
#endif
if (lv->size < *nsectors)
return grub_error (GRUB_ERR_OUT_OF_RANGE,
/* TRANSLATORS: it's a partition for embedding,
not a partition embed into something. GRUB
install tools put core.img into a place
usable for bootloaders (called generically
"embedding zone") and this operation is
called "embedding". */
N_("your LDM Embedding Partition is too small;"
" embedding won't be possible"));
*nsectors = lv->size;
if (*nsectors > max_nsectors)
*nsectors = max_nsectors;
*sectors = grub_malloc (*nsectors * sizeof (**sectors));
if (!*sectors)
return grub_errno;
for (i = 0; i < *nsectors; i++)
(*sectors)[i] = (lv->segments->nodes->start
+ comp->segments->nodes->start
+ comp->segments->nodes->pv->start_sector + i);
return GRUB_ERR_NONE;
}
return grub_error (GRUB_ERR_FILE_NOT_FOUND,
/* TRANSLATORS: it's a partition for embedding,
not a partition embed into something. */
N_("this LDM has no Embedding Partition;"
" embedding won't be possible"));
}
#endif
static struct grub_diskfilter grub_ldm_dev = {
.name = "ldm",
.detect = grub_ldm_detect,
.next = 0
};
GRUB_MOD_INIT (ldm)
{
grub_diskfilter_register_back (&grub_ldm_dev);
}
GRUB_MOD_FINI (ldm)
{
grub_diskfilter_unregister (&grub_ldm_dev);
}