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