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