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