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