fdisk.c revision 90866
1/*
2 * Mach Operating System
3 * Copyright (c) 1992 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
13 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19 *  School of Computer Science
20 *  Carnegie Mellon University
21 *  Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie Mellon
24 * the rights to redistribute these changes.
25 */
26
27#ifndef lint
28static const char rcsid[] =
29  "$FreeBSD: head/sbin/fdisk/fdisk.c 90866 2002-02-18 19:32:38Z joe $";
30#endif /* not lint */
31
32#include <sys/disklabel.h>
33#include <sys/param.h>
34#include <sys/stat.h>
35#include <sys/mount.h>
36#include <ctype.h>
37#include <fcntl.h>
38#include <err.h>
39#include <errno.h>
40#include <paths.h>
41#include <regex.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47int iotest;
48
49#define LBUF 100
50static char lbuf[LBUF];
51
52#define MBRSIGOFF	510
53
54/*
55 *
56 * Ported to 386bsd by Julian Elischer  Thu Oct 15 20:26:46 PDT 1992
57 *
58 * 14-Dec-89  Robert Baron (rvb) at Carnegie-Mellon University
59 *	Copyright (c) 1989	Robert. V. Baron
60 *	Created.
61 */
62
63#define Decimal(str, ans, tmp) if (decimal(str, &tmp, ans)) ans = tmp
64#define Hex(str, ans, tmp) if (hex(str, &tmp, ans)) ans = tmp
65#define String(str, ans, len) {char *z = ans; char **dflt = &z; if (string(str, dflt)) strncpy(ans, *dflt, len); }
66
67#define RoundCyl(x) ((((x) + cylsecs - 1) / cylsecs) * cylsecs)
68
69#define MAX_SEC_SIZE 2048	/* maximum section size that is supported */
70#define MIN_SEC_SIZE 512	/* the sector size to start sensing at */
71int secsize = 0;		/* the sensed sector size */
72
73char *disk;
74
75struct disklabel disklabel;		/* disk parameters */
76
77int cyls, sectors, heads, cylsecs, disksecs;
78
79struct mboot
80{
81	unsigned char padding[2]; /* force the longs to be long aligned */
82  	unsigned char *bootinst;  /* boot code */
83  	off_t bootinst_size;
84	struct	dos_partition parts[4];
85};
86struct mboot mboot = {{0}, NULL, 0};
87
88#define ACTIVE 0x80
89#define BOOT_MAGIC 0xAA55
90
91int dos_cyls;
92int dos_heads;
93int dos_sectors;
94int dos_cylsecs;
95
96#define DOSSECT(s,c) ((s & 0x3f) | ((c >> 2) & 0xc0))
97#define DOSCYL(c)	(c & 0xff)
98static int partition = -1;
99
100
101#define MAX_ARGS	10
102
103static int	current_line_number;
104
105static int	geom_processed = 0;
106static int	part_processed = 0;
107static int	active_processed = 0;
108
109
110typedef struct cmd {
111    char		cmd;
112    int			n_args;
113    struct arg {
114	char	argtype;
115	int	arg_val;
116    }			args[MAX_ARGS];
117} CMD;
118
119
120static int B_flag  = 0;		/* replace boot code */
121static int I_flag  = 0;		/* use entire disk for FreeBSD */
122static int a_flag  = 0;		/* set active partition */
123static char *b_flag = NULL;	/* path to boot code */
124static int i_flag  = 0;		/* replace partition data */
125static int u_flag  = 0;		/* update partition data */
126static int s_flag  = 0;		/* Print a summary and exit */
127static int t_flag  = 0;		/* test only, if f_flag is given */
128static char *f_flag = NULL;	/* Read config info from file */
129static int v_flag  = 0;		/* Be verbose */
130
131struct part_type
132{
133 unsigned char type;
134 char *name;
135}part_types[] =
136{
137	 {0x00, "unused"}
138	,{0x01, "Primary DOS with 12 bit FAT"}
139	,{0x02, "XENIX / filesystem"}
140	,{0x03, "XENIX /usr filesystem"}
141	,{0x04, "Primary DOS with 16 bit FAT (< 32MB)"}
142	,{0x05, "Extended DOS"}
143	,{0x06, "Primary 'big' DOS (>= 32MB)"}
144	,{0x07, "OS/2 HPFS, NTFS, QNX-2 (16 bit) or Advanced UNIX"}
145	,{0x08, "AIX filesystem or SplitDrive"}
146	,{0x09, "AIX boot partition or Coherent"}
147	,{0x0A, "OS/2 Boot Manager, OPUS or Coherent swap"}
148	,{0x0B, "DOS or Windows 95 with 32 bit FAT"}
149	,{0x0C, "DOS or Windows 95 with 32 bit FAT (LBA)"}
150	,{0x0E, "Primary 'big' DOS (>= 32MB, LBA)"}
151	,{0x0F, "Extended DOS (LBA)"}
152	,{0x10, "OPUS"}
153	,{0x11, "OS/2 BM: hidden DOS with 12-bit FAT"}
154	,{0x12, "Compaq diagnostics"}
155	,{0x14, "OS/2 BM: hidden DOS with 16-bit FAT (< 32MB)"}
156	,{0x16, "OS/2 BM: hidden DOS with 16-bit FAT (>= 32MB)"}
157	,{0x17, "OS/2 BM: hidden IFS (e.g. HPFS)"}
158	,{0x18, "AST Windows swapfile"}
159	,{0x24, "NEC DOS"}
160	,{0x3C, "PartitionMagic recovery"}
161	,{0x39, "plan9"}
162	,{0x40, "VENIX 286"}
163	,{0x41, "Linux/MINIX (sharing disk with DRDOS)"}
164	,{0x42, "SFS or Linux swap (sharing disk with DRDOS)"}
165	,{0x43, "Linux native (sharing disk with DRDOS)"}
166	,{0x4D, "QNX 4.2 Primary"}
167	,{0x4E, "QNX 4.2 Secondary"}
168	,{0x4F, "QNX 4.2 Tertiary"}
169	,{0x50, "DM (disk manager)"}
170	,{0x51, "DM6 Aux1 (or Novell)"}
171	,{0x52, "CP/M or Microport SysV/AT"}
172	,{0x53, "DM6 Aux3"}
173	,{0x54, "DM6"}
174	,{0x55, "EZ-Drive (disk manager)"}
175	,{0x56, "Golden Bow (disk manager)"}
176	,{0x5c, "Priam Edisk (disk manager)"} /* according to S. Widlake */
177	,{0x61, "SpeedStor"}
178	,{0x63, "System V/386 (such as ISC UNIX), GNU HURD or Mach"}
179	,{0x64, "Novell Netware/286 2.xx"}
180	,{0x65, "Novell Netware/386 3.xx"}
181	,{0x70, "DiskSecure Multi-Boot"}
182	,{0x75, "PCIX"}
183	,{0x77, "QNX4.x"}
184	,{0x78, "QNX4.x 2nd part"}
185	,{0x79, "QNX4.x 3rd part"}
186	,{0x80, "Minix until 1.4a"}
187	,{0x81, "Minix since 1.4b, early Linux partition or Mitac (disk manager)"}
188	,{0x82, "Linux swap or Solaris x86"}
189	,{0x83, "Linux native"}
190	,{0x84, "OS/2 hidden C: drive"}
191	,{0x85, "Linux extended"}
192	,{0x86, "NTFS volume set??"}
193	,{0x87, "NTFS volume set??"}
194	,{0x93, "Amoeba filesystem"}
195	,{0x94, "Amoeba bad block table"}
196	,{0x9F, "BSD/OS"}
197	,{0xA0, "Suspend to Disk"}
198	,{0xA5, "FreeBSD/NetBSD/386BSD"}
199	,{0xA6, "OpenBSD"}
200	,{0xA7, "NeXTSTEP"}
201	,{0xA9, "NetBSD"}
202	,{0xB7, "BSDI BSD/386 filesystem"}
203	,{0xB8, "BSDI BSD/386 swap"}
204	,{0xC1, "DRDOS/sec with 12-bit FAT"}
205	,{0xC4, "DRDOS/sec with 16-bit FAT (< 32MB)"}
206	,{0xC6, "DRDOS/sec with 16-bit FAT (>= 32MB)"}
207	,{0xC7, "Syrinx"}
208	,{0xDB, "CP/M, Concurrent CP/M, Concurrent DOS or CTOS"}
209	,{0xE1, "DOS access or SpeedStor with 12-bit FAT extended partition"}
210	,{0xE3, "DOS R/O or SpeedStor"}
211	,{0xE4, "SpeedStor with 16-bit FAT extended partition < 1024 cyl."}
212	,{0xEB, "BeOS filesystem"}
213	,{0xEE, "EFI GPT"}
214	,{0xEF, "EFI System Partition"}
215	,{0xF1, "SpeedStor"}
216	,{0xF2, "DOS 3.3+ Secondary"}
217	,{0xF4, "SpeedStor large partition"}
218	,{0xFE, "SpeedStor >1024 cyl. or LANstep"}
219	,{0xFF, "Xenix bad blocks table"}
220};
221
222static void print_s0(int which);
223static void print_part(int i);
224static void init_sector0(unsigned long start);
225static void init_boot(void);
226static void change_part(int i);
227static void print_params();
228static void change_active(int which);
229static void change_code();
230static void get_params_to_use();
231static char *get_rootdisk(void);
232static void dos(struct dos_partition *partp);
233static int open_disk(int u_flag);
234static ssize_t read_disk(off_t sector, void *buf);
235static ssize_t write_disk(off_t sector, void *buf);
236static int get_params();
237static int read_s0();
238static int write_s0();
239static int ok(char *str);
240static int decimal(char *str, int *num, int deflt);
241static char *get_type(int type);
242static int read_config(char *config_file);
243static void reset_boot(void);
244static int sanitize_partition(struct dos_partition *);
245static void usage(void);
246#if 0
247static int hex(char *str, int *num, int deflt);
248static int string(char *str, char **ans);
249#endif
250
251
252int
253main(int argc, char *argv[])
254{
255	struct	stat sb;
256	int	c, i;
257
258	while ((c = getopt(argc, argv, "BIab:f:istuv1234")) != -1)
259		switch (c) {
260		case 'B':
261			B_flag = 1;
262			break;
263		case 'I':
264			I_flag = 1;
265			break;
266		case 'a':
267			a_flag = 1;
268			break;
269		case 'b':
270			b_flag = optarg;
271			break;
272		case 'f':
273			f_flag = optarg;
274			break;
275		case 'i':
276			i_flag = 1;
277			break;
278		case 's':
279			s_flag = 1;
280			break;
281		case 't':
282			t_flag = 1;
283			break;
284		case 'u':
285			u_flag = 1;
286			break;
287		case 'v':
288			v_flag = 1;
289			break;
290		case '1':
291		case '2':
292		case '3':
293		case '4':
294			partition = c - '0';
295			break;
296		default:
297			usage();
298		}
299	if (f_flag || i_flag)
300		u_flag = 1;
301	if (t_flag)
302		v_flag = 1;
303	argc -= optind;
304	argv += optind;
305
306	if (argc == 0) {
307		disk = get_rootdisk();
308	} else {
309		if (stat(argv[0], &sb) == 0) {
310			/* OK, full pathname given */
311			disk = argv[0];
312		} else if (errno == ENOENT) {
313			/* Try prepending "/dev" */
314			if ((disk = malloc(strlen(argv[0]) + strlen(_PATH_DEV) +
315			     1)) == NULL)
316				errx(1, "out of memory");
317			strcpy(disk, _PATH_DEV);
318			strcat(disk, argv[0]);
319		} else {
320			/* other stat error, let it fail below */
321			disk = argv[0];
322		}
323	}
324	if (open_disk(u_flag) < 0)
325		err(1, "cannot open disk %s", disk);
326
327	/* (abu)use mboot.bootinst to probe for the sector size */
328	if ((mboot.bootinst = malloc(MAX_SEC_SIZE)) == NULL)
329		err(1, "cannot allocate buffer to determine disk sector size");
330	read_disk(0, mboot.bootinst);
331	free(mboot.bootinst);
332	mboot.bootinst = NULL;
333
334	if (s_flag)
335	{
336		int i;
337		struct dos_partition *partp;
338
339		if (read_s0())
340			err(1, "read_s0");
341		printf("%s: %d cyl %d hd %d sec\n", disk, dos_cyls, dos_heads,
342		    dos_sectors);
343		printf("Part  %11s %11s Type Flags\n", "Start", "Size");
344		for (i = 0; i < NDOSPART; i++) {
345			partp = ((struct dos_partition *) &mboot.parts) + i;
346			if (partp->dp_start == 0 && partp->dp_size == 0)
347				continue;
348			printf("%4d: %11lu %11lu 0x%02x 0x%02x\n", i + 1,
349			    (u_long) partp->dp_start,
350			    (u_long) partp->dp_size, partp->dp_typ,
351			    partp->dp_flag);
352		}
353		exit(0);
354	}
355
356	printf("******* Working on device %s *******\n",disk);
357
358	if (I_flag)
359	{
360		struct dos_partition *partp;
361
362		read_s0();
363		reset_boot();
364		partp = (struct dos_partition *) (&mboot.parts[0]);
365		partp->dp_typ = DOSPTYP_386BSD;
366		partp->dp_flag = ACTIVE;
367		partp->dp_start = dos_sectors;
368		partp->dp_size = (disksecs / dos_cylsecs) * dos_cylsecs -
369		    dos_sectors;
370
371		dos(partp);
372		if (v_flag)
373			print_s0(-1);
374		write_s0();
375		exit(0);
376	}
377	if (f_flag)
378	{
379	    if (read_s0() || i_flag)
380	    {
381		reset_boot();
382	    }
383
384	    if (!read_config(f_flag))
385	    {
386		exit(1);
387	    }
388	    if (v_flag)
389	    {
390		print_s0(-1);
391	    }
392	    if (!t_flag)
393	    {
394		write_s0();
395	    }
396	}
397	else
398	{
399	    if(u_flag)
400	    {
401		get_params_to_use();
402	    }
403	    else
404	    {
405		print_params();
406	    }
407
408	    if (read_s0())
409		init_sector0(dos_sectors);
410
411	    printf("Media sector size is %d\n", secsize);
412	    printf("Warning: BIOS sector numbering starts with sector 1\n");
413	    printf("Information from DOS bootblock is:\n");
414	    if (partition == -1)
415		for (i = 1; i <= NDOSPART; i++)
416		    change_part(i);
417	    else
418		change_part(partition);
419
420	    if (u_flag || a_flag)
421		change_active(partition);
422
423	    if (B_flag)
424		change_code();
425
426	    if (u_flag || a_flag || B_flag) {
427		if (!t_flag)
428		{
429		    printf("\nWe haven't changed the partition table yet.  ");
430		    printf("This is your last chance.\n");
431		}
432		print_s0(-1);
433		if (!t_flag)
434		{
435		    if (ok("Should we write new partition table?"))
436			write_s0();
437		}
438		else
439		{
440		    printf("\n-t flag specified -- partition table not written.\n");
441		}
442	    }
443	}
444
445	exit(0);
446}
447
448static void
449usage()
450{
451	fprintf(stderr, "%s%s",
452		"usage: fdisk [-BIaistu] [-b bootcode] [-1234] [disk]\n",
453 		"       fdisk -f configfile [-itv] [disk]\n");
454        exit(1);
455}
456
457static void
458print_s0(int which)
459{
460int	i;
461
462	print_params();
463	printf("Information from DOS bootblock is:\n");
464	if (which == -1)
465		for (i = 1; i <= NDOSPART; i++)
466			printf("%d: ", i), print_part(i);
467	else
468		print_part(which);
469}
470
471static struct dos_partition mtpart = { 0 };
472
473static void
474print_part(int i)
475{
476	struct	  dos_partition *partp;
477	u_int64_t part_mb;
478
479	partp = ((struct dos_partition *) &mboot.parts) + i - 1;
480
481	if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) {
482		printf("<UNUSED>\n");
483		return;
484	}
485	/*
486	 * Be careful not to overflow.
487	 */
488	part_mb = partp->dp_size;
489	part_mb *= secsize;
490	part_mb /= (1024 * 1024);
491	printf("sysid %d (%#04x),(%s)\n", partp->dp_typ, partp->dp_typ,
492	    get_type(partp->dp_typ));
493	printf("    start %lu, size %lu (%qd Meg), flag %x%s\n",
494		(u_long)partp->dp_start,
495		(u_long)partp->dp_size,
496		part_mb,
497		partp->dp_flag,
498		partp->dp_flag == ACTIVE ? " (active)" : "");
499	printf("\tbeg: cyl %d/ head %d/ sector %d;\n\tend: cyl %d/ head %d/ sector %d\n"
500		,DPCYL(partp->dp_scyl, partp->dp_ssect)
501		,partp->dp_shd
502		,DPSECT(partp->dp_ssect)
503		,DPCYL(partp->dp_ecyl, partp->dp_esect)
504		,partp->dp_ehd
505		,DPSECT(partp->dp_esect));
506}
507
508
509static void
510init_boot(void)
511{
512	const char *fname;
513	int fd, n;
514	struct stat sb;
515
516	fname = b_flag ? b_flag : "/boot/mbr";
517	if ((fd = open(fname, O_RDONLY)) == -1 ||
518	    fstat(fd, &sb) == -1)
519		err(1, "%s", fname);
520	if ((mboot.bootinst_size = sb.st_size) % secsize != 0)
521		errx(1, "%s: length must be a multiple of sector size", fname);
522	if (mboot.bootinst != NULL)
523		free(mboot.bootinst);
524	if ((mboot.bootinst = malloc(mboot.bootinst_size = sb.st_size)) == NULL)
525		errx(1, "%s: unable to allocate read buffer", fname);
526	if ((n = read(fd, mboot.bootinst, mboot.bootinst_size)) == -1 ||
527	    close(fd))
528		err(1, "%s", fname);
529	if (n != mboot.bootinst_size)
530		errx(1, "%s: short read", fname);
531}
532
533
534static void
535init_sector0(unsigned long start)
536{
537struct dos_partition *partp = (struct dos_partition *) (&mboot.parts[3]);
538
539	init_boot();
540
541	partp->dp_typ = DOSPTYP_386BSD;
542	partp->dp_flag = ACTIVE;
543	start = ((start + dos_sectors - 1) / dos_sectors) * dos_sectors;
544	if(start == 0)
545		start = dos_sectors;
546	partp->dp_start = start;
547	partp->dp_size = (disksecs / dos_cylsecs) * dos_cylsecs - start;
548
549	dos(partp);
550}
551
552static void
553change_part(int i)
554{
555struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i - 1;
556
557    printf("The data for partition %d is:\n", i);
558    print_part(i);
559
560    if (u_flag && ok("Do you want to change it?")) {
561	int tmp;
562
563	if (i_flag) {
564		bzero((char *)partp, sizeof (struct dos_partition));
565		if (i == 4) {
566			init_sector0(1);
567			printf("\nThe static data for the DOS partition 4 has been reinitialized to:\n");
568			print_part(i);
569		}
570	}
571
572	do {
573		Decimal("sysid (165=FreeBSD)", partp->dp_typ, tmp);
574		Decimal("start", partp->dp_start, tmp);
575		Decimal("size", partp->dp_size, tmp);
576		if (!sanitize_partition(partp)) {
577			warnx("ERROR: failed to adjust; setting sysid to 0");
578			partp->dp_typ = 0;
579		}
580
581		if (ok("Explicitly specify beg/end address ?"))
582		{
583			int	tsec,tcyl,thd;
584			tcyl = DPCYL(partp->dp_scyl,partp->dp_ssect);
585			thd = partp->dp_shd;
586			tsec = DPSECT(partp->dp_ssect);
587			Decimal("beginning cylinder", tcyl, tmp);
588			Decimal("beginning head", thd, tmp);
589			Decimal("beginning sector", tsec, tmp);
590			partp->dp_scyl = DOSCYL(tcyl);
591			partp->dp_ssect = DOSSECT(tsec,tcyl);
592			partp->dp_shd = thd;
593
594			tcyl = DPCYL(partp->dp_ecyl,partp->dp_esect);
595			thd = partp->dp_ehd;
596			tsec = DPSECT(partp->dp_esect);
597			Decimal("ending cylinder", tcyl, tmp);
598			Decimal("ending head", thd, tmp);
599			Decimal("ending sector", tsec, tmp);
600			partp->dp_ecyl = DOSCYL(tcyl);
601			partp->dp_esect = DOSSECT(tsec,tcyl);
602			partp->dp_ehd = thd;
603		} else
604			dos(partp);
605
606		print_part(i);
607	} while (!ok("Are we happy with this entry?"));
608    }
609}
610
611static void
612print_params()
613{
614	printf("parameters extracted from in-core disklabel are:\n");
615	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
616			,cyls,heads,sectors,cylsecs);
617	if((dos_sectors > 63) || (dos_cyls > 1023) || (dos_heads > 255))
618		printf("Figures below won't work with BIOS for partitions not in cyl 1\n");
619	printf("parameters to be used for BIOS calculations are:\n");
620	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
621		,dos_cyls,dos_heads,dos_sectors,dos_cylsecs);
622}
623
624static void
625change_active(int which)
626{
627	struct dos_partition *partp = &mboot.parts[0];
628	int active, i, new, tmp;
629
630	active = -1;
631	for (i = 0; i < NDOSPART; i++) {
632		if ((partp[i].dp_flag & ACTIVE) == 0)
633			continue;
634		printf("Partition %d is marked active\n", i + 1);
635		if (active == -1)
636			active = i + 1;
637	}
638	if (a_flag && which != -1)
639		active = which;
640	else if (active == -1)
641		active = 1;
642
643	if (!ok("Do you want to change the active partition?"))
644		return;
645setactive:
646	do {
647		new = active;
648		Decimal("active partition", new, tmp);
649		if (new < 1 || new > 4) {
650			printf("Active partition number must be in range 1-4."
651					"  Try again.\n");
652			goto setactive;
653		}
654		active = new;
655	} while (!ok("Are you happy with this choice"));
656	for (i = 0; i < NDOSPART; i++)
657		partp[i].dp_flag = 0;
658	if (active > 0 && active <= NDOSPART)
659		partp[active-1].dp_flag = ACTIVE;
660}
661
662static void
663change_code()
664{
665	if (ok("Do you want to change the boot code?"))
666		init_boot();
667}
668
669void
670get_params_to_use()
671{
672	int	tmp;
673	print_params();
674	if (ok("Do you want to change our idea of what BIOS thinks ?"))
675	{
676		do
677		{
678			Decimal("BIOS's idea of #cylinders", dos_cyls, tmp);
679			Decimal("BIOS's idea of #heads", dos_heads, tmp);
680			Decimal("BIOS's idea of #sectors", dos_sectors, tmp);
681			dos_cylsecs = dos_heads * dos_sectors;
682			print_params();
683		}
684		while(!ok("Are you happy with this choice"));
685	}
686}
687
688
689/***********************************************\
690* Change real numbers into strange dos numbers	*
691\***********************************************/
692static void
693dos(partp)
694	struct dos_partition *partp;
695{
696	int cy, sec;
697	u_int32_t end;
698
699	if (partp->dp_typ == 0 && partp->dp_start == 0 && partp->dp_size == 0) {
700		memcpy(partp, &mtpart, sizeof(*partp));
701		return;
702	}
703
704	/* Start c/h/s. */
705	partp->dp_shd = partp->dp_start % dos_cylsecs / dos_sectors;
706	cy = partp->dp_start / dos_cylsecs;
707	sec = partp->dp_start % dos_sectors + 1;
708	partp->dp_scyl = DOSCYL(cy);
709	partp->dp_ssect = DOSSECT(sec, cy);
710
711	/* End c/h/s. */
712	end = partp->dp_start + partp->dp_size - 1;
713	partp->dp_ehd = end % dos_cylsecs / dos_sectors;
714	cy = end / dos_cylsecs;
715	sec = end % dos_sectors + 1;
716	partp->dp_ecyl = DOSCYL(cy);
717	partp->dp_esect = DOSSECT(sec, cy);
718}
719
720int fd;
721
722	/* Getting device status */
723
724static int
725open_disk(int u_flag)
726{
727	struct stat 	st;
728
729	if (stat(disk, &st) == -1) {
730		if (errno == ENOENT)
731			return -2;
732		warnx("can't get file status of %s", disk);
733		return -1;
734	}
735	if ( !(st.st_mode & S_IFCHR) )
736		warnx("device %s is not character special", disk);
737	if ((fd = open(disk,
738	    a_flag || I_flag || B_flag || u_flag ? O_RDWR : O_RDONLY)) == -1) {
739		if(errno == ENXIO)
740			return -2;
741		warnx("can't open device %s", disk);
742		return -1;
743	}
744	if (get_params(0) == -1) {
745		warnx("can't get disk parameters on %s", disk);
746		return -1;
747	}
748	return fd;
749}
750
751static ssize_t
752read_disk(off_t sector, void *buf)
753{
754	lseek(fd,(sector * 512), 0);
755	if( secsize == 0 )
756		for( secsize = MIN_SEC_SIZE; secsize <= MAX_SEC_SIZE; secsize *= 2 )
757			{
758			/* try the read */
759			int size = read(fd, buf, secsize);
760			if( size == secsize )
761				/* it worked so return */
762				return secsize;
763			}
764	else
765		return read( fd, buf, secsize );
766
767	/* we failed to read at any of the sizes */
768	return -1;
769}
770
771static ssize_t
772write_disk(off_t sector, void *buf)
773{
774	lseek(fd,(sector * 512), 0);
775	/* write out in the size that the read_disk found worked */
776	return write(fd, buf, secsize);
777}
778
779static int
780get_params()
781{
782
783    if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) {
784	warnx("can't get disk parameters on %s; supplying dummy ones", disk);
785	dos_cyls = cyls = 1;
786	dos_heads = heads = 1;
787	dos_sectors = sectors = 1;
788	dos_cylsecs = cylsecs = heads * sectors;
789	disksecs = cyls * heads * sectors;
790	return disksecs;
791    }
792
793    dos_cyls = cyls = disklabel.d_ncylinders;
794    dos_heads = heads = disklabel.d_ntracks;
795    dos_sectors = sectors = disklabel.d_nsectors;
796    dos_cylsecs = cylsecs = heads * sectors;
797    disksecs = cyls * heads * sectors;
798
799    return (disksecs);
800}
801
802
803static int
804read_s0()
805{
806	mboot.bootinst_size = secsize;
807	if (mboot.bootinst != NULL)
808		free(mboot.bootinst);
809	if ((mboot.bootinst = malloc(mboot.bootinst_size)) == NULL) {
810		warnx("unable to allocate buffer to read fdisk "
811		      "partition table");
812		return -1;
813	}
814	if (read_disk(0, mboot.bootinst) == -1) {
815		warnx("can't read fdisk partition table");
816		return -1;
817	}
818	if (*(uint16_t *)&mboot.bootinst[MBRSIGOFF] != BOOT_MAGIC) {
819		warnx("invalid fdisk partition table found");
820		/* So should we initialize things */
821		return -1;
822	}
823	memcpy(mboot.parts, &mboot.bootinst[DOSPARTOFF], sizeof(mboot.parts));
824	return 0;
825}
826
827static int
828write_s0()
829{
830#ifdef NOT_NOW
831	int	flag;
832#endif
833	int	sector;
834
835	if (iotest) {
836		print_s0(-1);
837		return 0;
838	}
839	memcpy(&mboot.bootinst[DOSPARTOFF], mboot.parts, sizeof(mboot.parts));
840	/*
841	 * write enable label sector before write (if necessary),
842	 * disable after writing.
843	 * needed if the disklabel protected area also protects
844	 * sector 0. (e.g. empty disk)
845	 */
846#ifdef NOT_NOW
847	flag = 1;
848	if (ioctl(fd, DIOCWLABEL, &flag) < 0)
849		warn("ioctl DIOCWLABEL");
850#endif
851	for(sector = 0; sector < mboot.bootinst_size / secsize; sector++)
852		if (write_disk(sector,
853			       &mboot.bootinst[sector * secsize]) == -1) {
854			warn("can't write fdisk partition table");
855			return -1;
856#ifdef NOT_NOW
857			flag = 0;
858			(void) ioctl(fd, DIOCWLABEL, &flag);
859#endif
860		}
861#ifdef NOT_NOW
862	flag = 0;
863	(void) ioctl(fd, DIOCWLABEL, &flag);
864#endif
865	return(0);
866}
867
868
869static int
870ok(str)
871char *str;
872{
873	printf("%s [n] ", str);
874	fflush(stdout);
875	if (fgets(lbuf, LBUF, stdin) == NULL)
876		exit(1);
877	lbuf[strlen(lbuf)-1] = 0;
878
879	if (*lbuf &&
880		(!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") ||
881		 !strcmp(lbuf, "y") || !strcmp(lbuf, "Y")))
882		return 1;
883	else
884		return 0;
885}
886
887static int
888decimal(char *str, int *num, int deflt)
889{
890int acc = 0, c;
891char *cp;
892
893	while (1) {
894		printf("Supply a decimal value for \"%s\" [%d] ", str, deflt);
895		fflush(stdout);
896		if (fgets(lbuf, LBUF, stdin) == NULL)
897			exit(1);
898		lbuf[strlen(lbuf)-1] = 0;
899
900		if (!*lbuf)
901			return 0;
902
903		cp = lbuf;
904		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
905		if (!c)
906			return 0;
907		while ((c = *cp++)) {
908			if (c <= '9' && c >= '0')
909				acc = acc * 10 + c - '0';
910			else
911				break;
912		}
913		if (c == ' ' || c == '\t')
914			while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
915		if (!c) {
916			*num = acc;
917			return 1;
918		} else
919			printf("%s is an invalid decimal number.  Try again.\n",
920				lbuf);
921	}
922
923}
924
925#if 0
926static int
927hex(char *str, int *num, int deflt)
928{
929int acc = 0, c;
930char *cp;
931
932	while (1) {
933		printf("Supply a hex value for \"%s\" [%x] ", str, deflt);
934		fgets(lbuf, LBUF, stdin);
935		lbuf[strlen(lbuf)-1] = 0;
936
937		if (!*lbuf)
938			return 0;
939
940		cp = lbuf;
941		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
942		if (!c)
943			return 0;
944		while ((c = *cp++)) {
945			if (c <= '9' && c >= '0')
946				acc = (acc << 4) + c - '0';
947			else if (c <= 'f' && c >= 'a')
948				acc = (acc << 4) + c - 'a' + 10;
949			else if (c <= 'F' && c >= 'A')
950				acc = (acc << 4) + c - 'A' + 10;
951			else
952				break;
953		}
954		if (c == ' ' || c == '\t')
955			while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
956		if (!c) {
957			*num = acc;
958			return 1;
959		} else
960			printf("%s is an invalid hex number.  Try again.\n",
961				lbuf);
962	}
963
964}
965
966static int
967string(char *str, char **ans)
968{
969int c;
970char *cp = lbuf;
971
972	while (1) {
973		printf("Supply a string value for \"%s\" [%s] ", str, *ans);
974		fgets(lbuf, LBUF, stdin);
975		lbuf[strlen(lbuf)-1] = 0;
976
977		if (!*lbuf)
978			return 0;
979
980		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
981		if (c == '"') {
982			c = *++cp;
983			*ans = cp;
984			while ((c = *cp) && c != '"') cp++;
985		} else {
986			*ans = cp;
987			while ((c = *cp) && c != ' ' && c != '\t') cp++;
988		}
989
990		if (c)
991			*cp = 0;
992		return 1;
993	}
994}
995#endif
996
997static char *
998get_type(int type)
999{
1000	int	numentries = (sizeof(part_types)/sizeof(struct part_type));
1001	int	counter = 0;
1002	struct	part_type *ptr = part_types;
1003
1004
1005	while(counter < numentries)
1006	{
1007		if(ptr->type == type)
1008		{
1009			return(ptr->name);
1010		}
1011		ptr++;
1012		counter++;
1013	}
1014	return("unknown");
1015}
1016
1017
1018static void
1019parse_config_line(line, command)
1020    char	*line;
1021    CMD		*command;
1022{
1023    char	*cp, *end;
1024
1025    cp = line;
1026    while (1)	/* dirty trick used to insure one exit point for this
1027		   function */
1028    {
1029	memset(command, 0, sizeof(*command));
1030
1031	while (isspace(*cp)) ++cp;
1032	if (*cp == '\0' || *cp == '#')
1033	{
1034	    break;
1035	}
1036	command->cmd = *cp++;
1037
1038	/*
1039	 * Parse args
1040	 */
1041	while (1)
1042	{
1043	    while (isspace(*cp)) ++cp;
1044	    if (*cp == '#')
1045	    {
1046		break;		/* found comment */
1047	    }
1048	    if (isalpha(*cp))
1049	    {
1050		command->args[command->n_args].argtype = *cp++;
1051	    }
1052	    if (!isdigit(*cp))
1053	    {
1054		break;		/* assume end of line */
1055	    }
1056	    end = NULL;
1057	    command->args[command->n_args].arg_val = strtol(cp, &end, 0);
1058	    if (cp == end)
1059	    {
1060		break;		/* couldn't parse number */
1061	    }
1062	    cp = end;
1063	    command->n_args++;
1064	}
1065	break;
1066    }
1067}
1068
1069
1070static int
1071process_geometry(command)
1072    CMD		*command;
1073{
1074    int		status = 1, i;
1075
1076    while (1)
1077    {
1078	geom_processed = 1;
1079	if (part_processed)
1080	{
1081	    warnx(
1082	"ERROR line %d: the geometry specification line must occur before\n\
1083    all partition specifications",
1084		    current_line_number);
1085	    status = 0;
1086	    break;
1087	}
1088	if (command->n_args != 3)
1089	{
1090	    warnx("ERROR line %d: incorrect number of geometry args",
1091		    current_line_number);
1092	    status = 0;
1093	    break;
1094	}
1095	dos_cyls = -1;
1096	dos_heads = -1;
1097	dos_sectors = -1;
1098	for (i = 0; i < 3; ++i)
1099	{
1100	    switch (command->args[i].argtype)
1101	    {
1102	    case 'c':
1103		dos_cyls = command->args[i].arg_val;
1104		break;
1105	    case 'h':
1106		dos_heads = command->args[i].arg_val;
1107		break;
1108	    case 's':
1109		dos_sectors = command->args[i].arg_val;
1110		break;
1111	    default:
1112		warnx(
1113		"ERROR line %d: unknown geometry arg type: '%c' (0x%02x)",
1114			current_line_number, command->args[i].argtype,
1115			command->args[i].argtype);
1116		status = 0;
1117		break;
1118	    }
1119	}
1120	if (status == 0)
1121	{
1122	    break;
1123	}
1124
1125	dos_cylsecs = dos_heads * dos_sectors;
1126
1127	/*
1128	 * Do sanity checks on parameter values
1129	 */
1130	if (dos_cyls < 0)
1131	{
1132	    warnx("ERROR line %d: number of cylinders not specified",
1133		    current_line_number);
1134	    status = 0;
1135	}
1136	if (dos_cyls == 0 || dos_cyls > 1024)
1137	{
1138	    warnx(
1139	"WARNING line %d: number of cylinders (%d) may be out-of-range\n\
1140    (must be within 1-1024 for normal BIOS operation, unless the entire disk\n\
1141    is dedicated to FreeBSD)",
1142		    current_line_number, dos_cyls);
1143	}
1144
1145	if (dos_heads < 0)
1146	{
1147	    warnx("ERROR line %d: number of heads not specified",
1148		    current_line_number);
1149	    status = 0;
1150	}
1151	else if (dos_heads < 1 || dos_heads > 256)
1152	{
1153	    warnx("ERROR line %d: number of heads must be within (1-256)",
1154		    current_line_number);
1155	    status = 0;
1156	}
1157
1158	if (dos_sectors < 0)
1159	{
1160	    warnx("ERROR line %d: number of sectors not specified",
1161		    current_line_number);
1162	    status = 0;
1163	}
1164	else if (dos_sectors < 1 || dos_sectors > 63)
1165	{
1166	    warnx("ERROR line %d: number of sectors must be within (1-63)",
1167		    current_line_number);
1168	    status = 0;
1169	}
1170
1171	break;
1172    }
1173    return (status);
1174}
1175
1176
1177static int
1178process_partition(command)
1179    CMD		*command;
1180{
1181    int				status = 0, partition;
1182    u_int32_t			prev_head_boundary, prev_cyl_boundary;
1183    u_int32_t			adj_size, max_end;
1184    struct dos_partition	*partp;
1185
1186    while (1)
1187    {
1188	part_processed = 1;
1189	if (command->n_args != 4)
1190	{
1191	    warnx("ERROR line %d: incorrect number of partition args",
1192		    current_line_number);
1193	    break;
1194	}
1195	partition = command->args[0].arg_val;
1196	if (partition < 1 || partition > 4)
1197	{
1198	    warnx("ERROR line %d: invalid partition number %d",
1199		    current_line_number, partition);
1200	    break;
1201	}
1202	partp = ((struct dos_partition *) &mboot.parts) + partition - 1;
1203	bzero((char *)partp, sizeof (struct dos_partition));
1204	partp->dp_typ = command->args[1].arg_val;
1205	partp->dp_start = command->args[2].arg_val;
1206	partp->dp_size = command->args[3].arg_val;
1207	max_end = partp->dp_start + partp->dp_size;
1208
1209	if (partp->dp_typ == 0)
1210	{
1211	    /*
1212	     * Get out, the partition is marked as unused.
1213	     */
1214	    /*
1215	     * Insure that it's unused.
1216	     */
1217	    bzero((char *)partp, sizeof (struct dos_partition));
1218	    status = 1;
1219	    break;
1220	}
1221
1222	/*
1223	 * Adjust start upwards, if necessary, to fall on an head boundary.
1224	 */
1225	if (partp->dp_start % dos_sectors != 0)
1226	{
1227	    prev_head_boundary = partp->dp_start / dos_sectors * dos_sectors;
1228	    if (max_end < dos_sectors ||
1229		prev_head_boundary > max_end - dos_sectors)
1230	    {
1231		/*
1232		 * Can't go past end of partition
1233		 */
1234		warnx(
1235	"ERROR line %d: unable to adjust start of partition %d to fall on\n\
1236    a head boundary",
1237			current_line_number, partition);
1238		break;
1239	    }
1240	    warnx(
1241	"WARNING: adjusting start offset of partition %d\n\
1242    from %u to %u, to fall on a head boundary",
1243		    partition, (u_int)partp->dp_start,
1244		    (u_int)(prev_head_boundary + dos_sectors));
1245	    partp->dp_start = prev_head_boundary + dos_sectors;
1246	}
1247
1248	/*
1249	 * Adjust size downwards, if necessary, to fall on a cylinder
1250	 * boundary.
1251	 */
1252	prev_cyl_boundary =
1253	    ((partp->dp_start + partp->dp_size) / dos_cylsecs) * dos_cylsecs;
1254	if (prev_cyl_boundary > partp->dp_start)
1255	    adj_size = prev_cyl_boundary - partp->dp_start;
1256	else
1257	{
1258	    warnx(
1259	"ERROR: could not adjust partition to start on a head boundary\n\
1260    and end on a cylinder boundary.");
1261	    return (0);
1262	}
1263	if (adj_size != partp->dp_size)
1264	{
1265	    warnx(
1266	"WARNING: adjusting size of partition %d from %u to %u\n\
1267    to end on a cylinder boundary",
1268		    partition, (u_int)partp->dp_size, (u_int)adj_size);
1269	    partp->dp_size = adj_size;
1270	}
1271	if (partp->dp_size == 0)
1272	{
1273	    warnx("ERROR line %d: size of partition %d is zero",
1274		    current_line_number, partition);
1275	    break;
1276	}
1277
1278	dos(partp);
1279	status = 1;
1280	break;
1281    }
1282    return (status);
1283}
1284
1285
1286static int
1287process_active(command)
1288    CMD		*command;
1289{
1290    int				status = 0, partition, i;
1291    struct dos_partition	*partp;
1292
1293    while (1)
1294    {
1295	active_processed = 1;
1296	if (command->n_args != 1)
1297	{
1298	    warnx("ERROR line %d: incorrect number of active args",
1299		    current_line_number);
1300	    status = 0;
1301	    break;
1302	}
1303	partition = command->args[0].arg_val;
1304	if (partition < 1 || partition > 4)
1305	{
1306	    warnx("ERROR line %d: invalid partition number %d",
1307		    current_line_number, partition);
1308	    break;
1309	}
1310	/*
1311	 * Reset active partition
1312	 */
1313	partp = ((struct dos_partition *) &mboot.parts);
1314	for (i = 0; i < NDOSPART; i++)
1315	    partp[i].dp_flag = 0;
1316	partp[partition-1].dp_flag = ACTIVE;
1317
1318	status = 1;
1319	break;
1320    }
1321    return (status);
1322}
1323
1324
1325static int
1326process_line(line)
1327    char	*line;
1328{
1329    CMD		command;
1330    int		status = 1;
1331
1332    while (1)
1333    {
1334	parse_config_line(line, &command);
1335	switch (command.cmd)
1336	{
1337	case 0:
1338	    /*
1339	     * Comment or blank line
1340	     */
1341	    break;
1342	case 'g':
1343	    /*
1344	     * Set geometry
1345	     */
1346	    status = process_geometry(&command);
1347	    break;
1348	case 'p':
1349	    status = process_partition(&command);
1350	    break;
1351	case 'a':
1352	    status = process_active(&command);
1353	    break;
1354	default:
1355	    status = 0;
1356	    break;
1357	}
1358	break;
1359    }
1360    return (status);
1361}
1362
1363
1364static int
1365read_config(config_file)
1366    char *config_file;
1367{
1368    FILE	*fp = NULL;
1369    int		status = 1;
1370    char	buf[1010];
1371
1372    while (1)	/* dirty trick used to insure one exit point for this
1373		   function */
1374    {
1375	if (strcmp(config_file, "-") != 0)
1376	{
1377	    /*
1378	     * We're not reading from stdin
1379	     */
1380	    if ((fp = fopen(config_file, "r")) == NULL)
1381	    {
1382		status = 0;
1383		break;
1384	    }
1385	}
1386	else
1387	{
1388	    fp = stdin;
1389	}
1390	current_line_number = 0;
1391	while (!feof(fp))
1392	{
1393	    if (fgets(buf, sizeof(buf), fp) == NULL)
1394	    {
1395		break;
1396	    }
1397	    ++current_line_number;
1398	    status = process_line(buf);
1399	    if (status == 0)
1400	    {
1401		break;
1402	    }
1403	}
1404	break;
1405    }
1406    if (fp)
1407    {
1408	/*
1409	 * It doesn't matter if we're reading from stdin, as we've reached EOF
1410	 */
1411	fclose(fp);
1412    }
1413    return (status);
1414}
1415
1416
1417static void
1418reset_boot(void)
1419{
1420    int				i;
1421    struct dos_partition	*partp;
1422
1423    init_boot();
1424    for (i = 0; i < 4; ++i)
1425    {
1426	partp = ((struct dos_partition *) &mboot.parts) + i;
1427	bzero((char *)partp, sizeof (struct dos_partition));
1428    }
1429}
1430
1431static int
1432sanitize_partition(partp)
1433    struct dos_partition	*partp;
1434{
1435    u_int32_t			prev_head_boundary, prev_cyl_boundary;
1436    u_int32_t			max_end, size, start;
1437
1438    start = partp->dp_start;
1439    size = partp->dp_size;
1440    max_end = start + size;
1441    /* Only allow a zero size if the partition is being marked unused. */
1442    if (size == 0) {
1443	if (start == 0 && partp->dp_typ == 0)
1444	    return (1);
1445	warnx("ERROR: size of partition is zero");
1446	return (0);
1447    }
1448    /* Return if no adjustment is necessary. */
1449    if (start % dos_sectors == 0 && (start + size) % dos_sectors == 0)
1450	return (1);
1451
1452    if (start % dos_sectors != 0)
1453	warnx("WARNING: partition does not start on a head boundary");
1454    if ((start  +size) % dos_sectors != 0)
1455	warnx("WARNING: partition does not end on a cylinder boundary");
1456    warnx("WARNING: this may confuse the BIOS or some operating systems");
1457    if (!ok("Correct this automatically?"))
1458	return (1);
1459
1460    /*
1461     * Adjust start upwards, if necessary, to fall on an head boundary.
1462     */
1463    if (start % dos_sectors != 0) {
1464	prev_head_boundary = start / dos_sectors * dos_sectors;
1465	if (max_end < dos_sectors ||
1466	    prev_head_boundary >= max_end - dos_sectors) {
1467	    /*
1468	     * Can't go past end of partition
1469	     */
1470	    warnx(
1471    "ERROR: unable to adjust start of partition to fall on a head boundary");
1472	    return (0);
1473        }
1474	start = prev_head_boundary + dos_sectors;
1475    }
1476
1477    /*
1478     * Adjust size downwards, if necessary, to fall on a cylinder
1479     * boundary.
1480     */
1481    prev_cyl_boundary = ((start + size) / dos_cylsecs) * dos_cylsecs;
1482    if (prev_cyl_boundary > start)
1483	size = prev_cyl_boundary - start;
1484    else {
1485	warnx("ERROR: could not adjust partition to start on a head boundary\n\
1486    and end on a cylinder boundary.");
1487	return (0);
1488    }
1489
1490    /* Finally, commit any changes to partp and return. */
1491    if (start != partp->dp_start) {
1492	warnx("WARNING: adjusting start offset of partition to %u",
1493	    (u_int)start);
1494	partp->dp_start = start;
1495    }
1496    if (size != partp->dp_size) {
1497	warnx("WARNING: adjusting size of partition to %u", (u_int)size);
1498	partp->dp_size = size;
1499    }
1500
1501    return (1);
1502}
1503
1504/*
1505 * Try figuring out the root device's canonical disk name.
1506 * The following choices are considered:
1507 *   /dev/ad0s1a     => /dev/ad0
1508 *   /dev/da0a       => /dev/da0
1509 *   /dev/vinum/root => /dev/vinum/root
1510 */
1511static char *
1512get_rootdisk(void)
1513{
1514	struct statfs rootfs;
1515	regex_t re;
1516#define NMATCHES 2
1517	regmatch_t rm[NMATCHES];
1518	char *s;
1519	int rv;
1520
1521	if (statfs("/", &rootfs) == -1)
1522		err(1, "statfs(\"/\")");
1523
1524	if ((rv = regcomp(&re, "^(/dev/.*)(\\d+(s\\d+)?[a-h])?$",
1525		    REG_EXTENDED)) != 0)
1526		errx(1, "regcomp() failed (%d)", rv);
1527	if ((rv = regexec(&re, rootfs.f_mntfromname, NMATCHES, rm, 0)) != 0)
1528		errx(1,
1529"mounted root fs resource doesn't match expectations (regexec returned %d)",
1530		    rv);
1531	if ((s = malloc(rm[1].rm_eo - rm[1].rm_so + 1)) == NULL)
1532		errx(1, "out of memory");
1533	memcpy(s, rootfs.f_mntfromname + rm[1].rm_so,
1534	    rm[1].rm_eo - rm[1].rm_so);
1535	s[rm[1].rm_eo - rm[1].rm_so] = 0;
1536
1537	return s;
1538}
1539