disks.c revision 161059
155714Skris/*
255714Skris * The new sysinstall program.
355714Skris *
455714Skris * This is probably the last program in the `sysinstall' line - the next
555714Skris * generation being essentially a complete rewrite.
655714Skris *
755714Skris * $FreeBSD: head/usr.sbin/sade/disks.c 156123 2006-02-28 21:49:33Z jhb $
855714Skris *
955714Skris * Copyright (c) 1995
1055714Skris *	Jordan Hubbard.  All rights reserved.
1155714Skris *
1255714Skris * Redistribution and use in source and binary forms, with or without
1355714Skris * modification, are permitted provided that the following conditions
1455714Skris * are met:
1555714Skris * 1. Redistributions of source code must retain the above copyright
1655714Skris *    notice, this list of conditions and the following disclaimer,
1755714Skris *    verbatim and that no modifications are made prior to this
1855714Skris *    point in the file.
1955714Skris * 2. Redistributions in binary form must reproduce the above copyright
2055714Skris *    notice, this list of conditions and the following disclaimer in the
2155714Skris *    documentation and/or other materials provided with the distribution.
2255714Skris *
2355714Skris * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
2455714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2555714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2655714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
2755714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2855714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2955714Skris * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
3055714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3155714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3255714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3355714Skris * SUCH DAMAGE.
3455714Skris *
3555714Skris */
3655714Skris
3755714Skris#include "sysinstall.h"
3855714Skris#include <ctype.h>
3955714Skris#include <fcntl.h>
4055714Skris#include <inttypes.h>
4155714Skris#include <libdisk.h>
4255714Skris#include <sys/stat.h>
4355714Skris#include <sys/disklabel.h>
4455714Skris
4555714Skris#ifdef WITH_SLICES
4655714Skrisenum size_units_t { UNIT_BLOCKS, UNIT_KILO, UNIT_MEG, UNIT_GIG, UNIT_SIZE };
4755714Skris
4855714Skris#ifdef PC98
4955714Skris#define	SUBTYPE_FREEBSD		50324
5055714Skris#define	SUBTYPE_FAT		37218
5155714Skris#else
5255714Skris#define	SUBTYPE_FREEBSD		165
5355714Skris#define	SUBTYPE_FAT		6
5455714Skris#endif
5555714Skris#define	SUBTYPE_EFI		239
5655714Skris
5755714Skris#ifdef PC98
5855714Skris#define	OTHER_SLICE_VALUES						\
5955714Skris	"Other popular values are 37218 for a\n"			\
6055714Skris	"DOS FAT partition.\n\n"
6155714Skris#else
6255714Skris#define	OTHER_SLICE_VALUES						\
6355714Skris	"Other popular values are 6 for a\n"				\
6455714Skris	"DOS FAT partition, 131 for a Linux ext2fs partition, or\n"	\
6555714Skris	"130 for a Linux swap partition.\n\n"
6655714Skris#endif
6755714Skris#define	NON_FREEBSD_NOTE						\
6855714Skris	"Note:  If you choose a non-FreeBSD partition type, it will not\n" \
6955714Skris	"be formatted or otherwise prepared, it will simply reserve space\n" \
7055714Skris	"for you to use another tool, such as DOS format, to later format\n" \
7155714Skris	"and actually use the partition."
7255714Skris
7355714Skris/* Where we start displaying chunk information on the screen */
7455714Skris#define CHUNK_START_ROW		5
7559191Skris
7655714Skris/* Where we keep track of MBR chunks */
7755714Skris#define	CHUNK_INFO_ENTRIES	16
7855714Skrisstatic struct chunk *chunk_info[CHUNK_INFO_ENTRIES];
7955714Skrisstatic int current_chunk;
8055714Skris
8155714Skrisstatic void	diskPartitionNonInteractive(Device *dev);
8255714Skrisstatic u_char *	bootalloc(char *name, size_t *size);
8355714Skris
8455714Skrisstatic void
8555714Skrisrecord_chunks(Disk *d)
8655714Skris{
8759191Skris    struct chunk *c1 = NULL;
8859191Skris    int i = 0;
8959191Skris    daddr_t last_free = 0;
9055714Skris
9155714Skris    if (!d->chunks)
9255714Skris	msgFatal("No chunk list found for %s!", d->name);
9355714Skris
9455714Skris    for (c1 = d->chunks->part; c1; c1 = c1->next) {
9555714Skris	if (c1->type == unused && c1->size > last_free) {
9655714Skris	    last_free = c1->size;
9755714Skris	    current_chunk = i;
9855714Skris	}
9955714Skris	chunk_info[i++] = c1;
10055714Skris    }
10155714Skris    chunk_info[i] = NULL;
10255714Skris    if (current_chunk >= i)
10355714Skris	current_chunk = i - 1;
10455714Skris}
10555714Skris
10655714Skrisstatic daddr_t Total;
10755714Skris
10855714Skrisstatic void
10955714Skrisprint_chunks(Disk *d, int u)
11055714Skris{
11155714Skris    int row;
11255714Skris    int i;
11355714Skris    daddr_t sz;
11455714Skris    char *szstr;
11555714Skris
11655714Skris    szstr = (u == UNIT_GIG ? "GB" : (u == UNIT_MEG ? "MB" :
11755714Skris	(u == UNIT_KILO ? "KB" : "ST")));
11855714Skris
11955714Skris    Total = 0;
12055714Skris    for (i = 0; chunk_info[i]; i++)
12155714Skris	Total += chunk_info[i]->size;
12255714Skris#ifdef PC98
12355714Skris    if (d->bios_cyl >= 65536 || d->bios_hd > 256 || d->bios_sect >= 256) {
12455714Skris#else
12555714Skris    if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64) {
12655714Skris#endif
12755714Skris	dialog_clear_norefresh();
12855714Skris	msgConfirm("WARNING:  A geometry of %lu/%lu/%lu for %s is incorrect.  Using\n"
12955714Skris		   "a more likely geometry.  If this geometry is incorrect or you\n"
13055714Skris		   "are unsure as to whether or not it's correct, please consult\n"
13155714Skris		   "the Hardware Guide in the Documentation submenu or use the\n"
13255714Skris		   "(G)eometry command to change it now.\n\n"
13355714Skris		   "Remember: you need to enter whatever your BIOS thinks the\n"
13455714Skris		   "geometry is!  For IDE, it's what you were told in the BIOS\n"
13555714Skris		   "setup. For SCSI, it's the translation mode your controller is\n"
13655714Skris		   "using.  Do NOT use a ``physical geometry''.",
13755714Skris	  d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
13855714Skris	Sanitize_Bios_Geom(d);
13955714Skris    }
14055714Skris    attrset(A_NORMAL);
14155714Skris    mvaddstr(0, 0, "Disk name:\t");
14255714Skris    clrtobot();
14355714Skris    attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL);
14455714Skris    attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL);
14555714Skris    mvprintw(1, 0,
14655714Skris	     "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors = %jd sectors (%jdMB)",
14755714Skris	     d->bios_cyl, d->bios_hd, d->bios_sect,
14855714Skris	     (intmax_t)d->bios_cyl * d->bios_hd * d->bios_sect,
14955714Skris	     (intmax_t)d->bios_cyl * d->bios_hd * d->bios_sect / (1024/512) / 1024);
15055714Skris    mvprintw(3, 0, "%6s %10s(%s) %10s %8s %6s %10s %8s %8s",
15155714Skris	     "Offset", "Size", szstr, "End", "Name", "PType", "Desc",
15255714Skris	     "Subtype", "Flags");
15355714Skris    for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
15455714Skris	switch(u) {
15555714Skris	default:	/* fall thru */
15655714Skris	case UNIT_BLOCKS:
15755714Skris	    sz = chunk_info[i]->size;
15855714Skris	    break;
15955714Skris	case UNIT_KILO:
16055714Skris	    sz = chunk_info[i]->size / (1024/512);
16155714Skris	    break;
16255714Skris	case UNIT_MEG:
16355714Skris	    sz = chunk_info[i]->size / (1024/512) / 1024;
16455714Skris	    break;
16555714Skris	case UNIT_GIG:
16655714Skris	    sz = chunk_info[i]->size / (1024/512) / 1024 / 1024;
16755714Skris	    break;
16855714Skris	}
16955714Skris	if (i == current_chunk)
17055714Skris	    attrset(ATTR_SELECTED);
17155714Skris	mvprintw(row, 0, "%10jd %10jd %10jd %8s %6d %10s %8d\t%-6s",
17255714Skris		 (intmax_t)chunk_info[i]->offset, (intmax_t)sz,
17355714Skris		 (intmax_t)chunk_info[i]->end, chunk_info[i]->name,
17455714Skris		 chunk_info[i]->type,
17555714Skris		 slice_type_name(chunk_info[i]->type, chunk_info[i]->subtype),
17655714Skris		 chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
17755714Skris	if (i == current_chunk)
17855714Skris	    attrset(A_NORMAL);
17955714Skris    }
18055714Skris}
18155714Skris
18255714Skrisstatic void
18355714Skrisprint_command_summary(void)
18455714Skris{
18555714Skris    mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
18655714Skris    mvprintw(16, 0, "A = Use Entire Disk   G = set Drive Geometry   C = Create Slice   F = `DD' mode");
18755714Skris    mvprintw(17, 0, "D = Delete Slice      Z = Toggle Size Units    S = Set Bootable   | = Wizard m.");
18855714Skris    mvprintw(18, 0, "T = Change Type       U = Undo All Changes     Q = Finish");
18955714Skris    if (!RunningAsInit)
19055714Skris	mvprintw(18, 47, "W = Write Changes");
19155714Skris    mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select.");
19255714Skris    move(0, 0);
19355714Skris}
19455714Skris
19555714Skris#ifdef PC98
19655714Skrisstatic void
19755714SkrisgetBootMgr(char *dname, u_char **bootipl, size_t *bootipl_size,
19855714Skris	   u_char **bootmenu, size_t *bootmenu_size)
19955714Skris{
20055714Skris    static u_char *boot0;
20155714Skris    static size_t boot0_size;
20255714Skris    static u_char *boot05;
20355714Skris    static size_t boot05_size;
20455714Skris
20555714Skris    char str[80];
20655714Skris    char *cp;
20755714Skris    int i = 0;
20855714Skris
20955714Skris    cp = variable_get(VAR_BOOTMGR);
21055714Skris    if (!cp) {
21155714Skris	/* Figure out what kind of IPL the user wants */
21255714Skris	sprintf(str, "Install Boot Manager for drive %s?", dname);
21355714Skris	MenuIPLType.title = str;
21455714Skris	i = dmenuOpenSimple(&MenuIPLType, FALSE);
21555714Skris    } else {
21655714Skris	if (!strncmp(cp, "boot", 4))
21755714Skris	    BootMgr = 0;
21855714Skris	else
21955714Skris	    BootMgr = 1;
22055714Skris    }
22155714Skris    if (cp || i) {
22255714Skris	switch (BootMgr) {
22355714Skris	case 0:
22455714Skris	    if (!boot0) boot0 = bootalloc("boot0", &boot0_size);
22555714Skris	    *bootipl = boot0;
22655714Skris	    *bootipl_size = boot0_size;
22755714Skris	    if (!boot05) boot05 = bootalloc("boot0.5", &boot05_size);
22855714Skris	    *bootmenu = boot05;
22955714Skris	    *bootmenu_size = boot05_size;
23055714Skris	    return;
23155714Skris	case 1:
23255714Skris	default:
23355714Skris	    break;
23455714Skris	}
23555714Skris    }
23655714Skris    *bootipl = NULL;
23755714Skris    *bootipl_size = 0;
23855714Skris    *bootmenu = NULL;
23955714Skris    *bootmenu_size = 0;
24055714Skris}
24155714Skris#else
24255714Skrisstatic void
24355714SkrisgetBootMgr(char *dname, u_char **bootCode, size_t *bootCodeSize)
24455714Skris{
24555714Skris#if defined(__i386__) || defined(__amd64__)	/* only meaningful on x86 */
24655714Skris    static u_char *mbr, *boot0;
24755714Skris    static size_t mbr_size, boot0_size;
24855714Skris    char str[80];
24955714Skris    char *cp;
25055714Skris    int i = 0;
25155714Skris
25255714Skris    cp = variable_get(VAR_BOOTMGR);
25355714Skris    if (!cp) {
25455714Skris	/* Figure out what kind of MBR the user wants */
25555714Skris	sprintf(str, "Install Boot Manager for drive %s?", dname);
25655714Skris	MenuMBRType.title = str;
25755714Skris	i = dmenuOpenSimple(&MenuMBRType, FALSE);
25855714Skris    }
25955714Skris    else {
26055714Skris	if (!strncmp(cp, "boot", 4))
26155714Skris	    BootMgr = 0;
26255714Skris	else if (!strcmp(cp, "standard"))
26355714Skris	    BootMgr = 1;
26455714Skris	else
26555714Skris	    BootMgr = 2;
26655714Skris    }
26755714Skris    if (cp || i) {
26855714Skris	switch (BootMgr) {
26955714Skris	case 0:
27055714Skris	    if (!boot0) boot0 = bootalloc("boot0", &boot0_size);
27155714Skris	    *bootCode = boot0;
27255714Skris	    *bootCodeSize = boot0_size;
27355714Skris	    return;
27455714Skris	case 1:
27555714Skris	    if (!mbr) mbr = bootalloc("mbr", &mbr_size);
27655714Skris	    *bootCode = mbr;
27755714Skris	    *bootCodeSize = mbr_size;
27855714Skris	    return;
27955714Skris	case 2:
28055714Skris	default:
28155714Skris	    break;
28255714Skris	}
28355714Skris    }
28455714Skris#endif
28555714Skris    *bootCode = NULL;
28655714Skris    *bootCodeSize = 0;
28755714Skris}
28855714Skris#endif
28955714Skris#endif /* WITH_SLICES */
29055714Skris
29155714Skrisint
29255714SkrisdiskGetSelectCount(Device ***devs)
29355714Skris{
29455714Skris    int i, cnt, enabled;
29555714Skris    char *cp;
29655714Skris    Device **dp;
29755714Skris
29855714Skris    cp = variable_get(VAR_DISK);
29955714Skris    dp = *devs = deviceFind(cp, DEVICE_TYPE_DISK);
30055714Skris    cnt = deviceCount(dp);
30155714Skris    if (!cnt)
30255714Skris	return -1;
30355714Skris    for (i = 0, enabled = 0; i < cnt; i++) {
30455714Skris	if (dp[i]->enabled)
30555714Skris	    ++enabled;
30655714Skris    }
30755714Skris    return enabled;
30855714Skris}
30955714Skris
31055714Skris#ifdef WITH_SLICES
31155714Skrisvoid
312diskPartition(Device *dev)
313{
314    char *cp, *p;
315    int rv, key = 0;
316    int i;
317    Boolean chunking;
318    char *msg = NULL;
319#ifdef PC98
320    u_char *bootipl;
321    size_t bootipl_size;
322    u_char *bootmenu;
323    size_t bootmenu_size;
324#else
325    u_char *mbrContents;
326    size_t mbrSize;
327#endif
328    WINDOW *w = savescr();
329    Disk *d = (Disk *)dev->private;
330    int size_unit;
331
332    size_unit = UNIT_BLOCKS;
333    chunking = TRUE;
334    keypad(stdscr, TRUE);
335
336    /* Flush both the dialog and curses library views of the screen
337       since we don't always know who called us */
338    dialog_clear_norefresh(), clear();
339    current_chunk = 0;
340
341    /* Set up the chunk array */
342    record_chunks(d);
343
344    while (chunking) {
345	char *val, geometry[80];
346
347	/* Now print our overall state */
348	if (d)
349	    print_chunks(d, size_unit);
350	print_command_summary();
351	if (msg) {
352	    attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
353	    beep();
354	    msg = NULL;
355	}
356	else {
357	    move(23, 0);
358	    clrtoeol();
359	}
360
361	/* Get command character */
362	key = getch();
363	switch (toupper(key)) {
364	case '\014':	/* ^L (redraw) */
365	    clear();
366	    msg = NULL;
367	    break;
368
369	case '\020':	/* ^P */
370	case KEY_UP:
371	case '-':
372	    if (current_chunk != 0)
373		--current_chunk;
374	    break;
375
376	case '\016':	/* ^N */
377	case KEY_DOWN:
378	case '+':
379	case '\r':
380	case '\n':
381	    if (chunk_info[current_chunk + 1])
382		++current_chunk;
383	    break;
384
385	case KEY_HOME:
386	    current_chunk = 0;
387	    break;
388
389	case KEY_END:
390	    while (chunk_info[current_chunk + 1])
391		++current_chunk;
392	    break;
393
394	case KEY_F(1):
395	case '?':
396	    systemDisplayHelp("slice");
397	    clear();
398	    break;
399
400	case 'A':
401	case 'F':	/* Undocumented magic Dangerously Dedicated mode */
402#if !defined(__i386__) && !defined(__amd64__)
403	    rv = 1;
404#else	    /* The rest is only relevant on x86 */
405	    cp = variable_get(VAR_DEDICATE_DISK);
406	    if (cp && !strcasecmp(cp, "always"))
407		rv = 1;
408	    else if (toupper(key) == 'A')
409		rv = 0;
410	    else {
411		rv = msgYesNo("Do you want to do this with a true partition entry\n"
412			      "so as to remain cooperative with any future possible\n"
413			      "operating systems on the drive(s)?\n"
414			      "(See also the section about ``dangerously dedicated''\n"
415			      "disks in the FreeBSD FAQ.)");
416		if (rv == -1)
417		    rv = 0;
418	    }
419#endif
420	    All_FreeBSD(d, rv);
421	    variable_set2(DISK_PARTITIONED, "yes", 0);
422	    record_chunks(d);
423	    clear();
424	    break;
425
426	case 'C':
427	    if (chunk_info[current_chunk]->type != unused)
428		msg = "Slice in use, delete it first or move to an unused one.";
429	    else {
430		char *val, tmp[20], name[16], *cp;
431		daddr_t size;
432		int subtype;
433		chunk_e partitiontype;
434#ifdef PC98
435		snprintf(name, sizeof (name), "%s", "FreeBSD");
436		val = msgGetInput(name,
437			"Please specify the name for new FreeBSD slice.");
438		if (val)
439			strncpy(name, val, sizeof (name));
440#else
441		name[0] = '\0';
442#endif
443		snprintf(tmp, 20, "%jd", (intmax_t)chunk_info[current_chunk]->size);
444		val = msgGetInput(tmp, "Please specify the size for new FreeBSD slice in blocks\n"
445				  "or append a trailing `M' for megabytes (e.g. 20M).");
446		if (val && (size = strtoimax(val, &cp, 0)) > 0) {
447		    if (*cp && toupper(*cp) == 'M')
448			size *= ONE_MEG;
449		    else if (*cp && toupper(*cp) == 'G')
450			size *= ONE_GIG;
451		    sprintf(tmp, "%d", SUBTYPE_FREEBSD);
452		    val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
453			"Pressing Enter will choose the default, a native FreeBSD\n"
454			"slice (type %u).  "
455			OTHER_SLICE_VALUES
456			NON_FREEBSD_NOTE, SUBTYPE_FREEBSD);
457		    if (val && (subtype = strtol(val, NULL, 0)) > 0) {
458			if (subtype == SUBTYPE_FREEBSD)
459			    partitiontype = freebsd;
460			else if (subtype == SUBTYPE_FAT)
461			    partitiontype = fat;
462			else if (subtype == SUBTYPE_EFI)
463			    partitiontype = efi;
464			else
465#ifdef PC98
466			    partitiontype = pc98;
467#else
468			    partitiontype = mbr;
469#endif
470			Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
471				     (chunk_info[current_chunk]->flags & CHUNK_ALIGN), name);
472			variable_set2(DISK_PARTITIONED, "yes", 0);
473			record_chunks(d);
474		    }
475		}
476		clear();
477	    }
478	    break;
479
480	case KEY_DC:
481	case 'D':
482	    if (chunk_info[current_chunk]->type == unused)
483		msg = "Slice is already unused!";
484	    else {
485		Delete_Chunk(d, chunk_info[current_chunk]);
486		variable_set2(DISK_PARTITIONED, "yes", 0);
487		record_chunks(d);
488	    }
489	    break;
490
491	case 'T':
492	    if (chunk_info[current_chunk]->type == unused)
493		msg = "Slice is currently unused (use create instead)";
494	    else {
495		char *val, tmp[20];
496		int subtype;
497		chunk_e partitiontype;
498
499		sprintf(tmp, "%d", chunk_info[current_chunk]->subtype);
500		val = msgGetInput(tmp, "New partition type:\n\n"
501		    "Pressing Enter will use the current type. To choose a native\n"
502		    "FreeBSD slice enter %u.  "
503		    OTHER_SLICE_VALUES
504		    NON_FREEBSD_NOTE, SUBTYPE_FREEBSD);
505		if (val && (subtype = strtol(val, NULL, 0)) > 0) {
506		    if (subtype == SUBTYPE_FREEBSD)
507			partitiontype = freebsd;
508		    else if (subtype == SUBTYPE_FAT)
509			partitiontype = fat;
510		    else if (subtype == SUBTYPE_EFI)
511			partitiontype = efi;
512		    else
513#ifdef PC98
514			partitiontype = pc98;
515#else
516			partitiontype = mbr;
517#endif
518		    chunk_info[current_chunk]->type = partitiontype;
519		    chunk_info[current_chunk]->subtype = subtype;
520		}
521	    }
522	    break;
523
524	case 'G':
525	    snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
526	    val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
527			      "Don't forget to use the two slash (/) separator characters!\n"
528			      "It's not possible to parse the field without them.");
529	    if (val) {
530		long nc, nh, ns;
531		nc = strtol(val, &val, 0);
532		nh = strtol(val + 1, &val, 0);
533		ns = strtol(val + 1, 0, 0);
534		Set_Bios_Geom(d, nc, nh, ns);
535	    }
536	    clear();
537	    break;
538
539	case 'S':
540	    /* Clear active states so we won't have two */
541	    for (i = 0; (chunk_info[i] != NULL) && (i < CHUNK_INFO_ENTRIES); i++)
542		chunk_info[i]->flags &= !CHUNK_ACTIVE;
543
544	    /* Set Bootable */
545	    chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
546	    break;
547
548	case 'U':
549	    if (!variable_cmp(DISK_LABELLED, "written")) {
550		msgConfirm("You've already written this information out - you\n"
551			   "can't undo it.");
552	    }
553	    else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
554		char cp[BUFSIZ];
555
556		sstrncpy(cp, d->name, sizeof cp);
557		Free_Disk(dev->private);
558		d = Open_Disk(cp);
559		if (!d)
560		    msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp);
561		dev->private = d;
562		variable_unset(DISK_PARTITIONED);
563		variable_unset(DISK_LABELLED);
564		if (d)
565		    record_chunks(d);
566	    }
567	    clear();
568	    break;
569
570	case 'W':
571	    if (!msgNoYes("WARNING:  This should only be used when modifying an EXISTING\n"
572			       "installation.  If you are installing FreeBSD for the first time\n"
573			       "then you should simply type Q when you're finished here and your\n"
574			       "changes will be committed in one batch automatically at the end of\n"
575			       "these questions.  If you're adding a disk, you should NOT write\n"
576			       "from this screen, you should do it from the label editor.\n\n"
577			       "Are you absolutely sure you want to do this now?")) {
578		variable_set2(DISK_PARTITIONED, "yes", 0);
579
580#ifdef PC98
581		/*
582		 * Don't trash the IPL if the first (and therefore only) chunk
583		 * is marked for a truly dedicated disk (i.e., the disklabel
584		 * starts at sector 0), even in cases where the user has
585		 * requested a FreeBSD Boot Manager -- both would be fatal in
586		 * this case.
587		 */
588		/*
589		 * Don't offer to update the IPL on this disk if the first
590		 * "real" chunk looks like a FreeBSD "all disk" partition,
591		 * or the disk is entirely FreeBSD.
592		 */
593		if ((d->chunks->part->type != freebsd) ||
594		    (d->chunks->part->offset > 1))
595		    getBootMgr(d->name, &bootipl, &bootipl_size,
596			       &bootmenu, &bootmenu_size);
597		else {
598		    bootipl = NULL;
599		    bootipl_size = 0;
600		    bootmenu = NULL;
601		    bootmenu_size = 0;
602		}
603		Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
604#else
605		/*
606		 * Don't trash the MBR if the first (and therefore only) chunk
607		 * is marked for a truly dedicated disk (i.e., the disklabel
608		 * starts at sector 0), even in cases where the user has
609		 * requested booteasy or a "standard" MBR -- both would be
610		 * fatal in this case.
611		 */
612		/*
613		 * Don't offer to update the MBR on this disk if the first
614		 * "real" chunk looks like a FreeBSD "all disk" partition,
615		 * or the disk is entirely FreeBSD.
616		 */
617		if ((d->chunks->part->type != freebsd) ||
618		    (d->chunks->part->offset > 1))
619		    getBootMgr(d->name, &mbrContents, &mbrSize);
620		else {
621		    mbrContents = NULL;
622		    mbrSize = 0;
623		}
624		Set_Boot_Mgr(d, mbrContents, mbrSize);
625#endif
626
627		if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
628		    msgConfirm("Disk partition write returned an error status!");
629		else
630		    msgConfirm("Wrote FDISK partition information out successfully.");
631	    }
632	    clear();
633	    break;
634
635	case '|':
636	    if (!msgNoYes("Are you SURE you want to go into Wizard mode?\n"
637			  "No seat belts whatsoever are provided!")) {
638		clear();
639		refresh();
640		slice_wizard(d);
641		variable_set2(DISK_PARTITIONED, "yes", 0);
642		record_chunks(d);
643	    }
644	    else
645		msg = "Wise choice!";
646	    clear();
647	    break;
648
649	case '\033':	/* ESC */
650	case 'Q':
651	    chunking = FALSE;
652#ifdef PC98
653	    /*
654	     * Don't trash the IPL if the first (and therefore only) chunk
655	     * is marked for a truly dedicated disk (i.e., the disklabel
656	     * starts at sector 0), even in cases where the user has requested
657	     * a FreeBSD Boot Manager -- both would be fatal in this case.
658	     */
659	    /*
660	     * Don't offer to update the IPL on this disk if the first "real"
661	     * chunk looks like a FreeBSD "all disk" partition, or the disk is
662	     * entirely FreeBSD.
663	     */
664	    if ((d->chunks->part->type != freebsd) ||
665		(d->chunks->part->offset > 1)) {
666		if (variable_cmp(DISK_PARTITIONED, "written")) {
667		    getBootMgr(d->name, &bootipl, &bootipl_size,
668			&bootmenu, &bootmenu_size);
669		    if (bootipl != NULL && bootmenu != NULL)
670			Set_Boot_Mgr(d, bootipl, bootipl_size,
671			    bootmenu, bootmenu_size);
672		}
673	    }
674#else
675	    /*
676	     * Don't trash the MBR if the first (and therefore only) chunk
677	     * is marked for a truly dedicated disk (i.e., the disklabel
678	     * starts at sector 0), even in cases where the user has requested
679	     * booteasy or a "standard" MBR -- both would be fatal in this case.
680	     */
681	    /*
682	     * Don't offer to update the MBR on this disk if the first "real"
683	     * chunk looks like a FreeBSD "all disk" partition, or the disk is
684	     * entirely FreeBSD.
685	     */
686	    if ((d->chunks->part->type != freebsd) ||
687		(d->chunks->part->offset > 1)) {
688		if (variable_cmp(DISK_PARTITIONED, "written")) {
689		    getBootMgr(d->name, &mbrContents, &mbrSize);
690		    if (mbrContents != NULL)
691			Set_Boot_Mgr(d, mbrContents, mbrSize);
692		}
693	    }
694#endif
695	    break;
696
697	case 'Z':
698	    size_unit = (size_unit + 1) % UNIT_SIZE;
699	    break;
700
701	default:
702	    beep();
703	    msg = "Type F1 or ? for help";
704	    break;
705	}
706    }
707    p = CheckRules(d);
708    if (p) {
709	char buf[FILENAME_MAX];
710
711        use_helpline("Press F1 to read more about disk slices.");
712	use_helpfile(systemHelpFile("partition", buf));
713	if (!variable_get(VAR_NO_WARN))
714	    dialog_mesgbox("Disk slicing warning:", p, -1, -1);
715	free(p);
716    }
717    restorescr(w);
718}
719#endif /* WITH_SLICES */
720
721static u_char *
722bootalloc(char *name, size_t *size)
723{
724    char buf[FILENAME_MAX];
725    struct stat sb;
726
727    snprintf(buf, sizeof buf, "/boot/%s", name);
728    if (stat(buf, &sb) != -1) {
729	int fd;
730
731	fd = open(buf, O_RDONLY);
732	if (fd != -1) {
733	    u_char *cp;
734
735	    cp = malloc(sb.st_size);
736	    if (read(fd, cp, sb.st_size) != sb.st_size) {
737		free(cp);
738		close(fd);
739		msgDebug("bootalloc: couldn't read %ld bytes from %s\n", (long)sb.st_size, buf);
740		return NULL;
741	    }
742	    close(fd);
743	    if (size != NULL)
744		*size = sb.st_size;
745	    return cp;
746	}
747	msgDebug("bootalloc: couldn't open %s\n", buf);
748    }
749    else
750	msgDebug("bootalloc: can't stat %s\n", buf);
751    return NULL;
752}
753
754#ifdef WITH_SLICES
755static int
756partitionHook(dialogMenuItem *selected)
757{
758    Device **devs = NULL;
759
760    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
761    if (!devs) {
762	msgConfirm("Unable to find disk %s!", selected->prompt);
763	return DITEM_FAILURE;
764    }
765    /* Toggle enabled status? */
766    if (!devs[0]->enabled) {
767	devs[0]->enabled = TRUE;
768	diskPartition(devs[0]);
769    }
770    else
771	devs[0]->enabled = FALSE;
772    return DITEM_SUCCESS;
773}
774
775static int
776partitionCheck(dialogMenuItem *selected)
777{
778    Device **devs = NULL;
779
780    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
781    if (!devs || devs[0]->enabled == FALSE)
782	return FALSE;
783    return TRUE;
784}
785
786int
787diskPartitionEditor(dialogMenuItem *self)
788{
789    DMenu *menu;
790    Device **devs;
791    int i, cnt, devcnt;
792
793    cnt = diskGetSelectCount(&devs);
794    devcnt = deviceCount(devs);
795    if (cnt == -1) {
796	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
797		   "properly probed at boot time.  See the Hardware Guide on the\n"
798		   "Documentation menu for clues on diagnosing this type of problem.");
799	return DITEM_FAILURE;
800    }
801    else if (cnt) {
802	/* Some are already selected */
803	for (i = 0; i < devcnt; i++) {
804	    if (devs[i]->enabled) {
805		if (variable_get(VAR_NONINTERACTIVE) &&
806		  !variable_get(VAR_DISKINTERACTIVE))
807		    diskPartitionNonInteractive(devs[i]);
808		else
809		    diskPartition(devs[i]);
810	    }
811	}
812    }
813    else {
814	/* No disks are selected, fall-back case now */
815	if (devcnt == 1) {
816	    devs[0]->enabled = TRUE;
817	    if (variable_get(VAR_NONINTERACTIVE) &&
818	      !variable_get(VAR_DISKINTERACTIVE))
819		diskPartitionNonInteractive(devs[0]);
820	    else
821		diskPartition(devs[0]);
822	    return DITEM_SUCCESS;
823	}
824	else {
825	    menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
826	    if (!menu) {
827		msgConfirm("No devices suitable for installation found!\n\n"
828			   "Please verify that your disk controller (and attached drives)\n"
829			   "were detected properly.  This can be done by pressing the\n"
830			   "[Scroll Lock] key and using the Arrow keys to move back to\n"
831			   "the boot messages.  Press [Scroll Lock] again to return.");
832		return DITEM_FAILURE;
833	    }
834	    else {
835		i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
836		free(menu);
837	    }
838	    return i;
839	}
840    }
841    return DITEM_SUCCESS;
842}
843#endif /* WITH_SLICES */
844
845int
846diskPartitionWrite(dialogMenuItem *self)
847{
848    Device **devs;
849    int i;
850
851    if (!variable_cmp(DISK_PARTITIONED, "written"))
852	return DITEM_SUCCESS;
853
854    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
855    if (!devs) {
856	msgConfirm("Unable to find any disks to write to??");
857	return DITEM_FAILURE;
858    }
859    if (isDebug())
860	msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
861    for (i = 0; devs[i]; i++) {
862	Disk *d = (Disk *)devs[i]->private;
863	static u_char *boot1;
864#if defined(__i386__) || defined(__amd64__)
865	static u_char *boot2;
866#endif
867
868	if (!devs[i]->enabled)
869	    continue;
870
871#if defined(__i386__) || defined(__amd64__)
872	if (!boot1) boot1 = bootalloc("boot1", NULL);
873	if (!boot2) boot2 = bootalloc("boot2", NULL);
874	Set_Boot_Blocks(d, boot1, boot2);
875#elif !defined(__ia64__)
876	if (!boot1) boot1 = bootalloc("boot1", NULL);
877	Set_Boot_Blocks(d, boot1, NULL);
878#endif
879
880	msgNotify("Writing partition information to drive %s", d->name);
881	if (!Fake && Write_Disk(d)) {
882	    msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
883	    return DITEM_FAILURE;
884	}
885    }
886    /* Now it's not "yes", but "written" */
887    variable_set2(DISK_PARTITIONED, "written", 0);
888    return DITEM_SUCCESS | DITEM_RESTORE;
889}
890
891#ifdef WITH_SLICES
892/* Partition a disk based wholly on which variables are set */
893static void
894diskPartitionNonInteractive(Device *dev)
895{
896    char *cp;
897    int i, all_disk = 0;
898    daddr_t sz;
899#ifdef PC98
900    u_char *bootipl;
901    size_t bootipl_size;
902    u_char *bootmenu;
903    size_t bootmenu_size;
904#else
905    u_char *mbrContents;
906    size_t mbrSize;
907#endif
908    Disk *d = (Disk *)dev->private;
909
910    record_chunks(d);
911    cp = variable_get(VAR_GEOMETRY);
912    if (cp) {
913	msgDebug("Setting geometry from script to: %s\n", cp);
914	d->bios_cyl = strtol(cp, &cp, 0);
915	d->bios_hd = strtol(cp + 1, &cp, 0);
916	d->bios_sect = strtol(cp + 1, 0, 0);
917    }
918
919#ifdef PC98
920    if (d->bios_cyl >= 65536 || d->bios_hd > 256 || d->bios_sect >= 256) {
921#else
922    if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64) {
923#endif
924	msgDebug("Warning:  A geometry of %lu/%lu/%lu for %s is incorrect.\n",
925	    d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
926	Sanitize_Bios_Geom(d);
927	msgDebug("Sanitized geometry for %s is %lu/%lu/%lu.\n",
928	    d->name, d->bios_cyl, d->bios_hd, d->bios_sect);
929    }
930
931    cp = variable_get(VAR_PARTITION);
932    if (cp) {
933	if (!strcmp(cp, "free")) {
934	    /* Do free disk space case */
935	    for (i = 0; chunk_info[i]; i++) {
936		/* If a chunk is at least 10MB in size, use it. */
937		if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
938		    Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
939				 freebsd, 3,
940				 (chunk_info[i]->flags & CHUNK_ALIGN),
941				 "FreeBSD");
942		    variable_set2(DISK_PARTITIONED, "yes", 0);
943		    break;
944		}
945	    }
946	    if (!chunk_info[i]) {
947		msgConfirm("Unable to find any free space on this disk!");
948		return;
949	    }
950	}
951	else if (!strcmp(cp, "all")) {
952	    /* Do all disk space case */
953	    msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
954
955	    All_FreeBSD(d, FALSE);
956	}
957	else if (!strcmp(cp, "exclusive")) {
958	    /* Do really-all-the-disk-space case */
959	    msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
960
961	    All_FreeBSD(d, all_disk = TRUE);
962	}
963	else if ((sz = strtoimax(cp, &cp, 0))) {
964	    /* Look for sz bytes free */
965	    if (*cp && toupper(*cp) == 'M')
966		sz *= ONE_MEG;
967	    else if (*cp && toupper(*cp) == 'G')
968		sz *= ONE_GIG;
969	    for (i = 0; chunk_info[i]; i++) {
970		/* If a chunk is at least sz MB, use it. */
971		if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
972		    Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
973				 (chunk_info[i]->flags & CHUNK_ALIGN),
974				 "FreeBSD");
975		    variable_set2(DISK_PARTITIONED, "yes", 0);
976		    break;
977		}
978	    }
979	    if (!chunk_info[i]) {
980		    msgConfirm("Unable to find %jd free blocks on this disk!",
981			(intmax_t)sz);
982		return;
983	    }
984	}
985	else if (!strcmp(cp, "existing")) {
986	    /* Do existing FreeBSD case */
987	    for (i = 0; chunk_info[i]; i++) {
988		if (chunk_info[i]->type == freebsd)
989		    break;
990	    }
991	    if (!chunk_info[i]) {
992		msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
993		return;
994	    }
995	}
996	else {
997	    msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
998	    return;
999	}
1000	if (!all_disk) {
1001#ifdef PC98
1002	    getBootMgr(d->name, &bootipl, &bootipl_size,
1003		       &bootmenu, &bootmenu_size);
1004	    Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
1005#else
1006	    getBootMgr(d->name, &mbrContents, &mbrSize);
1007	    Set_Boot_Mgr(d, mbrContents, mbrSize);
1008#endif
1009	}
1010	variable_set2(DISK_PARTITIONED, "yes", 0);
1011    }
1012}
1013#endif /* WITH_SLICES */
1014