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