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