diff options
author | Kevin Cernekee <cernekee@gmail.com> | 2010-10-25 04:00:24 +0400 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-10-25 04:00:24 +0400 |
commit | ccb070450e79c33fb3f755dbea2fdd979f09d3fb (patch) | |
tree | ed842febd5fa0b19bbb7e42d2d98d59f6e3c5c8d /util-linux/fdisk_gpt.c | |
parent | 3b060528a26830ee26aab4e9829ac3a310f06218 (diff) |
fdisk: initial stab at GPT partition support
Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'util-linux/fdisk_gpt.c')
-rw-r--r-- | util-linux/fdisk_gpt.c | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/util-linux/fdisk_gpt.c b/util-linux/fdisk_gpt.c new file mode 100644 index 000000000..98803ec88 --- /dev/null +++ b/util-linux/fdisk_gpt.c @@ -0,0 +1,203 @@ +#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 { + LEGACY_GPT_TYPE = 0xee, + 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; + uint16_t name[36]; +} gpt_partition; + +static gpt_header *gpt_hdr; + +static char *part_array; +static unsigned int n_parts; +static unsigned int part_array_len; +static unsigned int part_entry_len; + +static uint32_t *crc32_table; + +static inline gpt_partition * +gpt_part(int i) +{ + if (i >= n_parts) { + return NULL; + } + return (gpt_partition *)&part_array[i * part_entry_len]; +} + +/* TODO: move to libbb */ +static uint32_t +gpt_crc32(void *buf, int len) +{ + uint32_t crc = 0xffffffff; + + for (; len > 0; len--, buf++) { + crc = crc32_table[(crc ^ *((char *)buf)) & 0xff] ^ (crc >> 8); + } + return crc ^ 0xffffffff; +} + +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]); +} + +/* TODO: real unicode support */ +static void +gpt_print_wide(uint16_t *s, int max_len) +{ + int i = 0; + + while (i < max_len) { + if (*s == 0) + return; + fputc(*s, stdout); + s++; + } +} + +static void +gpt_list_table(int xtra UNUSED_PARAM) +{ + int i; + char numstr6[6]; + + numstr6[5] = '\0'; + + smart_ulltoa5(total_number_of_sectors, numstr6, " KMGTPEZY"); + 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)); + + printf("Number Start (sector) End (sector) Size Code Name\n"); + for (i = 0; i < n_parts; i++) { + gpt_partition *p = gpt_part(i); + if (p->lba_start) { + smart_ulltoa5(1 + SWAP_LE64(p->lba_end) - SWAP_LE64(p->lba_start), + numstr6, " KMGTPEZY"); + printf("%4u %15llu %15llu %11s %04x ", + i + 1, + (unsigned long long)SWAP_LE64(p->lba_start), + (unsigned long long)SWAP_LE64(p->lba_end), + numstr6, + 0x0700 /* FIXME */); + gpt_print_wide(p->name, 18); + printf("\n"); + } + } +} + +static int +check_gpt_label(void) +{ + 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; + } + + if (!crc32_table) { + crc32_table = crc32_filltable(NULL, 0); + } + + 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 */ |