2010-10-25 00:00:24 +00:00
|
|
|
#if ENABLE_FEATURE_GPT_LABEL
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2010 Kevin Cernekee <cernekee@gmail.com>
|
|
|
|
*
|
|
|
|
* Licensed under GPLv2, see file LICENSE in this source tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define GPT_MAGIC 0x5452415020494645ULL
|
|
|
|
enum {
|
2010-10-28 16:57:19 +00:00
|
|
|
LEGACY_GPT_TYPE = 0xee,
|
2010-10-25 00:00:24 +00:00
|
|
|
GPT_MAX_PARTS = 256,
|
|
|
|
GPT_MAX_PART_ENTRY_LEN = 4096,
|
|
|
|
GUID_LEN = 16,
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
uint64_t magic;
|
|
|
|
uint32_t revision;
|
|
|
|
uint32_t hdr_size;
|
|
|
|
uint32_t hdr_crc32;
|
|
|
|
uint32_t reserved;
|
|
|
|
uint64_t current_lba;
|
|
|
|
uint64_t backup_lba;
|
|
|
|
uint64_t first_usable_lba;
|
|
|
|
uint64_t last_usable_lba;
|
|
|
|
uint8_t disk_guid[GUID_LEN];
|
|
|
|
uint64_t first_part_lba;
|
|
|
|
uint32_t n_parts;
|
|
|
|
uint32_t part_entry_len;
|
|
|
|
uint32_t part_array_crc32;
|
|
|
|
} gpt_header;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
uint8_t type_guid[GUID_LEN];
|
|
|
|
uint8_t part_guid[GUID_LEN];
|
|
|
|
uint64_t lba_start;
|
|
|
|
uint64_t lba_end;
|
|
|
|
uint64_t flags;
|
2016-11-27 05:13:43 +00:00
|
|
|
uint16_t name36[36];
|
2010-10-25 00:00:24 +00:00
|
|
|
} gpt_partition;
|
|
|
|
|
|
|
|
static gpt_header *gpt_hdr;
|
|
|
|
|
|
|
|
static char *part_array;
|
|
|
|
static unsigned int n_parts;
|
|
|
|
static unsigned int part_entry_len;
|
|
|
|
|
|
|
|
static inline gpt_partition *
|
|
|
|
gpt_part(int i)
|
|
|
|
{
|
|
|
|
if (i >= n_parts) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return (gpt_partition *)&part_array[i * part_entry_len];
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
gpt_crc32(void *buf, int len)
|
|
|
|
{
|
2010-10-28 03:38:11 +00:00
|
|
|
return ~crc32_block_endian0(0xffffffff, buf, len, global_crc32_table);
|
2010-10-25 00:00:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gpt_print_guid(uint8_t *buf)
|
|
|
|
{
|
|
|
|
printf(
|
|
|
|
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
|
|
|
buf[3], buf[2], buf[1], buf[0],
|
|
|
|
buf[5], buf[4],
|
|
|
|
buf[7], buf[6],
|
|
|
|
buf[8], buf[9],
|
|
|
|
buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-11-27 05:13:43 +00:00
|
|
|
gpt_print_wide36(uint16_t *s)
|
2010-10-25 00:00:24 +00:00
|
|
|
{
|
2016-11-27 05:13:43 +00:00
|
|
|
#if ENABLE_UNICODE_SUPPORT
|
|
|
|
char buf[37 * 4];
|
|
|
|
wchar_t wc[37];
|
2010-10-25 00:00:24 +00:00
|
|
|
int i = 0;
|
2016-11-27 05:13:43 +00:00
|
|
|
while (i < ARRAY_SIZE(wc)-1) {
|
|
|
|
if (s[i] == 0)
|
|
|
|
break;
|
|
|
|
wc[i] = s[i];
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
wc[i] = 0;
|
|
|
|
if (wcstombs(buf, wc, sizeof(buf)) <= sizeof(buf)-1)
|
|
|
|
fputs(printable_string(NULL, buf), stdout);
|
|
|
|
#else
|
|
|
|
char buf[37];
|
|
|
|
int i = 0;
|
|
|
|
while (i < ARRAY_SIZE(buf)-1) {
|
|
|
|
if (s[i] == 0)
|
|
|
|
break;
|
2016-11-27 19:47:01 +00:00
|
|
|
buf[i] = (0x20 <= s[i] && s[i] < 0x7f) ? s[i] : '?';
|
2016-11-27 05:13:43 +00:00
|
|
|
i++;
|
2010-10-25 00:00:24 +00:00
|
|
|
}
|
2016-11-27 05:13:43 +00:00
|
|
|
buf[i] = '\0';
|
2016-11-27 19:47:01 +00:00
|
|
|
fputs(buf, stdout);
|
2016-11-27 05:13:43 +00:00
|
|
|
#endif
|
2010-10-25 00:00:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gpt_list_table(int xtra UNUSED_PARAM)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char numstr6[6];
|
|
|
|
|
2013-09-06 10:53:14 +00:00
|
|
|
smart_ulltoa5(total_number_of_sectors * sector_size, numstr6, " KMGTPEZY")[0] = '\0';
|
2010-10-25 00:00:24 +00:00
|
|
|
printf("Disk %s: %llu sectors, %s\n", disk_device,
|
|
|
|
(unsigned long long)total_number_of_sectors,
|
|
|
|
numstr6);
|
|
|
|
printf("Logical sector size: %u\n", sector_size);
|
|
|
|
printf("Disk identifier (GUID): ");
|
|
|
|
gpt_print_guid(gpt_hdr->disk_guid);
|
|
|
|
printf("\nPartition table holds up to %u entries\n",
|
|
|
|
(int)SWAP_LE32(gpt_hdr->n_parts));
|
|
|
|
printf("First usable sector is %llu, last usable sector is %llu\n\n",
|
|
|
|
(unsigned long long)SWAP_LE64(gpt_hdr->first_usable_lba),
|
|
|
|
(unsigned long long)SWAP_LE64(gpt_hdr->last_usable_lba));
|
|
|
|
|
2016-11-27 05:13:43 +00:00
|
|
|
/* "GPT fdisk" has a concept of 16-bit extension of the original MBR 8-bit type codes,
|
|
|
|
* which it displays here: its output columns are ... Size Code Name
|
|
|
|
* They are their own invention and are not stored on disk.
|
|
|
|
* Looks like they use them to support "hybrid" GPT: for example, they have
|
|
|
|
* AddType(0x8307, "69DAD710-2CE4-4E3C-B16C-21A1D49ABED3", "Linux ARM32 root (/)");
|
|
|
|
* and then (code>>8) matches what you need to put into MBR's type field for such a partition.
|
|
|
|
* To print those codes, we'd need a GUID lookup table. Lets just drop the "Code" column instead:
|
|
|
|
*/
|
|
|
|
puts("Number Start (sector) End (sector) Size Name");
|
|
|
|
// 123456 123456789012345 123456789012345 12345 abc
|
2010-10-25 00:00:24 +00:00
|
|
|
for (i = 0; i < n_parts; i++) {
|
|
|
|
gpt_partition *p = gpt_part(i);
|
|
|
|
if (p->lba_start) {
|
2013-08-20 15:42:06 +00:00
|
|
|
smart_ulltoa5((1 + SWAP_LE64(p->lba_end) - SWAP_LE64(p->lba_start)) * sector_size,
|
2013-09-06 10:53:14 +00:00
|
|
|
numstr6, " KMGTPEZY")[0] = '\0';
|
2016-11-27 05:13:43 +00:00
|
|
|
printf("%6u %15llu %15llu %s ",
|
2010-10-25 00:00:24 +00:00
|
|
|
i + 1,
|
|
|
|
(unsigned long long)SWAP_LE64(p->lba_start),
|
|
|
|
(unsigned long long)SWAP_LE64(p->lba_end),
|
2016-11-27 05:13:43 +00:00
|
|
|
numstr6
|
|
|
|
);
|
|
|
|
gpt_print_wide36(p->name36);
|
2015-10-07 20:42:45 +00:00
|
|
|
bb_putchar('\n');
|
2010-10-25 00:00:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
check_gpt_label(void)
|
|
|
|
{
|
2016-11-27 05:13:43 +00:00
|
|
|
unsigned part_array_len;
|
2010-10-25 00:00:24 +00:00
|
|
|
struct partition *first = pt_offset(MBRbuffer, 0);
|
|
|
|
struct pte pe;
|
|
|
|
uint32_t crc;
|
|
|
|
|
|
|
|
/* LBA 0 contains the legacy MBR */
|
|
|
|
|
|
|
|
if (!valid_part_table_flag(MBRbuffer)
|
|
|
|
|| first->sys_ind != LEGACY_GPT_TYPE
|
|
|
|
) {
|
|
|
|
current_label_type = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* LBA 1 contains the GPT header */
|
|
|
|
|
|
|
|
read_pte(&pe, 1);
|
|
|
|
gpt_hdr = (void *)pe.sectorbuffer;
|
|
|
|
|
|
|
|
if (gpt_hdr->magic != SWAP_LE64(GPT_MAGIC)) {
|
|
|
|
current_label_type = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-11-27 05:13:43 +00:00
|
|
|
init_unicode();
|
2010-10-27 13:26:45 +00:00
|
|
|
if (!global_crc32_table) {
|
|
|
|
global_crc32_table = crc32_filltable(NULL, 0);
|
2010-10-25 00:00:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
crc = SWAP_LE32(gpt_hdr->hdr_crc32);
|
|
|
|
gpt_hdr->hdr_crc32 = 0;
|
|
|
|
if (gpt_crc32(gpt_hdr, SWAP_LE32(gpt_hdr->hdr_size)) != crc) {
|
|
|
|
/* FIXME: read the backup table */
|
|
|
|
puts("\nwarning: GPT header CRC is invalid\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
n_parts = SWAP_LE32(gpt_hdr->n_parts);
|
|
|
|
part_entry_len = SWAP_LE32(gpt_hdr->part_entry_len);
|
|
|
|
if (n_parts > GPT_MAX_PARTS
|
|
|
|
|| part_entry_len > GPT_MAX_PART_ENTRY_LEN
|
|
|
|
|| SWAP_LE32(gpt_hdr->hdr_size) > sector_size
|
|
|
|
) {
|
|
|
|
puts("\nwarning: unable to parse GPT disklabel\n");
|
|
|
|
current_label_type = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
part_array_len = n_parts * part_entry_len;
|
|
|
|
part_array = xmalloc(part_array_len);
|
|
|
|
seek_sector(SWAP_LE64(gpt_hdr->first_part_lba));
|
|
|
|
if (full_read(dev_fd, part_array, part_array_len) != part_array_len) {
|
|
|
|
fdisk_fatal(unable_to_read);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gpt_crc32(part_array, part_array_len) != gpt_hdr->part_array_crc32) {
|
|
|
|
/* FIXME: read the backup table */
|
|
|
|
puts("\nwarning: GPT array CRC is invalid\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
puts("Found valid GPT with protective MBR; using GPT\n");
|
|
|
|
|
|
|
|
current_label_type = LABEL_GPT;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* GPT_LABEL */
|