fdisk.c revision 50476
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 50476 1999-08-28 00:22:10Z peter $";
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/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/r%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	int	flag;
735	if (iotest) {
736		print_s0(-1);
737		return 0;
738	}
739	/*
740	 * write enable label sector before write (if necessary),
741	 * disable after writing.
742	 * needed if the disklabel protected area also protects
743	 * sector 0. (e.g. empty disk)
744	 */
745#ifdef NOT_NOW
746	flag = 1;
747	if (ioctl(fd, DIOCWLABEL, &flag) < 0)
748		warn("ioctl DIOCWLABEL");
749#endif
750	if (write_disk(0, (char *) mboot.bootinst) == -1) {
751		warn("can't write fdisk partition table");
752		return -1;
753#ifdef NOT_NOW
754	flag = 0;
755	(void) ioctl(fd, DIOCWLABEL, &flag);
756#endif
757	}
758	return(0);
759}
760
761
762static int
763ok(str)
764char *str;
765{
766	printf("%s [n] ", str);
767	fgets(lbuf, LBUF, stdin);
768	lbuf[strlen(lbuf)-1] = 0;
769
770	if (*lbuf &&
771		(!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") ||
772		 !strcmp(lbuf, "y") || !strcmp(lbuf, "Y")))
773		return 1;
774	else
775		return 0;
776}
777
778static int
779decimal(char *str, int *num, int deflt)
780{
781int acc = 0, c;
782char *cp;
783
784	while (1) {
785		printf("Supply a decimal value for \"%s\" [%d] ", str, deflt);
786		fgets(lbuf, LBUF, stdin);
787		lbuf[strlen(lbuf)-1] = 0;
788
789		if (!*lbuf)
790			return 0;
791
792		cp = lbuf;
793		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
794		if (!c)
795			return 0;
796		while ((c = *cp++)) {
797			if (c <= '9' && c >= '0')
798				acc = acc * 10 + c - '0';
799			else
800				break;
801		}
802		if (c == ' ' || c == '\t')
803			while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
804		if (!c) {
805			*num = acc;
806			return 1;
807		} else
808			printf("%s is an invalid decimal number.  Try again.\n",
809				lbuf);
810	}
811
812}
813
814#if 0
815static int
816hex(char *str, int *num, int deflt)
817{
818int acc = 0, c;
819char *cp;
820
821	while (1) {
822		printf("Supply a hex value for \"%s\" [%x] ", str, deflt);
823		fgets(lbuf, LBUF, stdin);
824		lbuf[strlen(lbuf)-1] = 0;
825
826		if (!*lbuf)
827			return 0;
828
829		cp = lbuf;
830		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
831		if (!c)
832			return 0;
833		while ((c = *cp++)) {
834			if (c <= '9' && c >= '0')
835				acc = (acc << 4) + c - '0';
836			else if (c <= 'f' && c >= 'a')
837				acc = (acc << 4) + c - 'a' + 10;
838			else if (c <= 'F' && c >= 'A')
839				acc = (acc << 4) + c - 'A' + 10;
840			else
841				break;
842		}
843		if (c == ' ' || c == '\t')
844			while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
845		if (!c) {
846			*num = acc;
847			return 1;
848		} else
849			printf("%s is an invalid hex number.  Try again.\n",
850				lbuf);
851	}
852
853}
854
855static int
856string(char *str, char **ans)
857{
858int c;
859char *cp = lbuf;
860
861	while (1) {
862		printf("Supply a string value for \"%s\" [%s] ", str, *ans);
863		fgets(lbuf, LBUF, stdin);
864		lbuf[strlen(lbuf)-1] = 0;
865
866		if (!*lbuf)
867			return 0;
868
869		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
870		if (c == '"') {
871			c = *++cp;
872			*ans = cp;
873			while ((c = *cp) && c != '"') cp++;
874		} else {
875			*ans = cp;
876			while ((c = *cp) && c != ' ' && c != '\t') cp++;
877		}
878
879		if (c)
880			*cp = 0;
881		return 1;
882	}
883}
884#endif
885
886static char *
887get_type(int type)
888{
889	int	numentries = (sizeof(part_types)/sizeof(struct part_type));
890	int	counter = 0;
891	struct	part_type *ptr = part_types;
892
893
894	while(counter < numentries)
895	{
896		if(ptr->type == type)
897		{
898			return(ptr->name);
899		}
900		ptr++;
901		counter++;
902	}
903	return("unknown");
904}
905
906
907static void
908parse_config_line(line, command)
909    char	*line;
910    CMD		*command;
911{
912    char	*cp, *end;
913
914    cp = line;
915    while (1)	/* dirty trick used to insure one exit point for this
916		   function */
917    {
918	memset(command, 0, sizeof(*command));
919
920	while (isspace(*cp)) ++cp;
921	if (*cp == '\0' || *cp == '#')
922	{
923	    break;
924	}
925	command->cmd = *cp++;
926
927	/*
928	 * Parse args
929	 */
930	while (1)
931	{
932	    while (isspace(*cp)) ++cp;
933	    if (*cp == '#')
934	    {
935		break;		/* found comment */
936	    }
937	    if (isalpha(*cp))
938	    {
939		command->args[command->n_args].argtype = *cp++;
940	    }
941	    if (!isdigit(*cp))
942	    {
943		break;		/* assume end of line */
944	    }
945	    end = NULL;
946	    command->args[command->n_args].arg_val = strtol(cp, &end, 0);
947	    if (cp == end)
948	    {
949		break;		/* couldn't parse number */
950	    }
951	    cp = end;
952	    command->n_args++;
953	}
954	break;
955    }
956}
957
958
959static int
960process_geometry(command)
961    CMD		*command;
962{
963    int		status = 1, i;
964
965    while (1)
966    {
967	geom_processed = 1;
968	if (part_processed)
969	{
970	    warnx(
971	"ERROR line %d: the geometry specification line must occur before\n\
972    all partition specifications",
973		    current_line_number);
974	    status = 0;
975	    break;
976	}
977	if (command->n_args != 3)
978	{
979	    warnx("ERROR line %d: incorrect number of geometry args",
980		    current_line_number);
981	    status = 0;
982	    break;
983	}
984	dos_cyls = -1;
985	dos_heads = -1;
986	dos_sectors = -1;
987	for (i = 0; i < 3; ++i)
988	{
989	    switch (command->args[i].argtype)
990	    {
991	    case 'c':
992		dos_cyls = command->args[i].arg_val;
993		break;
994	    case 'h':
995		dos_heads = command->args[i].arg_val;
996		break;
997	    case 's':
998		dos_sectors = command->args[i].arg_val;
999		break;
1000	    default:
1001		warnx(
1002		"ERROR line %d: unknown geometry arg type: '%c' (0x%02x)",
1003			current_line_number, command->args[i].argtype,
1004			command->args[i].argtype);
1005		status = 0;
1006		break;
1007	    }
1008	}
1009	if (status == 0)
1010	{
1011	    break;
1012	}
1013
1014	dos_cylsecs = dos_heads * dos_sectors;
1015
1016	/*
1017	 * Do sanity checks on parameter values
1018	 */
1019	if (dos_cyls < 0)
1020	{
1021	    warnx("ERROR line %d: number of cylinders not specified",
1022		    current_line_number);
1023	    status = 0;
1024	}
1025	if (dos_cyls == 0 || dos_cyls > 1024)
1026	{
1027	    warnx(
1028	"WARNING line %d: number of cylinders (%d) may be out-of-range\n\
1029    (must be within 1-1024 for normal BIOS operation, unless the entire disk\n\
1030    is dedicated to FreeBSD)",
1031		    current_line_number, dos_cyls);
1032	}
1033
1034	if (dos_heads < 0)
1035	{
1036	    warnx("ERROR line %d: number of heads not specified",
1037		    current_line_number);
1038	    status = 0;
1039	}
1040	else if (dos_heads < 1 || dos_heads > 256)
1041	{
1042	    warnx("ERROR line %d: number of heads must be within (1-256)",
1043		    current_line_number);
1044	    status = 0;
1045	}
1046
1047	if (dos_sectors < 0)
1048	{
1049	    warnx("ERROR line %d: number of sectors not specified",
1050		    current_line_number);
1051	    status = 0;
1052	}
1053	else if (dos_sectors < 1 || dos_sectors > 63)
1054	{
1055	    warnx("ERROR line %d: number of sectors must be within (1-63)",
1056		    current_line_number);
1057	    status = 0;
1058	}
1059
1060	break;
1061    }
1062    return (status);
1063}
1064
1065
1066static int
1067process_partition(command)
1068    CMD		*command;
1069{
1070    int				status = 0, partition;
1071    unsigned long		chunks, adj_size, max_end;
1072    struct dos_partition	*partp;
1073
1074    while (1)
1075    {
1076	part_processed = 1;
1077	if (command->n_args != 4)
1078	{
1079	    warnx("ERROR line %d: incorrect number of partition args",
1080		    current_line_number);
1081	    break;
1082	}
1083	partition = command->args[0].arg_val;
1084	if (partition < 1 || partition > 4)
1085	{
1086	    warnx("ERROR line %d: invalid partition number %d",
1087		    current_line_number, partition);
1088	    break;
1089	}
1090	partp = ((struct dos_partition *) &mboot.parts) + partition - 1;
1091	bzero((char *)partp, sizeof (struct dos_partition));
1092	partp->dp_typ = command->args[1].arg_val;
1093	partp->dp_start = command->args[2].arg_val;
1094	partp->dp_size = command->args[3].arg_val;
1095	max_end = partp->dp_start + partp->dp_size;
1096
1097	if (partp->dp_typ == 0)
1098	{
1099	    /*
1100	     * Get out, the partition is marked as unused.
1101	     */
1102	    /*
1103	     * Insure that it's unused.
1104	     */
1105	    bzero((char *)partp, sizeof (struct dos_partition));
1106	    status = 1;
1107	    break;
1108	}
1109
1110	/*
1111	 * Adjust start upwards, if necessary, to fall on an head boundary.
1112	 */
1113	if (partp->dp_start % dos_sectors != 0)
1114	{
1115	    adj_size =
1116		(partp->dp_start / dos_sectors + 1) * dos_sectors;
1117	    if (adj_size > max_end)
1118	    {
1119		/*
1120		 * Can't go past end of partition
1121		 */
1122		warnx(
1123	"ERROR line %d: unable to adjust start of partition %d to fall on\n\
1124    a cylinder boundary",
1125			current_line_number, partition);
1126		break;
1127	    }
1128	    warnx(
1129	"WARNING: adjusting start offset of partition '%d' from %lu\n\
1130    to %lu, to round to an head boundary",
1131		    partition, (u_long)partp->dp_start, adj_size);
1132	    partp->dp_start = adj_size;
1133	}
1134
1135	/*
1136	 * Adjust size downwards, if necessary, to fall on a cylinder
1137	 * boundary.
1138	 */
1139	chunks =
1140	    ((partp->dp_start + partp->dp_size) / dos_cylsecs) * dos_cylsecs;
1141	adj_size = chunks - partp->dp_start;
1142	if (adj_size != partp->dp_size)
1143	{
1144	    warnx(
1145	"WARNING: adjusting size of partition '%d' from %lu to %lu,\n\
1146    to round to a cylinder boundary",
1147		    partition, (u_long)partp->dp_size, adj_size);
1148	    if (chunks > 0)
1149	    {
1150		partp->dp_size = adj_size;
1151	    }
1152	    else
1153	    {
1154		partp->dp_size = 0;
1155	    }
1156	}
1157	if (partp->dp_size < 1)
1158	{
1159	    warnx("ERROR line %d: size for partition '%d' is zero",
1160		    current_line_number, partition);
1161	    break;
1162	}
1163
1164	dos(partp->dp_start, partp->dp_size,
1165	    &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
1166	dos(partp->dp_start+partp->dp_size - 1, partp->dp_size,
1167	    &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
1168	status = 1;
1169	break;
1170    }
1171    return (status);
1172}
1173
1174
1175static int
1176process_active(command)
1177    CMD		*command;
1178{
1179    int				status = 0, partition, i;
1180    struct dos_partition	*partp;
1181
1182    while (1)
1183    {
1184	active_processed = 1;
1185	if (command->n_args != 1)
1186	{
1187	    warnx("ERROR line %d: incorrect number of active args",
1188		    current_line_number);
1189	    status = 0;
1190	    break;
1191	}
1192	partition = command->args[0].arg_val;
1193	if (partition < 1 || partition > 4)
1194	{
1195	    warnx("ERROR line %d: invalid partition number %d",
1196		    current_line_number, partition);
1197	    break;
1198	}
1199	/*
1200	 * Reset active partition
1201	 */
1202	partp = ((struct dos_partition *) &mboot.parts);
1203	for (i = 0; i < NDOSPART; i++)
1204	    partp[i].dp_flag = 0;
1205	partp[partition-1].dp_flag = ACTIVE;
1206
1207	status = 1;
1208	break;
1209    }
1210    return (status);
1211}
1212
1213
1214static int
1215process_line(line)
1216    char	*line;
1217{
1218    CMD		command;
1219    int		status = 1;
1220
1221    while (1)
1222    {
1223	parse_config_line(line, &command);
1224	switch (command.cmd)
1225	{
1226	case 0:
1227	    /*
1228	     * Comment or blank line
1229	     */
1230	    break;
1231	case 'g':
1232	    /*
1233	     * Set geometry
1234	     */
1235	    status = process_geometry(&command);
1236	    break;
1237	case 'p':
1238	    status = process_partition(&command);
1239	    break;
1240	case 'a':
1241	    status = process_active(&command);
1242	    break;
1243	default:
1244	    status = 0;
1245	    break;
1246	}
1247	break;
1248    }
1249    return (status);
1250}
1251
1252
1253static int
1254read_config(config_file)
1255    char *config_file;
1256{
1257    FILE	*fp = NULL;
1258    int		status = 1;
1259    char	buf[1010];
1260
1261    while (1)	/* dirty trick used to insure one exit point for this
1262		   function */
1263    {
1264	if (strcmp(config_file, "-") != 0)
1265	{
1266	    /*
1267	     * We're not reading from stdin
1268	     */
1269	    if ((fp = fopen(config_file, "r")) == NULL)
1270	    {
1271		status = 0;
1272		break;
1273	    }
1274	}
1275	else
1276	{
1277	    fp = stdin;
1278	}
1279	current_line_number = 0;
1280	while (!feof(fp))
1281	{
1282	    if (fgets(buf, sizeof(buf), fp) == NULL)
1283	    {
1284		break;
1285	    }
1286	    ++current_line_number;
1287	    status = process_line(buf);
1288	    if (status == 0)
1289	    {
1290		break;
1291	    }
1292	}
1293	break;
1294    }
1295    if (fp)
1296    {
1297	/*
1298	 * It doesn't matter if we're reading from stdin, as we've reached EOF
1299	 */
1300	fclose(fp);
1301    }
1302    return (status);
1303}
1304
1305
1306static void
1307reset_boot(void)
1308{
1309    int				i;
1310    struct dos_partition	*partp;
1311
1312    init_boot();
1313    for (i = 0; i < 4; ++i)
1314    {
1315	partp = ((struct dos_partition *) &mboot.parts) + i;
1316	bzero((char *)partp, sizeof (struct dos_partition));
1317    }
1318}
1319