MFC r199017,199228

Fix handling of GPT headers when size is > 92 bytes.

This should address reading GPT headers written by opensolaris.
This commit is contained in:
Robert Noland 2009-11-21 14:53:08 +00:00
parent 1c7de26f34
commit 4cb9493f3f

View file

@ -73,7 +73,7 @@ enum gpt_state {
struct g_part_gpt_table {
struct g_part_table base;
u_char mbr[MBRSIZE];
struct gpt_hdr hdr;
struct gpt_hdr *hdr;
quad_t lba[GPT_ELT_COUNT];
enum gpt_state state[GPT_ELT_COUNT];
};
@ -143,13 +143,12 @@ static struct uuid gpt_uuid_linux_swap = GPT_ENT_TYPE_LINUX_SWAP;
static struct uuid gpt_uuid_mbr = GPT_ENT_TYPE_MBR;
static struct uuid gpt_uuid_unused = GPT_ENT_TYPE_UNUSED;
static void
static struct gpt_hdr *
gpt_read_hdr(struct g_part_gpt_table *table, struct g_consumer *cp,
enum gpt_elt elt, struct gpt_hdr *hdr)
enum gpt_elt elt)
{
struct uuid uuid;
struct gpt_hdr *buf, *hdr;
struct g_provider *pp;
char *buf;
quad_t lba, last;
int error;
uint32_t crc, sz;
@ -161,63 +160,75 @@ gpt_read_hdr(struct g_part_gpt_table *table, struct g_consumer *cp,
buf = g_read_data(cp, table->lba[elt] * pp->sectorsize, pp->sectorsize,
&error);
if (buf == NULL)
return;
bcopy(buf, hdr, sizeof(*hdr));
if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0)
return;
return (NULL);
hdr = NULL;
if (memcmp(buf->hdr_sig, GPT_HDR_SIG, sizeof(buf->hdr_sig)) != 0)
goto fail;
table->state[elt] = GPT_STATE_CORRUPT;
sz = le32toh(hdr->hdr_size);
sz = le32toh(buf->hdr_size);
if (sz < 92 || sz > pp->sectorsize)
return;
crc = le32toh(hdr->hdr_crc_self);
hdr->hdr_crc_self = 0;
if (crc32(hdr, sz) != crc)
return;
goto fail;
hdr = g_malloc(sz, M_WAITOK | M_ZERO);
bcopy(buf, hdr, sz);
hdr->hdr_size = sz;
crc = le32toh(buf->hdr_crc_self);
buf->hdr_crc_self = 0;
if (crc32(buf, sz) != crc)
goto fail;
hdr->hdr_crc_self = crc;
table->state[elt] = GPT_STATE_INVALID;
hdr->hdr_revision = le32toh(hdr->hdr_revision);
hdr->hdr_revision = le32toh(buf->hdr_revision);
if (hdr->hdr_revision < 0x00010000)
return;
hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self);
goto fail;
hdr->hdr_lba_self = le64toh(buf->hdr_lba_self);
if (hdr->hdr_lba_self != table->lba[elt])
return;
hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt);
goto fail;
hdr->hdr_lba_alt = le64toh(buf->hdr_lba_alt);
/* Check the managed area. */
hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start);
hdr->hdr_lba_start = le64toh(buf->hdr_lba_start);
if (hdr->hdr_lba_start < 2 || hdr->hdr_lba_start >= last)
return;
hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end);
goto fail;
hdr->hdr_lba_end = le64toh(buf->hdr_lba_end);
if (hdr->hdr_lba_end < hdr->hdr_lba_start || hdr->hdr_lba_end >= last)
return;
goto fail;
/* Check the table location and size of the table. */
hdr->hdr_entries = le32toh(hdr->hdr_entries);
hdr->hdr_entsz = le32toh(hdr->hdr_entsz);
hdr->hdr_entries = le32toh(buf->hdr_entries);
hdr->hdr_entsz = le32toh(buf->hdr_entsz);
if (hdr->hdr_entries == 0 || hdr->hdr_entsz < 128 ||
(hdr->hdr_entsz & 7) != 0)
return;
hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table);
goto fail;
hdr->hdr_lba_table = le64toh(buf->hdr_lba_table);
if (hdr->hdr_lba_table < 2 || hdr->hdr_lba_table >= last)
return;
goto fail;
if (hdr->hdr_lba_table >= hdr->hdr_lba_start &&
hdr->hdr_lba_table <= hdr->hdr_lba_end)
return;
goto fail;
lba = hdr->hdr_lba_table +
(hdr->hdr_entries * hdr->hdr_entsz + pp->sectorsize - 1) /
pp->sectorsize - 1;
if (lba >= last)
return;
goto fail;
if (lba >= hdr->hdr_lba_start && lba <= hdr->hdr_lba_end)
return;
goto fail;
table->state[elt] = GPT_STATE_OK;
le_uuid_dec(&hdr->hdr_uuid, &uuid);
hdr->hdr_uuid = uuid;
hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table);
le_uuid_dec(&buf->hdr_uuid, &hdr->hdr_uuid);
hdr->hdr_crc_table = le32toh(buf->hdr_crc_table);
g_free(buf);
return (hdr);
fail:
if (hdr != NULL)
g_free(hdr);
g_free(buf);
return (NULL);
}
static struct gpt_ent *
@ -230,6 +241,9 @@ gpt_read_tbl(struct g_part_gpt_table *table, struct g_consumer *cp,
unsigned int idx, sectors, tblsz;
int error;
if (hdr == NULL)
return (NULL);
pp = cp->provider;
table->lba[elt] = hdr->hdr_lba_table;
@ -271,6 +285,9 @@ static int
gpt_matched_hdrs(struct gpt_hdr *pri, struct gpt_hdr *sec)
{
if (pri == NULL || sec == NULL)
return (0);
if (!EQUUID(&pri->hdr_uuid, &sec->hdr_uuid))
return (0);
return ((pri->hdr_revision == sec->hdr_revision &&
@ -427,17 +444,20 @@ g_part_gpt_create(struct g_part_table *basetable, struct g_part_parms *gpp)
table->lba[GPT_ELT_SECHDR] = last;
table->lba[GPT_ELT_SECTBL] = last - tblsz;
bcopy(GPT_HDR_SIG, table->hdr.hdr_sig, sizeof(table->hdr.hdr_sig));
table->hdr.hdr_revision = GPT_HDR_REVISION;
table->hdr.hdr_size = offsetof(struct gpt_hdr, padding);
table->hdr.hdr_lba_start = 2 + tblsz;
table->hdr.hdr_lba_end = last - tblsz - 1;
kern_uuidgen(&table->hdr.hdr_uuid, 1);
table->hdr.hdr_entries = basetable->gpt_entries;
table->hdr.hdr_entsz = sizeof(struct gpt_ent);
/* Allocate space for the header */
table->hdr = g_malloc(sizeof(struct gpt_hdr), M_WAITOK | M_ZERO);
basetable->gpt_first = table->hdr.hdr_lba_start;
basetable->gpt_last = table->hdr.hdr_lba_end;
bcopy(GPT_HDR_SIG, table->hdr->hdr_sig, sizeof(table->hdr->hdr_sig));
table->hdr->hdr_revision = GPT_HDR_REVISION;
table->hdr->hdr_size = offsetof(struct gpt_hdr, padding);
table->hdr->hdr_lba_start = 2 + tblsz;
table->hdr->hdr_lba_end = last - tblsz - 1;
kern_uuidgen(&table->hdr->hdr_uuid, 1);
table->hdr->hdr_entries = basetable->gpt_entries;
table->hdr->hdr_entsz = sizeof(struct gpt_ent);
basetable->gpt_first = table->hdr->hdr_lba_start;
basetable->gpt_last = table->hdr->hdr_lba_end;
return (0);
}
@ -582,7 +602,7 @@ g_part_gpt_probe(struct g_part_table *table, struct g_consumer *cp)
static int
g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp)
{
struct gpt_hdr prihdr, sechdr;
struct gpt_hdr *prihdr, *sechdr;
struct gpt_ent *tbl, *pritbl, *sectbl;
struct g_provider *pp;
struct g_part_gpt_table *table;
@ -601,18 +621,18 @@ g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp)
g_free(buf);
/* Read the primary header and table. */
gpt_read_hdr(table, cp, GPT_ELT_PRIHDR, &prihdr);
prihdr = gpt_read_hdr(table, cp, GPT_ELT_PRIHDR);
if (table->state[GPT_ELT_PRIHDR] == GPT_STATE_OK) {
pritbl = gpt_read_tbl(table, cp, GPT_ELT_PRITBL, &prihdr);
pritbl = gpt_read_tbl(table, cp, GPT_ELT_PRITBL, prihdr);
} else {
table->state[GPT_ELT_PRITBL] = GPT_STATE_MISSING;
pritbl = NULL;
}
/* Read the secondary header and table. */
gpt_read_hdr(table, cp, GPT_ELT_SECHDR, &sechdr);
sechdr = gpt_read_hdr(table, cp, GPT_ELT_SECHDR);
if (table->state[GPT_ELT_SECHDR] == GPT_STATE_OK) {
sectbl = gpt_read_tbl(table, cp, GPT_ELT_SECTBL, &sechdr);
sectbl = gpt_read_tbl(table, cp, GPT_ELT_SECTBL, sechdr);
} else {
table->state[GPT_ELT_SECTBL] = GPT_STATE_MISSING;
sectbl = NULL;
@ -635,13 +655,17 @@ g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp)
*/
if (table->state[GPT_ELT_PRIHDR] == GPT_STATE_OK &&
table->state[GPT_ELT_SECHDR] == GPT_STATE_OK &&
!gpt_matched_hdrs(&prihdr, &sechdr)) {
!gpt_matched_hdrs(prihdr, sechdr)) {
if (table->state[GPT_ELT_PRITBL] == GPT_STATE_OK) {
table->state[GPT_ELT_SECHDR] = GPT_STATE_INVALID;
table->state[GPT_ELT_SECTBL] = GPT_STATE_MISSING;
g_free(sechdr);
sechdr = NULL;
} else {
table->state[GPT_ELT_PRIHDR] = GPT_STATE_INVALID;
table->state[GPT_ELT_PRITBL] = GPT_STATE_MISSING;
g_free(prihdr);
prihdr = NULL;
}
}
@ -651,6 +675,8 @@ g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp)
printf("GEOM: %s: using the secondary instead -- recovery "
"strongly advised.\n", pp->name);
table->hdr = sechdr;
if (prihdr != NULL)
g_free(prihdr);
tbl = sectbl;
if (pritbl != NULL)
g_free(pritbl);
@ -662,14 +688,16 @@ g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp)
"suggested.\n", pp->name);
}
table->hdr = prihdr;
if (sechdr != NULL)
g_free(sechdr);
tbl = pritbl;
if (sectbl != NULL)
g_free(sectbl);
}
basetable->gpt_first = table->hdr.hdr_lba_start;
basetable->gpt_last = table->hdr.hdr_lba_end;
basetable->gpt_entries = table->hdr.hdr_entries;
basetable->gpt_first = table->hdr->hdr_lba_start;
basetable->gpt_last = table->hdr->hdr_lba_end;
basetable->gpt_entries = table->hdr->hdr_entries;
for (index = basetable->gpt_entries - 1; index >= 0; index--) {
if (EQUUID(&tbl[index].ent_type, &gpt_uuid_unused))
@ -729,7 +757,7 @@ g_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp)
pp = cp->provider;
table = (struct g_part_gpt_table *)basetable;
tlbsz = (table->hdr.hdr_entries * table->hdr.hdr_entsz +
tlbsz = (table->hdr->hdr_entries * table->hdr->hdr_entsz +
pp->sectorsize - 1) / pp->sectorsize;
/* Write the PMBR */
@ -743,21 +771,21 @@ g_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp)
/* Allocate space for the header and entries. */
buf = g_malloc((tlbsz + 1) * pp->sectorsize, M_WAITOK | M_ZERO);
memcpy(buf, table->hdr.hdr_sig, sizeof(table->hdr.hdr_sig));
le32enc(buf + 8, table->hdr.hdr_revision);
le32enc(buf + 12, table->hdr.hdr_size);
le64enc(buf + 40, table->hdr.hdr_lba_start);
le64enc(buf + 48, table->hdr.hdr_lba_end);
le_uuid_enc(buf + 56, &table->hdr.hdr_uuid);
le32enc(buf + 80, table->hdr.hdr_entries);
le32enc(buf + 84, table->hdr.hdr_entsz);
memcpy(buf, table->hdr->hdr_sig, sizeof(table->hdr->hdr_sig));
le32enc(buf + 8, table->hdr->hdr_revision);
le32enc(buf + 12, table->hdr->hdr_size);
le64enc(buf + 40, table->hdr->hdr_lba_start);
le64enc(buf + 48, table->hdr->hdr_lba_end);
le_uuid_enc(buf + 56, &table->hdr->hdr_uuid);
le32enc(buf + 80, table->hdr->hdr_entries);
le32enc(buf + 84, table->hdr->hdr_entsz);
LIST_FOREACH(baseentry, &basetable->gpt_entry, gpe_entry) {
if (baseentry->gpe_deleted)
continue;
entry = (struct g_part_gpt_entry *)baseentry;
index = baseentry->gpe_index - 1;
bp = buf + pp->sectorsize + table->hdr.hdr_entsz * index;
bp = buf + pp->sectorsize + table->hdr->hdr_entsz * index;
le_uuid_enc(bp, &entry->ent.ent_type);
le_uuid_enc(bp + 16, &entry->ent.ent_uuid);
le64enc(bp + 32, entry->ent.ent_lba_start);
@ -768,7 +796,7 @@ g_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp)
}
crc = crc32(buf + pp->sectorsize,
table->hdr.hdr_entries * table->hdr.hdr_entsz);
table->hdr->hdr_entries * table->hdr->hdr_entsz);
le32enc(buf + 88, crc);
/* Write primary meta-data. */
@ -776,7 +804,7 @@ g_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp)
le64enc(buf + 24, table->lba[GPT_ELT_PRIHDR]); /* hdr_lba_self. */
le64enc(buf + 32, table->lba[GPT_ELT_SECHDR]); /* hdr_lba_alt. */
le64enc(buf + 72, table->lba[GPT_ELT_PRITBL]); /* hdr_lba_table. */
crc = crc32(buf, table->hdr.hdr_size);
crc = crc32(buf, table->hdr->hdr_size);
le32enc(buf + 16, crc);
error = g_write_data(cp, table->lba[GPT_ELT_PRITBL] * pp->sectorsize,
@ -793,7 +821,7 @@ g_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp)
le64enc(buf + 24, table->lba[GPT_ELT_SECHDR]); /* hdr_lba_self. */
le64enc(buf + 32, table->lba[GPT_ELT_PRIHDR]); /* hdr_lba_alt. */
le64enc(buf + 72, table->lba[GPT_ELT_SECTBL]); /* hdr_lba_table. */
crc = crc32(buf, table->hdr.hdr_size);
crc = crc32(buf, table->hdr->hdr_size);
le32enc(buf + 16, crc);
error = g_write_data(cp, table->lba[GPT_ELT_SECTBL] * pp->sectorsize,