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