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