1/*	$NetBSD$ */
2
3/*
4 * Copyright 1997 Piermont Information Systems Inc.
5 * All rights reserved.
6 *
7 * Written by Philip A. Nelson for Piermont Information Systems Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. The name of Piermont Information Systems Inc. may not be used to endorse
18 *    or promote products derived from this software without specific prior
19 *    written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31 * THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34
35/*
36 * Following applies to the geometry guessing code
37 */
38
39/*
40 * Mach Operating System
41 * Copyright (c) 1992 Carnegie Mellon University
42 * All Rights Reserved.
43 *
44 * Permission to use, copy, modify and distribute this software and its
45 * documentation is hereby granted, provided that both the copyright
46 * notice and this permission notice appear in all copies of the
47 * software, derivative works or modified versions, and any portions
48 * thereof, and that both notices appear in supporting documentation.
49 *
50 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
51 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
52 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
53 *
54 * Carnegie Mellon requests users of this software to return to
55 *
56 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
57 *  School of Computer Science
58 *  Carnegie Mellon University
59 *  Pittsburgh PA 15213-3890
60 *
61 * any improvements or extensions that they make and grant Carnegie Mellon
62 * the rights to redistribute these changes.
63 */
64
65/* mbr.c -- DOS Master Boot Record editing code */
66
67#include <sys/param.h>
68#include <sys/types.h>
69#include <stdio.h>
70#include <unistd.h>
71#include <fcntl.h>
72#include <util.h>
73#include "defs.h"
74#include "mbr.h"
75#include "md.h"
76#include "msg_defs.h"
77#include "menu_defs.h"
78#include "endian.h"
79
80#define NO_BOOTMENU (-0x100)
81
82#define MAXCYL		1023    /* Possibly 1024 */
83#define MAXHEAD		255     /* Possibly 256 */
84#define MAXSECTOR	63
85
86struct part_id {
87	int id;
88	const char *name;
89} part_ids[] = {
90	{0,			"unused"},
91	{MBR_PTYPE_NETBSD,	"NetBSD"},
92	{MBR_PTYPE_EXT_LBA,	"Extended partition, LBA"},
93	{MBR_PTYPE_386BSD,	"FreeBSD/386BSD"},
94	{MBR_PTYPE_OPENBSD,	"OpenBSD"},
95	{MBR_PTYPE_LNXEXT2,	"Linux native"},
96	{MBR_PTYPE_LNXSWAP,	"Linux swap"},
97	{MBR_PTYPE_FAT12,	"DOS FAT12"},
98	{MBR_PTYPE_FAT16S,	"DOS FAT16, <32M"},
99	{MBR_PTYPE_FAT16B,	"DOS FAT16, >32M"},
100	{MBR_PTYPE_FAT16L,	"Windows FAT16, LBA"},
101	{MBR_PTYPE_FAT32,	"Windows FAT32"},
102	{MBR_PTYPE_FAT32L,	"Windows FAT32, LBA"},
103	{MBR_PTYPE_NTFSVOL,	"NTFS volume set"},
104	{MBR_PTYPE_NTFS,	"NTFS"},
105	{MBR_PTYPE_PREP,	"PReP Boot"},
106#ifdef MBR_PTYPE_SOLARIS
107	{MBR_PTYPE_SOLARIS,	"Solaris"},
108#endif
109	{-1,			"Unknown"},
110};
111
112static int get_mapping(struct mbr_partition *, int, int *, int *, int *,
113			    daddr_t *);
114static void convert_mbr_chs(int, int, int, uint8_t *, uint8_t *,
115				 uint8_t *, uint32_t);
116static void get_ptn_alignment(struct mbr_partition *);
117
118static unsigned int ptn_alignment;
119static unsigned int ptn_0_offset;
120
121/*
122 * Notes on the extended partition editor.
123 *
124 * The extended partition structure is actually a singly linked list.
125 * Each of the 'mbr' sectors can only contain 2 items, the first describes
126 * a user partition (relative to that mbr sector), the second describes
127 * the following partition (relative to the start of the extended partition).
128 *
129 * The 'start' sector for the user partition is always the size of one
130 * track - very often 63.  The extended partitions themselves should
131 * always start on a cylinder boundary using the BIOS geometry - often
132 * 16065 sectors per cylinder.
133 *
134 * The disk is also always described in increasing sector order.
135 *
136 * During editing we keep the mbr sectors accurate (it might have been
137 * easier to use absolute sector numbers though), and keep a copy of the
138 * entire sector - to preserve any information any other OS has tried
139 * to squirrel away in the (apparently) unused space.
140 *
141 * For simplicity we add entries for unused space.  These should not
142 * get written to the disk.
143 *
144 * Typical disk (with some small numbers):
145 *
146 *      0 -> a       63       37	dos
147 *           b      100     1000	extended LBA (type 15)
148 *
149 *    100 -> a       63       37        user
150 *           b      100      200	extended partiton (type 5)
151 *
152 *    200 -> a       63       37        user
153 *           b      200      300	extended partiton (type 5)
154 *
155 *    300 -> a       63       37	user
156 *           b        0        0        0 (end of chain)
157 *
158 * If there is a gap, the 'b' partition will start beyond the area
159 * described by the 'a' partition.
160 *
161 * While writing this comment, I can't remember what happens is there
162 * is space at the start of the extended partition.
163 */
164
165#ifndef debug_extended
166#define dump_mbr(mbr, msg)
167#else
168void
169dump_mbr(mbr_info_t *mbr, const char *msg)
170{
171	int i;
172
173	fprintf(stderr, "%s: bsec %d\n", msg, bsec);
174	do {
175		fprintf(stderr, "%9p: %9d %9p %6.6s:",
176		    mbr, mbr->sector, mbr->extended,
177		    mbr->prev_ext, mbr->last_mounted);
178		for (i = 0; i < 4; i++)
179			fprintf(stderr, " %*d %9d %9d %9d,\n",
180			    i ? 41 : 3,
181			    mbr->mbr.mbr_parts[i].mbrp_type,
182			    mbr->mbr.mbr_parts[i].mbrp_start,
183			    mbr->mbr.mbr_parts[i].mbrp_size,
184			    mbr->mbr.mbr_parts[i].mbrp_start +
185				mbr->mbr.mbr_parts[i].mbrp_size);
186	} while ((mbr = mbr->extended));
187}
188#endif
189
190/*
191 * To be used only on ports which cannot provide any bios geometry
192 */
193int
194set_bios_geom_with_mbr_guess(void)
195{
196	int cyl, head;
197	daddr_t sec;
198
199	read_mbr(diskdev, &mbr);
200	msg_display(MSG_nobiosgeom, dlcyl, dlhead, dlsec);
201	if (guess_biosgeom_from_mbr(&mbr, &cyl, &head, &sec) >= 0)
202		msg_display_add(MSG_biosguess, cyl, head, sec);
203	set_bios_geom(cyl, head, sec);
204	return edit_mbr(&mbr);
205}
206
207/*
208 * get C/H/S geometry from user via menu interface and
209 * store in globals.
210 */
211void
212set_bios_geom(int cyl, int head, int sec)
213{
214	char res[80];
215
216	msg_display_add(MSG_setbiosgeom);
217
218	do {
219		snprintf(res, 80, "%d", sec);
220		msg_prompt_add(MSG_sectors, res, res, 80);
221		bsec = atoi(res);
222	} while (bsec <= 0 || bsec > 63);
223
224	do {
225		snprintf(res, 80, "%d", head);
226		msg_prompt_add(MSG_heads, res, res, 80);
227		bhead = atoi(res);
228	} while (bhead <= 0 || bhead > 256);
229
230	bcyl = dlsize / bsec / bhead;
231	if (dlsize != bcyl * bsec * bhead)
232		bcyl++;
233}
234
235#ifdef notdef
236void
237disp_cur_geom(void)
238{
239
240	msg_display_add(MSG_realgeom, dlcyl, dlhead, dlsec);
241	msg_display_add(MSG_biosgeom, bcyl, bhead, bsec);
242}
243#endif
244
245
246/*
247 * Then,  the partition stuff...
248 */
249
250/*
251 * If we change the mbr partitioning, the we must remove any references
252 * in the netbsd disklabel to the part we changed.
253 */
254static void
255remove_old_partitions(uint start, int64_t size)
256{
257	partinfo *p;
258	uint end;
259
260	if (size > 0) {
261		end = start + size;
262	} else {
263		end = start;
264		start = end - size;
265	}
266
267	if (end == 0)
268		return;
269
270	for (p = oldlabel; p < oldlabel + nelem(oldlabel); p++) {
271		if (p->pi_offset >= end || p->pi_offset + p->pi_size <= start)
272			continue;
273		memset(p, 0, sizeof *p);
274	}
275}
276
277static int
278find_mbr_space(struct mbr_sector *mbrs, uint *start, uint *size, uint from, int ignore)
279{
280	uint sz;
281	int i;
282	uint s, e;
283
284    check_again:
285	sz = dlsize - from;
286	for (i = 0; i < MBR_PART_COUNT; i++) {
287		if (i == ignore)
288			continue;
289		s = mbrs->mbr_parts[i].mbrp_start;
290		e = s + mbrs->mbr_parts[i].mbrp_size;
291		if (s <= from && e > from) {
292			from = e;
293			goto check_again;
294		}
295		if (s > from && s - from < sz)
296			sz = s - from;
297	}
298	if (sz == 0)
299		return -1;
300	if (start != NULL)
301		*start = from;
302	if (size != NULL)
303		*size = sz;
304	return 0;
305}
306
307static struct mbr_partition *
308get_mbrp(mbr_info_t **mbrip, int opt)
309{
310	mbr_info_t *mbri = *mbrip;
311
312	if (opt >= MBR_PART_COUNT)
313		for (opt -= MBR_PART_COUNT - 1; opt; opt--)
314			mbri = mbri->extended;
315
316	*mbrip = mbri;
317	return &mbri->mbr.mbr_parts[opt];
318}
319
320static int
321err_msg_win(const char *errmsg)
322{
323	const char *cont;
324	int l, l1;
325
326	errmsg = msg_string(errmsg);
327	cont = msg_string(MSG_Hit_enter_to_continue);
328
329	l = strlen(errmsg);
330	l1 = strlen(cont);
331	if (l < l1)
332		l = l1;
333
334	msg_prompt_win("%s.\n%s", -1, 18, l + 5, 4,
335			NULL, NULL, 1, errmsg, cont);
336	return 0;
337}
338
339static int
340set_mbr_type(menudesc *m, void *arg)
341{
342	mbr_info_t *mbri = arg;
343	mbr_info_t *ombri = arg;
344	mbr_info_t *ext;
345	struct mbr_partition *mbrp;
346	char *cp;
347	int opt = mbri->opt;
348	int type;
349	u_int start, sz;
350	int i;
351	char numbuf[5];
352
353	dump_mbr(ombri, "set type");
354
355	mbrp = get_mbrp(&mbri, opt);
356	if (opt >= MBR_PART_COUNT)
357		opt = 0;
358
359	type = m->cursel;
360	if (type == 0)
361		return 1;
362	type = part_ids[type - 1].id;
363	while (type == -1) {
364		snprintf(numbuf, sizeof numbuf, "%u", mbrp->mbrp_type);
365		msg_prompt_win(MSG_get_ptn_id, -1, 18, 0, 0,
366			numbuf, numbuf, sizeof numbuf);
367		type = strtoul(numbuf, &cp, 0);
368		if (*cp != 0)
369			type = -1;
370	}
371
372	if (type == mbrp->mbrp_type)
373		/* type not changed... */
374		return 1;
375
376	mbri->last_mounted[opt < MBR_PART_COUNT ? opt : 0] = NULL;
377
378	if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
379		/* deleting extended partition.... */
380		if (mbri->sector || mbri->extended->extended)
381			/* We should have stopped this happening... */
382			return err_msg_win("can't delete extended");
383		free(mbri->extended);
384		mbri->extended = NULL;
385	}
386
387	if (type == 0) {
388		/* Deleting partition */
389		mbrp->mbrp_type = 0;
390		/* Remove references to this space from the NetBSD label */
391		remove_old_partitions(mbri->sector + mbrp->mbrp_start,
392		    mbrp->mbrp_size);
393#ifdef BOOTSEL
394		if (ombri->bootsec == mbri->sector + mbrp->mbrp_start)
395			ombri->bootsec = 0;
396
397		memset(mbri->mbrb.mbrbs_nametab[opt], 0,
398		    sizeof mbri->mbrb.mbrbs_nametab[opt]);
399#endif
400		if (mbri->sector == 0) {
401			/* A main partition */
402			memset(mbrp, 0, sizeof *mbrp);
403			return 1;
404		}
405
406		/* Merge with previous and next free areas */
407		ext = mbri->prev_ext;
408		if (ext != NULL && ext->mbr.mbr_parts[0].mbrp_type == 0) {
409			/* previous was free - back up one entry */
410			mbri = ext;
411			ombri->opt--;
412		}
413		while ((ext = mbri->extended)) {
414			if (ext->mbr.mbr_parts[0].mbrp_type != 0)
415				break;
416			sz = ext->mbr.mbr_parts[0].mbrp_start +
417						ext->mbr.mbr_parts[0].mbrp_size;
418			/* Increase size of our (empty) partition */
419			mbri->mbr.mbr_parts[0].mbrp_size += sz;
420			/* Make us describe the next partition */
421			mbri->mbr.mbr_parts[1] = ext->mbr.mbr_parts[1];
422			/* fix list of extended partitions */
423			mbri->extended = ext->extended;
424			if (ext->extended != NULL)
425				ext->extended->prev_ext = mbri;
426			free(ext);
427			/* Make previous size cover all our ptn */
428			ext = mbri->prev_ext;
429			if (ext != NULL)
430				ext->mbr.mbr_parts[1].mbrp_size += sz;
431		}
432		return 1;
433	}
434
435	if (mbrp->mbrp_start == 0) {
436		/* Find first chunk of space... */
437		/* Must be in the main partition */
438		if (mbri->sector != 0)
439			/* shouldn't be possible to have null start... */
440			return err_msg_win("main-extended mixup");
441		if (find_mbr_space(&mbri->mbr, &start, &sz, bsec, -1) != 0)
442			/* no space */
443			return err_msg_win(MSG_No_free_space);
444		mbrp->mbrp_start = start;
445		mbrp->mbrp_size = sz;
446		/* If there isn't an active partition mark this one active */
447		if (!MBR_IS_EXTENDED(type)) {
448			for (i = 0; i < MBR_PART_COUNT; i++)
449				if (mbri->mbr.mbr_parts[i].mbrp_flag != 0)
450					break;
451			if (i == MBR_PART_COUNT)
452				mbrp->mbrp_flag = MBR_PFLAG_ACTIVE;
453		}
454	}
455
456	if (MBR_IS_EXTENDED(type)) {
457		if (mbri->sector != 0)
458			/* Can't set extended partition in an extended one */
459			return err_msg_win(MSG_Only_one_extended_ptn);
460		if (mbri->extended)
461			/* Can't have two extended partitions */
462			return err_msg_win(MSG_Only_one_extended_ptn);
463		/* Create new extended partition */
464		ext = calloc(1, sizeof *mbri->extended);
465		if (!ext)
466			return 0;
467		mbri->extended = ext;
468		ext->sector = mbrp->mbrp_start;
469		ext->mbr.mbr_parts[0].mbrp_start = ptn_0_offset;
470		ext->mbr.mbr_parts[0].mbrp_size =
471		    mbrp->mbrp_size - ptn_0_offset;
472	}
473	mbrp->mbrp_type = type;
474
475	return 1;
476}
477
478static void
479set_type_label(menudesc *m, int opt, void *arg)
480{
481
482	if (opt == 0) {
483		wprintw(m->mw, "%s", msg_string(MSG_Dont_change));
484		return;
485	}
486	if (opt == 1) {
487		wprintw(m->mw, "%s", msg_string(MSG_Delete_partition));
488		return;
489	}
490	if (part_ids[opt - 1].id == -1) {
491		wprintw(m->mw, "%s", msg_string(MSG_Other_kind));
492		return;
493	}
494	wprintw(m->mw, "%s", part_ids[opt - 1].name);
495}
496
497static int
498edit_mbr_type(menudesc *m, void *arg)
499{
500	static menu_ent type_opts[1 + nelem(part_ids)];
501	static int type_menu = -1;
502	unsigned int i;
503
504	if (type_menu == -1) {
505		for (i = 0; i < nelem(type_opts); i++) {
506			type_opts[i].opt_menu = OPT_NOMENU;
507			type_opts[i].opt_action = set_mbr_type;
508		}
509		type_menu = new_menu(NULL, type_opts, nelem(type_opts),
510			13, 12, 0, 30,
511			MC_SUBMENU | MC_SCROLL | MC_NOEXITOPT | MC_NOCLEAR,
512			NULL, set_type_label, NULL,
513			NULL, NULL);
514	}
515
516	if (type_menu != -1)
517		process_menu(type_menu, arg);
518
519	return 0;
520}
521
522static int
523edit_mbr_start(menudesc *m, void *arg)
524{
525	mbr_info_t *mbri = arg;
526	mbr_info_t *ext;
527	struct mbr_partition *mbrp;
528	int opt = mbri->opt;
529	uint start, sz;
530	uint new_r, new, limit, dflt_r;
531	int64_t delta;
532	const char *errmsg;
533	char *cp;
534	struct {
535		uint	start;
536		uint	start_r;
537		uint	limit;
538	} freespace[MBR_PART_COUNT];
539	unsigned int spaces;
540	unsigned int i;
541	char prompt[MBR_PART_COUNT * 60];
542	unsigned int len;
543	char numbuf[12];
544
545	if (opt >= MBR_PART_COUNT)
546		/* should not be able to get here... */
547		return 1;
548
549	mbrp = mbri->mbr.mbr_parts + opt;
550	/* locate the start of all free areas */
551	spaces = 0;
552	for (start = bsec, i = 0; i < MBR_PART_COUNT; start += sz, i++) {
553		if (find_mbr_space(&mbri->mbr, &start, &sz, start, opt))
554			break;
555		if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
556			/* Only want the area that contains this partition */
557			if (mbrp->mbrp_start < start ||
558			    mbrp->mbrp_start >= start + sz)
559				continue;
560			i = MBR_PART_COUNT - 1;
561		}
562		freespace[spaces].start = start;
563		freespace[spaces].start_r = start / sizemult;
564		freespace[spaces].limit = start + sz;
565		if (++spaces >= sizeof freespace)
566			/* shouldn't happen... */
567			break;
568	}
569
570	/* Add description of start/size to user prompt */
571	len = 0;
572	for (i = 0; i < spaces; i++) {
573		len += snprintf(prompt + len, sizeof prompt - len,
574		    msg_string(MSG_ptn_starts),
575		    freespace[i].start_r,
576		    freespace[i].limit / sizemult, multname,
577		    freespace[i].limit / sizemult - freespace[i].start_r,
578		    multname);
579		if (len >= sizeof prompt)
580			break;
581	}
582
583	/* And loop until the user gives a sensible answer */
584	dflt_r = mbrp->mbrp_start / sizemult;
585	errmsg = "";
586	for (;;) {
587		snprintf(numbuf, sizeof numbuf, "%d", dflt_r);
588		msg_prompt_win(MSG_get_ptn_start, -1, 18, 60, spaces + 3,
589			numbuf, numbuf, sizeof numbuf,
590			prompt, msg_string(errmsg), multname);
591		new_r = strtoul(numbuf, &cp, 0);
592		if (*cp != 0) {
593			errmsg = MSG_Invalid_numeric;
594			continue;
595		}
596		if (new_r == dflt_r)
597			/* Unchanged */
598			return 0;
599		/*
600		 * Check that the start address from the user is inside one
601		 * of the free areas.
602		 */
603		new = new_r * sizemult;
604		for (i = 0; i < spaces; i++) {
605			if (new_r == freespace[i].start_r) {
606				new = freespace[i].start;
607				break;
608			}
609			if (new >= freespace[i].start &&
610			    new < freespace[i].limit)
611				break;
612		}
613		if (i >= spaces) {
614			errmsg = MSG_Space_allocated;
615			continue;
616		}
617		limit = freespace[i].limit;
618		/*
619		 * We can only increase the start of an extended partition
620		 * if the corresponding space inside the partition isn't used.
621		 */
622		if (new > mbrp->mbrp_start &&
623		    MBR_IS_EXTENDED(mbrp->mbrp_type) &&
624		    (mbri->extended->mbr.mbr_parts[0].mbrp_type != 0 ||
625			    mbri->extended->mbr.mbr_parts[0].mbrp_size <
626						new - mbrp->mbrp_start)) {
627			errmsg = MSG_Space_allocated;
628			continue;
629		}
630		break;
631	}
632
633	if (new < mbrp->mbrp_start + mbrp->mbrp_size &&
634	    limit > mbrp->mbrp_start)
635		/* Keep end of partition in the same place */
636		limit = mbrp->mbrp_start + mbrp->mbrp_size;
637
638	delta = new - mbrp->mbrp_start;
639	if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
640		ext = mbri->extended;
641		if (ext->mbr.mbr_parts[0].mbrp_type != 0) {
642			/* allocate an extended ptn for the free item */
643			ext = calloc(1, sizeof *ext);
644			if (!ext)
645				return 0;
646			ext->sector = mbrp->mbrp_start;
647			ext->extended = mbri->extended;
648			mbri->extended->prev_ext = ext;
649			mbri->extended = ext;
650			ext->mbr.mbr_parts[0].mbrp_start = bsec;
651			ext->mbr.mbr_parts[0].mbrp_size = -bsec;
652			ext->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
653			ext->mbr.mbr_parts[1].mbrp_start = 0;
654			ext->mbr.mbr_parts[1].mbrp_size =
655				ext->extended->mbr.mbr_parts[0].mbrp_start +
656				ext->extended->mbr.mbr_parts[0].mbrp_size;
657		}
658		/* adjust size of first free item */
659		ext->mbr.mbr_parts[0].mbrp_size -= delta;
660		ext->sector += delta;
661		/* and the link of all extended partitions */
662		do
663			if (ext->extended)
664				ext->mbr.mbr_parts[1].mbrp_start -= delta;
665		while ((ext = ext->extended));
666	}
667	remove_old_partitions(mbri->sector + mbrp->mbrp_start, delta);
668
669	/* finally set partition base and size */
670	mbrp->mbrp_start = new;
671	mbrp->mbrp_size = limit - new;
672	mbri->last_mounted[opt] = NULL;
673
674	return 0;
675}
676
677static int
678edit_mbr_size(menudesc *m, void *arg)
679{
680	mbr_info_t *mbri = arg;
681	mbr_info_t *ombri = arg;
682	mbr_info_t *ext;
683	struct mbr_partition *mbrp;
684	int opt = mbri->opt;
685	uint start, max, max_r, dflt, dflt_r, new;
686	uint freespace;
687	int delta;
688	char numbuf[12];
689	char *cp;
690	const char *errmsg;
691
692	mbrp = get_mbrp(&mbri, opt);
693	dflt = mbrp->mbrp_size;
694	if (opt < MBR_PART_COUNT) {
695		max = 0;
696		find_mbr_space(&mbri->mbr, &start, &max, mbrp->mbrp_start, opt);
697		if (start != mbrp->mbrp_start)
698			return 0;
699		if (dflt == 0)
700			dflt = max;
701	} else {
702		ext = mbri->extended;
703		max = dflt;
704		/*
705		 * If the next extended partition describes a free area,
706		 * then merge it onto this area.
707		 */
708		if (ext != NULL && ext->mbr.mbr_parts[0].mbrp_type == 0) {
709			if (ext->extended)
710				ext->extended->prev_ext = mbri;
711			mbri->extended = ext->extended;
712			if (mbri->prev_ext)
713				mbri->prev_ext->mbr.mbr_parts[1].mbrp_size
714					+= mbri->mbr.mbr_parts[1].mbrp_size;
715			mbrp->mbrp_size += mbri->mbr.mbr_parts[1].mbrp_size;
716			max += mbri->mbr.mbr_parts[1].mbrp_size;
717			mbri->mbr.mbr_parts[1] = ext->mbr.mbr_parts[1];
718			free(ext);
719		}
720	}
721
722	start = mbri->sector + mbrp->mbrp_start;
723	/* We need to keep both the unrounded and rounded (_r) max and dflt */
724	dflt_r = (start + dflt) / sizemult - start / sizemult;
725	if (max == dflt)
726		max_r = dflt_r;
727	else
728		max_r = max / sizemult;
729	for (errmsg = "";;) {
730		snprintf(numbuf, sizeof numbuf, "%d", dflt_r);
731		msg_prompt_win(MSG_get_ptn_size, -1, 18, 0, 0,
732			numbuf, numbuf, sizeof numbuf,
733			msg_string(errmsg), max_r, multname);
734		new = strtoul(numbuf, &cp, 0);
735		if (*cp != 0) {
736			errmsg = MSG_Invalid_numeric;
737			continue;
738		}
739		if (new > max_r) {
740			errmsg = MSG_Too_large;
741			continue;
742		}
743		if (new == 0)
744			/* Treat zero as a request for the maximum */
745			new = max_r;
746		if (new == dflt_r)
747			/* If unchanged, don't re-round size */
748			new = dflt;
749		else {
750			/* Round end to the partition alignment */
751			if (sizemult != 1) {
752				new *= sizemult;
753				new += rounddown(start, ptn_alignment);
754				new = roundup(new, ptn_alignment);
755				new -= start;
756				while (new <= 0)
757					new += ptn_alignment;
758			}
759		}
760		if (new > max)
761			/* We rounded the value to above the max */
762			new = max;
763
764		if (new == dflt || opt >= MBR_PART_COUNT
765		    || !MBR_IS_EXTENDED(mbrp->mbrp_type))
766			break;
767		/*
768		 * We've been asked to change the size of the main extended
769		 * partition.  If this reduces the size, then that space
770		 * must be unallocated.  If it increases the size then
771		 * we must add a description ofthe new free space.
772		 */
773		/* Find last extended partition */
774		for (ext = mbri->extended; ext->extended; ext = ext->extended)
775			continue;
776		if ((new < dflt && (ext->mbr.mbr_parts[0].mbrp_type != 0
777			    || (mbrp->mbrp_start + new < ext->sector + bsec
778				&& mbrp->mbrp_start + new != ext->sector)))
779		    || (new > dflt && ext->mbr.mbr_parts[0].mbrp_type != 0
780							&& new < dflt + bsec)) {
781			errmsg = MSG_Space_allocated;
782			continue;
783		}
784		delta = new - dflt;
785		if (ext->mbr.mbr_parts[0].mbrp_type == 0) {
786			/* adjust size of last item (free space) */
787			if (mbrp->mbrp_start + new == ext->sector) {
788				/* kill last extended ptn */
789				ext = ext->prev_ext;
790				free(ext->extended);
791				ext->extended = NULL;
792				memset(&ext->mbr.mbr_parts[1], 0,
793					sizeof ext->mbr.mbr_parts[1]);
794				break;
795			}
796			ext->mbr.mbr_parts[0].mbrp_size += delta;
797			ext = ext->prev_ext;
798			if (ext != NULL)
799				ext->mbr.mbr_parts[1].mbrp_size += delta;
800			break;
801		}
802		/* Joy of joys, we must allocate another extended ptn */
803		mbri = ext;
804		ext = calloc(1, sizeof *ext);
805		if (!ext)
806			return 0;
807		mbri->extended = ext;
808		ext->prev_ext = mbri;
809		ext->mbr.mbr_parts[0].mbrp_start = bsec;
810		ext->mbr.mbr_parts[0].mbrp_size = delta - bsec;
811		ext->sector = mbri->sector + mbri->mbr.mbr_parts[0].mbrp_start
812					   + mbri->mbr.mbr_parts[0].mbrp_size;
813		mbri->mbr.mbr_parts[1].mbrp_start = ext->sector - ombri->extended->sector;
814		mbri->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
815		mbri->mbr.mbr_parts[1].mbrp_size = delta;
816		break;
817	}
818
819	if (opt >= MBR_PART_COUNT && max - new <= (uint32_t)bsec)
820		/* Round up if not enough space for a header for free area */
821		new = max;
822
823	if (new != mbrp->mbrp_size) {
824		/* Kill information about old partition from label */
825		mbri->last_mounted[opt < MBR_PART_COUNT ? opt : 0] = NULL;
826		remove_old_partitions(mbri->sector + mbrp->mbrp_start +
827			    mbrp->mbrp_size, (int64_t)new - mbrp->mbrp_size);
828	}
829
830	mbrp->mbrp_size = new;
831	if (opt < MBR_PART_COUNT || new == max)
832		return 0;
833
834	/* Add extended partition for the free space */
835	ext = calloc(1, sizeof *ext);
836	if (!ext) {
837		mbrp->mbrp_size = max;
838		return 0;
839	}
840	/* Link into our extended chain */
841	ext->extended = mbri->extended;
842	mbri->extended = ext;
843	ext->prev_ext = mbri;
844	if (ext->extended != NULL)
845		ext->extended->prev_ext = ext;
846	ext->mbr.mbr_parts[1] = mbri->mbr.mbr_parts[1];
847	freespace = max - new;
848	if (mbri->prev_ext != NULL)
849		mbri->prev_ext->mbr.mbr_parts[1].mbrp_size -= freespace;
850
851	ext->mbr.mbr_parts[0].mbrp_start = bsec;
852	ext->mbr.mbr_parts[0].mbrp_size = freespace - bsec;
853
854	ext->sector = mbri->sector + mbri->mbr.mbr_parts[0].mbrp_start
855				   + mbri->mbr.mbr_parts[0].mbrp_size;
856	mbri->mbr.mbr_parts[1].mbrp_start = ext->sector - ombri->extended->sector;
857	mbri->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
858	mbri->mbr.mbr_parts[1].mbrp_size = freespace;
859
860	return 0;
861}
862
863static int
864edit_mbr_active(menudesc *m, void *arg)
865{
866	mbr_info_t *mbri = arg;
867	int i;
868	uint8_t *fl;
869
870	if (mbri->opt >= MBR_PART_COUNT)
871		/* sanity */
872		return 0;
873
874	/* Invert active flag */
875	fl = &mbri->mbr.mbr_parts[mbri->opt].mbrp_flag;
876	if (*fl == MBR_PFLAG_ACTIVE) {
877		*fl = 0;
878		return 0;
879	}
880
881	/* Ensure there is at most one active partition */
882	for (i = 0; i < MBR_PART_COUNT; i++)
883		mbri->mbr.mbr_parts[i].mbrp_flag = 0;
884	*fl = MBR_PFLAG_ACTIVE;
885
886	return 0;
887}
888
889static int
890edit_mbr_install(menudesc *m, void *arg)
891{
892	mbr_info_t *mbri = arg;
893	mbr_info_t *ombri = arg;
894	struct mbr_partition *mbrp;
895	int opt = mbri->opt;
896	uint start;
897
898	mbrp = get_mbrp(&mbri, opt);
899	if (opt >= MBR_PART_COUNT)
900		opt = 0;
901
902	start = mbri->sector + mbrp->mbrp_start;
903	/* We just remember the start address of the partition... */
904	if (start == ombri->install)
905		ombri->install = 0;
906	else
907		ombri->install = start;
908	return 0;
909}
910
911#ifdef BOOTSEL
912static int
913edit_mbr_bootmenu(menudesc *m, void *arg)
914{
915	mbr_info_t *mbri = arg;
916	mbr_info_t *ombri = arg;
917	struct mbr_partition *mbrp;
918	int opt = mbri->opt;
919
920	mbrp = get_mbrp(&mbri, opt);
921	if (opt >= MBR_PART_COUNT)
922		opt = 0;
923
924	msg_prompt_win(/* XXX translate? */ "bootmenu", -1, 18, 0, 0,
925		mbri->mbrb.mbrbs_nametab[opt],
926		mbri->mbrb.mbrbs_nametab[opt],
927		sizeof mbri->mbrb.mbrbs_nametab[opt]);
928	if (mbri->mbrb.mbrbs_nametab[opt][0] == ' ')
929		mbri->mbrb.mbrbs_nametab[opt][0] = 0;
930	if (mbri->mbrb.mbrbs_nametab[opt][0] == 0
931	    && ombri->bootsec == mbri->sector + mbrp->mbrp_start)
932		ombri->bootsec = 0;
933	return 0;
934}
935
936static int
937edit_mbr_bootdefault(menudesc *m, void *arg)
938{
939	mbr_info_t *mbri = arg;
940	mbr_info_t *ombri = arg;
941	struct mbr_partition *mbrp;
942
943	mbrp = get_mbrp(&mbri, mbri->opt);
944
945	ombri->bootsec = mbri->sector + mbrp->mbrp_start;
946	return 0;
947}
948#endif
949
950static void set_ptn_label(menudesc *m, int line, void *arg);
951static void set_ptn_header(menudesc *m, void *arg);
952
953static int
954edit_mbr_entry(menudesc *m, void *arg)
955{
956	mbr_info_t *mbri = arg;
957	static int ptn_menu = -1;
958
959	static menu_ent ptn_opts[] = {
960#define PTN_OPT_TYPE		0
961		{NULL, OPT_NOMENU, 0, edit_mbr_type},
962#define PTN_OPT_START		1
963		{NULL, OPT_NOMENU, 0, edit_mbr_start},
964#define PTN_OPT_SIZE		2
965		{NULL, OPT_NOMENU, 0, edit_mbr_size},
966#define PTN_OPT_END		3
967		{NULL, OPT_NOMENU, OPT_IGNORE, NULL},	/* display end */
968#define PTN_OPT_ACTIVE		4
969		{NULL, OPT_NOMENU, 0, edit_mbr_active},
970#define PTN_OPT_INSTALL		5
971		{NULL, OPT_NOMENU, 0, edit_mbr_install},
972#ifdef BOOTSEL
973#define PTN_OPT_BOOTMENU	6
974		{NULL, OPT_NOMENU, 0, edit_mbr_bootmenu},
975#define PTN_OPT_BOOTDEFAULT	7
976		{NULL, OPT_NOMENU, 0, edit_mbr_bootdefault},
977#endif
978		{MSG_askunits, MENU_sizechoice, OPT_SUB, NULL},
979	};
980
981	if (ptn_menu == -1)
982		ptn_menu = new_menu(NULL, ptn_opts, nelem(ptn_opts),
983			15, 6, 0, 54,
984			MC_SUBMENU | MC_SCROLL | MC_NOCLEAR,
985			set_ptn_header, set_ptn_label, NULL,
986			NULL, MSG_Partition_OK);
987	if (ptn_menu == -1)
988		return 1;
989
990	mbri->opt = m->cursel;
991	process_menu(ptn_menu, mbri);
992	return 0;
993}
994
995static void
996set_ptn_label(menudesc *m, int line, void *arg)
997{
998	mbr_info_t *mbri = arg;
999	mbr_info_t *ombri = arg;
1000	struct mbr_partition *mbrp;
1001	int opt;
1002	static const char *yes, *no;
1003
1004	if (yes == NULL) {
1005		yes = msg_string(MSG_Yes);
1006		no = msg_string(MSG_No);
1007	}
1008
1009	opt = mbri->opt;
1010	mbrp = get_mbrp(&mbri, opt);
1011	if (opt >= MBR_PART_COUNT)
1012		opt = 0;
1013
1014	switch (line) {
1015	case PTN_OPT_TYPE:
1016		wprintw(m->mw, msg_string(MSG_ptn_type),
1017			get_partname(mbrp->mbrp_type));
1018		break;
1019	case PTN_OPT_START:
1020		wprintw(m->mw, msg_string(MSG_ptn_start),
1021		    (mbri->sector + mbrp->mbrp_start) / sizemult, multname);
1022		break;
1023	case PTN_OPT_SIZE:
1024		wprintw(m->mw, msg_string(MSG_ptn_size),
1025		    (mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size) /
1026			    sizemult -
1027		    (mbri->sector + mbrp->mbrp_start) / sizemult, multname);
1028		break;
1029	case PTN_OPT_END:
1030		wprintw(m->mw, msg_string(MSG_ptn_end),
1031		    (mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size) /
1032			    sizemult, multname);
1033		break;
1034	case PTN_OPT_ACTIVE:
1035		wprintw(m->mw, msg_string(MSG_ptn_active),
1036		    mbrp->mbrp_flag == MBR_PFLAG_ACTIVE ? yes : no);
1037		break;
1038	case PTN_OPT_INSTALL:
1039		wprintw(m->mw, msg_string(MSG_ptn_install),
1040		    mbri->sector + mbrp->mbrp_start == ombri->install &&
1041			mbrp->mbrp_type == MBR_PTYPE_NETBSD ? yes : no);
1042		break;
1043#ifdef BOOTSEL
1044	case PTN_OPT_BOOTMENU:
1045		wprintw(m->mw, msg_string(MSG_bootmenu),
1046		    mbri->mbrb.mbrbs_nametab[opt]);
1047		break;
1048	case PTN_OPT_BOOTDEFAULT:
1049		wprintw(m->mw, msg_string(MSG_boot_dflt),
1050		    ombri->bootsec == mbri->sector + mbrp->mbrp_start ? yes
1051								      : no);
1052		break;
1053#endif
1054	}
1055
1056}
1057
1058static void
1059set_ptn_header(menudesc *m, void *arg)
1060{
1061	mbr_info_t *mbri = arg;
1062	struct mbr_partition *mbrp;
1063	int opt = mbri->opt;
1064	int typ;
1065
1066	mbrp = get_mbrp(&mbri, opt);
1067	if (opt >= MBR_PART_COUNT)
1068		opt = 0;
1069	typ = mbrp->mbrp_type;
1070
1071#define DISABLE(opt,cond) \
1072	if (cond) \
1073		m->opts[opt].opt_flags |= OPT_IGNORE; \
1074	else \
1075		m->opts[opt].opt_flags &= ~OPT_IGNORE;
1076
1077	/* Can't change type of the extended partition unless it is empty */
1078	DISABLE(PTN_OPT_TYPE, MBR_IS_EXTENDED(typ) &&
1079	    (mbri->extended->mbr.mbr_parts[0].mbrp_type != 0 ||
1080					    mbri->extended->extended != NULL));
1081
1082	/* It is unnecessary to be able to change the base of an extended ptn */
1083	DISABLE(PTN_OPT_START, mbri->sector || typ == 0);
1084
1085	/* or the size of a free area */
1086	DISABLE(PTN_OPT_SIZE, typ == 0);
1087
1088	/* Only 'normal' partitions can be 'Active' */
1089	DISABLE(PTN_OPT_ACTIVE, mbri->sector != 0 || MBR_IS_EXTENDED(typ) || typ == 0);
1090
1091	/* Can only install into NetBSD partition */
1092	DISABLE(PTN_OPT_INSTALL, typ != MBR_PTYPE_NETBSD);
1093
1094#ifdef BOOTSEL
1095	/* The extended partition isn't bootable */
1096	DISABLE(PTN_OPT_BOOTMENU, MBR_IS_EXTENDED(typ) || typ == 0);
1097
1098	if (typ == 0)
1099		mbri->mbrb.mbrbs_nametab[opt][0] = 0;
1100
1101	/* Only partitions with bootmenu names can be made the default */
1102	DISABLE(PTN_OPT_BOOTDEFAULT, mbri->mbrb.mbrbs_nametab[opt][0] == 0);
1103#endif
1104#undef DISABLE
1105}
1106
1107static void
1108set_mbr_label(menudesc *m, int opt, void *arg)
1109{
1110	mbr_info_t *mbri = arg;
1111	mbr_info_t *ombri = arg;
1112	struct mbr_partition *mbrp;
1113	uint rstart, rend;
1114	const char *name, *cp, *mounted;
1115	int len;
1116
1117	mbrp = get_mbrp(&mbri, opt);
1118	if (opt >= MBR_PART_COUNT)
1119		opt = 0;
1120
1121	if (mbrp->mbrp_type == 0 && mbri->sector == 0) {
1122		len = snprintf(0, 0, msg_string(MSG_part_row_used), 0, 0, 0);
1123		wprintw(m->mw, "%*s", len, "");
1124	} else {
1125		rstart = mbri->sector + mbrp->mbrp_start;
1126		rend = (rstart + mbrp->mbrp_size) / sizemult;
1127		rstart = rstart / sizemult;
1128		wprintw(m->mw, msg_string(MSG_part_row_used),
1129		    rstart, rend - rstart,
1130		    mbrp->mbrp_flag == MBR_PFLAG_ACTIVE ? 'a' : ' ',
1131#ifdef BOOTSEL
1132		    ombri->bootsec == mbri->sector + mbrp->mbrp_start ? 'd' :
1133#endif
1134			' ',
1135		    mbri->sector + mbrp->mbrp_start == ombri->install &&
1136			mbrp->mbrp_type == MBR_PTYPE_NETBSD ? 'I' : ' ');
1137	}
1138	name = get_partname(mbrp->mbrp_type);
1139	mounted = mbri->last_mounted[opt];
1140	len = strlen(name);
1141	cp = strchr(name, ',');
1142	if (cp != NULL)
1143		len = cp - name;
1144	if (mounted && *mounted != 0) {
1145		wprintw(m->mw, " %*s (%s)", len, name, mounted);
1146	} else
1147		wprintw(m->mw, " %.*s", len, name);
1148#ifdef BOOTSEL
1149	if (mbri->mbrb.mbrbs_nametab[opt][0] != 0) {
1150		int x, y;
1151		if (opt >= MBR_PART_COUNT)
1152			opt = 0;
1153		getyx(m->mw, y, x);
1154		if (x > 52) {
1155			x = 52;
1156			wmove(m->mw, y, x);
1157		}
1158		wprintw(m->mw, "%*s %s", 53 - x, "",
1159		    mbri->mbrb.mbrbs_nametab[opt]);
1160	}
1161#endif
1162}
1163
1164static void
1165set_mbr_header(menudesc *m, void *arg)
1166{
1167	mbr_info_t *mbri = arg;
1168	static menu_ent *opts;
1169	static int num_opts;
1170	mbr_info_t *ext;
1171	menu_ent *op;
1172	int i;
1173	int left;
1174
1175	msg_display(MSG_editparttable);
1176
1177	msg_table_add(MSG_part_header, (unsigned long)(dlsize/sizemult),
1178	    multname, multname, multname, multname);
1179
1180	if (num_opts == 0) {
1181		num_opts = 6;
1182		opts = malloc(6 * sizeof *opts);
1183		if (opts == NULL) {
1184			m->numopts = 0;
1185			return;
1186		}
1187	}
1188
1189	/* First four items are the main partitions */
1190	for (op = opts, i = 0; i < MBR_PART_COUNT; op++, i++) {
1191		op->opt_name = NULL;
1192		op->opt_menu = OPT_NOMENU;
1193		op->opt_flags = OPT_SUB;
1194		op->opt_action = edit_mbr_entry;
1195	}
1196	left = num_opts - MBR_PART_COUNT;
1197
1198	/* Followed by the extended partitions */
1199	for (ext = mbri->extended; ext; left--, op++, ext = ext->extended) {
1200		if (left <= 1) {
1201			menu_ent *new = realloc(opts,
1202						(num_opts + 4) * sizeof *opts);
1203			if (new == NULL)
1204				break;
1205			num_opts += 4;
1206			left += 4;
1207			op = new + (op - opts);
1208			opts = new;
1209		}
1210		op->opt_name = NULL;
1211		op->opt_menu = OPT_NOMENU;
1212		op->opt_flags = 0;
1213		op->opt_action = edit_mbr_entry;
1214	}
1215
1216	/* and unit changer */
1217	op->opt_name = MSG_askunits;
1218	op->opt_menu = MENU_sizechoice;
1219	op->opt_flags = OPT_SUB;
1220	op->opt_action = NULL;
1221	op++;
1222
1223	m->opts = opts;
1224	m->numopts = op - opts;
1225}
1226
1227int
1228mbr_use_wholedisk(mbr_info_t *mbri)
1229{
1230	struct mbr_sector *mbrs = &mbri->mbr;
1231	mbr_info_t *ext;
1232	struct mbr_partition *part;
1233
1234	part = &mbrs->mbr_parts[0];
1235	/* Set the partition information for full disk usage. */
1236	while ((ext = mbri->extended)) {
1237		mbri->extended = ext->extended;
1238		free(ext);
1239	}
1240	memset(part, 0, MBR_PART_COUNT * sizeof *part);
1241#ifdef BOOTSEL
1242	memset(&mbri->mbrb, 0, sizeof mbri->mbrb);
1243#endif
1244	part[0].mbrp_type = MBR_PTYPE_NETBSD;
1245	part[0].mbrp_size = dlsize - ptn_0_offset;
1246	part[0].mbrp_start = ptn_0_offset;
1247	part[0].mbrp_flag = MBR_PFLAG_ACTIVE;
1248
1249	ptstart = ptn_0_offset;
1250	ptsize = dlsize - ptn_0_offset;
1251	return 1;
1252}
1253
1254/*
1255 * Let user change incore Master Boot Record partitions via menu.
1256 */
1257int
1258edit_mbr(mbr_info_t *mbri)
1259{
1260	struct mbr_sector *mbrs = &mbri->mbr;
1261	mbr_info_t *ext;
1262	struct mbr_partition *part;
1263	int i, j;
1264	int usefull;
1265	int mbr_menu;
1266	int activepart;
1267	int numbsd;
1268	uint bsdstart, bsdsize;
1269	uint start;
1270
1271	/* Ask full/part */
1272
1273	part = &mbrs->mbr_parts[0];
1274	get_ptn_alignment(part);	/* update ptn_alignment */
1275	msg_display(MSG_fullpart, diskdev);
1276	process_menu(MENU_fullpart, &usefull);
1277
1278	/* DOS fdisk label checking and value setting. */
1279	if (usefull) {
1280		/* Count nonempty, non-BSD partitions. */
1281		numbsd = 0;
1282		for (i = 0; i < MBR_PART_COUNT; i++) {
1283			j = part[i].mbrp_type;
1284			if (j == 0)
1285				continue;
1286			numbsd++;
1287			if (j != MBR_PTYPE_NETBSD)
1288				numbsd++;
1289		}
1290
1291		/* Ask if we really want to blow away non-NetBSD stuff */
1292		if (numbsd > 1) {
1293			msg_display(MSG_ovrwrite);
1294			process_menu(MENU_noyes, NULL);
1295			if (!yesno) {
1296				if (logfp)
1297					(void)fprintf(logfp, "User answered no to destroy other data, aborting.\n");
1298				return 0;
1299			}
1300		}
1301		return(md_mbr_use_wholedisk(mbri));
1302	}
1303
1304	mbr_menu = new_menu(NULL, NULL, 16, 0, -1, 15, 70,
1305			MC_NOBOX | MC_ALWAYS_SCROLL | MC_NOCLEAR,
1306			set_mbr_header, set_mbr_label, NULL,
1307			NULL, MSG_Partition_table_ok);
1308	if (mbr_menu == -1)
1309		return 0;
1310
1311	/* Default to MB, and use bios geometry for cylinder size */
1312	set_sizemultname_meg();
1313	current_cylsize = bhead * bsec;
1314
1315	for (;;) {
1316		ptstart = 0;
1317		ptsize = 0;
1318		process_menu(mbr_menu, mbri);
1319
1320		activepart = 0;
1321		bsdstart = 0;
1322		bsdsize = 0;
1323		for (ext = mbri; ext; ext = ext->extended) {
1324			part = ext->mbr.mbr_parts;
1325			for (i = 0; i < MBR_PART_COUNT; part++, i++) {
1326				if (part->mbrp_flag != 0)
1327					activepart = 1;
1328				if (part->mbrp_type != MBR_PTYPE_NETBSD)
1329					continue;
1330				start = ext->sector + part->mbrp_start;
1331				if (start == mbri->install) {
1332					ptstart = mbri->install;
1333					ptsize = part->mbrp_size;
1334				}
1335				if (bsdstart != 0)
1336					bsdstart = ~0;
1337				else {
1338					bsdstart = start;
1339					bsdsize = part->mbrp_size;
1340				}
1341			}
1342		}
1343
1344		/* Install in only netbsd partition if none tagged */
1345		if (ptstart == 0 && bsdstart != ~0u) {
1346			ptstart = bsdstart;
1347			ptsize = bsdsize;
1348		}
1349
1350		if (ptstart == 0) {
1351			if (bsdstart == 0)
1352				msg_display(MSG_nobsdpart);
1353			else
1354				msg_display(MSG_multbsdpart, 0);
1355			msg_display_add(MSG_reeditpart, 0);
1356			process_menu(MENU_yesno, NULL);
1357			if (!yesno)
1358				return 0;
1359			continue;
1360		}
1361
1362		if (activepart == 0) {
1363			msg_display(MSG_noactivepart);
1364			process_menu(MENU_yesno, NULL);
1365			if (yesno)
1366				continue;
1367		}
1368		/* the md_check_mbr function has 3 ret codes to deal with
1369		 * the different possible states. 0, 1, >1
1370		 */
1371		j = md_check_mbr(mbri);
1372		if (j == 0)
1373			return 0;
1374		if (j == 1)
1375			continue;
1376
1377		break;
1378	}
1379
1380	free_menu(mbr_menu);
1381
1382	return 1;
1383}
1384
1385const char *
1386get_partname(int typ)
1387{
1388	int j;
1389	static char unknown[32];
1390
1391	for (j = 0; part_ids[j].id != -1; j++)
1392		if (part_ids[j].id == typ)
1393			return part_ids[j].name;
1394
1395	snprintf(unknown, sizeof unknown, "Unknown (%d)", typ);
1396	return unknown;
1397}
1398
1399#ifdef BOOTSEL
1400static int
1401validate_and_set_names(mbr_info_t *mbri, const struct mbr_bootsel *src,
1402    uint32_t ext_base)
1403{
1404	size_t i, l;
1405	const unsigned char *p;
1406
1407	/*
1408	 * The 16 bit magic used to detect whether mbr_bootsel is valid
1409	 * or not is pretty week - collisions have been seen in the wild;
1410	 * but maybe it is just foreign tools corruption reminiscents
1411	 * of NetBSD MBRs. Anyway, before accepting a boot menu definition,
1412	 * make sure it is kinda "sane".
1413	 */
1414
1415	for (i = 0; i < MBR_PART_COUNT; i++) {
1416		/*
1417		 * Make sure the name does not contain controll chars
1418		 * (not using iscntrl due to minimalistic locale support
1419		 * in miniroot environments) and is properly 0-terminated.
1420		 */
1421		for (l = 0, p = (const unsigned char *)&src->mbrbs_nametab[i];
1422		    *p != 0; l++, p++) {
1423			if (l >	MBR_BS_PARTNAMESIZE)
1424				return 0;
1425			if (*p < ' ')	/* hacky 'iscntrl' */
1426				return 0;
1427		}
1428	}
1429
1430	memcpy(&mbri->mbrb, src, sizeof(*src));
1431
1432	if (ext_base == 0)
1433		return mbri->mbrb.mbrbs_defkey - SCAN_1;
1434	return 0;
1435}
1436#endif
1437
1438int
1439read_mbr(const char *disk, mbr_info_t *mbri)
1440{
1441	struct mbr_partition *mbrp;
1442	struct mbr_sector *mbrs = &mbri->mbr;
1443	mbr_info_t *ext = NULL;
1444	char diskpath[MAXPATHLEN];
1445	int fd, i;
1446	uint32_t ext_base = 0, next_ext = 0, ext_size = 0;
1447	int rval = -1;
1448#ifdef BOOTSEL
1449	mbr_info_t *ombri = mbri;
1450	int bootkey = 0;
1451#endif
1452
1453	/*
1454	 * Fake up a likely 'bios sectors per track' for any extended
1455	 * partition headers we might have to produce.
1456	 */
1457	if (bsec == 0)
1458		bsec = dlsec;
1459	ptn_0_offset = bsec;
1460	/* use 1MB default offset on large disks as fdisk(8) */
1461	if (dlsize > 2048 * 1024 * 128)
1462		ptn_0_offset = 2048;
1463
1464	memset(mbri, 0, sizeof *mbri);
1465
1466	/* Open the disk. */
1467	fd = opendisk(disk, O_RDONLY, diskpath, sizeof(diskpath), 0);
1468	if (fd < 0)
1469		goto bad_mbr;
1470
1471	for (;;) {
1472		if (pread(fd, mbrs, sizeof *mbrs,
1473		    (ext_base + next_ext) * (off_t)MBR_SECSIZE) - sizeof *mbrs != 0)
1474			break;
1475
1476		if (!valid_mbr(mbrs))
1477			break;
1478
1479		mbrp = &mbrs->mbr_parts[0];
1480		if (ext_base == 0) {
1481			get_ptn_alignment(mbrp);	/* get ptn_0_offset */
1482		} else {
1483			/* sanity check extended chain */
1484			if (MBR_IS_EXTENDED(mbrp[0].mbrp_type))
1485				break;
1486			if (mbrp[1].mbrp_type != 0 &&
1487			    !MBR_IS_EXTENDED(mbrp[1].mbrp_type))
1488				break;
1489			if (mbrp[2].mbrp_type != 0 || mbrp[3].mbrp_type != 0)
1490				break;
1491			/* Looks ok, link into extended chain */
1492			mbri->extended = ext;
1493			ext->prev_ext = next_ext != 0 ? mbri : NULL;
1494			ext->extended = NULL;
1495			mbri = ext;
1496			ext = NULL;
1497		}
1498#if BOOTSEL
1499		if (mbrs->mbr_bootsel_magic == htole16(MBR_MAGIC)) {
1500			/* old bootsel, grab bootsel info */
1501			bootkey = validate_and_set_names(mbri,
1502				(struct mbr_bootsel *)
1503				((uint8_t *)mbrs + MBR_BS_OLD_OFFSET),
1504				ext_base);
1505		} else if (mbrs->mbr_bootsel_magic == htole16(MBR_BS_MAGIC)) {
1506			/* new location */
1507			bootkey = validate_and_set_names(mbri,
1508			    &mbrs->mbr_bootsel, ext_base);
1509		}
1510		/* Save original flags for mbr code update tests */
1511		mbri->oflags = mbri->mbrb.mbrbs_flags;
1512#endif
1513		mbri->sector = next_ext + ext_base;
1514		next_ext = 0;
1515		rval = 0;
1516		for (i = 0; i < MBR_PART_COUNT; mbrp++, i++) {
1517			if (mbrp->mbrp_type == 0) {
1518				/* type is unused, discard scum */
1519				memset(mbrp, 0, sizeof *mbrp);
1520				continue;
1521			}
1522			mbrp->mbrp_start = le32toh(mbrp->mbrp_start);
1523			mbrp->mbrp_size = le32toh(mbrp->mbrp_size);
1524			if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
1525				next_ext = mbrp->mbrp_start;
1526				if (ext_base == 0)
1527					ext_size = mbrp->mbrp_size;
1528			} else {
1529				mbri->last_mounted[i] = strdup(get_last_mounted(
1530					fd, mbri->sector + mbrp->mbrp_start, NULL));
1531#if BOOTSEL
1532				if (ombri->install == 0 &&
1533				    strcmp(mbri->last_mounted[i], "/") == 0)
1534					ombri->install = mbri->sector +
1535							mbrp->mbrp_start;
1536#endif
1537			}
1538#if BOOTSEL
1539			if (mbri->mbrb.mbrbs_nametab[i][0] != 0
1540			    && bootkey-- == 0)
1541				ombri->bootsec = mbri->sector +
1542							mbrp->mbrp_start;
1543#endif
1544		}
1545
1546		if (ext_base != 0) {
1547			/* Is there a gap before the next partition? */
1548			unsigned int limit = next_ext;
1549			unsigned int base;
1550			if (limit == 0)
1551				limit = ext_size;
1552			mbrp -= MBR_PART_COUNT;
1553			base =mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size;
1554			if (mbrp->mbrp_type != 0 && ext_base + limit != base) {
1555				/* Mock up an extry for the space */
1556				ext = calloc(1, sizeof *ext);
1557				if (!ext)
1558					break;
1559				ext->sector = base;
1560				ext->mbr.mbr_magic = htole16(MBR_MAGIC);
1561				ext->mbr.mbr_parts[1] = mbrp[1];
1562				ext->mbr.mbr_parts[0].mbrp_start = ptn_0_offset;
1563				ext->mbr.mbr_parts[0].mbrp_size =
1564				    ext_base + limit - base - ptn_0_offset;
1565				mbrp[1].mbrp_type = MBR_PTYPE_EXT;
1566				mbrp[1].mbrp_start = base - ext_base;
1567				mbrp[1].mbrp_size = limit - mbrp[1].mbrp_start;
1568				mbri->extended = ext;
1569				ext->prev_ext = mbri;
1570				ext->extended = NULL;
1571				mbri = ext;
1572				ext = NULL;
1573			}
1574		}
1575
1576		if (next_ext == 0 || ext_base + next_ext <= mbri->sector)
1577			break;
1578		if (ext_base == 0) {
1579			ext_base = next_ext;
1580			next_ext = 0;
1581		}
1582		ext = calloc(sizeof *ext, 1);
1583		if (!ext)
1584			break;
1585		mbrs = &ext->mbr;
1586	}
1587
1588    bad_mbr:
1589	free(ext);
1590	if (fd >= 0)
1591		close(fd);
1592	if (rval == -1) {
1593		memset(&mbrs->mbr_parts, 0, sizeof mbrs->mbr_parts);
1594		mbrs->mbr_magic = htole16(MBR_MAGIC);
1595	}
1596	dump_mbr(ombri, "read");
1597	return rval;
1598}
1599
1600int
1601write_mbr(const char *disk, mbr_info_t *mbri, int convert)
1602{
1603	char diskpath[MAXPATHLEN];
1604	int fd, i, ret = 0;
1605	struct mbr_partition *mbrp;
1606	u_int32_t pstart, psize;
1607#ifdef BOOTSEL
1608	struct mbr_sector *mbrs;
1609#endif
1610	struct mbr_sector mbrsec;
1611	mbr_info_t *ext;
1612	uint sector;
1613
1614	/* Open the disk. */
1615	fd = opendisk(disk, O_WRONLY, diskpath, sizeof(diskpath), 0);
1616	if (fd < 0)
1617		return -1;
1618
1619#ifdef BOOTSEL
1620	/*
1621	 * If the main boot code (appears to) contain the netbsd bootcode,
1622	 * copy in all the menu strings and set the default keycode
1623	 * to be that for the default partition.
1624	 * Unfortunately we can't rely on the user having actually updated
1625	 * to the new mbr code :-(
1626	 */
1627	if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_BS_MAGIC)
1628	    || mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC)) {
1629		int8_t key = SCAN_1;
1630		uint offset = MBR_BS_OFFSET;
1631		if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC))
1632			offset = MBR_BS_OLD_OFFSET;
1633		mbri->mbrb.mbrbs_defkey = SCAN_ENTER;
1634		if (mbri->mbrb.mbrbs_timeo == 0)
1635			mbri->mbrb.mbrbs_timeo = 182;	/* 10 seconds */
1636		for (ext = mbri; ext != NULL; ext = ext->extended) {
1637			mbrs = &ext->mbr;
1638			mbrp = &mbrs->mbr_parts[0];
1639			/* Ensure marker is set in each sector */
1640			mbrs->mbr_bootsel_magic = mbri->mbr.mbr_bootsel_magic;
1641			/* and copy in bootsel parameters */
1642			*(struct mbr_bootsel *)((uint8_t *)mbrs + offset) =
1643								    ext->mbrb;
1644			for (i = 0; i < MBR_PART_COUNT; i++) {
1645				if (ext->mbrb.mbrbs_nametab[i][0] == 0)
1646					continue;
1647				if (ext->sector + mbrp->mbrp_start ==
1648								mbri->bootsec)
1649					mbri->mbrb.mbrbs_defkey = key;
1650				key++;
1651			}
1652		}
1653		/* copy main data (again) since we've put the 'key' in */
1654		*(struct mbr_bootsel *)((uint8_t *)&mbri->mbr + offset) =
1655								    mbri->mbrb;
1656	}
1657#endif
1658
1659	for (ext = mbri; ext != NULL; ext = ext->extended) {
1660		sector = ext->sector;
1661		mbrsec = ext->mbr;	/* copy sector */
1662		mbrp = &mbrsec.mbr_parts[0];
1663
1664		if (sector != 0 && ext->extended != NULL
1665		    && ext->extended->mbr.mbr_parts[0].mbrp_type == 0) {
1666			/* We are followed by an empty slot, collapse out */
1667			ext = ext->extended;
1668			/* Make us describe the next non-empty partition */
1669			mbrp[1] = ext->mbr.mbr_parts[1];
1670		}
1671
1672		for (i = 0; i < MBR_PART_COUNT; i++) {
1673			if (mbrp[i].mbrp_start == 0 && mbrp[i].mbrp_size == 0) {
1674				mbrp[i].mbrp_scyl = 0;
1675				mbrp[i].mbrp_shd = 0;
1676				mbrp[i].mbrp_ssect = 0;
1677				mbrp[i].mbrp_ecyl = 0;
1678				mbrp[i].mbrp_ehd = 0;
1679				mbrp[i].mbrp_esect = 0;
1680				continue;
1681			}
1682			pstart = mbrp[i].mbrp_start;
1683			psize = mbrp[i].mbrp_size;
1684			mbrp[i].mbrp_start = htole32(pstart);
1685			mbrp[i].mbrp_size = htole32(psize);
1686			if (convert) {
1687				convert_mbr_chs(bcyl, bhead, bsec,
1688				    &mbrp[i].mbrp_scyl, &mbrp[i].mbrp_shd,
1689				    &mbrp[i].mbrp_ssect, pstart);
1690				convert_mbr_chs(bcyl, bhead, bsec,
1691				    &mbrp[i].mbrp_ecyl, &mbrp[i].mbrp_ehd,
1692				    &mbrp[i].mbrp_esect, pstart + psize - 1);
1693			}
1694		}
1695
1696		mbrsec.mbr_magic = htole16(MBR_MAGIC);
1697		if (pwrite(fd, &mbrsec, sizeof mbrsec,
1698					    sector * (off_t)MBR_SECSIZE) < 0) {
1699			ret = -1;
1700			break;
1701		}
1702	}
1703
1704	(void)close(fd);
1705	return ret;
1706}
1707
1708int
1709valid_mbr(struct mbr_sector *mbrs)
1710{
1711
1712	return (le16toh(mbrs->mbr_magic) == MBR_MAGIC);
1713}
1714
1715static void
1716convert_mbr_chs(int cyl, int head, int sec,
1717		uint8_t *cylp, uint8_t *headp, uint8_t *secp,
1718		uint32_t relsecs)
1719{
1720	unsigned int tcyl, temp, thead, tsec;
1721
1722	temp = head * sec;
1723	tcyl = relsecs / temp;
1724	relsecs -= tcyl * temp;
1725
1726	thead = relsecs / sec;
1727	tsec = relsecs - thead * sec + 1;
1728
1729	if (tcyl > MAXCYL)
1730		tcyl = MAXCYL;
1731
1732	*cylp = MBR_PUT_LSCYL(tcyl);
1733	*headp = thead;
1734	*secp = MBR_PUT_MSCYLANDSEC(tcyl, tsec);
1735}
1736
1737/*
1738 * This function is ONLY to be used as a last resort to provide a
1739 * hint for the user. Ports should provide a more reliable way
1740 * of getting the BIOS geometry. The i386 code, for example,
1741 * uses the BIOS geometry as passed on from the bootblocks,
1742 * and only uses this as a hint to the user when that information
1743 * is not present, or a match could not be made with a NetBSD
1744 * device.
1745 */
1746
1747int
1748guess_biosgeom_from_mbr(mbr_info_t *mbri, int *cyl, int *head, daddr_t *sec)
1749{
1750	struct mbr_sector *mbrs = &mbri->mbr;
1751	struct mbr_partition *parts = &mbrs->mbr_parts[0];
1752	int xcylinders, xheads, i, j;
1753	daddr_t xsectors;
1754	int c1, h1, s1, c2, h2, s2;
1755	daddr_t a1, a2;
1756	uint64_t num, denom;
1757
1758	/*
1759	 * The physical parameters may be invalid as bios geometry.
1760	 * If we cannot determine the actual bios geometry, we are
1761	 * better off picking a likely 'faked' geometry than leaving
1762	 * the invalid physical one.
1763	 */
1764
1765	xcylinders = dlcyl;
1766	xheads = dlhead;
1767	xsectors = dlsec;
1768	if (xcylinders > MAXCYL || xheads > MAXHEAD || xsectors > MAXSECTOR) {
1769		xsectors = MAXSECTOR;
1770		xheads = MAXHEAD;
1771		xcylinders = dlsize / (MAXSECTOR * MAXHEAD);
1772		if (xcylinders > MAXCYL)
1773			xcylinders = MAXCYL;
1774	}
1775	*cyl = xcylinders;
1776	*head = xheads;
1777	*sec = xsectors;
1778
1779	xheads = -1;
1780
1781	/* Try to deduce the number of heads from two different mappings. */
1782	for (i = 0; i < MBR_PART_COUNT * 2 - 1; i++) {
1783		if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
1784			continue;
1785		a1 -= s1;
1786		for (j = i + 1; j < MBR_PART_COUNT * 2; j++) {
1787			if (get_mapping(parts, j, &c2, &h2, &s2, &a2) < 0)
1788				continue;
1789			a2 -= s2;
1790			num = (uint64_t)h1 * a2 - (quad_t)h2 * a1;
1791			denom = (uint64_t)c2 * a1 - (quad_t)c1 * a2;
1792			if (num != 0 && denom != 0 && num % denom == 0) {
1793				xheads = (int)(num / denom);
1794				xsectors = a1 / (c1 * xheads + h1);
1795				break;
1796			}
1797		}
1798		if (xheads != -1)
1799			break;
1800	}
1801
1802	if (xheads == -1)
1803		return -1;
1804
1805	/*
1806	 * Estimate the number of cylinders.
1807	 * XXX relies on get_disks having been called.
1808	 */
1809	xcylinders = dlsize / xheads / xsectors;
1810	if (dlsize != xcylinders * xheads * xsectors)
1811		xcylinders++;
1812
1813	/*
1814	 * Now verify consistency with each of the partition table entries.
1815	 * Be willing to shove cylinders up a little bit to make things work,
1816	 * but translation mismatches are fatal.
1817	 */
1818	for (i = 0; i < MBR_PART_COUNT * 2; i++) {
1819		if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
1820			continue;
1821		if (c1 >= MAXCYL - 1)
1822			/* Ignore anything that is near the CHS limit */
1823			continue;
1824		if (xsectors * (c1 * xheads + h1) + s1 != a1)
1825			return -1;
1826	}
1827
1828	/*
1829	 * Everything checks out.  Reset the geometry to use for further
1830	 * calculations.
1831	 */
1832	*cyl = MIN(xcylinders, MAXCYL);
1833	*head = xheads;
1834	*sec = xsectors;
1835	return 0;
1836}
1837
1838static int
1839get_mapping(struct mbr_partition *parts, int i,
1840	    int *cylinder, int *head, int *sector, daddr_t *absolute)
1841{
1842	struct mbr_partition *apart = &parts[i / 2];
1843
1844	if (apart->mbrp_type == 0)
1845		return -1;
1846	if (i % 2 == 0) {
1847		*cylinder = MBR_PCYL(apart->mbrp_scyl, apart->mbrp_ssect);
1848		*head = apart->mbrp_shd;
1849		*sector = MBR_PSECT(apart->mbrp_ssect) - 1;
1850		*absolute = le32toh(apart->mbrp_start);
1851	} else {
1852		*cylinder = MBR_PCYL(apart->mbrp_ecyl, apart->mbrp_esect);
1853		*head = apart->mbrp_ehd;
1854		*sector = MBR_PSECT(apart->mbrp_esect) - 1;
1855		*absolute = le32toh(apart->mbrp_start)
1856			+ le32toh(apart->mbrp_size) - 1;
1857	}
1858	/* Sanity check the data against max values */
1859	if ((((*cylinder * MAXHEAD) + *head) * (uint32_t)MAXSECTOR + *sector) < *absolute)
1860		/* cannot be a CHS mapping */
1861		return -1;
1862
1863	return 0;
1864}
1865
1866/*
1867 * Determine partition boundary alignment as fdisk(8) does.
1868 */
1869static void
1870get_ptn_alignment(struct mbr_partition *mbrp0)
1871{
1872	uint32_t ptn_0_base, ptn_0_limit;
1873
1874	/* Default to using 'traditional' cylinder alignment */
1875	ptn_alignment = bhead * bsec;
1876	ptn_0_offset = bsec;
1877
1878	if (mbrp0->mbrp_type != 0) {
1879		/* Try to copy offset of first partition */
1880		ptn_0_base = le32toh(mbrp0->mbrp_start);
1881		ptn_0_limit = ptn_0_base + le32toh(mbrp0->mbrp_size);
1882		if (!(ptn_0_limit & 2047)) {
1883			/* Partition ends on a 1MB boundary, align to 1MB */
1884			ptn_alignment = 2048;
1885			if (ptn_0_base <= 2048
1886			    && !(ptn_0_base & (ptn_0_base - 1))) {
1887				/* ptn_base is a power of 2, use it */
1888				ptn_0_offset = ptn_0_base;
1889			}
1890		}
1891	} else {
1892		/* Use 1MB offset for large (>128GB) disks */
1893		if (dlsize > 2048 * 1024 * 128) {
1894			ptn_alignment = 2048;
1895			ptn_0_offset = 2048;
1896		}
1897	}
1898}
1899