diff --git a/lib/bdev/gpt/gpt.c b/lib/bdev/gpt/gpt.c index cea9f919e..13c5b8532 100644 --- a/lib/bdev/gpt/gpt.c +++ b/lib/bdev/gpt/gpt.c @@ -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"); diff --git a/lib/bdev/gpt/gpt.h b/lib/bdev/gpt/gpt.h index 923bdc1c0..dc2d0c1f2 100644 --- a/lib/bdev/gpt/gpt.h +++ b/lib/bdev/gpt/gpt.h @@ -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 */ diff --git a/lib/bdev/gpt/vbdev_gpt.c b/lib/bdev/gpt/vbdev_gpt.c index 02a475a4d..fec9d896a 100644 --- a/lib/bdev/gpt/vbdev_gpt.c +++ b/lib/bdev/gpt/vbdev_gpt.c @@ -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; } diff --git a/test/unit/lib/bdev/gpt/gpt.c/gpt_ut.c b/test/unit/lib/bdev/gpt/gpt.c/gpt_ut.c index 5b9046314..5a906fa83 100644 --- a/test/unit/lib/bdev/gpt/gpt.c/gpt_ut.c +++ b/test/unit/lib/bdev/gpt/gpt.c/gpt_ut.c @@ -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",