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