1254721Semaste/*
2254721Semaste * Mach Operating System
3254721Semaste * Copyright (c) 1992 Carnegie Mellon University
4254721Semaste * All Rights Reserved.
5254721Semaste *
6254721Semaste * Permission to use, copy, modify and distribute this software and its
7254721Semaste * documentation is hereby granted, provided that both the copyright
8254721Semaste * notice and this permission notice appear in all copies of the
9254721Semaste * software, derivative works or modified versions, and any portions
10254721Semaste * thereof, and that both notices appear in supporting documentation.
11254721Semaste *
12254721Semaste * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
13254721Semaste * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14254721Semaste * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15254721Semaste *
16254721Semaste * Carnegie Mellon requests users of this software to return to
17254721Semaste *
18254721Semaste *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19254721Semaste *  School of Computer Science
20254721Semaste *  Carnegie Mellon University
21254721Semaste *  Pittsburgh PA 15213-3890
22254721Semaste *
23254721Semaste * any improvements or extensions that they make and grant Carnegie Mellon
24254721Semaste * the rights to redistribute these changes.
25269024Semaste */
26254721Semaste
27254721Semaste#include <sys/cdefs.h>
28254721Semaste__FBSDID("$FreeBSD$");
29254721Semaste
30254721Semaste#include <sys/disk.h>
31254721Semaste#include <sys/disklabel.h>
32254721Semaste#include <sys/diskmbr.h>
33254721Semaste#include <sys/endian.h>
34254721Semaste#include <sys/param.h>
35254721Semaste#include <sys/stat.h>
36254721Semaste#include <sys/mount.h>
37254721Semaste#include <ctype.h>
38254721Semaste#include <fcntl.h>
39254721Semaste#include <err.h>
40254721Semaste#include <errno.h>
41254721Semaste#include <libgeom.h>
42254721Semaste#include <paths.h>
43254721Semaste#include <regex.h>
44254721Semaste#include <stdint.h>
45254721Semaste#include <stdio.h>
46254721Semaste#include <stdlib.h>
47254721Semaste#include <string.h>
48269024Semaste#include <unistd.h>
49254721Semaste
50254721Semastestatic int iotest;
51269024Semaste
52254721Semaste#define NO_DISK_SECTORS ((u_int32_t)-1)
53254721Semaste#define NO_TRACK_CYLINDERS 1023
54254721Semaste#define NO_TRACK_HEADS 255
55254721Semaste#define NO_TRACK_SECTORS 63
56254721Semaste#define LBUF 100
57254721Semastestatic char lbuf[LBUF];
58254721Semaste
59254721Semaste/*
60254721Semaste *
61254721Semaste * Ported to 386bsd by Julian Elischer  Thu Oct 15 20:26:46 PDT 1992
62254721Semaste *
63254721Semaste * 14-Dec-89  Robert Baron (rvb) at Carnegie-Mellon University
64254721Semaste *	Copyright (c) 1989	Robert. V. Baron
65254721Semaste *	Created.
66254721Semaste */
67254721Semaste
68254721Semaste#define Decimal(str, ans, tmp, maxval) if (decimal(str, &tmp, ans, maxval)) ans = tmp
69254721Semaste
70254721Semaste#define RoundCyl(x) ((((x) + cylsecs - 1) / cylsecs) * cylsecs)
71254721Semaste
72254721Semaste#define MAX_SEC_SIZE 2048	/* maximum section size that is supported */
73254721Semaste#define MIN_SEC_SIZE 512	/* the sector size to start sensing at */
74254721Semastestatic int secsize = 0;		/* the sensed sector size */
75254721Semaste
76254721Semastestatic char *disk;
77254721Semaste
78254721Semastestatic int cyls, sectors, heads, cylsecs, disksecs;
79254721Semaste
80254721Semastestruct mboot {
81254721Semaste	unsigned char *bootinst;  /* boot code */
82254721Semaste	off_t bootinst_size;
83254721Semaste	struct	dos_partition parts[NDOSPART];
84254721Semaste};
85254721Semaste
86254721Semastestatic struct mboot mboot;
87254721Semastestatic int fd;
88254721Semaste
89254721Semaste#define ACTIVE 0x80
90254721Semaste
91254721Semastestatic uint dos_cyls;
92254721Semastestatic uint dos_heads;
93254721Semastestatic uint dos_sectors;
94254721Semastestatic uint dos_cylsecs;
95254721Semaste
96254721Semaste#define DOSSECT(s,c) ((s & 0x3f) | ((c >> 2) & 0xc0))
97254721Semaste#define DOSCYL(c)	(c & 0xff)
98254721Semaste
99254721Semaste#define MAX_ARGS	10
100254721Semaste
101254721Semastestatic int	current_line_number;
102254721Semaste
103254721Semastestatic int	geom_processed = 0;
104269024Semastestatic int	part_processed = 0;
105254721Semastestatic int	active_processed = 0;
106254721Semaste
107254721Semastetypedef struct cmd {
108254721Semaste    char		cmd;
109254721Semaste    int			n_args;
110269024Semaste    struct arg {
111254721Semaste	char		argtype;
112254721Semaste	unsigned long	arg_val;
113254721Semaste	char *		arg_str;
114254721Semaste    }			args[MAX_ARGS];
115254721Semaste} CMD;
116254721Semaste
117254721Semastestatic int B_flag  = 0;		/* replace boot code */
118254721Semastestatic int I_flag  = 0;		/* use entire disk for FreeBSD */
119254721Semastestatic int a_flag  = 0;		/* set active partition */
120254721Semastestatic char *b_flag = NULL;	/* path to boot code */
121254721Semastestatic int i_flag  = 0;		/* replace partition data */
122254721Semastestatic int q_flag  = 0;		/* Be quiet */
123254721Semastestatic int u_flag  = 0;		/* update partition data */
124254721Semastestatic int s_flag  = 0;		/* Print a summary and exit */
125254721Semastestatic int t_flag  = 0;		/* test only */
126254721Semastestatic char *f_flag = NULL;	/* Read config info from file */
127254721Semastestatic int v_flag  = 0;		/* Be verbose */
128254721Semastestatic int print_config_flag = 0;
129254721Semaste
130254721Semaste/*
131254721Semaste * A list of partition types, probably outdated.
132254721Semaste */
133254721Semastestatic const char *const part_types[256] = {
134254721Semaste	[0x00] = "unused",
135254721Semaste	[0x01] = "Primary DOS with 12 bit FAT",
136254721Semaste	[0x02] = "XENIX / file system",
137254721Semaste	[0x03] = "XENIX /usr file system",
138254721Semaste	[0x04] = "Primary DOS with 16 bit FAT (< 32MB)",
139254721Semaste	[0x05] = "Extended DOS",
140254721Semaste	[0x06] = "Primary DOS, 16 bit FAT (>= 32MB)",
141254721Semaste	[0x07] = "NTFS, OS/2 HPFS, QNX-2 (16 bit) or Advanced UNIX",
142254721Semaste	[0x08] = "AIX file system or SplitDrive",
143254721Semaste	[0x09] = "AIX boot partition or Coherent",
144254721Semaste	[0x0A] = "OS/2 Boot Manager, OPUS or Coherent swap",
145254721Semaste	[0x0B] = "DOS or Windows 95 with 32 bit FAT",
146254721Semaste	[0x0C] = "DOS or Windows 95 with 32 bit FAT (LBA)",
147254721Semaste	[0x0E] = "Primary 'big' DOS (>= 32MB, LBA)",
148254721Semaste	[0x0F] = "Extended DOS (LBA)",
149254721Semaste	[0x10] = "OPUS",
150254721Semaste	[0x11] = "OS/2 BM: hidden DOS with 12-bit FAT",
151254721Semaste	[0x12] = "Compaq diagnostics",
152254721Semaste	[0x14] = "OS/2 BM: hidden DOS with 16-bit FAT (< 32MB)",
153254721Semaste	[0x16] = "OS/2 BM: hidden DOS with 16-bit FAT (>= 32MB)",
154254721Semaste	[0x17] = "OS/2 BM: hidden IFS (e.g. HPFS)",
155254721Semaste	[0x18] = "AST Windows swapfile",
156254721Semaste	[0x1b] = "ASUS Recovery partition (NTFS)",
157254721Semaste	[0x24] = "NEC DOS",
158254721Semaste	[0x3C] = "PartitionMagic recovery",
159254721Semaste	[0x39] = "plan9",
160254721Semaste	[0x40] = "VENIX 286",
161254721Semaste	[0x41] = "Linux/MINIX (sharing disk with DRDOS)",
162254721Semaste	[0x42] = "SFS or Linux swap (sharing disk with DRDOS)",
163254721Semaste	[0x43] = "Linux native (sharing disk with DRDOS)",
164254721Semaste	[0x4D] = "QNX 4.2 Primary",
165254721Semaste	[0x4E] = "QNX 4.2 Secondary",
166254721Semaste	[0x4F] = "QNX 4.2 Tertiary",
167254721Semaste	[0x50] = "DM (disk manager)",
168254721Semaste	[0x51] = "DM6 Aux1 (or Novell)",
169254721Semaste	[0x52] = "CP/M or Microport SysV/AT",
170254721Semaste	[0x53] = "DM6 Aux3",
171254721Semaste	[0x54] = "DM6",
172254721Semaste	[0x55] = "EZ-Drive (disk manager)",
173254721Semaste	[0x56] = "Golden Bow (disk manager)",
174254721Semaste	[0x5c] = "Priam Edisk (disk manager)", /* according to S. Widlake */
175254721Semaste	[0x61] = "SpeedStor",
176254721Semaste	[0x63] = "System V/386 (such as ISC UNIX), GNU HURD or Mach",
177254721Semaste	[0x64] = "Novell Netware/286 2.xx",
178254721Semaste	[0x65] = "Novell Netware/386 3.xx",
179254721Semaste	[0x70] = "DiskSecure Multi-Boot",
180254721Semaste	[0x75] = "PCIX",
181254721Semaste	[0x77] = "QNX4.x",
182254721Semaste	[0x78] = "QNX4.x 2nd part",
183254721Semaste	[0x79] = "QNX4.x 3rd part",
184254721Semaste	[0x80] = "Minix until 1.4a",
185254721Semaste	[0x81] = "Minix since 1.4b, early Linux partition or Mitac disk manager",
186254721Semaste	[0x82] = "Linux swap or Solaris x86",
187254721Semaste	[0x83] = "Linux native",
188254721Semaste	[0x84] = "OS/2 hidden C: drive",
189254721Semaste	[0x85] = "Linux extended",
190254721Semaste	[0x86] = "NTFS volume set??",
191254721Semaste	[0x87] = "NTFS volume set??",
192254721Semaste	[0x93] = "Amoeba file system",
193254721Semaste	[0x94] = "Amoeba bad block table",
194254721Semaste	[0x9F] = "BSD/OS",
195254721Semaste	[0xA0] = "Suspend to Disk",
196254721Semaste	[0xA5] = "FreeBSD/NetBSD/386BSD",
197254721Semaste	[0xA6] = "OpenBSD",
198254721Semaste	[0xA7] = "NeXTSTEP",
199254721Semaste	[0xA9] = "NetBSD",
200254721Semaste	[0xAC] = "IBM JFS",
201254721Semaste	[0xAF] = "HFS+",
202254721Semaste	[0xB7] = "BSDI BSD/386 file system",
203254721Semaste	[0xB8] = "BSDI BSD/386 swap",
204254721Semaste	[0xBE] = "Solaris x86 boot",
205254721Semaste	[0xBF] = "Solaris x86 (new)",
206254721Semaste	[0xC1] = "DRDOS/sec with 12-bit FAT",
207254721Semaste	[0xC4] = "DRDOS/sec with 16-bit FAT (< 32MB)",
208254721Semaste	[0xC6] = "DRDOS/sec with 16-bit FAT (>= 32MB)",
209254721Semaste	[0xC7] = "Syrinx",
210254721Semaste	[0xDB] = "CP/M, Concurrent CP/M, Concurrent DOS or CTOS",
211254721Semaste	[0xDE] = "DELL Utilities - FAT filesystem",
212254721Semaste	[0xE1] = "DOS access or SpeedStor with 12-bit FAT extended partition",
213254721Semaste	[0xE3] = "DOS R/O or SpeedStor",
214254721Semaste	[0xE4] = "SpeedStor with 16-bit FAT extended partition < 1024 cyl.",
215254721Semaste	[0xEB] = "BeOS file system",
216254721Semaste	[0xEE] = "EFI GPT",
217254721Semaste	[0xEF] = "EFI System Partition",
218254721Semaste	[0xF1] = "SpeedStor",
219254721Semaste	[0xF2] = "DOS 3.3+ Secondary",
220254721Semaste	[0xF4] = "SpeedStor large partition",
221254721Semaste	[0xFB] = "VMware VMFS",
222254721Semaste	[0xFE] = "SpeedStor >1024 cyl. or LANstep",
223254721Semaste	[0xFF] = "Xenix bad blocks table",
224254721Semaste};
225254721Semaste
226254721Semastestatic const char *
227254721Semasteget_type(int t)
228254721Semaste{
229254721Semaste	const char *ret;
230254721Semaste
231254721Semaste	ret = (t >= 0 && t <= 255) ? part_types[t] : NULL;
232254721Semaste	return ret ? ret : "unknown";
233254721Semaste}
234254721Semaste
235263363Semaste
236263363Semastestatic int geom_class_available(const char *);
237263363Semastestatic void print_s0(void);
238263363Semastestatic void print_part(const struct dos_partition *);
239263363Semastestatic void init_sector0(unsigned long start);
240263363Semastestatic void init_boot(void);
241263363Semastestatic void change_part(int i);
242254721Semastestatic void print_params(void);
243254721Semastestatic void change_active(int which);
244254721Semastestatic void change_code(void);
245254721Semastestatic void get_params_to_use(void);
246254721Semastestatic char *get_rootdisk(void);
247254721Semastestatic void dos(struct dos_partition *partp);
248254721Semastestatic int open_disk(int flag);
249254721Semastestatic ssize_t read_disk(off_t sector, void *buf);
250254721Semastestatic int write_disk(off_t sector, void *buf);
251254721Semastestatic int get_params(void);
252254721Semastestatic int read_s0(void);
253254721Semastestatic int write_s0(void);
254254721Semastestatic int ok(const char *str);
255254721Semastestatic int decimal(const char *str, int *num, int deflt, uint32_t maxval);
256254721Semastestatic int read_config(char *config_file);
257254721Semastestatic void reset_boot(void);
258254721Semastestatic int sanitize_partition(struct dos_partition *);
259254721Semastestatic void usage(void);
260254721Semaste
261254721Semasteint
262254721Semastemain(int argc, char *argv[])
263254721Semaste{
264254721Semaste	int	c, i;
265254721Semaste	int	partition = -1;
266254721Semaste	struct	dos_partition *partp;
267254721Semaste
268254721Semaste	while ((c = getopt(argc, argv, "BIab:f:ipqstuv1234")) != -1)
269254721Semaste		switch (c) {
270254721Semaste		case 'B':
271254721Semaste			B_flag = 1;
272254721Semaste			break;
273254721Semaste		case 'I':
274254721Semaste			I_flag = 1;
275254721Semaste			break;
276254721Semaste		case 'a':
277254721Semaste			a_flag = 1;
278254721Semaste			break;
279254721Semaste		case 'b':
280254721Semaste			b_flag = optarg;
281254721Semaste			break;
282254721Semaste		case 'f':
283254721Semaste			f_flag = optarg;
284254721Semaste			break;
285254721Semaste		case 'i':
286254721Semaste			i_flag = 1;
287254721Semaste			break;
288254721Semaste		case 'p':
289254721Semaste			print_config_flag = 1;
290254721Semaste			break;
291254721Semaste		case 'q':
292254721Semaste			q_flag = 1;
293254721Semaste			break;
294254721Semaste		case 's':
295254721Semaste			s_flag = 1;
296254721Semaste			break;
297254721Semaste		case 't':
298254721Semaste			t_flag = 1;
299254721Semaste			break;
300254721Semaste		case 'u':
301254721Semaste			u_flag = 1;
302254721Semaste			break;
303254721Semaste		case 'v':
304254721Semaste			v_flag = 1;
305254721Semaste			break;
306254721Semaste		case '1':
307254721Semaste		case '2':
308254721Semaste		case '3':
309254721Semaste		case '4':
310254721Semaste			partition = c - '0';
311254721Semaste			break;
312254721Semaste		default:
313254721Semaste			usage();
314254721Semaste		}
315254721Semaste	if (f_flag || i_flag)
316254721Semaste		u_flag = 1;
317254721Semaste	if (t_flag)
318254721Semaste		v_flag = 1;
319254721Semaste	argc -= optind;
320254721Semaste	argv += optind;
321254721Semaste
322254721Semaste	if (argc == 0) {
323254721Semaste		disk = get_rootdisk();
324254721Semaste	} else {
325254721Semaste		disk = g_device_path(argv[0]);
326254721Semaste		if (disk == NULL)
327254721Semaste			err(1, "unable to get correct path for %s", argv[0]);
328254721Semaste	}
329254721Semaste	if (open_disk(u_flag) < 0)
330263363Semaste		err(1, "cannot open disk %s", disk);
331254721Semaste
332254721Semaste	/* (abu)use mboot.bootinst to probe for the sector size */
333254721Semaste	if ((mboot.bootinst = malloc(MAX_SEC_SIZE)) == NULL)
334254721Semaste		err(1, "cannot allocate buffer to determine disk sector size");
335254721Semaste	if (read_disk(0, mboot.bootinst) == -1)
336254721Semaste		errx(1, "could not detect sector size");
337254721Semaste	free(mboot.bootinst);
338254721Semaste	mboot.bootinst = NULL;
339254721Semaste
340254721Semaste	if (print_config_flag) {
341254721Semaste		if (read_s0())
342254721Semaste			err(1, "read_s0");
343254721Semaste
344254721Semaste		printf("# %s\n", disk);
345254721Semaste		printf("g c%d h%d s%d\n", dos_cyls, dos_heads, dos_sectors);
346254721Semaste
347254721Semaste		for (i = 0; i < NDOSPART; i++) {
348254721Semaste			partp = &mboot.parts[i];
349254721Semaste
350254721Semaste			if (partp->dp_start == 0 && partp->dp_size == 0)
351254721Semaste				continue;
352254721Semaste
353254721Semaste			printf("p %d 0x%02x %lu %lu\n", i + 1, partp->dp_typ,
354254721Semaste			    (u_long)partp->dp_start, (u_long)partp->dp_size);
355254721Semaste
356254721Semaste			/* Fill flags for the partition. */
357254721Semaste			if (partp->dp_flag & 0x80)
358254721Semaste				printf("a %d\n", i + 1);
359254721Semaste		}
360254721Semaste		exit(0);
361254721Semaste	}
362254721Semaste	if (s_flag) {
363254721Semaste		if (read_s0())
364254721Semaste			err(1, "read_s0");
365254721Semaste		printf("%s: %d cyl %d hd %d sec\n", disk, dos_cyls, dos_heads,
366254721Semaste		    dos_sectors);
367254721Semaste		printf("Part  %11s %11s Type Flags\n", "Start", "Size");
368254721Semaste		for (i = 0; i < NDOSPART; i++) {
369254721Semaste			partp = &mboot.parts[i];
370254721Semaste			if (partp->dp_start == 0 && partp->dp_size == 0)
371254721Semaste				continue;
372254721Semaste			printf("%4d: %11lu %11lu 0x%02x 0x%02x\n", i + 1,
373254721Semaste			    (u_long) partp->dp_start,
374254721Semaste			    (u_long) partp->dp_size, partp->dp_typ,
375254721Semaste			    partp->dp_flag);
376254721Semaste		}
377254721Semaste		exit(0);
378254721Semaste	}
379254721Semaste
380254721Semaste	printf("******* Working on device %s *******\n",disk);
381254721Semaste
382254721Semaste	if (I_flag) {
383269024Semaste		read_s0();
384254721Semaste		reset_boot();
385254721Semaste		partp = &mboot.parts[0];
386254721Semaste		partp->dp_typ = DOSPTYP_386BSD;
387254721Semaste		partp->dp_flag = ACTIVE;
388254721Semaste		partp->dp_start = dos_sectors;
389254721Semaste		partp->dp_size = (disksecs / dos_cylsecs) * dos_cylsecs -
390254721Semaste		    dos_sectors;
391254721Semaste		dos(partp);
392254721Semaste		if (v_flag)
393254721Semaste			print_s0();
394254721Semaste		if (!t_flag)
395254721Semaste			write_s0();
396254721Semaste		exit(0);
397254721Semaste	}
398254721Semaste	if (f_flag) {
399254721Semaste	    if (read_s0() || i_flag)
400254721Semaste		reset_boot();
401254721Semaste	    if (!read_config(f_flag))
402254721Semaste		exit(1);
403254721Semaste	    if (v_flag)
404254721Semaste		print_s0();
405254721Semaste	    if (!t_flag)
406254721Semaste		write_s0();
407254721Semaste	} else {
408254721Semaste	    if(u_flag)
409254721Semaste		get_params_to_use();
410254721Semaste	    else
411254721Semaste		print_params();
412254721Semaste
413254721Semaste	    if (read_s0())
414254721Semaste		init_sector0(dos_sectors);
415254721Semaste
416254721Semaste	    printf("Media sector size is %d\n", secsize);
417254721Semaste	    printf("Warning: BIOS sector numbering starts with sector 1\n");
418254721Semaste	    printf("Information from DOS bootblock is:\n");
419254721Semaste	    if (partition == -1)
420254721Semaste		for (i = 1; i <= NDOSPART; i++)
421254721Semaste		    change_part(i);
422254721Semaste	    else
423254721Semaste		change_part(partition);
424254721Semaste
425254721Semaste	    if (u_flag || a_flag)
426254721Semaste		change_active(partition);
427254721Semaste
428254721Semaste	    if (B_flag)
429254721Semaste		change_code();
430254721Semaste
431254721Semaste	    if (u_flag || a_flag || B_flag) {
432254721Semaste		if (!t_flag) {
433254721Semaste		    printf("\nWe haven't changed the partition table yet.  ");
434254721Semaste		    printf("This is your last chance.\n");
435254721Semaste		}
436254721Semaste		print_s0();
437254721Semaste		if (!t_flag) {
438254721Semaste		    if (ok("Should we write new partition table?"))
439254721Semaste			write_s0();
440254721Semaste		} else {
441254721Semaste		    printf("\n-t flag specified -- partition table not written.\n");
442254721Semaste		}
443254721Semaste	    }
444254721Semaste	}
445254721Semaste
446254721Semaste	exit(0);
447254721Semaste}
448254721Semaste
449254721Semastestatic void
450254721Semasteusage()
451254721Semaste{
452254721Semaste	fprintf(stderr, "%s%s",
453254721Semaste		"usage: fdisk [-BIaipqstu] [-b bootcode] [-1234] [disk]\n",
454254721Semaste 		"       fdisk -f configfile [-itv] [disk]\n");
455254721Semaste        exit(1);
456254721Semaste}
457254721Semaste
458254721Semastestatic void
459254721Semasteprint_s0(void)
460254721Semaste{
461254721Semaste	int	i;
462254721Semaste
463254721Semaste	print_params();
464254721Semaste	printf("Information from DOS bootblock is:\n");
465254721Semaste	for (i = 1; i <= NDOSPART; i++) {
466254721Semaste		printf("%d: ", i);
467254721Semaste		print_part(&mboot.parts[i - 1]);
468254721Semaste	}
469254721Semaste}
470254721Semaste
471254721Semastestatic struct dos_partition mtpart;
472254721Semaste
473254721Semastestatic void
474254721Semasteprint_part(const struct dos_partition *partp)
475254721Semaste{
476254721Semaste	u_int64_t part_mb;
477254721Semaste
478254721Semaste	if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) {
479254721Semaste		printf("<UNUSED>\n");
480254721Semaste		return;
481254721Semaste	}
482254721Semaste	/*
483254721Semaste	 * Be careful not to overflow.
484254721Semaste	 */
485254721Semaste	part_mb = partp->dp_size;
486254721Semaste	part_mb *= secsize;
487254721Semaste	part_mb /= (1024 * 1024);
488254721Semaste	printf("sysid %d (%#04x),(%s)\n", partp->dp_typ, partp->dp_typ,
489254721Semaste	    get_type(partp->dp_typ));
490254721Semaste	printf("    start %lu, size %lu (%ju Meg), flag %x%s\n",
491254721Semaste		(u_long)partp->dp_start,
492254721Semaste		(u_long)partp->dp_size,
493254721Semaste		(uintmax_t)part_mb,
494254721Semaste		partp->dp_flag,
495254721Semaste		partp->dp_flag == ACTIVE ? " (active)" : "");
496254721Semaste	printf("\tbeg: cyl %d/ head %d/ sector %d;\n\tend: cyl %d/ head %d/ sector %d\n"
497254721Semaste		,DPCYL(partp->dp_scyl, partp->dp_ssect)
498254721Semaste		,partp->dp_shd
499254721Semaste		,DPSECT(partp->dp_ssect)
500254721Semaste		,DPCYL(partp->dp_ecyl, partp->dp_esect)
501254721Semaste		,partp->dp_ehd
502254721Semaste		,DPSECT(partp->dp_esect));
503254721Semaste}
504254721Semaste
505254721Semaste
506254721Semastestatic void
507254721Semasteinit_boot(void)
508254721Semaste{
509254721Semaste#ifndef __ia64__
510254721Semaste	const char *fname;
511254721Semaste	int fdesc, n;
512254721Semaste	struct stat sb;
513254721Semaste
514254721Semaste	fname = b_flag ? b_flag : "/boot/mbr";
515254721Semaste	if ((fdesc = open(fname, O_RDONLY)) == -1 ||
516254721Semaste	    fstat(fdesc, &sb) == -1)
517254721Semaste		err(1, "%s", fname);
518254721Semaste	if (sb.st_size == 0)
519254721Semaste		errx(1, "%s is empty, must not be.", fname);
520254721Semaste	if ((mboot.bootinst_size = sb.st_size) % secsize != 0)
521254721Semaste		errx(1, "%s: length must be a multiple of sector size", fname);
522254721Semaste	if (mboot.bootinst != NULL)
523254721Semaste		free(mboot.bootinst);
524254721Semaste	if ((mboot.bootinst = malloc(mboot.bootinst_size = sb.st_size)) == NULL)
525254721Semaste		errx(1, "%s: unable to allocate read buffer", fname);
526254721Semaste	if ((n = read(fdesc, mboot.bootinst, mboot.bootinst_size)) == -1 ||
527254721Semaste	    close(fdesc))
528254721Semaste		err(1, "%s", fname);
529254721Semaste	if (n != mboot.bootinst_size)
530254721Semaste		errx(1, "%s: short read", fname);
531254721Semaste#else
532254721Semaste	if (mboot.bootinst != NULL)
533254721Semaste		free(mboot.bootinst);
534254721Semaste	mboot.bootinst_size = secsize;
535254721Semaste	if ((mboot.bootinst = malloc(mboot.bootinst_size)) == NULL)
536254721Semaste		errx(1, "unable to allocate boot block buffer");
537254721Semaste	memset(mboot.bootinst, 0, mboot.bootinst_size);
538254721Semaste	le16enc(&mboot.bootinst[DOSMAGICOFFSET], DOSMAGIC);
539254721Semaste#endif
540254721Semaste}
541254721Semaste
542254721Semaste
543254721Semastestatic void
544254721Semasteinit_sector0(unsigned long start)
545254721Semaste{
546254721Semaste	struct dos_partition *partp = &mboot.parts[0];
547254721Semaste
548254721Semaste	init_boot();
549254721Semaste
550254721Semaste	partp->dp_typ = DOSPTYP_386BSD;
551254721Semaste	partp->dp_flag = ACTIVE;
552254721Semaste	start = ((start + dos_sectors - 1) / dos_sectors) * dos_sectors;
553254721Semaste	if(start == 0)
554254721Semaste		start = dos_sectors;
555254721Semaste	partp->dp_start = start;
556254721Semaste	partp->dp_size = (disksecs / dos_cylsecs) * dos_cylsecs - start;
557254721Semaste
558254721Semaste	dos(partp);
559254721Semaste}
560254721Semaste
561254721Semastestatic void
562254721Semastechange_part(int i)
563254721Semaste{
564254721Semaste    struct dos_partition *partp = &mboot.parts[i - 1];
565254721Semaste
566254721Semaste    printf("The data for partition %d is:\n", i);
567254721Semaste    print_part(partp);
568254721Semaste
569254721Semaste    if (u_flag && ok("Do you want to change it?")) {
570254721Semaste	int tmp;
571254721Semaste
572254721Semaste	if (i_flag) {
573254721Semaste	    bzero(partp, sizeof (*partp));
574254721Semaste	    if (i == 1) {
575254721Semaste		init_sector0(1);
576254721Semaste		printf("\nThe static data for the slice 1 has been reinitialized to:\n");
577254721Semaste		print_part(partp);
578254721Semaste	    }
579254721Semaste	}
580254721Semaste
581254721Semaste	do {
582254721Semaste		Decimal("sysid (165=FreeBSD)", partp->dp_typ, tmp, 255);
583254721Semaste		Decimal("start", partp->dp_start, tmp, NO_DISK_SECTORS);
584254721Semaste		Decimal("size", partp->dp_size, tmp, NO_DISK_SECTORS);
585254721Semaste		if (!sanitize_partition(partp)) {
586254721Semaste			warnx("ERROR: failed to adjust; setting sysid to 0");
587254721Semaste			partp->dp_typ = 0;
588254721Semaste		}
589254721Semaste
590254721Semaste		if (ok("Explicitly specify beg/end address ?"))
591254721Semaste		{
592254721Semaste			int	tsec,tcyl,thd;
593254721Semaste			tcyl = DPCYL(partp->dp_scyl,partp->dp_ssect);
594254721Semaste			thd = partp->dp_shd;
595254721Semaste			tsec = DPSECT(partp->dp_ssect);
596254721Semaste			Decimal("beginning cylinder", tcyl, tmp, NO_TRACK_CYLINDERS);
597254721Semaste			Decimal("beginning head", thd, tmp, NO_TRACK_HEADS);
598254721Semaste			Decimal("beginning sector", tsec, tmp, NO_TRACK_SECTORS);
599254721Semaste			partp->dp_scyl = DOSCYL(tcyl);
600263363Semaste			partp->dp_ssect = DOSSECT(tsec,tcyl);
601254721Semaste			partp->dp_shd = thd;
602254721Semaste
603254721Semaste			tcyl = DPCYL(partp->dp_ecyl,partp->dp_esect);
604254721Semaste			thd = partp->dp_ehd;
605254721Semaste			tsec = DPSECT(partp->dp_esect);
606254721Semaste			Decimal("ending cylinder", tcyl, tmp, NO_TRACK_CYLINDERS);
607254721Semaste			Decimal("ending head", thd, tmp, NO_TRACK_HEADS);
608254721Semaste			Decimal("ending sector", tsec, tmp, NO_TRACK_SECTORS);
609254721Semaste			partp->dp_ecyl = DOSCYL(tcyl);
610254721Semaste			partp->dp_esect = DOSSECT(tsec,tcyl);
611254721Semaste			partp->dp_ehd = thd;
612254721Semaste		} else
613254721Semaste			dos(partp);
614254721Semaste
615254721Semaste		print_part(partp);
616254721Semaste	} while (!ok("Are we happy with this entry?"));
617254721Semaste    }
618254721Semaste}
619254721Semaste
620254721Semastestatic void
621254721Semasteprint_params()
622254721Semaste{
623254721Semaste	printf("parameters extracted from in-core disklabel are:\n");
624254721Semaste	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
625254721Semaste			,cyls,heads,sectors,cylsecs);
626254721Semaste	if (dos_cyls > 1023 || dos_heads > 255 || dos_sectors > 63)
627254721Semaste		printf("Figures below won't work with BIOS for partitions not in cyl 1\n");
628254721Semaste	printf("parameters to be used for BIOS calculations are:\n");
629254721Semaste	printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
630254721Semaste		,dos_cyls,dos_heads,dos_sectors,dos_cylsecs);
631254721Semaste}
632254721Semaste
633263363Semastestatic void
634263363Semastechange_active(int which)
635263363Semaste{
636263363Semaste	struct dos_partition *partp = &mboot.parts[0];
637263363Semaste	int active, i, new, tmp;
638263363Semaste
639263363Semaste	active = -1;
640263363Semaste	for (i = 0; i < NDOSPART; i++) {
641263363Semaste		if ((partp[i].dp_flag & ACTIVE) == 0)
642263363Semaste			continue;
643263363Semaste		printf("Partition %d is marked active\n", i + 1);
644263363Semaste		if (active == -1)
645263363Semaste			active = i + 1;
646263363Semaste	}
647263363Semaste	if (a_flag && which != -1)
648263363Semaste		active = which;
649263363Semaste	else if (active == -1)
650263363Semaste		active = 1;
651263363Semaste
652263363Semaste	if (!ok("Do you want to change the active partition?"))
653254721Semaste		return;
654254721Semastesetactive:
655254721Semaste	do {
656254721Semaste		new = active;
657254721Semaste		Decimal("active partition", new, tmp, 0);
658254721Semaste		if (new < 1 || new > 4) {
659254721Semaste			printf("Active partition number must be in range 1-4."
660254721Semaste					"  Try again.\n");
661254721Semaste			goto setactive;
662254721Semaste		}
663254721Semaste		active = new;
664254721Semaste	} while (!ok("Are you happy with this choice"));
665254721Semaste	for (i = 0; i < NDOSPART; i++)
666254721Semaste		partp[i].dp_flag = 0;
667254721Semaste	if (active > 0 && active <= NDOSPART)
668254721Semaste		partp[active-1].dp_flag = ACTIVE;
669254721Semaste}
670254721Semaste
671254721Semastestatic void
672254721Semastechange_code()
673254721Semaste{
674254721Semaste	if (ok("Do you want to change the boot code?"))
675254721Semaste		init_boot();
676254721Semaste}
677254721Semaste
678254721Semastevoid
679254721Semasteget_params_to_use()
680254721Semaste{
681254721Semaste	int	tmp;
682254721Semaste	print_params();
683254721Semaste	if (ok("Do you want to change our idea of what BIOS thinks ?"))
684254721Semaste	{
685254721Semaste		do
686254721Semaste		{
687254721Semaste			Decimal("BIOS's idea of #cylinders", dos_cyls, tmp, 0);
688254721Semaste			Decimal("BIOS's idea of #heads", dos_heads, tmp, 0);
689254721Semaste			Decimal("BIOS's idea of #sectors", dos_sectors, tmp, 0);
690254721Semaste			dos_cylsecs = dos_heads * dos_sectors;
691254721Semaste			print_params();
692254721Semaste		}
693254721Semaste		while(!ok("Are you happy with this choice"));
694254721Semaste	}
695254721Semaste}
696254721Semaste
697254721Semaste
698254721Semaste/***********************************************\
699254721Semaste* Change real numbers into strange dos numbers	*
700254721Semaste\***********************************************/
701254721Semastestatic void
702254721Semastedos(struct dos_partition *partp)
703254721Semaste{
704254721Semaste	int cy, sec;
705254721Semaste	u_int32_t end;
706254721Semaste
707254721Semaste	if (partp->dp_typ == 0 && partp->dp_start == 0 && partp->dp_size == 0) {
708254721Semaste		memcpy(partp, &mtpart, sizeof(*partp));
709254721Semaste		return;
710254721Semaste	}
711254721Semaste
712254721Semaste	/* Start c/h/s. */
713254721Semaste	partp->dp_shd = partp->dp_start % dos_cylsecs / dos_sectors;
714254721Semaste	cy = partp->dp_start / dos_cylsecs;
715254721Semaste	sec = partp->dp_start % dos_sectors + 1;
716254721Semaste	partp->dp_scyl = DOSCYL(cy);
717254721Semaste	partp->dp_ssect = DOSSECT(sec, cy);
718254721Semaste
719254721Semaste	/* End c/h/s. */
720254721Semaste	end = partp->dp_start + partp->dp_size - 1;
721254721Semaste	partp->dp_ehd = end % dos_cylsecs / dos_sectors;
722254721Semaste	cy = end / dos_cylsecs;
723254721Semaste	sec = end % dos_sectors + 1;
724254721Semaste	partp->dp_ecyl = DOSCYL(cy);
725254721Semaste	partp->dp_esect = DOSSECT(sec, cy);
726254721Semaste}
727254721Semaste
728254721Semastestatic int
729254721Semasteopen_disk(int flag)
730254721Semaste{
731254721Semaste	int rwmode;
732254721Semaste
733254721Semaste	/* Write mode if one of these flags are set. */
734254721Semaste	rwmode = (a_flag || I_flag || B_flag || flag);
735254721Semaste	fd = g_open(disk, rwmode);
736254721Semaste	/* If the mode fails, try read-only if we didn't. */
737254721Semaste	if (fd == -1 && errno == EPERM && rwmode)
738254721Semaste		fd = g_open(disk, 0);
739254721Semaste	if (fd == -1 && errno == ENXIO)
740254721Semaste		return -2;
741254721Semaste	if (fd == -1) {
742254721Semaste		warnx("can't open device %s", disk);
743254721Semaste		return -1;
744254721Semaste	}
745254721Semaste	if (get_params() == -1) {
746254721Semaste		warnx("can't get disk parameters on %s", disk);
747254721Semaste		return -1;
748254721Semaste	}
749254721Semaste	return fd;
750254721Semaste}
751254721Semaste
752254721Semastestatic ssize_t
753254721Semasteread_disk(off_t sector, void *buf)
754254721Semaste{
755254721Semaste
756254721Semaste	lseek(fd, (sector * 512), 0);
757254721Semaste	if (secsize == 0)
758254721Semaste		for (secsize = MIN_SEC_SIZE; secsize <= MAX_SEC_SIZE;
759254721Semaste		     secsize *= 2) {
760254721Semaste			/* try the read */
761254721Semaste			int size = read(fd, buf, secsize);
762254721Semaste			if (size == secsize)
763254721Semaste				/* it worked so return */
764254721Semaste				return secsize;
765254721Semaste		}
766254721Semaste	else
767254721Semaste		return read(fd, buf, secsize);
768254721Semaste
769254721Semaste	/* we failed to read at any of the sizes */
770254721Semaste	return -1;
771254721Semaste}
772254721Semaste
773254721Semastestatic int
774254721Semastegeom_class_available(const char *name)
775254721Semaste{
776254721Semaste	struct gclass *class;
777254721Semaste	struct gmesh mesh;
778254721Semaste	int error;
779254721Semaste
780254721Semaste	error = geom_gettree(&mesh);
781254721Semaste	if (error != 0)
782254721Semaste		errc(1, error, "Cannot get GEOM tree");
783254721Semaste
784254721Semaste	LIST_FOREACH(class, &mesh.lg_class, lg_class) {
785254721Semaste		if (strcmp(class->lg_name, name) == 0) {
786254721Semaste			geom_deletetree(&mesh);
787254721Semaste			return (1);
788254721Semaste		}
789254721Semaste	}
790254721Semaste
791254721Semaste	geom_deletetree(&mesh);
792254721Semaste
793254721Semaste	return (0);
794254721Semaste}
795254721Semaste
796254721Semastestatic int
797254721Semastewrite_disk(off_t sector, void *buf)
798254721Semaste{
799254721Semaste	struct gctl_req *grq;
800254721Semaste	const char *errmsg;
801254721Semaste	char *pname;
802254721Semaste	int error;
803254721Semaste
804254721Semaste	/* Check that GEOM_MBR is available */
805254721Semaste	if (geom_class_available("MBR") != 0) {
806254721Semaste		grq = gctl_get_handle();
807254721Semaste		gctl_ro_param(grq, "verb", -1, "write MBR");
808254721Semaste		gctl_ro_param(grq, "class", -1, "MBR");
809254721Semaste		pname = g_providername(fd);
810254721Semaste		if (pname == NULL) {
811254721Semaste			warn("Error getting providername for %s", disk);
812254721Semaste			return (-1);
813254721Semaste		}
814254721Semaste		gctl_ro_param(grq, "geom", -1, pname);
815254721Semaste		gctl_ro_param(grq, "data", secsize, buf);
816254721Semaste		errmsg = gctl_issue(grq);
817254721Semaste		free(pname);
818254721Semaste		if (errmsg == NULL) {
819254721Semaste			gctl_free(grq);
820254721Semaste			return(0);
821254721Semaste		}
822254721Semaste		if (!q_flag)
823254721Semaste			warnx("GEOM_MBR: %s", errmsg);
824254721Semaste		gctl_free(grq);
825254721Semaste	} else {
826254721Semaste		/*
827254721Semaste		 * Try to write MBR directly. This may help when disk
828254721Semaste		 * is not in use.
829254721Semaste		 * XXX: hardcoded sectorsize
830254721Semaste		 */
831254721Semaste		error = pwrite(fd, buf, secsize, (sector * 512));
832254721Semaste		if (error == secsize)
833254721Semaste			return (0);
834254721Semaste	}
835254721Semaste
836254721Semaste	/*
837254721Semaste	 * GEOM_MBR is not available or failed to write MBR.
838254721Semaste	 * Now check that we have GEOM_PART and recommend to use gpart (8).
839254721Semaste	 */
840254721Semaste	if (geom_class_available("PART") != 0)
841254721Semaste		warnx("Failed to write MBR. Try to use gpart(8).");
842254721Semaste	else
843254721Semaste		warnx("Failed to write sector zero");
844254721Semaste	return(EINVAL);
845254721Semaste}
846254721Semaste
847254721Semastestatic int
848254721Semasteget_params()
849254721Semaste{
850254721Semaste	int error;
851254721Semaste	u_int u;
852254721Semaste	off_t o;
853254721Semaste
854254721Semaste	error = ioctl(fd, DIOCGFWSECTORS, &u);
855254721Semaste	if (error == 0)
856254721Semaste		sectors = dos_sectors = u;
857254721Semaste	else
858254721Semaste		sectors = dos_sectors = 63;
859254721Semaste
860254721Semaste	error = ioctl(fd, DIOCGFWHEADS, &u);
861254721Semaste	if (error == 0)
862254721Semaste		heads = dos_heads = u;
863254721Semaste	else
864254721Semaste		heads = dos_heads = 255;
865254721Semaste
866254721Semaste	dos_cylsecs = cylsecs = heads * sectors;
867254721Semaste	disksecs = cyls * heads * sectors;
868254721Semaste
869254721Semaste	u = g_sectorsize(fd);
870254721Semaste	if (u <= 0)
871254721Semaste		return (-1);
872254721Semaste
873254721Semaste	o = g_mediasize(fd);
874254721Semaste	if (o < 0)
875254721Semaste		return (-1);
876254721Semaste	disksecs = o / u;
877254721Semaste	cyls = dos_cyls = o / (u * dos_heads * dos_sectors);
878254721Semaste
879254721Semaste	return (disksecs);
880254721Semaste}
881254721Semaste
882254721Semastestatic int
883254721Semasteread_s0()
884254721Semaste{
885254721Semaste	int i;
886254721Semaste
887254721Semaste	mboot.bootinst_size = secsize;
888254721Semaste	if (mboot.bootinst != NULL)
889254721Semaste		free(mboot.bootinst);
890254721Semaste	if ((mboot.bootinst = malloc(mboot.bootinst_size)) == NULL) {
891254721Semaste		warnx("unable to allocate buffer to read fdisk "
892254721Semaste		      "partition table");
893254721Semaste		return -1;
894254721Semaste	}
895254721Semaste	if (read_disk(0, mboot.bootinst) == -1) {
896254721Semaste		warnx("can't read fdisk partition table");
897254721Semaste		return -1;
898254721Semaste	}
899254721Semaste	if (le16dec(&mboot.bootinst[DOSMAGICOFFSET]) != DOSMAGIC) {
900254721Semaste		warnx("invalid fdisk partition table found");
901254721Semaste		/* So should we initialize things */
902254721Semaste		return -1;
903254721Semaste	}
904254721Semaste	for (i = 0; i < NDOSPART; i++)
905254721Semaste		dos_partition_dec(
906254721Semaste		    &mboot.bootinst[DOSPARTOFF + i * DOSPARTSIZE],
907254721Semaste		    &mboot.parts[i]);
908254721Semaste	return 0;
909254721Semaste}
910254721Semaste
911254721Semastestatic int
912254721Semastewrite_s0()
913254721Semaste{
914254721Semaste	int	sector, i;
915254721Semaste
916254721Semaste	if (iotest) {
917254721Semaste		print_s0();
918254721Semaste		return 0;
919254721Semaste	}
920254721Semaste	for(i = 0; i < NDOSPART; i++)
921254721Semaste		dos_partition_enc(&mboot.bootinst[DOSPARTOFF + i * DOSPARTSIZE],
922254721Semaste		    &mboot.parts[i]);
923254721Semaste	le16enc(&mboot.bootinst[DOSMAGICOFFSET], DOSMAGIC);
924254721Semaste	for(sector = 0; sector < mboot.bootinst_size / secsize; sector++)
925254721Semaste		if (write_disk(sector,
926254721Semaste			       &mboot.bootinst[sector * secsize]) == -1) {
927254721Semaste			warn("can't write fdisk partition table");
928254721Semaste			return -1;
929254721Semaste		}
930254721Semaste	return(0);
931254721Semaste}
932254721Semaste
933254721Semaste
934254721Semastestatic int
935254721Semasteok(const char *str)
936254721Semaste{
937254721Semaste	printf("%s [n] ", str);
938254721Semaste	fflush(stdout);
939254721Semaste	if (fgets(lbuf, LBUF, stdin) == NULL)
940254721Semaste		exit(1);
941254721Semaste	lbuf[strlen(lbuf)-1] = 0;
942254721Semaste
943254721Semaste	if (*lbuf &&
944254721Semaste		(!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") ||
945254721Semaste		 !strcmp(lbuf, "y") || !strcmp(lbuf, "Y")))
946254721Semaste		return 1;
947254721Semaste	else
948254721Semaste		return 0;
949254721Semaste}
950254721Semaste
951254721Semastestatic int
952254721Semastedecimal(const char *str, int *num, int deflt, uint32_t maxval)
953254721Semaste{
954254721Semaste	long long acc;
955254721Semaste	int c;
956254721Semaste	char *cp;
957254721Semaste
958254721Semaste	while (1) {
959254721Semaste		acc = 0;
960254721Semaste		printf("Supply a decimal value for \"%s\" [%d] ", str, deflt);
961254721Semaste		fflush(stdout);
962254721Semaste		if (fgets(lbuf, LBUF, stdin) == NULL)
963254721Semaste			exit(1);
964254721Semaste		lbuf[strlen(lbuf)-1] = 0;
965254721Semaste
966254721Semaste		if (!*lbuf)
967254721Semaste			return 0;
968254721Semaste
969254721Semaste		cp = lbuf;
970254721Semaste		while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
971254721Semaste		if (!c)
972254721Semaste			return 0;
973254721Semaste		while ((c = *cp++)) {
974254721Semaste			if (c <= '9' && c >= '0') {
975254721Semaste				if (acc <= maxval || maxval == 0)
976254721Semaste					acc = acc * 10 + c - '0';
977254721Semaste			} else
978254721Semaste				break;
979254721Semaste		}
980254721Semaste		if (c == ' ' || c == '\t')
981254721Semaste			while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
982254721Semaste		if (!c) {
983254721Semaste			if (maxval > 0 && acc > maxval) {
984254721Semaste				acc = maxval;
985254721Semaste				printf("%s exceeds maximum value allowed for "
986254721Semaste				  "this field. The value has been reduced "
987254721Semaste				  "to %lld\n", lbuf, acc);
988254721Semaste			}
989254721Semaste			*num = acc;
990254721Semaste			return 1;
991254721Semaste		} else
992254721Semaste			printf("%s is an invalid decimal number.  Try again.\n",
993254721Semaste				lbuf);
994254721Semaste	}
995254721Semaste}
996254721Semaste
997254721Semaste
998254721Semastestatic void
999254721Semasteparse_config_line(char *line, CMD *command)
1000254721Semaste{
1001254721Semaste    char	*cp, *end;
1002254721Semaste
1003254721Semaste    cp = line;
1004254721Semaste    while (1) {
1005254721Semaste	memset(command, 0, sizeof(*command));
1006254721Semaste
1007254721Semaste	while (isspace(*cp)) ++cp;
1008254721Semaste	if (*cp == '\0' || *cp == '#')
1009254721Semaste	    break;
1010254721Semaste	command->cmd = *cp++;
1011254721Semaste
1012254721Semaste	/*
1013254721Semaste	 * Parse args
1014254721Semaste	 */
1015254721Semaste	    while (1) {
1016254721Semaste	    while (isspace(*cp)) ++cp;
1017254721Semaste	    if (*cp == '\0')
1018254721Semaste		break;		/* eol */
1019254721Semaste	    if (*cp == '#')
1020254721Semaste		break;		/* found comment */
1021254721Semaste	    if (isalpha(*cp))
1022254721Semaste		command->args[command->n_args].argtype = *cp++;
1023254721Semaste	    end = NULL;
1024254721Semaste	    command->args[command->n_args].arg_val = strtoul(cp, &end, 0);
1025254721Semaste 	    if (cp == end || (!isspace(*end) && *end != '\0')) {
1026254721Semaste 		char ch;
1027254721Semaste 		end = cp;
1028254721Semaste 		while (!isspace(*end) && *end != '\0') ++end;
1029254721Semaste 		ch = *end; *end = '\0';
1030254721Semaste 		command->args[command->n_args].arg_str = strdup(cp);
1031254721Semaste 		*end = ch;
1032254721Semaste 	    } else
1033254721Semaste 		command->args[command->n_args].arg_str = NULL;
1034254721Semaste	    cp = end;
1035254721Semaste	    command->n_args++;
1036254721Semaste	}
1037254721Semaste	break;
1038254721Semaste    }
1039254721Semaste}
1040254721Semaste
1041254721Semaste
1042254721Semastestatic int
1043254721Semasteprocess_geometry(CMD *command)
1044254721Semaste{
1045254721Semaste    int		status = 1, i;
1046254721Semaste
1047254721Semaste    while (1) {
1048254721Semaste	geom_processed = 1;
1049254721Semaste	    if (part_processed) {
1050254721Semaste	    warnx(
1051254721Semaste	"ERROR line %d: the geometry specification line must occur before\n\
1052254721Semaste    all partition specifications",
1053254721Semaste		    current_line_number);
1054254721Semaste	    status = 0;
1055254721Semaste	    break;
1056254721Semaste	}
1057254721Semaste	    if (command->n_args != 3) {
1058254721Semaste	    warnx("ERROR line %d: incorrect number of geometry args",
1059254721Semaste		    current_line_number);
1060254721Semaste	    status = 0;
1061254721Semaste	    break;
1062254721Semaste	}
1063254721Semaste	    dos_cyls = 0;
1064254721Semaste	    dos_heads = 0;
1065254721Semaste	    dos_sectors = 0;
1066254721Semaste	    for (i = 0; i < 3; ++i) {
1067254721Semaste		    switch (command->args[i].argtype) {
1068254721Semaste	    case 'c':
1069254721Semaste		dos_cyls = command->args[i].arg_val;
1070254721Semaste		break;
1071254721Semaste	    case 'h':
1072254721Semaste		dos_heads = command->args[i].arg_val;
1073254721Semaste		break;
1074254721Semaste	    case 's':
1075254721Semaste		dos_sectors = command->args[i].arg_val;
1076254721Semaste		break;
1077254721Semaste	    default:
1078254721Semaste		warnx(
1079254721Semaste		"ERROR line %d: unknown geometry arg type: '%c' (0x%02x)",
1080254721Semaste			current_line_number, command->args[i].argtype,
1081254721Semaste			command->args[i].argtype);
1082254721Semaste		status = 0;
1083254721Semaste		break;
1084254721Semaste	    }
1085254721Semaste	}
1086254721Semaste	if (status == 0)
1087254721Semaste	    break;
1088254721Semaste
1089254721Semaste	dos_cylsecs = dos_heads * dos_sectors;
1090254721Semaste
1091254721Semaste	/*
1092254721Semaste	 * Do sanity checks on parameter values
1093254721Semaste	 */
1094254721Semaste	    if (dos_cyls == 0) {
1095254721Semaste	    warnx("ERROR line %d: number of cylinders not specified",
1096254721Semaste		    current_line_number);
1097254721Semaste	    status = 0;
1098254721Semaste	}
1099254721Semaste	    if (dos_cyls > 1024) {
1100254721Semaste	    warnx(
1101254721Semaste	"WARNING line %d: number of cylinders (%d) may be out-of-range\n\
1102254721Semaste    (must be within 1-1024 for normal BIOS operation, unless the entire disk\n\
1103254721Semaste    is dedicated to FreeBSD)",
1104254721Semaste		    current_line_number, dos_cyls);
1105254721Semaste	}
1106254721Semaste
1107254721Semaste	    if (dos_heads == 0) {
1108254721Semaste	    warnx("ERROR line %d: number of heads not specified",
1109254721Semaste		    current_line_number);
1110254721Semaste	    status = 0;
1111254721Semaste	    } else if (dos_heads > 256) {
1112254721Semaste	    warnx("ERROR line %d: number of heads must be within (1-256)",
1113254721Semaste		    current_line_number);
1114254721Semaste	    status = 0;
1115254721Semaste	}
1116254721Semaste
1117254721Semaste	    if (dos_sectors == 0) {
1118254721Semaste	    warnx("ERROR line %d: number of sectors not specified",
1119254721Semaste		    current_line_number);
1120254721Semaste	    status = 0;
1121254721Semaste	    } else if (dos_sectors > 63) {
1122254721Semaste	    warnx("ERROR line %d: number of sectors must be within (1-63)",
1123254721Semaste		    current_line_number);
1124254721Semaste	    status = 0;
1125254721Semaste	}
1126254721Semaste
1127254721Semaste	break;
1128254721Semaste    }
1129254721Semaste    return (status);
1130254721Semaste}
1131254721Semaste
1132254721Semastestatic u_int32_t
1133254721Semastestr2sectors(const char *str)
1134254721Semaste{
1135254721Semaste	char *end;
1136254721Semaste	unsigned long val;
1137254721Semaste
1138254721Semaste	val = strtoul(str, &end, 0);
1139254721Semaste	if (str == end || *end == '\0') {
1140254721Semaste		warnx("ERROR line %d: unexpected size: \'%s\'",
1141254721Semaste		    current_line_number, str);
1142254721Semaste		return NO_DISK_SECTORS;
1143254721Semaste	}
1144254721Semaste
1145254721Semaste	if (*end == 'K')
1146254721Semaste		val *= 1024UL / secsize;
1147254721Semaste	else if (*end == 'M')
1148254721Semaste		val *= 1024UL * 1024UL / secsize;
1149254721Semaste	else if (*end == 'G')
1150254721Semaste		val *= 1024UL * 1024UL * 1024UL / secsize;
1151254721Semaste	else {
1152254721Semaste		warnx("ERROR line %d: unexpected modifier: %c "
1153254721Semaste		    "(not K/M/G)", current_line_number, *end);
1154254721Semaste		return NO_DISK_SECTORS;
1155254721Semaste	}
1156254721Semaste
1157254721Semaste	return val;
1158254721Semaste}
1159254721Semaste
1160254721Semastestatic int
1161254721Semasteprocess_partition(CMD *command)
1162254721Semaste{
1163254721Semaste    int				status = 0, partition;
1164254721Semaste    u_int32_t			prev_head_boundary, prev_cyl_boundary;
1165254721Semaste    u_int32_t			adj_size, max_end;
1166254721Semaste    struct dos_partition	*partp;
1167254721Semaste
1168254721Semaste	while (1) {
1169254721Semaste	part_processed = 1;
1170254721Semaste		if (command->n_args != 4) {
1171254721Semaste	    warnx("ERROR line %d: incorrect number of partition args",
1172254721Semaste		    current_line_number);
1173254721Semaste	    break;
1174254721Semaste	}
1175254721Semaste	partition = command->args[0].arg_val;
1176254721Semaste		if (partition < 1 || partition > 4) {
1177254721Semaste	    warnx("ERROR line %d: invalid partition number %d",
1178254721Semaste		    current_line_number, partition);
1179254721Semaste	    break;
1180254721Semaste	}
1181254721Semaste	partp = &mboot.parts[partition - 1];
1182254721Semaste	bzero(partp, sizeof (*partp));
1183254721Semaste	partp->dp_typ = command->args[1].arg_val;
1184254721Semaste	if (command->args[2].arg_str != NULL) {
1185254721Semaste		if (strcmp(command->args[2].arg_str, "*") == 0) {
1186254721Semaste			int i;
1187254721Semaste			partp->dp_start = dos_sectors;
1188254721Semaste			for (i = 1; i < partition; i++) {
1189254721Semaste    				struct dos_partition *prev_partp;
1190254721Semaste				prev_partp = ((struct dos_partition *)
1191254721Semaste				    &mboot.parts) + i - 1;
1192254721Semaste				if (prev_partp->dp_typ != 0)
1193254721Semaste					partp->dp_start = prev_partp->dp_start +
1194254721Semaste					    prev_partp->dp_size;
1195254721Semaste			}
1196254721Semaste			if (partp->dp_start % dos_sectors != 0) {
1197254721Semaste		    		prev_head_boundary = partp->dp_start /
1198254721Semaste				    dos_sectors * dos_sectors;
1199254721Semaste		    		partp->dp_start = prev_head_boundary +
1200254721Semaste				    dos_sectors;
1201254721Semaste			}
1202254721Semaste		} else {
1203254721Semaste			partp->dp_start = str2sectors(command->args[2].arg_str);
1204254721Semaste			if (partp->dp_start == NO_DISK_SECTORS)
1205254721Semaste				break;
1206254721Semaste		}
1207254721Semaste	} else
1208254721Semaste		partp->dp_start = command->args[2].arg_val;
1209254721Semaste
1210254721Semaste	if (command->args[3].arg_str != NULL) {
1211254721Semaste		if (strcmp(command->args[3].arg_str, "*") == 0)
1212254721Semaste			partp->dp_size = ((disksecs / dos_cylsecs) *
1213254721Semaste			    dos_cylsecs) - partp->dp_start;
1214254721Semaste		else {
1215254721Semaste			partp->dp_size = str2sectors(command->args[3].arg_str);
1216254721Semaste			if (partp->dp_size == NO_DISK_SECTORS)
1217254721Semaste				break;
1218254721Semaste		}
1219254721Semaste		prev_cyl_boundary = ((partp->dp_start + partp->dp_size) /
1220254721Semaste		    dos_cylsecs) * dos_cylsecs;
1221254721Semaste		if (prev_cyl_boundary > partp->dp_start)
1222254721Semaste			partp->dp_size = prev_cyl_boundary - partp->dp_start;
1223254721Semaste	} else
1224254721Semaste		partp->dp_size = command->args[3].arg_val;
1225254721Semaste
1226254721Semaste	max_end = partp->dp_start + partp->dp_size;
1227254721Semaste
1228254721Semaste	if (partp->dp_typ == 0) {
1229254721Semaste	    /*
1230254721Semaste	     * Get out, the partition is marked as unused.
1231254721Semaste	     */
1232254721Semaste	    /*
1233254721Semaste	     * Insure that it's unused.
1234254721Semaste	     */
1235254721Semaste	    bzero(partp, sizeof(*partp));
1236254721Semaste	    status = 1;
1237254721Semaste	    break;
1238254721Semaste	}
1239254721Semaste
1240254721Semaste	/*
1241254721Semaste	 * Adjust start upwards, if necessary, to fall on a head boundary.
1242254721Semaste	 */
1243254721Semaste		if (partp->dp_start % dos_sectors != 0) {
1244254721Semaste	    prev_head_boundary = partp->dp_start / dos_sectors * dos_sectors;
1245254721Semaste	    if (max_end < dos_sectors ||
1246254721Semaste			    prev_head_boundary > max_end - dos_sectors) {
1247254721Semaste		/*
1248254721Semaste		 * Can't go past end of partition
1249254721Semaste		 */
1250254721Semaste		warnx(
1251254721Semaste	"ERROR line %d: unable to adjust start of partition %d to fall on\n\
1252254721Semaste    a head boundary",
1253254721Semaste			current_line_number, partition);
1254254721Semaste		break;
1255254721Semaste	    }
1256254721Semaste	    warnx(
1257254721Semaste	"WARNING: adjusting start offset of partition %d\n\
1258254721Semaste    from %u to %u, to fall on a head boundary",
1259254721Semaste		    partition, (u_int)partp->dp_start,
1260254721Semaste		    (u_int)(prev_head_boundary + dos_sectors));
1261254721Semaste	    partp->dp_start = prev_head_boundary + dos_sectors;
1262254721Semaste	}
1263254721Semaste
1264254721Semaste	/*
1265254721Semaste	 * Adjust size downwards, if necessary, to fall on a cylinder
1266254721Semaste	 * boundary.
1267254721Semaste	 */
1268254721Semaste	prev_cyl_boundary =
1269254721Semaste	    ((partp->dp_start + partp->dp_size) / dos_cylsecs) * dos_cylsecs;
1270254721Semaste	if (prev_cyl_boundary > partp->dp_start)
1271254721Semaste	    adj_size = prev_cyl_boundary - partp->dp_start;
1272254721Semaste		else {
1273254721Semaste	    warnx(
1274254721Semaste	"ERROR: could not adjust partition to start on a head boundary\n\
1275254721Semaste    and end on a cylinder boundary.");
1276254721Semaste	    return (0);
1277254721Semaste	}
1278254721Semaste		if (adj_size != partp->dp_size) {
1279254721Semaste	    warnx(
1280254721Semaste	"WARNING: adjusting size of partition %d from %u to %u\n\
1281254721Semaste    to end on a cylinder boundary",
1282254721Semaste		    partition, (u_int)partp->dp_size, (u_int)adj_size);
1283254721Semaste	    partp->dp_size = adj_size;
1284254721Semaste	}
1285254721Semaste		if (partp->dp_size == 0) {
1286254721Semaste	    warnx("ERROR line %d: size of partition %d is zero",
1287254721Semaste		    current_line_number, partition);
1288254721Semaste	    break;
1289254721Semaste	}
1290254721Semaste
1291254721Semaste	dos(partp);
1292254721Semaste	status = 1;
1293254721Semaste	break;
1294254721Semaste    }
1295254721Semaste    return (status);
1296254721Semaste}
1297254721Semaste
1298254721Semaste
1299254721Semastestatic int
1300254721Semasteprocess_active(CMD *command)
1301254721Semaste{
1302254721Semaste    int				status = 0, partition, i;
1303254721Semaste    struct dos_partition	*partp;
1304254721Semaste
1305254721Semaste	while (1) {
1306254721Semaste	active_processed = 1;
1307254721Semaste		if (command->n_args != 1) {
1308254721Semaste	    warnx("ERROR line %d: incorrect number of active args",
1309254721Semaste		    current_line_number);
1310254721Semaste	    status = 0;
1311254721Semaste	    break;
1312254721Semaste	}
1313254721Semaste	partition = command->args[0].arg_val;
1314254721Semaste		if (partition < 1 || partition > 4) {
1315254721Semaste	    warnx("ERROR line %d: invalid partition number %d",
1316254721Semaste		    current_line_number, partition);
1317254721Semaste	    break;
1318254721Semaste	}
1319254721Semaste	/*
1320254721Semaste	 * Reset active partition
1321254721Semaste	 */
1322254721Semaste	partp = mboot.parts;
1323254721Semaste	for (i = 0; i < NDOSPART; i++)
1324254721Semaste	    partp[i].dp_flag = 0;
1325254721Semaste	partp[partition-1].dp_flag = ACTIVE;
1326254721Semaste
1327254721Semaste	status = 1;
1328254721Semaste	break;
1329254721Semaste    }
1330254721Semaste    return (status);
1331254721Semaste}
1332254721Semaste
1333254721Semaste
1334254721Semastestatic int
1335254721Semasteprocess_line(char *line)
1336254721Semaste{
1337254721Semaste    CMD		command;
1338254721Semaste    int		status = 1;
1339254721Semaste
1340254721Semaste	while (1) {
1341254721Semaste	parse_config_line(line, &command);
1342254721Semaste		switch (command.cmd) {
1343254721Semaste	case 0:
1344254721Semaste	    /*
1345254721Semaste	     * Comment or blank line
1346254721Semaste	     */
1347254721Semaste	    break;
1348254721Semaste	case 'g':
1349254721Semaste	    /*
1350254721Semaste	     * Set geometry
1351254721Semaste	     */
1352254721Semaste	    status = process_geometry(&command);
1353254721Semaste	    break;
1354254721Semaste	case 'p':
1355254721Semaste	    status = process_partition(&command);
1356254721Semaste	    break;
1357254721Semaste	case 'a':
1358254721Semaste	    status = process_active(&command);
1359263363Semaste	    break;
1360254721Semaste	default:
1361263363Semaste	    status = 0;
1362254721Semaste	    break;
1363254721Semaste	}
1364254721Semaste	break;
1365254721Semaste    }
1366254721Semaste    return (status);
1367254721Semaste}
1368254721Semaste
1369254721Semaste
1370254721Semastestatic int
1371254721Semasteread_config(char *config_file)
1372254721Semaste{
1373254721Semaste    FILE	*fp = NULL;
1374254721Semaste    int		status = 1;
1375254721Semaste    char	buf[1010];
1376254721Semaste
1377254721Semaste	while (1) {
1378254721Semaste		if (strcmp(config_file, "-") != 0) {
1379254721Semaste	    /*
1380254721Semaste	     * We're not reading from stdin
1381254721Semaste	     */
1382254721Semaste			if ((fp = fopen(config_file, "r")) == NULL) {
1383254721Semaste		status = 0;
1384254721Semaste		break;
1385254721Semaste	    }
1386254721Semaste		} else {
1387254721Semaste	    fp = stdin;
1388254721Semaste	}
1389254721Semaste	current_line_number = 0;
1390254721Semaste		while (!feof(fp)) {
1391254721Semaste	    if (fgets(buf, sizeof(buf), fp) == NULL)
1392254721Semaste		break;
1393254721Semaste	    ++current_line_number;
1394254721Semaste	    status = process_line(buf);
1395254721Semaste	    if (status == 0)
1396254721Semaste		break;
1397254721Semaste	    }
1398254721Semaste	break;
1399254721Semaste    }
1400254721Semaste	if (fp) {
1401254721Semaste	/*
1402254721Semaste	 * It doesn't matter if we're reading from stdin, as we've reached EOF
1403254721Semaste	 */
1404254721Semaste	fclose(fp);
1405254721Semaste    }
1406254721Semaste    return (status);
1407254721Semaste}
1408254721Semaste
1409254721Semaste
1410254721Semastestatic void
1411254721Semastereset_boot(void)
1412254721Semaste{
1413254721Semaste    int				i;
1414254721Semaste    struct dos_partition	*partp;
1415254721Semaste
1416254721Semaste    init_boot();
1417254721Semaste    for (i = 0; i < 4; ++i) {
1418254721Semaste	partp = &mboot.parts[i];
1419254721Semaste	bzero(partp, sizeof(*partp));
1420254721Semaste    }
1421254721Semaste}
1422254721Semaste
1423254721Semastestatic int
1424254721Semastesanitize_partition(struct dos_partition *partp)
1425254721Semaste{
1426254721Semaste    u_int32_t			prev_head_boundary, prev_cyl_boundary;
1427254721Semaste    u_int32_t			max_end, size, start;
1428254721Semaste
1429254721Semaste    start = partp->dp_start;
1430254721Semaste    size = partp->dp_size;
1431254721Semaste    max_end = start + size;
1432254721Semaste    /* Only allow a zero size if the partition is being marked unused. */
1433254721Semaste    if (size == 0) {
1434254721Semaste	if (start == 0 && partp->dp_typ == 0)
1435254721Semaste	    return (1);
1436254721Semaste	warnx("ERROR: size of partition is zero");
1437254721Semaste	return (0);
1438254721Semaste    }
1439254721Semaste    /* Return if no adjustment is necessary. */
1440254721Semaste    if (start % dos_sectors == 0 && (start + size) % dos_sectors == 0)
1441254721Semaste	return (1);
1442263367Semaste
1443263367Semaste    if (start == 0) {
1444263367Semaste	    warnx("WARNING: partition overlaps with partition table");
1445263367Semaste	    if (ok("Correct this automatically?"))
1446263367Semaste		    start = dos_sectors;
1447263367Semaste    }
1448254721Semaste    if (start % dos_sectors != 0)
1449254721Semaste	warnx("WARNING: partition does not start on a head boundary");
1450254721Semaste    if ((start  +size) % dos_sectors != 0)
1451254721Semaste	warnx("WARNING: partition does not end on a cylinder boundary");
1452254721Semaste    warnx("WARNING: this may confuse the BIOS or some operating systems");
1453254721Semaste    if (!ok("Correct this automatically?"))
1454254721Semaste	return (1);
1455254721Semaste
1456254721Semaste    /*
1457254721Semaste     * Adjust start upwards, if necessary, to fall on a head boundary.
1458254721Semaste     */
1459254721Semaste    if (start % dos_sectors != 0) {
1460254721Semaste	prev_head_boundary = start / dos_sectors * dos_sectors;
1461254721Semaste	if (max_end < dos_sectors ||
1462254721Semaste	    prev_head_boundary >= max_end - dos_sectors) {
1463254721Semaste	    /*
1464254721Semaste	     * Can't go past end of partition
1465254721Semaste	     */
1466254721Semaste	    warnx(
1467254721Semaste    "ERROR: unable to adjust start of partition to fall on a head boundary");
1468254721Semaste	    return (0);
1469254721Semaste        }
1470254721Semaste	start = prev_head_boundary + dos_sectors;
1471254721Semaste    }
1472254721Semaste
1473254721Semaste    /*
1474254721Semaste     * Adjust size downwards, if necessary, to fall on a cylinder
1475254721Semaste     * boundary.
1476254721Semaste     */
1477254721Semaste    prev_cyl_boundary = ((start + size) / dos_cylsecs) * dos_cylsecs;
1478254721Semaste    if (prev_cyl_boundary > start)
1479254721Semaste	size = prev_cyl_boundary - start;
1480254721Semaste    else {
1481254721Semaste	warnx("ERROR: could not adjust partition to start on a head boundary\n\
1482254721Semaste    and end on a cylinder boundary.");
1483254721Semaste	return (0);
1484254721Semaste    }
1485254721Semaste
1486254721Semaste    /* Finally, commit any changes to partp and return. */
1487254721Semaste    if (start != partp->dp_start) {
1488254721Semaste	warnx("WARNING: adjusting start offset of partition to %u",
1489254721Semaste	    (u_int)start);
1490254721Semaste	partp->dp_start = start;
1491254721Semaste    }
1492254721Semaste    if (size != partp->dp_size) {
1493254721Semaste	warnx("WARNING: adjusting size of partition to %u", (u_int)size);
1494254721Semaste	partp->dp_size = size;
1495254721Semaste    }
1496254721Semaste
1497254721Semaste    return (1);
1498254721Semaste}
1499254721Semaste
1500254721Semaste/*
1501254721Semaste * Try figuring out the root device's canonical disk name.
1502254721Semaste * The following choices are considered:
1503254721Semaste *   /dev/ad0s1a     => /dev/ad0
1504254721Semaste *   /dev/da0a       => /dev/da0
1505254721Semaste *   /dev/vinum/root => /dev/vinum/root
1506263367Semaste * A ".eli" part is removed if it exists (see geli(8)).
1507263367Semaste * A ".journal" ending is removed if it exists (see gjournal(8)).
1508263367Semaste */
1509254721Semastestatic char *
1510254721Semasteget_rootdisk(void)
1511254721Semaste{
1512254721Semaste	struct statfs rootfs;
1513254721Semaste	regex_t re;
1514254721Semaste#define NMATCHES 2
1515254721Semaste	regmatch_t rm[NMATCHES];
1516254721Semaste	char dev[PATH_MAX], *s;
1517254721Semaste	int rv;
1518254721Semaste
1519254721Semaste	if (statfs("/", &rootfs) == -1)
1520254721Semaste		err(1, "statfs(\"/\")");
1521254721Semaste
1522254721Semaste	if ((rv = regcomp(&re, "^(/dev/[a-z/]+[0-9]*)([sp][0-9]+)?[a-h]?(\\.journal)?$",
1523254721Semaste		    REG_EXTENDED)) != 0)
1524254721Semaste		errx(1, "regcomp() failed (%d)", rv);
1525254721Semaste	strlcpy(dev, rootfs.f_mntfromname, sizeof (dev));
1526254721Semaste	if ((s = strstr(dev, ".eli")) != NULL)
1527254721Semaste	    memmove(s, s+4, strlen(s + 4) + 1);
1528254721Semaste
1529254721Semaste	if ((rv = regexec(&re, dev, NMATCHES, rm, 0)) != 0)
1530254721Semaste		errx(1,
1531254721Semaste"mounted root fs resource doesn't match expectations (regexec returned %d)",
1532254721Semaste		    rv);
1533254721Semaste	if ((s = malloc(rm[1].rm_eo - rm[1].rm_so + 1)) == NULL)
1534254721Semaste		errx(1, "out of memory");
1535254721Semaste	memcpy(s, rootfs.f_mntfromname + rm[1].rm_so,
1536254721Semaste	    rm[1].rm_eo - rm[1].rm_so);
1537254721Semaste	s[rm[1].rm_eo - rm[1].rm_so] = 0;
1538254721Semaste
1539254721Semaste	return s;
1540254721Semaste}
1541254721Semaste