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