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