fdisk: initial stab at GPT partition support
[oweals/busybox.git] / util-linux / fdisk_gpt.c
1 #if ENABLE_FEATURE_GPT_LABEL
2 /*
3  * Copyright (C) 2010 Kevin Cernekee <cernekee@gmail.com>
4  *
5  * Licensed under GPLv2, see file LICENSE in this source tree.
6  */
7
8 #define GPT_MAGIC 0x5452415020494645ULL
9 enum {
10         LEGACY_GPT_TYPE = 0xee,
11         GPT_MAX_PARTS   = 256,
12         GPT_MAX_PART_ENTRY_LEN = 4096,
13         GUID_LEN        = 16,
14 };
15
16 typedef struct {
17         uint64_t magic;
18         uint32_t revision;
19         uint32_t hdr_size;
20         uint32_t hdr_crc32;
21         uint32_t reserved;
22         uint64_t current_lba;
23         uint64_t backup_lba;
24         uint64_t first_usable_lba;
25         uint64_t last_usable_lba;
26         uint8_t  disk_guid[GUID_LEN];
27         uint64_t first_part_lba;
28         uint32_t n_parts;
29         uint32_t part_entry_len;
30         uint32_t part_array_crc32;
31 } gpt_header;
32
33 typedef struct {
34         uint8_t  type_guid[GUID_LEN];
35         uint8_t  part_guid[GUID_LEN];
36         uint64_t lba_start;
37         uint64_t lba_end;
38         uint64_t flags;
39         uint16_t name[36];
40 } gpt_partition;
41
42 static gpt_header *gpt_hdr;
43
44 static char *part_array;
45 static unsigned int n_parts;
46 static unsigned int part_array_len;
47 static unsigned int part_entry_len;
48
49 static uint32_t *crc32_table;
50
51 static inline gpt_partition *
52 gpt_part(int i)
53 {
54         if (i >= n_parts) {
55                 return NULL;
56         }
57         return (gpt_partition *)&part_array[i * part_entry_len];
58 }
59
60 /* TODO: move to libbb */
61 static uint32_t
62 gpt_crc32(void *buf, int len)
63 {
64         uint32_t crc = 0xffffffff;
65
66         for (; len > 0; len--, buf++) {
67                 crc = crc32_table[(crc ^ *((char *)buf)) & 0xff] ^ (crc >> 8);
68         }
69         return crc ^ 0xffffffff;
70 }
71
72 static void
73 gpt_print_guid(uint8_t *buf)
74 {
75         printf(
76                 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
77                 buf[3], buf[2], buf[1], buf[0],
78                 buf[5], buf[4],
79                 buf[7], buf[6],
80                 buf[8], buf[9],
81                 buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
82 }
83
84 /* TODO: real unicode support */
85 static void
86 gpt_print_wide(uint16_t *s, int max_len)
87 {
88         int i = 0;
89
90         while (i < max_len) {
91                 if (*s == 0)
92                         return;
93                 fputc(*s, stdout);
94                 s++;
95         }
96 }
97
98 static void
99 gpt_list_table(int xtra UNUSED_PARAM)
100 {
101         int i;
102         char numstr6[6];
103
104         numstr6[5] = '\0';
105
106         smart_ulltoa5(total_number_of_sectors, numstr6, " KMGTPEZY");
107         printf("Disk %s: %llu sectors, %s\n", disk_device,
108                 (unsigned long long)total_number_of_sectors,
109                 numstr6);
110         printf("Logical sector size: %u\n", sector_size);
111         printf("Disk identifier (GUID): ");
112         gpt_print_guid(gpt_hdr->disk_guid);
113         printf("\nPartition table holds up to %u entries\n",
114                 (int)SWAP_LE32(gpt_hdr->n_parts));
115         printf("First usable sector is %llu, last usable sector is %llu\n\n",
116                 (unsigned long long)SWAP_LE64(gpt_hdr->first_usable_lba),
117                 (unsigned long long)SWAP_LE64(gpt_hdr->last_usable_lba));
118
119         printf("Number  Start (sector)    End (sector)  Size       Code  Name\n");
120         for (i = 0; i < n_parts; i++) {
121                 gpt_partition *p = gpt_part(i);
122                 if (p->lba_start) {
123                         smart_ulltoa5(1 + SWAP_LE64(p->lba_end) - SWAP_LE64(p->lba_start),
124                                 numstr6, " KMGTPEZY");
125                         printf("%4u %15llu %15llu %11s   %04x  ",
126                                 i + 1,
127                                 (unsigned long long)SWAP_LE64(p->lba_start),
128                                 (unsigned long long)SWAP_LE64(p->lba_end),
129                                 numstr6,
130                                 0x0700 /* FIXME */);
131                         gpt_print_wide(p->name, 18);
132                         printf("\n");
133                 }
134         }
135 }
136
137 static int
138 check_gpt_label(void)
139 {
140         struct partition *first = pt_offset(MBRbuffer, 0);
141         struct pte pe;
142         uint32_t crc;
143
144         /* LBA 0 contains the legacy MBR */
145
146         if (!valid_part_table_flag(MBRbuffer)
147          || first->sys_ind != LEGACY_GPT_TYPE
148         ) {
149                 current_label_type = 0;
150                 return 0;
151         }
152
153         /* LBA 1 contains the GPT header */
154
155         read_pte(&pe, 1);
156         gpt_hdr = (void *)pe.sectorbuffer;
157
158         if (gpt_hdr->magic != SWAP_LE64(GPT_MAGIC)) {
159                 current_label_type = 0;
160                 return 0;
161         }
162
163         if (!crc32_table) {
164                 crc32_table = crc32_filltable(NULL, 0);
165         }
166
167         crc = SWAP_LE32(gpt_hdr->hdr_crc32);
168         gpt_hdr->hdr_crc32 = 0;
169         if (gpt_crc32(gpt_hdr, SWAP_LE32(gpt_hdr->hdr_size)) != crc) {
170                 /* FIXME: read the backup table */
171                 puts("\nwarning: GPT header CRC is invalid\n");
172         }
173
174         n_parts = SWAP_LE32(gpt_hdr->n_parts);
175         part_entry_len = SWAP_LE32(gpt_hdr->part_entry_len);
176         if (n_parts > GPT_MAX_PARTS
177          || part_entry_len > GPT_MAX_PART_ENTRY_LEN
178          || SWAP_LE32(gpt_hdr->hdr_size) > sector_size
179         ) {
180                 puts("\nwarning: unable to parse GPT disklabel\n");
181                 current_label_type = 0;
182                 return 0;
183         }
184
185         part_array_len = n_parts * part_entry_len;
186         part_array = xmalloc(part_array_len);
187         seek_sector(SWAP_LE64(gpt_hdr->first_part_lba));
188         if (full_read(dev_fd, part_array, part_array_len) != part_array_len) {
189                 fdisk_fatal(unable_to_read);
190         }
191
192         if (gpt_crc32(part_array, part_array_len) != gpt_hdr->part_array_crc32) {
193                 /* FIXME: read the backup table */
194                 puts("\nwarning: GPT array CRC is invalid\n");
195         }
196
197         puts("Found valid GPT with protective MBR; using GPT\n");
198
199         current_label_type = LABEL_GPT;
200         return 1;
201 }
202
203 #endif /* GPT_LABEL */