xref: /netbsd-src/sbin/fdisk/fdisk.c (revision c41a4eebefede43f6950f838a387dc18c6a431bf)
1 /*	$NetBSD: fdisk.c,v 1.22 1997/12/22 01:54:07 enami Exp $	*/
2 
3 /*
4  * Mach Operating System
5  * Copyright (c) 1992 Carnegie Mellon University
6  * All Rights Reserved.
7  *
8  * Permission to use, copy, modify and distribute this software and its
9  * documentation is hereby granted, provided that both the copyright
10  * notice and this permission notice appear in all copies of the
11  * software, derivative works or modified versions, and any portions
12  * thereof, and that both notices appear in supporting documentation.
13  *
14  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
15  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
16  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17  *
18  * Carnegie Mellon requests users of this software to return to
19  *
20  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
21  *  School of Computer Science
22  *  Carnegie Mellon University
23  *  Pittsburgh PA 15213-3890
24  *
25  * any improvements or extensions that they make and grant Carnegie Mellon
26  * the rights to redistribute these changes.
27  */
28 
29 #include <sys/cdefs.h>
30 
31 #ifndef lint
32 __RCSID("$NetBSD: fdisk.c,v 1.22 1997/12/22 01:54:07 enami Exp $");
33 #endif /* not lint */
34 
35 #include <sys/types.h>
36 #include <sys/disklabel.h>
37 #include <sys/ioctl.h>
38 #include <sys/param.h>
39 #include <sys/stat.h>
40 
41 #include <ctype.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <paths.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <util.h>
51 
52 #define LBUF 100
53 static char lbuf[LBUF];
54 
55 /*
56  * 14-Dec-89  Robert Baron (rvb) at Carnegie-Mellon University
57  *	Copyright (c) 1989	Robert. V. Baron
58  *	Created.
59  */
60 
61 char *disk = "/dev/rwd0d";
62 
63 struct disklabel disklabel;		/* disk parameters */
64 
65 int cylinders, sectors, heads, cylindersectors, disksectors;
66 
67 struct mboot {
68 	unsigned char padding[2]; /* force the longs to be long alligned */
69 	unsigned char bootinst[DOSPARTOFF];
70 	struct	dos_partition parts[4];
71 	unsigned short int	signature;
72 };
73 struct mboot mboot;
74 
75 #define ACTIVE 0x80
76 #define BOOT_MAGIC 0xAA55
77 
78 int dos_cylinders;
79 int dos_heads;
80 int dos_sectors;
81 int dos_cylindersectors;
82 
83 #define DOSSECT(s,c)	(((s) & 0x3f) | (((c) >> 2) & 0xc0))
84 #define DOSCYL(c)	((c) & 0xff)
85 int partition = -1;
86 
87 int a_flag;		/* set active partition */
88 int i_flag;		/* replace partition data */
89 int u_flag;		/* update partition data */
90 int sh_flag;		/* Output data as shell defines */
91 int f_flag;		/* force --not interactive */
92 int s_flag;		/* set id,offset,size */
93 int b_flag;		/* Set cyl, heads, secs (as c/h/s) */
94 int b_cyl, b_head, b_sec;  /* b_flag values. */
95 
96 unsigned char bootcode[] = {
97 0x33, 0xc0, 0xfa, 0x8e, 0xd0, 0xbc, 0x00, 0x7c, 0x8e, 0xc0, 0x8e, 0xd8, 0xfb, 0x8b, 0xf4, 0xbf,
98 0x00, 0x06, 0xb9, 0x00, 0x02, 0xfc, 0xf3, 0xa4, 0xea, 0x1d, 0x06, 0x00, 0x00, 0xb0, 0x04, 0xbe,
99 0xbe, 0x07, 0x80, 0x3c, 0x80, 0x74, 0x0c, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x75, 0xf4, 0xbe, 0xbd,
100 0x06, 0xeb, 0x43, 0x8b, 0xfe, 0x8b, 0x14, 0x8b, 0x4c, 0x02, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x74,
101 0x0a, 0x80, 0x3c, 0x80, 0x75, 0xf4, 0xbe, 0xbd, 0x06, 0xeb, 0x2b, 0xbd, 0x05, 0x00, 0xbb, 0x00,
102 0x7c, 0xb8, 0x01, 0x02, 0xcd, 0x13, 0x73, 0x0c, 0x33, 0xc0, 0xcd, 0x13, 0x4d, 0x75, 0xef, 0xbe,
103 0x9e, 0x06, 0xeb, 0x12, 0x81, 0x3e, 0xfe, 0x7d, 0x55, 0xaa, 0x75, 0x07, 0x8b, 0xf7, 0xea, 0x00,
104 0x7c, 0x00, 0x00, 0xbe, 0x85, 0x06, 0x2e, 0xac, 0x0a, 0xc0, 0x74, 0x06, 0xb4, 0x0e, 0xcd, 0x10,
105 0xeb, 0xf4, 0xfb, 0xeb, 0xfe,
106 'M', 'i', 's', 's', 'i', 'n', 'g', ' ',
107 	'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
108 'E', 'r', 'r', 'o', 'r', ' ', 'l', 'o', 'a', 'd', 'i', 'n', 'g', ' ',
109 	'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
110 'I', 'n', 'v', 'a', 'l', 'i', 'd', ' ',
111 	'p', 'a', 'r', 't', 'i', 't', 'i', 'o', 'n', ' ', 't', 'a', 'b', 'l', 'e', 0,
112 'A', 'u', 't', 'h', 'o', 'r', ' ', '-', ' ',
113 	'S', 'i', 'e', 'g', 'm', 'a', 'r', ' ', 'S', 'c', 'h', 'm', 'i', 'd', 't', 0,0,0,
114 
115   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
116   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
117   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
118   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
119   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
120   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
121   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
122   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
123   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
124   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
125   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
126   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
127   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
128 };
129 
130 struct part_type {
131 	int type;
132 	char *name;
133 } part_types[] = {
134 	{0x00, "unused"},
135 	{0x01, "Primary DOS with 12 bit FAT"},
136 	{0x02, "XENIX / filesystem"},
137 	{0x03, "XENIX /usr filesystem"},
138 	{0x04, "Primary DOS with 16 bit FAT <32M"},
139 	{0x05, "Extended DOS"},
140 	{0x06, "Primary 'big' DOS, 16-bit FAT (> 32MB)"},
141 	{0x07, "OS/2 HPFS or NTFS or QNX2 or Advanced UNIX"},
142 	{0x08, "AIX filesystem"},
143 	{0x09, "AIX boot partition or Coherent"},
144 	{0x0A, "OS/2 Boot Manager or Coherent swap or OPUS"},
145 	{0x0E, "DOS (16-bit FAT), CHS-mapped"},
146 	{0x0F, "Ext. partition, CHS-mapped"},
147 	{0x10, "OPUS"},
148 	{0x11, "OS/2 BM: hidden DOS 12-bit FAT"},
149 	{0x12, "Compaq diagnostics"},
150 	{0x14, "OS/2 BM: hidden DOS 16-bit FAT <32M"},
151 	{0x16, "OS/2 BM: hidden DOS 16-bit FAT >=32M"},
152 	{0x17, "OS/2 BM: hidden IFS"},
153 	{0x18, "AST Windows swapfile"},
154 	{0x24, "NEC DOS"},
155 	{0x3C, "PartitionMagic recovery"},
156 	{0x40, "VENIX 286"},
157 	{0x41, "Linux/MINIX (sharing disk with DRDOS)"},
158 	{0x42, "SFS or Linux swap (sharing disk with DRDOS)"},
159 	{0x43, "Linux native (sharing disk with DRDOS)"},
160 	{0x50, "DM (disk manager)"},
161 	{0x51, "DM6 Aux1 (or Novell)"},
162 	{0x52, "CP/M or Microport SysV/AT"},
163 	{0x53, "DM6 Aux3"},
164 	{0x54, "DM6"},
165 	{0x55, "EZ-Drive (disk manager)"},
166 	{0x56, "Golden Bow (disk manager)"},
167 	{0x5C, "Priam Edisk (disk manager)"},
168 	{0x61, "SpeedStor"},
169 	{0x63, "GNU HURD or Mach or Sys V/386 (such as ISC UNIX)"},
170 	{0x64, "Novell Netware 2.xx"},
171 	{0x65, "Novell Netware 3.xx"},
172 	{0x70, "DiskSecure Multi-Boot"},
173 	{0x75, "PC/IX"},
174 	{0x77, "QNX4.x"},
175 	{0x78, "QNX4.x 2nd part"},
176 	{0x79, "QNX4.x 3rd part"},
177 	{0x80, "MINIX until 1.4a"},
178 	{0x81, "MINIX since 1.4b, early Linux, Mitac dmgr"},
179 	{0x82, "Linux swap"},
180 	{0x83, "Linux native"},
181 	{0x84, "OS/2 hidden C: drive"},
182 	{0x85, "Linux extended"},
183 	{0x86, "NTFS volume set??"},
184 	{0x87, "NTFS volume set??"},
185 	{0x93, "Amoeba filesystem"},
186 	{0x94, "Amoeba bad block table"},
187 	{0xA0, "IBM Thinkpad hibernation"},
188 	{0xA5, "NetBSD or FreeBSD or 386BSD"},
189 	{0xA6, "OpenBSD"},
190 	{0xA7, "NeXTSTEP 486"},
191 	{0xB7, "BSDI BSD/386 filesystem"},
192 	{0xB8, "BSDI BSD/386 swap"},
193 	{0xC1, "DRDOS/sec (FAT-12)"},
194 	{0xC4, "DRDOS/sec (FAT-16, < 32M)"},
195 	{0xC6, "DRDOS/sec (FAT-16, >= 32M)"},
196 	{0xC7, "Syrinx"},
197 	{0xDB, "CP/M or Concurrent CP/M or Concurrent DOS or CTOS"},
198 	{0xE1, "DOS access or SpeedStor 12-bit FAT extended partition"},
199 	{0xE3, "DOS R/O or SpeedStor"},
200 	{0xE4, "SpeedStor 16-bit FAT extended partition < 1024 cyl."},
201 	{0xF1, "SpeedStor"},
202 	{0xF2, "DOS 3.3+ Secondary"},
203 	{0xF4, "SpeedStor large partition"},
204 	{0xFE, "SpeedStor >1024 cyl. or LANstep"},
205 	{0xFF, "Xenix Bad Block Table"},
206 };
207 
208 void	usage __P((void));
209 void	print_s0 __P((int));
210 void	print_part __P((int));
211 void	init_sector0 __P((int));
212 void	intuit_translated_geometry __P((void));
213 int	try_heads __P((quad_t, quad_t, quad_t, quad_t, quad_t, quad_t, quad_t,
214 		       quad_t));
215 int	try_sectors __P((quad_t, quad_t, quad_t, quad_t, quad_t));
216 void	change_part __P((int, int, int, int));
217 void	print_params __P((void));
218 void	change_active __P((int));
219 void	get_params_to_use __P((void));
220 void	dos __P((int, unsigned char *, unsigned char *, unsigned char *));
221 int	open_disk __P((int));
222 int	read_disk __P((int, void *));
223 int	write_disk __P((int, void *));
224 int	get_params __P((void));
225 int	read_s0 __P((void));
226 int	write_s0 __P((void));
227 int	yesno __P((char *));
228 void	decimal __P((char *, int *));
229 int	type_match __P((const void *, const void *));
230 char	*get_type __P((int));
231 int	get_mapping __P((int, int *, int *, int *, long *));
232 
233 static inline unsigned short getshort __P((void *));
234 static inline void putshort __P((void *p, unsigned short));
235 static inline unsigned long getlong __P((void *));
236 static inline void putlong __P((void *,	unsigned long));
237 
238 
239 int	main __P((int, char **));
240 
241 int
242 main(argc, argv)
243 	int argc;
244 	char *argv[];
245 {
246 	int ch;
247 	int part;
248 
249 	int csysid, cstart, csize;	/* For the b_flag. */
250 
251 	a_flag = i_flag = u_flag = sh_flag = f_flag = s_flag = b_flag = 0;
252 	csysid = cstart = csize = 0;
253 	while ((ch = getopt(argc, argv, "0123Safius:b:")) != -1)
254 		switch (ch) {
255 		case '0':
256 			partition = 0;
257 			break;
258 		case '1':
259 			partition = 1;
260 			break;
261 		case '2':
262 			partition = 2;
263 			break;
264 		case '3':
265 			partition = 3;
266 			break;
267 		case 'S':
268 			sh_flag = 1;
269 			break;
270 		case 'a':
271 			a_flag = 1;
272 			break;
273 		case 'f':
274 			f_flag = 1;
275 			break;
276 		case 'i':
277 			i_flag = 1;
278 			break;
279 		case 'u':
280 			u_flag = 1;
281 			break;
282 		case 's':
283 			s_flag = 1;
284 			if (sscanf (optarg, "%d/%d/%d",
285 				    &csysid, &cstart, &csize) != 3) {
286 				(void)fprintf (stderr, "%s: Bad argument "
287 					       "to the -s flag.\n",
288 					       argv[0]);
289 				exit (1);
290 			}
291 			break;
292 		case 'b':
293 			b_flag = 1;
294 			if (sscanf (optarg, "%d/%d/%d",
295 				    &b_cyl, &b_head, &b_sec) != 3) {
296 				(void)fprintf (stderr, "%s: Bad argument "
297 					       "to the -b flag.\n",
298 					       argv[0]);
299 				exit (1);
300 			}
301 			break;
302 		default:
303 			usage();
304 		}
305 	argc -= optind;
306 	argv += optind;
307 
308 	if (sh_flag && (a_flag || i_flag || u_flag || f_flag || s_flag))
309 		usage();
310 
311 	if (partition == -1 && s_flag) {
312 		(void) fprintf (stderr,
313 				"-s flag requires a partition selected.\n");
314 		usage();
315 	}
316 
317 	if (argc > 0)
318 		disk = argv[0];
319 
320 	if (open_disk(a_flag || i_flag || u_flag) < 0)
321 		exit(1);
322 
323 	if (read_s0())
324 		init_sector0(sectors > 63 ? 63 : sectors);
325 
326 	intuit_translated_geometry();
327 
328 	if (!sh_flag && !f_flag)
329 		printf("******* Working on device %s *******\n", disk);
330 
331 
332 	if ((i_flag || u_flag) && (!f_flag || b_flag))
333 		get_params_to_use();
334 
335 	if (i_flag)
336 		init_sector0(dos_sectors > 63 ? 63 : dos_sectors);
337 
338 	if (!sh_flag && !f_flag)
339 		printf("Warning: BIOS sector numbering starts with sector 1\n");
340 
341 	/* Do the update stuff! */
342 	if (u_flag) {
343 		if (!f_flag)
344 			printf("Information from DOS bootblock is:\n");
345 		if (partition == -1)
346 			for (part = 0; part < NDOSPART; part++)
347 				change_part(part,-1, -1, -1);
348 		else
349 			change_part(partition, csysid, cstart, csize);
350 	} else
351 		if (!i_flag)
352 			print_s0(partition);
353 
354 	if (a_flag)
355 		change_active(partition);
356 
357 	if (u_flag || a_flag || i_flag) {
358 		if (!f_flag) {
359 			printf("\nWe haven't changed the partition table "
360 			       "yet.  This is your last chance.\n");
361 			print_s0(-1);
362 			if (yesno("Should we write new partition table?"))
363 				write_s0();
364 		} else
365 			write_s0();
366 	}
367 
368 	exit(0);
369 }
370 
371 void
372 usage()
373 {
374 	(void)fprintf(stderr, "usage: fdisk [-aiufS] [-0|-1|-2|-3] "
375 		      "[device]\n");
376 	exit(1);
377 }
378 
379 void
380 print_s0(which)
381 	int which;
382 {
383 	int part;
384 
385 	print_params();
386 	if (!sh_flag)
387 		printf("Information from DOS bootblock is:\n");
388 	if (which == -1) {
389 		for (part = 0; part < NDOSPART; part++) {
390 			if (!sh_flag)
391 				printf("%d: ", part);
392 			print_part(part);
393 		}
394 	} else
395 		print_part(which);
396 }
397 
398 static struct dos_partition mtpart = { 0 };
399 
400 static inline unsigned short
401 getshort(p)
402 	void *p;
403 {
404 	unsigned char *cp = p;
405 
406 	return cp[0] | (cp[1] << 8);
407 }
408 
409 static inline void
410 putshort(p, l)
411 	void *p;
412 	unsigned short l;
413 {
414 	unsigned char *cp = p;
415 
416 	*cp++ = l;
417 	*cp++ = l >> 8;
418 }
419 
420 static inline unsigned long
421 getlong(p)
422 	void *p;
423 {
424 	unsigned char *cp = p;
425 
426 	return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24);
427 }
428 
429 static inline void
430 putlong(p, l)
431 	void *p;
432 	unsigned long l;
433 {
434 	unsigned char *cp = p;
435 
436 	*cp++ = l;
437 	*cp++ = l >> 8;
438 	*cp++ = l >> 16;
439 	*cp++ = l >> 24;
440 }
441 
442 void
443 print_part(part)
444 	int part;
445 {
446 	struct dos_partition *partp;
447 	int empty;
448 
449 	partp = &mboot.parts[part];
450 	empty = !memcmp(partp, &mtpart, sizeof(struct dos_partition));
451 
452 	if (sh_flag) {
453 		if (empty) {
454 			printf("PART%dSIZE=0\n", part);
455 			return;
456 		}
457 
458 		printf("PART%dID=%d\n", part, partp->dp_typ);
459 		printf("PART%dSIZE=%ld\n", part, getlong(&partp->dp_size));
460 		printf("PART%dSTART=%ld\n", part, getlong(&partp->dp_start));
461 		printf("PART%dFLAG=0x%x\n", part, partp->dp_flag);
462 		printf("PART%dBCYL=%d\n", part, DPCYL(partp->dp_scyl,
463 						      partp->dp_ssect));
464 		printf("PART%dBHEAD=%d\n", part, partp->dp_shd);
465 		printf("PART%dBSEC=%d\n", part, DPSECT(partp->dp_ssect));
466 		printf("PART%dECYL=%d\n", part, DPCYL(partp->dp_ecyl,
467 						      partp->dp_esect));
468 		printf("PART%dEHEAD=%d\n", part, partp->dp_ehd);
469 		printf("PART%dESEC=%d\n", part, DPSECT(partp->dp_esect));
470 		return;
471 	}
472 
473 	/* Not sh_flag. */
474 	if (empty) {
475 		printf("<UNUSED>\n");
476 		return;
477 	}
478 	printf("sysid %d (%s)\n", partp->dp_typ, get_type(partp->dp_typ));
479 	printf("    start %ld, size %ld (%ld MB), flag 0x%x\n",
480 	    getlong(&partp->dp_start), getlong(&partp->dp_size),
481 	    getlong(&partp->dp_size) * 512 / (1024 * 1024), partp->dp_flag);
482 	printf("\tbeg: cylinder %4d, head %3d, sector %2d\n",
483 	    DPCYL(partp->dp_scyl, partp->dp_ssect),
484 	    partp->dp_shd, DPSECT(partp->dp_ssect));
485 	printf("\tend: cylinder %4d, head %3d, sector %2d\n",
486 	    DPCYL(partp->dp_ecyl, partp->dp_esect),
487 	    partp->dp_ehd, DPSECT(partp->dp_esect));
488 }
489 
490 void
491 init_sector0(start)
492 	int start;
493 {
494 	int i;
495 	struct dos_partition *partp;
496 
497 	int dos_disksectors = dos_cylinders * dos_heads * dos_sectors;
498 
499 	memcpy(mboot.bootinst, bootcode, sizeof(bootcode));
500 	putshort(&mboot.signature, BOOT_MAGIC);
501 
502 	for (i=0; i<3; i++)
503 		memset (&mboot.parts[i], 0, sizeof(struct dos_partition));
504 
505 	partp = &mboot.parts[3];
506 	partp->dp_typ = DOSPTYP_386BSD;
507 	partp->dp_flag = ACTIVE;
508 	putlong(&partp->dp_start, start);
509 	putlong(&partp->dp_size, dos_disksectors - start);
510 
511 	dos(getlong(&partp->dp_start),
512 	    &partp->dp_scyl, &partp->dp_shd, &partp->dp_ssect);
513 	dos(getlong(&partp->dp_start) + getlong(&partp->dp_size) - 1,
514 	    &partp->dp_ecyl, &partp->dp_ehd, &partp->dp_esect);
515 
516 	printf ("DOS partition table initialized.\n");
517 }
518 
519 /* Prerequisite: the disklabel parameters and master boot record must
520  *		 have been read (i.e. dos_* and mboot are meaningful).
521  * Specification: modifies dos_cylinders, dos_heads, dos_sectors, and
522  *		  dos_cylindersectors to be consistent with what the
523  *		  partition table is using, if we can find a geometry
524  *		  which is consistent with all partition table entries.
525  *		  We may get the number of cylinders slightly wrong (in
526  *		  the conservative direction).  The idea is to be able
527  *		  to create a NetBSD partition on a disk we don't know
528  *		  the translated geometry of.
529  * This whole routine should be replaced with a kernel interface to get
530  * the BIOS geometry (which in turn requires modifications to the i386
531  * boot loader to pass in the BIOS geometry for each disk). */
532 void
533 intuit_translated_geometry()
534 {
535 
536 	int cylinders = -1, heads = -1, sectors = -1, i, j;
537 	int c1, h1, s1, c2, h2, s2;
538 	long a1, a2;
539 	quad_t num, denom;
540 
541 	/* Try to deduce the number of heads from two different mappings. */
542 	for (i = 0; i < NDOSPART * 2; i++) {
543 		if (get_mapping(i, &c1, &h1, &s1, &a1) < 0)
544 			continue;
545 		for (j = 0; j < 8; j++) {
546 			if (get_mapping(j, &c2, &h2, &s2, &a2) < 0)
547 				continue;
548 			num = (quad_t)h1*(a2-s2) - (quad_t)h2*(a1-s1);
549 			denom = (quad_t)c2*(a1-s1) - (quad_t)c1*(a2-s2);
550 			if (denom != 0 && num % denom == 0) {
551 				heads = num / denom;
552 				break;
553 			}
554 		}
555 		if (heads != -1)
556 			break;
557 	}
558 
559 	if (heads == -1)
560 		return;
561 
562 	/* Now figure out the number of sectors from a single mapping. */
563 	for (i = 0; i < NDOSPART * 2; i++) {
564 		if (get_mapping(i, &c1, &h1, &s1, &a1) < 0)
565 			continue;
566 		num = a1 - s1;
567 		denom = c1 * heads + h1;
568 		if (denom != 0 && num % denom == 0) {
569 			sectors = num / denom;
570 			break;
571 		}
572 	}
573 
574 	if (sectors == -1)
575 		return;
576 
577 	/* Estimate the number of cylinders. */
578 	cylinders = dos_cylinders * dos_cylindersectors / heads / sectors;
579 
580 	/* Now verify consistency with each of the partition table entries.
581 	 * Be willing to shove cylinders up a little bit to make things work,
582 	 * but translation mismatches are fatal. */
583 	for (i = 0; i < NDOSPART * 2; i++) {
584 		if (get_mapping(i, &c1, &h1, &s1, &a1) < 0)
585 			continue;
586 		if (sectors * (c1 * heads + h1) + s1 != a1)
587 			return;
588 		if (c1 >= cylinders)
589 			cylinders = c1 + 1;
590 	}
591 
592 	/* Everything checks out.  Reset the geometry to use for further
593 	 * calculations. */
594 	dos_cylinders = cylinders;
595 	dos_heads = heads;
596 	dos_sectors = sectors;
597 	dos_cylindersectors = heads * sectors;
598 }
599 
600 /* For the purposes of intuit_translated_geometry(), treat the partition
601  * table as a list of eight mapping between (cylinder, head, sector)
602  * triplets and absolute sectors.  Get the relevant geometry triplet and
603  * absolute sectors for a given entry, or return -1 if it isn't present.
604  * Note: for simplicity, the returned sector is 0-based. */
605 int
606 get_mapping(i, cylinder, head, sector, absolute)
607 	int i, *cylinder, *head, *sector;
608 	long *absolute;
609 {
610 	struct dos_partition *part = &mboot.parts[i / 2];
611 
612 	if (part->dp_typ == 0)
613 		return -1;
614 	if (i % 2 == 0) {
615 		*cylinder = DPCYL(part->dp_scyl, part->dp_ssect);
616 		*head = part->dp_shd;
617 		*sector = DPSECT(part->dp_ssect) - 1;
618 		*absolute = getlong(&part->dp_start);
619 	} else {
620 		*cylinder = DPCYL(part->dp_ecyl, part->dp_esect);
621 		*head = part->dp_ehd;
622 		*sector = DPSECT(part->dp_esect) - 1;
623 		*absolute = getlong(&part->dp_start)
624 		    + getlong(&part->dp_size) - 1;
625 	}
626 	return 0;
627 }
628 
629 void
630 change_part(part, csysid, cstart, csize)
631 	int part, csysid, cstart, csize;
632 {
633 	struct dos_partition *partp;
634 
635 	partp = &mboot.parts[part];
636 
637 	if (s_flag) {
638 		partp->dp_typ = csysid;
639 		putlong(&partp->dp_start, cstart);
640 		putlong(&partp->dp_size, csize);
641 		dos(getlong(&partp->dp_start),
642 		    &partp->dp_scyl, &partp->dp_shd, &partp->dp_ssect);
643 		dos(getlong(&partp->dp_start)
644 		    + getlong(&partp->dp_size) - 1,
645 		    &partp->dp_ecyl, &partp->dp_ehd, &partp->dp_esect);
646 		if (f_flag)
647 			return;
648 	}
649 
650 	printf("The data for partition %d is:\n", part);
651 	print_part(part);
652 	if (!u_flag || !yesno("Do you want to change it?"))
653 		return;
654 
655 	do {
656 		{
657 			int sysid, start, size;
658 
659 			sysid = partp->dp_typ,
660 			start = getlong(&partp->dp_start),
661 			size = getlong(&partp->dp_size);
662 			decimal("sysid", &sysid);
663 			decimal("start", &start);
664 			decimal("size", &size);
665 			partp->dp_typ = sysid;
666 			putlong(&partp->dp_start, start);
667 			putlong(&partp->dp_size, size);
668 		}
669 
670 		if (yesno("Explicitly specify beg/end address?")) {
671 			int tsector, tcylinder, thead;
672 
673 			tcylinder = DPCYL(partp->dp_scyl, partp->dp_ssect);
674 			thead = partp->dp_shd;
675 			tsector = DPSECT(partp->dp_ssect);
676 			decimal("beginning cylinder", &tcylinder);
677 			decimal("beginning head", &thead);
678 			decimal("beginning sector", &tsector);
679 			partp->dp_scyl = DOSCYL(tcylinder);
680 			partp->dp_shd = thead;
681 			partp->dp_ssect = DOSSECT(tsector, tcylinder);
682 
683 			tcylinder = DPCYL(partp->dp_ecyl, partp->dp_esect);
684 			thead = partp->dp_ehd;
685 			tsector = DPSECT(partp->dp_esect);
686 			decimal("ending cylinder", &tcylinder);
687 			decimal("ending head", &thead);
688 			decimal("ending sector", &tsector);
689 			partp->dp_ecyl = DOSCYL(tcylinder);
690 			partp->dp_ehd = thead;
691 			partp->dp_esect = DOSSECT(tsector, tcylinder);
692 		} else {
693 			dos(getlong(&partp->dp_start),
694 			    &partp->dp_scyl, &partp->dp_shd, &partp->dp_ssect);
695 			dos(getlong(&partp->dp_start)
696 			    + getlong(&partp->dp_size) - 1,
697 			    &partp->dp_ecyl, &partp->dp_ehd, &partp->dp_esect);
698 		}
699 
700 		print_part(part);
701 	} while (!yesno("Is this entry okay?"));
702 }
703 
704 void
705 print_params()
706 {
707 
708 	if (sh_flag) {
709 		printf ("DLCYL=%d\nDLHEAD=%d\nDLSEC=%d\n",
710 			cylinders, heads, sectors);
711 		printf ("BCYL=%d\nBHEAD=%d\nBSEC=%d\n",
712 			dos_cylinders, dos_heads, dos_sectors);
713 		return;
714 	}
715 
716 	/* Not sh_flag */
717 	printf("parameters extracted from in-core disklabel are:\n");
718 	printf("cylinders=%d heads=%d sectors/track=%d (%d sectors/cylinder)\n\n",
719 	    cylinders, heads, sectors, cylindersectors);
720 	if (dos_sectors > 63 || dos_cylinders > 1023 || dos_heads > 255)
721 		printf("Figures below won't work with BIOS for partitions not in cylinder 1\n");
722 	printf("parameters to be used for BIOS calculations are:\n");
723 	printf("cylinders=%d heads=%d sectors/track=%d (%d sectors/cylinder)\n\n",
724 	    dos_cylinders, dos_heads, dos_sectors, dos_cylindersectors);
725 }
726 
727 void
728 change_active(which)
729 	int which;
730 {
731 	struct dos_partition *partp;
732 	int part;
733 	int active = 4;
734 
735 	partp = &mboot.parts[0];
736 
737 	if (a_flag && which != -1)
738 		active = which;
739 	else {
740 		for (part = 0; part < NDOSPART; part++)
741 			if (partp[part].dp_flag & ACTIVE)
742 				active = part;
743 	}
744 	if (!f_flag) {
745 		if (yesno("Do you want to change the active partition?")) {
746 			printf ("Choosing 4 will make no partition active.\n");
747 			do {
748 				decimal("active partition", &active);
749 			} while (!yesno("Are you happy with this choice?"));
750 		} else
751 			return;
752 	} else
753 		if (active != 4)
754 			printf ("Making partition %d active.\n", active);
755 
756 	for (part = 0; part < NDOSPART; part++)
757 		partp[part].dp_flag &= ~ACTIVE;
758 	if (active < 4)
759 		partp[active].dp_flag |= ACTIVE;
760 }
761 
762 void
763 get_params_to_use()
764 {
765 	if (b_flag) {
766 		dos_cylinders = b_cyl;
767 		dos_heads = b_head;
768 		dos_sectors = b_sec;
769 		dos_cylindersectors = dos_heads * dos_sectors;
770 		return;
771 	}
772 
773 	print_params();
774 	if (yesno("Do you want to change our idea of what BIOS thinks?")) {
775 		do {
776 			decimal("BIOS's idea of #cylinders", &dos_cylinders);
777 			decimal("BIOS's idea of #heads", &dos_heads);
778 			decimal("BIOS's idea of #sectors", &dos_sectors);
779 			dos_cylindersectors = dos_heads * dos_sectors;
780 			print_params();
781 		} while (!yesno("Are you happy with this choice?"));
782 	}
783 }
784 
785 /***********************************************\
786 * Change real numbers into strange dos numbers	*
787 \***********************************************/
788 void
789 dos(sector, cylinderp, headp, sectorp)
790 	int sector;
791 	unsigned char *cylinderp, *headp, *sectorp;
792 {
793 	int cylinder, head;
794 
795 	cylinder = sector / dos_cylindersectors;
796 	sector -= cylinder * dos_cylindersectors;
797 
798 	head = sector / dos_sectors;
799 	sector -= head * dos_sectors;
800 
801 	*cylinderp = DOSCYL(cylinder);
802 	*headp = head;
803 	*sectorp = DOSSECT(sector + 1, cylinder);
804 }
805 
806 int fd;
807 
808 int
809 open_disk(u_flag)
810 	int u_flag;
811 {
812 	static char namebuf[MAXPATHLEN + 1];
813 	struct stat st;
814 
815 	fd = opendisk(disk, u_flag ? O_RDWR : O_RDONLY, namebuf,
816 	    sizeof(namebuf), 0);
817 	if (fd < 0) {
818 		warn("%s", namebuf);
819 		return (-1);
820 	}
821 	disk = namebuf;
822 	if (fstat(fd, &st) == -1) {
823 		close(fd);
824 		warn("%s", disk);
825 		return (-1);
826 	}
827 	if (!S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode)) {
828 		close(fd);
829 		warnx("%s is not a character device or regular file", disk);
830 		return (-1);
831 	}
832 	if (get_params() == -1) {
833 		close(fd);
834 		return (-1);
835 	}
836 	return (0);
837 }
838 
839 int
840 read_disk(sector, buf)
841 	int sector;
842 	void *buf;
843 {
844 
845 	if (lseek(fd, (off_t)(sector * 512), 0) == -1)
846 		return (-1);
847 	return (read(fd, buf, 512));
848 }
849 
850 int
851 write_disk(sector, buf)
852 	int sector;
853 	void *buf;
854 {
855 
856 	if (lseek(fd, (off_t)(sector * 512), 0) == -1)
857 		return (-1);
858 	return (write(fd, buf, 512));
859 }
860 
861 int
862 get_params()
863 {
864 
865 	if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) {
866 		warn("DIOCGDINFO");
867 		return (-1);
868 	}
869 
870 	dos_cylinders = cylinders = disklabel.d_ncylinders;
871 	dos_heads = heads = disklabel.d_ntracks;
872 	dos_sectors = sectors = disklabel.d_nsectors;
873 	dos_cylindersectors = cylindersectors = heads * sectors;
874 	disksectors = cylinders * heads * sectors;
875 
876 	return (0);
877 }
878 
879 int
880 read_s0()
881 {
882 
883 	if (read_disk(0, mboot.bootinst) == -1) {
884 		warn("can't read fdisk partition table");
885 		return (-1);
886 	}
887 	if (getshort(&mboot.signature) != BOOT_MAGIC) {
888 		warnx("invalid fdisk partition table found");
889 		/* So should we initialize things? */
890 		return (-1);
891 	}
892 	return (0);
893 }
894 
895 int
896 write_s0()
897 {
898 	int flag;
899 
900 	/*
901 	 * write enable label sector before write (if necessary),
902 	 * disable after writing.
903 	 * needed if the disklabel protected area also protects
904 	 * sector 0. (e.g. empty disk)
905 	 */
906 	flag = 1;
907 	if (ioctl(fd, DIOCWLABEL, &flag) < 0)
908 		warn("DIOCWLABEL");
909 	if (write_disk(0, mboot.bootinst) == -1) {
910 		warn("can't write fdisk partition table");
911 		return -1;
912 	}
913 	flag = 0;
914 	if (ioctl(fd, DIOCWLABEL, &flag) < 0)
915 		warn("DIOCWLABEL");
916 	return 0;
917 }
918 
919 int
920 yesno(str)
921 	char *str;
922 {
923 	int ch, first;
924 
925 	printf("%s [n] ", str);
926 
927 	first = ch = getchar();
928 	while (ch != '\n' && ch != EOF)
929 		ch = getchar();
930 	return (first == 'y' || first == 'Y');
931 }
932 
933 void
934 decimal(str, num)
935 	char *str;
936 	int *num;
937 {
938 	int acc = 0;
939 	char *cp;
940 
941 	for (;; printf("%s is not a valid decimal number.\n", lbuf)) {
942 		printf("Supply a decimal value for \"%s\" [%d] ", str, *num);
943 
944 		fgets(lbuf, LBUF, stdin);
945 		lbuf[strlen(lbuf)-1] = '\0';
946 		cp = lbuf;
947 
948 		cp += strspn(cp, " \t");
949 		if (*cp == '\0')
950 			return;
951 
952 		if (!isdigit(*cp))
953 			continue;
954 		acc = strtol(lbuf, &cp, 10);
955 
956 		cp += strspn(cp, " \t");
957 		if (*cp != '\0')
958 			continue;
959 
960 		*num = acc;
961 		return;
962 	}
963 
964 }
965 
966 int
967 type_match(key, item)
968 	const void *key, *item;
969 {
970 	const int *typep = key;
971 	const struct part_type *ptr = item;
972 
973 	if (*typep < ptr->type)
974 		return (-1);
975 	if (*typep > ptr->type)
976 		return (1);
977 	return (0);
978 }
979 
980 char *
981 get_type(type)
982 	int type;
983 {
984 	struct part_type *ptr;
985 
986 	ptr = bsearch(&type, part_types,
987 	    sizeof(part_types) / sizeof(struct part_type),
988 	    sizeof(struct part_type), type_match);
989 	if (ptr == 0)
990 		return ("unknown");
991 	return (ptr->name);
992 }
993