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