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