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