fdisk: initial stab at GPT partition support

Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Kevin Cernekee 2010-10-25 02:00:24 +02:00 committed by Denys Vlasenko
parent 3b060528a2
commit ccb070450e
5 changed files with 275 additions and 26 deletions

View File

@ -746,7 +746,7 @@ char *itoa(int n) FAST_FUNC;
char *utoa_to_buf(unsigned n, char *buf, unsigned buflen) 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; char *itoa_to_buf(int n, char *buf, unsigned buflen) FAST_FUNC;
/* Intelligent formatters of bignums */ /* 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; void smart_ulltoa5(unsigned long long ul, char buf[5], const char *scale) FAST_FUNC;
/* If block_size == 0, display size without fractional part, /* If block_size == 0, display size without fractional part,
* else display (size * block_size) with one decimal digit. * 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_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
void sha512_end(sha512_ctx_t *ctx, void *resbuf) 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; uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC;
typedef struct masks_labels_t { typedef struct masks_labels_t {

View File

@ -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. /* Convert unsigned long long value into compact 5-char representation.
* String is not terminated (buf[5] is untouched) */ * 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; const char *fmt;
char c; 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 /* Convert unsigned long long value into compact 4-char
* representation. Examples: "1234", "1.2k", " 27M", "123T" * representation. Examples: "1234", "1.2k", " 27M", "123T"
* String is not terminated (buf[4] is untouched) */ * 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; const char *fmt;
char c; char c;

View File

@ -181,6 +181,14 @@ config FEATURE_OSF_LABEL
Enabling this option allows you to create or change BSD disklabels Enabling this option allows you to create or change BSD disklabels
and define and edit BSD disk slices. 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 config FEATURE_FDISK_ADVANCED
bool "Support expert mode" bool "Support expert mode"
default y default y

View File

@ -107,12 +107,30 @@ struct partition {
unsigned char size4[4]; /* nr of sectors in partition */ unsigned char size4[4]; /* nr of sectors in partition */
} PACKED; } 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_open "can't open '%s'"
#define unable_to_read "can't read from %s" #define unable_to_read "can't read from %s"
#define unable_to_seek "can't seek on %s" #define unable_to_seek "can't seek on %s"
enum label_type { 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) #define LABEL_IS_DOS (LABEL_DOS == current_label_type)
@ -149,6 +167,14 @@ enum label_type {
#define STATIC_OSF extern #define STATIC_OSF extern
#endif #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 }; enum action { OPEN_MAIN, TRY_ONLY, CREATE_EMPTY_DOS, CREATE_EMPTY_SUN };
static void update_units(void); 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 #endif
static const char *partition_type(unsigned char type); static const char *partition_type(unsigned char type);
static void get_geometry(void); static void get_geometry(void);
static void read_pte(struct pte *pe, sector_t offset);
#if ENABLE_FEATURE_SUN_LABEL || ENABLE_FEATURE_FDISK_WRITABLE #if ENABLE_FEATURE_SUN_LABEL || ENABLE_FEATURE_FDISK_WRITABLE
static int get_boot(enum action what); static int get_boot(enum action what);
#else #else
@ -174,24 +201,6 @@ static int get_boot(void);
static sector_t get_start_sect(const struct partition *p); static sector_t get_start_sect(const struct partition *p);
static sector_t get_nr_sects(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 */ /* DOS partition types */
static const char *const i386_sys_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); STATIC_OSF void xbsd_print_disklabel(int);
#include "fdisk_osf.c" #include "fdisk_osf.c"
#include "fdisk_gpt.c"
#if ENABLE_FEATURE_SGI_LABEL || ENABLE_FEATURE_SUN_LABEL #if ENABLE_FEATURE_SGI_LABEL || ENABLE_FEATURE_SUN_LABEL
static uint16_t static uint16_t
fdisk_swap16(uint16_t x) fdisk_swap16(uint16_t x)
@ -833,6 +844,11 @@ menu(void)
puts("o\tcreate a new empty DOS partition table"); puts("o\tcreate a new empty DOS partition table");
puts("q\tquit without saving changes"); puts("q\tquit without saving changes");
puts("s\tcreate a new empty Sun disklabel"); /* sun */ 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 { } else {
puts("a\ttoggle a bootable flag"); puts("a\ttoggle a bootable flag");
puts("b\tedit bsd disklabel"); puts("b\tedit bsd disklabel");
@ -1308,7 +1324,18 @@ get_geometry(void)
/* /*
* Opens disk_device and optionally reads MBR. * 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: * Returns:
* -1: no 0xaa55 flag present (possibly entire disk BSD) * -1: no 0xaa55 flag present (possibly entire disk BSD)
* 0: found or created label * 0: found or created label
@ -1390,6 +1417,10 @@ static int get_boot(void)
if (check_aix_label()) if (check_aix_label())
return 0; return 0;
#endif #endif
#if ENABLE_FEATURE_GPT_LABEL
if (check_gpt_label())
return 0;
#endif
#if ENABLE_FEATURE_OSF_LABEL #if ENABLE_FEATURE_OSF_LABEL
if (check_osf_label()) { if (check_osf_label()) {
possibly_osf_label = 1; possibly_osf_label = 1;
@ -1409,7 +1440,7 @@ static int get_boot(void)
if (!valid_part_table_flag(MBRbuffer)) { if (!valid_part_table_flag(MBRbuffer)) {
if (what == OPEN_MAIN) { if (what == OPEN_MAIN) {
printf("Device contains neither a valid DOS " printf("Device contains neither a valid DOS "
"partition table, nor Sun, SGI or OSF " "partition table, nor Sun, SGI, OSF or GPT "
"disklabel\n"); "disklabel\n");
#ifdef __sparc__ #ifdef __sparc__
IF_FEATURE_SUN_LABEL(create_sunlabel();) IF_FEATURE_SUN_LABEL(create_sunlabel();)
@ -2056,10 +2087,14 @@ list_table(int xtra)
sun_list_table(xtra); sun_list_table(xtra);
return; return;
} }
if (LABEL_IS_SUN) { if (LABEL_IS_SGI) {
sgi_list_table(xtra); sgi_list_table(xtra);
return; return;
} }
if (LABEL_IS_GPT) {
gpt_list_table(xtra);
return;
}
list_disk_geometry(); list_disk_geometry();

203
util-linux/fdisk_gpt.c Normal file
View File

@ -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 */