label.c revision 107902
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 107902 2002-12-15 12:05:00Z kuriyama $
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.ufs2 = 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.ufs2 ? "-O2" : "-O1",
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.ufs2)
690				strcat(newfs, "2");
691			else
692				strcat(newfs, "1");
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 '2':
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#ifdef __i386__
883			if (label_chunk_info[here].c->flags & CHUNK_IS_ROOT)
884				msg = MSG_NOT_APPLICABLE;
885			else
886#endif
887				pi->newfs_data.newfs_ufs.ufs2 =
888				    !pi->newfs_data.newfs_ufs.ufs2;
889		} else
890		    msg = MSG_NOT_APPLICABLE;
891	    } else
892		msg = MSG_NOT_APPLICABLE;
893	    break;
894		break;
895
896	case 'A':
897	    if (label_chunk_info[here].type != PART_SLICE) {
898		msg = "You can only do this in a disk slice (at top of screen)";
899		break;
900	    }
901	    /*
902	     * Generate standard partitions automatically.  If we do not
903	     * have sufficient space we attempt to scale-down the size
904	     * of the partitions within certain bounds.
905	     */
906	    {
907		int perc;
908		int req = 0;
909
910		for (perc = 100; perc > 0; perc -= 5) {
911		    req = 0;	/* reset for each loop */
912		    if ((msg = try_auto_label(devs, dev, perc, &req)) == NULL)
913			break;
914		}
915		if (msg) {
916		    if (req) {
917			msgConfirm(msg);
918			clear_wins();
919			msg = NULL;
920		    }
921		}
922	    }
923	    break;
924
925	case 'C':
926	    if (label_chunk_info[here].type != PART_SLICE) {
927		msg = "You can only do this in a master partition (see top of screen)";
928		break;
929	    }
930	    sz = space_free(label_chunk_info[here].c);
931	    if (sz <= FS_MIN_SIZE) {
932		msg = "Not enough space to create an additional FreeBSD partition";
933		break;
934	    }
935	    else {
936		char *val;
937		int size;
938		struct chunk *tmp;
939		char osize[80];
940		u_long flags = 0;
941
942		sprintf(osize, "%d", sz);
943		val = msgGetInput(osize,
944				  "Please specify the partition size in blocks or append a trailing G for\n"
945				  "gigabytes, M for megabytes, or C for cylinders.\n"
946				  "%d blocks (%dMB) are free.",
947				  sz, sz / ONE_MEG);
948		if (!val || (size = strtol(val, &cp, 0)) <= 0) {
949		    clear_wins();
950		    break;
951		}
952
953		if (*cp) {
954		    if (toupper(*cp) == 'M')
955			size *= ONE_MEG;
956		    else if (toupper(*cp) == 'G')
957			size *= ONE_GIG;
958		    else if (toupper(*cp) == 'C')
959			size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
960		}
961		if (size <= FS_MIN_SIZE) {
962		    msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
963		    clear_wins();
964		    break;
965		}
966		type = get_partition_type();
967		if (type == PART_NONE) {
968		    clear_wins();
969		    beep();
970		    break;
971		}
972
973		if (type == PART_FILESYSTEM) {
974		    if ((p = get_mountpoint(NULL)) == NULL) {
975			clear_wins();
976			beep();
977			break;
978		    }
979		    else if (!strcmp(p->mountpoint, "/"))
980			flags |= CHUNK_IS_ROOT;
981		    else
982			flags &= ~CHUNK_IS_ROOT;
983		}
984		else
985		    p = NULL;
986
987		if ((flags & CHUNK_IS_ROOT) && (size < (ROOT_MIN_SIZE * ONE_MEG))) {
988		    msgConfirm("Warning: This is smaller than the recommended size for a\n"
989			       "root partition.  For a variety of reasons, root\n"
990			       "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
991		}
992		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
993					label_chunk_info[here].c,
994					size, part,
995					(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
996					flags);
997		if (!tmp) {
998		    msgConfirm("Unable to create the partition. Too big?");
999		    clear_wins();
1000		    break;
1001		}
1002
1003#ifdef __alpha__
1004		/*
1005		 * SRM requires that the root partition is at the
1006		 * begining of the disk and cannot boot otherwise.
1007		 * Warn Alpha users if they are about to shoot themselves in
1008		 * the foot in this way.
1009		 *
1010		 * Since partitions may not start precisely at offset 0 we
1011		 * check for a "close to 0" instead. :-(
1012		 */
1013		if ((flags & CHUNK_IS_ROOT) && (tmp->offset > 1024)) {
1014		    msgConfirm("Your root partition `a' does not seem to be the first\n"
1015			       "partition.  The Alpha's firmware can only boot from the\n"
1016			       "first partition.  So it is unlikely that your current\n"
1017			       "disk layout will be bootable boot after installation.\n"
1018			       "\n"
1019			       "Please allocate the root partition before allocating\n"
1020			       "any others.\n");
1021		}
1022#endif	/* alpha */
1023
1024		tmp->private_data = p;
1025		tmp->private_free = safe_free;
1026		if (variable_cmp(DISK_LABELLED, "written"))
1027		    variable_set2(DISK_LABELLED, "yes", 0);
1028		record_label_chunks(devs, dev);
1029		clear_wins();
1030                /* This is where we assign focus to new label so it shows. */
1031                {
1032                    int i;
1033		    label_focus = -1;
1034                    for (i = 0; label_chunk_info[i].c; ++i) {
1035                    	if (label_chunk_info[i].c == tmp) {
1036			    label_focus = i;
1037			    break;
1038			}
1039		    }
1040		    if (label_focus == -1)
1041                    	label_focus = i - 1;
1042                }
1043	    }
1044	    break;
1045
1046	case KEY_DC:
1047	case 'R':	/* recover space (delete w/ recover) */
1048	    /*
1049	     * Delete the partition w/ space recovery.
1050	     */
1051	    rflags = DELCHUNK_RECOVER;
1052	    /* fall through */
1053	case 'D':	/* delete */
1054	    if (label_chunk_info[here].type == PART_SLICE) {
1055		msg = MSG_NOT_APPLICABLE;
1056		break;
1057	    }
1058	    else if (label_chunk_info[here].type == PART_FAT) {
1059		msg = "Use the Disk Partition Editor to delete DOS partitions";
1060		break;
1061	    }
1062	    Delete_Chunk2(label_chunk_info[here].c->disk, label_chunk_info[here].c, rflags);
1063	    if (variable_cmp(DISK_LABELLED, "written"))
1064		variable_set2(DISK_LABELLED, "yes", 0);
1065	    record_label_chunks(devs, dev);
1066	    break;
1067
1068	case 'M':	/* mount */
1069	    switch(label_chunk_info[here].type) {
1070	    case PART_SLICE:
1071		msg = MSG_NOT_APPLICABLE;
1072		break;
1073
1074	    case PART_SWAP:
1075		msg = "You don't need to specify a mountpoint for a swap partition.";
1076		break;
1077
1078	    case PART_FAT:
1079	    case PART_FILESYSTEM:
1080		oldp = label_chunk_info[here].c->private_data;
1081		p = get_mountpoint(label_chunk_info[here].c);
1082		if (p) {
1083		    if (!oldp)
1084		    	p->do_newfs = FALSE;
1085		    if (label_chunk_info[here].type == PART_FAT
1086			&& (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr")
1087			    || !strcmp(p->mountpoint, "/var"))) {
1088			msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
1089			strcpy(p->mountpoint, "/bogus");
1090		    }
1091		}
1092		if (variable_cmp(DISK_LABELLED, "written"))
1093		    variable_set2(DISK_LABELLED, "yes", 0);
1094		record_label_chunks(devs, dev);
1095		clear_wins();
1096		break;
1097
1098	    default:
1099		msgFatal("Bogus partition under cursor???");
1100		break;
1101	    }
1102	    break;
1103
1104	case 'N':	/* Set newfs options */
1105	    if (label_chunk_info[here].c->private_data &&
1106		((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1107		getNewfsOptionalArguments(
1108		    label_chunk_info[here].c->private_data);
1109	    else
1110		msg = MSG_NOT_APPLICABLE;
1111	    clear_wins();
1112	    break;
1113
1114	case 'S':	/* Toggle soft updates flag */
1115	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
1116		PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1117		if (pi != NULL &&
1118		    pi->newfs_type == NEWFS_UFS)
1119			pi->newfs_data.newfs_ufs.softupdates =
1120			    !pi->newfs_data.newfs_ufs.softupdates;
1121		else
1122		    msg = MSG_NOT_APPLICABLE;
1123	    }
1124	    else
1125		msg = MSG_NOT_APPLICABLE;
1126	    break;
1127
1128	case 'T':	/* Toggle newfs state */
1129	    if ((label_chunk_info[here].type == PART_FILESYSTEM) &&
1130	        (label_chunk_info[here].c->private_data)) {
1131		PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1132		if (!pi->do_newfs)
1133		    label_chunk_info[here].c->flags |= CHUNK_NEWFS;
1134		else
1135		    label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
1136
1137		label_chunk_info[here].c->private_data =
1138		    new_part(pi ? pi->mountpoint : NULL, pi ? !pi->do_newfs
1139		    : TRUE);
1140		if (pi != NULL &&
1141		    pi->newfs_type == NEWFS_UFS) {
1142		    PartInfo *pi_new = label_chunk_info[here].c->private_data;
1143
1144		    pi_new->newfs_data.newfs_ufs = pi->newfs_data.newfs_ufs;
1145		}
1146		safe_free(pi);
1147		label_chunk_info[here].c->private_free = safe_free;
1148		if (variable_cmp(DISK_LABELLED, "written"))
1149		    variable_set2(DISK_LABELLED, "yes", 0);
1150	    }
1151#if defined(__ia64__)
1152	    else if (label_chunk_info[here].type == PART_FAT &&
1153	      label_chunk_info[here].c->type == efi &&
1154	      label_chunk_info[here].c->private_data) {
1155		PartInfo *pi =
1156		    ((PartInfo *)label_chunk_info[here].c->private_data);
1157
1158		if (!pi->do_newfs)
1159		    label_chunk_info[here].c->flags |= CHUNK_NEWFS;
1160		else
1161		    label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
1162
1163		label_chunk_info[here].c->private_data =
1164		    new_efi_part(pi->mountpoint, !pi->do_newfs);
1165		safe_free(pi);
1166		label_chunk_info[here].c->private_free = safe_free;
1167		if (variable_cmp(DISK_LABELLED, "written"))
1168		    variable_set2(DISK_LABELLED, "yes", 0);
1169	    }
1170#endif
1171	    else
1172		msg = MSG_NOT_APPLICABLE;
1173	    break;
1174
1175	case 'U':
1176	    clear();
1177	    if (!variable_cmp(DISK_LABELLED, "written")) {
1178		msgConfirm("You've already written out your changes -\n"
1179			   "it's too late to undo!");
1180	    }
1181	    else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
1182		variable_unset(DISK_PARTITIONED);
1183		variable_unset(DISK_LABELLED);
1184		for (i = 0; devs[i]; i++) {
1185		    Disk *d;
1186
1187		    if (!devs[i]->enabled)
1188			continue;
1189		    else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1190			Free_Disk(devs[i]->private);
1191			devs[i]->private = d;
1192#ifdef WITH_SLICES
1193			diskPartition(devs[i]);
1194#endif
1195		    }
1196		}
1197		record_label_chunks(devs, dev);
1198	    }
1199	    clear_wins();
1200	    break;
1201
1202	case 'W':
1203	    if (!variable_cmp(DISK_LABELLED, "written")) {
1204		msgConfirm("You've already written out your changes - if you\n"
1205			   "wish to overwrite them, you'll have to restart\n"
1206			   "sysinstall first.");
1207	    }
1208	    else if (!msgNoYes("WARNING:  This should only be used when modifying an EXISTING\n"
1209			  "installation.  If you are installing FreeBSD for the first time\n"
1210			  "then you should simply type Q when you're finished here and your\n"
1211			  "changes will be committed in one batch automatically at the end of\n"
1212			  "these questions.\n\n"
1213			  "Are you absolutely sure you want to do this now?")) {
1214		variable_set2(DISK_LABELLED, "yes", 0);
1215		diskLabelCommit(NULL);
1216	    }
1217	    clear_wins();
1218	    break;
1219
1220	case 'Z':	/* Set newfs command line */
1221	    if (label_chunk_info[here].c->private_data &&
1222		((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1223		getNewfsCmd(label_chunk_info[here].c->private_data);
1224	    else
1225		msg = MSG_NOT_APPLICABLE;
1226	    clear_wins();
1227	    break;
1228
1229
1230	case '|':
1231	    if (!msgNoYes("Are you sure you want to go into Wizard mode?\n\n"
1232			  "This is an entirely undocumented feature which you are not\n"
1233			  "expected to understand!")) {
1234		int i;
1235		Device **devs;
1236
1237		dialog_clear();
1238		end_dialog();
1239		DialogActive = FALSE;
1240		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1241		if (!devs) {
1242		    msgConfirm("Can't find any disk devices!");
1243		    break;
1244		}
1245		for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1246		    if (devs[i]->enabled)
1247		    	slice_wizard(((Disk *)devs[i]->private));
1248		}
1249		if (variable_cmp(DISK_LABELLED, "written"))
1250		    variable_set2(DISK_LABELLED, "yes", 0);
1251		DialogActive = TRUE;
1252		record_label_chunks(devs, dev);
1253		clear_wins();
1254	    }
1255	    else
1256		msg = "A most prudent choice!";
1257	    break;
1258
1259	case '\033':	/* ESC */
1260	case 'Q':
1261	    labeling = FALSE;
1262	    break;
1263
1264	default:
1265	    beep();
1266	    sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1267	    msg = _msg;
1268	    break;
1269	}
1270        if (label_chunk_info[here].type == PART_SLICE)
1271            pslice_focus = here;
1272        else
1273            label_focus = here;
1274    }
1275    restorescr(w);
1276    return DITEM_SUCCESS;
1277}
1278
1279static __inline int
1280requested_part_size(char *varName, int nom, int def, int perc)
1281{
1282    char *cp;
1283    int sz;
1284
1285    if ((cp = variable_get(varName)) != NULL)
1286	sz = atoi(cp);
1287    else
1288	sz = nom + (def - nom) * perc / 100;
1289    return(sz * ONE_MEG);
1290}
1291
1292/*
1293 * Attempt to auto-label the disk.  'perc' (0-100) scales
1294 * the size of the various partitions within appropriate
1295 * bounds (NOMINAL through DEFAULT sizes).  The procedure
1296 * succeeds of NULL is returned.  A non-null return message
1297 * is either a failure-status message (*req == 0), or
1298 * a confirmation requestor (*req == 1).  *req is 0 on
1299 * entry to this call.
1300 *
1301 * We autolabel the following partitions:  /, swap, /var, /tmp, /usr,
1302 * and /home.  /home receives any extra left over disk space.
1303 */
1304static char *
1305try_auto_label(Device **devs, Device *dev, int perc, int *req)
1306{
1307    int sz;
1308    struct chunk *root_chunk = NULL;
1309    struct chunk *swap_chunk = NULL;
1310    struct chunk *usr_chunk = NULL;
1311    struct chunk *var_chunk = NULL;
1312    struct chunk *tmp_chunk = NULL;
1313    struct chunk *home_chunk = NULL;
1314    int mib[2];
1315    unsigned long physmem;
1316    size_t size;
1317    Chunk *rootdev, *swapdev, *usrdev, *vardev;
1318    Chunk *tmpdev, *homedev;
1319    char *msg = NULL;
1320
1321    sz = space_free(label_chunk_info[here].c);
1322    if (sz <= FS_MIN_SIZE)
1323	return("Not enough free space to create a new partition in the slice");
1324
1325    (void)checkLabels(FALSE, &rootdev, &swapdev, &usrdev,
1326			&vardev, &tmpdev, &homedev);
1327    if (!rootdev) {
1328	sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
1329
1330	root_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1331			    label_chunk_info[here].c, sz, part,
1332			    FS_BSDFFS,  CHUNK_IS_ROOT | CHUNK_AUTO_SIZE);
1333	if (!root_chunk) {
1334	    *req = 1;
1335	    msg = "Unable to create the root partition. Too big?";
1336	    goto done;
1337	}
1338	root_chunk->private_data = new_part("/", TRUE);
1339	root_chunk->private_free = safe_free;
1340	root_chunk->flags |= CHUNK_NEWFS;
1341	record_label_chunks(devs, dev);
1342    }
1343    if (!swapdev) {
1344	sz = requested_part_size(VAR_SWAP_SIZE, 0, 0, perc);
1345	if (sz == 0) {
1346	    int nom;
1347	    int def;
1348
1349	    mib[0] = CTL_HW;
1350	    mib[1] = HW_PHYSMEM;
1351	    size = sizeof physmem;
1352	    sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1353	    def = 2 * (int)(physmem / 512);
1354	    if (def < SWAP_MIN_SIZE * ONE_MEG)
1355		def = SWAP_MIN_SIZE * ONE_MEG;
1356	    if (def > SWAP_AUTO_LIMIT_SIZE * ONE_MEG)
1357		def = SWAP_AUTO_LIMIT_SIZE * ONE_MEG;
1358	    nom = (int)(physmem / 512) / 2;
1359	    sz = nom + (def - nom) * perc / 100;
1360	}
1361	swap_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1362			    label_chunk_info[here].c, sz, part,
1363			    FS_SWAP, CHUNK_AUTO_SIZE);
1364	if (!swap_chunk) {
1365	    *req = 1;
1366	    msg = "Unable to create the swap partition. Too big?";
1367	    goto done;
1368	}
1369	swap_chunk->private_data = 0;
1370	swap_chunk->private_free = safe_free;
1371	record_label_chunks(devs, dev);
1372    }
1373    if (!vardev) {
1374	sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE, VAR_DEFAULT_SIZE, perc);
1375
1376	var_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1377				label_chunk_info[here].c, sz, part,
1378				FS_BSDFFS, CHUNK_AUTO_SIZE);
1379	if (!var_chunk) {
1380	    *req = 1;
1381	    msg = "Not enough free space for /var - you will need to\n"
1382		   "partition your disk manually with a custom install!";
1383	    goto done;
1384	}
1385	var_chunk->private_data = new_part("/var", TRUE);
1386	var_chunk->private_free = safe_free;
1387	var_chunk->flags |= CHUNK_NEWFS;
1388	record_label_chunks(devs, dev);
1389    }
1390    if (!tmpdev && !variable_get(VAR_NO_TMP)) {
1391	sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
1392
1393	tmp_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1394				label_chunk_info[here].c, sz, part,
1395				FS_BSDFFS, CHUNK_AUTO_SIZE);
1396	if (!tmp_chunk) {
1397	    *req = 1;
1398	    msg = "Not enough free space for /tmp - you will need to\n"
1399		   "partition your disk manually with a custom install!";
1400	    goto done;
1401	}
1402	tmp_chunk->private_data = new_part("/tmp", TRUE);
1403	tmp_chunk->private_free = safe_free;
1404	tmp_chunk->flags |= CHUNK_NEWFS;
1405	record_label_chunks(devs, dev);
1406    }
1407    if (!usrdev && !variable_get(VAR_NO_USR)) {
1408	sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
1409#if AUTO_HOME == 0
1410	    sz = space_free(label_chunk_info[here].c);
1411#endif
1412	if (sz) {
1413	    if (sz < (USR_MIN_SIZE * ONE_MEG)) {
1414		*req = 1;
1415		msg = "Not enough free space for /usr - you will need to\n"
1416		       "partition your disk manually with a custom install!";
1417	    }
1418
1419	    usr_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1420				    label_chunk_info[here].c, sz, part,
1421				    FS_BSDFFS, CHUNK_AUTO_SIZE);
1422	    if (!usr_chunk) {
1423		msg = "Unable to create the /usr partition.  Not enough space?\n"
1424			   "You will need to partition your disk manually with a custom install!";
1425		goto done;
1426	    }
1427	    usr_chunk->private_data = new_part("/usr", TRUE);
1428	    usr_chunk->private_free = safe_free;
1429	    usr_chunk->flags |= CHUNK_NEWFS;
1430	    record_label_chunks(devs, dev);
1431	}
1432    }
1433#if AUTO_HOME == 1
1434    if (!homedev && !variable_get(VAR_NO_HOME)) {
1435	sz = requested_part_size(VAR_HOME_SIZE, HOME_NOMINAL_SIZE, HOME_DEFAULT_SIZE, perc);
1436	if (sz < space_free(label_chunk_info[here].c))
1437	    sz = space_free(label_chunk_info[here].c);
1438	if (sz) {
1439	    if (sz < (HOME_MIN_SIZE * ONE_MEG)) {
1440		*req = 1;
1441		msg = "Not enough free space for /home - you will need to\n"
1442		       "partition your disk manually with a custom install!";
1443		goto done;
1444	    }
1445
1446	    home_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1447				    label_chunk_info[here].c, sz, part,
1448				    FS_BSDFFS, CHUNK_AUTO_SIZE);
1449	    if (!home_chunk) {
1450		msg = "Unable to create the /home partition.  Not enough space?\n"
1451			   "You will need to partition your disk manually with a custom install!";
1452		goto done;
1453	    }
1454	    home_chunk->private_data = new_part("/home", TRUE);
1455	    home_chunk->private_free = safe_free;
1456	    home_chunk->flags |= CHUNK_NEWFS;
1457	    record_label_chunks(devs, dev);
1458	}
1459    }
1460#endif
1461
1462    /* At this point, we're reasonably "labelled" */
1463    if (variable_cmp(DISK_LABELLED, "written"))
1464	variable_set2(DISK_LABELLED, "yes", 0);
1465
1466done:
1467    if (msg) {
1468	if (root_chunk)
1469	    Delete_Chunk(root_chunk->disk, root_chunk);
1470	if (swap_chunk)
1471	    Delete_Chunk(swap_chunk->disk, swap_chunk);
1472	if (var_chunk)
1473	    Delete_Chunk(var_chunk->disk, var_chunk);
1474	if (tmp_chunk)
1475	    Delete_Chunk(tmp_chunk->disk, tmp_chunk);
1476	if (usr_chunk)
1477	    Delete_Chunk(usr_chunk->disk, usr_chunk);
1478	if (home_chunk)
1479	    Delete_Chunk(home_chunk->disk, home_chunk);
1480	record_label_chunks(devs, dev);
1481    }
1482    return(msg);
1483}
1484
1485static int
1486diskLabelNonInteractive(Device *dev)
1487{
1488    char *cp;
1489    PartType type;
1490    PartInfo *p;
1491    u_long flags;
1492    int i, status;
1493    Device **devs;
1494    Disk *d;
1495
1496    status = DITEM_SUCCESS;
1497    cp = variable_get(VAR_DISK);
1498    if (!cp) {
1499	msgConfirm("diskLabel:  No disk selected - can't label automatically.");
1500	return DITEM_FAILURE;
1501    }
1502    devs = deviceFind(cp, DEVICE_TYPE_DISK);
1503    if (!devs) {
1504	msgConfirm("diskLabel: No disk device %s found!", cp);
1505	return DITEM_FAILURE;
1506    }
1507    if (dev)
1508	d = dev->private;
1509    else
1510	d = devs[0]->private;
1511    record_label_chunks(devs, dev);
1512    for (i = 0; label_chunk_info[i].c; i++) {
1513	Chunk *c1 = label_chunk_info[i].c;
1514
1515	if (label_chunk_info[i].type == PART_SLICE) {
1516	    char name[512];
1517	    char typ[10], mpoint[50];
1518	    int entries;
1519
1520	    for (entries = 1;; entries++) {
1521		int sz, soft = 0;
1522		snprintf(name, sizeof name, "%s-%d", c1->name, entries);
1523		if ((cp = variable_get(name)) == NULL)
1524		    break;
1525		if (sscanf(cp, "%s %d %s %d", typ, &sz, mpoint, &soft) < 3) {
1526		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s",  c1->name, cp);
1527		    status = DITEM_FAILURE;
1528		    break;
1529		} else {
1530		    Chunk *tmp;
1531
1532		    flags = 0;
1533		    if (!strcmp(typ, "swap")) {
1534			type = PART_SWAP;
1535			strcpy(mpoint, "SWAP");
1536		    } else {
1537			type = PART_FILESYSTEM;
1538			if (!strcmp(mpoint, "/"))
1539			    flags |= CHUNK_IS_ROOT;
1540		    }
1541		    if (!sz)
1542			sz = space_free(c1);
1543		    if (sz > space_free(c1)) {
1544			msgConfirm("Not enough free space to create partition: %s", mpoint);
1545			status = DITEM_FAILURE;
1546			break;
1547		    }
1548		    if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
1549			(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
1550			msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
1551			status = DITEM_FAILURE;
1552			break;
1553		    } else {
1554			PartInfo *pi;
1555			pi = tmp->private_data = new_part(mpoint, TRUE);
1556			tmp->private_free = safe_free;
1557			pi->newfs_data.newfs_ufs.softupdates = soft;
1558		    }
1559		}
1560	    }
1561	} else {
1562	    /* Must be something we can set a mountpoint for */
1563	    cp = variable_get(c1->name);
1564	    if (cp) {
1565		char mpoint[50], do_newfs[8];
1566		Boolean newfs = FALSE;
1567
1568		do_newfs[0] = '\0';
1569		if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
1570		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1571		    status = DITEM_FAILURE;
1572		    break;
1573		}
1574		newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1575		if (c1->private_data) {
1576		    p = c1->private_data;
1577		    p->do_newfs = newfs;
1578		    strcpy(p->mountpoint, mpoint);
1579		}
1580		else {
1581		    c1->private_data = new_part(mpoint, newfs);
1582		    c1->private_free = safe_free;
1583		}
1584		if (!strcmp(mpoint, "/"))
1585		    c1->flags |= CHUNK_IS_ROOT;
1586		else
1587		    c1->flags &= ~CHUNK_IS_ROOT;
1588	    }
1589	}
1590    }
1591    if (status == DITEM_SUCCESS)
1592	variable_set2(DISK_LABELLED, "yes", 0);
1593    return status;
1594}
1595