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