1/* see LICENSE file for copyright and license details */
2#include "diskutil.h"
3#include "util.h"
4
5#include <fcntl.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <strings.h>
10#include <unistd.h>
11
12struct MbrPartition {
13 uint8_t boot;
14 uint8_t start_chs[3];
15 uint8_t type;
16 uint8_t end_chs[3];
17 uint32_t start_lba;
18 uint32_t size;
19} __attribute__((packed));
20
21struct MbrHeader {
22 uint8_t code[446];
23 struct MbrPartition parts[4];
24 uint8_t sig[2];
25} __attribute__((packed));
26
27struct GptHeader {
28 uint8_t sig[8];
29 uint32_t rev;
30 uint32_t size;
31 uint32_t crc;
32 uint32_t reserved;
33 uint64_t current_lba;
34 uint64_t backup_lba;
35 uint64_t first_usable;
36 uint64_t last_usable;
37 uint8_t disk_guid[16];
38 uint64_t partition_lba;
39 uint32_t num_parts;
40 uint32_t part_size;
41 uint32_t parts_crc;
42} __attribute__((packed));
43
44struct GptPartition {
45 uint8_t type_guid[16];
46 uint8_t part_guid[16];
47 uint64_t start_lba;
48 uint64_t end_lba;
49 uint64_t flags;
50 uint16_t name[36];
51} __attribute__((packed));
52
53struct BsdPartition {
54 uint32_t size;
55 uint32_t offset;
56 uint16_t offset_h;
57 uint16_t size_h;
58 uint8_t fstype;
59 uint8_t fragblock;
60 uint16_t cpg;
61} __attribute__((packed));
62
63struct BsdDisklabel {
64 uint32_t d_magic;
65 uint16_t d_type;
66 uint16_t d_subtype;
67 char d_typename[16];
68 char d_packname[16];
69 uint32_t d_secsize;
70 uint32_t d_nsectors;
71 uint32_t d_ntracks;
72 uint32_t d_ncylinders;
73 uint32_t d_secpercyl;
74 uint32_t d_secperunit;
75 uint8_t d_uid[8];
76 uint32_t d_acylinders;
77 uint16_t d_bstarth;
78 uint16_t d_bendh;
79 uint32_t d_bstart;
80 uint32_t d_bend;
81 uint32_t d_flags;
82 uint32_t d_spare4[5];
83 uint16_t d_secperunith;
84 uint16_t d_version;
85 uint32_t d_spare[4];
86 uint32_t d_magic2;
87 uint16_t d_checksum;
88 uint16_t d_npartitions;
89 uint32_t d_spare2;
90 uint32_t d_spare3;
91 struct BsdPartition d_partitions[16];
92} __attribute__((packed));
93
94static uint32_t crc32_table[256];
95static int crc32_table_initialized = 0;
96
97static void
98init_crc32_table(void)
99{
100 uint32_t i, j, c;
101 for (i = 0; i < 256; i++) {
102 c = i;
103 for (j = 0; j < 8; j++) {
104 if (c & 1)
105 c = 0xedb88320U ^ (c >> 1);
106 else
107 c = c >> 1;
108 }
109 crc32_table[i] = c;
110 }
111 crc32_table_initialized = 1;
112}
113
114static uint32_t
115crc32(uint32_t crc, const void *buf, size_t len)
116{
117 const uint8_t *p = buf;
118 if (!crc32_table_initialized)
119 init_crc32_table();
120 while (len--)
121 crc = (crc >> 8) ^ crc32_table[(crc ^ *p++) & 0xFF];
122 return crc;
123}
124
125/* short two-byte codes match gdisk/sgdisk's own convention, since
126 * every caller already writing "-t ef00" etc against those tools
127 * expects the same codes here */
128static const struct {
129 const char *code;
130 uint8_t guid[16];
131} gpt_type_table[] = {
132 /* ef00: efi system partition */
133 {"ef00",
134 {0x28, 0x73, 0x2a, 0xc1, 0x1f, 0xf8, 0xd2, 0x11, 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9,
135 0x3b} },
136 /* ef02: bios boot partition */
137 {"ef02",
138 {0x48, 0x61, 0x68, 0x21, 0x49, 0x64, 0x6f, 0x6e, 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46,
139 0x49} },
140 /* 8200: linux swap */
141 {"8200",
142 {0x6d, 0xfd, 0x57, 0x06, 0xab, 0xa4, 0xc4, 0x43, 0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f,
143 0x4f} },
144 /* 8300: linux filesystem data */
145 {"8300",
146 {0xaf, 0x3d, 0xc6, 0x0f, 0x84, 0x83, 0x72, 0x47, 0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d,
147 0xe4} },
148};
149#define GPT_TYPE_TABLE_LEN (int)(sizeof(gpt_type_table) / sizeof(gpt_type_table[0]))
150
151/* canonical XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX form: the first three
152 * groups store little-endian, the last two store big-endian, the same
153 * mixed layout every other GUID field in this file already uses */
154static int
155parse_guid_string(const char *s, uint8_t out[16])
156{
157 static const int group_len[5] = {8, 4, 4, 4, 12};
158 static const int group_le[5] = {1, 1, 1, 0, 0};
159 int g, i, hi, lo, byte_idx = 0;
160 const char *p = s;
161
162 for (g = 0; g < 5; g++) {
163 uint8_t bytes[6];
164 int nbytes = group_len[g] / 2;
165 for (i = 0; i < nbytes; i++) {
166 hi = hexval(p[i * 2]);
167 lo = hexval(p[i * 2 + 1]);
168 if (hi < 0 || lo < 0)
169 return -1;
170 bytes[i] = (uint8_t)((hi << 4) | lo);
171 }
172 if (group_le[g]) {
173 for (i = nbytes - 1; i >= 0; i--)
174 out[byte_idx++] = bytes[i];
175 } else {
176 for (i = 0; i < nbytes; i++)
177 out[byte_idx++] = bytes[i];
178 }
179 p += group_len[g];
180 if (g < 4) {
181 if (*p != '-')
182 return -1;
183 p++;
184 }
185 }
186 return *p == '\0' ? 0 : -1;
187}
188
189/* opt_type is a short gdisk-style code (see gpt_type_table above), a
190 * full canonical GUID string, or unset: unset (or unrecognized, with
191 * a warning) keeps the long-standing default of "linux filesystem
192 * data", matching what every caller got before -t existed */
193static void
194gpt_type_guid(const char *type, uint8_t out[16])
195{
196 int i;
197
198 if (type) {
199 for (i = 0; i < GPT_TYPE_TABLE_LEN; i++) {
200 if (strcasecmp(type, gpt_type_table[i].code) == 0) {
201 memcpy(out, gpt_type_table[i].guid, 16);
202 return;
203 }
204 }
205 if (parse_guid_string(type, out) == 0)
206 return;
207 weprintf("unrecognized GPT type '%s', using linux filesystem data\n", type);
208 }
209 memcpy(out, gpt_type_table[GPT_TYPE_TABLE_LEN - 1].guid, 16);
210}
211
212/* resolves -b for a GPT add: a plain sector number, or 0/unset for
213 * the first free sector right after every existing partition's own
214 * end_lba (gpt_hdr.first_usable if the table is still empty), the
215 * same "0 means auto-place" convention gdisk-family tools already use */
216static uint64_t
217gpt_resolve_start(struct GptHeader *hdr, struct GptPartition *parts, const char *s)
218{
219 uint32_t i;
220 uint64_t next = hdr->first_usable;
221
222 if (s && strcmp(s, "0") != 0)
223 return (uint64_t)estrtoul(s, 10);
224
225 for (i = 0; i < hdr->num_parts; i++) {
226 if (parts[i].start_lba == 0 && parts[i].end_lba == 0)
227 continue;
228 if (parts[i].end_lba + 1 > next)
229 next = parts[i].end_lba + 1;
230 }
231 return next;
232}
233
234/* resolves -e for a GPT add: a plain sector number, 0/unset for the
235 * disk's own last usable sector, or +SIZE for a size relative to the
236 * already-resolved start */
237static uint64_t
238gpt_resolve_end(struct GptHeader *hdr, size_t sec_size, uint64_t start, const char *s)
239{
240 off_t size_bytes;
241 uint64_t sectors;
242
243 if (!s || strcmp(s, "0") == 0)
244 return hdr->last_usable;
245 if (s[0] == '+') {
246 size_bytes = parseoffset(s + 1);
247 if (size_bytes < 0)
248 eprintf("fdisk: malformed size '%s'\n", s);
249 sectors = ((uint64_t)size_bytes + sec_size - 1) / sec_size;
250 return start + sectors - 1;
251 }
252 return (uint64_t)estrtoul(s, 10);
253}
254
255/* GPT partition names are UTF-16LE, 36 code units, null-padded: every
256 * caller so far only ever needs a plain ASCII label, so this widens
257 * each byte rather than pulling in a real UTF-8 decoder for it */
258static void
259gpt_set_name(uint16_t name[36], const char *s)
260{
261 size_t i;
262
263 memset(name, 0, 36 * sizeof(uint16_t));
264 if (!s)
265 return;
266 for (i = 0; i < 35 && s[i]; i++)
267 name[i] = (uint16_t)(unsigned char)s[i];
268}
269
270static int opt_list = 0;
271static int opt_print = 0;
272static int opt_init_gpt = 0;
273static int opt_init_mbr = 0;
274static int opt_init_bsd = 0;
275static int opt_add = 0;
276static int opt_del = 0;
277static int opt_part_idx = -1;
278static int opt_part_is_letter = 0;
279static const char *opt_start_str = NULL;
280static const char *opt_end_str = NULL;
281static const char *opt_type = NULL;
282static const char *opt_name = NULL;
283
284static void
285usage(void)
286{
287 eprintf(
288 "usage: %s [-l] [-p] [-g] [-m] [-s] [-a] [-d] [-n index] [-b "
289 "start] [-e end] [-t type] [-c name] [device]\n",
290 argv0
291 );
292}
293
294static void
295mbr_print(struct MbrHeader *mbr, const char *path)
296{
297 int i;
298 printf("Disk: %s\n", path);
299 printf("Partition table (MBR/dos):\n");
300 printf("%-5s %-4s %-12s %-12s %-4s\n", "Index", "Boot", "Start", "Size", "Type");
301 for (i = 0; i < 4; i++) {
302 struct MbrPartition *p = &mbr->parts[i];
303 if (p->size == 0)
304 continue;
305 printf(
306 "%-5d %-4s %-12u %-12u 0x%02x\n",
307 i + 1,
308 p->boot == 0x80 ? "Yes" : "No",
309 p->start_lba,
310 p->size,
311 p->type
312 );
313 }
314}
315
316static void
317gpt_print(struct GptHeader *hdr, struct GptPartition *parts, const char *path)
318{
319 uint32_t i;
320 char guid[37];
321 printf("Disk: %s\n", path);
322 printf("Partition table (GPT):\n");
323 printf("%-5s %-12s %-12s %-36s\n", "Index", "Start", "End", "Type GUID");
324 for (i = 0; i < hdr->num_parts; i++) {
325 struct GptPartition *p = &parts[i];
326 if (p->start_lba == 0 && p->end_lba == 0)
327 continue;
328
329 snprintf(
330 guid,
331 sizeof(guid),
332 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%"
333 "02x%02x%02x%02x",
334 p->type_guid[3],
335 p->type_guid[2],
336 p->type_guid[1],
337 p->type_guid[0],
338 p->type_guid[5],
339 p->type_guid[4],
340 p->type_guid[7],
341 p->type_guid[6],
342 p->type_guid[8],
343 p->type_guid[9],
344 p->type_guid[10],
345 p->type_guid[11],
346 p->type_guid[12],
347 p->type_guid[13],
348 p->type_guid[14],
349 p->type_guid[15]
350 );
351
352 printf(
353 "%-5d %-12llu %-12llu %-36s\n",
354 i + 1,
355 (unsigned long long)p->start_lba,
356 (unsigned long long)p->end_lba,
357 guid
358 );
359 }
360}
361
362static int
363gpt_read(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts)
364{
365 unsigned char sector[512];
366 size_t part_sectors;
367
368 if (blockdev_read(dev, 1, sector, 1) < 0)
369 return -1;
370
371 memcpy(hdr, sector, sizeof(*hdr));
372
373 if (memcmp(hdr->sig, "EFI PART", 8) != 0)
374 return -1;
375
376 part_sectors = (hdr->num_parts * hdr->part_size + dev->sec_size - 1) / dev->sec_size;
377 if (blockdev_read(dev, hdr->partition_lba, parts, part_sectors) < 0)
378 return -1;
379
380 return 0;
381}
382
383static int
384gpt_write(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts)
385{
386 unsigned char sector[512];
387 struct MbrHeader pmbr;
388 struct GptHeader backup_hdr;
389 uint64_t limit;
390 size_t part_sectors;
391
392 part_sectors = (hdr->num_parts * hdr->part_size + dev->sec_size - 1) / dev->sec_size;
393
394 hdr->parts_crc = crc32(~0U, parts, hdr->num_parts * hdr->part_size) ^ ~0U;
395
396 hdr->crc = 0;
397 hdr->crc = crc32(~0U, hdr, hdr->size) ^ ~0U;
398
399 memset(&pmbr, 0, sizeof(pmbr));
400 pmbr.parts[0].type = 0xEE;
401 pmbr.parts[0].start_lba = 1;
402 limit = dev->size / dev->sec_size - 1;
403 pmbr.parts[0].size = limit > 0xFFFFFFFFU ? 0xFFFFFFFFU : (uint32_t)limit;
404 pmbr.sig[0] = 0x55;
405 pmbr.sig[1] = 0xAA;
406 if (blockdev_write(dev, 0, &pmbr, 1) < 0)
407 return -1;
408
409 memset(sector, 0, sizeof(sector));
410 memcpy(sector, hdr, sizeof(*hdr));
411 if (blockdev_write(dev, 1, sector, 1) < 0)
412 return -1;
413 if (blockdev_write(dev, hdr->partition_lba, parts, part_sectors) < 0)
414 return -1;
415
416 backup_hdr = *hdr;
417 backup_hdr.current_lba = hdr->backup_lba;
418 backup_hdr.backup_lba = hdr->current_lba;
419 backup_hdr.partition_lba = backup_hdr.current_lba - part_sectors;
420
421 backup_hdr.crc = 0;
422 backup_hdr.crc = crc32(~0U, &backup_hdr, backup_hdr.size) ^ ~0U;
423
424 if (blockdev_write(dev, backup_hdr.partition_lba, parts, part_sectors) < 0)
425 return -1;
426
427 memset(sector, 0, sizeof(sector));
428 memcpy(sector, &backup_hdr, sizeof(backup_hdr));
429 if (blockdev_write(dev, backup_hdr.current_lba, sector, 1) < 0)
430 return -1;
431
432 return 0;
433}
434
435static uint16_t
436bsd_dkcksum(void *lp, size_t nparts)
437{
438 uint16_t *start;
439 uint16_t *end;
440 uint16_t sum = 0;
441
442 start = (uint16_t *)lp;
443 end = (uint16_t *)((char *)lp + 148 + nparts * 16);
444 while (start < end)
445 sum ^= *start++;
446 return sum;
447}
448
449static int
450bsd_is_partition_type(uint8_t type)
451{
452 return type == 0xa5 || type == 0xa6 || type == 0xa9;
453}
454
455static int
456bsd_read(struct BlockDev *dev, uint64_t base_sec, struct BsdDisklabel *lp)
457{
458 unsigned char sector[512];
459 if (blockdev_read(dev, base_sec + 1, sector, 1) < 0)
460 return -1;
461 memcpy(lp, sector, sizeof(*lp));
462 if (lp->d_magic != 0x82564557U || lp->d_magic2 != 0x82564557U)
463 return -1;
464 return 0;
465}
466
467static int
468bsd_write(struct BlockDev *dev, uint64_t base_sec, struct BsdDisklabel *lp)
469{
470 unsigned char sector[512];
471 memset(sector, 0, sizeof(sector));
472 lp->d_checksum = 0;
473 lp->d_checksum = bsd_dkcksum(lp, lp->d_npartitions);
474 memcpy(sector, lp, sizeof(*lp));
475 if (blockdev_write(dev, base_sec + 1, sector, 1) < 0)
476 return -1;
477 return 0;
478}
479
480static void
481bsd_print(struct BsdDisklabel *lp, const char *path, uint64_t base_sec)
482{
483 uint64_t start, size;
484 int i;
485
486 printf("Disk: %s (at sector %llu)\n", path, (unsigned long long)base_sec);
487 printf("Partition table (BSD disklabel):\n");
488 printf("%-5s %-12s %-12s %-10s\n", "Index", "Start", "Size", "Type");
489 for (i = 0; i < lp->d_npartitions; i++) {
490 struct BsdPartition *p = &lp->d_partitions[i];
491 start = ((uint64_t)p->offset_h << 32) | p->offset;
492 size = ((uint64_t)p->size_h << 32) | p->size;
493 if (size == 0)
494 continue;
495 printf(
496 "%-5c %-12llu %-12llu 0x%02x\n",
497 'a' + i,
498 (unsigned long long)start,
499 (unsigned long long)size,
500 p->fstype
501 );
502 }
503}
504
505static int
506bsd_find_label(struct BlockDev *dev, uint64_t *base_sec, struct BsdDisklabel *lp)
507{
508 struct MbrHeader mbr;
509 int i;
510
511 if (bsd_read(dev, 0, lp) == 0) {
512 *base_sec = 0;
513 return 0;
514 }
515
516 if (blockdev_read(dev, 0, &mbr, 1) == 0 && mbr.sig[0] == 0x55 && mbr.sig[1] == 0xAA) {
517 for (i = 0; i < 4; i++) {
518 if (mbr.parts[i].size > 0 && bsd_is_partition_type(mbr.parts[i].type)) {
519 if (bsd_read(dev, mbr.parts[i].start_lba, lp) == 0) {
520 *base_sec = mbr.parts[i].start_lba;
521 return 0;
522 }
523 }
524 }
525 }
526
527 return -1;
528}
529
530static int
531do_print(const char *path)
532{
533 struct BlockDev dev;
534 struct MbrHeader mbr;
535 struct GptHeader gpt_hdr;
536 struct GptPartition gpt_parts[128];
537 struct BsdDisklabel bsd_lp;
538 int i;
539 int found_bsd = 0;
540
541 if (blockdev_open(&dev, path, 0) < 0) {
542 weprintf("cannot open %s:", path);
543 return -1;
544 }
545
546 if (gpt_read(&dev, &gpt_hdr, gpt_parts) == 0) {
547 gpt_print(&gpt_hdr, gpt_parts, path);
548 } else if (blockdev_read(&dev, 0, &mbr, 1) == 0 && mbr.sig[0] == 0x55 && mbr.sig[1] == 0xAA) {
549 for (i = 0; i < 4; i++) {
550 if (mbr.parts[i].size > 0 && bsd_is_partition_type(mbr.parts[i].type)) {
551 if (bsd_read(&dev, mbr.parts[i].start_lba, &bsd_lp) == 0) {
552 bsd_print(&bsd_lp, path, mbr.parts[i].start_lba);
553 found_bsd = 1;
554 }
555 }
556 }
557 if (!found_bsd) {
558 if (bsd_read(&dev, 0, &bsd_lp) == 0) {
559 bsd_print(&bsd_lp, path, 0);
560 } else {
561 mbr_print(&mbr, path);
562 }
563 }
564 } else {
565 if (bsd_read(&dev, 0, &bsd_lp) == 0) {
566 bsd_print(&bsd_lp, path, 0);
567 } else {
568 printf("Disk %s: unpartitioned or unknown format\n", path);
569 }
570 }
571
572 blockdev_close(&dev);
573 return 0;
574}
575
576static int
577do_fdisk(const char *path)
578{
579 struct BlockDev dev;
580 struct MbrHeader mbr;
581 struct GptHeader gpt_hdr;
582 struct GptPartition gpt_parts[128];
583 struct BsdDisklabel lp;
584 struct BsdDisklabel bsd_lp;
585 struct GptPartition *gp;
586 struct BsdPartition *bp;
587 struct MbrPartition *mp;
588 uint64_t base_sec;
589 uint64_t limit;
590 uint64_t bsd_base;
591 uint64_t size;
592 int idx;
593 int has_gpt = 0;
594 int has_bsd = 0;
595 uint64_t bsd_start, bsd_end;
596 uint64_t mbr_start, mbr_end;
597
598 if (blockdev_open(&dev, path, 1) < 0) {
599 weprintf("cannot open %s:", path);
600 return -1;
601 }
602
603 has_gpt = (gpt_read(&dev, &gpt_hdr, gpt_parts) == 0);
604
605 if (opt_init_mbr) {
606 memset(&mbr, 0, sizeof(mbr));
607 mbr.sig[0] = 0x55;
608 mbr.sig[1] = 0xAA;
609 if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
610 weprintf("cannot write MBR to %s:", path);
611 blockdev_close(&dev);
612 return -1;
613 }
614 printf("Initialized MBR partition table on %s\n", path);
615 blockdev_close(&dev);
616 return 0;
617 }
618
619 if (opt_init_gpt) {
620 memset(&gpt_hdr, 0, sizeof(gpt_hdr));
621 memcpy(gpt_hdr.sig, "EFI PART", 8);
622 gpt_hdr.rev = 0x00010000;
623 gpt_hdr.size = 92;
624 gpt_hdr.current_lba = 1;
625 gpt_hdr.backup_lba = dev.size / dev.sec_size - 1;
626 gpt_hdr.first_usable = 34;
627 gpt_hdr.last_usable = gpt_hdr.backup_lba - 34;
628 gpt_hdr.partition_lba = 2;
629 gpt_hdr.num_parts = 128;
630 gpt_hdr.part_size = 128;
631 memset(gpt_parts, 0, sizeof(gpt_parts));
632
633 if (gpt_write(&dev, &gpt_hdr, gpt_parts) < 0) {
634 weprintf("cannot write GPT to %s:", path);
635 blockdev_close(&dev);
636 return -1;
637 }
638 printf("Initialized GPT partition table on %s\n", path);
639 blockdev_close(&dev);
640 return 0;
641 }
642
643 if (opt_init_bsd) {
644 base_sec = 0;
645 limit = dev.size / dev.sec_size;
646
647 if (blockdev_read(&dev, 0, &mbr, 1) == 0 && mbr.sig[0] == 0x55 && mbr.sig[1] == 0xAA) {
648 if (!opt_part_is_letter && opt_part_idx >= 1 && opt_part_idx <= 4) {
649 mp = &mbr.parts[opt_part_idx - 1];
650 if (mp->size > 0) {
651 base_sec = mp->start_lba;
652 limit = base_sec + mp->size;
653 }
654 }
655 }
656
657 memset(&lp, 0, sizeof(lp));
658 lp.d_magic = 0x82564557U;
659 lp.d_magic2 = 0x82564557U;
660 lp.d_secsize = dev.sec_size;
661 lp.d_npartitions = 8;
662 lp.d_partitions[2].offset = (uint32_t)base_sec;
663 lp.d_partitions[2].offset_h = (uint16_t)(base_sec >> 32);
664 size = limit - base_sec;
665 lp.d_partitions[2].size = (uint32_t)size;
666 lp.d_partitions[2].size_h = (uint16_t)(size >> 32);
667 lp.d_partitions[2].fstype = 0;
668
669 if (bsd_write(&dev, base_sec, &lp) < 0) {
670 weprintf("cannot write BSD disklabel to %s:", path);
671 blockdev_close(&dev);
672 return -1;
673 }
674 printf(
675 "Initialized BSD disklabel on %s (at sector %llu)\n", path, (unsigned long long)base_sec
676 );
677 blockdev_close(&dev);
678 return 0;
679 }
680
681 if (opt_add) {
682 if (opt_part_idx < 1) {
683 weprintf("add requires partition index (-n)\n");
684 blockdev_close(&dev);
685 return -1;
686 }
687
688 if (has_gpt) {
689 if (opt_part_idx > 128) {
690 weprintf("GPT partition index must be 1-128\n");
691 blockdev_close(&dev);
692 return -1;
693 }
694 gp = &gpt_parts[opt_part_idx - 1];
695 gp->start_lba = gpt_resolve_start(&gpt_hdr, gpt_parts, opt_start_str);
696 gp->end_lba = gpt_resolve_end(&gpt_hdr, dev.sec_size, gp->start_lba, opt_end_str);
697 gpt_type_guid(opt_type, gp->type_guid);
698 gpt_set_name(gp->name, opt_name);
699
700 if (gpt_write(&dev, &gpt_hdr, gpt_parts) < 0) {
701 weprintf("cannot update GPT on %s:", path);
702 blockdev_close(&dev);
703 return -1;
704 }
705 printf("Added GPT partition %d to %s\n", opt_part_idx, path);
706 } else {
707 has_bsd = (bsd_find_label(&dev, &bsd_base, &bsd_lp) == 0);
708 if (opt_part_is_letter || has_bsd) {
709 if (!has_bsd) {
710 weprintf(
711 "BSD disklabel not found on "
712 "%s\n",
713 path
714 );
715 blockdev_close(&dev);
716 return -1;
717 }
718 idx = opt_part_idx - 1;
719 if (idx >= 16) {
720 weprintf(
721 "BSD partition index must be "
722 "a-p\n"
723 );
724 blockdev_close(&dev);
725 return -1;
726 }
727 bp = &bsd_lp.d_partitions[idx];
728 bsd_start = opt_start_str ? (uint64_t)estrtoul(opt_start_str, 10) : 0;
729 bsd_end = opt_end_str ? (uint64_t)estrtoul(opt_end_str, 10) : 0;
730 bp->offset = (uint32_t)bsd_start;
731 bp->offset_h = (uint16_t)(bsd_start >> 32);
732 size = bsd_end - bsd_start + 1;
733 bp->size = (uint32_t)size;
734 bp->size_h = (uint16_t)(size >> 32);
735 bp->fstype = opt_type ? (uint8_t)strtoul(opt_type, NULL, 0) : 7;
736 if (idx >= bsd_lp.d_npartitions)
737 bsd_lp.d_npartitions = idx + 1;
738
739 if (bsd_write(&dev, bsd_base, &bsd_lp) < 0) {
740 weprintf(
741 "cannot update BSD disklabel "
742 "on %s:",
743 path
744 );
745 blockdev_close(&dev);
746 return -1;
747 }
748 printf("Added BSD partition %c to %s\n", 'a' + idx, path);
749 } else {
750 if (blockdev_read(&dev, 0, &mbr, 1) < 0) {
751 weprintf("cannot read MBR from %s:", path);
752 blockdev_close(&dev);
753 return -1;
754 }
755 if (opt_part_idx > 4) {
756 weprintf(
757 "MBR partition index must be "
758 "1-4\n"
759 );
760 blockdev_close(&dev);
761 return -1;
762 }
763 mp = &mbr.parts[opt_part_idx - 1];
764 mbr_start = opt_start_str ? (uint64_t)estrtoul(opt_start_str, 10) : 0;
765 mbr_end = opt_end_str ? (uint64_t)estrtoul(opt_end_str, 10) : 0;
766 mp->start_lba = (uint32_t)mbr_start;
767 mp->size = (uint32_t)(mbr_end - mbr_start + 1);
768 mp->type = opt_type ? (uint8_t)strtoul(opt_type, NULL, 0) : 0x83;
769 mbr.sig[0] = 0x55;
770 mbr.sig[1] = 0xAA;
771
772 if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
773 weprintf("cannot update MBR on %s:", path);
774 blockdev_close(&dev);
775 return -1;
776 }
777 printf("Added MBR partition %d to %s\n", opt_part_idx, path);
778 }
779 }
780 }
781
782 if (opt_del) {
783 if (opt_part_idx < 1) {
784 weprintf("delete requires partition index (-n)\n");
785 blockdev_close(&dev);
786 return -1;
787 }
788
789 if (has_gpt) {
790 if (opt_part_idx > 128) {
791 weprintf("GPT partition index must be 1-128\n");
792 blockdev_close(&dev);
793 return -1;
794 }
795 memset(&gpt_parts[opt_part_idx - 1], 0, sizeof(struct GptPartition));
796 if (gpt_write(&dev, &gpt_hdr, gpt_parts) < 0) {
797 weprintf("cannot update GPT on %s:", path);
798 blockdev_close(&dev);
799 return -1;
800 }
801 printf("Deleted GPT partition %d from %s\n", opt_part_idx, path);
802 } else {
803 has_bsd = (bsd_find_label(&dev, &bsd_base, &bsd_lp) == 0);
804 if (opt_part_is_letter || has_bsd) {
805 if (!has_bsd) {
806 weprintf(
807 "BSD disklabel not found on "
808 "%s\n",
809 path
810 );
811 blockdev_close(&dev);
812 return -1;
813 }
814 idx = opt_part_idx - 1;
815 if (idx >= 16) {
816 weprintf(
817 "BSD partition index must be "
818 "a-p\n"
819 );
820 blockdev_close(&dev);
821 return -1;
822 }
823 memset(&bsd_lp.d_partitions[idx], 0, sizeof(struct BsdPartition));
824 if (bsd_write(&dev, bsd_base, &bsd_lp) < 0) {
825 weprintf(
826 "cannot update BSD disklabel "
827 "on %s:",
828 path
829 );
830 blockdev_close(&dev);
831 return -1;
832 }
833 printf("Deleted BSD partition %c from %s\n", 'a' + idx, path);
834 } else {
835 if (blockdev_read(&dev, 0, &mbr, 1) < 0) {
836 weprintf("cannot read MBR from %s:", path);
837 blockdev_close(&dev);
838 return -1;
839 }
840 if (opt_part_idx > 4) {
841 weprintf(
842 "MBR partition index must be "
843 "1-4\n"
844 );
845 blockdev_close(&dev);
846 return -1;
847 }
848 mp = &mbr.parts[opt_part_idx - 1];
849 memset(mp, 0, sizeof(struct MbrPartition));
850 if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
851 weprintf("cannot update MBR on %s:", path);
852 blockdev_close(&dev);
853 return -1;
854 }
855 printf("Deleted MBR partition %d from %s\n", opt_part_idx, path);
856 }
857 }
858 }
859
860 blockdev_close(&dev);
861 return 0;
862}
863
864// ?man fdisk: partition table manipulator
865// ?man arguments: [-l] [-p] [-g] [-m] [-s] [-a] [-d] [-n index] [-b start] [-e
866// end] [-t type] [-c name] [device] ?man fdisk performs partition table
867// operations for MBR, GPT and BSD disklabels
868int
869main(int argc, char *argv[])
870{
871 int ret = 0;
872
873 ARGBEGIN
874 {
875 // ?man -l:list partitions of all devices
876 case 'l':
877 opt_list = 1;
878 break;
879 // ?man -p:print partition table of specified device
880 case 'p':
881 opt_print = 1;
882 break;
883 // ?man -g:initialize disk with GPT
884 case 'g':
885 opt_init_gpt = 1;
886 break;
887 // ?man -m:initialize disk with MBR
888 case 'm':
889 opt_init_mbr = 1;
890 break;
891 // ?man -s:initialize disk with BSD disklabel
892 case 's':
893 opt_init_bsd = 1;
894 break;
895 // ?man -a:add partition
896 case 'a':
897 opt_add = 1;
898 break;
899 // ?man -d:delete partition
900 case 'd':
901 opt_del = 1;
902 break;
903 // ?man -n:partition index or letter
904 case 'n': {
905 char *arg = EARGF(usage());
906 if (arg[0] >= 'a' && arg[0] <= 'p' && arg[1] == '\0') {
907 opt_part_idx = arg[0] - 'a' + 1;
908 opt_part_is_letter = 1;
909 } else {
910 opt_part_idx = (int)estrtol(arg, 10);
911 opt_part_is_letter = 0;
912 }
913 break;
914 }
915 // ?man -b:starting sector LBA, or 0 for right after the last
916 // ?man partition already on the disk
917 case 'b':
918 opt_start_str = EARGF(usage());
919 break;
920 // ?man -e:ending sector LBA, 0 for the disk's last usable
921 // ?man sector, or +SIZE (K/M/G/T, e.g. +512M) for a size
922 // ?man relative to the start
923 case 'e':
924 opt_end_str = EARGF(usage());
925 break;
926 // ?man -t:partition type: MBR/BSD hex, or for GPT a gdisk-style
927 // ?man code (ef00, ef02, 8200, 8300) or a full GUID string
928 case 't':
929 opt_type = EARGF(usage());
930 break;
931 // ?man -c:GPT partition name, ignored for MBR/BSD
932 case 'c':
933 opt_name = EARGF(usage());
934 break;
935 default:
936 usage();
937 }
938 ARGEND
939
940 if (opt_list) {
941 struct BlockDevInfo *list = blockdev_get_list();
942 struct BlockDevInfo *curr;
943 if (!list)
944 return 1;
945 for (curr = list; curr; curr = curr->next) {
946 do_print(curr->path);
947 }
948 blockdev_free_list(list);
949 if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
950 ret = 2;
951 return ret;
952 }
953
954 if (argc != 1)
955 usage();
956
957 if (opt_print) {
958 ret = do_print(argv[0]) < 0 ? 1 : 0;
959 } else {
960 ret = do_fdisk(argv[0]) < 0 ? 1 : 0;
961 }
962
963 if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
964 ret = 2;
965 return ret;
966}