a16f4a822f
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>
1052 lines
26 KiB
C
1052 lines
26 KiB
C
/*
|
||
* 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, §or);
|
||
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);
|
||
}
|