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 <unistd.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
 52struct BsdPartition {
 53  uint32_t size;
 54  uint32_t offset;
 55  uint16_t offset_h;
 56  uint16_t size_h;
 57  uint8_t  fstype;
 58  uint8_t  fragblock;
 59  uint16_t cpg;
 60} __attribute__((packed));
 61
 62struct BsdDisklabel {
 63  uint32_t            d_magic;
 64  uint16_t            d_type;
 65  uint16_t            d_subtype;
 66  char                d_typename[16];
 67  char                d_packname[16];
 68  uint32_t            d_secsize;
 69  uint32_t            d_nsectors;
 70  uint32_t            d_ntracks;
 71  uint32_t            d_ncylinders;
 72  uint32_t            d_secpercyl;
 73  uint32_t            d_secperunit;
 74  uint8_t             d_uid[8];
 75  uint32_t            d_acylinders;
 76  uint16_t            d_bstarth;
 77  uint16_t            d_bendh;
 78  uint32_t            d_bstart;
 79  uint32_t            d_bend;
 80  uint32_t            d_flags;
 81  uint32_t            d_spare4[5];
 82  uint16_t            d_secperunith;
 83  uint16_t            d_version;
 84  uint32_t            d_spare[4];
 85  uint32_t            d_magic2;
 86  uint16_t            d_checksum;
 87  uint16_t            d_npartitions;
 88  uint32_t            d_spare2;
 89  uint32_t            d_spare3;
 90  struct BsdPartition d_partitions[16];
 91} __attribute__((packed));
 92
 93static uint32_t crc32_table[256];
 94static int      crc32_table_initialized = 0;
 95
 96static void
 97init_crc32_table(void)
 98{
 99  uint32_t i, j, c;
100  for (i = 0; i < 256; i++) {
101    c = i;
102    for (j = 0; j < 8; j++) {
103      if (c & 1)
104        c = 0xedb88320U ^ (c >> 1);
105      else
106        c = c >> 1;
107    }
108    crc32_table[i] = c;
109  }
110  crc32_table_initialized = 1;
111}
112
113static uint32_t
114crc32(uint32_t crc, const void *buf, size_t len)
115{
116  const uint8_t *p = buf;
117  if (!crc32_table_initialized)
118    init_crc32_table();
119  while (len--)
120    crc = (crc >> 8) ^ crc32_table[(crc ^ *p++) & 0xFF];
121  return crc;
122}
123
124static int         opt_list           = 0;
125static int         opt_print          = 0;
126static int         opt_init_gpt       = 0;
127static int         opt_init_mbr       = 0;
128static int         opt_init_bsd       = 0;
129static int         opt_add            = 0;
130static int         opt_del            = 0;
131static int         opt_part_idx       = -1;
132static int         opt_part_is_letter = 0;
133static uint64_t    opt_start          = 0;
134static uint64_t    opt_end            = 0;
135static const char *opt_type           = NULL;
136
137static void
138usage(void)
139{
140  eprintf(
141      "usage: %s [-l] [-p] [-g] [-m] [-s] [-a] [-d] [-n index] [-b "
142      "start] [-e end] [-t type] [device]\n",
143      argv0
144  );
145}
146
147static void
148mbr_print(struct MbrHeader *mbr, const char *path)
149{
150  int i;
151  printf("Disk: %s\n", path);
152  printf("Partition table (MBR/dos):\n");
153  printf("%-5s %-4s %-12s %-12s %-4s\n", "Index", "Boot", "Start", "Size", "Type");
154  for (i = 0; i < 4; i++) {
155    struct MbrPartition *p = &mbr->parts[i];
156    if (p->size == 0)
157      continue;
158    printf(
159        "%-5d %-4s %-12u %-12u 0x%02x\n",
160        i + 1,
161        p->boot == 0x80 ? "Yes" : "No",
162        p->start_lba,
163        p->size,
164        p->type
165    );
166  }
167}
168
169static void
170gpt_print(struct GptHeader *hdr, struct GptPartition *parts, const char *path)
171{
172  uint32_t i;
173  char     guid[37];
174  printf("Disk: %s\n", path);
175  printf("Partition table (GPT):\n");
176  printf("%-5s %-12s %-12s %-36s\n", "Index", "Start", "End", "Type GUID");
177  for (i = 0; i < hdr->num_parts; i++) {
178    struct GptPartition *p = &parts[i];
179    if (p->start_lba == 0 && p->end_lba == 0)
180      continue;
181
182    snprintf(
183        guid,
184        sizeof(guid),
185        "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%"
186        "02x%02x%02x%02x",
187        p->type_guid[3],
188        p->type_guid[2],
189        p->type_guid[1],
190        p->type_guid[0],
191        p->type_guid[5],
192        p->type_guid[4],
193        p->type_guid[7],
194        p->type_guid[6],
195        p->type_guid[8],
196        p->type_guid[9],
197        p->type_guid[10],
198        p->type_guid[11],
199        p->type_guid[12],
200        p->type_guid[13],
201        p->type_guid[14],
202        p->type_guid[15]
203    );
204
205    printf(
206        "%-5d %-12llu %-12llu %-36s\n",
207        i + 1,
208        (unsigned long long)p->start_lba,
209        (unsigned long long)p->end_lba,
210        guid
211    );
212  }
213}
214
215static int
216gpt_read(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts)
217{
218  unsigned char sector[512];
219  size_t        part_sectors;
220
221  if (blockdev_read(dev, 1, sector, 1) < 0)
222    return -1;
223
224  memcpy(hdr, sector, sizeof(*hdr));
225
226  if (memcmp(hdr->sig, "EFI PART", 8) != 0)
227    return -1;
228
229  part_sectors = (hdr->num_parts * hdr->part_size + dev->sec_size - 1) / dev->sec_size;
230  if (blockdev_read(dev, hdr->partition_lba, parts, part_sectors) < 0)
231    return -1;
232
233  return 0;
234}
235
236static int
237gpt_write(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts)
238{
239  unsigned char    sector[512];
240  struct MbrHeader pmbr;
241  struct GptHeader backup_hdr;
242  uint64_t         limit;
243  size_t           part_sectors;
244
245  part_sectors = (hdr->num_parts * hdr->part_size + dev->sec_size - 1) / dev->sec_size;
246
247  hdr->parts_crc = crc32(~0U, parts, hdr->num_parts * hdr->part_size) ^ ~0U;
248
249  hdr->crc = 0;
250  hdr->crc = crc32(~0U, hdr, hdr->size) ^ ~0U;
251
252  memset(&pmbr, 0, sizeof(pmbr));
253  pmbr.parts[0].type      = 0xEE;
254  pmbr.parts[0].start_lba = 1;
255  limit                   = dev->size / dev->sec_size - 1;
256  pmbr.parts[0].size      = limit > 0xFFFFFFFFU ? 0xFFFFFFFFU : (uint32_t)limit;
257  pmbr.sig[0]             = 0x55;
258  pmbr.sig[1]             = 0xAA;
259  if (blockdev_write(dev, 0, &pmbr, 1) < 0)
260    return -1;
261
262  memset(sector, 0, sizeof(sector));
263  memcpy(sector, hdr, sizeof(*hdr));
264  if (blockdev_write(dev, 1, sector, 1) < 0)
265    return -1;
266  if (blockdev_write(dev, hdr->partition_lba, parts, part_sectors) < 0)
267    return -1;
268
269  backup_hdr               = *hdr;
270  backup_hdr.current_lba   = hdr->backup_lba;
271  backup_hdr.backup_lba    = hdr->current_lba;
272  backup_hdr.partition_lba = backup_hdr.current_lba - part_sectors;
273
274  backup_hdr.crc = 0;
275  backup_hdr.crc = crc32(~0U, &backup_hdr, backup_hdr.size) ^ ~0U;
276
277  if (blockdev_write(dev, backup_hdr.partition_lba, parts, part_sectors) < 0)
278    return -1;
279
280  memset(sector, 0, sizeof(sector));
281  memcpy(sector, &backup_hdr, sizeof(backup_hdr));
282  if (blockdev_write(dev, backup_hdr.current_lba, sector, 1) < 0)
283    return -1;
284
285  return 0;
286}
287
288static uint16_t
289bsd_dkcksum(void *lp, size_t nparts)
290{
291  uint16_t *start;
292  uint16_t *end;
293  uint16_t  sum = 0;
294
295  start = (uint16_t *)lp;
296  end   = (uint16_t *)((char *)lp + 148 + nparts * 16);
297  while (start < end)
298    sum ^= *start++;
299  return sum;
300}
301
302static int
303bsd_is_partition_type(uint8_t type)
304{
305  return type == 0xa5 || type == 0xa6 || type == 0xa9;
306}
307
308static int
309bsd_read(struct BlockDev *dev, uint64_t base_sec, struct BsdDisklabel *lp)
310{
311  unsigned char sector[512];
312  if (blockdev_read(dev, base_sec + 1, sector, 1) < 0)
313    return -1;
314  memcpy(lp, sector, sizeof(*lp));
315  if (lp->d_magic != 0x82564557U || lp->d_magic2 != 0x82564557U)
316    return -1;
317  return 0;
318}
319
320static int
321bsd_write(struct BlockDev *dev, uint64_t base_sec, struct BsdDisklabel *lp)
322{
323  unsigned char sector[512];
324  memset(sector, 0, sizeof(sector));
325  lp->d_checksum = 0;
326  lp->d_checksum = bsd_dkcksum(lp, lp->d_npartitions);
327  memcpy(sector, lp, sizeof(*lp));
328  if (blockdev_write(dev, base_sec + 1, sector, 1) < 0)
329    return -1;
330  return 0;
331}
332
333static void
334bsd_print(struct BsdDisklabel *lp, const char *path, uint64_t base_sec)
335{
336  uint64_t start, size;
337  int      i;
338
339  printf("Disk: %s (at sector %llu)\n", path, (unsigned long long)base_sec);
340  printf("Partition table (BSD disklabel):\n");
341  printf("%-5s %-12s %-12s %-10s\n", "Index", "Start", "Size", "Type");
342  for (i = 0; i < lp->d_npartitions; i++) {
343    struct BsdPartition *p = &lp->d_partitions[i];
344    start                  = ((uint64_t)p->offset_h << 32) | p->offset;
345    size                   = ((uint64_t)p->size_h << 32) | p->size;
346    if (size == 0)
347      continue;
348    printf(
349        "%-5c %-12llu %-12llu 0x%02x\n",
350        'a' + i,
351        (unsigned long long)start,
352        (unsigned long long)size,
353        p->fstype
354    );
355  }
356}
357
358static int
359bsd_find_label(struct BlockDev *dev, uint64_t *base_sec, struct BsdDisklabel *lp)
360{
361  struct MbrHeader mbr;
362  int              i;
363
364  if (bsd_read(dev, 0, lp) == 0) {
365    *base_sec = 0;
366    return 0;
367  }
368
369  if (blockdev_read(dev, 0, &mbr, 1) == 0 && mbr.sig[0] == 0x55 && mbr.sig[1] == 0xAA) {
370    for (i = 0; i < 4; i++) {
371      if (mbr.parts[i].size > 0 && bsd_is_partition_type(mbr.parts[i].type)) {
372        if (bsd_read(dev, mbr.parts[i].start_lba, lp) == 0) {
373          *base_sec = mbr.parts[i].start_lba;
374          return 0;
375        }
376      }
377    }
378  }
379
380  return -1;
381}
382
383static int
384do_print(const char *path)
385{
386  struct BlockDev     dev;
387  struct MbrHeader    mbr;
388  struct GptHeader    gpt_hdr;
389  struct GptPartition gpt_parts[128];
390  struct BsdDisklabel bsd_lp;
391  int                 i;
392  int                 found_bsd = 0;
393
394  if (blockdev_open(&dev, path, 0) < 0) {
395    weprintf("cannot open %s:", path);
396    return -1;
397  }
398
399  if (gpt_read(&dev, &gpt_hdr, gpt_parts) == 0) {
400    gpt_print(&gpt_hdr, gpt_parts, path);
401  } else if (blockdev_read(&dev, 0, &mbr, 1) == 0 && mbr.sig[0] == 0x55 && mbr.sig[1] == 0xAA) {
402    for (i = 0; i < 4; i++) {
403      if (mbr.parts[i].size > 0 && bsd_is_partition_type(mbr.parts[i].type)) {
404        if (bsd_read(&dev, mbr.parts[i].start_lba, &bsd_lp) == 0) {
405          bsd_print(&bsd_lp, path, mbr.parts[i].start_lba);
406          found_bsd = 1;
407        }
408      }
409    }
410    if (!found_bsd) {
411      if (bsd_read(&dev, 0, &bsd_lp) == 0) {
412        bsd_print(&bsd_lp, path, 0);
413      } else {
414        mbr_print(&mbr, path);
415      }
416    }
417  } else {
418    if (bsd_read(&dev, 0, &bsd_lp) == 0) {
419      bsd_print(&bsd_lp, path, 0);
420    } else {
421      printf("Disk %s: unpartitioned or unknown format\n", path);
422    }
423  }
424
425  blockdev_close(&dev);
426  return 0;
427}
428
429static int
430do_fdisk(const char *path)
431{
432  struct BlockDev      dev;
433  struct MbrHeader     mbr;
434  struct GptHeader     gpt_hdr;
435  struct GptPartition  gpt_parts[128];
436  struct BsdDisklabel  lp;
437  struct BsdDisklabel  bsd_lp;
438  struct GptPartition *gp;
439  struct BsdPartition *bp;
440  struct MbrPartition *mp;
441  uint64_t             base_sec;
442  uint64_t             limit;
443  uint64_t             bsd_base;
444  uint64_t             size;
445  int                  idx;
446  int                  has_gpt = 0;
447  int                  has_bsd = 0;
448
449  if (blockdev_open(&dev, path, 1) < 0) {
450    weprintf("cannot open %s:", path);
451    return -1;
452  }
453
454  has_gpt = (gpt_read(&dev, &gpt_hdr, gpt_parts) == 0);
455
456  if (opt_init_mbr) {
457    memset(&mbr, 0, sizeof(mbr));
458    mbr.sig[0] = 0x55;
459    mbr.sig[1] = 0xAA;
460    if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
461      weprintf("cannot write MBR to %s:", path);
462      blockdev_close(&dev);
463      return -1;
464    }
465    printf("Initialized MBR partition table on %s\n", path);
466    blockdev_close(&dev);
467    return 0;
468  }
469
470  if (opt_init_gpt) {
471    memset(&gpt_hdr, 0, sizeof(gpt_hdr));
472    memcpy(gpt_hdr.sig, "EFI PART", 8);
473    gpt_hdr.rev           = 0x00010000;
474    gpt_hdr.size          = 92;
475    gpt_hdr.current_lba   = 1;
476    gpt_hdr.backup_lba    = dev.size / dev.sec_size - 1;
477    gpt_hdr.first_usable  = 34;
478    gpt_hdr.last_usable   = gpt_hdr.backup_lba - 34;
479    gpt_hdr.partition_lba = 2;
480    gpt_hdr.num_parts     = 128;
481    gpt_hdr.part_size     = 128;
482    memset(gpt_parts, 0, sizeof(gpt_parts));
483
484    if (gpt_write(&dev, &gpt_hdr, gpt_parts) < 0) {
485      weprintf("cannot write GPT to %s:", path);
486      blockdev_close(&dev);
487      return -1;
488    }
489    printf("Initialized GPT partition table on %s\n", path);
490    blockdev_close(&dev);
491    return 0;
492  }
493
494  if (opt_init_bsd) {
495    base_sec = 0;
496    limit    = dev.size / dev.sec_size;
497
498    if (blockdev_read(&dev, 0, &mbr, 1) == 0 && mbr.sig[0] == 0x55 && mbr.sig[1] == 0xAA) {
499      if (!opt_part_is_letter && opt_part_idx >= 1 && opt_part_idx <= 4) {
500        mp = &mbr.parts[opt_part_idx - 1];
501        if (mp->size > 0) {
502          base_sec = mp->start_lba;
503          limit    = base_sec + mp->size;
504        }
505      }
506    }
507
508    memset(&lp, 0, sizeof(lp));
509    lp.d_magic                  = 0x82564557U;
510    lp.d_magic2                 = 0x82564557U;
511    lp.d_secsize                = dev.sec_size;
512    lp.d_npartitions            = 8;
513    lp.d_partitions[2].offset   = (uint32_t)base_sec;
514    lp.d_partitions[2].offset_h = (uint16_t)(base_sec >> 32);
515    size                        = limit - base_sec;
516    lp.d_partitions[2].size     = (uint32_t)size;
517    lp.d_partitions[2].size_h   = (uint16_t)(size >> 32);
518    lp.d_partitions[2].fstype   = 0;
519
520    if (bsd_write(&dev, base_sec, &lp) < 0) {
521      weprintf("cannot write BSD disklabel to %s:", path);
522      blockdev_close(&dev);
523      return -1;
524    }
525    printf(
526        "Initialized BSD disklabel on %s (at sector %llu)\n", path, (unsigned long long)base_sec
527    );
528    blockdev_close(&dev);
529    return 0;
530  }
531
532  if (opt_add) {
533    if (opt_part_idx < 1) {
534      weprintf("add requires partition index (-n)\n");
535      blockdev_close(&dev);
536      return -1;
537    }
538
539    if (has_gpt) {
540      if (opt_part_idx > 128) {
541        weprintf("GPT partition index must be 1-128\n");
542        blockdev_close(&dev);
543        return -1;
544      }
545      gp                = &gpt_parts[opt_part_idx - 1];
546      gp->start_lba     = opt_start;
547      gp->end_lba       = opt_end;
548      gp->type_guid[0]  = 0xAF;
549      gp->type_guid[1]  = 0x3D;
550      gp->type_guid[2]  = 0xC6;
551      gp->type_guid[3]  = 0x0F;
552      gp->type_guid[4]  = 0x83;
553      gp->type_guid[5]  = 0x84;
554      gp->type_guid[6]  = 0x72;
555      gp->type_guid[7]  = 0x47;
556      gp->type_guid[8]  = 0x8E;
557      gp->type_guid[9]  = 0x79;
558      gp->type_guid[10] = 0x3D;
559      gp->type_guid[11] = 0x69;
560      gp->type_guid[12] = 0xD8;
561      gp->type_guid[13] = 0x47;
562      gp->type_guid[14] = 0x7D;
563      gp->type_guid[15] = 0xE4;
564
565      if (gpt_write(&dev, &gpt_hdr, gpt_parts) < 0) {
566        weprintf("cannot update GPT on %s:", path);
567        blockdev_close(&dev);
568        return -1;
569      }
570      printf("Added GPT partition %d to %s\n", opt_part_idx, path);
571    } else {
572      has_bsd = (bsd_find_label(&dev, &bsd_base, &bsd_lp) == 0);
573      if (opt_part_is_letter || has_bsd) {
574        if (!has_bsd) {
575          weprintf(
576              "BSD disklabel not found on "
577              "%s\n",
578              path
579          );
580          blockdev_close(&dev);
581          return -1;
582        }
583        idx = opt_part_idx - 1;
584        if (idx >= 16) {
585          weprintf(
586              "BSD partition index must be "
587              "a-p\n"
588          );
589          blockdev_close(&dev);
590          return -1;
591        }
592        bp           = &bsd_lp.d_partitions[idx];
593        bp->offset   = (uint32_t)opt_start;
594        bp->offset_h = (uint16_t)(opt_start >> 32);
595        size         = opt_end - opt_start + 1;
596        bp->size     = (uint32_t)size;
597        bp->size_h   = (uint16_t)(size >> 32);
598        bp->fstype   = opt_type ? (uint8_t)strtoul(opt_type, NULL, 0) : 7;
599        if (idx >= bsd_lp.d_npartitions)
600          bsd_lp.d_npartitions = idx + 1;
601
602        if (bsd_write(&dev, bsd_base, &bsd_lp) < 0) {
603          weprintf(
604              "cannot update BSD disklabel "
605              "on %s:",
606              path
607          );
608          blockdev_close(&dev);
609          return -1;
610        }
611        printf("Added BSD partition %c to %s\n", 'a' + idx, path);
612      } else {
613        if (blockdev_read(&dev, 0, &mbr, 1) < 0) {
614          weprintf("cannot read MBR from %s:", path);
615          blockdev_close(&dev);
616          return -1;
617        }
618        if (opt_part_idx > 4) {
619          weprintf(
620              "MBR partition index must be "
621              "1-4\n"
622          );
623          blockdev_close(&dev);
624          return -1;
625        }
626        mp            = &mbr.parts[opt_part_idx - 1];
627        mp->start_lba = (uint32_t)opt_start;
628        mp->size      = (uint32_t)(opt_end - opt_start + 1);
629        mp->type      = opt_type ? (uint8_t)strtoul(opt_type, NULL, 0) : 0x83;
630        mbr.sig[0]    = 0x55;
631        mbr.sig[1]    = 0xAA;
632
633        if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
634          weprintf("cannot update MBR on %s:", path);
635          blockdev_close(&dev);
636          return -1;
637        }
638        printf("Added MBR partition %d to %s\n", opt_part_idx, path);
639      }
640    }
641  }
642
643  if (opt_del) {
644    if (opt_part_idx < 1) {
645      weprintf("delete requires partition index (-n)\n");
646      blockdev_close(&dev);
647      return -1;
648    }
649
650    if (has_gpt) {
651      if (opt_part_idx > 128) {
652        weprintf("GPT partition index must be 1-128\n");
653        blockdev_close(&dev);
654        return -1;
655      }
656      memset(&gpt_parts[opt_part_idx - 1], 0, sizeof(struct GptPartition));
657      if (gpt_write(&dev, &gpt_hdr, gpt_parts) < 0) {
658        weprintf("cannot update GPT on %s:", path);
659        blockdev_close(&dev);
660        return -1;
661      }
662      printf("Deleted GPT partition %d from %s\n", opt_part_idx, path);
663    } else {
664      has_bsd = (bsd_find_label(&dev, &bsd_base, &bsd_lp) == 0);
665      if (opt_part_is_letter || has_bsd) {
666        if (!has_bsd) {
667          weprintf(
668              "BSD disklabel not found on "
669              "%s\n",
670              path
671          );
672          blockdev_close(&dev);
673          return -1;
674        }
675        idx = opt_part_idx - 1;
676        if (idx >= 16) {
677          weprintf(
678              "BSD partition index must be "
679              "a-p\n"
680          );
681          blockdev_close(&dev);
682          return -1;
683        }
684        memset(&bsd_lp.d_partitions[idx], 0, sizeof(struct BsdPartition));
685        if (bsd_write(&dev, bsd_base, &bsd_lp) < 0) {
686          weprintf(
687              "cannot update BSD disklabel "
688              "on %s:",
689              path
690          );
691          blockdev_close(&dev);
692          return -1;
693        }
694        printf("Deleted BSD partition %c from %s\n", 'a' + idx, path);
695      } else {
696        if (blockdev_read(&dev, 0, &mbr, 1) < 0) {
697          weprintf("cannot read MBR from %s:", path);
698          blockdev_close(&dev);
699          return -1;
700        }
701        if (opt_part_idx > 4) {
702          weprintf(
703              "MBR partition index must be "
704              "1-4\n"
705          );
706          blockdev_close(&dev);
707          return -1;
708        }
709        mp = &mbr.parts[opt_part_idx - 1];
710        memset(mp, 0, sizeof(struct MbrPartition));
711        if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
712          weprintf("cannot update MBR on %s:", path);
713          blockdev_close(&dev);
714          return -1;
715        }
716        printf("Deleted MBR partition %d from %s\n", opt_part_idx, path);
717      }
718    }
719  }
720
721  blockdev_close(&dev);
722  return 0;
723}
724
725// ?man fdisk: partition table manipulator
726// ?man arguments: [-l] [-p] [-g] [-m] [-s] [-a] [-d] [-n index] [-b start] [-e
727// end] [-t type] [device] ?man fdisk performs partition table operations for
728// MBR, GPT and BSD disklabels
729int
730main(int argc, char *argv[])
731{
732  int ret = 0;
733
734  ARGBEGIN
735  {
736    // ?man -l:list partitions of all devices
737    case 'l':
738      opt_list = 1;
739      break;
740    // ?man -p:print partition table of specified device
741    case 'p':
742      opt_print = 1;
743      break;
744    // ?man -g:initialize disk with GPT
745    case 'g':
746      opt_init_gpt = 1;
747      break;
748    // ?man -m:initialize disk with MBR
749    case 'm':
750      opt_init_mbr = 1;
751      break;
752    // ?man -s:initialize disk with BSD disklabel
753    case 's':
754      opt_init_bsd = 1;
755      break;
756    // ?man -a:add partition
757    case 'a':
758      opt_add = 1;
759      break;
760    // ?man -d:delete partition
761    case 'd':
762      opt_del = 1;
763      break;
764    // ?man -n:partition index or letter
765    case 'n': {
766      char *arg = EARGF(usage());
767      if (arg[0] >= 'a' && arg[0] <= 'p' && arg[1] == '\0') {
768        opt_part_idx       = arg[0] - 'a' + 1;
769        opt_part_is_letter = 1;
770      } else {
771        opt_part_idx       = (int)estrtol(arg, 10);
772        opt_part_is_letter = 0;
773      }
774      break;
775    }
776    // ?man -b:starting sector LBA
777    case 'b':
778      opt_start = (uint64_t)estrtoul(EARGF(usage()), 10);
779      break;
780    // ?man -e:ending sector LBA
781    case 'e':
782      opt_end = (uint64_t)estrtoul(EARGF(usage()), 10);
783      break;
784    // ?man -t:partition type (MBR/BSD hex or GPT string)
785    case 't':
786      opt_type = EARGF(usage());
787      break;
788    default:
789      usage();
790  }
791  ARGEND
792
793  if (opt_list) {
794    struct BlockDevInfo *list = blockdev_get_list();
795    struct BlockDevInfo *curr;
796    if (!list)
797      return 1;
798    for (curr = list; curr; curr = curr->next) {
799      do_print(curr->path);
800    }
801    blockdev_free_list(list);
802    if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
803      ret = 2;
804    return ret;
805  }
806
807  if (argc != 1)
808    usage();
809
810  if (opt_print) {
811    ret = do_print(argv[0]) < 0 ? 1 : 0;
812  } else {
813    ret = do_fdisk(argv[0]) < 0 ? 1 : 0;
814  }
815
816  if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
817    ret = 2;
818  return ret;
819}