bdev/gpt: support parsing secondary partition table

Modify existing code of parsing primary partition table to support parsing
the secondary.

Main difference of these two tables is that they have inverse buffer layout.
For primary table, header is in front of partition entries. And for secondary
table, header is after partition entries. So add helper functions to extract
header and partition entries buffer region from primary or secondary table
based on current parse phase.

Split the exported funtion spdk_gpt_parse into two functions spdk_gpt_parse_mbr
and spdk_gpt_parse_partition_table. So spdk_gpt_parse_partition_table could be
used to parse both primary and secondary table.

Change-Id: I7f7827e0ee7e3f1b2e88c56607ee5b702fb2490c
Signed-off-by: lorneli <lorneli@163.com>
Reviewed-on: https://review.gerrithub.io/c/441200
Reviewed-by: wuzhouhui <wuzhouhui@kingsoft.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: GangCao <gang.cao@intel.com>
Reviewed-by: Darek Stojaczyk <dariusz.stojaczyk@intel.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
This commit is contained in:
lorneli 2019-01-19 14:56:14 +08:00 committed by Darek Stojaczyk
parent 2240a8b2ef
commit 0744f10860
4 changed files with 188 additions and 27 deletions

View File

@ -44,6 +44,65 @@
#define GPT_PROTECTIVE_MBR 1
#define SPDK_MAX_NUM_PARTITION_ENTRIES 128
static uint64_t
spdk_gpt_get_expected_head_lba(struct spdk_gpt *gpt)
{
switch (gpt->parse_phase) {
case SPDK_GPT_PARSE_PHASE_PRIMARY:
return GPT_PRIMARY_PARTITION_TABLE_LBA;
case SPDK_GPT_PARSE_PHASE_SECONDARY:
return gpt->lba_end;
default:
assert(false);
}
return 0;
}
static struct spdk_gpt_header *
spdk_gpt_get_header_buf(struct spdk_gpt *gpt)
{
switch (gpt->parse_phase) {
case SPDK_GPT_PARSE_PHASE_PRIMARY:
return (struct spdk_gpt_header *)
(gpt->buf + GPT_PRIMARY_PARTITION_TABLE_LBA * gpt->sector_size);
case SPDK_GPT_PARSE_PHASE_SECONDARY:
return (struct spdk_gpt_header *)
(gpt->buf + (gpt->buf_size - gpt->sector_size));
default:
assert(false);
}
return NULL;
}
static struct spdk_gpt_partition_entry *
spdk_gpt_get_partitions_buf(struct spdk_gpt *gpt, uint64_t total_partition_size,
uint64_t partition_start_lba)
{
uint64_t secondary_total_size;
switch (gpt->parse_phase) {
case SPDK_GPT_PARSE_PHASE_PRIMARY:
if ((total_partition_size + partition_start_lba * gpt->sector_size) >
gpt->buf_size) {
SPDK_ERRLOG("Buffer size is not enough\n");
return NULL;
}
return (struct spdk_gpt_partition_entry *)
(gpt->buf + partition_start_lba * gpt->sector_size);
case SPDK_GPT_PARSE_PHASE_SECONDARY:
secondary_total_size = (gpt->lba_end - partition_start_lba + 1) * gpt->sector_size;
if (secondary_total_size > gpt->buf_size) {
SPDK_ERRLOG("Buffer size is not enough\n");
return NULL;
}
return (struct spdk_gpt_partition_entry *)
(gpt->buf + (gpt->buf_size - secondary_total_size));
default:
assert(false);
}
return NULL;
}
static int
spdk_gpt_read_partitions(struct spdk_gpt *gpt)
{
@ -68,14 +127,13 @@ spdk_gpt_read_partitions(struct spdk_gpt *gpt)
total_partition_size = num_partition_entries * partition_entry_size;
partition_start_lba = from_le64(&head->partition_entry_lba);
if ((total_partition_size + partition_start_lba * gpt->sector_size) > SPDK_GPT_BUFFER_SIZE) {
SPDK_ERRLOG("Buffer size is not enough\n");
gpt->partitions = spdk_gpt_get_partitions_buf(gpt, total_partition_size,
partition_start_lba);
if (!gpt->partitions) {
SPDK_ERRLOG("Failed to get gpt partitions buf\n");
return -1;
}
gpt->partitions = (struct spdk_gpt_partition_entry *)(gpt->buf +
partition_start_lba * gpt->sector_size);
crc32 = spdk_crc32_ieee_update(gpt->partitions, total_partition_size, ~0);
crc32 ^= ~0;
@ -121,10 +179,15 @@ spdk_gpt_read_header(struct spdk_gpt *gpt)
{
uint32_t head_size;
uint32_t new_crc, original_crc;
uint64_t my_lba;
uint64_t my_lba, head_lba;
struct spdk_gpt_header *head;
head = (struct spdk_gpt_header *)(gpt->buf + GPT_PRIMARY_PARTITION_TABLE_LBA * gpt->sector_size);
head = spdk_gpt_get_header_buf(gpt);
if (!head) {
SPDK_ERRLOG("Failed to get gpt header buf\n");
return -1;
}
head_size = from_le32(&head->header_size);
if (head_size < sizeof(*head) || head_size > gpt->sector_size) {
SPDK_ERRLOG("head_size=%u\n", head_size);
@ -150,10 +213,11 @@ spdk_gpt_read_header(struct spdk_gpt *gpt)
return -1;
}
head_lba = spdk_gpt_get_expected_head_lba(gpt);
my_lba = from_le64(&head->my_lba);
if (my_lba != GPT_PRIMARY_PARTITION_TABLE_LBA) {
SPDK_ERRLOG("head my_lba(%" PRIu64 ") != expected(%d)\n",
my_lba, GPT_PRIMARY_PARTITION_TABLE_LBA);
if (my_lba != head_lba) {
SPDK_ERRLOG("head my_lba(%" PRIu64 ") != expected(%" PRIu64 ")\n",
my_lba, head_lba);
return -1;
}
@ -215,7 +279,7 @@ spdk_gpt_check_mbr(struct spdk_gpt *gpt)
}
int
spdk_gpt_parse(struct spdk_gpt *gpt)
spdk_gpt_parse_mbr(struct spdk_gpt *gpt)
{
int rc;
@ -230,6 +294,14 @@ spdk_gpt_parse(struct spdk_gpt *gpt)
return rc;
}
return 0;
}
int
spdk_gpt_parse_partition_table(struct spdk_gpt *gpt)
{
int rc;
rc = spdk_gpt_read_header(gpt);
if (rc) {
SPDK_ERRLOG("Failed to read gpt header\n");

View File

@ -46,7 +46,14 @@
#define SPDK_GPT_BUFFER_SIZE 32768 /* 32KB */
#define SPDK_GPT_GUID_EQUAL(x,y) (memcmp(x, y, sizeof(struct spdk_gpt_guid)) == 0)
enum spdk_gpt_parse_phase {
SPDK_GPT_PARSE_PHASE_INVALID = 0,
SPDK_GPT_PARSE_PHASE_PRIMARY,
SPDK_GPT_PARSE_PHASE_SECONDARY,
};
struct spdk_gpt {
uint8_t parse_phase;
unsigned char *buf;
uint64_t buf_size;
uint64_t lba_start;
@ -57,6 +64,7 @@ struct spdk_gpt {
struct spdk_gpt_partition_entry *partitions;
};
int spdk_gpt_parse(struct spdk_gpt *gpt);
int spdk_gpt_parse_mbr(struct spdk_gpt *gpt);
int spdk_gpt_parse_partition_table(struct spdk_gpt *gpt);
#endif /* SPDK_INTERNAL_GPT_H */

View File

@ -145,6 +145,7 @@ spdk_gpt_base_bdev_init(struct spdk_bdev *bdev)
}
gpt = &gpt_base->gpt;
gpt->parse_phase = SPDK_GPT_PARSE_PHASE_PRIMARY;
gpt->buf_size = spdk_max(SPDK_GPT_BUFFER_SIZE, bdev->blocklen);
gpt->buf = spdk_dma_zmalloc(gpt->buf_size, spdk_bdev_get_buf_align(bdev), NULL);
if (!gpt->buf) {
@ -355,9 +356,15 @@ spdk_gpt_bdev_complete(struct spdk_bdev_io *bdev_io, bool status, void *arg)
goto end;
}
rc = spdk_gpt_parse(&gpt_base->gpt);
rc = spdk_gpt_parse_mbr(&gpt_base->gpt);
if (rc) {
SPDK_DEBUGLOG(SPDK_LOG_VBDEV_GPT, "Failed to parse gpt\n");
SPDK_DEBUGLOG(SPDK_LOG_VBDEV_GPT, "Failed to parse mbr\n");
goto end;
}
rc = spdk_gpt_parse_partition_table(&gpt_base->gpt);
if (rc) {
SPDK_DEBUGLOG(SPDK_LOG_VBDEV_GPT, "Failed to parse primary partition table\n");
goto end;
}

View File

@ -45,9 +45,15 @@ test_check_mbr(void)
unsigned char a[SPDK_GPT_BUFFER_SIZE];
int re;
/* spdk_gpt_check_mbr(NULL) does not exist, NULL is filtered out in spdk_gpt_parse() */
/* Set gpt is NULL */
re = spdk_gpt_parse_mbr(NULL);
CU_ASSERT(re == -1);
/* Set gpt->buf is NULL */
gpt = calloc(1, sizeof(*gpt));
SPDK_CU_ASSERT_FATAL(gpt != NULL);
re = spdk_gpt_parse_mbr(gpt);
CU_ASSERT(re == -1);
/* Set *gpt is "aaa...", all are mismatch include mbr_signature */
memset(a, 'a', sizeof(a));
@ -87,13 +93,16 @@ test_read_header(void)
unsigned char a[SPDK_GPT_BUFFER_SIZE];
int re;
/* spdk_gpt_read_header(NULL) does not exist, NULL is filtered out in spdk_gpt_parse() */
/* spdk_gpt_read_header(NULL) does not exist, NULL is filtered out in spdk_gpt_parse_mbr() */
gpt = calloc(1, sizeof(*gpt));
SPDK_CU_ASSERT_FATAL(gpt != NULL);
gpt->parse_phase = SPDK_GPT_PARSE_PHASE_PRIMARY;
gpt->sector_size = 512;
/* Set *gpt is "aaa..." */
memset(a, 'a', sizeof(a));
gpt->buf = &a[0];
gpt->buf_size = sizeof(a);
/* Set header_size mismatch */
gpt->sector_size = 512;
@ -152,13 +161,16 @@ test_read_partitions(void)
unsigned char a[SPDK_GPT_BUFFER_SIZE];
int re;
/* spdk_gpt_read_partitions(NULL) does not exist, NULL is filtered out in spdk_gpt_parse() */
/* spdk_gpt_read_partitions(NULL) does not exist, NULL is filtered out in spdk_gpt_parse_mbr() */
gpt = calloc(1, sizeof(*gpt));
SPDK_CU_ASSERT_FATAL(gpt != NULL);
gpt->parse_phase = SPDK_GPT_PARSE_PHASE_PRIMARY;
gpt->sector_size = 512;
/* Set *gpt is "aaa..." */
memset(a, 'a', sizeof(a));
gpt->buf = &a[0];
gpt->buf_size = sizeof(a);
/* Set num_partition_entries exceeds Max value of entries GPT supported */
gpt->sector_size = 512;
@ -200,7 +212,7 @@ test_read_partitions(void)
}
static void
test_parse(void)
test_parse_mbr_and_primary(void)
{
struct spdk_gpt *gpt;
struct spdk_mbr *mbr;
@ -209,32 +221,38 @@ test_parse(void)
int re;
/* Set gpt is NULL */
re = spdk_gpt_parse(NULL);
re = spdk_gpt_parse_mbr(NULL);
CU_ASSERT(re == -1);
/* Set gpt->buf is NULL */
gpt = calloc(1, sizeof(*gpt));
SPDK_CU_ASSERT_FATAL(gpt != NULL);
re = spdk_gpt_parse(gpt);
gpt->parse_phase = SPDK_GPT_PARSE_PHASE_PRIMARY;
gpt->sector_size = 512;
re = spdk_gpt_parse_mbr(gpt);
CU_ASSERT(re == -1);
/* Set *gpt is "aaa...", check_mbr failed */
memset(a, 'a', sizeof(a));
gpt->buf = &a[0];
re = spdk_gpt_parse(gpt);
gpt->buf_size = sizeof(a);
re = spdk_gpt_parse_mbr(gpt);
CU_ASSERT(re == -1);
/* Set check_mbr passed, read_header failed */
/* Set check_mbr passed */
mbr = (struct spdk_mbr *)gpt->buf;
mbr->mbr_signature = 0xAA55;
mbr->partitions[0].start_lba = 1;
mbr->partitions[0].os_type = 0xEE;
mbr->partitions[0].size_lba = 0xFFFFFFFF;
re = spdk_gpt_parse(gpt);
re = spdk_gpt_parse_mbr(gpt);
CU_ASSERT(re == 0);
/* Expect read_header failed */
re = spdk_gpt_parse_partition_table(gpt);
CU_ASSERT(re == -1);
/* Set read_header passed, read_partitions failed */
gpt->sector_size = 512;
head = (struct spdk_gpt_header *)(gpt->buf + GPT_PRIMARY_PARTITION_TABLE_LBA * gpt->sector_size);
head->header_size = sizeof(*head);
head->gpt_signature[0] = 'E';
@ -251,7 +269,7 @@ test_parse(void)
to_le64(&gpt->lba_end, 0x2E935FFE);
to_le64(&head->first_usable_lba, 0xA);
to_le64(&head->last_usable_lba, 0xF4240);
re = spdk_gpt_parse(gpt);
re = spdk_gpt_parse_partition_table(gpt);
CU_ASSERT(re == -1);
/* Set read_partitions passed, all passed */
@ -260,7 +278,61 @@ test_parse(void)
to_le32(&head->header_crc32, 0x845A09AA);
to_le32(&head->partition_entry_array_crc32, 0xEBEE44FB);
to_le32(&head->num_partition_entries, 0x80);
re = spdk_gpt_parse(gpt);
re = spdk_gpt_parse_partition_table(gpt);
CU_ASSERT(re == 0);
free(gpt);
}
static void
test_parse_secondary(void)
{
struct spdk_gpt *gpt;
struct spdk_gpt_header *head;
unsigned char a[SPDK_GPT_BUFFER_SIZE];
int re;
/* spdk_gpt_parse_partition_table(NULL) does not exist, NULL is filtered out in spdk_gpt_parse_mbr() */
gpt = calloc(1, sizeof(*gpt));
SPDK_CU_ASSERT_FATAL(gpt != NULL);
gpt->parse_phase = SPDK_GPT_PARSE_PHASE_SECONDARY;
gpt->sector_size = 512;
/* Set *gpt is "aaa...", read_header failed */
memset(a, 'a', sizeof(a));
gpt->buf = &a[0];
gpt->buf_size = sizeof(a);
re = spdk_gpt_parse_partition_table(gpt);
CU_ASSERT(re == -1);
/* Set read_header passed, read_partitions failed */
head = (struct spdk_gpt_header *)(gpt->buf + gpt->buf_size - gpt->sector_size);
head->header_size = sizeof(*head);
head->gpt_signature[0] = 'E';
head->gpt_signature[1] = 'F';
head->gpt_signature[2] = 'I';
head->gpt_signature[3] = ' ';
head->gpt_signature[4] = 'P';
head->gpt_signature[5] = 'A';
head->gpt_signature[6] = 'R';
head->gpt_signature[7] = 'T';
to_le32(&head->header_crc32, 0xAA68A167);
to_le64(&head->my_lba, 0x63FFFFF);
to_le64(&gpt->lba_start, 0x0);
to_le64(&gpt->lba_end, 0x63FFFFF);
to_le64(&gpt->total_sectors, 0x6400000);
to_le64(&head->first_usable_lba, 0xA);
to_le64(&head->last_usable_lba, 0x63FFFDE);
re = spdk_gpt_parse_partition_table(gpt);
CU_ASSERT(re == -1);
/* Set read_partitions passed, all passed */
to_le32(&head->size_of_partition_entry, 0x80);
to_le64(&head->partition_entry_lba, 0x63FFFDF);
to_le32(&head->header_crc32, 0x204129E8);
to_le32(&head->partition_entry_array_crc32, 0xEBEE44FB);
to_le32(&head->num_partition_entries, 0x80);
re = spdk_gpt_parse_partition_table(gpt);
CU_ASSERT(re == 0);
free(gpt);
@ -284,7 +356,9 @@ main(int argc, char **argv)
if (
CU_add_test(suite, "parse",
test_parse) == NULL ||
test_parse_mbr_and_primary) == NULL ||
CU_add_test(suite, "parse secondary",
test_parse_secondary) == NULL ||
CU_add_test(suite, "check mbr",
test_check_mbr) == NULL ||
CU_add_test(suite, "read header",