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