Since 3TB drives will soon be readily available, it would be nice if
busybox had a way to partition them.  The three options that immediately
come to mind are:

1) Add GPT to the existing fdisk applet, alongside SUN, SGI, AIX, and
OSF.

Pros: It's a relatively small change and can leverage much of what is
already in the tree.

Cons: There's an awful lot of legacy code in there, and the non-DOS
disklabels are not terribly well-supported today.  For instance:

a) This does not look like it is in working order...

        if (LABEL_IS_SUN) {
                sun_list_table(xtra);
                return;
        }
        if (LABEL_IS_SUN) {
                sgi_list_table(xtra);
                return;
        }

b) This segfaults (as do many of the other commands, when in AIX mode):

echo -ne "\xc1\xd4\xc2\xc9" | dd bs=512 count=1 of=/tmp/aix conv=sync
./busybox fdisk -l /tmp/aix

The amount of effort needed to extend sector_t to 64 bits is unknown.
It may be tricky to retest all of the supported permutations:

FDISK_SUPPORT_LARGE_DISKS
FEATURE_FDISK_WRITABLE
FEATURE_AIX_LABEL
FEATURE_SGI_LABEL
FEATURE_SUN_LABEL
FEATURE_OSF_LABEL
FEATURE_FDISK_ADVANCED
FEATURE_FDISK_BLKSIZE
32-bit hosts vs. 64-bit hosts
LE vs. BE


2) Add a new applet for this purpose.  e.g. a trimmed-down C version of
gdisk.  Optionally add a scriptable version (sgdisk), or command-line
options that would facilitate scripting.

Pros: Provides a "clean start" and could be used to start phasing out
the old fdisk applet.  Unnecessary complications like the CHS formulas
could mostly go away for good.  New code would be simpler (based
entirely on 64-bit LBA numbers) and easier to maintain long-term.

Cons: May necessitate reimplementing the UI, utility functions, and
other things we already have today.  Adding an applet is a bigger
change than tweaking an existing one, and there may be resistance to
the idea.


3) Just use the full-blown gdisk for partitioning.

Pros: Less work.

Cons: gdisk + sgdisk add up to ~250K and require C++ runtime support.
busybox fdisk is about a tenth of the size.  Many embedded applications
(including mine) just need something basic - and that is busybox's
niche.


Any thoughts/opinions on the matter?

Signed-off-by: Kevin Cernekee <[email protected]>
---
 util-linux/Config.src  |    8 ++
 util-linux/fdisk.c     |   77 ++++++++++++-----
 util-linux/fdisk_gpt.c |  217 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 281 insertions(+), 21 deletions(-)
 create mode 100644 util-linux/fdisk_gpt.c

diff --git a/util-linux/Config.src b/util-linux/Config.src
index 98953c1..0e3502f 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 aa718c7..d218a85 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();)
@@ -2061,6 +2092,10 @@ list_table(int xtra)
                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..0596678
--- /dev/null
+++ b/util-linux/fdisk_gpt.c
@@ -0,0 +1,217 @@
+#if ENABLE_FEATURE_GPT_LABEL
+/*
+ * Copyright (C) 2010 Kevin Cernekee <[email protected]>
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+enum { LEGACY_GPT_TYPE = 0xee };
+enum { GPT_MAGIC               = 0x5452415020494645ULL };
+
+enum { GPT_MAX_PARTS   = 256 };
+enum { GPT_MAX_PART_ENTRY_LEN = 4096 };
+
+enum { 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];
+}
+
+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]);
+}
+
+static char *
+gpt_size(unsigned long long sectors)
+{
+       unsigned int tmp;
+       static char buf[32];
+
+       sectors *= sector_size;
+       if (sectors < 10000) {
+               sprintf(buf, "%llu B", sectors);
+       } else if (sectors < 1024ULL * 10000) {
+               tmp = 10 * sectors / 1024ULL;
+               sprintf(buf, "%d.%d KiB", tmp / 10, tmp % 10);
+       } else if (sectors < 1024ULL * 1024 * 10000) {
+               tmp = 10 * sectors / (1024ULL * 1024);
+               sprintf(buf, "%d.%d MiB", tmp / 10, tmp % 10);
+       } else if (sectors < 1024ULL * 1024 * 1024 * 10000) {
+               tmp = 10 * sectors / (1024ULL * 1024 * 1024);
+               sprintf(buf, "%d.%d GiB", tmp / 10, tmp % 10);
+       } else {
+               tmp = 10 * sectors / (1024ULL * 1024 * 1024 * 1024);
+               sprintf(buf, "%d.%d TiB", tmp / 10, tmp % 10);
+       }
+       return buf;
+}
+
+static void
+gpt_print_wide(uint16_t *s, int max_len)
+{
+       int i = 0;
+
+       while (i < max_len) {
+               if (*s == 0)
+                       return;
+               printf("%c", *s);
+               s++;
+       }
+}
+
+static void
+gpt_list_table(int xtra)
+{
+       int i;
+
+       printf("Disk %s: %llu sectors, %s\n", disk_device,
+               (unsigned long long)total_number_of_sectors,
+               gpt_size(total_number_of_sectors));
+       printf("Logical sector size: %d\n", sector_size);
+       printf("Disk identifier (GUID): ");
+       gpt_print_guid(gpt_hdr->disk_guid);
+       printf("\nPartition table holds up to %d entries\n",
+               SWAP_LE32(gpt_hdr->n_parts));
+       printf("First usable sector is %llu, last usable sector is %llu\n",
+               (unsigned long long)SWAP_LE64(gpt_hdr->first_usable_lba),
+               (unsigned long long)SWAP_LE64(gpt_hdr->last_usable_lba));
+       printf("\n");
+
+       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) {
+                       printf("%4d %15llu %15llu %11s   %04x  ", i + 1,
+                               (unsigned long long)SWAP_LE64(p->lba_start),
+                               (unsigned long long)SWAP_LE64(p->lba_end),
+                               gpt_size(1 + SWAP_LE64(p->lba_end) - 
SWAP_LE64(p->lba_start)),
+                               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 (SWAP_LE64(gpt_hdr->magic) != 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 /* ENABLE_FEATURE_GPT_LABEL */
-- 
1.6.5

_______________________________________________
busybox mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to