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