label.c revision 115293
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 115293 2003-05-24 21:12:14Z peter $
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__) || defined(__amd64__)
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#ifdef PC98
341    pi->newfs_data.newfs_ufs.ufs1 = TRUE;
342#else
343    pi->newfs_data.newfs_ufs.ufs1 = FALSE;
344#endif
345
346    return pi;
347}
348
349#if defined(__ia64__)
350static PartInfo *
351new_efi_part(char *mpoint, Boolean newfs)
352{
353    PartInfo *pi;
354
355    if (!mpoint)
356	mpoint = "/efi";
357
358    pi = (PartInfo *)safe_malloc(sizeof(PartInfo));
359    sstrncpy(pi->mountpoint, mpoint, FILENAME_MAX);
360
361    pi->do_newfs = newfs;
362    pi->newfs_type = NEWFS_MSDOS;
363
364    return pi;
365}
366#endif
367
368/* Get the mountpoint for a partition and save it away */
369static PartInfo *
370get_mountpoint(struct chunk *old)
371{
372    char *val;
373    PartInfo *tmp;
374    Boolean newfs;
375
376    if (old && old->private_data)
377	tmp = old->private_data;
378    else
379	tmp = NULL;
380    val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
381    if (!val || !*val) {
382	if (!old)
383	    return NULL;
384	else {
385	    free(old->private_data);
386	    old->private_data = NULL;
387	}
388	return NULL;
389    }
390
391    /* Is it just the same value? */
392    if (tmp && !strcmp(tmp->mountpoint, val))
393	return NULL;
394
395    /* Did we use it already? */
396    if (check_conflict(val)) {
397	msgConfirm("You already have a mount point for %s assigned!", val);
398	return NULL;
399    }
400
401    /* Is it bogus? */
402    if (*val != '/') {
403	msgConfirm("Mount point must start with a / character");
404	return NULL;
405    }
406
407    /* Is it going to be mounted on root? */
408    if (!strcmp(val, "/")) {
409	if (old)
410	    old->flags |= CHUNK_IS_ROOT;
411    }
412    else if (old)
413	old->flags &= ~CHUNK_IS_ROOT;
414
415    newfs = TRUE;
416    if (tmp) {
417	newfs = tmp->do_newfs;
418    	safe_free(tmp);
419    }
420    val = string_skipwhite(string_prune(val));
421    tmp = new_part(val, newfs);
422    if (old) {
423	old->private_data = tmp;
424	old->private_free = safe_free;
425    }
426    return tmp;
427}
428
429/* Get the type of the new partiton */
430static PartType
431get_partition_type(void)
432{
433    char selection[20];
434    int i;
435    static unsigned char *fs_types[] = {
436	"FS",
437	"A file system",
438	"Swap",
439	"A swap partition.",
440    };
441    WINDOW *w = savescr();
442
443    i = dialog_menu("Please choose a partition type",
444		    "If you want to use this partition for swap space, select Swap.\n"
445		    "If you want to put a filesystem on it, choose FS.",
446		    -1, -1, 2, 2, fs_types, selection, NULL, NULL);
447    restorescr(w);
448    if (!i) {
449	if (!strcmp(selection, "FS"))
450	    return PART_FILESYSTEM;
451	else if (!strcmp(selection, "Swap"))
452	    return PART_SWAP;
453    }
454    return PART_NONE;
455}
456
457/* If the user wants a special newfs command for this, set it */
458static void
459getNewfsCmd(PartInfo *p)
460{
461    char buffer[NEWFS_CMD_ARGS_MAX];
462    char *val;
463
464    switch (p->newfs_type) {
465    case NEWFS_UFS:
466	snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s %s %s %s",
467	    NEWFS_UFS_CMD, p->newfs_data.newfs_ufs.softupdates ?  "-U" : "",
468	    p->newfs_data.newfs_ufs.ufs1 ? "-O1" : "-O2",
469	    p->newfs_data.newfs_ufs.user_options);
470	break;
471    case NEWFS_MSDOS:
472	snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s", NEWFS_MSDOS_CMD);
473	break;
474    case NEWFS_CUSTOM:
475	strcpy(buffer, p->newfs_data.newfs_custom.command);
476	break;
477    }
478
479    val = msgGetInput(buffer,
480	"Please enter the newfs command and options you'd like to use in\n"
481	"creating this file system.");
482    if (val != NULL) {
483	p->newfs_type = NEWFS_CUSTOM;
484	strlcpy(p->newfs_data.newfs_custom.command, val, NEWFS_CMD_ARGS_MAX);
485    }
486}
487
488static void
489getNewfsOptionalArguments(PartInfo *p)
490{
491	char buffer[NEWFS_CMD_ARGS_MAX];
492	char *val;
493
494	/* Must be UFS, per argument checking in I/O routines. */
495
496	strlcpy(buffer,  p->newfs_data.newfs_ufs.user_options,
497	    NEWFS_CMD_ARGS_MAX);
498	val = msgGetInput(buffer,
499	    "Please enter any additional UFS newfs options you'd like to\n"
500	    "use in creating this file system.");
501	if (val != NULL)
502		strlcpy(p->newfs_data.newfs_ufs.user_options, val,
503		    NEWFS_CMD_ARGS_MAX);
504}
505
506#define MAX_MOUNT_NAME	9
507
508#define PART_PART_COL	0
509#define PART_MOUNT_COL	10
510#define PART_SIZE_COL	(PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
511#define PART_NEWFS_COL	(PART_SIZE_COL + 8)
512#define PART_OFF	38
513
514#define TOTAL_AVAIL_LINES       (10)
515#define PSLICE_SHOWABLE          (4)
516
517
518/* stick this all up on the screen */
519static void
520print_label_chunks(void)
521{
522    int  i, j, spaces, srow, prow, pcol;
523    int  sz;
524    char clrmsg[80];
525    int ChunkPartStartRow;
526    WINDOW *ChunkWin;
527
528    /********************************************************/
529    /*** These values are for controling screen resources ***/
530    /*** Each label line holds up to 2 labels, so beware! ***/
531    /*** strategy will be to try to always make sure the  ***/
532    /*** highlighted label is in the active display area. ***/
533    /********************************************************/
534    int  pslice_max, label_max;
535    int  pslice_count, label_count, label_focus_found, pslice_focus_found;
536
537    attrset(A_REVERSE);
538    mvaddstr(0, 25, "FreeBSD Disklabel Editor");
539    attrset(A_NORMAL);
540
541    /*** Count the number of parition slices ***/
542    pslice_count = 0;
543    for (i = 0; label_chunk_info[i].c ; i++) {
544        if (label_chunk_info[i].type == PART_SLICE)
545            ++pslice_count;
546    }
547    pslice_max = pslice_count;
548
549    /*** 4 line max for partition slices ***/
550    if (pslice_max > PSLICE_SHOWABLE) {
551        pslice_max = PSLICE_SHOWABLE;
552    }
553    ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3 + pslice_max;
554
555    /*** View partition slices modulo pslice_max ***/
556    label_max = TOTAL_AVAIL_LINES - pslice_max;
557
558    for (i = 0; i < 2; i++) {
559	mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
560	mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
561
562	mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
563	mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
564
565	mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 3, "Size");
566	mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 3, "----");
567
568	mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
569	mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
570    }
571    srow = CHUNK_SLICE_START_ROW;
572    prow = 0;
573    pcol = 0;
574
575    /*** these variables indicate that the focused item is shown currently ***/
576    label_focus_found = 0;
577    pslice_focus_found = 0;
578
579    label_count = 0;
580    pslice_count = 0;
581    mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "          ");
582    mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "          ");
583
584    ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
585
586    wclear(ChunkWin);
587    /*** wrefresh(ChunkWin); ***/
588
589    for (i = 0; label_chunk_info[i].c; i++) {
590	/* Is it a slice entry displayed at the top? */
591	if (label_chunk_info[i].type == PART_SLICE) {
592            /*** This causes the new pslice to replace the previous display ***/
593            /*** focus must remain on the most recently active pslice       ***/
594            if (pslice_count == pslice_max) {
595                if (pslice_focus_found) {
596                    /*** This is where we can mark the more following ***/
597                    attrset(A_BOLD);
598                    mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
599                    attrset(A_NORMAL);
600                    continue;
601                }
602                else {
603                    /*** this is where we set the more previous ***/
604                    attrset(A_BOLD);
605                    mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
606                    attrset(A_NORMAL);
607                    pslice_count = 0;
608                    srow = CHUNK_SLICE_START_ROW;
609                }
610            }
611
612	    sz = space_free(label_chunk_info[i].c);
613	    if (i == here)
614		attrset(ATTR_SELECTED);
615            if (i == pslice_focus)
616                pslice_focus_found = -1;
617
618	    if (sz >= 100 * ONE_GIG)
619		    mvprintw(srow++, 0,
620			"Disk: %s\tPartition name: %s\tFree: %d blocks (%dGB)",
621			label_chunk_info[i].c->disk->name,
622			label_chunk_info[i].c->name,
623			sz, (sz / ONE_GIG));
624	    else
625		    mvprintw(srow++, 0,
626			"Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
627			label_chunk_info[i].c->disk->name,
628			label_chunk_info[i].c->name,
629			sz, (sz / ONE_MEG));
630	    attrset(A_NORMAL);
631	    clrtoeol();
632	    move(0, 0);
633	    /*** refresh(); ***/
634            ++pslice_count;
635	}
636	/* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
637	else {
638	    char onestr[PART_OFF], num[10], *mountpoint, newfs[12];
639
640	    /*
641	     * We copy this into a blank-padded string so that it looks like
642	     * a solid bar in reverse-video
643	     */
644	    memset(onestr, ' ', PART_OFF - 1);
645	    onestr[PART_OFF - 1] = '\0';
646
647            /*** Track how many labels have been displayed ***/
648            if (label_count == ((label_max - 1 ) * 2)) {
649                if (label_focus_found) {
650                    continue;
651                }
652                else {
653                    label_count = 0;
654                    prow = 0;
655                    pcol = 0;
656                }
657            }
658
659	    /* Go for two columns if we've written one full columns worth */
660	    /*** if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) ***/
661            if (label_count == label_max - 1) {
662		pcol = PART_OFF;
663		prow = 0;
664	    }
665	    memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
666	    /* If it's a filesystem, display the mountpoint */
667	    if (label_chunk_info[i].c->private_data
668		&& (label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT))
669	        mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
670	    else if (label_chunk_info[i].type == PART_SWAP)
671		mountpoint = "swap";
672	    else
673	        mountpoint = "<none>";
674
675	    /* Now display the newfs field */
676	    if (label_chunk_info[i].type == PART_FAT) {
677		strcpy(newfs, "DOS");
678#if defined(__ia64__)
679		if (label_chunk_info[i].c->private_data &&
680		    label_chunk_info[i].c->type == efi) {
681			strcat(newfs, "  ");
682			PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
683			strcat(newfs, pi->do_newfs ? " Y" : " N");
684		}
685#endif
686	    }
687	    else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM) {
688		PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
689
690		switch (pi->newfs_type) {
691		case NEWFS_UFS:
692			strcpy(newfs, NEWFS_UFS_STRING);
693			if (pi->newfs_data.newfs_ufs.ufs1)
694				strcat(newfs, "1");
695			else
696				strcat(newfs, "2");
697			if (pi->newfs_data.newfs_ufs.softupdates)
698				strcat(newfs, "+S");
699			else
700				strcat(newfs, "  ");
701
702			break;
703		case NEWFS_MSDOS:
704			strcpy(newfs, "FAT");
705			break;
706		case NEWFS_CUSTOM:
707			strcpy(newfs, "CUST");
708			break;
709		}
710		strcat(newfs, pi->do_newfs ? " Y" : " N ");
711	    }
712	    else if (label_chunk_info[i].type == PART_SWAP)
713		strcpy(newfs, "SWAP");
714	    else
715		strcpy(newfs, "*");
716	    for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
717		onestr[PART_MOUNT_COL + j] = mountpoint[j];
718	    if (label_chunk_info[i].c->size == 0)
719	        snprintf(num, 10, "%5ldMB", 0);
720	    else if (label_chunk_info[i].c->size < (100 * ONE_GIG))
721		snprintf(num, 10, "%5ldMB",
722		    label_chunk_info[i].c->size / ONE_MEG);
723	    else
724		snprintf(num, 10, "%5ldGB",
725		    label_chunk_info[i].c->size / ONE_GIG);
726	    memcpy(onestr + PART_SIZE_COL, num, strlen(num));
727	    memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
728	    onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
729            if (i == label_focus) {
730                label_focus_found = -1;
731                wattrset(ChunkWin, A_BOLD);
732            }
733	    if (i == here)
734		wattrset(ChunkWin, ATTR_SELECTED);
735
736            /*** lazy man's way of expensively padding this string ***/
737            while (strlen(onestr) < 37)
738                strcat(onestr, " ");
739
740	    mvwaddstr(ChunkWin, prow, pcol, onestr);
741	    wattrset(ChunkWin, A_NORMAL);
742	    move(0, 0);
743	    ++prow;
744            ++label_count;
745	}
746    }
747
748    /*** this will erase all the extra stuff ***/
749    memset(clrmsg, ' ', 37);
750    clrmsg[37] = '\0';
751
752    while (pslice_count < pslice_max) {
753        mvprintw(srow++, 0, clrmsg);
754        clrtoeol();
755        ++pslice_count;
756    }
757    while (label_count < (2 * (label_max - 1))) {
758        mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
759	++label_count;
760	if (prow == (label_max - 1)) {
761	    prow = 0;
762	    pcol = PART_OFF;
763	}
764    }
765    refresh();
766    wrefresh(ChunkWin);
767}
768
769static void
770print_command_summary(void)
771{
772    mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
773    mvprintw(18, 0, "C = Create        D = Delete   M = Mount pt.");
774    if (!RunningAsInit)
775	mvprintw(18, 56, "W = Write");
776    mvprintw(19, 0, "N = Newfs Opts    Q = Finish   S = Toggle SoftUpdates   Z = Custom Newfs");
777    mvprintw(20, 0, "T = Toggle Newfs  U = Undo     A = Auto Defaults        R = Delete+Merge");
778    mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
779    move(0, 0);
780}
781
782static void
783clear_wins(void)
784{
785    extern void print_label_chunks();
786    clear();
787    print_label_chunks();
788}
789
790static int
791diskLabel(Device *dev)
792{
793    int sz, key = 0;
794    Boolean labeling;
795    char *msg = NULL;
796    PartInfo *p, *oldp;
797    PartType type;
798    Device **devs;
799    WINDOW *w = savescr();
800
801    label_focus = 0;
802    pslice_focus = 0;
803    here = 0;
804
805    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
806    if (!devs) {
807	msgConfirm("No disks found!");
808	restorescr(w);
809	return DITEM_FAILURE;
810    }
811    labeling = TRUE;
812    keypad(stdscr, TRUE);
813    record_label_chunks(devs, dev);
814
815    clear();
816    while (labeling) {
817	char *cp;
818	int rflags = DELCHUNK_NORMAL;
819
820	print_label_chunks();
821	print_command_summary();
822	if (msg) {
823	    attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
824	    clrtoeol();
825	    beep();
826	    msg = NULL;
827	}
828	else {
829	    move(23, 0);
830	    clrtoeol();
831	}
832
833	refresh();
834	key = getch();
835	switch (toupper(key)) {
836	    int i;
837	    static char _msg[40];
838
839	case '\014':	/* ^L */
840	    clear_wins();
841	    break;
842
843	case '\020':	/* ^P */
844	case KEY_UP:
845	case '-':
846	    if (here != 0)
847		--here;
848	    else
849		while (label_chunk_info[here + 1].c)
850		    ++here;
851	    break;
852
853	case '\016':	/* ^N */
854	case KEY_DOWN:
855	case '+':
856	case '\r':
857	case '\n':
858	    if (label_chunk_info[here + 1].c)
859		++here;
860	    else
861		here = 0;
862	    break;
863
864	case KEY_HOME:
865	    here = 0;
866	    break;
867
868	case KEY_END:
869	    while (label_chunk_info[here + 1].c)
870		++here;
871	    break;
872
873	case KEY_F(1):
874	case '?':
875	    systemDisplayHelp("partition");
876	    clear_wins();
877	    break;
878
879	case '1':
880	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
881		PartInfo *pi =
882		    ((PartInfo *)label_chunk_info[here].c->private_data);
883
884		if ((pi != NULL) &&
885		    (pi->newfs_type == NEWFS_UFS)) {
886			pi->newfs_data.newfs_ufs.ufs1 = true;
887		} else
888		    msg = MSG_NOT_APPLICABLE;
889	    } else
890		msg = MSG_NOT_APPLICABLE;
891	    break;
892		break;
893
894	case '2':
895	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
896		PartInfo *pi =
897		    ((PartInfo *)label_chunk_info[here].c->private_data);
898
899		if ((pi != NULL) &&
900		    (pi->newfs_type == NEWFS_UFS)) {
901			pi->newfs_data.newfs_ufs.ufs1 = false;
902		} else
903		    msg = MSG_NOT_APPLICABLE;
904	    } else
905		msg = MSG_NOT_APPLICABLE;
906	    break;
907		break;
908
909	case 'A':
910	    if (label_chunk_info[here].type != PART_SLICE) {
911		msg = "You can only do this in a disk slice (at top of screen)";
912		break;
913	    }
914	    /*
915	     * Generate standard partitions automatically.  If we do not
916	     * have sufficient space we attempt to scale-down the size
917	     * of the partitions within certain bounds.
918	     */
919	    {
920		int perc;
921		int req = 0;
922
923		for (perc = 100; perc > 0; perc -= 5) {
924		    req = 0;	/* reset for each loop */
925		    if ((msg = try_auto_label(devs, dev, perc, &req)) == NULL)
926			break;
927		}
928		if (msg) {
929		    if (req) {
930			msgConfirm(msg);
931			clear_wins();
932			msg = NULL;
933		    }
934		}
935	    }
936	    break;
937
938	case 'C':
939	    if (label_chunk_info[here].type != PART_SLICE) {
940		msg = "You can only do this in a master partition (see top of screen)";
941		break;
942	    }
943	    sz = space_free(label_chunk_info[here].c);
944	    if (sz <= FS_MIN_SIZE) {
945		msg = "Not enough space to create an additional FreeBSD partition";
946		break;
947	    }
948	    else {
949		char *val;
950		int size;
951		struct chunk *tmp;
952		char osize[80];
953		u_long flags = 0;
954
955		sprintf(osize, "%d", sz);
956		val = msgGetInput(osize,
957				  "Please specify the partition size in blocks or append a trailing G for\n"
958				  "gigabytes, M for megabytes, or C for cylinders.\n"
959				  "%d blocks (%dMB) are free.",
960				  sz, sz / ONE_MEG);
961		if (!val || (size = strtol(val, &cp, 0)) <= 0) {
962		    clear_wins();
963		    break;
964		}
965
966		if (*cp) {
967		    if (toupper(*cp) == 'M')
968			size *= ONE_MEG;
969		    else if (toupper(*cp) == 'G')
970			size *= ONE_GIG;
971		    else if (toupper(*cp) == 'C')
972			size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
973		}
974		if (size <= FS_MIN_SIZE) {
975		    msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
976		    clear_wins();
977		    break;
978		}
979		type = get_partition_type();
980		if (type == PART_NONE) {
981		    clear_wins();
982		    beep();
983		    break;
984		}
985
986		if (type == PART_FILESYSTEM) {
987		    if ((p = get_mountpoint(NULL)) == NULL) {
988			clear_wins();
989			beep();
990			break;
991		    }
992		    else if (!strcmp(p->mountpoint, "/"))
993			flags |= CHUNK_IS_ROOT;
994		    else
995			flags &= ~CHUNK_IS_ROOT;
996		}
997		else
998		    p = NULL;
999
1000		if ((flags & CHUNK_IS_ROOT) && (size < (ROOT_MIN_SIZE * ONE_MEG))) {
1001		    msgConfirm("Warning: This is smaller than the recommended size for a\n"
1002			       "root partition.  For a variety of reasons, root\n"
1003			       "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
1004		}
1005		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1006					label_chunk_info[here].c,
1007					size, part,
1008					(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1009					flags);
1010		if (!tmp) {
1011		    msgConfirm("Unable to create the partition. Too big?");
1012		    clear_wins();
1013		    break;
1014		}
1015
1016#ifdef __alpha__
1017		/*
1018		 * SRM requires that the root partition is at the
1019		 * begining of the disk and cannot boot otherwise.
1020		 * Warn Alpha users if they are about to shoot themselves in
1021		 * the foot in this way.
1022		 *
1023		 * Since partitions may not start precisely at offset 0 we
1024		 * check for a "close to 0" instead. :-(
1025		 */
1026		if ((flags & CHUNK_IS_ROOT) && (tmp->offset > 1024)) {
1027		    msgConfirm("Your root partition `a' does not seem to be the first\n"
1028			       "partition.  The Alpha's firmware can only boot from the\n"
1029			       "first partition.  So it is unlikely that your current\n"
1030			       "disk layout will be bootable boot after installation.\n"
1031			       "\n"
1032			       "Please allocate the root partition before allocating\n"
1033			       "any others.\n");
1034		}
1035#endif	/* alpha */
1036
1037		tmp->private_data = p;
1038		tmp->private_free = safe_free;
1039		if (variable_cmp(DISK_LABELLED, "written"))
1040		    variable_set2(DISK_LABELLED, "yes", 0);
1041		record_label_chunks(devs, dev);
1042		clear_wins();
1043                /* This is where we assign focus to new label so it shows. */
1044                {
1045                    int i;
1046		    label_focus = -1;
1047                    for (i = 0; label_chunk_info[i].c; ++i) {
1048                    	if (label_chunk_info[i].c == tmp) {
1049			    label_focus = i;
1050			    break;
1051			}
1052		    }
1053		    if (label_focus == -1)
1054                    	label_focus = i - 1;
1055                }
1056	    }
1057	    break;
1058
1059	case KEY_DC:
1060	case 'R':	/* recover space (delete w/ recover) */
1061	    /*
1062	     * Delete the partition w/ space recovery.
1063	     */
1064	    rflags = DELCHUNK_RECOVER;
1065	    /* fall through */
1066	case 'D':	/* delete */
1067	    if (label_chunk_info[here].type == PART_SLICE) {
1068		msg = MSG_NOT_APPLICABLE;
1069		break;
1070	    }
1071	    else if (label_chunk_info[here].type == PART_FAT) {
1072		msg = "Use the Disk Partition Editor to delete DOS partitions";
1073		break;
1074	    }
1075	    Delete_Chunk2(label_chunk_info[here].c->disk, label_chunk_info[here].c, rflags);
1076	    if (variable_cmp(DISK_LABELLED, "written"))
1077		variable_set2(DISK_LABELLED, "yes", 0);
1078	    record_label_chunks(devs, dev);
1079	    break;
1080
1081	case 'M':	/* mount */
1082	    switch(label_chunk_info[here].type) {
1083	    case PART_SLICE:
1084		msg = MSG_NOT_APPLICABLE;
1085		break;
1086
1087	    case PART_SWAP:
1088		msg = "You don't need to specify a mountpoint for a swap partition.";
1089		break;
1090
1091	    case PART_FAT:
1092	    case PART_FILESYSTEM:
1093		oldp = label_chunk_info[here].c->private_data;
1094		p = get_mountpoint(label_chunk_info[here].c);
1095		if (p) {
1096		    if (!oldp)
1097		    	p->do_newfs = FALSE;
1098		    if (label_chunk_info[here].type == PART_FAT
1099			&& (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr")
1100			    || !strcmp(p->mountpoint, "/var"))) {
1101			msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
1102			strcpy(p->mountpoint, "/bogus");
1103		    }
1104		}
1105		if (variable_cmp(DISK_LABELLED, "written"))
1106		    variable_set2(DISK_LABELLED, "yes", 0);
1107		record_label_chunks(devs, dev);
1108		clear_wins();
1109		break;
1110
1111	    default:
1112		msgFatal("Bogus partition under cursor???");
1113		break;
1114	    }
1115	    break;
1116
1117	case 'N':	/* Set newfs options */
1118	    if (label_chunk_info[here].c->private_data &&
1119		((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1120		getNewfsOptionalArguments(
1121		    label_chunk_info[here].c->private_data);
1122	    else
1123		msg = MSG_NOT_APPLICABLE;
1124	    clear_wins();
1125	    break;
1126
1127	case 'S':	/* Toggle soft updates flag */
1128	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
1129		PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1130		if (pi != NULL &&
1131		    pi->newfs_type == NEWFS_UFS)
1132			pi->newfs_data.newfs_ufs.softupdates =
1133			    !pi->newfs_data.newfs_ufs.softupdates;
1134		else
1135		    msg = MSG_NOT_APPLICABLE;
1136	    }
1137	    else
1138		msg = MSG_NOT_APPLICABLE;
1139	    break;
1140
1141	case 'T':	/* Toggle newfs state */
1142	    if ((label_chunk_info[here].type == PART_FILESYSTEM) &&
1143	        (label_chunk_info[here].c->private_data)) {
1144		PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1145		if (!pi->do_newfs)
1146		    label_chunk_info[here].c->flags |= CHUNK_NEWFS;
1147		else
1148		    label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
1149
1150		label_chunk_info[here].c->private_data =
1151		    new_part(pi ? pi->mountpoint : NULL, pi ? !pi->do_newfs
1152		    : TRUE);
1153		if (pi != NULL &&
1154		    pi->newfs_type == NEWFS_UFS) {
1155		    PartInfo *pi_new = label_chunk_info[here].c->private_data;
1156
1157		    pi_new->newfs_data.newfs_ufs = pi->newfs_data.newfs_ufs;
1158		}
1159		safe_free(pi);
1160		label_chunk_info[here].c->private_free = safe_free;
1161		if (variable_cmp(DISK_LABELLED, "written"))
1162		    variable_set2(DISK_LABELLED, "yes", 0);
1163	    }
1164#if defined(__ia64__)
1165	    else if (label_chunk_info[here].type == PART_FAT &&
1166	      label_chunk_info[here].c->type == efi &&
1167	      label_chunk_info[here].c->private_data) {
1168		PartInfo *pi =
1169		    ((PartInfo *)label_chunk_info[here].c->private_data);
1170
1171		if (!pi->do_newfs)
1172		    label_chunk_info[here].c->flags |= CHUNK_NEWFS;
1173		else
1174		    label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
1175
1176		label_chunk_info[here].c->private_data =
1177		    new_efi_part(pi->mountpoint, !pi->do_newfs);
1178		safe_free(pi);
1179		label_chunk_info[here].c->private_free = safe_free;
1180		if (variable_cmp(DISK_LABELLED, "written"))
1181		    variable_set2(DISK_LABELLED, "yes", 0);
1182	    }
1183#endif
1184	    else
1185		msg = MSG_NOT_APPLICABLE;
1186	    break;
1187
1188	case 'U':
1189	    clear();
1190	    if (!variable_cmp(DISK_LABELLED, "written")) {
1191		msgConfirm("You've already written out your changes -\n"
1192			   "it's too late to undo!");
1193	    }
1194	    else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
1195		variable_unset(DISK_PARTITIONED);
1196		variable_unset(DISK_LABELLED);
1197		for (i = 0; devs[i]; i++) {
1198		    Disk *d;
1199
1200		    if (!devs[i]->enabled)
1201			continue;
1202		    else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1203			Free_Disk(devs[i]->private);
1204			devs[i]->private = d;
1205#ifdef WITH_SLICES
1206			diskPartition(devs[i]);
1207#endif
1208		    }
1209		}
1210		record_label_chunks(devs, dev);
1211	    }
1212	    clear_wins();
1213	    break;
1214
1215	case 'W':
1216	    if (!variable_cmp(DISK_LABELLED, "written")) {
1217		msgConfirm("You've already written out your changes - if you\n"
1218			   "wish to overwrite them, you'll have to restart\n"
1219			   "sysinstall first.");
1220	    }
1221	    else if (!msgNoYes("WARNING:  This should only be used when modifying an EXISTING\n"
1222			  "installation.  If you are installing FreeBSD for the first time\n"
1223			  "then you should simply type Q when you're finished here and your\n"
1224			  "changes will be committed in one batch automatically at the end of\n"
1225			  "these questions.\n\n"
1226			  "Are you absolutely sure you want to do this now?")) {
1227		variable_set2(DISK_LABELLED, "yes", 0);
1228		diskLabelCommit(NULL);
1229	    }
1230	    clear_wins();
1231	    break;
1232
1233	case 'Z':	/* Set newfs command line */
1234	    if (label_chunk_info[here].c->private_data &&
1235		((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1236		getNewfsCmd(label_chunk_info[here].c->private_data);
1237	    else
1238		msg = MSG_NOT_APPLICABLE;
1239	    clear_wins();
1240	    break;
1241
1242
1243	case '|':
1244	    if (!msgNoYes("Are you sure you want to go into Wizard mode?\n\n"
1245			  "This is an entirely undocumented feature which you are not\n"
1246			  "expected to understand!")) {
1247		int i;
1248		Device **devs;
1249
1250		dialog_clear();
1251		end_dialog();
1252		DialogActive = FALSE;
1253		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1254		if (!devs) {
1255		    msgConfirm("Can't find any disk devices!");
1256		    break;
1257		}
1258		for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1259		    if (devs[i]->enabled)
1260		    	slice_wizard(((Disk *)devs[i]->private));
1261		}
1262		if (variable_cmp(DISK_LABELLED, "written"))
1263		    variable_set2(DISK_LABELLED, "yes", 0);
1264		DialogActive = TRUE;
1265		record_label_chunks(devs, dev);
1266		clear_wins();
1267	    }
1268	    else
1269		msg = "A most prudent choice!";
1270	    break;
1271
1272	case '\033':	/* ESC */
1273	case 'Q':
1274	    labeling = FALSE;
1275	    break;
1276
1277	default:
1278	    beep();
1279	    sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1280	    msg = _msg;
1281	    break;
1282	}
1283        if (label_chunk_info[here].type == PART_SLICE)
1284            pslice_focus = here;
1285        else
1286            label_focus = here;
1287    }
1288    restorescr(w);
1289    return DITEM_SUCCESS;
1290}
1291
1292static __inline int
1293requested_part_size(char *varName, int nom, int def, int perc)
1294{
1295    char *cp;
1296    int sz;
1297
1298    if ((cp = variable_get(varName)) != NULL)
1299	sz = atoi(cp);
1300    else
1301	sz = nom + (def - nom) * perc / 100;
1302    return(sz * ONE_MEG);
1303}
1304
1305/*
1306 * Attempt to auto-label the disk.  'perc' (0-100) scales
1307 * the size of the various partitions within appropriate
1308 * bounds (NOMINAL through DEFAULT sizes).  The procedure
1309 * succeeds of NULL is returned.  A non-null return message
1310 * is either a failure-status message (*req == 0), or
1311 * a confirmation requestor (*req == 1).  *req is 0 on
1312 * entry to this call.
1313 *
1314 * We autolabel the following partitions:  /, swap, /var, /tmp, /usr,
1315 * and /home.  /home receives any extra left over disk space.
1316 */
1317static char *
1318try_auto_label(Device **devs, Device *dev, int perc, int *req)
1319{
1320    int sz;
1321    struct chunk *root_chunk = NULL;
1322    struct chunk *swap_chunk = NULL;
1323    struct chunk *usr_chunk = NULL;
1324    struct chunk *var_chunk = NULL;
1325    struct chunk *tmp_chunk = NULL;
1326    struct chunk *home_chunk = NULL;
1327    int mib[2];
1328    unsigned long physmem;
1329    size_t size;
1330    Chunk *rootdev, *swapdev, *usrdev, *vardev;
1331    Chunk *tmpdev, *homedev;
1332    char *msg = NULL;
1333
1334    sz = space_free(label_chunk_info[here].c);
1335    if (sz <= FS_MIN_SIZE)
1336	return("Not enough free space to create a new partition in the slice");
1337
1338    (void)checkLabels(FALSE, &rootdev, &swapdev, &usrdev,
1339			&vardev, &tmpdev, &homedev);
1340    if (!rootdev) {
1341	sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
1342
1343	root_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1344			    label_chunk_info[here].c, sz, part,
1345			    FS_BSDFFS,  CHUNK_IS_ROOT | CHUNK_AUTO_SIZE);
1346	if (!root_chunk) {
1347	    *req = 1;
1348	    msg = "Unable to create the root partition. Too big?";
1349	    goto done;
1350	}
1351	root_chunk->private_data = new_part("/", TRUE);
1352	root_chunk->private_free = safe_free;
1353	root_chunk->flags |= CHUNK_NEWFS;
1354	record_label_chunks(devs, dev);
1355    }
1356    if (!swapdev) {
1357	sz = requested_part_size(VAR_SWAP_SIZE, 0, 0, perc);
1358	if (sz == 0) {
1359	    int nom;
1360	    int def;
1361
1362	    mib[0] = CTL_HW;
1363	    mib[1] = HW_PHYSMEM;
1364	    size = sizeof physmem;
1365	    sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1366	    def = 2 * (int)(physmem / 512);
1367	    if (def < SWAP_MIN_SIZE * ONE_MEG)
1368		def = SWAP_MIN_SIZE * ONE_MEG;
1369	    if (def > SWAP_AUTO_LIMIT_SIZE * ONE_MEG)
1370		def = SWAP_AUTO_LIMIT_SIZE * ONE_MEG;
1371	    nom = (int)(physmem / 512) / 8;
1372	    sz = nom + (def - nom) * perc / 100;
1373	}
1374	swap_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1375			    label_chunk_info[here].c, sz, part,
1376			    FS_SWAP, CHUNK_AUTO_SIZE);
1377	if (!swap_chunk) {
1378	    *req = 1;
1379	    msg = "Unable to create the swap partition. Too big?";
1380	    goto done;
1381	}
1382	swap_chunk->private_data = 0;
1383	swap_chunk->private_free = safe_free;
1384	record_label_chunks(devs, dev);
1385    }
1386    if (!vardev) {
1387	sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE, VAR_DEFAULT_SIZE, perc);
1388
1389	var_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1390				label_chunk_info[here].c, sz, part,
1391				FS_BSDFFS, CHUNK_AUTO_SIZE);
1392	if (!var_chunk) {
1393	    *req = 1;
1394	    msg = "Not enough free space for /var - you will need to\n"
1395		   "partition your disk manually with a custom install!";
1396	    goto done;
1397	}
1398	var_chunk->private_data = new_part("/var", TRUE);
1399	var_chunk->private_free = safe_free;
1400	var_chunk->flags |= CHUNK_NEWFS;
1401	record_label_chunks(devs, dev);
1402    }
1403    if (!tmpdev && !variable_get(VAR_NO_TMP)) {
1404	sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
1405
1406	tmp_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1407				label_chunk_info[here].c, sz, part,
1408				FS_BSDFFS, CHUNK_AUTO_SIZE);
1409	if (!tmp_chunk) {
1410	    *req = 1;
1411	    msg = "Not enough free space for /tmp - you will need to\n"
1412		   "partition your disk manually with a custom install!";
1413	    goto done;
1414	}
1415	tmp_chunk->private_data = new_part("/tmp", TRUE);
1416	tmp_chunk->private_free = safe_free;
1417	tmp_chunk->flags |= CHUNK_NEWFS;
1418	record_label_chunks(devs, dev);
1419    }
1420    if (!usrdev && !variable_get(VAR_NO_USR)) {
1421	sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
1422#if AUTO_HOME == 0
1423	    sz = space_free(label_chunk_info[here].c);
1424#endif
1425	if (sz) {
1426	    if (sz < (USR_MIN_SIZE * ONE_MEG)) {
1427		*req = 1;
1428		msg = "Not enough free space for /usr - you will need to\n"
1429		       "partition your disk manually with a custom install!";
1430	    }
1431
1432	    usr_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1433				    label_chunk_info[here].c, sz, part,
1434				    FS_BSDFFS, CHUNK_AUTO_SIZE);
1435	    if (!usr_chunk) {
1436		msg = "Unable to create the /usr partition.  Not enough space?\n"
1437			   "You will need to partition your disk manually with a custom install!";
1438		goto done;
1439	    }
1440	    usr_chunk->private_data = new_part("/usr", TRUE);
1441	    usr_chunk->private_free = safe_free;
1442	    usr_chunk->flags |= CHUNK_NEWFS;
1443	    record_label_chunks(devs, dev);
1444	}
1445    }
1446#if AUTO_HOME == 1
1447    if (!homedev && !variable_get(VAR_NO_HOME)) {
1448	sz = requested_part_size(VAR_HOME_SIZE, HOME_NOMINAL_SIZE, HOME_DEFAULT_SIZE, perc);
1449	if (sz < space_free(label_chunk_info[here].c))
1450	    sz = space_free(label_chunk_info[here].c);
1451	if (sz) {
1452	    if (sz < (HOME_MIN_SIZE * ONE_MEG)) {
1453		*req = 1;
1454		msg = "Not enough free space for /home - you will need to\n"
1455		       "partition your disk manually with a custom install!";
1456		goto done;
1457	    }
1458
1459	    home_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1460				    label_chunk_info[here].c, sz, part,
1461				    FS_BSDFFS, CHUNK_AUTO_SIZE);
1462	    if (!home_chunk) {
1463		msg = "Unable to create the /home partition.  Not enough space?\n"
1464			   "You will need to partition your disk manually with a custom install!";
1465		goto done;
1466	    }
1467	    home_chunk->private_data = new_part("/home", TRUE);
1468	    home_chunk->private_free = safe_free;
1469	    home_chunk->flags |= CHUNK_NEWFS;
1470	    record_label_chunks(devs, dev);
1471	}
1472    }
1473#endif
1474
1475    /* At this point, we're reasonably "labelled" */
1476    if (variable_cmp(DISK_LABELLED, "written"))
1477	variable_set2(DISK_LABELLED, "yes", 0);
1478
1479done:
1480    if (msg) {
1481	if (root_chunk)
1482	    Delete_Chunk(root_chunk->disk, root_chunk);
1483	if (swap_chunk)
1484	    Delete_Chunk(swap_chunk->disk, swap_chunk);
1485	if (var_chunk)
1486	    Delete_Chunk(var_chunk->disk, var_chunk);
1487	if (tmp_chunk)
1488	    Delete_Chunk(tmp_chunk->disk, tmp_chunk);
1489	if (usr_chunk)
1490	    Delete_Chunk(usr_chunk->disk, usr_chunk);
1491	if (home_chunk)
1492	    Delete_Chunk(home_chunk->disk, home_chunk);
1493	record_label_chunks(devs, dev);
1494    }
1495    return(msg);
1496}
1497
1498static int
1499diskLabelNonInteractive(Device *dev)
1500{
1501    char *cp;
1502    PartType type;
1503    PartInfo *p;
1504    u_long flags;
1505    int i, status;
1506    Device **devs;
1507    Disk *d;
1508
1509    status = DITEM_SUCCESS;
1510    cp = variable_get(VAR_DISK);
1511    if (!cp) {
1512	msgConfirm("diskLabel:  No disk selected - can't label automatically.");
1513	return DITEM_FAILURE;
1514    }
1515    devs = deviceFind(cp, DEVICE_TYPE_DISK);
1516    if (!devs) {
1517	msgConfirm("diskLabel: No disk device %s found!", cp);
1518	return DITEM_FAILURE;
1519    }
1520    if (dev)
1521	d = dev->private;
1522    else
1523	d = devs[0]->private;
1524    record_label_chunks(devs, dev);
1525    for (i = 0; label_chunk_info[i].c; i++) {
1526	Chunk *c1 = label_chunk_info[i].c;
1527
1528	if (label_chunk_info[i].type == PART_SLICE) {
1529	    char name[512];
1530	    char typ[10], mpoint[50];
1531	    int entries;
1532
1533	    for (entries = 1;; entries++) {
1534		int sz, soft = 0;
1535		snprintf(name, sizeof name, "%s-%d", c1->name, entries);
1536		if ((cp = variable_get(name)) == NULL)
1537		    break;
1538		if (sscanf(cp, "%s %d %s %d", typ, &sz, mpoint, &soft) < 3) {
1539		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s",  c1->name, cp);
1540		    status = DITEM_FAILURE;
1541		    break;
1542		} else {
1543		    Chunk *tmp;
1544
1545		    flags = 0;
1546		    if (!strcmp(typ, "swap")) {
1547			type = PART_SWAP;
1548			strcpy(mpoint, "SWAP");
1549		    } else {
1550			type = PART_FILESYSTEM;
1551			if (!strcmp(mpoint, "/"))
1552			    flags |= CHUNK_IS_ROOT;
1553		    }
1554		    if (!sz)
1555			sz = space_free(c1);
1556		    if (sz > space_free(c1)) {
1557			msgConfirm("Not enough free space to create partition: %s", mpoint);
1558			status = DITEM_FAILURE;
1559			break;
1560		    }
1561		    if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
1562			(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
1563			msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
1564			status = DITEM_FAILURE;
1565			break;
1566		    } else {
1567			PartInfo *pi;
1568			pi = tmp->private_data = new_part(mpoint, TRUE);
1569			tmp->private_free = safe_free;
1570			pi->newfs_data.newfs_ufs.softupdates = soft;
1571		    }
1572		}
1573	    }
1574	} else {
1575	    /* Must be something we can set a mountpoint for */
1576	    cp = variable_get(c1->name);
1577	    if (cp) {
1578		char mpoint[50], do_newfs[8];
1579		Boolean newfs = FALSE;
1580
1581		do_newfs[0] = '\0';
1582		if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
1583		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1584		    status = DITEM_FAILURE;
1585		    break;
1586		}
1587		newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1588		if (c1->private_data) {
1589		    p = c1->private_data;
1590		    p->do_newfs = newfs;
1591		    strcpy(p->mountpoint, mpoint);
1592		}
1593		else {
1594		    c1->private_data = new_part(mpoint, newfs);
1595		    c1->private_free = safe_free;
1596		}
1597		if (!strcmp(mpoint, "/"))
1598		    c1->flags |= CHUNK_IS_ROOT;
1599		else
1600		    c1->flags &= ~CHUNK_IS_ROOT;
1601	    }
1602	}
1603    }
1604    if (status == DITEM_SUCCESS)
1605	variable_set2(DISK_LABELLED, "yes", 0);
1606    return status;
1607}
1608