disks.c revision 121863
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 121863 2003-11-01 20:04:12Z 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
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#ifndef __ia64__
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#endif
640
641	case '\033':	/* ESC */
642	case 'Q':
643	    chunking = FALSE;
644#ifdef PC98
645	    /*
646	     * Don't trash the IPL if the first (and therefore only) chunk
647	     * is marked for a truly dedicated disk (i.e., the disklabel
648	     * starts at sector 0), even in cases where the user has requested
649	     * a FreeBSD Boot Manager -- both would be fatal in this case.
650	     */
651	    /*
652	     * Don't offer to update the IPL on this disk if the first "real"
653	     * chunk looks like a FreeBSD "all disk" partition, or the disk is
654	     * entirely FreeBSD.
655	     */
656	    if ((d->chunks->part->type != freebsd) ||
657		(d->chunks->part->offset > 1)) {
658		if (variable_cmp(DISK_PARTITIONED, "written")) {
659		    getBootMgr(d->name, &bootipl, &bootipl_size,
660			&bootmenu, &bootmenu_size);
661		    if (bootipl != NULL && bootmenu != NULL)
662			Set_Boot_Mgr(d, bootipl, bootipl_size,
663			    bootmenu, bootmenu_size);
664		}
665	    }
666#else
667	    /*
668	     * Don't trash the MBR if the first (and therefore only) chunk
669	     * is marked for a truly dedicated disk (i.e., the disklabel
670	     * starts at sector 0), even in cases where the user has requested
671	     * booteasy or a "standard" MBR -- both would be fatal in this case.
672	     */
673	    /*
674	     * Don't offer to update the MBR on this disk if the first "real"
675	     * chunk looks like a FreeBSD "all disk" partition, or the disk is
676	     * entirely FreeBSD.
677	     */
678	    if ((d->chunks->part->type != freebsd) ||
679		(d->chunks->part->offset > 1)) {
680		if (variable_cmp(DISK_PARTITIONED, "written")) {
681		    getBootMgr(d->name, &mbrContents, &mbrSize);
682		    if (mbrContents != NULL)
683			Set_Boot_Mgr(d, mbrContents, mbrSize);
684		}
685	    }
686#endif
687	    break;
688
689	case 'Z':
690	    size_unit = (size_unit + 1) % UNIT_SIZE;
691	    break;
692
693	default:
694	    beep();
695	    msg = "Type F1 or ? for help";
696	    break;
697	}
698    }
699    p = CheckRules(d);
700    if (p) {
701	char buf[FILENAME_MAX];
702
703        use_helpline("Press F1 to read more about disk slices.");
704	use_helpfile(systemHelpFile("partition", buf));
705	if (!variable_get(VAR_NO_WARN))
706	    dialog_mesgbox("Disk slicing warning:", p, -1, -1);
707	free(p);
708    }
709    restorescr(w);
710}
711#endif /* WITH_SLICES */
712
713static u_char *
714bootalloc(char *name, size_t *size)
715{
716    char buf[FILENAME_MAX];
717    struct stat sb;
718
719    snprintf(buf, sizeof buf, "/boot/%s", name);
720    if (stat(buf, &sb) != -1) {
721	int fd;
722
723	fd = open(buf, O_RDONLY);
724	if (fd != -1) {
725	    u_char *cp;
726
727	    cp = malloc(sb.st_size);
728	    if (read(fd, cp, sb.st_size) != sb.st_size) {
729		free(cp);
730		close(fd);
731		msgDebug("bootalloc: couldn't read %ld bytes from %s\n", (long)sb.st_size, buf);
732		return NULL;
733	    }
734	    close(fd);
735	    if (size != NULL)
736		*size = sb.st_size;
737	    return cp;
738	}
739	msgDebug("bootalloc: couldn't open %s\n", buf);
740    }
741    else
742	msgDebug("bootalloc: can't stat %s\n", buf);
743    return NULL;
744}
745
746#ifdef WITH_SLICES
747static int
748partitionHook(dialogMenuItem *selected)
749{
750    Device **devs = NULL;
751
752    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
753    if (!devs) {
754	msgConfirm("Unable to find disk %s!", selected->prompt);
755	return DITEM_FAILURE;
756    }
757    /* Toggle enabled status? */
758    if (!devs[0]->enabled) {
759	devs[0]->enabled = TRUE;
760	diskPartition(devs[0]);
761    }
762    else
763	devs[0]->enabled = FALSE;
764    return DITEM_SUCCESS;
765}
766
767static int
768partitionCheck(dialogMenuItem *selected)
769{
770    Device **devs = NULL;
771
772    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
773    if (!devs || devs[0]->enabled == FALSE)
774	return FALSE;
775    return TRUE;
776}
777
778int
779diskPartitionEditor(dialogMenuItem *self)
780{
781    DMenu *menu;
782    Device **devs;
783    int i, cnt, devcnt;
784
785    cnt = diskGetSelectCount(&devs);
786    devcnt = deviceCount(devs);
787    if (cnt == -1) {
788	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
789		   "properly probed at boot time.  See the Hardware Guide on the\n"
790		   "Documentation menu for clues on diagnosing this type of problem.");
791	return DITEM_FAILURE;
792    }
793    else if (cnt) {
794	/* Some are already selected */
795	for (i = 0; i < devcnt; i++) {
796	    if (devs[i]->enabled) {
797		if (variable_get(VAR_NONINTERACTIVE) &&
798		  !variable_get(VAR_DISKINTERACTIVE))
799		    diskPartitionNonInteractive(devs[i]);
800		else
801		    diskPartition(devs[i]);
802	    }
803	}
804    }
805    else {
806	/* No disks are selected, fall-back case now */
807	if (devcnt == 1) {
808	    devs[0]->enabled = TRUE;
809	    if (variable_get(VAR_NONINTERACTIVE) &&
810	      !variable_get(VAR_DISKINTERACTIVE))
811		diskPartitionNonInteractive(devs[0]);
812	    else
813		diskPartition(devs[0]);
814	    return DITEM_SUCCESS;
815	}
816	else {
817	    menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
818	    if (!menu) {
819		msgConfirm("No devices suitable for installation found!\n\n"
820			   "Please verify that your disk controller (and attached drives)\n"
821			   "were detected properly.  This can be done by pressing the\n"
822			   "[Scroll Lock] key and using the Arrow keys to move back to\n"
823			   "the boot messages.  Press [Scroll Lock] again to return.");
824		return DITEM_FAILURE;
825	    }
826	    else {
827		i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
828		free(menu);
829	    }
830	    return i;
831	}
832    }
833    return DITEM_SUCCESS;
834}
835#endif /* WITH_SLICES */
836
837int
838diskPartitionWrite(dialogMenuItem *self)
839{
840    Device **devs;
841    int i;
842
843    if (!variable_cmp(DISK_PARTITIONED, "written"))
844	return DITEM_SUCCESS;
845
846    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
847    if (!devs) {
848	msgConfirm("Unable to find any disks to write to??");
849	return DITEM_FAILURE;
850    }
851    if (isDebug())
852	msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
853    for (i = 0; devs[i]; i++) {
854	Disk *d = (Disk *)devs[i]->private;
855	static u_char *boot1;
856#if defined(__i386__) || defined(__ia64__) || defined(__amd64__)
857	static u_char *boot2;
858#endif
859
860	if (!devs[i]->enabled)
861	    continue;
862
863#if defined(__i386__) || defined(__amd64__)
864	if (!boot1) boot1 = bootalloc("boot1", NULL);
865	if (!boot2) boot2 = bootalloc("boot2", NULL);
866	Set_Boot_Blocks(d, boot1, boot2);
867#elif !defined(__ia64__)
868	if (!boot1) boot1 = bootalloc("boot1", NULL);
869	Set_Boot_Blocks(d, boot1, NULL);
870#endif
871
872	msgNotify("Writing partition information to drive %s", d->name);
873	if (!Fake && Write_Disk(d)) {
874	    msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
875	    return DITEM_FAILURE;
876	}
877    }
878    /* Now it's not "yes", but "written" */
879    variable_set2(DISK_PARTITIONED, "written", 0);
880    return DITEM_SUCCESS | DITEM_RESTORE;
881}
882
883#ifdef WITH_SLICES
884/* Partition a disk based wholly on which variables are set */
885static void
886diskPartitionNonInteractive(Device *dev)
887{
888    char *cp;
889    int i, sz, all_disk = 0;
890#ifdef PC98
891    u_char *bootipl;
892    size_t bootipl_size;
893    u_char *bootmenu;
894    size_t bootmenu_size;
895#else
896    u_char *mbrContents;
897    size_t mbrSize;
898#endif
899    Disk *d = (Disk *)dev->private;
900
901    record_chunks(d);
902    cp = variable_get(VAR_GEOMETRY);
903    if (cp) {
904	msgDebug("Setting geometry from script to: %s\n", cp);
905	d->bios_cyl = strtol(cp, &cp, 0);
906	d->bios_hd = strtol(cp + 1, &cp, 0);
907	d->bios_sect = strtol(cp + 1, 0, 0);
908    }
909
910    cp = variable_get(VAR_PARTITION);
911    if (cp) {
912	if (!strcmp(cp, "free")) {
913	    /* Do free disk space case */
914	    for (i = 0; chunk_info[i]; i++) {
915		/* If a chunk is at least 10MB in size, use it. */
916		if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
917		    Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
918				 freebsd, 3,
919				 (chunk_info[i]->flags & CHUNK_ALIGN),
920				 "FreeBSD");
921		    variable_set2(DISK_PARTITIONED, "yes", 0);
922		    break;
923		}
924	    }
925	    if (!chunk_info[i]) {
926		msgConfirm("Unable to find any free space on this disk!");
927		return;
928	    }
929	}
930	else if (!strcmp(cp, "all")) {
931	    /* Do all disk space case */
932	    msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
933
934	    All_FreeBSD(d, FALSE);
935	}
936	else if (!strcmp(cp, "exclusive")) {
937	    /* Do really-all-the-disk-space case */
938	    msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
939
940	    All_FreeBSD(d, all_disk = TRUE);
941	}
942	else if ((sz = strtol(cp, &cp, 0))) {
943	    /* Look for sz bytes free */
944	    if (*cp && toupper(*cp) == 'M')
945		sz *= ONE_MEG;
946	    else if (*cp && toupper(*cp) == 'G')
947		sz *= ONE_GIG;
948	    for (i = 0; chunk_info[i]; i++) {
949		/* If a chunk is at least sz MB, use it. */
950		if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
951		    Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
952				 (chunk_info[i]->flags & CHUNK_ALIGN),
953				 "FreeBSD");
954		    variable_set2(DISK_PARTITIONED, "yes", 0);
955		    break;
956		}
957	    }
958	    if (!chunk_info[i]) {
959		msgConfirm("Unable to find %d free blocks on this disk!", sz);
960		return;
961	    }
962	}
963	else if (!strcmp(cp, "existing")) {
964	    /* Do existing FreeBSD case */
965	    for (i = 0; chunk_info[i]; i++) {
966		if (chunk_info[i]->type == freebsd)
967		    break;
968	    }
969	    if (!chunk_info[i]) {
970		msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
971		return;
972	    }
973	}
974	else {
975	    msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
976	    return;
977	}
978	if (!all_disk) {
979#ifdef PC98
980	    getBootMgr(d->name, &bootipl, &bootipl_size,
981		       &bootmenu, &bootmenu_size);
982	    Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
983#else
984	    getBootMgr(d->name, &mbrContents, &mbrSize);
985	    Set_Boot_Mgr(d, mbrContents, mbrSize);
986#endif
987	}
988	variable_set2(DISK_PARTITIONED, "yes", 0);
989    }
990}
991#endif /* WITH_SLICES */
992