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