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