disks.c revision 71051
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 71051 2001-01-15 05:29:53Z peter $
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 = 0;
373#endif
374	    All_FreeBSD(d, rv);
375	    variable_set2(DISK_PARTITIONED, "yes", 0);
376	    record_chunks(d);
377	    clear();
378	    break;
379
380	case 'C':
381	    if (chunk_info[current_chunk]->type != unused)
382		msg = "Slice in use, delete it first or move to an unused one.";
383	    else {
384		char *val, tmp[20], *cp;
385		int size;
386#ifdef PC98
387		char name[16];
388
389		snprintf(name, 16, "%s", "FreeBSD");
390		val = msgGetInput(name,
391			"Please specify the name for new FreeBSD slice.");
392		if (val)
393			strncpy(name, val, 16);
394#else
395		int subtype;
396		chunk_e partitiontype;
397#endif
398		snprintf(tmp, 20, "%lu", chunk_info[current_chunk]->size);
399		val = msgGetInput(tmp, "Please specify the size for new FreeBSD slice in blocks\n"
400				  "or append a trailing `M' for megabytes (e.g. 20M).");
401		if (val && (size = strtol(val, &cp, 0)) > 0) {
402		    if (*cp && toupper(*cp) == 'M')
403			size *= ONE_MEG;
404		    else if (*cp && toupper(*cp) == 'G')
405			size *= ONE_GIG;
406#ifdef PC98
407		    Create_Chunk(d, chunk_info[current_chunk]->offset, size,
408			freebsd, 3,
409			(chunk_info[current_chunk]->flags & CHUNK_ALIGN),
410			name);
411		    variable_set2(DISK_PARTITIONED, "yes", 0);
412		    record_chunks(d);
413#else
414		    sprintf(tmp, "%d", SUBTYPE_FREEBSD);
415		    val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
416				      "Pressing Enter will choose the default, a native FreeBSD\n"
417				      "slice (type 165).  You can choose other types, 6 for a\n"
418				      "DOS partition or 131 for a Linux partition, for example.\n\n"
419				      "Note:  If you choose a non-FreeBSD partition type, it will not\n"
420				      "be formatted or otherwise prepared, it will simply reserve space\n"
421				      "for you to use another tool, such as DOS FORMAT, to later format\n"
422				      "and use the partition.");
423		    if (val && (subtype = strtol(val, NULL, 0)) > 0) {
424			if (subtype == SUBTYPE_FREEBSD)
425			    partitiontype = freebsd;
426			else if (subtype == SUBTYPE_FAT)
427			    partitiontype = fat;
428			else
429			    partitiontype = unknown;
430#ifdef __alpha__
431			if (partitiontype == freebsd && size == chunk_info[current_chunk]->size)
432			    All_FreeBSD(d, 1);
433			else
434#endif
435			Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
436				     (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
437			variable_set2(DISK_PARTITIONED, "yes", 0);
438			record_chunks(d);
439		    }
440#endif /* PC98 */
441		}
442		clear();
443	    }
444	    break;
445
446	case KEY_DC:
447	case 'D':
448	    if (chunk_info[current_chunk]->type == unused)
449		msg = "Slice is already unused!";
450	    else {
451		Delete_Chunk(d, chunk_info[current_chunk]);
452		variable_set2(DISK_PARTITIONED, "yes", 0);
453		record_chunks(d);
454	    }
455	    break;
456
457	case 'T':
458	    if (chunk_info[current_chunk]->type == unused)
459		msg = "Slice is currently unused (use create instead)";
460	    else {
461		char *val, tmp[20];
462		int subtype;
463		chunk_e partitiontype;
464
465		sprintf(tmp, "%d", SUBTYPE_FREEBSD);
466#ifdef PC98
467		val = msgGetInput(tmp, "New partition type:\n\n"
468				  "Pressing Enter will choose the default, a native FreeBSD\n"
469				  "slice (type 50324).  Other popular values are 37218 for\n"
470				  "DOS FAT partition.\n\n"
471				  "Note:  If you choose a non-FreeBSD partition type, it will not\n"
472				  "be formatted or otherwise prepared, it will simply reserve space\n"
473				  "for you to use another tool, such as DOS format, to later format\n"
474				  "and actually use the partition.");
475#else
476		val = msgGetInput(tmp, "New partition type:\n\n"
477				  "Pressing Enter will choose the default, a native FreeBSD\n"
478				  "slice (type 165).  Other popular values are 6 for\n"
479				  "DOS FAT partition, 131 for a Linux ext2fs partition or\n"
480				  "130 for a Linux swap partition.\n\n"
481				  "Note:  If you choose a non-FreeBSD partition type, it will not\n"
482				  "be formatted or otherwise prepared, it will simply reserve space\n"
483				  "for you to use another tool, such as DOS format, to later format\n"
484				  "and actually use the partition.");
485#endif /* PC98 */
486		if (val && (subtype = strtol(val, NULL, 0)) > 0) {
487		    if (subtype == SUBTYPE_FREEBSD)
488			partitiontype = freebsd;
489		    else if (subtype == SUBTYPE_FAT)
490			partitiontype = fat;
491		    else
492			partitiontype = unknown;
493		    chunk_info[current_chunk]->type = partitiontype;
494		    chunk_info[current_chunk]->subtype = subtype;
495		}
496	    }
497	    break;
498
499	case 'G':
500	    snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
501	    val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
502			      "Don't forget to use the two slash (/) separator characters!\n"
503			      "It's not possible to parse the field without them.");
504	    if (val) {
505		long nc, nh, ns;
506		nc = strtol(val, &val, 0);
507		nh = strtol(val + 1, &val, 0);
508		ns = strtol(val + 1, 0, 0);
509		Set_Bios_Geom(d, nc, nh, ns);
510	    }
511	    clear();
512	    break;
513
514	case 'S':
515	    /* Set Bootable */
516	    chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
517	    break;
518
519	case 'U':
520	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
521		msgConfirm("You've already written this information out - you\n"
522			   "can't undo it.");
523	    }
524	    else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
525		char cp[BUFSIZ];
526
527		sstrncpy(cp, d->name, sizeof cp);
528		Free_Disk(dev->private);
529		d = Open_Disk(cp);
530		if (!d)
531		    msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp);
532		dev->private = d;
533		variable_unset(DISK_PARTITIONED);
534		variable_unset(DISK_LABELLED);
535		if (d)
536		    record_chunks(d);
537	    }
538	    clear();
539	    break;
540
541	case 'W':
542	    if (!msgNoYes("WARNING:  This should only be used when modifying an EXISTING\n"
543			       "installation.  If you are installing FreeBSD for the first time\n"
544			       "then you should simply type Q when you're finished here and your\n"
545			       "changes will be committed in one batch automatically at the end of\n"
546			       "these questions.  If you're adding a disk, you should NOT write\n"
547			       "from this screen, you should do it from the label editor.\n\n"
548			       "Are you absolutely sure you want to do this now?")) {
549		variable_set2(DISK_PARTITIONED, "yes", 0);
550
551		/*
552		 * Don't trash the MBR if the first (and therefore only) chunk
553		 * is marked for a truly dedicated disk (i.e., the disklabel
554		 * starts at sector 0), even in cases where the user has
555		 * requested booteasy or a "standard" MBR -- both would be
556		 * fatal in this case.
557		 */
558		/*
559		 * Don't offer to update the MBR on this disk if the first
560		 * "real" chunk looks like a FreeBSD "all disk" partition,
561		 * or the disk is entirely FreeBSD.
562		 */
563#ifdef PC98
564		if ((d->chunks->part->type != freebsd) ||
565		    (d->chunks->part->offset > 1))
566		    getBootMgr(d->name, &bootipl, &bootipl_size,
567			       &bootmenu, &bootmenu_size);
568		else {
569		    bootipl = NULL;
570		    bootipl_size = 0;
571		    bootmenu = NULL;
572		    bootmenu_size = 0;
573		}
574		Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
575#else
576		if ((d->chunks->part->type != freebsd) ||
577		    (d->chunks->part->offset > 1))
578		    getBootMgr(d->name, &mbrContents, &mbrSize);
579		else {
580		    mbrContents = NULL;
581		    mbrSize = 0;
582		}
583		Set_Boot_Mgr(d, mbrContents, mbrSize);
584#endif
585
586		if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
587		    msgConfirm("Disk partition write returned an error status!");
588		else
589		    msgConfirm("Wrote FDISK partition information out successfully.");
590	    }
591	    clear();
592	    break;
593
594	case '|':
595	    if (!msgNoYes("Are you SURE you want to go into Wizard mode?\n"
596			  "No seat belts whatsoever are provided!")) {
597		clear();
598		refresh();
599		slice_wizard(d);
600		variable_set2(DISK_PARTITIONED, "yes", 0);
601		record_chunks(d);
602	    }
603	    else
604		msg = "Wise choice!";
605	    clear();
606	    break;
607
608	case '\033':	/* ESC */
609	case 'Q':
610	    chunking = FALSE;
611	    /*
612	     * Don't trash the MBR if the first (and therefore only) chunk
613	     * is marked for a truly dedicated disk (i.e., the disklabel
614	     * starts at sector 0), even in cases where the user has requested
615	     * booteasy or a "standard" MBR -- both would be fatal in this case.
616	     */
617#if 0
618	    if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL) {
619#ifdef PC98
620		getBootMgr(d->name, &bootipl, &bootipl_size,
621			   &bootmenu, &bootmenu_size);
622		if (bootipl != NULL && bootmenu != NULL)
623		    Set_Boot_Mgr(d, bootipl, bootipl_size,
624				 bootmenu, bootmenu_size);
625#else
626		getBootMgr(d->name, &mbrContents, &mbrSize);
627		if (mbrContents != NULL)
628		    Set_Boot_Mgr(d, mbrContents, mbrSize);
629#endif
630	    }
631#else
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#ifdef PC98
640		getBootMgr(d->name, &bootipl, &bootipl_size,
641			   &bootmenu, &bootmenu_size);
642		if (bootipl != NULL && bootmenu != NULL)
643		    Set_Boot_Mgr(d, bootipl, bootipl_size,
644				 bootmenu, bootmenu_size);
645#else
646		getBootMgr(d->name, &mbrContents, &mbrSize);
647		if (mbrContents != NULL)
648		    Set_Boot_Mgr(d, mbrContents, mbrSize);
649#endif
650	    }
651#endif
652	    break;
653
654	case 'Z':
655	    size_unit = (size_unit + 1) % UNIT_SIZE;
656	    break;
657
658	default:
659	    beep();
660	    msg = "Type F1 or ? for help";
661	    break;
662	}
663    }
664    p = CheckRules(d);
665    if (p) {
666	char buf[FILENAME_MAX];
667
668        use_helpline("Press F1 to read more about disk slices.");
669	use_helpfile(systemHelpFile("partition", buf));
670	if (!variable_get(VAR_NO_WARN))
671	    dialog_mesgbox("Disk slicing warning:", p, -1, -1);
672	free(p);
673    }
674    restorescr(w);
675}
676
677static u_char *
678bootalloc(char *name)
679{
680    char buf[FILENAME_MAX];
681    struct stat sb;
682
683    snprintf(buf, sizeof buf, "/boot/%s", name);
684    if (stat(buf, &sb) != -1) {
685	int fd;
686
687	fd = open(buf, O_RDONLY);
688	if (fd != -1) {
689	    u_char *cp;
690
691	    cp = malloc(sb.st_size);
692	    if (read(fd, cp, sb.st_size) != sb.st_size) {
693		free(cp);
694		close(fd);
695		msgDebug("bootalloc: couldn't read %d bytes from %s\n", sb.st_size, buf);
696		return NULL;
697	    }
698	    close(fd);
699	    return cp;
700	}
701	msgDebug("bootalloc: couldn't open %s\n", buf);
702    }
703    else
704	msgDebug("bootalloc: can't stat %s\n", buf);
705    return NULL;
706}
707
708static int
709partitionHook(dialogMenuItem *selected)
710{
711    Device **devs = NULL;
712
713    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
714    if (!devs) {
715	msgConfirm("Unable to find disk %s!", selected->prompt);
716	return DITEM_FAILURE;
717    }
718    /* Toggle enabled status? */
719    if (!devs[0]->enabled) {
720	devs[0]->enabled = TRUE;
721	diskPartition(devs[0]);
722    }
723    else
724	devs[0]->enabled = FALSE;
725    return DITEM_SUCCESS;
726}
727
728static int
729partitionCheck(dialogMenuItem *selected)
730{
731    Device **devs = NULL;
732
733    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
734    if (!devs || devs[0]->enabled == FALSE)
735	return FALSE;
736    return TRUE;
737}
738
739int
740diskPartitionEditor(dialogMenuItem *self)
741{
742    DMenu *menu;
743    Device **devs;
744    int i, cnt, devcnt;
745
746    cnt = diskGetSelectCount(&devs);
747    devcnt = deviceCount(devs);
748    if (cnt == -1) {
749	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
750		   "properly probed at boot time.  See the Hardware Guide on the\n"
751		   "Documentation menu for clues on diagnosing this type of problem.");
752	return DITEM_FAILURE;
753    }
754    else if (cnt) {
755	/* Some are already selected */
756	for (i = 0; i < devcnt; i++) {
757	    if (devs[i]->enabled) {
758		if (variable_get(VAR_NONINTERACTIVE))
759		    diskPartitionNonInteractive(devs[i]);
760		else
761		    diskPartition(devs[i]);
762	    }
763	}
764    }
765    else {
766	/* No disks are selected, fall-back case now */
767	if (devcnt == 1) {
768	    devs[0]->enabled = TRUE;
769	    if (variable_get(VAR_NONINTERACTIVE))
770		diskPartitionNonInteractive(devs[0]);
771	    else
772		diskPartition(devs[0]);
773	    return DITEM_SUCCESS;
774	}
775	else {
776	    menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
777	    if (!menu) {
778		msgConfirm("No devices suitable for installation found!\n\n"
779			   "Please verify that your disk controller (and attached drives)\n"
780			   "were detected properly.  This can be done by pressing the\n"
781			   "[Scroll Lock] key and using the Arrow keys to move back to\n"
782			   "the boot messages.  Press [Scroll Lock] again to return.");
783		return DITEM_FAILURE;
784	    }
785	    else {
786		i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
787		free(menu);
788	    }
789	    return i;
790	}
791    }
792    return DITEM_SUCCESS;
793}
794
795int
796diskPartitionWrite(dialogMenuItem *self)
797{
798    Device **devs;
799    int i;
800    char *cp;
801
802    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
803    if (!devs) {
804	msgConfirm("Unable to find any disks to write to??");
805	return DITEM_FAILURE;
806    }
807    if (isDebug())
808	msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
809    cp = variable_get(DISK_PARTITIONED);
810    if (cp && !strcmp(cp, "written"))
811	return DITEM_SUCCESS;
812
813    for (i = 0; devs[i]; i++) {
814	Disk *d = (Disk *)devs[i]->private;
815	static u_char *boot1;
816#ifndef __alpha__
817	static u_char *boot2;
818#endif
819
820	if (!devs[i]->enabled)
821	    continue;
822
823#ifdef __alpha__
824	if (!boot1) boot1 = bootalloc("boot1");
825	Set_Boot_Blocks(d, boot1, NULL);
826#else
827	if (!boot1) boot1 = bootalloc("boot1");
828	if (!boot2) boot2 = bootalloc("boot2");
829	Set_Boot_Blocks(d, boot1, boot2);
830#endif
831
832	msgNotify("Writing partition information to drive %s", d->name);
833	if (!Fake && Write_Disk(d)) {
834	    msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
835	    return DITEM_FAILURE;
836	}
837
838	/* If we've been through here before, we don't need to do the rest */
839	if (cp && !strcmp(cp, "written"))
840	    return DITEM_SUCCESS;
841    }
842    /* Now it's not "yes", but "written" */
843    variable_set2(DISK_PARTITIONED, "written", 0);
844    return DITEM_SUCCESS | DITEM_RESTORE;
845}
846
847/* Partition a disk based wholly on which variables are set */
848static void
849diskPartitionNonInteractive(Device *dev)
850{
851    char *cp;
852    int i, sz, all_disk = 0;
853#ifdef PC98
854    u_char *bootipl;
855    size_t bootipl_size;
856    u_char *bootmenu;
857    size_t bootmenu_size;
858#else
859    u_char *mbrContents;
860    size_t mbrSize;
861#endif
862    Disk *d = (Disk *)dev->private;
863
864    record_chunks(d);
865    cp = variable_get(VAR_GEOMETRY);
866    if (cp) {
867	msgDebug("Setting geometry from script to: %s\n", cp);
868	d->bios_cyl = strtol(cp, &cp, 0);
869	d->bios_hd = strtol(cp + 1, &cp, 0);
870	d->bios_sect = strtol(cp + 1, 0, 0);
871    }
872
873    cp = variable_get(VAR_PARTITION);
874    if (cp) {
875	if (!strcmp(cp, "free")) {
876	    /* Do free disk space case */
877	    for (i = 0; chunk_info[i]; i++) {
878		/* If a chunk is at least 10MB in size, use it. */
879		if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
880#ifdef PC98
881		    Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
882				 freebsd, 3,
883				 (chunk_info[i]->flags & CHUNK_ALIGN),
884				 "FreeBSD");
885#else
886		    Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
887				 freebsd, 3,
888				 (chunk_info[i]->flags & CHUNK_ALIGN));
889#endif
890		    variable_set2(DISK_PARTITIONED, "yes", 0);
891		    break;
892		}
893	    }
894	    if (!chunk_info[i]) {
895		msgConfirm("Unable to find any free space on this disk!");
896		return;
897	    }
898	}
899	else if (!strcmp(cp, "all")) {
900	    /* Do all disk space case */
901	    msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
902
903	    All_FreeBSD(d, FALSE);
904	}
905	else if (!strcmp(cp, "exclusive")) {
906	    /* Do really-all-the-disk-space case */
907	    msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
908
909	    All_FreeBSD(d, all_disk = TRUE);
910	}
911	else if ((sz = strtol(cp, &cp, 0))) {
912	    /* Look for sz bytes free */
913	    if (*cp && toupper(*cp) == 'M')
914		sz *= ONE_MEG;
915	    else if (*cp && toupper(*cp) == 'G')
916		sz *= ONE_GIG;
917	    for (i = 0; chunk_info[i]; i++) {
918		/* If a chunk is at least sz MB, use it. */
919		if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
920#ifdef PC98
921		    Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
922				 (chunk_info[i]->flags & CHUNK_ALIGN),
923				 "FreeBSD");
924#else
925		    Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
926				 (chunk_info[i]->flags & CHUNK_ALIGN));
927#endif
928		    variable_set2(DISK_PARTITIONED, "yes", 0);
929		    break;
930		}
931	    }
932	    if (!chunk_info[i]) {
933		msgConfirm("Unable to find %d free blocks on this disk!", sz);
934		return;
935	    }
936	}
937	else if (!strcmp(cp, "existing")) {
938	    /* Do existing FreeBSD case */
939	    for (i = 0; chunk_info[i]; i++) {
940		if (chunk_info[i]->type == freebsd)
941		    break;
942	    }
943	    if (!chunk_info[i]) {
944		msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
945		return;
946	    }
947	}
948	else {
949	    msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
950	    return;
951	}
952	if (!all_disk) {
953#ifdef PC98
954	    getBootMgr(d->name, &bootipl, &bootipl_size,
955		       &bootmenu, &bootmenu_size);
956	    Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
957#else
958	    getBootMgr(d->name, &mbrContents, &mbrSize);
959	    Set_Boot_Mgr(d, mbrContents, mbrSize);
960#endif
961	}
962	variable_set2(DISK_PARTITIONED, "yes", 0);
963    }
964}
965