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