From ccb070450e79c33fb3f755dbea2fdd979f09d3fb Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Mon, 25 Oct 2010 02:00:24 +0200 Subject: [PATCH] fdisk: initial stab at GPT partition support Signed-off-by: Kevin Cernekee Signed-off-by: Denys Vlasenko --- include/libbb.h | 7 +- libbb/human_readable.c | 4 +- util-linux/Config.src | 8 ++ util-linux/fdisk.c | 79 +++++++++++----- util-linux/fdisk_gpt.c | 203 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 275 insertions(+), 26 deletions(-) create mode 100644 util-linux/fdisk_gpt.c diff --git a/include/libbb.h b/include/libbb.h index bd1d586c7..d14728ed0 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -746,7 +746,7 @@ char *itoa(int n) FAST_FUNC; char *utoa_to_buf(unsigned n, char *buf, unsigned buflen) FAST_FUNC; char *itoa_to_buf(int n, char *buf, unsigned buflen) FAST_FUNC; /* Intelligent formatters of bignums */ -void smart_ulltoa4(unsigned long long ul, char buf[5], const char *scale) FAST_FUNC; +void smart_ulltoa4(unsigned long long ul, char buf[4], const char *scale) FAST_FUNC; void smart_ulltoa5(unsigned long long ul, char buf[5], const char *scale) FAST_FUNC; /* If block_size == 0, display size without fractional part, * else display (size * block_size) with one decimal digit. @@ -1543,7 +1543,10 @@ void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC; void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; void sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC; - +/* TODO: add global crc32_table pointer and create + * LE and BE functions to calculate crc32 over given bytes. + * Currently we have about five reimplementations... + */ uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC; typedef struct masks_labels_t { diff --git a/libbb/human_readable.c b/libbb/human_readable.c index 22dc5d23f..50cbe41bb 100644 --- a/libbb/human_readable.c +++ b/libbb/human_readable.c @@ -94,7 +94,7 @@ const char* FAST_FUNC make_human_readable_str(unsigned long long val, /* Convert unsigned long long value into compact 5-char representation. * String is not terminated (buf[5] is untouched) */ -void FAST_FUNC smart_ulltoa5(unsigned long long ul, char buf[6], const char *scale) +void FAST_FUNC smart_ulltoa5(unsigned long long ul, char buf[5], const char *scale) { const char *fmt; char c; @@ -150,7 +150,7 @@ void FAST_FUNC smart_ulltoa5(unsigned long long ul, char buf[6], const char *sca /* Convert unsigned long long value into compact 4-char * representation. Examples: "1234", "1.2k", " 27M", "123T" * String is not terminated (buf[4] is untouched) */ -void FAST_FUNC smart_ulltoa4(unsigned long long ul, char buf[5], const char *scale) +void FAST_FUNC smart_ulltoa4(unsigned long long ul, char buf[4], const char *scale) { const char *fmt; char c; diff --git a/util-linux/Config.src b/util-linux/Config.src index afa30923b..19b309e57 100644 --- a/util-linux/Config.src +++ b/util-linux/Config.src @@ -181,6 +181,14 @@ config FEATURE_OSF_LABEL Enabling this option allows you to create or change BSD disklabels and define and edit BSD disk slices. +config FEATURE_GPT_LABEL + bool "Support GPT disklabels" + default n + depends on FDISK && FEATURE_FDISK_WRITABLE + help + Enabling this option allows you to view GUID Partition Table + disklabels. + config FEATURE_FDISK_ADVANCED bool "Support expert mode" default y diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index b6417a355..3f2e0d3ae 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -107,12 +107,30 @@ struct partition { unsigned char size4[4]; /* nr of sectors in partition */ } PACKED; +/* + * per partition table entry data + * + * The four primary partitions have the same sectorbuffer (MBRbuffer) + * and have NULL ext_pointer. + * Each logical partition table entry has two pointers, one for the + * partition and one link to the next one. + */ +struct pte { + struct partition *part_table; /* points into sectorbuffer */ + struct partition *ext_pointer; /* points into sectorbuffer */ + sector_t offset_from_dev_start; /* disk sector number */ + char *sectorbuffer; /* disk sector contents */ +#if ENABLE_FEATURE_FDISK_WRITABLE + char changed; /* boolean */ +#endif +}; + #define unable_to_open "can't open '%s'" #define unable_to_read "can't read from %s" #define unable_to_seek "can't seek on %s" enum label_type { - LABEL_DOS, LABEL_SUN, LABEL_SGI, LABEL_AIX, LABEL_OSF + LABEL_DOS, LABEL_SUN, LABEL_SGI, LABEL_AIX, LABEL_OSF, LABEL_GPT }; #define LABEL_IS_DOS (LABEL_DOS == current_label_type) @@ -149,6 +167,14 @@ enum label_type { #define STATIC_OSF extern #endif +#if ENABLE_FEATURE_GPT_LABEL +#define LABEL_IS_GPT (LABEL_GPT == current_label_type) +#define STATIC_GPT static +#else +#define LABEL_IS_GPT 0 +#define STATIC_GPT extern +#endif + enum action { OPEN_MAIN, TRY_ONLY, CREATE_EMPTY_DOS, CREATE_EMPTY_SUN }; static void update_units(void); @@ -162,6 +188,7 @@ static sector_t read_int(sector_t low, sector_t dflt, sector_t high, sector_t ba #endif static const char *partition_type(unsigned char type); static void get_geometry(void); +static void read_pte(struct pte *pe, sector_t offset); #if ENABLE_FEATURE_SUN_LABEL || ENABLE_FEATURE_FDISK_WRITABLE static int get_boot(enum action what); #else @@ -174,24 +201,6 @@ static int get_boot(void); static sector_t get_start_sect(const struct partition *p); static sector_t get_nr_sects(const struct partition *p); -/* - * per partition table entry data - * - * The four primary partitions have the same sectorbuffer (MBRbuffer) - * and have NULL ext_pointer. - * Each logical partition table entry has two pointers, one for the - * partition and one link to the next one. - */ -struct pte { - struct partition *part_table; /* points into sectorbuffer */ - struct partition *ext_pointer; /* points into sectorbuffer */ - sector_t offset_from_dev_start; /* disk sector number */ - char *sectorbuffer; /* disk sector contents */ -#if ENABLE_FEATURE_FDISK_WRITABLE - char changed; /* boolean */ -#endif -}; - /* DOS partition types */ static const char *const i386_sys_types[] = { @@ -653,6 +662,8 @@ STATIC_OSF void bsd_select(void); STATIC_OSF void xbsd_print_disklabel(int); #include "fdisk_osf.c" +#include "fdisk_gpt.c" + #if ENABLE_FEATURE_SGI_LABEL || ENABLE_FEATURE_SUN_LABEL static uint16_t fdisk_swap16(uint16_t x) @@ -833,6 +844,11 @@ menu(void) puts("o\tcreate a new empty DOS partition table"); puts("q\tquit without saving changes"); puts("s\tcreate a new empty Sun disklabel"); /* sun */ + } else if (LABEL_IS_GPT) { + puts("o\tcreate a new empty DOS partition table"); + puts("p\tprint the partition table"); + puts("q\tquit without saving changes"); + puts("s\tcreate a new empty Sun disklabel"); /* sun */ } else { puts("a\ttoggle a bootable flag"); puts("b\tedit bsd disklabel"); @@ -1308,7 +1324,18 @@ get_geometry(void) /* * Opens disk_device and optionally reads MBR. - * FIXME: document what each 'what' value will do! + * If what == OPEN_MAIN: + * Open device, read MBR. Abort program on short read. Create empty + * disklabel if the on-disk structure is invalid (WRITABLE mode). + * If what == TRY_ONLY: + * Open device, read MBR. Return an error if anything is out of place. + * Do not create an empty disklabel. This is used for the "list" + * operations: "fdisk -l /dev/sda" and "fdisk -l" (all devices). + * If what == CREATE_EMPTY_*: + * This means that get_boot() was called recursively from create_*label(). + * Do not re-open the device; just set up the ptes array and print + * geometry warnings. + * * Returns: * -1: no 0xaa55 flag present (possibly entire disk BSD) * 0: found or created label @@ -1390,6 +1417,10 @@ static int get_boot(void) if (check_aix_label()) return 0; #endif +#if ENABLE_FEATURE_GPT_LABEL + if (check_gpt_label()) + return 0; +#endif #if ENABLE_FEATURE_OSF_LABEL if (check_osf_label()) { possibly_osf_label = 1; @@ -1409,7 +1440,7 @@ static int get_boot(void) if (!valid_part_table_flag(MBRbuffer)) { if (what == OPEN_MAIN) { printf("Device contains neither a valid DOS " - "partition table, nor Sun, SGI or OSF " + "partition table, nor Sun, SGI, OSF or GPT " "disklabel\n"); #ifdef __sparc__ IF_FEATURE_SUN_LABEL(create_sunlabel();) @@ -2056,10 +2087,14 @@ list_table(int xtra) sun_list_table(xtra); return; } - if (LABEL_IS_SUN) { + if (LABEL_IS_SGI) { sgi_list_table(xtra); return; } + if (LABEL_IS_GPT) { + gpt_list_table(xtra); + return; + } list_disk_geometry(); 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 + * + * 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 */