label.c revision 70005
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 70005 2000-12-14 02:49: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 G for\n"
905				  "gigabytes, M for megabytes, or C for cylinders.\n"
906				  "%d blocks (%dMB) are free.",
907				  sz, sz / ONE_MEG);
908		if (!val || (size = strtol(val, &cp, 0)) <= 0) {
909		    clear_wins();
910		    break;
911		}
912
913		if (*cp) {
914		    if (toupper(*cp) == 'M')
915			size *= ONE_MEG;
916		    else if (toupper(*cp) == 'G')
917			size *= ONE_GIG;
918		    else if (toupper(*cp) == 'C')
919			size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
920		}
921		if (size <= FS_MIN_SIZE) {
922		    msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
923		    clear_wins();
924		    break;
925		}
926		type = get_partition_type();
927		if (type == PART_NONE) {
928		    clear_wins();
929		    beep();
930		    break;
931		}
932
933		if (type == PART_FILESYSTEM) {
934		    if ((p = get_mountpoint(NULL)) == NULL) {
935			clear_wins();
936			beep();
937			break;
938		    }
939		    else if (!strcmp(p->mountpoint, "/"))
940			flags |= CHUNK_IS_ROOT;
941		    else
942			flags &= ~CHUNK_IS_ROOT;
943		}
944		else
945		    p = NULL;
946
947		if ((flags & CHUNK_IS_ROOT)) {
948		    if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) {
949			msgConfirm("This region cannot be used for your root partition as the\n"
950				   "FreeBSD boot code cannot deal with a root partition created\n"
951				   "in that location.  Please choose another location or smaller\n"
952				   "size for your root partition and try again!");
953			clear_wins();
954			break;
955		    }
956		    if (size < (ROOT_MIN_SIZE * ONE_MEG)) {
957			msgConfirm("Warning: This is smaller than the recommended size for a\n"
958				   "root partition.  For a variety of reasons, root\n"
959				   "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
960		    }
961		}
962		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
963					label_chunk_info[here].c,
964					size, part,
965					(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
966					flags);
967		if (!tmp) {
968		    msgConfirm("Unable to create the partition. Too big?");
969		    clear_wins();
970		    break;
971		}
972		if (type != PART_SWAP) {
973		    /* This is needed to tell the newfs -u about the size */
974		    tmp->private_data = new_part(p->mountpoint, p->newfs, tmp->size);
975		    safe_free(p);
976		}
977		else
978		    tmp->private_data = p;
979		tmp->private_free = safe_free;
980		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
981		    variable_set2(DISK_LABELLED, "yes", 0);
982		record_label_chunks(devs, dev);
983		clear_wins();
984                /*** This is where we assign focus to new label so it shows ***/
985                {
986                    int i;
987		    label_focus = -1;
988                    for (i = 0; label_chunk_info[i].c; ++i) {
989                    	if (label_chunk_info[i].c == tmp) {
990			    label_focus = i;
991			    break;
992			}
993		    }
994		    if (label_focus == -1)
995                    	label_focus = i - 1;
996                }
997	    }
998	    break;
999
1000	case KEY_DC:
1001	case 'D':	/* delete */
1002	    if (label_chunk_info[here].type == PART_SLICE) {
1003		msg = MSG_NOT_APPLICABLE;
1004		break;
1005	    }
1006	    else if (label_chunk_info[here].type == PART_FAT) {
1007		msg = "Use the Disk Partition Editor to delete DOS partitions";
1008		break;
1009	    }
1010	    Delete_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c);
1011	    if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1012		variable_set2(DISK_LABELLED, "yes", 0);
1013	    record_label_chunks(devs, dev);
1014	    break;
1015
1016	case 'M':	/* mount */
1017	    switch(label_chunk_info[here].type) {
1018	    case PART_SLICE:
1019		msg = MSG_NOT_APPLICABLE;
1020		break;
1021
1022	    case PART_SWAP:
1023		msg = "You don't need to specify a mountpoint for a swap partition.";
1024		break;
1025
1026	    case PART_FAT:
1027	    case PART_FILESYSTEM:
1028		oldp = label_chunk_info[here].c->private_data;
1029		p = get_mountpoint(label_chunk_info[here].c);
1030		if (p) {
1031		    if (!oldp)
1032		    	p->newfs = FALSE;
1033		    if (label_chunk_info[here].type == PART_FAT
1034			&& (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr")
1035			    || !strcmp(p->mountpoint, "/var"))) {
1036			msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
1037			strcpy(p->mountpoint, "/bogus");
1038		    }
1039		}
1040		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1041		    variable_set2(DISK_LABELLED, "yes", 0);
1042		record_label_chunks(devs, dev);
1043		clear_wins();
1044		break;
1045
1046	    default:
1047		msgFatal("Bogus partition under cursor???");
1048		break;
1049	    }
1050	    break;
1051
1052	case 'N':	/* Set newfs options */
1053	    if (label_chunk_info[here].c->private_data &&
1054		((PartInfo *)label_chunk_info[here].c->private_data)->newfs)
1055		getNewfsCmd(label_chunk_info[here].c->private_data);
1056	    else
1057		msg = MSG_NOT_APPLICABLE;
1058	    clear_wins();
1059	    break;
1060
1061	case 'T':	/* Toggle newfs state */
1062	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
1063		PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1064		label_chunk_info[here].c->private_data =
1065		    new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
1066		safe_free(pi);
1067		label_chunk_info[here].c->private_free = safe_free;
1068		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1069		    variable_set2(DISK_LABELLED, "yes", 0);
1070	    }
1071	    else
1072		msg = MSG_NOT_APPLICABLE;
1073	    break;
1074
1075	case 'U':
1076	    clear();
1077	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
1078		msgConfirm("You've already written out your changes -\n"
1079			   "it's too late to undo!");
1080	    }
1081	    else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
1082		variable_unset(DISK_PARTITIONED);
1083		variable_unset(DISK_LABELLED);
1084		for (i = 0; devs[i]; i++) {
1085		    Disk *d;
1086
1087		    if (!devs[i]->enabled)
1088			continue;
1089		    else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1090			Free_Disk(devs[i]->private);
1091			devs[i]->private = d;
1092			diskPartition(devs[i]);
1093		    }
1094		}
1095		record_label_chunks(devs, dev);
1096	    }
1097	    clear_wins();
1098	    break;
1099
1100	case 'W':
1101	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
1102		msgConfirm("You've already written out your changes - if you\n"
1103			   "wish to overwrite them, you'll have to start this\n"
1104			   "procedure again from the beginning.");
1105	    }
1106	    else if (!msgNoYes("WARNING:  This should only be used when modifying an EXISTING\n"
1107			  "installation.  If you are installing FreeBSD for the first time\n"
1108			  "then you should simply type Q when you're finished here and your\n"
1109			  "changes will be committed in one batch automatically at the end of\n"
1110			  "these questions.\n\n"
1111			  "Are you absolutely sure you want to do this now?")) {
1112		variable_set2(DISK_LABELLED, "yes", 0);
1113		diskLabelCommit(NULL);
1114	    }
1115	    clear_wins();
1116	    break;
1117
1118	case '|':
1119	    if (!msgNoYes("Are you sure you want to go into Wizard mode?\n\n"
1120			  "This is an entirely undocumented feature which you are not\n"
1121			  "expected to understand!")) {
1122		int i;
1123		Device **devs;
1124
1125		dialog_clear();
1126		end_dialog();
1127		DialogActive = FALSE;
1128		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1129		if (!devs) {
1130		    msgConfirm("Can't find any disk devices!");
1131		    break;
1132		}
1133		for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1134		    if (devs[i]->enabled)
1135		    	slice_wizard(((Disk *)devs[i]->private));
1136		}
1137		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1138		    variable_set2(DISK_LABELLED, "yes", 0);
1139		DialogActive = TRUE;
1140		record_label_chunks(devs, dev);
1141		clear_wins();
1142	    }
1143	    else
1144		msg = "A most prudent choice!";
1145	    break;
1146
1147	case '\033':	/* ESC */
1148	case 'Q':
1149	    labeling = FALSE;
1150	    break;
1151
1152	default:
1153	    beep();
1154	    sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1155	    msg = _msg;
1156	    break;
1157	}
1158        if (label_chunk_info[here].type == PART_SLICE)
1159            pslice_focus = here;
1160        else
1161            label_focus = here;
1162    }
1163    restorescr(w);
1164    return DITEM_SUCCESS;
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    cp = variable_get(VAR_DISK);
1180    if (!cp) {
1181	msgConfirm("diskLabel:  No disk selected - can't label automatically.");
1182	return DITEM_FAILURE;
1183    }
1184    devs = deviceFind(cp, DEVICE_TYPE_DISK);
1185    if (!devs) {
1186	msgConfirm("diskLabel: No disk device %s found!", cp);
1187	return DITEM_FAILURE;
1188    }
1189    if (dev)
1190	d = dev->private;
1191    else
1192	d = devs[0]->private;
1193#ifdef __alpha__
1194    maybe_dedicate(d);
1195#endif
1196    record_label_chunks(devs, dev);
1197    for (i = 0; label_chunk_info[i].c; i++) {
1198	Chunk *c1 = label_chunk_info[i].c;
1199
1200	if (label_chunk_info[i].type == PART_SLICE) {
1201	    char name[512];
1202	    int entries = 1;
1203
1204	    while (entries) {
1205		snprintf(name, sizeof name, "%s-%d", c1->name, entries);
1206		if ((cp = variable_get(name)) != NULL) {
1207		    int sz;
1208		    char typ[10], mpoint[50];
1209
1210		    if (sscanf(cp, "%s %d %s", typ, &sz, mpoint) != 3) {
1211			msgConfirm("For slice entry %s, got an invalid detail entry of: %s",  c1->name, cp);
1212			status = DITEM_FAILURE;
1213			continue;
1214		    }
1215		    else {
1216			Chunk *tmp;
1217
1218			if (!strcmp(typ, "swap")) {
1219			    type = PART_SWAP;
1220			    strcpy(mpoint, "SWAP");
1221			}
1222			else {
1223			    type = PART_FILESYSTEM;
1224			    if (!strcmp(mpoint, "/"))
1225				flags |= CHUNK_IS_ROOT;
1226			    else
1227				flags &= ~CHUNK_IS_ROOT;
1228			}
1229			if (!sz)
1230			    sz = space_free(c1);
1231			if (sz > space_free(c1)) {
1232			    msgConfirm("Not enough free space to create partition: %s", mpoint);
1233			    status = DITEM_FAILURE;
1234			    continue;
1235			}
1236			if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
1237						      (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
1238			    msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
1239			    status = DITEM_FAILURE;
1240			    break;
1241			}
1242			else {
1243			    tmp->private_data = new_part(mpoint, TRUE, sz);
1244			    tmp->private_free = safe_free;
1245			    status = DITEM_SUCCESS;
1246			}
1247		    }
1248		    entries++;
1249		}
1250		else {
1251		    /* No more matches, leave the loop */
1252		    entries = 0;
1253		}
1254	    }
1255	}
1256	else {
1257	    /* Must be something we can set a mountpoint for */
1258	    cp = variable_get(c1->name);
1259	    if (cp) {
1260		char mpoint[50], do_newfs[8];
1261		Boolean newfs = FALSE;
1262
1263		do_newfs[0] = '\0';
1264		if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
1265		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1266		    status = DITEM_FAILURE;
1267		    continue;
1268		}
1269		newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1270		if (c1->private_data) {
1271		    p = c1->private_data;
1272		    p->newfs = newfs;
1273		    strcpy(p->mountpoint, mpoint);
1274		}
1275		else {
1276		    c1->private_data = new_part(mpoint, newfs, 0);
1277		    c1->private_free = safe_free;
1278		}
1279		if (!strcmp(mpoint, "/"))
1280		    c1->flags |= CHUNK_IS_ROOT;
1281		else
1282		    c1->flags &= ~CHUNK_IS_ROOT;
1283	    }
1284	}
1285    }
1286    if (status == DITEM_SUCCESS)
1287	variable_set2(DISK_LABELLED, "yes", 0);
1288    return status;
1289}
1290