label.c revision 54473
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 54473 1999-12-12 04:58:02Z jkh $
8 *
9 * Copyright (c) 1995
10 *	Jordan Hubbard.  All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer,
17 *    verbatim and that no modifications are made prior to this
18 *    point in the file.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in the
21 *    documentation and/or other materials provided with the distribution.
22 *
23 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 */
36
37#include "sysinstall.h"
38#include <ctype.h>
39#include <sys/disklabel.h>
40#include <sys/param.h>
41#include <sys/sysctl.h>
42
43/*
44 * Everything to do with editing the contents of disk labels.
45 */
46
47/* A nice message we use a lot in the disklabel editor */
48#define MSG_NOT_APPLICABLE	"That option is not applicable here"
49
50/* Where to start printing the freebsd slices */
51#define CHUNK_SLICE_START_ROW		2
52#define CHUNK_PART_START_ROW		11
53
54/* The smallest filesystem we're willing to create */
55#define FS_MIN_SIZE			ONE_MEG
56
57/* The smallest root filesystem we're willing to create */
58#ifdef __alpha__
59#define ROOT_MIN_SIZE			40
60#else
61#define ROOT_MIN_SIZE			30
62#endif
63
64/* The default root filesystem size */
65#ifdef __alpha__
66#define ROOT_DEFAULT_SIZE		60
67#else
68#define ROOT_DEFAULT_SIZE		40
69#endif
70
71/* The smallest swap partition we want to create by default */
72#define SWAP_MIN_SIZE			16
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 | DITEM_RESTORE;
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	    i |= DITEM_RESTORE;
175	}
176    }
177    if (DITEM_STATUS(i) != DITEM_FAILURE) {
178	char *cp;
179
180	if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
181	    variable_set2(DISK_LABELLED, "yes", 0);
182    }
183    return i;
184}
185
186int
187diskLabelCommit(dialogMenuItem *self)
188{
189    char *cp;
190    int i;
191
192    /* Already done? */
193    if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes"))
194	i = DITEM_SUCCESS;
195    else if (!cp) {
196	msgConfirm("You must assign disk labels before this option can be used.");
197	i = DITEM_FAILURE;
198    }
199    /* The routine will guard against redundant writes, just as this one does */
200    else if (DITEM_STATUS(diskPartitionWrite(self)) != DITEM_SUCCESS)
201	i = DITEM_FAILURE;
202    else if (DITEM_STATUS(installFilesystems(self)) != DITEM_SUCCESS)
203	i = DITEM_FAILURE;
204    else {
205	msgInfo("All filesystem information written successfully.");
206	variable_set2(DISK_LABELLED, "written", 0);
207	i = DITEM_SUCCESS;
208    }
209    return i;
210}
211
212/* See if we're already using a desired partition name */
213static Boolean
214check_conflict(char *name)
215{
216    int i;
217
218    for (i = 0; label_chunk_info[i].c; i++)
219	if ((label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT)
220	    && label_chunk_info[i].c->private_data
221	    && !strcmp(((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint, name))
222	    return TRUE;
223    return FALSE;
224}
225
226/* How much space is in this FreeBSD slice? */
227static int
228space_free(struct chunk *c)
229{
230    struct chunk *c1;
231    int sz = c->size;
232
233    for (c1 = c->part; c1; c1 = c1->next) {
234	if (c1->type != unused)
235	    sz -= c1->size;
236    }
237    if (sz < 0)
238	msgFatal("Partitions are larger than actual chunk??");
239    return sz;
240}
241
242/* Snapshot the current situation into the displayed chunks structure */
243static void
244record_label_chunks(Device **devs, Device *dev)
245{
246    int i, j, p;
247    struct chunk *c1, *c2;
248    Disk *d;
249
250    j = p = 0;
251    /* First buzz through and pick up the FreeBSD slices */
252    for (i = 0; devs[i]; i++) {
253	if ((dev && devs[i] != dev) || !devs[i]->enabled)
254	    continue;
255	d = (Disk *)devs[i]->private;
256	if (!d->chunks)
257	    msgFatal("No chunk list found for %s!", d->name);
258
259	/* Put the slice entries first */
260	for (c1 = d->chunks->part; c1; c1 = c1->next) {
261	    if (c1->type == freebsd) {
262		label_chunk_info[j].type = PART_SLICE;
263		label_chunk_info[j].c = c1;
264		++j;
265	    }
266	}
267    }
268
269    /* Now run through again and get the FreeBSD partition entries */
270    for (i = 0; devs[i]; i++) {
271	if (!devs[i]->enabled)
272	    continue;
273	d = (Disk *)devs[i]->private;
274	/* Then buzz through and pick up the partitions */
275	for (c1 = d->chunks->part; c1; c1 = c1->next) {
276	    if (c1->type == freebsd) {
277		for (c2 = c1->part; c2; c2 = c2->next) {
278		    if (c2->type == part) {
279			if (c2->subtype == FS_SWAP)
280			    label_chunk_info[j].type = PART_SWAP;
281			else
282			    label_chunk_info[j].type = PART_FILESYSTEM;
283			label_chunk_info[j].c = c2;
284			++j;
285		    }
286		}
287	    }
288	    else if (c1->type == fat) {
289		label_chunk_info[j].type = PART_FAT;
290		label_chunk_info[j].c = c1;
291		++j;
292	    }
293	}
294    }
295    label_chunk_info[j].c = NULL;
296    if (here >= j) {
297	here = j  ? j - 1 : 0;
298    }
299}
300
301/* A new partition entry */
302static PartInfo *
303new_part(char *mpoint, Boolean newfs, u_long size)
304{
305    PartInfo *ret;
306
307    if (!mpoint)
308	mpoint = "/change_me";
309
310    ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
311    sstrncpy(ret->mountpoint, mpoint, FILENAME_MAX);
312    strcpy(ret->newfs_cmd, "newfs ");
313    strcat(ret->newfs_cmd, variable_get(VAR_NEWFS_ARGS));
314    ret->newfs = newfs;
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    if (!old) {
332	DialogX = 14;
333	DialogY = 16;
334    }
335    val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
336    DialogX = DialogY = 0;
337    if (!val || !*val) {
338	if (!old)
339	    return NULL;
340	else {
341	    free(old->private_data);
342	    old->private_data = NULL;
343	}
344	return NULL;
345    }
346
347    /* Is it just the same value? */
348    if (tmp && !strcmp(tmp->mountpoint, val))
349	return NULL;
350
351    /* Did we use it already? */
352    if (check_conflict(val)) {
353	msgConfirm("You already have a mount point for %s assigned!", val);
354	return NULL;
355    }
356
357    /* Is it bogus? */
358    if (*val != '/') {
359	msgConfirm("Mount point must start with a / character");
360	return NULL;
361    }
362
363    /* Is it going to be mounted on root? */
364    if (!strcmp(val, "/")) {
365	if (old)
366	    old->flags |= CHUNK_IS_ROOT;
367    }
368    else if (old)
369	old->flags &= ~CHUNK_IS_ROOT;
370
371    safe_free(tmp);
372    val = string_skipwhite(string_prune(val));
373    tmp = new_part(val, TRUE, 0);
374    if (old) {
375	old->private_data = tmp;
376	old->private_free = safe_free;
377    }
378    return tmp;
379}
380
381/* Get the type of the new partiton */
382static PartType
383get_partition_type(void)
384{
385    char selection[20];
386    int i;
387
388    static unsigned char *fs_types[] = {
389	"FS",
390	"A file system",
391	"Swap",
392	"A swap partition.",
393    };
394    DialogX = 7;
395    DialogY = 8;
396    i = dialog_menu("Please choose a partition type",
397		    "If you want to use this partition for swap space, select Swap.\n"
398		    "If you want to put a filesystem on it, choose FS.",
399		    -1, -1, 2, 2, fs_types, selection, NULL, NULL);
400    DialogX = DialogY = 0;
401    if (!i) {
402	if (!strcmp(selection, "FS"))
403	    return PART_FILESYSTEM;
404	else if (!strcmp(selection, "Swap"))
405	    return PART_SWAP;
406    }
407    return PART_NONE;
408}
409
410/* If the user wants a special newfs command for this, set it */
411static void
412getNewfsCmd(PartInfo *p)
413{
414    char *val;
415
416    val = msgGetInput(p->newfs_cmd,
417		      "Please enter the newfs command and options you'd like to use in\n"
418		      "creating this file system.");
419    if (val)
420	sstrncpy(p->newfs_cmd, val, NEWFS_CMD_MAX);
421}
422
423#define MAX_MOUNT_NAME	10
424
425#define PART_PART_COL	0
426#define PART_MOUNT_COL	10
427#define PART_SIZE_COL	(PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
428#define PART_NEWFS_COL	(PART_SIZE_COL + 8)
429#define PART_OFF	38
430
431#define TOTAL_AVAIL_LINES       (10)
432#define PSLICE_SHOWABLE          (4)
433
434
435/* stick this all up on the screen */
436static void
437print_label_chunks(void)
438{
439    int  i, j, srow, prow, pcol;
440    int  sz;
441    char clrmsg[80];
442    int ChunkPartStartRow;
443    WINDOW *ChunkWin;
444
445    /********************************************************/
446    /*** These values are for controling screen resources ***/
447    /*** Each label line holds up to 2 labels, so beware! ***/
448    /*** strategy will be to try to always make sure the  ***/
449    /*** highlighted label is in the active display area. ***/
450    /********************************************************/
451    int  pslice_max, label_max;
452    int  pslice_count, label_count, label_focus_found, pslice_focus_found;
453
454    attrset(A_REVERSE);
455    mvaddstr(0, 25, "FreeBSD Disklabel Editor");
456    attrset(A_NORMAL);
457
458    /*** Count the number of parition slices ***/
459    pslice_count = 0;
460    for (i = 0; label_chunk_info[i].c ; i++) {
461        if (label_chunk_info[i].type == PART_SLICE)
462            ++pslice_count;
463    }
464    pslice_max = pslice_count;
465
466    /*** 4 line max for partition slices ***/
467    if (pslice_max > PSLICE_SHOWABLE) {
468        pslice_max = PSLICE_SHOWABLE;
469    }
470    ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3 + pslice_max;
471
472    /*** View partition slices modulo pslice_max ***/
473    label_max = TOTAL_AVAIL_LINES - pslice_max;
474
475    for (i = 0; i < 2; i++) {
476	mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
477	mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
478
479	mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
480	mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
481
482	mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 3, "Size");
483	mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 3, "----");
484
485	mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
486	mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
487    }
488    srow = CHUNK_SLICE_START_ROW;
489    prow = 0;
490    pcol = 0;
491
492    /*** these variables indicate that the focused item is shown currently ***/
493    label_focus_found = 0;
494    pslice_focus_found = 0;
495
496    label_count = 0;
497    pslice_count = 0;
498    mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "          ");
499    mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "          ");
500
501    ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
502
503    wclear(ChunkWin);
504    /*** wrefresh(ChunkWin); ***/
505
506    for (i = 0; label_chunk_info[i].c; i++) {
507	/* Is it a slice entry displayed at the top? */
508	if (label_chunk_info[i].type == PART_SLICE) {
509            /*** This causes the new pslice to replace the previous display ***/
510            /*** focus must remain on the most recently active pslice       ***/
511            if (pslice_count == pslice_max) {
512                if (pslice_focus_found) {
513                    /*** This is where we can mark the more following ***/
514                    attrset(A_BOLD);
515                    mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
516                    attrset(A_NORMAL);
517                    continue;
518                }
519                else {
520                    /*** this is where we set the more previous ***/
521                    attrset(A_BOLD);
522                    mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
523                    attrset(A_NORMAL);
524                    pslice_count = 0;
525                    srow = CHUNK_SLICE_START_ROW;
526                }
527            }
528
529	    sz = space_free(label_chunk_info[i].c);
530	    if (i == here)
531		attrset(ATTR_SELECTED);
532            if (i == pslice_focus)
533                pslice_focus_found = -1;
534
535	    mvprintw(srow++, 0,
536		     "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
537		     label_chunk_info[i].c->disk->name, label_chunk_info[i].c->name,
538		     sz, (sz / ONE_MEG));
539	    attrset(A_NORMAL);
540	    clrtoeol();
541	    move(0, 0);
542	    /*** refresh(); ***/
543            ++pslice_count;
544	}
545	/* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
546	else {
547	    char onestr[PART_OFF], num[10], *mountpoint, *newfs;
548
549	    /*
550	     * We copy this into a blank-padded string so that it looks like
551	     * a solid bar in reverse-video
552	     */
553	    memset(onestr, ' ', PART_OFF - 1);
554	    onestr[PART_OFF - 1] = '\0';
555
556            /*** Track how many labels have been displayed ***/
557            if (label_count == ((label_max - 1 ) * 2)) {
558                if (label_focus_found) {
559                    continue;
560                }
561                else {
562                    label_count = 0;
563                    prow = 0;
564                    pcol = 0;
565                }
566            }
567
568	    /* Go for two columns if we've written one full columns worth */
569	    /*** if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) ***/
570            if (label_count == label_max - 1) {
571		pcol = PART_OFF;
572		prow = 0;
573	    }
574	    memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
575	    /* If it's a filesystem, display the mountpoint */
576	    if (label_chunk_info[i].c->private_data
577		&& (label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT))
578	        mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
579	    else if (label_chunk_info[i].type == PART_SWAP)
580		mountpoint = "swap";
581	    else
582	        mountpoint = "<none>";
583
584	    /* Now display the newfs field */
585	    if (label_chunk_info[i].type == PART_FAT)
586	        newfs = "DOS";
587	    else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM)
588		newfs = ((PartInfo *)label_chunk_info[i].c->private_data)->newfs ? "UFS Y" : "UFS N";
589	    else if (label_chunk_info[i].type == PART_SWAP)
590		newfs = "SWAP";
591	    else
592		newfs = "*";
593	    for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
594		onestr[PART_MOUNT_COL + j] = mountpoint[j];
595	    snprintf(num, 10, "%5ldMB", label_chunk_info[i].c->size ? label_chunk_info[i].c->size / ONE_MEG : 0);
596	    memcpy(onestr + PART_SIZE_COL, num, strlen(num));
597	    memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
598	    onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
599            if (i == label_focus) {
600                label_focus_found = -1;
601                wattrset(ChunkWin, A_BOLD);
602            }
603	    if (i == here)
604		wattrset(ChunkWin, ATTR_SELECTED);
605
606            /*** lazy man's way of padding this string ***/
607            while (strlen( onestr ) < 37)
608                strcat(onestr, " ");
609
610	    mvwaddstr(ChunkWin, prow, pcol, onestr);
611	    wattrset(ChunkWin, A_NORMAL);
612	    move(0, 0);
613	    ++prow;
614            ++label_count;
615	}
616    }
617
618    /*** this will erase all the extra stuff ***/
619    memset(clrmsg, ' ', 37);
620    clrmsg[37] = '\0';
621
622    while (pslice_count < pslice_max) {
623        mvprintw(srow++, 0, clrmsg);
624        clrtoeol();
625        ++pslice_count;
626    }
627    while (label_count < (2 * (label_max - 1))) {
628        mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
629	++label_count;
630	if (prow == (label_max - 1)) {
631	    prow = 0;
632	    pcol = PART_OFF;
633	}
634    }
635    refresh();
636    wrefresh(ChunkWin);
637}
638
639static void
640print_command_summary(void)
641{
642    mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
643    mvprintw(18, 0, "C = Create      D = Delete         M = Mount pt.");
644    if (!RunningAsInit)
645	mvprintw(18, 49, "W = Write");
646    mvprintw(19, 0, "N = Newfs Opts  T = Newfs Toggle   U = Undo      Q = Finish");
647    mvprintw(20, 0, "A = Auto Defaults for all!");
648    mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
649    move(0, 0);
650}
651
652static void
653clear_wins(void)
654{
655    extern void print_label_chunks();
656    clear();
657    print_label_chunks();
658}
659
660#ifdef __alpha__
661
662/*
663 * If there isn't a freebsd chunk already (i.e. there is no label),
664 * dedicate the disk.
665 */
666static void
667maybe_dedicate(Disk* d)
668{
669    struct chunk *c;
670
671    for (c = d->chunks->part; c; c = c->next) {
672	if (c->type == freebsd)
673	    break;
674    }
675
676    if (!c) {
677	msgDebug("dedicating disk");
678	All_FreeBSD(d, 1);
679    }
680}
681
682#endif
683
684static int
685diskLabel(Device *dev)
686{
687    int sz, key = 0;
688    Boolean labeling;
689    char *msg = NULL;
690    PartInfo *p, *oldp;
691    PartType type;
692    Device **devs;
693#ifdef __alpha__
694    int i;
695#endif
696
697    label_focus = 0;
698    pslice_focus = 0;
699    here = 0;
700
701    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
702    if (!devs) {
703	msgConfirm("No disks found!");
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		DialogX = 3;
909		DialogY = 2;
910		val = msgGetInput(osize,
911				  "Please specify the partition size in blocks or append a trailing M for\n"
912				  "megabytes or C for cylinders.  %d blocks (%dMB) are free.",
913				  sz, sz / ONE_MEG);
914		DialogX = DialogY = 0;
915		if (!val || (size = strtol(val, &cp, 0)) <= 0) {
916		    clear_wins();
917		    break;
918		}
919
920		if (*cp) {
921		    if (toupper(*cp) == 'M')
922			size *= ONE_MEG;
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 ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) {
978		    msgConfirm("This region cannot be used for your root partition as it starts\n"
979			       "or extends past the 1024'th cylinder mark and is thus a\n"
980			       "poor location to boot from.  Please choose another\n"
981			       "location (or smaller size) for your root partition and try again!");
982		    Delete_Chunk(label_chunk_info[here].c->disk, tmp);
983		    clear_wins();
984		    break;
985		}
986		if (type != PART_SWAP) {
987		    /* This is needed to tell the newfs -u about the size */
988		    tmp->private_data = new_part(p->mountpoint, p->newfs, tmp->size);
989		    safe_free(p);
990		}
991		else
992		    tmp->private_data = p;
993		tmp->private_free = safe_free;
994		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
995		    variable_set2(DISK_LABELLED, "yes", 0);
996		record_label_chunks(devs, dev);
997		clear_wins();
998                /*** This is where we assign focus to new label so it shows ***/
999                {
1000                    int i;
1001		    label_focus = -1;
1002                    for (i = 0; label_chunk_info[i].c; ++i) {
1003                    	if (label_chunk_info[i].c == tmp) {
1004			    label_focus = i;
1005			    break;
1006			}
1007		    }
1008		    if (label_focus == -1)
1009                    	label_focus = i - 1;
1010                }
1011	    }
1012	    break;
1013
1014	case KEY_DC:
1015	case 'D':	/* delete */
1016	    if (label_chunk_info[here].type == PART_SLICE) {
1017		msg = MSG_NOT_APPLICABLE;
1018		break;
1019	    }
1020	    else if (label_chunk_info[here].type == PART_FAT) {
1021		msg = "Use the Disk Partition Editor to delete DOS partitions";
1022		break;
1023	    }
1024	    Delete_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c);
1025	    if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1026		variable_set2(DISK_LABELLED, "yes", 0);
1027	    record_label_chunks(devs, dev);
1028	    break;
1029
1030	case 'M':	/* mount */
1031	    switch(label_chunk_info[here].type) {
1032	    case PART_SLICE:
1033		msg = MSG_NOT_APPLICABLE;
1034		break;
1035
1036	    case PART_SWAP:
1037		msg = "You don't need to specify a mountpoint for a swap partition.";
1038		break;
1039
1040	    case PART_FAT:
1041	    case PART_FILESYSTEM:
1042		oldp = label_chunk_info[here].c->private_data;
1043		p = get_mountpoint(label_chunk_info[here].c);
1044		if (p) {
1045		    if (!oldp)
1046		    	p->newfs = FALSE;
1047		    if (label_chunk_info[here].type == PART_FAT
1048			&& (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr")
1049			    || !strcmp(p->mountpoint, "/var"))) {
1050			msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
1051			strcpy(p->mountpoint, "/bogus");
1052		    }
1053		}
1054		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1055		    variable_set2(DISK_LABELLED, "yes", 0);
1056		record_label_chunks(devs, dev);
1057		clear_wins();
1058		break;
1059
1060	    default:
1061		msgFatal("Bogus partition under cursor???");
1062		break;
1063	    }
1064	    break;
1065
1066	case 'N':	/* Set newfs options */
1067	    if (label_chunk_info[here].c->private_data &&
1068		((PartInfo *)label_chunk_info[here].c->private_data)->newfs)
1069		getNewfsCmd(label_chunk_info[here].c->private_data);
1070	    else
1071		msg = MSG_NOT_APPLICABLE;
1072	    clear_wins();
1073	    break;
1074
1075	case 'T':	/* Toggle newfs state */
1076	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
1077		PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1078		label_chunk_info[here].c->private_data =
1079		    new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
1080		safe_free(pi);
1081		label_chunk_info[here].c->private_free = safe_free;
1082		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1083		    variable_set2(DISK_LABELLED, "yes", 0);
1084	    }
1085	    else
1086		msg = MSG_NOT_APPLICABLE;
1087	    break;
1088
1089	case 'U':
1090	    clear();
1091	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
1092		msgConfirm("You've already written out your changes -\n"
1093			   "it's too late to undo!");
1094	    }
1095	    else if (!msgYesNo("Are you SURE you want to Undo everything?")) {
1096		variable_unset(DISK_PARTITIONED);
1097		variable_unset(DISK_LABELLED);
1098		for (i = 0; devs[i]; i++) {
1099		    Disk *d;
1100
1101		    if (!devs[i]->enabled)
1102			continue;
1103		    else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1104			Free_Disk(devs[i]->private);
1105			devs[i]->private = d;
1106			diskPartition(devs[i]);
1107		    }
1108		}
1109		record_label_chunks(devs, dev);
1110	    }
1111	    clear_wins();
1112	    break;
1113
1114	case 'W':
1115	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
1116		msgConfirm("You've already written out your changes - if you\n"
1117			   "wish to overwrite them, you'll have to start this\n"
1118			   "procedure again from the beginning.");
1119	    }
1120	    else if (!msgYesNo("WARNING:  This should only be used when modifying an EXISTING\n"
1121			  "installation.  If you are installing FreeBSD for the first time\n"
1122			  "then you should simply type Q when you're finished here and your\n"
1123			  "changes will be committed in one batch automatically at the end of\n"
1124			  "these questions.\n\n"
1125			  "Are you absolutely sure you want to do this now?")) {
1126		variable_set2(DISK_LABELLED, "yes", 0);
1127		diskLabelCommit(NULL);
1128	    }
1129	    clear_wins();
1130	    break;
1131
1132	case '|':
1133	    if (!msgYesNo("Are you sure you want to go into Wizard mode?\n\n"
1134			  "This is an entirely undocumented feature which you are not\n"
1135			  "expected to understand!")) {
1136		int i;
1137		Device **devs;
1138
1139		dialog_clear();
1140		end_dialog();
1141		DialogActive = FALSE;
1142		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1143		if (!devs) {
1144		    msgConfirm("Can't find any disk devices!");
1145		    break;
1146		}
1147		for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1148		    if (devs[i]->enabled)
1149		    	slice_wizard(((Disk *)devs[i]->private));
1150		}
1151		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1152		    variable_set2(DISK_LABELLED, "yes", 0);
1153		DialogActive = TRUE;
1154		record_label_chunks(devs, dev);
1155		clear_wins();
1156	    }
1157	    else
1158		msg = "A most prudent choice!";
1159	    break;
1160
1161	case '\033':	/* ESC */
1162	case 'Q':
1163	    labeling = FALSE;
1164	    break;
1165
1166	default:
1167	    beep();
1168	    sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1169	    msg = _msg;
1170	    break;
1171	}
1172        if (label_chunk_info[here].type == PART_SLICE)
1173            pslice_focus = here;
1174        else
1175            label_focus = here;
1176    }
1177    return DITEM_SUCCESS | DITEM_RESTORE;
1178}
1179
1180static int
1181diskLabelNonInteractive(Device *dev)
1182{
1183    char *cp;
1184    PartType type;
1185    PartInfo *p;
1186    u_long flags = 0;
1187    int i, status;
1188    Device **devs;
1189    Disk *d;
1190
1191    status = DITEM_SUCCESS;
1192
1193    cp = variable_get(VAR_DISK);
1194    if (!cp) {
1195	dialog_clear();
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		    dialog_clear();
1281		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1282		    status = DITEM_FAILURE;
1283		    continue;
1284		}
1285		newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1286		if (c1->private_data) {
1287		    p = c1->private_data;
1288		    p->newfs = newfs;
1289		    strcpy(p->mountpoint, mpoint);
1290		}
1291		else {
1292		    c1->private_data = new_part(mpoint, newfs, 0);
1293		    c1->private_free = safe_free;
1294		}
1295		if (!strcmp(mpoint, "/"))
1296		    c1->flags |= CHUNK_IS_ROOT;
1297		else
1298		    c1->flags &= ~CHUNK_IS_ROOT;
1299	    }
1300	}
1301    }
1302    if (status == DITEM_SUCCESS)
1303	variable_set2(DISK_LABELLED, "yes", 0);
1304    return status;
1305}
1306