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