Home | History | Annotate | Line # | Download | only in sysinst
      1 /*	$NetBSD: mbr.c,v 1.48 2024/04/11 06:42:18 andvar 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 (at) 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 #ifdef HAVE_MBR
     68 
     69 #include <sys/param.h>
     70 #include <sys/types.h>
     71 #include <assert.h>
     72 #include <stdio.h>
     73 #include <stdlib.h>
     74 #include <unistd.h>
     75 #include <fcntl.h>
     76 #include <util.h>
     77 #include <paths.h>
     78 #include <sys/ioctl.h>
     79 #include "defs.h"
     80 #include "mbr.h"
     81 #include "md.h"
     82 #include "msg_defs.h"
     83 #include "menu_defs.h"
     84 #include "defsizes.h"
     85 #include "endian.h"
     86 
     87 #define NO_BOOTMENU (-0x100)
     88 
     89 #define MAXCYL		1023    /* Possibly 1024 */
     90 #define MAXHEAD		255     /* Possibly 256 */
     91 #define MAXSECTOR	63
     92 
     93 
     94 #define	MBR_UNKNOWN_PTYPE	94	/* arbitrary not widely used value */
     95 
     96 
     97 /* A list of predefined partition types */
     98 const struct {
     99 	unsigned int ptype;
    100 	const char *desc;
    101 } mbr_part_types_src[] = {
    102 	{ .ptype=MBR_PTYPE_NETBSD, .desc="NetBSD" },
    103 	{ .ptype=MBR_PTYPE_FAT32L, .desc="Windows FAT32, LBA" },
    104 	{ .ptype=MBR_PTYPE_EXT_LBA, .desc="Extended partition, LBA" },
    105 	{ .ptype=MBR_PTYPE_386BSD, .desc="FreeBSD/386BSD" },
    106 	{ .ptype=MBR_PTYPE_OPENBSD, .desc="OpenBSD"  },
    107 	{ .ptype=MBR_PTYPE_LNXEXT2, .desc="Linux native" },
    108 	{ .ptype=MBR_PTYPE_LNXSWAP, .desc="Linux swap" },
    109 	{ .ptype=MBR_PTYPE_NTFSVOL, .desc="NTFS volume set" },
    110 	{ .ptype=MBR_PTYPE_NTFS, .desc="NTFS" },
    111 	{ .ptype=MBR_PTYPE_PREP, .desc="PReP Boot" },
    112 #ifdef MBR_PTYPE_SOLARIS
    113 	{ .ptype=MBR_PTYPE_SOLARIS, .desc="Solaris" },
    114 #endif
    115 	{ .ptype=MBR_PTYPE_FAT12, .desc="DOS FAT12" },
    116 	{ .ptype=MBR_PTYPE_FAT16S, .desc="DOS FAT16, <32M" },
    117 	{ .ptype=MBR_PTYPE_FAT16B, .desc="DOS FAT16, >32M" },
    118 	{ .ptype=MBR_PTYPE_FAT16L, .desc="Windows FAT16, LBA" },
    119 	{ .ptype=MBR_PTYPE_FAT32, .desc="Windows FAT32" },
    120 	{ .ptype=MBR_PTYPE_EFI, .desc="(U)EFI system partition" },
    121 };
    122 
    123 /* bookeeping of available partition types (including custom ones) */
    124 struct mbr_part_type_info {
    125 	struct part_type_desc gen;	/* generic description */
    126 	char desc_buf[40], short_buf[10];
    127 	size_t next_ptype;		/* user interface order */
    128 };
    129 
    130 const struct disk_partitioning_scheme mbr_parts;
    131 struct mbr_disk_partitions {
    132 	struct disk_partitions dp, *dlabel;
    133 	mbr_info_t mbr;
    134 	uint ptn_alignment, ptn_0_offset, ext_ptn_alignment,
    135 	    geo_sec, geo_head, geo_cyl, target;
    136 };
    137 
    138 const struct disk_partitioning_scheme mbr_parts;
    139 
    140 static size_t known_part_types = 0, last_added_part_type = 0;
    141 static const size_t first_part_type = MBR_PTYPE_NETBSD;
    142 
    143 /* all partition types (we are lucky, only a fixed number is possible) */
    144 struct mbr_part_type_info mbr_gen_type_desc[256];
    145 
    146 extern const struct disk_partitioning_scheme disklabel_parts;
    147 
    148 static void convert_mbr_chs(int, int, int, uint8_t *, uint8_t *,
    149 				 uint8_t *, uint32_t);
    150 
    151 static part_id mbr_add_part(struct disk_partitions *arg,
    152     const struct disk_part_info *info, const char **errmsg);
    153 
    154 static size_t mbr_get_free_spaces(const struct disk_partitions *arg,
    155     struct disk_part_free_space *result, size_t max_num_result,
    156     daddr_t min_size, daddr_t align, daddr_t lower_bound, daddr_t ignore);
    157 
    158 static size_t mbr_type_from_gen_desc(const struct part_type_desc *desc);
    159 
    160 /*
    161  * Notes on the extended partition editor.
    162  *
    163  * The extended partition structure is actually a singly linked list.
    164  * Each of the 'mbr' sectors can only contain 2 items, the first describes
    165  * a user partition (relative to that mbr sector), the second describes
    166  * the following partition (relative to the start of the extended partition).
    167  *
    168  * The 'start' sector for the user partition is always the size of one
    169  * track - very often 63.  The extended partitions themselves should
    170  * always start on a cylinder boundary using the BIOS geometry - often
    171  * 16065 sectors per cylinder.
    172  *
    173  * The disk is also always described in increasing sector order.
    174  *
    175  * During editing we keep the mbr sectors accurate (it might have been
    176  * easier to use absolute sector numbers though), and keep a copy of the
    177  * entire sector - to preserve any information any other OS has tried
    178  * to squirrel away in the (apparently) unused space.
    179  *
    180  * Typical disk (with some small numbers):
    181  *
    182  *      0 -> a       63       37	dos
    183  *           b      100     1000	extended LBA (type 15)
    184  *
    185  *    100 -> a       63       37        user
    186  *           b      100      200	extended partition (type 5)
    187  *
    188  *    200 -> a       63       37        user
    189  *           b      200      300	extended partition (type 5)
    190  *
    191  *    300 -> a       63       37	user
    192  *           b        0        0        0 (end of chain)
    193  *
    194  */
    195 
    196 #ifndef debug_extended
    197 #define dump_mbr(mbr, msg)
    198 #else
    199 static void
    200 dump_mbr(mbr_info_t *m, const char *label)
    201 {
    202 	int i;
    203 	uint ext_base = 0;
    204 
    205 	fprintf(stderr, "\n%s: bsec %d\n", label, bsec);
    206 	do {
    207 		fprintf(stderr, "%p: %12u %p\n",
    208 		    m, m->sector, m->extended);
    209 		for (i = 0; i < MBR_PART_COUNT; i++) {
    210 			fprintf(stderr, " %*d %12u %12u %12" PRIu64,
    211 			    10,
    212 			    m->mbr.mbr_parts[i].mbrp_type,
    213 			    m->mbr.mbr_parts[i].mbrp_start,
    214 			    m->mbr.mbr_parts[i].mbrp_size,
    215 			    (uint64_t)m->mbr.mbr_parts[i].mbrp_start +
    216 				(uint64_t)m->mbr.mbr_parts[i].mbrp_size);
    217 			if (m->sector == 0 &&
    218 			    MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
    219 				ext_base = m->mbr.mbr_parts[i].mbrp_start;
    220 			if (m->sector > 0 &&
    221 			    m->mbr.mbr_parts[i].mbrp_size > 0) {
    222 				uint off = MBR_IS_EXTENDED(
    223 				    m->mbr.mbr_parts[i].mbrp_type)
    224 				    ? ext_base : m->sector;
    225 				fprintf(stderr, " -> [%u .. %u]",
    226 				    m->mbr.mbr_parts[i].mbrp_start + off,
    227 				    m->mbr.mbr_parts[i].mbrp_size +
    228 				    m->mbr.mbr_parts[i].mbrp_start + off);
    229 			}
    230 			fprintf(stderr, ",\n");
    231 			if (m->sector > 0 && i >= 1)
    232 				break;
    233 		}
    234 	} while ((m = m->extended));
    235 }
    236 #endif
    237 
    238 /*
    239  * Like pread, but handles re-blocking for non 512 byte sector disks
    240  */
    241 static ssize_t
    242 blockread(int fd, size_t secsize, void *buf, size_t nbytes, off_t offset)
    243 {
    244         ssize_t nr;
    245 	off_t sector = offset / 512;
    246         off_t offs = sector * (off_t)secsize;
    247         off_t mod = offs & (secsize - 1);
    248         off_t rnd = offs & ~(secsize - 1);
    249 	char *iobuf;
    250 
    251 	assert(nbytes <= 512);
    252 
    253 	if (secsize == 512)
    254 		return pread(fd, buf, nbytes, offset);
    255 
    256 	iobuf = malloc(secsize);
    257 	if (iobuf == NULL)
    258 		return -1;
    259 	nr = pread(fd, iobuf, secsize, rnd);
    260 	if (nr == (off_t)secsize)
    261 		memcpy(buf, &iobuf[mod], nbytes);
    262 	free(iobuf);
    263 
    264 	return nr == (off_t)secsize ? (off_t)nbytes : -1;
    265 }
    266 
    267 /*
    268  * Same for pwrite
    269  */
    270 static ssize_t
    271 blockwrite(int fd, size_t secsize, const void *buf, size_t nbytes,
    272     off_t offset)
    273 {
    274         ssize_t nr;
    275 	off_t sector = offset / secsize;
    276         off_t offs = sector * (off_t)secsize;
    277         off_t mod = offs & (secsize - 1);
    278         off_t rnd = offs & ~(secsize - 1);
    279 	char *iobuf;
    280 
    281 	assert(nbytes <= 512);
    282 
    283 	if (secsize == 512)
    284 		return pwrite(fd, buf, nbytes, offset);
    285 
    286 	iobuf = malloc(secsize);
    287 	if (iobuf == NULL)
    288 		return -1;
    289 	nr = pread(fd, iobuf, secsize, rnd);
    290 	if (nr == (off_t)secsize) {
    291 		memcpy(&iobuf[mod], buf, nbytes);
    292 		nr = pwrite(fd, iobuf, secsize, rnd);
    293 	}
    294 	free(iobuf);
    295 
    296 	return nr == (off_t)secsize ? (off_t)nbytes : -1;
    297 }
    298 
    299 static void
    300 free_last_mounted(mbr_info_t *m)
    301 {
    302 	size_t i;
    303 
    304 	for (i = 0; i < MBR_PART_COUNT; i++)
    305 		free(__UNCONST(m->last_mounted[i]));
    306 }
    307 
    308 static void
    309 free_mbr_info(mbr_info_t *m)
    310 {
    311 	if (m == NULL)
    312 		return;
    313 	free_last_mounted(m);
    314 	free(m);
    315 }
    316 
    317 /*
    318  * To be used only on ports which cannot provide any bios geometry
    319  */
    320 int
    321 set_bios_geom_with_mbr_guess(struct disk_partitions *parts)
    322 {
    323 	int cyl, head, sec;
    324 
    325 	msg_fmt_display(MSG_nobiosgeom, "%d%d%d",
    326 	    pm->dlcyl, pm->dlsec, pm->dlhead);
    327 	if (guess_biosgeom_from_parts(parts, &cyl, &head, &sec) >= 0)
    328 		msg_fmt_display_add(MSG_biosguess, "%d%d%d", cyl, head, sec);
    329 	set_bios_geom(parts, &cyl, &head, &sec);
    330 	if (parts->pscheme->change_disk_geom)
    331 		parts->pscheme->change_disk_geom(parts, cyl, head, sec);
    332 
    333 	return edit_outer_parts(parts);
    334 }
    335 
    336 static void
    337 mbr_init_chs(struct mbr_disk_partitions *parts, int ncyl, int nhead, int nsec)
    338 {
    339 	if (ncyl > MAXCYL)
    340 		ncyl = MAXCYL;
    341 	pm->current_cylsize = nhead*nsec;
    342 	pm->max_chs = (unsigned long)ncyl*nhead*nsec;
    343 	parts->geo_sec = nsec;
    344 	parts->geo_head = nhead;
    345 	parts->geo_cyl = ncyl;
    346 }
    347 
    348 /*
    349  * get C/H/S geometry from user via menu interface and
    350  * store in globals.
    351  */
    352 void
    353 set_bios_geom(struct disk_partitions *parts, int *cyl, int *head, int *sec)
    354 {
    355 	char res[80];
    356 	int bsec, bhead, bcyl;
    357 	daddr_t s;
    358 
    359 	if (parts->pscheme->change_disk_geom == NULL)
    360 		return;
    361 
    362 	msg_display_add(MSG_setbiosgeom);
    363 
    364 	do {
    365 		snprintf(res, 80, "%d", *sec);
    366 		msg_prompt_add(MSG_sectors, res, res, 80);
    367 		bsec = atoi(res);
    368 	} while (bsec <= 0 || bsec > MAXSECTOR);
    369 
    370 	do {
    371 		snprintf(res, 80, "%d", *head);
    372 		msg_prompt_add(MSG_heads, res, res, 80);
    373 		bhead = atoi(res);
    374 	} while (bhead <= 0 || bhead > MAXHEAD);
    375 	s = min(pm->dlsize, mbr_parts.size_limit);
    376 	bcyl = s / bsec / bhead;
    377 	if (s != bcyl * bsec * bhead)
    378 		bcyl++;
    379 	if (bcyl > MAXCYL)
    380 		bcyl = MAXCYL;
    381 	pm->max_chs = (unsigned long)bcyl * bhead * bsec;
    382 	pm->current_cylsize = bhead * bsec;
    383 	parts->pscheme->change_disk_geom(parts, bcyl, bhead, bsec);
    384 	*cyl = bcyl;
    385 	*head = bhead;
    386 	*sec = bsec;
    387 }
    388 
    389 static int
    390 find_mbr_space(const struct mbr_info_t *mbrs, uint *start, uint *size,
    391     uint from, uint end_of_disk, uint ignore_at, bool primary_only)
    392 {
    393 	uint sz;
    394 	int i, j;
    395 	uint s, e;
    396 	const mbr_info_t *m, *me;
    397 	bool is_extended;
    398 
    399     check_again:
    400 	m = mbrs;
    401 	sz = end_of_disk-from;
    402 	if (from >= end_of_disk)
    403 		return -1;
    404 
    405 	for (i = 0; i < MBR_PART_COUNT; i++) {
    406 		if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
    407 			continue;
    408 
    409 		is_extended = MBR_IS_EXTENDED(
    410 		    m->mbr.mbr_parts[i].mbrp_type);
    411 
    412 		s = m->mbr.mbr_parts[i].mbrp_start + m->sector;
    413 		if (s == ignore_at)
    414 			continue;
    415 		e = s + m->mbr.mbr_parts[i].mbrp_size;
    416 		if (s <= from && e > from
    417 		    && (!is_extended || primary_only)) {
    418 			if (e - 1 >= end_of_disk)
    419 				break;
    420 			if (e >= UINT_MAX) {
    421 				sz = 0;
    422 				break;
    423 			}
    424 			from = e + 1;
    425 			goto check_again;
    426 		}
    427 		if (s <= from && e > from && is_extended) {
    428 			/*
    429 			 * if we start in the extended partition,
    430 			 * we must end before its end
    431 			 */
    432 			sz = e - from;
    433 		}
    434 		if (s > from && s - from < sz)
    435 			sz = s - from;
    436 
    437 		if (is_extended) {
    438 			for (me = m->extended; me != NULL; me = me->extended) {
    439 				for (j = 0; j < MBR_PART_COUNT; j++) {
    440 					if (me->mbr.mbr_parts[j].mbrp_type ==
    441 					    MBR_PTYPE_UNUSED)
    442 						continue;
    443 
    444 					is_extended = MBR_IS_EXTENDED(
    445 					    me->mbr.mbr_parts[j].mbrp_type);
    446 
    447 					if (is_extended && i > 0)
    448 						break;
    449 
    450 					s = me->mbr.mbr_parts[j].mbrp_start +
    451 					    me->sector;
    452 					if (s == ignore_at)
    453 						continue;
    454 					e = s + me->mbr.mbr_parts[j].mbrp_size;
    455 					if (me->sector != 0 && me->sector< s)
    456 						/*
    457 						 * can not allow to overwrite
    458 						 * the ext mbr
    459 						 */
    460 						s = me->sector;
    461 					if (s <= from && e > from) {
    462 						if (e - 1 >= end_of_disk)
    463 							break;
    464 						from = e + 1;
    465 						goto check_again;
    466 					}
    467 					if (s > from && s - from < sz)
    468 						sz = s - from;
    469 
    470 				}
    471 			}
    472 		}
    473 	}
    474 
    475 	if (sz == 0)
    476 		return -1;
    477 	if (start != NULL)
    478 		*start = from;
    479 	if (size != NULL)
    480 		*size = sz;
    481 	return 0;
    482 }
    483 
    484 #ifdef BOOTSEL
    485 static int
    486 validate_and_set_names(mbr_info_t *mbri, const struct mbr_bootsel *src,
    487     uint32_t ext_base)
    488 {
    489 	size_t i, l;
    490 	const unsigned char *p;
    491 
    492 	/*
    493 	 * The 16 bit magic used to detect whether mbr_bootsel is valid
    494 	 * or not is pretty week - collisions have been seen in the wild;
    495 	 * but maybe it is just foreign tools corruption reminiscents
    496 	 * of NetBSD MBRs. Anyway, before accepting a boot menu definition,
    497 	 * make sure it is kinda "sane".
    498 	 */
    499 
    500 	for (i = 0; i < MBR_PART_COUNT; i++) {
    501 		/*
    502 		 * Make sure the name does not contain control chars
    503 		 * (not using iscntrl due to minimalistic locale support
    504 		 * in miniroot environments) and is properly 0-terminated.
    505 		 */
    506 		for (l = 0, p = (const unsigned char *)&src->mbrbs_nametab[i];
    507 		    *p != 0; l++, p++) {
    508 			if (l >	MBR_BS_PARTNAMESIZE)
    509 				return 0;
    510 			if (*p < ' ')	/* hacky 'iscntrl' */
    511 				return 0;
    512 		}
    513 	}
    514 
    515 	memcpy(&mbri->mbrb, src, sizeof(*src));
    516 
    517 	if (ext_base == 0)
    518 		return mbri->mbrb.mbrbs_defkey - SCAN_1;
    519 	return 0;
    520 }
    521 #endif
    522 
    523 static int
    524 valid_mbr(struct mbr_sector *mbrs)
    525 {
    526 
    527 	return (le16toh(mbrs->mbr_magic) == MBR_MAGIC);
    528 }
    529 
    530 static int
    531 read_mbr(const char *disk, size_t secsize, mbr_info_t *mbri,
    532      struct mbr_disk_partitions *parts)
    533 {
    534 	struct mbr_partition *mbrp;
    535 	struct mbr_sector *mbrs = &mbri->mbr;
    536 	mbr_info_t *ext = NULL;
    537 	char diskpath[MAXPATHLEN];
    538 	int fd, i;
    539 	uint32_t ext_base = 0, next_ext = 0;
    540 	int rval = -1;
    541 #ifdef BOOTSEL
    542 	mbr_info_t *ombri = mbri;
    543 	int bootkey = 0;
    544 #endif
    545 
    546 	memset(mbri, 0, sizeof *mbri);
    547 
    548 	/* Open the disk. */
    549 	fd = opendisk(disk, O_RDONLY, diskpath, sizeof(diskpath), 0);
    550 	if (fd < 0)
    551 		goto bad_mbr;
    552 
    553 	for (;;) {
    554 		if (blockread(fd, secsize, mbrs, sizeof *mbrs,
    555 		    (ext_base + next_ext) * (off_t)MBR_SECSIZE)
    556 		    - sizeof *mbrs != 0)
    557 			break;
    558 
    559 		if (!valid_mbr(mbrs))
    560 			break;
    561 
    562 		mbrp = &mbrs->mbr_parts[0];
    563 		if (ext_base != 0) {
    564 			/* sanity check extended chain */
    565 			if (MBR_IS_EXTENDED(mbrp[0].mbrp_type))
    566 				break;
    567 			if (mbrp[1].mbrp_type != MBR_PTYPE_UNUSED &&
    568 			    !MBR_IS_EXTENDED(mbrp[1].mbrp_type))
    569 				break;
    570 			if (mbrp[2].mbrp_type != MBR_PTYPE_UNUSED
    571 			    || mbrp[3].mbrp_type != MBR_PTYPE_UNUSED)
    572 				break;
    573 			/* Looks ok, link into extended chain */
    574 			mbri->extended = ext;
    575 			ext->extended = NULL;
    576 			mbri = ext;
    577 			ext = NULL;
    578 		}
    579 #if BOOTSEL
    580 		if (mbrs->mbr_bootsel_magic == htole16(MBR_MAGIC)) {
    581 			/* old bootsel, grab bootsel info */
    582 			bootkey = validate_and_set_names(mbri,
    583 				(struct mbr_bootsel *)
    584 				((uint8_t *)mbrs + MBR_BS_OLD_OFFSET),
    585 				ext_base);
    586 		} else if (mbrs->mbr_bootsel_magic == htole16(MBR_BS_MAGIC)) {
    587 			/* new location */
    588 			bootkey = validate_and_set_names(mbri,
    589 			    &mbrs->mbr_bootsel, ext_base);
    590 		}
    591 		/* Save original flags for mbr code update tests */
    592 		mbri->oflags = mbri->mbrb.mbrbs_flags;
    593 #endif
    594 		mbri->sector = next_ext + ext_base;
    595 		next_ext = 0;
    596 		rval = 0;
    597 		for (i = 0; i < MBR_PART_COUNT; mbrp++, i++) {
    598 			if (mbrp->mbrp_type == MBR_PTYPE_UNUSED) {
    599 				/* type is unused, discard scum */
    600 				memset(mbrp, 0, sizeof *mbrp);
    601 				continue;
    602 			}
    603 			mbrp->mbrp_start = le32toh(mbrp->mbrp_start);
    604 			mbrp->mbrp_size = le32toh(mbrp->mbrp_size);
    605 			if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
    606 				next_ext = mbrp->mbrp_start;
    607 			} else {
    608 				uint flags = 0;
    609 				if (mbrp->mbrp_type == MBR_PTYPE_NETBSD) {
    610 					flags |= GLM_LIKELY_FFS;
    611 					if (parts->target == ~0U)
    612 						parts->target =
    613 						    mbri->sector +
    614 						    mbrp->mbrp_start;
    615 				} else if (mbrp->mbrp_type == MBR_PTYPE_FAT12 ||
    616 				    mbrp->mbrp_type == MBR_PTYPE_FAT16S ||
    617 				    mbrp->mbrp_type == MBR_PTYPE_FAT16B ||
    618 				    mbrp->mbrp_type == MBR_PTYPE_FAT32 ||
    619 				    mbrp->mbrp_type == MBR_PTYPE_FAT32L ||
    620 				    mbrp->mbrp_type == MBR_PTYPE_FAT16L ||
    621 				    mbrp->mbrp_type == MBR_PTYPE_EFI)
    622 					flags |= GLM_MAYBE_FAT32;
    623 				else if (mbrp->mbrp_type == MBR_PTYPE_NTFS)
    624 					flags |= GLM_MAYBE_NTFS;
    625 				if (flags != 0) {
    626 					const char *mount = get_last_mounted(
    627 					    fd, mbri->sector + mbrp->mbrp_start,
    628 					    &mbri->fs_type[i],
    629 					    &mbri->fs_sub_type[i],
    630 					    flags);
    631 					char *p = strdup(mount);
    632 					canonicalize_last_mounted(p);
    633 					mbri->last_mounted[i] = p;
    634 				}
    635 			}
    636 #if BOOTSEL
    637 			if (mbri->mbrb.mbrbs_nametab[i][0] != 0
    638 			    && bootkey-- == 0)
    639 				ombri->bootsec = mbri->sector +
    640 							mbrp->mbrp_start;
    641 #endif
    642 		}
    643 
    644 		if (next_ext == 0 || ext_base + next_ext <= mbri->sector)
    645 			break;
    646 		if (ext_base == 0) {
    647 			ext_base = next_ext;
    648 			next_ext = 0;
    649 		}
    650 		ext = calloc(1, sizeof *ext);
    651 		if (!ext)
    652 			break;
    653 		mbrs = &ext->mbr;
    654 	}
    655 
    656     bad_mbr:
    657 	free_mbr_info(ext);
    658 	if (fd >= 0)
    659 		close(fd);
    660 	if (rval == -1) {
    661 		memset(&mbrs->mbr_parts, 0, sizeof mbrs->mbr_parts);
    662 		mbrs->mbr_magic = htole16(MBR_MAGIC);
    663 	}
    664 	dump_mbr(ombri, "read");
    665 	return rval;
    666 }
    667 
    668 static int
    669 write_mbr(const char *disk, size_t secsize, mbr_info_t *mbri, int bsec,
    670     int bhead, int bcyl)
    671 {
    672 	char diskpath[MAXPATHLEN];
    673 	int fd, i, ret = 0, bits = 0;
    674 	struct mbr_partition *mbrp;
    675 	u_int32_t pstart, psize;
    676 #ifdef BOOTSEL
    677 	struct mbr_sector *mbrs;
    678 #endif
    679 	struct mbr_sector mbrsec;
    680 	mbr_info_t *ext;
    681 	uint sector;
    682 
    683 	dump_mbr(mbri, "write");
    684 
    685 	/* Open the disk. */
    686 	fd = opendisk(disk, secsize == 512 ? O_WRONLY : O_RDWR,
    687 	    diskpath, sizeof(diskpath), 0);
    688 	if (fd < 0)
    689 		return -1;
    690 
    691 	/* Remove all wedges */
    692 	if (ioctl(fd, DIOCRMWEDGES, &bits) == -1)
    693 		return -1;
    694 
    695 #ifdef BOOTSEL
    696 	/*
    697 	 * If the main boot code (appears to) contain the netbsd bootcode,
    698 	 * copy in all the menu strings and set the default keycode
    699 	 * to be that for the default partition.
    700 	 * Unfortunately we can't rely on the user having actually updated
    701 	 * to the new mbr code :-(
    702 	 */
    703 	if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_BS_MAGIC)
    704 	    || mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC)) {
    705 		int8_t key = SCAN_1;
    706 		uint offset = MBR_BS_OFFSET;
    707 		if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC))
    708 			offset = MBR_BS_OLD_OFFSET;
    709 		mbri->mbrb.mbrbs_defkey = SCAN_ENTER;
    710 		if (mbri->mbrb.mbrbs_timeo == 0)
    711 			mbri->mbrb.mbrbs_timeo = 182;	/* 10 seconds */
    712 		for (ext = mbri; ext != NULL; ext = ext->extended) {
    713 			mbrs = &ext->mbr;
    714 			mbrp = &mbrs->mbr_parts[0];
    715 			/* Ensure marker is set in each sector */
    716 			mbrs->mbr_bootsel_magic = mbri->mbr.mbr_bootsel_magic;
    717 			/* and copy in bootsel parameters */
    718 			*(struct mbr_bootsel *)((uint8_t *)mbrs + offset) =
    719 								    ext->mbrb;
    720 			for (i = 0; i < MBR_PART_COUNT; i++) {
    721 				if (ext->mbrb.mbrbs_nametab[i][0] == 0)
    722 					continue;
    723 				if (ext->sector + mbrp->mbrp_start ==
    724 								mbri->bootsec)
    725 					mbri->mbrb.mbrbs_defkey = key;
    726 				key++;
    727 			}
    728 		}
    729 		/* copy main data (again) since we've put the 'key' in */
    730 		*(struct mbr_bootsel *)((uint8_t *)&mbri->mbr + offset) =
    731 								    mbri->mbrb;
    732 	}
    733 #endif
    734 
    735 	for (ext = mbri; ext != NULL; ext = ext->extended) {
    736 		memset(mbri->wedge, 0, sizeof mbri->wedge);
    737 		sector = ext->sector;
    738 		mbrsec = ext->mbr;	/* copy sector */
    739 		mbrp = &mbrsec.mbr_parts[0];
    740 
    741 		if (sector != 0 && ext->extended != NULL
    742 		    && ext->extended->mbr.mbr_parts[0].mbrp_type
    743 		    == MBR_PTYPE_UNUSED) {
    744 
    745 			/*
    746 			 * This should never happen nowadays, old code
    747 			 * inserted empty ext sectors in the chain to
    748 			 * help the gui out - we do not do that anymore.
    749 			 */
    750 			assert(false);
    751 
    752 			/* We are followed by an empty slot, collapse out */
    753 			ext = ext->extended;
    754 			/* Make us describe the next non-empty partition */
    755 			mbrp[1] = ext->mbr.mbr_parts[1];
    756 		}
    757 
    758 		for (i = 0; i < MBR_PART_COUNT; i++) {
    759 			if (mbrp[i].mbrp_start == 0 && mbrp[i].mbrp_size == 0) {
    760 				mbrp[i].mbrp_scyl = 0;
    761 				mbrp[i].mbrp_shd = 0;
    762 				mbrp[i].mbrp_ssect = 0;
    763 				mbrp[i].mbrp_ecyl = 0;
    764 				mbrp[i].mbrp_ehd = 0;
    765 				mbrp[i].mbrp_esect = 0;
    766 				continue;
    767 			}
    768 			pstart = mbrp[i].mbrp_start;
    769 			psize = mbrp[i].mbrp_size;
    770 			mbrp[i].mbrp_start = htole32(pstart);
    771 			mbrp[i].mbrp_size = htole32(psize);
    772 			if (bsec && bcyl && bhead) {
    773 				convert_mbr_chs(bcyl, bhead, bsec,
    774 				    &mbrp[i].mbrp_scyl, &mbrp[i].mbrp_shd,
    775 				    &mbrp[i].mbrp_ssect, pstart);
    776 				convert_mbr_chs(bcyl, bhead, bsec,
    777 				    &mbrp[i].mbrp_ecyl, &mbrp[i].mbrp_ehd,
    778 				    &mbrp[i].mbrp_esect, pstart + psize - 1);
    779 			}
    780 		}
    781 
    782 		mbrsec.mbr_magic = htole16(MBR_MAGIC);
    783 		if (blockwrite(fd, secsize, &mbrsec, sizeof mbrsec,
    784 					    sector * (off_t)MBR_SECSIZE) < 0) {
    785 			ret = -1;
    786 			break;
    787 		}
    788 	}
    789 
    790 	(void)close(fd);
    791 	return ret;
    792 }
    793 
    794 static void
    795 convert_mbr_chs(int cyl, int head, int sec,
    796 		uint8_t *cylp, uint8_t *headp, uint8_t *secp,
    797 		uint32_t relsecs)
    798 {
    799 	unsigned int tcyl, temp, thead, tsec;
    800 
    801 	temp = head * sec;
    802 	tcyl = relsecs / temp;
    803 	relsecs -= tcyl * temp;
    804 
    805 	thead = relsecs / sec;
    806 	tsec = relsecs - thead * sec + 1;
    807 
    808 	if (tcyl > MAXCYL)
    809 		tcyl = MAXCYL;
    810 
    811 	*cylp = MBR_PUT_LSCYL(tcyl);
    812 	*headp = thead;
    813 	*secp = MBR_PUT_MSCYLANDSEC(tcyl, tsec);
    814 }
    815 
    816 /*
    817  * This function is ONLY to be used as a last resort to provide a
    818  * hint for the user. Ports should provide a more reliable way
    819  * of getting the BIOS geometry. The i386 code, for example,
    820  * uses the BIOS geometry as passed on from the bootblocks,
    821  * and only uses this as a hint to the user when that information
    822  * is not present, or a match could not be made with a NetBSD
    823  * device.
    824  */
    825 int
    826 guess_biosgeom_from_parts(struct disk_partitions *parts,
    827     int *cyl, int *head, int *sec)
    828 {
    829 
    830 	/*
    831 	 * The physical parameters may be invalid as bios geometry.
    832 	 * If we cannot determine the actual bios geometry, we are
    833 	 * better off picking a likely 'faked' geometry than leaving
    834 	 * the invalid physical one.
    835 	 */
    836 
    837 	int xcylinders = pm->dlcyl;
    838 	int xheads = pm->dlhead;
    839 	daddr_t xsectors = pm->dlsec;
    840 	daddr_t xsize = min(pm->dlsize, mbr_parts.size_limit);
    841 	if (xcylinders > MAXCYL || xheads > MAXHEAD || xsectors > MAXSECTOR) {
    842 		xsectors = MAXSECTOR;
    843 		xheads = MAXHEAD;
    844 		xcylinders = xsize / (MAXSECTOR * MAXHEAD);
    845 		if (xcylinders > MAXCYL)
    846 			xcylinders = MAXCYL;
    847 	}
    848 	*cyl = xcylinders;
    849 	*head = xheads;
    850 	*sec = xsectors;
    851 
    852 	if (parts->pscheme->guess_disk_geom == NULL)
    853 		return -1;
    854 
    855 	return parts->pscheme->guess_disk_geom(parts, cyl, head, sec);
    856 }
    857 
    858 static int
    859 mbr_comp_part_entry(const void *a, const void *b)
    860 {
    861 	const struct mbr_partition *part_a = a,
    862 		*part_b = b;
    863 
    864 	if (part_a->mbrp_type == MBR_PTYPE_UNUSED
    865 	    && part_b->mbrp_type != MBR_PTYPE_UNUSED)
    866 		return 1;
    867 
    868 	if (part_b->mbrp_type == MBR_PTYPE_UNUSED
    869 	    && part_a->mbrp_type != MBR_PTYPE_UNUSED)
    870 		return -1;
    871 
    872 	return part_a->mbrp_start < part_b->mbrp_start ? -1 : 1;
    873 }
    874 
    875 static void
    876 mbr_sort_main_mbr(struct mbr_sector *m)
    877 {
    878 	qsort(&m->mbr_parts[0], MBR_PART_COUNT,
    879 	    sizeof(m->mbr_parts[0]), mbr_comp_part_entry);
    880 }
    881 
    882 static void
    883 mbr_init_default_alignments(struct mbr_disk_partitions *parts, uint track)
    884 {
    885 	if (track == 0)
    886 		track = 16065;
    887 
    888 	assert(parts->dp.disk_size > 0);
    889 	if (parts->dp.disk_size < 0)
    890 		return;
    891 
    892 	parts->ptn_0_offset = parts->geo_sec;
    893 
    894 	/* Use 1MB offset/alignment for large (>128GB) disks */
    895 	if (parts->dp.disk_size > HUGE_DISK_SIZE) {
    896 		parts->ptn_alignment = 2048;
    897 	} else if (parts->dp.disk_size > TINY_DISK_SIZE) {
    898 		parts->ptn_alignment = 64;
    899 	} else {
    900 		parts->ptn_alignment = 1;
    901 	}
    902 	parts->ext_ptn_alignment = track;
    903 }
    904 
    905 static struct disk_partitions *
    906 mbr_create_new(const char *disk, daddr_t start, daddr_t len,
    907     bool is_boot_drive, struct disk_partitions *parent)
    908 {
    909 	struct mbr_disk_partitions *parts;
    910 	struct disk_geom geo;
    911 
    912 	assert(start == 0);
    913 	if (start != 0)
    914 		return NULL;
    915 
    916 	parts = calloc(1, sizeof(*parts));
    917 	if (!parts)
    918 		return NULL;
    919 
    920 	parts->dp.pscheme = &mbr_parts;
    921 	parts->dp.disk = strdup(disk);
    922 	if (len > mbr_parts.size_limit)
    923 		len = mbr_parts.size_limit;
    924 	parts->dp.disk_start = start;
    925 	parts->dp.disk_size = len;
    926 	parts->dp.free_space = len-1;
    927 	parts->dp.bytes_per_sector = 512;
    928 	parts->geo_sec = MAXSECTOR;
    929 	parts->geo_head = MAXHEAD;
    930 	parts->geo_cyl = len/MAXHEAD/MAXSECTOR+1;
    931 	parts->target = ~0U;
    932 
    933 	if (get_disk_geom(disk, &geo)) {
    934 		parts->geo_sec = geo.dg_nsectors;
    935 		parts->geo_head = geo.dg_ntracks;
    936 		parts->geo_cyl = geo.dg_ncylinders;
    937 		parts->dp.bytes_per_sector = geo.dg_secsize;
    938 	}
    939 
    940 	mbr_init_default_alignments(parts, 0);
    941 
    942 	return &parts->dp;
    943 }
    944 
    945 static void
    946 mbr_calc_free_space(struct mbr_disk_partitions *parts)
    947 {
    948 	size_t i;
    949 	mbr_info_t *m;
    950 
    951 	parts->dp.free_space = parts->dp.disk_size - 1;
    952 	parts->dp.num_part = 0;
    953 	m = &parts->mbr;
    954 	do {
    955 		for (i = 0; i < MBR_PART_COUNT; i++) {
    956 			if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
    957 				continue;
    958 
    959 			if (m != &parts->mbr && i > 0 &&
    960 			    MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
    961 				break;
    962 
    963 			parts->dp.num_part++;
    964 			if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
    965 				continue;
    966 
    967 			daddr_t psize = m->mbr.mbr_parts[i].mbrp_size;
    968 			if (m != &parts->mbr)
    969 				psize += m->mbr.mbr_parts[i].mbrp_start;
    970 
    971 			if (psize > parts->dp.free_space)
    972 				parts->dp.free_space = 0;
    973 			else
    974 				parts->dp.free_space -= psize;
    975 		}
    976 	} while ((m = m->extended));
    977 }
    978 
    979 static struct disk_partitions *
    980 mbr_read_from_disk(const char *disk, daddr_t start, daddr_t len, size_t bps,
    981     const struct disk_partitioning_scheme *scheme)
    982 {
    983 	struct mbr_disk_partitions *parts;
    984 
    985 	assert(start == 0);
    986 	if (start != 0)
    987 		return NULL;
    988 
    989 	parts = calloc(1, sizeof(*parts));
    990 	if (!parts)
    991 		return NULL;
    992 
    993 	parts->dp.pscheme = scheme;
    994 	parts->dp.disk = strdup(disk);
    995 	if (len >= mbr_parts.size_limit)
    996 		len = mbr_parts.size_limit;
    997 	parts->dp.disk_start = start;
    998 	parts->dp.disk_size = len;
    999 	parts->geo_sec = MAXSECTOR;
   1000 	parts->geo_head = MAXHEAD;
   1001 	parts->geo_cyl = len/MAXHEAD/MAXSECTOR+1;
   1002 	parts->dp.bytes_per_sector = bps;
   1003 	parts->target = ~0U;
   1004 	mbr_init_default_alignments(parts, 0);
   1005 	if (read_mbr(disk, parts->dp.bytes_per_sector, &parts->mbr, parts)
   1006 	     == -1) {
   1007 		free(parts);
   1008 		return NULL;
   1009 	}
   1010 	mbr_calc_free_space(parts);
   1011 	if (parts->dp.num_part == 1 &&
   1012 	    parts->dp.free_space < parts->ptn_alignment) {
   1013 		struct disk_part_info info;
   1014 
   1015 		/*
   1016 		 * Check if this is a GPT protective MBR
   1017 		 */
   1018 		if (parts->dp.pscheme->get_part_info(&parts->dp, 0, &info)
   1019 		    && info.nat_type != NULL
   1020 		    && mbr_type_from_gen_desc(info.nat_type) == 0xEE) {
   1021 			parts->dp.pscheme->free(&parts->dp);
   1022 			return NULL;
   1023 		}
   1024 	}
   1025 
   1026 	return &parts->dp;
   1027 }
   1028 
   1029 static bool
   1030 mbr_write_to_disk(struct disk_partitions *new_state)
   1031 {
   1032 	struct mbr_disk_partitions *parts =
   1033 	    (struct mbr_disk_partitions *)new_state;
   1034 	unsigned long bsec, bhead, bcyl;
   1035 	daddr_t t;
   1036 
   1037 	assert(parts->geo_sec != 0);
   1038 	if (parts->geo_sec != 0) {
   1039 		bsec = parts->geo_sec;
   1040 		bhead = parts->ext_ptn_alignment / bsec;
   1041 	} else {
   1042 		bsec = MAXSECTOR;
   1043 		bhead = MAXHEAD;
   1044 	}
   1045 	t = bsec * bhead;
   1046 	assert(t != 0);
   1047 	if ((daddr_t)(1UL<<10) * t <= parts->dp.disk_size)
   1048 		bcyl = (1UL<<10) - 1;
   1049 	else
   1050 		bcyl = (unsigned long)(parts->dp.disk_size / t);
   1051 
   1052 	return write_mbr(parts->dp.disk, parts->dp.bytes_per_sector,
   1053 	    &parts->mbr, bsec, bhead, bcyl) == 0;
   1054 }
   1055 
   1056 static bool
   1057 mbr_change_disk_geom(struct disk_partitions *arg, int ncyl, int nhead,
   1058     int nsec)
   1059 {
   1060 	struct mbr_disk_partitions *parts = (struct mbr_disk_partitions *)arg;
   1061 	daddr_t ptn_0_base, ptn_0_limit;
   1062 	struct disk_part_info info;
   1063 
   1064 	/* Default to using 'traditional' cylinder alignment */
   1065 	mbr_init_chs(parts, ncyl, nhead, nsec);
   1066 	mbr_init_default_alignments(parts, nhead * nsec);
   1067 
   1068 	if (parts->dp.disk_size <= TINY_DISK_SIZE) {
   1069 		set_default_sizemult(arg->disk,
   1070 		    parts->dp.bytes_per_sector, parts->dp.bytes_per_sector);
   1071 		return true;
   1072 	}
   1073 
   1074 	if (parts->dp.num_part > 0 &&
   1075 	    parts->dp.pscheme->get_part_info(arg, 0, &info)) {
   1076 
   1077 		/* Try to copy offset of first partition */
   1078 		ptn_0_base = info.start;
   1079 		ptn_0_limit = info.start + info.size;
   1080 		if (!(ptn_0_limit & 2047)) {
   1081 			/* Partition ends on a 1MB boundary, align to 1MB */
   1082 			parts->ptn_alignment = 2048;
   1083 			if ((ptn_0_base <= 2048
   1084 			    && !(ptn_0_base & (ptn_0_base - 1)))
   1085 			    || (ptn_0_base < parts->ptn_0_offset)) {
   1086 				/*
   1087 				 * If ptn_base is a power of 2, use it.
   1088 				 * Also use it if the first partition
   1089 				 * already is close to the beginning
   1090 				 * of the disk and we can't enforce
   1091 				 * better alignment.
   1092 				 */
   1093 				parts->ptn_0_offset = ptn_0_base;
   1094 			}
   1095 		}
   1096 	}
   1097 	set_default_sizemult(arg->disk, MEG, parts->dp.bytes_per_sector);
   1098 	return true;
   1099 }
   1100 
   1101 static size_t
   1102 mbr_type_from_gen_desc(const struct part_type_desc *desc)
   1103 {
   1104 	for (size_t i = 0; i < __arraycount(mbr_gen_type_desc); i++)
   1105 		if (&mbr_gen_type_desc[i].gen == desc)
   1106 			return i;
   1107 
   1108 	return SIZE_T_MAX;
   1109 }
   1110 
   1111 static enum part_type
   1112 mbr_map_part_type(unsigned int t)
   1113 {
   1114 	/* Map some special MBR partition types */
   1115 	switch (t) {
   1116 	case MBR_PTYPE_FAT32:
   1117 	case MBR_PTYPE_FAT32L:
   1118 	case MBR_PTYPE_FAT16S:
   1119 	case MBR_PTYPE_FAT16B:
   1120 	case MBR_PTYPE_FAT16L:
   1121 	case MBR_PTYPE_FAT12:
   1122 	case MBR_PTYPE_FT_FAT32:
   1123 	case MBR_PTYPE_FT_FAT32_EXT:
   1124 		return PT_FAT;
   1125 	case MBR_PTYPE_EFI:
   1126 		return PT_EFI_SYSTEM;
   1127 	case MBR_PTYPE_LNXEXT2:
   1128 		return PT_EXT2;
   1129 	case MBR_PTYPE_NETBSD:
   1130 		return PT_root;
   1131 	}
   1132 
   1133 	return PT_unknown;
   1134 }
   1135 
   1136 static void
   1137 map_mbr_part_types(void)
   1138 {
   1139 
   1140 	for (size_t i = 0; i < __arraycount(mbr_part_types_src); i++) {
   1141 		unsigned int v = mbr_part_types_src[i].ptype;
   1142 
   1143 		snprintf(mbr_gen_type_desc[v].short_buf,
   1144 		    sizeof(mbr_gen_type_desc[v].short_buf), "%u", v);
   1145 		mbr_gen_type_desc[v].gen.short_desc =
   1146 		    mbr_gen_type_desc[v].short_buf;
   1147 		mbr_gen_type_desc[v].gen.description =
   1148 		    mbr_part_types_src[i].desc;
   1149 		mbr_gen_type_desc[v].gen.generic_ptype = mbr_map_part_type(v);
   1150 		mbr_gen_type_desc[v].next_ptype = ~0U;
   1151 		mbr_gen_type_desc[last_added_part_type].next_ptype = v;
   1152 		known_part_types++;
   1153 		last_added_part_type = v;
   1154 	}
   1155 }
   1156 
   1157 static size_t
   1158 mbr_get_part_type_count(void)
   1159 {
   1160 	if (known_part_types == 0)
   1161 		map_mbr_part_types();
   1162 
   1163 	return known_part_types;
   1164 }
   1165 
   1166 static const struct part_type_desc *
   1167 mbr_get_fs_part_type(enum part_type pt, unsigned fs_type, unsigned sub_type)
   1168 {
   1169 	if (known_part_types == 0)
   1170 		map_mbr_part_types();
   1171 
   1172 	switch (fs_type) {
   1173 	case FS_BSDFFS:
   1174 		return &mbr_gen_type_desc[MBR_PTYPE_NETBSD].gen;
   1175 	case FS_EX2FS:
   1176 		return &mbr_gen_type_desc[MBR_PTYPE_LNXEXT2].gen;
   1177 	case FS_MSDOS:
   1178 		if (sub_type == 0)
   1179 			sub_type = MBR_PTYPE_FAT32L;
   1180 
   1181 		switch (sub_type) {
   1182 		case MBR_PTYPE_FAT12:
   1183 		case MBR_PTYPE_FAT16S:
   1184 		case MBR_PTYPE_FAT16B:
   1185 		case MBR_PTYPE_FAT32:
   1186 		case MBR_PTYPE_FAT32L:
   1187 		case MBR_PTYPE_FAT16L:
   1188 			return &mbr_gen_type_desc[sub_type].gen;
   1189 		}
   1190 		break;
   1191 	case FS_EFI_SP:
   1192 		return &mbr_gen_type_desc[MBR_PTYPE_EFI].gen;
   1193 	}
   1194 
   1195 	return NULL;
   1196 }
   1197 
   1198 static const struct part_type_desc *
   1199 mbr_get_part_type(size_t index)
   1200 {
   1201 	size_t i, no;
   1202 
   1203 	if (known_part_types == 0)
   1204 		map_mbr_part_types();
   1205 
   1206 	if (index >= known_part_types)
   1207 		return NULL;
   1208 
   1209 	for (i = first_part_type, no = 0; i < __arraycount(mbr_gen_type_desc)
   1210 	    && no != index;  no++)
   1211 		i = mbr_gen_type_desc[i].next_ptype;
   1212 
   1213 	if (i >= __arraycount(mbr_gen_type_desc))
   1214 		return NULL;
   1215 	return &mbr_gen_type_desc[i].gen;
   1216 }
   1217 
   1218 static const struct part_type_desc *
   1219 mbr_new_custom_part_type(unsigned int v)
   1220 {
   1221 	snprintf(mbr_gen_type_desc[v].short_buf,
   1222 	    sizeof(mbr_gen_type_desc[v].short_buf), "%u", v);
   1223 	snprintf(mbr_gen_type_desc[v].desc_buf,
   1224 	     sizeof(mbr_gen_type_desc[v].desc_buf), "%s (%u)",
   1225 	    msg_string(MSG_custom_type), v);
   1226 	mbr_gen_type_desc[v].gen.short_desc = mbr_gen_type_desc[v].short_buf;
   1227 	mbr_gen_type_desc[v].gen.description = mbr_gen_type_desc[v].desc_buf;
   1228 	mbr_gen_type_desc[v].gen.generic_ptype = mbr_map_part_type(v);
   1229 	mbr_gen_type_desc[v].next_ptype = ~0U;
   1230 	mbr_gen_type_desc[last_added_part_type].next_ptype = v;
   1231 	known_part_types++;
   1232 	last_added_part_type = v;
   1233 
   1234 	return &mbr_gen_type_desc[v].gen;
   1235 }
   1236 
   1237 static const struct part_type_desc *
   1238 mbr_custom_part_type(const char *custom, const char **err_msg)
   1239 {
   1240 	unsigned long v;
   1241 	char *endp;
   1242 
   1243 	if (known_part_types == 0)
   1244 		map_mbr_part_types();
   1245 
   1246 	v = strtoul(custom, &endp, 10);
   1247 	if (v > 255 || (v == 0 && *endp != 0)) {
   1248 		if (err_msg != NULL)
   1249 			*err_msg = msg_string(MSG_mbr_type_invalid);
   1250 		return NULL;
   1251 	}
   1252 
   1253 	if (mbr_gen_type_desc[v].gen.short_desc != NULL)
   1254 		return &mbr_gen_type_desc[v].gen;
   1255 
   1256 	return mbr_new_custom_part_type(v);
   1257 }
   1258 
   1259 static const struct part_type_desc *
   1260 mbr_create_unknown_part_type(void)
   1261 {
   1262 
   1263 	if (mbr_gen_type_desc[MBR_UNKNOWN_PTYPE].gen.short_desc != NULL)
   1264 		return &mbr_gen_type_desc[MBR_UNKNOWN_PTYPE].gen;
   1265 
   1266 	return mbr_new_custom_part_type(MBR_UNKNOWN_PTYPE);
   1267 }
   1268 
   1269 static const struct part_type_desc *
   1270 mbr_get_gen_type_desc(unsigned int pt)
   1271 {
   1272 
   1273 	if (known_part_types == 0)
   1274 		map_mbr_part_types();
   1275 
   1276 	if (pt >= __arraycount(mbr_gen_type_desc))
   1277 		return NULL;
   1278 
   1279 	if (mbr_gen_type_desc[pt].gen.short_desc != NULL)
   1280 		return &mbr_gen_type_desc[pt].gen;
   1281 
   1282 	return mbr_new_custom_part_type(pt);
   1283 }
   1284 
   1285 static const struct part_type_desc *
   1286 mbr_get_generic_part_type(enum part_type pt)
   1287 {
   1288 	switch (pt) {
   1289 	case PT_root:
   1290 		return mbr_get_gen_type_desc(MBR_PTYPE_NETBSD);
   1291 	case PT_FAT:
   1292 		return mbr_get_gen_type_desc(MBR_PTYPE_FAT32L);
   1293 	case PT_EXT2:
   1294 		return mbr_get_gen_type_desc(MBR_PTYPE_LNXEXT2);
   1295 	case PT_EFI_SYSTEM:
   1296 		return mbr_get_gen_type_desc(MBR_PTYPE_EFI);
   1297 	default:
   1298 		break;
   1299 	}
   1300 	assert(false);
   1301 	return NULL;
   1302 }
   1303 
   1304 static void
   1305 mbr_partition_to_info(const struct mbr_partition *mp, daddr_t start_off,
   1306     struct disk_part_info *info)
   1307 {
   1308 	memset(info, 0, sizeof(*info));
   1309 	info->start = mp->mbrp_start + start_off;
   1310 	info->size = mp->mbrp_size;
   1311 	info->nat_type = mbr_get_gen_type_desc(mp->mbrp_type);
   1312 	if (mp->mbrp_type == MBR_PTYPE_NETBSD) {
   1313 		info->flags |= PTI_SEC_CONTAINER;
   1314 	} else if (MBR_IS_EXTENDED(mp->mbrp_type))
   1315 		info->flags |= PTI_PSCHEME_INTERNAL;
   1316 }
   1317 
   1318 static bool
   1319 mbr_part_apply(const struct disk_partitions *arg, part_id id,
   1320     bool (*func)(const struct disk_partitions *arg, part_id id,
   1321 	const mbr_info_t *mb, int i, bool primary,
   1322 	const struct mbr_partition *mp, void *),
   1323     void *cookie)
   1324 {
   1325 	const struct mbr_disk_partitions *parts =
   1326 	    (const struct mbr_disk_partitions*)arg;
   1327 	part_id i, j, no;
   1328 	const mbr_info_t *m = &parts->mbr, *me;
   1329 
   1330 	no = 0;
   1331 	for (i = 0; i < MBR_PART_COUNT; i++) {
   1332 		if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
   1333 			continue;
   1334 
   1335 		if (no == id) {
   1336 			return func(arg, id, m, i, true,
   1337 			    &m->mbr.mbr_parts[i], cookie);
   1338 		}
   1339 		no++;
   1340 
   1341 		if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type)) {
   1342 			for (me = m->extended; me != NULL; me = me->extended) {
   1343 				for (j = 0; j < MBR_PART_COUNT; j++) {
   1344 					if (me->mbr.mbr_parts[j].mbrp_type ==
   1345 					    MBR_PTYPE_UNUSED)
   1346 						continue;
   1347 					if (j > 0 && MBR_IS_EXTENDED(
   1348 					    me->mbr.mbr_parts[j].mbrp_type))
   1349 						break;
   1350 					if (no == id) {
   1351 						return func(arg, id, me, j,
   1352 						    false,
   1353 						    &me->mbr.mbr_parts[j],
   1354 						    cookie);
   1355 					}
   1356 					no++;
   1357 				}
   1358 			}
   1359 		}
   1360 	}
   1361 
   1362 
   1363 	return false;
   1364 }
   1365 
   1366 static bool
   1367 mbr_do_get_part_info(const struct disk_partitions *arg, part_id id,
   1368     const mbr_info_t *mb, int i, bool primary,
   1369     const struct mbr_partition *mp, void *cookie)
   1370 {
   1371 	struct disk_part_info *info = cookie;
   1372 	const struct mbr_disk_partitions *parts =
   1373 	    (const struct mbr_disk_partitions*)arg;
   1374 
   1375 	mbr_partition_to_info(mp, mb->sector, info);
   1376 	if (mp->mbrp_start + mb->sector == parts->target)
   1377 		info->flags |= PTI_INSTALL_TARGET;
   1378 	if (mb->last_mounted[i] != NULL && mb->last_mounted[i][0] != 0)
   1379 		info->last_mounted = mb->last_mounted[i];
   1380 	if (mb->fs_type[i] != FS_UNUSED) {
   1381 		info->fs_type = mb->fs_type[i];
   1382 		info->fs_sub_type = mb->fs_sub_type[i];
   1383 	} else {
   1384 		info->fs_sub_type = 0;
   1385 		switch (mp->mbrp_type) {
   1386 		case MBR_PTYPE_FAT12:
   1387 		case MBR_PTYPE_FAT16S:
   1388 		case MBR_PTYPE_FAT16B:
   1389 		case MBR_PTYPE_FAT32:
   1390 		case MBR_PTYPE_FAT32L:
   1391 		case MBR_PTYPE_FAT16L:
   1392 		case MBR_PTYPE_OS2_DOS12:
   1393 		case MBR_PTYPE_OS2_DOS16S:
   1394 		case MBR_PTYPE_OS2_DOS16B:
   1395 		case MBR_PTYPE_HID_FAT32:
   1396 		case MBR_PTYPE_HID_FAT32_LBA:
   1397 		case MBR_PTYPE_HID_FAT16_LBA:
   1398 		case MBR_PTYPE_MDOS_FAT12:
   1399 		case MBR_PTYPE_MDOS_FAT16S:
   1400 		case MBR_PTYPE_MDOS_EXT:
   1401 		case MBR_PTYPE_MDOS_FAT16B:
   1402 		case MBR_PTYPE_SPEEDSTOR_16S:
   1403 		case MBR_PTYPE_EFI:
   1404 			info->fs_type = FS_MSDOS;
   1405 			info->fs_sub_type = mp->mbrp_type;
   1406 			break;
   1407 		case MBR_PTYPE_LNXEXT2:
   1408 			info->fs_type = FS_EX2FS;
   1409 			break;
   1410 		case MBR_PTYPE_XENIX_ROOT:
   1411 		case MBR_PTYPE_XENIX_USR:
   1412 			info->fs_type = FS_SYSV;
   1413 			break;
   1414 		case MBR_PTYPE_NTFS:
   1415 			info->fs_type = FS_NTFS;
   1416 			break;
   1417 		case MBR_PTYPE_APPLE_HFS:
   1418 			info->fs_type = FS_HFS;
   1419 			break;
   1420 		case MBR_PTYPE_VMWARE:
   1421 			info->fs_type = FS_VMFS;
   1422 			break;
   1423 		case MBR_PTYPE_AST_SWAP:
   1424 		case MBR_PTYPE_DRDOS_LSWAP:
   1425 		case MBR_PTYPE_LNXSWAP:
   1426 		case MBR_PTYPE_BSDI_SWAP:
   1427 		case MBR_PTYPE_HID_LNX_SWAP:
   1428 		case MBR_PTYPE_VMWARE_SWAP:
   1429 			info->fs_type = FS_SWAP;
   1430 			break;
   1431 		}
   1432 	}
   1433 	return true;
   1434 }
   1435 
   1436 static bool
   1437 get_wedge_devname(const struct disk_partitions *arg, part_id id,
   1438     const mbr_info_t *mb, int i, bool primary,
   1439     const struct mbr_partition *mp, void *cookie)
   1440 {
   1441 	char **res = cookie;
   1442 
   1443 	if (!res)
   1444 		return false;
   1445 
   1446 	*res = __UNCONST(mb->wedge[i]);
   1447 	return true;
   1448 }
   1449 
   1450 static bool
   1451 mbr_part_get_wedge(const struct disk_partitions *arg, part_id id,
   1452     char **res)
   1453 {
   1454 	return mbr_part_apply(arg, id, get_wedge_devname, res);
   1455 }
   1456 
   1457 static bool
   1458 mbr_get_part_info(const struct disk_partitions *arg, part_id id,
   1459     struct disk_part_info *info)
   1460 {
   1461 	return mbr_part_apply(arg, id, mbr_do_get_part_info, info);
   1462 }
   1463 
   1464 static bool
   1465 type_can_change(const struct disk_partitions *arg, part_id id,
   1466     const mbr_info_t *mb, int i, bool primary,
   1467     const struct mbr_partition *mp, void *cookie)
   1468 {
   1469 	/*
   1470 	 * The extended partition can only change type or be
   1471 	 * deleted if it is empty
   1472 	 */
   1473 	if (!MBR_IS_EXTENDED(mp->mbrp_type))
   1474 		return true;
   1475 	return primary && mb->extended == NULL;
   1476 }
   1477 
   1478 static bool
   1479 mbr_part_type_can_change(const struct disk_partitions *arg, part_id id)
   1480 {
   1481 	return mbr_part_apply(arg, id, type_can_change, NULL);
   1482 }
   1483 
   1484 struct part_get_str_data {
   1485 	char *str;
   1486 	size_t avail_space;
   1487 	size_t col;
   1488 };
   1489 
   1490 
   1491 static bool
   1492 mbr_get_part_table_str(const struct disk_partitions *arg, part_id id,
   1493     const mbr_info_t *mb, int i, bool primary,
   1494     const struct mbr_partition *mp, void *cookie)
   1495 {
   1496 	struct part_get_str_data *data = cookie;
   1497 	char *str = data->str;
   1498 	const struct part_type_desc *ptype;
   1499 
   1500 	switch (data->col) {
   1501 	case 0:
   1502 		ptype = mbr_get_gen_type_desc(mp->mbrp_type);
   1503 		if (ptype != NULL)
   1504 			strncpy(str, ptype->description, data->avail_space);
   1505 		else
   1506 			snprintf(str, data->avail_space, "%u", mp->mbrp_type);
   1507 		str[data->avail_space-1] = 0;
   1508 		break;
   1509 	case 1:
   1510 		if (mb->last_mounted[i])
   1511 			strlcpy(str, mb->last_mounted[i], data->avail_space);
   1512 		else
   1513 			*str = 0;
   1514 		break;
   1515 #ifdef BOOTSEL
   1516 	case 2:
   1517 		if (mb->mbrb.mbrbs_nametab[i][0] != 0)
   1518 			strlcpy(str, mb->mbrb.mbrbs_nametab[i],
   1519 			    data->avail_space);
   1520 		else
   1521 			*str = 0;
   1522 		break;
   1523 #endif
   1524 	}
   1525 
   1526 	return true;
   1527 }
   1528 
   1529 static bool
   1530 mbr_table_str(const struct disk_partitions *arg, part_id id, size_t col,
   1531     char *str, size_t avail_space)
   1532 {
   1533 	struct part_get_str_data data;
   1534 
   1535 	data.str = str;
   1536 	data.avail_space = avail_space;
   1537 	data.col = col;
   1538 	return mbr_part_apply(arg, id, mbr_get_part_table_str, &data);
   1539 }
   1540 
   1541 static bool
   1542 mbr_get_part_attr_str(const struct disk_partitions *arg, part_id id,
   1543     const mbr_info_t *mb, int i, bool primary,
   1544     const struct mbr_partition *mp, void *cookie)
   1545 {
   1546 #ifdef BOOTSEL
   1547 	const struct mbr_disk_partitions *parts =
   1548 	    (const struct mbr_disk_partitions*)arg;
   1549 #endif
   1550 	struct part_get_str_data *data = cookie;
   1551 	static const char *flags = NULL;
   1552 	char *str = data->str;
   1553 
   1554 	if (flags == NULL)
   1555 		flags = msg_string(MSG_mbr_flags);
   1556 
   1557 	if (mp->mbrp_flag & MBR_PFLAG_ACTIVE)
   1558 		*str++ = flags[0];
   1559 #ifdef BOOTSEL
   1560 	if (parts->mbr.bootsec == mb->sector+mp->mbrp_start)
   1561 		*str++ = flags[1];
   1562 #endif
   1563 	*str = 0;
   1564 	return true;
   1565 }
   1566 
   1567 static bool
   1568 mbr_part_attr_str(const struct disk_partitions *arg, part_id id,
   1569     char *str, size_t avail_space)
   1570 {
   1571 	struct part_get_str_data data;
   1572 
   1573 	if (avail_space < 3)
   1574 		return false;
   1575 
   1576 	data.str = str;
   1577 	data.avail_space = avail_space;
   1578 	return mbr_part_apply(arg, id, mbr_get_part_attr_str, &data);
   1579 }
   1580 
   1581 static bool
   1582 mbr_info_to_partitition(const struct disk_part_info *info,
   1583    struct mbr_partition *mp, uint sector,
   1584    struct mbr_info_t *mb, size_t index, const char **err_msg)
   1585 {
   1586 	size_t pt = mbr_type_from_gen_desc(info->nat_type);
   1587 	if (info->start + info->size > UINT_MAX
   1588 	    || pt > __arraycount(mbr_gen_type_desc)) {
   1589 		if (err_msg)
   1590 			*err_msg = err_outofmem;
   1591 		return false;
   1592 	}
   1593 	mp->mbrp_start = info->start - sector;
   1594 	mp->mbrp_size = info->size;
   1595 	mp->mbrp_type = pt;
   1596 	if (info->flags & PTI_INSTALL_TARGET) {
   1597 		mp->mbrp_flag |= MBR_PFLAG_ACTIVE;
   1598 #ifdef BOOTSEL
   1599 		strcpy(mb->mbrb.mbrbs_nametab[index], "NetBSD");
   1600 #endif
   1601 	}
   1602 
   1603 	return true;
   1604 }
   1605 
   1606 static bool
   1607 inside_ext_part(mbr_info_t *m, daddr_t start)
   1608 {
   1609 	size_t i;
   1610 	struct mbr_partition *mp = NULL;
   1611 
   1612 	for (i = 0; i < MBR_PART_COUNT; i++) {
   1613 		if (!MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
   1614 			continue;
   1615 		mp = &m->mbr.mbr_parts[i];
   1616 		break;
   1617 	}
   1618 
   1619 	if (mp == NULL) {
   1620 		assert(false);
   1621 		return false;
   1622 	}
   1623 
   1624 	if (mp->mbrp_start > start)
   1625 		return false;
   1626 
   1627 	return true;
   1628 }
   1629 
   1630 static void
   1631 adjust_ext_part(mbr_info_t *m, daddr_t start, daddr_t size)
   1632 {
   1633 	size_t i;
   1634 	struct mbr_partition *mp = NULL;
   1635 
   1636 	for (i = 0; i < MBR_PART_COUNT; i++) {
   1637 		if (!MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
   1638 			continue;
   1639 		mp = &m->mbr.mbr_parts[i];
   1640 		break;
   1641 	}
   1642 
   1643 	if (mp == NULL) {
   1644 		assert(false);
   1645 		return;
   1646 	}
   1647 
   1648 	if (mp->mbrp_start + mp->mbrp_size >= start + size)
   1649 		return;
   1650 
   1651 	daddr_t new_end = start + size;
   1652 	mp->mbrp_size = new_end - mp->mbrp_start;
   1653 }
   1654 
   1655 static bool
   1656 ext_part_good(mbr_info_t *m, daddr_t ext_start, daddr_t ext_size)
   1657 {
   1658 	for (m = m->extended; m != NULL; m = m->extended) {
   1659 		for (size_t i = 0; i < MBR_PART_COUNT; i++) {
   1660 			if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
   1661 				continue;
   1662 
   1663 			if (i > 0 &&
   1664 			    MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
   1665 				break;
   1666 
   1667 			daddr_t pstart = m->mbr.mbr_parts[i].mbrp_start +
   1668 			    m->sector;
   1669 			daddr_t pend = pstart + m->mbr.mbr_parts[i].mbrp_size;
   1670 
   1671 			if (pstart < ext_start || pend > ext_start+ext_size)
   1672 				return false;
   1673 		}
   1674 	}
   1675 
   1676 	return true;
   1677 }
   1678 
   1679 static bool
   1680 mbr_set_part_info(struct disk_partitions *arg, part_id id,
   1681     const struct disk_part_info *info, const char **err_msg)
   1682 {
   1683 	struct mbr_disk_partitions *parts =
   1684 	    (struct mbr_disk_partitions*)arg;
   1685 	struct disk_part_info data = *info;
   1686 	part_id i, j, no, ext_ndx, t;
   1687 	mbr_info_t *m = &parts->mbr, *me;
   1688 	uint pt = mbr_type_from_gen_desc(info->nat_type);
   1689 
   1690 	if (MBR_IS_EXTENDED(pt)) {
   1691 		/* check for duplicate ext part */
   1692 		no = 0;
   1693 		t = ext_ndx = MBR_PART_COUNT;
   1694 		for (i = 0; i < MBR_PART_COUNT; i++) {
   1695 			if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
   1696 				continue;
   1697 			if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
   1698 				ext_ndx = i;
   1699 			if (no == id)
   1700 				t = i;
   1701 			no++;
   1702 		}
   1703 		if (ext_ndx < MBR_PART_COUNT && t != ext_ndx) {
   1704 			if (err_msg)
   1705 				*err_msg =
   1706 				    msg_string(MSG_Only_one_extended_ptn);
   1707 			return false;
   1708 		}
   1709 		/* this partition becomes an extended one, apply alignment */
   1710 		data.start = max(roundup(data.start, parts->ext_ptn_alignment),
   1711 			parts->ext_ptn_alignment);
   1712 	}
   1713 
   1714 	no = 0;
   1715 	for (i = 0; i < MBR_PART_COUNT; i++) {
   1716 		if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
   1717 			continue;
   1718 
   1719 		if (no == id)
   1720 			goto found;
   1721 		no++;
   1722 
   1723 		if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type)) {
   1724 			for (me = m->extended; me != NULL; me = me->extended) {
   1725 				for (j = 0; j < MBR_PART_COUNT; j++) {
   1726 					if (me->mbr.mbr_parts[j].mbrp_type ==
   1727 					    MBR_PTYPE_UNUSED)
   1728 						continue;
   1729 					if (j > 0 && MBR_IS_EXTENDED(
   1730 					    me->mbr.mbr_parts[j].mbrp_type))
   1731 						break;
   1732 					if (no == id) {
   1733 						i = j;
   1734 						m = me;
   1735 						goto found;
   1736 					}
   1737 					no++;
   1738 				}
   1739 			}
   1740 		}
   1741 	}
   1742 
   1743 	if (err_msg)
   1744 		*err_msg = INTERNAL_ERROR;
   1745 	return false;
   1746 
   1747 found:
   1748 	/*
   1749 	 * We assume that m is the mbr we want to update and
   1750 	 * i is the local partition index into it.
   1751 	 */
   1752 	if (m == &parts->mbr) {
   1753 		if (MBR_IS_EXTENDED(
   1754 		    m->mbr.mbr_parts[i].mbrp_type) &&
   1755 		    !ext_part_good(&parts->mbr, data.start, data.size)) {
   1756 			if (err_msg)
   1757 				*err_msg =
   1758 				    MSG_mbr_ext_nofit;
   1759 			return false;
   1760 		}
   1761 	} else if (!inside_ext_part(&parts->mbr, data.start)) {
   1762 		if (err_msg)
   1763 			*err_msg = msg_string(MSG_mbr_inside_ext);
   1764 		return false;
   1765 	}
   1766 	uint start = data.start, size = data.size;
   1767 	uint oldstart = m->mbr.mbr_parts[i].mbrp_start + m->sector;
   1768 	if (parts->ptn_0_offset > 0 &&
   1769 	    start < parts->ptn_0_offset)
   1770 		start = parts->ptn_0_offset;
   1771 	if (find_mbr_space(m, &start, &size, start, parts->dp.disk_size,
   1772 	    oldstart, false) < 0) {
   1773 		if (err_msg != NULL)
   1774 			*err_msg = INTERNAL_ERROR;
   1775 		return false;
   1776 	}
   1777 	data.start = start;
   1778 	if (size < data.size)
   1779 		data.size = size;
   1780 	uint old_start = m->mbr.mbr_parts[i].mbrp_start;
   1781 	if (!mbr_info_to_partitition(&data,
   1782 	   &m->mbr.mbr_parts[i], m->sector, m, i, err_msg))
   1783 		return false;
   1784 	if (data.flags & PTI_INSTALL_TARGET)
   1785 		parts->target = start;
   1786 	else if (old_start == parts->target)
   1787 		parts->target = -1;
   1788 	if (data.last_mounted && m->last_mounted[i] &&
   1789 	    data.last_mounted != m->last_mounted[i]) {
   1790 		free(__UNCONST(m->last_mounted[i]));
   1791 		m->last_mounted[i] = strdup(data.last_mounted);
   1792 	}
   1793 	if (data.fs_type != 0)
   1794 		m->fs_type[i] = data.fs_type;
   1795 	if (data.fs_sub_type != 0)
   1796 		m->fs_sub_type[i] = data.fs_sub_type;
   1797 
   1798 	if (m == &parts->mbr) {
   1799 		if (m->mbr.mbr_parts[i].mbrp_start !=
   1800 		    old_start)
   1801 			mbr_sort_main_mbr(&m->mbr);
   1802 	} else {
   1803 		adjust_ext_part(&parts->mbr,
   1804 		    data.start, data.size);
   1805 	}
   1806 	mbr_calc_free_space(parts);
   1807 	return true;
   1808 }
   1809 
   1810 static bool
   1811 mbr_find_netbsd(const struct mbr_info_t *m, uint start,
   1812     struct disk_part_info *info)
   1813 {
   1814 	size_t i;
   1815 	bool prim = true;
   1816 
   1817 	do {
   1818 		for (i = 0; i < MBR_PART_COUNT; i++) {
   1819 			if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
   1820 				continue;
   1821 
   1822 			if (!prim && i > 0 &&
   1823 			    MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
   1824 				break;
   1825 
   1826 			const struct mbr_partition *mp = &m->mbr.mbr_parts[i];
   1827 			if (mp->mbrp_type != MBR_PTYPE_NETBSD)
   1828 				continue;
   1829 
   1830 			mbr_partition_to_info(mp, m->sector, info);
   1831 			if (m->last_mounted[i] && *m->last_mounted[i] != 0)
   1832 					info->last_mounted =
   1833 					    m->last_mounted[i];
   1834 			info->fs_type = m->fs_type[i];
   1835 			info->fs_sub_type = m->fs_sub_type[i];
   1836 			if (start > 0 && start != info->start)
   1837 				continue;
   1838 			return true;
   1839 		}
   1840 		prim = false;
   1841 	} while ((m = m->extended));
   1842 
   1843 	return false;
   1844 }
   1845 
   1846 static struct disk_partitions *
   1847 mbr_read_disklabel(struct disk_partitions *arg, daddr_t start, bool force_empty)
   1848 {
   1849 	struct mbr_disk_partitions *myparts =
   1850 	    (struct mbr_disk_partitions*)arg;
   1851 	struct disk_part_info part;
   1852 	struct disk_part_free_space space;
   1853 
   1854 	if (force_empty && myparts->dlabel)
   1855 		myparts->dlabel->pscheme->delete_all_partitions(
   1856 		    myparts->dlabel);
   1857 
   1858 	if (myparts->dlabel == NULL) {
   1859 		/*
   1860 		 * Find the NetBSD MBR partition
   1861 		 */
   1862 		if (!mbr_find_netbsd(&myparts->mbr, start, &part)) {
   1863 			if (!force_empty)
   1864 				return NULL;
   1865 
   1866 			/* add a "whole disk" NetBSD partition */
   1867 			memset(&part, 0, sizeof part);
   1868 			part.start = min(myparts->ptn_0_offset,start);
   1869 			if (!mbr_get_free_spaces(arg, &space, 1,
   1870 			    part.start, myparts->ptn_alignment, -1, -1))
   1871 				return NULL;
   1872 			part.start = space.start;
   1873 			part.size = space.size;
   1874 			part.nat_type = &mbr_gen_type_desc[MBR_PTYPE_NETBSD].gen;
   1875 			mbr_add_part(arg, &part, NULL);
   1876 			if (!mbr_find_netbsd(&myparts->mbr, start, &part))
   1877 				return NULL;
   1878 		}
   1879 
   1880 		if (!force_empty) {
   1881 			myparts->dlabel = disklabel_parts.read_from_disk(
   1882 			    myparts->dp.disk, part.start, part.size,
   1883 			    myparts->dp.bytes_per_sector, &disklabel_parts);
   1884 			if (myparts->dlabel != NULL)
   1885 				myparts->dlabel->parent = &myparts->dp;
   1886 		}
   1887 
   1888 		if (myparts->dlabel == NULL && part.size > 0) {
   1889 			/* we just created the outer partitions? */
   1890 			myparts->dlabel =
   1891 			    disklabel_parts.create_new_for_disk(
   1892 			    myparts->dp.disk, part.start, part.size,
   1893 			    false, &myparts->dp);
   1894 		}
   1895 
   1896 		if (myparts->dlabel != NULL)
   1897 			myparts->dlabel->pscheme->change_disk_geom(
   1898 			    myparts->dlabel, myparts->geo_cyl,
   1899 			    myparts->geo_head, myparts->geo_sec);
   1900 	}
   1901 	return myparts->dlabel;
   1902 }
   1903 
   1904 static int
   1905 get_mapping(struct mbr_partition *parts, int i,
   1906 	    int *cylinder, int *head, int *sector, daddr_t *absolute)
   1907 {
   1908 	struct mbr_partition *apart = &parts[i / 2];
   1909 
   1910 	if (apart->mbrp_type == MBR_PTYPE_UNUSED)
   1911 		return -1;
   1912 	if (i % 2 == 0) {
   1913 		*cylinder = MBR_PCYL(apart->mbrp_scyl, apart->mbrp_ssect);
   1914 		*head = apart->mbrp_shd;
   1915 		*sector = MBR_PSECT(apart->mbrp_ssect) - 1;
   1916 		*absolute = le32toh(apart->mbrp_start);
   1917 	} else {
   1918 		*cylinder = MBR_PCYL(apart->mbrp_ecyl, apart->mbrp_esect);
   1919 		*head = apart->mbrp_ehd;
   1920 		*sector = MBR_PSECT(apart->mbrp_esect) - 1;
   1921 		*absolute = le32toh(apart->mbrp_start)
   1922 			+ le32toh(apart->mbrp_size) - 1;
   1923 	}
   1924 	/* Sanity check the data against max values */
   1925 	if ((((*cylinder * MAXHEAD) + *head) * (uint32_t)MAXSECTOR + *sector) < *absolute)
   1926 		/* cannot be a CHS mapping */
   1927 		return -1;
   1928 
   1929 	return 0;
   1930 }
   1931 
   1932 static bool
   1933 mbr_delete_all(struct disk_partitions *arg)
   1934 {
   1935 	struct mbr_disk_partitions *myparts = (struct mbr_disk_partitions*)arg;
   1936 	struct mbr_sector *mbrs = &myparts->mbr.mbr;
   1937 	struct mbr_info_t *mbri = &myparts->mbr;
   1938 	mbr_info_t *ext;
   1939 	struct mbr_partition *part;
   1940 
   1941 	part = &mbrs->mbr_parts[0];
   1942 	/* Set the partition information for full disk usage. */
   1943 	while ((ext = mbri->extended)) {
   1944 		mbri->extended = ext->extended;
   1945 		free_mbr_info(ext);
   1946 	}
   1947 	memset(part, 0, MBR_PART_COUNT * sizeof *part);
   1948 #ifdef BOOTSEL
   1949 	memset(&mbri->mbrb, 0, sizeof mbri->mbrb);
   1950 #endif
   1951 
   1952 	/*
   1953 	 * We may have changed alignment settings due to partitions
   1954 	 * ending on an MB boundary - undo that, now that the partitions
   1955 	 * are gone.
   1956 	 */
   1957 	mbr_change_disk_geom(arg, myparts->geo_cyl, myparts->geo_head,
   1958 	    myparts->geo_sec);
   1959 
   1960 	return true;
   1961 }
   1962 
   1963 /*
   1964  * helper function to fix up mbrp_start and mbrp_size for the
   1965  * extended MBRs "partition b" entries after addition/deletion
   1966  * of some partition.
   1967  */
   1968 static void
   1969 mbr_fixup_ext_chain(mbr_info_t *primary, uint ext_start, uint ext_end)
   1970 {
   1971 	for (mbr_info_t *m = primary->extended; m != NULL; m = m->extended) {
   1972 		if (m->extended == NULL) {
   1973 			m->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_UNUSED;
   1974 			m->mbr.mbr_parts[1].mbrp_start = 0;
   1975 			m->mbr.mbr_parts[1].mbrp_size = 0;
   1976 		} else {
   1977 			uint n_end, n_start = m->extended->sector;
   1978 			if (m->extended->extended)
   1979 				n_end = m->extended->extended->sector;
   1980 			else
   1981 				n_end = ext_end;
   1982 			m->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
   1983 			m->mbr.mbr_parts[1].mbrp_start = n_start - ext_start;
   1984 			m->mbr.mbr_parts[1].mbrp_size = n_end - n_start;
   1985 		}
   1986 	}
   1987 }
   1988 
   1989 struct delete_part_args {
   1990 	struct mbr_disk_partitions *parts;
   1991 	daddr_t start, size;
   1992 	const char **err_msg;
   1993 };
   1994 
   1995 static bool
   1996 mbr_do_delete_part(const struct disk_partitions *arg, part_id id,
   1997     const mbr_info_t *mb, int i, bool primary,
   1998     const struct mbr_partition *mp, void *cookie)
   1999 {
   2000 	struct delete_part_args *marg = cookie;
   2001 	bool is_ext_part = MBR_IS_EXTENDED(mp->mbrp_type);
   2002 
   2003 	/* can not delete non-empty extended partitions */
   2004 	if (MBR_IS_EXTENDED(mp->mbrp_type)
   2005 	    && marg->parts->mbr.extended != NULL) {
   2006 		if (marg->err_msg)
   2007 			*marg->err_msg = msg_string(MSG_mbr_ext_not_empty);
   2008 		return false;
   2009 	}
   2010 
   2011 	/* return position/size to caller */
   2012 	marg->start = mb->sector + mp->mbrp_start;
   2013 	marg->size = mp->mbrp_size;
   2014 
   2015 	if (primary) {
   2016 		/* if deleting the primary extended partition, just kill it */
   2017 		struct mbr_partition *md = &marg->parts->mbr.mbr.mbr_parts[i];
   2018 		md->mbrp_size = 0;
   2019 		md->mbrp_start = 0;
   2020 		md->mbrp_type = MBR_PTYPE_UNUSED;
   2021 		if (marg->parts->mbr.last_mounted[i]) {
   2022 			free(__UNCONST(marg->parts->mbr.last_mounted[i]));
   2023 			marg->parts->mbr.last_mounted[i] = NULL;
   2024 		}
   2025 		if (is_ext_part) {
   2026 			for (mbr_info_t *m = marg->parts->mbr.extended;
   2027 			    m != NULL; ) {
   2028 				mbr_info_t *n = m->extended;
   2029 				free_mbr_info(m);
   2030 				m = n;
   2031 			}
   2032 			marg->parts->mbr.extended = NULL;
   2033 		}
   2034 	} else {
   2035 		/* find the size of the primary extended partition */
   2036 		uint ext_start = 0, ext_size = 0;
   2037 		for (i = 0; i < MBR_PART_COUNT; i++) {
   2038 			if (!MBR_IS_EXTENDED(marg->parts->mbr.mbr.mbr_parts[i]
   2039 			    .mbrp_type))
   2040 				continue;
   2041 			ext_start = marg->parts->mbr.mbr.mbr_parts[i]
   2042 			    .mbrp_start;
   2043 			ext_size = marg->parts->mbr.mbr.mbr_parts[i]
   2044 			    .mbrp_size;
   2045 			break;
   2046 		}
   2047 
   2048 		/*
   2049 		 * If we are in an extended partition chain, unlink this MBR,
   2050 		 * unless it is the very first one at the start of the extended
   2051 		 * partition (we would have no previous ext mbr to fix up
   2052 		 * the chain in that case)
   2053 		 */
   2054 		if (marg->parts->mbr.extended == mb) {
   2055 			struct mbr_partition *part =
   2056 			    &marg->parts->mbr.extended->mbr.mbr_parts[0];
   2057 			part->mbrp_type = MBR_PTYPE_UNUSED;
   2058 			part->mbrp_start = 0;
   2059 			part->mbrp_size = 0;
   2060 		} else {
   2061 			mbr_info_t *p, *last;
   2062 			for (last = NULL, p = &marg->parts->mbr; p != NULL;
   2063 			    last = p, p = p->extended)
   2064 				if (p == mb)
   2065 					break;
   2066 			if (last == NULL) {
   2067 				if (marg->err_msg != NULL)
   2068 					*marg->err_msg= INTERNAL_ERROR;
   2069 				return false;
   2070 			}
   2071 			last->extended = p->extended;
   2072 			free_mbr_info(p);
   2073 			if (last == &marg->parts->mbr && last->extended &&
   2074 			    last->extended->extended == NULL &&
   2075 			    last->extended->mbr.mbr_parts[0].mbrp_type ==
   2076 			    MBR_PTYPE_UNUSED) {
   2077 				/*
   2078 				 * we deleted the last extended sector,
   2079 				 * remove the whole chain
   2080 				 */
   2081 				free_mbr_info(last->extended);
   2082 				last->extended = NULL;
   2083 			}
   2084 		}
   2085 		mbr_fixup_ext_chain(&marg->parts->mbr, ext_start,
   2086 		    ext_start+ext_size);
   2087 	}
   2088 	mbr_calc_free_space(marg->parts);
   2089 	return true;
   2090 }
   2091 
   2092 static bool
   2093 mbr_delete_part(struct disk_partitions *arg, part_id pno, const char **err_msg)
   2094 {
   2095 	struct mbr_disk_partitions *parts =
   2096 	    (struct mbr_disk_partitions*)arg;
   2097 	struct delete_part_args data = { .parts = parts, .err_msg = err_msg };
   2098 
   2099 	if (!mbr_part_apply(arg, pno, mbr_do_delete_part, &data)) {
   2100 		if (err_msg)
   2101 			*err_msg = INTERNAL_ERROR;
   2102 		return false;
   2103 	}
   2104 
   2105 	if (parts->target == data.start)
   2106 		parts->target = ~0U;
   2107 
   2108 	if (parts->dlabel) {
   2109 		/*
   2110 		 * If we change the mbr partitioning, the we must
   2111 		 * remove any references in the netbsd disklabel
   2112 		 * to the part we changed.
   2113 		 */
   2114 		parts->dlabel->pscheme->delete_partitions_in_range(
   2115 		    parts->dlabel, data.start, data.size);
   2116 	}
   2117 
   2118 	if (err_msg)
   2119 		*err_msg = NULL;
   2120 
   2121 	dump_mbr(&parts->mbr, "after delete");
   2122 	return true;
   2123 }
   2124 
   2125 static struct mbr_partition *
   2126 mbr_ptr_from_start(mbr_info_t *m, daddr_t start)
   2127 {
   2128 	bool primary = true;
   2129 
   2130 	do {
   2131 		for (uint i = 0; i < MBR_PART_COUNT; i++) {
   2132 			if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
   2133 				continue;
   2134 			if (!primary &&
   2135 			    MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
   2136 				break;
   2137 
   2138 			daddr_t pstart = m->sector +
   2139 			    m->mbr.mbr_parts[i].mbrp_start;
   2140 			if (pstart == start)
   2141 				return &m->mbr.mbr_parts[i];
   2142 
   2143 		}
   2144 		primary = false;
   2145 	} while ((m = m->extended));
   2146 
   2147 	return NULL;
   2148 }
   2149 
   2150 static uint8_t
   2151 mbr_type_from_start(const mbr_info_t *m, daddr_t start)
   2152 {
   2153 	bool primary = true;
   2154 
   2155 	do {
   2156 		for (uint i = 0; i < MBR_PART_COUNT; i++) {
   2157 			if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
   2158 				continue;
   2159 			if (!primary &&
   2160 			    MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
   2161 				break;
   2162 
   2163 			daddr_t pstart = m->sector +
   2164 			    m->mbr.mbr_parts[i].mbrp_start;
   2165 			if (pstart == start)
   2166 				return m->mbr.mbr_parts[i].mbrp_type;
   2167 
   2168 		}
   2169 		primary = false;
   2170 	} while ((m = m->extended));
   2171 
   2172 	return MBR_PTYPE_UNUSED;
   2173 }
   2174 
   2175 static part_id
   2176 mbr_add_part(struct disk_partitions *arg,
   2177     const struct disk_part_info *info, const char **errmsg)
   2178 {
   2179 	struct mbr_disk_partitions *parts =
   2180 	    (struct mbr_disk_partitions*)arg;
   2181 	part_id i, j, no, free_primary = UINT_MAX;
   2182 	mbr_info_t *m = &parts->mbr, *me, *last, *t;
   2183 	daddr_t ext_start = 0, ext_size = 0;
   2184 	uint start, size;
   2185 	struct disk_part_info data = *info;
   2186 	struct mbr_partition *newp;
   2187 
   2188 	if (errmsg != NULL)
   2189 		*errmsg = NULL;
   2190 
   2191 	assert(info->nat_type != NULL);
   2192 	if (info->nat_type == NULL) {
   2193 		if (errmsg != NULL)
   2194 			*errmsg = INTERNAL_ERROR;
   2195 		return NO_PART;
   2196 	}
   2197 	if (mbr_type_from_gen_desc(info->nat_type) == MBR_PTYPE_UNUSED) {
   2198 		if (errmsg != NULL)
   2199 			*errmsg = INTERNAL_ERROR;
   2200 		return NO_PART;
   2201 	}
   2202 
   2203 	/* do we have free primary slots and/or an extended partition? */
   2204 	for (i = 0; i < MBR_PART_COUNT; i++) {
   2205 		if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED
   2206 		    && free_primary > MBR_PART_COUNT)
   2207 			free_primary = i;
   2208 		if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type)) {
   2209 			ext_start = m->mbr.mbr_parts[i].mbrp_start+m->sector;
   2210 			ext_size = m->mbr.mbr_parts[i].mbrp_size;
   2211 			continue;
   2212 		}
   2213 		if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED
   2214 		    && m->mbr.mbr_parts[i].mbrp_size == 0)
   2215 			continue;
   2216 	}
   2217 	if (ext_start > 0 && ext_size > 0 &&
   2218 	    MBR_IS_EXTENDED(mbr_type_from_gen_desc(info->nat_type))) {
   2219 		/*
   2220 		 * Do not allow a second extended partition
   2221 		 */
   2222 		if (errmsg)
   2223 			*errmsg = MSG_Only_one_extended_ptn;
   2224 		return NO_PART;
   2225 	}
   2226 
   2227 	/* should this go into the extended partition? */
   2228 	if (ext_size > 0 && info->start >= ext_start
   2229 	    && info->start < ext_start + ext_size) {
   2230 
   2231 		/* must fit into the extended partition */
   2232 		if (info->start + info->size > ext_start + ext_size) {
   2233 			if (errmsg != NULL)
   2234 				*errmsg = MSG_mbr_ext_nofit;
   2235 			return NO_PART;
   2236 		}
   2237 
   2238 		/* walk the chain untill we find a proper insert position */
   2239 		daddr_t e_end, e_start;
   2240 		for (last = m, m = m->extended; m != NULL;
   2241 		    last = m, m = m->extended) {
   2242 			e_start = m->mbr.mbr_parts[1].mbrp_start
   2243 			    + ext_start;
   2244 			e_end = e_start + m->mbr.mbr_parts[1].mbrp_size;
   2245 			if (data.start <= e_start)
   2246 				break;
   2247 		}
   2248 		if (m == NULL) {
   2249 			/* add new tail record */
   2250 			e_end = ext_start + ext_size;
   2251 			/* new part needs to fit inside primary extended one */
   2252 			if (data.start + data.size > e_end) {
   2253 				if (errmsg)
   2254 					*errmsg = MSG_No_free_space;
   2255 				return NO_PART;
   2256 			}
   2257 		} else if (data.start + data.size > e_start) {
   2258 			/* new part needs to fit before next extended */
   2259 			if (errmsg)
   2260 				*errmsg = MSG_No_free_space;
   2261 			return NO_PART;
   2262 		}
   2263 		/*
   2264 		 * now last points to previous mbr (maybe primary), m
   2265 		 * points to the one that should take the new partition
   2266 		 * or we have to insert a new mbr between the two, or
   2267 		 * m needs to be split and we go into the one after it.
   2268 		 */
   2269 		if (m && m->mbr.mbr_parts[0].mbrp_type == MBR_PTYPE_UNUSED) {
   2270 			/* empty slot, we can just use it */
   2271 			newp = &m->mbr.mbr_parts[0];
   2272 			mbr_info_to_partitition(&data, &m->mbr.mbr_parts[0],
   2273 			    m->sector, m, 0, errmsg);
   2274 			if (data.last_mounted && m->last_mounted[0] &&
   2275 			    data.last_mounted != m->last_mounted[0]) {
   2276 				free(__UNCONST(m->last_mounted[0]));
   2277 				m->last_mounted[0] = strdup(data.last_mounted);
   2278 			}
   2279 		} else {
   2280 			mbr_info_t *new_mbr;
   2281 			if (m == NULL)
   2282 				m = last;
   2283 			daddr_t p_start = m->mbr.mbr_parts[0].mbrp_start
   2284 			    + m->sector;
   2285 			daddr_t p_end = p_start
   2286 			    + m->mbr.mbr_parts[0].mbrp_size;
   2287 			bool before;
   2288 			if (m == last || data.start > p_end)
   2289 				before = false;
   2290 			else if (data.start + data.size < p_start)
   2291 				before = true;
   2292 			else {
   2293 				if (errmsg)
   2294 					*errmsg = MSG_No_free_space;
   2295 				return NO_PART;
   2296 			}
   2297 			new_mbr = calloc(1, sizeof *new_mbr);
   2298 			if (!new_mbr) {
   2299 				if (errmsg)
   2300 					*errmsg = err_outofmem;
   2301 				return NO_PART;
   2302 			}
   2303 			new_mbr->mbr.mbr_magic = htole16(MBR_MAGIC);
   2304 			new_mbr->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
   2305 			if (before) {
   2306 				/*
   2307 				 * This is a hypthetical case where
   2308 				 * an extended MBR uses an unusual high
   2309 				 * offset (m->sector to parts[0].mbrp_start)
   2310 				 * and we want to go into that space.
   2311 				 * Should not happen in the real world (tm)
   2312 				 * and is untested....
   2313 				 */
   2314 
   2315 				/* make sure the aligned new mbr fits */
   2316 				uint mbrsec = rounddown(p_start,
   2317 				    parts->ext_ptn_alignment);
   2318 				if (mbrsec <= data.start + data.size)
   2319 					data.size = mbrsec-1-data.start;
   2320 
   2321 				/* now the new partition data is ready,
   2322 				 * write out to old position */
   2323 				new_mbr->sector = m->sector;
   2324 				newp = &new_mbr->mbr.mbr_parts[0];
   2325 				mbr_info_to_partitition(&data,
   2326 				    &new_mbr->mbr.mbr_parts[0],
   2327 				    new_mbr->sector, new_mbr, 0, errmsg);
   2328 				if (data.last_mounted && m->last_mounted[0] &&
   2329 				    data.last_mounted != m->last_mounted[0]) {
   2330 					free(__UNCONST(m->last_mounted[0]));
   2331 					m->last_mounted[0] =
   2332 					    strdup(data.last_mounted);
   2333 				}
   2334 				new_mbr->extended = m;
   2335 			} else {
   2336 				new_mbr->sector = max(roundup(data.start,
   2337 				    parts->ext_ptn_alignment),
   2338 				    parts->ext_ptn_alignment);
   2339 				uint off = new_mbr->sector - data.start;
   2340 				data.start += parts->ptn_0_offset+off;
   2341 				if (data.start + data.size > e_end)
   2342 					data.size = e_end - data.start;
   2343 				newp = &new_mbr->mbr.mbr_parts[0];
   2344 				mbr_info_to_partitition(&data,
   2345 				    &new_mbr->mbr.mbr_parts[0],
   2346 				    new_mbr->sector, new_mbr, 0, errmsg);
   2347 				if (data.last_mounted && m->last_mounted[0] &&
   2348 				    data.last_mounted != m->last_mounted[0]) {
   2349 					free(__UNCONST(m->last_mounted[0]));
   2350 					m->last_mounted[0] =
   2351 					    strdup(data.last_mounted);
   2352 				}
   2353 				/*
   2354 				 * Special case: if we are creating the
   2355 				 * first extended mbr, but do not start
   2356 				 * at the beginning of the primary
   2357 				 * extended partition, we need to insert
   2358 				 * another extended mbr at the start.
   2359 				 */
   2360 				if (m == &parts->mbr && m->extended == NULL
   2361 				    && new_mbr->sector > ext_start) {
   2362 					t = calloc(1, sizeof *new_mbr);
   2363 					if (!t) {
   2364 						free_mbr_info(new_mbr);
   2365 						if (errmsg)
   2366 							*errmsg = err_outofmem;
   2367 						return NO_PART;
   2368 					}
   2369 					t->sector = ext_start;
   2370 					t->mbr.mbr_magic = htole16(MBR_MAGIC);
   2371 					t->mbr.mbr_parts[1].mbrp_type =
   2372 					    MBR_PTYPE_EXT;
   2373 					m->extended = t;
   2374 					m = t;
   2375 				}
   2376 				new_mbr->extended = m->extended;
   2377 				m->extended = new_mbr;
   2378 			}
   2379 		}
   2380 		mbr_fixup_ext_chain(&parts->mbr, ext_start, ext_start+ext_size);
   2381 		dump_mbr(&parts->mbr, "after adding in extended");
   2382 		goto find_rval;
   2383 	}
   2384 
   2385 	/* this one is for the primary boot block */
   2386 	if (free_primary > MBR_PART_COUNT) {
   2387 		if (errmsg != NULL)
   2388 			*errmsg = ext_size > 0 ?
   2389 				MSG_mbr_no_free_primary_have_ext
   2390 				: MSG_mbr_no_free_primary_no_ext;
   2391 		return NO_PART;
   2392 	}
   2393 
   2394 	start = max(info->start, parts->ptn_0_offset);
   2395 	size = info->size;
   2396 	if (find_mbr_space(m, &start, &size, start, parts->dp.disk_size,
   2397 	    start, true) < 0 || size < info->size) {
   2398 		if (errmsg != NULL)
   2399 			*errmsg = MSG_No_free_space;
   2400 		return NO_PART;
   2401 	}
   2402 	data.start = start;
   2403 	if (MBR_IS_EXTENDED(mbr_type_from_gen_desc(info->nat_type))) {
   2404 		data.start = max(roundup(data.start, parts->ext_ptn_alignment),
   2405 		   parts->ext_ptn_alignment);
   2406 	}
   2407 	if (data.start + data.size > start + size)
   2408 		data.size = start + size - data.start;
   2409 	mbr_info_to_partitition(&data, &m->mbr.mbr_parts[free_primary],
   2410 	     m->sector, m, free_primary, errmsg);
   2411 	if (data.last_mounted && m->last_mounted[free_primary] &&
   2412 	    data.last_mounted != m->last_mounted[free_primary]) {
   2413 		free(__UNCONST(m->last_mounted[free_primary]));
   2414 		m->last_mounted[free_primary] = strdup(data.last_mounted);
   2415 	}
   2416 	start = m->mbr.mbr_parts[free_primary].mbrp_start;
   2417 	mbr_sort_main_mbr(&m->mbr);
   2418 
   2419 	/* find the partition again after sorting */
   2420 	newp = NULL;
   2421 	for (i = 0; i < MBR_PART_COUNT; i++) {
   2422 		if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
   2423 			continue;
   2424 		if (m->mbr.mbr_parts[i].mbrp_start != start)
   2425 			continue;
   2426 		newp = &m->mbr.mbr_parts[i];
   2427 		break;
   2428 	}
   2429 
   2430 	dump_mbr(&parts->mbr, "after adding in primary");
   2431 
   2432 find_rval:
   2433 	mbr_calc_free_space(parts);
   2434 	if (newp == NULL)
   2435 		return 0;
   2436 
   2437 	/*
   2438 	 * Now newp points to the modified partition entry but we do not know
   2439 	 * a good part_id for it.
   2440 	 * Iterate from start and find it.
   2441 	 */
   2442 	no = 0;
   2443 	for (i = 0; i < MBR_PART_COUNT; i++) {
   2444 		if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
   2445 			continue;
   2446 
   2447 		if (newp == &m->mbr.mbr_parts[i])
   2448 			return no;
   2449 		no++;
   2450 
   2451 		if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type)) {
   2452 			for (me = m->extended; me != NULL; me = me->extended) {
   2453 				for (j = 0; j < MBR_PART_COUNT; j++) {
   2454 					if (me->mbr.mbr_parts[j].mbrp_type ==
   2455 					    MBR_PTYPE_UNUSED)
   2456 						continue;
   2457 					if (j > 0 && MBR_IS_EXTENDED(
   2458 					    me->mbr.mbr_parts[j].mbrp_type))
   2459 						break;
   2460 					if (newp == &me->mbr.mbr_parts[j])
   2461 						return no;
   2462 					no++;
   2463 				}
   2464 			}
   2465 		}
   2466 	}
   2467 	return 0;
   2468 }
   2469 
   2470 static int
   2471 mbr_guess_geom(struct disk_partitions *arg, int *cyl, int *head, int *sec)
   2472 {
   2473 	struct mbr_disk_partitions *myparts = (struct mbr_disk_partitions*)arg;
   2474 	struct mbr_sector *mbrs = &myparts->mbr.mbr;
   2475 	struct mbr_partition *parts = &mbrs->mbr_parts[0];
   2476 	int xcylinders, xheads, i, j;
   2477 	daddr_t xsectors, xsize;
   2478 	int c1, h1, s1, c2, h2, s2;
   2479 	daddr_t a1, a2;
   2480 	uint64_t num, denom;
   2481 
   2482 	xheads = -1;
   2483 
   2484 	/* Try to deduce the number of heads from two different mappings. */
   2485 	for (i = 0; i < MBR_PART_COUNT * 2 - 1; i++) {
   2486 		if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
   2487 			continue;
   2488 		a1 -= s1;
   2489 		for (j = i + 1; j < MBR_PART_COUNT * 2; j++) {
   2490 			if (get_mapping(parts, j, &c2, &h2, &s2, &a2) < 0)
   2491 				continue;
   2492 			a2 -= s2;
   2493 			num = (uint64_t)h1 * a2 - (quad_t)h2 * a1;
   2494 			denom = (uint64_t)c2 * a1 - (quad_t)c1 * a2;
   2495 			if (num != 0 && denom != 0 && num % denom == 0) {
   2496 				xheads = (int)(num / denom);
   2497 				xsectors = a1 / (c1 * xheads + h1);
   2498 				break;
   2499 			}
   2500 		}
   2501 		if (xheads != -1)
   2502 			break;
   2503 	}
   2504 
   2505 	if (xheads == -1)
   2506 		return -1;
   2507 
   2508 	/*
   2509 	 * Estimate the number of cylinders.
   2510 	 * XXX relies on get_disks having been called.
   2511 	 */
   2512 	xsize = min(pm->dlsize, mbr_parts.size_limit);
   2513 	xcylinders = xsize / xheads / xsectors;
   2514 	if (xsize != xcylinders * xheads * xsectors)
   2515 		xcylinders++;
   2516 
   2517 	/*
   2518 	 * Now verify consistency with each of the partition table entries.
   2519 	 * Be willing to shove cylinders up a little bit to make things work,
   2520 	 * but translation mismatches are fatal.
   2521 	 */
   2522 	for (i = 0; i < MBR_PART_COUNT * 2; i++) {
   2523 		if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
   2524 			continue;
   2525 		if (c1 >= MAXCYL - 1)
   2526 			/* Ignore anything that is near the CHS limit */
   2527 			continue;
   2528 		if (xsectors * (c1 * xheads + h1) + s1 != a1)
   2529 			return -1;
   2530 	}
   2531 
   2532 	/*
   2533 	 * Everything checks out.  Reset the geometry to use for further
   2534 	 * calculations.
   2535 	 */
   2536 	*cyl = MIN(xcylinders, MAXCYL);
   2537 	*head = xheads;
   2538 	*sec = xsectors;
   2539 	return 0;
   2540 }
   2541 
   2542 static size_t
   2543 mbr_get_cylinder(const struct disk_partitions *arg)
   2544 {
   2545 	const struct mbr_disk_partitions *parts =
   2546 	    (const struct mbr_disk_partitions*)arg;
   2547 
   2548 	return parts->geo_cyl;
   2549 }
   2550 
   2551 static daddr_t
   2552 mbr_max_part_size(const struct disk_partitions *arg, daddr_t fp_start)
   2553 {
   2554 	const struct mbr_disk_partitions *parts =
   2555 	    (const struct mbr_disk_partitions*)arg;
   2556 	uint start = fp_start, size = 0;
   2557 	uint8_t pt;
   2558 
   2559 	start = fp_start;
   2560 	pt = mbr_type_from_start(&parts->mbr, start);
   2561 	if (find_mbr_space(&parts->mbr, &start, &size, start,
   2562 	    parts->dp.disk_size, start, MBR_IS_EXTENDED(pt)) < 0)
   2563 		return 0;
   2564 
   2565 	return size;
   2566 }
   2567 
   2568 static size_t
   2569 mbr_get_free_spaces(const struct disk_partitions *arg,
   2570     struct disk_part_free_space *result, size_t max_num_result,
   2571     daddr_t min_size, daddr_t align, daddr_t lower_bound, daddr_t ignore)
   2572 {
   2573 	const struct mbr_disk_partitions *parts =
   2574 	    (const struct mbr_disk_partitions*)arg;
   2575 	uint start = 0, size = 0, from, next;
   2576 	size_t spaces = 0;
   2577 
   2578 	if (min_size < 1)
   2579 		min_size = 1;
   2580 	from = parts->ptn_0_offset;
   2581 	if (lower_bound > from)
   2582 		from = lower_bound;
   2583 	for ( ; from < parts->dp.disk_size && spaces < max_num_result; ) {
   2584 		if (find_mbr_space(&parts->mbr, &start, &size, from,
   2585 		    parts->dp.disk_size, ignore > 0 ? (uint)ignore : UINT_MAX,
   2586 		    false) < 0)
   2587 			break;
   2588 		next = start + size + 1;
   2589 		if (align > 0) {
   2590 			uint nv = max(roundup(start, align), align);
   2591 			uint off = nv - start;
   2592 			start = nv;
   2593 			if (size > off)
   2594 				size -= off;
   2595 			else
   2596 				size = 0;
   2597 		}
   2598 		if (size > min_size) {
   2599 			result[spaces].start = start;
   2600 			result[spaces].size = size;
   2601 			spaces++;
   2602 		}
   2603 		if ((daddr_t)start + (daddr_t)size + 1 >= mbr_parts.size_limit)
   2604 			break;
   2605 		from = next;
   2606 	}
   2607 
   2608 	return spaces;
   2609 }
   2610 
   2611 static bool
   2612 mbr_can_add_partition(const struct disk_partitions *arg)
   2613 {
   2614 	const struct mbr_disk_partitions *myparts =
   2615 	    (const struct mbr_disk_partitions*)arg;
   2616 	struct disk_part_free_space space;
   2617 	bool free_primary, have_extended;
   2618 
   2619 	if (arg->free_space < myparts->ptn_alignment)
   2620 		return false;
   2621 
   2622 	if (mbr_get_free_spaces(arg, &space, 1, myparts->ptn_alignment,
   2623 	    myparts->ptn_alignment, 0, -1) < 1)
   2624 		return false;
   2625 
   2626 	for (int i = 0; i < MBR_PART_COUNT; i++) {
   2627 		uint8_t t = myparts->mbr.mbr.mbr_parts[i].mbrp_type;
   2628 
   2629 		if (t == MBR_PTYPE_UNUSED &&
   2630 		     myparts->mbr.mbr.mbr_parts[i].mbrp_size == 0)
   2631 			free_primary = true;
   2632 
   2633 		if (MBR_IS_EXTENDED(t))
   2634 			have_extended = true;
   2635 	}
   2636 
   2637 	if (have_extended)
   2638 		return true;
   2639 
   2640 	return free_primary;
   2641 }
   2642 
   2643 static void
   2644 mbr_free_wedge(int *fd, const char *disk, const char *wedge)
   2645 {
   2646 	struct dkwedge_info dkw;
   2647 	char diskpath[MAXPATHLEN];
   2648 
   2649 	if (*fd == -1)
   2650 		*fd = opendisk(disk, O_RDWR, diskpath,
   2651 		    sizeof(diskpath), 0);
   2652 	if (*fd != -1) {
   2653 		memset(&dkw, 0, sizeof(dkw));
   2654 		strlcpy(dkw.dkw_devname, wedge,
   2655 		    sizeof(dkw.dkw_devname));
   2656 		ioctl(*fd, DIOCDWEDGE, &dkw);
   2657 	}
   2658 }
   2659 
   2660 static void
   2661 mbr_free(struct disk_partitions *arg)
   2662 {
   2663 	struct mbr_disk_partitions *parts = (struct mbr_disk_partitions*)arg;
   2664 	mbr_info_t *m;
   2665 	int i, fd;
   2666 
   2667 	assert(parts != NULL);
   2668 
   2669 	fd = -1;
   2670 	m = &parts->mbr;
   2671 	do {
   2672 		for (i = 0; i < MBR_PART_COUNT; i++) {
   2673 			if (m->wedge[i][0] != 0)
   2674 				mbr_free_wedge(&fd, arg->disk, m->wedge[i]);
   2675 		}
   2676 	} while ((m = m->extended));
   2677 
   2678 	if (fd != -1)
   2679 		close(fd);
   2680 
   2681 	if (parts->dlabel)
   2682 		parts->dlabel->pscheme->free(parts->dlabel);
   2683 
   2684 	free_mbr_info(parts->mbr.extended);
   2685 	free_last_mounted(&parts->mbr);
   2686 	free(__UNCONST(parts->dp.disk));
   2687 	free(parts);
   2688 }
   2689 
   2690 static void
   2691 mbr_destroy_part_scheme(struct disk_partitions *arg)
   2692 {
   2693 	struct mbr_disk_partitions *parts = (struct mbr_disk_partitions*)arg;
   2694 	char diskpath[MAXPATHLEN];
   2695 	int fd;
   2696 
   2697 	if (parts->dlabel != NULL)
   2698 		parts->dlabel->pscheme->destroy_part_scheme(parts->dlabel);
   2699 	fd = opendisk(arg->disk, O_RDWR, diskpath, sizeof(diskpath), 0);
   2700 	if (fd != -1) {
   2701 		char *buf;
   2702 
   2703 		buf = calloc(arg->bytes_per_sector, 1);
   2704 		if (buf != NULL) {
   2705 			write(fd, buf, arg->bytes_per_sector);
   2706 			free(buf);
   2707 		}
   2708 		close(fd);
   2709 	}
   2710 	mbr_free(arg);
   2711 }
   2712 
   2713 static bool
   2714 mbr_verify_for_update(struct disk_partitions *arg)
   2715 {
   2716 	struct mbr_disk_partitions *parts =
   2717 	    (struct mbr_disk_partitions*)arg;
   2718 
   2719 	return md_mbr_update_check(arg, &parts->mbr);
   2720 }
   2721 
   2722 static int
   2723 mbr_verify(struct disk_partitions *arg, bool quiet)
   2724 {
   2725 	struct mbr_disk_partitions *parts =
   2726 	    (struct mbr_disk_partitions*)arg;
   2727 	mbr_info_t *m = &parts->mbr;
   2728 	int i;
   2729 	bool active_found = false;
   2730 
   2731 	for (i = 0; i < MBR_PART_COUNT; i++) {
   2732 		if (m->mbr.mbr_parts[i].mbrp_flag & MBR_PFLAG_ACTIVE) {
   2733 			active_found = true;
   2734 			break;
   2735 		}
   2736 	}
   2737 
   2738 	if (!active_found && pm->ptstart > 0) {
   2739 		struct mbr_partition *mp = mbr_ptr_from_start(m, pm->ptstart);
   2740 
   2741 		if (mp) {
   2742 			if (!quiet)
   2743 				msg_display(MSG_noactivepart);
   2744 			if (quiet || ask_yesno(MSG_fixactivepart)) {
   2745 				mp->mbrp_flag |= MBR_PFLAG_ACTIVE;
   2746 				active_found = true;
   2747 			}
   2748 		}
   2749 	}
   2750 	if (!active_found && !quiet) {
   2751 		msg_display(MSG_noactivepart);
   2752 		i = ask_reedit(arg);
   2753 		if (i <= 1)
   2754 			return i;
   2755 	}
   2756 
   2757 	for (i = 0; i < MBR_PART_COUNT; i++) {
   2758 		if (m->mbr.mbr_parts[i].mbrp_type != MBR_PTYPE_NETBSD)
   2759 			continue;
   2760 		m->mbr.mbr_parts[i].mbrp_flag |= MBR_PFLAG_ACTIVE;
   2761 		break;
   2762 	}
   2763 
   2764 	return md_check_mbr(arg, &parts->mbr, quiet);
   2765 }
   2766 
   2767 static bool
   2768 mbr_guess_root(const struct disk_partitions *arg,
   2769     daddr_t *start, daddr_t *size)
   2770 {
   2771 	const struct mbr_disk_partitions *parts =
   2772 	    (const struct mbr_disk_partitions*)arg;
   2773 	const mbr_info_t *m = &parts->mbr;
   2774 	size_t i, num_found;
   2775 	bool prim = true;
   2776 	daddr_t pstart, psize;
   2777 
   2778 	num_found = 0;
   2779 	do {
   2780 		for (i = 0; i < MBR_PART_COUNT; i++) {
   2781 			if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
   2782 				continue;
   2783 
   2784 			if (!prim && i > 0 &&
   2785 			    MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
   2786 				break;
   2787 
   2788 			const struct mbr_partition *mp = &m->mbr.mbr_parts[i];
   2789 			if (mp->mbrp_type != MBR_PTYPE_NETBSD)
   2790 				continue;
   2791 
   2792 			if (num_found == 0) {
   2793 				pstart = m->sector + mp->mbrp_start;
   2794 				psize = mp->mbrp_size;
   2795 			}
   2796 			num_found++;
   2797 
   2798 			if (m->last_mounted[i] != NULL &&
   2799 			    strcmp(m->last_mounted[i], "/") == 0) {
   2800 				*start = pstart;
   2801 				*size = psize;
   2802 				return true;
   2803 			}
   2804 		}
   2805 		prim = false;
   2806 	} while ((m = m->extended));
   2807 
   2808 	if (num_found == 1) {
   2809 		*start = pstart;
   2810 		*size = psize;
   2811 		return true;
   2812 	}
   2813 
   2814 	return false;
   2815 }
   2816 
   2817 struct part_attr_fmt_data {
   2818 	char *str;
   2819 	size_t avail_space, attr_no;
   2820 	const struct mbr_disk_partitions *parts;
   2821 	const struct disk_part_info *info;
   2822 };
   2823 
   2824 struct part_attr_set_data {
   2825 	size_t attr_no;
   2826 	const struct mbr_disk_partitions *parts;
   2827 	const char *str;
   2828 	mbr_info_t *mbr;
   2829 };
   2830 
   2831 static bool
   2832 part_attr_format_str(const struct disk_partitions *arg, part_id id,
   2833     const mbr_info_t *mb, int i, bool primary,
   2834     const struct mbr_partition *mp, void *cookie)
   2835 {
   2836 	const struct mbr_disk_partitions *parts =
   2837 	    (const struct mbr_disk_partitions*)arg;
   2838 	struct part_attr_fmt_data *data = cookie;
   2839 	const char *attrtype = parts->dp.pscheme
   2840 	    ->custom_attributes[data->attr_no].label;
   2841 
   2842 	if (attrtype == MSG_ptn_active) {
   2843 		strlcpy(data->str,
   2844 		    msg_string(primary && (mp->mbrp_flag & MBR_PFLAG_ACTIVE) ?
   2845 		    MSG_Yes : MSG_No), data->avail_space);
   2846 		return true;
   2847 #if BOOTSEL
   2848 	} else if (attrtype == MSG_boot_dflt) {
   2849 		strlcpy(data->str,
   2850 		    msg_string(
   2851 			(parts->mbr.bootsec == mb->sector+mp->mbrp_start) ?
   2852 		    MSG_Yes : MSG_No), data->avail_space);
   2853 		return true;
   2854 	} else if (attrtype == MSG_bootmenu) {
   2855 		strlcpy(data->str, mb->mbrb.mbrbs_nametab[i],
   2856 		    data->avail_space);
   2857 #endif
   2858 	}
   2859 
   2860 	return false;
   2861 }
   2862 
   2863 static bool
   2864 part_attr_set_str(const struct disk_partitions *arg, part_id id,
   2865     const mbr_info_t *mb, int i, bool primary,
   2866     const struct mbr_partition *mp, void *cookie)
   2867 {
   2868 	struct part_attr_set_data *data = cookie;
   2869 	const char *str = data->str;
   2870 #ifdef BOOTSEL
   2871 	const struct mbr_disk_partitions *parts =
   2872 	    (const struct mbr_disk_partitions*)arg;
   2873 	const char *attrtype = parts->dp.pscheme
   2874 	    ->custom_attributes[data->attr_no].label;
   2875 	mbr_info_t *m;
   2876 #endif
   2877 
   2878 	while (*str == ' ')
   2879 		str++;
   2880 
   2881 #if BOOTSEL
   2882 	if (attrtype == MSG_bootmenu) {
   2883 		for (m = data->mbr; m != mb; m = m->extended)
   2884 			;
   2885 		strncpy(m->mbrb.mbrbs_nametab[i], str,
   2886 		    sizeof(m->mbrb.mbrbs_nametab[i]));
   2887 	}
   2888 #endif
   2889 
   2890 	return false;
   2891 }
   2892 
   2893 static bool
   2894 part_attr_toggle(const struct disk_partitions *arg, part_id id,
   2895     const mbr_info_t *mb, int i, bool primary,
   2896     const struct mbr_partition *mp, void *cookie)
   2897 {
   2898 	const struct mbr_disk_partitions *parts =
   2899 	    (const struct mbr_disk_partitions*)arg;
   2900 	struct part_attr_set_data *data = cookie;
   2901 	const char *attrtype = parts->dp.pscheme
   2902 	    ->custom_attributes[data->attr_no].label;
   2903 	int j;
   2904 
   2905 	if (attrtype == MSG_ptn_active) {
   2906 		if (!primary)
   2907 			return false;
   2908 
   2909 		data->mbr->mbr.mbr_parts[i].mbrp_flag ^= MBR_PFLAG_ACTIVE;
   2910 		for (j = 0; j < MBR_PART_COUNT; j++) {
   2911 			if (j == i)
   2912 				continue;
   2913 			data->mbr->mbr.mbr_parts[j].mbrp_flag
   2914 			    &= ~MBR_PFLAG_ACTIVE;
   2915 		}
   2916 		return true;
   2917 #ifdef BOOTSEL
   2918 	} else if (attrtype == MSG_boot_dflt) {
   2919 		if (data->mbr->bootsec == mb->sector+mp->mbrp_start)
   2920 			data->mbr->bootsec = 0;
   2921 		else
   2922 			data->mbr->bootsec = mb->sector+mp->mbrp_start;
   2923 		return true;
   2924 #endif
   2925 	}
   2926 
   2927 	return false;
   2928 }
   2929 
   2930 static bool
   2931 mbr_custom_attribute_format(const struct disk_partitions *arg,
   2932     part_id id, size_t attr_no, const struct disk_part_info *info,
   2933     char *res, size_t space)
   2934 {
   2935 	const struct mbr_disk_partitions *parts =
   2936 	    (const struct mbr_disk_partitions*)arg;
   2937 	struct part_attr_fmt_data data;
   2938 
   2939 	data.str = res;
   2940 	data.avail_space = space;
   2941 	data.attr_no = attr_no;
   2942 	data.parts = parts;
   2943 	data.info = info;
   2944 
   2945 	return mbr_part_apply(arg, id, part_attr_format_str, &data);
   2946 }
   2947 
   2948 static bool
   2949 mbr_custom_attribute_toggle(struct disk_partitions *arg,
   2950     part_id id, size_t attr_no)
   2951 {
   2952 	struct mbr_disk_partitions *parts =
   2953 	    (struct mbr_disk_partitions*)arg;
   2954 	struct part_attr_set_data data;
   2955 
   2956 	data.attr_no = attr_no;
   2957 	data.parts = parts;
   2958 	data.str = NULL;
   2959 #ifdef BOOTSEL
   2960 	data.mbr = &parts->mbr;
   2961 #endif
   2962 
   2963 	return mbr_part_apply(arg, id, part_attr_toggle, &data);
   2964 }
   2965 
   2966 static bool
   2967 mbr_custom_attribute_set_str(struct disk_partitions *arg,
   2968     part_id id, size_t attr_no, const char *new_val)
   2969 {
   2970 	struct mbr_disk_partitions *parts =
   2971 	    (struct mbr_disk_partitions*)arg;
   2972 	struct part_attr_set_data data;
   2973 
   2974 	data.attr_no = attr_no;
   2975 	data.parts = parts;
   2976 	data.str = new_val;
   2977 #ifdef BOOTSEL
   2978 	data.mbr = &parts->mbr;
   2979 #endif
   2980 
   2981 	return mbr_part_apply(arg, id, part_attr_set_str, &data);
   2982 }
   2983 
   2984 static daddr_t
   2985 mbr_part_alignment(const struct disk_partitions *arg)
   2986 {
   2987 	const struct mbr_disk_partitions *parts =
   2988 	    (const struct mbr_disk_partitions*)arg;
   2989 
   2990 	return parts->ptn_alignment;
   2991 }
   2992 
   2993 static bool
   2994 add_wedge(const char *disk, daddr_t start, daddr_t size,
   2995     char *wname, size_t max_len)
   2996 {
   2997 	struct dkwedge_info dkw;
   2998 	char diskpath[MAXPATHLEN];
   2999 	int fd;
   3000 
   3001 	memset(&dkw, 0, sizeof(dkw));
   3002 	dkw.dkw_offset = start;
   3003 	dkw.dkw_size = size;
   3004 	snprintf((char*)dkw.dkw_wname, sizeof dkw.dkw_wname,
   3005 	    "%s_%" PRIi64 "@%" PRIi64, disk, size, start);
   3006 
   3007 	*wname = 0;
   3008 
   3009 	fd = opendisk(disk, O_RDWR, diskpath, sizeof(diskpath), 0);
   3010 	if (fd < 0)
   3011 		return false;
   3012 	if (ioctl(fd, DIOCAWEDGE, &dkw) == -1) {
   3013 		close(fd);
   3014 		return false;
   3015 	}
   3016 	close(fd);
   3017 	strlcpy(wname, dkw.dkw_devname, max_len);
   3018 	return true;
   3019 }
   3020 
   3021 static bool
   3022 mbr_get_part_device(const struct disk_partitions *arg,
   3023     part_id ptn, char *devname, size_t max_devname_len, int *part,
   3024     enum dev_name_usage usage, bool with_path, bool life)
   3025 {
   3026 	const struct mbr_disk_partitions *parts =
   3027 	    (const struct mbr_disk_partitions*)arg;
   3028 	struct disk_part_info info, tmp;
   3029 	part_id dptn;
   3030 	char *wedge_dev;
   3031 
   3032 	if (!mbr_get_part_info(arg, ptn, &info))
   3033 		return false;
   3034 
   3035 	if (!mbr_part_get_wedge(arg, ptn, &wedge_dev) || wedge_dev == NULL)
   3036 		return false;
   3037 
   3038 	if (wedge_dev[0] == 0) {
   3039 		/*
   3040 		 * If we have secondary partitions, try to find a match there
   3041 		 * and use that...
   3042 		 */
   3043 		if (parts->dlabel != NULL) {
   3044 			for (dptn = 0; dptn < parts->dlabel->num_part; dptn++) {
   3045 				if (!parts->dlabel->pscheme->get_part_info(
   3046 				    parts->dlabel, dptn, &tmp))
   3047 					continue;
   3048 				if (tmp.start != info.start ||
   3049 				    tmp.size != info.size)
   3050 					continue;
   3051 				return parts->dlabel->pscheme->get_part_device(
   3052 				    parts->dlabel, dptn, devname,
   3053 				     max_devname_len,
   3054 				    part, usage, with_path, life);
   3055 			}
   3056 		}
   3057 
   3058 		/*
   3059 		 * Configure a new wedge and remember the name
   3060 		 */
   3061 		if (!add_wedge(arg->disk, info.start, info.size, wedge_dev,
   3062 		    MBR_DEV_LEN))
   3063 			return false;
   3064 	}
   3065 
   3066 	assert(wedge_dev[0] != 0);
   3067 
   3068 	switch (usage) {
   3069 	case logical_name:
   3070 	case plain_name:
   3071 		if (with_path)
   3072 			snprintf(devname, max_devname_len, _PATH_DEV "%s",
   3073 			    wedge_dev);
   3074 		else
   3075 			strlcpy(devname, wedge_dev, max_devname_len);
   3076 		return true;
   3077 	case raw_dev_name:
   3078 		if (with_path)
   3079 			snprintf(devname, max_devname_len, _PATH_DEV "r%s",
   3080 			    wedge_dev);
   3081 		else
   3082 			snprintf(devname, max_devname_len, "r%s",
   3083 			    wedge_dev);
   3084 		return true;
   3085 	default:
   3086 		return false;
   3087 	}
   3088 }
   3089 
   3090 static bool
   3091 is_custom_attribute_writable(const struct disk_partitions *arg, part_id id,
   3092     const mbr_info_t *mb, int i, bool primary,
   3093     const struct mbr_partition *mp, void *cookie)
   3094 {
   3095 	const struct mbr_disk_partitions *parts =
   3096 	    (const struct mbr_disk_partitions*)arg;
   3097 	struct part_attr_set_data *data = cookie;
   3098 	const char *attrtype = parts->dp.pscheme
   3099 	    ->custom_attributes[data->attr_no].label;
   3100 
   3101 	if (attrtype == MSG_ptn_active)
   3102 	        /* Only 'normal' partitions can be 'Active' */
   3103 		return primary && !MBR_IS_EXTENDED(mp->mbrp_type);
   3104 #ifdef BOOTSEL
   3105 	else if (attrtype == MSG_boot_dflt)
   3106 	        /* Only partitions with bootmenu names can be default */
   3107 		return mb->mbrb.mbrbs_nametab[i][0] != 0;
   3108 	else if (attrtype == MSG_bootmenu)
   3109         	/* The extended partition isn't bootable */
   3110 		return !MBR_IS_EXTENDED(mp->mbrp_type);
   3111 #endif
   3112 
   3113 	return false;
   3114 }
   3115 
   3116 static bool
   3117 mbr_custom_attribute_writable(const struct disk_partitions *arg,
   3118     part_id id, size_t attr_no)
   3119 {
   3120 	const struct mbr_disk_partitions *parts =
   3121 	    (const struct mbr_disk_partitions*)arg;
   3122 	struct part_attr_set_data data;
   3123 
   3124 	data.attr_no = attr_no;
   3125 	data.parts = parts;
   3126 	data.str = NULL;
   3127 #ifdef BOOTSEL
   3128 	data.mbr = NULL;
   3129 #endif
   3130 
   3131 	return mbr_part_apply(arg, id, is_custom_attribute_writable, &data);
   3132 }
   3133 
   3134 const struct disk_part_edit_column_desc mbr_edit_columns[] = {
   3135 	{ .title = MSG_mbr_part_header_1,
   3136 #if BOOTSEL
   3137 	  .width = 16U
   3138 #else
   3139 	  .width = 26U
   3140 #endif
   3141 	 },
   3142 	{ .title = MSG_mbr_part_header_2, .width = 8U },
   3143 #if BOOTSEL
   3144 	{ .title = MSG_mbr_part_header_3, .width = 9U },
   3145 #endif
   3146 };
   3147 
   3148 const struct disk_part_custom_attribute mbr_custom_attrs[] = {
   3149 	{ .label = MSG_ptn_active,	.type = pet_bool },
   3150 #if BOOTSEL
   3151 	{ .label = MSG_boot_dflt,	.type = pet_bool },
   3152 	{ .label = MSG_bootmenu,	.type = pet_str,
   3153 					.strlen = MBR_BS_PARTNAMESIZE },
   3154 #endif
   3155 };
   3156 
   3157 const struct disk_partitioning_scheme
   3158 mbr_parts = {
   3159 	.name = MSG_parttype_mbr,
   3160 	.short_name = MSG_parttype_mbr_short,
   3161 	.new_type_prompt = MSG_mbr_get_ptn_id,
   3162 	.part_flag_desc = MSG_mbr_flag_desc,
   3163 	.size_limit = (daddr_t)UINT32_MAX,
   3164 	.secondary_scheme = &disklabel_parts,
   3165 	.edit_columns_count = __arraycount(mbr_edit_columns),
   3166 	.edit_columns = mbr_edit_columns,
   3167 	.custom_attribute_count = __arraycount(mbr_custom_attrs),
   3168 	.custom_attributes = mbr_custom_attrs,
   3169 	.get_part_alignment = mbr_part_alignment,
   3170 	.get_part_info = mbr_get_part_info,
   3171 	.get_part_attr_str = mbr_part_attr_str,
   3172 	.format_partition_table_str = mbr_table_str,
   3173 	.part_type_can_change = mbr_part_type_can_change,
   3174 	.can_add_partition = mbr_can_add_partition,
   3175 	.custom_attribute_writable = mbr_custom_attribute_writable,
   3176 	.format_custom_attribute = mbr_custom_attribute_format,
   3177 	.custom_attribute_toggle = mbr_custom_attribute_toggle,
   3178 	.custom_attribute_set_str = mbr_custom_attribute_set_str,
   3179 	.get_part_types_count = mbr_get_part_type_count,
   3180 	.adapt_foreign_part_info = generic_adapt_foreign_part_info,
   3181 	.get_part_type = mbr_get_part_type,
   3182 	.get_fs_part_type = mbr_get_fs_part_type,
   3183 	.get_generic_part_type = mbr_get_generic_part_type,
   3184 	.create_custom_part_type = mbr_custom_part_type,
   3185 	.create_unknown_part_type = mbr_create_unknown_part_type,
   3186 	.secondary_partitions = mbr_read_disklabel,
   3187 	.write_to_disk = mbr_write_to_disk,
   3188 	.read_from_disk = mbr_read_from_disk,
   3189 	.create_new_for_disk = mbr_create_new,
   3190 	.guess_disk_geom = mbr_guess_geom,
   3191 	.get_cylinder_size = mbr_get_cylinder,
   3192 	.change_disk_geom = mbr_change_disk_geom,
   3193 	.get_part_device = mbr_get_part_device,
   3194 	.max_free_space_at = mbr_max_part_size,
   3195 	.get_free_spaces = mbr_get_free_spaces,
   3196 	.set_part_info = mbr_set_part_info,
   3197 	.delete_all_partitions = mbr_delete_all,
   3198 	.delete_partition = mbr_delete_part,
   3199 	.add_partition = mbr_add_part,
   3200 	.guess_install_target = mbr_guess_root,
   3201 	.post_edit_verify = mbr_verify,
   3202 	.pre_update_verify = mbr_verify_for_update,
   3203 	.free = mbr_free,
   3204 	.destroy_part_scheme = mbr_destroy_part_scheme,
   3205 };
   3206 
   3207 #endif
   3208