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