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