fdisk.c revision 226908
1/*
2 * Mach Operating System
3 * Copyright (c) 1992 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
13 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19 *  School of Computer Science
20 *  Carnegie Mellon University
21 *  Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie Mellon
24 * the rights to redistribute these changes.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sbin/fdisk/fdisk.c 226908 2011-10-29 17:30:57Z jmg $");
29
30#include <sys/disk.h>
31#include <sys/disklabel.h>
32#include <sys/diskmbr.h>
33#include <sys/endian.h>
34#include <sys/param.h>
35#include <sys/stat.h>
36#include <sys/mount.h>
37#include <ctype.h>
38#include <fcntl.h>
39#include <err.h>
40#include <errno.h>
41#include <libgeom.h>
42#include <paths.h>
43#include <regex.h>
44#include <stdint.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <unistd.h>
49
50int iotest;
51
52#define NO_DISK_SECTORS ((u_int32_t)-1)
53#define NO_TRACK_CYLINDERS 1023
54#define NO_TRACK_HEADS 255
55#define NO_TRACK_SECTORS 63
56#define LBUF 100
57static char lbuf[LBUF];
58
59/*
60 *
61 * Ported to 386bsd by Julian Elischer  Thu Oct 15 20:26:46 PDT 1992
62 *
63 * 14-Dec-89  Robert Baron (rvb) at Carnegie-Mellon University
64 *	Copyright (c) 1989	Robert. V. Baron
65 *	Created.
66 */
67
68#define Decimal(str, ans, tmp, maxval) if (decimal(str, &tmp, ans, maxval)) ans = tmp
69
70#define RoundCyl(x) ((((x) + cylsecs - 1) / cylsecs) * cylsecs)
71
72#define MAX_SEC_SIZE 2048	/* maximum section size that is supported */
73#define MIN_SEC_SIZE 512	/* the sector size to start sensing at */
74static int secsize = 0;		/* the sensed sector size */
75
76static char *disk;
77
78static int cyls, sectors, heads, cylsecs, disksecs;
79
80struct mboot {
81	unsigned char *bootinst;  /* boot code */
82	off_t bootinst_size;
83	struct	dos_partition parts[NDOSPART];
84};
85
86static struct mboot mboot;
87static int fd;
88
89#define ACTIVE 0x80
90
91static uint dos_cyls;
92static uint dos_heads;
93static uint dos_sectors;
94static uint dos_cylsecs;
95
96#define DOSSECT(s,c) ((s & 0x3f) | ((c >> 2) & 0xc0))
97#define DOSCYL(c)	(c & 0xff)
98
99#define MAX_ARGS	10
100
101static int	current_line_number;
102
103static int	geom_processed = 0;
104static int	part_processed = 0;
105static int	active_processed = 0;
106
107typedef struct cmd {
108    char		cmd;
109    int			n_args;
110    struct arg {
111	char		argtype;
112	unsigned long	arg_val;
113	char *		arg_str;
114    }			args[MAX_ARGS];
115} CMD;
116
117static int B_flag  = 0;		/* replace boot code */
118static int I_flag  = 0;		/* use entire disk for FreeBSD */
119static int a_flag  = 0;		/* set active partition */
120static char *b_flag = NULL;	/* path to boot code */
121static int i_flag  = 0;		/* replace partition data */
122static int q_flag  = 0;		/* Be quiet */
123static int u_flag  = 0;		/* update partition data */
124static int s_flag  = 0;		/* Print a summary and exit */
125static int t_flag  = 0;		/* test only */
126static char *f_flag = NULL;	/* Read config info from file */
127static int v_flag  = 0;		/* Be verbose */
128static int print_config_flag = 0;
129
130/*
131 * A list of partition types, probably outdated.
132 */
133static const char *const part_types[256] = {
134	[0x00] = "unused",
135	[0x01] = "Primary DOS with 12 bit FAT",
136	[0x02] = "XENIX / file system",
137	[0x03] = "XENIX /usr file system",
138	[0x04] = "Primary DOS with 16 bit FAT (< 32MB)",
139	[0x05] = "Extended DOS",
140	[0x06] = "Primary DOS, 16 bit FAT (>= 32MB)",
141	[0x07] = "NTFS, OS/2 HPFS, QNX-2 (16 bit) or Advanced UNIX",
142	[0x08] = "AIX file system or SplitDrive",
143	[0x09] = "AIX boot partition or Coherent",
144	[0x0A] = "OS/2 Boot Manager, OPUS or Coherent swap",
145	[0x0B] = "DOS or Windows 95 with 32 bit FAT",
146	[0x0C] = "DOS or Windows 95 with 32 bit FAT (LBA)",
147	[0x0E] = "Primary 'big' DOS (>= 32MB, LBA)",
148	[0x0F] = "Extended DOS (LBA)",
149	[0x10] = "OPUS",
150	[0x11] = "OS/2 BM: hidden DOS with 12-bit FAT",
151	[0x12] = "Compaq diagnostics",
152	[0x14] = "OS/2 BM: hidden DOS with 16-bit FAT (< 32MB)",
153	[0x16] = "OS/2 BM: hidden DOS with 16-bit FAT (>= 32MB)",
154	[0x17] = "OS/2 BM: hidden IFS (e.g. HPFS)",
155	[0x18] = "AST Windows swapfile",
156	[0x1b] = "ASUS Recovery partition (NTFS)",
157	[0x24] = "NEC DOS",
158	[0x3C] = "PartitionMagic recovery",
159	[0x39] = "plan9",
160	[0x40] = "VENIX 286",
161	[0x41] = "Linux/MINIX (sharing disk with DRDOS)",
162	[0x42] = "SFS or Linux swap (sharing disk with DRDOS)",
163	[0x43] = "Linux native (sharing disk with DRDOS)",
164	[0x4D] = "QNX 4.2 Primary",
165	[0x4E] = "QNX 4.2 Secondary",
166	[0x4F] = "QNX 4.2 Tertiary",
167	[0x50] = "DM (disk manager)",
168	[0x51] = "DM6 Aux1 (or Novell)",
169	[0x52] = "CP/M or Microport SysV/AT",
170	[0x53] = "DM6 Aux3",
171	[0x54] = "DM6",
172	[0x55] = "EZ-Drive (disk manager)",
173	[0x56] = "Golden Bow (disk manager)",
174	[0x5c] = "Priam Edisk (disk manager)", /* according to S. Widlake */
175	[0x61] = "SpeedStor",
176	[0x63] = "System V/386 (such as ISC UNIX), GNU HURD or Mach",
177	[0x64] = "Novell Netware/286 2.xx",
178	[0x65] = "Novell Netware/386 3.xx",
179	[0x70] = "DiskSecure Multi-Boot",
180	[0x75] = "PCIX",
181	[0x77] = "QNX4.x",
182	[0x78] = "QNX4.x 2nd part",
183	[0x79] = "QNX4.x 3rd part",
184	[0x80] = "Minix until 1.4a",
185	[0x81] = "Minix since 1.4b, early Linux partition or Mitac disk manager",
186	[0x82] = "Linux swap or Solaris x86",
187	[0x83] = "Linux native",
188	[0x84] = "OS/2 hidden C: drive",
189	[0x85] = "Linux extended",
190	[0x86] = "NTFS volume set??",
191	[0x87] = "NTFS volume set??",
192	[0x93] = "Amoeba file system",
193	[0x94] = "Amoeba bad block table",
194	[0x9F] = "BSD/OS",
195	[0xA0] = "Suspend to Disk",
196	[0xA5] = "FreeBSD/NetBSD/386BSD",
197	[0xA6] = "OpenBSD",
198	[0xA7] = "NeXTSTEP",
199	[0xA9] = "NetBSD",
200	[0xAC] = "IBM JFS",
201	[0xAF] = "HFS+",
202	[0xB7] = "BSDI BSD/386 file system",
203	[0xB8] = "BSDI BSD/386 swap",
204	[0xBE] = "Solaris x86 boot",
205	[0xBF] = "Solaris x86 (new)",
206	[0xC1] = "DRDOS/sec with 12-bit FAT",
207	[0xC4] = "DRDOS/sec with 16-bit FAT (< 32MB)",
208	[0xC6] = "DRDOS/sec with 16-bit FAT (>= 32MB)",
209	[0xC7] = "Syrinx",
210	[0xDB] = "CP/M, Concurrent CP/M, Concurrent DOS or CTOS",
211	[0xDE] = "DELL Utilities - FAT filesystem",
212	[0xE1] = "DOS access or SpeedStor with 12-bit FAT extended partition",
213	[0xE3] = "DOS R/O or SpeedStor",
214	[0xE4] = "SpeedStor with 16-bit FAT extended partition < 1024 cyl.",
215	[0xEB] = "BeOS file system",
216	[0xEE] = "EFI GPT",
217	[0xEF] = "EFI System Partition",
218	[0xF1] = "SpeedStor",
219	[0xF2] = "DOS 3.3+ Secondary",
220	[0xF4] = "SpeedStor large partition",
221	[0xFE] = "SpeedStor >1024 cyl. or LANstep",
222	[0xFF] = "Xenix bad blocks table",
223};
224
225static const char *
226get_type(int t)
227{
228	const char *ret;
229
230	ret = (t >= 0 && t <= 255) ? part_types[t] : NULL;
231	return ret ? ret : "unknown";
232}
233
234
235static void print_s0(void);
236static void print_part(const struct dos_partition *);
237static void init_sector0(unsigned long start);
238static void init_boot(void);
239static void change_part(int i);
240static void print_params(void);
241static void change_active(int which);
242static void change_code(void);
243static void get_params_to_use(void);
244static char *get_rootdisk(void);
245static void dos(struct dos_partition *partp);
246static int open_disk(int flag);
247static ssize_t read_disk(off_t sector, void *buf);
248static int write_disk(off_t sector, void *buf);
249static int get_params(void);
250static int read_s0(void);
251static int write_s0(void);
252static int ok(const char *str);
253static int decimal(const char *str, int *num, int deflt, uint32_t maxval);
254static int read_config(char *config_file);
255static void reset_boot(void);
256static int sanitize_partition(struct dos_partition *);
257static void usage(void);
258
259int
260main(int argc, char *argv[])
261{
262	int	c, i;
263	int	partition = -1;
264	struct	dos_partition *partp;
265
266	while ((c = getopt(argc, argv, "BIab:f:ipqstuv1234")) != -1)
267		switch (c) {
268		case 'B':
269			B_flag = 1;
270			break;
271		case 'I':
272			I_flag = 1;
273			break;
274		case 'a':
275			a_flag = 1;
276			break;
277		case 'b':
278			b_flag = optarg;
279			break;
280		case 'f':
281			f_flag = optarg;
282			break;
283		case 'i':
284			i_flag = 1;
285			break;
286		case 'p':
287			print_config_flag = 1;
288			break;
289		case 'q':
290			q_flag = 1;
291			break;
292		case 's':
293			s_flag = 1;
294			break;
295		case 't':
296			t_flag = 1;
297			break;
298		case 'u':
299			u_flag = 1;
300			break;
301		case 'v':
302			v_flag = 1;
303			break;
304		case '1':
305		case '2':
306		case '3':
307		case '4':
308			partition = c - '0';
309			break;
310		default:
311			usage();
312		}
313	if (f_flag || i_flag)
314		u_flag = 1;
315	if (t_flag)
316		v_flag = 1;
317	argc -= optind;
318	argv += optind;
319
320	if (argc == 0) {
321		disk = get_rootdisk();
322	} else {
323		disk = g_device_path(argv[0]);
324		if (disk == NULL)
325			err(1, "unable to get correct path for %s", argv[0]);
326	}
327	if (open_disk(u_flag) < 0)
328		err(1, "cannot open disk %s", disk);
329
330	/* (abu)use mboot.bootinst to probe for the sector size */
331	if ((mboot.bootinst = malloc(MAX_SEC_SIZE)) == NULL)
332		err(1, "cannot allocate buffer to determine disk sector size");
333	if (read_disk(0, mboot.bootinst) == -1)
334		errx(1, "could not detect sector size");
335	free(mboot.bootinst);
336	mboot.bootinst = NULL;
337
338	if (print_config_flag) {
339		if (read_s0())
340			err(1, "read_s0");
341
342		printf("# %s\n", disk);
343		printf("g c%d h%d s%d\n", dos_cyls, dos_heads, dos_sectors);
344
345		for (i = 0; i < NDOSPART; i++) {
346			partp = &mboot.parts[i];
347
348			if (partp->dp_start == 0 && partp->dp_size == 0)
349				continue;
350
351			printf("p %d 0x%02x %lu %lu\n", i + 1, partp->dp_typ,
352			    (u_long)partp->dp_start, (u_long)partp->dp_size);
353
354			/* Fill flags for the partition. */
355			if (partp->dp_flag & 0x80)
356				printf("a %d\n", i + 1);
357		}
358		exit(0);
359	}
360	if (s_flag) {
361		if (read_s0())
362			err(1, "read_s0");
363		printf("%s: %d cyl %d hd %d sec\n", disk, dos_cyls, dos_heads,
364		    dos_sectors);
365		printf("Part  %11s %11s Type Flags\n", "Start", "Size");
366		for (i = 0; i < NDOSPART; i++) {
367			partp = &mboot.parts[i];
368			if (partp->dp_start == 0 && partp->dp_size == 0)
369				continue;
370			printf("%4d: %11lu %11lu 0x%02x 0x%02x\n", i + 1,
371			    (u_long) partp->dp_start,
372			    (u_long) partp->dp_size, partp->dp_typ,
373			    partp->dp_flag);
374		}
375		exit(0);
376	}
377
378	printf("******* Working on device %s *******\n",disk);
379
380	if (I_flag) {
381		read_s0();
382		reset_boot();
383		partp = &mboot.parts[0];
384		partp->dp_typ = DOSPTYP_386BSD;
385		partp->dp_flag = ACTIVE;
386		partp->dp_start = dos_sectors;
387		partp->dp_size = (disksecs / dos_cylsecs) * dos_cylsecs -
388		    dos_sectors;
389		dos(partp);
390		if (v_flag)
391			print_s0();
392		if (!t_flag)
393			write_s0();
394		exit(0);
395	}
396	if (f_flag) {
397	    if (read_s0() || i_flag)
398		reset_boot();
399	    if (!read_config(f_flag))
400		exit(1);
401	    if (v_flag)
402		print_s0();
403	    if (!t_flag)
404		write_s0();
405	} else {
406	    if(u_flag)
407		get_params_to_use();
408	    else
409		print_params();
410
411	    if (read_s0())
412		init_sector0(dos_sectors);
413
414	    printf("Media sector size is %d\n", secsize);
415	    printf("Warning: BIOS sector numbering starts with sector 1\n");
416	    printf("Information from DOS bootblock is:\n");
417	    if (partition == -1)
418		for (i = 1; i <= NDOSPART; i++)
419		    change_part(i);
420	    else
421		change_part(partition);
422
423	    if (u_flag || a_flag)
424		change_active(partition);
425
426	    if (B_flag)
427		change_code();
428
429	    if (u_flag || a_flag || B_flag) {
430		if (!t_flag) {
431		    printf("\nWe haven't changed the partition table yet.  ");
432		    printf("This is your last chance.\n");
433		}
434		print_s0();
435		if (!t_flag) {
436		    if (ok("Should we write new partition table?"))
437			write_s0();
438		} else {
439		    printf("\n-t flag specified -- partition table not written.\n");
440		}
441	    }
442	}
443
444	exit(0);
445}
446
447static void
448usage()
449{
450	fprintf(stderr, "%s%s",
451		"usage: fdisk [-BIaipqstu] [-b bootcode] [-1234] [disk]\n",
452 		"       fdisk -f configfile [-itv] [disk]\n");
453        exit(1);
454}
455
456static void
457print_s0(void)
458{
459	int	i;
460
461	print_params();
462	printf("Information from DOS bootblock is:\n");
463	for (i = 1; i <= NDOSPART; i++) {
464		printf("%d: ", i);
465		print_part(&mboot.parts[i - 1]);
466	}
467}
468
469static struct dos_partition mtpart;
470
471static void
472print_part(const struct dos_partition *partp)
473{
474	u_int64_t part_mb;
475
476	if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) {
477		printf("<UNUSED>\n");
478		return;
479	}
480	/*
481	 * Be careful not to overflow.
482	 */
483	part_mb = partp->dp_size;
484	part_mb *= secsize;
485	part_mb /= (1024 * 1024);
486	printf("sysid %d (%#04x),(%s)\n", partp->dp_typ, partp->dp_typ,
487	    get_type(partp->dp_typ));
488	printf("    start %lu, size %lu (%ju Meg), flag %x%s\n",
489		(u_long)partp->dp_start,
490		(u_long)partp->dp_size,
491		(uintmax_t)part_mb,
492		partp->dp_flag,
493		partp->dp_flag == ACTIVE ? " (active)" : "");
494	printf("\tbeg: cyl %d/ head %d/ sector %d;\n\tend: cyl %d/ head %d/ sector %d\n"
495		,DPCYL(partp->dp_scyl, partp->dp_ssect)
496		,partp->dp_shd
497		,DPSECT(partp->dp_ssect)
498		,DPCYL(partp->dp_ecyl, partp->dp_esect)
499		,partp->dp_ehd
500		,DPSECT(partp->dp_esect));
501}
502
503
504static void
505init_boot(void)
506{
507#ifndef __ia64__
508	const char *fname;
509	int fdesc, n;
510	struct stat sb;
511
512	fname = b_flag ? b_flag : "/boot/mbr";
513	if ((fdesc = open(fname, O_RDONLY)) == -1 ||
514	    fstat(fdesc, &sb) == -1)
515		err(1, "%s", fname);
516	if (sb.st_size == 0)
517		errx(1, "%s is empty, must not be.", fname);
518	if ((mboot.bootinst_size = sb.st_size) % secsize != 0)
519		errx(1, "%s: length must be a multiple of sector size", fname);
520	if (mboot.bootinst != NULL)
521		free(mboot.bootinst);
522	if ((mboot.bootinst = malloc(mboot.bootinst_size = sb.st_size)) == NULL)
523		errx(1, "%s: unable to allocate read buffer", fname);
524	if ((n = read(fdesc, mboot.bootinst, mboot.bootinst_size)) == -1 ||
525	    close(fdesc))
526		err(1, "%s", fname);
527	if (n != mboot.bootinst_size)
528		errx(1, "%s: short read", fname);
529#else
530	if (mboot.bootinst != NULL)
531		free(mboot.bootinst);
532	mboot.bootinst_size = secsize;
533	if ((mboot.bootinst = malloc(mboot.bootinst_size)) == NULL)
534		errx(1, "unable to allocate boot block buffer");
535	memset(mboot.bootinst, 0, mboot.bootinst_size);
536	le16enc(&mboot.bootinst[DOSMAGICOFFSET], DOSMAGIC);
537#endif
538}
539
540
541static void
542init_sector0(unsigned long start)
543{
544	struct dos_partition *partp = &mboot.parts[0];
545
546	init_boot();
547
548	partp->dp_typ = DOSPTYP_386BSD;
549	partp->dp_flag = ACTIVE;
550	start = ((start + dos_sectors - 1) / dos_sectors) * dos_sectors;
551	if(start == 0)
552		start = dos_sectors;
553	partp->dp_start = start;
554	partp->dp_size = (disksecs / dos_cylsecs) * dos_cylsecs - start;
555
556	dos(partp);
557}
558
559static void
560change_part(int i)
561{
562    struct dos_partition *partp = &mboot.parts[i - 1];
563
564    printf("The data for partition %d is:\n", i);
565    print_part(partp);
566
567    if (u_flag && ok("Do you want to change it?")) {
568	int tmp;
569
570	if (i_flag) {
571	    bzero(partp, sizeof (*partp));
572	    if (i == 1) {
573		init_sector0(1);
574		printf("\nThe static data for the slice 1 has been reinitialized to:\n");
575		print_part(partp);
576	    }
577	}
578
579	do {
580		Decimal("sysid (165=FreeBSD)", partp->dp_typ, tmp, 255);
581		Decimal("start", partp->dp_start, tmp, NO_DISK_SECTORS);
582		Decimal("size", partp->dp_size, tmp, NO_DISK_SECTORS);
583		if (!sanitize_partition(partp)) {
584			warnx("ERROR: failed to adjust; setting sysid to 0");
585			partp->dp_typ = 0;
586		}
587
588		if (ok("Explicitly specify beg/end address ?"))
589		{
590			int	tsec,tcyl,thd;
591			tcyl = DPCYL(partp->dp_scyl,partp->dp_ssect);
592			thd = partp->dp_shd;
593			tsec = DPSECT(partp->dp_ssect);
594			Decimal("beginning cylinder", tcyl, tmp, NO_TRACK_CYLINDERS);
595			Decimal("beginning head", thd, tmp, NO_TRACK_HEADS);
596			Decimal("beginning sector", tsec, tmp, NO_TRACK_SECTORS);
597			partp->dp_scyl = DOSCYL(tcyl);
598			partp->dp_ssect = DOSSECT(tsec,tcyl);
599			partp->dp_shd = thd;
600
601			tcyl = DPCYL(partp->dp_ecyl,partp->dp_esect);
602			thd = partp->dp_ehd;
603			tsec = DPSECT(partp->dp_esect);
604			Decimal("ending cylinder", tcyl, tmp, NO_TRACK_CYLINDERS);
605			Decimal("ending head", thd, tmp, NO_TRACK_HEADS);
606			Decimal("ending sector", tsec, tmp, NO_TRACK_SECTORS);
607			partp->dp_ecyl = DOSCYL(tcyl);
608			partp->dp_esect = DOSSECT(tsec,tcyl);
609			partp->dp_ehd = thd;
610		} else
611			dos(partp);
612
613		print_part(partp);
614	} while (!ok("Are we happy with this entry?"));
615    }
616}
617
618static void
619print_params()
620{
621	printf("parameters extracted from in-core disklabel are:\n");
622	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
623			,cyls,heads,sectors,cylsecs);
624	if (dos_cyls > 1023 || dos_heads > 255 || dos_sectors > 63)
625		printf("Figures below won't work with BIOS for partitions not in cyl 1\n");
626	printf("parameters to be used for BIOS calculations are:\n");
627	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
628		,dos_cyls,dos_heads,dos_sectors,dos_cylsecs);
629}
630
631static void
632change_active(int which)
633{
634	struct dos_partition *partp = &mboot.parts[0];
635	int active, i, new, tmp;
636
637	active = -1;
638	for (i = 0; i < NDOSPART; i++) {
639		if ((partp[i].dp_flag & ACTIVE) == 0)
640			continue;
641		printf("Partition %d is marked active\n", i + 1);
642		if (active == -1)
643			active = i + 1;
644	}
645	if (a_flag && which != -1)
646		active = which;
647	else if (active == -1)
648		active = 1;
649
650	if (!ok("Do you want to change the active partition?"))
651		return;
652setactive:
653	do {
654		new = active;
655		Decimal("active partition", new, tmp, 0);
656		if (new < 1 || new > 4) {
657			printf("Active partition number must be in range 1-4."
658					"  Try again.\n");
659			goto setactive;
660		}
661		active = new;
662	} while (!ok("Are you happy with this choice"));
663	for (i = 0; i < NDOSPART; i++)
664		partp[i].dp_flag = 0;
665	if (active > 0 && active <= NDOSPART)
666		partp[active-1].dp_flag = ACTIVE;
667}
668
669static void
670change_code()
671{
672	if (ok("Do you want to change the boot code?"))
673		init_boot();
674}
675
676void
677get_params_to_use()
678{
679	int	tmp;
680	print_params();
681	if (ok("Do you want to change our idea of what BIOS thinks ?"))
682	{
683		do
684		{
685			Decimal("BIOS's idea of #cylinders", dos_cyls, tmp, 0);
686			Decimal("BIOS's idea of #heads", dos_heads, tmp, 0);
687			Decimal("BIOS's idea of #sectors", dos_sectors, tmp, 0);
688			dos_cylsecs = dos_heads * dos_sectors;
689			print_params();
690		}
691		while(!ok("Are you happy with this choice"));
692	}
693}
694
695
696/***********************************************\
697* Change real numbers into strange dos numbers	*
698\***********************************************/
699static void
700dos(struct dos_partition *partp)
701{
702	int cy, sec;
703	u_int32_t end;
704
705	if (partp->dp_typ == 0 && partp->dp_start == 0 && partp->dp_size == 0) {
706		memcpy(partp, &mtpart, sizeof(*partp));
707		return;
708	}
709
710	/* Start c/h/s. */
711	partp->dp_shd = partp->dp_start % dos_cylsecs / dos_sectors;
712	cy = partp->dp_start / dos_cylsecs;
713	sec = partp->dp_start % dos_sectors + 1;
714	partp->dp_scyl = DOSCYL(cy);
715	partp->dp_ssect = DOSSECT(sec, cy);
716
717	/* End c/h/s. */
718	end = partp->dp_start + partp->dp_size - 1;
719	partp->dp_ehd = end % dos_cylsecs / dos_sectors;
720	cy = end / dos_cylsecs;
721	sec = end % dos_sectors + 1;
722	partp->dp_ecyl = DOSCYL(cy);
723	partp->dp_esect = DOSSECT(sec, cy);
724}
725
726static int
727open_disk(int flag)
728{
729	int rwmode;
730
731	/* Write mode if one of these flags are set. */
732	rwmode = (a_flag || I_flag || B_flag || flag);
733	fd = g_open(disk, rwmode);
734	/* If the mode fails, try read-only if we didn't. */
735	if (fd == -1 && errno == EPERM && rwmode)
736		fd = g_open(disk, 0);
737	if (fd == -1 && errno == ENXIO)
738		return -2;
739	if (fd == -1) {
740		warnx("can't open device %s", disk);
741		return -1;
742	}
743	if (get_params() == -1) {
744		warnx("can't get disk parameters on %s", disk);
745		return -1;
746	}
747	return fd;
748}
749
750static ssize_t
751read_disk(off_t sector, void *buf)
752{
753
754	lseek(fd, (sector * 512), 0);
755	if (secsize == 0)
756		for (secsize = MIN_SEC_SIZE; secsize <= MAX_SEC_SIZE;
757		     secsize *= 2) {
758			/* try the read */
759			int size = read(fd, buf, secsize);
760			if (size == secsize)
761				/* it worked so return */
762				return secsize;
763		}
764	else
765		return read(fd, buf, secsize);
766
767	/* we failed to read at any of the sizes */
768	return -1;
769}
770
771static int
772write_disk(off_t sector, void *buf)
773{
774	int error;
775	struct gctl_req *grq;
776	const char *errmsg;
777	char fbuf[BUFSIZ], *pname;
778	int i, fdw;
779
780	grq = gctl_get_handle();
781	gctl_ro_param(grq, "verb", -1, "write MBR");
782	gctl_ro_param(grq, "class", -1, "MBR");
783	pname = g_providername(fd);
784	if (pname == NULL) {
785		warn("Error getting providername for %s", disk);
786		return (-1);
787	}
788	gctl_ro_param(grq, "geom", -1, pname);
789	gctl_ro_param(grq, "data", secsize, buf);
790	errmsg = gctl_issue(grq);
791	free(pname);
792	if (errmsg == NULL) {
793		gctl_free(grq);
794		return(0);
795	}
796	if (!q_flag)	/* GEOM errors are benign, not all devices supported */
797		warnx("%s", errmsg);
798	gctl_free(grq);
799
800	error = pwrite(fd, buf, secsize, (sector * 512));
801	if (error == secsize)
802		return (0);
803
804	for (i = 1; i < 5; i++) {
805		sprintf(fbuf, "%ss%d", disk, i);
806		fdw = open(fbuf, O_RDWR, 0);
807		if (fdw < 0)
808			continue;
809		error = ioctl(fdw, DIOCSMBR, buf);
810		close(fdw);
811		if (error == 0)
812			return (0);
813	}
814	warnx("Failed to write sector zero");
815	return(EINVAL);
816}
817
818static int
819get_params()
820{
821	int error;
822	u_int u;
823	off_t o;
824
825	error = ioctl(fd, DIOCGFWSECTORS, &u);
826	if (error == 0)
827		sectors = dos_sectors = u;
828	else
829		sectors = dos_sectors = 63;
830
831	error = ioctl(fd, DIOCGFWHEADS, &u);
832	if (error == 0)
833		heads = dos_heads = u;
834	else
835		heads = dos_heads = 255;
836
837	dos_cylsecs = cylsecs = heads * sectors;
838	disksecs = cyls * heads * sectors;
839
840	u = g_sectorsize(fd);
841	if (u <= 0)
842		return (-1);
843
844	o = g_mediasize(fd);
845	if (o < 0)
846		return (-1);
847	disksecs = o / u;
848	cyls = dos_cyls = o / (u * dos_heads * dos_sectors);
849
850	return (disksecs);
851}
852
853static int
854read_s0()
855{
856	int i;
857
858	mboot.bootinst_size = secsize;
859	if (mboot.bootinst != NULL)
860		free(mboot.bootinst);
861	if ((mboot.bootinst = malloc(mboot.bootinst_size)) == NULL) {
862		warnx("unable to allocate buffer to read fdisk "
863		      "partition table");
864		return -1;
865	}
866	if (read_disk(0, mboot.bootinst) == -1) {
867		warnx("can't read fdisk partition table");
868		return -1;
869	}
870	if (le16dec(&mboot.bootinst[DOSMAGICOFFSET]) != DOSMAGIC) {
871		warnx("invalid fdisk partition table found");
872		/* So should we initialize things */
873		return -1;
874	}
875	for (i = 0; i < NDOSPART; i++)
876		dos_partition_dec(
877		    &mboot.bootinst[DOSPARTOFF + i * DOSPARTSIZE],
878		    &mboot.parts[i]);
879	return 0;
880}
881
882static int
883write_s0()
884{
885	int	sector, i;
886
887	if (iotest) {
888		print_s0();
889		return 0;
890	}
891	for(i = 0; i < NDOSPART; i++)
892		dos_partition_enc(&mboot.bootinst[DOSPARTOFF + i * DOSPARTSIZE],
893		    &mboot.parts[i]);
894	le16enc(&mboot.bootinst[DOSMAGICOFFSET], DOSMAGIC);
895	for(sector = 0; sector < mboot.bootinst_size / secsize; sector++)
896		if (write_disk(sector,
897			       &mboot.bootinst[sector * secsize]) == -1) {
898			warn("can't write fdisk partition table");
899			return -1;
900		}
901	return(0);
902}
903
904
905static int
906ok(const char *str)
907{
908	printf("%s [n] ", str);
909	fflush(stdout);
910	if (fgets(lbuf, LBUF, stdin) == NULL)
911		exit(1);
912	lbuf[strlen(lbuf)-1] = 0;
913
914	if (*lbuf &&
915		(!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") ||
916		 !strcmp(lbuf, "y") || !strcmp(lbuf, "Y")))
917		return 1;
918	else
919		return 0;
920}
921
922static int
923decimal(const char *str, int *num, int deflt, uint32_t maxval)
924{
925	long long acc = 0;
926	int c;
927	char *cp;
928
929	while (1) {
930		printf("Supply a decimal value for \"%s\" [%d] ", str, deflt);
931		fflush(stdout);
932		if (fgets(lbuf, LBUF, stdin) == NULL)
933			exit(1);
934		lbuf[strlen(lbuf)-1] = 0;
935
936		if (!*lbuf)
937			return 0;
938
939		cp = lbuf;
940		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
941		if (!c)
942			return 0;
943		while ((c = *cp++)) {
944			if (c <= '9' && c >= '0') {
945				if (acc <= maxval || maxval == 0)
946					acc = acc * 10 + c - '0';
947			} else
948				break;
949		}
950		if (c == ' ' || c == '\t')
951			while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
952		if (!c) {
953			if (maxval > 0 && acc > maxval) {
954				acc = maxval;
955				printf("%s exceeds maximum value allowed for "
956				  "this field. The value has been reduced "
957				  "to %lld\n", lbuf, acc);
958			}
959			*num = acc;
960			return 1;
961		} else
962			printf("%s is an invalid decimal number.  Try again.\n",
963				lbuf);
964	}
965
966}
967
968
969static void
970parse_config_line(char *line, CMD *command)
971{
972    char	*cp, *end;
973
974    cp = line;
975    while (1) {
976	memset(command, 0, sizeof(*command));
977
978	while (isspace(*cp)) ++cp;
979	if (*cp == '\0' || *cp == '#')
980	    break;
981	command->cmd = *cp++;
982
983	/*
984	 * Parse args
985	 */
986	    while (1) {
987	    while (isspace(*cp)) ++cp;
988	    if (*cp == '\0')
989		break;		/* eol */
990	    if (*cp == '#')
991		break;		/* found comment */
992	    if (isalpha(*cp))
993		command->args[command->n_args].argtype = *cp++;
994	    end = NULL;
995	    command->args[command->n_args].arg_val = strtoul(cp, &end, 0);
996 	    if (cp == end || (!isspace(*end) && *end != '\0')) {
997 		char ch;
998 		end = cp;
999 		while (!isspace(*end) && *end != '\0') ++end;
1000 		ch = *end; *end = '\0';
1001 		command->args[command->n_args].arg_str = strdup(cp);
1002 		*end = ch;
1003 	    } else
1004 		command->args[command->n_args].arg_str = NULL;
1005	    cp = end;
1006	    command->n_args++;
1007	}
1008	break;
1009    }
1010}
1011
1012
1013static int
1014process_geometry(CMD *command)
1015{
1016    int		status = 1, i;
1017
1018    while (1) {
1019	geom_processed = 1;
1020	    if (part_processed) {
1021	    warnx(
1022	"ERROR line %d: the geometry specification line must occur before\n\
1023    all partition specifications",
1024		    current_line_number);
1025	    status = 0;
1026	    break;
1027	}
1028	    if (command->n_args != 3) {
1029	    warnx("ERROR line %d: incorrect number of geometry args",
1030		    current_line_number);
1031	    status = 0;
1032	    break;
1033	}
1034	    dos_cyls = 0;
1035	    dos_heads = 0;
1036	    dos_sectors = 0;
1037	    for (i = 0; i < 3; ++i) {
1038		    switch (command->args[i].argtype) {
1039	    case 'c':
1040		dos_cyls = command->args[i].arg_val;
1041		break;
1042	    case 'h':
1043		dos_heads = command->args[i].arg_val;
1044		break;
1045	    case 's':
1046		dos_sectors = command->args[i].arg_val;
1047		break;
1048	    default:
1049		warnx(
1050		"ERROR line %d: unknown geometry arg type: '%c' (0x%02x)",
1051			current_line_number, command->args[i].argtype,
1052			command->args[i].argtype);
1053		status = 0;
1054		break;
1055	    }
1056	}
1057	if (status == 0)
1058	    break;
1059
1060	dos_cylsecs = dos_heads * dos_sectors;
1061
1062	/*
1063	 * Do sanity checks on parameter values
1064	 */
1065	    if (dos_cyls == 0) {
1066	    warnx("ERROR line %d: number of cylinders not specified",
1067		    current_line_number);
1068	    status = 0;
1069	}
1070	    if (dos_cyls > 1024) {
1071	    warnx(
1072	"WARNING line %d: number of cylinders (%d) may be out-of-range\n\
1073    (must be within 1-1024 for normal BIOS operation, unless the entire disk\n\
1074    is dedicated to FreeBSD)",
1075		    current_line_number, dos_cyls);
1076	}
1077
1078	    if (dos_heads == 0) {
1079	    warnx("ERROR line %d: number of heads not specified",
1080		    current_line_number);
1081	    status = 0;
1082	    } else if (dos_heads > 256) {
1083	    warnx("ERROR line %d: number of heads must be within (1-256)",
1084		    current_line_number);
1085	    status = 0;
1086	}
1087
1088	    if (dos_sectors == 0) {
1089	    warnx("ERROR line %d: number of sectors not specified",
1090		    current_line_number);
1091	    status = 0;
1092	    } else if (dos_sectors > 63) {
1093	    warnx("ERROR line %d: number of sectors must be within (1-63)",
1094		    current_line_number);
1095	    status = 0;
1096	}
1097
1098	break;
1099    }
1100    return (status);
1101}
1102
1103static u_int32_t
1104str2sectors(const char *str)
1105{
1106	char *end;
1107	unsigned long val;
1108
1109	val = strtoul(str, &end, 0);
1110	if (str == end || *end == '\0') {
1111		warnx("ERROR line %d: unexpected size: \'%s\'",
1112		    current_line_number, str);
1113		return NO_DISK_SECTORS;
1114	}
1115
1116	if (*end == 'K')
1117		val *= 1024UL / secsize;
1118	else if (*end == 'M')
1119		val *= 1024UL * 1024UL / secsize;
1120	else if (*end == 'G')
1121		val *= 1024UL * 1024UL * 1024UL / secsize;
1122	else {
1123		warnx("ERROR line %d: unexpected modifier: %c "
1124		    "(not K/M/G)", current_line_number, *end);
1125		return NO_DISK_SECTORS;
1126	}
1127
1128	return val;
1129}
1130
1131static int
1132process_partition(CMD *command)
1133{
1134    int				status = 0, partition;
1135    u_int32_t			prev_head_boundary, prev_cyl_boundary;
1136    u_int32_t			adj_size, max_end;
1137    struct dos_partition	*partp;
1138
1139	while (1) {
1140	part_processed = 1;
1141		if (command->n_args != 4) {
1142	    warnx("ERROR line %d: incorrect number of partition args",
1143		    current_line_number);
1144	    break;
1145	}
1146	partition = command->args[0].arg_val;
1147		if (partition < 1 || partition > 4) {
1148	    warnx("ERROR line %d: invalid partition number %d",
1149		    current_line_number, partition);
1150	    break;
1151	}
1152	partp = &mboot.parts[partition - 1];
1153	bzero(partp, sizeof (*partp));
1154	partp->dp_typ = command->args[1].arg_val;
1155	if (command->args[2].arg_str != NULL) {
1156		if (strcmp(command->args[2].arg_str, "*") == 0) {
1157			int i;
1158			partp->dp_start = dos_sectors;
1159			for (i = 1; i < partition; i++) {
1160    				struct dos_partition *prev_partp;
1161				prev_partp = ((struct dos_partition *)
1162				    &mboot.parts) + i - 1;
1163				if (prev_partp->dp_typ != 0)
1164					partp->dp_start = prev_partp->dp_start +
1165					    prev_partp->dp_size;
1166			}
1167			if (partp->dp_start % dos_sectors != 0) {
1168		    		prev_head_boundary = partp->dp_start /
1169				    dos_sectors * dos_sectors;
1170		    		partp->dp_start = prev_head_boundary +
1171				    dos_sectors;
1172			}
1173		} else {
1174			partp->dp_start = str2sectors(command->args[2].arg_str);
1175			if (partp->dp_start == NO_DISK_SECTORS)
1176				break;
1177		}
1178	} else
1179		partp->dp_start = command->args[2].arg_val;
1180
1181	if (command->args[3].arg_str != NULL) {
1182		if (strcmp(command->args[3].arg_str, "*") == 0)
1183			partp->dp_size = ((disksecs / dos_cylsecs) *
1184			    dos_cylsecs) - partp->dp_start;
1185		else {
1186			partp->dp_size = str2sectors(command->args[3].arg_str);
1187			if (partp->dp_size == NO_DISK_SECTORS)
1188				break;
1189		}
1190		prev_cyl_boundary = ((partp->dp_start + partp->dp_size) /
1191		    dos_cylsecs) * dos_cylsecs;
1192		if (prev_cyl_boundary > partp->dp_start)
1193			partp->dp_size = prev_cyl_boundary - partp->dp_start;
1194	} else
1195		partp->dp_size = command->args[3].arg_val;
1196
1197	max_end = partp->dp_start + partp->dp_size;
1198
1199	if (partp->dp_typ == 0) {
1200	    /*
1201	     * Get out, the partition is marked as unused.
1202	     */
1203	    /*
1204	     * Insure that it's unused.
1205	     */
1206	    bzero(partp, sizeof(*partp));
1207	    status = 1;
1208	    break;
1209	}
1210
1211	/*
1212	 * Adjust start upwards, if necessary, to fall on a head boundary.
1213	 */
1214		if (partp->dp_start % dos_sectors != 0) {
1215	    prev_head_boundary = partp->dp_start / dos_sectors * dos_sectors;
1216	    if (max_end < dos_sectors ||
1217			    prev_head_boundary > max_end - dos_sectors) {
1218		/*
1219		 * Can't go past end of partition
1220		 */
1221		warnx(
1222	"ERROR line %d: unable to adjust start of partition %d to fall on\n\
1223    a head boundary",
1224			current_line_number, partition);
1225		break;
1226	    }
1227	    warnx(
1228	"WARNING: adjusting start offset of partition %d\n\
1229    from %u to %u, to fall on a head boundary",
1230		    partition, (u_int)partp->dp_start,
1231		    (u_int)(prev_head_boundary + dos_sectors));
1232	    partp->dp_start = prev_head_boundary + dos_sectors;
1233	}
1234
1235	/*
1236	 * Adjust size downwards, if necessary, to fall on a cylinder
1237	 * boundary.
1238	 */
1239	prev_cyl_boundary =
1240	    ((partp->dp_start + partp->dp_size) / dos_cylsecs) * dos_cylsecs;
1241	if (prev_cyl_boundary > partp->dp_start)
1242	    adj_size = prev_cyl_boundary - partp->dp_start;
1243		else {
1244	    warnx(
1245	"ERROR: could not adjust partition to start on a head boundary\n\
1246    and end on a cylinder boundary.");
1247	    return (0);
1248	}
1249		if (adj_size != partp->dp_size) {
1250	    warnx(
1251	"WARNING: adjusting size of partition %d from %u to %u\n\
1252    to end on a cylinder boundary",
1253		    partition, (u_int)partp->dp_size, (u_int)adj_size);
1254	    partp->dp_size = adj_size;
1255	}
1256		if (partp->dp_size == 0) {
1257	    warnx("ERROR line %d: size of partition %d is zero",
1258		    current_line_number, partition);
1259	    break;
1260	}
1261
1262	dos(partp);
1263	status = 1;
1264	break;
1265    }
1266    return (status);
1267}
1268
1269
1270static int
1271process_active(CMD *command)
1272{
1273    int				status = 0, partition, i;
1274    struct dos_partition	*partp;
1275
1276	while (1) {
1277	active_processed = 1;
1278		if (command->n_args != 1) {
1279	    warnx("ERROR line %d: incorrect number of active args",
1280		    current_line_number);
1281	    status = 0;
1282	    break;
1283	}
1284	partition = command->args[0].arg_val;
1285		if (partition < 1 || partition > 4) {
1286	    warnx("ERROR line %d: invalid partition number %d",
1287		    current_line_number, partition);
1288	    break;
1289	}
1290	/*
1291	 * Reset active partition
1292	 */
1293	partp = mboot.parts;
1294	for (i = 0; i < NDOSPART; i++)
1295	    partp[i].dp_flag = 0;
1296	partp[partition-1].dp_flag = ACTIVE;
1297
1298	status = 1;
1299	break;
1300    }
1301    return (status);
1302}
1303
1304
1305static int
1306process_line(char *line)
1307{
1308    CMD		command;
1309    int		status = 1;
1310
1311	while (1) {
1312	parse_config_line(line, &command);
1313		switch (command.cmd) {
1314	case 0:
1315	    /*
1316	     * Comment or blank line
1317	     */
1318	    break;
1319	case 'g':
1320	    /*
1321	     * Set geometry
1322	     */
1323	    status = process_geometry(&command);
1324	    break;
1325	case 'p':
1326	    status = process_partition(&command);
1327	    break;
1328	case 'a':
1329	    status = process_active(&command);
1330	    break;
1331	default:
1332	    status = 0;
1333	    break;
1334	}
1335	break;
1336    }
1337    return (status);
1338}
1339
1340
1341static int
1342read_config(char *config_file)
1343{
1344    FILE	*fp = NULL;
1345    int		status = 1;
1346    char	buf[1010];
1347
1348	while (1) {
1349		if (strcmp(config_file, "-") != 0) {
1350	    /*
1351	     * We're not reading from stdin
1352	     */
1353			if ((fp = fopen(config_file, "r")) == NULL) {
1354		status = 0;
1355		break;
1356	    }
1357		} else {
1358	    fp = stdin;
1359	}
1360	current_line_number = 0;
1361		while (!feof(fp)) {
1362	    if (fgets(buf, sizeof(buf), fp) == NULL)
1363		break;
1364	    ++current_line_number;
1365	    status = process_line(buf);
1366	    if (status == 0)
1367		break;
1368	    }
1369	break;
1370    }
1371	if (fp) {
1372	/*
1373	 * It doesn't matter if we're reading from stdin, as we've reached EOF
1374	 */
1375	fclose(fp);
1376    }
1377    return (status);
1378}
1379
1380
1381static void
1382reset_boot(void)
1383{
1384    int				i;
1385    struct dos_partition	*partp;
1386
1387    init_boot();
1388    for (i = 0; i < 4; ++i) {
1389	partp = &mboot.parts[i];
1390	bzero(partp, sizeof(*partp));
1391    }
1392}
1393
1394static int
1395sanitize_partition(struct dos_partition *partp)
1396{
1397    u_int32_t			prev_head_boundary, prev_cyl_boundary;
1398    u_int32_t			max_end, size, start;
1399
1400    start = partp->dp_start;
1401    size = partp->dp_size;
1402    max_end = start + size;
1403    /* Only allow a zero size if the partition is being marked unused. */
1404    if (size == 0) {
1405	if (start == 0 && partp->dp_typ == 0)
1406	    return (1);
1407	warnx("ERROR: size of partition is zero");
1408	return (0);
1409    }
1410    /* Return if no adjustment is necessary. */
1411    if (start % dos_sectors == 0 && (start + size) % dos_sectors == 0)
1412	return (1);
1413
1414    if (start == 0) {
1415	    warnx("WARNING: partition overlaps with partition table");
1416	    if (ok("Correct this automatically?"))
1417		    start = dos_sectors;
1418    }
1419    if (start % dos_sectors != 0)
1420	warnx("WARNING: partition does not start on a head boundary");
1421    if ((start  +size) % dos_sectors != 0)
1422	warnx("WARNING: partition does not end on a cylinder boundary");
1423    warnx("WARNING: this may confuse the BIOS or some operating systems");
1424    if (!ok("Correct this automatically?"))
1425	return (1);
1426
1427    /*
1428     * Adjust start upwards, if necessary, to fall on a head boundary.
1429     */
1430    if (start % dos_sectors != 0) {
1431	prev_head_boundary = start / dos_sectors * dos_sectors;
1432	if (max_end < dos_sectors ||
1433	    prev_head_boundary >= max_end - dos_sectors) {
1434	    /*
1435	     * Can't go past end of partition
1436	     */
1437	    warnx(
1438    "ERROR: unable to adjust start of partition to fall on a head boundary");
1439	    return (0);
1440        }
1441	start = prev_head_boundary + dos_sectors;
1442    }
1443
1444    /*
1445     * Adjust size downwards, if necessary, to fall on a cylinder
1446     * boundary.
1447     */
1448    prev_cyl_boundary = ((start + size) / dos_cylsecs) * dos_cylsecs;
1449    if (prev_cyl_boundary > start)
1450	size = prev_cyl_boundary - start;
1451    else {
1452	warnx("ERROR: could not adjust partition to start on a head boundary\n\
1453    and end on a cylinder boundary.");
1454	return (0);
1455    }
1456
1457    /* Finally, commit any changes to partp and return. */
1458    if (start != partp->dp_start) {
1459	warnx("WARNING: adjusting start offset of partition to %u",
1460	    (u_int)start);
1461	partp->dp_start = start;
1462    }
1463    if (size != partp->dp_size) {
1464	warnx("WARNING: adjusting size of partition to %u", (u_int)size);
1465	partp->dp_size = size;
1466    }
1467
1468    return (1);
1469}
1470
1471/*
1472 * Try figuring out the root device's canonical disk name.
1473 * The following choices are considered:
1474 *   /dev/ad0s1a     => /dev/ad0
1475 *   /dev/da0a       => /dev/da0
1476 *   /dev/vinum/root => /dev/vinum/root
1477 * A ".eli" part is removed if it exists (see geli(8)).
1478 * A ".journal" ending is removed if it exists (see gjournal(8)).
1479 */
1480static char *
1481get_rootdisk(void)
1482{
1483	struct statfs rootfs;
1484	regex_t re;
1485#define NMATCHES 2
1486	regmatch_t rm[NMATCHES];
1487	char dev[PATH_MAX], *s;
1488	int rv;
1489
1490	if (statfs("/", &rootfs) == -1)
1491		err(1, "statfs(\"/\")");
1492
1493	if ((rv = regcomp(&re, "^(/dev/[a-z/]+[0-9]*)([sp][0-9]+)?[a-h]?(\\.journal)?$",
1494		    REG_EXTENDED)) != 0)
1495		errx(1, "regcomp() failed (%d)", rv);
1496	strlcpy(dev, rootfs.f_mntfromname, sizeof (dev));
1497	if ((s = strstr(dev, ".eli")) != NULL)
1498	    memmove(s, s+4, strlen(s + 4) + 1);
1499
1500	if ((rv = regexec(&re, dev, NMATCHES, rm, 0)) != 0)
1501		errx(1,
1502"mounted root fs resource doesn't match expectations (regexec returned %d)",
1503		    rv);
1504	if ((s = malloc(rm[1].rm_eo - rm[1].rm_so + 1)) == NULL)
1505		errx(1, "out of memory");
1506	memcpy(s, rootfs.f_mntfromname + rm[1].rm_so,
1507	    rm[1].rm_eo - rm[1].rm_so);
1508	s[rm[1].rm_eo - rm[1].rm_so] = 0;
1509
1510	return s;
1511}
1512