1/* See LICENSE file for copyright and license details. */
2#include "util.h"
3#include "diskutil.h"
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <unistd.h>
9#include <fcntl.h>
10
11struct MbrPartition {
12 uint8_t boot;
13 uint8_t start_chs[3];
14 uint8_t type;
15 uint8_t end_chs[3];
16 uint32_t start_lba;
17 uint32_t size;
18} __attribute__((packed));
19
20struct MbrHeader {
21 uint8_t code[446];
22 struct MbrPartition parts[4];
23 uint8_t sig[2];
24} __attribute__((packed));
25
26struct GptHeader {
27 uint8_t sig[8];
28 uint32_t rev;
29 uint32_t size;
30 uint32_t crc;
31 uint32_t reserved;
32 uint64_t current_lba;
33 uint64_t backup_lba;
34 uint64_t first_usable;
35 uint64_t last_usable;
36 uint8_t disk_guid[16];
37 uint64_t partition_lba;
38 uint32_t num_parts;
39 uint32_t part_size;
40 uint32_t parts_crc;
41} __attribute__((packed));
42
43struct GptPartition {
44 uint8_t type_guid[16];
45 uint8_t part_guid[16];
46 uint64_t start_lba;
47 uint64_t end_lba;
48 uint64_t flags;
49 uint16_t name[36];
50} __attribute__((packed));
51
52static uint32_t crc32_table[256];
53static int crc32_table_initialized = 0;
54
55static void
56init_crc32_table(void)
57{
58 uint32_t i, j, c;
59 for (i = 0; i < 256; i++) {
60 c = i;
61 for (j = 0; j < 8; j++) {
62 if (c & 1)
63 c = 0xedb88320U ^ (c >> 1);
64 else
65 c = c >> 1;
66 }
67 crc32_table[i] = c;
68 }
69 crc32_table_initialized = 1;
70}
71
72static uint32_t
73crc32(uint32_t crc, const void *buf, size_t len)
74{
75 const uint8_t *p = buf;
76 if (!crc32_table_initialized)
77 init_crc32_table();
78 while (len--)
79 crc = (crc >> 8) ^ crc32_table[(crc ^ *p++) & 0xFF];
80 return crc;
81}
82
83static int opt_list = 0;
84static int opt_print = 0;
85static int opt_init_gpt = 0;
86static int opt_init_mbr = 0;
87static int opt_add = 0;
88static int opt_del = 0;
89static int opt_part_idx = -1;
90static uint64_t opt_start = 0;
91static uint64_t opt_end = 0;
92static const char *opt_type = NULL;
93
94static void
95usage(void)
96{
97 eprintf("usage: %s [-l] [-p] [-g] [-m] [-a] [-d] [-n index] [-b start] [-e end] [-t type] [device]\n", argv0);
98}
99
100static void
101print_mbr(struct MbrHeader *mbr, const char *path)
102{
103 int i;
104 printf("Disk: %s\n", path);
105 printf("Partition table (MBR/dos):\n");
106 printf("%-5s %-4s %-12s %-12s %-4s\n", "Index", "Boot", "Start", "Size", "Type");
107 for (i = 0; i < 4; i++) {
108 struct MbrPartition *p = &mbr->parts[i];
109 if (p->size == 0)
110 continue;
111 printf("%-5d %-4s %-12u %-12u 0x%02x\n",
112 i + 1,
113 p->boot == 0x80 ? "Yes" : "No",
114 p->start_lba,
115 p->size,
116 p->type);
117 }
118}
119
120static void
121print_gpt(struct GptHeader *hdr, struct GptPartition *parts, const char *path)
122{
123 uint32_t i;
124 char guid[37];
125 printf("Disk: %s\n", path);
126 printf("Partition table (GPT):\n");
127 printf("%-5s %-12s %-12s %-36s\n", "Index", "Start", "End", "Type GUID");
128 for (i = 0; i < hdr->num_parts; i++) {
129 struct GptPartition *p = &parts[i];
130 if (p->start_lba == 0 && p->end_lba == 0)
131 continue;
132
133 snprintf(guid, sizeof(guid),
134 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
135 p->type_guid[3], p->type_guid[2], p->type_guid[1], p->type_guid[0],
136 p->type_guid[5], p->type_guid[4],
137 p->type_guid[7], p->type_guid[6],
138 p->type_guid[8], p->type_guid[9],
139 p->type_guid[10], p->type_guid[11], p->type_guid[12], p->type_guid[13], p->type_guid[14], p->type_guid[15]);
140
141 printf("%-5d %-12llu %-12llu %-36s\n",
142 i + 1,
143 (unsigned long long)p->start_lba,
144 (unsigned long long)p->end_lba,
145 guid);
146 }
147}
148
149static int
150read_gpt(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts)
151{
152 unsigned char sector[512];
153 size_t part_sectors;
154
155 if (blockdev_read(dev, 1, sector, 1) < 0)
156 return -1;
157
158 memcpy(hdr, sector, sizeof(*hdr));
159
160 if (memcmp(hdr->sig, "EFI PART", 8) != 0)
161 return -1;
162
163 part_sectors = (hdr->num_parts * hdr->part_size + dev->sec_size - 1) / dev->sec_size;
164 if (blockdev_read(dev, hdr->partition_lba, parts, part_sectors) < 0)
165 return -1;
166
167 return 0;
168}
169
170static int
171write_gpt(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts)
172{
173 unsigned char sector[512];
174 size_t part_sectors = (hdr->num_parts * hdr->part_size + dev->sec_size - 1) / dev->sec_size;
175
176 /* update partition entries crc */
177 hdr->parts_crc = crc32(~0U, parts, hdr->num_parts * hdr->part_size) ^ ~0U;
178
179 /* calculate header crc (zeroing the crc field first) */
180 hdr->crc = 0;
181 hdr->crc = crc32(~0U, hdr, hdr->size) ^ ~0U;
182
183 /* write protective mbr to sector 0 */
184 struct MbrHeader pmbr;
185 memset(&pmbr, 0, sizeof(pmbr));
186 pmbr.parts[0].type = 0xEE;
187 pmbr.parts[0].start_lba = 1;
188 uint64_t limit = dev->size / dev->sec_size - 1;
189 pmbr.parts[0].size = limit > 0xFFFFFFFFU ? 0xFFFFFFFFU : (uint32_t)limit;
190 pmbr.sig[0] = 0x55;
191 pmbr.sig[1] = 0xAA;
192 if (blockdev_write(dev, 0, &pmbr, 1) < 0)
193 return -1;
194
195 /* write primary header and partition entries */
196 memset(sector, 0, sizeof(sector));
197 memcpy(sector, hdr, sizeof(*hdr));
198 if (blockdev_write(dev, 1, sector, 1) < 0)
199 return -1;
200 if (blockdev_write(dev, hdr->partition_lba, parts, part_sectors) < 0)
201 return -1;
202
203 /* write backup partition entries and backup header */
204 struct GptHeader backup_hdr = *hdr;
205 backup_hdr.current_lba = hdr->backup_lba;
206 backup_hdr.backup_lba = hdr->current_lba;
207 backup_hdr.partition_lba = backup_hdr.current_lba - part_sectors;
208
209 backup_hdr.crc = 0;
210 backup_hdr.crc = crc32(~0U, &backup_hdr, backup_hdr.size) ^ ~0U;
211
212 if (blockdev_write(dev, backup_hdr.partition_lba, parts, part_sectors) < 0)
213 return -1;
214
215 memset(sector, 0, sizeof(sector));
216 memcpy(sector, &backup_hdr, sizeof(backup_hdr));
217 if (blockdev_write(dev, backup_hdr.current_lba, sector, 1) < 0)
218 return -1;
219
220 return 0;
221}
222
223static int
224do_print(const char *path)
225{
226 struct BlockDev dev;
227 struct MbrHeader mbr;
228 struct GptHeader gpt_hdr;
229 struct GptPartition gpt_parts[128];
230
231 if (blockdev_open(&dev, path, 0) < 0) {
232 weprintf("cannot open %s:", path);
233 return -1;
234 }
235
236 if (read_gpt(&dev, &gpt_hdr, gpt_parts) == 0) {
237 print_gpt(&gpt_hdr, gpt_parts, path);
238 } else {
239 if (blockdev_read(&dev, 0, &mbr, 1) == 0 && mbr.sig[0] == 0x55 && mbr.sig[1] == 0xAA) {
240 print_mbr(&mbr, path);
241 } else {
242 printf("Disk %s: unpartitioned or unknown format\n", path);
243 }
244 }
245
246 blockdev_close(&dev);
247 return 0;
248}
249
250static int
251do_fdisk(const char *path)
252{
253 struct BlockDev dev;
254 struct MbrHeader mbr;
255 struct GptHeader gpt_hdr;
256 struct GptPartition gpt_parts[128];
257 int has_gpt = 0;
258
259 if (blockdev_open(&dev, path, 1) < 0) {
260 weprintf("cannot open %s:", path);
261 return -1;
262 }
263
264 has_gpt = (read_gpt(&dev, &gpt_hdr, gpt_parts) == 0);
265
266 if (opt_init_mbr) {
267 memset(&mbr, 0, sizeof(mbr));
268 mbr.sig[0] = 0x55;
269 mbr.sig[1] = 0xAA;
270 if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
271 weprintf("cannot write MBR to %s:", path);
272 blockdev_close(&dev);
273 return -1;
274 }
275 printf("Initialized MBR partition table on %s\n", path);
276 blockdev_close(&dev);
277 return 0;
278 }
279
280 if (opt_init_gpt) {
281 memset(&gpt_hdr, 0, sizeof(gpt_hdr));
282 memcpy(gpt_hdr.sig, "EFI PART", 8);
283 gpt_hdr.rev = 0x00010000;
284 gpt_hdr.size = 92;
285 gpt_hdr.current_lba = 1;
286 gpt_hdr.backup_lba = dev.size / dev.sec_size - 1;
287 gpt_hdr.first_usable = 34;
288 gpt_hdr.last_usable = gpt_hdr.backup_lba - 34;
289 gpt_hdr.partition_lba = 2;
290 gpt_hdr.num_parts = 128;
291 gpt_hdr.part_size = 128;
292 memset(gpt_parts, 0, sizeof(gpt_parts));
293
294 if (write_gpt(&dev, &gpt_hdr, gpt_parts) < 0) {
295 weprintf("cannot write GPT to %s:", path);
296 blockdev_close(&dev);
297 return -1;
298 }
299 printf("Initialized GPT partition table on %s\n", path);
300 blockdev_close(&dev);
301 return 0;
302 }
303
304 if (opt_add) {
305 if (opt_part_idx < 1) {
306 weprintf("add requires partition index (-n)\n");
307 blockdev_close(&dev);
308 return -1;
309 }
310
311 if (has_gpt) {
312 if (opt_part_idx > 128) {
313 weprintf("GPT partition index must be 1-128\n");
314 blockdev_close(&dev);
315 return -1;
316 }
317 struct GptPartition *p = &gpt_parts[opt_part_idx - 1];
318 p->start_lba = opt_start;
319 p->end_lba = opt_end;
320 /* default type guid: linux filesystem data */
321 p->type_guid[0] = 0xAF; p->type_guid[1] = 0x3D; p->type_guid[2] = 0xC6; p->type_guid[3] = 0x0F;
322 p->type_guid[4] = 0x83; p->type_guid[5] = 0x84;
323 p->type_guid[6] = 0x72; p->type_guid[7] = 0x47;
324 p->type_guid[8] = 0x8E; p->type_guid[9] = 0x79;
325 p->type_guid[10] = 0x3D; p->type_guid[11] = 0x69; p->type_guid[12] = 0xD8; p->type_guid[13] = 0x47; p->type_guid[14] = 0x7D; p->type_guid[15] = 0xE4;
326
327 if (write_gpt(&dev, &gpt_hdr, gpt_parts) < 0) {
328 weprintf("cannot update GPT on %s:", path);
329 blockdev_close(&dev);
330 return -1;
331 }
332 printf("Added GPT partition %d to %s\n", opt_part_idx, path);
333 } else {
334 if (blockdev_read(&dev, 0, &mbr, 1) < 0) {
335 weprintf("cannot read MBR from %s:", path);
336 blockdev_close(&dev);
337 return -1;
338 }
339 if (opt_part_idx > 4) {
340 weprintf("MBR partition index must be 1-4\n");
341 blockdev_close(&dev);
342 return -1;
343 }
344 struct MbrPartition *p = &mbr.parts[opt_part_idx - 1];
345 p->start_lba = (uint32_t)opt_start;
346 p->size = (uint32_t)(opt_end - opt_start + 1);
347 p->type = opt_type ? (uint8_t)strtoul(opt_type, NULL, 0) : 0x83;
348 mbr.sig[0] = 0x55;
349 mbr.sig[1] = 0xAA;
350
351 if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
352 weprintf("cannot update MBR on %s:", path);
353 blockdev_close(&dev);
354 return -1;
355 }
356 printf("Added MBR partition %d to %s\n", opt_part_idx, path);
357 }
358 }
359
360 if (opt_del) {
361 if (opt_part_idx < 1) {
362 weprintf("delete requires partition index (-n)\n");
363 blockdev_close(&dev);
364 return -1;
365 }
366
367 if (has_gpt) {
368 if (opt_part_idx > 128) {
369 weprintf("GPT partition index must be 1-128\n");
370 blockdev_close(&dev);
371 return -1;
372 }
373 memset(&gpt_parts[opt_part_idx - 1], 0, sizeof(struct GptPartition));
374 if (write_gpt(&dev, &gpt_hdr, gpt_parts) < 0) {
375 weprintf("cannot update GPT on %s:", path);
376 blockdev_close(&dev);
377 return -1;
378 }
379 printf("Deleted GPT partition %d from %s\n", opt_part_idx, path);
380 } else {
381 if (blockdev_read(&dev, 0, &mbr, 1) < 0) {
382 weprintf("cannot read MBR from %s:", path);
383 blockdev_close(&dev);
384 return -1;
385 }
386 if (opt_part_idx > 4) {
387 weprintf("MBR partition index must be 1-4\n");
388 blockdev_close(&dev);
389 return -1;
390 }
391 memset(&mbr.parts[opt_part_idx - 1], 0, sizeof(struct MbrPartition));
392 if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
393 weprintf("cannot update MBR on %s:", path);
394 blockdev_close(&dev);
395 return -1;
396 }
397 printf("Deleted MBR partition %d from %s\n", opt_part_idx, path);
398 }
399 }
400
401 blockdev_close(&dev);
402 return 0;
403}
404
405// ?man fdisk: partition table manipulator
406// ?man arguments: [-l] [-p] [-g] [-m] [-a] [-d] [-n index] [-b start] [-e end] [-t type] [device]
407// ?man fdisk performs partition table operations for MBR and GPT disks
408int
409main(int argc, char *argv[])
410{
411 ARGBEGIN {
412 // ?man -l:list partitions of all devices
413 case 'l':
414 opt_list = 1;
415 break;
416 // ?man -p:print partition table of specified device
417 case 'p':
418 opt_print = 1;
419 break;
420 // ?man -g:initialize disk with GPT
421 case 'g':
422 opt_init_gpt = 1;
423 break;
424 // ?man -m:initialize disk with MBR
425 case 'm':
426 opt_init_mbr = 1;
427 break;
428 // ?man -a:add partition
429 case 'a':
430 opt_add = 1;
431 break;
432 // ?man -d:delete partition
433 case 'd':
434 opt_del = 1;
435 break;
436 // ?man -n:partition index
437 case 'n':
438 opt_part_idx = (int)estrtol(EARGF(usage()), 10);
439 break;
440 // ?man -b:starting sector LBA
441 case 'b':
442 opt_start = (uint64_t)estrtoul(EARGF(usage()), 10);
443 break;
444 // ?man -e:ending sector LBA
445 case 'e':
446 opt_end = (uint64_t)estrtoul(EARGF(usage()), 10);
447 break;
448 // ?man -t:partition type (MBR hex or GPT string)
449 case 't':
450 opt_type = EARGF(usage());
451 break;
452 default:
453 usage();
454 } ARGEND
455
456 if (opt_list) {
457 struct BlockDevInfo *list = blockdev_get_list();
458 struct BlockDevInfo *curr;
459 if (!list)
460 return 1;
461 for (curr = list; curr; curr = curr->next) {
462 char devpath[128];
463 snprintf(devpath, sizeof(devpath), "/dev/%s", curr->name);
464 do_print(devpath);
465 }
466 blockdev_free_list(list);
467 return 0;
468 }
469
470 if (argc != 1)
471 usage();
472
473 if (opt_print) {
474 return do_print(argv[0]) < 0 ? 1 : 0;
475 }
476
477 return do_fdisk(argv[0]) < 0 ? 1 : 0;
478}