xref: /netbsd-src/usr.sbin/sysinst/mbr.c (revision f3cfa6f6ce31685c6c4a758bc430e69eb99f50a4)
1 /*	$NetBSD: mbr.c,v 1.10 2018/11/27 17:13:41 martin Exp $ */
2 
3 /*
4  * Copyright 1997 Piermont Information Systems Inc.
5  * All rights reserved.
6  *
7  * Written by Philip A. Nelson for Piermont Information Systems Inc.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The name of Piermont Information Systems Inc. may not be used to endorse
18  *    or promote products derived from this software without specific prior
19  *    written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31  * THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34 
35 /*
36  * Following applies to the geometry guessing code
37  */
38 
39 /*
40  * Mach Operating System
41  * Copyright (c) 1992 Carnegie Mellon University
42  * All Rights Reserved.
43  *
44  * Permission to use, copy, modify and distribute this software and its
45  * documentation is hereby granted, provided that both the copyright
46  * notice and this permission notice appear in all copies of the
47  * software, derivative works or modified versions, and any portions
48  * thereof, and that both notices appear in supporting documentation.
49  *
50  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
51  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
52  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
53  *
54  * Carnegie Mellon requests users of this software to return to
55  *
56  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
57  *  School of Computer Science
58  *  Carnegie Mellon University
59  *  Pittsburgh PA 15213-3890
60  *
61  * any improvements or extensions that they make and grant Carnegie Mellon
62  * the rights to redistribute these changes.
63  */
64 
65 /* mbr.c -- DOS Master Boot Record editing code */
66 
67 #include <sys/param.h>
68 #include <sys/types.h>
69 #include <stdio.h>
70 #include <unistd.h>
71 #include <fcntl.h>
72 #include <util.h>
73 #include "defs.h"
74 #include "mbr.h"
75 #include "md.h"
76 #include "msg_defs.h"
77 #include "menu_defs.h"
78 #include "defsizes.h"
79 #include "endian.h"
80 
81 #define NO_BOOTMENU (-0x100)
82 
83 #define MAXCYL		1023    /* Possibly 1024 */
84 #define MAXHEAD		255     /* Possibly 256 */
85 #define MAXSECTOR	63
86 
87 struct part_id {
88 	int id;
89 	const char *name;
90 } part_ids[] = {
91 	{0,			"unused"},
92 	{MBR_PTYPE_NETBSD,	"NetBSD"},
93 	{MBR_PTYPE_EXT_LBA,	"Extended partition, LBA"},
94 	{MBR_PTYPE_386BSD,	"FreeBSD/386BSD"},
95 	{MBR_PTYPE_OPENBSD,	"OpenBSD"},
96 	{MBR_PTYPE_LNXEXT2,	"Linux native"},
97 	{MBR_PTYPE_LNXSWAP,	"Linux swap"},
98 	{MBR_PTYPE_FAT12,	"DOS FAT12"},
99 	{MBR_PTYPE_FAT16S,	"DOS FAT16, <32M"},
100 	{MBR_PTYPE_FAT16B,	"DOS FAT16, >32M"},
101 	{MBR_PTYPE_FAT16L,	"Windows FAT16, LBA"},
102 	{MBR_PTYPE_FAT32,	"Windows FAT32"},
103 	{MBR_PTYPE_FAT32L,	"Windows FAT32, LBA"},
104 	{MBR_PTYPE_NTFSVOL,	"NTFS volume set"},
105 	{MBR_PTYPE_NTFS,	"NTFS"},
106 	{MBR_PTYPE_PREP,	"PReP Boot"},
107 #ifdef MBR_PTYPE_SOLARIS
108 	{MBR_PTYPE_SOLARIS,	"Solaris"},
109 #endif
110 	{-1,			"Unknown"},
111 };
112 
113 static int get_mapping(struct mbr_partition *, int, int *, int *, int *,
114 			    daddr_t *);
115 static void convert_mbr_chs(int, int, int, uint8_t *, uint8_t *,
116 				 uint8_t *, uint32_t);
117 static void get_ptn_alignment(struct mbr_partition *);
118 
119 static unsigned int ptn_alignment;
120 static unsigned int ptn_0_offset;
121 
122 /*
123  * Notes on the extended partition editor.
124  *
125  * The extended partition structure is actually a singly linked list.
126  * Each of the 'mbr' sectors can only contain 2 items, the first describes
127  * a user partition (relative to that mbr sector), the second describes
128  * the following partition (relative to the start of the extended partition).
129  *
130  * The 'start' sector for the user partition is always the size of one
131  * track - very often 63.  The extended partitions themselves should
132  * always start on a cylinder boundary using the BIOS geometry - often
133  * 16065 sectors per cylinder.
134  *
135  * The disk is also always described in increasing sector order.
136  *
137  * During editing we keep the mbr sectors accurate (it might have been
138  * easier to use absolute sector numbers though), and keep a copy of the
139  * entire sector - to preserve any information any other OS has tried
140  * to squirrel away in the (apparently) unused space.
141  *
142  * For simplicity we add entries for unused space.  These should not
143  * get written to the disk.
144  *
145  * Typical disk (with some small numbers):
146  *
147  *      0 -> a       63       37	dos
148  *           b      100     1000	extended LBA (type 15)
149  *
150  *    100 -> a       63       37        user
151  *           b      100      200	extended partiton (type 5)
152  *
153  *    200 -> a       63       37        user
154  *           b      200      300	extended partiton (type 5)
155  *
156  *    300 -> a       63       37	user
157  *           b        0        0        0 (end of chain)
158  *
159  * If there is a gap, the 'b' partition will start beyond the area
160  * described by the 'a' partition.
161  *
162  * While writing this comment, I can't remember what happens is there
163  * is space at the start of the extended partition.
164  */
165 
166 #ifndef debug_extended
167 #define dump_mbr(mbr, msg)
168 #else
169 void
170 dump_mbr(mbr_info_t *mbr, const char *msg)
171 {
172 	int i;
173 
174 	fprintf(stderr, "%s: bsec %d\n", msg, bsec);
175 	do {
176 		fprintf(stderr, "%9p: %9d %9p %6.6s:",
177 		    mbr, mbr->sector, mbr->extended,
178 		    mbr->prev_ext, mbr->last_mounted);
179 		for (i = 0; i < 4; i++)
180 			fprintf(stderr, " %*d %9d %9d %9d,\n",
181 			    i ? 41 : 3,
182 			    mbr->mbr.mbr_parts[i].mbrp_type,
183 			    mbr->mbr.mbr_parts[i].mbrp_start,
184 			    mbr->mbr.mbr_parts[i].mbrp_size,
185 			    mbr->mbr.mbr_parts[i].mbrp_start +
186 				mbr->mbr.mbr_parts[i].mbrp_size);
187 	} while ((mbr = mbr->extended));
188 }
189 #endif
190 
191 /*
192  * To be used only on ports which cannot provide any bios geometry
193  */
194 int
195 set_bios_geom_with_mbr_guess(void)
196 {
197 	int cyl, head;
198 	daddr_t sec;
199 
200 	read_mbr(pm->diskdev, &mbr);
201 	msg_display(MSG_nobiosgeom, pm->dlcyl, pm->dlhead, pm->dlsec);
202 	if (guess_biosgeom_from_mbr(&mbr, &cyl, &head, &sec) >= 0)
203 		msg_display_add(MSG_biosguess, cyl, head, sec);
204 	set_bios_geom(cyl, head, sec);
205 	return edit_mbr(&mbr);
206 }
207 
208 /*
209  * get C/H/S geometry from user via menu interface and
210  * store in globals.
211  */
212 void
213 set_bios_geom(int cyl, int head, int sec)
214 {
215 	char res[80];
216 
217 	msg_display_add(MSG_setbiosgeom);
218 
219 	do {
220 		snprintf(res, 80, "%d", sec);
221 		msg_prompt_add(MSG_sectors, res, res, 80);
222 		bsec = atoi(res);
223 	} while (bsec <= 0 || bsec > 63);
224 
225 	do {
226 		snprintf(res, 80, "%d", head);
227 		msg_prompt_add(MSG_heads, res, res, 80);
228 		bhead = atoi(res);
229 	} while (bhead <= 0 || bhead > 256);
230 
231 	bcyl = pm->dlsize / bsec / bhead;
232 	if (pm->dlsize != bcyl * bsec * bhead)
233 		bcyl++;
234 }
235 
236 #ifdef notdef
237 void
238 disp_cur_geom(void)
239 {
240 
241 	msg_display_add(MSG_realgeom, pm->dlcyl, pm->dlhead, pm->dlsec);
242 	msg_display_add(MSG_biosgeom, bcyl, bhead, bsec);
243 }
244 #endif
245 
246 
247 /*
248  * Then,  the partition stuff...
249  */
250 
251 /*
252  * If we change the mbr partitioning, the we must remove any references
253  * in the netbsd disklabel to the part we changed.
254  */
255 static void
256 remove_old_partitions(uint start, int64_t size)
257 {
258 	partinfo *p;
259 	uint end;
260 
261 	if (size > 0) {
262 		end = start + size;
263 	} else {
264 		end = start;
265 		start = end - size;
266 	}
267 
268 	if (end == 0)
269 		return;
270 
271 	for (p = pm->oldlabel; p < pm->oldlabel + nelem(pm->oldlabel); p++) {
272 		if (p->pi_offset >= end || p->pi_offset + p->pi_size <= start)
273 			continue;
274 		memset(p, 0, sizeof *p);
275 	}
276 }
277 
278 static int
279 find_mbr_space(struct mbr_sector *mbrs, uint *start, uint *size, uint from, int ignore)
280 {
281 	uint sz;
282 	int i;
283 	uint s, e;
284 
285     check_again:
286 	sz = pm->dlsize - from;
287 	for (i = 0; i < MBR_PART_COUNT; i++) {
288 		if (i == ignore)
289 			continue;
290 		s = mbrs->mbr_parts[i].mbrp_start;
291 		e = s + mbrs->mbr_parts[i].mbrp_size;
292 		if (s <= from && e > from) {
293 			from = e;
294 			goto check_again;
295 		}
296 		if (s > from && s - from < sz)
297 			sz = s - from;
298 	}
299 	if (sz == 0)
300 		return -1;
301 	if (start != NULL)
302 		*start = from;
303 	if (size != NULL)
304 		*size = sz;
305 	return 0;
306 }
307 
308 static struct mbr_partition *
309 get_mbrp(mbr_info_t **mbrip, int opt)
310 {
311 	mbr_info_t *mbri = *mbrip;
312 
313 	if (opt >= MBR_PART_COUNT)
314 		for (opt -= MBR_PART_COUNT - 1; opt; opt--)
315 			mbri = mbri->extended;
316 
317 	*mbrip = mbri;
318 	return &mbri->mbr.mbr_parts[opt];
319 }
320 
321 static int
322 err_msg_win(const char *errmsg)
323 {
324 	const char *cont;
325 	int l, l1;
326 
327 	errmsg = msg_string(errmsg);
328 	cont = msg_string(MSG_Hit_enter_to_continue);
329 
330 	l = strlen(errmsg);
331 	l1 = strlen(cont);
332 	if (l < l1)
333 		l = l1;
334 
335 	msg_prompt_win("%s.\n%s", -1, 18, l + 5, 4,
336 			NULL, NULL, 1, errmsg, cont);
337 	return 0;
338 }
339 
340 static int
341 set_mbr_type(menudesc *m, void *arg)
342 {
343 	mbr_info_t *mbri = arg;
344 	mbr_info_t *ombri = arg;
345 	mbr_info_t *ext;
346 	struct mbr_partition *mbrp;
347 	char *cp;
348 	int opt = mbri->opt;
349 	int type;
350 	u_int start, sz;
351 	int i;
352 	char numbuf[5];
353 
354 	dump_mbr(ombri, "set type");
355 
356 	mbrp = get_mbrp(&mbri, opt);
357 	if (opt >= MBR_PART_COUNT)
358 		opt = 0;
359 
360 	type = m->cursel;
361 	if (type == 0)
362 		return 1;
363 	type = part_ids[type - 1].id;
364 	while (type == -1) {
365 		snprintf(numbuf, sizeof numbuf, "%u", mbrp->mbrp_type);
366 		msg_prompt_win(MSG_get_ptn_id, -1, 18, 0, 0,
367 			numbuf, numbuf, sizeof numbuf);
368 		type = strtoul(numbuf, &cp, 0);
369 		if (*cp != 0)
370 			type = -1;
371 	}
372 
373 	if (type == mbrp->mbrp_type)
374 		/* type not changed... */
375 		return 1;
376 
377 	mbri->last_mounted[opt < MBR_PART_COUNT ? opt : 0] = NULL;
378 
379 	if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
380 		/* deleting extended partition.... */
381 		if (mbri->sector || mbri->extended->extended)
382 			/* We should have stopped this happening... */
383 			return err_msg_win("can't delete extended");
384 		free(mbri->extended);
385 		mbri->extended = NULL;
386 	}
387 
388 	if (type == 0) {
389 		/* Deleting partition */
390 		mbrp->mbrp_type = 0;
391 		/* Remove references to this space from the NetBSD label */
392 		remove_old_partitions(mbri->sector + mbrp->mbrp_start,
393 		    mbrp->mbrp_size);
394 #ifdef BOOTSEL
395 		if (ombri->bootsec == mbri->sector + mbrp->mbrp_start)
396 			ombri->bootsec = 0;
397 
398 		memset(mbri->mbrb.mbrbs_nametab[opt], 0,
399 		    sizeof mbri->mbrb.mbrbs_nametab[opt]);
400 #endif
401 		if (mbri->sector == 0) {
402 			/* A main partition */
403 			memset(mbrp, 0, sizeof *mbrp);
404 			return 1;
405 		}
406 
407 		/* Merge with previous and next free areas */
408 		ext = mbri->prev_ext;
409 		if (ext != NULL && ext->mbr.mbr_parts[0].mbrp_type == 0) {
410 			/* previous was free - back up one entry */
411 			mbri = ext;
412 			ombri->opt--;
413 		}
414 		while ((ext = mbri->extended)) {
415 			if (ext->mbr.mbr_parts[0].mbrp_type != 0)
416 				break;
417 			sz = ext->mbr.mbr_parts[0].mbrp_start +
418 						ext->mbr.mbr_parts[0].mbrp_size;
419 			/* Increase size of our (empty) partition */
420 			mbri->mbr.mbr_parts[0].mbrp_size += sz;
421 			/* Make us describe the next partition */
422 			mbri->mbr.mbr_parts[1] = ext->mbr.mbr_parts[1];
423 			/* fix list of extended partitions */
424 			mbri->extended = ext->extended;
425 			if (ext->extended != NULL)
426 				ext->extended->prev_ext = mbri;
427 			free(ext);
428 			/* Make previous size cover all our ptn */
429 			ext = mbri->prev_ext;
430 			if (ext != NULL)
431 				ext->mbr.mbr_parts[1].mbrp_size += sz;
432 		}
433 		return 1;
434 	}
435 
436 	if (mbrp->mbrp_start == 0) {
437 		/* Find first chunk of space... */
438 		/* Must be in the main partition */
439 		if (mbri->sector != 0)
440 			/* shouldn't be possible to have null start... */
441 			return err_msg_win("main-extended mixup");
442 		if (find_mbr_space(&mbri->mbr, &start, &sz, bsec, -1) != 0)
443 			/* no space */
444 			return err_msg_win(MSG_No_free_space);
445 		mbrp->mbrp_start = start;
446 		mbrp->mbrp_size = sz;
447 		/* If there isn't an active partition mark this one active */
448 		if (!MBR_IS_EXTENDED(type)) {
449 			for (i = 0; i < MBR_PART_COUNT; i++)
450 				if (mbri->mbr.mbr_parts[i].mbrp_flag != 0)
451 					break;
452 			if (i == MBR_PART_COUNT)
453 				mbrp->mbrp_flag = MBR_PFLAG_ACTIVE;
454 		}
455 	}
456 
457 	if (MBR_IS_EXTENDED(type)) {
458 		if (mbri->sector != 0)
459 			/* Can't set extended partition in an extended one */
460 			return err_msg_win(MSG_Only_one_extended_ptn);
461 		if (mbri->extended)
462 			/* Can't have two extended partitions */
463 			return err_msg_win(MSG_Only_one_extended_ptn);
464 		/* Create new extended partition */
465 		ext = calloc(1, sizeof *mbri->extended);
466 		if (!ext)
467 			return 0;
468 		mbri->extended = ext;
469 		ext->sector = mbrp->mbrp_start;
470 		ext->mbr.mbr_parts[0].mbrp_start = ptn_0_offset;
471 		ext->mbr.mbr_parts[0].mbrp_size =
472 		    mbrp->mbrp_size - ptn_0_offset;
473 	}
474 	mbrp->mbrp_type = type;
475 
476 	return 1;
477 }
478 
479 static void
480 set_type_label(menudesc *m, int opt, void *arg)
481 {
482 
483 	if (opt == 0) {
484 		wprintw(m->mw, "%s", msg_string(MSG_Dont_change));
485 		return;
486 	}
487 	if (opt == 1) {
488 		wprintw(m->mw, "%s", msg_string(MSG_Delete_partition));
489 		return;
490 	}
491 	if (part_ids[opt - 1].id == -1) {
492 		wprintw(m->mw, "%s", msg_string(MSG_Other_kind));
493 		return;
494 	}
495 	wprintw(m->mw, "%s", part_ids[opt - 1].name);
496 }
497 
498 static int
499 edit_mbr_type(menudesc *m, void *arg)
500 {
501 	static menu_ent type_opts[1 + nelem(part_ids)];
502 	static int type_menu = -1;
503 	unsigned int i;
504 
505 	if (type_menu == -1) {
506 		for (i = 0; i < nelem(type_opts); i++) {
507 			type_opts[i].opt_menu = OPT_NOMENU;
508 			type_opts[i].opt_action = set_mbr_type;
509 		}
510 		type_menu = new_menu(NULL, type_opts, nelem(type_opts),
511 			13, 12, 0, 30,
512 			MC_SUBMENU | MC_SCROLL | MC_NOEXITOPT | MC_NOCLEAR,
513 			NULL, set_type_label, NULL,
514 			NULL, NULL);
515 	}
516 
517 	if (type_menu != -1)
518 		process_menu(type_menu, arg);
519 
520 	return 0;
521 }
522 
523 static int
524 edit_mbr_start(menudesc *m, void *arg)
525 {
526 	mbr_info_t *mbri = arg;
527 	mbr_info_t *ext;
528 	struct mbr_partition *mbrp;
529 	int opt = mbri->opt;
530 	uint start, sz;
531 	uint new_r, new, limit, dflt_r;
532 	int64_t delta;
533 	const char *errmsg;
534 	char *cp;
535 	struct {
536 		uint	start;
537 		uint	start_r;
538 		uint	limit;
539 	} freespace[MBR_PART_COUNT];
540 	unsigned int spaces;
541 	unsigned int i;
542 	char prompt[MBR_PART_COUNT * 60];
543 	unsigned int len;
544 	char numbuf[12];
545 
546 	if (opt >= MBR_PART_COUNT)
547 		/* should not be able to get here... */
548 		return 1;
549 
550 	mbrp = mbri->mbr.mbr_parts + opt;
551 	/* locate the start of all free areas */
552 	spaces = 0;
553 	for (start = bsec, i = 0; i < MBR_PART_COUNT; start += sz, i++) {
554 		if (find_mbr_space(&mbri->mbr, &start, &sz, start, opt))
555 			break;
556 		if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
557 			/* Only want the area that contains this partition */
558 			if (mbrp->mbrp_start < start ||
559 			    mbrp->mbrp_start >= start + sz)
560 				continue;
561 			i = MBR_PART_COUNT - 1;
562 		}
563 		freespace[spaces].start = start;
564 		freespace[spaces].start_r = start / sizemult;
565 		freespace[spaces].limit = start + sz;
566 		if (++spaces >= sizeof freespace)
567 			/* shouldn't happen... */
568 			break;
569 	}
570 
571 	/* Add description of start/size to user prompt */
572 	len = 0;
573 	for (i = 0; i < spaces; i++) {
574 		len += snprintf(prompt + len, sizeof prompt - len,
575 		    msg_string(MSG_ptn_starts),
576 		    freespace[i].start_r,
577 		    freespace[i].limit / sizemult, multname,
578 		    freespace[i].limit / sizemult - freespace[i].start_r,
579 		    multname);
580 		if (len >= sizeof prompt)
581 			break;
582 	}
583 
584 	/* And loop until the user gives a sensible answer */
585 	dflt_r = mbrp->mbrp_start / sizemult;
586 	errmsg = "";
587 	for (;;) {
588 		snprintf(numbuf, sizeof numbuf, "%d", dflt_r);
589 		msg_prompt_win(MSG_get_ptn_start, -1, 18, 60, spaces + 3,
590 			numbuf, numbuf, sizeof numbuf,
591 			prompt, msg_string(errmsg), multname);
592 		new_r = strtoul(numbuf, &cp, 0);
593 		if (*cp != 0) {
594 			errmsg = MSG_Invalid_numeric;
595 			continue;
596 		}
597 		if (new_r == dflt_r)
598 			/* Unchanged */
599 			return 0;
600 		/*
601 		 * Check that the start address from the user is inside one
602 		 * of the free areas.
603 		 */
604 		new = new_r * sizemult;
605 		for (i = 0; i < spaces; i++) {
606 			if (new_r == freespace[i].start_r) {
607 				new = freespace[i].start;
608 				break;
609 			}
610 			if (new >= freespace[i].start &&
611 			    new < freespace[i].limit)
612 				break;
613 		}
614 		if (i >= spaces) {
615 			errmsg = MSG_Space_allocated;
616 			continue;
617 		}
618 		limit = freespace[i].limit;
619 		/*
620 		 * We can only increase the start of an extended partition
621 		 * if the corresponding space inside the partition isn't used.
622 		 */
623 		if (new > mbrp->mbrp_start &&
624 		    MBR_IS_EXTENDED(mbrp->mbrp_type) &&
625 		    (mbri->extended->mbr.mbr_parts[0].mbrp_type != 0 ||
626 			    mbri->extended->mbr.mbr_parts[0].mbrp_size <
627 						new - mbrp->mbrp_start)) {
628 			errmsg = MSG_Space_allocated;
629 			continue;
630 		}
631 		break;
632 	}
633 
634 	if (new < mbrp->mbrp_start + mbrp->mbrp_size &&
635 	    limit > mbrp->mbrp_start)
636 		/* Keep end of partition in the same place */
637 		limit = mbrp->mbrp_start + mbrp->mbrp_size;
638 
639 	delta = new - mbrp->mbrp_start;
640 	if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
641 		ext = mbri->extended;
642 		if (ext->mbr.mbr_parts[0].mbrp_type != 0) {
643 			/* allocate an extended ptn for the free item */
644 			ext = calloc(1, sizeof *ext);
645 			if (!ext)
646 				return 0;
647 			ext->sector = mbrp->mbrp_start;
648 			ext->extended = mbri->extended;
649 			mbri->extended->prev_ext = ext;
650 			mbri->extended = ext;
651 			ext->mbr.mbr_parts[0].mbrp_start = bsec;
652 			ext->mbr.mbr_parts[0].mbrp_size = -bsec;
653 			ext->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
654 			ext->mbr.mbr_parts[1].mbrp_start = 0;
655 			ext->mbr.mbr_parts[1].mbrp_size =
656 				ext->extended->mbr.mbr_parts[0].mbrp_start +
657 				ext->extended->mbr.mbr_parts[0].mbrp_size;
658 		}
659 		/* adjust size of first free item */
660 		ext->mbr.mbr_parts[0].mbrp_size -= delta;
661 		ext->sector += delta;
662 		/* and the link of all extended partitions */
663 		do
664 			if (ext->extended)
665 				ext->mbr.mbr_parts[1].mbrp_start -= delta;
666 		while ((ext = ext->extended));
667 	}
668 	remove_old_partitions(mbri->sector + mbrp->mbrp_start, delta);
669 
670 	/* finally set partition base and size */
671 	mbrp->mbrp_start = new;
672 	mbrp->mbrp_size = limit - new;
673 	mbri->last_mounted[opt] = NULL;
674 
675 	return 0;
676 }
677 
678 static int
679 edit_mbr_size(menudesc *m, void *arg)
680 {
681 	mbr_info_t *mbri = arg;
682 	mbr_info_t *ombri = arg;
683 	mbr_info_t *ext;
684 	struct mbr_partition *mbrp;
685 	int opt = mbri->opt;
686 	uint start, max, max_r, dflt, dflt_r, new;
687 	uint freespace;
688 	int delta;
689 	char numbuf[12];
690 	char *cp;
691 	const char *errmsg;
692 
693 	mbrp = get_mbrp(&mbri, opt);
694 	dflt = mbrp->mbrp_size;
695 	if (opt < MBR_PART_COUNT) {
696 		max = 0;
697 		find_mbr_space(&mbri->mbr, &start, &max, mbrp->mbrp_start, opt);
698 		if (start != mbrp->mbrp_start)
699 			return 0;
700 		if (dflt == 0)
701 			dflt = max;
702 	} else {
703 		ext = mbri->extended;
704 		max = dflt;
705 		/*
706 		 * If the next extended partition describes a free area,
707 		 * then merge it onto this area.
708 		 */
709 		if (ext != NULL && ext->mbr.mbr_parts[0].mbrp_type == 0) {
710 			if (ext->extended)
711 				ext->extended->prev_ext = mbri;
712 			mbri->extended = ext->extended;
713 			if (mbri->prev_ext)
714 				mbri->prev_ext->mbr.mbr_parts[1].mbrp_size
715 					+= mbri->mbr.mbr_parts[1].mbrp_size;
716 			mbrp->mbrp_size += mbri->mbr.mbr_parts[1].mbrp_size;
717 			max += mbri->mbr.mbr_parts[1].mbrp_size;
718 			mbri->mbr.mbr_parts[1] = ext->mbr.mbr_parts[1];
719 			free(ext);
720 		}
721 	}
722 
723 	start = mbri->sector + mbrp->mbrp_start;
724 	/* We need to keep both the unrounded and rounded (_r) max and dflt */
725 	dflt_r = (start + dflt) / sizemult - start / sizemult;
726 	if (max == dflt)
727 		max_r = dflt_r;
728 	else
729 		max_r = max / sizemult;
730 	for (errmsg = "";;) {
731 		snprintf(numbuf, sizeof numbuf, "%d", dflt_r);
732 		msg_prompt_win(MSG_get_ptn_size, -1, 18, 0, 0,
733 			numbuf, numbuf, sizeof numbuf,
734 			msg_string(errmsg), max_r, multname);
735 		new = strtoul(numbuf, &cp, 0);
736 		if (*cp != 0) {
737 			errmsg = MSG_Invalid_numeric;
738 			continue;
739 		}
740 		if (new > max_r) {
741 			errmsg = MSG_Too_large;
742 			continue;
743 		}
744 		if (new == 0)
745 			/* Treat zero as a request for the maximum */
746 			new = max_r;
747 		if (new == dflt_r)
748 			/* If unchanged, don't re-round size */
749 			new = dflt;
750 		else {
751 			/* Round end to the partition alignment */
752 			if (sizemult != 1) {
753 				new *= sizemult;
754 				new += rounddown(start, ptn_alignment);
755 				new = roundup(new, ptn_alignment);
756 				new -= start;
757 				while (new <= 0)
758 					new += ptn_alignment;
759 			}
760 		}
761 		if (new > max)
762 			/* We rounded the value to above the max */
763 			new = max;
764 
765 		if (new == dflt || opt >= MBR_PART_COUNT
766 		    || !MBR_IS_EXTENDED(mbrp->mbrp_type))
767 			break;
768 		/*
769 		 * We've been asked to change the size of the main extended
770 		 * partition.  If this reduces the size, then that space
771 		 * must be unallocated.  If it increases the size then
772 		 * we must add a description ofthe new free space.
773 		 */
774 		/* Find last extended partition */
775 		for (ext = mbri->extended; ext->extended; ext = ext->extended)
776 			continue;
777 		if ((new < dflt && (ext->mbr.mbr_parts[0].mbrp_type != 0
778 			    || (mbrp->mbrp_start + new < ext->sector + bsec
779 				&& mbrp->mbrp_start + new != ext->sector)))
780 		    || (new > dflt && ext->mbr.mbr_parts[0].mbrp_type != 0
781 							&& new < dflt + bsec)) {
782 			errmsg = MSG_Space_allocated;
783 			continue;
784 		}
785 		delta = new - dflt;
786 		if (ext->mbr.mbr_parts[0].mbrp_type == 0) {
787 			/* adjust size of last item (free space) */
788 			if (mbrp->mbrp_start + new == ext->sector) {
789 				/* kill last extended ptn */
790 				ext = ext->prev_ext;
791 				free(ext->extended);
792 				ext->extended = NULL;
793 				memset(&ext->mbr.mbr_parts[1], 0,
794 					sizeof ext->mbr.mbr_parts[1]);
795 				break;
796 			}
797 			ext->mbr.mbr_parts[0].mbrp_size += delta;
798 			ext = ext->prev_ext;
799 			if (ext != NULL)
800 				ext->mbr.mbr_parts[1].mbrp_size += delta;
801 			break;
802 		}
803 		/* Joy of joys, we must allocate another extended ptn */
804 		mbri = ext;
805 		ext = calloc(1, sizeof *ext);
806 		if (!ext)
807 			return 0;
808 		mbri->extended = ext;
809 		ext->prev_ext = mbri;
810 		ext->mbr.mbr_parts[0].mbrp_start = bsec;
811 		ext->mbr.mbr_parts[0].mbrp_size = delta - bsec;
812 		ext->sector = mbri->sector + mbri->mbr.mbr_parts[0].mbrp_start
813 					   + mbri->mbr.mbr_parts[0].mbrp_size;
814 		mbri->mbr.mbr_parts[1].mbrp_start = ext->sector - ombri->extended->sector;
815 		mbri->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
816 		mbri->mbr.mbr_parts[1].mbrp_size = delta;
817 		break;
818 	}
819 
820 	if (opt >= MBR_PART_COUNT && max - new <= (uint32_t)bsec)
821 		/* Round up if not enough space for a header for free area */
822 		new = max;
823 
824 	if (new != mbrp->mbrp_size) {
825 		/* Kill information about old partition from label */
826 		mbri->last_mounted[opt < MBR_PART_COUNT ? opt : 0] = NULL;
827 		remove_old_partitions(mbri->sector + mbrp->mbrp_start +
828 			    mbrp->mbrp_size, (int64_t)new - mbrp->mbrp_size);
829 	}
830 
831 	mbrp->mbrp_size = new;
832 	if (opt < MBR_PART_COUNT || new == max)
833 		return 0;
834 
835 	/* Add extended partition for the free space */
836 	ext = calloc(1, sizeof *ext);
837 	if (!ext) {
838 		mbrp->mbrp_size = max;
839 		return 0;
840 	}
841 	/* Link into our extended chain */
842 	ext->extended = mbri->extended;
843 	mbri->extended = ext;
844 	ext->prev_ext = mbri;
845 	if (ext->extended != NULL)
846 		ext->extended->prev_ext = ext;
847 	ext->mbr.mbr_parts[1] = mbri->mbr.mbr_parts[1];
848 	freespace = max - new;
849 	if (mbri->prev_ext != NULL)
850 		mbri->prev_ext->mbr.mbr_parts[1].mbrp_size -= freespace;
851 
852 	ext->mbr.mbr_parts[0].mbrp_start = bsec;
853 	ext->mbr.mbr_parts[0].mbrp_size = freespace - bsec;
854 
855 	ext->sector = mbri->sector + mbri->mbr.mbr_parts[0].mbrp_start
856 				   + mbri->mbr.mbr_parts[0].mbrp_size;
857 	mbri->mbr.mbr_parts[1].mbrp_start = ext->sector - ombri->extended->sector;
858 	mbri->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
859 	mbri->mbr.mbr_parts[1].mbrp_size = freespace;
860 
861 	return 0;
862 }
863 
864 static int
865 edit_mbr_active(menudesc *m, void *arg)
866 {
867 	mbr_info_t *mbri = arg;
868 	int i;
869 	uint8_t *fl;
870 
871 	if (mbri->opt >= MBR_PART_COUNT)
872 		/* sanity */
873 		return 0;
874 
875 	/* Invert active flag */
876 	fl = &mbri->mbr.mbr_parts[mbri->opt].mbrp_flag;
877 	if (*fl == MBR_PFLAG_ACTIVE) {
878 		*fl = 0;
879 		return 0;
880 	}
881 
882 	/* Ensure there is at most one active partition */
883 	for (i = 0; i < MBR_PART_COUNT; i++)
884 		mbri->mbr.mbr_parts[i].mbrp_flag = 0;
885 	*fl = MBR_PFLAG_ACTIVE;
886 
887 	return 0;
888 }
889 
890 static int
891 edit_mbr_install(menudesc *m, void *arg)
892 {
893 	mbr_info_t *mbri = arg;
894 	mbr_info_t *ombri = arg;
895 	struct mbr_partition *mbrp;
896 	int opt = mbri->opt;
897 	uint start;
898 
899 	mbrp = get_mbrp(&mbri, opt);
900 	if (opt >= MBR_PART_COUNT)
901 		opt = 0;
902 
903 	start = mbri->sector + mbrp->mbrp_start;
904 	/* We just remember the start address of the partition... */
905 	if (start == ombri->install)
906 		ombri->install = 0;
907 	else
908 		ombri->install = start;
909 	return 0;
910 }
911 
912 #ifdef BOOTSEL
913 static int
914 edit_mbr_bootmenu(menudesc *m, void *arg)
915 {
916 	mbr_info_t *mbri = arg;
917 	mbr_info_t *ombri = arg;
918 	struct mbr_partition *mbrp;
919 	int opt = mbri->opt;
920 
921 	mbrp = get_mbrp(&mbri, opt);
922 	if (opt >= MBR_PART_COUNT)
923 		opt = 0;
924 
925 	msg_prompt_win(/* XXX translate? */ "bootmenu", -1, 18, 0, 0,
926 		mbri->mbrb.mbrbs_nametab[opt],
927 		mbri->mbrb.mbrbs_nametab[opt],
928 		sizeof mbri->mbrb.mbrbs_nametab[opt]);
929 	if (mbri->mbrb.mbrbs_nametab[opt][0] == ' ')
930 		mbri->mbrb.mbrbs_nametab[opt][0] = 0;
931 	if (mbri->mbrb.mbrbs_nametab[opt][0] == 0
932 	    && ombri->bootsec == mbri->sector + mbrp->mbrp_start)
933 		ombri->bootsec = 0;
934 	return 0;
935 }
936 
937 static int
938 edit_mbr_bootdefault(menudesc *m, void *arg)
939 {
940 	mbr_info_t *mbri = arg;
941 	mbr_info_t *ombri = arg;
942 	struct mbr_partition *mbrp;
943 
944 	mbrp = get_mbrp(&mbri, mbri->opt);
945 
946 	ombri->bootsec = mbri->sector + mbrp->mbrp_start;
947 	return 0;
948 }
949 #endif
950 
951 static void set_ptn_label(menudesc *m, int line, void *arg);
952 static void set_ptn_header(menudesc *m, void *arg);
953 
954 static int
955 edit_mbr_entry(menudesc *m, void *arg)
956 {
957 	mbr_info_t *mbri = arg;
958 	static int ptn_menu = -1;
959 
960 	static menu_ent ptn_opts[] = {
961 #define PTN_OPT_TYPE		0
962 		{ .opt_menu=OPT_NOMENU, .opt_action=edit_mbr_type },
963 #define PTN_OPT_START		1
964 		{ .opt_menu=OPT_NOMENU, .opt_action=edit_mbr_start },
965 #define PTN_OPT_SIZE		2
966 		{ .opt_menu=OPT_NOMENU, .opt_action=edit_mbr_size },
967 #define PTN_OPT_END		3
968 		{ .opt_menu=OPT_NOMENU, .opt_flags=OPT_IGNORE }, /* display end */
969 #define PTN_OPT_ACTIVE		4
970 		{ .opt_menu=OPT_NOMENU, .opt_action=edit_mbr_active },
971 #define PTN_OPT_INSTALL		5
972 		{ .opt_menu=OPT_NOMENU, .opt_action=edit_mbr_install },
973 #ifdef BOOTSEL
974 #define PTN_OPT_BOOTMENU	6
975 		{ .opt_menu=OPT_NOMENU, .opt_action=edit_mbr_bootmenu },
976 #define PTN_OPT_BOOTDEFAULT	7
977 		{ .opt_menu=OPT_NOMENU, .opt_action=edit_mbr_bootdefault },
978 #endif
979 		{ .opt_name=MSG_askunits, .opt_menu=MENU_sizechoice,
980 		  .opt_flags=OPT_SUB },
981 	};
982 
983 	if (ptn_menu == -1)
984 		ptn_menu = new_menu(NULL, ptn_opts, nelem(ptn_opts),
985 			15, 6, 0, 54,
986 			MC_SUBMENU | MC_SCROLL | MC_NOCLEAR,
987 			set_ptn_header, set_ptn_label, NULL,
988 			NULL, MSG_Partition_OK);
989 	if (ptn_menu == -1)
990 		return 1;
991 
992 	mbri->opt = m->cursel;
993 	process_menu(ptn_menu, mbri);
994 	return 0;
995 }
996 
997 static void
998 set_ptn_label(menudesc *m, int line, void *arg)
999 {
1000 	mbr_info_t *mbri = arg;
1001 	mbr_info_t *ombri = arg;
1002 	struct mbr_partition *mbrp;
1003 	int opt;
1004 	static const char *yes, *no;
1005 
1006 	if (yes == NULL) {
1007 		yes = msg_string(MSG_Yes);
1008 		no = msg_string(MSG_No);
1009 	}
1010 
1011 	opt = mbri->opt;
1012 	mbrp = get_mbrp(&mbri, opt);
1013 	if (opt >= MBR_PART_COUNT)
1014 		opt = 0;
1015 
1016 	switch (line) {
1017 	case PTN_OPT_TYPE:
1018 		wprintw(m->mw, msg_string(MSG_ptn_type),
1019 			get_partname(mbrp->mbrp_type));
1020 		break;
1021 	case PTN_OPT_START:
1022 		wprintw(m->mw, msg_string(MSG_ptn_start),
1023 		    (mbri->sector + mbrp->mbrp_start) / sizemult, multname);
1024 		break;
1025 	case PTN_OPT_SIZE:
1026 		wprintw(m->mw, msg_string(MSG_ptn_size),
1027 		    (mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size) /
1028 			    sizemult -
1029 		    (mbri->sector + mbrp->mbrp_start) / sizemult, multname);
1030 		break;
1031 	case PTN_OPT_END:
1032 		wprintw(m->mw, msg_string(MSG_ptn_end),
1033 		    (mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size) /
1034 			    sizemult, multname);
1035 		break;
1036 	case PTN_OPT_ACTIVE:
1037 		wprintw(m->mw, msg_string(MSG_ptn_active),
1038 		    mbrp->mbrp_flag == MBR_PFLAG_ACTIVE ? yes : no);
1039 		break;
1040 	case PTN_OPT_INSTALL:
1041 		wprintw(m->mw, msg_string(MSG_ptn_install),
1042 		    mbri->sector + mbrp->mbrp_start == ombri->install &&
1043 			mbrp->mbrp_type == MBR_PTYPE_NETBSD ? yes : no);
1044 		break;
1045 #ifdef BOOTSEL
1046 	case PTN_OPT_BOOTMENU:
1047 		wprintw(m->mw, msg_string(MSG_bootmenu),
1048 		    mbri->mbrb.mbrbs_nametab[opt]);
1049 		break;
1050 	case PTN_OPT_BOOTDEFAULT:
1051 		wprintw(m->mw, msg_string(MSG_boot_dflt),
1052 		    ombri->bootsec == mbri->sector + mbrp->mbrp_start ? yes
1053 								      : no);
1054 		break;
1055 #endif
1056 	}
1057 
1058 }
1059 
1060 static void
1061 set_ptn_header(menudesc *m, void *arg)
1062 {
1063 	mbr_info_t *mbri = arg;
1064 	struct mbr_partition *mbrp;
1065 	int opt = mbri->opt;
1066 	int typ;
1067 
1068 	mbrp = get_mbrp(&mbri, opt);
1069 	if (opt >= MBR_PART_COUNT)
1070 		opt = 0;
1071 	typ = mbrp->mbrp_type;
1072 
1073 #define DISABLE(opt,cond) \
1074 	if (cond) \
1075 		m->opts[opt].opt_flags |= OPT_IGNORE; \
1076 	else \
1077 		m->opts[opt].opt_flags &= ~OPT_IGNORE;
1078 
1079 	/* Can't change type of the extended partition unless it is empty */
1080 	DISABLE(PTN_OPT_TYPE, MBR_IS_EXTENDED(typ) &&
1081 	    (mbri->extended->mbr.mbr_parts[0].mbrp_type != 0 ||
1082 					    mbri->extended->extended != NULL));
1083 
1084 	/* It is unnecessary to be able to change the base of an extended ptn */
1085 	DISABLE(PTN_OPT_START, mbri->sector || typ == 0);
1086 
1087 	/* or the size of a free area */
1088 	DISABLE(PTN_OPT_SIZE, typ == 0);
1089 
1090 	/* Only 'normal' partitions can be 'Active' */
1091 	DISABLE(PTN_OPT_ACTIVE, mbri->sector != 0 || MBR_IS_EXTENDED(typ) || typ == 0);
1092 
1093 	/* Can only install into NetBSD partition */
1094 	DISABLE(PTN_OPT_INSTALL, typ != MBR_PTYPE_NETBSD);
1095 
1096 #ifdef BOOTSEL
1097 	/* The extended partition isn't bootable */
1098 	DISABLE(PTN_OPT_BOOTMENU, MBR_IS_EXTENDED(typ) || typ == 0);
1099 
1100 	if (typ == 0)
1101 		mbri->mbrb.mbrbs_nametab[opt][0] = 0;
1102 
1103 	/* Only partitions with bootmenu names can be made the default */
1104 	DISABLE(PTN_OPT_BOOTDEFAULT, mbri->mbrb.mbrbs_nametab[opt][0] == 0);
1105 #endif
1106 #undef DISABLE
1107 }
1108 
1109 static void
1110 set_mbr_label(menudesc *m, int opt, void *arg)
1111 {
1112 	mbr_info_t *mbri = arg;
1113 	mbr_info_t *ombri = arg;
1114 	struct mbr_partition *mbrp;
1115 	uint rstart, rend;
1116 	const char *name, *cp, *mounted;
1117 	int len;
1118 
1119 	mbrp = get_mbrp(&mbri, opt);
1120 	if (opt >= MBR_PART_COUNT)
1121 		opt = 0;
1122 
1123 	if (mbrp->mbrp_type == 0 && mbri->sector == 0) {
1124 		len = snprintf(0, 0, msg_string(MSG_part_row_used), 0, 0, 0);
1125 		wprintw(m->mw, "%*s", len, "");
1126 	} else {
1127 		rstart = mbri->sector + mbrp->mbrp_start;
1128 		rend = (rstart + mbrp->mbrp_size) / sizemult;
1129 		rstart = rstart / sizemult;
1130 		wprintw(m->mw, msg_string(MSG_part_row_used),
1131 		    rstart, rend - rstart,
1132 		    mbrp->mbrp_flag == MBR_PFLAG_ACTIVE ? 'a' : ' ',
1133 #ifdef BOOTSEL
1134 		    ombri->bootsec == mbri->sector + mbrp->mbrp_start ? 'd' :
1135 #endif
1136 			' ',
1137 		    mbri->sector + mbrp->mbrp_start == ombri->install &&
1138 			mbrp->mbrp_type == MBR_PTYPE_NETBSD ? 'I' : ' ');
1139 	}
1140 	name = get_partname(mbrp->mbrp_type);
1141 	mounted = mbri->last_mounted[opt];
1142 	len = strlen(name);
1143 	cp = strchr(name, ',');
1144 	if (cp != NULL)
1145 		len = cp - name;
1146 	if (mounted && *mounted != 0) {
1147 		wprintw(m->mw, " %*s (%s)", len, name, mounted);
1148 	} else
1149 		wprintw(m->mw, " %.*s", len, name);
1150 #ifdef BOOTSEL
1151 	if (mbri->mbrb.mbrbs_nametab[opt][0] != 0) {
1152 		int x, y;
1153 
1154 		getyx(m->mw, y, x);
1155 		if (x > 52) {
1156 			x = 52;
1157 			wmove(m->mw, y, x);
1158 		}
1159 		wprintw(m->mw, "%*s %s", 53 - x, "",
1160 		    mbri->mbrb.mbrbs_nametab[opt]);
1161 	}
1162 #endif
1163 }
1164 
1165 static void
1166 set_mbr_header(menudesc *m, void *arg)
1167 {
1168 	mbr_info_t *mbri = arg;
1169 	static menu_ent *opts;
1170 	static int num_opts;
1171 	mbr_info_t *ext;
1172 	menu_ent *op;
1173 	int i;
1174 	int left;
1175 
1176 	msg_display(MSG_editparttable);
1177 
1178 	msg_table_add(MSG_part_header, (unsigned long)(pm->dlsize/sizemult),
1179 	    multname, multname, multname, multname);
1180 
1181 	if (num_opts == 0) {
1182 		num_opts = 6;
1183 		opts = malloc(6 * sizeof *opts);
1184 		if (opts == NULL) {
1185 			m->numopts = 0;
1186 			return;
1187 		}
1188 	}
1189 
1190 	/* First four items are the main partitions */
1191 	for (op = opts, i = 0; i < MBR_PART_COUNT; op++, i++) {
1192 		op->opt_name = NULL;
1193 		op->opt_menu = OPT_NOMENU;
1194 		op->opt_flags = OPT_SUB;
1195 		op->opt_action = edit_mbr_entry;
1196 	}
1197 	left = num_opts - MBR_PART_COUNT;
1198 
1199 	/* Followed by the extended partitions */
1200 	for (ext = mbri->extended; ext; left--, op++, ext = ext->extended) {
1201 		if (left <= 1) {
1202 			menu_ent *new = realloc(opts,
1203 						(num_opts + 4) * sizeof *opts);
1204 			if (new == NULL)
1205 				break;
1206 			num_opts += 4;
1207 			left += 4;
1208 			op = new + (op - opts);
1209 			opts = new;
1210 		}
1211 		op->opt_name = NULL;
1212 		op->opt_menu = OPT_NOMENU;
1213 		op->opt_flags = 0;
1214 		op->opt_action = edit_mbr_entry;
1215 	}
1216 
1217 	/* and unit changer */
1218 	op->opt_name = MSG_askunits;
1219 	op->opt_menu = MENU_sizechoice;
1220 	op->opt_flags = OPT_SUB;
1221 	op->opt_action = NULL;
1222 	op++;
1223 
1224 	m->opts = opts;
1225 	m->numopts = op - opts;
1226 }
1227 
1228 int
1229 mbr_use_wholedisk(mbr_info_t *mbri)
1230 {
1231 	struct mbr_sector *mbrs = &mbri->mbr;
1232 	mbr_info_t *ext;
1233 	struct mbr_partition *part;
1234 
1235 	part = &mbrs->mbr_parts[0];
1236 	/* Set the partition information for full disk usage. */
1237 	while ((ext = mbri->extended)) {
1238 		mbri->extended = ext->extended;
1239 		free(ext);
1240 	}
1241 	memset(part, 0, MBR_PART_COUNT * sizeof *part);
1242 #ifdef BOOTSEL
1243 	memset(&mbri->mbrb, 0, sizeof mbri->mbrb);
1244 #endif
1245 	part[0].mbrp_type = MBR_PTYPE_NETBSD;
1246 	part[0].mbrp_size = pm->dlsize - ptn_0_offset;
1247 	part[0].mbrp_start = ptn_0_offset;
1248 	part[0].mbrp_flag = MBR_PFLAG_ACTIVE;
1249 
1250 	pm->ptstart = ptn_0_offset;
1251 	pm->ptsize = pm->dlsize - ptn_0_offset;
1252 	return 1;
1253 }
1254 
1255 /*
1256  * Let user change incore Master Boot Record partitions via menu.
1257  */
1258 int
1259 edit_mbr(mbr_info_t *mbri)
1260 {
1261 	struct mbr_sector *mbrs = &mbri->mbr;
1262 	mbr_info_t *ext;
1263 	struct mbr_partition *part;
1264 	int i, j;
1265 	int usefull;
1266 	int mbr_menu;
1267 	int activepart;
1268 	int numbsd;
1269 	uint bsdstart, bsdsize;
1270 	uint start;
1271 
1272 	/* If targeting a wedge, do not ask for further partitioning */
1273 	if (pm && pm->no_part)
1274 		return 1;
1275 
1276 	/* Ask full/part */
1277 	part = &mbrs->mbr_parts[0];
1278 	get_ptn_alignment(part);  /* update ptn_alignment */
1279 	if (partman_go)
1280 		usefull = 0;
1281 	else {
1282 		uint64_t m_size =
1283 		    DEFROOTSIZE + DEFSWAPSIZE + DEFUSRSIZE + XNEEDMB;
1284 		char min_size[5], build_size[5];
1285 
1286 		humanize_number(min_size, sizeof(min_size),
1287 		    2 * m_size * MEG,
1288 		    "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1289 		humanize_number(build_size, sizeof(build_size),
1290 		     SYSTEM_BUILD_SIZE * MEG, "", HN_AUTOSCALE,
1291 		     HN_B | HN_NOSPACE | HN_DECIMAL);
1292 
1293 		msg_display_subst(MSG_fullpart, 7,
1294 		    pm->diskdev,
1295 		    msg_string(MSG_parttype_mbr),
1296 		    msg_string(MSG_parttype_disklabel),
1297 		    msg_string(MSG_parttype_mbr_short),
1298 		    msg_string(MSG_parttype_disklabel_short),
1299 		    min_size, build_size);
1300 		process_menu(MENU_fullpart, &usefull);
1301 	}
1302 
1303 	/* DOS fdisk label checking and value setting. */
1304 	if (usefull) {
1305 		/* Count nonempty, non-BSD partitions. */
1306 		numbsd = 0;
1307 		for (i = 0; i < MBR_PART_COUNT; i++) {
1308 			j = part[i].mbrp_type;
1309 			if (j == 0)
1310 				continue;
1311 			numbsd++;
1312 			if (j != MBR_PTYPE_NETBSD)
1313 				numbsd++;
1314 		}
1315 
1316 		/* Ask if we really want to blow away non-NetBSD stuff */
1317 		if (numbsd > 1) {
1318 			msg_display(MSG_ovrwrite);
1319 			if (!ask_noyes(NULL)) {
1320 				if (logfp)
1321 					(void)fprintf(logfp, "User answered no to destroy other data, aborting.\n");
1322 				return 0;
1323 			}
1324 		}
1325 		return(md_mbr_use_wholedisk(mbri));
1326 	}
1327 
1328 	mbr_menu = new_menu(NULL, NULL, 16, 0, -1, 15, 70,
1329 			MC_NOBOX | MC_ALWAYS_SCROLL | MC_NOCLEAR,
1330 			set_mbr_header, set_mbr_label, NULL,
1331 			NULL, MSG_Partition_table_ok);
1332 	if (mbr_menu == -1)
1333 		return 0;
1334 
1335 	/* Default to MB, and use bios geometry for cylinder size */
1336 	set_sizemultname_meg();
1337 	pm->current_cylsize = bhead * bsec;
1338 
1339 	for (;;) {
1340 		pm->ptstart = 0;
1341 		pm->ptsize = 0;
1342 		process_menu(mbr_menu, mbri);
1343 
1344 		activepart = 0;
1345 		bsdstart = 0;
1346 		bsdsize = 0;
1347 		for (ext = mbri; ext; ext = ext->extended) {
1348 			part = ext->mbr.mbr_parts;
1349 			for (i = 0; i < MBR_PART_COUNT; part++, i++) {
1350 				if (part->mbrp_flag != 0)
1351 					activepart = 1;
1352 				if (part->mbrp_type != MBR_PTYPE_NETBSD)
1353 					continue;
1354 				start = ext->sector + part->mbrp_start;
1355 				if (start == mbri->install) {
1356 					pm->ptstart = mbri->install;
1357 					pm->ptsize = part->mbrp_size;
1358 				}
1359 				if (bsdstart != 0)
1360 					bsdstart = ~0;
1361 				else {
1362 					bsdstart = start;
1363 					bsdsize = part->mbrp_size;
1364 				}
1365 			}
1366 		}
1367 
1368 		/* Install in only netbsd partition if none tagged */
1369 		if (pm->ptstart == 0 && bsdstart != ~0u) {
1370 			pm->ptstart = bsdstart;
1371 			pm->ptsize = bsdsize;
1372 		}
1373 
1374 		if (pm->ptstart == 0) {
1375 			if (bsdstart == 0)
1376 				msg_display(MSG_nobsdpart);
1377 			else
1378 				msg_display(MSG_multbsdpart, 0);
1379 			msg_display_add(MSG_reeditpart, 0);
1380 			if (!ask_yesno(NULL))
1381 				return 0;
1382 			continue;
1383 		}
1384 
1385 		if (activepart == 0) {
1386 			msg_display(MSG_noactivepart);
1387 			if (ask_yesno(NULL))
1388 				continue;
1389 		}
1390 		/* the md_check_mbr function has 3 ret codes to deal with
1391 		 * the different possible states. 0, 1, >1
1392 		 */
1393 		j = md_check_mbr(mbri);
1394 		if (j == 0)
1395 			return 0;
1396 		if (j == 1)
1397 			continue;
1398 
1399 		break;
1400 	}
1401 
1402 	free_menu(mbr_menu);
1403 
1404 	return 1;
1405 }
1406 
1407 const char *
1408 get_partname(int typ)
1409 {
1410 	int j;
1411 	static char unknown[32];
1412 
1413 	for (j = 0; part_ids[j].id != -1; j++)
1414 		if (part_ids[j].id == typ)
1415 			return part_ids[j].name;
1416 
1417 	snprintf(unknown, sizeof unknown, "Unknown (%d)", typ);
1418 	return unknown;
1419 }
1420 
1421 #ifdef BOOTSEL
1422 static int
1423 validate_and_set_names(mbr_info_t *mbri, const struct mbr_bootsel *src,
1424     uint32_t ext_base)
1425 {
1426 	size_t i, l;
1427 	const unsigned char *p;
1428 
1429 	/*
1430 	 * The 16 bit magic used to detect whether mbr_bootsel is valid
1431 	 * or not is pretty week - collisions have been seen in the wild;
1432 	 * but maybe it is just foreign tools corruption reminiscents
1433 	 * of NetBSD MBRs. Anyway, before accepting a boot menu definition,
1434 	 * make sure it is kinda "sane".
1435 	 */
1436 
1437 	for (i = 0; i < MBR_PART_COUNT; i++) {
1438 		/*
1439 		 * Make sure the name does not contain controll chars
1440 		 * (not using iscntrl due to minimalistic locale support
1441 		 * in miniroot environments) and is properly 0-terminated.
1442 		 */
1443 		for (l = 0, p = (const unsigned char *)&src->mbrbs_nametab[i];
1444 		    *p != 0; l++, p++) {
1445 			if (l >	MBR_BS_PARTNAMESIZE)
1446 				return 0;
1447 			if (*p < ' ')	/* hacky 'iscntrl' */
1448 				return 0;
1449 		}
1450 	}
1451 
1452 	memcpy(&mbri->mbrb, src, sizeof(*src));
1453 
1454 	if (ext_base == 0)
1455 		return mbri->mbrb.mbrbs_defkey - SCAN_1;
1456 	return 0;
1457 }
1458 #endif
1459 
1460 int
1461 read_mbr(const char *disk, mbr_info_t *mbri)
1462 {
1463 	struct mbr_partition *mbrp;
1464 	struct mbr_sector *mbrs = &mbri->mbr;
1465 	mbr_info_t *ext = NULL;
1466 	char diskpath[MAXPATHLEN];
1467 	int fd, i;
1468 	uint32_t ext_base = 0, next_ext = 0, ext_size = 0;
1469 	int rval = -1;
1470 #ifdef BOOTSEL
1471 	mbr_info_t *ombri = mbri;
1472 	int bootkey = 0;
1473 #endif
1474 
1475 	/*
1476 	 * Fake up a likely 'bios sectors per track' for any extended
1477 	 * partition headers we might have to produce.
1478 	 */
1479 	if (bsec == 0)
1480 		bsec = pm->dlsec;
1481 	if (bhead == 0)
1482 		bhead = pm->dlhead;
1483 	if (bcyl == 0)
1484 		bhead = pm->dlcyl;
1485 
1486 	ptn_0_offset = bsec;
1487 	/* use 1MB default offset on large disks as fdisk(8) */
1488 	if ((unsigned long)pm->dlsize > 128 * GIG / 512)
1489 		ptn_0_offset = 2048;
1490 
1491 	memset(mbri, 0, sizeof *mbri);
1492 
1493 	/* Open the disk. */
1494 	fd = opendisk(disk, O_RDONLY, diskpath, sizeof(diskpath), 0);
1495 	if (fd < 0)
1496 		goto bad_mbr;
1497 
1498 	for (;;) {
1499 		if (pread(fd, mbrs, sizeof *mbrs,
1500 		    (ext_base + next_ext) * (off_t)MBR_SECSIZE) - sizeof *mbrs != 0)
1501 			break;
1502 
1503 		if (!valid_mbr(mbrs))
1504 			break;
1505 
1506 		mbrp = &mbrs->mbr_parts[0];
1507 		if (ext_base == 0) {
1508 			get_ptn_alignment(mbrp);	/* get ptn_0_offset */
1509 		} else {
1510 			/* sanity check extended chain */
1511 			if (MBR_IS_EXTENDED(mbrp[0].mbrp_type))
1512 				break;
1513 			if (mbrp[1].mbrp_type != 0 &&
1514 			    !MBR_IS_EXTENDED(mbrp[1].mbrp_type))
1515 				break;
1516 			if (mbrp[2].mbrp_type != 0 || mbrp[3].mbrp_type != 0)
1517 				break;
1518 			/* Looks ok, link into extended chain */
1519 			mbri->extended = ext;
1520 			ext->prev_ext = next_ext != 0 ? mbri : NULL;
1521 			ext->extended = NULL;
1522 			mbri = ext;
1523 			ext = NULL;
1524 		}
1525 #if BOOTSEL
1526 		if (mbrs->mbr_bootsel_magic == htole16(MBR_MAGIC)) {
1527 			/* old bootsel, grab bootsel info */
1528 			bootkey = validate_and_set_names(mbri,
1529 				(struct mbr_bootsel *)
1530 				((uint8_t *)mbrs + MBR_BS_OLD_OFFSET),
1531 				ext_base);
1532 		} else if (mbrs->mbr_bootsel_magic == htole16(MBR_BS_MAGIC)) {
1533 			/* new location */
1534 			bootkey = validate_and_set_names(mbri,
1535 			    &mbrs->mbr_bootsel, ext_base);
1536 		}
1537 		/* Save original flags for mbr code update tests */
1538 		mbri->oflags = mbri->mbrb.mbrbs_flags;
1539 #endif
1540 		mbri->sector = next_ext + ext_base;
1541 		next_ext = 0;
1542 		rval = 0;
1543 		for (i = 0; i < MBR_PART_COUNT; mbrp++, i++) {
1544 			if (mbrp->mbrp_type == 0) {
1545 				/* type is unused, discard scum */
1546 				memset(mbrp, 0, sizeof *mbrp);
1547 				continue;
1548 			}
1549 			mbrp->mbrp_start = le32toh(mbrp->mbrp_start);
1550 			mbrp->mbrp_size = le32toh(mbrp->mbrp_size);
1551 			if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
1552 				next_ext = mbrp->mbrp_start;
1553 				if (ext_base == 0)
1554 					ext_size = mbrp->mbrp_size;
1555 			} else {
1556 				mbri->last_mounted[i] = strdup(get_last_mounted(
1557 					fd, mbri->sector + mbrp->mbrp_start, NULL));
1558 #if BOOTSEL
1559 				if (ombri->install == 0 &&
1560 				    strcmp(mbri->last_mounted[i], "/") == 0)
1561 					ombri->install = mbri->sector +
1562 							mbrp->mbrp_start;
1563 #endif
1564 			}
1565 #if BOOTSEL
1566 			if (mbri->mbrb.mbrbs_nametab[i][0] != 0
1567 			    && bootkey-- == 0)
1568 				ombri->bootsec = mbri->sector +
1569 							mbrp->mbrp_start;
1570 #endif
1571 		}
1572 
1573 		if (ext_base != 0) {
1574 			/* Is there a gap before the next partition? */
1575 			unsigned int limit = next_ext;
1576 			unsigned int base;
1577 			if (limit == 0)
1578 				limit = ext_size;
1579 			mbrp -= MBR_PART_COUNT;
1580 			base =mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size;
1581 			if (mbrp->mbrp_type != 0 && ext_base + limit != base) {
1582 				/* Mock up an extry for the space */
1583 				ext = calloc(1, sizeof *ext);
1584 				if (!ext)
1585 					break;
1586 				ext->sector = base;
1587 				ext->mbr.mbr_magic = htole16(MBR_MAGIC);
1588 				ext->mbr.mbr_parts[1] = mbrp[1];
1589 				ext->mbr.mbr_parts[0].mbrp_start = ptn_0_offset;
1590 				ext->mbr.mbr_parts[0].mbrp_size =
1591 				    ext_base + limit - base - ptn_0_offset;
1592 				mbrp[1].mbrp_type = MBR_PTYPE_EXT;
1593 				mbrp[1].mbrp_start = base - ext_base;
1594 				mbrp[1].mbrp_size = limit - mbrp[1].mbrp_start;
1595 				mbri->extended = ext;
1596 				ext->prev_ext = mbri;
1597 				ext->extended = NULL;
1598 				mbri = ext;
1599 				ext = NULL;
1600 			}
1601 		}
1602 
1603 		if (next_ext == 0 || ext_base + next_ext <= mbri->sector)
1604 			break;
1605 		if (ext_base == 0) {
1606 			ext_base = next_ext;
1607 			next_ext = 0;
1608 		}
1609 		ext = calloc(sizeof *ext, 1);
1610 		if (!ext)
1611 			break;
1612 		mbrs = &ext->mbr;
1613 	}
1614 
1615     bad_mbr:
1616 	free(ext);
1617 	if (fd >= 0)
1618 		close(fd);
1619 	if (rval == -1) {
1620 		memset(&mbrs->mbr_parts, 0, sizeof mbrs->mbr_parts);
1621 		mbrs->mbr_magic = htole16(MBR_MAGIC);
1622 	}
1623 	dump_mbr(ombri, "read");
1624 	return rval;
1625 }
1626 
1627 int
1628 write_mbr(const char *disk, mbr_info_t *mbri, int convert)
1629 {
1630 	char diskpath[MAXPATHLEN];
1631 	int fd, i, ret = 0;
1632 	struct mbr_partition *mbrp;
1633 	u_int32_t pstart, psize;
1634 #ifdef BOOTSEL
1635 	struct mbr_sector *mbrs;
1636 #endif
1637 	struct mbr_sector mbrsec;
1638 	mbr_info_t *ext;
1639 	uint sector;
1640 
1641 	/* Open the disk. */
1642 	fd = opendisk(disk, O_WRONLY, diskpath, sizeof(diskpath), 0);
1643 	if (fd < 0)
1644 		return -1;
1645 
1646 #ifdef BOOTSEL
1647 	/*
1648 	 * If the main boot code (appears to) contain the netbsd bootcode,
1649 	 * copy in all the menu strings and set the default keycode
1650 	 * to be that for the default partition.
1651 	 * Unfortunately we can't rely on the user having actually updated
1652 	 * to the new mbr code :-(
1653 	 */
1654 	if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_BS_MAGIC)
1655 	    || mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC)) {
1656 		int8_t key = SCAN_1;
1657 		uint offset = MBR_BS_OFFSET;
1658 		if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC))
1659 			offset = MBR_BS_OLD_OFFSET;
1660 		mbri->mbrb.mbrbs_defkey = SCAN_ENTER;
1661 		if (mbri->mbrb.mbrbs_timeo == 0)
1662 			mbri->mbrb.mbrbs_timeo = 182;	/* 10 seconds */
1663 		for (ext = mbri; ext != NULL; ext = ext->extended) {
1664 			mbrs = &ext->mbr;
1665 			mbrp = &mbrs->mbr_parts[0];
1666 			/* Ensure marker is set in each sector */
1667 			mbrs->mbr_bootsel_magic = mbri->mbr.mbr_bootsel_magic;
1668 			/* and copy in bootsel parameters */
1669 			*(struct mbr_bootsel *)((uint8_t *)mbrs + offset) =
1670 								    ext->mbrb;
1671 			for (i = 0; i < MBR_PART_COUNT; i++) {
1672 				if (ext->mbrb.mbrbs_nametab[i][0] == 0)
1673 					continue;
1674 				if (ext->sector + mbrp->mbrp_start ==
1675 								mbri->bootsec)
1676 					mbri->mbrb.mbrbs_defkey = key;
1677 				key++;
1678 			}
1679 		}
1680 		/* copy main data (again) since we've put the 'key' in */
1681 		*(struct mbr_bootsel *)((uint8_t *)&mbri->mbr + offset) =
1682 								    mbri->mbrb;
1683 	}
1684 #endif
1685 
1686 	for (ext = mbri; ext != NULL; ext = ext->extended) {
1687 		sector = ext->sector;
1688 		mbrsec = ext->mbr;	/* copy sector */
1689 		mbrp = &mbrsec.mbr_parts[0];
1690 
1691 		if (sector != 0 && ext->extended != NULL
1692 		    && ext->extended->mbr.mbr_parts[0].mbrp_type == 0) {
1693 			/* We are followed by an empty slot, collapse out */
1694 			ext = ext->extended;
1695 			/* Make us describe the next non-empty partition */
1696 			mbrp[1] = ext->mbr.mbr_parts[1];
1697 		}
1698 
1699 		for (i = 0; i < MBR_PART_COUNT; i++) {
1700 			if (mbrp[i].mbrp_start == 0 && mbrp[i].mbrp_size == 0) {
1701 				mbrp[i].mbrp_scyl = 0;
1702 				mbrp[i].mbrp_shd = 0;
1703 				mbrp[i].mbrp_ssect = 0;
1704 				mbrp[i].mbrp_ecyl = 0;
1705 				mbrp[i].mbrp_ehd = 0;
1706 				mbrp[i].mbrp_esect = 0;
1707 				continue;
1708 			}
1709 			pstart = mbrp[i].mbrp_start;
1710 			psize = mbrp[i].mbrp_size;
1711 			mbrp[i].mbrp_start = htole32(pstart);
1712 			mbrp[i].mbrp_size = htole32(psize);
1713 			if (convert) {
1714 				convert_mbr_chs(bcyl, bhead, bsec,
1715 				    &mbrp[i].mbrp_scyl, &mbrp[i].mbrp_shd,
1716 				    &mbrp[i].mbrp_ssect, pstart);
1717 				convert_mbr_chs(bcyl, bhead, bsec,
1718 				    &mbrp[i].mbrp_ecyl, &mbrp[i].mbrp_ehd,
1719 				    &mbrp[i].mbrp_esect, pstart + psize - 1);
1720 			}
1721 		}
1722 
1723 		mbrsec.mbr_magic = htole16(MBR_MAGIC);
1724 		if (pwrite(fd, &mbrsec, sizeof mbrsec,
1725 					    sector * (off_t)MBR_SECSIZE) < 0) {
1726 			ret = -1;
1727 			break;
1728 		}
1729 	}
1730 
1731 	(void)close(fd);
1732 	return ret;
1733 }
1734 
1735 int
1736 valid_mbr(struct mbr_sector *mbrs)
1737 {
1738 
1739 	return (le16toh(mbrs->mbr_magic) == MBR_MAGIC);
1740 }
1741 
1742 static void
1743 convert_mbr_chs(int cyl, int head, int sec,
1744 		uint8_t *cylp, uint8_t *headp, uint8_t *secp,
1745 		uint32_t relsecs)
1746 {
1747 	unsigned int tcyl, temp, thead, tsec;
1748 
1749 	temp = head * sec;
1750 	tcyl = relsecs / temp;
1751 	relsecs -= tcyl * temp;
1752 
1753 	thead = relsecs / sec;
1754 	tsec = relsecs - thead * sec + 1;
1755 
1756 	if (tcyl > MAXCYL)
1757 		tcyl = MAXCYL;
1758 
1759 	*cylp = MBR_PUT_LSCYL(tcyl);
1760 	*headp = thead;
1761 	*secp = MBR_PUT_MSCYLANDSEC(tcyl, tsec);
1762 }
1763 
1764 /*
1765  * This function is ONLY to be used as a last resort to provide a
1766  * hint for the user. Ports should provide a more reliable way
1767  * of getting the BIOS geometry. The i386 code, for example,
1768  * uses the BIOS geometry as passed on from the bootblocks,
1769  * and only uses this as a hint to the user when that information
1770  * is not present, or a match could not be made with a NetBSD
1771  * device.
1772  */
1773 
1774 int
1775 guess_biosgeom_from_mbr(mbr_info_t *mbri, int *cyl, int *head, daddr_t *sec)
1776 {
1777 	struct mbr_sector *mbrs = &mbri->mbr;
1778 	struct mbr_partition *parts = &mbrs->mbr_parts[0];
1779 	int xcylinders, xheads, i, j;
1780 	daddr_t xsectors;
1781 	int c1, h1, s1, c2, h2, s2;
1782 	daddr_t a1, a2;
1783 	uint64_t num, denom;
1784 
1785 	/*
1786 	 * The physical parameters may be invalid as bios geometry.
1787 	 * If we cannot determine the actual bios geometry, we are
1788 	 * better off picking a likely 'faked' geometry than leaving
1789 	 * the invalid physical one.
1790 	 */
1791 
1792 	xcylinders = pm->dlcyl;
1793 	xheads = pm->dlhead;
1794 	xsectors = pm->dlsec;
1795 	if (xcylinders > MAXCYL || xheads > MAXHEAD || xsectors > MAXSECTOR) {
1796 		xsectors = MAXSECTOR;
1797 		xheads = MAXHEAD;
1798 		xcylinders = pm->dlsize / (MAXSECTOR * MAXHEAD);
1799 		if (xcylinders > MAXCYL)
1800 			xcylinders = MAXCYL;
1801 	}
1802 	*cyl = xcylinders;
1803 	*head = xheads;
1804 	*sec = xsectors;
1805 
1806 	xheads = -1;
1807 
1808 	/* Try to deduce the number of heads from two different mappings. */
1809 	for (i = 0; i < MBR_PART_COUNT * 2 - 1; i++) {
1810 		if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
1811 			continue;
1812 		a1 -= s1;
1813 		for (j = i + 1; j < MBR_PART_COUNT * 2; j++) {
1814 			if (get_mapping(parts, j, &c2, &h2, &s2, &a2) < 0)
1815 				continue;
1816 			a2 -= s2;
1817 			num = (uint64_t)h1 * a2 - (quad_t)h2 * a1;
1818 			denom = (uint64_t)c2 * a1 - (quad_t)c1 * a2;
1819 			if (num != 0 && denom != 0 && num % denom == 0) {
1820 				xheads = (int)(num / denom);
1821 				xsectors = a1 / (c1 * xheads + h1);
1822 				break;
1823 			}
1824 		}
1825 		if (xheads != -1)
1826 			break;
1827 	}
1828 
1829 	if (xheads == -1)
1830 		return -1;
1831 
1832 	/*
1833 	 * Estimate the number of cylinders.
1834 	 * XXX relies on get_disks having been called.
1835 	 */
1836 	xcylinders = pm->dlsize / xheads / xsectors;
1837 	if (pm->dlsize != xcylinders * xheads * xsectors)
1838 		xcylinders++;
1839 
1840 	/*
1841 	 * Now verify consistency with each of the partition table entries.
1842 	 * Be willing to shove cylinders up a little bit to make things work,
1843 	 * but translation mismatches are fatal.
1844 	 */
1845 	for (i = 0; i < MBR_PART_COUNT * 2; i++) {
1846 		if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
1847 			continue;
1848 		if (c1 >= MAXCYL - 1)
1849 			/* Ignore anything that is near the CHS limit */
1850 			continue;
1851 		if (xsectors * (c1 * xheads + h1) + s1 != a1)
1852 			return -1;
1853 	}
1854 
1855 	/*
1856 	 * Everything checks out.  Reset the geometry to use for further
1857 	 * calculations.
1858 	 */
1859 	*cyl = MIN(xcylinders, MAXCYL);
1860 	*head = xheads;
1861 	*sec = xsectors;
1862 	return 0;
1863 }
1864 
1865 static int
1866 get_mapping(struct mbr_partition *parts, int i,
1867 	    int *cylinder, int *head, int *sector, daddr_t *absolute)
1868 {
1869 	struct mbr_partition *apart = &parts[i / 2];
1870 
1871 	if (apart->mbrp_type == 0)
1872 		return -1;
1873 	if (i % 2 == 0) {
1874 		*cylinder = MBR_PCYL(apart->mbrp_scyl, apart->mbrp_ssect);
1875 		*head = apart->mbrp_shd;
1876 		*sector = MBR_PSECT(apart->mbrp_ssect) - 1;
1877 		*absolute = le32toh(apart->mbrp_start);
1878 	} else {
1879 		*cylinder = MBR_PCYL(apart->mbrp_ecyl, apart->mbrp_esect);
1880 		*head = apart->mbrp_ehd;
1881 		*sector = MBR_PSECT(apart->mbrp_esect) - 1;
1882 		*absolute = le32toh(apart->mbrp_start)
1883 			+ le32toh(apart->mbrp_size) - 1;
1884 	}
1885 	/* Sanity check the data against max values */
1886 	if ((((*cylinder * MAXHEAD) + *head) * (uint32_t)MAXSECTOR + *sector) < *absolute)
1887 		/* cannot be a CHS mapping */
1888 		return -1;
1889 
1890 	return 0;
1891 }
1892 
1893 /*
1894  * Determine partition boundary alignment as fdisk(8) does.
1895  */
1896 static void
1897 get_ptn_alignment(struct mbr_partition *mbrp0)
1898 {
1899 	uint32_t ptn_0_base, ptn_0_limit;
1900 
1901 	/* Default to using 'traditional' cylinder alignment */
1902 	ptn_alignment = bhead * bsec;
1903 	ptn_0_offset = bsec;
1904 
1905 	if (mbrp0->mbrp_type != 0) {
1906 		/* Try to copy offset of first partition */
1907 		ptn_0_base = le32toh(mbrp0->mbrp_start);
1908 		ptn_0_limit = ptn_0_base + le32toh(mbrp0->mbrp_size);
1909 		if (!(ptn_0_limit & 2047)) {
1910 			/* Partition ends on a 1MB boundary, align to 1MB */
1911 			ptn_alignment = 2048;
1912 			if (ptn_0_base <= 2048
1913 			    && !(ptn_0_base & (ptn_0_base - 1))) {
1914 				/* ptn_base is a power of 2, use it */
1915 				ptn_0_offset = ptn_0_base;
1916 			}
1917 		}
1918 	} else {
1919 		/* Use 1MB offset for large (>128GB) disks */
1920 		if ((unsigned long)pm->dlsize > 128 * GIG / 512) {
1921 			ptn_alignment = 2048;
1922 			ptn_0_offset = 2048;
1923 		}
1924 	}
1925 }
1926