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