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