1/*	$NetBSD$ */
2
3/*
4 * Mach Operating System
5 * Copyright (c) 1992 Carnegie Mellon University
6 * All Rights Reserved.
7 *
8 * Permission to use, copy, modify and distribute this software and its
9 * documentation is hereby granted, provided that both the copyright
10 * notice and this permission notice appear in all copies of the
11 * software, derivative works or modified versions, and any portions
12 * thereof, and that both notices appear in supporting documentation.
13 *
14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
15 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17 *
18 * Carnegie Mellon requests users of this software to return to
19 *
20 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
21 *  School of Computer Science
22 *  Carnegie Mellon University
23 *  Pittsburgh PA 15213-3890
24 *
25 * any improvements or extensions that they make and grant Carnegie Mellon
26 * the rights to redistribute these changes.
27 */
28
29/*
30 * 14-Dec-89  Robert Baron (rvb) at Carnegie-Mellon University
31 *	Copyright (c) 1989	Robert. V. Baron
32 *	Created.
33 */
34
35#if HAVE_NBTOOL_CONFIG_H
36#include "nbtool_config.h"
37#endif
38
39#include <sys/cdefs.h>
40
41#ifndef lint
42__RCSID("$NetBSD$");
43#endif /* not lint */
44
45#define MBRPTYPENAMES
46#include <sys/types.h>
47#include <sys/param.h>
48#include <sys/stat.h>
49#include <ctype.h>
50#include <err.h>
51#include <errno.h>
52#include <fcntl.h>
53#include <paths.h>
54#include <stdarg.h>
55#include <stddef.h>
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
59#include <unistd.h>
60#include <vis.h>
61
62#if !HAVE_NBTOOL_CONFIG_H
63#include <sys/disklabel.h>
64#include <sys/disklabel_gpt.h>
65#include <sys/bootblock.h>
66#include <sys/ioctl.h>
67#include <sys/sysctl.h>
68#include <disktab.h>
69#include <util.h>
70#include <zlib.h>
71#else
72#include <nbinclude/sys/disklabel.h>
73#include <nbinclude/sys/disklabel_gpt.h>
74#include <nbinclude/sys/bootblock.h>
75#include "../../include/disktab.h"
76/* We enforce -F, so none of these possibly undefined items can be needed */
77#define opendisk(path, fl, buf, buflen, cooked) (-1)
78#ifndef DIOCGDEFLABEL
79#define DIOCGDEFLABEL 0
80#endif
81#ifndef DIOCGDINFO
82#define DIOCGDINFO 0
83#endif
84#ifndef DIOCWLABEL
85#define DIOCWLABEL 0
86#endif
87#endif /* HAVE_NBTOOL_CONFIG_H */
88
89#ifndef	DEFAULT_BOOTDIR
90#define	DEFAULT_BOOTDIR		"/usr/mdec"
91#endif
92
93#define	LE_MBR_MAGIC		htole16(MBR_MAGIC)
94#define	LE_MBR_BS_MAGIC		htole16(MBR_BS_MAGIC)
95
96#ifdef BOOTSEL
97
98#define	DEFAULT_BOOTCODE	"mbr"
99#define	DEFAULT_BOOTSELCODE	"mbr_bootsel"
100#define	DEFAULT_BOOTEXTCODE	"mbr_ext"
101
102/* Scan values for the various keys we use, as returned by the BIOS */
103#define	SCAN_ENTER	0x1c
104#define	SCAN_F1		0x3b
105#define	SCAN_1		0x2
106
107
108#define	MAX_BIOS_DISKS	16	/* Going beyond F12 is hard though! */
109
110/* We same the dflt 'boot partition' as a disk block, with some magic values. */
111#define DEFAULT_ACTIVE	(~(daddr_t)0)
112#define	DEFAULT_DISK(n)	(DEFAULT_ACTIVE - MAX_BIOS_DISKS + (n))
113
114#endif
115
116#define GPT_TYPE(offs) ((offs) == GPT_HDR_BLKNO ?  "primary" : "secondary")
117
118#ifndef PRIdaddr
119#define PRIdaddr PRId64
120#endif
121
122#ifndef _PATH_DEFDISK
123#define _PATH_DEFDISK	"/dev/rwd0d"
124#endif
125
126struct {
127	struct mbr_sector *ptn;		/* array of pbrs */
128	daddr_t		base;		/* first sector of ext. ptn */
129	daddr_t		limit;		/* last sector of ext. ptn */
130	int		num_ptn;	/* number of contained partitions */
131	int		ptn_id;		/* entry in mbr */
132	int		is_corrupt;	/* 1 if extended chain illegal */
133} ext;
134
135#define LBUF 100
136static char lbuf[LBUF];
137
138static const char *disk = _PATH_DEFDISK;
139
140static struct disklabel disklabel;		/* disk parameters */
141
142static struct mbr_sector mboot;
143
144static const char *boot_dir = DEFAULT_BOOTDIR;
145static char *boot_path = NULL;			/* name of file we actually opened */
146
147#ifdef BOOTSEL
148#define BOOTSEL_OPTIONS	"B"
149#else
150#define BOOTSEL_OPTIONS
151#define change_part(e, p, id, st, sz, bm) change__part(e, p, id, st, sz)
152#endif
153#define OPTIONS	BOOTSEL_OPTIONS "0123FSafiIluvA:b:c:E:r:s:w:"
154
155/*
156 * Disk geometry and partition alignment.
157 *
158 * Modern disks do not have a fixed geomery and will always give a 'faked'
159 * geometry that matches the ATA standard - max 16 heads and 256 sec/track.
160 * The ATA geometry allows access to 2^28 sectors (as does LBA mode).
161 *
162 * The BIOS calls originally used an 8bit register for cylinder, head and
163 * sector. Later 2 bits were stolen from the sector number and added to
164 * cylinder number. The BIOS will translate this faked geometry either to
165 * the geometry reported by the disk, or do LBA reads (possibly LBA48).
166 * BIOS CHS reads have all sorts of limits, but 2^24 is absolute.
167 * For historic reasons the BIOS geometry is the called the dos geometry!
168 *
169 * If you know the disks real geometry it is usually worth aligning
170 * disk partitions to cylinder boundaries (certainly traditional!).
171 * For 'mbr' disks this has always been done with the BIOS geometry.
172 * The first track (typically 63 sectors) is reserved because the first
173 * sector is used for boot code. Similarly the data partition in an
174 * extended partition will start one track in. If an extended partition
175 * starts at the beginning of the disk you lose 2 tracks.
176 *
177 * However non-magnetic media in particular has physical sectors that are
178 * not the same size as those reported, so has to do read modify write
179 * sequences for misaligned transfers. The alignment of partitions to
180 * cylinder boundaries makes this happen all the time.
181 *
182 * It is thus sensible to align partitions on a sensible sector boundary.
183 * For instance 1MB (2048 sectors).
184 * Common code can do this by using a geometry with 1 head and 2048
185 * sectors per track.
186 */
187
188/* Disks reported geometry and overall size from device driver */
189static unsigned int cylinders, sectors, heads;
190static daddr_t disksectors;
191#define cylindersectors (heads * sectors)
192
193/* Geometry from the BIOS */
194static unsigned int dos_cylinders;
195static unsigned int dos_heads;
196static unsigned int dos_sectors;
197static daddr_t dos_disksectors;
198#define dos_cylindersectors (dos_heads * dos_sectors)
199#define dos_totalsectors (dos_heads * dos_sectors * dos_cylinders)
200
201#define DOSSECT(s,c)	(((s) & 0x3f) | (((c) >> 2) & 0xc0))
202#define DOSCYL(c)	((c) & 0xff)
203#define SEC_IN_1M (1024 * 1024 / 512)
204#define SEC_TO_MB(sec) ((unsigned int)(((sec) + SEC_IN_1M / 2) / SEC_IN_1M))
205#define SEC_TO_CYL(sec) (((sec) + dos_cylindersectors/2) / dos_cylindersectors)
206
207#define MAXCYL		1024	/* Usual limit is 1023 */
208#define	MAXHEAD		256	/* Usual limit is 255 */
209#define	MAXSECTOR	63
210static int partition = -1;
211
212/* Alignment of partition, and offset if first sector unusable */
213static unsigned int ptn_alignment;	/* default dos_cylindersectors */
214static unsigned int ptn_0_offset;	/* default dos_sectors */
215
216static int fd = -1, wfd = -1, *rfd = &fd;
217static char *disk_file = NULL;
218static char *disk_type = NULL;
219
220static int a_flag;		/* set active partition */
221static int i_flag;		/* init bootcode */
222static int I_flag;		/* ignore errors */
223static int u_flag;		/* update partition data */
224static int v_flag;		/* more verbose */
225static int sh_flag;		/* Output data as shell defines */
226static int f_flag;		/* force --not interactive */
227static int s_flag;		/* set id,offset,size */
228static int b_flag;		/* Set cyl, heads, secs (as c/h/s) */
229static int B_flag;		/* Edit/install bootselect code */
230static int E_flag;		/* extended partition number */
231static int b_cyl, b_head, b_sec;  /* b_flag values. */
232
233#if !HAVE_NBTOOL_CONFIG_H
234static int F_flag = 0;
235#else
236/* Tool - force 'file' mode to avoid unsupported functions and ioctls */
237static int F_flag = 1;
238#endif
239
240static struct gpt_hdr gpt1, gpt2;	/* GUID partition tables */
241
242static struct mbr_sector bootcode[8192 / sizeof (struct mbr_sector)];
243static int bootsize;		/* actual size of bootcode */
244static int boot_installed;	/* 1 if we've copied code into the mbr */
245
246#if defined(USE_DISKLIST)
247#include <machine/cpu.h>
248static struct disklist *dl;
249#endif
250
251
252#define KNOWN_SYSIDS	(sizeof(mbr_ptypes)/sizeof(mbr_ptypes[0]))
253
254__dead static void	usage(void);
255static void	print_s0(int);
256static void	print_part(struct mbr_sector *, int, daddr_t);
257static void	print_mbr_partition(struct mbr_sector *, int, daddr_t, daddr_t, int);
258static void	print_pbr(daddr_t, int, uint8_t);
259static int	is_all_zero(const unsigned char *, size_t);
260static void	printvis(int, const char *, const char *, size_t);
261static int	read_boot(const char *, void *, size_t, int);
262static void	init_sector0(int);
263static void	intuit_translated_geometry(void);
264static void	get_bios_geometry(void);
265static void	get_extended_ptn(void);
266static void	get_ptn_alignmemt(void);
267#if defined(USE_DISKLIST)
268static void	get_diskname(const char *, char *, size_t);
269#endif
270static int	change_part(int, int, int, daddr_t, daddr_t, char *);
271static void	print_geometry(void);
272static int	first_active(void);
273static void	change_active(int);
274static void	change_bios_geometry(void);
275static void	dos(int, unsigned char *, unsigned char *, unsigned char *);
276static int	open_disk(int);
277static int	read_disk(daddr_t, void *);
278static int	write_disk(daddr_t, void *);
279static int	get_params(void);
280static int	read_s0(daddr_t, struct mbr_sector *);
281static int	write_mbr(void);
282static int	read_gpt(daddr_t, struct gpt_hdr *);
283static int	delete_gpt(struct gpt_hdr *);
284static int	yesno(const char *, ...);
285static int64_t	decimal(const char *, int64_t, int, int64_t, int64_t);
286#define DEC_SEC		1		/* asking for a sector number */
287#define	DEC_RND		2		/* round to end of first track */
288#define	DEC_RND_0	4		/* convert 0 to size of a track */
289#define DEC_RND_DOWN	8		/* subtract 1 track */
290#define DEC_RND_DOWN_2	16		/* subtract 2 tracks */
291static int	ptn_id(const char *, int *);
292static int	type_match(const void *, const void *);
293static const char *get_type(int);
294static int	get_mapping(int, unsigned int *, unsigned int *, unsigned int *, unsigned long *);
295#ifdef BOOTSEL
296static daddr_t	configure_bootsel(daddr_t);
297static void	install_bootsel(int);
298static daddr_t	get_default_boot(void);
299static void	set_default_boot(daddr_t);
300static void	string(const char *, int, char *);
301#endif
302
303static void
304initvar_disk(const char **diskp)
305{
306#if !HAVE_NBTOOL_CONFIG_H
307	int mib[2];
308	size_t len;
309	char *root_device;
310
311	mib[0] = CTL_KERN;
312	mib[1] = KERN_ROOT_DEVICE;
313	if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1 ||
314	    (root_device = malloc(len)) == NULL ||
315	    sysctl(mib, 2, root_device, &len, NULL, 0) == -1)
316		return;
317
318	*diskp = root_device;
319#endif /* HAVE_NBTOOL_CONFIG_H */
320}
321
322int
323main(int argc, char *argv[])
324{
325	struct stat sb;
326	int ch;
327	size_t len;
328	char *cp;
329	int n;
330#ifdef BOOTSEL
331	daddr_t default_ptn;		/* start sector of default ptn */
332	char *cbootmenu = 0;
333#endif
334
335	int csysid;	/* For the s_flag. */
336	unsigned int cstart, csize;
337	a_flag = u_flag = sh_flag = f_flag = s_flag = b_flag = 0;
338	i_flag = B_flag = 0;
339	v_flag = 0;
340	E_flag = 0;
341	csysid = cstart = csize = 0;
342	while ((ch = getopt(argc, argv, OPTIONS)) != -1) {
343		switch (ch) {
344		case '0':
345			partition = 0;
346			break;
347		case '1':
348			partition = 1;
349			break;
350		case '2':
351			partition = 2;
352			break;
353		case '3':
354			partition = 3;
355			break;
356		case 'E':	/* Extended partition number */
357			E_flag = 1;
358			partition = strtoul(optarg, &cp, 0);
359			if (*cp || partition < 0)
360				errx(1, "Bad partition number -E %s.", optarg);
361			break;
362#ifdef BOOTSEL
363		case 'B':	/* Bootselect parameters */
364			B_flag = 1;
365			break;
366#endif
367		case 'F':	/* device argument is really a file */
368			F_flag = 1;
369			break;
370		case 'S':	/* Output as shell variables */
371			sh_flag = 1;
372			break;
373		case 'a':	/* Set active partition */
374			a_flag = 1;
375			break;
376		case 'f':	/* Non interactive */
377			f_flag = 1;
378			break;
379		case 'i':	/* Always update bootcode */
380			i_flag = 1;
381			break;
382		case 'I':	/* Ignore errors */
383			I_flag = 1;
384			break;
385		case 'l':	/* List known partition types */
386			for (len = 0; len < KNOWN_SYSIDS; len++)
387				printf("%03d %s\n", mbr_ptypes[len].id,
388				    mbr_ptypes[len].name);
389			return 0;
390		case 'u':	/* Update partition details */
391			u_flag = 1;
392			break;
393		case 'v':	/* Be verbose */
394			v_flag++;
395			break;
396		case 's':	/* Partition details */
397			s_flag = 1;
398			if (sscanf(optarg, "%d/%u/%u%n", &csysid, &cstart,
399			    &csize, &n) == 3) {
400				if (optarg[n] == 0)
401					break;
402#ifdef BOOTSEL
403				if (optarg[n] == '/') {
404					cbootmenu = optarg + n + 1;
405					break;
406				}
407#endif
408			}
409			errx(1, "Bad argument to the -s flag.");
410			break;
411		case 'b':	/* BIOS geometry */
412			b_flag = 1;
413			if (sscanf(optarg, "%d/%d/%d%n", &b_cyl, &b_head,
414			    &b_sec, &n) != 3 || optarg[n] != 0)
415				errx(1, "Bad argument to the -b flag.");
416			if (b_cyl > MAXCYL)
417				b_cyl = MAXCYL;
418			break;
419		case 'A':	/* Partition alignment[/offset] */
420			if (sscanf(optarg, "%u%n/%u%n", &ptn_alignment,
421				    &n, &ptn_0_offset, &n) < 1
422			    || optarg[n] != 0
423			    || ptn_0_offset > ptn_alignment)
424				errx(1, "Bad argument to the -A flag.");
425			if (ptn_0_offset == 0)
426				ptn_0_offset = ptn_alignment;
427			break;
428		case 'c':	/* file/directory containing boot code */
429			if (strchr(optarg, '/') != NULL &&
430			    stat(optarg, &sb) == 0 &&
431			    (sb.st_mode & S_IFMT) == S_IFDIR) {
432				boot_dir = optarg;
433				break;
434			}
435			bootsize = read_boot(optarg, bootcode,
436						sizeof bootcode, 1);
437			i_flag = 1;
438			break;
439		case 'r':	/* read data from disk_file (not raw disk) */
440			rfd = &wfd;
441			/* FALLTHROUGH */
442		case 'w':	/* write data to disk_file */
443			disk_file = optarg;
444			break;
445		case 't':
446			if (setdisktab(optarg) == -1)
447				errx(EXIT_FAILURE, "bad disktab");
448			break;
449		case 'T':
450			disk_type = optarg;
451			break;
452		default:
453			usage();
454		}
455	}
456	argc -= optind;
457	argv += optind;
458
459	if (disk_type != NULL && getdiskbyname(disk_type) == NULL)
460		errx(EXIT_FAILURE, "bad disktype");
461
462	if (sh_flag && (a_flag || i_flag || u_flag || f_flag || s_flag))
463		usage();
464
465	if (B_flag && f_flag) {
466		warnx("Bootselector may only be configured interactively");
467		usage();
468	}
469
470	if (f_flag && u_flag && !s_flag) {
471		warnx("Partition data not specified");
472		usage();
473	}
474
475	if (s_flag && partition == -1) {
476		warnx("-s flag requires a partition selected.");
477		usage();
478	}
479
480	if (argc > 1)
481		usage();
482
483	if (argc > 0)
484		disk = argv[0];
485	else if (!F_flag) {
486		/* Default to boot device */
487		initvar_disk(&disk);
488	}
489
490	if (!F_flag && stat(disk, &sb) == 0 && S_ISREG(sb.st_mode))
491		F_flag = 1;
492
493	if (open_disk(B_flag || a_flag || i_flag || u_flag) < 0)
494		exit(1);
495
496	if (read_s0(0, &mboot))
497		/* must have been a blank disk */
498		init_sector0(1);
499
500	read_gpt(GPT_HDR_BLKNO, &gpt1);
501	read_gpt(disksectors - 1, &gpt2);
502
503	if (b_flag) {
504		dos_cylinders = b_cyl;
505		dos_heads = b_head;
506		dos_sectors = b_sec;
507	} else {
508		get_bios_geometry();
509	}
510
511	if (ptn_alignment == 0)
512		get_ptn_alignmemt();
513
514	get_extended_ptn();
515
516#ifdef BOOTSEL
517	default_ptn = get_default_boot();
518#endif
519
520	if (E_flag && !u_flag && partition >= ext.num_ptn)
521		errx(1, "Extended partition %d is not defined.", partition);
522
523	/* Do the update stuff! */
524	if (u_flag) {
525		if (!f_flag && !b_flag)
526			change_bios_geometry();
527
528		if (s_flag)
529			change_part(E_flag, partition, csysid, cstart, csize,
530				cbootmenu);
531		else {
532			int part = partition, chg_ext = E_flag, prompt = 1;
533			do {
534				if (prompt) {
535					printf("\n");
536					print_s0(partition);
537				}
538				if (partition == -1)
539					part = ptn_id(
540				    "Which partition do you want to change?",
541							&chg_ext);
542				if (part < 0)
543					break;
544				prompt = change_part(chg_ext, part, 0, 0, 0, 0);
545			} while (partition == -1);
546		}
547	} else {
548		if (!i_flag && !B_flag) {
549			print_geometry();
550			print_s0(partition);
551		}
552	}
553
554	if (a_flag && !E_flag)
555		change_active(partition);
556
557#ifdef BOOTSEL
558	if (B_flag || u_flag || i_flag)
559		/* Ensure the mbr code supports this configuration */
560		install_bootsel(0);
561	if (B_flag)
562		default_ptn = configure_bootsel(default_ptn);
563	set_default_boot(default_ptn);
564#else
565	if (i_flag)
566		init_sector0(0);
567#endif
568
569	if (u_flag || a_flag || i_flag || B_flag) {
570		if (!f_flag) {
571			printf("\nWe haven't written the MBR back to disk "
572			       "yet.  This is your last chance.\n");
573			if (u_flag)
574				print_s0(-1);
575			if (gpt1.hdr_size != 0 || gpt2.hdr_size != 0)
576				printf("\nWARNING: The disk is carrying "
577				       "GUID Partition Tables.\n"
578				       "         If you continue, "
579				       "GPT headers will be deleted.\n\n");
580			if (yesno("Should we write new partition table?")) {
581				delete_gpt(&gpt1);
582				delete_gpt(&gpt2);
583				write_mbr();
584			}
585		} else {
586			if (delete_gpt(&gpt1) > 0)
587				warnx("Primary GPT header was deleted");
588			if (delete_gpt(&gpt2) > 0)
589				warnx("Secondary GPT header was deleted");
590			write_mbr();
591		}
592	}
593
594	exit(0);
595}
596
597static void
598usage(void)
599{
600	int indent = 7 + (int)strlen(getprogname()) + 1;
601
602	(void)fprintf(stderr, "usage: %s [-aBFfIilSuv] "
603		"[-A ptn_alignment[/ptn_0_offset]] \\\n"
604		"%*s[-b cylinders/heads/sectors] \\\n"
605		"%*s[-0123 | -E num "
606		"[-s id/start/size[/bootmenu]]] \\\n"
607		"%*s[-t disktab] [-T disktype] \\\n"
608		"%*s[-c bootcode] "
609		"[-r|-w file] [device]\n"
610		"\t-a change active partition\n"
611		"\t-f force - not interactive\n"
612		"\t-i initialise MBR code\n"
613		"\t-I ignore errors about no space or overlapping partitions\n"
614		"\t-l list partition types\n"
615		"\t-u update partition data\n"
616		"\t-v verbose output, -v -v more verbose still\n"
617		"\t-B update bootselect options\n"
618		"\t-F treat device as a regular file\n"
619		"\t-S output as shell defines\n"
620		"\t-r and -w access 'file' for non-destructive testing\n",
621		getprogname(), indent, "", indent, "", indent, "", indent, "");
622	exit(1);
623}
624
625static daddr_t
626ext_offset(int part)
627{
628	daddr_t offset = ext.base;
629
630	if (part != 0)
631		offset += le32toh(ext.ptn[part - 1].mbr_parts[1].mbrp_start);
632	return offset;
633}
634
635static void
636print_s0(int which)
637{
638	int part;
639
640	if (which == -1) {
641		if (!sh_flag)
642			printf("Partition table:\n");
643		for (part = 0; part < MBR_PART_COUNT; part++) {
644			if (!sh_flag)
645				printf("%d: ", part);
646			print_part(&mboot, part, 0);
647		}
648		if (!sh_flag) {
649			if (ext.is_corrupt)
650				printf("Extended partition table is corrupt\n");
651			else
652				if (ext.num_ptn != 0)
653					printf("Extended partition table:\n");
654		}
655		for (part = 0; part < ext.num_ptn; part++) {
656			if (!sh_flag)
657				printf("E%d: ", part);
658			print_part(&ext.ptn[part], 0, ext_offset(part));
659			if (!sh_flag && v_flag >= 2) {
660				printf("link: ");
661				print_mbr_partition(&ext.ptn[part], 1,
662						ext_offset(part), ext.base, 0);
663			}
664		}
665#ifdef BOOTSEL
666		if (!sh_flag && mboot.mbr_bootsel_magic == LE_MBR_BS_MAGIC) {
667			int tmo;
668
669			printf("Bootselector ");
670			if (mboot.mbr_bootsel.mbrbs_flags & MBR_BS_ACTIVE) {
671				printf("enabled");
672				tmo = le16toh(mboot.mbr_bootsel.mbrbs_timeo);
673				if (tmo == 0xffff)
674					printf(", infinite timeout");
675				else
676					printf(", timeout %d seconds",
677						    (10 * tmo + 9) / 182);
678			} else
679				printf("disabled");
680			printf(".\n");
681		}
682#endif
683		if (!sh_flag) {
684			int active = first_active();
685			if (active == MBR_PART_COUNT)
686				printf("No active partition.\n");
687			else
688				printf("First active partition: %d\n", active);
689		}
690		if (!sh_flag && mboot.mbr_dsn != 0)
691			printf("Drive serial number: %"PRIu32" (0x%08x)\n",
692			    le32toh(mboot.mbr_dsn),
693			    le32toh(mboot.mbr_dsn));
694		return;
695	}
696
697	if (E_flag) {
698		if (!sh_flag)
699			printf("Extended partition E%d:\n", which);
700		if (which > ext.num_ptn)
701			printf("Undefined\n");
702		else
703			print_part(&ext.ptn[which], 0, ext_offset(which));
704	} else {
705		if (!sh_flag)
706			printf("Partition %d:\n", which);
707		print_part(&mboot, which, 0);
708	}
709}
710
711static void
712print_part(struct mbr_sector *boot, int part, daddr_t offset)
713{
714	struct mbr_partition *partp;
715	const char *e;
716
717	if (!sh_flag) {
718		print_mbr_partition(boot, part, offset, 0, 0);
719		return;
720	}
721
722	partp = &boot->mbr_parts[part];
723	if (boot != &mboot) {
724		part = boot - ext.ptn;
725		e = "E";
726	} else
727		e = "";
728
729	if (partp->mbrp_type == 0) {
730		printf("PART%s%dSIZE=0\n", e, part);
731		return;
732	}
733
734	printf("PART%s%dID=%d\n", e, part, partp->mbrp_type);
735	printf("PART%s%dSIZE=%u\n", e, part, le32toh(partp->mbrp_size));
736	printf("PART%s%dSTART=%"PRIdaddr"\n", e, part,
737	    offset + le32toh(partp->mbrp_start));
738	printf("PART%s%dFLAG=0x%x\n", e, part, partp->mbrp_flag);
739	printf("PART%s%dBCYL=%d\n", e, part,
740	    MBR_PCYL(partp->mbrp_scyl, partp->mbrp_ssect));
741	printf("PART%s%dBHEAD=%d\n", e, part, partp->mbrp_shd);
742	printf("PART%s%dBSEC=%d\n", e, part, MBR_PSECT(partp->mbrp_ssect));
743	printf("PART%s%dECYL=%d\n", e, part,
744	    MBR_PCYL(partp->mbrp_ecyl, partp->mbrp_esect));
745	printf("PART%s%dEHEAD=%d\n", e, part, partp->mbrp_ehd);
746	printf("PART%s%dESEC=%d\n", e, part, MBR_PSECT(partp->mbrp_esect));
747}
748
749static void
750pr_cyls(daddr_t sector, int is_end)
751{
752	unsigned long cyl, head, sect;
753	cyl = sector / dos_cylindersectors;
754	sect = sector - cyl * dos_cylindersectors;
755	head = sect / dos_sectors;
756	sect -= head * dos_sectors;
757
758	printf("%lu", cyl);
759
760	if (is_end) {
761		if (head == dos_heads - 1 && sect == dos_sectors - 1)
762			return;
763	} else {
764		if (head == 0 && sect == 0)
765			return;
766	}
767
768	printf("/%lu/%lu", head, sect + 1);
769}
770
771static void
772print_mbr_partition(struct mbr_sector *boot, int part,
773    daddr_t offset, daddr_t exoffset, int indent)
774{
775	daddr_t	start;
776	daddr_t	size;
777	struct mbr_partition *partp = &boot->mbr_parts[part];
778	struct mbr_sector eboot;
779	int p;
780	static int dumped = 0;
781
782	if (partp->mbrp_type == 0 && v_flag < 2) {
783		printf("<UNUSED>\n");
784		return;
785	}
786
787	start = le32toh(partp->mbrp_start);
788	size = le32toh(partp->mbrp_size);
789	if (MBR_IS_EXTENDED(partp->mbrp_type))
790		start += exoffset;
791	else
792		start += offset;
793
794	printf("%s (sysid %d)\n", get_type(partp->mbrp_type), partp->mbrp_type);
795#ifdef BOOTSEL
796	if (boot->mbr_bootsel_magic == LE_MBR_BS_MAGIC &&
797	    boot->mbr_bootsel.mbrbs_nametab[part][0])
798		printf("%*s    bootmenu: %s\n", indent, "",
799		    boot->mbr_bootsel.mbrbs_nametab[part]);
800#endif
801
802	printf("%*s    start %"PRIdaddr", size %"PRIdaddr,
803	    indent, "", start, size);
804	if (size != 0) {
805		printf(" (%u MB, Cyls ", SEC_TO_MB(size));
806		if (v_flag == 0 && le32toh(partp->mbrp_start) == ptn_0_offset)
807			pr_cyls(start - ptn_0_offset, 0);
808		else
809			pr_cyls(start, 0);
810		printf("-");
811		pr_cyls(start + size - 1, 1);
812		printf(")");
813	}
814
815	switch (partp->mbrp_flag) {
816	case 0:
817		break;
818	case MBR_PFLAG_ACTIVE:
819		printf(", Active");
820		break;
821	default:
822		printf(", flag 0x%x", partp->mbrp_flag);
823		break;
824	}
825	printf("\n");
826
827	if (v_flag) {
828		printf("%*s        beg: cylinder %4d, head %3d, sector %2d\n",
829		    indent, "",
830		    MBR_PCYL(partp->mbrp_scyl, partp->mbrp_ssect),
831		    partp->mbrp_shd, MBR_PSECT(partp->mbrp_ssect));
832		printf("%*s        end: cylinder %4d, head %3d, sector %2d\n",
833		    indent, "",
834		    MBR_PCYL(partp->mbrp_ecyl, partp->mbrp_esect),
835		    partp->mbrp_ehd, MBR_PSECT(partp->mbrp_esect));
836	}
837
838	if (partp->mbrp_type == 0 && start == 0 && v_flag < 3)
839		return;
840
841	if (! MBR_IS_EXTENDED(partp->mbrp_type))
842		print_pbr(start, indent + 8, partp->mbrp_type);
843
844	if (!MBR_IS_EXTENDED(partp->mbrp_type) ||
845	    (v_flag <= 2 && !ext.is_corrupt))
846		return;
847
848	/*
849	 * Recursive dump extended table,
850	 * This is read from the disk - so is wrong during editing.
851	 * Just ensure we only show it once.
852	 */
853	if (dumped)
854		return;
855
856	printf("%*s    Extended partition table:\n", indent, "");
857	indent += 4;
858	if (read_s0(start, &eboot) == -1)
859		return;
860	for (p = 0; p < MBR_PART_COUNT; p++) {
861		printf("%*s%d: ", indent, "", p);
862		print_mbr_partition(&eboot, p, start,
863				    exoffset ? exoffset : start, indent);
864	}
865
866	if (exoffset == 0)
867		dumped = 1;
868}
869
870/* Print a line with a label and a vis-encoded string */
871static void
872printvis(int indent, const char *label, const char *buf, size_t size)
873{
874	char *visbuf;
875
876	if ((visbuf = malloc(size * 4 + 1)) == NULL)
877		err(1, "Malloc failed");
878	strsvisx(visbuf, buf, size, VIS_TAB|VIS_NL|VIS_OCTAL, "\"");
879	printf("%*s%s: \"%s\"\n",
880	    indent, "",
881	    label, visbuf);
882	free(visbuf);
883}
884
885/* Check whether a buffer contains all bytes zero */
886static int
887is_all_zero(const unsigned char *p, size_t size)
888{
889
890	while (size-- > 0) {
891		if (*p++ != 0)
892			return 0;
893	}
894	return 1;
895}
896
897/*
898 * Report on the contents of a PBR sector.
899 *
900 * We first perform several sanity checks.  If vflag >= 2, we report all
901 * failing tests, but for smaller values of v_flag we stop after the
902 * first failing test.  Tests are ordered in an attempt to get the most
903 * useful error message from the first failing test.
904 *
905 * If v_flag >= 2, we also report some decoded values from the PBR.
906 * These results may be meaningless, if the PBR doesn't follow common
907 * conventions.
908 *
909 * Trying to decode anything more than the magic number in the last
910 * two bytes is a layering violation, but it can be very useful in
911 * diagnosing boot failures.
912 */
913static void
914print_pbr(daddr_t sector, int indent, uint8_t part_type)
915{
916	struct mbr_sector pboot;
917	unsigned char *p, *endp;
918	unsigned char val;
919	int ok;
920	int errcount = 0;
921
922#define PBR_ERROR(...)							\
923	do {								\
924		++errcount;						\
925		printf("%*s%s: ", indent, "",				\
926		    (v_flag < 2 ? "PBR is not bootable" : "Not bootable")); \
927		printf(__VA_ARGS__);					\
928		if (v_flag < 2)						\
929			return;						\
930	} while (/*CONSTCOND*/ 0)
931
932	if (v_flag >= 2) {
933		printf("%*sInformation from PBR:\n",
934		    indent, "");
935		indent += 4;
936	}
937
938	if (read_disk(sector, &pboot) == -1) {
939		PBR_ERROR("Sector %"PRIdaddr" is unreadable (%s)\n",
940		    sector, strerror(errno));
941		return;
942	}
943
944	/* all bytes identical? */
945	p = (unsigned char *)&pboot;
946	endp = p + sizeof(pboot);
947	val = *p;
948	ok = 0;
949	for (; p < endp; p++) {
950		if (*p != val) {
951			ok = 1;
952			break;
953		}
954	}
955	if (! ok)
956		PBR_ERROR("All bytes are identical (0x%02x)\n", val);
957
958	if (pboot.mbr_magic != LE_MBR_MAGIC)
959		PBR_ERROR("Bad magic number (0x%04x)\n",
960			le16toh(pboot.mbr_magic));
961
962#if 0
963	/* Some i386 OS might fail this test.  All non-i386 will fail. */
964	if (pboot.mbr_jmpboot[0] != 0xE9
965	    && pboot.mbr_jmpboot[0] != 0xEB) {
966		PBR_ERROR("Does not begin with i386 JMP instruction"
967			" (0x%02x 0x%02x0 0x%02x)\n",
968		    pboot.mbr_jmpboot[0], pboot.mbr_jmpboot[1],
969		    pboot.mbr_jmpboot[2]);
970	}
971#endif
972
973	if (v_flag > 0 && errcount == 0)
974		printf("%*sPBR appears to be bootable\n",
975		    indent, "");
976	if (v_flag < 2)
977		return;
978
979	if (! is_all_zero(pboot.mbr_oemname, sizeof(pboot.mbr_oemname))) {
980		printvis(indent, "OEM name", (char *)pboot.mbr_oemname,
981			sizeof(pboot.mbr_oemname));
982	}
983
984	if (pboot.mbr_bpb.bpb16.bsBootSig == 0x29)
985		printf("%*sBPB FAT16 boot signature found\n",
986		    indent, "");
987	if (pboot.mbr_bpb.bpb32.bsBootSig == 0x29)
988		printf("%*sBPB FAT32 boot signature found\n",
989		    indent, "");
990
991#undef PBR_ERROR
992}
993
994static int
995read_boot(const char *name, void *buf, size_t len, int err_exit)
996{
997	int bfd, ret;
998	struct stat st;
999
1000	if (boot_path != NULL)
1001		free(boot_path);
1002	if (strchr(name, '/') == 0)
1003		asprintf(&boot_path, "%s/%s", boot_dir, name);
1004	else
1005		boot_path = strdup(name);
1006	if (boot_path == NULL)
1007		err(1, "Malloc failed");
1008
1009	if ((bfd = open(boot_path, O_RDONLY)) < 0 || fstat(bfd, &st) == -1) {
1010		warn("%s", boot_path);
1011		goto fail;
1012	}
1013
1014	if (st.st_size > (off_t)len) {
1015		warnx("%s: bootcode too large", boot_path);
1016		goto fail;
1017	}
1018	ret = st.st_size;
1019	if (ret < 0x200) {
1020		warnx("%s: bootcode too small", boot_path);
1021		goto fail;
1022	}
1023	if (read(bfd, buf, len) != ret) {
1024		warn("%s", boot_path);
1025		goto fail;
1026	}
1027
1028	/*
1029	 * Do some sanity checking here
1030	 */
1031	if (((struct mbr_sector *)buf)->mbr_magic != LE_MBR_MAGIC) {
1032		warnx("%s: invalid magic", boot_path);
1033		goto fail;
1034	}
1035
1036	close(bfd);
1037	ret = (ret + 0x1ff) & ~0x1ff;
1038	return ret;
1039
1040    fail:
1041	if (bfd >= 0)
1042		close(bfd);
1043	if (err_exit)
1044		exit(1);
1045	return 0;
1046}
1047
1048static void
1049init_sector0(int zappart)
1050{
1051	int i;
1052	int copy_size = offsetof(struct mbr_sector, mbr_dsn);
1053
1054#ifdef DEFAULT_BOOTCODE
1055	if (bootsize == 0)
1056		bootsize = read_boot(DEFAULT_BOOTCODE, bootcode,
1057			sizeof bootcode, 0);
1058#endif
1059#ifdef BOOTSEL
1060	if (mboot.mbr_bootsel_magic == LE_MBR_BS_MAGIC
1061	    && bootcode[0].mbr_bootsel_magic == LE_MBR_BS_MAGIC)
1062		copy_size = MBR_BS_OFFSET;
1063#endif
1064
1065	if (bootsize != 0) {
1066		boot_installed = 1;
1067		memcpy(&mboot, bootcode, copy_size);
1068		mboot.mbr_bootsel_magic = bootcode[0].mbr_bootsel_magic;
1069	}
1070	mboot.mbr_magic = LE_MBR_MAGIC;
1071
1072	if (!zappart)
1073		return;
1074	for (i = 0; i < MBR_PART_COUNT; i++)
1075		memset(&mboot.mbr_parts[i], 0, sizeof(mboot.mbr_parts[i]));
1076}
1077
1078static void
1079get_extended_ptn(void)
1080{
1081	struct mbr_partition *mp;
1082	struct mbr_sector *boot;
1083	daddr_t offset;
1084	struct mbr_sector *nptn;
1085
1086	/* find first (there should only be one) extended partition */
1087	for (mp = mboot.mbr_parts; !MBR_IS_EXTENDED(mp->mbrp_type); mp++)
1088		if (mp >= &mboot.mbr_parts[MBR_PART_COUNT])
1089			return;
1090
1091	/*
1092	 * The extended partition should be structured as a linked list
1093	 * (even though it appears, at first glance, to be a tree).
1094	 */
1095	ext.base = le32toh(mp->mbrp_start);
1096	ext.limit = ext.base + le32toh(mp->mbrp_size);
1097	ext.ptn_id = mp - mboot.mbr_parts;
1098	for (offset = 0;; offset = le32toh(boot->mbr_parts[1].mbrp_start)) {
1099		nptn = realloc(ext.ptn, (ext.num_ptn + 1) * sizeof *ext.ptn);
1100		if (nptn == NULL)
1101			err(1, "Malloc failed");
1102		ext.ptn = nptn;
1103		boot = ext.ptn + ext.num_ptn;
1104		if (read_s0(offset + ext.base, boot) == -1)
1105			break;
1106		/* expect p0 to be valid and p1 to be another extended ptn */
1107		if (MBR_IS_EXTENDED(boot->mbr_parts[0].mbrp_type))
1108			break;
1109		if (boot->mbr_parts[1].mbrp_type != 0 &&
1110		    !MBR_IS_EXTENDED(boot->mbr_parts[1].mbrp_type))
1111			break;
1112		/* p2 and p3 should be unallocated */
1113		if (boot->mbr_parts[2].mbrp_type != 0 ||
1114		    boot->mbr_parts[3].mbrp_type != 0)
1115			break;
1116		/* data ptn inside extended one */
1117		if (boot->mbr_parts[0].mbrp_type != 0 &&
1118		    offset + le32toh(boot->mbr_parts[0].mbrp_start)
1119		    + le32toh(boot->mbr_parts[0].mbrp_size) > ext.limit)
1120			break;
1121
1122		ext.num_ptn++;
1123
1124		if (boot->mbr_parts[1].mbrp_type == 0)
1125			/* end of extended partition chain */
1126			return;
1127		/* must be in sector order */
1128		if (offset >= le32toh(boot->mbr_parts[1].mbrp_start))
1129			break;
1130	}
1131
1132	warnx("Extended partition table is corrupt\n");
1133	ext.is_corrupt = 1;
1134	ext.num_ptn = 0;
1135}
1136
1137#if defined(USE_DISKLIST)
1138static void
1139get_diskname(const char *fullname, char *diskname, size_t size)
1140{
1141	const char *p, *p2;
1142	size_t len;
1143
1144	p = strrchr(fullname, '/');
1145	if (p == NULL)
1146		p = fullname;
1147	else
1148		p++;
1149
1150	if (*p == 0) {
1151		strlcpy(diskname, fullname, size);
1152		return;
1153	}
1154
1155	if (*p == 'r')
1156		p++;
1157
1158	for (p2 = p; *p2 != 0; p2++)
1159		if (isdigit((unsigned char)*p2))
1160			break;
1161	if (*p2 == 0) {
1162		/* XXX invalid diskname? */
1163		strlcpy(diskname, fullname, size);
1164		return;
1165	}
1166	while (isdigit((unsigned char)*p2))
1167		p2++;
1168
1169	len = p2 - p;
1170	if (len > size) {
1171		/* XXX */
1172		strlcpy(diskname, fullname, size);
1173		return;
1174	}
1175
1176	memcpy(diskname, p, len);
1177	diskname[len] = 0;
1178}
1179#endif
1180
1181static void
1182get_ptn_alignmemt(void)
1183{
1184	struct mbr_partition *partp = &mboot.mbr_parts[0];
1185	uint32_t ptn_0_base, ptn_0_limit;
1186
1187	/* Default to using 'traditional' cylinder alignment */
1188	ptn_alignment = dos_cylindersectors;
1189	ptn_0_offset = dos_sectors;
1190
1191	if (partp->mbrp_type != 0) {
1192		/* Try to copy alignment of first partition */
1193		ptn_0_base = le32toh(partp->mbrp_start);
1194		ptn_0_limit = ptn_0_base + le32toh(partp->mbrp_size);
1195		if (!(ptn_0_limit & 2047)) {
1196			/* Partition ends on a 1MB boundary, align to 1MB */
1197			ptn_alignment = 2048;
1198			if (ptn_0_base <= 2048
1199			    && !(ptn_0_base & (ptn_0_base - 1))) {
1200				/* ptn_base is a power of 2, use it */
1201				ptn_0_offset = ptn_0_base;
1202			}
1203		}
1204	} else {
1205		/* Use 1MB alignment for large disks */
1206		if (disksectors > 2048 * 1024 * 128) {
1207			ptn_alignment = 2048;
1208			ptn_0_offset = 2048;
1209		}
1210	}
1211}
1212
1213static void
1214get_bios_geometry(void)
1215{
1216#if defined(USE_DISKLIST)
1217	int mib[2], i;
1218	size_t len;
1219	struct biosdisk_info *bip;
1220	struct nativedisk_info *nip;
1221	char diskname[8];
1222
1223	mib[0] = CTL_MACHDEP;
1224	mib[1] = CPU_DISKINFO;
1225	if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0) {
1226		goto out;
1227	}
1228	dl = (struct disklist *) malloc(len);
1229	if (dl == NULL)
1230		err(1, "Malloc failed");
1231	if (sysctl(mib, 2, dl, &len, NULL, 0) < 0) {
1232		free(dl);
1233		dl = 0;
1234		goto out;
1235	}
1236
1237	get_diskname(disk, diskname, sizeof diskname);
1238
1239	for (i = 0; i < dl->dl_nnativedisks; i++) {
1240		nip = &dl->dl_nativedisks[i];
1241		if (strcmp(diskname, nip->ni_devname))
1242			continue;
1243		/*
1244		 * XXX listing possible matches is better. This is ok for
1245		 * now because the user has a chance to change it later.
1246		 * Also, if all the disks have the same parameters then we can
1247		 * just use them, we don't need to know which disk is which.
1248		 */
1249		if (nip->ni_nmatches != 0) {
1250			bip = &dl->dl_biosdisks[nip->ni_biosmatches[0]];
1251			dos_cylinders = bip->bi_cyl;
1252			dos_heads = bip->bi_head;
1253			dos_sectors = bip->bi_sec;
1254			if (bip->bi_lbasecs)
1255				dos_disksectors = bip->bi_lbasecs;
1256			return;
1257		}
1258	}
1259 out:
1260#endif
1261	/* Allright, allright, make a stupid guess.. */
1262	intuit_translated_geometry();
1263}
1264
1265#ifdef BOOTSEL
1266static daddr_t
1267get_default_boot(void)
1268{
1269	unsigned int id;
1270	int p;
1271
1272	if (mboot.mbr_bootsel_magic != LE_MBR_BS_MAGIC)
1273		/* default to first active partition */
1274		return DEFAULT_ACTIVE;
1275
1276	id = mboot.mbr_bootsel.mbrbs_defkey;
1277
1278	if (mboot.mbr_bootsel.mbrbs_flags & MBR_BS_ASCII) {
1279		/* Keycode is ascii */
1280		if (id == '\r')
1281		    return DEFAULT_ACTIVE;
1282		/* '1'+ => allocated partition id, 'a'+ => disk 0+ */
1283		if (id >= 'a' && id < 'a' + MAX_BIOS_DISKS)
1284			return DEFAULT_DISK(id - 'a');
1285		id -= '1';
1286	} else {
1287		/* keycode is PS/2 keycode */
1288		if (id == SCAN_ENTER)
1289			return DEFAULT_ACTIVE;
1290		/* 1+ => allocated partition id, F1+ => disk 0+ */
1291		if (id >= SCAN_F1 && id < SCAN_F1 + MAX_BIOS_DISKS)
1292			return DEFAULT_DISK(id - SCAN_F1);
1293		id -= SCAN_1;
1294	}
1295
1296	/* Convert partition index to the invariant start sector number */
1297
1298	for (p = 0; p < MBR_PART_COUNT; p++) {
1299		if (mboot.mbr_parts[p].mbrp_type == 0)
1300			continue;
1301		if (mboot.mbr_bootsel.mbrbs_nametab[p][0] == 0)
1302			continue;
1303		if (id-- == 0)
1304			return le32toh(mboot.mbr_parts[p].mbrp_start);
1305	}
1306
1307	for (p = 0; p < ext.num_ptn; p++) {
1308		if (ext.ptn[p].mbr_parts[0].mbrp_type == 0)
1309			continue;
1310		if (ext.ptn[p].mbr_bootsel.mbrbs_nametab[0][0] == 0)
1311			continue;
1312		if (id-- == 0)
1313			return ext_offset(p)
1314				+ le32toh(ext.ptn[p].mbr_parts[0].mbrp_start);
1315	}
1316
1317	return DEFAULT_ACTIVE;
1318}
1319
1320static void
1321set_default_boot(daddr_t default_ptn)
1322{
1323	int p;
1324	static const unsigned char key_list[] = { SCAN_ENTER, SCAN_F1, SCAN_1,
1325						'\r', 'a', '1' };
1326	const unsigned char *key = key_list;
1327
1328	if (mboot.mbr_bootsel_magic != LE_MBR_BS_MAGIC)
1329		/* sanity */
1330		return;
1331
1332	if (mboot.mbr_bootsel.mbrbs_flags & MBR_BS_ASCII)
1333		/* Use ascii values */
1334		key += 3;
1335
1336	if (default_ptn == DEFAULT_ACTIVE) {
1337		mboot.mbr_bootsel.mbrbs_defkey = key[0];
1338		return;
1339	}
1340
1341	if (default_ptn >= DEFAULT_DISK(0)
1342	    && default_ptn < DEFAULT_DISK(MAX_BIOS_DISKS)) {
1343		mboot.mbr_bootsel.mbrbs_defkey = key[1]
1344		    + default_ptn - DEFAULT_DISK(0);
1345		return;
1346	}
1347
1348	mboot.mbr_bootsel.mbrbs_defkey = key[2];
1349	for (p = 0; p < MBR_PART_COUNT; p++) {
1350		if (mboot.mbr_parts[p].mbrp_type == 0)
1351			continue;
1352		if (mboot.mbr_bootsel.mbrbs_nametab[p][0] == 0)
1353			continue;
1354		if (le32toh(mboot.mbr_parts[p].mbrp_start) == default_ptn)
1355			return;
1356		mboot.mbr_bootsel.mbrbs_defkey++;
1357	}
1358
1359	if (mboot.mbr_bootsel.mbrbs_flags & MBR_BS_EXTLBA) {
1360		for (p = 0; p < ext.num_ptn; p++) {
1361			if (ext.ptn[p].mbr_parts[0].mbrp_type == 0)
1362				continue;
1363			if (ext.ptn[p].mbr_bootsel.mbrbs_nametab[0][0] == 0)
1364				continue;
1365			if (le32toh(ext.ptn[p].mbr_parts[0].mbrp_start) +
1366			    ext_offset(p) == default_ptn)
1367				return;
1368			mboot.mbr_bootsel.mbrbs_defkey++;
1369		}
1370	}
1371
1372	/* Default to first active partition */
1373	mboot.mbr_bootsel.mbrbs_defkey = key[0];
1374}
1375
1376static void
1377install_bootsel(int needed)
1378{
1379	struct mbr_bootsel *mbs = &mboot.mbr_bootsel;
1380	int p;
1381	int ext13 = 0;
1382	const char *code;
1383
1384	needed |= MBR_BS_NEWMBR;	/* need new bootsel code */
1385
1386	/* Work out which boot code we need for this configuration */
1387	for (p = 0; p < MBR_PART_COUNT; p++) {
1388		if (mboot.mbr_parts[p].mbrp_type == 0)
1389			continue;
1390		if (mboot.mbr_bootsel_magic != LE_MBR_BS_MAGIC)
1391			break;
1392		if (mbs->mbrbs_nametab[p][0] == 0)
1393			continue;
1394		needed |= MBR_BS_ACTIVE;
1395		if (le32toh(mboot.mbr_parts[p].mbrp_start) >= dos_totalsectors)
1396			ext13 = MBR_BS_EXTINT13;
1397	}
1398
1399	for (p = 0; p < ext.num_ptn; p++) {
1400		if (ext.ptn[p].mbr_bootsel_magic != LE_MBR_BS_MAGIC)
1401			continue;
1402		if (ext.ptn[p].mbr_parts[0].mbrp_type == 0)
1403			continue;
1404		if (ext.ptn[p].mbr_bootsel.mbrbs_nametab[p][0] == 0)
1405			continue;
1406		needed |= MBR_BS_EXTLBA | MBR_BS_ACTIVE;
1407	}
1408
1409	if (B_flag)
1410		needed |= MBR_BS_ACTIVE;
1411
1412	/* Is the installed code good enough ? */
1413	if (!i_flag && (needed == 0 ||
1414	    (mboot.mbr_bootsel_magic == LE_MBR_BS_MAGIC
1415	    && (mbs->mbrbs_flags & needed) == needed))) {
1416		/* yes - just set flags */
1417		mbs->mbrbs_flags |= ext13;
1418		return;
1419	}
1420
1421	/* ok - we need to replace the bootcode */
1422
1423	if (f_flag && !(i_flag || B_flag)) {
1424		warnx("Installed bootfile doesn't support required options.");
1425		return;
1426	}
1427
1428	if (!f_flag && bootsize == 0 && !i_flag)
1429		/* Output an explanation for the 'update bootcode' prompt. */
1430		printf("\n%s\n",
1431		    "Installed bootfile doesn't support required options.");
1432
1433	/* Were we told a specific file ? (which we have already read) */
1434	/* If so check that it supports what we need. */
1435	if (bootsize != 0 && needed != 0
1436	    && (bootcode[0].mbr_bootsel_magic != LE_MBR_BS_MAGIC
1437	    || ((bootcode[0].mbr_bootsel.mbrbs_flags & needed) != needed))) {
1438		/* No it doesn't... */
1439		if (f_flag)
1440			warnx("Bootfile %s doesn't support "
1441				    "required bootsel options", boot_path );
1442			/* But install it anyway */
1443		else
1444			if (yesno("Bootfile %s doesn't support the required "
1445			    "options,\ninstall default bootfile instead?",
1446			    boot_path))
1447				bootsize = 0;
1448	}
1449
1450	if (bootsize == 0) {
1451		/* Get name of bootfile that supports the required facilities */
1452		code = DEFAULT_BOOTCODE;
1453		if (needed & MBR_BS_ACTIVE)
1454			code = DEFAULT_BOOTSELCODE;
1455#ifdef DEFAULT_BOOTEXTCODE
1456		if (needed & MBR_BS_EXTLBA)
1457			code = DEFAULT_BOOTEXTCODE;
1458#endif
1459
1460		bootsize = read_boot(code, bootcode, sizeof bootcode, 0);
1461		if (bootsize == 0)
1462			/* The old bootcode is better than no bootcode at all */
1463			return;
1464		if ((bootcode[0].mbr_bootsel.mbrbs_flags & needed) != needed)
1465			warnx("Default bootfile %s doesn't support required "
1466				"options.  Got flags 0x%x, wanted 0x%x\n",
1467				boot_path, bootcode[0].mbr_bootsel.mbrbs_flags,
1468				needed);
1469	}
1470
1471	if (!f_flag && !yesno("Update the bootcode from %s?", boot_path))
1472		return;
1473
1474	init_sector0(0);
1475
1476	if (mboot.mbr_bootsel_magic == LE_MBR_BS_MAGIC)
1477		mbs->mbrbs_flags = bootcode[0].mbr_bootsel.mbrbs_flags | ext13;
1478}
1479
1480static daddr_t
1481configure_bootsel(daddr_t default_ptn)
1482{
1483	struct mbr_bootsel *mbs = &mboot.mbr_bootsel;
1484	int i, item, opt;
1485	int tmo;
1486	daddr_t *off;
1487	int num_bios_disks;
1488
1489#if defined(USE_DISKLIST)
1490	if (dl != NULL) {
1491		num_bios_disks = dl->dl_nbiosdisks;
1492		if (num_bios_disks > MAX_BIOS_DISKS)
1493			num_bios_disks = MAX_BIOS_DISKS;
1494	} else
1495#endif
1496		num_bios_disks = MAX_BIOS_DISKS;
1497
1498	printf("\nBoot selector configuration:\n");
1499
1500	/* The timeout value is in ticks, ~18.2 Hz. Avoid using floats.
1501	 * Ticks are nearly 64k/3600 - so our long timers are sligtly out!
1502	 * Newer bootcode always waits for 1 tick, so treats 0xffff
1503	 * as wait forever.
1504	 */
1505	tmo = le16toh(mbs->mbrbs_timeo);
1506	tmo = tmo == 0xffff ? -1 : (10 * tmo + 9) / 182;
1507	tmo = decimal("Timeout value (0 to 3600 seconds, -1 => never)",
1508			tmo, 0, -1, 3600);
1509	mbs->mbrbs_timeo = htole16(tmo == -1 ? 0xffff : (tmo * 182) / 10);
1510
1511	off = calloc(1 + MBR_PART_COUNT + ext.num_ptn + num_bios_disks, sizeof *off);
1512	if (off == NULL)
1513		err(1, "Malloc failed");
1514
1515	printf("Select the default boot option. Options are:\n\n");
1516	item = 0;
1517	opt = 0;
1518	off[opt] = DEFAULT_ACTIVE;
1519	printf("%d: The first active partition\n", opt);
1520	for (i = 0; i < MBR_PART_COUNT; i++) {
1521		if (mboot.mbr_parts[i].mbrp_type == 0)
1522			continue;
1523		if (mbs->mbrbs_nametab[i][0] == 0)
1524			continue;
1525		printf("%d: %s\n", ++opt, &mbs->mbrbs_nametab[i][0]);
1526		off[opt] = le32toh(mboot.mbr_parts[i].mbrp_start);
1527		if (off[opt] == default_ptn)
1528			item = opt;
1529	}
1530	if (mbs->mbrbs_flags & MBR_BS_EXTLBA) {
1531		for (i = 0; i < ext.num_ptn; i++) {
1532			if (ext.ptn[i].mbr_parts[0].mbrp_type == 0)
1533				continue;
1534			if (ext.ptn[i].mbr_bootsel.mbrbs_nametab[0][0] == 0)
1535				continue;
1536			printf("%d: %s\n",
1537			    ++opt, ext.ptn[i].mbr_bootsel.mbrbs_nametab[0]);
1538			off[opt] = ext_offset(i) +
1539			    le32toh(ext.ptn[i].mbr_parts[0].mbrp_start);
1540			if (off[opt] == default_ptn)
1541				item = opt;
1542		}
1543	}
1544	for (i = 0; i < num_bios_disks; i++) {
1545		printf("%d: Harddisk %d\n", ++opt, i);
1546		off[opt] = DEFAULT_DISK(i);
1547		if (DEFAULT_DISK(i) == default_ptn)
1548			item = opt;
1549	}
1550
1551	item = decimal("Default boot option", item, 0, 0, opt);
1552
1553	default_ptn = off[item];
1554	free(off);
1555	return default_ptn;
1556}
1557#endif /* BOOTSEL */
1558
1559
1560/* Prerequisite: the disklabel parameters and master boot record must
1561 *		 have been read (i.e. dos_* and mboot are meaningful).
1562 * Specification: modifies dos_cylinders, dos_heads, dos_sectors, and
1563 *		  dos_cylindersectors to be consistent with what the
1564 *		  partition table is using, if we can find a geometry
1565 *		  which is consistent with all partition table entries.
1566 *		  We may get the number of cylinders slightly wrong (in
1567 *		  the conservative direction).  The idea is to be able
1568 *		  to create a NetBSD partition on a disk we don't know
1569 *		  the translated geometry of.
1570 * This routine is only used for non-x86 systems or when we fail to
1571 * get the BIOS geometry from the kernel.
1572 */
1573static void
1574intuit_translated_geometry(void)
1575{
1576	uint32_t xcylinders;
1577	int xheads = -1, xsectors = -1, i, j;
1578	unsigned int c1, h1, s1, c2, h2, s2;
1579	unsigned long a1, a2;
1580	uint64_t num, denom;
1581
1582	/*
1583	 * The physical parameters may be invalid as bios geometry.
1584	 * If we cannot determine the actual bios geometry, we are
1585	 * better off picking a likely 'faked' geometry than leaving
1586	 * the invalid physical one.
1587	 */
1588
1589	if (dos_cylinders > MAXCYL || dos_heads > MAXHEAD ||
1590	    dos_sectors > MAXSECTOR) {
1591		h1 = MAXHEAD - 1;
1592		c1 = MAXCYL - 1;
1593#if defined(USE_DISKLIST)
1594		if (dl != NULL) {
1595			/* BIOS may use 256 heads or 1024 cylinders */
1596			for (i = 0; i < dl->dl_nbiosdisks; i++) {
1597				if (h1 < (unsigned int)dl->dl_biosdisks[i].bi_head)
1598					h1 = dl->dl_biosdisks[i].bi_head;
1599				if (c1 < (unsigned int)dl->dl_biosdisks[i].bi_cyl)
1600					c1 = dl->dl_biosdisks[i].bi_cyl;
1601			}
1602		}
1603#endif
1604		dos_sectors = MAXSECTOR;
1605		dos_heads = h1;
1606		dos_cylinders = disklabel.d_secperunit / (MAXSECTOR * h1);
1607		if (dos_cylinders > c1)
1608			dos_cylinders = c1;
1609	}
1610
1611	/* Try to deduce the number of heads from two different mappings. */
1612	for (i = 0; i < MBR_PART_COUNT * 2 - 1; i++) {
1613		if (get_mapping(i, &c1, &h1, &s1, &a1) < 0)
1614			continue;
1615		a1 -= s1;
1616		for (j = i + 1; j < MBR_PART_COUNT * 2; j++) {
1617			if (get_mapping(j, &c2, &h2, &s2, &a2) < 0)
1618				continue;
1619			a2 -= s2;
1620			num = (uint64_t)h1 * a2 - (uint64_t)h2 * a1;
1621			denom = (uint64_t)c2 * a1 - (uint64_t)c1 * a2;
1622			if (denom != 0 && num != 0 && num % denom == 0) {
1623				xheads = num / denom;
1624				xsectors = a1 / (c1 * xheads + h1);
1625				break;
1626			}
1627		}
1628		if (xheads != -1)
1629			break;
1630	}
1631
1632	if (xheads == -1) {
1633		warnx("Cannot determine the number of heads");
1634		return;
1635	}
1636
1637	if (xsectors == -1) {
1638		warnx("Cannot determine the number of sectors");
1639		return;
1640	}
1641
1642	/* Estimate the number of cylinders. */
1643	xcylinders = disklabel.d_secperunit / xheads / xsectors;
1644	if (disklabel.d_secperunit > xcylinders * xheads * xsectors)
1645		xcylinders++;
1646
1647	/*
1648	 * Now verify consistency with each of the partition table entries.
1649	 * Be willing to shove cylinders up a little bit to make things work,
1650	 * but translation mismatches are fatal.
1651	 */
1652	for (i = 0; i < MBR_PART_COUNT * 2; i++) {
1653		if (get_mapping(i, &c1, &h1, &s1, &a1) < 0)
1654			continue;
1655		if (c1 >= MAXCYL - 2)
1656			continue;
1657		if (xsectors * (c1 * xheads + h1) + s1 != a1)
1658			return;
1659	}
1660
1661
1662	/* Everything checks out.
1663	 * Reset the geometry to use for further calculations.
1664	 * But cylinders cannot be > 1024.
1665	 */
1666	if (xcylinders > MAXCYL)
1667		dos_cylinders = MAXCYL;
1668	else
1669		dos_cylinders = xcylinders;
1670	dos_heads = xheads;
1671	dos_sectors = xsectors;
1672}
1673
1674/*
1675 * For the purposes of intuit_translated_geometry(), treat the partition
1676 * table as a list of eight mapping between (cylinder, head, sector)
1677 * triplets and absolute sectors.  Get the relevant geometry triplet and
1678 * absolute sectors for a given entry, or return -1 if it isn't present.
1679 * Note: for simplicity, the returned sector is 0-based.
1680 */
1681static int
1682get_mapping(int i, unsigned int *cylinder, unsigned int *head, unsigned int *sector,
1683    unsigned long *absolute)
1684{
1685	struct mbr_partition *part = &mboot.mbr_parts[i / 2];
1686
1687	if (part->mbrp_type == 0)
1688		return -1;
1689	if (i % 2 == 0) {
1690		*cylinder = MBR_PCYL(part->mbrp_scyl, part->mbrp_ssect);
1691		*head = part->mbrp_shd;
1692		*sector = MBR_PSECT(part->mbrp_ssect);
1693		*absolute = le32toh(part->mbrp_start);
1694	} else {
1695		*cylinder = MBR_PCYL(part->mbrp_ecyl, part->mbrp_esect);
1696		*head = part->mbrp_ehd;
1697		*sector = MBR_PSECT(part->mbrp_esect);
1698		*absolute = le32toh(part->mbrp_start)
1699		    + le32toh(part->mbrp_size) - 1;
1700	}
1701	/* Sanity check the data against all zeroes */
1702	if ((*cylinder == 0) && (*sector == 0) && (*head == 0))
1703		return -1;
1704	/* sector numbers in the MBR partition table start at 1 */
1705	*sector = *sector - 1;
1706	/* Sanity check the data against max values */
1707	if ((((*cylinder * MAXHEAD) + *head) * MAXSECTOR + *sector) < *absolute)
1708		/* cannot be a CHS mapping */
1709		return -1;
1710	return 0;
1711}
1712
1713static void
1714delete_ptn(int part)
1715{
1716	if (part == ext.ptn_id) {
1717		/* forget all about the extended partition */
1718		free(ext.ptn);
1719		memset(&ext, 0, sizeof ext);
1720	}
1721
1722	mboot.mbr_parts[part].mbrp_type = 0;
1723}
1724
1725static void
1726delete_ext_ptn(int part)
1727{
1728
1729	if (part == 0) {
1730		ext.ptn[0].mbr_parts[0].mbrp_type = 0;
1731		return;
1732	}
1733	ext.ptn[part - 1].mbr_parts[1] = ext.ptn[part].mbr_parts[1];
1734	memmove(&ext.ptn[part], &ext.ptn[part + 1],
1735		(ext.num_ptn - part - 1) * sizeof ext.ptn[0]);
1736	ext.num_ptn--;
1737}
1738
1739static int
1740add_ext_ptn(daddr_t start, daddr_t size)
1741{
1742	int part;
1743	struct mbr_partition *partp;
1744	struct mbr_sector *nptn;
1745
1746	nptn = realloc(ext.ptn, (ext.num_ptn + 1) * sizeof *ext.ptn);
1747	if (!nptn)
1748		err(1, "realloc");
1749	ext.ptn = nptn;
1750	for (part = 0; part < ext.num_ptn; part++)
1751		if (ext_offset(part) > start)
1752			break;
1753	/* insert before 'part' - make space... */
1754	memmove(&ext.ptn[part + 1], &ext.ptn[part],
1755		(ext.num_ptn - part) * sizeof ext.ptn[0]);
1756	memset(&ext.ptn[part], 0, sizeof ext.ptn[0]);
1757	ext.ptn[part].mbr_magic = LE_MBR_MAGIC;
1758	/* we will be 'part' */
1759	if (part == 0) {
1760		/* link us to 'next' */
1761		partp = &ext.ptn[0].mbr_parts[1];
1762		/* offset will be fixed by caller */
1763		partp->mbrp_size = htole32(
1764		    le32toh(ext.ptn[1].mbr_parts[0].mbrp_start) +
1765		    le32toh(ext.ptn[1].mbr_parts[0].mbrp_size));
1766	} else {
1767		/* link us to prev's next */
1768		partp = &ext.ptn[part - 1].mbr_parts[1];
1769		ext.ptn[part].mbr_parts[1] = *partp;
1770		/* and prev onto us */
1771		partp->mbrp_start = htole32(start - ptn_0_offset - ext.base);
1772		partp->mbrp_size = htole32(size + ptn_0_offset);
1773	}
1774	partp->mbrp_type = 5;	/* as used by win98 */
1775	partp->mbrp_flag = 0;
1776	/* wallop in some CHS values - win98 doesn't saturate them */
1777	dos(le32toh(partp->mbrp_start),
1778	    &partp->mbrp_scyl, &partp->mbrp_shd, &partp->mbrp_ssect);
1779	dos(le32toh(partp->mbrp_start) + le32toh(partp->mbrp_size) - 1,
1780	    &partp->mbrp_ecyl, &partp->mbrp_ehd, &partp->mbrp_esect);
1781	ext.num_ptn++;
1782
1783	return part;
1784}
1785
1786static const char *
1787check_overlap(int part, int sysid, daddr_t start, daddr_t size, int fix)
1788{
1789	int p;
1790	unsigned int p_s, p_e;
1791
1792	if (sysid != 0) {
1793		if (start == 0)
1794			return "Sector zero is reserved for the MBR";
1795#if 0
1796		if (start < ptn_0_offset)
1797			/* This is just a convention, not a requirement */
1798			return "Track zero is reserved for the BIOS";
1799#endif
1800		if (start + size > disksectors)
1801			return "Partition exceeds size of disk";
1802		for (p = 0; p < MBR_PART_COUNT; p++) {
1803			if (p == part || mboot.mbr_parts[p].mbrp_type == 0)
1804				continue;
1805			p_s = le32toh(mboot.mbr_parts[p].mbrp_start);
1806			p_e = p_s + le32toh(mboot.mbr_parts[p].mbrp_size);
1807			if (start + size <= p_s || start >= p_e)
1808				continue;
1809			if (f_flag) {
1810				if (fix)
1811					delete_ptn(p);
1812				return 0;
1813			}
1814			return "Overlaps another partition";
1815		}
1816	}
1817
1818	/* Are we trying to create an extended partition */
1819	if (!MBR_IS_EXTENDED(mboot.mbr_parts[part].mbrp_type)) {
1820		/* this wasn't the extended partition */
1821		if (!MBR_IS_EXTENDED(sysid))
1822			return 0;
1823		/* making an extended partition */
1824		if (ext.base != 0) {
1825			if (!f_flag)
1826				return "There cannot be 2 extended partitions";
1827			if (fix)
1828				delete_ptn(ext.ptn_id);
1829		}
1830		if (fix) {
1831			/* allocate a new extended partition */
1832			ext.ptn = calloc(1, sizeof ext.ptn[0]);
1833			if (ext.ptn == NULL)
1834				err(1, "Malloc failed");
1835			ext.ptn[0].mbr_magic = LE_MBR_MAGIC;
1836			ext.ptn_id = part;
1837			ext.base = start;
1838			ext.limit = start + size;
1839			ext.num_ptn = 1;
1840		}
1841		return 0;
1842	}
1843
1844	/* Check we haven't cut space allocated to an extended ptn */
1845
1846	if (!MBR_IS_EXTENDED(sysid)) {
1847		/* no longer an extended partition */
1848		if (fix) {
1849			/* Kill all memory of the extended partitions */
1850			delete_ptn(part);
1851			return 0;
1852		}
1853		if (ext.num_ptn == 0 ||
1854		    (ext.num_ptn == 1 && ext.ptn[0].mbr_parts[0].mbrp_type == 0))
1855			/* nothing in extended partition */
1856			return 0;
1857		if (f_flag)
1858			return 0;
1859		if (yesno("Do you really want to delete all the extended partitions?"))
1860			return 0;
1861		return "Extended partition busy";
1862	}
1863
1864	if (le32toh(mboot.mbr_parts[part].mbrp_start) != ext.base)
1865		/* maybe impossible, but an extra sanity check */
1866		return 0;
1867
1868	for (p = ext.num_ptn; --p >= 0;) {
1869		if (ext.ptn[p].mbr_parts[0].mbrp_type == 0)
1870			continue;
1871		p_s = ext_offset(p);
1872		p_e = p_s + le32toh(ext.ptn[p].mbr_parts[0].mbrp_start)
1873			  + le32toh(ext.ptn[p].mbr_parts[0].mbrp_size);
1874		if (p_s >= start && p_e <= start + size)
1875			continue;
1876		if (!f_flag)
1877			return "Extended partition outside main partition";
1878		if (fix)
1879			delete_ext_ptn(p);
1880	}
1881
1882	if (fix && start != ext.base) {
1883		/* The internal offsets need to be fixed up */
1884		for (p = 0; p < ext.num_ptn - 1; p++)
1885			ext.ptn[p].mbr_parts[1].mbrp_start = htole32(
1886			    le32toh(ext.ptn[p].mbr_parts[1].mbrp_start)
1887				    + ext.base - start);
1888		/* and maybe an empty partition at the start */
1889		if (ext.ptn[0].mbr_parts[0].mbrp_type == 0) {
1890			if (le32toh(ext.ptn[0].mbr_parts[1].mbrp_start) == 0) {
1891				/* don't need the empty slot */
1892				memmove(&ext.ptn[0], &ext.ptn[1],
1893					(ext.num_ptn - 1) * sizeof ext.ptn[0]);
1894				ext.num_ptn--;
1895			}
1896		} else {
1897			/* must create an empty slot */
1898			add_ext_ptn(start, ptn_0_offset);
1899			ext.ptn[0].mbr_parts[1].mbrp_start = htole32(ext.base
1900								- start);
1901		}
1902	}
1903	if (fix) {
1904		ext.base = start;
1905		ext.limit = start + size;
1906	}
1907	return 0;
1908}
1909
1910static const char *
1911check_ext_overlap(int part, int sysid, daddr_t start, daddr_t size, int fix)
1912{
1913	int p;
1914	unsigned int p_s, p_e;
1915
1916	if (sysid == 0)
1917		return 0;
1918
1919	if (MBR_IS_EXTENDED(sysid))
1920		return "Nested extended partitions are not allowed";
1921
1922	/* allow one track at start for extended partition header */
1923	start -= ptn_0_offset;
1924	size += ptn_0_offset;
1925	if (start < ext.base || start + size > ext.limit)
1926		return "Outside bounds of extended partition";
1927
1928	if (f_flag && !fix)
1929		return 0;
1930
1931	for (p = ext.num_ptn; --p >= 0;) {
1932		if (p == part || ext.ptn[p].mbr_parts[0].mbrp_type == 0)
1933			continue;
1934		p_s = ext_offset(p);
1935		p_e = p_s + le32toh(ext.ptn[p].mbr_parts[0].mbrp_start)
1936			+ le32toh(ext.ptn[p].mbr_parts[0].mbrp_size);
1937		if (p == 0)
1938			p_s += le32toh(ext.ptn[p].mbr_parts[0].mbrp_start)
1939							- ptn_0_offset;
1940		if (start < p_e && start + size > p_s) {
1941			if (!f_flag)
1942				return "Overlaps another extended partition";
1943			if (fix) {
1944				if (part == -1)
1945					delete_ext_ptn(p);
1946				else
1947					/* must not change numbering yet */
1948					ext.ptn[p].mbr_parts[0].mbrp_type = 0;
1949			}
1950		}
1951	}
1952	return 0;
1953}
1954
1955static int
1956change_part(int extended, int part, int sysid, daddr_t start, daddr_t size,
1957	char *bootmenu)
1958{
1959	struct mbr_partition *partp;
1960	struct mbr_sector *boot;
1961	daddr_t offset;
1962	const char *e;
1963	int upart = part;
1964	int p;
1965	int fl;
1966	daddr_t n_s, n_e;
1967	const char *errtext;
1968#ifdef BOOTSEL
1969	char tmp_bootmenu[MBR_PART_COUNT * (MBR_BS_PARTNAMESIZE + 1)];
1970	int bootmenu_len = (extended ? MBR_PART_COUNT : 1) * (MBR_BS_PARTNAMESIZE + 1);
1971#endif
1972
1973	if (extended) {
1974		if (part != -1 && part < ext.num_ptn) {
1975			boot = &ext.ptn[part];
1976			partp = &boot->mbr_parts[0];
1977			offset = ext_offset(part);
1978		} else {
1979			part = -1;
1980			boot = 0;
1981			partp = 0;
1982			offset = 0;
1983		}
1984		upart = 0;
1985		e = "E";
1986	} else {
1987		boot = &mboot;
1988		partp = &boot->mbr_parts[part];
1989		offset = 0;
1990		e = "";
1991	}
1992
1993	if (!f_flag && part != -1) {
1994		printf("The data for partition %s%d is:\n", e, part);
1995		print_part(boot, upart, offset);
1996	}
1997
1998#ifdef BOOTSEL
1999	if (bootmenu != NULL)
2000		strlcpy(tmp_bootmenu, bootmenu, bootmenu_len);
2001	else
2002		if (boot != NULL && boot->mbr_bootsel_magic == LE_MBR_BS_MAGIC)
2003			strlcpy(tmp_bootmenu,
2004				boot->mbr_bootsel.mbrbs_nametab[upart],
2005				bootmenu_len);
2006		else
2007			tmp_bootmenu[0] = 0;
2008#endif
2009
2010	if (!s_flag && partp != NULL) {
2011		/* values not specified, default to current ones */
2012		sysid = partp->mbrp_type;
2013		start = offset + le32toh(partp->mbrp_start);
2014		size = le32toh(partp->mbrp_size);
2015	}
2016
2017	/* creating a new partition, default to free space */
2018	if (!s_flag && sysid == 0 && extended) {
2019		/* non-extended partition */
2020		start = ext.base;
2021		for (p = 0; p < ext.num_ptn; p++) {
2022			if (ext.ptn[p].mbr_parts[0].mbrp_type == 0)
2023				continue;
2024			n_s = ext_offset(p);
2025			if (n_s > start + ptn_0_offset)
2026				break;
2027			start = ext_offset(p)
2028				+ le32toh(ext.ptn[p].mbr_parts[0].mbrp_start)
2029				+ le32toh(ext.ptn[p].mbr_parts[0].mbrp_size);
2030		}
2031		if (ext.limit - start <= ptn_0_offset) {
2032			printf("No space in extended partition\n");
2033			return 0;
2034		}
2035		start += ptn_0_offset;
2036	}
2037
2038	if (!s_flag && sysid == 0 && !extended) {
2039		/* same for non-extended partition */
2040		/* first see if old start is free */
2041		if (start < ptn_0_offset)
2042			start = 0;
2043		for (p = 0; start != 0 && p < MBR_PART_COUNT; p++) {
2044			if (mboot.mbr_parts[p].mbrp_type == 0)
2045				continue;
2046			n_s = le32toh(mboot.mbr_parts[p].mbrp_start);
2047			if (start >= n_s &&
2048			    start < n_s + le32toh(mboot.mbr_parts[p].mbrp_size))
2049				start = 0;
2050		}
2051		if (start == 0) {
2052			/* Look for first gap */
2053			start = ptn_0_offset;
2054			for (p = 0; p < MBR_PART_COUNT; p++) {
2055				if (mboot.mbr_parts[p].mbrp_type == 0)
2056					continue;
2057				n_s = le32toh(mboot.mbr_parts[p].mbrp_start);
2058				n_e = n_s + le32toh(mboot.mbr_parts[p].mbrp_size);
2059				if (start >= n_s && start < n_e) {
2060					start = n_e;
2061					p = -1;
2062				}
2063			}
2064			if (start >= disksectors && !I_flag) {
2065				printf("No free space\n");
2066				return 0;
2067			}
2068		}
2069	}
2070
2071	if (!f_flag) {
2072		/* request new values from user */
2073		if (sysid == 0)
2074			sysid = 169;
2075		sysid = decimal("sysid", sysid, 0, 0, 255);
2076		if (sysid == 0 && !v_flag) {
2077			start = 0;
2078			size = 0;
2079#ifdef BOOTSEL
2080			tmp_bootmenu[0] = 0;
2081#endif
2082		} else {
2083			daddr_t old = start;
2084			daddr_t lim = extended ? ext.limit : disksectors;
2085			start = decimal("start", start,
2086				DEC_SEC | DEC_RND_0 | (extended ? DEC_RND : 0),
2087				extended ? ext.base : 0, lim);
2088			/* Adjust 'size' so that end doesn't move when 'start'
2089			 * is only changed slightly.
2090			 */
2091			if (size > start - old)
2092				size -= start - old;
2093			else
2094				size = 0;
2095			/* Find end of available space from this start point */
2096			if (extended) {
2097				for (p = 0; p < ext.num_ptn; p++) {
2098					if (p == part)
2099						continue;
2100					if (ext.ptn[p].mbr_parts[0].mbrp_type == 0)
2101						continue;
2102					n_s = ext_offset(p);
2103					if (n_s > start && n_s < lim)
2104						lim = n_s;
2105					if (start >= n_s && start < n_s
2106				+ le32toh(ext.ptn[p].mbr_parts[0].mbrp_start)
2107				+ le32toh(ext.ptn[p].mbr_parts[0].mbrp_size)) {
2108						lim = start;
2109						break;
2110					}
2111				}
2112			} else {
2113				for (p = 0; p < MBR_PART_COUNT; p++) {
2114					if (p == part)
2115						continue;
2116					if (mboot.mbr_parts[p].mbrp_type == 0)
2117						continue;
2118					n_s = le32toh(mboot.mbr_parts[p].mbrp_start);
2119					if (n_s > start && n_s < lim)
2120						lim = n_s;
2121					if (start >= n_s && start < n_s
2122				    + le32toh(mboot.mbr_parts[p].mbrp_size)) {
2123						lim = start;
2124						break;
2125					}
2126				}
2127			}
2128			lim -= start;
2129			if (lim == 0) {
2130				printf("Start sector already allocated\n");
2131				return 0;
2132			}
2133			if (size == 0 || size > lim)
2134				size = lim;
2135			fl = DEC_SEC;
2136			if (start % ptn_alignment == ptn_0_offset)
2137				fl |= DEC_RND_DOWN;
2138			if (start == 2 * ptn_0_offset)
2139				fl |= DEC_RND_DOWN | DEC_RND_DOWN_2;
2140			size = decimal("size", size, fl, 0, lim);
2141#ifdef BOOTSEL
2142#ifndef DEFAULT_BOOTEXTCODE
2143			if (!extended)
2144#endif
2145				string("bootmenu", bootmenu_len, tmp_bootmenu);
2146#endif
2147		}
2148	}
2149
2150	/*
2151	 * Before we write these away, we must verify that nothing
2152	 * untoward has been requested.
2153	 */
2154
2155	if (extended)
2156		errtext = check_ext_overlap(part, sysid, start, size, 0);
2157	else
2158		errtext = check_overlap(part, sysid, start, size, 0);
2159	if (errtext != NULL && !I_flag) {
2160		if (f_flag)
2161			errx(2, "%s\n", errtext);
2162		printf("%s\n", errtext);
2163		return 0;
2164	}
2165
2166	/*
2167	 * Before proceeding, delete any overlapped partitions.
2168	 * This can only happen if '-f' was supplied on the command line.
2169	 * Just hope the caller knows what they are doing.
2170	 * This also fixes the base of each extended partition if the
2171	 * partition itself has moved.
2172	 */
2173	if (!I_flag) {
2174		if (extended)
2175			errtext = check_ext_overlap(part, sysid, start, size, 1);
2176		else
2177			errtext = check_overlap(part, sysid, start, size, 1);
2178		if (errtext)
2179			errx(1, "%s\n", errtext);
2180	}
2181
2182
2183	if (sysid == 0) {
2184		/* delete this partition - save info though */
2185		if (partp == NULL)
2186			/* must have been trying to create an extended ptn */
2187			return 0;
2188		if (start == 0 && size == 0)
2189			memset(partp, 0, sizeof *partp);
2190#ifdef BOOTSEL
2191		if (boot->mbr_bootsel_magic == LE_MBR_BS_MAGIC)
2192			memset(boot->mbr_bootsel.mbrbs_nametab[upart], 0,
2193				sizeof boot->mbr_bootsel.mbrbs_nametab[0]);
2194#endif
2195		if (extended)
2196			delete_ext_ptn(part);
2197		else
2198			delete_ptn(part);
2199		return 1;
2200	}
2201
2202
2203	if (extended) {
2204		if (part != -1)
2205			delete_ext_ptn(part);
2206		if (start == ext.base + ptn_0_offset)
2207			/* First one must have been free */
2208			part = 0;
2209		else
2210			part = add_ext_ptn(start, size);
2211
2212		/* These must be re-calculated because of the realloc */
2213		boot = &ext.ptn[part];
2214		partp = &boot->mbr_parts[0];
2215		offset = ext_offset(part);
2216	}
2217
2218	partp->mbrp_type = sysid;
2219	partp->mbrp_start = htole32( start - offset);
2220	partp->mbrp_size = htole32( size);
2221	dos(start, &partp->mbrp_scyl, &partp->mbrp_shd, &partp->mbrp_ssect);
2222	dos(start + size - 1,
2223		    &partp->mbrp_ecyl, &partp->mbrp_ehd, &partp->mbrp_esect);
2224#ifdef BOOTSEL
2225	if (extended) {
2226		boot->mbr_bootsel_magic = LE_MBR_BS_MAGIC;
2227		strncpy(boot->mbr_bootsel.mbrbs_nametab[upart], tmp_bootmenu,
2228			bootmenu_len);
2229	} else {
2230		/* We need to bootselect code installed in order to have
2231		 * somewhere to safely write the menu tag.
2232		 */
2233		if (boot->mbr_bootsel_magic != LE_MBR_BS_MAGIC) {
2234			if (f_flag ||
2235			    yesno("The bootselect code is not installed, "
2236				"do you want to install it now?"))
2237				install_bootsel(MBR_BS_ACTIVE);
2238		}
2239		if (boot->mbr_bootsel_magic == LE_MBR_BS_MAGIC) {
2240			strncpy(boot->mbr_bootsel.mbrbs_nametab[upart],
2241				tmp_bootmenu, bootmenu_len);
2242		}
2243	}
2244#endif
2245
2246	if (v_flag && !f_flag && yesno("Explicitly specify beg/end address?")) {
2247		/* this really isn't a good idea.... */
2248		int tsector, tcylinder, thead;
2249
2250		tcylinder = MBR_PCYL(partp->mbrp_scyl, partp->mbrp_ssect);
2251		thead = partp->mbrp_shd;
2252		tsector = MBR_PSECT(partp->mbrp_ssect);
2253		tcylinder = decimal("beginning cylinder",
2254				tcylinder, 0, 0, dos_cylinders - 1);
2255		thead = decimal("beginning head",
2256				thead, 0, 0, dos_heads - 1);
2257		tsector = decimal("beginning sector",
2258				tsector, 0, 1, dos_sectors);
2259		partp->mbrp_scyl = DOSCYL(tcylinder);
2260		partp->mbrp_shd = thead;
2261		partp->mbrp_ssect = DOSSECT(tsector, tcylinder);
2262
2263		tcylinder = MBR_PCYL(partp->mbrp_ecyl, partp->mbrp_esect);
2264		thead = partp->mbrp_ehd;
2265		tsector = MBR_PSECT(partp->mbrp_esect);
2266		tcylinder = decimal("ending cylinder",
2267				tcylinder, 0, 0, dos_cylinders - 1);
2268		thead = decimal("ending head",
2269				thead, 0, 0, dos_heads - 1);
2270		tsector = decimal("ending sector",
2271				tsector, 0, 1, dos_sectors);
2272		partp->mbrp_ecyl = DOSCYL(tcylinder);
2273		partp->mbrp_ehd = thead;
2274		partp->mbrp_esect = DOSSECT(tsector, tcylinder);
2275	}
2276
2277	/* If we had to mark an extended partition as deleted because
2278	 * another request would have overlapped it, now is the time
2279	 * to do the actual delete.
2280	 */
2281	if (extended && f_flag) {
2282		for (p = ext.num_ptn; --p >= 0;)
2283			if (ext.ptn[p].mbr_parts[0].mbrp_type == 0)
2284				delete_ext_ptn(p);
2285	}
2286	return 1;
2287}
2288
2289static void
2290print_geometry(void)
2291{
2292
2293	if (sh_flag) {
2294		printf("DISK=%s\n", disk);
2295		printf("DLCYL=%d\nDLHEAD=%d\nDLSEC=%d\nDLSIZE=%"PRIdaddr"\n",
2296			cylinders, heads, sectors, disksectors);
2297		printf("BCYL=%d\nBHEAD=%d\nBSEC=%d\nBDLSIZE=%"PRIdaddr"\n",
2298			dos_cylinders, dos_heads, dos_sectors, dos_disksectors);
2299		printf("NUMEXTPTN=%d\n", ext.num_ptn);
2300		return;
2301	}
2302
2303	/* Not sh_flag */
2304	printf("Disk: %s\n", disk);
2305	printf("NetBSD disklabel disk geometry:\n");
2306	printf("cylinders: %d, heads: %d, sectors/track: %d "
2307	    "(%d sectors/cylinder)\ntotal sectors: %"PRIdaddr"\n\n",
2308	    cylinders, heads, sectors, cylindersectors, disksectors);
2309	printf("BIOS disk geometry:\n");
2310	printf("cylinders: %d, heads: %d, sectors/track: %d "
2311	    "(%d sectors/cylinder)\ntotal sectors: %"PRIdaddr"\n\n",
2312	    dos_cylinders, dos_heads, dos_sectors, dos_cylindersectors,
2313	    dos_disksectors);
2314	printf("Partitions aligned to %d sector boundaries, offset %d\n\n",
2315	    ptn_alignment, ptn_0_offset);
2316}
2317
2318/* Find the first active partition, else return MBR_PART_COUNT */
2319static int
2320first_active(void)
2321{
2322	struct mbr_partition *partp = &mboot.mbr_parts[0];
2323	int part;
2324
2325	for (part = 0; part < MBR_PART_COUNT; part++)
2326		if (partp[part].mbrp_flag & MBR_PFLAG_ACTIVE)
2327			return part;
2328	return MBR_PART_COUNT;
2329}
2330
2331static void
2332change_active(int which)
2333{
2334	struct mbr_partition *partp;
2335	int part;
2336	int active = MBR_PART_COUNT;
2337
2338	partp = &mboot.mbr_parts[0];
2339
2340	if (a_flag && which != -1)
2341		active = which;
2342	else
2343		active = first_active();
2344	if (!f_flag) {
2345		if (yesno("Do you want to change the active partition?")) {
2346			printf ("Choosing %d will make no partition active.\n",
2347			    MBR_PART_COUNT);
2348			do {
2349				active = decimal("active partition",
2350						active, 0, 0, MBR_PART_COUNT);
2351			} while (!yesno("Are you happy with this choice?"));
2352		} else
2353			return;
2354	} else
2355		if (active != MBR_PART_COUNT)
2356			printf ("Making partition %d active.\n", active);
2357
2358	for (part = 0; part < MBR_PART_COUNT; part++)
2359		partp[part].mbrp_flag &= ~MBR_PFLAG_ACTIVE;
2360	if (active < MBR_PART_COUNT)
2361		partp[active].mbrp_flag |= MBR_PFLAG_ACTIVE;
2362}
2363
2364static void
2365change_bios_geometry(void)
2366{
2367	print_geometry();
2368	if (!yesno("Do you want to change our idea of what BIOS thinks?"))
2369		return;
2370
2371#if defined(USE_DISKLIST)
2372	if (dl != NULL) {
2373		struct biosdisk_info *bip;
2374		int i;
2375
2376		for (i = 0; i < dl->dl_nbiosdisks; i++) {
2377			if (i == 0)
2378				printf("\nGeometries of known disks:\n");
2379			bip = &dl->dl_biosdisks[i];
2380			printf("Disk %d: cylinders %u, heads %u, sectors %u"
2381				" (%"PRIdaddr" sectors, %dMB)\n",
2382			    i, bip->bi_cyl, bip->bi_head, bip->bi_sec,
2383			    bip->bi_lbasecs, SEC_TO_MB(bip->bi_lbasecs));
2384
2385		}
2386		printf("\n");
2387	}
2388#endif
2389	do {
2390		dos_cylinders = decimal("BIOS's idea of #cylinders",
2391					dos_cylinders, 0, 0, MAXCYL);
2392		dos_heads = decimal("BIOS's idea of #heads",
2393					dos_heads, 0, 0, MAXHEAD);
2394		dos_sectors = decimal("BIOS's idea of #sectors",
2395					dos_sectors, 0, 1, MAXSECTOR);
2396		print_geometry();
2397	} while (!yesno("Are you happy with this choice?"));
2398}
2399
2400
2401/***********************************************\
2402* Change real numbers into strange dos numbers	*
2403\***********************************************/
2404static void
2405dos(int sector, unsigned char *cylinderp, unsigned char *headp,
2406    unsigned char *sectorp)
2407{
2408	int cylinder, head;
2409
2410	cylinder = sector / dos_cylindersectors;
2411	sector -= cylinder * dos_cylindersectors;
2412
2413	head = sector / dos_sectors;
2414	sector -= head * dos_sectors;
2415	if (cylinder > 1023)
2416		cylinder = 1023;
2417
2418	*cylinderp = DOSCYL(cylinder);
2419	*headp = head;
2420	*sectorp = DOSSECT(sector + 1, cylinder);
2421}
2422
2423static int
2424open_disk(int update)
2425{
2426	static char namebuf[MAXPATHLEN + 1];
2427	int flags = update && disk_file == NULL ? O_RDWR : O_RDONLY;
2428
2429	if (!F_flag) {
2430		fd = opendisk(disk, flags, namebuf, sizeof(namebuf), 0);
2431		if (fd < 0) {
2432			if (errno == ENODEV)
2433				warnx("%s is not a character device", namebuf);
2434			else
2435				warn("cannot opendisk %s", namebuf);
2436			return (-1);
2437		}
2438		disk = namebuf;
2439	} else {
2440		fd = open(disk, flags, 0);
2441		if (fd == -1) {
2442			warn("cannot open %s", disk);
2443			return -1;
2444		}
2445	}
2446
2447	if (get_params() == -1) {
2448		close(fd);
2449		fd = -1;
2450		return (-1);
2451	}
2452	if (disk_file != NULL) {
2453		/* for testing: read/write data from a disk file */
2454		wfd = open(disk_file, update ? O_RDWR|O_CREAT : O_RDONLY, 0777);
2455		if (wfd == -1) {
2456			warn("%s", disk_file);
2457			close(fd);
2458			fd = -1;
2459			return -1;
2460		}
2461	} else
2462		wfd = fd;
2463	return (0);
2464}
2465
2466static int
2467read_disk(daddr_t sector, void *buf)
2468{
2469
2470	if (*rfd == -1)
2471		errx(1, "read_disk(); fd == -1");
2472	if (lseek(*rfd, sector * (off_t)512, 0) == -1)
2473		return (-1);
2474	return (read(*rfd, buf, 512));
2475}
2476
2477static int
2478write_disk(daddr_t sector, void *buf)
2479{
2480
2481	if (wfd == -1)
2482		errx(1, "write_disk(); wfd == -1");
2483	if (lseek(wfd, sector * (off_t)512, 0) == -1)
2484		return (-1);
2485	return (write(wfd, buf, 512));
2486}
2487
2488static void
2489guess_geometry(daddr_t _sectors)
2490{
2491	dos_sectors = MAXSECTOR;
2492	dos_heads = MAXHEAD - 1;	/* some BIOS might use 256 */
2493	dos_cylinders = _sectors / (MAXSECTOR * (MAXHEAD - 1));
2494	if (dos_cylinders < 1)
2495		dos_cylinders = 1;
2496	else if (dos_cylinders > MAXCYL - 1)
2497		dos_cylinders = MAXCYL - 1;
2498}
2499
2500static int
2501get_params(void)
2502{
2503	if (disk_type != NULL) {
2504		struct disklabel *tmplabel;
2505
2506		if ((tmplabel = getdiskbyname(disk_type)) == NULL) {
2507			warn("bad disktype");
2508			return (-1);
2509		}
2510		disklabel = *tmplabel;
2511	} else if (F_flag) {
2512		struct stat st;
2513		if (fstat(fd, &st) == -1) {
2514			warn("fstat");
2515			return (-1);
2516		}
2517		if (st.st_size % 512 != 0) {
2518			warnx("%s size (%lld) is not divisible "
2519			    "by sector size (%d)", disk, (long long)st.st_size,
2520			    512);
2521		}
2522		disklabel.d_secperunit = st.st_size / 512;
2523		guess_geometry(disklabel.d_secperunit);
2524		disklabel.d_ncylinders = dos_cylinders;
2525		disklabel.d_ntracks = dos_heads;
2526		disklabel.d_nsectors = dos_sectors;
2527	} else if (ioctl(fd, DIOCGDEFLABEL, &disklabel) == -1) {
2528		warn("DIOCGDEFLABEL");
2529		if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) {
2530			warn("DIOCGDINFO");
2531			return (-1);
2532		}
2533	}
2534
2535	disksectors = disklabel.d_secperunit;
2536	cylinders = disklabel.d_ncylinders;
2537	heads = disklabel.d_ntracks;
2538	sectors = disklabel.d_nsectors;
2539
2540	/* pick up some defaults for the BIOS sizes */
2541	if (sectors <= MAXSECTOR) {
2542		dos_cylinders = cylinders;
2543		dos_heads = heads;
2544		dos_sectors = sectors;
2545	} else {
2546		/* guess - has to better than the above */
2547		guess_geometry(disksectors);
2548	}
2549	dos_disksectors = disksectors;
2550
2551	return (0);
2552}
2553
2554#ifdef BOOTSEL
2555/*
2556 * Rather unfortunately the bootsel 'magic' number is at the end of the
2557 * the structure, and there is no checksum.  So when other operating
2558 * systems install mbr code by only writing the length of their code they
2559 * can overwrite part of the structure but keeping the magic number intact.
2560 * This code attempts to empirically detect this problem.
2561 */
2562static int
2563validate_bootsel(struct mbr_bootsel *mbs)
2564{
2565	unsigned int key = mbs->mbrbs_defkey;
2566	unsigned int tmo;
2567	size_t i;
2568
2569	if (v_flag)
2570		return 0;
2571
2572	/*
2573	 * Check default key is sane
2574	 * - this is the most likely field to be stuffed
2575	 * 16 disks and 16 bootable partitions seems enough!
2576	 * (the keymap decode starts falling apart at that point)
2577	 */
2578	if (mbs->mbrbs_flags & MBR_BS_ASCII) {
2579		if (key != 0 && !(key == '\r'
2580		    || (key >= '1' && key < '1' + MAX_BIOS_DISKS)
2581		    || (key >= 'a' && key < 'a' + MAX_BIOS_DISKS)))
2582			return 1;
2583	} else {
2584		if (key != 0 && !(key == SCAN_ENTER
2585		    || (key >= SCAN_1 && key < SCAN_1 + MAX_BIOS_DISKS)
2586		    || (key >= SCAN_F1 && key < SCAN_F1 + MAX_BIOS_DISKS)))
2587			return 1;
2588	}
2589
2590	/* Checking the flags will lead to breakage... */
2591
2592	/* Timeout value is expected to be a multiple of a second */
2593	tmo = htole16(mbs->mbrbs_timeo);
2594	if (tmo != 0 && tmo != 0xffff && tmo != (10 * tmo + 9) / 182 * 182 / 10)
2595		return 2;
2596
2597	/* Check the menu strings are printable */
2598	/* Unfortunately they aren't zero filled... */
2599	for (i = 0; i < sizeof(mbs->mbrbs_nametab); i++) {
2600		int c = (uint8_t)mbs->mbrbs_nametab[0][i];
2601		if (c == 0 || isprint(c))
2602			continue;
2603		return 3;
2604	}
2605
2606	return 0;
2607}
2608#endif
2609
2610static int
2611read_s0(daddr_t offset, struct mbr_sector *boot)
2612{
2613	const char *tabletype = offset ? "extended" : "primary";
2614#ifdef BOOTSEL
2615	static int reported;
2616#endif
2617
2618	if (read_disk(offset, boot) == -1) {
2619		warn("Can't read %s partition table", tabletype);
2620		return -1;
2621	}
2622	if (boot->mbr_magic != LE_MBR_MAGIC) {
2623		warnx("%s partition table invalid, "
2624		    "no magic in sector %"PRIdaddr, tabletype, offset);
2625		return -1;
2626
2627	}
2628#ifdef BOOTSEL
2629	if (boot->mbr_bootsel_magic == LE_MBR_BS_MAGIC) {
2630		/* mbr_bootsel in new location */
2631		if (validate_bootsel(&boot->mbr_bootsel)) {
2632			warnx("removing corrupt bootsel information");
2633			boot->mbr_bootsel_magic = 0;
2634		}
2635		return 0;
2636	}
2637	if (boot->mbr_bootsel_magic != LE_MBR_MAGIC)
2638		return 0;
2639
2640	/* mbr_bootsel in old location */
2641	if (!reported)
2642		warnx("%s partition table: using old-style bootsel information",
2643		    tabletype);
2644	reported = 1;
2645	if (validate_bootsel((void *)((uint8_t *)boot + MBR_BS_OFFSET + 4))) {
2646		warnx("%s bootsel information corrupt - ignoring", tabletype);
2647		return 0;
2648	}
2649	memmove((uint8_t *)boot + MBR_BS_OFFSET,
2650		(uint8_t *)boot + MBR_BS_OFFSET + 4,
2651		sizeof(struct mbr_bootsel));
2652	if ( ! (boot->mbr_bootsel.mbrbs_flags & MBR_BS_NEWMBR)) {
2653			/* old style default key */
2654		int id;
2655			/* F1..F4 => ptn 0..3, F5+ => disk 0+ */
2656		id = boot->mbr_bootsel.mbrbs_defkey;
2657		id -= SCAN_F1;
2658		if (id >= MBR_PART_COUNT)
2659			id -= MBR_PART_COUNT; /* Use number of disk */
2660		else if (mboot.mbr_parts[id].mbrp_type != 0)
2661			id = le32toh(boot->mbr_parts[id].mbrp_start);
2662		else
2663			id = DEFAULT_ACTIVE;
2664		boot->mbr_bootsel.mbrbs_defkey = id;
2665	}
2666	boot->mbr_bootsel_magic = LE_MBR_BS_MAGIC;
2667		/* highlight that new bootsel code is necessary */
2668	boot->mbr_bootsel.mbrbs_flags &= ~MBR_BS_NEWMBR;
2669#endif /* BOOTSEL */
2670	return 0;
2671}
2672
2673static int
2674write_mbr(void)
2675{
2676	int flag, i;
2677	daddr_t offset;
2678	int rval = -1;
2679
2680	/*
2681	 * write enable label sector before write (if necessary),
2682	 * disable after writing.
2683	 * needed if the disklabel protected area also protects
2684	 * sector 0. (e.g. empty disk)
2685	 */
2686	flag = 1;
2687	if (wfd == fd && F_flag == 0 && ioctl(wfd, DIOCWLABEL, &flag) < 0)
2688		warn("DIOCWLABEL");
2689	if (write_disk(0, &mboot) == -1) {
2690		warn("Can't write fdisk partition table");
2691		goto protect_label;
2692	}
2693	if (boot_installed)
2694		for (i = bootsize; (i -= 0x200) > 0;)
2695			if (write_disk(i / 0x200, &bootcode[i / 0x200]) == -1) {
2696				warn("Can't write bootcode");
2697				goto protect_label;
2698			}
2699	for (offset = 0, i = 0; i < ext.num_ptn; i++) {
2700		if (write_disk(ext.base + offset, ext.ptn + i) == -1) {
2701			warn("Can't write %dth extended partition", i);
2702			goto protect_label;
2703		}
2704		offset = le32toh(ext.ptn[i].mbr_parts[1].mbrp_start);
2705	}
2706	rval = 0;
2707    protect_label:
2708	flag = 0;
2709	if (wfd == fd && F_flag == 0 && ioctl(wfd, DIOCWLABEL, &flag) < 0)
2710		warn("DIOCWLABEL");
2711	return rval;
2712}
2713
2714static int
2715yesno(const char *str, ...)
2716{
2717	int ch, first;
2718	va_list ap;
2719
2720	va_start(ap, str);
2721
2722	vprintf(str, ap);
2723	printf(" [n] ");
2724
2725	first = ch = getchar();
2726	while (ch != '\n' && ch != EOF)
2727		ch = getchar();
2728	if (ch == EOF)
2729		errx(1, "EOF");
2730	return (first == 'y' || first == 'Y');
2731}
2732
2733static int64_t
2734decimal(const char *prompt, int64_t dflt, int flags, int64_t minval, int64_t maxval)
2735{
2736	int64_t acc = 0;
2737	int valid;
2738	int len;
2739	char *cp;
2740
2741	for (;;) {
2742		if (flags & DEC_SEC) {
2743			printf("%s: [%" PRId64 "..%" PRId64 "cyl default: %" PRId64 ", %" PRId64 "cyl, %uMB] ",
2744			    prompt, SEC_TO_CYL(minval), SEC_TO_CYL(maxval),
2745			    dflt, SEC_TO_CYL(dflt), SEC_TO_MB(dflt));
2746		} else
2747			printf("%s: [%" PRId64 "..%" PRId64 " default: %" PRId64 "] ",
2748			    prompt, minval, maxval, dflt);
2749
2750		if (!fgets(lbuf, LBUF, stdin))
2751			errx(1, "EOF");
2752		cp = lbuf;
2753
2754		cp += strspn(cp, " \t");
2755		if (*cp == '\n')
2756			return dflt;
2757
2758		if (cp[0] == '$' && cp[1] == '\n')
2759			return maxval;
2760
2761		if (isdigit((unsigned char)*cp) || *cp == '-') {
2762			acc = strtoll(lbuf, &cp, 10);
2763			len = strcspn(cp, " \t\n");
2764			valid = 0;
2765			if (len != 0 && (flags & DEC_SEC)) {
2766				if (!strncasecmp(cp, "gb", len)) {
2767					acc *= 1024;
2768					valid = 1;
2769				}
2770				if (valid || !strncasecmp(cp, "mb", len)) {
2771					acc *= SEC_IN_1M;
2772					/* round to whole number of cylinders */
2773					acc += ptn_alignment / 2;
2774					acc /= ptn_alignment;
2775					valid = 1;
2776				}
2777				if (valid || !strncasecmp(cp, "cyl", len)) {
2778					acc *= ptn_alignment;
2779					/* adjustments for cylinder boundary */
2780					if (acc == 0 && flags & DEC_RND_0)
2781						acc += ptn_0_offset;
2782					if (flags & DEC_RND)
2783						acc += ptn_0_offset;
2784					if (flags & DEC_RND_DOWN)
2785						acc -= ptn_0_offset;
2786					if (flags & DEC_RND_DOWN_2)
2787						acc -= ptn_0_offset;
2788					cp += len;
2789				}
2790			}
2791		}
2792
2793		cp += strspn(cp, " \t");
2794		if (*cp != '\n') {
2795			lbuf[strlen(lbuf) - 1] = 0;
2796			printf("%s is not a valid %s number.\n", lbuf,
2797			    flags & DEC_SEC ? "sector" : "decimal");
2798			continue;
2799		}
2800
2801		if (acc >= minval && acc <= maxval)
2802			return acc;
2803		printf("%" PRId64 " is not between %" PRId64 " and %" PRId64 ".\n", acc, minval, maxval);
2804	}
2805}
2806
2807static int
2808ptn_id(const char *prompt, int *extended)
2809{
2810	unsigned int acc = 0;
2811	char *cp;
2812
2813	for (;; printf("%s is not a valid partition number.\n", lbuf)) {
2814		printf("%s: [none] ", prompt);
2815
2816		if (!fgets(lbuf, LBUF, stdin))
2817			errx(1, "EOF");
2818		lbuf[strlen(lbuf)-1] = '\0';
2819		cp = lbuf;
2820
2821		cp += strspn(cp, " \t");
2822		*extended = 0;
2823		if (*cp == 0)
2824			return -1;
2825
2826		if (*cp == 'E' || *cp == 'e') {
2827			cp++;
2828			*extended = 1;
2829		}
2830
2831		acc = strtoul(cp, &cp, 10);
2832
2833		cp += strspn(cp, " \t");
2834		if (*cp != '\0')
2835			continue;
2836
2837		if (*extended || acc < MBR_PART_COUNT)
2838			return acc;
2839	}
2840}
2841
2842#ifdef BOOTSEL
2843static void
2844string(const char *prompt, int length, char *buf)
2845{
2846	int len;
2847
2848	for (;;) {
2849		printf("%s: [%.*s] ", prompt, length, buf);
2850
2851		if (!fgets(lbuf, LBUF, stdin))
2852			errx(1, "EOF");
2853		len = strlen(lbuf);
2854		if (len <= 1)
2855			/* unchanged if just <enter> */
2856			return;
2857		/* now strip trailing spaces, <space><enter> deletes string */
2858		do
2859			lbuf[--len] = 0;
2860		while (len != 0 && lbuf[len - 1] == ' ');
2861		if (len < length)
2862			break;
2863		printf("'%s' is longer than %d characters.\n",
2864		    lbuf, length - 1);
2865	}
2866	strncpy(buf, lbuf, length);
2867}
2868#endif
2869
2870static int
2871type_match(const void *key, const void *item)
2872{
2873	const int *idp = key;
2874	const struct mbr_ptype *ptr = item;
2875
2876	if (*idp < ptr->id)
2877		return (-1);
2878	if (*idp > ptr->id)
2879		return (1);
2880	return (0);
2881}
2882
2883static const char *
2884get_type(int type)
2885{
2886	struct mbr_ptype *ptr;
2887
2888	ptr = bsearch(&type, mbr_ptypes, KNOWN_SYSIDS,
2889	    sizeof(mbr_ptypes[0]), type_match);
2890	if (ptr == 0)
2891		return ("unknown");
2892	return (ptr->name);
2893}
2894
2895static int
2896read_gpt(daddr_t offset, struct gpt_hdr *gptp)
2897{
2898	char buf[512];
2899	struct gpt_hdr *hdr = (void *)buf;
2900	const char *tabletype = GPT_TYPE(offset);
2901
2902	if (read_disk(offset, buf) == -1) {
2903		warn("Can't read %s GPT header", tabletype);
2904		return -1;
2905	}
2906	(void)memcpy(gptp, buf, GPT_HDR_SIZE);
2907
2908	/* GPT CRC should be calculated with CRC field preset to zero */
2909	hdr->hdr_crc_self = 0;
2910
2911	if (memcmp(gptp->hdr_sig, GPT_HDR_SIG, sizeof(gptp->hdr_sig))
2912	    || gptp->hdr_lba_self != (uint64_t)offset
2913	    || crc32(0, (void *)hdr, gptp->hdr_size) != gptp->hdr_crc_self) {
2914		/* not a GPT */
2915		(void)memset(gptp, 0, GPT_HDR_SIZE);
2916	}
2917
2918	if (v_flag && gptp->hdr_size != 0) {
2919		printf("Found %s GPT header CRC %"PRIu32" "
2920		    "at sector %"PRIdaddr", backup at %"PRIdaddr"\n",
2921		    tabletype, gptp->hdr_crc_self, offset, gptp->hdr_lba_alt);
2922	}
2923	return gptp->hdr_size;
2924
2925}
2926
2927static int
2928delete_gpt(struct gpt_hdr *gptp)
2929{
2930	char buf[512];
2931	struct gpt_hdr *hdr = (void *)buf;
2932
2933	if (gptp->hdr_size == 0)
2934		return 0;
2935
2936	/* don't accidently overwrite something important */
2937	if (gptp->hdr_lba_self != GPT_HDR_BLKNO &&
2938	    gptp->hdr_lba_self != (uint64_t)disksectors - 1) {
2939		warnx("given GPT header location doesn't seem correct");
2940		return -1;
2941	}
2942
2943	(void)memcpy(buf, gptp, GPT_HDR_SIZE);
2944	/*
2945	 * Don't really delete GPT, just "disable" it, so it can
2946	 * be recovered later in case of mistake or something
2947	 */
2948	(void)memset(hdr->hdr_sig, 0, sizeof(gptp->hdr_sig));
2949	if (write_disk(gptp->hdr_lba_self, hdr) == -1) {
2950		warn("can't delete %s GPT header",
2951		    GPT_TYPE(gptp->hdr_lba_self));
2952		return -1;
2953	}
2954	(void)memset(gptp, 0, GPT_HDR_SIZE);
2955	return 1;
2956}
2957