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