label.c revision 178940
1101242Srwatson/*
2101242Srwatson * $FreeBSD: head/usr.sbin/sade/label.c 178940 2008-05-11 07:13:08Z obrien $
3107603Sru *
4101242Srwatson * Copyright (c) 1995
5101242Srwatson *	Jordan Hubbard.  All rights reserved.
6101242Srwatson *
7101242Srwatson * Redistribution and use in source and binary forms, with or without
8101242Srwatson * modification, are permitted provided that the following conditions
9107603Sru * are met:
10101242Srwatson * 1. Redistributions of source code must retain the above copyright
11101242Srwatson *    notice, this list of conditions and the following disclaimer,
12101242Srwatson *    verbatim and that no modifications are made prior to this
13101242Srwatson *    point in the file.
14101242Srwatson * 2. Redistributions in binary form must reproduce the above copyright
15101242Srwatson *    notice, this list of conditions and the following disclaimer in the
16101242Srwatson *    documentation and/or other materials provided with the distribution.
17101242Srwatson *
18107603Sru * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
19101242Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20101242Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21101242Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
22101242Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23101242Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24101242Srwatson * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
25101242Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26101242Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27101242Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28101242Srwatson * SUCH DAMAGE.
29101242Srwatson *
30107603Sru */
31101242Srwatson
32107603Sru#include "sade.h"
33101242Srwatson#include <ctype.h>
34101242Srwatson#include <inttypes.h>
35107744Sru#include <libdisk.h>
36101242Srwatson#include <sys/disklabel.h>
37101242Srwatson#include <sys/param.h>
38101242Srwatson#include <sys/sysctl.h>
39101242Srwatson
40101242Srwatson#define AUTO_HOME	0	/* do not create /home automatically */
41101242Srwatson
42101242Srwatson/*
43101242Srwatson * Everything to do with editing the contents of disk labels.
44106509Schris */
45106509Schris
46106509Schris/* A nice message we use a lot in the disklabel editor */
47106509Schris#define MSG_NOT_APPLICABLE	"That option is not applicable here"
48101242Srwatson
49101242Srwatson/* Where to start printing the freebsd slices */
50101242Srwatson#define CHUNK_SLICE_START_ROW		2
51101242Srwatson#define CHUNK_PART_START_ROW		11
52106509Schris
53107603Sru/* The smallest filesystem we're willing to create */
54106509Schris#define FS_MIN_SIZE			ONE_MEG
55106509Schris
56101242Srwatson/*
57122777Srwatson * Minimum partition sizes
58106509Schris */
59101242Srwatson#if defined(__alpha__) || defined(__ia64__) || defined(__sparc64__) || defined(__amd64__)
60101242Srwatson#define ROOT_MIN_SIZE			128
61106509Schris#else
62106509Schris#define ROOT_MIN_SIZE			118
63106509Schris#endif
64106509Schris#define SWAP_MIN_SIZE			32
65101242Srwatson#define USR_MIN_SIZE			128
66106104Schris#define VAR_MIN_SIZE			20
67106104Schris#define TMP_MIN_SIZE			20
68106104Schris#define HOME_MIN_SIZE			20
69101242Srwatson
70106509Schris/*
71101242Srwatson * Swap size limit for auto-partitioning (4G).
72101242Srwatson */
73106510Schris#define SWAP_AUTO_LIMIT_SIZE		4096
74101242Srwatson
75122777Srwatson/*
76122777Srwatson * Default partition sizes.  If we do not have sufficient disk space
77122777Srwatson * for this configuration we scale things relative to the NOM vs DEFAULT
78122777Srwatson * sizes.  If the disk is larger then /home will get any remaining space.
79122777Srwatson */
80122777Srwatson#define ROOT_DEFAULT_SIZE		512
81122777Srwatson#define USR_DEFAULT_SIZE		8192
82122777Srwatson#define VAR_DEFAULT_SIZE		1024
83122777Srwatson#define TMP_DEFAULT_SIZE		512
84122777Srwatson#define HOME_DEFAULT_SIZE		USR_DEFAULT_SIZE
85122777Srwatson
86122777Srwatson/*
87131365Sru * Nominal partition sizes.  These are used to scale the default sizes down
88101242Srwatson * when we have insufficient disk space.  If this isn't sufficient we scale
89101242Srwatson * down using the MIN sizes instead.
90101242Srwatson */
91101242Srwatson#define ROOT_NOMINAL_SIZE		256
92101242Srwatson#define USR_NOMINAL_SIZE		1536
93101242Srwatson#define VAR_NOMINAL_SIZE		128
94101242Srwatson#define TMP_NOMINAL_SIZE		128
95122777Srwatson#define HOME_NOMINAL_SIZE		USR_NOMINAL_SIZE
96101242Srwatson
97101242Srwatson/* The bottom-most row we're allowed to scribble on */
98160154Srwatson#define CHUNK_ROW_MAX			16
99109263Schris
100106104Schris
101196123Srwatson/* All the chunks currently displayed on the screen */
102109272Schrisstatic struct {
103106104Schris    struct chunk *c;
104101242Srwatson    PartType type;
105101242Srwatson} label_chunk_info[MAX_CHUNKS + 1];
106101242Srwatsonstatic int here;
107101242Srwatson
108101242Srwatson/*** with this value we try to track the most recently added label ***/
109101242Srwatsonstatic int label_focus = 0, pslice_focus = 0;
110101242Srwatson
111101242Srwatsonstatic int diskLabel(Device *dev);
112119321Srwatsonstatic int diskLabelNonInteractive(Device *dev);
113119321Srwatsonstatic char *try_auto_label(Device **devs, Device *dev, int perc, int *req);
114119321Srwatson
115119321Srwatsonstatic int
116119321SrwatsonlabelHook(dialogMenuItem *selected)
117119321Srwatson{
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
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			   "%s first.", StartName);
1280	    }
1281	    else if (!msgNoYes("WARNING:  You are about to modify an EXISTING\n"
1282			  "installation.\n\n"
1283			  "Are you absolutely sure you want to continue?")) {
1284		variable_set2(DISK_LABELLED, "yes", 0);
1285		diskLabelCommit(NULL);
1286	    }
1287	    clear_wins();
1288	    break;
1289
1290	case 'Z':	/* Set newfs command line */
1291	    if (label_chunk_info[here].c->private_data &&
1292		((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1293		getNewfsCmd(label_chunk_info[here].c->private_data);
1294	    else
1295		msg = MSG_NOT_APPLICABLE;
1296	    clear_wins();
1297	    break;
1298
1299#ifndef __ia64__
1300	case '|':
1301	    if (!msgNoYes("Are you sure you want to go into Wizard mode?\n\n"
1302			  "This is an entirely undocumented feature which you are not\n"
1303			  "expected to understand!")) {
1304		int i;
1305		Device **devs;
1306
1307		dialog_clear();
1308		end_dialog();
1309		DialogActive = FALSE;
1310		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1311		if (!devs) {
1312		    msgConfirm("Can't find any disk devices!");
1313		    break;
1314		}
1315		for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1316		    if (devs[i]->enabled)
1317		    	slice_wizard(((Disk *)devs[i]->private));
1318		}
1319		if (variable_cmp(DISK_LABELLED, "written"))
1320		    variable_set2(DISK_LABELLED, "yes", 0);
1321		DialogActive = TRUE;
1322		record_label_chunks(devs, dev);
1323		clear_wins();
1324	    }
1325	    else
1326		msg = "A most prudent choice!";
1327	    break;
1328#endif
1329
1330	case '\033':	/* ESC */
1331	case 'Q':
1332	    labeling = FALSE;
1333	    break;
1334
1335	default:
1336	    beep();
1337	    sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1338	    msg = _msg;
1339	    break;
1340	}
1341        if (label_chunk_info[here].type == PART_SLICE)
1342            pslice_focus = here;
1343        else
1344            label_focus = here;
1345    }
1346    restorescr(w);
1347    return DITEM_SUCCESS;
1348}
1349
1350static __inline daddr_t
1351requested_part_size(char *varName, daddr_t nom, int def, int perc)
1352{
1353    char *cp;
1354    daddr_t sz;
1355
1356    if ((cp = variable_get(varName)) != NULL)
1357	sz = strtoimax(cp, NULL, 0);
1358    else
1359	sz = nom + (def - nom) * perc / 100;
1360    return(sz * ONE_MEG);
1361}
1362
1363/*
1364 * Attempt to auto-label the disk.  'perc' (0-100) scales
1365 * the size of the various partitions within appropriate
1366 * bounds (NOMINAL through DEFAULT sizes).  The procedure
1367 * succeeds of NULL is returned.  A non-null return message
1368 * is either a failure-status message (*req == 0), or
1369 * a confirmation requestor (*req == 1).  *req is 0 on
1370 * entry to this call.
1371 *
1372 * As a special exception to the usual sizing rules, /var is given
1373 * additional space equal to the amount of physical memory present
1374 * if perc == 100 in order to ensure that users with large hard drives
1375 * will have enough space to store a crashdump in /var/crash.
1376 *
1377 * We autolabel the following partitions:  /, swap, /var, /tmp, /usr,
1378 * and /home.  /home receives any extra left over disk space.
1379 */
1380static char *
1381try_auto_label(Device **devs, Device *dev, int perc, int *req)
1382{
1383    daddr_t sz;
1384    Chunk *AutoHome, *AutoRoot, *AutoSwap;
1385    Chunk *AutoTmp, *AutoUsr, *AutoVar;
1386#ifdef __ia64__
1387    Chunk *AutoEfi;
1388#endif
1389    int mib[2];
1390    unsigned long physmem;
1391    size_t size;
1392    char *msg = NULL;
1393
1394    sz = space_free(label_chunk_info[here].c);
1395    if (sz <= FS_MIN_SIZE)
1396	return("Not enough free space to create a new partition in the slice");
1397
1398    (void)checkLabels(FALSE);
1399    AutoHome = AutoRoot = AutoSwap = NULL;
1400    AutoTmp = AutoUsr = AutoVar = NULL;
1401
1402#ifdef __ia64__
1403    AutoEfi = NULL;
1404    if (EfiChunk == NULL) {
1405	sz = 100 * ONE_MEG;
1406	AutoEfi = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1407	    label_chunk_info[here].c, sz, efi, 0, 0);
1408	if (AutoEfi == NULL) {
1409	    *req = 1;
1410	    msg = "Unable to create the EFI system partition. Too big?";
1411	    goto done;
1412	}
1413	AutoEfi->private_data = new_part(PART_EFI, "/efi", TRUE);
1414	AutoEfi->private_free = safe_free;
1415	AutoEfi->flags |= CHUNK_NEWFS;
1416	record_label_chunks(devs, dev);
1417    }
1418#endif
1419
1420    if (RootChunk == NULL) {
1421	sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
1422
1423	AutoRoot = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1424			    label_chunk_info[here].c, sz, part,
1425			    FS_BSDFFS,  CHUNK_IS_ROOT | CHUNK_AUTO_SIZE);
1426	if (!AutoRoot) {
1427	    *req = 1;
1428	    msg = "Unable to create the root partition. Too big?";
1429	    goto done;
1430	}
1431	AutoRoot->private_data = new_part(PART_FILESYSTEM, "/", TRUE);
1432	AutoRoot->private_free = safe_free;
1433	AutoRoot->flags |= CHUNK_NEWFS;
1434	record_label_chunks(devs, dev);
1435    }
1436    if (SwapChunk == NULL) {
1437	sz = requested_part_size(VAR_SWAP_SIZE, 0, 0, perc);
1438	if (sz == 0) {
1439	    daddr_t nom;
1440	    daddr_t def;
1441
1442	    mib[0] = CTL_HW;
1443	    mib[1] = HW_PHYSMEM;
1444	    size = sizeof physmem;
1445	    sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1446	    def = 2 * (int)(physmem / 512);
1447	    if (def < SWAP_MIN_SIZE * ONE_MEG)
1448		def = SWAP_MIN_SIZE * ONE_MEG;
1449	    if (def > SWAP_AUTO_LIMIT_SIZE * ONE_MEG)
1450		def = SWAP_AUTO_LIMIT_SIZE * ONE_MEG;
1451	    nom = (int)(physmem / 512) / 8;
1452	    sz = nom + (def - nom) * perc / 100;
1453	}
1454	AutoSwap = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1455			    label_chunk_info[here].c, sz, part,
1456			    FS_SWAP, CHUNK_AUTO_SIZE);
1457	if (!AutoSwap) {
1458	    *req = 1;
1459	    msg = "Unable to create the swap partition. Too big?";
1460	    goto done;
1461	}
1462	AutoSwap->private_data = 0;
1463	AutoSwap->private_free = safe_free;
1464	record_label_chunks(devs, dev);
1465    }
1466    if (VarChunk == NULL) {
1467	/* Work out how much extra space we want for a crash dump */
1468	unsigned long crashdumpsz;
1469
1470	mib[0] = CTL_HW;
1471	mib[1] = HW_PHYSMEM;
1472	size = sizeof(physmem);
1473	sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1474
1475	if (perc == 100)
1476		crashdumpsz = physmem / 1048576;
1477	else
1478		crashdumpsz = 0;
1479
1480	sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE,	\
1481	    VAR_DEFAULT_SIZE + crashdumpsz, perc);
1482
1483	AutoVar = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1484				label_chunk_info[here].c, sz, part,
1485				FS_BSDFFS, CHUNK_AUTO_SIZE);
1486	if (!AutoVar) {
1487	    *req = 1;
1488	    msg = "Not enough free space for /var - you will need to\n"
1489		   "partition your disk manually with a custom install!";
1490	    goto done;
1491	}
1492	AutoVar->private_data = new_part(PART_FILESYSTEM, "/var", TRUE);
1493	AutoVar->private_free = safe_free;
1494	AutoVar->flags |= CHUNK_NEWFS;
1495	record_label_chunks(devs, dev);
1496    }
1497    if (TmpChunk == NULL && !variable_get(VAR_NO_TMP)) {
1498	sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
1499
1500	AutoTmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1501				label_chunk_info[here].c, sz, part,
1502				FS_BSDFFS, CHUNK_AUTO_SIZE);
1503	if (!AutoTmp) {
1504	    *req = 1;
1505	    msg = "Not enough free space for /tmp - you will need to\n"
1506		   "partition your disk manually with a custom install!";
1507	    goto done;
1508	}
1509	AutoTmp->private_data = new_part(PART_FILESYSTEM, "/tmp", TRUE);
1510	AutoTmp->private_free = safe_free;
1511	AutoTmp->flags |= CHUNK_NEWFS;
1512	record_label_chunks(devs, dev);
1513    }
1514    if (UsrChunk == NULL && !variable_get(VAR_NO_USR)) {
1515	sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
1516#if AUTO_HOME == 0
1517	if (sz < space_free(label_chunk_info[here].c))
1518	    sz = space_free(label_chunk_info[here].c);
1519#endif
1520	if (sz) {
1521	    if (sz < (USR_MIN_SIZE * ONE_MEG)) {
1522		*req = 1;
1523		msg = "Not enough free space for /usr - you will need to\n"
1524		       "partition your disk manually with a custom install!";
1525	    }
1526
1527	    AutoUsr = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1528				    label_chunk_info[here].c, sz, part,
1529				    FS_BSDFFS, CHUNK_AUTO_SIZE);
1530	    if (!AutoUsr) {
1531		msg = "Unable to create the /usr partition.  Not enough space?\n"
1532			   "You will need to partition your disk manually with a custom install!";
1533		goto done;
1534	    }
1535	    AutoUsr->private_data = new_part(PART_FILESYSTEM, "/usr", TRUE);
1536	    AutoUsr->private_free = safe_free;
1537	    AutoUsr->flags |= CHUNK_NEWFS;
1538	    record_label_chunks(devs, dev);
1539	}
1540    }
1541#if AUTO_HOME == 1
1542    if (HomeChunk == NULL && !variable_get(VAR_NO_HOME)) {
1543	sz = requested_part_size(VAR_HOME_SIZE, HOME_NOMINAL_SIZE, HOME_DEFAULT_SIZE, perc);
1544	if (sz < space_free(label_chunk_info[here].c))
1545	    sz = space_free(label_chunk_info[here].c);
1546	if (sz) {
1547	    if (sz < (HOME_MIN_SIZE * ONE_MEG)) {
1548		*req = 1;
1549		msg = "Not enough free space for /home - you will need to\n"
1550		       "partition your disk manually with a custom install!";
1551		goto done;
1552	    }
1553
1554	    AutoHome = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1555				    label_chunk_info[here].c, sz, part,
1556				    FS_BSDFFS, CHUNK_AUTO_SIZE);
1557	    if (!AutoHome) {
1558		msg = "Unable to create the /home partition.  Not enough space?\n"
1559			   "You will need to partition your disk manually with a custom install!";
1560		goto done;
1561	    }
1562	    AutoHome->private_data = new_part(PART_FILESYSTEM, "/home", TRUE);
1563	    AutoHome->private_free = safe_free;
1564	    AutoHome->flags |= CHUNK_NEWFS;
1565	    record_label_chunks(devs, dev);
1566	}
1567    }
1568#endif
1569
1570    /* At this point, we're reasonably "labelled" */
1571    if (variable_cmp(DISK_LABELLED, "written"))
1572	variable_set2(DISK_LABELLED, "yes", 0);
1573
1574done:
1575    if (msg) {
1576	if (AutoRoot != NULL)
1577	    Delete_Chunk(AutoRoot->disk, AutoRoot);
1578	if (AutoSwap != NULL)
1579	    Delete_Chunk(AutoSwap->disk, AutoSwap);
1580	if (AutoVar != NULL)
1581	    Delete_Chunk(AutoVar->disk, AutoVar);
1582	if (AutoTmp != NULL)
1583	    Delete_Chunk(AutoTmp->disk, AutoTmp);
1584	if (AutoUsr != NULL)
1585	    Delete_Chunk(AutoUsr->disk, AutoUsr);
1586	if (AutoHome != NULL)
1587	    Delete_Chunk(AutoHome->disk, AutoHome);
1588	record_label_chunks(devs, dev);
1589    }
1590    return(msg);
1591}
1592
1593static int
1594diskLabelNonInteractive(Device *dev)
1595{
1596    char *cp;
1597    PartType type;
1598    PartInfo *p;
1599    u_long flags;
1600    int i, status;
1601    Device **devs;
1602    Disk *d;
1603
1604    status = DITEM_SUCCESS;
1605    cp = variable_get(VAR_DISK);
1606    if (!cp) {
1607	msgConfirm("diskLabel:  No disk selected - can't label automatically.");
1608	return DITEM_FAILURE;
1609    }
1610    devs = deviceFind(cp, DEVICE_TYPE_DISK);
1611    if (!devs) {
1612	msgConfirm("diskLabel: No disk device %s found!", cp);
1613	return DITEM_FAILURE;
1614    }
1615    if (dev)
1616	d = dev->private;
1617    else
1618	d = devs[0]->private;
1619    record_label_chunks(devs, dev);
1620    for (i = 0; label_chunk_info[i].c; i++) {
1621	Chunk *c1 = label_chunk_info[i].c;
1622
1623	if (label_chunk_info[i].type == PART_SLICE) {
1624	    char name[512];
1625	    char typ[10], mpoint[50];
1626	    int entries;
1627
1628	    for (entries = 1;; entries++) {
1629		intmax_t sz;
1630		int soft = 0;
1631		snprintf(name, sizeof name, "%s-%d", c1->name, entries);
1632		if ((cp = variable_get(name)) == NULL)
1633		    break;
1634		if (sscanf(cp, "%s %jd %s %d", typ, &sz, mpoint, &soft) < 3) {
1635		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s",  c1->name, cp);
1636		    status = DITEM_FAILURE;
1637		    break;
1638		} else {
1639		    Chunk *tmp;
1640
1641		    flags = 0;
1642		    if (!strcmp(typ, "swap")) {
1643			type = PART_SWAP;
1644			strcpy(mpoint, "SWAP");
1645		    } else {
1646			type = PART_FILESYSTEM;
1647			if (!strcmp(mpoint, "/"))
1648			    flags |= CHUNK_IS_ROOT;
1649		    }
1650		    if (!sz)
1651			sz = space_free(c1);
1652		    if (sz > space_free(c1)) {
1653			msgConfirm("Not enough free space to create partition: %s", mpoint);
1654			status = DITEM_FAILURE;
1655			break;
1656		    }
1657		    if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
1658			(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
1659			msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
1660			status = DITEM_FAILURE;
1661			break;
1662		    } else {
1663			PartInfo *pi;
1664			pi = tmp->private_data = new_part(PART_FILESYSTEM, mpoint, TRUE);
1665			tmp->private_free = safe_free;
1666			pi->newfs_data.newfs_ufs.softupdates = soft;
1667		    }
1668		}
1669	    }
1670	} else {
1671	    /* Must be something we can set a mountpoint for */
1672	    cp = variable_get(c1->name);
1673	    if (cp) {
1674		char mpoint[50], do_newfs[8];
1675		Boolean newfs = FALSE;
1676
1677		do_newfs[0] = '\0';
1678		if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
1679		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1680		    status = DITEM_FAILURE;
1681		    break;
1682		}
1683		newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1684		if (c1->private_data) {
1685		    p = c1->private_data;
1686		    p->do_newfs = newfs;
1687		    strcpy(p->mountpoint, mpoint);
1688		}
1689		else {
1690		    c1->private_data = new_part(PART_FILESYSTEM, mpoint, newfs);
1691		    c1->private_free = safe_free;
1692		}
1693		if (!strcmp(mpoint, "/"))
1694		    c1->flags |= CHUNK_IS_ROOT;
1695		else
1696		    c1->flags &= ~CHUNK_IS_ROOT;
1697	    }
1698	}
1699    }
1700    if (status == DITEM_SUCCESS)
1701	variable_set2(DISK_LABELLED, "yes", 0);
1702    return status;
1703}
1704