label.c revision 15442
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.46 1996/04/28 22:54:18 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	devs[0]->enabled = TRUE;
107	if (DITEM_STATUS(diskPartitionEditor(self)) == DITEM_FAILURE)
108	    return DITEM_FAILURE;
109    }
110    i = diskLabel(devs[0]->name);
111    if (DITEM_STATUS(i) != DITEM_FAILURE)
112	variable_set2(DISK_LABELLED, "yes");
113    return i;
114}
115
116int
117diskLabelCommit(dialogMenuItem *self)
118{
119    char *cp;
120    int i;
121
122    /* Already done? */
123    if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes")) {
124        variable_set2(DISK_PARTITIONED, "yes");
125	i = DITEM_SUCCESS;
126    }
127    else if (!cp) {
128	msgConfirm("You must assign disk labels before this option can be used.");
129	i = DITEM_FAILURE;
130    }
131    /* The routine will guard against redundant writes, just as this one does */
132    else if (DITEM_STATUS(diskPartitionWrite(self)) != DITEM_SUCCESS)
133	i = DITEM_FAILURE;
134    else if (DITEM_STATUS(installFilesystems(self)) != DITEM_SUCCESS)
135	i = DITEM_FAILURE;
136    else {
137	msgInfo("All filesystem information written successfully.");
138	variable_set2(DISK_LABELLED, "written");
139	i = DITEM_SUCCESS;
140    }
141    return i;
142}
143
144/* See if we're already using a desired partition name */
145static Boolean
146check_conflict(char *name)
147{
148    int i;
149
150    for (i = 0; label_chunk_info[i].c; i++)
151	if (label_chunk_info[i].type == PART_FILESYSTEM && label_chunk_info[i].c->private_data
152	    && !strcmp(((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint, name))
153	    return TRUE;
154    return FALSE;
155}
156
157/* How much space is in this FreeBSD slice? */
158static int
159space_free(struct chunk *c)
160{
161    struct chunk *c1;
162    int sz = c->size;
163
164    for (c1 = c->part; c1; c1 = c1->next) {
165	if (c1->type != unused)
166	    sz -= c1->size;
167    }
168    if (sz < 0)
169	msgFatal("Partitions are larger than actual chunk??");
170    return sz;
171}
172
173/* Snapshot the current situation into the displayed chunks structure */
174static void
175record_label_chunks(Device **devs)
176{
177    int i, j, p;
178    struct chunk *c1, *c2;
179    Disk *d;
180
181    ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3;
182    j = p = 0;
183    /* First buzz through and pick up the FreeBSD slices */
184    for (i = 0; devs[i]; i++) {
185	if (!devs[i]->enabled)
186	    continue;
187	d = (Disk *)devs[i]->private;
188	if (!d->chunks)
189	    msgFatal("No chunk list found for %s!", d->name);
190
191	/* Put the slice entries first */
192	for (c1 = d->chunks->part; c1; c1 = c1->next) {
193	    if (c1->type == freebsd) {
194		label_chunk_info[j].type = PART_SLICE;
195		label_chunk_info[j].c = c1;
196		++j;
197		++ChunkPartStartRow;
198	    }
199	}
200    }
201
202    /* Now run through again and get the FreeBSD partition entries */
203    for (i = 0; devs[i]; i++) {
204	if (!devs[i]->enabled)
205	    continue;
206	d = (Disk *)devs[i]->private;
207	/* Then buzz through and pick up the partitions */
208	for (c1 = d->chunks->part; c1; c1 = c1->next) {
209	    if (c1->type == freebsd) {
210		for (c2 = c1->part; c2; c2 = c2->next) {
211		    if (c2->type == part) {
212			if (c2->subtype == FS_SWAP)
213			    label_chunk_info[j].type = PART_SWAP;
214			else
215			    label_chunk_info[j].type = PART_FILESYSTEM;
216			label_chunk_info[j].c = c2;
217			++j;
218		    }
219		}
220	    }
221	    else if (c1->type == fat) {
222		label_chunk_info[j].type = PART_FAT;
223		label_chunk_info[j].c = c1;
224		++j;
225	    }
226	}
227    }
228    label_chunk_info[j].c = NULL;
229    if (here >= j)
230	here = j  ? j - 1 : 0;
231    if (ChunkWin) {
232	wclear(ChunkWin);
233	wrefresh(ChunkWin);
234    }
235    else
236	ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
237}
238
239/* A new partition entry */
240static PartInfo *
241new_part(char *mpoint, Boolean newfs, u_long size)
242{
243    PartInfo *ret;
244    u_long target, divisor;
245
246    if (!mpoint)
247	mpoint = "/change_me";
248
249    ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
250    strncpy(ret->mountpoint, mpoint, FILENAME_MAX);
251    strcpy(ret->newfs_cmd, "newfs -b 8192 -f 1024");
252    ret->newfs = newfs;
253    if (!size)
254	    return ret;
255    for (target = size; target; target--) {
256	for (divisor = 4096 ; divisor > 1023; divisor--) {
257	    if (!(target % divisor)) {
258		sprintf(ret->newfs_cmd + strlen(ret->newfs_cmd), " -u %ld",divisor);
259		return ret;
260	    }
261	}
262    }
263    return ret;
264}
265
266/* Get the mountpoint for a partition and save it away */
267static PartInfo *
268get_mountpoint(struct chunk *old)
269{
270    char *val;
271    PartInfo *tmp;
272
273    if (old && old->private_data)
274	tmp = old->private_data;
275    else
276	tmp = NULL;
277    val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
278    if (!val || !*val) {
279	if (!old)
280	    return NULL;
281	else {
282	    free(old->private_data);
283	    old->private_data = NULL;
284	}
285	return NULL;
286    }
287
288    /* Is it just the same value? */
289    if (tmp && !strcmp(tmp->mountpoint, val))
290	return NULL;
291
292    /* Did we use it already? */
293    if (check_conflict(val)) {
294	msgConfirm("You already have a mount point for %s assigned!", val);
295	return NULL;
296    }
297
298    /* Is it bogus? */
299    if (*val != '/') {
300	msgConfirm("Mount point must start with a / character");
301	return NULL;
302    }
303
304    /* Is it going to be mounted on root? */
305    if (!strcmp(val, "/")) {
306	if (old)
307	    old->flags |= CHUNK_IS_ROOT;
308    }
309    else if (old)
310	old->flags &= ~CHUNK_IS_ROOT;
311
312    safe_free(tmp);
313    tmp = new_part(val, TRUE, 0);
314    if (old) {
315	old->private_data = tmp;
316	old->private_free = safe_free;
317    }
318    return tmp;
319}
320
321/* Get the type of the new partiton */
322static PartType
323get_partition_type(void)
324{
325    char selection[20];
326    int i;
327    WINDOW *w = savescr();
328
329    static unsigned char *fs_types[] = {
330	"FS",
331	"A file system",
332	"Swap",
333	"A swap partition.",
334    };
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    restorescr(w);
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	strncpy(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(A_REVERSE);
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
432	        mountpoint = "<none>";
433
434	    /* Now display the newfs field */
435	    if (label_chunk_info[i].type == PART_FAT)
436	        newfs = "DOS";
437	    else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM)
438		newfs = ((PartInfo *)label_chunk_info[i].c->private_data)->newfs ? "UFS Y" : "UFS N";
439	    else if (label_chunk_info[i].type == PART_SWAP)
440		newfs = "SWAP";
441	    else
442		newfs = "*";
443	    for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
444		onestr[PART_MOUNT_COL + j] = mountpoint[j];
445	    snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? label_chunk_info[i].c->size / ONE_MEG : 0);
446	    memcpy(onestr + PART_SIZE_COL, num, strlen(num));
447	    memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
448	    onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
449	    if (i == here)
450		wattrset(ChunkWin, A_REVERSE);
451	    mvwaddstr(ChunkWin, prow, pcol, onestr);
452	    wattrset(ChunkWin, A_NORMAL);
453	    wrefresh(ChunkWin);
454	    move(0, 0);
455	    ++prow;
456	}
457    }
458}
459
460static void
461print_command_summary()
462{
463    mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
464    mvprintw(18, 0, "C = Create      D = Delete         M = Mount");
465    if (!RunningAsInit)
466	mvprintw(18, 48, "W = Write");
467    mvprintw(19, 0, "N = Newfs Opts  T = Newfs Toggle   U = Undo    Q = Finish");
468    mvprintw(20, 0, "A = Auto Defaults for all!");
469    mvprintw(22, 0, "The default target will be displayed in ");
470
471    attrset(A_REVERSE);
472    addstr("reverse");
473    attrset(A_NORMAL);
474    addstr(" video.");
475    mvprintw(23, 0, "Use F1 or ? to get more help, arrow keys to move.");
476    move(0, 0);
477}
478
479static int
480diskLabel(char *str)
481{
482    int sz, key = 0, first_time = 1;
483    Boolean labeling;
484    char *msg = NULL;
485    PartInfo *p, *oldp;
486    PartType type;
487    Device **devs;
488    WINDOW *w;
489
490    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
491    if (!devs) {
492	msgConfirm("No disks found!");
493	return DITEM_FAILURE;
494    }
495
496    labeling = TRUE;
497    keypad(stdscr, TRUE);
498    record_label_chunks(devs);
499
500    w = savescr();
501    dialog_clear(); clear();
502    while (labeling) {
503	print_label_chunks();
504	if (first_time) {
505	    print_command_summary();
506	    first_time = 0;
507	}
508	if (msg) {
509	    attrset(A_REVERSE); 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	key = toupper(getch());
519	switch (key) {
520	    int i;
521
522	case '\014':	/* ^L */
523	    continue;
524
525	case KEY_UP:
526	case '-':
527	    if (here != 0)
528		--here;
529	    else
530		while (label_chunk_info[here + 1].c)
531		    ++here;
532	    break;
533
534	case KEY_DOWN:
535	case '+':
536	case '\r':
537	case '\n':
538	    if (label_chunk_info[here + 1].c)
539		++here;
540	    else
541		here = 0;
542	    break;
543
544	case KEY_HOME:
545	    here = 0;
546	    break;
547
548	case KEY_END:
549	    while (label_chunk_info[here + 1].c)
550		++here;
551	    break;
552
553	case KEY_F(1):
554	case '?':
555	    systemDisplayHelp("partition");
556	    break;
557
558	case 'A':
559	    if (label_chunk_info[here].type != PART_SLICE) {
560		msg = "You can only do this in a disk slice (at top of screen)";
561		break;
562	    }
563	    sz = space_free(label_chunk_info[here].c);
564	    if (sz <= FS_MIN_SIZE) {
565		msg = "Not enough free space to create a new partition in the slice";
566		break;
567	    }
568	    else {
569		struct chunk *tmp;
570		int mib[2];
571		int physmem;
572		size_t size, swsize;
573		char *cp;
574
575		cp = variable_get(VAR_ROOT_SIZE);
576		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
577					label_chunk_info[here].c,
578					(cp ? atoi(cp) : 32) * ONE_MEG, part, FS_BSDFFS,
579					CHUNK_IS_ROOT);
580
581		if (!tmp) {
582		    msgConfirm("Unable to create the root partition. Too big?");
583		    break;
584		}
585		tmp->private_data = new_part("/", TRUE, tmp->size);
586		tmp->private_free = safe_free;
587		record_label_chunks(devs);
588
589		cp = variable_get(VAR_SWAP_SIZE);
590		if (cp)
591		    swsize = atoi(cp) * ONE_MEG;
592		else {
593		    mib[0] = CTL_HW;
594		    mib[1] = HW_PHYSMEM;
595		    size = sizeof physmem;
596		    sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
597		    swsize = 16 * ONE_MEG + (physmem * 2 / 512);
598		}
599		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
600					label_chunk_info[here].c,
601					swsize,
602					part, FS_SWAP, 0);
603		if (!tmp) {
604		    msgConfirm("Unable to create the swap partition. Too big?");
605		    break;
606		}
607
608		tmp->private_data = 0;
609		tmp->private_free = safe_free;
610		record_label_chunks(devs);
611
612		cp = variable_get(VAR_VAR_SIZE);
613		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
614					label_chunk_info[here].c,
615					(cp ? atoi(cp) : VAR_MIN_SIZE) * ONE_MEG, part, FS_BSDFFS, 0);
616		if (!tmp) {
617		    msgConfirm("Less than %dMB free for /var - you will need to\n"
618			       "partition your disk manually with a custom install!", (cp ? atoi(cp) : VAR_MIN_SIZE));
619		    break;
620		}
621		tmp->private_data = new_part("/var", TRUE, tmp->size);
622		tmp->private_free = safe_free;
623		record_label_chunks(devs);
624
625		cp = variable_get(VAR_USR_SIZE);
626		if (cp)
627		    sz = atoi(cp) * ONE_MEG;
628		else
629		    sz = space_free(label_chunk_info[here].c);
630		if (!sz || sz < (USR_MIN_SIZE * ONE_MEG)) {
631		    msgConfirm("Less than %dMB free for /usr - you will need to\n"
632			       "partition your disk manually with a custom install!", USR_MIN_SIZE);
633		    break;
634		}
635
636		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
637					label_chunk_info[here].c,
638					sz, part, FS_BSDFFS, 0);
639		if (!tmp) {
640		    msgConfirm("Unable to create the /usr partition.  Not enough space?\n"
641			       "You will need to partition your disk manually with a custom install!");
642		    break;
643		}
644		/* At this point, we're reasonably "labelled" */
645		variable_set2(DISK_LABELLED, "yes");
646		tmp->private_data = new_part("/usr", TRUE, tmp->size);
647		tmp->private_free = safe_free;
648		record_label_chunks(devs);
649	    }
650	    break;
651
652	case 'C':
653	    if (label_chunk_info[here].type != PART_SLICE) {
654		msg = "You can only do this in a master partition (see top of screen)";
655		break;
656	    }
657	    sz = space_free(label_chunk_info[here].c);
658	    if (sz <= FS_MIN_SIZE) {
659		msg = "Not enough space to create an additional FreeBSD partition";
660		break;
661	    }
662	    else {
663		char *val, *cp;
664		int size;
665		struct chunk *tmp;
666		char osize[80];
667		u_long flags = 0;
668
669		sprintf(osize, "%d", sz);
670		val = msgGetInput(osize, "Please specify the size for new FreeBSD partition in blocks, or\n"
671				  "append a trailing `M' for megabytes (e.g. 20M) or `C' for cylinders.\n\n"
672				  "Space free is %d blocks (%dMB)", sz, sz / ONE_MEG);
673		if (!val || (size = strtol(val, &cp, 0)) <= 0)
674		    break;
675
676		if (*cp) {
677		    if (toupper(*cp) == 'M')
678			size *= ONE_MEG;
679		    else if (toupper(*cp) == 'C')
680			size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
681		}
682		if (size <= FS_MIN_SIZE) {
683		    msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
684		    break;
685		}
686		type = get_partition_type();
687		if (type == PART_NONE)
688		    break;
689
690		if (type == PART_FILESYSTEM) {
691		    if ((p = get_mountpoint(NULL)) == NULL)
692			break;
693		    else if (!strcmp(p->mountpoint, "/"))
694			flags |= CHUNK_IS_ROOT;
695		    else
696			flags &= ~CHUNK_IS_ROOT;
697		} else
698		    p = NULL;
699
700		if ((flags & CHUNK_IS_ROOT)) {
701		    if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) {
702			msgConfirm("This region cannot be used for your root partition as the\n"
703				   "FreeBSD boot code cannot deal with a root partition created\n"
704				   "in that location.  Please choose another location or smaller\n"
705				   "size for your root partition and try again!");
706			break;
707		    }
708		    if (size < (ROOT_MIN_SIZE * ONE_MEG)) {
709			msgConfirm("Warning: This is smaller than the recommended size for a\n"
710				   "root partition.  For a variety of reasons, root\n"
711				   "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
712		    }
713		}
714		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
715					label_chunk_info[here].c,
716					size, part,
717					(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
718					flags);
719		if (!tmp) {
720		    msgConfirm("Unable to create the partition. Too big?");
721		    break;
722		}
723		if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) {
724		    msgConfirm("This region cannot be used for your root partition as it starts\n"
725			       "or extends past the 1024'th cylinder mark and is thus a\n"
726			       "poor location to boot from.  Please choose another\n"
727			       "location (or smaller size) for your root partition and try again!");
728		    Delete_Chunk(label_chunk_info[here].c->disk, tmp);
729		    break;
730		}
731		if (type != PART_SWAP) {
732		    /* This is needed to tell the newfs -u about the size */
733		    tmp->private_data = new_part(p->mountpoint, p->newfs, tmp->size);
734		    tmp->private_free = safe_free;
735		    safe_free(p);
736		}
737		else
738		    tmp->private_data = p;
739		tmp->private_free = safe_free;
740		variable_set2(DISK_LABELLED, "yes");
741		record_label_chunks(devs);
742	    }
743	    break;
744
745	case '\177':
746	case 'D':	/* delete */
747	    if (label_chunk_info[here].type == PART_SLICE) {
748		msg = MSG_NOT_APPLICABLE;
749		break;
750	    }
751	    else if (label_chunk_info[here].type == PART_FAT) {
752		msg = "Use the Disk Partition Editor to delete DOS partitions";
753		break;
754	    }
755	    Delete_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c);
756	    variable_set2(DISK_LABELLED, "yes");
757	    record_label_chunks(devs);
758	    break;
759
760	case 'M':	/* mount */
761	    switch(label_chunk_info[here].type) {
762	    case PART_SLICE:
763		msg = MSG_NOT_APPLICABLE;
764		break;
765
766	    case PART_SWAP:
767		msg = "You don't need to specify a mountpoint for a swap partition.";
768		break;
769
770	    case PART_FAT:
771	    case PART_FILESYSTEM:
772		oldp = label_chunk_info[here].c->private_data;
773		p = get_mountpoint(label_chunk_info[here].c);
774		if (p) {
775		    if (!oldp)
776		    	p->newfs = FALSE;
777		    if (label_chunk_info[here].type == PART_FAT
778			&& (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr")
779			    || !strcmp(p->mountpoint, "/var"))) {
780			msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
781			strcpy(p->mountpoint, "/bogus");
782		    }
783		}
784		variable_set2(DISK_LABELLED, "yes");
785		record_label_chunks(devs);
786		break;
787
788	    default:
789		msgFatal("Bogus partition under cursor???");
790		break;
791	    }
792	    break;
793
794	case 'N':	/* Set newfs options */
795	    if (label_chunk_info[here].c->private_data &&
796		((PartInfo *)label_chunk_info[here].c->private_data)->newfs)
797		getNewfsCmd(label_chunk_info[here].c->private_data);
798	    else
799		msg = MSG_NOT_APPLICABLE;
800	    break;
801
802	case 'T':	/* Toggle newfs state */
803	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
804		    PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
805		    label_chunk_info[here].c->private_data =
806			new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
807		    safe_free(pi);
808		    label_chunk_info[here].c->private_free = safe_free;
809		    variable_set2(DISK_LABELLED, "yes");
810		}
811	    else
812		msg = MSG_NOT_APPLICABLE;
813	    break;
814
815	case 'U':
816	    clear();
817	    if (msgYesNo("Are you SURE you want to Undo everything?"))
818		break;
819	    variable_unset(DISK_PARTITIONED);
820	    for (i = 0; devs[i]; i++) {
821		Disk *d;
822
823		if (!devs[i]->enabled)
824		    continue;
825		else if ((d = Open_Disk(devs[i]->name)) != NULL) {
826		    Free_Disk(devs[i]->private);
827		    devs[i]->private = d;
828		    diskPartition(devs[i], d);
829		}
830	    }
831	    variable_unset(DISK_LABELLED);
832	    record_label_chunks(devs);
833	    break;
834
835	case 'W':
836	    if (!msgYesNo("You also have the option of doing this later in one final 'commit'\n"
837			  "operation, and it should also be noted that this option is NOT for\n"
838			  "use during new installations but rather for modifying existing ones.\n\n"
839			  "Are you absolutely SURE you want to do this now?")) {
840		variable_set2(DISK_LABELLED, "yes");
841		diskLabelCommit(NULL);
842	    }
843	    break;
844
845	case '|':
846	    if (!msgYesNo("Are you sure you want to go into Wizard mode?\n\n"
847			  "This is an entirely undocumented feature which you are not\n"
848			  "expected to understand!")) {
849		int i;
850		Device **devs;
851		WINDOW *save = savescr();
852
853		dialog_clear();
854		end_dialog();
855		DialogActive = FALSE;
856		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
857		if (!devs) {
858		    msgConfirm("Can't find any disk devices!");
859		    break;
860		}
861		for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
862		    if (devs[i]->enabled)
863		    	slice_wizard(((Disk *)devs[i]->private));
864		}
865		variable_set2(DISK_LABELLED, "yes");
866		DialogActive = TRUE;
867		dialog_clear();
868		restorescr(save);
869		record_label_chunks(devs);
870	    }
871	    else
872		msg = "A most prudent choice!";
873	    break;
874
875	case 'Q':
876	    labeling = FALSE;
877	    break;
878
879	default:
880	    beep();
881	    msg = "Type F1 or ? for help";
882	    break;
883	}
884    }
885    restorescr(w);
886    return DITEM_SUCCESS;
887}
888