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