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