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