fdisk.c revision 43054
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	"$Id: fdisk.c,v 1.27 1998/11/26 12:24:35 joerg Exp $";
30#endif /* not lint */
31
32#include <sys/disklabel.h>
33#include <sys/stat.h>
34#include <ctype.h>
35#include <fcntl.h>
36#include <err.h>
37#include <errno.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42
43int iotest;
44
45#define LBUF 100
46static char lbuf[LBUF];
47
48/*
49 *
50 * Ported to 386bsd by Julian Elischer  Thu Oct 15 20:26:46 PDT 1992
51 *
52 * 14-Dec-89  Robert Baron (rvb) at Carnegie-Mellon University
53 *	Copyright (c) 1989	Robert. V. Baron
54 *	Created.
55 */
56
57#define Decimal(str, ans, tmp) if (decimal(str, &tmp, ans)) ans = tmp
58#define Hex(str, ans, tmp) if (hex(str, &tmp, ans)) ans = tmp
59#define String(str, ans, len) {char *z = ans; char **dflt = &z; if (string(str, dflt)) strncpy(ans, *dflt, len); }
60
61#define RoundCyl(x) ((((x) + cylsecs - 1) / cylsecs) * cylsecs)
62
63#define MAX_SEC_SIZE 2048	/* maximum section size that is supported */
64#define MIN_SEC_SIZE 512	/* the sector size to start sensing at */
65int secsize = 0;		/* the sensed sector size */
66
67const char *disk;
68const char *disks[] =
69{
70  "/dev/rwd0", "/dev/rda0", "/dev/rod0", 0
71};
72
73struct disklabel disklabel;		/* disk parameters */
74
75int cyls, sectors, heads, cylsecs, disksecs;
76
77struct mboot
78{
79	unsigned char padding[2]; /* force the longs to be long aligned */
80	unsigned char bootinst[DOSPARTOFF];
81	struct	dos_partition parts[4];
82	unsigned short int	signature;
83	/* room to read in MBRs that are bigger then DEV_BSIZE */
84	unsigned char large_sector_overflow[MAX_SEC_SIZE-MIN_SEC_SIZE];
85};
86struct mboot mboot;
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 a_flag  = 0;		/* set active partition */
121static int b_flag  = 0;		/* replace boot code */
122static int i_flag  = 0;		/* replace partition data */
123static int u_flag  = 0;		/* update partition data */
124static int t_flag  = 0;		/* test only, if f_flag is given */
125static char *f_flag = NULL;	/* Read config info from file */
126static int v_flag  = 0;		/* Be verbose */
127
128static unsigned char bootcode[] = {
1290x33, 0xc0, 0xfa, 0x8e, 0xd0, 0xbc, 0x00, 0x7c, 0x8e, 0xc0, 0x8e, 0xd8, 0xfb, 0x8b, 0xf4, 0xbf,
1300x00, 0x06, 0xb9, 0x00, 0x02, 0xfc, 0xf3, 0xa4, 0xea, 0x1d, 0x06, 0x00, 0x00, 0xb0, 0x04, 0xbe,
1310xbe, 0x07, 0x80, 0x3c, 0x80, 0x74, 0x0c, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x75, 0xf4, 0xbe, 0xbd,
1320x06, 0xeb, 0x43, 0x8b, 0xfe, 0x8b, 0x14, 0x8b, 0x4c, 0x02, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x74,
1330x0a, 0x80, 0x3c, 0x80, 0x75, 0xf4, 0xbe, 0xbd, 0x06, 0xeb, 0x2b, 0xbd, 0x05, 0x00, 0xbb, 0x00,
1340x7c, 0xb8, 0x01, 0x02, 0xcd, 0x13, 0x73, 0x0c, 0x33, 0xc0, 0xcd, 0x13, 0x4d, 0x75, 0xef, 0xbe,
1350x9e, 0x06, 0xeb, 0x12, 0x81, 0x3e, 0xfe, 0x7d, 0x55, 0xaa, 0x75, 0x07, 0x8b, 0xf7, 0xea, 0x00,
1360x7c, 0x00, 0x00, 0xbe, 0x85, 0x06, 0x2e, 0xac, 0x0a, 0xc0, 0x74, 0x06, 0xb4, 0x0e, 0xcd, 0x10,
1370xeb, 0xf4, 0xfb, 0xeb, 0xfe,
138'M', 'i', 's', 's', 'i', 'n', 'g', ' ',
139	'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
140'E', 'r', 'r', 'o', 'r', ' ', 'l', 'o', 'a', 'd', 'i', 'n', 'g', ' ',
141	'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
142'I', 'n', 'v', 'a', 'l', 'i', 'd', ' ',
143	'p', 'a', 'r', 't', 'i', 't', 'i', 'o', 'n', ' ', 't', 'a', 'b', 'l', 'e', 0,
144'A', 'u', 't', 'h', 'o', 'r', ' ', '-', ' ',
145	'S', 'i', 'e', 'g', 'm', 'a', 'r', ' ', 'S', 'c', 'h', 'm', 'i', 'd', 't', 0,0,0,
146
147  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
148  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
149  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
150  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
151  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
152  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
153  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
154  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
155  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
156  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
157  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
158  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
159  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
160};
161
162struct part_type
163{
164 unsigned char type;
165 char *name;
166}part_types[] =
167{
168	 {0x00, "unused"}
169	,{0x01, "Primary DOS with 12 bit FAT"}
170	,{0x02, "XENIX / filesystem"}
171	,{0x03, "XENIX /usr filesystem"}
172	,{0x04, "Primary DOS with 16 bit FAT (<= 32MB)"}
173	,{0x05, "Extended DOS"}
174	,{0x06, "Primary 'big' DOS (> 32MB)"}
175	,{0x07, "OS/2 HPFS, NTFS, QNX or Advanced UNIX"}
176	,{0x08, "AIX filesystem"}
177	,{0x09, "AIX boot partition or Coherent"}
178	,{0x0A, "OS/2 Boot Manager or OPUS"}
179	,{0x0B, "DOS or Windows 95 with 32 bit FAT"}
180	,{0x0C, "DOS or Windows 95 with 32 bit FAT, LBA"}
181	,{0x0E, "Primary 'big' DOS (> 32MB, LBA)"}
182	,{0x0F, "Extended DOS, LBA"}
183	,{0x10, "OPUS"}
184	,{0x40, "VENIX 286"}
185	,{0x50, "DM"}
186	,{0x51, "DM"}
187	,{0x52, "CP/M or Microport SysV/AT"}
188	,{0x56, "GB"}
189	,{0x61, "Speed"}
190	,{0x63, "ISC UNIX, other System V/386, GNU HURD or Mach"}
191	,{0x64, "Novell Netware 2.xx"}
192	,{0x65, "Novell Netware 3.xx"}
193	,{0x75, "PCIX"}
194	,{0x80, "Minix 1.1 ... 1.4a"}
195	,{0x81, "Minix 1.4b ... 1.5.10"}
196	,{0x82, "Linux swap or Solaris x86"}
197	,{0x83, "Linux filesystem"}
198	,{0x93, "Amoeba filesystem"}
199	,{0x94, "Amoeba bad block table"}
200	,{0x9F, "BSD/OS"}
201	,{0xA5, "FreeBSD/NetBSD/386BSD"}
202	,{0xA6, "OpenBSD"}
203	,{0xA7, "NEXTSTEP"}
204	,{0xA9, "NetBSD"}
205	,{0xB7, "BSDI BSD/386 filesystem"}
206	,{0xB8, "BSDI BSD/386 swap"}
207	,{0xDB, "Concurrent CPM or C.DOS or CTOS"}
208	,{0xE1, "Speed"}
209	,{0xE3, "Speed"}
210	,{0xE4, "Speed"}
211	,{0xF1, "Speed"}
212	,{0xF2, "DOS 3.3+ Secondary"}
213	,{0xF4, "Speed"}
214	,{0xFF, "BBT (Bad Blocks Table)"}
215};
216
217static void print_s0(int which);
218static void print_part(int i);
219static void init_sector0(unsigned long start);
220static void init_boot(void);
221static void change_part(int i);
222static void print_params();
223static void change_active(int which);
224static void change_code();
225static void get_params_to_use();
226static void dos(int sec, int size, unsigned char *c, unsigned char *s,
227		unsigned char *h);
228static int open_disk(int u_flag);
229static ssize_t read_disk(off_t sector, void *buf);
230static ssize_t write_disk(off_t sector, void *buf);
231static int get_params();
232static int read_s0();
233static int write_s0();
234static int ok(char *str);
235static int decimal(char *str, int *num, int deflt);
236static char *get_type(int type);
237static int read_config(char *config_file);
238static void reset_boot(void);
239static void usage(void);
240#if 0
241static int hex(char *str, int *num, int deflt);
242static int string(char *str, char **ans);
243#endif
244
245
246int
247main(int argc, char *argv[])
248{
249	int	i;
250
251	for ( argv++ ; --argc ; argv++ ) { register char *token = *argv;
252		if (*token++ != '-' || !*token)
253			break;
254		else { register int flag;
255			for ( ; (flag = *token++) ; ) {
256				switch (flag) {
257				case '1':
258					partition = 1;
259					break;
260				case '2':
261					partition = 2;
262					break;
263				case '3':
264					partition = 3;
265					break;
266				case '4':
267					partition = 4;
268					break;
269				case 'a':
270					a_flag = 1;
271					break;
272				case 'b':
273					b_flag = 1;
274					break;
275				case 'f':
276					if (*token)
277					{
278					    f_flag = token;
279					    token = "";
280					}
281					else
282					{
283					    if (argc == 1)
284						usage();
285					    --argc;
286					    f_flag = *++argv;
287					}
288					/*
289					 * u_flag is needed, because we're
290					 * writing to the disk.
291					 */
292					u_flag = 1;
293					break;
294				case 'i':
295					i_flag = 1;
296				case 'u':
297					u_flag = 1;
298					break;
299				case 't':
300					t_flag = 1;
301				case 'v':
302					v_flag = 1;
303					break;
304				default:
305					usage();
306				}
307			}
308		}
309	}
310
311	if (argc > 0)
312	{
313		static char realname[12];
314
315		if(strncmp(argv[0], "/dev", 4) == 0)
316			disk = argv[0];
317		else
318		{
319			snprintf(realname, 12, "/dev/r%s", argv[0]);
320			disk = realname;
321		}
322
323		if (open_disk(u_flag) < 0)
324			err(1, "cannot open disk %s", disk);
325	}
326	else
327	{
328		int i, rv = 0;
329
330		for(i = 0; disks[i]; i++)
331		{
332			disk = disks[i];
333			rv = open_disk(u_flag);
334			if(rv != -2) break;
335		}
336		if(rv < 0)
337			err(1, "cannot open any disk");
338	}
339
340	printf("******* Working on device %s *******\n",disk);
341
342	if (f_flag)
343	{
344	    if (read_s0() || i_flag)
345	    {
346		reset_boot();
347	    }
348
349	    if (!read_config(f_flag))
350	    {
351		exit(1);
352	    }
353	    if (v_flag)
354	    {
355		print_s0(-1);
356	    }
357	    if (!t_flag)
358	    {
359		write_s0();
360	    }
361	}
362	else
363	{
364	    if(u_flag)
365	    {
366		get_params_to_use();
367	    }
368	    else
369	    {
370		print_params();
371	    }
372
373	    if (read_s0())
374		init_sector0(1);
375
376	    printf("Media sector size is %d\n", secsize);
377	    printf("Warning: BIOS sector numbering starts with sector 1\n");
378	    printf("Information from DOS bootblock is:\n");
379	    if (partition == -1)
380		for (i = 1; i <= NDOSPART; i++)
381		    change_part(i);
382	    else
383		change_part(partition);
384
385	    if (u_flag || a_flag)
386		change_active(partition);
387
388	    if (b_flag)
389		change_code();
390
391	    if (u_flag || a_flag || b_flag) {
392		if (!t_flag)
393		{
394		    printf("\nWe haven't changed the partition table yet.  ");
395		    printf("This is your last chance.\n");
396		}
397		print_s0(-1);
398		if (!t_flag)
399		{
400		    if (ok("Should we write new partition table?"))
401			write_s0();
402		}
403		else
404		{
405		    printf("\n-t flag specified -- partition table not written.\n");
406		}
407	    }
408	}
409
410	exit(0);
411}
412
413static void
414usage()
415{
416	fprintf(stderr,
417 "usage: fdisk {-a|-b|-i|-u} [-f configfile [-t] [-v]] [-{1,2,3,4}] [disk]\n");
418        exit(1);
419}
420
421static void
422print_s0(int which)
423{
424int	i;
425
426	print_params();
427	printf("Information from DOS bootblock is:\n");
428	if (which == -1)
429		for (i = 1; i <= NDOSPART; i++)
430			printf("%d: ", i), print_part(i);
431	else
432		print_part(which);
433}
434
435static struct dos_partition mtpart = { 0 };
436
437static void
438print_part(int i)
439{
440	struct	  dos_partition *partp;
441	u_int64_t part_mb;
442
443	partp = ((struct dos_partition *) &mboot.parts) + i - 1;
444
445	if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) {
446		printf("<UNUSED>\n");
447		return;
448	}
449	/*
450	 * Be careful not to overflow.
451	 */
452	part_mb = partp->dp_size;
453	part_mb *= secsize;
454	part_mb /= (1024 * 1024);
455	printf("sysid %d,(%s)\n", partp->dp_typ, get_type(partp->dp_typ));
456	printf("    start %lu, size %lu (%qd Meg), flag %x%s\n",
457		(u_long)partp->dp_start,
458		(u_long)partp->dp_size,
459		part_mb,
460		partp->dp_flag,
461		partp->dp_flag == ACTIVE ? " (active)" : "");
462	printf("\tbeg: cyl %d/ sector %d/ head %d;\n\tend: cyl %d/ sector %d/ head %d\n"
463		,DPCYL(partp->dp_scyl, partp->dp_ssect)
464		,DPSECT(partp->dp_ssect)
465		,partp->dp_shd
466		,DPCYL(partp->dp_ecyl, partp->dp_esect)
467		,DPSECT(partp->dp_esect)
468		,partp->dp_ehd);
469}
470
471
472static void
473init_boot(void)
474{
475	memcpy(mboot.bootinst, bootcode, sizeof(bootcode));
476	mboot.signature = BOOT_MAGIC;
477}
478
479
480static void
481init_sector0(unsigned long start)
482{
483struct dos_partition *partp = (struct dos_partition *) (&mboot.parts[3]);
484unsigned long size = disksecs - start;
485
486	init_boot();
487
488	partp->dp_typ = DOSPTYP_386BSD;
489	partp->dp_flag = ACTIVE;
490	partp->dp_start = start;
491	partp->dp_size = size;
492
493	dos(partp->dp_start, partp->dp_size,
494	    &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
495	dos(partp->dp_start + partp->dp_size - 1, partp->dp_size,
496	    &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
497}
498
499static void
500change_part(int i)
501{
502struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i - 1;
503
504    printf("The data for partition %d is:\n", i);
505    print_part(i);
506
507    if (u_flag && ok("Do you want to change it?")) {
508	int tmp;
509
510	if (i_flag) {
511		bzero((char *)partp, sizeof (struct dos_partition));
512		if (i == 4) {
513			init_sector0(1);
514			printf("\nThe static data for the DOS partition 4 has been reinitialized to:\n");
515			print_part(i);
516		}
517	}
518
519	do {
520		Decimal("sysid (165=FreeBSD)", partp->dp_typ, tmp);
521		Decimal("start", partp->dp_start, tmp);
522		Decimal("size", partp->dp_size, tmp);
523
524		if (ok("Explicitly specify beg/end address ?"))
525		{
526			int	tsec,tcyl,thd;
527			tcyl = DPCYL(partp->dp_scyl,partp->dp_ssect);
528			thd = partp->dp_shd;
529			tsec = DPSECT(partp->dp_ssect);
530			Decimal("beginning cylinder", tcyl, tmp);
531			Decimal("beginning head", thd, tmp);
532			Decimal("beginning sector", tsec, tmp);
533			partp->dp_scyl = DOSCYL(tcyl);
534			partp->dp_ssect = DOSSECT(tsec,tcyl);
535			partp->dp_shd = thd;
536
537			tcyl = DPCYL(partp->dp_ecyl,partp->dp_esect);
538			thd = partp->dp_ehd;
539			tsec = DPSECT(partp->dp_esect);
540			Decimal("ending cylinder", tcyl, tmp);
541			Decimal("ending head", thd, tmp);
542			Decimal("ending sector", tsec, tmp);
543			partp->dp_ecyl = DOSCYL(tcyl);
544			partp->dp_esect = DOSSECT(tsec,tcyl);
545			partp->dp_ehd = thd;
546		} else {
547			dos(partp->dp_start, partp->dp_size,
548			    &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
549			dos(partp->dp_start + partp->dp_size - 1, partp->dp_size,
550			    &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
551		}
552
553		print_part(i);
554	} while (!ok("Are we happy with this entry?"));
555    }
556}
557
558static void
559print_params()
560{
561	printf("parameters extracted from in-core disklabel are:\n");
562	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
563			,cyls,heads,sectors,cylsecs);
564	if((dos_sectors > 63) || (dos_cyls > 1023) || (dos_heads > 255))
565		printf("Figures below won't work with BIOS for partitions not in cyl 1\n");
566	printf("parameters to be used for BIOS calculations are:\n");
567	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
568		,dos_cyls,dos_heads,dos_sectors,dos_cylsecs);
569}
570
571static void
572change_active(int which)
573{
574int i;
575int active = 4, tmp;
576struct dos_partition *partp = ((struct dos_partition *) &mboot.parts);
577
578	if (a_flag && which != -1)
579		active = which;
580	if (!ok("Do you want to change the active partition?"))
581		return;
582setactive:
583	active = 4;
584	do {
585		Decimal("active partition", active, tmp);
586		if (active < 1 || 4 < active) {
587			printf("Active partition number must be in range 1-4."
588					"  Try again.\n");
589			goto setactive;
590		}
591	} while (!ok("Are you happy with this choice"));
592	for (i = 0; i < NDOSPART; i++)
593		partp[i].dp_flag = 0;
594	if (active > 0 && active <= NDOSPART)
595		partp[active-1].dp_flag = ACTIVE;
596}
597
598static void
599change_code()
600{
601	if (ok("Do you want to change the boot code?"))
602		init_boot();
603
604}
605
606void
607get_params_to_use()
608{
609	int	tmp;
610	print_params();
611	if (ok("Do you want to change our idea of what BIOS thinks ?"))
612	{
613		do
614		{
615			Decimal("BIOS's idea of #cylinders", dos_cyls, tmp);
616			Decimal("BIOS's idea of #heads", dos_heads, tmp);
617			Decimal("BIOS's idea of #sectors", dos_sectors, tmp);
618			dos_cylsecs = dos_heads * dos_sectors;
619			print_params();
620		}
621		while(!ok("Are you happy with this choice"));
622	}
623}
624
625
626/***********************************************\
627* Change real numbers into strange dos numbers	*
628\***********************************************/
629static void
630dos(sec, size, c, s, h)
631int sec, size;
632unsigned char *c, *s, *h;
633{
634int cy;
635int hd;
636
637	if (sec == 0 && size == 0) {
638		*s = *c = *h = 0;
639		return;
640	}
641
642	cy = sec / ( dos_cylsecs );
643	sec = sec - cy * ( dos_cylsecs );
644
645	hd = sec / dos_sectors;
646	sec = (sec - hd * dos_sectors) + 1;
647
648	*h = hd;
649	*c = cy & 0xff;
650	*s = (sec & 0x3f) | ( (cy & 0x300) >> 2);
651}
652
653int fd;
654
655	/* Getting device status */
656
657static int
658open_disk(int u_flag)
659{
660struct stat 	st;
661
662	if (stat(disk, &st) == -1) {
663		warnx("can't get file status of %s", disk);
664		return -1;
665	}
666	if ( !(st.st_mode & S_IFCHR) )
667		warnx("device %s is not character special", disk);
668	if ((fd = open(disk,
669	    a_flag || b_flag || u_flag ? O_RDWR : O_RDONLY)) == -1) {
670		if(errno == ENXIO)
671			return -2;
672		warnx("can't open device %s", disk);
673		return -1;
674	}
675	if (get_params(0) == -1) {
676		warnx("can't get disk parameters on %s", disk);
677		return -1;
678	}
679	return fd;
680}
681
682static ssize_t
683read_disk(off_t sector, void *buf)
684{
685	lseek(fd,(sector * 512), 0);
686	if( secsize == 0 )
687		for( secsize = MIN_SEC_SIZE; secsize <= MAX_SEC_SIZE; secsize *= 2 )
688			{
689			/* try the read */
690			int size = read(fd, buf, secsize);
691			if( size == secsize )
692				/* it worked so return */
693				return secsize;
694			}
695	else
696		return read( fd, buf, secsize );
697
698	/* we failed to read at any of the sizes */
699	return -1;
700}
701
702static ssize_t
703write_disk(off_t sector, void *buf)
704{
705	lseek(fd,(sector * 512), 0);
706	/* write out in the size that the read_disk found worked */
707	return write(fd, buf, secsize);
708}
709
710static int
711get_params()
712{
713
714    if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) {
715	warnx("can't get disk parameters on %s; supplying dummy ones", disk);
716	dos_cyls = cyls = 1;
717	dos_heads = heads = 1;
718	dos_sectors = sectors = 1;
719	dos_cylsecs = cylsecs = heads * sectors;
720	disksecs = cyls * heads * sectors;
721	return disksecs;
722    }
723
724    dos_cyls = cyls = disklabel.d_ncylinders;
725    dos_heads = heads = disklabel.d_ntracks;
726    dos_sectors = sectors = disklabel.d_nsectors;
727    dos_cylsecs = cylsecs = heads * sectors;
728    disksecs = cyls * heads * sectors;
729
730    return (disksecs);
731}
732
733
734static int
735read_s0()
736{
737	if (read_disk(0, (char *) mboot.bootinst) == -1) {
738		warnx("can't read fdisk partition table");
739		return -1;
740	}
741	if (mboot.signature != BOOT_MAGIC) {
742		warnx("invalid fdisk partition table found");
743		/* So should we initialize things */
744		return -1;
745	}
746	return 0;
747}
748
749static int
750write_s0()
751{
752	int	flag;
753	if (iotest) {
754		print_s0(-1);
755		return 0;
756	}
757	/*
758	 * write enable label sector before write (if necessary),
759	 * disable after writing.
760	 * needed if the disklabel protected area also protects
761	 * sector 0. (e.g. empty disk)
762	 */
763	flag = 1;
764#ifdef NOT_NOW
765	if (ioctl(fd, DIOCWLABEL, &flag) < 0)
766		warn("ioctl DIOCWLABEL");
767#endif
768	if (write_disk(0, (char *) mboot.bootinst) == -1) {
769		warnx("can't write fdisk partition table");
770		return -1;
771	flag = 0;
772#ifdef NOT_NOW
773	(void) ioctl(fd, DIOCWLABEL, &flag);
774#endif
775	}
776	return(0);
777}
778
779
780static int
781ok(str)
782char *str;
783{
784	printf("%s [n] ", str);
785	fgets(lbuf, LBUF, stdin);
786	lbuf[strlen(lbuf)-1] = 0;
787
788	if (*lbuf &&
789		(!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") ||
790		 !strcmp(lbuf, "y") || !strcmp(lbuf, "Y")))
791		return 1;
792	else
793		return 0;
794}
795
796static int
797decimal(char *str, int *num, int deflt)
798{
799int acc = 0, c;
800char *cp;
801
802	while (1) {
803		printf("Supply a decimal value for \"%s\" [%d] ", str, deflt);
804		fgets(lbuf, LBUF, stdin);
805		lbuf[strlen(lbuf)-1] = 0;
806
807		if (!*lbuf)
808			return 0;
809
810		cp = lbuf;
811		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
812		if (!c)
813			return 0;
814		while ((c = *cp++)) {
815			if (c <= '9' && c >= '0')
816				acc = acc * 10 + c - '0';
817			else
818				break;
819		}
820		if (c == ' ' || c == '\t')
821			while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
822		if (!c) {
823			*num = acc;
824			return 1;
825		} else
826			printf("%s is an invalid decimal number.  Try again.\n",
827				lbuf);
828	}
829
830}
831
832#if 0
833static int
834hex(char *str, int *num, int deflt)
835{
836int acc = 0, c;
837char *cp;
838
839	while (1) {
840		printf("Supply a hex value for \"%s\" [%x] ", str, deflt);
841		fgets(lbuf, LBUF, stdin);
842		lbuf[strlen(lbuf)-1] = 0;
843
844		if (!*lbuf)
845			return 0;
846
847		cp = lbuf;
848		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
849		if (!c)
850			return 0;
851		while ((c = *cp++)) {
852			if (c <= '9' && c >= '0')
853				acc = (acc << 4) + c - '0';
854			else if (c <= 'f' && c >= 'a')
855				acc = (acc << 4) + c - 'a' + 10;
856			else if (c <= 'F' && c >= 'A')
857				acc = (acc << 4) + c - 'A' + 10;
858			else
859				break;
860		}
861		if (c == ' ' || c == '\t')
862			while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
863		if (!c) {
864			*num = acc;
865			return 1;
866		} else
867			printf("%s is an invalid hex number.  Try again.\n",
868				lbuf);
869	}
870
871}
872
873static int
874string(char *str, char **ans)
875{
876int c;
877char *cp = lbuf;
878
879	while (1) {
880		printf("Supply a string value for \"%s\" [%s] ", str, *ans);
881		fgets(lbuf, LBUF, stdin);
882		lbuf[strlen(lbuf)-1] = 0;
883
884		if (!*lbuf)
885			return 0;
886
887		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
888		if (c == '"') {
889			c = *++cp;
890			*ans = cp;
891			while ((c = *cp) && c != '"') cp++;
892		} else {
893			*ans = cp;
894			while ((c = *cp) && c != ' ' && c != '\t') cp++;
895		}
896
897		if (c)
898			*cp = 0;
899		return 1;
900	}
901}
902#endif
903
904static char *
905get_type(int type)
906{
907	int	numentries = (sizeof(part_types)/sizeof(struct part_type));
908	int	counter = 0;
909	struct	part_type *ptr = part_types;
910
911
912	while(counter < numentries)
913	{
914		if(ptr->type == type)
915		{
916			return(ptr->name);
917		}
918		ptr++;
919		counter++;
920	}
921	return("unknown");
922}
923
924
925static void
926parse_config_line(line, command)
927    char	*line;
928    CMD		*command;
929{
930    char	*cp, *end;
931
932    cp = line;
933    while (1)	/* dirty trick used to insure one exit point for this
934		   function */
935    {
936	memset(command, 0, sizeof(*command));
937
938	while (isspace(*cp)) ++cp;
939	if (*cp == '\0' || *cp == '#')
940	{
941	    break;
942	}
943	command->cmd = *cp++;
944
945	/*
946	 * Parse args
947	 */
948	while (1)
949	{
950	    while (isspace(*cp)) ++cp;
951	    if (*cp == '#')
952	    {
953		break;		/* found comment */
954	    }
955	    if (isalpha(*cp))
956	    {
957		command->args[command->n_args].argtype = *cp++;
958	    }
959	    if (!isdigit(*cp))
960	    {
961		break;		/* assume end of line */
962	    }
963	    end = NULL;
964	    command->args[command->n_args].arg_val = strtol(cp, &end, 0);
965	    if (cp == end)
966	    {
967		break;		/* couldn't parse number */
968	    }
969	    cp = end;
970	    command->n_args++;
971	}
972	break;
973    }
974}
975
976
977static int
978process_geometry(command)
979    CMD		*command;
980{
981    int		status = 1, i;
982
983    while (1)
984    {
985	geom_processed = 1;
986	if (part_processed)
987	{
988	    warnx(
989	"ERROR line %d: the geometry specification line must occur before\n\
990    all partition specifications",
991		    current_line_number);
992	    status = 0;
993	    break;
994	}
995	if (command->n_args != 3)
996	{
997	    warnx("ERROR line %d: incorrect number of geometry args",
998		    current_line_number);
999	    status = 0;
1000	    break;
1001	}
1002	dos_cyls = -1;
1003	dos_heads = -1;
1004	dos_sectors = -1;
1005	for (i = 0; i < 3; ++i)
1006	{
1007	    switch (command->args[i].argtype)
1008	    {
1009	    case 'c':
1010		dos_cyls = command->args[i].arg_val;
1011		break;
1012	    case 'h':
1013		dos_heads = command->args[i].arg_val;
1014		break;
1015	    case 's':
1016		dos_sectors = command->args[i].arg_val;
1017		break;
1018	    default:
1019		warnx(
1020		"ERROR line %d: unknown geometry arg type: '%c' (0x%02x)",
1021			current_line_number, command->args[i].argtype,
1022			command->args[i].argtype);
1023		status = 0;
1024		break;
1025	    }
1026	}
1027	if (status == 0)
1028	{
1029	    break;
1030	}
1031
1032	dos_cylsecs = dos_heads * dos_sectors;
1033
1034	/*
1035	 * Do sanity checks on parameter values
1036	 */
1037	if (dos_cyls < 0)
1038	{
1039	    warnx("ERROR line %d: number of cylinders not specified",
1040		    current_line_number);
1041	    status = 0;
1042	}
1043	if (dos_cyls == 0 || dos_cyls > 1024)
1044	{
1045	    warnx(
1046	"WARNING line %d: number of cylinders (%d) may be out-of-range\n\
1047    (must be within 1-1024 for normal BIOS operation, unless the entire disk\n\
1048    is dedicated to FreeBSD)",
1049		    current_line_number, dos_cyls);
1050	}
1051
1052	if (dos_heads < 0)
1053	{
1054	    warnx("ERROR line %d: number of heads not specified",
1055		    current_line_number);
1056	    status = 0;
1057	}
1058	else if (dos_heads < 1 || dos_heads > 256)
1059	{
1060	    warnx("ERROR line %d: number of heads must be within (1-256)",
1061		    current_line_number);
1062	    status = 0;
1063	}
1064
1065	if (dos_sectors < 0)
1066	{
1067	    warnx("ERROR line %d: number of sectors not specified",
1068		    current_line_number);
1069	    status = 0;
1070	}
1071	else if (dos_sectors < 1 || dos_sectors > 63)
1072	{
1073	    warnx("ERROR line %d: number of sectors must be within (1-63)",
1074		    current_line_number);
1075	    status = 0;
1076	}
1077
1078	break;
1079    }
1080    return (status);
1081}
1082
1083
1084static int
1085process_partition(command)
1086    CMD		*command;
1087{
1088    int				status = 0, partition;
1089    unsigned long		chunks, adj_size, max_end;
1090    struct dos_partition	*partp;
1091
1092    while (1)
1093    {
1094	part_processed = 1;
1095	if (command->n_args != 4)
1096	{
1097	    warnx("ERROR line %d: incorrect number of partition args",
1098		    current_line_number);
1099	    break;
1100	}
1101	partition = command->args[0].arg_val;
1102	if (partition < 1 || partition > 4)
1103	{
1104	    warnx("ERROR line %d: invalid partition number %d",
1105		    current_line_number, partition);
1106	    break;
1107	}
1108	partp = ((struct dos_partition *) &mboot.parts) + partition - 1;
1109	bzero((char *)partp, sizeof (struct dos_partition));
1110	partp->dp_typ = command->args[1].arg_val;
1111	partp->dp_start = command->args[2].arg_val;
1112	partp->dp_size = command->args[3].arg_val;
1113	max_end = partp->dp_start + partp->dp_size;
1114
1115	if (partp->dp_typ == 0)
1116	{
1117	    /*
1118	     * Get out, the partition is marked as unused.
1119	     */
1120	    /*
1121	     * Insure that it's unused.
1122	     */
1123	    bzero((char *)partp, sizeof (struct dos_partition));
1124	    status = 1;
1125	    break;
1126	}
1127
1128	/*
1129	 * Adjust start upwards, if necessary, to fall on an head boundary.
1130	 */
1131	if (partp->dp_start % dos_sectors != 0)
1132	{
1133	    adj_size =
1134		(partp->dp_start / dos_sectors + 1) * dos_sectors;
1135	    if (adj_size > max_end)
1136	    {
1137		/*
1138		 * Can't go past end of partition
1139		 */
1140		warnx(
1141	"ERROR line %d: unable to adjust start of partition %d to fall on\n\
1142    a cylinder boundary",
1143			current_line_number, partition);
1144		break;
1145	    }
1146	    warnx(
1147	"WARNING: adjusting start offset of partition '%d' from %lu\n\
1148    to %lu, to round to an head boundary",
1149		    partition, (u_long)partp->dp_start, adj_size);
1150	    partp->dp_start = adj_size;
1151	}
1152
1153	/*
1154	 * Adjust size downwards, if necessary, to fall on a cylinder
1155	 * boundary.
1156	 */
1157	chunks =
1158	    ((partp->dp_start + partp->dp_size) / dos_cylsecs) * dos_cylsecs;
1159	adj_size = chunks - partp->dp_start;
1160	if (adj_size != partp->dp_size)
1161	{
1162	    warnx(
1163	"WARNING: adjusting size of partition '%d' from %lu to %lu,\n\
1164    to round to a cylinder boundary",
1165		    partition, (u_long)partp->dp_size, adj_size);
1166	    if (chunks > 0)
1167	    {
1168		partp->dp_size = adj_size;
1169	    }
1170	    else
1171	    {
1172		partp->dp_size = 0;
1173	    }
1174	}
1175	if (partp->dp_size < 1)
1176	{
1177	    warnx("ERROR line %d: size for partition '%d' is zero",
1178		    current_line_number, partition);
1179	    break;
1180	}
1181
1182	dos(partp->dp_start, partp->dp_size,
1183	    &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
1184	dos(partp->dp_start+partp->dp_size - 1, partp->dp_size,
1185	    &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
1186	status = 1;
1187	break;
1188    }
1189    return (status);
1190}
1191
1192
1193static int
1194process_active(command)
1195    CMD		*command;
1196{
1197    int				status = 0, partition, i;
1198    struct dos_partition	*partp;
1199
1200    while (1)
1201    {
1202	active_processed = 1;
1203	if (command->n_args != 1)
1204	{
1205	    warnx("ERROR line %d: incorrect number of active args",
1206		    current_line_number);
1207	    status = 0;
1208	    break;
1209	}
1210	partition = command->args[0].arg_val;
1211	if (partition < 1 || partition > 4)
1212	{
1213	    warnx("ERROR line %d: invalid partition number %d",
1214		    current_line_number, partition);
1215	    break;
1216	}
1217	/*
1218	 * Reset active partition
1219	 */
1220	partp = ((struct dos_partition *) &mboot.parts);
1221	for (i = 0; i < NDOSPART; i++)
1222	    partp[i].dp_flag = 0;
1223	partp[partition-1].dp_flag = ACTIVE;
1224
1225	status = 1;
1226	break;
1227    }
1228    return (status);
1229}
1230
1231
1232static int
1233process_line(line)
1234    char	*line;
1235{
1236    CMD		command;
1237    int		status = 1;
1238
1239    while (1)
1240    {
1241	parse_config_line(line, &command);
1242	switch (command.cmd)
1243	{
1244	case 0:
1245	    /*
1246	     * Comment or blank line
1247	     */
1248	    break;
1249	case 'g':
1250	    /*
1251	     * Set geometry
1252	     */
1253	    status = process_geometry(&command);
1254	    break;
1255	case 'p':
1256	    status = process_partition(&command);
1257	    break;
1258	case 'a':
1259	    status = process_active(&command);
1260	    break;
1261	default:
1262	    status = 0;
1263	    break;
1264	}
1265	break;
1266    }
1267    return (status);
1268}
1269
1270
1271static int
1272read_config(config_file)
1273    char *config_file;
1274{
1275    FILE	*fp = NULL;
1276    int		status = 1;
1277    char	buf[1010];
1278
1279    while (1)	/* dirty trick used to insure one exit point for this
1280		   function */
1281    {
1282	if (strcmp(config_file, "-") != 0)
1283	{
1284	    /*
1285	     * We're not reading from stdin
1286	     */
1287	    if ((fp = fopen(config_file, "r")) == NULL)
1288	    {
1289		status = 0;
1290		break;
1291	    }
1292	}
1293	else
1294	{
1295	    fp = stdin;
1296	}
1297	current_line_number = 0;
1298	while (!feof(fp))
1299	{
1300	    if (fgets(buf, sizeof(buf), fp) == NULL)
1301	    {
1302		break;
1303	    }
1304	    ++current_line_number;
1305	    status = process_line(buf);
1306	    if (status == 0)
1307	    {
1308		break;
1309	    }
1310	}
1311	break;
1312    }
1313    if (fp)
1314    {
1315	/*
1316	 * It doesn't matter if we're reading from stdin, as we've reached EOF
1317	 */
1318	fclose(fp);
1319    }
1320    return (status);
1321}
1322
1323
1324static void
1325reset_boot(void)
1326{
1327    int				i;
1328    struct dos_partition	*partp;
1329
1330    init_boot();
1331    for (i = 0; i < 4; ++i)
1332    {
1333	partp = ((struct dos_partition *) &mboot.parts) + i;
1334	bzero((char *)partp, sizeof (struct dos_partition));
1335    }
1336}
1337