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