fdisk: initial stab at GPT partition support
No binary (build failed)
—
(log yok)
| @@ -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 { | |
| @@ -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; | |
| @@ -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 | |
| @@ -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(); | |
| @@ -0,0 +1,203 @@ | |
| + | #if ENABLE_FEATURE_GPT_LABEL |
| + | /* |
| + | * Copyright (C) 2010 Kevin Cernekee <[email protected]> |
| + | * |
| + | * 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 */ |