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