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