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