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