disks.c revision 61277
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 61277 2000-06-05 13:17:23Z nyan $
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#ifndef PC98
167static u_char *
168getBootMgr(char *dname)
169{
170#ifndef __alpha__	/* only meaningful on x86 */
171    extern u_char mbr[], boot0[];
172    char str[80];
173    char *cp;
174    int i = 0;
175
176    cp = variable_get(VAR_BOOTMGR);
177    if (!cp) {
178	/* Figure out what kind of MBR the user wants */
179	sprintf(str, "Install Boot Manager for drive %s?", dname);
180	MenuMBRType.title = str;
181	i = dmenuOpenSimple(&MenuMBRType, FALSE);
182    }
183    else {
184	if (!strncmp(cp, "boot", 4))
185	    BootMgr = 0;
186	else if (!strcmp(cp, "standard"))
187	    BootMgr = 1;
188	else
189	    BootMgr = 2;
190    }
191    if (cp || i) {
192	switch (BootMgr) {
193	case 0:
194	    return boot0;
195
196	case 1:
197	    return mbr;
198
199	case 2:
200	default:
201	    break;
202	}
203    }
204#endif
205    return NULL;
206}
207#endif
208
209int
210diskGetSelectCount(Device ***devs)
211{
212    int i, cnt, enabled;
213    char *cp;
214    Device **dp;
215
216    cp = variable_get(VAR_DISK);
217    dp = *devs = deviceFind(cp, DEVICE_TYPE_DISK);
218    cnt = deviceCount(dp);
219    if (!cnt)
220	return -1;
221    for (i = 0, enabled = 0; i < cnt; i++) {
222	if (dp[i]->enabled)
223	    ++enabled;
224    }
225    return enabled;
226}
227
228void
229diskPartition(Device *dev)
230{
231    char *cp, *p;
232    int rv, key = 0;
233    Boolean chunking;
234    char *msg = NULL;
235#ifndef PC98
236    u_char *mbrContents;
237#endif
238    WINDOW *w = savescr();
239    Disk *d = (Disk *)dev->private;
240    int size_unit;
241
242    size_unit = UNIT_BLOCKS;
243    chunking = TRUE;
244    keypad(stdscr, TRUE);
245
246    /* Flush both the dialog and curses library views of the screen
247       since we don't always know who called us */
248    dialog_clear_norefresh(), clear();
249    current_chunk = 0;
250
251    /* Set up the chunk array */
252    record_chunks(d);
253
254    while (chunking) {
255	char *val, geometry[80];
256
257	/* Now print our overall state */
258	if (d)
259	    print_chunks(d, size_unit);
260	print_command_summary();
261	if (msg) {
262	    attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
263	    beep();
264	    msg = NULL;
265	}
266	else {
267	    move(23, 0);
268	    clrtoeol();
269	}
270
271	/* Get command character */
272	key = getch();
273	switch (toupper(key)) {
274	case '\014':	/* ^L (redraw) */
275	    clear();
276	    msg = NULL;
277	    break;
278
279	case '\020':	/* ^P */
280	case KEY_UP:
281	case '-':
282	    if (current_chunk != 0)
283		--current_chunk;
284	    break;
285
286	case '\016':	/* ^N */
287	case KEY_DOWN:
288	case '+':
289	case '\r':
290	case '\n':
291	    if (chunk_info[current_chunk + 1])
292		++current_chunk;
293	    break;
294
295	case KEY_HOME:
296	    current_chunk = 0;
297	    break;
298
299	case KEY_END:
300	    while (chunk_info[current_chunk + 1])
301		++current_chunk;
302	    break;
303
304	case KEY_F(1):
305	case '?':
306	    systemDisplayHelp("slice");
307	    clear();
308	    break;
309
310	case 'A':
311#ifdef __alpha__
312	    rv = 1;
313#else	    /* The rest is only relevant on x86 */
314	    cp = variable_get(VAR_DEDICATE_DISK);
315	    if (cp && !strcasecmp(cp, "always"))
316		rv = 1;
317	    else {
318		rv = msgYesNo("Do you want to do this with a true partition entry\n"
319			      "so as to remain cooperative with any future possible\n"
320			      "operating systems on the drive(s)?\n"
321			      "(See also the section about ``dangerously dedicated''\n"
322			      "disks in the FreeBSD FAQ.)");
323		if (rv == -1)
324		    rv = 0;
325	    }
326#endif
327	    All_FreeBSD(d, rv);
328	    variable_set2(DISK_PARTITIONED, "yes", 0);
329	    record_chunks(d);
330	    clear();
331	    break;
332
333	case 'C':
334	    if (chunk_info[current_chunk]->type != unused)
335		msg = "Slice in use, delete it first or move to an unused one.";
336	    else {
337		char *val, tmp[20], *cp;
338		int size;
339#ifdef PC98
340		char name[16];
341
342		snprintf(name, 16, "%s", "FreeBSD");
343		val = msgGetInput(name,
344			"Please specify the name for new FreeBSD slice.");
345		if (val)
346			strncpy(name, val, 16);
347#else
348		int subtype;
349		chunk_e partitiontype;
350#endif
351		snprintf(tmp, 20, "%lu", chunk_info[current_chunk]->size);
352		val = msgGetInput(tmp, "Please specify the size for new FreeBSD slice in blocks\n"
353				  "or append a trailing `M' for megabytes (e.g. 20M).");
354		if (val && (size = strtol(val, &cp, 0)) > 0) {
355		    if (*cp && toupper(*cp) == 'M')
356			size *= ONE_MEG;
357		    else if (*cp && toupper(*cp) == 'G')
358			size *= ONE_GIG;
359#ifdef PC98
360		    Create_Chunk(d, chunk_info[current_chunk]->offset, size,
361			freebsd, 3,
362			(chunk_info[current_chunk]->flags & CHUNK_ALIGN),
363			name);
364		    variable_set2(DISK_PARTITIONED, "yes", 0);
365		    record_chunks(d);
366#else
367		    sprintf(tmp, "%d", SUBTYPE_FREEBSD);
368		    val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
369				      "Pressing Enter will choose the default, a native FreeBSD\n"
370				      "slice (type 165).  You can choose other types, 6 for a\n"
371				      "DOS partition or 131 for a Linux partition, for example.\n\n"
372				      "Note:  If you choose a non-FreeBSD partition type, it will not\n"
373				      "be formatted or otherwise prepared, it will simply reserve space\n"
374				      "for you to use another tool, such as DOS FORMAT, to later format\n"
375				      "and use the partition.");
376		    if (val && (subtype = strtol(val, NULL, 0)) > 0) {
377			if (subtype == SUBTYPE_FREEBSD)
378			    partitiontype = freebsd;
379			else if (subtype == SUBTYPE_FAT)
380			    partitiontype = fat;
381			else
382			    partitiontype = unknown;
383#ifdef __alpha__
384			if (partitiontype == freebsd && size == chunk_info[current_chunk]->size)
385			    All_FreeBSD(d, 1);
386			else
387#endif
388			Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
389				     (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
390			variable_set2(DISK_PARTITIONED, "yes", 0);
391			record_chunks(d);
392		    }
393#endif /* PC98 */
394		}
395		clear();
396	    }
397	    break;
398
399	case KEY_DC:
400	case 'D':
401	    if (chunk_info[current_chunk]->type == unused)
402		msg = "Slice is already unused!";
403	    else {
404		Delete_Chunk(d, chunk_info[current_chunk]);
405		variable_set2(DISK_PARTITIONED, "yes", 0);
406		record_chunks(d);
407	    }
408	    break;
409
410	case 'T':
411	    if (chunk_info[current_chunk]->type == unused)
412		msg = "Slice is currently unused (use create instead)";
413	    else {
414		char *val, tmp[20];
415		int subtype;
416		chunk_e partitiontype;
417
418		sprintf(tmp, "%d", SUBTYPE_FREEBSD);
419#ifdef PC98
420		val = msgGetInput(tmp, "New partition type:\n\n"
421				  "Pressing Enter will choose the default, a native FreeBSD\n"
422				  "slice (type 50324).  Other popular values are 37218 for\n"
423				  "DOS FAT partition.\n\n"
424				  "Note:  If you choose a non-FreeBSD partition type, it will not\n"
425				  "be formatted or otherwise prepared, it will simply reserve space\n"
426				  "for you to use another tool, such as DOS format, to later format\n"
427				  "and actually use the partition.");
428#else
429		val = msgGetInput(tmp, "New partition type:\n\n"
430				  "Pressing Enter will choose the default, a native FreeBSD\n"
431				  "slice (type 165).  Other popular values are 6 for\n"
432				  "DOS FAT partition, 131 for a Linux ext2fs partition or\n"
433				  "130 for a Linux swap partition.\n\n"
434				  "Note:  If you choose a non-FreeBSD partition type, it will not\n"
435				  "be formatted or otherwise prepared, it will simply reserve space\n"
436				  "for you to use another tool, such as DOS format, to later format\n"
437				  "and actually use the partition.");
438#endif /* PC98 */
439		if (val && (subtype = strtol(val, NULL, 0)) > 0) {
440		    if (subtype == SUBTYPE_FREEBSD)
441			partitiontype = freebsd;
442		    else if (subtype == SUBTYPE_FAT)
443			partitiontype = fat;
444		    else
445			partitiontype = unknown;
446		    chunk_info[current_chunk]->type = partitiontype;
447		    chunk_info[current_chunk]->subtype = subtype;
448		}
449	    }
450	    break;
451
452	case 'G':
453	    snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
454	    val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
455			      "Don't forget to use the two slash (/) separator characters!\n"
456			      "It's not possible to parse the field without them.");
457	    if (val) {
458		long nc, nh, ns;
459		nc = strtol(val, &val, 0);
460		nh = strtol(val + 1, &val, 0);
461		ns = strtol(val + 1, 0, 0);
462		Set_Bios_Geom(d, nc, nh, ns);
463	    }
464	    clear();
465	    break;
466
467	case 'S':
468	    /* Set Bootable */
469	    chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
470	    break;
471
472	case 'U':
473	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
474		msgConfirm("You've already written this information out - you\n"
475			   "can't undo it.");
476	    }
477	    else if (!msgYesNo("Are you SURE you want to Undo everything?")) {
478		char cp[BUFSIZ];
479
480		sstrncpy(cp, d->name, sizeof cp);
481		Free_Disk(dev->private);
482		d = Open_Disk(cp);
483		if (!d)
484		    msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp);
485		dev->private = d;
486		variable_unset(DISK_PARTITIONED);
487		variable_unset(DISK_LABELLED);
488		if (d)
489		    record_chunks(d);
490	    }
491	    clear();
492	    break;
493
494	case 'W':
495	    if (!msgYesNo("WARNING:  This should only be used when modifying an EXISTING\n"
496			       "installation.  If you are installing FreeBSD for the first time\n"
497			       "then you should simply type Q when you're finished here and your\n"
498			       "changes will be committed in one batch automatically at the end of\n"
499			       "these questions.  If you're adding a disk, you should NOT write\n"
500			       "from this screen, you should do it from the label editor.\n\n"
501			       "Are you absolutely sure you want to do this now?")) {
502		variable_set2(DISK_PARTITIONED, "yes", 0);
503
504#ifndef PC98
505		/* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
506		 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
507		 * booteasy or a "standard" MBR -- both would be fatal in this case.
508		 */
509		/*
510		 * Don't offer to update the MBR on this disk if the first "real" chunk looks like
511		 * a FreeBSD "all disk" partition, or the disk is entirely FreeBSD.
512		 */
513		if (((d->chunks->part->type != freebsd) || (d->chunks->part->offset > 1)))
514		    mbrContents = getBootMgr(d->name);
515		else
516		    mbrContents = NULL;
517		Set_Boot_Mgr(d, mbrContents);
518#endif /* !PC98 */
519
520		if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
521		    msgConfirm("Disk partition write returned an error status!");
522		else
523		    msgConfirm("Wrote FDISK partition information out successfully.");
524	    }
525	    clear();
526	    break;
527
528	case '|':
529	    if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n"
530			  "No seat belts whatsoever are provided!")) {
531		clear();
532		refresh();
533		slice_wizard(d);
534		variable_set2(DISK_PARTITIONED, "yes", 0);
535		record_chunks(d);
536	    }
537	    else
538		msg = "Wise choice!";
539	    clear();
540	    break;
541
542	case '\033':	/* ESC */
543	case 'Q':
544	    chunking = FALSE;
545#ifndef PC98
546	    /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
547	     * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
548	     * booteasy or a "standard" MBR -- both would be fatal in this case.
549	     */
550#if 0
551	    if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
552		&& (mbrContents = getBootMgr(d->name)) != NULL)
553		Set_Boot_Mgr(d, mbrContents);
554#else
555	    /*
556	     * Don't offer to update the MBR on this disk if the first "real" chunk looks like
557	     * a FreeBSD "all disk" partition, or the disk is entirely FreeBSD.
558	     */
559	    if (((d->chunks->part->type != freebsd) || (d->chunks->part->offset > 1)) &&
560		(mbrContents = getBootMgr(d->name)) != NULL)
561		Set_Boot_Mgr(d, mbrContents);
562#endif
563#endif /* !PC98 */
564	    break;
565
566	case 'Z':
567	    size_unit = (size_unit + 1) % UNIT_SIZE;
568	    break;
569
570	default:
571	    beep();
572	    msg = "Type F1 or ? for help";
573	    break;
574	}
575    }
576    p = CheckRules(d);
577    if (p) {
578	char buf[FILENAME_MAX];
579
580        use_helpline("Press F1 to read more about disk slices.");
581	use_helpfile(systemHelpFile("partition", buf));
582	if (!variable_get(VAR_NO_WARN))
583	    dialog_mesgbox("Disk slicing warning:", p, -1, -1);
584	free(p);
585    }
586    restorescr(w);
587}
588
589static u_char *
590bootalloc(char *name)
591{
592    char buf[FILENAME_MAX];
593    struct stat sb;
594
595    snprintf(buf, sizeof buf, "/boot/%s", name);
596    if (stat(buf, &sb) != -1) {
597	int fd;
598
599	fd = open(buf, O_RDONLY);
600	if (fd != -1) {
601	    u_char *cp;
602
603	    cp = malloc(sb.st_size);
604	    if (read(fd, cp, sb.st_size) != sb.st_size) {
605		free(cp);
606		close(fd);
607		msgDebug("bootalloc: couldn't read %d bytes from %s\n", sb.st_size, buf);
608		return NULL;
609	    }
610	    close(fd);
611	    return cp;
612	}
613	msgDebug("bootalloc: couldn't open %s\n", buf);
614    }
615    else
616	msgDebug("bootalloc: can't stat %s\n", buf);
617    return NULL;
618}
619
620static int
621partitionHook(dialogMenuItem *selected)
622{
623    Device **devs = NULL;
624
625    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
626    if (!devs) {
627	msgConfirm("Unable to find disk %s!", selected->prompt);
628	return DITEM_FAILURE;
629    }
630    /* Toggle enabled status? */
631    if (!devs[0]->enabled) {
632	devs[0]->enabled = TRUE;
633	diskPartition(devs[0]);
634    }
635    else
636	devs[0]->enabled = FALSE;
637    return DITEM_SUCCESS;
638}
639
640static int
641partitionCheck(dialogMenuItem *selected)
642{
643    Device **devs = NULL;
644
645    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
646    if (!devs || devs[0]->enabled == FALSE)
647	return FALSE;
648    return TRUE;
649}
650
651int
652diskPartitionEditor(dialogMenuItem *self)
653{
654    DMenu *menu;
655    Device **devs;
656    int i, cnt, devcnt;
657
658    cnt = diskGetSelectCount(&devs);
659    devcnt = deviceCount(devs);
660    if (cnt == -1) {
661	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
662		   "properly probed at boot time.  See the Hardware Guide on the\n"
663		   "Documentation menu for clues on diagnosing this type of problem.");
664	return DITEM_FAILURE;
665    }
666    else if (cnt) {
667	/* Some are already selected */
668	for (i = 0; i < devcnt; i++) {
669	    if (devs[i]->enabled) {
670		if (variable_get(VAR_NONINTERACTIVE))
671		    diskPartitionNonInteractive(devs[i]);
672		else
673		    diskPartition(devs[i]);
674	    }
675	}
676    }
677    else {
678	/* No disks are selected, fall-back case now */
679	if (devcnt == 1) {
680	    devs[0]->enabled = TRUE;
681	    if (variable_get(VAR_NONINTERACTIVE))
682		diskPartitionNonInteractive(devs[0]);
683	    else
684		diskPartition(devs[0]);
685	    return DITEM_SUCCESS;
686	}
687	else {
688	    menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
689	    if (!menu) {
690		msgConfirm("No devices suitable for installation found!\n\n"
691			   "Please verify that your disk controller (and attached drives)\n"
692			   "were detected properly.  This can be done by pressing the\n"
693			   "[Scroll Lock] key and using the Arrow keys to move back to\n"
694			   "the boot messages.  Press [Scroll Lock] again to return.");
695		return DITEM_FAILURE;
696	    }
697	    else {
698		i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
699		free(menu);
700	    }
701	    return i;
702	}
703    }
704    return DITEM_SUCCESS;
705}
706
707int
708diskPartitionWrite(dialogMenuItem *self)
709{
710    Device **devs;
711    int i;
712    char *cp;
713
714    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
715    if (!devs) {
716	msgConfirm("Unable to find any disks to write to??");
717	return DITEM_FAILURE;
718    }
719    if (isDebug())
720	msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
721    cp = variable_get(DISK_PARTITIONED);
722    if (cp && !strcmp(cp, "written"))
723	return DITEM_SUCCESS;
724
725    for (i = 0; devs[i]; i++) {
726	Disk *d = (Disk *)devs[i]->private;
727	static u_char *boot1;
728#ifndef __alpha__
729	static u_char *boot2;
730#endif
731
732	if (!devs[i]->enabled)
733	    continue;
734
735#ifdef __alpha__
736	if (!boot1) boot1 = bootalloc("boot1");
737	Set_Boot_Blocks(d, boot1, NULL);
738#else
739	if (!boot1) boot1 = bootalloc("boot1");
740	if (!boot2) boot2 = bootalloc("boot2");
741	Set_Boot_Blocks(d, boot1, boot2);
742#endif
743
744	msgNotify("Writing partition information to drive %s", d->name);
745	if (!Fake && Write_Disk(d)) {
746	    msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
747	    return DITEM_FAILURE;
748	}
749
750	/* If we've been through here before, we don't need to do the rest */
751	if (cp && !strcmp(cp, "written"))
752	    return DITEM_SUCCESS;
753    }
754    /* Now it's not "yes", but "written" */
755    variable_set2(DISK_PARTITIONED, "written", 0);
756    return DITEM_SUCCESS | DITEM_RESTORE;
757}
758
759/* Partition a disk based wholly on which variables are set */
760static void
761diskPartitionNonInteractive(Device *dev)
762{
763    char *cp;
764    int i, sz, all_disk = 0;
765#ifndef PC98
766    u_char *mbrContents;
767#endif
768    Disk *d = (Disk *)dev->private;
769
770    record_chunks(d);
771    cp = variable_get(VAR_GEOMETRY);
772    if (cp) {
773	msgDebug("Setting geometry from script to: %s\n", cp);
774	d->bios_cyl = strtol(cp, &cp, 0);
775	d->bios_hd = strtol(cp + 1, &cp, 0);
776	d->bios_sect = strtol(cp + 1, 0, 0);
777    }
778
779    cp = variable_get(VAR_PARTITION);
780    if (cp) {
781	if (!strcmp(cp, "free")) {
782	    /* Do free disk space case */
783	    for (i = 0; chunk_info[i]; i++) {
784		/* If a chunk is at least 10MB in size, use it. */
785		if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
786#ifdef PC98
787		    Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
788				 freebsd, 3,
789				 (chunk_info[i]->flags & CHUNK_ALIGN),
790				 "FreeBSD");
791#else
792		    Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
793				 freebsd, 3,
794				 (chunk_info[i]->flags & CHUNK_ALIGN));
795#endif
796		    variable_set2(DISK_PARTITIONED, "yes", 0);
797		    break;
798		}
799	    }
800	    if (!chunk_info[i]) {
801		msgConfirm("Unable to find any free space on this disk!");
802		return;
803	    }
804	}
805	else if (!strcmp(cp, "all")) {
806	    /* Do all disk space case */
807	    msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
808
809	    All_FreeBSD(d, FALSE);
810	}
811	else if (!strcmp(cp, "exclusive")) {
812	    /* Do really-all-the-disk-space case */
813	    msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
814
815	    All_FreeBSD(d, all_disk = TRUE);
816	}
817	else if ((sz = strtol(cp, &cp, 0))) {
818	    /* Look for sz bytes free */
819	    if (*cp && toupper(*cp) == 'M')
820		sz *= ONE_MEG;
821	    else if (*cp && toupper(*cp) == 'G')
822		sz *= ONE_GIG;
823	    for (i = 0; chunk_info[i]; i++) {
824		/* If a chunk is at least sz MB, use it. */
825		if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
826#ifdef PC98
827		    Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
828				 (chunk_info[i]->flags & CHUNK_ALIGN),
829				 "FreeBSD");
830#else
831		    Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
832				 (chunk_info[i]->flags & CHUNK_ALIGN));
833#endif
834		    variable_set2(DISK_PARTITIONED, "yes", 0);
835		    break;
836		}
837	    }
838	    if (!chunk_info[i]) {
839		msgConfirm("Unable to find %d free blocks on this disk!", sz);
840		return;
841	    }
842	}
843	else if (!strcmp(cp, "existing")) {
844	    /* Do existing FreeBSD case */
845	    for (i = 0; chunk_info[i]; i++) {
846		if (chunk_info[i]->type == freebsd)
847		    break;
848	    }
849	    if (!chunk_info[i]) {
850		msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
851		return;
852	    }
853	}
854	else {
855	    msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
856	    return;
857	}
858#ifndef PC98
859	if (!all_disk) {
860	    mbrContents = getBootMgr(d->name);
861	    Set_Boot_Mgr(d, mbrContents);
862	}
863#endif
864	variable_set2(DISK_PARTITIONED, "yes", 0);
865    }
866}
867