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