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