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