label.c revision 29249
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.72 1997/08/11 13:08:26 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 || sz < (USR_MIN_SIZE * ONE_MEG)) {
760			msgConfirm("Less than %dMB free for /usr - you will need to\n"
761				   "partition your disk manually with a custom install!", USR_MIN_SIZE);
762			clear_wins();
763			break;
764		    }
765
766		    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
767					    label_chunk_info[here].c,
768					    sz, part, FS_BSDFFS, 0);
769		    if (!tmp) {
770			msgConfirm("Unable to create the /usr partition.  Not enough space?\n"
771				   "You will need to partition your disk manually with a custom install!");
772			clear_wins();
773			break;
774		    }
775		    tmp->private_data = new_part("/usr", TRUE, tmp->size);
776		    tmp->private_free = safe_free;
777		    record_label_chunks(devs);
778		}
779		/* At this point, we're reasonably "labelled" */
780		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
781		    variable_set2(DISK_LABELLED, "yes");
782	    }
783	    break;
784
785	case 'C':
786	    if (label_chunk_info[here].type != PART_SLICE) {
787		msg = "You can only do this in a master partition (see top of screen)";
788		break;
789	    }
790	    sz = space_free(label_chunk_info[here].c);
791	    if (sz <= FS_MIN_SIZE) {
792		msg = "Not enough space to create an additional FreeBSD partition";
793		break;
794	    }
795	    else {
796		char *val;
797		int size;
798		struct chunk *tmp;
799		char osize[80];
800		u_long flags = 0;
801
802		sprintf(osize, "%d", sz);
803		DialogX = 3;
804		DialogY = 2;
805		val = msgGetInput(osize,
806				  "Please specify the partition size in blocks or append a trailing M for\n"
807				  "megabytes or C for cylinders.  %d blocks (%dMB) are free.",
808				  sz, sz / ONE_MEG);
809		DialogX = DialogY = 0;
810		if (!val || (size = strtol(val, &cp, 0)) <= 0) {
811		    clear_wins();
812		    break;
813		}
814
815		if (*cp) {
816		    if (toupper(*cp) == 'M')
817			size *= ONE_MEG;
818		    else if (toupper(*cp) == 'C')
819			size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
820		}
821		if (size <= FS_MIN_SIZE) {
822		    msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
823		    clear_wins();
824		    break;
825		}
826		type = get_partition_type();
827		if (type == PART_NONE) {
828		    clear_wins();
829		    beep();
830		    break;
831		}
832
833		if (type == PART_FILESYSTEM) {
834		    if ((p = get_mountpoint(NULL)) == NULL) {
835			clear_wins();
836			beep();
837			break;
838		    }
839		    else if (!strcmp(p->mountpoint, "/"))
840			flags |= CHUNK_IS_ROOT;
841		    else
842			flags &= ~CHUNK_IS_ROOT;
843		}
844		else
845		    p = NULL;
846
847		if ((flags & CHUNK_IS_ROOT)) {
848		    if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) {
849			msgConfirm("This region cannot be used for your root partition as the\n"
850				   "FreeBSD boot code cannot deal with a root partition created\n"
851				   "in that location.  Please choose another location or smaller\n"
852				   "size for your root partition and try again!");
853			clear_wins();
854			break;
855		    }
856		    if (size < (ROOT_MIN_SIZE * ONE_MEG)) {
857			msgConfirm("Warning: This is smaller than the recommended size for a\n"
858				   "root partition.  For a variety of reasons, root\n"
859				   "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
860		    }
861		}
862		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
863					label_chunk_info[here].c,
864					size, part,
865					(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
866					flags);
867		if (!tmp) {
868		    msgConfirm("Unable to create the partition. Too big?");
869		    clear_wins();
870		    break;
871		}
872		if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) {
873		    msgConfirm("This region cannot be used for your root partition as it starts\n"
874			       "or extends past the 1024'th cylinder mark and is thus a\n"
875			       "poor location to boot from.  Please choose another\n"
876			       "location (or smaller size) for your root partition and try again!");
877		    Delete_Chunk(label_chunk_info[here].c->disk, tmp);
878		    clear_wins();
879		    break;
880		}
881		if (type != PART_SWAP) {
882		    /* This is needed to tell the newfs -u about the size */
883		    tmp->private_data = new_part(p->mountpoint, p->newfs, tmp->size);
884		    safe_free(p);
885		}
886		else
887		    tmp->private_data = p;
888		tmp->private_free = safe_free;
889		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
890		    variable_set2(DISK_LABELLED, "yes");
891		record_label_chunks(devs);
892		clear_wins();
893                /*** This is where we assign focus to new label so it shows ***/
894                {
895                    int i;
896		    label_focus = -1;
897                    for (i = 0; label_chunk_info[i].c; ++i) {
898                    	if (label_chunk_info[i].c == tmp) {
899			    label_focus = i;
900			    override_focus_adjust = -1;
901			    break;
902			}
903		    }
904		    if (label_focus == -1)
905                    	label_focus = i - 1;
906                }
907	    }
908	    break;
909
910	case KEY_DC:
911	case 'D':	/* delete */
912	    if (label_chunk_info[here].type == PART_SLICE) {
913		msg = MSG_NOT_APPLICABLE;
914		break;
915	    }
916	    else if (label_chunk_info[here].type == PART_FAT) {
917		msg = "Use the Disk Partition Editor to delete DOS partitions";
918		break;
919	    }
920	    Delete_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c);
921	    if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
922		variable_set2(DISK_LABELLED, "yes");
923	    record_label_chunks(devs);
924	    break;
925
926	case 'M':	/* mount */
927	    switch(label_chunk_info[here].type) {
928	    case PART_SLICE:
929		msg = MSG_NOT_APPLICABLE;
930		break;
931
932	    case PART_SWAP:
933		msg = "You don't need to specify a mountpoint for a swap partition.";
934		break;
935
936	    case PART_FAT:
937	    case PART_FILESYSTEM:
938		oldp = label_chunk_info[here].c->private_data;
939		p = get_mountpoint(label_chunk_info[here].c);
940		if (p) {
941		    if (!oldp)
942		    	p->newfs = FALSE;
943		    if (label_chunk_info[here].type == PART_FAT
944			&& (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr")
945			    || !strcmp(p->mountpoint, "/var"))) {
946			msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
947			strcpy(p->mountpoint, "/bogus");
948		    }
949		}
950		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
951		    variable_set2(DISK_LABELLED, "yes");
952		record_label_chunks(devs);
953		clear_wins();
954		break;
955
956	    default:
957		msgFatal("Bogus partition under cursor???");
958		break;
959	    }
960	    break;
961
962	case 'N':	/* Set newfs options */
963	    if (label_chunk_info[here].c->private_data &&
964		((PartInfo *)label_chunk_info[here].c->private_data)->newfs)
965		getNewfsCmd(label_chunk_info[here].c->private_data);
966	    else
967		msg = MSG_NOT_APPLICABLE;
968	    clear_wins();
969	    break;
970
971	case 'T':	/* Toggle newfs state */
972	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
973		PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
974		label_chunk_info[here].c->private_data =
975		    new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
976		safe_free(pi);
977		label_chunk_info[here].c->private_free = safe_free;
978		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
979		    variable_set2(DISK_LABELLED, "yes");
980	    }
981	    else
982		msg = MSG_NOT_APPLICABLE;
983	    break;
984
985	case 'U':
986	    clear();
987	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
988		msgConfirm("You've already written out your changes -\n"
989			   "it's too late to undo!");
990	    }
991	    else if (!msgYesNo("Are you SURE you want to Undo everything?")) {
992		variable_unset(DISK_PARTITIONED);
993		variable_unset(DISK_LABELLED);
994		for (i = 0; devs[i]; i++) {
995		    Disk *d;
996
997		    if (!devs[i]->enabled)
998			continue;
999		    else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1000			Free_Disk(devs[i]->private);
1001			devs[i]->private = d;
1002			diskPartition(devs[i], d);
1003		    }
1004		}
1005		record_label_chunks(devs);
1006	    }
1007	    clear_wins();
1008	    break;
1009
1010	case 'W':
1011	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
1012		msgConfirm("You've already written out your changes - if you\n"
1013			   "wish to overwrite them, you'll have to start this\n"
1014			   "procedure again from the beginning.");
1015	    }
1016	    else if (!msgYesNo("WARNING:  This should only be used when modifying an EXISTING\n"
1017			  "installation.  If you are installing FreeBSD for the first time\n"
1018			  "then you should simply type Q when you're finished here and your\n"
1019			  "changes will be committed in one batch automatically at the end of\n"
1020			  "these questions.\n\n"
1021			  "Are you absolutely sure you want to do this now?")) {
1022		variable_set2(DISK_LABELLED, "yes");
1023		diskLabelCommit(NULL);
1024	    }
1025	    clear_wins();
1026	    break;
1027
1028	case '|':
1029	    if (!msgYesNo("Are you sure you want to go into Wizard mode?\n\n"
1030			  "This is an entirely undocumented feature which you are not\n"
1031			  "expected to understand!")) {
1032		int i;
1033		Device **devs;
1034
1035		dialog_clear();
1036		end_dialog();
1037		DialogActive = FALSE;
1038		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1039		if (!devs) {
1040		    msgConfirm("Can't find any disk devices!");
1041		    break;
1042		}
1043		for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1044		    if (devs[i]->enabled)
1045		    	slice_wizard(((Disk *)devs[i]->private));
1046		}
1047		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1048		    variable_set2(DISK_LABELLED, "yes");
1049		DialogActive = TRUE;
1050		record_label_chunks(devs);
1051		clear_wins();
1052	    }
1053	    else
1054		msg = "A most prudent choice!";
1055	    break;
1056
1057	case '\033':	/* ESC */
1058	case 'Q':
1059	    labeling = FALSE;
1060	    break;
1061
1062	default:
1063	    beep();
1064	    sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1065	    msg = _msg;
1066	    break;
1067	}
1068	if (override_focus_adjust) {
1069            if (label_chunk_info[here].type == PART_SLICE)
1070                pslice_focus = here;
1071            else
1072                label_focus = here;
1073	}
1074    }
1075    return DITEM_SUCCESS | DITEM_RESTORE;
1076}
1077
1078static int
1079diskLabelNonInteractive(char *str)
1080{
1081    char *cp;
1082    PartType type;
1083    PartInfo *p;
1084    u_long flags = 0;
1085    int i, status;
1086    Device **devs;
1087    Disk *d;
1088
1089    status = DITEM_SUCCESS;
1090    cp = variable_get(VAR_DISK);
1091    if (!cp) {
1092	dialog_clear();
1093	msgConfirm("diskLabel:  No disk selected - can't label automatically.");
1094	return DITEM_FAILURE;
1095    }
1096
1097    devs = deviceFind(cp, DEVICE_TYPE_DISK);
1098    if (!devs) {
1099	msgConfirm("diskLabel: No disk device %s found!", cp);
1100	return DITEM_FAILURE;
1101    }
1102    d = devs[0]->private;
1103
1104    record_label_chunks(devs);
1105    for (i = 0; label_chunk_info[i].c; i++) {
1106	Chunk *c1 = label_chunk_info[i].c;
1107
1108	if (label_chunk_info[i].type == PART_SLICE) {
1109	    char name[512];
1110	    int entries = 1;
1111
1112	    while (entries) {
1113		snprintf(name, sizeof name, "%s-%d", c1->name, entries);
1114		if ((cp = variable_get(name)) != NULL) {
1115		    int sz;
1116		    char typ[10], mpoint[50];
1117
1118		    if (sscanf(cp, "%s %d %s", typ, &sz, mpoint) != 3) {
1119			msgConfirm("For slice entry %s, got an invalid detail entry of: %s",  c1->name, cp);
1120			status = DITEM_FAILURE;
1121			continue;
1122		    }
1123		    else {
1124			Chunk *tmp;
1125
1126			if (!strcmp(typ, "swap")) {
1127			    type = PART_SWAP;
1128			    strcpy(mpoint, "SWAP");
1129			}
1130			else {
1131			    type = PART_FILESYSTEM;
1132			    if (!strcmp(mpoint, "/"))
1133				flags |= CHUNK_IS_ROOT;
1134			}
1135			if (!sz)
1136			    sz = space_free(c1);
1137			if (sz > space_free(c1)) {
1138			    msgConfirm("Not enough free space to create partition: %s", mpoint);
1139			    status = DITEM_FAILURE;
1140			    continue;
1141			}
1142			if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
1143						      (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
1144			    msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
1145			    status = DITEM_FAILURE;
1146			    break;
1147			}
1148			else {
1149			    tmp->private_data = new_part(mpoint, TRUE, sz);
1150			    tmp->private_free = safe_free;
1151			    status = DITEM_SUCCESS;
1152			}
1153		    }
1154		    entries++;
1155		}
1156		else {
1157		    /* No more matches, leave the loop */
1158		    entries = 0;
1159		}
1160	    }
1161	}
1162	else {
1163	    /* Must be something we can set a mountpoint for */
1164	    cp = variable_get(c1->name);
1165	    if (cp) {
1166		char mpoint[50], do_newfs[8];
1167		Boolean newfs = FALSE;
1168
1169		do_newfs[0] = '\0';
1170		if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
1171		    dialog_clear();
1172		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1173		    status = DITEM_FAILURE;
1174		    continue;
1175		}
1176		newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1177		if (c1->private_data) {
1178		    p = c1->private_data;
1179		    p->newfs = newfs;
1180		    strcpy(p->mountpoint, mpoint);
1181		}
1182		else {
1183		    c1->private_data = new_part(mpoint, newfs, 0);
1184		    c1->private_free = safe_free;
1185		}
1186		if (!strcmp(mpoint, "/"))
1187		    c1->flags |= CHUNK_IS_ROOT;
1188		else
1189		    c1->flags &= ~CHUNK_IS_ROOT;
1190	    }
1191	}
1192    }
1193    if (status == DITEM_SUCCESS)
1194	variable_set2(DISK_LABELLED, "yes");
1195    return status;
1196}
1197