disks.c revision 106744
1/*
2 * The new sysinstall program.
3 *
4 * This is probably the last program in the `sysinstall' line - the next
5 * generation being essentially a complete rewrite.
6 *
7 * $FreeBSD: head/usr.sbin/sade/disks.c 106744 2002-11-10 20:56:53Z marcel $
8 *
9 * Copyright (c) 1995
10 *	Jordan Hubbard.  All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer,
17 *    verbatim and that no modifications are made prior to this
18 *    point in the file.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in the
21 *    documentation and/or other materials provided with the distribution.
22 *
23 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 */
36
37#include "sysinstall.h"
38#include <ctype.h>
39#include <fcntl.h>
40#include <sys/stat.h>
41#include <sys/disklabel.h>
42
43enum size_units_t { UNIT_BLOCKS, UNIT_KILO, UNIT_MEG, UNIT_SIZE };
44
45#ifdef PC98
46#define	SUBTYPE_FREEBSD		50324
47#define	SUBTYPE_FAT		37218
48#else
49#define	SUBTYPE_FREEBSD		165
50#define	SUBTYPE_FAT		6
51#define	SUBTYPE_EFI		239
52#endif
53
54/* Where we start displaying chunk information on the screen */
55#define CHUNK_START_ROW		5
56
57/* Where we keep track of MBR chunks */
58static struct chunk *chunk_info[16];
59static int current_chunk;
60
61static void	diskPartitionNonInteractive(Device *dev);
62static u_char *	bootalloc(char *name, size_t *size);
63
64static void
65record_chunks(Disk *d)
66{
67    struct chunk *c1 = NULL;
68    int i = 0;
69    int last_free = 0;
70
71    if (!d->chunks)
72	msgFatal("No chunk list found for %s!", d->name);
73
74    for (c1 = d->chunks->part; c1; c1 = c1->next) {
75	if (c1->type == unused && c1->size > last_free) {
76	    last_free = c1->size;
77	    current_chunk = i;
78	}
79	chunk_info[i++] = c1;
80    }
81    chunk_info[i] = NULL;
82    if (current_chunk >= i)
83	current_chunk = i - 1;
84}
85
86static int Total;
87
88static void
89print_chunks(Disk *d, int u)
90{
91    int row;
92    int i;
93    int sz;
94    char *szstr;
95
96    szstr = (u == UNIT_MEG ? "MB" : (u == UNIT_KILO ? "KB" : "ST"));
97
98    for (i = Total = 0; chunk_info[i]; i++)
99	Total += chunk_info[i]->size;
100#ifdef PC98
101    if (d->bios_cyl >= 65536 || d->bios_hd > 16 || d->bios_sect >= 256) {
102#else
103    if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64) {
104#endif
105	dialog_clear_norefresh();
106	msgConfirm("WARNING:  A geometry of %lu/%lu/%lu for %s is incorrect.  Using\n"
107		   "a more likely geometry.  If this geometry is incorrect or you\n"
108		   "are unsure as to whether or not it's correct, please consult\n"
109		   "the Hardware Guide in the Documentation submenu or use the\n"
110		   "(G)eometry command to change it now.\n\n"
111		   "Remember: you need to enter whatever your BIOS thinks the\n"
112		   "geometry is!  For IDE, it's what you were told in the BIOS\n"
113		   "setup. For SCSI, it's the translation mode your controller is\n"
114		   "using.  Do NOT use a ``physical geometry''.",
115	  d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
116	Sanitize_Bios_Geom(d);
117    }
118    attrset(A_NORMAL);
119    mvaddstr(0, 0, "Disk name:\t");
120    clrtobot();
121    attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL);
122    attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL);
123    mvprintw(1, 0,
124	     "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors = %lu sectors (%luMB)",
125	     d->bios_cyl, d->bios_hd, d->bios_sect,
126	     d->bios_cyl * d->bios_hd * d->bios_sect,
127	     d->bios_cyl * d->bios_hd * d->bios_sect / (1024/512) / 1024);
128    mvprintw(3, 0, "%6s %10s(%s) %10s %8s %6s %10s %8s %8s",
129	     "Offset", "Size", szstr, "End", "Name", "PType", "Desc",
130	     "Subtype", "Flags");
131    for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
132	switch(u) {
133	default:	/* fall thru */
134	case UNIT_BLOCKS:
135	    sz = chunk_info[i]->size;
136	    break;
137	case UNIT_KILO:
138	    sz = chunk_info[i]->size / (1024/512);
139	    break;
140	case UNIT_MEG:
141	    sz = chunk_info[i]->size / (1024/512) / 1024;
142	    break;
143	}
144	if (i == current_chunk)
145	    attrset(ATTR_SELECTED);
146	mvprintw(row, 0, "%10ld %10lu %10lu %8s %6d %10s %8d\t%-6s",
147		 chunk_info[i]->offset, sz,
148		 chunk_info[i]->end, chunk_info[i]->name,
149		 chunk_info[i]->type,
150		 slice_type_name(chunk_info[i]->type, chunk_info[i]->subtype),
151		 chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
152	if (i == current_chunk)
153	    attrset(A_NORMAL);
154    }
155}
156
157static void
158print_command_summary()
159{
160    mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
161    mvprintw(16, 0, "A = Use Entire Disk   G = set Drive Geometry   C = Create Slice   F = `DD' mode");
162    mvprintw(17, 0, "D = Delete Slice      Z = Toggle Size Units    S = Set Bootable   | = Wizard m.");
163    mvprintw(18, 0, "T = Change Type       U = Undo All Changes     Q = Finish");
164    if (!RunningAsInit)
165	mvprintw(18, 47, "W = Write Changes");
166    mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select.");
167    move(0, 0);
168}
169
170#ifdef PC98
171static void
172getBootMgr(char *dname, u_char **bootipl, size_t *bootipl_size,
173	   u_char **bootmenu, size_t *bootmenu_size)
174{
175    static u_char *boot0;
176    static size_t boot0_size;
177    static u_char *boot05;
178    static size_t boot05_size;
179
180    char str[80];
181    char *cp;
182    int i = 0;
183
184    cp = variable_get(VAR_BOOTMGR);
185    if (!cp) {
186	/* Figure out what kind of IPL the user wants */
187	sprintf(str, "Install Boot Manager for drive %s?", dname);
188	MenuIPLType.title = str;
189	i = dmenuOpenSimple(&MenuIPLType, FALSE);
190    } else {
191	if (!strncmp(cp, "boot", 4))
192	    BootMgr = 0;
193	else
194	    BootMgr = 1;
195    }
196    if (cp || i) {
197	switch (BootMgr) {
198	case 0:
199	    if (!boot0) boot0 = bootalloc("boot0", &boot0_size);
200	    *bootipl = boot0;
201	    *bootipl_size = boot0_size;
202	    if (!boot05) boot05 = bootalloc("boot0.5", &boot05_size);
203	    *bootmenu = boot05;
204	    *bootmenu_size = boot05_size;
205	    return;
206	case 1:
207	default:
208	    break;
209	}
210    }
211    *bootipl = NULL;
212    *bootipl_size = 0;
213    *bootmenu = NULL;
214    *bootmenu_size = 0;
215}
216#else
217static void
218getBootMgr(char *dname, u_char **bootCode, size_t *bootCodeSize)
219{
220#ifdef __i386__	/* only meaningful on x86 */
221    static u_char *mbr, *boot0;
222    static size_t mbr_size, boot0_size;
223    char str[80];
224    char *cp;
225    int i = 0;
226
227    cp = variable_get(VAR_BOOTMGR);
228    if (!cp) {
229	/* Figure out what kind of MBR the user wants */
230	sprintf(str, "Install Boot Manager for drive %s?", dname);
231	MenuMBRType.title = str;
232	i = dmenuOpenSimple(&MenuMBRType, FALSE);
233    }
234    else {
235	if (!strncmp(cp, "boot", 4))
236	    BootMgr = 0;
237	else if (!strcmp(cp, "standard"))
238	    BootMgr = 1;
239	else
240	    BootMgr = 2;
241    }
242    if (cp || i) {
243	switch (BootMgr) {
244	case 0:
245	    if (!boot0) boot0 = bootalloc("boot0", &boot0_size);
246	    *bootCode = boot0;
247	    *bootCodeSize = boot0_size;
248	    return;
249	case 1:
250	    if (!mbr) mbr = bootalloc("mbr", &mbr_size);
251	    *bootCode = mbr;
252	    *bootCodeSize = mbr_size;
253	    return;
254	case 2:
255	default:
256	    break;
257	}
258    }
259#endif
260    *bootCode = NULL;
261    *bootCodeSize = 0;
262}
263#endif
264
265int
266diskGetSelectCount(Device ***devs)
267{
268    int i, cnt, enabled;
269    char *cp;
270    Device **dp;
271
272    cp = variable_get(VAR_DISK);
273    dp = *devs = deviceFind(cp, DEVICE_TYPE_DISK);
274    cnt = deviceCount(dp);
275    if (!cnt)
276	return -1;
277    for (i = 0, enabled = 0; i < cnt; i++) {
278	if (dp[i]->enabled)
279	    ++enabled;
280    }
281    return enabled;
282}
283
284void
285diskPartition(Device *dev)
286{
287    char *cp, *p;
288    int rv, key = 0;
289    Boolean chunking;
290    char *msg = NULL;
291#ifdef PC98
292    u_char *bootipl;
293    size_t bootipl_size;
294    u_char *bootmenu;
295    size_t bootmenu_size;
296#else
297    u_char *mbrContents;
298    size_t mbrSize;
299#endif
300    WINDOW *w = savescr();
301    Disk *d = (Disk *)dev->private;
302    int size_unit;
303
304    size_unit = UNIT_BLOCKS;
305    chunking = TRUE;
306    keypad(stdscr, TRUE);
307
308    /* Flush both the dialog and curses library views of the screen
309       since we don't always know who called us */
310    dialog_clear_norefresh(), clear();
311    current_chunk = 0;
312
313    /* Set up the chunk array */
314    record_chunks(d);
315
316    while (chunking) {
317	char *val, geometry[80];
318
319	/* Now print our overall state */
320	if (d)
321	    print_chunks(d, size_unit);
322	print_command_summary();
323	if (msg) {
324	    attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
325	    beep();
326	    msg = NULL;
327	}
328	else {
329	    move(23, 0);
330	    clrtoeol();
331	}
332
333	/* Get command character */
334	key = getch();
335	switch (toupper(key)) {
336	case '\014':	/* ^L (redraw) */
337	    clear();
338	    msg = NULL;
339	    break;
340
341	case '\020':	/* ^P */
342	case KEY_UP:
343	case '-':
344	    if (current_chunk != 0)
345		--current_chunk;
346	    break;
347
348	case '\016':	/* ^N */
349	case KEY_DOWN:
350	case '+':
351	case '\r':
352	case '\n':
353	    if (chunk_info[current_chunk + 1])
354		++current_chunk;
355	    break;
356
357	case KEY_HOME:
358	    current_chunk = 0;
359	    break;
360
361	case KEY_END:
362	    while (chunk_info[current_chunk + 1])
363		++current_chunk;
364	    break;
365
366	case KEY_F(1):
367	case '?':
368	    systemDisplayHelp("slice");
369	    clear();
370	    break;
371
372	case 'A':
373	case 'F':	/* Undocumented magic Dangerously Dedicated mode */
374#if defined(__alpha__) || defined(__sparc64__)
375	    rv = 1;
376#else	    /* The rest is only relevant on x86 */
377	    cp = variable_get(VAR_DEDICATE_DISK);
378	    if (cp && !strcasecmp(cp, "always"))
379		rv = 1;
380	    else if (toupper(key) == 'A')
381		rv = 0;
382	    else {
383		rv = msgYesNo("Do you want to do this with a true partition entry\n"
384			      "so as to remain cooperative with any future possible\n"
385			      "operating systems on the drive(s)?\n"
386			      "(See also the section about ``dangerously dedicated''\n"
387			      "disks in the FreeBSD FAQ.)");
388		if (rv == -1)
389		    rv = 0;
390	    }
391#endif
392	    All_FreeBSD(d, rv);
393	    variable_set2(DISK_PARTITIONED, "yes", 0);
394	    record_chunks(d);
395	    clear();
396	    break;
397
398	case 'C':
399	    if (chunk_info[current_chunk]->type != unused)
400		msg = "Slice in use, delete it first or move to an unused one.";
401	    else {
402		char *val, tmp[20], *cp;
403		int size;
404#ifdef PC98
405		char name[16];
406
407		snprintf(name, 16, "%s", "FreeBSD");
408		val = msgGetInput(name,
409			"Please specify the name for new FreeBSD slice.");
410		if (val)
411			strncpy(name, val, 16);
412#else
413		int subtype;
414		chunk_e partitiontype;
415#endif
416		snprintf(tmp, 20, "%lu", chunk_info[current_chunk]->size);
417		val = msgGetInput(tmp, "Please specify the size for new FreeBSD slice in blocks\n"
418				  "or append a trailing `M' for megabytes (e.g. 20M).");
419		if (val && (size = strtol(val, &cp, 0)) > 0) {
420		    if (*cp && toupper(*cp) == 'M')
421			size *= ONE_MEG;
422		    else if (*cp && toupper(*cp) == 'G')
423			size *= ONE_GIG;
424#ifdef PC98
425		    Create_Chunk(d, chunk_info[current_chunk]->offset, size,
426			freebsd, 3,
427			(chunk_info[current_chunk]->flags & CHUNK_ALIGN),
428			name);
429		    variable_set2(DISK_PARTITIONED, "yes", 0);
430		    record_chunks(d);
431#else
432		    sprintf(tmp, "%d", SUBTYPE_FREEBSD);
433		    val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
434				      "Pressing Enter will choose the default, a native FreeBSD\n"
435				      "slice (type 165).  You can choose other types, 6 for a\n"
436				      "DOS partition or 131 for a Linux partition, for example.\n\n"
437				      "Note:  If you choose a non-FreeBSD partition type, it will not\n"
438				      "be formatted or otherwise prepared, it will simply reserve space\n"
439				      "for you to use another tool, such as DOS FORMAT, to later format\n"
440				      "and use the partition.");
441		    if (val && (subtype = strtol(val, NULL, 0)) > 0) {
442			if (subtype == SUBTYPE_FREEBSD)
443			    partitiontype = freebsd;
444			else if (subtype == SUBTYPE_FAT)
445			    partitiontype = fat;
446			else if (subtype == SUBTYPE_EFI)
447			    partitiontype = efi;
448			else
449			    partitiontype = unknown;
450#if defined(__alpha__) || defined(__sparc64__)
451			if (partitiontype == freebsd && size == chunk_info[current_chunk]->size)
452			    All_FreeBSD(d, 1);
453			else
454#endif
455			Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
456				     (chunk_info[current_chunk]->flags & CHUNK_ALIGN), "");
457			variable_set2(DISK_PARTITIONED, "yes", 0);
458			record_chunks(d);
459		    }
460#endif /* PC98 */
461		}
462		clear();
463	    }
464	    break;
465
466	case KEY_DC:
467	case 'D':
468	    if (chunk_info[current_chunk]->type == unused)
469		msg = "Slice is already unused!";
470	    else {
471		Delete_Chunk(d, chunk_info[current_chunk]);
472		variable_set2(DISK_PARTITIONED, "yes", 0);
473		record_chunks(d);
474	    }
475	    break;
476
477	case 'T':
478	    if (chunk_info[current_chunk]->type == unused)
479		msg = "Slice is currently unused (use create instead)";
480	    else {
481		char *val, tmp[20];
482		int subtype;
483		chunk_e partitiontype;
484
485		sprintf(tmp, "%d", SUBTYPE_FREEBSD);
486#ifdef PC98
487		val = msgGetInput(tmp, "New partition type:\n\n"
488				  "Pressing Enter will choose the default, a native FreeBSD\n"
489				  "slice (type 50324).  Other popular values are 37218 for\n"
490				  "DOS FAT partition.\n\n"
491				  "Note:  If you choose a non-FreeBSD partition type, it will not\n"
492				  "be formatted or otherwise prepared, it will simply reserve space\n"
493				  "for you to use another tool, such as DOS format, to later format\n"
494				  "and actually use the partition.");
495#else
496		val = msgGetInput(tmp, "New partition type:\n\n"
497				  "Pressing Enter will choose the default, a native FreeBSD\n"
498				  "slice (type 165).  Other popular values are 6 for\n"
499				  "DOS FAT partition, 131 for a Linux ext2fs partition or\n"
500				  "130 for a Linux swap partition.\n\n"
501				  "Note:  If you choose a non-FreeBSD partition type, it will not\n"
502				  "be formatted or otherwise prepared, it will simply reserve space\n"
503				  "for you to use another tool, such as DOS format, to later format\n"
504				  "and actually use the partition.");
505#endif /* PC98 */
506		if (val && (subtype = strtol(val, NULL, 0)) > 0) {
507		    if (subtype == SUBTYPE_FREEBSD)
508			partitiontype = freebsd;
509		    else if (subtype == SUBTYPE_FAT)
510			partitiontype = fat;
511		    else
512			partitiontype = unknown;
513		    chunk_info[current_chunk]->type = partitiontype;
514		    chunk_info[current_chunk]->subtype = subtype;
515		}
516	    }
517	    break;
518
519	case 'G':
520	    snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
521	    val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
522			      "Don't forget to use the two slash (/) separator characters!\n"
523			      "It's not possible to parse the field without them.");
524	    if (val) {
525		long nc, nh, ns;
526		nc = strtol(val, &val, 0);
527		nh = strtol(val + 1, &val, 0);
528		ns = strtol(val + 1, 0, 0);
529		Set_Bios_Geom(d, nc, nh, ns);
530	    }
531	    clear();
532	    break;
533
534	case 'S':
535	    /* Set Bootable */
536	    chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
537	    break;
538
539	case 'U':
540	    if (!variable_cmp(DISK_LABELLED, "written")) {
541		msgConfirm("You've already written this information out - you\n"
542			   "can't undo it.");
543	    }
544	    else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
545		char cp[BUFSIZ];
546
547		sstrncpy(cp, d->name, sizeof cp);
548		Free_Disk(dev->private);
549		d = Open_Disk(cp);
550		if (!d)
551		    msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp);
552		dev->private = d;
553		variable_unset(DISK_PARTITIONED);
554		variable_unset(DISK_LABELLED);
555		if (d)
556		    record_chunks(d);
557	    }
558	    clear();
559	    break;
560
561	case 'W':
562	    if (!msgNoYes("WARNING:  This should only be used when modifying an EXISTING\n"
563			       "installation.  If you are installing FreeBSD for the first time\n"
564			       "then you should simply type Q when you're finished here and your\n"
565			       "changes will be committed in one batch automatically at the end of\n"
566			       "these questions.  If you're adding a disk, you should NOT write\n"
567			       "from this screen, you should do it from the label editor.\n\n"
568			       "Are you absolutely sure you want to do this now?")) {
569		variable_set2(DISK_PARTITIONED, "yes", 0);
570
571#ifdef PC98
572		/*
573		 * Don't trash the IPL if the first (and therefore only) chunk
574		 * is marked for a truly dedicated disk (i.e., the disklabel
575		 * starts at sector 0), even in cases where the user has
576		 * requested a FreeBSD Boot Manager -- both would be fatal in
577		 * this case.
578		 */
579		/*
580		 * Don't offer to update the IPL on this disk if the first
581		 * "real" chunk looks like a FreeBSD "all disk" partition,
582		 * or the disk is entirely FreeBSD.
583		 */
584		if ((d->chunks->part->type != freebsd) ||
585		    (d->chunks->part->offset > 1))
586		    getBootMgr(d->name, &bootipl, &bootipl_size,
587			       &bootmenu, &bootmenu_size);
588		else {
589		    bootipl = NULL;
590		    bootipl_size = 0;
591		    bootmenu = NULL;
592		    bootmenu_size = 0;
593		}
594		Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
595#else
596		/*
597		 * Don't trash the MBR if the first (and therefore only) chunk
598		 * is marked for a truly dedicated disk (i.e., the disklabel
599		 * starts at sector 0), even in cases where the user has
600		 * requested booteasy or a "standard" MBR -- both would be
601		 * fatal in this case.
602		 */
603		/*
604		 * Don't offer to update the MBR on this disk if the first
605		 * "real" chunk looks like a FreeBSD "all disk" partition,
606		 * or the disk is entirely FreeBSD.
607		 */
608		if ((d->chunks->part->type != freebsd) ||
609		    (d->chunks->part->offset > 1))
610		    getBootMgr(d->name, &mbrContents, &mbrSize);
611		else {
612		    mbrContents = NULL;
613		    mbrSize = 0;
614		}
615		Set_Boot_Mgr(d, mbrContents, mbrSize);
616#endif
617
618		if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
619		    msgConfirm("Disk partition write returned an error status!");
620		else
621		    msgConfirm("Wrote FDISK partition information out successfully.");
622	    }
623	    clear();
624	    break;
625
626	case '|':
627	    if (!msgNoYes("Are you SURE you want to go into Wizard mode?\n"
628			  "No seat belts whatsoever are provided!")) {
629		clear();
630		refresh();
631		slice_wizard(d);
632		variable_set2(DISK_PARTITIONED, "yes", 0);
633		record_chunks(d);
634	    }
635	    else
636		msg = "Wise choice!";
637	    clear();
638	    break;
639
640	case '\033':	/* ESC */
641	case 'Q':
642	    chunking = FALSE;
643#ifdef PC98
644	    /*
645	     * Don't trash the IPL if the first (and therefore only) chunk
646	     * is marked for a truly dedicated disk (i.e., the disklabel
647	     * starts at sector 0), even in cases where the user has requested
648	     * a FreeBSD Boot Manager -- both would be fatal in this case.
649	     */
650	    /*
651	     * Don't offer to update the IPL on this disk if the first "real"
652	     * chunk looks like a FreeBSD "all disk" partition, or the disk is
653	     * entirely FreeBSD.
654	     */
655	    if ((d->chunks->part->type != freebsd) ||
656		(d->chunks->part->offset > 1)) {
657		if (variable_cmp(DISK_PARTITIONED, "written")) {
658		    getBootMgr(d->name, &bootipl, &bootipl_size,
659			&bootmenu, &bootmenu_size);
660		    if (bootipl != NULL && bootmenu != NULL)
661			Set_Boot_Mgr(d, bootipl, bootipl_size,
662			    bootmenu, bootmenu_size);
663		}
664	    }
665#else
666	    /*
667	     * Don't trash the MBR if the first (and therefore only) chunk
668	     * is marked for a truly dedicated disk (i.e., the disklabel
669	     * starts at sector 0), even in cases where the user has requested
670	     * booteasy or a "standard" MBR -- both would be fatal in this case.
671	     */
672	    /*
673	     * Don't offer to update the MBR on this disk if the first "real"
674	     * chunk looks like a FreeBSD "all disk" partition, or the disk is
675	     * entirely FreeBSD.
676	     */
677	    if ((d->chunks->part->type != freebsd) ||
678		(d->chunks->part->offset > 1)) {
679		if (variable_cmp(DISK_PARTITIONED, "written")) {
680		    getBootMgr(d->name, &mbrContents, &mbrSize);
681		    if (mbrContents != NULL)
682			Set_Boot_Mgr(d, mbrContents, mbrSize);
683		}
684	    }
685#endif
686	    break;
687
688	case 'Z':
689	    size_unit = (size_unit + 1) % UNIT_SIZE;
690	    break;
691
692	default:
693	    beep();
694	    msg = "Type F1 or ? for help";
695	    break;
696	}
697    }
698    p = CheckRules(d);
699    if (p) {
700	char buf[FILENAME_MAX];
701
702        use_helpline("Press F1 to read more about disk slices.");
703	use_helpfile(systemHelpFile("partition", buf));
704	if (!variable_get(VAR_NO_WARN))
705	    dialog_mesgbox("Disk slicing warning:", p, -1, -1);
706	free(p);
707    }
708    restorescr(w);
709}
710
711static u_char *
712bootalloc(char *name, size_t *size)
713{
714    char buf[FILENAME_MAX];
715    struct stat sb;
716
717    snprintf(buf, sizeof buf, "/boot/%s", name);
718    if (stat(buf, &sb) != -1) {
719	int fd;
720
721	fd = open(buf, O_RDONLY);
722	if (fd != -1) {
723	    u_char *cp;
724
725	    cp = malloc(sb.st_size);
726	    if (read(fd, cp, sb.st_size) != sb.st_size) {
727		free(cp);
728		close(fd);
729		msgDebug("bootalloc: couldn't read %ld bytes from %s\n", (long)sb.st_size, buf);
730		return NULL;
731	    }
732	    close(fd);
733	    if (size != NULL)
734		*size = sb.st_size;
735	    return cp;
736	}
737	msgDebug("bootalloc: couldn't open %s\n", buf);
738    }
739    else
740	msgDebug("bootalloc: can't stat %s\n", buf);
741    return NULL;
742}
743
744static int
745partitionHook(dialogMenuItem *selected)
746{
747    Device **devs = NULL;
748
749    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
750    if (!devs) {
751	msgConfirm("Unable to find disk %s!", selected->prompt);
752	return DITEM_FAILURE;
753    }
754    /* Toggle enabled status? */
755    if (!devs[0]->enabled) {
756	devs[0]->enabled = TRUE;
757	diskPartition(devs[0]);
758    }
759    else
760	devs[0]->enabled = FALSE;
761    return DITEM_SUCCESS;
762}
763
764static int
765partitionCheck(dialogMenuItem *selected)
766{
767    Device **devs = NULL;
768
769    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
770    if (!devs || devs[0]->enabled == FALSE)
771	return FALSE;
772    return TRUE;
773}
774
775int
776diskPartitionEditor(dialogMenuItem *self)
777{
778    DMenu *menu;
779    Device **devs;
780    int i, cnt, devcnt;
781
782    cnt = diskGetSelectCount(&devs);
783    devcnt = deviceCount(devs);
784    if (cnt == -1) {
785	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
786		   "properly probed at boot time.  See the Hardware Guide on the\n"
787		   "Documentation menu for clues on diagnosing this type of problem.");
788	return DITEM_FAILURE;
789    }
790    else if (cnt) {
791	/* Some are already selected */
792	for (i = 0; i < devcnt; i++) {
793	    if (devs[i]->enabled) {
794		if (variable_get(VAR_NONINTERACTIVE) &&
795		  !variable_get(VAR_DISKINTERACTIVE))
796		    diskPartitionNonInteractive(devs[i]);
797		else
798		    diskPartition(devs[i]);
799	    }
800	}
801    }
802    else {
803	/* No disks are selected, fall-back case now */
804	if (devcnt == 1) {
805	    devs[0]->enabled = TRUE;
806	    if (variable_get(VAR_NONINTERACTIVE) &&
807	      !variable_get(VAR_DISKINTERACTIVE))
808		diskPartitionNonInteractive(devs[0]);
809	    else
810		diskPartition(devs[0]);
811	    return DITEM_SUCCESS;
812	}
813	else {
814	    menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
815	    if (!menu) {
816		msgConfirm("No devices suitable for installation found!\n\n"
817			   "Please verify that your disk controller (and attached drives)\n"
818			   "were detected properly.  This can be done by pressing the\n"
819			   "[Scroll Lock] key and using the Arrow keys to move back to\n"
820			   "the boot messages.  Press [Scroll Lock] again to return.");
821		return DITEM_FAILURE;
822	    }
823	    else {
824		i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
825		free(menu);
826	    }
827	    return i;
828	}
829    }
830    return DITEM_SUCCESS;
831}
832
833int
834diskPartitionWrite(dialogMenuItem *self)
835{
836    Device **devs;
837    int i;
838
839    if (!variable_cmp(DISK_PARTITIONED, "written"))
840	return DITEM_SUCCESS;
841
842    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
843    if (!devs) {
844	msgConfirm("Unable to find any disks to write to??");
845	return DITEM_FAILURE;
846    }
847    if (isDebug())
848	msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
849    for (i = 0; devs[i]; i++) {
850	Disk *d = (Disk *)devs[i]->private;
851	static u_char *boot1;
852#if defined(__i386__) || defined(__ia64__)
853	static u_char *boot2;
854#endif
855
856	if (!devs[i]->enabled)
857	    continue;
858
859#if defined(__i386__) || defined(__ia64__)
860	if (!boot1) boot1 = bootalloc("boot1", NULL);
861	if (!boot2) boot2 = bootalloc("boot2", NULL);
862	Set_Boot_Blocks(d, boot1, boot2);
863#else
864	if (!boot1) boot1 = bootalloc("boot1", NULL);
865	Set_Boot_Blocks(d, boot1, NULL);
866#endif
867
868	msgNotify("Writing partition information to drive %s", d->name);
869	if (!Fake && Write_Disk(d)) {
870	    msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
871	    return DITEM_FAILURE;
872	}
873    }
874    /* Now it's not "yes", but "written" */
875    variable_set2(DISK_PARTITIONED, "written", 0);
876    return DITEM_SUCCESS | DITEM_RESTORE;
877}
878
879/* Partition a disk based wholly on which variables are set */
880static void
881diskPartitionNonInteractive(Device *dev)
882{
883    char *cp;
884    int i, sz, all_disk = 0;
885#ifdef PC98
886    u_char *bootipl;
887    size_t bootipl_size;
888    u_char *bootmenu;
889    size_t bootmenu_size;
890#else
891    u_char *mbrContents;
892    size_t mbrSize;
893#endif
894    Disk *d = (Disk *)dev->private;
895
896    record_chunks(d);
897    cp = variable_get(VAR_GEOMETRY);
898    if (cp) {
899	msgDebug("Setting geometry from script to: %s\n", cp);
900	d->bios_cyl = strtol(cp, &cp, 0);
901	d->bios_hd = strtol(cp + 1, &cp, 0);
902	d->bios_sect = strtol(cp + 1, 0, 0);
903    }
904
905    cp = variable_get(VAR_PARTITION);
906    if (cp) {
907	if (!strcmp(cp, "free")) {
908	    /* Do free disk space case */
909	    for (i = 0; chunk_info[i]; i++) {
910		/* If a chunk is at least 10MB in size, use it. */
911		if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
912		    Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
913				 freebsd, 3,
914				 (chunk_info[i]->flags & CHUNK_ALIGN),
915				 "FreeBSD");
916		    variable_set2(DISK_PARTITIONED, "yes", 0);
917		    break;
918		}
919	    }
920	    if (!chunk_info[i]) {
921		msgConfirm("Unable to find any free space on this disk!");
922		return;
923	    }
924	}
925	else if (!strcmp(cp, "all")) {
926	    /* Do all disk space case */
927	    msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
928
929	    All_FreeBSD(d, FALSE);
930	}
931	else if (!strcmp(cp, "exclusive")) {
932	    /* Do really-all-the-disk-space case */
933	    msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
934
935	    All_FreeBSD(d, all_disk = TRUE);
936	}
937	else if ((sz = strtol(cp, &cp, 0))) {
938	    /* Look for sz bytes free */
939	    if (*cp && toupper(*cp) == 'M')
940		sz *= ONE_MEG;
941	    else if (*cp && toupper(*cp) == 'G')
942		sz *= ONE_GIG;
943	    for (i = 0; chunk_info[i]; i++) {
944		/* If a chunk is at least sz MB, use it. */
945		if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
946		    Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
947				 (chunk_info[i]->flags & CHUNK_ALIGN),
948				 "FreeBSD");
949		    variable_set2(DISK_PARTITIONED, "yes", 0);
950		    break;
951		}
952	    }
953	    if (!chunk_info[i]) {
954		msgConfirm("Unable to find %d free blocks on this disk!", sz);
955		return;
956	    }
957	}
958	else if (!strcmp(cp, "existing")) {
959	    /* Do existing FreeBSD case */
960	    for (i = 0; chunk_info[i]; i++) {
961		if (chunk_info[i]->type == freebsd)
962		    break;
963	    }
964	    if (!chunk_info[i]) {
965		msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
966		return;
967	    }
968	}
969	else {
970	    msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
971	    return;
972	}
973	if (!all_disk) {
974#ifdef PC98
975	    getBootMgr(d->name, &bootipl, &bootipl_size,
976		       &bootmenu, &bootmenu_size);
977	    Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
978#else
979	    getBootMgr(d->name, &mbrContents, &mbrSize);
980	    Set_Boot_Mgr(d, mbrContents, mbrSize);
981#endif
982	}
983	variable_set2(DISK_PARTITIONED, "yes", 0);
984    }
985}
986
987