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