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