master xplshn/aruu / cmd / extra / fdisk.c
  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}