xref: /netbsd-src/usr.sbin/sysinst/mbr.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: mbr.c,v 1.5 2015/05/10 10:14:02 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 	/* Ask full/part */
1271 
1272 	part = &mbrs->mbr_parts[0];
1273 	get_ptn_alignment(part);  /* update ptn_alignment */
1274 	if (partman_go)
1275 		usefull = 0;
1276 	else {
1277 		msg_display(MSG_fullpart, pm->diskdev);
1278 		process_menu(MENU_fullpart, &usefull);
1279 	}
1280 
1281 	/* DOS fdisk label checking and value setting. */
1282 	if (usefull) {
1283 		/* Count nonempty, non-BSD partitions. */
1284 		numbsd = 0;
1285 		for (i = 0; i < MBR_PART_COUNT; i++) {
1286 			j = part[i].mbrp_type;
1287 			if (j == 0)
1288 				continue;
1289 			numbsd++;
1290 			if (j != MBR_PTYPE_NETBSD)
1291 				numbsd++;
1292 		}
1293 
1294 		/* Ask if we really want to blow away non-NetBSD stuff */
1295 		if (numbsd > 1) {
1296 			msg_display(MSG_ovrwrite);
1297 			if (!ask_noyes(NULL)) {
1298 				if (logfp)
1299 					(void)fprintf(logfp, "User answered no to destroy other data, aborting.\n");
1300 				return 0;
1301 			}
1302 		}
1303 		return(md_mbr_use_wholedisk(mbri));
1304 	}
1305 
1306 	mbr_menu = new_menu(NULL, NULL, 16, 0, -1, 15, 70,
1307 			MC_NOBOX | MC_ALWAYS_SCROLL | MC_NOCLEAR,
1308 			set_mbr_header, set_mbr_label, NULL,
1309 			NULL, MSG_Partition_table_ok);
1310 	if (mbr_menu == -1)
1311 		return 0;
1312 
1313 	/* Default to MB, and use bios geometry for cylinder size */
1314 	set_sizemultname_meg();
1315 	pm->current_cylsize = bhead * bsec;
1316 
1317 	for (;;) {
1318 		pm->ptstart = 0;
1319 		pm->ptsize = 0;
1320 		process_menu(mbr_menu, mbri);
1321 
1322 		activepart = 0;
1323 		bsdstart = 0;
1324 		bsdsize = 0;
1325 		for (ext = mbri; ext; ext = ext->extended) {
1326 			part = ext->mbr.mbr_parts;
1327 			for (i = 0; i < MBR_PART_COUNT; part++, i++) {
1328 				if (part->mbrp_flag != 0)
1329 					activepart = 1;
1330 				if (part->mbrp_type != MBR_PTYPE_NETBSD)
1331 					continue;
1332 				start = ext->sector + part->mbrp_start;
1333 				if (start == mbri->install) {
1334 					pm->ptstart = mbri->install;
1335 					pm->ptsize = part->mbrp_size;
1336 				}
1337 				if (bsdstart != 0)
1338 					bsdstart = ~0;
1339 				else {
1340 					bsdstart = start;
1341 					bsdsize = part->mbrp_size;
1342 				}
1343 			}
1344 		}
1345 
1346 		/* Install in only netbsd partition if none tagged */
1347 		if (pm->ptstart == 0 && bsdstart != ~0u) {
1348 			pm->ptstart = bsdstart;
1349 			pm->ptsize = bsdsize;
1350 		}
1351 
1352 		if (pm->ptstart == 0) {
1353 			if (bsdstart == 0)
1354 				msg_display(MSG_nobsdpart);
1355 			else
1356 				msg_display(MSG_multbsdpart, 0);
1357 			msg_display_add(MSG_reeditpart, 0);
1358 			if (!ask_yesno(NULL))
1359 				return 0;
1360 			continue;
1361 		}
1362 
1363 		if (activepart == 0) {
1364 			msg_display(MSG_noactivepart);
1365 			if (ask_yesno(NULL))
1366 				continue;
1367 		}
1368 		/* the md_check_mbr function has 3 ret codes to deal with
1369 		 * the different possible states. 0, 1, >1
1370 		 */
1371 		j = md_check_mbr(mbri);
1372 		if (j == 0)
1373 			return 0;
1374 		if (j == 1)
1375 			continue;
1376 
1377 		break;
1378 	}
1379 
1380 	free_menu(mbr_menu);
1381 
1382 	return 1;
1383 }
1384 
1385 const char *
1386 get_partname(int typ)
1387 {
1388 	int j;
1389 	static char unknown[32];
1390 
1391 	for (j = 0; part_ids[j].id != -1; j++)
1392 		if (part_ids[j].id == typ)
1393 			return part_ids[j].name;
1394 
1395 	snprintf(unknown, sizeof unknown, "Unknown (%d)", typ);
1396 	return unknown;
1397 }
1398 
1399 #ifdef BOOTSEL
1400 static int
1401 validate_and_set_names(mbr_info_t *mbri, const struct mbr_bootsel *src,
1402     uint32_t ext_base)
1403 {
1404 	size_t i, l;
1405 	const unsigned char *p;
1406 
1407 	/*
1408 	 * The 16 bit magic used to detect whether mbr_bootsel is valid
1409 	 * or not is pretty week - collisions have been seen in the wild;
1410 	 * but maybe it is just foreign tools corruption reminiscents
1411 	 * of NetBSD MBRs. Anyway, before accepting a boot menu definition,
1412 	 * make sure it is kinda "sane".
1413 	 */
1414 
1415 	for (i = 0; i < MBR_PART_COUNT; i++) {
1416 		/*
1417 		 * Make sure the name does not contain controll chars
1418 		 * (not using iscntrl due to minimalistic locale support
1419 		 * in miniroot environments) and is properly 0-terminated.
1420 		 */
1421 		for (l = 0, p = (const unsigned char *)&src->mbrbs_nametab[i];
1422 		    *p != 0; l++, p++) {
1423 			if (l >	MBR_BS_PARTNAMESIZE)
1424 				return 0;
1425 			if (*p < ' ')	/* hacky 'iscntrl' */
1426 				return 0;
1427 		}
1428 	}
1429 
1430 	memcpy(&mbri->mbrb, src, sizeof(*src));
1431 
1432 	if (ext_base == 0)
1433 		return mbri->mbrb.mbrbs_defkey - SCAN_1;
1434 	return 0;
1435 }
1436 #endif
1437 
1438 int
1439 read_mbr(const char *disk, mbr_info_t *mbri)
1440 {
1441 	struct mbr_partition *mbrp;
1442 	struct mbr_sector *mbrs = &mbri->mbr;
1443 	mbr_info_t *ext = NULL;
1444 	char diskpath[MAXPATHLEN];
1445 	int fd, i;
1446 	uint32_t ext_base = 0, next_ext = 0, ext_size = 0;
1447 	int rval = -1;
1448 #ifdef BOOTSEL
1449 	mbr_info_t *ombri = mbri;
1450 	int bootkey = 0;
1451 #endif
1452 
1453 	/*
1454 	 * Fake up a likely 'bios sectors per track' for any extended
1455 	 * partition headers we might have to produce.
1456 	 */
1457 	if (bsec == 0)
1458 		bsec = pm->dlsec;
1459 	if (bhead == 0)
1460 		bhead = pm->dlhead;
1461 	if (bcyl == 0)
1462 		bhead = pm->dlcyl;
1463 
1464 	ptn_0_offset = bsec;
1465 	/* use 1MB default offset on large disks as fdisk(8) */
1466 	if (pm->dlsize > 2048 * 1024 * 128)
1467 		ptn_0_offset = 2048;
1468 
1469 	memset(mbri, 0, sizeof *mbri);
1470 
1471 	/* Open the disk. */
1472 	fd = opendisk(disk, O_RDONLY, diskpath, sizeof(diskpath), 0);
1473 	if (fd < 0)
1474 		goto bad_mbr;
1475 
1476 	for (;;) {
1477 		if (pread(fd, mbrs, sizeof *mbrs,
1478 		    (ext_base + next_ext) * (off_t)MBR_SECSIZE) - sizeof *mbrs != 0)
1479 			break;
1480 
1481 		if (!valid_mbr(mbrs))
1482 			break;
1483 
1484 		mbrp = &mbrs->mbr_parts[0];
1485 		if (ext_base == 0) {
1486 			get_ptn_alignment(mbrp);	/* get ptn_0_offset */
1487 		} else {
1488 			/* sanity check extended chain */
1489 			if (MBR_IS_EXTENDED(mbrp[0].mbrp_type))
1490 				break;
1491 			if (mbrp[1].mbrp_type != 0 &&
1492 			    !MBR_IS_EXTENDED(mbrp[1].mbrp_type))
1493 				break;
1494 			if (mbrp[2].mbrp_type != 0 || mbrp[3].mbrp_type != 0)
1495 				break;
1496 			/* Looks ok, link into extended chain */
1497 			mbri->extended = ext;
1498 			ext->prev_ext = next_ext != 0 ? mbri : NULL;
1499 			ext->extended = NULL;
1500 			mbri = ext;
1501 			ext = NULL;
1502 		}
1503 #if BOOTSEL
1504 		if (mbrs->mbr_bootsel_magic == htole16(MBR_MAGIC)) {
1505 			/* old bootsel, grab bootsel info */
1506 			bootkey = validate_and_set_names(mbri,
1507 				(struct mbr_bootsel *)
1508 				((uint8_t *)mbrs + MBR_BS_OLD_OFFSET),
1509 				ext_base);
1510 		} else if (mbrs->mbr_bootsel_magic == htole16(MBR_BS_MAGIC)) {
1511 			/* new location */
1512 			bootkey = validate_and_set_names(mbri,
1513 			    &mbrs->mbr_bootsel, ext_base);
1514 		}
1515 		/* Save original flags for mbr code update tests */
1516 		mbri->oflags = mbri->mbrb.mbrbs_flags;
1517 #endif
1518 		mbri->sector = next_ext + ext_base;
1519 		next_ext = 0;
1520 		rval = 0;
1521 		for (i = 0; i < MBR_PART_COUNT; mbrp++, i++) {
1522 			if (mbrp->mbrp_type == 0) {
1523 				/* type is unused, discard scum */
1524 				memset(mbrp, 0, sizeof *mbrp);
1525 				continue;
1526 			}
1527 			mbrp->mbrp_start = le32toh(mbrp->mbrp_start);
1528 			mbrp->mbrp_size = le32toh(mbrp->mbrp_size);
1529 			if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
1530 				next_ext = mbrp->mbrp_start;
1531 				if (ext_base == 0)
1532 					ext_size = mbrp->mbrp_size;
1533 			} else {
1534 				mbri->last_mounted[i] = strdup(get_last_mounted(
1535 					fd, mbri->sector + mbrp->mbrp_start, NULL));
1536 #if BOOTSEL
1537 				if (ombri->install == 0 &&
1538 				    strcmp(mbri->last_mounted[i], "/") == 0)
1539 					ombri->install = mbri->sector +
1540 							mbrp->mbrp_start;
1541 #endif
1542 			}
1543 #if BOOTSEL
1544 			if (mbri->mbrb.mbrbs_nametab[i][0] != 0
1545 			    && bootkey-- == 0)
1546 				ombri->bootsec = mbri->sector +
1547 							mbrp->mbrp_start;
1548 #endif
1549 		}
1550 
1551 		if (ext_base != 0) {
1552 			/* Is there a gap before the next partition? */
1553 			unsigned int limit = next_ext;
1554 			unsigned int base;
1555 			if (limit == 0)
1556 				limit = ext_size;
1557 			mbrp -= MBR_PART_COUNT;
1558 			base =mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size;
1559 			if (mbrp->mbrp_type != 0 && ext_base + limit != base) {
1560 				/* Mock up an extry for the space */
1561 				ext = calloc(1, sizeof *ext);
1562 				if (!ext)
1563 					break;
1564 				ext->sector = base;
1565 				ext->mbr.mbr_magic = htole16(MBR_MAGIC);
1566 				ext->mbr.mbr_parts[1] = mbrp[1];
1567 				ext->mbr.mbr_parts[0].mbrp_start = ptn_0_offset;
1568 				ext->mbr.mbr_parts[0].mbrp_size =
1569 				    ext_base + limit - base - ptn_0_offset;
1570 				mbrp[1].mbrp_type = MBR_PTYPE_EXT;
1571 				mbrp[1].mbrp_start = base - ext_base;
1572 				mbrp[1].mbrp_size = limit - mbrp[1].mbrp_start;
1573 				mbri->extended = ext;
1574 				ext->prev_ext = mbri;
1575 				ext->extended = NULL;
1576 				mbri = ext;
1577 				ext = NULL;
1578 			}
1579 		}
1580 
1581 		if (next_ext == 0 || ext_base + next_ext <= mbri->sector)
1582 			break;
1583 		if (ext_base == 0) {
1584 			ext_base = next_ext;
1585 			next_ext = 0;
1586 		}
1587 		ext = calloc(sizeof *ext, 1);
1588 		if (!ext)
1589 			break;
1590 		mbrs = &ext->mbr;
1591 	}
1592 
1593     bad_mbr:
1594 	free(ext);
1595 	if (fd >= 0)
1596 		close(fd);
1597 	if (rval == -1) {
1598 		memset(&mbrs->mbr_parts, 0, sizeof mbrs->mbr_parts);
1599 		mbrs->mbr_magic = htole16(MBR_MAGIC);
1600 	}
1601 	dump_mbr(ombri, "read");
1602 	return rval;
1603 }
1604 
1605 int
1606 write_mbr(const char *disk, mbr_info_t *mbri, int convert)
1607 {
1608 	char diskpath[MAXPATHLEN];
1609 	int fd, i, ret = 0;
1610 	struct mbr_partition *mbrp;
1611 	u_int32_t pstart, psize;
1612 #ifdef BOOTSEL
1613 	struct mbr_sector *mbrs;
1614 #endif
1615 	struct mbr_sector mbrsec;
1616 	mbr_info_t *ext;
1617 	uint sector;
1618 
1619 	/* Open the disk. */
1620 	fd = opendisk(disk, O_WRONLY, diskpath, sizeof(diskpath), 0);
1621 	if (fd < 0)
1622 		return -1;
1623 
1624 #ifdef BOOTSEL
1625 	/*
1626 	 * If the main boot code (appears to) contain the netbsd bootcode,
1627 	 * copy in all the menu strings and set the default keycode
1628 	 * to be that for the default partition.
1629 	 * Unfortunately we can't rely on the user having actually updated
1630 	 * to the new mbr code :-(
1631 	 */
1632 	if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_BS_MAGIC)
1633 	    || mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC)) {
1634 		int8_t key = SCAN_1;
1635 		uint offset = MBR_BS_OFFSET;
1636 		if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC))
1637 			offset = MBR_BS_OLD_OFFSET;
1638 		mbri->mbrb.mbrbs_defkey = SCAN_ENTER;
1639 		if (mbri->mbrb.mbrbs_timeo == 0)
1640 			mbri->mbrb.mbrbs_timeo = 182;	/* 10 seconds */
1641 		for (ext = mbri; ext != NULL; ext = ext->extended) {
1642 			mbrs = &ext->mbr;
1643 			mbrp = &mbrs->mbr_parts[0];
1644 			/* Ensure marker is set in each sector */
1645 			mbrs->mbr_bootsel_magic = mbri->mbr.mbr_bootsel_magic;
1646 			/* and copy in bootsel parameters */
1647 			*(struct mbr_bootsel *)((uint8_t *)mbrs + offset) =
1648 								    ext->mbrb;
1649 			for (i = 0; i < MBR_PART_COUNT; i++) {
1650 				if (ext->mbrb.mbrbs_nametab[i][0] == 0)
1651 					continue;
1652 				if (ext->sector + mbrp->mbrp_start ==
1653 								mbri->bootsec)
1654 					mbri->mbrb.mbrbs_defkey = key;
1655 				key++;
1656 			}
1657 		}
1658 		/* copy main data (again) since we've put the 'key' in */
1659 		*(struct mbr_bootsel *)((uint8_t *)&mbri->mbr + offset) =
1660 								    mbri->mbrb;
1661 	}
1662 #endif
1663 
1664 	for (ext = mbri; ext != NULL; ext = ext->extended) {
1665 		sector = ext->sector;
1666 		mbrsec = ext->mbr;	/* copy sector */
1667 		mbrp = &mbrsec.mbr_parts[0];
1668 
1669 		if (sector != 0 && ext->extended != NULL
1670 		    && ext->extended->mbr.mbr_parts[0].mbrp_type == 0) {
1671 			/* We are followed by an empty slot, collapse out */
1672 			ext = ext->extended;
1673 			/* Make us describe the next non-empty partition */
1674 			mbrp[1] = ext->mbr.mbr_parts[1];
1675 		}
1676 
1677 		for (i = 0; i < MBR_PART_COUNT; i++) {
1678 			if (mbrp[i].mbrp_start == 0 && mbrp[i].mbrp_size == 0) {
1679 				mbrp[i].mbrp_scyl = 0;
1680 				mbrp[i].mbrp_shd = 0;
1681 				mbrp[i].mbrp_ssect = 0;
1682 				mbrp[i].mbrp_ecyl = 0;
1683 				mbrp[i].mbrp_ehd = 0;
1684 				mbrp[i].mbrp_esect = 0;
1685 				continue;
1686 			}
1687 			pstart = mbrp[i].mbrp_start;
1688 			psize = mbrp[i].mbrp_size;
1689 			mbrp[i].mbrp_start = htole32(pstart);
1690 			mbrp[i].mbrp_size = htole32(psize);
1691 			if (convert) {
1692 				convert_mbr_chs(bcyl, bhead, bsec,
1693 				    &mbrp[i].mbrp_scyl, &mbrp[i].mbrp_shd,
1694 				    &mbrp[i].mbrp_ssect, pstart);
1695 				convert_mbr_chs(bcyl, bhead, bsec,
1696 				    &mbrp[i].mbrp_ecyl, &mbrp[i].mbrp_ehd,
1697 				    &mbrp[i].mbrp_esect, pstart + psize - 1);
1698 			}
1699 		}
1700 
1701 		mbrsec.mbr_magic = htole16(MBR_MAGIC);
1702 		if (pwrite(fd, &mbrsec, sizeof mbrsec,
1703 					    sector * (off_t)MBR_SECSIZE) < 0) {
1704 			ret = -1;
1705 			break;
1706 		}
1707 	}
1708 
1709 	(void)close(fd);
1710 	return ret;
1711 }
1712 
1713 int
1714 valid_mbr(struct mbr_sector *mbrs)
1715 {
1716 
1717 	return (le16toh(mbrs->mbr_magic) == MBR_MAGIC);
1718 }
1719 
1720 static void
1721 convert_mbr_chs(int cyl, int head, int sec,
1722 		uint8_t *cylp, uint8_t *headp, uint8_t *secp,
1723 		uint32_t relsecs)
1724 {
1725 	unsigned int tcyl, temp, thead, tsec;
1726 
1727 	temp = head * sec;
1728 	tcyl = relsecs / temp;
1729 	relsecs -= tcyl * temp;
1730 
1731 	thead = relsecs / sec;
1732 	tsec = relsecs - thead * sec + 1;
1733 
1734 	if (tcyl > MAXCYL)
1735 		tcyl = MAXCYL;
1736 
1737 	*cylp = MBR_PUT_LSCYL(tcyl);
1738 	*headp = thead;
1739 	*secp = MBR_PUT_MSCYLANDSEC(tcyl, tsec);
1740 }
1741 
1742 /*
1743  * This function is ONLY to be used as a last resort to provide a
1744  * hint for the user. Ports should provide a more reliable way
1745  * of getting the BIOS geometry. The i386 code, for example,
1746  * uses the BIOS geometry as passed on from the bootblocks,
1747  * and only uses this as a hint to the user when that information
1748  * is not present, or a match could not be made with a NetBSD
1749  * device.
1750  */
1751 
1752 int
1753 guess_biosgeom_from_mbr(mbr_info_t *mbri, int *cyl, int *head, daddr_t *sec)
1754 {
1755 	struct mbr_sector *mbrs = &mbri->mbr;
1756 	struct mbr_partition *parts = &mbrs->mbr_parts[0];
1757 	int xcylinders, xheads, i, j;
1758 	daddr_t xsectors;
1759 	int c1, h1, s1, c2, h2, s2;
1760 	daddr_t a1, a2;
1761 	uint64_t num, denom;
1762 
1763 	/*
1764 	 * The physical parameters may be invalid as bios geometry.
1765 	 * If we cannot determine the actual bios geometry, we are
1766 	 * better off picking a likely 'faked' geometry than leaving
1767 	 * the invalid physical one.
1768 	 */
1769 
1770 	xcylinders = pm->dlcyl;
1771 	xheads = pm->dlhead;
1772 	xsectors = pm->dlsec;
1773 	if (xcylinders > MAXCYL || xheads > MAXHEAD || xsectors > MAXSECTOR) {
1774 		xsectors = MAXSECTOR;
1775 		xheads = MAXHEAD;
1776 		xcylinders = pm->dlsize / (MAXSECTOR * MAXHEAD);
1777 		if (xcylinders > MAXCYL)
1778 			xcylinders = MAXCYL;
1779 	}
1780 	*cyl = xcylinders;
1781 	*head = xheads;
1782 	*sec = xsectors;
1783 
1784 	xheads = -1;
1785 
1786 	/* Try to deduce the number of heads from two different mappings. */
1787 	for (i = 0; i < MBR_PART_COUNT * 2 - 1; i++) {
1788 		if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
1789 			continue;
1790 		a1 -= s1;
1791 		for (j = i + 1; j < MBR_PART_COUNT * 2; j++) {
1792 			if (get_mapping(parts, j, &c2, &h2, &s2, &a2) < 0)
1793 				continue;
1794 			a2 -= s2;
1795 			num = (uint64_t)h1 * a2 - (quad_t)h2 * a1;
1796 			denom = (uint64_t)c2 * a1 - (quad_t)c1 * a2;
1797 			if (num != 0 && denom != 0 && num % denom == 0) {
1798 				xheads = (int)(num / denom);
1799 				xsectors = a1 / (c1 * xheads + h1);
1800 				break;
1801 			}
1802 		}
1803 		if (xheads != -1)
1804 			break;
1805 	}
1806 
1807 	if (xheads == -1)
1808 		return -1;
1809 
1810 	/*
1811 	 * Estimate the number of cylinders.
1812 	 * XXX relies on get_disks having been called.
1813 	 */
1814 	xcylinders = pm->dlsize / xheads / xsectors;
1815 	if (pm->dlsize != xcylinders * xheads * xsectors)
1816 		xcylinders++;
1817 
1818 	/*
1819 	 * Now verify consistency with each of the partition table entries.
1820 	 * Be willing to shove cylinders up a little bit to make things work,
1821 	 * but translation mismatches are fatal.
1822 	 */
1823 	for (i = 0; i < MBR_PART_COUNT * 2; i++) {
1824 		if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
1825 			continue;
1826 		if (c1 >= MAXCYL - 1)
1827 			/* Ignore anything that is near the CHS limit */
1828 			continue;
1829 		if (xsectors * (c1 * xheads + h1) + s1 != a1)
1830 			return -1;
1831 	}
1832 
1833 	/*
1834 	 * Everything checks out.  Reset the geometry to use for further
1835 	 * calculations.
1836 	 */
1837 	*cyl = MIN(xcylinders, MAXCYL);
1838 	*head = xheads;
1839 	*sec = xsectors;
1840 	return 0;
1841 }
1842 
1843 static int
1844 get_mapping(struct mbr_partition *parts, int i,
1845 	    int *cylinder, int *head, int *sector, daddr_t *absolute)
1846 {
1847 	struct mbr_partition *apart = &parts[i / 2];
1848 
1849 	if (apart->mbrp_type == 0)
1850 		return -1;
1851 	if (i % 2 == 0) {
1852 		*cylinder = MBR_PCYL(apart->mbrp_scyl, apart->mbrp_ssect);
1853 		*head = apart->mbrp_shd;
1854 		*sector = MBR_PSECT(apart->mbrp_ssect) - 1;
1855 		*absolute = le32toh(apart->mbrp_start);
1856 	} else {
1857 		*cylinder = MBR_PCYL(apart->mbrp_ecyl, apart->mbrp_esect);
1858 		*head = apart->mbrp_ehd;
1859 		*sector = MBR_PSECT(apart->mbrp_esect) - 1;
1860 		*absolute = le32toh(apart->mbrp_start)
1861 			+ le32toh(apart->mbrp_size) - 1;
1862 	}
1863 	/* Sanity check the data against max values */
1864 	if ((((*cylinder * MAXHEAD) + *head) * (uint32_t)MAXSECTOR + *sector) < *absolute)
1865 		/* cannot be a CHS mapping */
1866 		return -1;
1867 
1868 	return 0;
1869 }
1870 
1871 /*
1872  * Determine partition boundary alignment as fdisk(8) does.
1873  */
1874 static void
1875 get_ptn_alignment(struct mbr_partition *mbrp0)
1876 {
1877 	uint32_t ptn_0_base, ptn_0_limit;
1878 
1879 	/* Default to using 'traditional' cylinder alignment */
1880 	ptn_alignment = bhead * bsec;
1881 	ptn_0_offset = bsec;
1882 
1883 	if (mbrp0->mbrp_type != 0) {
1884 		/* Try to copy offset of first partition */
1885 		ptn_0_base = le32toh(mbrp0->mbrp_start);
1886 		ptn_0_limit = ptn_0_base + le32toh(mbrp0->mbrp_size);
1887 		if (!(ptn_0_limit & 2047)) {
1888 			/* Partition ends on a 1MB boundary, align to 1MB */
1889 			ptn_alignment = 2048;
1890 			if (ptn_0_base <= 2048
1891 			    && !(ptn_0_base & (ptn_0_base - 1))) {
1892 				/* ptn_base is a power of 2, use it */
1893 				ptn_0_offset = ptn_0_base;
1894 			}
1895 		}
1896 	} else {
1897 		/* Use 1MB offset for large (>128GB) disks */
1898 		if (pm->dlsize > 2048 * 1024 * 128) {
1899 			ptn_alignment = 2048;
1900 			ptn_0_offset = 2048;
1901 		}
1902 	}
1903 }
1904