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