18871Srgrimes/*
24Srgrimes * Mach Operating System
34Srgrimes * Copyright (c) 1992 Carnegie Mellon University
44Srgrimes * All Rights Reserved.
58871Srgrimes *
64Srgrimes * Permission to use, copy, modify and distribute this software and its
74Srgrimes * documentation is hereby granted, provided that both the copyright
84Srgrimes * notice and this permission notice appear in all copies of the
94Srgrimes * software, derivative works or modified versions, and any portions
104Srgrimes * thereof, and that both notices appear in supporting documentation.
118871Srgrimes *
124Srgrimes * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
134Srgrimes * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
144Srgrimes * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
158871Srgrimes *
164Srgrimes * Carnegie Mellon requests users of this software to return to
178871Srgrimes *
184Srgrimes *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
194Srgrimes *  School of Computer Science
204Srgrimes *  Carnegie Mellon University
214Srgrimes *  Pittsburgh PA 15213-3890
228871Srgrimes *
234Srgrimes * any improvements or extensions that they make and grant Carnegie Mellon
244Srgrimes * the rights to redistribute these changes.
254Srgrimes */
264Srgrimes
27114589Sobrien#include <sys/cdefs.h>
28114589Sobrien__FBSDID("$FreeBSD$");
2937415Scharnier
30103348Sphk#include <sys/disk.h>
314Srgrimes#include <sys/disklabel.h>
32104272Sphk#include <sys/diskmbr.h>
33113454Sphk#include <sys/endian.h>
3479681Sjoerg#include <sys/param.h>
3537415Scharnier#include <sys/stat.h>
3679681Sjoerg#include <sys/mount.h>
3737415Scharnier#include <ctype.h>
3837415Scharnier#include <fcntl.h>
3937415Scharnier#include <err.h>
4037415Scharnier#include <errno.h>
41148035Sphk#include <libgeom.h>
4279681Sjoerg#include <paths.h>
4379681Sjoerg#include <regex.h>
44100202Sbde#include <stdint.h>
454Srgrimes#include <stdio.h>
4637415Scharnier#include <stdlib.h>
4716561Salex#include <string.h>
4816561Salex#include <unistd.h>
494Srgrimes
50227081Sedstatic int iotest;
514Srgrimes
52217808Ssobomax#define NO_DISK_SECTORS ((u_int32_t)-1)
53217808Ssobomax#define NO_TRACK_CYLINDERS 1023
54217808Ssobomax#define NO_TRACK_HEADS 255
55217808Ssobomax#define NO_TRACK_SECTORS 63
564Srgrimes#define LBUF 100
574Srgrimesstatic char lbuf[LBUF];
584Srgrimes
594Srgrimes/*
604Srgrimes *
614Srgrimes * Ported to 386bsd by Julian Elischer  Thu Oct 15 20:26:46 PDT 1992
624Srgrimes *
634Srgrimes * 14-Dec-89  Robert Baron (rvb) at Carnegie-Mellon University
644Srgrimes *	Copyright (c) 1989	Robert. V. Baron
654Srgrimes *	Created.
664Srgrimes */
674Srgrimes
68217808Ssobomax#define Decimal(str, ans, tmp, maxval) if (decimal(str, &tmp, ans, maxval)) ans = tmp
694Srgrimes
704Srgrimes#define RoundCyl(x) ((((x) + cylsecs - 1) / cylsecs) * cylsecs)
714Srgrimes
7220061Ssos#define MAX_SEC_SIZE 2048	/* maximum section size that is supported */
7320061Ssos#define MIN_SEC_SIZE 512	/* the sector size to start sensing at */
7493394Sphkstatic int secsize = 0;		/* the sensed sector size */
754Srgrimes
7693394Sphkstatic char *disk;
7710514Sjoerg
7893394Sphkstatic int cyls, sectors, heads, cylsecs, disksecs;
794Srgrimes
8093394Sphkstruct mboot {
81145763Snyan	unsigned char *bootinst;  /* boot code */
82145763Snyan	off_t bootinst_size;
83113454Sphk	struct	dos_partition parts[NDOSPART];
844Srgrimes};
854Srgrimes
8693394Sphkstatic struct mboot mboot;
87148035Sphkstatic int fd;
8893394Sphk
894Srgrimes#define ACTIVE 0x80
904Srgrimes
9193394Sphkstatic uint dos_cyls;
9293394Sphkstatic uint dos_heads;
9393394Sphkstatic uint dos_sectors;
9493394Sphkstatic uint dos_cylsecs;
954Srgrimes
964Srgrimes#define DOSSECT(s,c) ((s & 0x3f) | ((c >> 2) & 0xc0))
974Srgrimes#define DOSCYL(c)	(c & 0xff)
984Srgrimes
9919459Sjkh#define MAX_ARGS	10
10019459Sjkh
10119459Sjkhstatic int	current_line_number;
10219459Sjkh
10319459Sjkhstatic int	geom_processed = 0;
10419459Sjkhstatic int	part_processed = 0;
10519459Sjkhstatic int	active_processed = 0;
10619459Sjkh
10719459Sjkhtypedef struct cmd {
10819459Sjkh    char		cmd;
10919459Sjkh    int			n_args;
11019459Sjkh    struct arg {
111224150Srstone	char		argtype;
112224150Srstone	unsigned long	arg_val;
113224150Srstone	char *		arg_str;
11419459Sjkh    }			args[MAX_ARGS];
11519459Sjkh} CMD;
11619459Sjkh
11748282Srnordierstatic int B_flag  = 0;		/* replace boot code */
11857896Simpstatic int I_flag  = 0;		/* use entire disk for FreeBSD */
1194Srgrimesstatic int a_flag  = 0;		/* set active partition */
12048282Srnordierstatic char *b_flag = NULL;	/* path to boot code */
1214Srgrimesstatic int i_flag  = 0;		/* replace partition data */
122181036Sobrienstatic int q_flag  = 0;		/* Be quiet */
1234Srgrimesstatic int u_flag  = 0;		/* update partition data */
12457896Simpstatic int s_flag  = 0;		/* Print a summary and exit */
12595002Strhodesstatic int t_flag  = 0;		/* test only */
12619459Sjkhstatic char *f_flag = NULL;	/* Read config info from file */
12719459Sjkhstatic int v_flag  = 0;		/* Be verbose */
128169143Smaximstatic int print_config_flag = 0;
1294Srgrimes
130187246Sluigi/*
131187246Sluigi * A list of partition types, probably outdated.
132187246Sluigi */
133187246Sluigistatic const char *const part_types[256] = {
134187246Sluigi	[0x00] = "unused",
135187246Sluigi	[0x01] = "Primary DOS with 12 bit FAT",
136187246Sluigi	[0x02] = "XENIX / file system",
137187246Sluigi	[0x03] = "XENIX /usr file system",
138187246Sluigi	[0x04] = "Primary DOS with 16 bit FAT (< 32MB)",
139187246Sluigi	[0x05] = "Extended DOS",
140187248Sluigi	[0x06] = "Primary DOS, 16 bit FAT (>= 32MB)",
141187248Sluigi	[0x07] = "NTFS, OS/2 HPFS, QNX-2 (16 bit) or Advanced UNIX",
142187246Sluigi	[0x08] = "AIX file system or SplitDrive",
143187246Sluigi	[0x09] = "AIX boot partition or Coherent",
144187246Sluigi	[0x0A] = "OS/2 Boot Manager, OPUS or Coherent swap",
145187246Sluigi	[0x0B] = "DOS or Windows 95 with 32 bit FAT",
146187246Sluigi	[0x0C] = "DOS or Windows 95 with 32 bit FAT (LBA)",
147187246Sluigi	[0x0E] = "Primary 'big' DOS (>= 32MB, LBA)",
148187246Sluigi	[0x0F] = "Extended DOS (LBA)",
149187246Sluigi	[0x10] = "OPUS",
150187246Sluigi	[0x11] = "OS/2 BM: hidden DOS with 12-bit FAT",
151187246Sluigi	[0x12] = "Compaq diagnostics",
152187246Sluigi	[0x14] = "OS/2 BM: hidden DOS with 16-bit FAT (< 32MB)",
153187246Sluigi	[0x16] = "OS/2 BM: hidden DOS with 16-bit FAT (>= 32MB)",
154187246Sluigi	[0x17] = "OS/2 BM: hidden IFS (e.g. HPFS)",
155187246Sluigi	[0x18] = "AST Windows swapfile",
156187248Sluigi	[0x1b] = "ASUS Recovery partition (NTFS)",
157187246Sluigi	[0x24] = "NEC DOS",
158187246Sluigi	[0x3C] = "PartitionMagic recovery",
159187246Sluigi	[0x39] = "plan9",
160187246Sluigi	[0x40] = "VENIX 286",
161187246Sluigi	[0x41] = "Linux/MINIX (sharing disk with DRDOS)",
162187246Sluigi	[0x42] = "SFS or Linux swap (sharing disk with DRDOS)",
163187246Sluigi	[0x43] = "Linux native (sharing disk with DRDOS)",
164187246Sluigi	[0x4D] = "QNX 4.2 Primary",
165187246Sluigi	[0x4E] = "QNX 4.2 Secondary",
166187246Sluigi	[0x4F] = "QNX 4.2 Tertiary",
167187246Sluigi	[0x50] = "DM (disk manager)",
168187246Sluigi	[0x51] = "DM6 Aux1 (or Novell)",
169187246Sluigi	[0x52] = "CP/M or Microport SysV/AT",
170187246Sluigi	[0x53] = "DM6 Aux3",
171187246Sluigi	[0x54] = "DM6",
172187246Sluigi	[0x55] = "EZ-Drive (disk manager)",
173187246Sluigi	[0x56] = "Golden Bow (disk manager)",
174187246Sluigi	[0x5c] = "Priam Edisk (disk manager)", /* according to S. Widlake */
175187246Sluigi	[0x61] = "SpeedStor",
176187246Sluigi	[0x63] = "System V/386 (such as ISC UNIX), GNU HURD or Mach",
177187246Sluigi	[0x64] = "Novell Netware/286 2.xx",
178187246Sluigi	[0x65] = "Novell Netware/386 3.xx",
179187246Sluigi	[0x70] = "DiskSecure Multi-Boot",
180187246Sluigi	[0x75] = "PCIX",
181187246Sluigi	[0x77] = "QNX4.x",
182187246Sluigi	[0x78] = "QNX4.x 2nd part",
183187246Sluigi	[0x79] = "QNX4.x 3rd part",
184187246Sluigi	[0x80] = "Minix until 1.4a",
185187246Sluigi	[0x81] = "Minix since 1.4b, early Linux partition or Mitac disk manager",
186187246Sluigi	[0x82] = "Linux swap or Solaris x86",
187187246Sluigi	[0x83] = "Linux native",
188187246Sluigi	[0x84] = "OS/2 hidden C: drive",
189187246Sluigi	[0x85] = "Linux extended",
190187246Sluigi	[0x86] = "NTFS volume set??",
191187246Sluigi	[0x87] = "NTFS volume set??",
192187246Sluigi	[0x93] = "Amoeba file system",
193187246Sluigi	[0x94] = "Amoeba bad block table",
194187246Sluigi	[0x9F] = "BSD/OS",
195187246Sluigi	[0xA0] = "Suspend to Disk",
196187246Sluigi	[0xA5] = "FreeBSD/NetBSD/386BSD",
197187246Sluigi	[0xA6] = "OpenBSD",
198187246Sluigi	[0xA7] = "NeXTSTEP",
199187246Sluigi	[0xA9] = "NetBSD",
200187246Sluigi	[0xAC] = "IBM JFS",
201187246Sluigi	[0xAF] = "HFS+",
202187246Sluigi	[0xB7] = "BSDI BSD/386 file system",
203187246Sluigi	[0xB8] = "BSDI BSD/386 swap",
204187246Sluigi	[0xBE] = "Solaris x86 boot",
205187246Sluigi	[0xBF] = "Solaris x86 (new)",
206187246Sluigi	[0xC1] = "DRDOS/sec with 12-bit FAT",
207187246Sluigi	[0xC4] = "DRDOS/sec with 16-bit FAT (< 32MB)",
208187246Sluigi	[0xC6] = "DRDOS/sec with 16-bit FAT (>= 32MB)",
209187246Sluigi	[0xC7] = "Syrinx",
210187246Sluigi	[0xDB] = "CP/M, Concurrent CP/M, Concurrent DOS or CTOS",
211187248Sluigi	[0xDE] = "DELL Utilities - FAT filesystem",
212187246Sluigi	[0xE1] = "DOS access or SpeedStor with 12-bit FAT extended partition",
213187246Sluigi	[0xE3] = "DOS R/O or SpeedStor",
214187246Sluigi	[0xE4] = "SpeedStor with 16-bit FAT extended partition < 1024 cyl.",
215187246Sluigi	[0xEB] = "BeOS file system",
216187246Sluigi	[0xEE] = "EFI GPT",
217187246Sluigi	[0xEF] = "EFI System Partition",
218187246Sluigi	[0xF1] = "SpeedStor",
219187246Sluigi	[0xF2] = "DOS 3.3+ Secondary",
220187246Sluigi	[0xF4] = "SpeedStor large partition",
221234345Smarck	[0xFB] = "VMware VMFS",
222187246Sluigi	[0xFE] = "SpeedStor >1024 cyl. or LANstep",
223187246Sluigi	[0xFF] = "Xenix bad blocks table",
2244Srgrimes};
2254Srgrimes
226187246Sluigistatic const char *
227187246Sluigiget_type(int t)
228187246Sluigi{
229187246Sluigi	const char *ret;
230187246Sluigi
231187246Sluigi	ret = (t >= 0 && t <= 255) ? part_types[t] : NULL;
232187246Sluigi	return ret ? ret : "unknown";
233187246Sluigi}
234187246Sluigi
235187246Sluigi
236227292Saestatic int geom_class_available(const char *);
237187239Sluigistatic void print_s0(void);
238187241Sluigistatic void print_part(const struct dos_partition *);
23916561Salexstatic void init_sector0(unsigned long start);
24019459Sjkhstatic void init_boot(void);
24116561Salexstatic void change_part(int i);
24293394Sphkstatic void print_params(void);
24316561Salexstatic void change_active(int which);
24493394Sphkstatic void change_code(void);
24593394Sphkstatic void get_params_to_use(void);
24679681Sjoergstatic char *get_rootdisk(void);
24781164Siedowsestatic void dos(struct dos_partition *partp);
24893394Sphkstatic int open_disk(int flag);
24916561Salexstatic ssize_t read_disk(off_t sector, void *buf);
250148035Sphkstatic int write_disk(off_t sector, void *buf);
25193394Sphkstatic int get_params(void);
25293394Sphkstatic int read_s0(void);
25393394Sphkstatic int write_s0(void);
25493394Sphkstatic int ok(const char *str);
255217808Ssobomaxstatic int decimal(const char *str, int *num, int deflt, uint32_t maxval);
25619459Sjkhstatic int read_config(char *config_file);
25719459Sjkhstatic void reset_boot(void);
25865054Sjhbstatic int sanitize_partition(struct dos_partition *);
25937415Scharnierstatic void usage(void);
2604Srgrimes
26116561Salexint
26216561Salexmain(int argc, char *argv[])
2634Srgrimes{
26448282Srnordier	int	c, i;
26593394Sphk	int	partition = -1;
26693394Sphk	struct	dos_partition *partp;
2674Srgrimes
268181036Sobrien	while ((c = getopt(argc, argv, "BIab:f:ipqstuv1234")) != -1)
26948282Srnordier		switch (c) {
27048282Srnordier		case 'B':
27148282Srnordier			B_flag = 1;
2724Srgrimes			break;
27357896Simp		case 'I':
27457896Simp			I_flag = 1;
27557896Simp			break;
27648282Srnordier		case 'a':
27748282Srnordier			a_flag = 1;
27848282Srnordier			break;
27948282Srnordier		case 'b':
28048282Srnordier			b_flag = optarg;
28148282Srnordier			break;
28248282Srnordier		case 'f':
28348282Srnordier			f_flag = optarg;
28448282Srnordier			break;
28548282Srnordier		case 'i':
28648282Srnordier			i_flag = 1;
28748282Srnordier			break;
288169143Smaxim		case 'p':
289169143Smaxim			print_config_flag = 1;
290169143Smaxim			break;
291181036Sobrien		case 'q':
292181036Sobrien			q_flag = 1;
293181036Sobrien			break;
29457896Simp		case 's':
29557896Simp			s_flag = 1;
29657896Simp			break;
29748282Srnordier		case 't':
29848282Srnordier			t_flag = 1;
29948282Srnordier			break;
30048282Srnordier		case 'u':
30148282Srnordier			u_flag = 1;
30248282Srnordier			break;
30348282Srnordier		case 'v':
30448282Srnordier			v_flag = 1;
30548282Srnordier			break;
30648282Srnordier		case '1':
30748282Srnordier		case '2':
30848282Srnordier		case '3':
30948282Srnordier		case '4':
31048282Srnordier			partition = c - '0';
31148282Srnordier			break;
31248282Srnordier		default:
31348282Srnordier			usage();
3144Srgrimes		}
31548282Srnordier	if (f_flag || i_flag)
31648282Srnordier		u_flag = 1;
31748282Srnordier	if (t_flag)
31848282Srnordier		v_flag = 1;
31948282Srnordier	argc -= optind;
32048282Srnordier	argv += optind;
3214Srgrimes
32279681Sjoerg	if (argc == 0) {
32379681Sjoerg		disk = get_rootdisk();
32479681Sjoerg	} else {
325182844Slulf		disk = g_device_path(argv[0]);
326182844Slulf		if (disk == NULL)
327187203Sluigi			err(1, "unable to get correct path for %s", argv[0]);
32810514Sjoerg	}
32979681Sjoerg	if (open_disk(u_flag) < 0)
33079681Sjoerg		err(1, "cannot open disk %s", disk);
3314Srgrimes
33263027Sjhb	/* (abu)use mboot.bootinst to probe for the sector size */
33363027Sjhb	if ((mboot.bootinst = malloc(MAX_SEC_SIZE)) == NULL)
33463027Sjhb		err(1, "cannot allocate buffer to determine disk sector size");
335159757Ssimon	if (read_disk(0, mboot.bootinst) == -1)
336159757Ssimon		errx(1, "could not detect sector size");
33763329Sjhb	free(mboot.bootinst);
33863329Sjhb	mboot.bootinst = NULL;
33963027Sjhb
340169143Smaxim	if (print_config_flag) {
341169143Smaxim		if (read_s0())
342169143Smaxim			err(1, "read_s0");
343169143Smaxim
344169143Smaxim		printf("# %s\n", disk);
345169143Smaxim		printf("g c%d h%d s%d\n", dos_cyls, dos_heads, dos_sectors);
346169143Smaxim
347169143Smaxim		for (i = 0; i < NDOSPART; i++) {
348187203Sluigi			partp = &mboot.parts[i];
349169143Smaxim
350169143Smaxim			if (partp->dp_start == 0 && partp->dp_size == 0)
351169143Smaxim				continue;
352169143Smaxim
353169143Smaxim			printf("p %d 0x%02x %lu %lu\n", i + 1, partp->dp_typ,
354169143Smaxim			    (u_long)partp->dp_start, (u_long)partp->dp_size);
355169143Smaxim
356169143Smaxim			/* Fill flags for the partition. */
357169143Smaxim			if (partp->dp_flag & 0x80)
358169143Smaxim				printf("a %d\n", i + 1);
359169143Smaxim		}
360169143Smaxim		exit(0);
361169143Smaxim	}
36293394Sphk	if (s_flag) {
36357896Simp		if (read_s0())
36457896Simp			err(1, "read_s0");
36557896Simp		printf("%s: %d cyl %d hd %d sec\n", disk, dos_cyls, dos_heads,
36657896Simp		    dos_sectors);
36757896Simp		printf("Part  %11s %11s Type Flags\n", "Start", "Size");
36857896Simp		for (i = 0; i < NDOSPART; i++) {
369187203Sluigi			partp = &mboot.parts[i];
37057896Simp			if (partp->dp_start == 0 && partp->dp_size == 0)
37157896Simp				continue;
37257896Simp			printf("%4d: %11lu %11lu 0x%02x 0x%02x\n", i + 1,
37357896Simp			    (u_long) partp->dp_start,
37457896Simp			    (u_long) partp->dp_size, partp->dp_typ,
37557896Simp			    partp->dp_flag);
37657896Simp		}
37757896Simp		exit(0);
37857896Simp	}
37957896Simp
3804Srgrimes	printf("******* Working on device %s *******\n",disk);
38119459Sjkh
38293394Sphk	if (I_flag) {
38350215Sphk		read_s0();
38450215Sphk		reset_boot();
385187203Sluigi		partp = &mboot.parts[0];
38650215Sphk		partp->dp_typ = DOSPTYP_386BSD;
38750215Sphk		partp->dp_flag = ACTIVE;
38850215Sphk		partp->dp_start = dos_sectors;
38965054Sjhb		partp->dp_size = (disksecs / dos_cylsecs) * dos_cylsecs -
39065054Sjhb		    dos_sectors;
39181164Siedowse		dos(partp);
39250215Sphk		if (v_flag)
393187239Sluigi			print_s0();
39495002Strhodes		if (!t_flag)
39595002Strhodes			write_s0();
39650215Sphk		exit(0);
39750215Sphk	}
39893394Sphk	if (f_flag) {
39919459Sjkh	    if (read_s0() || i_flag)
40019459Sjkh		reset_boot();
40119459Sjkh	    if (!read_config(f_flag))
40219459Sjkh		exit(1);
40319459Sjkh	    if (v_flag)
404187239Sluigi		print_s0();
40519459Sjkh	    if (!t_flag)
40619459Sjkh		write_s0();
40793394Sphk	} else {
40819459Sjkh	    if(u_flag)
40919459Sjkh		get_params_to_use();
41019459Sjkh	    else
4114Srgrimes		print_params();
4124Srgrimes
41319459Sjkh	    if (read_s0())
41465054Sjhb		init_sector0(dos_sectors);
4154Srgrimes
41620061Ssos	    printf("Media sector size is %d\n", secsize);
41719459Sjkh	    printf("Warning: BIOS sector numbering starts with sector 1\n");
41819459Sjkh	    printf("Information from DOS bootblock is:\n");
41919459Sjkh	    if (partition == -1)
42026421Sbrian		for (i = 1; i <= NDOSPART; i++)
42119459Sjkh		    change_part(i);
42219459Sjkh	    else
4234Srgrimes		change_part(partition);
4244Srgrimes
42519459Sjkh	    if (u_flag || a_flag)
4264Srgrimes		change_active(partition);
4274Srgrimes
42848282Srnordier	    if (B_flag)
42943054Srnordier		change_code();
43043054Srnordier
43148282Srnordier	    if (u_flag || a_flag || B_flag) {
43295002Strhodes		if (!t_flag) {
43319459Sjkh		    printf("\nWe haven't changed the partition table yet.  ");
43419459Sjkh		    printf("This is your last chance.\n");
43519459Sjkh		}
436187239Sluigi		print_s0();
43795002Strhodes		if (!t_flag) {
43819459Sjkh		    if (ok("Should we write new partition table?"))
4394Srgrimes			write_s0();
440145763Snyan		} else {
44119459Sjkh		    printf("\n-t flag specified -- partition table not written.\n");
44219459Sjkh		}
44319459Sjkh	    }
4444Srgrimes	}
4454Srgrimes
4464Srgrimes	exit(0);
44737415Scharnier}
4484Srgrimes
44937415Scharnierstatic void
45037415Scharnierusage()
45137415Scharnier{
45248282Srnordier	fprintf(stderr, "%s%s",
453181036Sobrien		"usage: fdisk [-BIaipqstu] [-b bootcode] [-1234] [disk]\n",
45448282Srnordier 		"       fdisk -f configfile [-itv] [disk]\n");
45537415Scharnier        exit(1);
4564Srgrimes}
4574Srgrimes
45816561Salexstatic void
459187239Sluigiprint_s0(void)
4604Srgrimes{
46193394Sphk	int	i;
4624Srgrimes
4634Srgrimes	print_params();
4644Srgrimes	printf("Information from DOS bootblock is:\n");
465187241Sluigi	for (i = 1; i <= NDOSPART; i++) {
466187241Sluigi		printf("%d: ", i);
467187241Sluigi		print_part(&mboot.parts[i - 1]);
468187241Sluigi	}
4694Srgrimes}
4704Srgrimes
47193394Sphkstatic struct dos_partition mtpart;
4724Srgrimes
47316561Salexstatic void
474187241Sluigiprint_part(const struct dos_partition *partp)
4754Srgrimes{
47626389Sgibbs	u_int64_t part_mb;
4774Srgrimes
4784Srgrimes	if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) {
4794Srgrimes		printf("<UNUSED>\n");
4804Srgrimes		return;
4814Srgrimes	}
48226389Sgibbs	/*
48326389Sgibbs	 * Be careful not to overflow.
48426389Sgibbs	 */
48526389Sgibbs	part_mb = partp->dp_size;
48626389Sgibbs	part_mb *= secsize;
48726389Sgibbs	part_mb /= (1024 * 1024);
48890866Sjoe	printf("sysid %d (%#04x),(%s)\n", partp->dp_typ, partp->dp_typ,
48990866Sjoe	    get_type(partp->dp_typ));
490100202Sbde	printf("    start %lu, size %lu (%ju Meg), flag %x%s\n",
49137244Sbde		(u_long)partp->dp_start,
492226908Sjmg		(u_long)partp->dp_size,
493100202Sbde		(uintmax_t)part_mb,
49434952Sobrien		partp->dp_flag,
49534952Sobrien		partp->dp_flag == ACTIVE ? " (active)" : "");
49669371Sobrien	printf("\tbeg: cyl %d/ head %d/ sector %d;\n\tend: cyl %d/ head %d/ sector %d\n"
4974Srgrimes		,DPCYL(partp->dp_scyl, partp->dp_ssect)
49869371Sobrien		,partp->dp_shd
4994Srgrimes		,DPSECT(partp->dp_ssect)
5004Srgrimes		,DPCYL(partp->dp_ecyl, partp->dp_esect)
50169371Sobrien		,partp->dp_ehd
50269371Sobrien		,DPSECT(partp->dp_esect));
5034Srgrimes}
5044Srgrimes
50519459Sjkh
50616561Salexstatic void
50719459Sjkhinit_boot(void)
50819459Sjkh{
50995860Speter#ifndef __ia64__
51048282Srnordier	const char *fname;
51193394Sphk	int fdesc, n;
51263027Sjhb	struct stat sb;
51348282Srnordier
51448282Srnordier	fname = b_flag ? b_flag : "/boot/mbr";
51593394Sphk	if ((fdesc = open(fname, O_RDONLY)) == -1 ||
51693394Sphk	    fstat(fdesc, &sb) == -1)
51763027Sjhb		err(1, "%s", fname);
518226907Sjmg	if (sb.st_size == 0)
519226907Sjmg		errx(1, "%s is empty, must not be.", fname);
52063027Sjhb	if ((mboot.bootinst_size = sb.st_size) % secsize != 0)
52163027Sjhb		errx(1, "%s: length must be a multiple of sector size", fname);
52263218Sache	if (mboot.bootinst != NULL)
52363218Sache		free(mboot.bootinst);
52463027Sjhb	if ((mboot.bootinst = malloc(mboot.bootinst_size = sb.st_size)) == NULL)
52563027Sjhb		errx(1, "%s: unable to allocate read buffer", fname);
52693394Sphk	if ((n = read(fdesc, mboot.bootinst, mboot.bootinst_size)) == -1 ||
52793394Sphk	    close(fdesc))
52848282Srnordier		err(1, "%s", fname);
52963027Sjhb	if (n != mboot.bootinst_size)
53063027Sjhb		errx(1, "%s: short read", fname);
53195860Speter#else
53295860Speter	if (mboot.bootinst != NULL)
53395860Speter		free(mboot.bootinst);
53495860Speter	mboot.bootinst_size = secsize;
53595860Speter	if ((mboot.bootinst = malloc(mboot.bootinst_size)) == NULL)
53695860Speter		errx(1, "unable to allocate boot block buffer");
53795860Speter	memset(mboot.bootinst, 0, mboot.bootinst_size);
538113454Sphk	le16enc(&mboot.bootinst[DOSMAGICOFFSET], DOSMAGIC);
53995860Speter#endif
54019459Sjkh}
54119459Sjkh
54219459Sjkh
54319459Sjkhstatic void
54416561Salexinit_sector0(unsigned long start)
5454Srgrimes{
546187203Sluigi	struct dos_partition *partp = &mboot.parts[0];
5474Srgrimes
54819459Sjkh	init_boot();
5494Srgrimes
5504Srgrimes	partp->dp_typ = DOSPTYP_386BSD;
5514Srgrimes	partp->dp_flag = ACTIVE;
55265054Sjhb	start = ((start + dos_sectors - 1) / dos_sectors) * dos_sectors;
55363027Sjhb	if(start == 0)
55463027Sjhb		start = dos_sectors;
5554Srgrimes	partp->dp_start = start;
55665054Sjhb	partp->dp_size = (disksecs / dos_cylsecs) * dos_cylsecs - start;
5574Srgrimes
55881164Siedowse	dos(partp);
5594Srgrimes}
5604Srgrimes
56116561Salexstatic void
56216561Salexchange_part(int i)
5634Srgrimes{
564187203Sluigi    struct dos_partition *partp = &mboot.parts[i - 1];
5654Srgrimes
5664Srgrimes    printf("The data for partition %d is:\n", i);
567187241Sluigi    print_part(partp);
5684Srgrimes
5694Srgrimes    if (u_flag && ok("Do you want to change it?")) {
5704Srgrimes	int tmp;
5714Srgrimes
5724Srgrimes	if (i_flag) {
573187241Sluigi	    bzero(partp, sizeof (*partp));
574187241Sluigi	    if (i == 1) {
575187241Sluigi		init_sector0(1);
576187241Sluigi		printf("\nThe static data for the slice 1 has been reinitialized to:\n");
577187241Sluigi		print_part(partp);
578187241Sluigi	    }
5794Srgrimes	}
5804Srgrimes
5814Srgrimes	do {
582217808Ssobomax		Decimal("sysid (165=FreeBSD)", partp->dp_typ, tmp, 255);
583217808Ssobomax		Decimal("start", partp->dp_start, tmp, NO_DISK_SECTORS);
584217808Ssobomax		Decimal("size", partp->dp_size, tmp, NO_DISK_SECTORS);
58581164Siedowse		if (!sanitize_partition(partp)) {
58681164Siedowse			warnx("ERROR: failed to adjust; setting sysid to 0");
58781164Siedowse			partp->dp_typ = 0;
58881164Siedowse		}
5894Srgrimes
59036262Sjraynard		if (ok("Explicitly specify beg/end address ?"))
5914Srgrimes		{
5924Srgrimes			int	tsec,tcyl,thd;
5934Srgrimes			tcyl = DPCYL(partp->dp_scyl,partp->dp_ssect);
5944Srgrimes			thd = partp->dp_shd;
5954Srgrimes			tsec = DPSECT(partp->dp_ssect);
596217808Ssobomax			Decimal("beginning cylinder", tcyl, tmp, NO_TRACK_CYLINDERS);
597217808Ssobomax			Decimal("beginning head", thd, tmp, NO_TRACK_HEADS);
598217808Ssobomax			Decimal("beginning sector", tsec, tmp, NO_TRACK_SECTORS);
5994Srgrimes			partp->dp_scyl = DOSCYL(tcyl);
6004Srgrimes			partp->dp_ssect = DOSSECT(tsec,tcyl);
6014Srgrimes			partp->dp_shd = thd;
6024Srgrimes
6034Srgrimes			tcyl = DPCYL(partp->dp_ecyl,partp->dp_esect);
6044Srgrimes			thd = partp->dp_ehd;
6054Srgrimes			tsec = DPSECT(partp->dp_esect);
606217808Ssobomax			Decimal("ending cylinder", tcyl, tmp, NO_TRACK_CYLINDERS);
607217808Ssobomax			Decimal("ending head", thd, tmp, NO_TRACK_HEADS);
608217808Ssobomax			Decimal("ending sector", tsec, tmp, NO_TRACK_SECTORS);
6094Srgrimes			partp->dp_ecyl = DOSCYL(tcyl);
6104Srgrimes			partp->dp_esect = DOSSECT(tsec,tcyl);
6114Srgrimes			partp->dp_ehd = thd;
61281164Siedowse		} else
61381164Siedowse			dos(partp);
6148871Srgrimes
615187241Sluigi		print_part(partp);
6164Srgrimes	} while (!ok("Are we happy with this entry?"));
6174Srgrimes    }
618145763Snyan}
6194Srgrimes
62016561Salexstatic void
6214Srgrimesprint_params()
6224Srgrimes{
6234Srgrimes	printf("parameters extracted from in-core disklabel are:\n");
6244Srgrimes	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
6254Srgrimes			,cyls,heads,sectors,cylsecs);
626145763Snyan	if (dos_cyls > 1023 || dos_heads > 255 || dos_sectors > 63)
62720061Ssos		printf("Figures below won't work with BIOS for partitions not in cyl 1\n");
6284Srgrimes	printf("parameters to be used for BIOS calculations are:\n");
6294Srgrimes	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
6304Srgrimes		,dos_cyls,dos_heads,dos_sectors,dos_cylsecs);
6314Srgrimes}
6324Srgrimes
63316561Salexstatic void
63416561Salexchange_active(int which)
6354Srgrimes{
63681164Siedowse	struct dos_partition *partp = &mboot.parts[0];
63781164Siedowse	int active, i, new, tmp;
6384Srgrimes
63981164Siedowse	active = -1;
64081164Siedowse	for (i = 0; i < NDOSPART; i++) {
64181164Siedowse		if ((partp[i].dp_flag & ACTIVE) == 0)
64281164Siedowse			continue;
64381164Siedowse		printf("Partition %d is marked active\n", i + 1);
64481164Siedowse		if (active == -1)
64581164Siedowse			active = i + 1;
64681164Siedowse	}
6474Srgrimes	if (a_flag && which != -1)
6484Srgrimes		active = which;
64981164Siedowse	else if (active == -1)
65081164Siedowse		active = 1;
65181164Siedowse
6523723Sbde	if (!ok("Do you want to change the active partition?"))
6533723Sbde		return;
65434952Sobriensetactive:
65534952Sobrien	do {
65681164Siedowse		new = active;
657217714Ssobomax		Decimal("active partition", new, tmp, 0);
65881164Siedowse		if (new < 1 || new > 4) {
65934952Sobrien			printf("Active partition number must be in range 1-4."
66034952Sobrien					"  Try again.\n");
66134952Sobrien			goto setactive;
66234952Sobrien		}
66381164Siedowse		active = new;
66434952Sobrien	} while (!ok("Are you happy with this choice"));
6654Srgrimes	for (i = 0; i < NDOSPART; i++)
6664Srgrimes		partp[i].dp_flag = 0;
66726421Sbrian	if (active > 0 && active <= NDOSPART)
66826421Sbrian		partp[active-1].dp_flag = ACTIVE;
6694Srgrimes}
6704Srgrimes
67143054Srnordierstatic void
67243054Srnordierchange_code()
67343054Srnordier{
67443054Srnordier	if (ok("Do you want to change the boot code?"))
67543054Srnordier		init_boot();
67643054Srnordier}
67743054Srnordier
67816561Salexvoid
6794Srgrimesget_params_to_use()
6804Srgrimes{
6814Srgrimes	int	tmp;
6824Srgrimes	print_params();
6834Srgrimes	if (ok("Do you want to change our idea of what BIOS thinks ?"))
6844Srgrimes	{
6854Srgrimes		do
6864Srgrimes		{
687217714Ssobomax			Decimal("BIOS's idea of #cylinders", dos_cyls, tmp, 0);
688217714Ssobomax			Decimal("BIOS's idea of #heads", dos_heads, tmp, 0);
689217714Ssobomax			Decimal("BIOS's idea of #sectors", dos_sectors, tmp, 0);
6904Srgrimes			dos_cylsecs = dos_heads * dos_sectors;
6914Srgrimes			print_params();
6924Srgrimes		}
6934Srgrimes		while(!ok("Are you happy with this choice"));
6944Srgrimes	}
6954Srgrimes}
6964Srgrimes
69719459Sjkh
6984Srgrimes/***********************************************\
6994Srgrimes* Change real numbers into strange dos numbers	*
7004Srgrimes\***********************************************/
70116561Salexstatic void
70293394Sphkdos(struct dos_partition *partp)
7034Srgrimes{
70481164Siedowse	int cy, sec;
70581164Siedowse	u_int32_t end;
7064Srgrimes
70788714Siedowse	if (partp->dp_typ == 0 && partp->dp_start == 0 && partp->dp_size == 0) {
70888714Siedowse		memcpy(partp, &mtpart, sizeof(*partp));
7093723Sbde		return;
7103723Sbde	}
7113723Sbde
71281164Siedowse	/* Start c/h/s. */
71381164Siedowse	partp->dp_shd = partp->dp_start % dos_cylsecs / dos_sectors;
71481164Siedowse	cy = partp->dp_start / dos_cylsecs;
71581164Siedowse	sec = partp->dp_start % dos_sectors + 1;
71681164Siedowse	partp->dp_scyl = DOSCYL(cy);
71781164Siedowse	partp->dp_ssect = DOSSECT(sec, cy);
7184Srgrimes
71981164Siedowse	/* End c/h/s. */
72081164Siedowse	end = partp->dp_start + partp->dp_size - 1;
72181164Siedowse	partp->dp_ehd = end % dos_cylsecs / dos_sectors;
72281164Siedowse	cy = end / dos_cylsecs;
72381164Siedowse	sec = end % dos_sectors + 1;
72481164Siedowse	partp->dp_ecyl = DOSCYL(cy);
72581164Siedowse	partp->dp_esect = DOSSECT(sec, cy);
7264Srgrimes}
7274Srgrimes
72816561Salexstatic int
72993394Sphkopen_disk(int flag)
7304Srgrimes{
731148035Sphk	int rwmode;
7324Srgrimes
733182844Slulf	/* Write mode if one of these flags are set. */
734182844Slulf	rwmode = (a_flag || I_flag || B_flag || flag);
735182844Slulf	fd = g_open(disk, rwmode);
736182844Slulf	/* If the mode fails, try read-only if we didn't. */
737182844Slulf	if (fd == -1 && errno == EPERM && rwmode)
738182844Slulf		fd = g_open(disk, 0);
739108395Sphk	if (fd == -1 && errno == ENXIO)
740108395Sphk		return -2;
741108395Sphk	if (fd == -1) {
74237415Scharnier		warnx("can't open device %s", disk);
7434Srgrimes		return -1;
7444Srgrimes	}
74593394Sphk	if (get_params() == -1) {
74637415Scharnier		warnx("can't get disk parameters on %s", disk);
7474Srgrimes		return -1;
7484Srgrimes	}
7494Srgrimes	return fd;
7504Srgrimes}
7514Srgrimes
75216561Salexstatic ssize_t
75316561Salexread_disk(off_t sector, void *buf)
7544Srgrimes{
755145763Snyan
756145763Snyan	lseek(fd, (sector * 512), 0);
757145763Snyan	if (secsize == 0)
758145763Snyan		for (secsize = MIN_SEC_SIZE; secsize <= MAX_SEC_SIZE;
759145763Snyan		     secsize *= 2) {
76020061Ssos			/* try the read */
76120061Ssos			int size = read(fd, buf, secsize);
762145763Snyan			if (size == secsize)
76320061Ssos				/* it worked so return */
76420061Ssos				return secsize;
765145763Snyan		}
76620061Ssos	else
767145763Snyan		return read(fd, buf, secsize);
76820061Ssos
76920061Ssos	/* we failed to read at any of the sizes */
77020061Ssos	return -1;
7714Srgrimes}
7724Srgrimes
773148035Sphkstatic int
774227292Saegeom_class_available(const char *name)
775227292Sae{
776227292Sae	struct gclass *class;
777227292Sae	struct gmesh mesh;
778227292Sae	int error;
779227292Sae
780227292Sae	error = geom_gettree(&mesh);
781227292Sae	if (error != 0)
782227292Sae		errc(1, error, "Cannot get GEOM tree");
783227292Sae
784227292Sae	LIST_FOREACH(class, &mesh.lg_class, lg_class) {
785227292Sae		if (strcmp(class->lg_name, name) == 0) {
786227292Sae			geom_deletetree(&mesh);
787227292Sae			return (1);
788227292Sae		}
789227292Sae	}
790227292Sae
791227292Sae	geom_deletetree(&mesh);
792227292Sae
793227292Sae	return (0);
794227292Sae}
795227292Sae
796227292Saestatic int
79716561Salexwrite_disk(off_t sector, void *buf)
7984Srgrimes{
799148035Sphk	struct gctl_req *grq;
800182844Slulf	const char *errmsg;
801227292Sae	char *pname;
802227292Sae	int error;
803108395Sphk
804227292Sae	/* Check that GEOM_MBR is available */
805227292Sae	if (geom_class_available("MBR") != 0) {
806227292Sae		grq = gctl_get_handle();
807227292Sae		gctl_ro_param(grq, "verb", -1, "write MBR");
808227292Sae		gctl_ro_param(grq, "class", -1, "MBR");
809227292Sae		pname = g_providername(fd);
810227292Sae		if (pname == NULL) {
811227292Sae			warn("Error getting providername for %s", disk);
812227292Sae			return (-1);
813227292Sae		}
814227292Sae		gctl_ro_param(grq, "geom", -1, pname);
815227292Sae		gctl_ro_param(grq, "data", secsize, buf);
816227292Sae		errmsg = gctl_issue(grq);
817227292Sae		free(pname);
818227292Sae		if (errmsg == NULL) {
819227292Sae			gctl_free(grq);
820227292Sae			return(0);
821227292Sae		}
822227292Sae		if (!q_flag)
823227292Sae			warnx("GEOM_MBR: %s", errmsg);
824150249Srodrigc		gctl_free(grq);
825227292Sae	} else {
826227295Sae		/*
827227295Sae		 * Try to write MBR directly. This may help when disk
828227292Sae		 * is not in use.
829227292Sae		 * XXX: hardcoded sectorsize
830227292Sae		 */
831227292Sae		error = pwrite(fd, buf, secsize, (sector * 512));
832227292Sae		if (error == secsize)
833227292Sae			return (0);
834150249Srodrigc	}
835182844Slulf
836227295Sae	/*
837227295Sae	 * GEOM_MBR is not available or failed to write MBR.
838227292Sae	 * Now check that we have GEOM_PART and recommend to use gpart (8).
839227292Sae	 */
840227292Sae	if (geom_class_available("PART") != 0)
841227292Sae		warnx("Failed to write MBR. Try to use gpart(8).");
842227292Sae	else
843227292Sae		warnx("Failed to write sector zero");
844148035Sphk	return(EINVAL);
8454Srgrimes}
8464Srgrimes
84716561Salexstatic int
84816561Salexget_params()
8494Srgrimes{
850103665Sphk	int error;
851103665Sphk	u_int u;
852103665Sphk	off_t o;
8534Srgrimes
854103665Sphk	error = ioctl(fd, DIOCGFWSECTORS, &u);
855103665Sphk	if (error == 0)
856103665Sphk		sectors = dos_sectors = u;
857112826Sphk	else
858112826Sphk		sectors = dos_sectors = 63;
859112826Sphk
860103665Sphk	error = ioctl(fd, DIOCGFWHEADS, &u);
861103665Sphk	if (error == 0)
862103665Sphk		heads = dos_heads = u;
863112826Sphk	else
864112826Sphk		heads = dos_heads = 255;
865103665Sphk
8662810Sbde	dos_cylsecs = cylsecs = heads * sectors;
8672810Sbde	disksecs = cyls * heads * sectors;
8684Srgrimes
869182844Slulf	u = g_sectorsize(fd);
870182844Slulf	if (u <= 0)
871182844Slulf		return (-1);
8724Srgrimes
873182844Slulf	o = g_mediasize(fd);
874182844Slulf	if (o < 0)
875182844Slulf		return (-1);
876182844Slulf	disksecs = o / u;
877182844Slulf	cyls = dos_cyls = o / (u * dos_heads * dos_sectors);
878103665Sphk
879103665Sphk	return (disksecs);
8804Srgrimes}
8814Srgrimes
88216561Salexstatic int
8834Srgrimesread_s0()
8844Srgrimes{
885113454Sphk	int i;
886113454Sphk
88763218Sache	mboot.bootinst_size = secsize;
88863218Sache	if (mboot.bootinst != NULL)
88963027Sjhb		free(mboot.bootinst);
89063218Sache	if ((mboot.bootinst = malloc(mboot.bootinst_size)) == NULL) {
89163218Sache		warnx("unable to allocate buffer to read fdisk "
89263218Sache		      "partition table");
89363218Sache		return -1;
89463027Sjhb	}
89563027Sjhb	if (read_disk(0, mboot.bootinst) == -1) {
89637415Scharnier		warnx("can't read fdisk partition table");
8974Srgrimes		return -1;
8984Srgrimes	}
899113454Sphk	if (le16dec(&mboot.bootinst[DOSMAGICOFFSET]) != DOSMAGIC) {
90037415Scharnier		warnx("invalid fdisk partition table found");
9014Srgrimes		/* So should we initialize things */
9024Srgrimes		return -1;
9034Srgrimes	}
904113454Sphk	for (i = 0; i < NDOSPART; i++)
905113454Sphk		dos_partition_dec(
906113454Sphk		    &mboot.bootinst[DOSPARTOFF + i * DOSPARTSIZE],
907113454Sphk		    &mboot.parts[i]);
9084Srgrimes	return 0;
9094Srgrimes}
9104Srgrimes
91116561Salexstatic int
9124Srgrimeswrite_s0()
9134Srgrimes{
914113454Sphk	int	sector, i;
91563027Sjhb
9164Srgrimes	if (iotest) {
917187239Sluigi		print_s0();
9184Srgrimes		return 0;
9194Srgrimes	}
920113454Sphk	for(i = 0; i < NDOSPART; i++)
921113454Sphk		dos_partition_enc(&mboot.bootinst[DOSPARTOFF + i * DOSPARTSIZE],
922113454Sphk		    &mboot.parts[i]);
923113454Sphk	le16enc(&mboot.bootinst[DOSMAGICOFFSET], DOSMAGIC);
924226908Sjmg	for(sector = 0; sector < mboot.bootinst_size / secsize; sector++)
92563027Sjhb		if (write_disk(sector,
92663027Sjhb			       &mboot.bootinst[sector * secsize]) == -1) {
92763027Sjhb			warn("can't write fdisk partition table");
92863027Sjhb			return -1;
92963027Sjhb		}
93016561Salex	return(0);
9314Srgrimes}
9324Srgrimes
9334Srgrimes
93416561Salexstatic int
93593394Sphkok(const char *str)
9364Srgrimes{
9374Srgrimes	printf("%s [n] ", str);
93881164Siedowse	fflush(stdout);
93981164Siedowse	if (fgets(lbuf, LBUF, stdin) == NULL)
94081164Siedowse		exit(1);
9414Srgrimes	lbuf[strlen(lbuf)-1] = 0;
9424Srgrimes
9434Srgrimes	if (*lbuf &&
9444Srgrimes		(!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") ||
9454Srgrimes		 !strcmp(lbuf, "y") || !strcmp(lbuf, "Y")))
9464Srgrimes		return 1;
9474Srgrimes	else
9484Srgrimes		return 0;
9494Srgrimes}
9504Srgrimes
95116561Salexstatic int
952217808Ssobomaxdecimal(const char *str, int *num, int deflt, uint32_t maxval)
9534Srgrimes{
954227280Sae	long long acc;
955217714Ssobomax	int c;
95693394Sphk	char *cp;
9574Srgrimes
9584Srgrimes	while (1) {
959227280Sae		acc = 0;
9604Srgrimes		printf("Supply a decimal value for \"%s\" [%d] ", str, deflt);
96181164Siedowse		fflush(stdout);
96281164Siedowse		if (fgets(lbuf, LBUF, stdin) == NULL)
96381164Siedowse			exit(1);
9644Srgrimes		lbuf[strlen(lbuf)-1] = 0;
9654Srgrimes
9664Srgrimes		if (!*lbuf)
9674Srgrimes			return 0;
9684Srgrimes
9694Srgrimes		cp = lbuf;
9704Srgrimes		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
9714Srgrimes		if (!c)
9724Srgrimes			return 0;
97316561Salex		while ((c = *cp++)) {
974217714Ssobomax			if (c <= '9' && c >= '0') {
975225007Sae				if (acc <= maxval || maxval == 0)
976217714Ssobomax					acc = acc * 10 + c - '0';
977217714Ssobomax			} else
9784Srgrimes				break;
9794Srgrimes		}
9804Srgrimes		if (c == ' ' || c == '\t')
9814Srgrimes			while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
9824Srgrimes		if (!c) {
983217808Ssobomax			if (maxval > 0 && acc > maxval) {
984217808Ssobomax				acc = maxval;
985217808Ssobomax				printf("%s exceeds maximum value allowed for "
986217808Ssobomax				  "this field. The value has been reduced "
987217808Ssobomax				  "to %lld\n", lbuf, acc);
988217714Ssobomax			}
9894Srgrimes			*num = acc;
9904Srgrimes			return 1;
9914Srgrimes		} else
99234952Sobrien			printf("%s is an invalid decimal number.  Try again.\n",
9934Srgrimes				lbuf);
9944Srgrimes	}
9954Srgrimes}
9964Srgrimes
9974Srgrimes
99819459Sjkhstatic void
99993394Sphkparse_config_line(char *line, CMD *command)
100019459Sjkh{
100119459Sjkh    char	*cp, *end;
100219459Sjkh
100319459Sjkh    cp = line;
100493394Sphk    while (1) {
100519459Sjkh	memset(command, 0, sizeof(*command));
100619459Sjkh
100719459Sjkh	while (isspace(*cp)) ++cp;
100819459Sjkh	if (*cp == '\0' || *cp == '#')
100919459Sjkh	    break;
101019459Sjkh	command->cmd = *cp++;
101119459Sjkh
101219459Sjkh	/*
101319459Sjkh	 * Parse args
101419459Sjkh	 */
101593394Sphk	    while (1) {
101619459Sjkh	    while (isspace(*cp)) ++cp;
1017192745Sbrian	    if (*cp == '\0')
1018192745Sbrian		break;		/* eol */
101919459Sjkh	    if (*cp == '#')
102019459Sjkh		break;		/* found comment */
102119459Sjkh	    if (isalpha(*cp))
102219459Sjkh		command->args[command->n_args].argtype = *cp++;
102319459Sjkh	    end = NULL;
1024224150Srstone	    command->args[command->n_args].arg_val = strtoul(cp, &end, 0);
1025192745Sbrian 	    if (cp == end || (!isspace(*end) && *end != '\0')) {
1026192745Sbrian 		char ch;
1027192745Sbrian 		end = cp;
1028192745Sbrian 		while (!isspace(*end) && *end != '\0') ++end;
1029192745Sbrian 		ch = *end; *end = '\0';
1030192745Sbrian 		command->args[command->n_args].arg_str = strdup(cp);
1031192745Sbrian 		*end = ch;
1032192745Sbrian 	    } else
1033192745Sbrian 		command->args[command->n_args].arg_str = NULL;
103419459Sjkh	    cp = end;
103519459Sjkh	    command->n_args++;
103619459Sjkh	}
103719459Sjkh	break;
103819459Sjkh    }
103919459Sjkh}
104019459Sjkh
104119459Sjkh
104219459Sjkhstatic int
104393394Sphkprocess_geometry(CMD *command)
104419459Sjkh{
104519459Sjkh    int		status = 1, i;
104619459Sjkh
104793394Sphk    while (1) {
104819459Sjkh	geom_processed = 1;
104993394Sphk	    if (part_processed) {
105037415Scharnier	    warnx(
105137415Scharnier	"ERROR line %d: the geometry specification line must occur before\n\
105237415Scharnier    all partition specifications",
105337415Scharnier		    current_line_number);
105419459Sjkh	    status = 0;
105519459Sjkh	    break;
105619459Sjkh	}
105793394Sphk	    if (command->n_args != 3) {
105837415Scharnier	    warnx("ERROR line %d: incorrect number of geometry args",
105937415Scharnier		    current_line_number);
106019459Sjkh	    status = 0;
106119459Sjkh	    break;
106219459Sjkh	}
106393394Sphk	    dos_cyls = 0;
106493394Sphk	    dos_heads = 0;
106593394Sphk	    dos_sectors = 0;
106693394Sphk	    for (i = 0; i < 3; ++i) {
106793394Sphk		    switch (command->args[i].argtype) {
106819459Sjkh	    case 'c':
106919459Sjkh		dos_cyls = command->args[i].arg_val;
107019459Sjkh		break;
107119459Sjkh	    case 'h':
107219459Sjkh		dos_heads = command->args[i].arg_val;
107319459Sjkh		break;
107419459Sjkh	    case 's':
107519459Sjkh		dos_sectors = command->args[i].arg_val;
107619459Sjkh		break;
107719459Sjkh	    default:
107837415Scharnier		warnx(
107937415Scharnier		"ERROR line %d: unknown geometry arg type: '%c' (0x%02x)",
108037415Scharnier			current_line_number, command->args[i].argtype,
108119459Sjkh			command->args[i].argtype);
108219459Sjkh		status = 0;
108319459Sjkh		break;
108419459Sjkh	    }
108519459Sjkh	}
108619459Sjkh	if (status == 0)
108719459Sjkh	    break;
108819459Sjkh
108919459Sjkh	dos_cylsecs = dos_heads * dos_sectors;
109019459Sjkh
109119459Sjkh	/*
109219459Sjkh	 * Do sanity checks on parameter values
109319459Sjkh	 */
109493394Sphk	    if (dos_cyls == 0) {
109537415Scharnier	    warnx("ERROR line %d: number of cylinders not specified",
109637415Scharnier		    current_line_number);
109719459Sjkh	    status = 0;
109819459Sjkh	}
109993394Sphk	    if (dos_cyls > 1024) {
110037415Scharnier	    warnx(
110137415Scharnier	"WARNING line %d: number of cylinders (%d) may be out-of-range\n\
110219459Sjkh    (must be within 1-1024 for normal BIOS operation, unless the entire disk\n\
110337415Scharnier    is dedicated to FreeBSD)",
110437415Scharnier		    current_line_number, dos_cyls);
110519459Sjkh	}
110619459Sjkh
110793394Sphk	    if (dos_heads == 0) {
110837415Scharnier	    warnx("ERROR line %d: number of heads not specified",
110937415Scharnier		    current_line_number);
111019459Sjkh	    status = 0;
1111194333Slulf	    } else if (dos_heads > 256) {
1112194333Slulf	    warnx("ERROR line %d: number of heads must be within (1-256)",
111337415Scharnier		    current_line_number);
111419459Sjkh	    status = 0;
111519459Sjkh	}
111619459Sjkh
111793394Sphk	    if (dos_sectors == 0) {
111837415Scharnier	    warnx("ERROR line %d: number of sectors not specified",
111937415Scharnier		    current_line_number);
112019459Sjkh	    status = 0;
112193394Sphk	    } else if (dos_sectors > 63) {
112237415Scharnier	    warnx("ERROR line %d: number of sectors must be within (1-63)",
112337415Scharnier		    current_line_number);
112419459Sjkh	    status = 0;
112519459Sjkh	}
112619459Sjkh
112719459Sjkh	break;
112819459Sjkh    }
112919459Sjkh    return (status);
113019459Sjkh}
113119459Sjkh
1132192745Sbrianstatic u_int32_t
1133192745Sbrianstr2sectors(const char *str)
1134192745Sbrian{
1135192745Sbrian	char *end;
1136192745Sbrian	unsigned long val;
113719459Sjkh
1138192745Sbrian	val = strtoul(str, &end, 0);
1139192745Sbrian	if (str == end || *end == '\0') {
1140192745Sbrian		warnx("ERROR line %d: unexpected size: \'%s\'",
1141192745Sbrian		    current_line_number, str);
1142217808Ssobomax		return NO_DISK_SECTORS;
1143192745Sbrian	}
1144192745Sbrian
1145226908Sjmg	if (*end == 'K')
1146192745Sbrian		val *= 1024UL / secsize;
1147192745Sbrian	else if (*end == 'M')
1148192745Sbrian		val *= 1024UL * 1024UL / secsize;
1149192745Sbrian	else if (*end == 'G')
1150192745Sbrian		val *= 1024UL * 1024UL * 1024UL / secsize;
1151192745Sbrian	else {
1152192745Sbrian		warnx("ERROR line %d: unexpected modifier: %c "
1153192745Sbrian		    "(not K/M/G)", current_line_number, *end);
1154217808Ssobomax		return NO_DISK_SECTORS;
1155192745Sbrian	}
1156192745Sbrian
1157192745Sbrian	return val;
1158192745Sbrian}
1159192745Sbrian
116019459Sjkhstatic int
116193394Sphkprocess_partition(CMD *command)
116219459Sjkh{
116319459Sjkh    int				status = 0, partition;
116465054Sjhb    u_int32_t			prev_head_boundary, prev_cyl_boundary;
116565054Sjhb    u_int32_t			adj_size, max_end;
116619459Sjkh    struct dos_partition	*partp;
116719459Sjkh
116893394Sphk	while (1) {
116919459Sjkh	part_processed = 1;
117093394Sphk		if (command->n_args != 4) {
117137415Scharnier	    warnx("ERROR line %d: incorrect number of partition args",
117237415Scharnier		    current_line_number);
117319459Sjkh	    break;
117419459Sjkh	}
117519459Sjkh	partition = command->args[0].arg_val;
117693394Sphk		if (partition < 1 || partition > 4) {
117737415Scharnier	    warnx("ERROR line %d: invalid partition number %d",
117837415Scharnier		    current_line_number, partition);
117919459Sjkh	    break;
118019459Sjkh	}
1181187203Sluigi	partp = &mboot.parts[partition - 1];
1182187203Sluigi	bzero(partp, sizeof (*partp));
118319459Sjkh	partp->dp_typ = command->args[1].arg_val;
1184192745Sbrian	if (command->args[2].arg_str != NULL) {
1185192745Sbrian		if (strcmp(command->args[2].arg_str, "*") == 0) {
1186192745Sbrian			int i;
1187192745Sbrian			partp->dp_start = dos_sectors;
1188192745Sbrian			for (i = 1; i < partition; i++) {
1189192745Sbrian    				struct dos_partition *prev_partp;
1190192745Sbrian				prev_partp = ((struct dos_partition *)
1191192745Sbrian				    &mboot.parts) + i - 1;
1192192745Sbrian				if (prev_partp->dp_typ != 0)
1193192745Sbrian					partp->dp_start = prev_partp->dp_start +
1194192745Sbrian					    prev_partp->dp_size;
1195192745Sbrian			}
1196192745Sbrian			if (partp->dp_start % dos_sectors != 0) {
1197192745Sbrian		    		prev_head_boundary = partp->dp_start /
1198192745Sbrian				    dos_sectors * dos_sectors;
1199192745Sbrian		    		partp->dp_start = prev_head_boundary +
1200192745Sbrian				    dos_sectors;
1201192745Sbrian			}
1202192745Sbrian		} else {
1203192745Sbrian			partp->dp_start = str2sectors(command->args[2].arg_str);
1204217808Ssobomax			if (partp->dp_start == NO_DISK_SECTORS)
1205192745Sbrian				break;
1206192745Sbrian		}
1207192745Sbrian	} else
1208192745Sbrian		partp->dp_start = command->args[2].arg_val;
1209192745Sbrian
1210192745Sbrian	if (command->args[3].arg_str != NULL) {
1211192745Sbrian		if (strcmp(command->args[3].arg_str, "*") == 0)
1212192745Sbrian			partp->dp_size = ((disksecs / dos_cylsecs) *
1213192745Sbrian			    dos_cylsecs) - partp->dp_start;
1214192745Sbrian		else {
1215192745Sbrian			partp->dp_size = str2sectors(command->args[3].arg_str);
1216217808Ssobomax			if (partp->dp_size == NO_DISK_SECTORS)
1217192745Sbrian				break;
1218192745Sbrian		}
1219192745Sbrian		prev_cyl_boundary = ((partp->dp_start + partp->dp_size) /
1220192745Sbrian		    dos_cylsecs) * dos_cylsecs;
1221192745Sbrian		if (prev_cyl_boundary > partp->dp_start)
1222192745Sbrian			partp->dp_size = prev_cyl_boundary - partp->dp_start;
1223192745Sbrian	} else
1224192745Sbrian		partp->dp_size = command->args[3].arg_val;
1225192745Sbrian
122619459Sjkh	max_end = partp->dp_start + partp->dp_size;
122719459Sjkh
1228187203Sluigi	if (partp->dp_typ == 0) {
122919459Sjkh	    /*
123019459Sjkh	     * Get out, the partition is marked as unused.
123119459Sjkh	     */
123219459Sjkh	    /*
123319459Sjkh	     * Insure that it's unused.
123419459Sjkh	     */
1235187203Sluigi	    bzero(partp, sizeof(*partp));
123619459Sjkh	    status = 1;
123719459Sjkh	    break;
123819459Sjkh	}
123919459Sjkh
124019459Sjkh	/*
1241108470Sschweikh	 * Adjust start upwards, if necessary, to fall on a head boundary.
124219459Sjkh	 */
124393394Sphk		if (partp->dp_start % dos_sectors != 0) {
124465054Sjhb	    prev_head_boundary = partp->dp_start / dos_sectors * dos_sectors;
124565054Sjhb	    if (max_end < dos_sectors ||
124693394Sphk			    prev_head_boundary > max_end - dos_sectors) {
124719459Sjkh		/*
124819459Sjkh		 * Can't go past end of partition
124919459Sjkh		 */
125037415Scharnier		warnx(
125137415Scharnier	"ERROR line %d: unable to adjust start of partition %d to fall on\n\
125265054Sjhb    a head boundary",
125337415Scharnier			current_line_number, partition);
125419459Sjkh		break;
125519459Sjkh	    }
125637415Scharnier	    warnx(
125765054Sjhb	"WARNING: adjusting start offset of partition %d\n\
125865054Sjhb    from %u to %u, to fall on a head boundary",
125965054Sjhb		    partition, (u_int)partp->dp_start,
126065054Sjhb		    (u_int)(prev_head_boundary + dos_sectors));
126165054Sjhb	    partp->dp_start = prev_head_boundary + dos_sectors;
126219459Sjkh	}
126319459Sjkh
126419459Sjkh	/*
126519459Sjkh	 * Adjust size downwards, if necessary, to fall on a cylinder
126619459Sjkh	 * boundary.
126719459Sjkh	 */
126865054Sjhb	prev_cyl_boundary =
126919459Sjkh	    ((partp->dp_start + partp->dp_size) / dos_cylsecs) * dos_cylsecs;
127065054Sjhb	if (prev_cyl_boundary > partp->dp_start)
127165054Sjhb	    adj_size = prev_cyl_boundary - partp->dp_start;
127293394Sphk		else {
127365054Sjhb	    warnx(
127465054Sjhb	"ERROR: could not adjust partition to start on a head boundary\n\
127565054Sjhb    and end on a cylinder boundary.");
127665054Sjhb	    return (0);
127765054Sjhb	}
127893394Sphk		if (adj_size != partp->dp_size) {
127937415Scharnier	    warnx(
128065054Sjhb	"WARNING: adjusting size of partition %d from %u to %u\n\
128165054Sjhb    to end on a cylinder boundary",
128265054Sjhb		    partition, (u_int)partp->dp_size, (u_int)adj_size);
128365054Sjhb	    partp->dp_size = adj_size;
128419459Sjkh	}
128593394Sphk		if (partp->dp_size == 0) {
128681164Siedowse	    warnx("ERROR line %d: size of partition %d is zero",
128737415Scharnier		    current_line_number, partition);
128819459Sjkh	    break;
128919459Sjkh	}
129019459Sjkh
129181164Siedowse	dos(partp);
129219459Sjkh	status = 1;
129319459Sjkh	break;
129419459Sjkh    }
129519459Sjkh    return (status);
129619459Sjkh}
129719459Sjkh
129819459Sjkh
129919459Sjkhstatic int
130093394Sphkprocess_active(CMD *command)
130119459Sjkh{
130219459Sjkh    int				status = 0, partition, i;
130319459Sjkh    struct dos_partition	*partp;
130419459Sjkh
130593394Sphk	while (1) {
130619459Sjkh	active_processed = 1;
130793394Sphk		if (command->n_args != 1) {
130837415Scharnier	    warnx("ERROR line %d: incorrect number of active args",
130937415Scharnier		    current_line_number);
131019459Sjkh	    status = 0;
131119459Sjkh	    break;
131219459Sjkh	}
131319459Sjkh	partition = command->args[0].arg_val;
131493394Sphk		if (partition < 1 || partition > 4) {
131537415Scharnier	    warnx("ERROR line %d: invalid partition number %d",
131637415Scharnier		    current_line_number, partition);
131719459Sjkh	    break;
131819459Sjkh	}
131919459Sjkh	/*
132019459Sjkh	 * Reset active partition
132119459Sjkh	 */
1322187203Sluigi	partp = mboot.parts;
132319459Sjkh	for (i = 0; i < NDOSPART; i++)
132419459Sjkh	    partp[i].dp_flag = 0;
132526421Sbrian	partp[partition-1].dp_flag = ACTIVE;
132619459Sjkh
132719459Sjkh	status = 1;
132819459Sjkh	break;
132919459Sjkh    }
133019459Sjkh    return (status);
133119459Sjkh}
133219459Sjkh
133319459Sjkh
133419459Sjkhstatic int
133593394Sphkprocess_line(char *line)
133619459Sjkh{
133719459Sjkh    CMD		command;
133819459Sjkh    int		status = 1;
133919459Sjkh
134093394Sphk	while (1) {
134119459Sjkh	parse_config_line(line, &command);
134293394Sphk		switch (command.cmd) {
134319459Sjkh	case 0:
134419459Sjkh	    /*
134519459Sjkh	     * Comment or blank line
134619459Sjkh	     */
134719459Sjkh	    break;
134819459Sjkh	case 'g':
134919459Sjkh	    /*
135019459Sjkh	     * Set geometry
135119459Sjkh	     */
135219459Sjkh	    status = process_geometry(&command);
135319459Sjkh	    break;
135419459Sjkh	case 'p':
135519459Sjkh	    status = process_partition(&command);
135619459Sjkh	    break;
135719459Sjkh	case 'a':
135819459Sjkh	    status = process_active(&command);
135919459Sjkh	    break;
136019459Sjkh	default:
136119459Sjkh	    status = 0;
136219459Sjkh	    break;
136319459Sjkh	}
136419459Sjkh	break;
136519459Sjkh    }
136619459Sjkh    return (status);
136719459Sjkh}
136819459Sjkh
136919459Sjkh
137019459Sjkhstatic int
137193394Sphkread_config(char *config_file)
137219459Sjkh{
137319459Sjkh    FILE	*fp = NULL;
137419459Sjkh    int		status = 1;
137519459Sjkh    char	buf[1010];
137619459Sjkh
137793394Sphk	while (1) {
137893394Sphk		if (strcmp(config_file, "-") != 0) {
137919459Sjkh	    /*
138019459Sjkh	     * We're not reading from stdin
138119459Sjkh	     */
138293394Sphk			if ((fp = fopen(config_file, "r")) == NULL) {
138319459Sjkh		status = 0;
138419459Sjkh		break;
138519459Sjkh	    }
138693394Sphk		} else {
138719459Sjkh	    fp = stdin;
138819459Sjkh	}
138919459Sjkh	current_line_number = 0;
139093394Sphk		while (!feof(fp)) {
139119459Sjkh	    if (fgets(buf, sizeof(buf), fp) == NULL)
139219459Sjkh		break;
139319459Sjkh	    ++current_line_number;
139419459Sjkh	    status = process_line(buf);
139519459Sjkh	    if (status == 0)
139619459Sjkh		break;
139719459Sjkh	    }
139819459Sjkh	break;
139919459Sjkh    }
140093394Sphk	if (fp) {
140119459Sjkh	/*
140219459Sjkh	 * It doesn't matter if we're reading from stdin, as we've reached EOF
140319459Sjkh	 */
140419459Sjkh	fclose(fp);
140519459Sjkh    }
140619459Sjkh    return (status);
140719459Sjkh}
140819459Sjkh
140919459Sjkh
141019459Sjkhstatic void
141119459Sjkhreset_boot(void)
141219459Sjkh{
141319459Sjkh    int				i;
141419459Sjkh    struct dos_partition	*partp;
141519459Sjkh
141619459Sjkh    init_boot();
1417187203Sluigi    for (i = 0; i < 4; ++i) {
1418187203Sluigi	partp = &mboot.parts[i];
1419187203Sluigi	bzero(partp, sizeof(*partp));
142019459Sjkh    }
142119459Sjkh}
142265054Sjhb
142365054Sjhbstatic int
142493394Sphksanitize_partition(struct dos_partition *partp)
142565054Sjhb{
142665054Sjhb    u_int32_t			prev_head_boundary, prev_cyl_boundary;
142781164Siedowse    u_int32_t			max_end, size, start;
142865054Sjhb
142981164Siedowse    start = partp->dp_start;
143081164Siedowse    size = partp->dp_size;
143181164Siedowse    max_end = start + size;
143281164Siedowse    /* Only allow a zero size if the partition is being marked unused. */
143381164Siedowse    if (size == 0) {
143481164Siedowse	if (start == 0 && partp->dp_typ == 0)
143581164Siedowse	    return (1);
143681164Siedowse	warnx("ERROR: size of partition is zero");
143781164Siedowse	return (0);
143881164Siedowse    }
143981164Siedowse    /* Return if no adjustment is necessary. */
144081164Siedowse    if (start % dos_sectors == 0 && (start + size) % dos_sectors == 0)
144181164Siedowse	return (1);
144265054Sjhb
1443122627Sdes    if (start == 0) {
1444122627Sdes	    warnx("WARNING: partition overlaps with partition table");
1445122627Sdes	    if (ok("Correct this automatically?"))
1446122627Sdes		    start = dos_sectors;
1447122627Sdes    }
144881164Siedowse    if (start % dos_sectors != 0)
144981164Siedowse	warnx("WARNING: partition does not start on a head boundary");
145081164Siedowse    if ((start  +size) % dos_sectors != 0)
145181164Siedowse	warnx("WARNING: partition does not end on a cylinder boundary");
145281164Siedowse    warnx("WARNING: this may confuse the BIOS or some operating systems");
145381164Siedowse    if (!ok("Correct this automatically?"))
145481164Siedowse	return (1);
145581164Siedowse
145665054Sjhb    /*
1457108470Sschweikh     * Adjust start upwards, if necessary, to fall on a head boundary.
145865054Sjhb     */
145981164Siedowse    if (start % dos_sectors != 0) {
146081164Siedowse	prev_head_boundary = start / dos_sectors * dos_sectors;
146165054Sjhb	if (max_end < dos_sectors ||
146281164Siedowse	    prev_head_boundary >= max_end - dos_sectors) {
146365054Sjhb	    /*
146465054Sjhb	     * Can't go past end of partition
146565054Sjhb	     */
146665054Sjhb	    warnx(
146765054Sjhb    "ERROR: unable to adjust start of partition to fall on a head boundary");
146865054Sjhb	    return (0);
146965054Sjhb        }
147081164Siedowse	start = prev_head_boundary + dos_sectors;
147165054Sjhb    }
147265054Sjhb
147365054Sjhb    /*
147465054Sjhb     * Adjust size downwards, if necessary, to fall on a cylinder
147565054Sjhb     * boundary.
147665054Sjhb     */
147781164Siedowse    prev_cyl_boundary = ((start + size) / dos_cylsecs) * dos_cylsecs;
147881164Siedowse    if (prev_cyl_boundary > start)
147981164Siedowse	size = prev_cyl_boundary - start;
148081164Siedowse    else {
148165054Sjhb	warnx("ERROR: could not adjust partition to start on a head boundary\n\
148265054Sjhb    and end on a cylinder boundary.");
148365054Sjhb	return (0);
148465054Sjhb    }
148581164Siedowse
148681164Siedowse    /* Finally, commit any changes to partp and return. */
148781164Siedowse    if (start != partp->dp_start) {
148881164Siedowse	warnx("WARNING: adjusting start offset of partition to %u",
148981164Siedowse	    (u_int)start);
149081164Siedowse	partp->dp_start = start;
149165054Sjhb    }
149281164Siedowse    if (size != partp->dp_size) {
149381164Siedowse	warnx("WARNING: adjusting size of partition to %u", (u_int)size);
149481164Siedowse	partp->dp_size = size;
149565054Sjhb    }
149665054Sjhb
149765054Sjhb    return (1);
149865054Sjhb}
149979681Sjoerg
150079681Sjoerg/*
150179681Sjoerg * Try figuring out the root device's canonical disk name.
150279681Sjoerg * The following choices are considered:
150379681Sjoerg *   /dev/ad0s1a     => /dev/ad0
150479681Sjoerg *   /dev/da0a       => /dev/da0
150579681Sjoerg *   /dev/vinum/root => /dev/vinum/root
1506212247Sbrian * A ".eli" part is removed if it exists (see geli(8)).
1507212724Sbrian * A ".journal" ending is removed if it exists (see gjournal(8)).
150879681Sjoerg */
150979681Sjoergstatic char *
151079681Sjoergget_rootdisk(void)
151179681Sjoerg{
151279681Sjoerg	struct statfs rootfs;
151379681Sjoerg	regex_t re;
151479681Sjoerg#define NMATCHES 2
151579681Sjoerg	regmatch_t rm[NMATCHES];
1516212247Sbrian	char dev[PATH_MAX], *s;
151779681Sjoerg	int rv;
151879681Sjoerg
151979681Sjoerg	if (statfs("/", &rootfs) == -1)
152079681Sjoerg		err(1, "statfs(\"/\")");
152179681Sjoerg
1522212724Sbrian	if ((rv = regcomp(&re, "^(/dev/[a-z/]+[0-9]*)([sp][0-9]+)?[a-h]?(\\.journal)?$",
152379681Sjoerg		    REG_EXTENDED)) != 0)
152479681Sjoerg		errx(1, "regcomp() failed (%d)", rv);
1525212247Sbrian	strlcpy(dev, rootfs.f_mntfromname, sizeof (dev));
1526212247Sbrian	if ((s = strstr(dev, ".eli")) != NULL)
1527212247Sbrian	    memmove(s, s+4, strlen(s + 4) + 1);
1528212247Sbrian
1529212247Sbrian	if ((rv = regexec(&re, dev, NMATCHES, rm, 0)) != 0)
153079681Sjoerg		errx(1,
153179681Sjoerg"mounted root fs resource doesn't match expectations (regexec returned %d)",
153279681Sjoerg		    rv);
153379681Sjoerg	if ((s = malloc(rm[1].rm_eo - rm[1].rm_so + 1)) == NULL)
153479681Sjoerg		errx(1, "out of memory");
153579681Sjoerg	memcpy(s, rootfs.f_mntfromname + rm[1].rm_so,
153679681Sjoerg	    rm[1].rm_eo - rm[1].rm_so);
153779681Sjoerg	s[rm[1].rm_eo - rm[1].rm_so] = 0;
153879681Sjoerg
153979681Sjoerg	return s;
154079681Sjoerg}
1541