label.c revision 29501
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.73 1997/09/09 16:32: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
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        pslice_focus = here;            /* VEG 09/05/97 */
242        label_focus = here;            /* VEG 09/05/97 */
243    }
244    if (ChunkWin) {
245	wclear(ChunkWin);
246	wrefresh(ChunkWin);
247    }
248    else
249	ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
250}
251
252/* A new partition entry */
253static PartInfo *
254new_part(char *mpoint, Boolean newfs, u_long size)
255{
256    PartInfo *ret;
257
258    if (!mpoint)
259	mpoint = "/change_me";
260
261    ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
262    sstrncpy(ret->mountpoint, mpoint, FILENAME_MAX);
263    strcpy(ret->newfs_cmd, "newfs -b 8192 -f 1024");
264    ret->newfs = newfs;
265    if (!size)
266	return ret;
267    return ret;
268}
269
270/* Get the mountpoint for a partition and save it away */
271static PartInfo *
272get_mountpoint(struct chunk *old)
273{
274    char *val;
275    PartInfo *tmp;
276
277    if (old && old->private_data)
278	tmp = old->private_data;
279    else
280	tmp = NULL;
281    if (!old) {
282	DialogX = 14;
283	DialogY = 16;
284    }
285    val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
286    DialogX = DialogY = 0;
287    if (!val || !*val) {
288	if (!old)
289	    return NULL;
290	else {
291	    free(old->private_data);
292	    old->private_data = NULL;
293	}
294	return NULL;
295    }
296
297    /* Is it just the same value? */
298    if (tmp && !strcmp(tmp->mountpoint, val))
299	return NULL;
300
301    /* Did we use it already? */
302    if (check_conflict(val)) {
303	msgConfirm("You already have a mount point for %s assigned!", val);
304	return NULL;
305    }
306
307    /* Is it bogus? */
308    if (*val != '/') {
309	msgConfirm("Mount point must start with a / character");
310	return NULL;
311    }
312
313    /* Is it going to be mounted on root? */
314    if (!strcmp(val, "/")) {
315	if (old)
316	    old->flags |= CHUNK_IS_ROOT;
317    }
318    else if (old)
319	old->flags &= ~CHUNK_IS_ROOT;
320
321    safe_free(tmp);
322    tmp = new_part(val, TRUE, 0);
323    if (old) {
324	old->private_data = tmp;
325	old->private_free = safe_free;
326    }
327    return tmp;
328}
329
330/* Get the type of the new partiton */
331static PartType
332get_partition_type(void)
333{
334    char selection[20];
335    int i;
336
337    static unsigned char *fs_types[] = {
338	"FS",
339	"A file system",
340	"Swap",
341	"A swap partition.",
342    };
343    DialogX = 7;
344    DialogY = 8;
345    i = dialog_menu("Please choose a partition type",
346		    "If you want to use this partition for swap space, select Swap.\n"
347		    "If you want to put a filesystem on it, choose FS.",
348		    -1, -1, 2, 2, fs_types, selection, NULL, NULL);
349    DialogX = DialogY = 0;
350    if (!i) {
351	if (!strcmp(selection, "FS"))
352	    return PART_FILESYSTEM;
353	else if (!strcmp(selection, "Swap"))
354	    return PART_SWAP;
355    }
356    return PART_NONE;
357}
358
359/* If the user wants a special newfs command for this, set it */
360static void
361getNewfsCmd(PartInfo *p)
362{
363    char *val;
364
365    val = msgGetInput(p->newfs_cmd,
366		      "Please enter the newfs command and options you'd like to use in\n"
367		      "creating this file system.");
368    if (val)
369	sstrncpy(p->newfs_cmd, val, NEWFS_CMD_MAX);
370}
371
372#define MAX_MOUNT_NAME	12
373
374#define PART_PART_COL	0
375#define PART_MOUNT_COL	8
376#define PART_SIZE_COL	(PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
377#define PART_NEWFS_COL	(PART_SIZE_COL + 7)
378#define PART_OFF	38
379
380#define TOTAL_AVAIL_LINES       (10)
381#define PSLICE_SHOWABLE          (4)
382
383
384/* stick this all up on the screen */
385static void
386print_label_chunks(void)
387{
388    int  i, j, srow, prow, pcol;
389    int  sz;
390    char clrmsg[80];
391
392    /********************************************************/
393    /*** These values are for controling screen resources ***/
394    /*** Each label line holds up to 2 labels, so beware! ***/
395    /*** strategy will be to try to always make sure the  ***/
396    /*** highlighted label is in the active display area. ***/
397    /********************************************************/
398    int  pslice_max, label_max;
399    int  pslice_count, label_count, label_focus_found, pslice_focus_found;
400
401    attrset(A_REVERSE);
402    mvaddstr(0, 25, "FreeBSD Disklabel Editor");
403    attrset(A_NORMAL);
404
405    for (i = 0; i < 2; i++) {
406	mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
407	mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
408
409	mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
410	mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
411
412	mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 2, "Size");
413	mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 2, "----");
414
415	mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
416	mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
417    }
418    srow = CHUNK_SLICE_START_ROW;
419    prow = 0;
420    pcol = 0;
421
422    /*** these variables indicate that the focused item is shown currently ***/
423    label_focus_found = 0;
424    pslice_focus_found = 0;
425
426    /*** Count the number of parition slices ***/
427    pslice_count = 0;
428    for (i = 0; label_chunk_info[i].c ; i++) {
429        if (label_chunk_info[i].type == PART_SLICE)
430            ++pslice_count;
431    }
432    pslice_max = pslice_count;
433
434    /*** 4 line max for partition slices ***/
435    if (pslice_max > PSLICE_SHOWABLE)
436        pslice_max = PSLICE_SHOWABLE;
437
438    /*** View partition slices modulo pslice_max ***/
439    label_max = TOTAL_AVAIL_LINES - pslice_max;
440
441    label_count = 0;
442    pslice_count = 0;
443    mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "          ");
444    mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "          ");
445
446    for (i = 0; label_chunk_info[i].c; i++) {
447	/* Is it a slice entry displayed at the top? */
448	if (label_chunk_info[i].type == PART_SLICE) {
449            /*** This causes the new pslice to replace the previous display ***/
450            /*** focus must remain on the most recently active pslice       ***/
451            if (pslice_count == pslice_max) {
452                if (pslice_focus_found) {
453                    /*** This is where we can mark the more following ***/
454                    attrset(A_BOLD);
455                    mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
456                    attrset(A_NORMAL);
457                    continue;
458                }
459                else {
460                    /*** this is where we set the more previous ***/
461                    attrset(A_BOLD);
462                    mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
463                    attrset(A_NORMAL);
464                    pslice_count = 0;
465                    srow = CHUNK_SLICE_START_ROW;
466                }
467            }
468
469	    sz = space_free(label_chunk_info[i].c);
470	    if (i == here)
471		attrset(ATTR_SELECTED);
472            if (i == pslice_focus)
473                pslice_focus_found = -1;
474
475	    mvprintw(srow++, 0,
476		     "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
477		     label_chunk_info[i].c->disk->name, label_chunk_info[i].c->name,
478		     sz, (sz / ONE_MEG));
479	    attrset(A_NORMAL);
480	    clrtoeol();
481	    move(0, 0);
482	    refresh();
483            ++pslice_count;
484	}
485	/* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
486	else {
487	    char onestr[PART_OFF], num[10], *mountpoint, *newfs;
488
489	    /*
490	     * We copy this into a blank-padded string so that it looks like
491	     * a solid bar in reverse-video
492	     */
493	    memset(onestr, ' ', PART_OFF - 1);
494	    onestr[PART_OFF - 1] = '\0';
495
496            /*** Track how many labels have been displayed ***/
497            if (label_count == ((label_max - 1 ) * 2)) {
498                if (label_focus_found) {
499                    continue;
500                }
501                else {
502                    label_count = 0;
503                    prow = 0;
504                    pcol = 0;
505                }
506            }
507
508	    /* Go for two columns if we've written one full columns worth */
509	    /*** if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) ***/
510            if (label_count == label_max - 1) {
511		pcol = PART_OFF;
512		prow = 0;
513	    }
514	    memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
515	    /* If it's a filesystem, display the mountpoint */
516	    if (label_chunk_info[i].c->private_data
517		&& (label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT))
518	        mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
519	    else if (label_chunk_info[i].type == PART_SWAP)
520		mountpoint = "swap";
521	    else
522	        mountpoint = "<none>";
523
524	    /* Now display the newfs field */
525	    if (label_chunk_info[i].type == PART_FAT)
526	        newfs = "DOS";
527	    else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM)
528		newfs = ((PartInfo *)label_chunk_info[i].c->private_data)->newfs ? "UFS Y" : "UFS N";
529	    else if (label_chunk_info[i].type == PART_SWAP)
530		newfs = "SWAP";
531	    else
532		newfs = "*";
533	    for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
534		onestr[PART_MOUNT_COL + j] = mountpoint[j];
535	    snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? label_chunk_info[i].c->size / ONE_MEG : 0);
536	    memcpy(onestr + PART_SIZE_COL, num, strlen(num));
537	    memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
538	    onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
539	    if (i == here)
540		wattrset(ChunkWin, ATTR_SELECTED);
541            if (i == label_focus)
542                label_focus_found = -1;
543
544            /*** lazy man's way of padding this string ***/
545            while (strlen( onestr ) < 37)
546                strcat(onestr, " ");
547
548	    mvwaddstr(ChunkWin, prow, pcol, onestr);
549	    wattrset(ChunkWin, A_NORMAL);
550	    /*** wrefresh(ChunkWin); ***/
551	    move(0, 0);
552	    ++prow;
553            ++label_count;
554	}
555    }
556
557    /*** this will erase all the extra stuff ***/
558    memset(clrmsg, ' ', 37);
559    clrmsg[37] = '\0';
560
561    while (pslice_count < pslice_max) {
562        mvprintw(srow++, 0, clrmsg);
563        clrtoeol();
564        ++pslice_count;
565    }
566    if (ChunkWin) {
567        while (label_count < (2 * (label_max - 1))) {
568	    mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
569	    ++label_count;
570	    if (prow == (label_max - 1)) {
571		prow = 0;
572		pcol = PART_OFF;
573	    }
574        }
575        wrefresh(ChunkWin);
576    }
577}
578
579static void
580print_command_summary(void)
581{
582    mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
583    mvprintw(18, 0, "C = Create      D = Delete         M = Mount pt.");
584    if (!RunningAsInit)
585	mvprintw(18, 47, "W = Write");
586    mvprintw(19, 0, "N = Newfs Opts  T = Newfs Toggle   U = Undo    Q = Finish");
587    mvprintw(20, 0, "A = Auto Defaults for all!");
588    mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
589    move(0, 0);
590}
591
592static void
593clear_wins(void)
594{
595    clear();
596    wclear(ChunkWin);
597}
598
599static int
600diskLabel(char *str)
601{
602    int sz, key = 0;
603    Boolean labeling;
604    char *msg = NULL;
605    PartInfo *p, *oldp;
606    PartType type;
607    Device **devs;
608    int override_focus_adjust = 0;
609
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