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