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