label.c revision 133241
1218822Sdim/*
2130561Sobrien * The new sysinstall program.
3218822Sdim *
4218822Sdim * 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 133241 2004-08-07 01:19:54Z marcel $
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 <inttypes.h>
40#include <libdisk.h>
41#include <sys/disklabel.h>
42#include <sys/param.h>
43#include <sys/sysctl.h>
44
45#define AUTO_HOME	0	/* do not create /home automatically */
46
47/*
48 * Everything to do with editing the contents of disk labels.
49 */
50
51/* A nice message we use a lot in the disklabel editor */
52#define MSG_NOT_APPLICABLE	"That option is not applicable here"
53
54/* Where to start printing the freebsd slices */
55#define CHUNK_SLICE_START_ROW		2
56#define CHUNK_PART_START_ROW		11
57
58/* The smallest filesystem we're willing to create */
59#define FS_MIN_SIZE			ONE_MEG
60
61/*
62 * Minimum partition sizes
63 */
64#if defined(__alpha__) || defined(__ia64__) || defined(__sparc64__) || defined(__amd64__)
65#define ROOT_MIN_SIZE			128
66#else
67#define ROOT_MIN_SIZE			118
68#endif
69#define SWAP_MIN_SIZE			32
70#define USR_MIN_SIZE			80
71#define VAR_MIN_SIZE			20
72#define TMP_MIN_SIZE			20
73#define HOME_MIN_SIZE			20
74
75/*
76 * Swap size limit for auto-partitioning (4G).
77 */
78#define SWAP_AUTO_LIMIT_SIZE		4096
79
80/*
81 * Default partition sizes.  If we do not have sufficient disk space
82 * for this configuration we scale things relative to the NOM vs DEFAULT
83 * sizes.  If the disk is larger then /home will get any remaining space.
84 */
85#define ROOT_DEFAULT_SIZE		256
86#define USR_DEFAULT_SIZE		3072
87#define VAR_DEFAULT_SIZE		256
88#define TMP_DEFAULT_SIZE		256
89#define HOME_DEFAULT_SIZE		USR_DEFAULT_SIZE
90
91/*
92 * Nominal partition sizes.  These are used to scale the default sizes down
93 * when we have insufficient disk space.  If this isn't sufficient we scale
94 * down using the MIN sizes instead.
95 */
96#define ROOT_NOMINAL_SIZE		192
97#define USR_NOMINAL_SIZE		512
98#define VAR_NOMINAL_SIZE		64
99#define TMP_NOMINAL_SIZE		64
100#define HOME_NOMINAL_SIZE		USR_NOMINAL_SIZE
101
102/* The bottom-most row we're allowed to scribble on */
103#define CHUNK_ROW_MAX			16
104
105
106/* All the chunks currently displayed on the screen */
107static struct {
108    struct chunk *c;
109    PartType type;
110} label_chunk_info[MAX_CHUNKS + 1];
111static int here;
112
113/*** with this value we try to track the most recently added label ***/
114static int label_focus = 0, pslice_focus = 0;
115
116static int diskLabel(Device *dev);
117static int diskLabelNonInteractive(Device *dev);
118static char *try_auto_label(Device **devs, Device *dev, int perc, int *req);
119
120static int
121labelHook(dialogMenuItem *selected)
122{
123    Device **devs = NULL;
124
125    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
126    if (!devs) {
127	msgConfirm("Unable to find disk %s!", selected->prompt);
128	return DITEM_FAILURE;
129    }
130    /* Toggle enabled status? */
131    if (!devs[0]->enabled) {
132	devs[0]->enabled = TRUE;
133	diskLabel(devs[0]);
134    }
135    else
136	devs[0]->enabled = FALSE;
137    return DITEM_SUCCESS;
138}
139
140static int
141labelCheck(dialogMenuItem *selected)
142{
143    Device **devs = NULL;
144
145    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
146    if (!devs || devs[0]->enabled == FALSE)
147	return FALSE;
148    return TRUE;
149}
150
151int
152diskLabelEditor(dialogMenuItem *self)
153{
154    DMenu *menu;
155    Device **devs;
156    int i, cnt;
157
158    i = 0;
159    cnt = diskGetSelectCount(&devs);
160    if (cnt == -1) {
161	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
162		   "properly probed at boot time.  See the Hardware Guide on the\n"
163		   "Documentation menu for clues on diagnosing this type of problem.");
164	return DITEM_FAILURE;
165    }
166    else if (cnt) {
167	/* Some are already selected */
168	if (variable_get(VAR_NONINTERACTIVE) &&
169	  !variable_get(VAR_DISKINTERACTIVE))
170	    i = diskLabelNonInteractive(NULL);
171	else
172	    i = diskLabel(NULL);
173    }
174    else {
175	/* No disks are selected, fall-back case now */
176	cnt = deviceCount(devs);
177	if (cnt == 1) {
178	    devs[0]->enabled = TRUE;
179	    if (variable_get(VAR_NONINTERACTIVE) &&
180	      !variable_get(VAR_DISKINTERACTIVE))
181		i = diskLabelNonInteractive(devs[0]);
182	    else
183		i = diskLabel(devs[0]);
184	}
185	else {
186	    menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, labelHook, labelCheck);
187	    if (!menu) {
188		msgConfirm("No devices suitable for installation found!\n\n"
189			   "Please verify that your disk controller (and attached drives)\n"
190			   "were detected properly.  This can be done by pressing the\n"
191			   "[Scroll Lock] key and using the Arrow keys to move back to\n"
192			   "the boot messages.  Press [Scroll Lock] again to return.");
193		i = DITEM_FAILURE;
194	    }
195	    else {
196		i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
197		free(menu);
198	    }
199	}
200    }
201    if (DITEM_STATUS(i) != DITEM_FAILURE) {
202	if (variable_cmp(DISK_LABELLED, "written"))
203	    variable_set2(DISK_LABELLED, "yes", 0);
204    }
205    return i;
206}
207
208int
209diskLabelCommit(dialogMenuItem *self)
210{
211    char *cp;
212    int i;
213
214    /* Already done? */
215    if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes"))
216	i = DITEM_SUCCESS;
217    else if (!cp) {
218	msgConfirm("You must assign disk labels before this option can be used.");
219	i = DITEM_FAILURE;
220    }
221    /* The routine will guard against redundant writes, just as this one does */
222    else if (DITEM_STATUS(diskPartitionWrite(self)) != DITEM_SUCCESS)
223	i = DITEM_FAILURE;
224    else if (DITEM_STATUS(installFilesystems(self)) != DITEM_SUCCESS)
225	i = DITEM_FAILURE;
226    else {
227	msgInfo("All filesystem information written successfully.");
228	variable_set2(DISK_LABELLED, "written", 0);
229	i = DITEM_SUCCESS;
230    }
231    return i;
232}
233
234/* See if we're already using a desired partition name */
235static Boolean
236check_conflict(char *name)
237{
238    int i;
239
240    for (i = 0; label_chunk_info[i].c; i++)
241	if ((label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT
242	    || label_chunk_info[i].type == PART_EFI) && label_chunk_info[i].c->private_data
243	    && !strcmp(((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint, name))
244	    return TRUE;
245    return FALSE;
246}
247
248/* How much space is in this FreeBSD slice? */
249static daddr_t
250space_free(struct chunk *c)
251{
252    struct chunk *c1;
253    daddr_t sz = c->size;
254
255    for (c1 = c->part; c1; c1 = c1->next) {
256	if (c1->type != unused)
257	    sz -= c1->size;
258    }
259    if (sz < 0)
260	msgFatal("Partitions are larger than actual chunk??");
261    return sz;
262}
263
264/* Snapshot the current situation into the displayed chunks structure */
265static void
266record_label_chunks(Device **devs, Device *dev)
267{
268    int i, j, p;
269    struct chunk *c1, *c2;
270    Disk *d;
271
272    j = p = 0;
273    /* First buzz through and pick up the FreeBSD slices */
274    for (i = 0; devs[i]; i++) {
275	if ((dev && devs[i] != dev) || !devs[i]->enabled)
276	    continue;
277	d = (Disk *)devs[i]->private;
278	if (!d->chunks)
279	    msgFatal("No chunk list found for %s!", d->name);
280
281#ifdef __ia64__
282	label_chunk_info[j].type = PART_SLICE;
283	label_chunk_info[j].c = d->chunks;
284	j++;
285#endif
286
287	/* Put the slice entries first */
288	for (c1 = d->chunks->part; c1; c1 = c1->next) {
289	    if (c1->type == freebsd) {
290		label_chunk_info[j].type = PART_SLICE;
291		label_chunk_info[j].c = c1;
292		++j;
293	    }
294#ifdef __powerpc__
295	    if (c1->type == apple) {
296    	        label_chunk_info[j].type = PART_SLICE;
297		label_chunk_info[j].c = c1;
298		++j;
299	    }
300#endif
301	}
302    }
303
304    /* Now run through again and get the FreeBSD partition entries */
305    for (i = 0; devs[i]; i++) {
306	if (!devs[i]->enabled)
307	    continue;
308	d = (Disk *)devs[i]->private;
309	/* Then buzz through and pick up the partitions */
310	for (c1 = d->chunks->part; c1; c1 = c1->next) {
311	    if (c1->type == freebsd) {
312		for (c2 = c1->part; c2; c2 = c2->next) {
313		    if (c2->type == part) {
314			if (c2->subtype == FS_SWAP)
315			    label_chunk_info[j].type = PART_SWAP;
316			else
317			    label_chunk_info[j].type = PART_FILESYSTEM;
318			label_chunk_info[j].c = c2;
319			++j;
320		    }
321		}
322	    }
323	    else if (c1->type == fat) {
324		label_chunk_info[j].type = PART_FAT;
325		label_chunk_info[j].c = c1;
326		++j;
327	    }
328#ifdef __ia64__
329	    else if (c1->type == efi) {
330		label_chunk_info[j].type = PART_EFI;
331		label_chunk_info[j].c = c1;
332		++j;
333	    }
334	    else if (c1->type == part) {
335		if (c1->subtype == FS_SWAP)
336		    label_chunk_info[j].type = PART_SWAP;
337		else
338		    label_chunk_info[j].type = PART_FILESYSTEM;
339		label_chunk_info[j].c = c1;
340		++j;
341	    }
342#endif
343#ifdef __powerpc__
344	    else if (c1->type == apple) {
345	        for (c2 = c1->part; c2; c2 = c2->next) {
346		    if (c2->type == part) {
347		        if (c2->subtype == FS_SWAP)
348			    label_chunk_info[j].type = PART_SWAP;
349			else
350			    label_chunk_info[j].type = PART_FILESYSTEM;
351			label_chunk_info[j].c = c2;
352			++j;
353		    }
354		}
355	    }
356#endif
357	}
358    }
359    label_chunk_info[j].c = NULL;
360    if (here >= j) {
361	here = j  ? j - 1 : 0;
362    }
363}
364
365/* A new partition entry */
366static PartInfo *
367new_part(PartType type, char *mpoint, Boolean newfs)
368{
369    PartInfo *pi;
370
371    if (!mpoint)
372	mpoint = (type == PART_EFI) ? "/efi" : "/change_me";
373
374    pi = (PartInfo *)safe_malloc(sizeof(PartInfo));
375    sstrncpy(pi->mountpoint, mpoint, FILENAME_MAX);
376
377    pi->do_newfs = newfs;
378
379    if (type == PART_EFI) {
380	pi->newfs_type = NEWFS_MSDOS;
381    } else {
382	pi->newfs_type = NEWFS_UFS;
383	strcpy(pi->newfs_data.newfs_ufs.user_options, "");
384	pi->newfs_data.newfs_ufs.acls = FALSE;
385	pi->newfs_data.newfs_ufs.multilabel = FALSE;
386	pi->newfs_data.newfs_ufs.softupdates = strcmp(mpoint, "/");
387#ifdef PC98
388	pi->newfs_data.newfs_ufs.ufs1 = TRUE;
389#else
390	pi->newfs_data.newfs_ufs.ufs1 = FALSE;
391#endif
392    }
393
394    return pi;
395}
396
397/* Get the mountpoint for a partition and save it away */
398static PartInfo *
399get_mountpoint(PartType type, struct chunk *old)
400{
401    char *val;
402    PartInfo *tmp;
403    Boolean newfs;
404
405    if (old && old->private_data)
406	tmp = old->private_data;
407    else
408	tmp = NULL;
409    val = (tmp != NULL) ? tmp->mountpoint : (type == PART_EFI) ? "/efi" : NULL;
410    val = msgGetInput(val, "Please specify a mount point for the partition");
411    if (!val || !*val) {
412	if (!old)
413	    return NULL;
414	else {
415	    free(old->private_data);
416	    old->private_data = NULL;
417	}
418	return NULL;
419    }
420
421    /* Is it just the same value? */
422    if (tmp && !strcmp(tmp->mountpoint, val))
423	return NULL;
424
425    /* Did we use it already? */
426    if (check_conflict(val)) {
427	msgConfirm("You already have a mount point for %s assigned!", val);
428	return NULL;
429    }
430
431    /* Is it bogus? */
432    if (*val != '/') {
433	msgConfirm("Mount point must start with a / character");
434	return NULL;
435    }
436
437    /* Is it going to be mounted on root? */
438    if (!strcmp(val, "/")) {
439	if (old)
440	    old->flags |= CHUNK_IS_ROOT;
441    }
442    else if (old)
443	old->flags &= ~CHUNK_IS_ROOT;
444
445    newfs = TRUE;
446    if (tmp) {
447	newfs = tmp->do_newfs;
448    	safe_free(tmp);
449    }
450    val = string_skipwhite(string_prune(val));
451    tmp = new_part(type, val, newfs);
452    if (old) {
453	old->private_data = tmp;
454	old->private_free = safe_free;
455    }
456    return tmp;
457}
458
459/* Get the type of the new partiton */
460static PartType
461get_partition_type(void)
462{
463    char selection[20];
464    int i;
465    static unsigned char *fs_types[] = {
466#ifdef __ia64__
467	"EFI",	"An EFI system partition",
468#endif
469	"FS",	"A file system",
470	"Swap",	"A swap partition.",
471    };
472    WINDOW *w = savescr();
473
474    i = dialog_menu("Please choose a partition type",
475	"If you want to use this partition for swap space, select Swap.\n"
476	"If you want to put a filesystem on it, choose FS.",
477	-1, -1,
478#ifdef __ia64__
479	3, 3,
480#else
481	2, 2,
482#endif
483	fs_types, selection, NULL, NULL);
484    restorescr(w);
485    if (!i) {
486#ifdef __ia64__
487	if (!strcmp(selection, "EFI"))
488	    return PART_EFI;
489#endif
490	if (!strcmp(selection, "FS"))
491	    return PART_FILESYSTEM;
492	else if (!strcmp(selection, "Swap"))
493	    return PART_SWAP;
494    }
495    return PART_NONE;
496}
497
498/* If the user wants a special newfs command for this, set it */
499static void
500getNewfsCmd(PartInfo *p)
501{
502    char buffer[NEWFS_CMD_ARGS_MAX];
503    char *val;
504
505    switch (p->newfs_type) {
506    case NEWFS_UFS:
507	snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s %s %s %s",
508	    NEWFS_UFS_CMD, p->newfs_data.newfs_ufs.softupdates ?  "-U" : "",
509	    p->newfs_data.newfs_ufs.ufs1 ? "-O1" : "-O2",
510	    p->newfs_data.newfs_ufs.user_options);
511	break;
512    case NEWFS_MSDOS:
513	snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s", NEWFS_MSDOS_CMD);
514	break;
515    case NEWFS_CUSTOM:
516	strcpy(buffer, p->newfs_data.newfs_custom.command);
517	break;
518    }
519
520    val = msgGetInput(buffer,
521	"Please enter the newfs command and options you'd like to use in\n"
522	"creating this file system.");
523    if (val != NULL) {
524	p->newfs_type = NEWFS_CUSTOM;
525	strlcpy(p->newfs_data.newfs_custom.command, val, NEWFS_CMD_ARGS_MAX);
526    }
527}
528
529static void
530getNewfsOptionalArguments(PartInfo *p)
531{
532	char buffer[NEWFS_CMD_ARGS_MAX];
533	char *val;
534
535	/* Must be UFS, per argument checking in I/O routines. */
536
537	strlcpy(buffer,  p->newfs_data.newfs_ufs.user_options,
538	    NEWFS_CMD_ARGS_MAX);
539	val = msgGetInput(buffer,
540	    "Please enter any additional UFS newfs options you'd like to\n"
541	    "use in creating this file system.");
542	if (val != NULL)
543		strlcpy(p->newfs_data.newfs_ufs.user_options, val,
544		    NEWFS_CMD_ARGS_MAX);
545}
546
547#define MAX_MOUNT_NAME	9
548
549#define PART_PART_COL	0
550#define PART_MOUNT_COL	10
551#define PART_SIZE_COL	(PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
552#define PART_NEWFS_COL	(PART_SIZE_COL + 8)
553#define PART_OFF	38
554
555#define TOTAL_AVAIL_LINES       (10)
556#define PSLICE_SHOWABLE          (4)
557
558
559/* stick this all up on the screen */
560static void
561print_label_chunks(void)
562{
563    int  i, j, srow, prow, pcol;
564    daddr_t sz;
565    char clrmsg[80];
566    int ChunkPartStartRow;
567    WINDOW *ChunkWin;
568
569    /********************************************************/
570    /*** These values are for controling screen resources ***/
571    /*** Each label line holds up to 2 labels, so beware! ***/
572    /*** strategy will be to try to always make sure the  ***/
573    /*** highlighted label is in the active display area. ***/
574    /********************************************************/
575    int  pslice_max, label_max;
576    int  pslice_count, label_count, label_focus_found, pslice_focus_found;
577
578    attrset(A_REVERSE);
579    mvaddstr(0, 25, "FreeBSD Disklabel Editor");
580    attrset(A_NORMAL);
581
582    /*** Count the number of parition slices ***/
583    pslice_count = 0;
584    for (i = 0; label_chunk_info[i].c ; i++) {
585        if (label_chunk_info[i].type == PART_SLICE)
586            ++pslice_count;
587    }
588    pslice_max = pslice_count;
589
590    /*** 4 line max for partition slices ***/
591    if (pslice_max > PSLICE_SHOWABLE) {
592        pslice_max = PSLICE_SHOWABLE;
593    }
594    ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3 + pslice_max;
595
596    /*** View partition slices modulo pslice_max ***/
597    label_max = TOTAL_AVAIL_LINES - pslice_max;
598
599    for (i = 0; i < 2; i++) {
600	mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
601	mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
602
603	mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
604	mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
605
606	mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 3, "Size");
607	mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 3, "----");
608
609	mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
610	mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
611    }
612    srow = CHUNK_SLICE_START_ROW;
613    prow = 0;
614    pcol = 0;
615
616    /*** these variables indicate that the focused item is shown currently ***/
617    label_focus_found = 0;
618    pslice_focus_found = 0;
619
620    label_count = 0;
621    pslice_count = 0;
622    mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "          ");
623    mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "          ");
624
625    ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
626
627    wclear(ChunkWin);
628    /*** wrefresh(ChunkWin); ***/
629
630    for (i = 0; label_chunk_info[i].c; i++) {
631	/* Is it a slice entry displayed at the top? */
632	if (label_chunk_info[i].type == PART_SLICE) {
633            /*** This causes the new pslice to replace the previous display ***/
634            /*** focus must remain on the most recently active pslice       ***/
635            if (pslice_count == pslice_max) {
636                if (pslice_focus_found) {
637                    /*** This is where we can mark the more following ***/
638                    attrset(A_BOLD);
639                    mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
640                    attrset(A_NORMAL);
641                    continue;
642                }
643                else {
644                    /*** this is where we set the more previous ***/
645                    attrset(A_BOLD);
646                    mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
647                    attrset(A_NORMAL);
648                    pslice_count = 0;
649                    srow = CHUNK_SLICE_START_ROW;
650                }
651            }
652
653	    sz = space_free(label_chunk_info[i].c);
654	    if (i == here)
655		attrset(ATTR_SELECTED);
656            if (i == pslice_focus)
657                pslice_focus_found = -1;
658
659	    if (label_chunk_info[i].c->type == whole) {
660		if (sz >= 100 * ONE_GIG)
661		    mvprintw(srow++, 0,
662			"Disk: %s\t\tFree: %jd blocks (%jdGB)",
663			label_chunk_info[i].c->disk->name, (intmax_t)sz,
664			(intmax_t)(sz / ONE_GIG));
665		else
666		    mvprintw(srow++, 0,
667			"Disk: %s\t\tFree: %jd blocks (%jdMB)",
668			label_chunk_info[i].c->disk->name, (intmax_t)sz,
669			(intmax_t)(sz / ONE_MEG));
670	    } else {
671		if (sz >= 100 * ONE_GIG)
672		    mvprintw(srow++, 0,
673			"Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdGB)",
674			label_chunk_info[i].c->disk->name,
675			label_chunk_info[i].c->name,
676			(intmax_t)sz, (intmax_t)(sz / ONE_GIG));
677		else
678		    mvprintw(srow++, 0,
679			"Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdMB)",
680			label_chunk_info[i].c->disk->name,
681			label_chunk_info[i].c->name,
682			(intmax_t)sz, (intmax_t)(sz / ONE_MEG));
683	    }
684	    attrset(A_NORMAL);
685	    clrtoeol();
686	    move(0, 0);
687	    /*** refresh(); ***/
688            ++pslice_count;
689	}
690	/* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
691	else {
692	    char onestr[PART_OFF], num[10], *mountpoint, newfs[12];
693
694	    /*
695	     * We copy this into a blank-padded string so that it looks like
696	     * a solid bar in reverse-video
697	     */
698	    memset(onestr, ' ', PART_OFF - 1);
699	    onestr[PART_OFF - 1] = '\0';
700
701            /*** Track how many labels have been displayed ***/
702            if (label_count == ((label_max - 1 ) * 2)) {
703                if (label_focus_found) {
704                    continue;
705                }
706                else {
707                    label_count = 0;
708                    prow = 0;
709                    pcol = 0;
710                }
711            }
712
713	    /* Go for two columns if we've written one full columns worth */
714	    /*** if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) ***/
715            if (label_count == label_max - 1) {
716		pcol = PART_OFF;
717		prow = 0;
718	    }
719	    memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
720	    /* If it's a filesystem, display the mountpoint */
721	    if (label_chunk_info[i].c->private_data && (label_chunk_info[i].type == PART_FILESYSTEM
722		|| label_chunk_info[i].type == PART_FAT || label_chunk_info[i].type == PART_EFI))
723		mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
724	    else if (label_chunk_info[i].type == PART_SWAP)
725		mountpoint = "swap";
726	    else
727	        mountpoint = "<none>";
728
729	    /* Now display the newfs field */
730	    if (label_chunk_info[i].type == PART_FAT)
731		strcpy(newfs, "DOS");
732#if defined(__ia64__)
733	    else if (label_chunk_info[i].type == PART_EFI) {
734		strcpy(newfs, "EFI");
735		if (label_chunk_info[i].c->private_data) {
736		    strcat(newfs, "  ");
737		    PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
738		    strcat(newfs, pi->do_newfs ? " Y" : " N");
739		}
740	    }
741#endif
742	    else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM) {
743		PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
744
745		switch (pi->newfs_type) {
746		case NEWFS_UFS:
747			strcpy(newfs, NEWFS_UFS_STRING);
748			if (pi->newfs_data.newfs_ufs.ufs1)
749				strcat(newfs, "1");
750			else
751				strcat(newfs, "2");
752			if (pi->newfs_data.newfs_ufs.softupdates)
753				strcat(newfs, "+S");
754			else
755				strcat(newfs, "  ");
756
757			break;
758		case NEWFS_MSDOS:
759			strcpy(newfs, "FAT");
760			break;
761		case NEWFS_CUSTOM:
762			strcpy(newfs, "CUST");
763			break;
764		}
765		strcat(newfs, pi->do_newfs ? " Y" : " N ");
766	    }
767	    else if (label_chunk_info[i].type == PART_SWAP)
768		strcpy(newfs, "SWAP");
769	    else
770		strcpy(newfs, "*");
771	    for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
772		onestr[PART_MOUNT_COL + j] = mountpoint[j];
773	    if (label_chunk_info[i].c->size == 0)
774	        snprintf(num, 10, "%5dMB", 0);
775	    else if (label_chunk_info[i].c->size < (100 * ONE_GIG))
776		snprintf(num, 10, "%5jdMB",
777		    (intmax_t)label_chunk_info[i].c->size / ONE_MEG);
778	    else
779		snprintf(num, 10, "%5jdGB",
780		    (intmax_t)label_chunk_info[i].c->size / ONE_GIG);
781	    memcpy(onestr + PART_SIZE_COL, num, strlen(num));
782	    memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
783	    onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
784            if (i == label_focus) {
785                label_focus_found = -1;
786                wattrset(ChunkWin, A_BOLD);
787            }
788	    if (i == here)
789		wattrset(ChunkWin, ATTR_SELECTED);
790
791            /*** lazy man's way of expensively padding this string ***/
792            while (strlen(onestr) < 37)
793                strcat(onestr, " ");
794
795	    mvwaddstr(ChunkWin, prow, pcol, onestr);
796	    wattrset(ChunkWin, A_NORMAL);
797	    move(0, 0);
798	    ++prow;
799            ++label_count;
800	}
801    }
802
803    /*** this will erase all the extra stuff ***/
804    memset(clrmsg, ' ', 37);
805    clrmsg[37] = '\0';
806
807    while (pslice_count < pslice_max) {
808        mvprintw(srow++, 0, clrmsg);
809        clrtoeol();
810        ++pslice_count;
811    }
812    while (label_count < (2 * (label_max - 1))) {
813        mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
814	++label_count;
815	if (prow == (label_max - 1)) {
816	    prow = 0;
817	    pcol = PART_OFF;
818	}
819    }
820    refresh();
821    wrefresh(ChunkWin);
822}
823
824static void
825print_command_summary(void)
826{
827    mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
828    mvprintw(18, 0, "C = Create        D = Delete   M = Mount pt.");
829    if (!RunningAsInit)
830	mvprintw(18, 56, "W = Write");
831    mvprintw(19, 0, "N = Newfs Opts    Q = Finish   S = Toggle SoftUpdates   Z = Custom Newfs");
832    mvprintw(20, 0, "T = Toggle Newfs  U = Undo     A = Auto Defaults        R = Delete+Merge");
833    mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
834    move(0, 0);
835}
836
837static void
838clear_wins(void)
839{
840    extern void print_label_chunks();
841    clear();
842    print_label_chunks();
843}
844
845static int
846diskLabel(Device *dev)
847{
848    daddr_t sz;
849    int  key = 0;
850    Boolean labeling;
851    char *msg = NULL;
852    PartInfo *p, *oldp;
853    PartType type;
854    Device **devs;
855    WINDOW *w = savescr();
856
857    label_focus = 0;
858    pslice_focus = 0;
859    here = 0;
860
861    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
862    if (!devs) {
863	msgConfirm("No disks found!");
864	restorescr(w);
865	return DITEM_FAILURE;
866    }
867    labeling = TRUE;
868    keypad(stdscr, TRUE);
869    record_label_chunks(devs, dev);
870
871    clear();
872    while (labeling) {
873	char *cp;
874	int rflags = DELCHUNK_NORMAL;
875
876	print_label_chunks();
877	print_command_summary();
878	if (msg) {
879	    attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
880	    clrtoeol();
881	    beep();
882	    msg = NULL;
883	}
884	else {
885	    move(23, 0);
886	    clrtoeol();
887	}
888
889	refresh();
890	key = getch();
891	switch (toupper(key)) {
892	    int i;
893	    static char _msg[40];
894
895	case '\014':	/* ^L */
896	    clear_wins();
897	    break;
898
899	case '\020':	/* ^P */
900	case KEY_UP:
901	case '-':
902	    if (here != 0)
903		--here;
904	    else
905		while (label_chunk_info[here + 1].c)
906		    ++here;
907	    break;
908
909	case '\016':	/* ^N */
910	case KEY_DOWN:
911	case '+':
912	case '\r':
913	case '\n':
914	    if (label_chunk_info[here + 1].c)
915		++here;
916	    else
917		here = 0;
918	    break;
919
920	case KEY_HOME:
921	    here = 0;
922	    break;
923
924	case KEY_END:
925	    while (label_chunk_info[here + 1].c)
926		++here;
927	    break;
928
929	case KEY_F(1):
930	case '?':
931	    systemDisplayHelp("partition");
932	    clear_wins();
933	    break;
934
935	case '1':
936	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
937		PartInfo *pi =
938		    ((PartInfo *)label_chunk_info[here].c->private_data);
939
940		if ((pi != NULL) &&
941		    (pi->newfs_type == NEWFS_UFS)) {
942			pi->newfs_data.newfs_ufs.ufs1 = true;
943		} else
944		    msg = MSG_NOT_APPLICABLE;
945	    } else
946		msg = MSG_NOT_APPLICABLE;
947	    break;
948		break;
949
950	case '2':
951	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
952		PartInfo *pi =
953		    ((PartInfo *)label_chunk_info[here].c->private_data);
954
955		if ((pi != NULL) &&
956		    (pi->newfs_type == NEWFS_UFS)) {
957			pi->newfs_data.newfs_ufs.ufs1 = false;
958		} else
959		    msg = MSG_NOT_APPLICABLE;
960	    } else
961		msg = MSG_NOT_APPLICABLE;
962	    break;
963		break;
964
965	case 'A':
966	    if (label_chunk_info[here].type != PART_SLICE) {
967		msg = "You can only do this in a disk slice (at top of screen)";
968		break;
969	    }
970	    /*
971	     * Generate standard partitions automatically.  If we do not
972	     * have sufficient space we attempt to scale-down the size
973	     * of the partitions within certain bounds.
974	     */
975	    {
976		int perc;
977		int req = 0;
978
979		for (perc = 100; perc > 0; perc -= 5) {
980		    req = 0;	/* reset for each loop */
981		    if ((msg = try_auto_label(devs, dev, perc, &req)) == NULL)
982			break;
983		}
984		if (msg) {
985		    if (req) {
986			msgConfirm(msg);
987			clear_wins();
988			msg = NULL;
989		    }
990		}
991	    }
992	    break;
993
994	case 'C':
995	    if (label_chunk_info[here].type != PART_SLICE) {
996		msg = "You can only do this in a master partition (see top of screen)";
997		break;
998	    }
999	    sz = space_free(label_chunk_info[here].c);
1000	    if (sz <= FS_MIN_SIZE) {
1001		msg = "Not enough space to create an additional FreeBSD partition";
1002		break;
1003	    }
1004	    else {
1005		char *val;
1006		daddr_t size;
1007		struct chunk *tmp;
1008		char osize[80];
1009		u_long flags = 0;
1010
1011#ifdef __powerpc__
1012		/* Always use the maximum size for apple partitions */
1013		if (label_chunk_info[here].c->type == apple)
1014		    size = sz;
1015		else {
1016#endif
1017		sprintf(osize, "%jd", (intmax_t)sz);
1018		val = msgGetInput(osize,
1019				  "Please specify the partition size in blocks or append a trailing G for\n"
1020#ifdef __ia64__
1021				  "gigabytes, M for megabytes.\n"
1022#else
1023				  "gigabytes, M for megabytes, or C for cylinders.\n"
1024#endif
1025				  "%jd blocks (%jdMB) are free.",
1026				  (intmax_t)sz, (intmax_t)sz / ONE_MEG);
1027		if (!val || (size = strtoimax(val, &cp, 0)) <= 0) {
1028		    clear_wins();
1029		    break;
1030		}
1031
1032		if (*cp) {
1033		    if (toupper(*cp) == 'M')
1034			size *= ONE_MEG;
1035		    else if (toupper(*cp) == 'G')
1036			size *= ONE_GIG;
1037#ifndef __ia64__
1038		    else if (toupper(*cp) == 'C')
1039			size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
1040#endif
1041		}
1042		if (size <= FS_MIN_SIZE) {
1043		    msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
1044		    clear_wins();
1045		    break;
1046		}
1047#ifdef __powerpc__
1048		}
1049#endif
1050		type = get_partition_type();
1051		if (type == PART_NONE) {
1052		    clear_wins();
1053		    beep();
1054		    break;
1055		}
1056
1057		if (type == PART_FILESYSTEM || type == PART_EFI) {
1058		    if ((p = get_mountpoint(type, NULL)) == NULL) {
1059			clear_wins();
1060			beep();
1061			break;
1062		    }
1063		    else if (!strcmp(p->mountpoint, "/")) {
1064			if (type != PART_FILESYSTEM) {
1065			    clear_wins();
1066			    beep();
1067			    break;
1068			}
1069			else
1070			    flags |= CHUNK_IS_ROOT;
1071		    }
1072		    else
1073			flags &= ~CHUNK_IS_ROOT;
1074		}
1075		else
1076		    p = NULL;
1077
1078		if ((flags & CHUNK_IS_ROOT) && (size < (ROOT_MIN_SIZE * ONE_MEG))) {
1079		    msgConfirm("Warning: This is smaller than the recommended size for a\n"
1080			       "root partition.  For a variety of reasons, root\n"
1081			       "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
1082		}
1083		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1084		    label_chunk_info[here].c, size,
1085#ifdef __ia64__
1086		    (type == PART_EFI) ? efi : part,
1087		    (type == PART_EFI) ? 0 : (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1088#else
1089		    part, (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1090#endif
1091		    flags);
1092		if (!tmp) {
1093		    msgConfirm("Unable to create the partition. Too big?");
1094		    clear_wins();
1095		    break;
1096		}
1097
1098#ifdef __alpha__
1099		/*
1100		 * SRM requires that the root partition is at the
1101		 * begining of the disk and cannot boot otherwise.
1102		 * Warn Alpha users if they are about to shoot themselves in
1103		 * the foot in this way.
1104		 *
1105		 * Since partitions may not start precisely at offset 0 we
1106		 * check for a "close to 0" instead. :-(
1107		 */
1108		if ((flags & CHUNK_IS_ROOT) && (tmp->offset > 1024)) {
1109		    msgConfirm("Your root partition `a' does not seem to be the first\n"
1110			       "partition.  The Alpha's firmware can only boot from the\n"
1111			       "first partition.  So it is unlikely that your current\n"
1112			       "disk layout will be bootable boot after installation.\n"
1113			       "\n"
1114			       "Please allocate the root partition before allocating\n"
1115			       "any others.\n");
1116		}
1117#endif	/* alpha */
1118
1119		tmp->private_data = p;
1120		tmp->private_free = safe_free;
1121		if (variable_cmp(DISK_LABELLED, "written"))
1122		    variable_set2(DISK_LABELLED, "yes", 0);
1123		record_label_chunks(devs, dev);
1124		clear_wins();
1125                /* This is where we assign focus to new label so it shows. */
1126                {
1127                    int i;
1128		    label_focus = -1;
1129                    for (i = 0; label_chunk_info[i].c; ++i) {
1130                    	if (label_chunk_info[i].c == tmp) {
1131			    label_focus = i;
1132			    break;
1133			}
1134		    }
1135		    if (label_focus == -1)
1136                    	label_focus = i - 1;
1137                }
1138	    }
1139	    break;
1140
1141	case KEY_DC:
1142	case 'R':	/* recover space (delete w/ recover) */
1143	    /*
1144	     * Delete the partition w/ space recovery.
1145	     */
1146	    rflags = DELCHUNK_RECOVER;
1147	    /* fall through */
1148	case 'D':	/* delete */
1149	    if (label_chunk_info[here].type == PART_SLICE) {
1150		msg = MSG_NOT_APPLICABLE;
1151		break;
1152	    }
1153	    else if (label_chunk_info[here].type == PART_FAT) {
1154		msg = "Use the Disk Partition Editor to delete DOS partitions";
1155		break;
1156	    }
1157	    Delete_Chunk2(label_chunk_info[here].c->disk, label_chunk_info[here].c, rflags);
1158	    if (variable_cmp(DISK_LABELLED, "written"))
1159		variable_set2(DISK_LABELLED, "yes", 0);
1160	    record_label_chunks(devs, dev);
1161	    break;
1162
1163	case 'M':	/* mount */
1164	    switch(label_chunk_info[here].type) {
1165	    case PART_SLICE:
1166		msg = MSG_NOT_APPLICABLE;
1167		break;
1168
1169	    case PART_SWAP:
1170		msg = "You don't need to specify a mountpoint for a swap partition.";
1171		break;
1172
1173	    case PART_FAT:
1174	    case PART_EFI:
1175	    case PART_FILESYSTEM:
1176		oldp = label_chunk_info[here].c->private_data;
1177		p = get_mountpoint(label_chunk_info[here].type, label_chunk_info[here].c);
1178		if (p) {
1179		    if (!oldp)
1180		    	p->do_newfs = FALSE;
1181		    if ((label_chunk_info[here].type == PART_FAT ||
1182			    label_chunk_info[here].type == PART_EFI) &&
1183			(!strcmp(p->mountpoint, "/") ||
1184			    !strcmp(p->mountpoint, "/usr") ||
1185			    !strcmp(p->mountpoint, "/var"))) {
1186			msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
1187			strcpy(p->mountpoint, "/bogus");
1188		    }
1189		}
1190		if (variable_cmp(DISK_LABELLED, "written"))
1191		    variable_set2(DISK_LABELLED, "yes", 0);
1192		record_label_chunks(devs, dev);
1193		clear_wins();
1194		break;
1195
1196	    default:
1197		msgFatal("Bogus partition under cursor???");
1198		break;
1199	    }
1200	    break;
1201
1202	case 'N':	/* Set newfs options */
1203	    if (label_chunk_info[here].c->private_data &&
1204		((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1205		getNewfsOptionalArguments(
1206		    label_chunk_info[here].c->private_data);
1207	    else
1208		msg = MSG_NOT_APPLICABLE;
1209	    clear_wins();
1210	    break;
1211
1212	case 'S':	/* Toggle soft updates flag */
1213	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
1214		PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1215		if (pi != NULL &&
1216		    pi->newfs_type == NEWFS_UFS)
1217			pi->newfs_data.newfs_ufs.softupdates =
1218			    !pi->newfs_data.newfs_ufs.softupdates;
1219		else
1220		    msg = MSG_NOT_APPLICABLE;
1221	    }
1222	    else
1223		msg = MSG_NOT_APPLICABLE;
1224	    break;
1225
1226	case 'T':	/* Toggle newfs state */
1227	    if ((label_chunk_info[here].type == PART_FILESYSTEM ||
1228		 label_chunk_info[here].type == PART_EFI) &&
1229	        (label_chunk_info[here].c->private_data)) {
1230		PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1231		if (!pi->do_newfs)
1232		    label_chunk_info[here].c->flags |= CHUNK_NEWFS;
1233		else
1234		    label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
1235
1236		label_chunk_info[here].c->private_data =
1237		    new_part(label_chunk_info[here].type, pi ? pi->mountpoint : NULL, pi ? !pi->do_newfs
1238		    : TRUE);
1239		if (pi != NULL &&
1240		    pi->newfs_type == NEWFS_UFS) {
1241		    PartInfo *pi_new = label_chunk_info[here].c->private_data;
1242
1243		    pi_new->newfs_data.newfs_ufs = pi->newfs_data.newfs_ufs;
1244		}
1245		safe_free(pi);
1246		label_chunk_info[here].c->private_free = safe_free;
1247		if (variable_cmp(DISK_LABELLED, "written"))
1248		    variable_set2(DISK_LABELLED, "yes", 0);
1249	    }
1250	    else
1251		msg = MSG_NOT_APPLICABLE;
1252	    break;
1253
1254	case 'U':
1255	    clear();
1256	    if (!variable_cmp(DISK_LABELLED, "written")) {
1257		msgConfirm("You've already written out your changes -\n"
1258			   "it's too late to undo!");
1259	    }
1260	    else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
1261		variable_unset(DISK_PARTITIONED);
1262		variable_unset(DISK_LABELLED);
1263		for (i = 0; devs[i]; i++) {
1264		    Disk *d;
1265
1266		    if (!devs[i]->enabled)
1267			continue;
1268		    else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1269			Free_Disk(devs[i]->private);
1270			devs[i]->private = d;
1271#ifdef WITH_SLICES
1272			diskPartition(devs[i]);
1273#endif
1274		    }
1275		}
1276		record_label_chunks(devs, dev);
1277	    }
1278	    clear_wins();
1279	    break;
1280
1281	case 'W':
1282	    if (!variable_cmp(DISK_LABELLED, "written")) {
1283		msgConfirm("You've already written out your changes - if you\n"
1284			   "wish to overwrite them, you'll have to restart\n"
1285			   "sysinstall first.");
1286	    }
1287	    else if (!msgNoYes("WARNING:  This should only be used when modifying an EXISTING\n"
1288			  "installation.  If you are installing FreeBSD for the first time\n"
1289			  "then you should simply type Q when you're finished here and your\n"
1290			  "changes will be committed in one batch automatically at the end of\n"
1291			  "these questions.\n\n"
1292			  "Are you absolutely sure you want to do this now?")) {
1293		variable_set2(DISK_LABELLED, "yes", 0);
1294		diskLabelCommit(NULL);
1295	    }
1296	    clear_wins();
1297	    break;
1298
1299	case 'Z':	/* Set newfs command line */
1300	    if (label_chunk_info[here].c->private_data &&
1301		((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1302		getNewfsCmd(label_chunk_info[here].c->private_data);
1303	    else
1304		msg = MSG_NOT_APPLICABLE;
1305	    clear_wins();
1306	    break;
1307
1308#ifndef __ia64__
1309	case '|':
1310	    if (!msgNoYes("Are you sure you want to go into Wizard mode?\n\n"
1311			  "This is an entirely undocumented feature which you are not\n"
1312			  "expected to understand!")) {
1313		int i;
1314		Device **devs;
1315
1316		dialog_clear();
1317		end_dialog();
1318		DialogActive = FALSE;
1319		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1320		if (!devs) {
1321		    msgConfirm("Can't find any disk devices!");
1322		    break;
1323		}
1324		for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1325		    if (devs[i]->enabled)
1326		    	slice_wizard(((Disk *)devs[i]->private));
1327		}
1328		if (variable_cmp(DISK_LABELLED, "written"))
1329		    variable_set2(DISK_LABELLED, "yes", 0);
1330		DialogActive = TRUE;
1331		record_label_chunks(devs, dev);
1332		clear_wins();
1333	    }
1334	    else
1335		msg = "A most prudent choice!";
1336	    break;
1337#endif
1338
1339	case '\033':	/* ESC */
1340	case 'Q':
1341	    labeling = FALSE;
1342	    break;
1343
1344	default:
1345	    beep();
1346	    sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1347	    msg = _msg;
1348	    break;
1349	}
1350        if (label_chunk_info[here].type == PART_SLICE)
1351            pslice_focus = here;
1352        else
1353            label_focus = here;
1354    }
1355    restorescr(w);
1356    return DITEM_SUCCESS;
1357}
1358
1359static __inline daddr_t
1360requested_part_size(char *varName, daddr_t nom, int def, int perc)
1361{
1362    char *cp;
1363    daddr_t sz;
1364
1365    if ((cp = variable_get(varName)) != NULL)
1366	sz = strtoimax(cp, NULL, 0);
1367    else
1368	sz = nom + (def - nom) * perc / 100;
1369    return(sz * ONE_MEG);
1370}
1371
1372/*
1373 * Attempt to auto-label the disk.  'perc' (0-100) scales
1374 * the size of the various partitions within appropriate
1375 * bounds (NOMINAL through DEFAULT sizes).  The procedure
1376 * succeeds of NULL is returned.  A non-null return message
1377 * is either a failure-status message (*req == 0), or
1378 * a confirmation requestor (*req == 1).  *req is 0 on
1379 * entry to this call.
1380 *
1381 * We autolabel the following partitions:  /, swap, /var, /tmp, /usr,
1382 * and /home.  /home receives any extra left over disk space.
1383 */
1384static char *
1385try_auto_label(Device **devs, Device *dev, int perc, int *req)
1386{
1387    daddr_t sz;
1388    Chunk *AutoHome, *AutoRoot, *AutoSwap;
1389    Chunk *AutoTmp, *AutoUsr, *AutoVar;
1390    int mib[2];
1391    unsigned long physmem;
1392    size_t size;
1393    char *msg = NULL;
1394
1395    sz = space_free(label_chunk_info[here].c);
1396    if (sz <= FS_MIN_SIZE)
1397	return("Not enough free space to create a new partition in the slice");
1398
1399    (void)checkLabels(FALSE);
1400    AutoHome = AutoRoot = AutoSwap = NULL;
1401    AutoTmp = AutoUsr = AutoVar = NULL;
1402    if (RootChunk == NULL) {
1403	sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
1404
1405	AutoRoot = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1406			    label_chunk_info[here].c, sz, part,
1407			    FS_BSDFFS,  CHUNK_IS_ROOT | CHUNK_AUTO_SIZE);
1408	if (!AutoRoot) {
1409	    *req = 1;
1410	    msg = "Unable to create the root partition. Too big?";
1411	    goto done;
1412	}
1413	AutoRoot->private_data = new_part(PART_FILESYSTEM, "/", TRUE);
1414	AutoRoot->private_free = safe_free;
1415	AutoRoot->flags |= CHUNK_NEWFS;
1416	record_label_chunks(devs, dev);
1417    }
1418    if (SwapChunk == NULL) {
1419	sz = requested_part_size(VAR_SWAP_SIZE, 0, 0, perc);
1420	if (sz == 0) {
1421	    daddr_t nom;
1422	    daddr_t def;
1423
1424	    mib[0] = CTL_HW;
1425	    mib[1] = HW_PHYSMEM;
1426	    size = sizeof physmem;
1427	    sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1428	    def = 2 * (int)(physmem / 512);
1429	    if (def < SWAP_MIN_SIZE * ONE_MEG)
1430		def = SWAP_MIN_SIZE * ONE_MEG;
1431	    if (def > SWAP_AUTO_LIMIT_SIZE * ONE_MEG)
1432		def = SWAP_AUTO_LIMIT_SIZE * ONE_MEG;
1433	    nom = (int)(physmem / 512) / 8;
1434	    sz = nom + (def - nom) * perc / 100;
1435	}
1436	AutoSwap = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1437			    label_chunk_info[here].c, sz, part,
1438			    FS_SWAP, CHUNK_AUTO_SIZE);
1439	if (!AutoSwap) {
1440	    *req = 1;
1441	    msg = "Unable to create the swap partition. Too big?";
1442	    goto done;
1443	}
1444	AutoSwap->private_data = 0;
1445	AutoSwap->private_free = safe_free;
1446	record_label_chunks(devs, dev);
1447    }
1448    if (VarChunk == NULL) {
1449	sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE, VAR_DEFAULT_SIZE, perc);
1450
1451	AutoVar = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1452				label_chunk_info[here].c, sz, part,
1453				FS_BSDFFS, CHUNK_AUTO_SIZE);
1454	if (!AutoVar) {
1455	    *req = 1;
1456	    msg = "Not enough free space for /var - you will need to\n"
1457		   "partition your disk manually with a custom install!";
1458	    goto done;
1459	}
1460	AutoVar->private_data = new_part(PART_FILESYSTEM, "/var", TRUE);
1461	AutoVar->private_free = safe_free;
1462	AutoVar->flags |= CHUNK_NEWFS;
1463	record_label_chunks(devs, dev);
1464    }
1465    if (TmpChunk == NULL && !variable_get(VAR_NO_TMP)) {
1466	sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
1467
1468	AutoTmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1469				label_chunk_info[here].c, sz, part,
1470				FS_BSDFFS, CHUNK_AUTO_SIZE);
1471	if (!AutoTmp) {
1472	    *req = 1;
1473	    msg = "Not enough free space for /tmp - you will need to\n"
1474		   "partition your disk manually with a custom install!";
1475	    goto done;
1476	}
1477	AutoTmp->private_data = new_part(PART_FILESYSTEM, "/tmp", TRUE);
1478	AutoTmp->private_free = safe_free;
1479	AutoTmp->flags |= CHUNK_NEWFS;
1480	record_label_chunks(devs, dev);
1481    }
1482    if (UsrChunk == NULL && !variable_get(VAR_NO_USR)) {
1483	sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
1484#if AUTO_HOME == 0
1485	    sz = space_free(label_chunk_info[here].c);
1486#endif
1487	if (sz) {
1488	    if (sz < (USR_MIN_SIZE * ONE_MEG)) {
1489		*req = 1;
1490		msg = "Not enough free space for /usr - you will need to\n"
1491		       "partition your disk manually with a custom install!";
1492	    }
1493
1494	    AutoUsr = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1495				    label_chunk_info[here].c, sz, part,
1496				    FS_BSDFFS, CHUNK_AUTO_SIZE);
1497	    if (!AutoUsr) {
1498		msg = "Unable to create the /usr partition.  Not enough space?\n"
1499			   "You will need to partition your disk manually with a custom install!";
1500		goto done;
1501	    }
1502	    AutoUsr->private_data = new_part(PART_FILESYSTEM, "/usr", TRUE);
1503	    AutoUsr->private_free = safe_free;
1504	    AutoUsr->flags |= CHUNK_NEWFS;
1505	    record_label_chunks(devs, dev);
1506	}
1507    }
1508#if AUTO_HOME == 1
1509    if (HomeChunk == NULL && !variable_get(VAR_NO_HOME)) {
1510	sz = requested_part_size(VAR_HOME_SIZE, HOME_NOMINAL_SIZE, HOME_DEFAULT_SIZE, perc);
1511	if (sz < space_free(label_chunk_info[here].c))
1512	    sz = space_free(label_chunk_info[here].c);
1513	if (sz) {
1514	    if (sz < (HOME_MIN_SIZE * ONE_MEG)) {
1515		*req = 1;
1516		msg = "Not enough free space for /home - you will need to\n"
1517		       "partition your disk manually with a custom install!";
1518		goto done;
1519	    }
1520
1521	    AutoHome = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1522				    label_chunk_info[here].c, sz, part,
1523				    FS_BSDFFS, CHUNK_AUTO_SIZE);
1524	    if (!AutoHome) {
1525		msg = "Unable to create the /home partition.  Not enough space?\n"
1526			   "You will need to partition your disk manually with a custom install!";
1527		goto done;
1528	    }
1529	    AutoHome->private_data = new_part(PART_FILESYSTEM, "/home", TRUE);
1530	    AutoHome->private_free = safe_free;
1531	    AutoHome->flags |= CHUNK_NEWFS;
1532	    record_label_chunks(devs, dev);
1533	}
1534    }
1535#endif
1536
1537    /* At this point, we're reasonably "labelled" */
1538    if (variable_cmp(DISK_LABELLED, "written"))
1539	variable_set2(DISK_LABELLED, "yes", 0);
1540
1541done:
1542    if (msg) {
1543	if (AutoRoot != NULL)
1544	    Delete_Chunk(AutoRoot->disk, AutoRoot);
1545	if (AutoSwap != NULL)
1546	    Delete_Chunk(AutoSwap->disk, AutoSwap);
1547	if (AutoVar != NULL)
1548	    Delete_Chunk(AutoVar->disk, AutoVar);
1549	if (AutoTmp != NULL)
1550	    Delete_Chunk(AutoTmp->disk, AutoTmp);
1551	if (AutoUsr != NULL)
1552	    Delete_Chunk(AutoUsr->disk, AutoUsr);
1553	if (AutoHome != NULL)
1554	    Delete_Chunk(AutoHome->disk, AutoHome);
1555	record_label_chunks(devs, dev);
1556    }
1557    return(msg);
1558}
1559
1560static int
1561diskLabelNonInteractive(Device *dev)
1562{
1563    char *cp;
1564    PartType type;
1565    PartInfo *p;
1566    u_long flags;
1567    int i, status;
1568    Device **devs;
1569    Disk *d;
1570
1571    status = DITEM_SUCCESS;
1572    cp = variable_get(VAR_DISK);
1573    if (!cp) {
1574	msgConfirm("diskLabel:  No disk selected - can't label automatically.");
1575	return DITEM_FAILURE;
1576    }
1577    devs = deviceFind(cp, DEVICE_TYPE_DISK);
1578    if (!devs) {
1579	msgConfirm("diskLabel: No disk device %s found!", cp);
1580	return DITEM_FAILURE;
1581    }
1582    if (dev)
1583	d = dev->private;
1584    else
1585	d = devs[0]->private;
1586    record_label_chunks(devs, dev);
1587    for (i = 0; label_chunk_info[i].c; i++) {
1588	Chunk *c1 = label_chunk_info[i].c;
1589
1590	if (label_chunk_info[i].type == PART_SLICE) {
1591	    char name[512];
1592	    char typ[10], mpoint[50];
1593	    int entries;
1594
1595	    for (entries = 1;; entries++) {
1596		intmax_t sz;
1597		int soft = 0;
1598		snprintf(name, sizeof name, "%s-%d", c1->name, entries);
1599		if ((cp = variable_get(name)) == NULL)
1600		    break;
1601		if (sscanf(cp, "%s %jd %s %d", typ, &sz, mpoint, &soft) < 3) {
1602		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s",  c1->name, cp);
1603		    status = DITEM_FAILURE;
1604		    break;
1605		} else {
1606		    Chunk *tmp;
1607
1608		    flags = 0;
1609		    if (!strcmp(typ, "swap")) {
1610			type = PART_SWAP;
1611			strcpy(mpoint, "SWAP");
1612		    } else {
1613			type = PART_FILESYSTEM;
1614			if (!strcmp(mpoint, "/"))
1615			    flags |= CHUNK_IS_ROOT;
1616		    }
1617		    if (!sz)
1618			sz = space_free(c1);
1619		    if (sz > space_free(c1)) {
1620			msgConfirm("Not enough free space to create partition: %s", mpoint);
1621			status = DITEM_FAILURE;
1622			break;
1623		    }
1624		    if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
1625			(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
1626			msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
1627			status = DITEM_FAILURE;
1628			break;
1629		    } else {
1630			PartInfo *pi;
1631			pi = tmp->private_data = new_part(PART_FILESYSTEM, mpoint, TRUE);
1632			tmp->private_free = safe_free;
1633			pi->newfs_data.newfs_ufs.softupdates = soft;
1634		    }
1635		}
1636	    }
1637	} else {
1638	    /* Must be something we can set a mountpoint for */
1639	    cp = variable_get(c1->name);
1640	    if (cp) {
1641		char mpoint[50], do_newfs[8];
1642		Boolean newfs = FALSE;
1643
1644		do_newfs[0] = '\0';
1645		if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
1646		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1647		    status = DITEM_FAILURE;
1648		    break;
1649		}
1650		newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1651		if (c1->private_data) {
1652		    p = c1->private_data;
1653		    p->do_newfs = newfs;
1654		    strcpy(p->mountpoint, mpoint);
1655		}
1656		else {
1657		    c1->private_data = new_part(PART_FILESYSTEM, mpoint, newfs);
1658		    c1->private_free = safe_free;
1659		}
1660		if (!strcmp(mpoint, "/"))
1661		    c1->flags |= CHUNK_IS_ROOT;
1662		else
1663		    c1->flags &= ~CHUNK_IS_ROOT;
1664	    }
1665	}
1666    }
1667    if (status == DITEM_SUCCESS)
1668	variable_set2(DISK_LABELLED, "yes", 0);
1669    return status;
1670}
1671