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