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