[git commit master 1/1] fdisk: initial stab at GPT partition support

Denys Vlasenko vda.linux at googlemail.com
Mon Oct 25 00:00:24 UTC 2010


commit: http://git.busybox.net/busybox/commit/?id=ccb070450e79c33fb3f755dbea2fdd979f09d3fb
branch: http://git.busybox.net/busybox/commit/?id=refs/heads/master

Signed-off-by: Kevin Cernekee <cernekee at gmail.com>
Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 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 bd1d586..d14728e 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 22dc5d2..50cbe41 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 afa3092..19b309e 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 b6417a3..3f2e0d3 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 0000000..98803ec
--- /dev/null
+++ b/util-linux/fdisk_gpt.c
@@ -0,0 +1,203 @@
+#if ENABLE_FEATURE_GPT_LABEL
+/*
+ * Copyright (C) 2010 Kevin Cernekee <cernekee at 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 */
-- 
1.7.1



More information about the busybox-cvs mailing list