label.c revision 156123
1/*
2 * The new sysinstall program.
3 *
4 * This is probably the last program in the `sysinstall' line - the next
5 * generation being essentially a complete rewrite.
6 *
7 * $FreeBSD: head/usr.sbin/sade/label.c 156123 2006-02-28 21:49:33Z jhb $
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			128
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		512
86#define USR_DEFAULT_SIZE		8192
87#define VAR_DEFAULT_SIZE		1024
88#define TMP_DEFAULT_SIZE		512
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		256
97#define USR_NOMINAL_SIZE		1536
98#define VAR_NOMINAL_SIZE		128
99#define TMP_NOMINAL_SIZE		128
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    clear();
841    print_label_chunks();
842}
843
844static int
845diskLabel(Device *dev)
846{
847    daddr_t sz;
848    int  key = 0;
849    Boolean labeling;
850    char *msg = NULL;
851    PartInfo *p, *oldp;
852    PartType type;
853    Device **devs;
854    WINDOW *w = savescr();
855
856    label_focus = 0;
857    pslice_focus = 0;
858    here = 0;
859
860    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
861    if (!devs) {
862	msgConfirm("No disks found!");
863	restorescr(w);
864	return DITEM_FAILURE;
865    }
866    labeling = TRUE;
867    keypad(stdscr, TRUE);
868    record_label_chunks(devs, dev);
869
870    clear();
871    while (labeling) {
872	char *cp;
873	int rflags = DELCHUNK_NORMAL;
874
875	print_label_chunks();
876	print_command_summary();
877	if (msg) {
878	    attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
879	    clrtoeol();
880	    beep();
881	    msg = NULL;
882	}
883	else {
884	    move(23, 0);
885	    clrtoeol();
886	}
887
888	refresh();
889	key = getch();
890	switch (toupper(key)) {
891	    int i;
892	    static char _msg[40];
893
894	case '\014':	/* ^L */
895	    clear_wins();
896	    break;
897
898	case '\020':	/* ^P */
899	case KEY_UP:
900	case '-':
901	    if (here != 0)
902		--here;
903	    else
904		while (label_chunk_info[here + 1].c)
905		    ++here;
906	    break;
907
908	case '\016':	/* ^N */
909	case KEY_DOWN:
910	case '+':
911	case '\r':
912	case '\n':
913	    if (label_chunk_info[here + 1].c)
914		++here;
915	    else
916		here = 0;
917	    break;
918
919	case KEY_HOME:
920	    here = 0;
921	    break;
922
923	case KEY_END:
924	    while (label_chunk_info[here + 1].c)
925		++here;
926	    break;
927
928	case KEY_F(1):
929	case '?':
930	    systemDisplayHelp("partition");
931	    clear_wins();
932	    break;
933
934	case '1':
935	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
936		PartInfo *pi =
937		    ((PartInfo *)label_chunk_info[here].c->private_data);
938
939		if ((pi != NULL) &&
940		    (pi->newfs_type == NEWFS_UFS)) {
941			pi->newfs_data.newfs_ufs.ufs1 = true;
942		} else
943		    msg = MSG_NOT_APPLICABLE;
944	    } else
945		msg = MSG_NOT_APPLICABLE;
946	    break;
947		break;
948
949	case '2':
950	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
951		PartInfo *pi =
952		    ((PartInfo *)label_chunk_info[here].c->private_data);
953
954		if ((pi != NULL) &&
955		    (pi->newfs_type == NEWFS_UFS)) {
956			pi->newfs_data.newfs_ufs.ufs1 = false;
957		} else
958		    msg = MSG_NOT_APPLICABLE;
959	    } else
960		msg = MSG_NOT_APPLICABLE;
961	    break;
962		break;
963
964	case 'A':
965	    if (label_chunk_info[here].type != PART_SLICE) {
966		msg = "You can only do this in a disk slice (at top of screen)";
967		break;
968	    }
969	    /*
970	     * Generate standard partitions automatically.  If we do not
971	     * have sufficient space we attempt to scale-down the size
972	     * of the partitions within certain bounds.
973	     */
974	    {
975		int perc;
976		int req = 0;
977
978		for (perc = 100; perc > 0; perc -= 5) {
979		    req = 0;	/* reset for each loop */
980		    if ((msg = try_auto_label(devs, dev, perc, &req)) == NULL)
981			break;
982		}
983		if (msg) {
984		    if (req) {
985			msgConfirm(msg);
986			clear_wins();
987			msg = NULL;
988		    }
989		}
990	    }
991	    break;
992
993	case 'C':
994	    if (label_chunk_info[here].type != PART_SLICE) {
995		msg = "You can only do this in a master partition (see top of screen)";
996		break;
997	    }
998	    sz = space_free(label_chunk_info[here].c);
999	    if (sz <= FS_MIN_SIZE) {
1000		msg = "Not enough space to create an additional FreeBSD partition";
1001		break;
1002	    }
1003	    else {
1004		char *val;
1005		daddr_t size;
1006		struct chunk *tmp;
1007		char osize[80];
1008		u_long flags = 0;
1009
1010#ifdef __powerpc__
1011		/* Always use the maximum size for apple partitions */
1012		if (label_chunk_info[here].c->type == apple)
1013		    size = sz;
1014		else {
1015#endif
1016		sprintf(osize, "%jd", (intmax_t)sz);
1017		val = msgGetInput(osize,
1018				  "Please specify the partition size in blocks or append a trailing G for\n"
1019#ifdef __ia64__
1020				  "gigabytes, M for megabytes.\n"
1021#else
1022				  "gigabytes, M for megabytes, or C for cylinders.\n"
1023#endif
1024				  "%jd blocks (%jdMB) are free.",
1025				  (intmax_t)sz, (intmax_t)sz / ONE_MEG);
1026		if (!val || (size = strtoimax(val, &cp, 0)) <= 0) {
1027		    clear_wins();
1028		    break;
1029		}
1030
1031		if (*cp) {
1032		    if (toupper(*cp) == 'M')
1033			size *= ONE_MEG;
1034		    else if (toupper(*cp) == 'G')
1035			size *= ONE_GIG;
1036#ifndef __ia64__
1037		    else if (toupper(*cp) == 'C')
1038			size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
1039#endif
1040		}
1041		if (size <= FS_MIN_SIZE) {
1042		    msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
1043		    clear_wins();
1044		    break;
1045		}
1046#ifdef __powerpc__
1047		}
1048#endif
1049		type = get_partition_type();
1050		if (type == PART_NONE) {
1051		    clear_wins();
1052		    beep();
1053		    break;
1054		}
1055
1056		if (type == PART_FILESYSTEM || type == PART_EFI) {
1057		    if ((p = get_mountpoint(type, NULL)) == NULL) {
1058			clear_wins();
1059			beep();
1060			break;
1061		    }
1062		    else if (!strcmp(p->mountpoint, "/")) {
1063			if (type != PART_FILESYSTEM) {
1064			    clear_wins();
1065			    beep();
1066			    break;
1067			}
1068			else
1069			    flags |= CHUNK_IS_ROOT;
1070		    }
1071		    else
1072			flags &= ~CHUNK_IS_ROOT;
1073		}
1074		else
1075		    p = NULL;
1076
1077		if ((flags & CHUNK_IS_ROOT) && (size < (ROOT_MIN_SIZE * ONE_MEG))) {
1078		    msgConfirm("Warning: This is smaller than the recommended size for a\n"
1079			       "root partition.  For a variety of reasons, root\n"
1080			       "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
1081		}
1082		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1083		    label_chunk_info[here].c, size,
1084#ifdef __ia64__
1085		    (type == PART_EFI) ? efi : part,
1086		    (type == PART_EFI) ? 0 : (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1087#else
1088		    part, (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1089#endif
1090		    flags);
1091		if (!tmp) {
1092		    msgConfirm("Unable to create the partition. Too big?");
1093		    clear_wins();
1094		    break;
1095		}
1096
1097#ifdef __alpha__
1098		/*
1099		 * SRM requires that the root partition is at the
1100		 * begining of the disk and cannot boot otherwise.
1101		 * Warn Alpha users if they are about to shoot themselves in
1102		 * the foot in this way.
1103		 *
1104		 * Since partitions may not start precisely at offset 0 we
1105		 * check for a "close to 0" instead. :-(
1106		 */
1107		if ((flags & CHUNK_IS_ROOT) && (tmp->offset > 1024)) {
1108		    msgConfirm("Your root partition `a' does not seem to be the first\n"
1109			       "partition.  The Alpha's firmware can only boot from the\n"
1110			       "first partition.  So it is unlikely that your current\n"
1111			       "disk layout will be bootable boot after installation.\n"
1112			       "\n"
1113			       "Please allocate the root partition before allocating\n"
1114			       "any others.\n");
1115		}
1116#endif	/* alpha */
1117
1118		tmp->private_data = p;
1119		tmp->private_free = safe_free;
1120		if (variable_cmp(DISK_LABELLED, "written"))
1121		    variable_set2(DISK_LABELLED, "yes", 0);
1122		record_label_chunks(devs, dev);
1123		clear_wins();
1124                /* This is where we assign focus to new label so it shows. */
1125                {
1126                    int i;
1127		    label_focus = -1;
1128                    for (i = 0; label_chunk_info[i].c; ++i) {
1129                    	if (label_chunk_info[i].c == tmp) {
1130			    label_focus = i;
1131			    break;
1132			}
1133		    }
1134		    if (label_focus == -1)
1135                    	label_focus = i - 1;
1136                }
1137	    }
1138	    break;
1139
1140	case KEY_DC:
1141	case 'R':	/* recover space (delete w/ recover) */
1142	    /*
1143	     * Delete the partition w/ space recovery.
1144	     */
1145	    rflags = DELCHUNK_RECOVER;
1146	    /* fall through */
1147	case 'D':	/* delete */
1148	    if (label_chunk_info[here].type == PART_SLICE) {
1149		msg = MSG_NOT_APPLICABLE;
1150		break;
1151	    }
1152	    else if (label_chunk_info[here].type == PART_FAT) {
1153		msg = "Use the Disk Partition Editor to delete DOS partitions";
1154		break;
1155	    }
1156	    Delete_Chunk2(label_chunk_info[here].c->disk, label_chunk_info[here].c, rflags);
1157	    if (variable_cmp(DISK_LABELLED, "written"))
1158		variable_set2(DISK_LABELLED, "yes", 0);
1159	    record_label_chunks(devs, dev);
1160	    break;
1161
1162	case 'M':	/* mount */
1163	    switch(label_chunk_info[here].type) {
1164	    case PART_SLICE:
1165		msg = MSG_NOT_APPLICABLE;
1166		break;
1167
1168	    case PART_SWAP:
1169		msg = "You don't need to specify a mountpoint for a swap partition.";
1170		break;
1171
1172	    case PART_FAT:
1173	    case PART_EFI:
1174	    case PART_FILESYSTEM:
1175		oldp = label_chunk_info[here].c->private_data;
1176		p = get_mountpoint(label_chunk_info[here].type, label_chunk_info[here].c);
1177		if (p) {
1178		    if (!oldp)
1179		    	p->do_newfs = FALSE;
1180		    if ((label_chunk_info[here].type == PART_FAT ||
1181			    label_chunk_info[here].type == PART_EFI) &&
1182			(!strcmp(p->mountpoint, "/") ||
1183			    !strcmp(p->mountpoint, "/usr") ||
1184			    !strcmp(p->mountpoint, "/var"))) {
1185			msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
1186			strcpy(p->mountpoint, "/bogus");
1187		    }
1188		}
1189		if (variable_cmp(DISK_LABELLED, "written"))
1190		    variable_set2(DISK_LABELLED, "yes", 0);
1191		record_label_chunks(devs, dev);
1192		clear_wins();
1193		break;
1194
1195	    default:
1196		msgFatal("Bogus partition under cursor???");
1197		break;
1198	    }
1199	    break;
1200
1201	case 'N':	/* Set newfs options */
1202	    if (label_chunk_info[here].c->private_data &&
1203		((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1204		getNewfsOptionalArguments(
1205		    label_chunk_info[here].c->private_data);
1206	    else
1207		msg = MSG_NOT_APPLICABLE;
1208	    clear_wins();
1209	    break;
1210
1211	case 'S':	/* Toggle soft updates flag */
1212	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
1213		PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1214		if (pi != NULL &&
1215		    pi->newfs_type == NEWFS_UFS)
1216			pi->newfs_data.newfs_ufs.softupdates =
1217			    !pi->newfs_data.newfs_ufs.softupdates;
1218		else
1219		    msg = MSG_NOT_APPLICABLE;
1220	    }
1221	    else
1222		msg = MSG_NOT_APPLICABLE;
1223	    break;
1224
1225	case 'T':	/* Toggle newfs state */
1226	    if ((label_chunk_info[here].type == PART_FILESYSTEM ||
1227		 label_chunk_info[here].type == PART_EFI) &&
1228	        (label_chunk_info[here].c->private_data)) {
1229		PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1230		if (!pi->do_newfs)
1231		    label_chunk_info[here].c->flags |= CHUNK_NEWFS;
1232		else
1233		    label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
1234
1235		label_chunk_info[here].c->private_data =
1236		    new_part(label_chunk_info[here].type, pi ? pi->mountpoint : NULL, pi ? !pi->do_newfs
1237		    : TRUE);
1238		if (pi != NULL &&
1239		    pi->newfs_type == NEWFS_UFS) {
1240		    PartInfo *pi_new = label_chunk_info[here].c->private_data;
1241
1242		    pi_new->newfs_data.newfs_ufs = pi->newfs_data.newfs_ufs;
1243		}
1244		safe_free(pi);
1245		label_chunk_info[here].c->private_free = safe_free;
1246		if (variable_cmp(DISK_LABELLED, "written"))
1247		    variable_set2(DISK_LABELLED, "yes", 0);
1248	    }
1249	    else
1250		msg = MSG_NOT_APPLICABLE;
1251	    break;
1252
1253	case 'U':
1254	    clear();
1255	    if (!variable_cmp(DISK_LABELLED, "written")) {
1256		msgConfirm("You've already written out your changes -\n"
1257			   "it's too late to undo!");
1258	    }
1259	    else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
1260		variable_unset(DISK_PARTITIONED);
1261		variable_unset(DISK_LABELLED);
1262		for (i = 0; devs[i]; i++) {
1263		    Disk *d;
1264
1265		    if (!devs[i]->enabled)
1266			continue;
1267		    else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1268			Free_Disk(devs[i]->private);
1269			devs[i]->private = d;
1270#ifdef WITH_SLICES
1271			diskPartition(devs[i]);
1272#endif
1273		    }
1274		}
1275		record_label_chunks(devs, dev);
1276	    }
1277	    clear_wins();
1278	    break;
1279
1280	case 'W':
1281	    if (!variable_cmp(DISK_LABELLED, "written")) {
1282		msgConfirm("You've already written out your changes - if you\n"
1283			   "wish to overwrite them, you'll have to restart\n"
1284			   "sysinstall first.");
1285	    }
1286	    else if (!msgNoYes("WARNING:  This should only be used when modifying an EXISTING\n"
1287			  "installation.  If you are installing FreeBSD for the first time\n"
1288			  "then you should simply type Q when you're finished here and your\n"
1289			  "changes will be committed in one batch automatically at the end of\n"
1290			  "these questions.\n\n"
1291			  "Are you absolutely sure you want to do this now?")) {
1292		variable_set2(DISK_LABELLED, "yes", 0);
1293		diskLabelCommit(NULL);
1294	    }
1295	    clear_wins();
1296	    break;
1297
1298	case 'Z':	/* Set newfs command line */
1299	    if (label_chunk_info[here].c->private_data &&
1300		((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1301		getNewfsCmd(label_chunk_info[here].c->private_data);
1302	    else
1303		msg = MSG_NOT_APPLICABLE;
1304	    clear_wins();
1305	    break;
1306
1307#ifndef __ia64__
1308	case '|':
1309	    if (!msgNoYes("Are you sure you want to go into Wizard mode?\n\n"
1310			  "This is an entirely undocumented feature which you are not\n"
1311			  "expected to understand!")) {
1312		int i;
1313		Device **devs;
1314
1315		dialog_clear();
1316		end_dialog();
1317		DialogActive = FALSE;
1318		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1319		if (!devs) {
1320		    msgConfirm("Can't find any disk devices!");
1321		    break;
1322		}
1323		for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1324		    if (devs[i]->enabled)
1325		    	slice_wizard(((Disk *)devs[i]->private));
1326		}
1327		if (variable_cmp(DISK_LABELLED, "written"))
1328		    variable_set2(DISK_LABELLED, "yes", 0);
1329		DialogActive = TRUE;
1330		record_label_chunks(devs, dev);
1331		clear_wins();
1332	    }
1333	    else
1334		msg = "A most prudent choice!";
1335	    break;
1336#endif
1337
1338	case '\033':	/* ESC */
1339	case 'Q':
1340	    labeling = FALSE;
1341	    break;
1342
1343	default:
1344	    beep();
1345	    sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1346	    msg = _msg;
1347	    break;
1348	}
1349        if (label_chunk_info[here].type == PART_SLICE)
1350            pslice_focus = here;
1351        else
1352            label_focus = here;
1353    }
1354    restorescr(w);
1355    return DITEM_SUCCESS;
1356}
1357
1358static __inline daddr_t
1359requested_part_size(char *varName, daddr_t nom, int def, int perc)
1360{
1361    char *cp;
1362    daddr_t sz;
1363
1364    if ((cp = variable_get(varName)) != NULL)
1365	sz = strtoimax(cp, NULL, 0);
1366    else
1367	sz = nom + (def - nom) * perc / 100;
1368    return(sz * ONE_MEG);
1369}
1370
1371/*
1372 * Attempt to auto-label the disk.  'perc' (0-100) scales
1373 * the size of the various partitions within appropriate
1374 * bounds (NOMINAL through DEFAULT sizes).  The procedure
1375 * succeeds of NULL is returned.  A non-null return message
1376 * is either a failure-status message (*req == 0), or
1377 * a confirmation requestor (*req == 1).  *req is 0 on
1378 * entry to this call.
1379 *
1380 * As a special exception to the usual sizing rules, /var is given
1381 * additional space equal to the amount of physical memory present
1382 * if perc == 100 in order to ensure that users with large hard drives
1383 * will have enough space to store a crashdump in /var/crash.
1384 *
1385 * We autolabel the following partitions:  /, swap, /var, /tmp, /usr,
1386 * and /home.  /home receives any extra left over disk space.
1387 */
1388static char *
1389try_auto_label(Device **devs, Device *dev, int perc, int *req)
1390{
1391    daddr_t sz;
1392    Chunk *AutoHome, *AutoRoot, *AutoSwap;
1393    Chunk *AutoTmp, *AutoUsr, *AutoVar;
1394#ifdef __ia64__
1395    Chunk *AutoEfi;
1396#endif
1397    int mib[2];
1398    unsigned long physmem;
1399    size_t size;
1400    char *msg = NULL;
1401
1402    sz = space_free(label_chunk_info[here].c);
1403    if (sz <= FS_MIN_SIZE)
1404	return("Not enough free space to create a new partition in the slice");
1405
1406    (void)checkLabels(FALSE);
1407    AutoHome = AutoRoot = AutoSwap = NULL;
1408    AutoTmp = AutoUsr = AutoVar = NULL;
1409
1410#ifdef __ia64__
1411    AutoEfi = NULL;
1412    if (EfiChunk == NULL) {
1413	sz = 100 * ONE_MEG;
1414	AutoEfi = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1415	    label_chunk_info[here].c, sz, efi, 0, 0);
1416	if (AutoEfi == NULL) {
1417	    *req = 1;
1418	    msg = "Unable to create the EFI system partition. Too big?";
1419	    goto done;
1420	}
1421	AutoEfi->private_data = new_part(PART_EFI, "/efi", TRUE);
1422	AutoEfi->private_free = safe_free;
1423	AutoEfi->flags |= CHUNK_NEWFS;
1424	record_label_chunks(devs, dev);
1425    }
1426#endif
1427
1428    if (RootChunk == NULL) {
1429	sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
1430
1431	AutoRoot = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1432			    label_chunk_info[here].c, sz, part,
1433			    FS_BSDFFS,  CHUNK_IS_ROOT | CHUNK_AUTO_SIZE);
1434	if (!AutoRoot) {
1435	    *req = 1;
1436	    msg = "Unable to create the root partition. Too big?";
1437	    goto done;
1438	}
1439	AutoRoot->private_data = new_part(PART_FILESYSTEM, "/", TRUE);
1440	AutoRoot->private_free = safe_free;
1441	AutoRoot->flags |= CHUNK_NEWFS;
1442	record_label_chunks(devs, dev);
1443    }
1444    if (SwapChunk == NULL) {
1445	sz = requested_part_size(VAR_SWAP_SIZE, 0, 0, perc);
1446	if (sz == 0) {
1447	    daddr_t nom;
1448	    daddr_t def;
1449
1450	    mib[0] = CTL_HW;
1451	    mib[1] = HW_PHYSMEM;
1452	    size = sizeof physmem;
1453	    sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1454	    def = 2 * (int)(physmem / 512);
1455	    if (def < SWAP_MIN_SIZE * ONE_MEG)
1456		def = SWAP_MIN_SIZE * ONE_MEG;
1457	    if (def > SWAP_AUTO_LIMIT_SIZE * ONE_MEG)
1458		def = SWAP_AUTO_LIMIT_SIZE * ONE_MEG;
1459	    nom = (int)(physmem / 512) / 8;
1460	    sz = nom + (def - nom) * perc / 100;
1461	}
1462	AutoSwap = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1463			    label_chunk_info[here].c, sz, part,
1464			    FS_SWAP, CHUNK_AUTO_SIZE);
1465	if (!AutoSwap) {
1466	    *req = 1;
1467	    msg = "Unable to create the swap partition. Too big?";
1468	    goto done;
1469	}
1470	AutoSwap->private_data = 0;
1471	AutoSwap->private_free = safe_free;
1472	record_label_chunks(devs, dev);
1473    }
1474    if (VarChunk == NULL) {
1475	/* Work out how much extra space we want for a crash dump */
1476	unsigned long crashdumpsz;
1477
1478	mib[0] = CTL_HW;
1479	mib[1] = HW_PHYSMEM;
1480	size = sizeof(physmem);
1481	sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1482
1483	if (perc == 100)
1484		crashdumpsz = physmem / 1048576;
1485	else
1486		crashdumpsz = 0;
1487
1488	sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE,	\
1489	    VAR_DEFAULT_SIZE + crashdumpsz, perc);
1490
1491	AutoVar = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1492				label_chunk_info[here].c, sz, part,
1493				FS_BSDFFS, CHUNK_AUTO_SIZE);
1494	if (!AutoVar) {
1495	    *req = 1;
1496	    msg = "Not enough free space for /var - you will need to\n"
1497		   "partition your disk manually with a custom install!";
1498	    goto done;
1499	}
1500	AutoVar->private_data = new_part(PART_FILESYSTEM, "/var", TRUE);
1501	AutoVar->private_free = safe_free;
1502	AutoVar->flags |= CHUNK_NEWFS;
1503	record_label_chunks(devs, dev);
1504    }
1505    if (TmpChunk == NULL && !variable_get(VAR_NO_TMP)) {
1506	sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
1507
1508	AutoTmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1509				label_chunk_info[here].c, sz, part,
1510				FS_BSDFFS, CHUNK_AUTO_SIZE);
1511	if (!AutoTmp) {
1512	    *req = 1;
1513	    msg = "Not enough free space for /tmp - you will need to\n"
1514		   "partition your disk manually with a custom install!";
1515	    goto done;
1516	}
1517	AutoTmp->private_data = new_part(PART_FILESYSTEM, "/tmp", TRUE);
1518	AutoTmp->private_free = safe_free;
1519	AutoTmp->flags |= CHUNK_NEWFS;
1520	record_label_chunks(devs, dev);
1521    }
1522    if (UsrChunk == NULL && !variable_get(VAR_NO_USR)) {
1523	sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
1524#if AUTO_HOME == 0
1525	    sz = space_free(label_chunk_info[here].c);
1526#endif
1527	if (sz) {
1528	    if (sz < (USR_MIN_SIZE * ONE_MEG)) {
1529		*req = 1;
1530		msg = "Not enough free space for /usr - you will need to\n"
1531		       "partition your disk manually with a custom install!";
1532	    }
1533
1534	    AutoUsr = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1535				    label_chunk_info[here].c, sz, part,
1536				    FS_BSDFFS, CHUNK_AUTO_SIZE);
1537	    if (!AutoUsr) {
1538		msg = "Unable to create the /usr partition.  Not enough space?\n"
1539			   "You will need to partition your disk manually with a custom install!";
1540		goto done;
1541	    }
1542	    AutoUsr->private_data = new_part(PART_FILESYSTEM, "/usr", TRUE);
1543	    AutoUsr->private_free = safe_free;
1544	    AutoUsr->flags |= CHUNK_NEWFS;
1545	    record_label_chunks(devs, dev);
1546	}
1547    }
1548#if AUTO_HOME == 1
1549    if (HomeChunk == NULL && !variable_get(VAR_NO_HOME)) {
1550	sz = requested_part_size(VAR_HOME_SIZE, HOME_NOMINAL_SIZE, HOME_DEFAULT_SIZE, perc);
1551	if (sz < space_free(label_chunk_info[here].c))
1552	    sz = space_free(label_chunk_info[here].c);
1553	if (sz) {
1554	    if (sz < (HOME_MIN_SIZE * ONE_MEG)) {
1555		*req = 1;
1556		msg = "Not enough free space for /home - you will need to\n"
1557		       "partition your disk manually with a custom install!";
1558		goto done;
1559	    }
1560
1561	    AutoHome = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1562				    label_chunk_info[here].c, sz, part,
1563				    FS_BSDFFS, CHUNK_AUTO_SIZE);
1564	    if (!AutoHome) {
1565		msg = "Unable to create the /home partition.  Not enough space?\n"
1566			   "You will need to partition your disk manually with a custom install!";
1567		goto done;
1568	    }
1569	    AutoHome->private_data = new_part(PART_FILESYSTEM, "/home", TRUE);
1570	    AutoHome->private_free = safe_free;
1571	    AutoHome->flags |= CHUNK_NEWFS;
1572	    record_label_chunks(devs, dev);
1573	}
1574    }
1575#endif
1576
1577    /* At this point, we're reasonably "labelled" */
1578    if (variable_cmp(DISK_LABELLED, "written"))
1579	variable_set2(DISK_LABELLED, "yes", 0);
1580
1581done:
1582    if (msg) {
1583	if (AutoRoot != NULL)
1584	    Delete_Chunk(AutoRoot->disk, AutoRoot);
1585	if (AutoSwap != NULL)
1586	    Delete_Chunk(AutoSwap->disk, AutoSwap);
1587	if (AutoVar != NULL)
1588	    Delete_Chunk(AutoVar->disk, AutoVar);
1589	if (AutoTmp != NULL)
1590	    Delete_Chunk(AutoTmp->disk, AutoTmp);
1591	if (AutoUsr != NULL)
1592	    Delete_Chunk(AutoUsr->disk, AutoUsr);
1593	if (AutoHome != NULL)
1594	    Delete_Chunk(AutoHome->disk, AutoHome);
1595	record_label_chunks(devs, dev);
1596    }
1597    return(msg);
1598}
1599
1600static int
1601diskLabelNonInteractive(Device *dev)
1602{
1603    char *cp;
1604    PartType type;
1605    PartInfo *p;
1606    u_long flags;
1607    int i, status;
1608    Device **devs;
1609    Disk *d;
1610
1611    status = DITEM_SUCCESS;
1612    cp = variable_get(VAR_DISK);
1613    if (!cp) {
1614	msgConfirm("diskLabel:  No disk selected - can't label automatically.");
1615	return DITEM_FAILURE;
1616    }
1617    devs = deviceFind(cp, DEVICE_TYPE_DISK);
1618    if (!devs) {
1619	msgConfirm("diskLabel: No disk device %s found!", cp);
1620	return DITEM_FAILURE;
1621    }
1622    if (dev)
1623	d = dev->private;
1624    else
1625	d = devs[0]->private;
1626    record_label_chunks(devs, dev);
1627    for (i = 0; label_chunk_info[i].c; i++) {
1628	Chunk *c1 = label_chunk_info[i].c;
1629
1630	if (label_chunk_info[i].type == PART_SLICE) {
1631	    char name[512];
1632	    char typ[10], mpoint[50];
1633	    int entries;
1634
1635	    for (entries = 1;; entries++) {
1636		intmax_t sz;
1637		int soft = 0;
1638		snprintf(name, sizeof name, "%s-%d", c1->name, entries);
1639		if ((cp = variable_get(name)) == NULL)
1640		    break;
1641		if (sscanf(cp, "%s %jd %s %d", typ, &sz, mpoint, &soft) < 3) {
1642		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s",  c1->name, cp);
1643		    status = DITEM_FAILURE;
1644		    break;
1645		} else {
1646		    Chunk *tmp;
1647
1648		    flags = 0;
1649		    if (!strcmp(typ, "swap")) {
1650			type = PART_SWAP;
1651			strcpy(mpoint, "SWAP");
1652		    } else {
1653			type = PART_FILESYSTEM;
1654			if (!strcmp(mpoint, "/"))
1655			    flags |= CHUNK_IS_ROOT;
1656		    }
1657		    if (!sz)
1658			sz = space_free(c1);
1659		    if (sz > space_free(c1)) {
1660			msgConfirm("Not enough free space to create partition: %s", mpoint);
1661			status = DITEM_FAILURE;
1662			break;
1663		    }
1664		    if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
1665			(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
1666			msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
1667			status = DITEM_FAILURE;
1668			break;
1669		    } else {
1670			PartInfo *pi;
1671			pi = tmp->private_data = new_part(PART_FILESYSTEM, mpoint, TRUE);
1672			tmp->private_free = safe_free;
1673			pi->newfs_data.newfs_ufs.softupdates = soft;
1674		    }
1675		}
1676	    }
1677	} else {
1678	    /* Must be something we can set a mountpoint for */
1679	    cp = variable_get(c1->name);
1680	    if (cp) {
1681		char mpoint[50], do_newfs[8];
1682		Boolean newfs = FALSE;
1683
1684		do_newfs[0] = '\0';
1685		if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
1686		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1687		    status = DITEM_FAILURE;
1688		    break;
1689		}
1690		newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1691		if (c1->private_data) {
1692		    p = c1->private_data;
1693		    p->do_newfs = newfs;
1694		    strcpy(p->mountpoint, mpoint);
1695		}
1696		else {
1697		    c1->private_data = new_part(PART_FILESYSTEM, mpoint, newfs);
1698		    c1->private_free = safe_free;
1699		}
1700		if (!strcmp(mpoint, "/"))
1701		    c1->flags |= CHUNK_IS_ROOT;
1702		else
1703		    c1->flags &= ~CHUNK_IS_ROOT;
1704	    }
1705	}
1706    }
1707    if (status == DITEM_SUCCESS)
1708	variable_set2(DISK_LABELLED, "yes", 0);
1709    return status;
1710}
1711