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