xref: /netbsd-src/usr.sbin/sysinst/mbr.c (revision 122b5006ee1bd67145794b4cde92f4fe4781a5ec)
1 /*	$NetBSD: mbr.c,v 1.39 2021/05/09 10:37:49 martin Exp $ */
2 
3 /*
4  * Copyright 1997 Piermont Information Systems Inc.
5  * All rights reserved.
6  *
7  * Written by Philip A. Nelson for Piermont Information Systems Inc.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The name of Piermont Information Systems Inc. may not be used to endorse
18  *    or promote products derived from this software without specific prior
19  *    written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31  * THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34 
35 /*
36  * Following applies to the geometry guessing code
37  */
38 
39 /*
40  * Mach Operating System
41  * Copyright (c) 1992 Carnegie Mellon University
42  * All Rights Reserved.
43  *
44  * Permission to use, copy, modify and distribute this software and its
45  * documentation is hereby granted, provided that both the copyright
46  * notice and this permission notice appear in all copies of the
47  * software, derivative works or modified versions, and any portions
48  * thereof, and that both notices appear in supporting documentation.
49  *
50  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
51  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
52  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
53  *
54  * Carnegie Mellon requests users of this software to return to
55  *
56  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
57  *  School of Computer Science
58  *  Carnegie Mellon University
59  *  Pittsburgh PA 15213-3890
60  *
61  * any improvements or extensions that they make and grant Carnegie Mellon
62  * the rights to redistribute these changes.
63  */
64 
65 /* mbr.c -- DOS Master Boot Record editing code */
66 
67 #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 	char short_desc[12];
101 	const char *desc;
102 } mbr_part_types_src[] = {
103 	{ .ptype=MBR_PTYPE_NETBSD, .desc="NetBSD" },
104 	{ .ptype=MBR_PTYPE_FAT32L, .desc="Windows FAT32, LBA" },
105 	{ .ptype=MBR_PTYPE_EXT_LBA, .desc="Extended partition, LBA" },
106 	{ .ptype=MBR_PTYPE_386BSD, .desc="FreeBSD/386BSD" },
107 	{ .ptype=MBR_PTYPE_OPENBSD, .desc="OpenBSD"  },
108 	{ .ptype=MBR_PTYPE_LNXEXT2, .desc="Linux native" },
109 	{ .ptype=MBR_PTYPE_LNXSWAP, .desc="Linux swap" },
110 	{ .ptype=MBR_PTYPE_NTFSVOL, .desc="NTFS volume set" },
111 	{ .ptype=MBR_PTYPE_NTFS, .desc="NTFS" },
112 	{ .ptype=MBR_PTYPE_PREP, .desc="PReP Boot" },
113 #ifdef MBR_PTYPE_SOLARIS
114 	{ .ptype=MBR_PTYPE_SOLARIS, .desc="Solaris" },
115 #endif
116 	{ .ptype=MBR_PTYPE_FAT12, .desc="DOS FAT12" },
117 	{ .ptype=MBR_PTYPE_FAT16S, .desc="DOS FAT16, <32M" },
118 	{ .ptype=MBR_PTYPE_FAT16B, .desc="DOS FAT16, >32M" },
119 	{ .ptype=MBR_PTYPE_FAT16L, .desc="Windows FAT16, LBA" },
120 	{ .ptype=MBR_PTYPE_FAT32, .desc="Windows FAT32" },
121 	{ .ptype=MBR_PTYPE_EFI, .desc="(U)EFI Boot" },
122 };
123 
124 /* bookeeping of available partition types (including custom ones) */
125 struct mbr_part_type_info {
126 	struct part_type_desc gen;	/* generic description */
127 	char desc_buf[40], short_buf[10];
128 	size_t next_ptype;		/* user interface order */
129 };
130 
131 const struct disk_partitioning_scheme mbr_parts;
132 struct mbr_disk_partitions {
133 	struct disk_partitions dp, *dlabel;
134 	mbr_info_t mbr;
135 	uint ptn_alignment, ptn_0_offset, ext_ptn_alignment,
136 	    geo_sec, geo_head, geo_cyl, target;
137 };
138 
139 const struct disk_partitioning_scheme mbr_parts;
140 
141 static size_t known_part_types = 0, last_added_part_type = 0;
142 static const size_t first_part_type = MBR_PTYPE_NETBSD;
143 
144 /* all partition types (we are lucky, only a fixed number is possible) */
145 struct mbr_part_type_info mbr_gen_type_desc[256];
146 
147 extern const struct disk_partitioning_scheme disklabel_parts;
148 
149 static void convert_mbr_chs(int, int, int, uint8_t *, uint8_t *,
150 				 uint8_t *, uint32_t);
151 
152 static part_id mbr_add_part(struct disk_partitions *arg,
153     const struct disk_part_info *info, const char **errmsg);
154 
155 static size_t mbr_get_free_spaces(const struct disk_partitions *arg,
156     struct disk_part_free_space *result, size_t max_num_result,
157     daddr_t min_size, daddr_t align, daddr_t lower_bound, daddr_t ignore);
158 
159 static size_t mbr_type_from_gen_desc(const struct part_type_desc *desc);
160 
161 /*
162  * Notes on the extended partition editor.
163  *
164  * The extended partition structure is actually a singly linked list.
165  * Each of the 'mbr' sectors can only contain 2 items, the first describes
166  * a user partition (relative to that mbr sector), the second describes
167  * the following partition (relative to the start of the extended partition).
168  *
169  * The 'start' sector for the user partition is always the size of one
170  * track - very often 63.  The extended partitions themselves should
171  * always start on a cylinder boundary using the BIOS geometry - often
172  * 16065 sectors per cylinder.
173  *
174  * The disk is also always described in increasing sector order.
175  *
176  * During editing we keep the mbr sectors accurate (it might have been
177  * easier to use absolute sector numbers though), and keep a copy of the
178  * entire sector - to preserve any information any other OS has tried
179  * to squirrel away in the (apparently) unused space.
180  *
181  * Typical disk (with some small numbers):
182  *
183  *      0 -> a       63       37	dos
184  *           b      100     1000	extended LBA (type 15)
185  *
186  *    100 -> a       63       37        user
187  *           b      100      200	extended partiton (type 5)
188  *
189  *    200 -> a       63       37        user
190  *           b      200      300	extended partiton (type 5)
191  *
192  *    300 -> a       63       37	user
193  *           b        0        0        0 (end of chain)
194  *
195  */
196 
197 #ifndef debug_extended
198 #define dump_mbr(mbr, msg)
199 #else
200 static void
201 dump_mbr(mbr_info_t *m, const char *label)
202 {
203 	int i;
204 	uint ext_base = 0;
205 
206 	fprintf(stderr, "\n%s: bsec %d\n", label, bsec);
207 	do {
208 		fprintf(stderr, "%p: %12u %p\n",
209 		    m, m->sector, m->extended);
210 		for (i = 0; i < MBR_PART_COUNT; i++) {
211 			fprintf(stderr, " %*d %12u %12u %12" PRIu64,
212 			    10,
213 			    m->mbr.mbr_parts[i].mbrp_type,
214 			    m->mbr.mbr_parts[i].mbrp_start,
215 			    m->mbr.mbr_parts[i].mbrp_size,
216 			    (uint64_t)m->mbr.mbr_parts[i].mbrp_start +
217 				(uint64_t)m->mbr.mbr_parts[i].mbrp_size);
218 			if (m->sector == 0 &&
219 			    MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
220 				ext_base = m->mbr.mbr_parts[i].mbrp_start;
221 			if (m->sector > 0 &&
222 			    m->mbr.mbr_parts[i].mbrp_size > 0) {
223 				uint off = MBR_IS_EXTENDED(
224 				    m->mbr.mbr_parts[i].mbrp_type)
225 				    ? ext_base : m->sector;
226 				fprintf(stderr, " -> [%u .. %u]",
227 				    m->mbr.mbr_parts[i].mbrp_start + off,
228 				    m->mbr.mbr_parts[i].mbrp_size +
229 				    m->mbr.mbr_parts[i].mbrp_start + off);
230 			}
231 			fprintf(stderr, ",\n");
232 			if (m->sector > 0 && i >= 1)
233 				break;
234 		}
235 	} while ((m = m->extended));
236 }
237 #endif
238 
239 /*
240  * Like pread, but handles re-blocking for non 512 byte sector disks
241  */
242 static ssize_t
243 blockread(int fd, size_t secsize, void *buf, size_t nbytes, off_t offset)
244 {
245         ssize_t nr;
246 	off_t sector = offset / 512;
247         off_t offs = sector * (off_t)secsize;
248         off_t mod = offs & (secsize - 1);
249         off_t rnd = offs & ~(secsize - 1);
250 	char *iobuf;
251 
252 	assert(nbytes <= 512);
253 
254 	if (secsize == 512)
255 		return pread(fd, buf, nbytes, offset);
256 
257 	iobuf = malloc(secsize);
258 	if (iobuf == NULL)
259 		return -1;
260 	nr = pread(fd, iobuf, secsize, rnd);
261 	if (nr == (off_t)secsize)
262 		memcpy(buf, &iobuf[mod], nbytes);
263 	free(iobuf);
264 
265 	return nr == (off_t)secsize ? (off_t)nbytes : -1;
266 }
267 
268 /*
269  * Same for pwrite
270  */
271 static ssize_t
272 blockwrite(int fd, size_t secsize, const void *buf, size_t nbytes,
273     off_t offset)
274 {
275         ssize_t nr;
276 	off_t sector = offset / secsize;
277         off_t offs = sector * (off_t)secsize;
278         off_t mod = offs & (secsize - 1);
279         off_t rnd = offs & ~(secsize - 1);
280 	char *iobuf;
281 
282 	assert(nbytes <= 512);
283 
284 	if (secsize == 512)
285 		return pwrite(fd, buf, nbytes, offset);
286 
287 	iobuf = malloc(secsize);
288 	if (iobuf == NULL)
289 		return -1;
290 	nr = pread(fd, iobuf, secsize, rnd);
291 	if (nr == (off_t)secsize) {
292 		memcpy(&iobuf[mod], buf, nbytes);
293 		nr = pwrite(fd, iobuf, secsize, rnd);
294 	}
295 	free(iobuf);
296 
297 	return nr == (off_t)secsize ? (off_t)nbytes : -1;
298 }
299 
300 static void
301 free_last_mounted(mbr_info_t *m)
302 {
303 	size_t i;
304 
305 	for (i = 0; i < MBR_PART_COUNT; i++)
306 		free(__UNCONST(m->last_mounted[i]));
307 }
308 
309 static void
310 free_mbr_info(mbr_info_t *m)
311 {
312 	if (m == NULL)
313 		return;
314 	free_last_mounted(m);
315 	free(m);
316 }
317 
318 /*
319  * To be used only on ports which cannot provide any bios geometry
320  */
321 int
322 set_bios_geom_with_mbr_guess(struct disk_partitions *parts)
323 {
324 	int cyl, head, sec;
325 
326 	msg_fmt_display(MSG_nobiosgeom, "%d%d%d",
327 	    pm->dlcyl, pm->dlsec, pm->dlhead);
328 	if (guess_biosgeom_from_parts(parts, &cyl, &head, &sec) >= 0)
329 		msg_fmt_display_add(MSG_biosguess, "%d%d%d", cyl, head, sec);
330 	set_bios_geom(parts, &cyl, &head, &sec);
331 	if (parts->pscheme->change_disk_geom)
332 		parts->pscheme->change_disk_geom(parts, cyl, head, sec);
333 
334 	return edit_outer_parts(parts);
335 }
336 
337 static void
338 mbr_init_chs(struct mbr_disk_partitions *parts, int ncyl, int nhead, int nsec)
339 {
340 	if (ncyl > MAXCYL)
341 		ncyl = MAXCYL;
342 	pm->current_cylsize = nhead*nsec;
343 	pm->max_chs = (unsigned long)ncyl*nhead*nsec;
344 	parts->geo_sec = nsec;
345 	parts->geo_head = nhead;
346 	parts->geo_cyl = ncyl;
347 }
348 
349 /*
350  * get C/H/S geometry from user via menu interface and
351  * store in globals.
352  */
353 void
354 set_bios_geom(struct disk_partitions *parts, int *cyl, int *head, int *sec)
355 {
356 	char res[80];
357 	int bsec, bhead, bcyl;
358 	daddr_t s;
359 
360 	if (parts->pscheme->change_disk_geom == NULL)
361 		return;
362 
363 	msg_display_add(MSG_setbiosgeom);
364 
365 	do {
366 		snprintf(res, 80, "%d", *sec);
367 		msg_prompt_add(MSG_sectors, res, res, 80);
368 		bsec = atoi(res);
369 	} while (bsec <= 0 || bsec > MAXSECTOR);
370 
371 	do {
372 		snprintf(res, 80, "%d", *head);
373 		msg_prompt_add(MSG_heads, res, res, 80);
374 		bhead = atoi(res);
375 	} while (bhead <= 0 || bhead > MAXHEAD);
376 	s = min(pm->dlsize, mbr_parts.size_limit);
377 	bcyl = s / bsec / bhead;
378 	if (s != bcyl * bsec * bhead)
379 		bcyl++;
380 	if (bcyl > MAXCYL)
381 		bcyl = MAXCYL;
382 	pm->max_chs = (unsigned long)bcyl * bhead * bsec;
383 	pm->current_cylsize = bhead * bsec;
384 	parts->pscheme->change_disk_geom(parts, bcyl, bhead, bsec);
385 	*cyl = bcyl;
386 	*head = bhead;
387 	*sec = bsec;
388 }
389 
390 static int
391 find_mbr_space(const struct mbr_info_t *mbrs, uint *start, uint *size,
392     uint from, uint end_of_disk, uint ignore_at, bool primary_only)
393 {
394 	uint sz;
395 	int i, j;
396 	uint s, e;
397 	const mbr_info_t *m, *me;
398 	bool is_extended;
399 
400     check_again:
401 	m = mbrs;
402 	sz = end_of_disk-from;
403 	if (from >= end_of_disk)
404 		return -1;
405 
406 	for (i = 0; i < MBR_PART_COUNT; i++) {
407 		if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
408 			continue;
409 
410 		is_extended = MBR_IS_EXTENDED(
411 		    m->mbr.mbr_parts[i].mbrp_type);
412 
413 		s = m->mbr.mbr_parts[i].mbrp_start + m->sector;
414 		if (s == ignore_at)
415 			continue;
416 		e = s + m->mbr.mbr_parts[i].mbrp_size;
417 		if (s <= from && e > from
418 		    && (!is_extended || primary_only)) {
419 			if (e - 1 >= end_of_disk)
420 				break;
421 			if (e >= UINT_MAX) {
422 				sz = 0;
423 				break;
424 			}
425 			from = e + 1;
426 			goto check_again;
427 		}
428 		if (s <= from && e > from && is_extended) {
429 			/*
430 			 * if we start in the extended partiton,
431 			 * we must end before its end
432 			 */
433 			sz = e - from;
434 		}
435 		if (s > from && s - from < sz)
436 			sz = s - from;
437 
438 		if (is_extended) {
439 			for (me = m->extended; me != NULL; me = me->extended) {
440 				for (j = 0; j < MBR_PART_COUNT; j++) {
441 					if (me->mbr.mbr_parts[j].mbrp_type ==
442 					    MBR_PTYPE_UNUSED)
443 						continue;
444 
445 					is_extended = MBR_IS_EXTENDED(
446 					    me->mbr.mbr_parts[j].mbrp_type);
447 
448 					if (is_extended && i > 0)
449 						break;
450 
451 					s = me->mbr.mbr_parts[j].mbrp_start +
452 					    me->sector;
453 					if (s == ignore_at)
454 						continue;
455 					e = s + me->mbr.mbr_parts[j].mbrp_size;
456 					if (me->sector != 0 && me->sector< s)
457 						/*
458 						 * can not allow to overwrite
459 						 * the ext mbr
460 						 */
461 						s = me->sector;
462 					if (s <= from && e > from) {
463 						if (e - 1 >= end_of_disk)
464 							break;
465 						from = e + 1;
466 						goto check_again;
467 					}
468 					if (s > from && s - from < sz)
469 						sz = s - from;
470 
471 				}
472 			}
473 		}
474 	}
475 
476 	if (sz == 0)
477 		return -1;
478 	if (start != NULL)
479 		*start = from;
480 	if (size != NULL)
481 		*size = sz;
482 	return 0;
483 }
484 
485 #ifdef BOOTSEL
486 static int
487 validate_and_set_names(mbr_info_t *mbri, const struct mbr_bootsel *src,
488     uint32_t ext_base)
489 {
490 	size_t i, l;
491 	const unsigned char *p;
492 
493 	/*
494 	 * The 16 bit magic used to detect whether mbr_bootsel is valid
495 	 * or not is pretty week - collisions have been seen in the wild;
496 	 * but maybe it is just foreign tools corruption reminiscents
497 	 * of NetBSD MBRs. Anyway, before accepting a boot menu definition,
498 	 * make sure it is kinda "sane".
499 	 */
500 
501 	for (i = 0; i < MBR_PART_COUNT; i++) {
502 		/*
503 		 * Make sure the name does not contain controll chars
504 		 * (not using iscntrl due to minimalistic locale support
505 		 * in miniroot environments) and is properly 0-terminated.
506 		 */
507 		for (l = 0, p = (const unsigned char *)&src->mbrbs_nametab[i];
508 		    *p != 0; l++, p++) {
509 			if (l >	MBR_BS_PARTNAMESIZE)
510 				return 0;
511 			if (*p < ' ')	/* hacky 'iscntrl' */
512 				return 0;
513 		}
514 	}
515 
516 	memcpy(&mbri->mbrb, src, sizeof(*src));
517 
518 	if (ext_base == 0)
519 		return mbri->mbrb.mbrbs_defkey - SCAN_1;
520 	return 0;
521 }
522 #endif
523 
524 static int
525 valid_mbr(struct mbr_sector *mbrs)
526 {
527 
528 	return (le16toh(mbrs->mbr_magic) == MBR_MAGIC);
529 }
530 
531 static int
532 read_mbr(const char *disk, size_t secsize, mbr_info_t *mbri,
533      struct mbr_disk_partitions *parts)
534 {
535 	struct mbr_partition *mbrp;
536 	struct mbr_sector *mbrs = &mbri->mbr;
537 	mbr_info_t *ext = NULL;
538 	char diskpath[MAXPATHLEN];
539 	int fd, i;
540 	uint32_t ext_base = 0, next_ext = 0;
541 	int rval = -1;
542 #ifdef BOOTSEL
543 	mbr_info_t *ombri = mbri;
544 	int bootkey = 0;
545 #endif
546 
547 	memset(mbri, 0, sizeof *mbri);
548 
549 	/* Open the disk. */
550 	fd = opendisk(disk, O_RDONLY, diskpath, sizeof(diskpath), 0);
551 	if (fd < 0)
552 		goto bad_mbr;
553 
554 	for (;;) {
555 		if (blockread(fd, secsize, mbrs, sizeof *mbrs,
556 		    (ext_base + next_ext) * (off_t)MBR_SECSIZE)
557 		    - sizeof *mbrs != 0)
558 			break;
559 
560 		if (!valid_mbr(mbrs))
561 			break;
562 
563 		mbrp = &mbrs->mbr_parts[0];
564 		if (ext_base != 0) {
565 			/* sanity check extended chain */
566 			if (MBR_IS_EXTENDED(mbrp[0].mbrp_type))
567 				break;
568 			if (mbrp[1].mbrp_type != MBR_PTYPE_UNUSED &&
569 			    !MBR_IS_EXTENDED(mbrp[1].mbrp_type))
570 				break;
571 			if (mbrp[2].mbrp_type != MBR_PTYPE_UNUSED
572 			    || mbrp[3].mbrp_type != MBR_PTYPE_UNUSED)
573 				break;
574 			/* Looks ok, link into extended chain */
575 			mbri->extended = ext;
576 			ext->extended = NULL;
577 			mbri = ext;
578 			ext = NULL;
579 		}
580 #if BOOTSEL
581 		if (mbrs->mbr_bootsel_magic == htole16(MBR_MAGIC)) {
582 			/* old bootsel, grab bootsel info */
583 			bootkey = validate_and_set_names(mbri,
584 				(struct mbr_bootsel *)
585 				((uint8_t *)mbrs + MBR_BS_OLD_OFFSET),
586 				ext_base);
587 		} else if (mbrs->mbr_bootsel_magic == htole16(MBR_BS_MAGIC)) {
588 			/* new location */
589 			bootkey = validate_and_set_names(mbri,
590 			    &mbrs->mbr_bootsel, ext_base);
591 		}
592 		/* Save original flags for mbr code update tests */
593 		mbri->oflags = mbri->mbrb.mbrbs_flags;
594 #endif
595 		mbri->sector = next_ext + ext_base;
596 		next_ext = 0;
597 		rval = 0;
598 		for (i = 0; i < MBR_PART_COUNT; mbrp++, i++) {
599 			if (mbrp->mbrp_type == MBR_PTYPE_UNUSED) {
600 				/* type is unused, discard scum */
601 				memset(mbrp, 0, sizeof *mbrp);
602 				continue;
603 			}
604 			mbrp->mbrp_start = le32toh(mbrp->mbrp_start);
605 			mbrp->mbrp_size = le32toh(mbrp->mbrp_size);
606 			if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
607 				next_ext = mbrp->mbrp_start;
608 			} else {
609 				uint flags = 0;
610 				if (mbrp->mbrp_type == MBR_PTYPE_NETBSD) {
611 					flags |= GLM_LIKELY_FFS;
612 					if (parts->target == ~0U)
613 						parts->target =
614 						    mbri->sector +
615 						    mbrp->mbrp_start;
616 				} else if (mbrp->mbrp_type == MBR_PTYPE_FAT12 ||
617 				    mbrp->mbrp_type == MBR_PTYPE_FAT16S ||
618 				    mbrp->mbrp_type == MBR_PTYPE_FAT16B ||
619 				    mbrp->mbrp_type == MBR_PTYPE_FAT32 ||
620 				    mbrp->mbrp_type == MBR_PTYPE_FAT32L ||
621 				    mbrp->mbrp_type == MBR_PTYPE_FAT16L)
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/alignemnt 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 begining
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 	}
1192 
1193 	return NULL;
1194 }
1195 
1196 static const struct part_type_desc *
1197 mbr_get_part_type(size_t index)
1198 {
1199 	size_t i, no;
1200 
1201 	if (known_part_types == 0)
1202 		map_mbr_part_types();
1203 
1204 	if (index >= known_part_types)
1205 		return NULL;
1206 
1207 	for (i = first_part_type, no = 0; i < __arraycount(mbr_gen_type_desc)
1208 	    && no != index;  no++)
1209 		i = mbr_gen_type_desc[i].next_ptype;
1210 
1211 	if (i >= __arraycount(mbr_gen_type_desc))
1212 		return NULL;
1213 	return &mbr_gen_type_desc[i].gen;
1214 }
1215 
1216 static const struct part_type_desc *
1217 mbr_new_custom_part_type(unsigned int v)
1218 {
1219 	snprintf(mbr_gen_type_desc[v].short_buf,
1220 	    sizeof(mbr_gen_type_desc[v].short_buf), "%u", v);
1221 	snprintf(mbr_gen_type_desc[v].desc_buf,
1222 	     sizeof(mbr_gen_type_desc[v].desc_buf), "%s (%u)",
1223 	    msg_string(MSG_custom_type), v);
1224 	mbr_gen_type_desc[v].gen.short_desc = mbr_gen_type_desc[v].short_buf;
1225 	mbr_gen_type_desc[v].gen.description = mbr_gen_type_desc[v].desc_buf;
1226 	mbr_gen_type_desc[v].gen.generic_ptype = mbr_map_part_type(v);
1227 	mbr_gen_type_desc[v].next_ptype = ~0U;
1228 	mbr_gen_type_desc[last_added_part_type].next_ptype = v;
1229 	known_part_types++;
1230 	last_added_part_type = v;
1231 
1232 	return &mbr_gen_type_desc[v].gen;
1233 }
1234 
1235 static const struct part_type_desc *
1236 mbr_custom_part_type(const char *custom, const char **err_msg)
1237 {
1238 	unsigned long v;
1239 	char *endp;
1240 
1241 	if (known_part_types == 0)
1242 		map_mbr_part_types();
1243 
1244 	v = strtoul(custom, &endp, 10);
1245 	if (v > 255 || (v == 0 && *endp != 0)) {
1246 		if (err_msg != NULL)
1247 			*err_msg = msg_string(MSG_mbr_type_invalid);
1248 		return NULL;
1249 	}
1250 
1251 	if (mbr_gen_type_desc[v].gen.short_desc != NULL)
1252 		return &mbr_gen_type_desc[v].gen;
1253 
1254 	return mbr_new_custom_part_type(v);
1255 }
1256 
1257 static const struct part_type_desc *
1258 mbr_create_unknown_part_type(void)
1259 {
1260 
1261 	if (mbr_gen_type_desc[MBR_UNKNOWN_PTYPE].gen.short_desc != NULL)
1262 		return &mbr_gen_type_desc[MBR_UNKNOWN_PTYPE].gen;
1263 
1264 	return mbr_new_custom_part_type(MBR_UNKNOWN_PTYPE);
1265 }
1266 
1267 static const struct part_type_desc *
1268 mbr_get_gen_type_desc(unsigned int pt)
1269 {
1270 
1271 	if (known_part_types == 0)
1272 		map_mbr_part_types();
1273 
1274 	if (pt >= __arraycount(mbr_gen_type_desc))
1275 		return NULL;
1276 
1277 	if (mbr_gen_type_desc[pt].gen.short_desc != NULL)
1278 		return &mbr_gen_type_desc[pt].gen;
1279 
1280 	return mbr_new_custom_part_type(pt);
1281 }
1282 
1283 static const struct part_type_desc *
1284 mbr_get_generic_part_type(enum part_type pt)
1285 {
1286 	switch (pt) {
1287 	case PT_root:
1288 		return mbr_get_gen_type_desc(MBR_PTYPE_NETBSD);
1289 	case PT_FAT:
1290 		return mbr_get_gen_type_desc(MBR_PTYPE_FAT32L);
1291 	case PT_EXT2:
1292 		return mbr_get_gen_type_desc(MBR_PTYPE_LNXEXT2);
1293 	case PT_EFI_SYSTEM:
1294 		return mbr_get_gen_type_desc(MBR_PTYPE_EFI);
1295 	default:
1296 		break;
1297 	}
1298 	assert(false);
1299 	return NULL;
1300 }
1301 
1302 static void
1303 mbr_partition_to_info(const struct mbr_partition *mp, daddr_t start_off,
1304     struct disk_part_info *info)
1305 {
1306 	memset(info, 0, sizeof(*info));
1307 	info->start = mp->mbrp_start + start_off;
1308 	info->size = mp->mbrp_size;
1309 	info->nat_type = mbr_get_gen_type_desc(mp->mbrp_type);
1310 	if (mp->mbrp_type == MBR_PTYPE_NETBSD) {
1311 		info->flags |= PTI_SEC_CONTAINER;
1312 	} else if (MBR_IS_EXTENDED(mp->mbrp_type))
1313 		info->flags |= PTI_PSCHEME_INTERNAL;
1314 }
1315 
1316 static bool
1317 mbr_part_apply(const struct disk_partitions *arg, part_id id,
1318     bool (*func)(const struct disk_partitions *arg, part_id id,
1319 	const mbr_info_t *mb, int i, bool primary,
1320 	const struct mbr_partition *mp, void *),
1321     void *cookie)
1322 {
1323 	const struct mbr_disk_partitions *parts =
1324 	    (const struct mbr_disk_partitions*)arg;
1325 	part_id i, j, no;
1326 	const mbr_info_t *m = &parts->mbr, *me;
1327 
1328 	no = 0;
1329 	for (i = 0; i < MBR_PART_COUNT; i++) {
1330 		if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
1331 			continue;
1332 
1333 		if (no == id) {
1334 			return func(arg, id, m, i, true,
1335 			    &m->mbr.mbr_parts[i], cookie);
1336 		}
1337 		no++;
1338 
1339 		if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type)) {
1340 			for (me = m->extended; me != NULL; me = me->extended) {
1341 				for (j = 0; j < MBR_PART_COUNT; j++) {
1342 					if (me->mbr.mbr_parts[j].mbrp_type ==
1343 					    MBR_PTYPE_UNUSED)
1344 						continue;
1345 					if (j > 0 && MBR_IS_EXTENDED(
1346 					    me->mbr.mbr_parts[j].mbrp_type))
1347 						break;
1348 					if (no == id) {
1349 						return func(arg, id, me, j,
1350 						    false,
1351 						    &me->mbr.mbr_parts[j],
1352 						    cookie);
1353 					}
1354 					no++;
1355 				}
1356 			}
1357 		}
1358 	}
1359 
1360 
1361 	return false;
1362 }
1363 
1364 static bool
1365 mbr_do_get_part_info(const struct disk_partitions *arg, part_id id,
1366     const mbr_info_t *mb, int i, bool primary,
1367     const struct mbr_partition *mp, void *cookie)
1368 {
1369 	struct disk_part_info *info = cookie;
1370 	const struct mbr_disk_partitions *parts =
1371 	    (const struct mbr_disk_partitions*)arg;
1372 
1373 	mbr_partition_to_info(mp, mb->sector, info);
1374 	if (mp->mbrp_start + mb->sector == parts->target)
1375 		info->flags |= PTI_INSTALL_TARGET;
1376 	if (mb->last_mounted[i] != NULL && mb->last_mounted[i][0] != 0)
1377 		info->last_mounted = mb->last_mounted[i];
1378 	if (mb->fs_type[i] != FS_UNUSED) {
1379 		info->fs_type = mb->fs_type[i];
1380 		info->fs_sub_type = mb->fs_sub_type[i];
1381 	} else {
1382 		info->fs_sub_type = 0;
1383 		switch (mp->mbrp_type) {
1384 		case MBR_PTYPE_FAT12:
1385 		case MBR_PTYPE_FAT16S:
1386 		case MBR_PTYPE_FAT16B:
1387 		case MBR_PTYPE_FAT32:
1388 		case MBR_PTYPE_FAT32L:
1389 		case MBR_PTYPE_FAT16L:
1390 		case MBR_PTYPE_OS2_DOS12:
1391 		case MBR_PTYPE_OS2_DOS16S:
1392 		case MBR_PTYPE_OS2_DOS16B:
1393 		case MBR_PTYPE_HID_FAT32:
1394 		case MBR_PTYPE_HID_FAT32_LBA:
1395 		case MBR_PTYPE_HID_FAT16_LBA:
1396 		case MBR_PTYPE_MDOS_FAT12:
1397 		case MBR_PTYPE_MDOS_FAT16S:
1398 		case MBR_PTYPE_MDOS_EXT:
1399 		case MBR_PTYPE_MDOS_FAT16B:
1400 		case MBR_PTYPE_SPEEDSTOR_16S:
1401 		case MBR_PTYPE_EFI:
1402 			info->fs_type = FS_MSDOS;
1403 			info->fs_sub_type = mp->mbrp_type;
1404 			break;
1405 		case MBR_PTYPE_LNXEXT2:
1406 			info->fs_type = FS_EX2FS;
1407 			break;
1408 		case MBR_PTYPE_XENIX_ROOT:
1409 		case MBR_PTYPE_XENIX_USR:
1410 			info->fs_type = FS_SYSV;
1411 			break;
1412 		case MBR_PTYPE_NTFS:
1413 			info->fs_type = FS_NTFS;
1414 			break;
1415 		case MBR_PTYPE_APPLE_HFS:
1416 			info->fs_type = FS_HFS;
1417 			break;
1418 		case MBR_PTYPE_VMWARE:
1419 			info->fs_type = FS_VMFS;
1420 			break;
1421 		case MBR_PTYPE_AST_SWAP:
1422 		case MBR_PTYPE_DRDOS_LSWAP:
1423 		case MBR_PTYPE_LNXSWAP:
1424 		case MBR_PTYPE_BSDI_SWAP:
1425 		case MBR_PTYPE_HID_LNX_SWAP:
1426 		case MBR_PTYPE_VMWARE_SWAP:
1427 			info->fs_type = FS_SWAP;
1428 			break;
1429 		}
1430 	}
1431 	return true;
1432 }
1433 
1434 static bool
1435 get_wedge_devname(const struct disk_partitions *arg, part_id id,
1436     const mbr_info_t *mb, int i, bool primary,
1437     const struct mbr_partition *mp, void *cookie)
1438 {
1439 	char **res = cookie;
1440 
1441 	if (!res)
1442 		return false;
1443 
1444 	*res = __UNCONST(mb->wedge[i]);
1445 	return true;
1446 }
1447 
1448 static bool
1449 mbr_part_get_wedge(const struct disk_partitions *arg, part_id id,
1450     char **res)
1451 {
1452 	return mbr_part_apply(arg, id, get_wedge_devname, res);
1453 }
1454 
1455 static bool
1456 mbr_get_part_info(const struct disk_partitions *arg, part_id id,
1457     struct disk_part_info *info)
1458 {
1459 	return mbr_part_apply(arg, id, mbr_do_get_part_info, info);
1460 }
1461 
1462 static bool
1463 type_can_change(const struct disk_partitions *arg, part_id id,
1464     const mbr_info_t *mb, int i, bool primary,
1465     const struct mbr_partition *mp, void *cookie)
1466 {
1467 	/*
1468 	 * The extended partition can only change type or be
1469 	 * deleted if it is empty
1470 	 */
1471 	if (!MBR_IS_EXTENDED(mp->mbrp_type))
1472 		return true;
1473 	return primary && mb->extended == NULL;
1474 }
1475 
1476 static bool
1477 mbr_part_type_can_change(const struct disk_partitions *arg, part_id id)
1478 {
1479 	return mbr_part_apply(arg, id, type_can_change, NULL);
1480 }
1481 
1482 struct part_get_str_data {
1483 	char *str;
1484 	size_t avail_space;
1485 	size_t col;
1486 };
1487 
1488 
1489 static bool
1490 mbr_get_part_table_str(const struct disk_partitions *arg, part_id id,
1491     const mbr_info_t *mb, int i, bool primary,
1492     const struct mbr_partition *mp, void *cookie)
1493 {
1494 	struct part_get_str_data *data = cookie;
1495 	char *str = data->str;
1496 	const struct part_type_desc *ptype;
1497 
1498 	switch (data->col) {
1499 	case 0:
1500 		ptype = mbr_get_gen_type_desc(mp->mbrp_type);
1501 		if (ptype != NULL)
1502 			strncpy(str, ptype->description, data->avail_space);
1503 		else
1504 			snprintf(str, data->avail_space, "%u", mp->mbrp_type);
1505 		str[data->avail_space-1] = 0;
1506 		break;
1507 	case 1:
1508 		if (mb->last_mounted[i])
1509 			strlcpy(str, mb->last_mounted[i], data->avail_space);
1510 		else
1511 			*str = 0;
1512 		break;
1513 #ifdef BOOTSEL
1514 	case 2:
1515 		if (mb->mbrb.mbrbs_nametab[i][0] != 0)
1516 			strlcpy(str, mb->mbrb.mbrbs_nametab[i],
1517 			    data->avail_space);
1518 		else
1519 			*str = 0;
1520 		break;
1521 #endif
1522 	}
1523 
1524 	return true;
1525 }
1526 
1527 static bool
1528 mbr_table_str(const struct disk_partitions *arg, part_id id, size_t col,
1529     char *str, size_t avail_space)
1530 {
1531 	struct part_get_str_data data;
1532 
1533 	data.str = str;
1534 	data.avail_space = avail_space;
1535 	data.col = col;
1536 	return mbr_part_apply(arg, id, mbr_get_part_table_str, &data);
1537 }
1538 
1539 static bool
1540 mbr_get_part_attr_str(const struct disk_partitions *arg, part_id id,
1541     const mbr_info_t *mb, int i, bool primary,
1542     const struct mbr_partition *mp, void *cookie)
1543 {
1544 #ifdef BOOTSEL
1545 	const struct mbr_disk_partitions *parts =
1546 	    (const struct mbr_disk_partitions*)arg;
1547 #endif
1548 	struct part_get_str_data *data = cookie;
1549 	static const char *flags = NULL;
1550 	char *str = data->str;
1551 
1552 	if (flags == NULL)
1553 		flags = msg_string(MSG_mbr_flags);
1554 
1555 	if (mp->mbrp_flag & MBR_PFLAG_ACTIVE)
1556 		*str++ = flags[0];
1557 #ifdef BOOTSEL
1558 	if (parts->mbr.bootsec == mb->sector+mp->mbrp_start)
1559 		*str++ = flags[1];
1560 #endif
1561 	*str = 0;
1562 	return true;
1563 }
1564 
1565 static bool
1566 mbr_part_attr_str(const struct disk_partitions *arg, part_id id,
1567     char *str, size_t avail_space)
1568 {
1569 	struct part_get_str_data data;
1570 
1571 	if (avail_space < 3)
1572 		return false;
1573 
1574 	data.str = str;
1575 	data.avail_space = avail_space;
1576 	return mbr_part_apply(arg, id, mbr_get_part_attr_str, &data);
1577 }
1578 
1579 static bool
1580 mbr_info_to_partitition(const struct disk_part_info *info,
1581    struct mbr_partition *mp, uint sector,
1582    struct mbr_info_t *mb, size_t index, const char **err_msg)
1583 {
1584 	size_t pt = mbr_type_from_gen_desc(info->nat_type);
1585 	if (info->start + info->size > UINT_MAX
1586 	    || pt > __arraycount(mbr_gen_type_desc)) {
1587 		if (err_msg)
1588 			*err_msg = err_outofmem;
1589 		return false;
1590 	}
1591 	mp->mbrp_start = info->start - sector;
1592 	mp->mbrp_size = info->size;
1593 	mp->mbrp_type = pt;
1594 	if (info->flags & PTI_INSTALL_TARGET) {
1595 		mp->mbrp_flag |= MBR_PFLAG_ACTIVE;
1596 #ifdef BOOTSEL
1597 		strcpy(mb->mbrb.mbrbs_nametab[index], "NetBSD");
1598 #endif
1599 	}
1600 
1601 	return true;
1602 }
1603 
1604 static bool
1605 inside_ext_part(mbr_info_t *m, daddr_t start)
1606 {
1607 	size_t i;
1608 	struct mbr_partition *mp = NULL;
1609 
1610 	for (i = 0; i < MBR_PART_COUNT; i++) {
1611 		if (!MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
1612 			continue;
1613 		mp = &m->mbr.mbr_parts[i];
1614 		break;
1615 	}
1616 
1617 	if (mp == NULL) {
1618 		assert(false);
1619 		return false;
1620 	}
1621 
1622 	if (mp->mbrp_start > start)
1623 		return false;
1624 
1625 	return true;
1626 }
1627 
1628 static void
1629 adjust_ext_part(mbr_info_t *m, daddr_t start, daddr_t size)
1630 {
1631 	size_t i;
1632 	struct mbr_partition *mp = NULL;
1633 
1634 	for (i = 0; i < MBR_PART_COUNT; i++) {
1635 		if (!MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
1636 			continue;
1637 		mp = &m->mbr.mbr_parts[i];
1638 		break;
1639 	}
1640 
1641 	if (mp == NULL) {
1642 		assert(false);
1643 		return;
1644 	}
1645 
1646 	if (mp->mbrp_start + mp->mbrp_size >= start + size)
1647 		return;
1648 
1649 	daddr_t new_end = start + size;
1650 	mp->mbrp_size = new_end - mp->mbrp_start;
1651 }
1652 
1653 static bool
1654 ext_part_good(mbr_info_t *m, daddr_t ext_start, daddr_t ext_size)
1655 {
1656 	for (m = m->extended; m != NULL; m = m->extended) {
1657 		for (size_t i = 0; i < MBR_PART_COUNT; i++) {
1658 			if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
1659 				continue;
1660 
1661 			if (i > 0 &&
1662 			    MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
1663 				break;
1664 
1665 			daddr_t pstart = m->mbr.mbr_parts[i].mbrp_start +
1666 			    m->sector;
1667 			daddr_t pend = pstart + m->mbr.mbr_parts[i].mbrp_size;
1668 
1669 			if (pstart < ext_start || pend > ext_start+ext_size)
1670 				return false;
1671 		}
1672 	}
1673 
1674 	return true;
1675 }
1676 
1677 static bool
1678 mbr_set_part_info(struct disk_partitions *arg, part_id id,
1679     const struct disk_part_info *info, const char **err_msg)
1680 {
1681 	struct mbr_disk_partitions *parts =
1682 	    (struct mbr_disk_partitions*)arg;
1683 	struct disk_part_info data = *info;
1684 	part_id i, j, no, ext_ndx, t;
1685 	mbr_info_t *m = &parts->mbr, *me;
1686 	uint pt = mbr_type_from_gen_desc(info->nat_type);
1687 
1688 	if (MBR_IS_EXTENDED(pt)) {
1689 		/* check for duplicate ext part */
1690 		no = 0;
1691 		t = ext_ndx = MBR_PART_COUNT;
1692 		for (i = 0; i < MBR_PART_COUNT; i++) {
1693 			if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
1694 				continue;
1695 			if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
1696 				ext_ndx = i;
1697 			if (no == id)
1698 				t = i;
1699 			no++;
1700 		}
1701 		if (ext_ndx < MBR_PART_COUNT && t != ext_ndx) {
1702 			if (err_msg)
1703 				*err_msg =
1704 				    msg_string(MSG_Only_one_extended_ptn);
1705 			return false;
1706 		}
1707 		/* this partition becomes an extended one, apply alignment */
1708 		data.start = max(roundup(data.start, parts->ext_ptn_alignment),
1709 			parts->ext_ptn_alignment);
1710 	}
1711 
1712 	no = 0;
1713 	for (i = 0; i < MBR_PART_COUNT; i++) {
1714 		if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
1715 			continue;
1716 
1717 		if (no == id)
1718 			goto found;
1719 		no++;
1720 
1721 		if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type)) {
1722 			for (me = m->extended; me != NULL; me = me->extended) {
1723 				for (j = 0; j < MBR_PART_COUNT; j++) {
1724 					if (me->mbr.mbr_parts[j].mbrp_type ==
1725 					    MBR_PTYPE_UNUSED)
1726 						continue;
1727 					if (j > 0 && MBR_IS_EXTENDED(
1728 					    me->mbr.mbr_parts[j].mbrp_type))
1729 						break;
1730 					if (no == id) {
1731 						i = j;
1732 						m = me;
1733 						goto found;
1734 					}
1735 					no++;
1736 				}
1737 			}
1738 		}
1739 	}
1740 
1741 	if (err_msg)
1742 		*err_msg = INTERNAL_ERROR;
1743 	return false;
1744 
1745 found:
1746 	/*
1747 	 * We assume that m is the mbr we want to update and
1748 	 * i is the local partition index into it.
1749 	 */
1750 	if (m == &parts->mbr) {
1751 		if (MBR_IS_EXTENDED(
1752 		    m->mbr.mbr_parts[i].mbrp_type) &&
1753 		    !ext_part_good(&parts->mbr, data.start, data.size)) {
1754 			if (err_msg)
1755 				*err_msg =
1756 				    MSG_mbr_ext_nofit;
1757 			return false;
1758 		}
1759 	} else if (!inside_ext_part(&parts->mbr, data.start)) {
1760 		if (err_msg)
1761 			*err_msg = msg_string(MSG_mbr_inside_ext);
1762 		return false;
1763 	}
1764 	uint start = data.start, size = data.size;
1765 	uint oldstart = m->mbr.mbr_parts[i].mbrp_start + m->sector;
1766 	if (parts->ptn_0_offset > 0 &&
1767 	    start < parts->ptn_0_offset)
1768 		start = parts->ptn_0_offset;
1769 	if (find_mbr_space(m, &start, &size, start, parts->dp.disk_size,
1770 	    oldstart, false) < 0) {
1771 		if (err_msg != NULL)
1772 			*err_msg = INTERNAL_ERROR;
1773 		return false;
1774 	}
1775 	data.start = start;
1776 	if (size < data.size)
1777 		data.size = size;
1778 	uint old_start = m->mbr.mbr_parts[i].mbrp_start;
1779 	if (!mbr_info_to_partitition(&data,
1780 	   &m->mbr.mbr_parts[i], m->sector, m, i, err_msg))
1781 		return false;
1782 	if (data.flags & PTI_INSTALL_TARGET)
1783 		parts->target = start;
1784 	else if (old_start == parts->target)
1785 		parts->target = -1;
1786 	if (data.last_mounted && m->last_mounted[i] &&
1787 	    data.last_mounted != m->last_mounted[i]) {
1788 		free(__UNCONST(m->last_mounted[i]));
1789 		m->last_mounted[i] = strdup(data.last_mounted);
1790 	}
1791 	if (data.fs_type != 0)
1792 		m->fs_type[i] = data.fs_type;
1793 	if (data.fs_sub_type != 0)
1794 		m->fs_sub_type[i] = data.fs_sub_type;
1795 
1796 	if (m == &parts->mbr) {
1797 		if (m->mbr.mbr_parts[i].mbrp_start !=
1798 		    old_start)
1799 			mbr_sort_main_mbr(&m->mbr);
1800 	} else {
1801 		adjust_ext_part(&parts->mbr,
1802 		    data.start, data.size);
1803 	}
1804 	mbr_calc_free_space(parts);
1805 	return true;
1806 }
1807 
1808 static bool
1809 mbr_find_netbsd(const struct mbr_info_t *m, uint start,
1810     struct disk_part_info *info)
1811 {
1812 	size_t i;
1813 	bool prim = true;
1814 
1815 	do {
1816 		for (i = 0; i < MBR_PART_COUNT; i++) {
1817 			if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
1818 				continue;
1819 
1820 			if (!prim && i > 0 &&
1821 			    MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
1822 				break;
1823 
1824 			const struct mbr_partition *mp = &m->mbr.mbr_parts[i];
1825 			if (mp->mbrp_type != MBR_PTYPE_NETBSD)
1826 				continue;
1827 
1828 			mbr_partition_to_info(mp, m->sector, info);
1829 			if (m->last_mounted[i] && *m->last_mounted[i] != 0)
1830 					info->last_mounted =
1831 					    m->last_mounted[i];
1832 			info->fs_type = m->fs_type[i];
1833 			info->fs_sub_type = m->fs_sub_type[i];
1834 			if (start > 0 && start != info->start)
1835 				continue;
1836 			return true;
1837 		}
1838 		prim = false;
1839 	} while ((m = m->extended));
1840 
1841 	return false;
1842 }
1843 
1844 static struct disk_partitions *
1845 mbr_read_disklabel(struct disk_partitions *arg, daddr_t start, bool force_empty)
1846 {
1847 	struct mbr_disk_partitions *myparts =
1848 	    (struct mbr_disk_partitions*)arg;
1849 	struct disk_part_info part;
1850 	struct disk_part_free_space space;
1851 
1852 	if (force_empty && myparts->dlabel)
1853 		myparts->dlabel->pscheme->delete_all_partitions(
1854 		    myparts->dlabel);
1855 
1856 	if (myparts->dlabel == NULL) {
1857 		/*
1858 		 * Find the NetBSD MBR partition
1859 		 */
1860 		if (!mbr_find_netbsd(&myparts->mbr, start, &part)) {
1861 			if (!force_empty)
1862 				return NULL;
1863 
1864 			/* add a "whole disk" NetBSD partition */
1865 			memset(&part, 0, sizeof part);
1866 			part.start = min(myparts->ptn_0_offset,start);
1867 			if (!mbr_get_free_spaces(arg, &space, 1,
1868 			    part.start, myparts->ptn_alignment, -1, -1))
1869 				return NULL;
1870 			part.start = space.start;
1871 			part.size = space.size;
1872 			part.nat_type = &mbr_gen_type_desc[MBR_PTYPE_NETBSD].gen;
1873 			mbr_add_part(arg, &part, NULL);
1874 			if (!mbr_find_netbsd(&myparts->mbr, start, &part))
1875 				return NULL;
1876 		}
1877 
1878 		if (!force_empty) {
1879 			myparts->dlabel = disklabel_parts.read_from_disk(
1880 			    myparts->dp.disk, part.start, part.size,
1881 			    myparts->dp.bytes_per_sector, &disklabel_parts);
1882 			if (myparts->dlabel != NULL)
1883 				myparts->dlabel->parent = &myparts->dp;
1884 		}
1885 
1886 		if (myparts->dlabel == NULL && part.size > 0) {
1887 			/* we just created the outer partitions? */
1888 			myparts->dlabel =
1889 			    disklabel_parts.create_new_for_disk(
1890 			    myparts->dp.disk, part.start, part.size,
1891 			    false, &myparts->dp);
1892 		}
1893 
1894 		if (myparts->dlabel != NULL)
1895 			myparts->dlabel->pscheme->change_disk_geom(
1896 			    myparts->dlabel, myparts->geo_cyl,
1897 			    myparts->geo_head, myparts->geo_sec);
1898 	}
1899 	return myparts->dlabel;
1900 }
1901 
1902 static int
1903 get_mapping(struct mbr_partition *parts, int i,
1904 	    int *cylinder, int *head, int *sector, daddr_t *absolute)
1905 {
1906 	struct mbr_partition *apart = &parts[i / 2];
1907 
1908 	if (apart->mbrp_type == MBR_PTYPE_UNUSED)
1909 		return -1;
1910 	if (i % 2 == 0) {
1911 		*cylinder = MBR_PCYL(apart->mbrp_scyl, apart->mbrp_ssect);
1912 		*head = apart->mbrp_shd;
1913 		*sector = MBR_PSECT(apart->mbrp_ssect) - 1;
1914 		*absolute = le32toh(apart->mbrp_start);
1915 	} else {
1916 		*cylinder = MBR_PCYL(apart->mbrp_ecyl, apart->mbrp_esect);
1917 		*head = apart->mbrp_ehd;
1918 		*sector = MBR_PSECT(apart->mbrp_esect) - 1;
1919 		*absolute = le32toh(apart->mbrp_start)
1920 			+ le32toh(apart->mbrp_size) - 1;
1921 	}
1922 	/* Sanity check the data against max values */
1923 	if ((((*cylinder * MAXHEAD) + *head) * (uint32_t)MAXSECTOR + *sector) < *absolute)
1924 		/* cannot be a CHS mapping */
1925 		return -1;
1926 
1927 	return 0;
1928 }
1929 
1930 static bool
1931 mbr_delete_all(struct disk_partitions *arg)
1932 {
1933 	struct mbr_disk_partitions *myparts = (struct mbr_disk_partitions*)arg;
1934 	struct mbr_sector *mbrs = &myparts->mbr.mbr;
1935 	struct mbr_info_t *mbri = &myparts->mbr;
1936 	mbr_info_t *ext;
1937 	struct mbr_partition *part;
1938 
1939 	part = &mbrs->mbr_parts[0];
1940 	/* Set the partition information for full disk usage. */
1941 	while ((ext = mbri->extended)) {
1942 		mbri->extended = ext->extended;
1943 		free_mbr_info(ext);
1944 	}
1945 	memset(part, 0, MBR_PART_COUNT * sizeof *part);
1946 #ifdef BOOTSEL
1947 	memset(&mbri->mbrb, 0, sizeof mbri->mbrb);
1948 #endif
1949 
1950 	/*
1951 	 * We may have changed alignment settings due to partitions
1952 	 * ending on an MB boundary - undo that, now that the partitions
1953 	 * are gone.
1954 	 */
1955 	mbr_change_disk_geom(arg, myparts->geo_cyl, myparts->geo_head,
1956 	    myparts->geo_sec);
1957 
1958 	return true;
1959 }
1960 
1961 /*
1962  * helper function to fix up mbrp_start and mbrp_size for the
1963  * extended MBRs "partition b" entries after addition/deletion
1964  * of some partition.
1965  */
1966 static void
1967 mbr_fixup_ext_chain(mbr_info_t *primary, uint ext_start, uint ext_end)
1968 {
1969 	for (mbr_info_t *m = primary->extended; m != NULL; m = m->extended) {
1970 		if (m->extended == NULL) {
1971 			m->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_UNUSED;
1972 			m->mbr.mbr_parts[1].mbrp_start = 0;
1973 			m->mbr.mbr_parts[1].mbrp_size = 0;
1974 		} else {
1975 			uint n_end, n_start = m->extended->sector;
1976 			if (m->extended->extended)
1977 				n_end = m->extended->extended->sector;
1978 			else
1979 				n_end = ext_end;
1980 			m->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
1981 			m->mbr.mbr_parts[1].mbrp_start = n_start - ext_start;
1982 			m->mbr.mbr_parts[1].mbrp_size = n_end - n_start;
1983 		}
1984 	}
1985 }
1986 
1987 struct delete_part_args {
1988 	struct mbr_disk_partitions *parts;
1989 	daddr_t start, size;
1990 	const char **err_msg;
1991 };
1992 
1993 static bool
1994 mbr_do_delete_part(const struct disk_partitions *arg, part_id id,
1995     const mbr_info_t *mb, int i, bool primary,
1996     const struct mbr_partition *mp, void *cookie)
1997 {
1998 	struct delete_part_args *marg = cookie;
1999 	bool is_ext_part = MBR_IS_EXTENDED(mp->mbrp_type);
2000 
2001 	/* can not delete non-empty extended partitions */
2002 	if (MBR_IS_EXTENDED(mp->mbrp_type)
2003 	    && marg->parts->mbr.extended != NULL) {
2004 		if (marg->err_msg)
2005 			*marg->err_msg = msg_string(MSG_mbr_ext_not_empty);
2006 		return false;
2007 	}
2008 
2009 	/* return position/size to caller */
2010 	marg->start = mb->sector + mp->mbrp_start;
2011 	marg->size = mp->mbrp_size;
2012 
2013 	if (primary) {
2014 		/* if deleting the primary extended partition, just kill it */
2015 		struct mbr_partition *md = &marg->parts->mbr.mbr.mbr_parts[i];
2016 		md->mbrp_size = 0;
2017 		md->mbrp_start = 0;
2018 		md->mbrp_type = MBR_PTYPE_UNUSED;
2019 		if (marg->parts->mbr.last_mounted[i]) {
2020 			free(__UNCONST(marg->parts->mbr.last_mounted[i]));
2021 			marg->parts->mbr.last_mounted[i] = NULL;
2022 		}
2023 		if (is_ext_part) {
2024 			for (mbr_info_t *m = marg->parts->mbr.extended;
2025 			    m != NULL; ) {
2026 				mbr_info_t *n = m->extended;
2027 				free_mbr_info(m);
2028 				m = n;
2029 			}
2030 			marg->parts->mbr.extended = NULL;
2031 		}
2032 	} else {
2033 		/* find the size of the primary extended partition */
2034 		uint ext_start = 0, ext_size = 0;
2035 		for (i = 0; i < MBR_PART_COUNT; i++) {
2036 			if (!MBR_IS_EXTENDED(marg->parts->mbr.mbr.mbr_parts[i]
2037 			    .mbrp_type))
2038 				continue;
2039 			ext_start = marg->parts->mbr.mbr.mbr_parts[i]
2040 			    .mbrp_start;
2041 			ext_size = marg->parts->mbr.mbr.mbr_parts[i]
2042 			    .mbrp_size;
2043 			break;
2044 		}
2045 
2046 		/*
2047 		 * If we are in an extended partition chain, unlink this MBR,
2048 		 * unless it is the very first one at the start of the extended
2049 		 * partition (we would have no previos ext mbr to fix up
2050 		 * the chain in that case)
2051 		 */
2052 		if (marg->parts->mbr.extended == mb) {
2053 			struct mbr_partition *part =
2054 			    &marg->parts->mbr.extended->mbr.mbr_parts[0];
2055 			part->mbrp_type = MBR_PTYPE_UNUSED;
2056 			part->mbrp_start = 0;
2057 			part->mbrp_size = 0;
2058 		} else {
2059 			mbr_info_t *p, *last;
2060 			for (last = NULL, p = &marg->parts->mbr; p != NULL;
2061 			    last = p, p = p->extended)
2062 				if (p == mb)
2063 					break;
2064 			if (last == NULL) {
2065 				if (marg->err_msg != NULL)
2066 					*marg->err_msg= INTERNAL_ERROR;
2067 				return false;
2068 			}
2069 			last->extended = p->extended;
2070 			free_mbr_info(p);
2071 			if (last == &marg->parts->mbr && last->extended &&
2072 			    last->extended->extended == NULL &&
2073 			    last->extended->mbr.mbr_parts[0].mbrp_type ==
2074 			    MBR_PTYPE_UNUSED) {
2075 				/*
2076 				 * we deleted the last extended sector,
2077 				 * remove the whole chain
2078 				 */
2079 				free_mbr_info(last->extended);
2080 				last->extended = NULL;
2081 			}
2082 		}
2083 		mbr_fixup_ext_chain(&marg->parts->mbr, ext_start,
2084 		    ext_start+ext_size);
2085 	}
2086 	mbr_calc_free_space(marg->parts);
2087 	return true;
2088 }
2089 
2090 static bool
2091 mbr_delete_part(struct disk_partitions *arg, part_id pno, const char **err_msg)
2092 {
2093 	struct mbr_disk_partitions *parts =
2094 	    (struct mbr_disk_partitions*)arg;
2095 	struct delete_part_args data = { .parts = parts, .err_msg = err_msg };
2096 
2097 	if (!mbr_part_apply(arg, pno, mbr_do_delete_part, &data)) {
2098 		if (err_msg)
2099 			*err_msg = INTERNAL_ERROR;
2100 		return false;
2101 	}
2102 
2103 	if (parts->target == data.start)
2104 		parts->target = ~0U;
2105 
2106 	if (parts->dlabel) {
2107 		/*
2108 		 * If we change the mbr partitioning, the we must
2109 		 * remove any references in the netbsd disklabel
2110 		 * to the part we changed.
2111 		 */
2112 		parts->dlabel->pscheme->delete_partitions_in_range(
2113 		    parts->dlabel, data.start, data.size);
2114 	}
2115 
2116 	if (err_msg)
2117 		*err_msg = NULL;
2118 
2119 	dump_mbr(&parts->mbr, "after delete");
2120 	return true;
2121 }
2122 
2123 static struct mbr_partition *
2124 mbr_ptr_from_start(mbr_info_t *m, daddr_t start)
2125 {
2126 	bool primary = true;
2127 
2128 	do {
2129 		for (uint i = 0; i < MBR_PART_COUNT; i++) {
2130 			if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
2131 				continue;
2132 			if (!primary &&
2133 			    MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
2134 				break;
2135 
2136 			daddr_t pstart = m->sector +
2137 			    m->mbr.mbr_parts[i].mbrp_start;
2138 			if (pstart == start)
2139 				return &m->mbr.mbr_parts[i];
2140 
2141 		}
2142 		primary = false;
2143 	} while ((m = m->extended));
2144 
2145 	return NULL;
2146 }
2147 
2148 static uint8_t
2149 mbr_type_from_start(const mbr_info_t *m, daddr_t start)
2150 {
2151 	bool primary = true;
2152 
2153 	do {
2154 		for (uint i = 0; i < MBR_PART_COUNT; i++) {
2155 			if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
2156 				continue;
2157 			if (!primary &&
2158 			    MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
2159 				break;
2160 
2161 			daddr_t pstart = m->sector +
2162 			    m->mbr.mbr_parts[i].mbrp_start;
2163 			if (pstart == start)
2164 				return m->mbr.mbr_parts[i].mbrp_type;
2165 
2166 		}
2167 		primary = false;
2168 	} while ((m = m->extended));
2169 
2170 	return MBR_PTYPE_UNUSED;
2171 }
2172 
2173 static part_id
2174 mbr_add_part(struct disk_partitions *arg,
2175     const struct disk_part_info *info, const char **errmsg)
2176 {
2177 	struct mbr_disk_partitions *parts =
2178 	    (struct mbr_disk_partitions*)arg;
2179 	part_id i, j, no, free_primary = UINT_MAX;
2180 	mbr_info_t *m = &parts->mbr, *me, *last, *t;
2181 	daddr_t ext_start = 0, ext_size = 0;
2182 	uint start, size;
2183 	struct disk_part_info data = *info;
2184 	struct mbr_partition *newp;
2185 
2186 	if (errmsg != NULL)
2187 		*errmsg = NULL;
2188 
2189 	assert(info->nat_type != NULL);
2190 	if (info->nat_type == NULL) {
2191 		if (errmsg != NULL)
2192 			*errmsg = INTERNAL_ERROR;
2193 		return NO_PART;
2194 	}
2195 	if (mbr_type_from_gen_desc(info->nat_type) == MBR_PTYPE_UNUSED) {
2196 		if (errmsg != NULL)
2197 			*errmsg = INTERNAL_ERROR;
2198 		return NO_PART;
2199 	}
2200 
2201 	/* do we have free primary slots and/or an extended partition? */
2202 	for (i = 0; i < MBR_PART_COUNT; i++) {
2203 		if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED
2204 		    && free_primary > MBR_PART_COUNT)
2205 			free_primary = i;
2206 		if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type)) {
2207 			ext_start = m->mbr.mbr_parts[i].mbrp_start+m->sector;
2208 			ext_size = m->mbr.mbr_parts[i].mbrp_size;
2209 			continue;
2210 		}
2211 		if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED
2212 		    && m->mbr.mbr_parts[i].mbrp_size == 0)
2213 			continue;
2214 	}
2215 	if (ext_start > 0 && ext_size > 0 &&
2216 	    MBR_IS_EXTENDED(mbr_type_from_gen_desc(info->nat_type))) {
2217 		/*
2218 		 * Do not allow a second extended partition
2219 		 */
2220 		if (errmsg)
2221 			*errmsg = MSG_Only_one_extended_ptn;
2222 		return NO_PART;
2223 	}
2224 
2225 	/* should this go into the extended partition? */
2226 	if (ext_size > 0 && info->start >= ext_start
2227 	    && info->start < ext_start + ext_size) {
2228 
2229 		/* must fit into the extended partition */
2230 		if (info->start + info->size > ext_start + ext_size) {
2231 			if (errmsg != NULL)
2232 				*errmsg = MSG_mbr_ext_nofit;
2233 			return NO_PART;
2234 		}
2235 
2236 		/* walk the chain untill we find a proper insert position */
2237 		daddr_t e_end, e_start;
2238 		for (last = m, m = m->extended; m != NULL;
2239 		    last = m, m = m->extended) {
2240 			e_start = m->mbr.mbr_parts[1].mbrp_start
2241 			    + ext_start;
2242 			e_end = e_start + m->mbr.mbr_parts[1].mbrp_size;
2243 			if (data.start <= e_start)
2244 				break;
2245 		}
2246 		if (m == NULL) {
2247 			/* add new tail record */
2248 			e_end = ext_start + ext_size;
2249 			/* new part needs to fit inside primary extended one */
2250 			if (data.start + data.size > e_end) {
2251 				if (errmsg)
2252 					*errmsg = MSG_No_free_space;
2253 				return NO_PART;
2254 			}
2255 		} else if (data.start + data.size > e_start) {
2256 			/* new part needs to fit before next extended */
2257 			if (errmsg)
2258 				*errmsg = MSG_No_free_space;
2259 			return NO_PART;
2260 		}
2261 		/*
2262 		 * now last points to previous mbr (maybe primary), m
2263 		 * points to the one that should take the new partition
2264 		 * or we have to insert a new mbr between the two, or
2265 		 * m needs to be split and we go into the one after it.
2266 		 */
2267 		if (m && m->mbr.mbr_parts[0].mbrp_type == MBR_PTYPE_UNUSED) {
2268 			/* empty slot, we can just use it */
2269 			newp = &m->mbr.mbr_parts[0];
2270 			mbr_info_to_partitition(&data, &m->mbr.mbr_parts[0],
2271 			    m->sector, m, 0, errmsg);
2272 			if (data.last_mounted && m->last_mounted[0] &&
2273 			    data.last_mounted != m->last_mounted[0]) {
2274 				free(__UNCONST(m->last_mounted[0]));
2275 				m->last_mounted[0] = strdup(data.last_mounted);
2276 			}
2277 		} else {
2278 			mbr_info_t *new_mbr;
2279 			if (m == NULL)
2280 				m = last;
2281 			daddr_t p_start = m->mbr.mbr_parts[0].mbrp_start
2282 			    + m->sector;
2283 			daddr_t p_end = p_start
2284 			    + m->mbr.mbr_parts[0].mbrp_size;
2285 			bool before;
2286 			if (m == last || data.start > p_end)
2287 				before = false;
2288 			else if (data.start + data.size < p_start)
2289 				before = true;
2290 			else {
2291 				if (errmsg)
2292 					*errmsg = MSG_No_free_space;
2293 				return NO_PART;
2294 			}
2295 			new_mbr = calloc(1, sizeof *new_mbr);
2296 			if (!new_mbr) {
2297 				if (errmsg)
2298 					*errmsg = err_outofmem;
2299 				return NO_PART;
2300 			}
2301 			new_mbr->mbr.mbr_magic = htole16(MBR_MAGIC);
2302 			new_mbr->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
2303 			if (before) {
2304 				/*
2305 				 * This is a hypthetical case where
2306 				 * an extended MBR uses an unusual high
2307 				 * offset (m->sector to parts[0].mbrp_start)
2308 				 * and we want to go into that space.
2309 				 * Should not happen in the real world (tm)
2310 				 * and is untested....
2311 				 */
2312 
2313 				/* make sure the aligned new mbr fits */
2314 				uint mbrsec = rounddown(p_start,
2315 				    parts->ext_ptn_alignment);
2316 				if (mbrsec <= data.start + data.size)
2317 					data.size = mbrsec-1-data.start;
2318 
2319 				/* now the new partition data is ready,
2320 				 * write out to old position */
2321 				new_mbr->sector = m->sector;
2322 				newp = &new_mbr->mbr.mbr_parts[0];
2323 				mbr_info_to_partitition(&data,
2324 				    &new_mbr->mbr.mbr_parts[0],
2325 				    new_mbr->sector, new_mbr, 0, errmsg);
2326 				if (data.last_mounted && m->last_mounted[0] &&
2327 				    data.last_mounted != m->last_mounted[0]) {
2328 					free(__UNCONST(m->last_mounted[0]));
2329 					m->last_mounted[0] =
2330 					    strdup(data.last_mounted);
2331 				}
2332 				new_mbr->extended = m;
2333 			} else {
2334 				new_mbr->sector = max(roundup(data.start,
2335 				    parts->ext_ptn_alignment),
2336 				    parts->ext_ptn_alignment);
2337 				uint off = new_mbr->sector - data.start;
2338 				data.start += parts->ptn_0_offset+off;
2339 				if (data.start + data.size > e_end)
2340 					data.size = e_end - data.start;
2341 				newp = &new_mbr->mbr.mbr_parts[0];
2342 				mbr_info_to_partitition(&data,
2343 				    &new_mbr->mbr.mbr_parts[0],
2344 				    new_mbr->sector, new_mbr, 0, errmsg);
2345 				if (data.last_mounted && m->last_mounted[0] &&
2346 				    data.last_mounted != m->last_mounted[0]) {
2347 					free(__UNCONST(m->last_mounted[0]));
2348 					m->last_mounted[0] =
2349 					    strdup(data.last_mounted);
2350 				}
2351 				/*
2352 				 * Special case: if we are creating the
2353 				 * first extended mbr, but do not start
2354 				 * at the beginning of the primary
2355 				 * extended partition, we need to insert
2356 				 * another extended mbr at the start.
2357 				 */
2358 				if (m == &parts->mbr && m->extended == NULL
2359 				    && new_mbr->sector > ext_start) {
2360 					t = calloc(1, sizeof *new_mbr);
2361 					if (!t) {
2362 						free_mbr_info(new_mbr);
2363 						if (errmsg)
2364 							*errmsg = err_outofmem;
2365 						return NO_PART;
2366 					}
2367 					t->sector = ext_start;
2368 					t->mbr.mbr_magic = htole16(MBR_MAGIC);
2369 					t->mbr.mbr_parts[1].mbrp_type =
2370 					    MBR_PTYPE_EXT;
2371 					m->extended = t;
2372 					m = t;
2373 				}
2374 				new_mbr->extended = m->extended;
2375 				m->extended = new_mbr;
2376 			}
2377 		}
2378 		mbr_fixup_ext_chain(&parts->mbr, ext_start, ext_start+ext_size);
2379 		dump_mbr(&parts->mbr, "after adding in extended");
2380 		goto find_rval;
2381 	}
2382 
2383 	/* this one is for the primary boot block */
2384 	if (free_primary > MBR_PART_COUNT) {
2385 		if (errmsg != NULL)
2386 			*errmsg = ext_size > 0 ?
2387 				MSG_mbr_no_free_primary_have_ext
2388 				: MSG_mbr_no_free_primary_no_ext;
2389 		return NO_PART;
2390 	}
2391 
2392 	start = max(info->start, parts->ptn_0_offset);
2393 	size = info->size;
2394 	if (find_mbr_space(m, &start, &size, start, parts->dp.disk_size,
2395 	    start, true) < 0 || size < info->size) {
2396 		if (errmsg != NULL)
2397 			*errmsg = MSG_No_free_space;
2398 		return NO_PART;
2399 	}
2400 	data.start = start;
2401 	if (MBR_IS_EXTENDED(mbr_type_from_gen_desc(info->nat_type))) {
2402 		data.start = max(roundup(data.start, parts->ext_ptn_alignment),
2403 		   parts->ext_ptn_alignment);
2404 	}
2405 	if (data.start + data.size > start + size)
2406 		data.size = start + size - data.start;
2407 	mbr_info_to_partitition(&data, &m->mbr.mbr_parts[free_primary],
2408 	     m->sector, m, free_primary, errmsg);
2409 	if (data.last_mounted && m->last_mounted[free_primary] &&
2410 	    data.last_mounted != m->last_mounted[free_primary]) {
2411 		free(__UNCONST(m->last_mounted[free_primary]));
2412 		m->last_mounted[free_primary] = strdup(data.last_mounted);
2413 	}
2414 	start = m->mbr.mbr_parts[free_primary].mbrp_start;
2415 	mbr_sort_main_mbr(&m->mbr);
2416 
2417 	/* find the partition again after sorting */
2418 	newp = NULL;
2419 	for (i = 0; i < MBR_PART_COUNT; i++) {
2420 		if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
2421 			continue;
2422 		if (m->mbr.mbr_parts[i].mbrp_start != start)
2423 			continue;
2424 		newp = &m->mbr.mbr_parts[i];
2425 		break;
2426 	}
2427 
2428 	dump_mbr(&parts->mbr, "after adding in primary");
2429 
2430 find_rval:
2431 	mbr_calc_free_space(parts);
2432 	if (newp == NULL)
2433 		return 0;
2434 
2435 	/*
2436 	 * Now newp points to the modified partition entry but we do not know
2437 	 * a good part_id for it.
2438 	 * Iterate from start and find it.
2439 	 */
2440 	no = 0;
2441 	for (i = 0; i < MBR_PART_COUNT; i++) {
2442 		if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
2443 			continue;
2444 
2445 		if (newp == &m->mbr.mbr_parts[i])
2446 			return no;
2447 		no++;
2448 
2449 		if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type)) {
2450 			for (me = m->extended; me != NULL; me = me->extended) {
2451 				for (j = 0; j < MBR_PART_COUNT; j++) {
2452 					if (me->mbr.mbr_parts[j].mbrp_type ==
2453 					    MBR_PTYPE_UNUSED)
2454 						continue;
2455 					if (j > 0 && MBR_IS_EXTENDED(
2456 					    me->mbr.mbr_parts[j].mbrp_type))
2457 						break;
2458 					if (newp == &me->mbr.mbr_parts[j])
2459 						return no;
2460 					no++;
2461 				}
2462 			}
2463 		}
2464 	}
2465 	return 0;
2466 }
2467 
2468 static int
2469 mbr_guess_geom(struct disk_partitions *arg, int *cyl, int *head, int *sec)
2470 {
2471 	struct mbr_disk_partitions *myparts = (struct mbr_disk_partitions*)arg;
2472 	struct mbr_sector *mbrs = &myparts->mbr.mbr;
2473 	struct mbr_partition *parts = &mbrs->mbr_parts[0];
2474 	int xcylinders, xheads, i, j;
2475 	daddr_t xsectors, xsize;
2476 	int c1, h1, s1, c2, h2, s2;
2477 	daddr_t a1, a2;
2478 	uint64_t num, denom;
2479 
2480 	xheads = -1;
2481 
2482 	/* Try to deduce the number of heads from two different mappings. */
2483 	for (i = 0; i < MBR_PART_COUNT * 2 - 1; i++) {
2484 		if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
2485 			continue;
2486 		a1 -= s1;
2487 		for (j = i + 1; j < MBR_PART_COUNT * 2; j++) {
2488 			if (get_mapping(parts, j, &c2, &h2, &s2, &a2) < 0)
2489 				continue;
2490 			a2 -= s2;
2491 			num = (uint64_t)h1 * a2 - (quad_t)h2 * a1;
2492 			denom = (uint64_t)c2 * a1 - (quad_t)c1 * a2;
2493 			if (num != 0 && denom != 0 && num % denom == 0) {
2494 				xheads = (int)(num / denom);
2495 				xsectors = a1 / (c1 * xheads + h1);
2496 				break;
2497 			}
2498 		}
2499 		if (xheads != -1)
2500 			break;
2501 	}
2502 
2503 	if (xheads == -1)
2504 		return -1;
2505 
2506 	/*
2507 	 * Estimate the number of cylinders.
2508 	 * XXX relies on get_disks having been called.
2509 	 */
2510 	xsize = min(pm->dlsize, mbr_parts.size_limit);
2511 	xcylinders = xsize / xheads / xsectors;
2512 	if (xsize != xcylinders * xheads * xsectors)
2513 		xcylinders++;
2514 
2515 	/*
2516 	 * Now verify consistency with each of the partition table entries.
2517 	 * Be willing to shove cylinders up a little bit to make things work,
2518 	 * but translation mismatches are fatal.
2519 	 */
2520 	for (i = 0; i < MBR_PART_COUNT * 2; i++) {
2521 		if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
2522 			continue;
2523 		if (c1 >= MAXCYL - 1)
2524 			/* Ignore anything that is near the CHS limit */
2525 			continue;
2526 		if (xsectors * (c1 * xheads + h1) + s1 != a1)
2527 			return -1;
2528 	}
2529 
2530 	/*
2531 	 * Everything checks out.  Reset the geometry to use for further
2532 	 * calculations.
2533 	 */
2534 	*cyl = MIN(xcylinders, MAXCYL);
2535 	*head = xheads;
2536 	*sec = xsectors;
2537 	return 0;
2538 }
2539 
2540 static size_t
2541 mbr_get_cylinder(const struct disk_partitions *arg)
2542 {
2543 	const struct mbr_disk_partitions *parts =
2544 	    (const struct mbr_disk_partitions*)arg;
2545 
2546 	return parts->geo_cyl;
2547 }
2548 
2549 static daddr_t
2550 mbr_max_part_size(const struct disk_partitions *arg, daddr_t fp_start)
2551 {
2552 	const struct mbr_disk_partitions *parts =
2553 	    (const struct mbr_disk_partitions*)arg;
2554 	uint start = fp_start, size = 0;
2555 	uint8_t pt;
2556 
2557 	start = fp_start;
2558 	pt = mbr_type_from_start(&parts->mbr, start);
2559 	if (find_mbr_space(&parts->mbr, &start, &size, start,
2560 	    parts->dp.disk_size, start, MBR_IS_EXTENDED(pt)) < 0)
2561 		return 0;
2562 
2563 	return size;
2564 }
2565 
2566 static size_t
2567 mbr_get_free_spaces(const struct disk_partitions *arg,
2568     struct disk_part_free_space *result, size_t max_num_result,
2569     daddr_t min_size, daddr_t align, daddr_t lower_bound, daddr_t ignore)
2570 {
2571 	const struct mbr_disk_partitions *parts =
2572 	    (const struct mbr_disk_partitions*)arg;
2573 	uint start = 0, size = 0, from, next;
2574 	size_t spaces = 0;
2575 
2576 	if (min_size < 1)
2577 		min_size = 1;
2578 	from = parts->ptn_0_offset;
2579 	if (lower_bound > from)
2580 		from = lower_bound;
2581 	for ( ; from < parts->dp.disk_size && spaces < max_num_result; ) {
2582 		if (find_mbr_space(&parts->mbr, &start, &size, from,
2583 		    parts->dp.disk_size, ignore > 0 ? (uint)ignore : UINT_MAX,
2584 		    false) < 0)
2585 			break;
2586 		next = start + size + 1;
2587 		if (align > 0) {
2588 			uint nv = max(roundup(start, align), align);
2589 			uint off = nv - start;
2590 			start = nv;
2591 			if (size > off)
2592 				size -= off;
2593 			else
2594 				size = 0;
2595 		}
2596 		if (size > min_size) {
2597 			result[spaces].start = start;
2598 			result[spaces].size = size;
2599 			spaces++;
2600 		}
2601 		if ((daddr_t)start + (daddr_t)size + 1 >= mbr_parts.size_limit)
2602 			break;
2603 		from = next;
2604 	}
2605 
2606 	return spaces;
2607 }
2608 
2609 static bool
2610 mbr_can_add_partition(const struct disk_partitions *arg)
2611 {
2612 	const struct mbr_disk_partitions *myparts =
2613 	    (const struct mbr_disk_partitions*)arg;
2614 	struct disk_part_free_space space;
2615 	bool free_primary, have_extended;
2616 
2617 	if (arg->free_space < myparts->ptn_alignment)
2618 		return false;
2619 
2620 	if (mbr_get_free_spaces(arg, &space, 1, myparts->ptn_alignment,
2621 	    myparts->ptn_alignment, 0, -1) < 1)
2622 		return false;
2623 
2624 	for (int i = 0; i < MBR_PART_COUNT; i++) {
2625 		uint8_t t = myparts->mbr.mbr.mbr_parts[i].mbrp_type;
2626 
2627 		if (t == MBR_PTYPE_UNUSED &&
2628 		     myparts->mbr.mbr.mbr_parts[i].mbrp_size == 0)
2629 			free_primary = true;
2630 
2631 		if (MBR_IS_EXTENDED(t))
2632 			have_extended = true;
2633 	}
2634 
2635 	if (have_extended)
2636 		return true;
2637 
2638 	return free_primary;
2639 }
2640 
2641 static void
2642 mbr_free_wedge(int *fd, const char *disk, const char *wedge)
2643 {
2644 	struct dkwedge_info dkw;
2645 	char diskpath[MAXPATHLEN];
2646 
2647 	if (*fd == -1)
2648 		*fd = opendisk(disk, O_RDWR, diskpath,
2649 		    sizeof(diskpath), 0);
2650 	if (*fd != -1) {
2651 		memset(&dkw, 0, sizeof(dkw));
2652 		strlcpy(dkw.dkw_devname, wedge,
2653 		    sizeof(dkw.dkw_devname));
2654 		ioctl(*fd, DIOCDWEDGE, &dkw);
2655 	}
2656 }
2657 
2658 static void
2659 mbr_free(struct disk_partitions *arg)
2660 {
2661 	struct mbr_disk_partitions *parts = (struct mbr_disk_partitions*)arg;
2662 	mbr_info_t *m;
2663 	int i, fd;
2664 
2665 	assert(parts != NULL);
2666 
2667 	fd = -1;
2668 	m = &parts->mbr;
2669 	do {
2670 		for (i = 0; i < MBR_PART_COUNT; i++) {
2671 			if (m->wedge[i][0] != 0)
2672 				mbr_free_wedge(&fd, arg->disk, m->wedge[i]);
2673 		}
2674 	} while ((m = m->extended));
2675 
2676 	if (fd != -1)
2677 		close(fd);
2678 
2679 	if (parts->dlabel)
2680 		parts->dlabel->pscheme->free(parts->dlabel);
2681 
2682 	free_mbr_info(parts->mbr.extended);
2683 	free_last_mounted(&parts->mbr);
2684 	free(__UNCONST(parts->dp.disk));
2685 	free(parts);
2686 }
2687 
2688 static void
2689 mbr_destroy_part_scheme(struct disk_partitions *arg)
2690 {
2691 	struct mbr_disk_partitions *parts = (struct mbr_disk_partitions*)arg;
2692 	char diskpath[MAXPATHLEN];
2693 	int fd;
2694 
2695 	if (parts->dlabel != NULL)
2696 		parts->dlabel->pscheme->destroy_part_scheme(parts->dlabel);
2697 	fd = opendisk(arg->disk, O_RDWR, diskpath, sizeof(diskpath), 0);
2698 	if (fd != -1) {
2699 		char *buf;
2700 
2701 		buf = calloc(arg->bytes_per_sector, 1);
2702 		if (buf != NULL) {
2703 			write(fd, buf, arg->bytes_per_sector);
2704 			free(buf);
2705 		}
2706 		close(fd);
2707 	}
2708 	mbr_free(arg);
2709 }
2710 
2711 static bool
2712 mbr_verify_for_update(struct disk_partitions *arg)
2713 {
2714 	struct mbr_disk_partitions *parts =
2715 	    (struct mbr_disk_partitions*)arg;
2716 
2717 	return md_mbr_update_check(arg, &parts->mbr);
2718 }
2719 
2720 static int
2721 mbr_verify(struct disk_partitions *arg, bool quiet)
2722 {
2723 	struct mbr_disk_partitions *parts =
2724 	    (struct mbr_disk_partitions*)arg;
2725 	mbr_info_t *m = &parts->mbr;
2726 	int i;
2727 	bool active_found = false;
2728 
2729 	for (i = 0; i < MBR_PART_COUNT; i++) {
2730 		if (m->mbr.mbr_parts[i].mbrp_flag & MBR_PFLAG_ACTIVE) {
2731 			active_found = true;
2732 			break;
2733 		}
2734 	}
2735 
2736 	if (!active_found && pm->ptstart > 0) {
2737 		struct mbr_partition *mp = mbr_ptr_from_start(m, pm->ptstart);
2738 
2739 		if (mp) {
2740 			if (!quiet)
2741 				msg_display(MSG_noactivepart);
2742 			if (quiet || ask_yesno(MSG_fixactivepart)) {
2743 				mp->mbrp_flag |= MBR_PFLAG_ACTIVE;
2744 				active_found = true;
2745 			}
2746 		}
2747 	}
2748 	if (!active_found && !quiet) {
2749 		msg_display(MSG_noactivepart);
2750 		i = ask_reedit(arg);
2751 		if (i <= 1)
2752 			return i;
2753 	}
2754 
2755 	for (i = 0; i < MBR_PART_COUNT; i++) {
2756 		if (m->mbr.mbr_parts[i].mbrp_type != MBR_PTYPE_NETBSD)
2757 			continue;
2758 		m->mbr.mbr_parts[i].mbrp_flag |= MBR_PFLAG_ACTIVE;
2759 		break;
2760 	}
2761 
2762 	return md_check_mbr(arg, &parts->mbr, quiet);
2763 }
2764 
2765 static bool
2766 mbr_guess_root(const struct disk_partitions *arg,
2767     daddr_t *start, daddr_t *size)
2768 {
2769 	const struct mbr_disk_partitions *parts =
2770 	    (const struct mbr_disk_partitions*)arg;
2771 	const mbr_info_t *m = &parts->mbr;
2772 	size_t i, num_found;
2773 	bool prim = true;
2774 	daddr_t pstart, psize;
2775 
2776 	num_found = 0;
2777 	do {
2778 		for (i = 0; i < MBR_PART_COUNT; i++) {
2779 			if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
2780 				continue;
2781 
2782 			if (!prim && i > 0 &&
2783 			    MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
2784 				break;
2785 
2786 			const struct mbr_partition *mp = &m->mbr.mbr_parts[i];
2787 			if (mp->mbrp_type != MBR_PTYPE_NETBSD)
2788 				continue;
2789 
2790 			if (num_found == 0) {
2791 				pstart = m->sector + mp->mbrp_start;
2792 				psize = mp->mbrp_size;
2793 			}
2794 			num_found++;
2795 
2796 			if (m->last_mounted[i] != NULL &&
2797 			    strcmp(m->last_mounted[i], "/") == 0) {
2798 				*start = pstart;
2799 				*size = psize;
2800 				return true;
2801 			}
2802 		}
2803 		prim = false;
2804 	} while ((m = m->extended));
2805 
2806 	if (num_found == 1) {
2807 		*start = pstart;
2808 		*size = psize;
2809 		return true;
2810 	}
2811 
2812 	return false;
2813 }
2814 
2815 struct part_attr_fmt_data {
2816 	char *str;
2817 	size_t avail_space, attr_no;
2818 	const struct mbr_disk_partitions *parts;
2819 	const struct disk_part_info *info;
2820 };
2821 
2822 struct part_attr_set_data {
2823 	size_t attr_no;
2824 	const struct mbr_disk_partitions *parts;
2825 	const char *str;
2826 	mbr_info_t *mbr;
2827 };
2828 
2829 static bool
2830 part_attr_fornat_str(const struct disk_partitions *arg, part_id id,
2831     const mbr_info_t *mb, int i, bool primary,
2832     const struct mbr_partition *mp, void *cookie)
2833 {
2834 	const struct mbr_disk_partitions *parts =
2835 	    (const struct mbr_disk_partitions*)arg;
2836 	struct part_attr_fmt_data *data = cookie;
2837 	const char *attrtype = parts->dp.pscheme
2838 	    ->custom_attributes[data->attr_no].label;
2839 
2840 	if (attrtype == MSG_ptn_active) {
2841 		strlcpy(data->str,
2842 		    msg_string(primary && (mp->mbrp_flag & MBR_PFLAG_ACTIVE) ?
2843 		    MSG_Yes : MSG_No), data->avail_space);
2844 		return true;
2845 #if BOOTSEL
2846 	} else if (attrtype == MSG_boot_dflt) {
2847 		strlcpy(data->str,
2848 		    msg_string(
2849 			(parts->mbr.bootsec == mb->sector+mp->mbrp_start) ?
2850 		    MSG_Yes : MSG_No), data->avail_space);
2851 		return true;
2852 	} else if (attrtype == MSG_bootmenu) {
2853 		strlcpy(data->str, mb->mbrb.mbrbs_nametab[i],
2854 		    data->avail_space);
2855 #endif
2856 	}
2857 
2858 	return false;
2859 }
2860 
2861 static bool
2862 part_attr_set_str(const struct disk_partitions *arg, part_id id,
2863     const mbr_info_t *mb, int i, bool primary,
2864     const struct mbr_partition *mp, void *cookie)
2865 {
2866 	struct part_attr_set_data *data = cookie;
2867 	const char *str = data->str;
2868 #ifdef BOOTSEL
2869 	const struct mbr_disk_partitions *parts =
2870 	    (const struct mbr_disk_partitions*)arg;
2871 	const char *attrtype = parts->dp.pscheme
2872 	    ->custom_attributes[data->attr_no].label;
2873 	mbr_info_t *m;
2874 #endif
2875 
2876 	while (*str == ' ')
2877 		str++;
2878 
2879 #if BOOTSEL
2880 	if (attrtype == MSG_bootmenu) {
2881 		for (m = data->mbr; m != mb; m = m->extended)
2882 			;
2883 		strncpy(m->mbrb.mbrbs_nametab[i], str,
2884 		    sizeof(m->mbrb.mbrbs_nametab[i]));
2885 	}
2886 #endif
2887 
2888 	return false;
2889 }
2890 
2891 static bool
2892 part_attr_toggle(const struct disk_partitions *arg, part_id id,
2893     const mbr_info_t *mb, int i, bool primary,
2894     const struct mbr_partition *mp, void *cookie)
2895 {
2896 	const struct mbr_disk_partitions *parts =
2897 	    (const struct mbr_disk_partitions*)arg;
2898 	struct part_attr_set_data *data = cookie;
2899 	const char *attrtype = parts->dp.pscheme
2900 	    ->custom_attributes[data->attr_no].label;
2901 	int j;
2902 
2903 	if (attrtype == MSG_ptn_active) {
2904 		if (!primary)
2905 			return false;
2906 
2907 		data->mbr->mbr.mbr_parts[i].mbrp_flag ^= MBR_PFLAG_ACTIVE;
2908 		for (j = 0; j < MBR_PART_COUNT; j++) {
2909 			if (j == i)
2910 				continue;
2911 			data->mbr->mbr.mbr_parts[j].mbrp_flag
2912 			    &= ~MBR_PFLAG_ACTIVE;
2913 		}
2914 		return true;
2915 #ifdef BOOTSEL
2916 	} else if (attrtype == MSG_boot_dflt) {
2917 		if (data->mbr->bootsec == mb->sector+mp->mbrp_start)
2918 			data->mbr->bootsec = 0;
2919 		else
2920 			data->mbr->bootsec = mb->sector+mp->mbrp_start;
2921 		return true;
2922 #endif
2923 	}
2924 
2925 	return false;
2926 }
2927 
2928 static bool
2929 mbr_custom_attribute_format(const struct disk_partitions *arg,
2930     part_id id, size_t attr_no, const struct disk_part_info *info,
2931     char *res, size_t space)
2932 {
2933 	const struct mbr_disk_partitions *parts =
2934 	    (const struct mbr_disk_partitions*)arg;
2935 	struct part_attr_fmt_data data;
2936 
2937 	data.str = res;
2938 	data.avail_space = space;
2939 	data.attr_no = attr_no;
2940 	data.parts = parts;
2941 	data.info = info;
2942 
2943 	return mbr_part_apply(arg, id, part_attr_fornat_str, &data);
2944 }
2945 
2946 static bool
2947 mbr_custom_attribute_toggle(struct disk_partitions *arg,
2948     part_id id, size_t attr_no)
2949 {
2950 	struct mbr_disk_partitions *parts =
2951 	    (struct mbr_disk_partitions*)arg;
2952 	struct part_attr_set_data data;
2953 
2954 	data.attr_no = attr_no;
2955 	data.parts = parts;
2956 	data.str = NULL;
2957 #ifdef BOOTSEL
2958 	data.mbr = &parts->mbr;
2959 #endif
2960 
2961 	return mbr_part_apply(arg, id, part_attr_toggle, &data);
2962 }
2963 
2964 static bool
2965 mbr_custom_attribute_set_str(struct disk_partitions *arg,
2966     part_id id, size_t attr_no, const char *new_val)
2967 {
2968 	struct mbr_disk_partitions *parts =
2969 	    (struct mbr_disk_partitions*)arg;
2970 	struct part_attr_set_data data;
2971 
2972 	data.attr_no = attr_no;
2973 	data.parts = parts;
2974 	data.str = new_val;
2975 #ifdef BOOTSEL
2976 	data.mbr = &parts->mbr;
2977 #endif
2978 
2979 	return mbr_part_apply(arg, id, part_attr_set_str, &data);
2980 }
2981 
2982 static daddr_t
2983 mbr_part_alignment(const struct disk_partitions *arg)
2984 {
2985 	const struct mbr_disk_partitions *parts =
2986 	    (const struct mbr_disk_partitions*)arg;
2987 
2988 	return parts->ptn_alignment;
2989 }
2990 
2991 static bool
2992 add_wedge(const char *disk, daddr_t start, daddr_t size,
2993     char *wname, size_t max_len)
2994 {
2995 	struct dkwedge_info dkw;
2996 	char diskpath[MAXPATHLEN];
2997 	int fd;
2998 
2999 	memset(&dkw, 0, sizeof(dkw));
3000 	dkw.dkw_offset = start;
3001 	dkw.dkw_size = size;
3002 	snprintf((char*)dkw.dkw_wname, sizeof dkw.dkw_wname,
3003 	    "%s_%" PRIi64 "@%" PRIi64, disk, size, start);
3004 
3005 	*wname = 0;
3006 
3007 	fd = opendisk(disk, O_RDWR, diskpath, sizeof(diskpath), 0);
3008 	if (fd < 0)
3009 		return false;
3010 	if (ioctl(fd, DIOCAWEDGE, &dkw) == -1) {
3011 		close(fd);
3012 		return false;
3013 	}
3014 	close(fd);
3015 	strlcpy(wname, dkw.dkw_devname, max_len);
3016 	return true;
3017 }
3018 
3019 static bool
3020 mbr_get_part_device(const struct disk_partitions *arg,
3021     part_id ptn, char *devname, size_t max_devname_len, int *part,
3022     enum dev_name_usage usage, bool with_path, bool life)
3023 {
3024 	const struct mbr_disk_partitions *parts =
3025 	    (const struct mbr_disk_partitions*)arg;
3026 	struct disk_part_info info, tmp;
3027 	part_id dptn;
3028 	char *wedge_dev;
3029 
3030 	if (!mbr_get_part_info(arg, ptn, &info))
3031 		return false;
3032 
3033 	if (!mbr_part_get_wedge(arg, ptn, &wedge_dev) || wedge_dev == NULL)
3034 		return false;
3035 
3036 	if (wedge_dev[0] == 0) {
3037 		/*
3038 		 * If we have secondary partitions, try to find a match there
3039 		 * and use that...
3040 		 */
3041 		if (parts->dlabel != NULL) {
3042 			for (dptn = 0; dptn < parts->dlabel->num_part; dptn++) {
3043 				if (!parts->dlabel->pscheme->get_part_info(
3044 				    parts->dlabel, dptn, &tmp))
3045 					continue;
3046 				if (tmp.start != info.start ||
3047 				    tmp.size != info.size)
3048 					continue;
3049 				return parts->dlabel->pscheme->get_part_device(
3050 				    parts->dlabel, dptn, devname,
3051 				     max_devname_len,
3052 				    part, usage, with_path, life);
3053 			}
3054 		}
3055 
3056 		/*
3057 		 * Configure a new wedge and remember the name
3058 		 */
3059 		if (!add_wedge(arg->disk, info.start, info.size, wedge_dev,
3060 		    MBR_DEV_LEN))
3061 			return false;
3062 	}
3063 
3064 	assert(wedge_dev[0] != 0);
3065 
3066 	switch (usage) {
3067 	case logical_name:
3068 	case plain_name:
3069 		if (with_path)
3070 			snprintf(devname, max_devname_len, _PATH_DEV "%s",
3071 			    wedge_dev);
3072 		else
3073 			strlcpy(devname, wedge_dev, max_devname_len);
3074 		return true;
3075 	case raw_dev_name:
3076 		if (with_path)
3077 			snprintf(devname, max_devname_len, _PATH_DEV "r%s",
3078 			    wedge_dev);
3079 		else
3080 			snprintf(devname, max_devname_len, "r%s",
3081 			    wedge_dev);
3082 		return true;
3083 	default:
3084 		return false;
3085 	}
3086 }
3087 
3088 static bool
3089 is_custom_attribute_writable(const struct disk_partitions *arg, part_id id,
3090     const mbr_info_t *mb, int i, bool primary,
3091     const struct mbr_partition *mp, void *cookie)
3092 {
3093 	const struct mbr_disk_partitions *parts =
3094 	    (const struct mbr_disk_partitions*)arg;
3095 	struct part_attr_set_data *data = cookie;
3096 	const char *attrtype = parts->dp.pscheme
3097 	    ->custom_attributes[data->attr_no].label;
3098 
3099 	if (attrtype == MSG_ptn_active)
3100 	        /* Only 'normal' partitions can be 'Active' */
3101 		return primary && !MBR_IS_EXTENDED(mp->mbrp_type);
3102 #ifdef BOOTSEL
3103 	else if (attrtype == MSG_boot_dflt)
3104 	        /* Only partitions with bootmenu names can be default */
3105 		return mb->mbrb.mbrbs_nametab[i][0] != 0;
3106 	else if (attrtype == MSG_bootmenu)
3107         	/* The extended partition isn't bootable */
3108 		return !MBR_IS_EXTENDED(mp->mbrp_type);
3109 #endif
3110 
3111 	return false;
3112 }
3113 
3114 static bool
3115 mbr_custom_attribute_writable(const struct disk_partitions *arg,
3116     part_id id, size_t attr_no)
3117 {
3118 	const struct mbr_disk_partitions *parts =
3119 	    (const struct mbr_disk_partitions*)arg;
3120 	struct part_attr_set_data data;
3121 
3122 	data.attr_no = attr_no;
3123 	data.parts = parts;
3124 	data.str = NULL;
3125 #ifdef BOOTSEL
3126 	data.mbr = NULL;
3127 #endif
3128 
3129 	return mbr_part_apply(arg, id, is_custom_attribute_writable, &data);
3130 }
3131 
3132 const struct disk_part_edit_column_desc mbr_edit_columns[] = {
3133 	{ .title = MSG_mbr_part_header_1,
3134 #if BOOTSEL
3135 	  .width = 16U
3136 #else
3137 	  .width = 26U
3138 #endif
3139 	 },
3140 	{ .title = MSG_mbr_part_header_2, .width = 8U },
3141 #if BOOTSEL
3142 	{ .title = MSG_mbr_part_header_3, .width = 9U },
3143 #endif
3144 };
3145 
3146 const struct disk_part_custom_attribute mbr_custom_attrs[] = {
3147 	{ .label = MSG_ptn_active,	.type = pet_bool },
3148 #if BOOTSEL
3149 	{ .label = MSG_boot_dflt,	.type = pet_bool },
3150 	{ .label = MSG_bootmenu,	.type = pet_str,
3151 					.strlen = MBR_BS_PARTNAMESIZE },
3152 #endif
3153 };
3154 
3155 const struct disk_partitioning_scheme
3156 mbr_parts = {
3157 	.name = MSG_parttype_mbr,
3158 	.short_name = MSG_parttype_mbr_short,
3159 	.new_type_prompt = MSG_mbr_get_ptn_id,
3160 	.part_flag_desc = MSG_mbr_flag_desc,
3161 	.size_limit = (daddr_t)UINT32_MAX,
3162 	.secondary_scheme = &disklabel_parts,
3163 	.edit_columns_count = __arraycount(mbr_edit_columns),
3164 	.edit_columns = mbr_edit_columns,
3165 	.custom_attribute_count = __arraycount(mbr_custom_attrs),
3166 	.custom_attributes = mbr_custom_attrs,
3167 	.get_part_alignment = mbr_part_alignment,
3168 	.get_part_info = mbr_get_part_info,
3169 	.get_part_attr_str = mbr_part_attr_str,
3170 	.format_partition_table_str = mbr_table_str,
3171 	.part_type_can_change = mbr_part_type_can_change,
3172 	.can_add_partition = mbr_can_add_partition,
3173 	.custom_attribute_writable = mbr_custom_attribute_writable,
3174 	.format_custom_attribute = mbr_custom_attribute_format,
3175 	.custom_attribute_toggle = mbr_custom_attribute_toggle,
3176 	.custom_attribute_set_str = mbr_custom_attribute_set_str,
3177 	.get_part_types_count = mbr_get_part_type_count,
3178 	.adapt_foreign_part_info = generic_adapt_foreign_part_info,
3179 	.get_part_type = mbr_get_part_type,
3180 	.get_fs_part_type = mbr_get_fs_part_type,
3181 	.get_generic_part_type = mbr_get_generic_part_type,
3182 	.create_custom_part_type = mbr_custom_part_type,
3183 	.create_unknown_part_type = mbr_create_unknown_part_type,
3184 	.secondary_partitions = mbr_read_disklabel,
3185 	.write_to_disk = mbr_write_to_disk,
3186 	.read_from_disk = mbr_read_from_disk,
3187 	.create_new_for_disk = mbr_create_new,
3188 	.guess_disk_geom = mbr_guess_geom,
3189 	.get_cylinder_size = mbr_get_cylinder,
3190 	.change_disk_geom = mbr_change_disk_geom,
3191 	.get_part_device = mbr_get_part_device,
3192 	.max_free_space_at = mbr_max_part_size,
3193 	.get_free_spaces = mbr_get_free_spaces,
3194 	.set_part_info = mbr_set_part_info,
3195 	.delete_all_partitions = mbr_delete_all,
3196 	.delete_partition = mbr_delete_part,
3197 	.add_partition = mbr_add_part,
3198 	.guess_install_target = mbr_guess_root,
3199 	.post_edit_verify = mbr_verify,
3200 	.pre_update_verify = mbr_verify_for_update,
3201 	.free = mbr_free,
3202 	.destroy_part_scheme = mbr_destroy_part_scheme,
3203 };
3204 
3205 #endif
3206