label.c revision 23729
1/*
2 * The new sysinstall program.
3 *
4 * This is probably the last program in the `sysinstall' line - the next
5 * generation being essentially a complete rewrite.
6 *
7 * $Id: label.c,v 1.69 1997/03/08 16:17:49 jkh Exp $
8 *
9 * Copyright (c) 1995
10 *	Jordan Hubbard.  All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer,
17 *    verbatim and that no modifications are made prior to this
18 *    point in the file.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in the
21 *    documentation and/or other materials provided with the distribution.
22 *
23 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 */
36
37#include "sysinstall.h"
38#include <ctype.h>
39#include <sys/disklabel.h>
40#include <sys/param.h>
41#include <sys/sysctl.h>
42
43/*
44 * Everything to do with editing the contents of disk labels.
45 */
46
47/* A nice message we use a lot in the disklabel editor */
48#define MSG_NOT_APPLICABLE	"That option is not applicable here"
49
50/* Where to start printing the freebsd slices */
51#define CHUNK_SLICE_START_ROW		2
52#define CHUNK_PART_START_ROW		11
53
54/* The smallest filesystem we're willing to create */
55#define FS_MIN_SIZE			ONE_MEG
56
57/* The smallest root filesystem we're willing to create */
58#define ROOT_MIN_SIZE			20
59
60/* The smallest swap partition we want to create by default */
61#define SWAP_MIN_SIZE			16
62
63/* The smallest /usr partition we're willing to create by default */
64#define USR_MIN_SIZE			80
65
66/* The smallest /var partition we're willing to create by default */
67#define VAR_MIN_SIZE			30
68
69/* The bottom-most row we're allowed to scribble on */
70#define CHUNK_ROW_MAX		16
71
72
73/* All the chunks currently displayed on the screen */
74static struct {
75    struct chunk *c;
76    PartType type;
77} label_chunk_info[MAX_CHUNKS + 1];
78static int here;
79
80static int ChunkPartStartRow;
81static WINDOW *ChunkWin;
82
83static int diskLabel(char *str);
84
85int
86diskLabelEditor(dialogMenuItem *self)
87{
88    Device **devs;
89    int i, cnt, enabled;
90    char *cp;
91
92    cp = variable_get(VAR_DISK);
93    devs = deviceFind(cp, DEVICE_TYPE_DISK);
94    cnt = deviceCount(devs);
95    if (!cnt) {
96	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
97		   "properly probed at boot time.  See the Hardware Guide on the\n"
98		   "Documentation menu for clues on diagnosing this type of problem.");
99	return DITEM_FAILURE;
100    }
101    for (i = 0, enabled = 0; i < cnt; i++) {
102	if (devs[i]->enabled)
103	    ++enabled;
104    }
105    if (!enabled) {
106	msgConfirm("No disks have been selected.  Please visit the Partition\n"
107		   "editor first to specify which disks you wish to operate on.");
108	return DITEM_FAILURE;
109    }
110    i = diskLabel(devs[0]->name);
111    if (DITEM_STATUS(i) != DITEM_FAILURE) {
112	char *cp;
113
114	if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
115	    variable_set2(DISK_LABELLED, "yes");
116    }
117    return i;
118}
119
120int
121diskLabelCommit(dialogMenuItem *self)
122{
123    char *cp;
124    int i;
125
126    /* Already done? */
127    if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes"))
128	i = DITEM_SUCCESS;
129    else if (!cp) {
130	msgConfirm("You must assign disk labels before this option can be used.");
131	i = DITEM_FAILURE;
132    }
133    /* The routine will guard against redundant writes, just as this one does */
134    else if (DITEM_STATUS(diskPartitionWrite(self)) != DITEM_SUCCESS)
135	i = DITEM_FAILURE;
136    else if (DITEM_STATUS(installFilesystems(self)) != DITEM_SUCCESS)
137	i = DITEM_FAILURE;
138    else {
139	msgInfo("All filesystem information written successfully.");
140	variable_set2(DISK_LABELLED, "written");
141	i = DITEM_SUCCESS;
142    }
143    return i;
144}
145
146/* See if we're already using a desired partition name */
147static Boolean
148check_conflict(char *name)
149{
150    int i;
151
152    for (i = 0; label_chunk_info[i].c; i++)
153	if ((label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT)
154	    && label_chunk_info[i].c->private_data
155	    && !strcmp(((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint, name))
156	    return TRUE;
157    return FALSE;
158}
159
160/* How much space is in this FreeBSD slice? */
161static int
162space_free(struct chunk *c)
163{
164    struct chunk *c1;
165    int sz = c->size;
166
167    for (c1 = c->part; c1; c1 = c1->next) {
168	if (c1->type != unused)
169	    sz -= c1->size;
170    }
171    if (sz < 0)
172	msgFatal("Partitions are larger than actual chunk??");
173    return sz;
174}
175
176/* Snapshot the current situation into the displayed chunks structure */
177static void
178record_label_chunks(Device **devs)
179{
180    int i, j, p;
181    struct chunk *c1, *c2;
182    Disk *d;
183
184    ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3;
185    j = p = 0;
186    /* First buzz through and pick up the FreeBSD slices */
187    for (i = 0; devs[i]; i++) {
188	if (!devs[i]->enabled)
189	    continue;
190	d = (Disk *)devs[i]->private;
191	if (!d->chunks)
192	    msgFatal("No chunk list found for %s!", d->name);
193
194	/* Put the slice entries first */
195	for (c1 = d->chunks->part; c1; c1 = c1->next) {
196	    if (c1->type == freebsd) {
197		label_chunk_info[j].type = PART_SLICE;
198		label_chunk_info[j].c = c1;
199		++j;
200		++ChunkPartStartRow;
201	    }
202	}
203    }
204
205    /* Now run through again and get the FreeBSD partition entries */
206    for (i = 0; devs[i]; i++) {
207	if (!devs[i]->enabled)
208	    continue;
209	d = (Disk *)devs[i]->private;
210	/* Then buzz through and pick up the partitions */
211	for (c1 = d->chunks->part; c1; c1 = c1->next) {
212	    if (c1->type == freebsd) {
213		for (c2 = c1->part; c2; c2 = c2->next) {
214		    if (c2->type == part) {
215			if (c2->subtype == FS_SWAP)
216			    label_chunk_info[j].type = PART_SWAP;
217			else
218			    label_chunk_info[j].type = PART_FILESYSTEM;
219			label_chunk_info[j].c = c2;
220			++j;
221		    }
222		}
223	    }
224	    else if (c1->type == fat) {
225		label_chunk_info[j].type = PART_FAT;
226		label_chunk_info[j].c = c1;
227		++j;
228	    }
229	}
230    }
231    label_chunk_info[j].c = NULL;
232    if (here >= j)
233	here = j  ? j - 1 : 0;
234    if (ChunkWin) {
235	wclear(ChunkWin);
236	wrefresh(ChunkWin);
237    }
238    else
239	ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
240}
241
242/* A new partition entry */
243static PartInfo *
244new_part(char *mpoint, Boolean newfs, u_long size)
245{
246    PartInfo *ret;
247
248    if (!mpoint)
249	mpoint = "/change_me";
250
251    ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
252    sstrncpy(ret->mountpoint, mpoint, FILENAME_MAX);
253    strcpy(ret->newfs_cmd, "newfs -b 8192 -f 1024");
254    ret->newfs = newfs;
255    if (!size)
256	    return ret;
257    return ret;
258}
259
260/* Get the mountpoint for a partition and save it away */
261static PartInfo *
262get_mountpoint(struct chunk *old)
263{
264    char *val;
265    PartInfo *tmp;
266
267    if (old && old->private_data)
268	tmp = old->private_data;
269    else
270	tmp = NULL;
271    if (!old) {
272	DialogX = 14;
273	DialogY = 16;
274    }
275    val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
276    DialogX = DialogY = 0;
277    if (!val || !*val) {
278	if (!old)
279	    return NULL;
280	else {
281	    free(old->private_data);
282	    old->private_data = NULL;
283	}
284	return NULL;
285    }
286
287    /* Is it just the same value? */
288    if (tmp && !strcmp(tmp->mountpoint, val))
289	return NULL;
290
291    /* Did we use it already? */
292    if (check_conflict(val)) {
293	msgConfirm("You already have a mount point for %s assigned!", val);
294	return NULL;
295    }
296
297    /* Is it bogus? */
298    if (*val != '/') {
299	msgConfirm("Mount point must start with a / character");
300	return NULL;
301    }
302
303    /* Is it going to be mounted on root? */
304    if (!strcmp(val, "/")) {
305	if (old)
306	    old->flags |= CHUNK_IS_ROOT;
307    }
308    else if (old)
309	old->flags &= ~CHUNK_IS_ROOT;
310
311    safe_free(tmp);
312    tmp = new_part(val, TRUE, 0);
313    if (old) {
314	old->private_data = tmp;
315	old->private_free = safe_free;
316    }
317    return tmp;
318}
319
320/* Get the type of the new partiton */
321static PartType
322get_partition_type(void)
323{
324    char selection[20];
325    int i;
326
327    static unsigned char *fs_types[] = {
328	"FS",
329	"A file system",
330	"Swap",
331	"A swap partition.",
332    };
333    DialogX = 7;
334    DialogY = 8;
335    i = dialog_menu("Please choose a partition type",
336		    "If you want to use this partition for swap space, select Swap.\n"
337		    "If you want to put a filesystem on it, choose FS.",
338		    -1, -1, 2, 2, fs_types, selection, NULL, NULL);
339    DialogX = DialogY = 0;
340    if (!i) {
341	if (!strcmp(selection, "FS"))
342	    return PART_FILESYSTEM;
343	else if (!strcmp(selection, "Swap"))
344	    return PART_SWAP;
345    }
346    return PART_NONE;
347}
348
349/* If the user wants a special newfs command for this, set it */
350static void
351getNewfsCmd(PartInfo *p)
352{
353    char *val;
354
355    val = msgGetInput(p->newfs_cmd,
356		      "Please enter the newfs command and options you'd like to use in\n"
357		      "creating this file system.");
358    if (val)
359	sstrncpy(p->newfs_cmd, val, NEWFS_CMD_MAX);
360}
361
362#define MAX_MOUNT_NAME	12
363
364#define PART_PART_COL	0
365#define PART_MOUNT_COL	8
366#define PART_SIZE_COL	(PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
367#define PART_NEWFS_COL	(PART_SIZE_COL + 7)
368#define PART_OFF	38
369
370/* stick this all up on the screen */
371static void
372print_label_chunks(void)
373{
374    int i, j, srow, prow, pcol;
375    int sz;
376
377    attrset(A_REVERSE);
378    mvaddstr(0, 25, "FreeBSD Disklabel Editor");
379    attrset(A_NORMAL);
380
381    for (i = 0; i < 2; i++) {
382	mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
383	mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
384
385	mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
386	mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
387
388	mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 2, "Size");
389	mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 2, "----");
390
391	mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
392	mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
393    }
394    srow = CHUNK_SLICE_START_ROW;
395    prow = 0;
396    pcol = 0;
397
398    for (i = 0; label_chunk_info[i].c; i++) {
399	/* Is it a slice entry displayed at the top? */
400	if (label_chunk_info[i].type == PART_SLICE) {
401	    sz = space_free(label_chunk_info[i].c);
402	    if (i == here)
403		attrset(ATTR_SELECTED);
404	    mvprintw(srow++, 0, "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
405		     label_chunk_info[i].c->disk->name, label_chunk_info[i].c->name, sz, (sz / ONE_MEG));
406	    attrset(A_NORMAL);
407	    clrtoeol();
408	    move(0, 0);
409	    refresh();
410	}
411	/* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
412	else {
413	    char onestr[PART_OFF], num[10], *mountpoint, *newfs;
414
415	    /*
416	     * We copy this into a blank-padded string so that it looks like
417	     * a solid bar in reverse-video
418	     */
419	    memset(onestr, ' ', PART_OFF - 1);
420	    onestr[PART_OFF - 1] = '\0';
421	    /* Go for two columns if we've written one full columns worth */
422	    if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) {
423		pcol = PART_OFF;
424		prow = 0;
425	    }
426	    memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
427	    /* If it's a filesystem, display the mountpoint */
428	    if (label_chunk_info[i].c->private_data
429		&& (label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT))
430	        mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
431	    else if (label_chunk_info[i].type == PART_SWAP)
432		mountpoint = "swap";
433	    else
434	        mountpoint = "<none>";
435
436	    /* Now display the newfs field */
437	    if (label_chunk_info[i].type == PART_FAT)
438	        newfs = "DOS";
439	    else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM)
440		newfs = ((PartInfo *)label_chunk_info[i].c->private_data)->newfs ? "UFS Y" : "UFS N";
441	    else if (label_chunk_info[i].type == PART_SWAP)
442		newfs = "SWAP";
443	    else
444		newfs = "*";
445	    for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
446		onestr[PART_MOUNT_COL + j] = mountpoint[j];
447	    snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? label_chunk_info[i].c->size / ONE_MEG : 0);
448	    memcpy(onestr + PART_SIZE_COL, num, strlen(num));
449	    memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
450	    onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
451	    if (i == here)
452		wattrset(ChunkWin, ATTR_SELECTED);
453	    mvwaddstr(ChunkWin, prow, pcol, onestr);
454	    wattrset(ChunkWin, A_NORMAL);
455	    wrefresh(ChunkWin);
456	    move(0, 0);
457	    ++prow;
458	}
459    }
460}
461
462static void
463print_command_summary(void)
464{
465    mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
466    mvprintw(18, 0, "C = Create      D = Delete         M = Mount pt.");
467    if (!RunningAsInit)
468	mvprintw(18, 47, "W = Write");
469    mvprintw(19, 0, "N = Newfs Opts  T = Newfs Toggle   U = Undo    Q = Finish");
470    mvprintw(20, 0, "A = Auto Defaults for all!");
471    mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
472    move(0, 0);
473}
474
475static void
476clear_wins(void)
477{
478    clear();
479    wclear(ChunkWin);
480}
481
482static int
483diskLabel(char *str)
484{
485    int sz, key = 0;
486    Boolean labeling;
487    char *msg = NULL;
488    PartInfo *p, *oldp;
489    PartType type;
490    Device **devs;
491
492    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
493    if (!devs) {
494	msgConfirm("No disks found!");
495	return DITEM_FAILURE;
496    }
497
498    labeling = TRUE;
499    keypad(stdscr, TRUE);
500    record_label_chunks(devs);
501
502    clear();
503    while (labeling) {
504	char *cp;
505
506	print_label_chunks();
507	print_command_summary();
508	if (msg) {
509	    attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
510	    clrtoeol();
511	    beep();
512	    msg = NULL;
513	}
514	else {
515	    move(23, 0);
516	    clrtoeol();
517	}
518
519	refresh();
520	key = getch();
521	switch (toupper(key)) {
522	    int i;
523	    static char _msg[40];
524
525	case '\014':	/* ^L */
526	    clear_wins();
527	    break;
528
529	case '\020':	/* ^P */
530	case KEY_UP:
531	case '-':
532	    if (here != 0)
533		--here;
534	    else
535		while (label_chunk_info[here + 1].c)
536		    ++here;
537	    break;
538
539	case '\016':	/* ^N */
540	case KEY_DOWN:
541	case '+':
542	case '\r':
543	case '\n':
544	    if (label_chunk_info[here + 1].c)
545		++here;
546	    else
547		here = 0;
548	    break;
549
550	case KEY_HOME:
551	    here = 0;
552	    break;
553
554	case KEY_END:
555	    while (label_chunk_info[here + 1].c)
556		++here;
557	    break;
558
559	case KEY_F(1):
560	case '?':
561	    systemDisplayHelp("partition");
562	    clear_wins();
563	    break;
564
565	case 'A':
566	    if (label_chunk_info[here].type != PART_SLICE) {
567		msg = "You can only do this in a disk slice (at top of screen)";
568		break;
569	    }
570	    sz = space_free(label_chunk_info[here].c);
571	    if (sz <= FS_MIN_SIZE)
572		msg = "Not enough free space to create a new partition in the slice";
573	    else {
574		struct chunk *tmp;
575		int mib[2];
576		int physmem;
577		size_t size, swsize;
578		char *cp;
579		Chunk *rootdev, *swapdev, *usrdev, *vardev;
580
581		(void)checkLabels(FALSE, &rootdev, &swapdev, &usrdev, &vardev);
582		if (!rootdev) {
583		    cp = variable_get(VAR_ROOT_SIZE);
584		    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
585					    (cp ? atoi(cp) : 32) * ONE_MEG, part, FS_BSDFFS,  CHUNK_IS_ROOT);
586		    if (!tmp) {
587			msgConfirm("Unable to create the root partition. Too big?");
588			clear_wins();
589			break;
590		    }
591		    tmp->private_data = new_part("/", TRUE, tmp->size);
592		    tmp->private_free = safe_free;
593		    record_label_chunks(devs);
594		}
595
596		if (!swapdev) {
597		    cp = variable_get(VAR_SWAP_SIZE);
598		    if (cp)
599			swsize = atoi(cp) * ONE_MEG;
600		    else {
601			mib[0] = CTL_HW;
602			mib[1] = HW_PHYSMEM;
603			size = sizeof physmem;
604			sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
605			swsize = 16 * ONE_MEG + (physmem * 2 / 512);
606		    }
607		    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
608					    swsize, part, FS_SWAP, 0);
609		    if (!tmp) {
610			msgConfirm("Unable to create the swap partition. Too big?");
611			clear_wins();
612			break;
613		    }
614		    tmp->private_data = 0;
615		    tmp->private_free = safe_free;
616		    record_label_chunks(devs);
617		}
618
619		if (!vardev) {
620		    cp = variable_get(VAR_VAR_SIZE);
621		    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
622					    (cp ? atoi(cp) : VAR_MIN_SIZE) * ONE_MEG, part, FS_BSDFFS, 0);
623		    if (!tmp) {
624			msgConfirm("Less than %dMB free for /var - you will need to\n"
625				   "partition your disk manually with a custom install!",
626				   (cp ? atoi(cp) : VAR_MIN_SIZE));
627			clear_wins();
628			break;
629		    }
630		    tmp->private_data = new_part("/var", TRUE, tmp->size);
631		    tmp->private_free = safe_free;
632		    record_label_chunks(devs);
633		}
634
635		if (!usrdev) {
636		    cp = variable_get(VAR_USR_SIZE);
637		    if (cp)
638			sz = atoi(cp) * ONE_MEG;
639		    else
640			sz = space_free(label_chunk_info[here].c);
641		    if (!sz || sz < (USR_MIN_SIZE * ONE_MEG)) {
642			msgConfirm("Less than %dMB free for /usr - you will need to\n"
643				   "partition your disk manually with a custom install!", USR_MIN_SIZE);
644			clear_wins();
645			break;
646		    }
647
648		    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
649					    label_chunk_info[here].c,
650					    sz, part, FS_BSDFFS, 0);
651		    if (!tmp) {
652			msgConfirm("Unable to create the /usr partition.  Not enough space?\n"
653				   "You will need to partition your disk manually with a custom install!");
654			clear_wins();
655			break;
656		    }
657		    tmp->private_data = new_part("/usr", TRUE, tmp->size);
658		    tmp->private_free = safe_free;
659		    record_label_chunks(devs);
660		}
661		/* At this point, we're reasonably "labelled" */
662		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
663		    variable_set2(DISK_LABELLED, "yes");
664	    }
665	    break;
666
667	case 'C':
668	    if (label_chunk_info[here].type != PART_SLICE) {
669		msg = "You can only do this in a master partition (see top of screen)";
670		break;
671	    }
672	    sz = space_free(label_chunk_info[here].c);
673	    if (sz <= FS_MIN_SIZE) {
674		msg = "Not enough space to create an additional FreeBSD partition";
675		break;
676	    }
677	    else {
678		char *val;
679		int size;
680		struct chunk *tmp;
681		char osize[80];
682		u_long flags = 0;
683
684		sprintf(osize, "%d", sz);
685		DialogX = 3;
686		DialogY = 2;
687		val = msgGetInput(osize,
688				  "Please specify the partition size in blocks or append a trailing M for\n"
689				  "megabytes or C for cylinders.  %d blocks (%dMB) are free.",
690				  sz, sz / ONE_MEG);
691		DialogX = DialogY = 0;
692		if (!val || (size = strtol(val, &cp, 0)) <= 0) {
693		    clear_wins();
694		    break;
695		}
696
697		if (*cp) {
698		    if (toupper(*cp) == 'M')
699			size *= ONE_MEG;
700		    else if (toupper(*cp) == 'C')
701			size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
702		}
703		if (size <= FS_MIN_SIZE) {
704		    msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
705		    clear_wins();
706		    break;
707		}
708		type = get_partition_type();
709		if (type == PART_NONE) {
710		    clear_wins();
711		    beep();
712		    break;
713		}
714
715		if (type == PART_FILESYSTEM) {
716		    if ((p = get_mountpoint(NULL)) == NULL) {
717			clear_wins();
718			beep();
719			break;
720		    }
721		    else if (!strcmp(p->mountpoint, "/"))
722			flags |= CHUNK_IS_ROOT;
723		    else
724			flags &= ~CHUNK_IS_ROOT;
725		}
726		else
727		    p = NULL;
728
729		if ((flags & CHUNK_IS_ROOT)) {
730		    if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) {
731			msgConfirm("This region cannot be used for your root partition as the\n"
732				   "FreeBSD boot code cannot deal with a root partition created\n"
733				   "in that location.  Please choose another location or smaller\n"
734				   "size for your root partition and try again!");
735			clear_wins();
736			break;
737		    }
738		    if (size < (ROOT_MIN_SIZE * ONE_MEG)) {
739			msgConfirm("Warning: This is smaller than the recommended size for a\n"
740				   "root partition.  For a variety of reasons, root\n"
741				   "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
742		    }
743		}
744		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
745					label_chunk_info[here].c,
746					size, part,
747					(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
748					flags);
749		if (!tmp) {
750		    msgConfirm("Unable to create the partition. Too big?");
751		    clear_wins();
752		    break;
753		}
754		if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) {
755		    msgConfirm("This region cannot be used for your root partition as it starts\n"
756			       "or extends past the 1024'th cylinder mark and is thus a\n"
757			       "poor location to boot from.  Please choose another\n"
758			       "location (or smaller size) for your root partition and try again!");
759		    Delete_Chunk(label_chunk_info[here].c->disk, tmp);
760		    clear_wins();
761		    break;
762		}
763		if (type != PART_SWAP) {
764		    /* This is needed to tell the newfs -u about the size */
765		    tmp->private_data = new_part(p->mountpoint, p->newfs, tmp->size);
766		    safe_free(p);
767		}
768		else
769		    tmp->private_data = p;
770		tmp->private_free = safe_free;
771		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
772		    variable_set2(DISK_LABELLED, "yes");
773		record_label_chunks(devs);
774		clear_wins();
775	    }
776	    break;
777
778	case KEY_DC:
779	case 'D':	/* delete */
780	    if (label_chunk_info[here].type == PART_SLICE) {
781		msg = MSG_NOT_APPLICABLE;
782		break;
783	    }
784	    else if (label_chunk_info[here].type == PART_FAT) {
785		msg = "Use the Disk Partition Editor to delete DOS partitions";
786		break;
787	    }
788	    Delete_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c);
789	    if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
790		variable_set2(DISK_LABELLED, "yes");
791	    record_label_chunks(devs);
792	    break;
793
794	case 'M':	/* mount */
795	    switch(label_chunk_info[here].type) {
796	    case PART_SLICE:
797		msg = MSG_NOT_APPLICABLE;
798		break;
799
800	    case PART_SWAP:
801		msg = "You don't need to specify a mountpoint for a swap partition.";
802		break;
803
804	    case PART_FAT:
805	    case PART_FILESYSTEM:
806		oldp = label_chunk_info[here].c->private_data;
807		p = get_mountpoint(label_chunk_info[here].c);
808		if (p) {
809		    if (!oldp)
810		    	p->newfs = FALSE;
811		    if (label_chunk_info[here].type == PART_FAT
812			&& (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr")
813			    || !strcmp(p->mountpoint, "/var"))) {
814			msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
815			strcpy(p->mountpoint, "/bogus");
816		    }
817		}
818		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
819		    variable_set2(DISK_LABELLED, "yes");
820		record_label_chunks(devs);
821		clear_wins();
822		break;
823
824	    default:
825		msgFatal("Bogus partition under cursor???");
826		break;
827	    }
828	    break;
829
830	case 'N':	/* Set newfs options */
831	    if (label_chunk_info[here].c->private_data &&
832		((PartInfo *)label_chunk_info[here].c->private_data)->newfs)
833		getNewfsCmd(label_chunk_info[here].c->private_data);
834	    else
835		msg = MSG_NOT_APPLICABLE;
836	    clear_wins();
837	    break;
838
839	case 'T':	/* Toggle newfs state */
840	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
841		PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
842		label_chunk_info[here].c->private_data =
843		    new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
844		safe_free(pi);
845		label_chunk_info[here].c->private_free = safe_free;
846		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
847		    variable_set2(DISK_LABELLED, "yes");
848	    }
849	    else
850		msg = MSG_NOT_APPLICABLE;
851	    break;
852
853	case 'U':
854	    clear();
855	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
856		msgConfirm("You've already written out your changes -\n"
857			   "it's too late to undo!");
858	    }
859	    else if (!msgYesNo("Are you SURE you want to Undo everything?")) {
860		variable_unset(DISK_PARTITIONED);
861		variable_unset(DISK_LABELLED);
862		for (i = 0; devs[i]; i++) {
863		    Disk *d;
864
865		    if (!devs[i]->enabled)
866			continue;
867		    else if ((d = Open_Disk(devs[i]->name)) != NULL) {
868			Free_Disk(devs[i]->private);
869			devs[i]->private = d;
870			diskPartition(devs[i], d);
871		    }
872		}
873		record_label_chunks(devs);
874	    }
875	    clear_wins();
876	    break;
877
878	case 'W':
879	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
880		msgConfirm("You've already written out your changes - if you\n"
881			   "wish to overwrite them, you'll have to start this\n"
882			   "procedure again from the beginning.");
883	    }
884	    else if (!msgYesNo("WARNING:  This should only be used when modifying an EXISTING\n"
885			  "installation.  If you are installing FreeBSD for the first time\n"
886			  "then you should simply type Q when you're finished here and your\n"
887			  "changes will be committed in one batch automatically at the end of\n"
888			  "these questions.\n\n"
889			  "Are you absolutely sure you want to do this now?")) {
890		variable_set2(DISK_LABELLED, "yes");
891		diskLabelCommit(NULL);
892	    }
893	    clear_wins();
894	    break;
895
896	case '|':
897	    if (!msgYesNo("Are you sure you want to go into Wizard mode?\n\n"
898			  "This is an entirely undocumented feature which you are not\n"
899			  "expected to understand!")) {
900		int i;
901		Device **devs;
902
903		dialog_clear();
904		end_dialog();
905		DialogActive = FALSE;
906		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
907		if (!devs) {
908		    msgConfirm("Can't find any disk devices!");
909		    break;
910		}
911		for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
912		    if (devs[i]->enabled)
913		    	slice_wizard(((Disk *)devs[i]->private));
914		}
915		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
916		    variable_set2(DISK_LABELLED, "yes");
917		DialogActive = TRUE;
918		record_label_chunks(devs);
919		clear_wins();
920	    }
921	    else
922		msg = "A most prudent choice!";
923	    break;
924
925	case '\033':	/* ESC */
926	case 'Q':
927	    labeling = FALSE;
928	    break;
929
930	default:
931	    beep();
932	    sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
933	    msg = _msg;
934	    break;
935	}
936    }
937    return DITEM_SUCCESS | DITEM_RESTORE;
938}
939