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