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