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