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