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