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