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