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