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