fdisk.c revision 40946
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	"$Id: fdisk.c,v 1.24 1998/09/16 19:25:47 obrien Exp $";
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/rsd0", "/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 a_flag  = 0;		/* set active partition */
121static int i_flag  = 0;		/* replace partition data */
122static int u_flag  = 0;		/* update partition data */
123static int t_flag  = 0;		/* test only, if f_flag is given */
124static char *f_flag = NULL;	/* Read config info from file */
125static int v_flag  = 0;		/* Be verbose */
126
127static unsigned char bootcode[] = {
1280x33, 0xc0, 0xfa, 0x8e, 0xd0, 0xbc, 0x00, 0x7c, 0x8e, 0xc0, 0x8e, 0xd8, 0xfb, 0x8b, 0xf4, 0xbf,
1290x00, 0x06, 0xb9, 0x00, 0x02, 0xfc, 0xf3, 0xa4, 0xea, 0x1d, 0x06, 0x00, 0x00, 0xb0, 0x04, 0xbe,
1300xbe, 0x07, 0x80, 0x3c, 0x80, 0x74, 0x0c, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x75, 0xf4, 0xbe, 0xbd,
1310x06, 0xeb, 0x43, 0x8b, 0xfe, 0x8b, 0x14, 0x8b, 0x4c, 0x02, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x74,
1320x0a, 0x80, 0x3c, 0x80, 0x75, 0xf4, 0xbe, 0xbd, 0x06, 0xeb, 0x2b, 0xbd, 0x05, 0x00, 0xbb, 0x00,
1330x7c, 0xb8, 0x01, 0x02, 0xcd, 0x13, 0x73, 0x0c, 0x33, 0xc0, 0xcd, 0x13, 0x4d, 0x75, 0xef, 0xbe,
1340x9e, 0x06, 0xeb, 0x12, 0x81, 0x3e, 0xfe, 0x7d, 0x55, 0xaa, 0x75, 0x07, 0x8b, 0xf7, 0xea, 0x00,
1350x7c, 0x00, 0x00, 0xbe, 0x85, 0x06, 0x2e, 0xac, 0x0a, 0xc0, 0x74, 0x06, 0xb4, 0x0e, 0xcd, 0x10,
1360xeb, 0xf4, 0xfb, 0xeb, 0xfe,
137'M', 'i', 's', 's', 'i', 'n', 'g', ' ',
138	'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
139'E', 'r', 'r', 'o', 'r', ' ', 'l', 'o', 'a', 'd', 'i', 'n', 'g', ' ',
140	'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
141'I', 'n', 'v', 'a', 'l', 'i', 'd', ' ',
142	'p', 'a', 'r', 't', 'i', 't', 'i', 'o', 'n', ' ', 't', 'a', 'b', 'l', 'e', 0,
143'A', 'u', 't', 'h', 'o', 'r', ' ', '-', ' ',
144	'S', 'i', 'e', 'g', 'm', 'a', 'r', ' ', 'S', 'c', 'h', 'm', 'i', 'd', 't', 0,0,0,
145
146  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
147  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
148  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
149  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
150  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
151  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
152  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
153  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
154  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
155  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
156  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
157  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
158  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
159};
160
161struct part_type
162{
163 unsigned char type;
164 char *name;
165}part_types[] =
166{
167	 {0x00, "unused"}
168	,{0x01, "Primary DOS with 12 bit FAT"}
169	,{0x02, "XENIX / filesystem"}
170	,{0x03, "XENIX /usr filesystem"}
171	,{0x04, "Primary DOS with 16 bit FAT (<= 32MB)"}
172	,{0x05, "Extended DOS"}
173	,{0x06, "Primary 'big' DOS (> 32MB)"}
174	,{0x07, "OS/2 HPFS, NTFS, QNX or Advanced UNIX"}
175	,{0x08, "AIX filesystem"}
176	,{0x09, "AIX boot partition or Coherent"}
177	,{0x0A, "OS/2 Boot Manager or OPUS"}
178	,{0x0B, "DOS or Windows 95 with 32 bit FAT"}
179	,{0x0C, "DOS or Windows 95 with 32 bit FAT, LBA"}
180	,{0x0E, "Primary 'big' DOS (> 32MB, LBA)"}
181	,{0x0F, "Extended DOS, LBA"}
182	,{0x10, "OPUS"}
183	,{0x40, "VENIX 286"}
184	,{0x50, "DM"}
185	,{0x51, "DM"}
186	,{0x52, "CP/M or Microport SysV/AT"}
187	,{0x56, "GB"}
188	,{0x61, "Speed"}
189	,{0x63, "ISC UNIX, other System V/386, GNU HURD or Mach"}
190	,{0x64, "Novell Netware 2.xx"}
191	,{0x65, "Novell Netware 3.xx"}
192	,{0x75, "PCIX"}
193	,{0x80, "Minix 1.1 ... 1.4a"}
194	,{0x81, "Minix 1.4b ... 1.5.10"}
195	,{0x82, "Linux swap or Solaris x86"}
196	,{0x83, "Linux filesystem"}
197	,{0x93, "Amoeba filesystem"}
198	,{0x94, "Amoeba bad block table"}
199	,{0x9F, "BSD/OS"}
200	,{0xA5, "FreeBSD/NetBSD/386BSD"}
201	,{0xA6, "OpenBSD"}
202	,{0xA7, "NEXTSTEP"}
203	,{0xB7, "BSDI BSD/386 filesystem"}
204	,{0xB8, "BSDI BSD/386 swap"}
205	,{0xDB, "Concurrent CPM or C.DOS or CTOS"}
206	,{0xE1, "Speed"}
207	,{0xE3, "Speed"}
208	,{0xE4, "Speed"}
209	,{0xF1, "Speed"}
210	,{0xF2, "DOS 3.3+ Secondary"}
211	,{0xF4, "Speed"}
212	,{0xFF, "BBT (Bad Blocks Table)"}
213};
214
215static void print_s0(int which);
216static void print_part(int i);
217static void init_sector0(unsigned long start);
218static void init_boot(void);
219static void change_part(int i);
220static void print_params();
221static void change_active(int which);
222static void get_params_to_use();
223static void dos(int sec, int size, unsigned char *c, unsigned char *s,
224		unsigned char *h);
225static int open_disk(int u_flag);
226static ssize_t read_disk(off_t sector, void *buf);
227static ssize_t write_disk(off_t sector, void *buf);
228static int get_params();
229static int read_s0();
230static int write_s0();
231static int ok(char *str);
232static int decimal(char *str, int *num, int deflt);
233static char *get_type(int type);
234static int read_config(char *config_file);
235static void reset_boot(void);
236static void usage(void);
237#if 0
238static int hex(char *str, int *num, int deflt);
239static int string(char *str, char **ans);
240#endif
241
242
243int
244main(int argc, char *argv[])
245{
246	int	i;
247
248	for ( argv++ ; --argc ; argv++ ) { register char *token = *argv;
249		if (*token++ != '-' || !*token)
250			break;
251		else { register int flag;
252			for ( ; (flag = *token++) ; ) {
253				switch (flag) {
254				case '1':
255					partition = 1;
256					break;
257				case '2':
258					partition = 2;
259					break;
260				case '3':
261					partition = 3;
262					break;
263				case '4':
264					partition = 4;
265					break;
266				case 'a':
267					a_flag = 1;
268					break;
269				case 'f':
270					if (*token)
271					{
272					    f_flag = token;
273					    token = "";
274					}
275					else
276					{
277					    if (argc == 1)
278						usage();
279					    --argc;
280					    f_flag = *++argv;
281					}
282					/*
283					 * u_flag is needed, because we're
284					 * writing to the disk.
285					 */
286					u_flag = 1;
287					break;
288				case 'i':
289					i_flag = 1;
290				case 'u':
291					u_flag = 1;
292					break;
293				case 't':
294					t_flag = 1;
295				case 'v':
296					v_flag = 1;
297					break;
298				default:
299					usage();
300				}
301			}
302		}
303	}
304
305	if (argc > 0)
306	{
307		static char realname[12];
308
309		if(strncmp(argv[0], "/dev", 4) == 0)
310			disk = argv[0];
311		else
312		{
313			snprintf(realname, 12, "/dev/r%s", argv[0]);
314			disk = realname;
315		}
316
317		if (open_disk(u_flag) < 0)
318			err(1, "cannot open disk %s", disk);
319	}
320	else
321	{
322		int i, rv = 0;
323
324		for(i = 0; disks[i]; i++)
325		{
326			disk = disks[i];
327			rv = open_disk(u_flag);
328			if(rv != -2) break;
329		}
330		if(rv < 0)
331			err(1, "cannot open any disk");
332	}
333
334	printf("******* Working on device %s *******\n",disk);
335
336	if (f_flag)
337	{
338	    if (read_s0() || i_flag)
339	    {
340		reset_boot();
341	    }
342
343	    if (!read_config(f_flag))
344	    {
345		exit(1);
346	    }
347	    if (v_flag)
348	    {
349		print_s0(-1);
350	    }
351	    if (!t_flag)
352	    {
353		write_s0();
354	    }
355	}
356	else
357	{
358	    if(u_flag)
359	    {
360		get_params_to_use();
361	    }
362	    else
363	    {
364		print_params();
365	    }
366
367	    if (read_s0())
368		init_sector0(1);
369
370	    printf("Media sector size is %d\n", secsize);
371	    printf("Warning: BIOS sector numbering starts with sector 1\n");
372	    printf("Information from DOS bootblock is:\n");
373	    if (partition == -1)
374		for (i = 1; i <= NDOSPART; i++)
375		    change_part(i);
376	    else
377		change_part(partition);
378
379	    if (u_flag || a_flag)
380		change_active(partition);
381
382	    if (u_flag || a_flag) {
383		if (!t_flag)
384		{
385		    printf("\nWe haven't changed the partition table yet.  ");
386		    printf("This is your last chance.\n");
387		}
388		print_s0(-1);
389		if (!t_flag)
390		{
391		    if (ok("Should we write new partition table?"))
392			write_s0();
393		}
394		else
395		{
396		    printf("\n-t flag specified -- partition table not written.\n");
397		}
398	    }
399	}
400
401	exit(0);
402}
403
404static void
405usage()
406{
407	fprintf(stderr,
408 "usage: fdisk {-a|-i|-u} [-f <config file> [-t] [-v]] [-{1,2,3,4}] [disk]\n");
409        exit(1);
410}
411
412static void
413print_s0(int which)
414{
415int	i;
416
417	print_params();
418	printf("Information from DOS bootblock is:\n");
419	if (which == -1)
420		for (i = 1; i <= NDOSPART; i++)
421			printf("%d: ", i), print_part(i);
422	else
423		print_part(which);
424}
425
426static struct dos_partition mtpart = { 0 };
427
428static void
429print_part(int i)
430{
431	struct	  dos_partition *partp;
432	u_int64_t part_mb;
433
434	partp = ((struct dos_partition *) &mboot.parts) + i - 1;
435
436	if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) {
437		printf("<UNUSED>\n");
438		return;
439	}
440	/*
441	 * Be careful not to overflow.
442	 */
443	part_mb = partp->dp_size;
444	part_mb *= secsize;
445	part_mb /= (1024 * 1024);
446	printf("sysid %d,(%s)\n", partp->dp_typ, get_type(partp->dp_typ));
447	printf("    start %lu, size %lu (%qd Meg), flag %x%s\n",
448		(u_long)partp->dp_start,
449		(u_long)partp->dp_size,
450		part_mb,
451		partp->dp_flag,
452		partp->dp_flag == ACTIVE ? " (active)" : "");
453	printf("\tbeg: cyl %d/ sector %d/ head %d;\n\tend: cyl %d/ sector %d/ head %d\n"
454		,DPCYL(partp->dp_scyl, partp->dp_ssect)
455		,DPSECT(partp->dp_ssect)
456		,partp->dp_shd
457		,DPCYL(partp->dp_ecyl, partp->dp_esect)
458		,DPSECT(partp->dp_esect)
459		,partp->dp_ehd);
460}
461
462
463static void
464init_boot(void)
465{
466	memcpy(mboot.bootinst, bootcode, sizeof(bootcode));
467	mboot.signature = BOOT_MAGIC;
468}
469
470
471static void
472init_sector0(unsigned long start)
473{
474struct dos_partition *partp = (struct dos_partition *) (&mboot.parts[3]);
475unsigned long size = disksecs - start;
476
477	init_boot();
478
479	partp->dp_typ = DOSPTYP_386BSD;
480	partp->dp_flag = ACTIVE;
481	partp->dp_start = start;
482	partp->dp_size = size;
483
484	dos(partp->dp_start, partp->dp_size,
485	    &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
486	dos(partp->dp_start + partp->dp_size - 1, partp->dp_size,
487	    &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
488}
489
490static void
491change_part(int i)
492{
493struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i - 1;
494
495    printf("The data for partition %d is:\n", i);
496    print_part(i);
497
498    if (u_flag && ok("Do you want to change it?")) {
499	int tmp;
500
501	if (i_flag) {
502		bzero((char *)partp, sizeof (struct dos_partition));
503		if (i == 4) {
504			init_sector0(1);
505			printf("\nThe static data for the DOS partition 4 has been reinitialized to:\n");
506			print_part(i);
507		}
508	}
509
510	do {
511		Decimal("sysid (165=FreeBSD)", partp->dp_typ, tmp);
512		Decimal("start", partp->dp_start, tmp);
513		Decimal("size", partp->dp_size, tmp);
514
515		if (ok("Explicitly specify beg/end address ?"))
516		{
517			int	tsec,tcyl,thd;
518			tcyl = DPCYL(partp->dp_scyl,partp->dp_ssect);
519			thd = partp->dp_shd;
520			tsec = DPSECT(partp->dp_ssect);
521			Decimal("beginning cylinder", tcyl, tmp);
522			Decimal("beginning head", thd, tmp);
523			Decimal("beginning sector", tsec, tmp);
524			partp->dp_scyl = DOSCYL(tcyl);
525			partp->dp_ssect = DOSSECT(tsec,tcyl);
526			partp->dp_shd = thd;
527
528			tcyl = DPCYL(partp->dp_ecyl,partp->dp_esect);
529			thd = partp->dp_ehd;
530			tsec = DPSECT(partp->dp_esect);
531			Decimal("ending cylinder", tcyl, tmp);
532			Decimal("ending head", thd, tmp);
533			Decimal("ending sector", tsec, tmp);
534			partp->dp_ecyl = DOSCYL(tcyl);
535			partp->dp_esect = DOSSECT(tsec,tcyl);
536			partp->dp_ehd = thd;
537		} else {
538			dos(partp->dp_start, partp->dp_size,
539			    &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
540			dos(partp->dp_start + partp->dp_size - 1, partp->dp_size,
541			    &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
542		}
543
544		print_part(i);
545	} while (!ok("Are we happy with this entry?"));
546    }
547}
548
549static void
550print_params()
551{
552	printf("parameters extracted from in-core disklabel are:\n");
553	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
554			,cyls,heads,sectors,cylsecs);
555	if((dos_sectors > 63) || (dos_cyls > 1023) || (dos_heads > 255))
556		printf("Figures below won't work with BIOS for partitions not in cyl 1\n");
557	printf("parameters to be used for BIOS calculations are:\n");
558	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
559		,dos_cyls,dos_heads,dos_sectors,dos_cylsecs);
560}
561
562static void
563change_active(int which)
564{
565int i;
566int active = 4, tmp;
567struct dos_partition *partp = ((struct dos_partition *) &mboot.parts);
568
569	if (a_flag && which != -1)
570		active = which;
571	if (!ok("Do you want to change the active partition?"))
572		return;
573setactive:
574	active = 4;
575	do {
576		Decimal("active partition", active, tmp);
577		if (active < 1 || 4 < active) {
578			printf("Active partition number must be in range 1-4."
579					"  Try again.\n");
580			goto setactive;
581		}
582	} while (!ok("Are you happy with this choice"));
583	for (i = 0; i < NDOSPART; i++)
584		partp[i].dp_flag = 0;
585	if (active > 0 && active <= NDOSPART)
586		partp[active-1].dp_flag = ACTIVE;
587}
588
589void
590get_params_to_use()
591{
592	int	tmp;
593	print_params();
594	if (ok("Do you want to change our idea of what BIOS thinks ?"))
595	{
596		do
597		{
598			Decimal("BIOS's idea of #cylinders", dos_cyls, tmp);
599			Decimal("BIOS's idea of #heads", dos_heads, tmp);
600			Decimal("BIOS's idea of #sectors", dos_sectors, tmp);
601			dos_cylsecs = dos_heads * dos_sectors;
602			print_params();
603		}
604		while(!ok("Are you happy with this choice"));
605	}
606}
607
608
609/***********************************************\
610* Change real numbers into strange dos numbers	*
611\***********************************************/
612static void
613dos(sec, size, c, s, h)
614int sec, size;
615unsigned char *c, *s, *h;
616{
617int cy;
618int hd;
619
620	if (sec == 0 && size == 0) {
621		*s = *c = *h = 0;
622		return;
623	}
624
625	cy = sec / ( dos_cylsecs );
626	sec = sec - cy * ( dos_cylsecs );
627
628	hd = sec / dos_sectors;
629	sec = (sec - hd * dos_sectors) + 1;
630
631	*h = hd;
632	*c = cy & 0xff;
633	*s = (sec & 0x3f) | ( (cy & 0x300) >> 2);
634}
635
636int fd;
637
638	/* Getting device status */
639
640static int
641open_disk(int u_flag)
642{
643struct stat 	st;
644
645	if (stat(disk, &st) == -1) {
646		warnx("can't get file status of %s", disk);
647		return -1;
648	}
649	if ( !(st.st_mode & S_IFCHR) )
650		warnx("device %s is not character special", disk);
651	if ((fd = open(disk, a_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	flag = 1;
746#ifdef NOT_NOW
747	if (ioctl(fd, DIOCWLABEL, &flag) < 0)
748		warn("ioctl DIOCWLABEL");
749#endif
750	if (write_disk(0, (char *) mboot.bootinst) == -1) {
751		warnx("can't write fdisk partition table");
752		return -1;
753	flag = 0;
754#ifdef NOT_NOW
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