label.c revision 43685
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.85 1999/01/29 11:39:04 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", 0);
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", 0);
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
647#ifdef __alpha__
648
649/*
650 * If there isn't a freebsd chunk already (i.e. there is no label),
651 * dedicate the disk.
652 */
653static void
654maybe_dedicate(Disk* d)
655{
656    struct chunk *c;
657
658    for (c = d->chunks->part; c; c = c->next) {
659	if (c->type == freebsd)
660	    break;
661    }
662
663    if (!c) {
664	msgDebug("dedicating disk");
665	All_FreeBSD(d, 1);
666    }
667}
668
669#endif
670
671static int
672diskLabel(Device *dev)
673{
674    int sz, key = 0;
675    Boolean labeling;
676    char *msg = NULL;
677    PartInfo *p, *oldp;
678    PartType type;
679    Device **devs;
680#ifdef __alpha__
681    int i;
682#endif
683
684    label_focus = 0;
685    pslice_focus = 0;
686    here = 0;
687
688    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
689    if (!devs) {
690	msgConfirm("No disks found!");
691	return DITEM_FAILURE;
692    }
693    labeling = TRUE;
694    keypad(stdscr, TRUE);
695#ifdef __alpha__
696    for (i = 0; devs[i]; i++) {
697	maybe_dedicate((Disk*) devs[i]->private);
698    }
699#endif
700    record_label_chunks(devs, dev);
701
702    clear();
703    while (labeling) {
704	char *cp;
705
706	print_label_chunks();
707	print_command_summary();
708	if (msg) {
709	    attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
710	    clrtoeol();
711	    beep();
712	    msg = NULL;
713	}
714	else {
715	    move(23, 0);
716	    clrtoeol();
717	}
718
719	refresh();
720	key = getch();
721	switch (toupper(key)) {
722	    int i;
723	    static char _msg[40];
724
725	case '\014':	/* ^L */
726	    clear_wins();
727	    break;
728
729	case '\020':	/* ^P */
730	case KEY_UP:
731	case '-':
732	    if (here != 0)
733		--here;
734	    else
735		while (label_chunk_info[here + 1].c)
736		    ++here;
737	    break;
738
739	case '\016':	/* ^N */
740	case KEY_DOWN:
741	case '+':
742	case '\r':
743	case '\n':
744	    if (label_chunk_info[here + 1].c)
745		++here;
746	    else
747		here = 0;
748	    break;
749
750	case KEY_HOME:
751	    here = 0;
752	    break;
753
754	case KEY_END:
755	    while (label_chunk_info[here + 1].c)
756		++here;
757	    break;
758
759	case KEY_F(1):
760	case '?':
761	    systemDisplayHelp("partition");
762	    clear_wins();
763	    break;
764
765	case 'A':
766	    if (label_chunk_info[here].type != PART_SLICE) {
767		msg = "You can only do this in a disk slice (at top of screen)";
768		break;
769	    }
770	    sz = space_free(label_chunk_info[here].c);
771	    if (sz <= FS_MIN_SIZE)
772		msg = "Not enough free space to create a new partition in the slice";
773	    else {
774		struct chunk *tmp;
775		int mib[2];
776		int physmem;
777		size_t size, swsize;
778		char *cp;
779		Chunk *rootdev, *swapdev, *usrdev, *vardev;
780
781		(void)checkLabels(FALSE, &rootdev, &swapdev, &usrdev, &vardev);
782		if (!rootdev) {
783		    cp = variable_get(VAR_ROOT_SIZE);
784		    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
785					    (cp ? atoi(cp) : 32) * ONE_MEG, part, FS_BSDFFS,  CHUNK_IS_ROOT);
786		    if (!tmp) {
787			msgConfirm("Unable to create the root partition. Too big?");
788			clear_wins();
789			break;
790		    }
791		    tmp->private_data = new_part("/", TRUE, tmp->size);
792		    tmp->private_free = safe_free;
793		    record_label_chunks(devs, dev);
794		}
795
796		if (!swapdev) {
797		    cp = variable_get(VAR_SWAP_SIZE);
798		    if (cp)
799			swsize = atoi(cp) * ONE_MEG;
800		    else {
801			mib[0] = CTL_HW;
802			mib[1] = HW_PHYSMEM;
803			size = sizeof physmem;
804			sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
805			swsize = 16 * ONE_MEG + (physmem * 2 / 512);
806		    }
807		    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
808					    swsize, part, FS_SWAP, 0);
809		    if (!tmp) {
810			msgConfirm("Unable to create the swap partition. Too big?");
811			clear_wins();
812			break;
813		    }
814		    tmp->private_data = 0;
815		    tmp->private_free = safe_free;
816		    record_label_chunks(devs, dev);
817		}
818
819		if (!vardev) {
820		    cp = variable_get(VAR_VAR_SIZE);
821		    if (cp)
822			sz = atoi(cp) * ONE_MEG;
823		    else
824			sz = variable_get(VAR_NO_USR)
825				?  space_free(label_chunk_info[here].c)
826				:  VAR_MIN_SIZE * ONE_MEG;
827
828		    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
829					    sz, part, FS_BSDFFS, 0);
830		    if (!tmp) {
831			msgConfirm("Less than %dMB free for /var - you will need to\n"
832				   "partition your disk manually with a custom install!",
833				   (cp ? atoi(cp) : VAR_MIN_SIZE));
834			clear_wins();
835			break;
836		    }
837		    tmp->private_data = new_part("/var", TRUE, tmp->size);
838		    tmp->private_free = safe_free;
839		    record_label_chunks(devs, dev);
840		}
841
842		if (!usrdev && !variable_get(VAR_NO_USR)) {
843		    cp = variable_get(VAR_USR_SIZE);
844		    if (cp)
845			sz = atoi(cp) * ONE_MEG;
846		    else
847			sz = space_free(label_chunk_info[here].c);
848		    if (sz) {
849			if (sz < (USR_MIN_SIZE * ONE_MEG)) {
850			    msgConfirm("Less than %dMB free for /usr - you will need to\n"
851				       "partition your disk manually with a custom install!", USR_MIN_SIZE);
852			    clear_wins();
853			    break;
854			}
855
856			tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
857						label_chunk_info[here].c,
858						sz, part, FS_BSDFFS, 0);
859			if (!tmp) {
860			    msgConfirm("Unable to create the /usr partition.  Not enough space?\n"
861				       "You will need to partition your disk manually with a custom install!");
862			    clear_wins();
863			    break;
864			}
865			tmp->private_data = new_part("/usr", TRUE, tmp->size);
866			tmp->private_free = safe_free;
867			record_label_chunks(devs, dev);
868		    }
869		}
870
871		/* At this point, we're reasonably "labelled" */
872		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
873		    variable_set2(DISK_LABELLED, "yes", 0);
874	    }
875	    break;
876
877	case 'C':
878	    if (label_chunk_info[here].type != PART_SLICE) {
879		msg = "You can only do this in a master partition (see top of screen)";
880		break;
881	    }
882	    sz = space_free(label_chunk_info[here].c);
883	    if (sz <= FS_MIN_SIZE) {
884		msg = "Not enough space to create an additional FreeBSD partition";
885		break;
886	    }
887	    else {
888		char *val;
889		int size;
890		struct chunk *tmp;
891		char osize[80];
892		u_long flags = 0;
893
894		sprintf(osize, "%d", sz);
895		DialogX = 3;
896		DialogY = 2;
897		val = msgGetInput(osize,
898				  "Please specify the partition size in blocks or append a trailing M for\n"
899				  "megabytes or C for cylinders.  %d blocks (%dMB) are free.",
900				  sz, sz / ONE_MEG);
901		DialogX = DialogY = 0;
902		if (!val || (size = strtol(val, &cp, 0)) <= 0) {
903		    clear_wins();
904		    break;
905		}
906
907		if (*cp) {
908		    if (toupper(*cp) == 'M')
909			size *= ONE_MEG;
910		    else if (toupper(*cp) == 'C')
911			size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
912		}
913		if (size <= FS_MIN_SIZE) {
914		    msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
915		    clear_wins();
916		    break;
917		}
918		type = get_partition_type();
919		if (type == PART_NONE) {
920		    clear_wins();
921		    beep();
922		    break;
923		}
924
925		if (type == PART_FILESYSTEM) {
926		    if ((p = get_mountpoint(NULL)) == NULL) {
927			clear_wins();
928			beep();
929			break;
930		    }
931		    else if (!strcmp(p->mountpoint, "/"))
932			flags |= CHUNK_IS_ROOT;
933		    else
934			flags &= ~CHUNK_IS_ROOT;
935		}
936		else
937		    p = NULL;
938
939		if ((flags & CHUNK_IS_ROOT)) {
940		    if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) {
941			msgConfirm("This region cannot be used for your root partition as the\n"
942				   "FreeBSD boot code cannot deal with a root partition created\n"
943				   "in that location.  Please choose another location or smaller\n"
944				   "size for your root partition and try again!");
945			clear_wins();
946			break;
947		    }
948		    if (size < (ROOT_MIN_SIZE * ONE_MEG)) {
949			msgConfirm("Warning: This is smaller than the recommended size for a\n"
950				   "root partition.  For a variety of reasons, root\n"
951				   "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
952		    }
953		}
954		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
955					label_chunk_info[here].c,
956					size, part,
957					(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
958					flags);
959		if (!tmp) {
960		    msgConfirm("Unable to create the partition. Too big?");
961		    clear_wins();
962		    break;
963		}
964		if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) {
965		    msgConfirm("This region cannot be used for your root partition as it starts\n"
966			       "or extends past the 1024'th cylinder mark and is thus a\n"
967			       "poor location to boot from.  Please choose another\n"
968			       "location (or smaller size) for your root partition and try again!");
969		    Delete_Chunk(label_chunk_info[here].c->disk, tmp);
970		    clear_wins();
971		    break;
972		}
973		if (type != PART_SWAP) {
974		    /* This is needed to tell the newfs -u about the size */
975		    tmp->private_data = new_part(p->mountpoint, p->newfs, tmp->size);
976		    safe_free(p);
977		}
978		else
979		    tmp->private_data = p;
980		tmp->private_free = safe_free;
981		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
982		    variable_set2(DISK_LABELLED, "yes", 0);
983		record_label_chunks(devs, dev);
984		clear_wins();
985                /*** This is where we assign focus to new label so it shows ***/
986                {
987                    int i;
988		    label_focus = -1;
989                    for (i = 0; label_chunk_info[i].c; ++i) {
990                    	if (label_chunk_info[i].c == tmp) {
991			    label_focus = i;
992			    break;
993			}
994		    }
995		    if (label_focus == -1)
996                    	label_focus = i - 1;
997                }
998	    }
999	    break;
1000
1001	case KEY_DC:
1002	case 'D':	/* delete */
1003	    if (label_chunk_info[here].type == PART_SLICE) {
1004		msg = MSG_NOT_APPLICABLE;
1005		break;
1006	    }
1007	    else if (label_chunk_info[here].type == PART_FAT) {
1008		msg = "Use the Disk Partition Editor to delete DOS partitions";
1009		break;
1010	    }
1011	    Delete_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c);
1012	    if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1013		variable_set2(DISK_LABELLED, "yes", 0);
1014	    record_label_chunks(devs, dev);
1015	    break;
1016
1017	case 'M':	/* mount */
1018	    switch(label_chunk_info[here].type) {
1019	    case PART_SLICE:
1020		msg = MSG_NOT_APPLICABLE;
1021		break;
1022
1023	    case PART_SWAP:
1024		msg = "You don't need to specify a mountpoint for a swap partition.";
1025		break;
1026
1027	    case PART_FAT:
1028	    case PART_FILESYSTEM:
1029		oldp = label_chunk_info[here].c->private_data;
1030		p = get_mountpoint(label_chunk_info[here].c);
1031		if (p) {
1032		    if (!oldp)
1033		    	p->newfs = FALSE;
1034		    if (label_chunk_info[here].type == PART_FAT
1035			&& (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr")
1036			    || !strcmp(p->mountpoint, "/var"))) {
1037			msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
1038			strcpy(p->mountpoint, "/bogus");
1039		    }
1040		}
1041		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1042		    variable_set2(DISK_LABELLED, "yes", 0);
1043		record_label_chunks(devs, dev);
1044		clear_wins();
1045		break;
1046
1047	    default:
1048		msgFatal("Bogus partition under cursor???");
1049		break;
1050	    }
1051	    break;
1052
1053	case 'N':	/* Set newfs options */
1054	    if (label_chunk_info[here].c->private_data &&
1055		((PartInfo *)label_chunk_info[here].c->private_data)->newfs)
1056		getNewfsCmd(label_chunk_info[here].c->private_data);
1057	    else
1058		msg = MSG_NOT_APPLICABLE;
1059	    clear_wins();
1060	    break;
1061
1062	case 'T':	/* Toggle newfs state */
1063	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
1064		PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1065		label_chunk_info[here].c->private_data =
1066		    new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
1067		safe_free(pi);
1068		label_chunk_info[here].c->private_free = safe_free;
1069		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1070		    variable_set2(DISK_LABELLED, "yes", 0);
1071	    }
1072	    else
1073		msg = MSG_NOT_APPLICABLE;
1074	    break;
1075
1076	case 'U':
1077	    clear();
1078	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
1079		msgConfirm("You've already written out your changes -\n"
1080			   "it's too late to undo!");
1081	    }
1082	    else if (!msgYesNo("Are you SURE you want to Undo everything?")) {
1083		variable_unset(DISK_PARTITIONED);
1084		variable_unset(DISK_LABELLED);
1085		for (i = 0; devs[i]; i++) {
1086		    Disk *d;
1087
1088		    if (!devs[i]->enabled)
1089			continue;
1090		    else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1091			Free_Disk(devs[i]->private);
1092			devs[i]->private = d;
1093			diskPartition(devs[i]);
1094		    }
1095		}
1096		record_label_chunks(devs, dev);
1097	    }
1098	    clear_wins();
1099	    break;
1100
1101	case 'W':
1102	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
1103		msgConfirm("You've already written out your changes - if you\n"
1104			   "wish to overwrite them, you'll have to start this\n"
1105			   "procedure again from the beginning.");
1106	    }
1107	    else if (!msgYesNo("WARNING:  This should only be used when modifying an EXISTING\n"
1108			  "installation.  If you are installing FreeBSD for the first time\n"
1109			  "then you should simply type Q when you're finished here and your\n"
1110			  "changes will be committed in one batch automatically at the end of\n"
1111			  "these questions.\n\n"
1112			  "Are you absolutely sure you want to do this now?")) {
1113		variable_set2(DISK_LABELLED, "yes", 0);
1114		diskLabelCommit(NULL);
1115	    }
1116	    clear_wins();
1117	    break;
1118
1119	case '|':
1120	    if (!msgYesNo("Are you sure you want to go into Wizard mode?\n\n"
1121			  "This is an entirely undocumented feature which you are not\n"
1122			  "expected to understand!")) {
1123		int i;
1124		Device **devs;
1125
1126		dialog_clear();
1127		end_dialog();
1128		DialogActive = FALSE;
1129		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1130		if (!devs) {
1131		    msgConfirm("Can't find any disk devices!");
1132		    break;
1133		}
1134		for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1135		    if (devs[i]->enabled)
1136		    	slice_wizard(((Disk *)devs[i]->private));
1137		}
1138		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1139		    variable_set2(DISK_LABELLED, "yes", 0);
1140		DialogActive = TRUE;
1141		record_label_chunks(devs, dev);
1142		clear_wins();
1143	    }
1144	    else
1145		msg = "A most prudent choice!";
1146	    break;
1147
1148	case '\033':	/* ESC */
1149	case 'Q':
1150	    labeling = FALSE;
1151	    break;
1152
1153	default:
1154	    beep();
1155	    sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1156	    msg = _msg;
1157	    break;
1158	}
1159        if (label_chunk_info[here].type == PART_SLICE)
1160            pslice_focus = here;
1161        else
1162            label_focus = here;
1163    }
1164    return DITEM_SUCCESS | DITEM_RESTORE;
1165}
1166
1167static int
1168diskLabelNonInteractive(Device *dev)
1169{
1170    char *cp;
1171    PartType type;
1172    PartInfo *p;
1173    u_long flags = 0;
1174    int i, status;
1175    Device **devs;
1176    Disk *d;
1177
1178    status = DITEM_SUCCESS;
1179
1180    cp = variable_get(VAR_DISK);
1181    if (!cp) {
1182	dialog_clear();
1183	msgConfirm("diskLabel:  No disk selected - can't label automatically.");
1184	return DITEM_FAILURE;
1185    }
1186    devs = deviceFind(cp, DEVICE_TYPE_DISK);
1187    if (!devs) {
1188	msgConfirm("diskLabel: No disk device %s found!", cp);
1189	return DITEM_FAILURE;
1190    }
1191    if (dev)
1192	d = dev->private;
1193    else
1194	d = devs[0]->private;
1195#ifdef __alpha__
1196    maybe_dedicate(d);
1197#endif
1198    record_label_chunks(devs, dev);
1199    for (i = 0; label_chunk_info[i].c; i++) {
1200	Chunk *c1 = label_chunk_info[i].c;
1201
1202	if (label_chunk_info[i].type == PART_SLICE) {
1203	    char name[512];
1204	    int entries = 1;
1205
1206	    while (entries) {
1207		snprintf(name, sizeof name, "%s-%d", c1->name, entries);
1208		if ((cp = variable_get(name)) != NULL) {
1209		    int sz;
1210		    char typ[10], mpoint[50];
1211
1212		    if (sscanf(cp, "%s %d %s", typ, &sz, mpoint) != 3) {
1213			msgConfirm("For slice entry %s, got an invalid detail entry of: %s",  c1->name, cp);
1214			status = DITEM_FAILURE;
1215			continue;
1216		    }
1217		    else {
1218			Chunk *tmp;
1219
1220			if (!strcmp(typ, "swap")) {
1221			    type = PART_SWAP;
1222			    strcpy(mpoint, "SWAP");
1223			}
1224			else {
1225			    type = PART_FILESYSTEM;
1226			    if (!strcmp(mpoint, "/"))
1227				flags |= CHUNK_IS_ROOT;
1228			    else
1229				flags &= ~CHUNK_IS_ROOT;
1230			}
1231			if (!sz)
1232			    sz = space_free(c1);
1233			if (sz > space_free(c1)) {
1234			    msgConfirm("Not enough free space to create partition: %s", mpoint);
1235			    status = DITEM_FAILURE;
1236			    continue;
1237			}
1238			if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
1239						      (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
1240			    msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
1241			    status = DITEM_FAILURE;
1242			    break;
1243			}
1244			else {
1245			    tmp->private_data = new_part(mpoint, TRUE, sz);
1246			    tmp->private_free = safe_free;
1247			    status = DITEM_SUCCESS;
1248			}
1249		    }
1250		    entries++;
1251		}
1252		else {
1253		    /* No more matches, leave the loop */
1254		    entries = 0;
1255		}
1256	    }
1257	}
1258	else {
1259	    /* Must be something we can set a mountpoint for */
1260	    cp = variable_get(c1->name);
1261	    if (cp) {
1262		char mpoint[50], do_newfs[8];
1263		Boolean newfs = FALSE;
1264
1265		do_newfs[0] = '\0';
1266		if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
1267		    dialog_clear();
1268		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1269		    status = DITEM_FAILURE;
1270		    continue;
1271		}
1272		newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1273		if (c1->private_data) {
1274		    p = c1->private_data;
1275		    p->newfs = newfs;
1276		    strcpy(p->mountpoint, mpoint);
1277		}
1278		else {
1279		    c1->private_data = new_part(mpoint, newfs, 0);
1280		    c1->private_free = safe_free;
1281		}
1282		if (!strcmp(mpoint, "/"))
1283		    c1->flags |= CHUNK_IS_ROOT;
1284		else
1285		    c1->flags &= ~CHUNK_IS_ROOT;
1286	    }
1287	}
1288    }
1289    if (status == DITEM_SUCCESS)
1290	variable_set2(DISK_LABELLED, "yes", 0);
1291    return status;
1292}
1293