label.c revision 15091
1178481Sjb/*
2178481Sjb * The new sysinstall program.
3178481Sjb *
4178481Sjb * This is probably the last program in the `sysinstall' line - the next
5178481Sjb * generation being essentially a complete rewrite.
6178481Sjb *
7178481Sjb * $Id: label.c,v 1.40 1996/03/24 18:57:37 joerg Exp $
8178481Sjb *
9178481Sjb * Copyright (c) 1995
10178481Sjb *	Jordan Hubbard.  All rights reserved.
11178481Sjb *
12178481Sjb * Redistribution and use in source and binary forms, with or without
13178481Sjb * modification, are permitted provided that the following conditions
14178481Sjb * are met:
15178481Sjb * 1. Redistributions of source code must retain the above copyright
16178481Sjb *    notice, this list of conditions and the following disclaimer,
17178481Sjb *    verbatim and that no modifications are made prior to this
18178481Sjb *    point in the file.
19178481Sjb * 2. Redistributions in binary form must reproduce the above copyright
20178481Sjb *    notice, this list of conditions and the following disclaimer in the
21178481Sjb *    documentation and/or other materials provided with the distribution.
22178481Sjb * 3. All advertising materials mentioning features or use of this software
23178481Sjb *    must display the following acknowledgement:
24178481Sjb *	This product includes software developed by Jordan Hubbard
25178481Sjb *	for the FreeBSD Project.
26178481Sjb * 4. The name of Jordan Hubbard or the FreeBSD project may not be used to
27178481Sjb *    endorse or promote products derived from this software without specific
28178481Sjb *    prior written permission.
29178481Sjb *
30178481Sjb * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
31178481Sjb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32178481Sjb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33178481Sjb * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
34178481Sjb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35178481Sjb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36178481Sjb * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
37178481Sjb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38178481Sjb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39178481Sjb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40178481Sjb * SUCH DAMAGE.
41178481Sjb *
42178481Sjb */
43178481Sjb
44178481Sjb#include "sysinstall.h"
45178481Sjb#include <ctype.h>
46178481Sjb#include <sys/disklabel.h>
47178481Sjb#include <sys/param.h>
48178481Sjb#include <sys/sysctl.h>
49178481Sjb
50178481Sjb/*
51178481Sjb * Everything to do with editing the contents of disk labels.
52178481Sjb */
53178481Sjb
54178481Sjb/* A nice message we use a lot in the disklabel editor */
55178481Sjb#define MSG_NOT_APPLICABLE	"That option is not applicable here"
56178481Sjb
57178481Sjb/* Where to start printing the freebsd slices */
58178481Sjb#define CHUNK_SLICE_START_ROW		2
59178481Sjb#define CHUNK_PART_START_ROW		11
60178481Sjb
61178481Sjb/* The smallest filesystem we're willing to create */
62178481Sjb#define FS_MIN_SIZE			ONE_MEG
63178481Sjb
64178481Sjb/* The smallest root filesystem we're willing to create */
65178481Sjb#define ROOT_MIN_SIZE			20
66178481Sjb
67178481Sjb/* The smallest swap partition we want to create by default */
68178481Sjb#define SWAP_MIN_SIZE			16
69178481Sjb
70178481Sjb/* The smallest /usr partition we're willing to create by default */
71178481Sjb#define USR_MIN_SIZE			80
72178481Sjb
73178481Sjb/* The smallest /var partition we're willing to create by default */
74178481Sjb#define VAR_MIN_SIZE			30
75178481Sjb
76178481Sjb/* All the chunks currently displayed on the screen */
77178481Sjbstatic struct {
78178481Sjb    struct chunk *c;
79178481Sjb    PartType type;
80178481Sjb} label_chunk_info[MAX_CHUNKS + 1];
81178481Sjbstatic int here;
82178481Sjb
83178481Sjbstatic int diskLabel(char *str);
84178481Sjb
85178481Sjbstatic int
86178481SjblabelHook(char *str)
87178481Sjb{
88178481Sjb    Device **devs = NULL;
89178481Sjb
90178481Sjb    /* Clip garbage off the ends */
91178481Sjb    string_prune(str);
92178481Sjb    str = string_skipwhite(str);
93178481Sjb    /* Try and open all the disks */
94178481Sjb    while (str) {
95178481Sjb	char *cp;
96178481Sjb
97178481Sjb	cp = index(str, '\n');
98178481Sjb	if (cp)
99178481Sjb	   *cp++ = 0;
100178481Sjb	if (!*str) {
101178481Sjb	    beep();
102178481Sjb	    return 0;
103178481Sjb	}
104178481Sjb	devs = deviceFind(str, DEVICE_TYPE_DISK);
105178481Sjb	if (!devs) {
106178481Sjb	    dialog_clear();
107178481Sjb	    msgConfirm("Unable to find disk %s!", str);
108178481Sjb	    return 0;
109178481Sjb	}
110178481Sjb	devs[0]->enabled = TRUE;
111178481Sjb	str = cp;
112178481Sjb    }
113178481Sjb    return devs ? 1 : 0;
114178481Sjb}
115178481Sjb
116178481Sjbint
117178481SjbdiskLabelEditor(dialogMenuItem *self)
118178481Sjb{
119178481Sjb    Device **devs;
120178481Sjb    DMenu *menu;
121178481Sjb    int i, cnt;
122178481Sjb    char *cp, *str;
123178481Sjb
124178481Sjb    cp = variable_get(VAR_DISK);
125178481Sjb    str = variable_get(SYSTEM_STATE);
126178481Sjb    devs = deviceFind(cp, DEVICE_TYPE_DISK);
127178481Sjb    cnt = deviceCount(devs);
128178481Sjb    if (!cnt) {
129178481Sjb	dialog_clear();
130178481Sjb	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
131178481Sjb		   "properly probed at boot time.  See the Hardware Guide on the\n"
132178481Sjb		   "Documentation menu for clues on diagnosing this type of problem.");
133178481Sjb	return RET_FAIL;
134178481Sjb    }
135178546Sjb    else if (cnt == 1 || variable_get(DISK_SELECTED)) {
136178481Sjb	if (cnt == 1)
137178481Sjb	    devs[0]->enabled = TRUE;
138178481Sjb	i = diskLabel(str);
139178481Sjb    }
140178481Sjb    else {
141178481Sjb	menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, labelHook);
142178481Sjb	if (!menu) {
143178481Sjb	    dialog_clear();
144178481Sjb	    msgConfirm("No devices suitable for installation found!\n\n"
145178481Sjb		       "Please verify that your disk controller (and attached drives)\n"
146178481Sjb		       "were detected properly.  This can be done by pressing the\n"
147178481Sjb		       "[Scroll Lock] key and using the Arrow keys to move back to\n"
148178481Sjb		       "the boot messages.  Press [Scroll Lock] again to return.");
149178481Sjb	    i = RET_FAIL;
150178481Sjb	}
151178481Sjb	else {
152178481Sjb	    if (!dmenuOpenSimple(menu))
153178481Sjb		i = RET_FAIL;
154178481Sjb	    else
155178481Sjb		i = diskLabel(str);
156178481Sjb	    free(menu);
157178481Sjb	}
158178481Sjb    }
159178481Sjb    return i;
160178481Sjb}
161178481Sjb
162178481Sjbint
163178481SjbdiskLabelCommit(dialogMenuItem *self)
164178481Sjb{
165178481Sjb    char *cp;
166178481Sjb    int i;
167178481Sjb
168178481Sjb    /* Already done? */
169178481Sjb    if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes")) {
170178481Sjb        variable_set2(DISK_PARTITIONED, "yes");
171178481Sjb	i = RET_SUCCESS;
172178481Sjb    }
173178481Sjb    else if (!cp) {
174178481Sjb	dialog_clear();
175178481Sjb	msgConfirm("You must assign disk labels before this option can be used.");
176178481Sjb	i = RET_FAIL;
177178481Sjb    }
178178481Sjb    /* The routine will guard against redundant writes, just as this one does */
179178481Sjb    else if (diskPartitionWrite(self) != RET_SUCCESS)
180178481Sjb	i = RET_FAIL;
181178481Sjb    else if (installFilesystems(self) != RET_SUCCESS)
182178546Sjb	i = RET_FAIL;
183178481Sjb    else {
184178546Sjb	msgInfo("All filesystem information written successfully.");
185178481Sjb	variable_set2(DISK_LABELLED, "written");
186178481Sjb	i = RET_SUCCESS;
187178546Sjb    }
188178481Sjb    return i;
189178481Sjb}
190178481Sjb
191178481Sjb/* See if we're already using a desired partition name */
192178481Sjbstatic Boolean
193178546Sjbcheck_conflict(char *name)
194178481Sjb{
195178546Sjb    int i;
196178546Sjb
197178481Sjb    for (i = 0; label_chunk_info[i].c; i++)
198178481Sjb	if (label_chunk_info[i].type == PART_FILESYSTEM && label_chunk_info[i].c->private_data
199178481Sjb	    && !strcmp(((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint, name))
200178481Sjb	    return TRUE;
201178481Sjb    return FALSE;
202178481Sjb}
203178481Sjb
204178481Sjb/* How much space is in this FreeBSD slice? */
205178481Sjbstatic int
206178481Sjbspace_free(struct chunk *c)
207178481Sjb{
208178481Sjb    struct chunk *c1;
209178481Sjb    int sz = c->size;
210178481Sjb
211178481Sjb    for (c1 = c->part; c1; c1 = c1->next) {
212178481Sjb	if (c1->type != unused)
213178481Sjb	    sz -= c1->size;
214178481Sjb    }
215178481Sjb    if (sz < 0)
216178481Sjb	msgFatal("Partitions are larger than actual chunk??");
217178481Sjb    return sz;
218178481Sjb}
219178546Sjb
220178481Sjb/* Snapshot the current situation into the displayed chunks structure */
221178481Sjbstatic void
222178481Sjbrecord_label_chunks(Device **devs)
223178481Sjb{
224178481Sjb    int i, j, p;
225178481Sjb    struct chunk *c1, *c2;
226178481Sjb    Disk *d;
227178481Sjb
228178481Sjb    j = p = 0;
229178481Sjb    /* First buzz through and pick up the FreeBSD slices */
230178481Sjb    for (i = 0; devs[i]; i++) {
231178481Sjb	if (!devs[i]->enabled)
232178481Sjb	    continue;
233178481Sjb	d = (Disk *)devs[i]->private;
234178481Sjb	if (!d->chunks)
235178481Sjb	    msgFatal("No chunk list found for %s!", d->name);
236178481Sjb
237178481Sjb	/* Put the slice entries first */
238178481Sjb	for (c1 = d->chunks->part; c1; c1 = c1->next) {
239178481Sjb	    if (c1->type == freebsd) {
240178481Sjb		label_chunk_info[j].type = PART_SLICE;
241178481Sjb		label_chunk_info[j].c = c1;
242178481Sjb		++j;
243178481Sjb	    }
244178481Sjb	}
245178481Sjb    }
246178481Sjb
247178481Sjb    /* Now run through again and get the FreeBSD partition entries */
248178481Sjb    for (i = 0; devs[i]; i++) {
249178481Sjb	if (!devs[i]->enabled)
250178481Sjb	    continue;
251178481Sjb	d = (Disk *)devs[i]->private;
252178481Sjb	/* Then buzz through and pick up the partitions */
253178481Sjb	for (c1 = d->chunks->part; c1; c1 = c1->next) {
254178481Sjb	    if (c1->type == freebsd) {
255178481Sjb		for (c2 = c1->part; c2; c2 = c2->next) {
256178481Sjb		    if (c2->type == part) {
257178481Sjb			if (c2->subtype == FS_SWAP)
258178481Sjb			    label_chunk_info[j].type = PART_SWAP;
259178546Sjb			else
260178481Sjb			    label_chunk_info[j].type = PART_FILESYSTEM;
261178481Sjb			label_chunk_info[j].c = c2;
262178481Sjb			++j;
263178481Sjb		    }
264178481Sjb		}
265178481Sjb	    }
266178481Sjb	    else if (c1->type == fat) {
267178481Sjb		label_chunk_info[j].type = PART_FAT;
268178481Sjb		label_chunk_info[j].c = c1;
269178481Sjb		++j;
270178481Sjb	    }
271178481Sjb	}
272178481Sjb    }
273178481Sjb    label_chunk_info[j].c = NULL;
274178481Sjb    if (here >= j)
275178481Sjb	here = j  ? j - 1 : 0;
276178481Sjb}
277178481Sjb
278178481Sjb/* A new partition entry */
279178481Sjbstatic PartInfo *
280178481Sjbnew_part(char *mpoint, Boolean newfs, u_long size)
281178481Sjb{
282178481Sjb    PartInfo *ret;
283178481Sjb    u_long target, divisor;
284178481Sjb
285178481Sjb    if (!mpoint)
286178481Sjb	mpoint = "/change_me";
287178481Sjb
288178481Sjb    ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
289178481Sjb    strncpy(ret->mountpoint, mpoint, FILENAME_MAX);
290178481Sjb    strcpy(ret->newfs_cmd, "newfs -b 8192 -f 1024");
291178481Sjb    ret->newfs = newfs;
292178481Sjb    if (!size)
293178481Sjb	    return ret;
294178481Sjb    for (target = size; target; target--) {
295178481Sjb	for (divisor = 4096 ; divisor > 1023; divisor--) {
296178481Sjb	    if (!(target % divisor)) {
297178481Sjb		sprintf(ret->newfs_cmd + strlen(ret->newfs_cmd), " -u %ld",divisor);
298178481Sjb		return ret;
299178481Sjb	    }
300178481Sjb	}
301178481Sjb    }
302178481Sjb    return ret;
303178481Sjb}
304178481Sjb
305178481Sjb/* Get the mountpoint for a partition and save it away */
306178481Sjbstatic PartInfo *
307178481Sjbget_mountpoint(struct chunk *old)
308178481Sjb{
309178481Sjb    char *val;
310178481Sjb    PartInfo *tmp;
311178481Sjb
312178481Sjb    if (old && old->private_data)
313178481Sjb	tmp = old->private_data;
314178481Sjb    else
315178481Sjb	tmp = NULL;
316178546Sjb    val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
317178481Sjb    if (!val || !*val) {
318178481Sjb	if (!old)
319178481Sjb	    return NULL;
320178481Sjb	else {
321178481Sjb	    free(old->private_data);
322178481Sjb	    old->private_data = NULL;
323178481Sjb	}
324178481Sjb	return NULL;
325178481Sjb    }
326178481Sjb
327178481Sjb    /* Is it just the same value? */
328178481Sjb    if (tmp && !strcmp(tmp->mountpoint, val))
329178481Sjb	return NULL;
330178481Sjb
331178481Sjb    /* Did we use it already? */
332178481Sjb    if (check_conflict(val)) {
333178481Sjb	msgConfirm("You already have a mount point for %s assigned!", val);
334178481Sjb	return NULL;
335178481Sjb    }
336178481Sjb
337178481Sjb    /* Is it bogus? */
338178546Sjb    if (*val != '/') {
339178481Sjb	msgConfirm("Mount point must start with a / character");
340178481Sjb	return NULL;
341178481Sjb    }
342178481Sjb
343178481Sjb    /* Is it going to be mounted on root? */
344178481Sjb    if (!strcmp(val, "/")) {
345178481Sjb	if (old)
346178481Sjb	    old->flags |= CHUNK_IS_ROOT;
347178481Sjb    }
348178481Sjb    else if (old)
349178481Sjb	old->flags &= ~CHUNK_IS_ROOT;
350178481Sjb
351178481Sjb    safe_free(tmp);
352178481Sjb    tmp = new_part(val, TRUE, 0);
353178481Sjb    if (old) {
354178481Sjb	old->private_data = tmp;
355178481Sjb	old->private_free = safe_free;
356178481Sjb    }
357178546Sjb    return tmp;
358178481Sjb}
359178481Sjb
360178481Sjb/* Get the type of the new partiton */
361178481Sjbstatic PartType
362178481Sjbget_partition_type(void)
363178481Sjb{
364178481Sjb    char selection[20];
365178481Sjb    int i;
366178481Sjb
367178481Sjb    static unsigned char *fs_types[] = {
368178481Sjb	"FS",
369178481Sjb	"A file system",
370178481Sjb	"Swap",
371178481Sjb	"A swap partition.",
372178481Sjb    };
373178481Sjb    i = dialog_menu("Please choose a partition type",
374178481Sjb		    "If you want to use this partition for swap space, select Swap.\n"
375178481Sjb		    "If you want to put a filesystem on it, choose FS.",
376178481Sjb		    -1, -1, 2, 2, fs_types, selection, NULL, NULL);
377178481Sjb    if (!i) {
378178481Sjb	if (!strcmp(selection, "FS"))
379178481Sjb	    return PART_FILESYSTEM;
380178481Sjb	else if (!strcmp(selection, "Swap"))
381178481Sjb	    return PART_SWAP;
382178481Sjb    }
383178481Sjb    return PART_NONE;
384178481Sjb}
385178481Sjb
386178481Sjb/* If the user wants a special newfs command for this, set it */
387178481Sjbstatic void
388178481SjbgetNewfsCmd(PartInfo *p)
389178481Sjb{
390178481Sjb    char *val;
391178481Sjb
392178481Sjb    val = msgGetInput(p->newfs_cmd,
393178481Sjb		      "Please enter the newfs command and options you'd like to use in\n"
394178481Sjb		      "creating this file system.");
395178481Sjb    if (val)
396178481Sjb	strncpy(p->newfs_cmd, val, NEWFS_CMD_MAX);
397178481Sjb}
398178481Sjb
399178481Sjb#define MAX_MOUNT_NAME	12
400178481Sjb
401178481Sjb#define PART_PART_COL	0
402178481Sjb#define PART_MOUNT_COL	8
403178481Sjb#define PART_SIZE_COL	(PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
404178481Sjb#define PART_NEWFS_COL	(PART_SIZE_COL + 7)
405178481Sjb#define PART_OFF	38
406178481Sjb
407178481Sjb/* How many mounted partitions to display in column before going to next */
408178481Sjb#define CHUNK_COLUMN_MAX	5
409178481Sjb
410178481Sjb/* stick this all up on the screen */
411178481Sjbstatic void
412178481Sjbprint_label_chunks(void)
413178481Sjb{
414178481Sjb    int i, j, srow, prow, pcol;
415178481Sjb    int sz;
416178481Sjb
417178481Sjb    attrset(A_REVERSE);
418178481Sjb    mvaddstr(0, 25, "FreeBSD Disklabel Editor");
419178481Sjb    clrtobot();
420178481Sjb    attrset(A_NORMAL);
421178546Sjb
422178546Sjb    for (i = 0; i < 2; i++) {
423178481Sjb	mvaddstr(CHUNK_PART_START_ROW - 2, PART_PART_COL + (i * PART_OFF), "Part");
424178481Sjb	mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF), "----");
425178481Sjb
426178481Sjb	mvaddstr(CHUNK_PART_START_ROW - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
427178481Sjb	mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
428178481Sjb
429178481Sjb	mvaddstr(CHUNK_PART_START_ROW - 2, PART_SIZE_COL + (i * PART_OFF) + 2, "Size");
430178481Sjb	mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF) + 2, "----");
431178481Sjb
432178481Sjb	mvaddstr(CHUNK_PART_START_ROW - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
433178546Sjb	mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
434178481Sjb    }
435178481Sjb    srow = CHUNK_SLICE_START_ROW;
436178481Sjb    prow = CHUNK_PART_START_ROW;
437178481Sjb    pcol = 0;
438178481Sjb
439178481Sjb    for (i = 0; label_chunk_info[i].c; i++) {
440178481Sjb	if (i == here)
441178481Sjb	    attrset(A_REVERSE);
442178481Sjb	/* Is it a slice entry displayed at the top? */
443178481Sjb	if (label_chunk_info[i].type == PART_SLICE) {
444178481Sjb	    sz = space_free(label_chunk_info[i].c);
445178546Sjb	    mvprintw(srow++, 0, "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
446178481Sjb		     label_chunk_info[i].c->disk->name, label_chunk_info[i].c->name, sz, (sz / ONE_MEG));
447178481Sjb	}
448178481Sjb	/* Otherwise it's a DOS, swap or filesystem entry, at the bottom */
449178481Sjb	else {
450178481Sjb	    char onestr[PART_OFF], num[10], *mountpoint, *newfs;
451178481Sjb
452178481Sjb	    /*
453178481Sjb	     * We copy this into a blank-padded string so that it looks like
454178481Sjb	     * a solid bar in reverse-video
455178481Sjb	     */
456178546Sjb	    memset(onestr, ' ', PART_OFF - 1);
457178481Sjb	    onestr[PART_OFF - 1] = '\0';
458178481Sjb	    /* Go for two columns */
459178481Sjb	    if (prow == (CHUNK_PART_START_ROW + CHUNK_COLUMN_MAX)) {
460178481Sjb		pcol = PART_OFF;
461178481Sjb		prow = CHUNK_PART_START_ROW;
462178481Sjb	    }
463178481Sjb	    memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
464178546Sjb	    /* If it's a filesystem, display the mountpoint */
465178481Sjb	    if (label_chunk_info[i].c->private_data
466178481Sjb		&& (label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT))
467178481Sjb	        mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
468178481Sjb	    else
469178481Sjb	        mountpoint = "<none>";
470178481Sjb
471178481Sjb	    /* Now display the newfs field */
472178481Sjb	    if (label_chunk_info[i].type == PART_FAT)
473178481Sjb	        newfs = "DOS";
474178481Sjb	    else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM)
475178481Sjb		newfs = ((PartInfo *)label_chunk_info[i].c->private_data)->newfs ? "UFS Y" : "UFS N";
476178481Sjb	    else if (label_chunk_info[i].type == PART_SWAP)
477178481Sjb		newfs = "SWAP";
478178481Sjb	    else
479178481Sjb		newfs = "*";
480178481Sjb	    for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
481178481Sjb		onestr[PART_MOUNT_COL + j] = mountpoint[j];
482178481Sjb	    snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? label_chunk_info[i].c->size / ONE_MEG : 0);
483178481Sjb	    memcpy(onestr + PART_SIZE_COL, num, strlen(num));
484178481Sjb	    memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
485178546Sjb	    onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
486178481Sjb	    mvaddstr(prow, pcol, onestr);
487178481Sjb	    ++prow;
488178481Sjb	}
489178481Sjb	if (i == here)
490178481Sjb	    attrset(A_NORMAL);
491178481Sjb    }
492178481Sjb}
493178481Sjb
494178481Sjbstatic void
495178481Sjbprint_command_summary()
496178481Sjb{
497178546Sjb    mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
498178481Sjb    mvprintw(18, 0, "C = Create      D = Delete         M = Mount   W = Write");
499178481Sjb    mvprintw(19, 0, "N = Newfs Opts  T = Newfs Toggle   U = Undo    Q = Finish");
500178481Sjb    mvprintw(20, 0, "A = Auto Defaults for all!");
501178481Sjb    mvprintw(22, 0, "The default target will be displayed in ");
502178481Sjb
503178481Sjb    attrset(A_REVERSE);
504178481Sjb    addstr("reverse");
505178481Sjb    attrset(A_NORMAL);
506178481Sjb    addstr(" video.");
507178481Sjb    mvprintw(23, 0, "Use F1 or ? to get more help, arrow keys to move.");
508178481Sjb    move(0, 0);
509178481Sjb}
510178546Sjb
511178546Sjbstatic int
512178481SjbdiskLabel(char *str)
513178481Sjb{
514178481Sjb    int sz, key = 0;
515178481Sjb    Boolean labeling;
516178481Sjb    char *msg = NULL;
517178481Sjb    PartInfo *p, *oldp;
518178481Sjb    PartType type;
519178481Sjb    Device **devs;
520178481Sjb
521178481Sjb    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
522178546Sjb    if (!devs) {
523178546Sjb	dialog_clear();
524178546Sjb	msgConfirm("No disks found!");
525178481Sjb	return RET_FAIL;
526178481Sjb    }
527178481Sjb
528178546Sjb    labeling = TRUE;
529178481Sjb    keypad(stdscr, TRUE);
530178481Sjb    record_label_chunks(devs);
531178481Sjb
532178481Sjb    dialog_clear(); clear();
533178481Sjb    while (labeling) {
534178481Sjb	clear();
535178481Sjb	print_label_chunks();
536178481Sjb	print_command_summary();
537178481Sjb	if (msg) {
538178481Sjb	    attrset(A_REVERSE); mvprintw(23, 0, msg); attrset(A_NORMAL);
539178481Sjb	    clrtoeol();
540178481Sjb	    beep();
541178481Sjb	    msg = NULL;
542178481Sjb	}
543178481Sjb	refresh();
544178481Sjb	key = toupper(getch());
545178481Sjb	switch (key) {
546178481Sjb	    int i, cnt;
547178481Sjb
548178481Sjb	case '\014':	/* ^L */
549178481Sjb	    continue;
550178481Sjb
551178481Sjb	case KEY_UP:
552178481Sjb	case '-':
553178481Sjb	    if (here != 0)
554178481Sjb		--here;
555178481Sjb	    else
556178481Sjb		while (label_chunk_info[here + 1].c)
557178481Sjb		    ++here;
558178481Sjb	    break;
559178481Sjb
560178481Sjb	case KEY_DOWN:
561178481Sjb	case '+':
562178481Sjb	case '\r':
563178481Sjb	case '\n':
564178481Sjb	    if (label_chunk_info[here + 1].c)
565178481Sjb		++here;
566178481Sjb	    else
567178481Sjb		here = 0;
568178481Sjb	    break;
569178481Sjb
570178481Sjb	case KEY_HOME:
571178481Sjb	    here = 0;
572178481Sjb	    break;
573178481Sjb
574178481Sjb	case KEY_END:
575178481Sjb	    while (label_chunk_info[here + 1].c)
576178481Sjb		++here;
577178481Sjb	    break;
578178481Sjb
579178481Sjb	case KEY_F(1):
580178481Sjb	case '?':
581178481Sjb	    systemDisplayHelp("partition");
582178481Sjb	    break;
583178481Sjb
584178481Sjb	case 'A':
585178481Sjb	    if (label_chunk_info[here].type != PART_SLICE) {
586178481Sjb		msg = "You can only do this in a master partition (see top of screen)";
587178481Sjb		break;
588178481Sjb	    }
589178481Sjb
590178481Sjb	    cnt = i = 0;
591178481Sjb	    while (label_chunk_info[i].c)
592178481Sjb		if (label_chunk_info[i++].type != PART_SLICE)
593178481Sjb		    cnt++;
594178481Sjb	    if (cnt == (CHUNK_COLUMN_MAX * 2) + 4) {
595178481Sjb		dialog_clear();
596178481Sjb		msgConfirm("Sorry, I can't fit any more partitions on the screen!  You can get around\n"
597178481Sjb			   "this limitation by partitioning your disks individually rather than all\n"
598178481Sjb			   "at once.  This will be fixed just as soon as we get a scrolling partition\n"
599178481Sjb			   "box written.  Sorry for the inconvenience!");
600178481Sjb		break;
601178481Sjb	    }
602178481Sjb
603178481Sjb	    sz = space_free(label_chunk_info[here].c);
604178481Sjb	    if (sz <= FS_MIN_SIZE) {
605178481Sjb		msg = "Not enough space to create an additional FreeBSD partition";
606178481Sjb		break;
607178481Sjb	    }
608178481Sjb	{
609178481Sjb	    struct chunk *tmp;
610178481Sjb	    int mib[2];
611178481Sjb	    int physmem;
612178481Sjb	    size_t size, swsize;
613178481Sjb	    char *cp;
614178481Sjb
615178481Sjb	    cp = variable_get(VAR_ROOT_SIZE);
616178481Sjb	    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
617178481Sjb				    label_chunk_info[here].c,
618178481Sjb				    (cp ? atoi(cp) : 32) * ONE_MEG, part, FS_BSDFFS,
619178481Sjb				    CHUNK_IS_ROOT);
620178481Sjb
621178481Sjb	    if (!tmp) {
622178481Sjb		dialog_clear();
623178481Sjb		msgConfirm("Unable to create the root partition. Too big?");
624178481Sjb		break;
625178481Sjb	    }
626178481Sjb	    tmp->private_data = new_part("/", TRUE, tmp->size);
627178481Sjb	    tmp->private_free = safe_free;
628178481Sjb	    record_label_chunks(devs);
629178481Sjb
630178481Sjb	    cp = variable_get(VAR_SWAP_SIZE);
631178481Sjb	    if (cp)
632178481Sjb		swsize = atoi(cp) * ONE_MEG;
633178481Sjb	    else {
634178481Sjb		mib[0] = CTL_HW;
635178481Sjb		mib[1] = HW_PHYSMEM;
636178481Sjb		size = sizeof physmem;
637178481Sjb		sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
638178481Sjb		swsize = 16 * ONE_MEG + (physmem * 2 / 512);
639178481Sjb	    }
640178481Sjb	    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
641178481Sjb				    label_chunk_info[here].c,
642178481Sjb				    swsize,
643178481Sjb				    part, FS_SWAP, 0);
644178481Sjb	    if (!tmp) {
645178481Sjb		dialog_clear();
646178481Sjb		msgConfirm("Unable to create the swap partition. Too big?");
647178481Sjb		break;
648178481Sjb	    }
649178481Sjb
650178481Sjb	    tmp->private_data = 0;
651178481Sjb	    tmp->private_free = safe_free;
652178481Sjb	    record_label_chunks(devs);
653178481Sjb
654178481Sjb	    cp = variable_get(VAR_VAR_SIZE);
655178481Sjb	    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
656178481Sjb				    label_chunk_info[here].c,
657178481Sjb				    (cp ? atoi(cp) : VAR_MIN_SIZE) * ONE_MEG, part, FS_BSDFFS, 0);
658178481Sjb	    if (!tmp) {
659178481Sjb		dialog_clear();
660178481Sjb		msgConfirm("Less than %dMB free for /var - you will need to\n"
661178481Sjb			   "partition your disk manually with a custom install!", (cp ? atoi(cp) : VAR_MIN_SIZE));
662178481Sjb		break;
663178481Sjb	    }
664178481Sjb	    tmp->private_data = new_part("/var", TRUE, tmp->size);
665178481Sjb	    tmp->private_free = safe_free;
666178481Sjb	    record_label_chunks(devs);
667178481Sjb
668178481Sjb	    cp = variable_get(VAR_USR_SIZE);
669178481Sjb	    if (cp)
670178481Sjb		sz = atoi(cp) * ONE_MEG;
671178481Sjb	    else
672178481Sjb		sz = space_free(label_chunk_info[here].c);
673178481Sjb	    if (!sz || sz < (USR_MIN_SIZE * ONE_MEG)) {
674178481Sjb		dialog_clear();
675178481Sjb		msgConfirm("Less than %dMB free for /usr - you will need to\n"
676178481Sjb			   "partition your disk manually with a custom install!", USR_MIN_SIZE);
677178481Sjb		break;
678178481Sjb	    }
679178481Sjb
680178481Sjb	    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
681178481Sjb				    label_chunk_info[here].c,
682178481Sjb				    sz, part, FS_BSDFFS, 0);
683178481Sjb	    if (!tmp) {
684178481Sjb		dialog_clear();
685178481Sjb		msgConfirm("Unable to create the /usr partition.  Not enough space?\n"
686178481Sjb			   "You will need to partition your disk manually with a custom install!");
687178481Sjb		break;
688178481Sjb	    }
689178481Sjb	    /* At this point, we're reasonably "labelled" */
690178481Sjb	    variable_set2(DISK_LABELLED, "yes");
691178481Sjb	    tmp->private_data = new_part("/usr", TRUE, tmp->size);
692178481Sjb	    tmp->private_free = safe_free;
693178481Sjb	    record_label_chunks(devs);
694178481Sjb	}
695178481Sjb	    break;
696178481Sjb
697178481Sjb	case 'C':
698178481Sjb	    if (label_chunk_info[here].type != PART_SLICE) {
699178481Sjb		msg = "You can only do this in a master partition (see top of screen)";
700178481Sjb		break;
701178546Sjb	    }
702178481Sjb	    else {
703178481Sjb		int i, cnt;
704178481Sjb
705178481Sjb		cnt = i = 0;
706178481Sjb		while (label_chunk_info[i].c)
707178546Sjb		    if (label_chunk_info[i++].type != PART_SLICE)
708178546Sjb			cnt++;
709178481Sjb		if (cnt == (CHUNK_COLUMN_MAX * 2)) {
710178481Sjb		    dialog_clear();
711178481Sjb		    msgConfirm("Sorry, I can't fit any more partitions on the screen!  You can get around\n"
712178481Sjb			       "this limitation by partitioning your disks individually rather than all\n"
713178481Sjb			       "at once.  This will be fixed just as soon as we get a scrolling partition\n"
714178481Sjb			       "box written.  Sorry for the inconvenience!");
715178481Sjb		    break;
716178481Sjb		}
717178481Sjb	    }
718178481Sjb	    sz = space_free(label_chunk_info[here].c);
719178481Sjb	    if (sz <= FS_MIN_SIZE) {
720178481Sjb		msg = "Not enough space to create an additional FreeBSD partition";
721178481Sjb		break;
722178481Sjb	    }
723178481Sjb	    {
724178481Sjb		char *val, *cp;
725178481Sjb		int size;
726178481Sjb		struct chunk *tmp;
727178481Sjb		char osize[80];
728178481Sjb		u_long flags = 0;
729178481Sjb
730178481Sjb		sprintf(osize, "%d", sz);
731178481Sjb		val = msgGetInput(osize, "Please specify the size for new FreeBSD partition in blocks, or\n"
732178481Sjb				  "append a trailing `M' for megabytes (e.g. 20M) or `C' for cylinders.\n\n"
733178481Sjb				  "Space free is %d blocks (%dMB)", sz, sz / ONE_MEG);
734178546Sjb		if (!val || (size = strtol(val, &cp, 0)) <= 0)
735178481Sjb		    break;
736178481Sjb
737178481Sjb		if (*cp) {
738178481Sjb		    if (toupper(*cp) == 'M')
739178481Sjb			size *= ONE_MEG;
740178481Sjb		    else if (toupper(*cp) == 'C')
741178481Sjb			size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
742178481Sjb		}
743178481Sjb		if (size <= FS_MIN_SIZE) {
744178481Sjb		    msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
745178481Sjb		    break;
746178481Sjb		}
747178481Sjb		type = get_partition_type();
748178481Sjb		if (type == PART_NONE)
749178481Sjb		    break;
750178481Sjb
751178481Sjb		if (type == PART_FILESYSTEM) {
752178481Sjb		    if ((p = get_mountpoint(NULL)) == NULL)
753178481Sjb			break;
754178481Sjb		    else if (!strcmp(p->mountpoint, "/"))
755178481Sjb			flags |= CHUNK_IS_ROOT;
756178481Sjb		    else
757178481Sjb			flags &= ~CHUNK_IS_ROOT;
758178481Sjb		} else
759178481Sjb		    p = NULL;
760178481Sjb
761178481Sjb		if ((flags & CHUNK_IS_ROOT)) {
762178481Sjb		    if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) {
763178481Sjb			msgConfirm("This region cannot be used for your root partition as the\n"
764178481Sjb				   "FreeBSD boot code cannot deal with a root partition created\n"
765178481Sjb				   "in that location.  Please choose another location or smaller\n"
766178481Sjb				   "size for your root partition and try again!");
767178481Sjb			break;
768178481Sjb		    }
769178481Sjb		    if (size < (ROOT_MIN_SIZE * ONE_MEG)) {
770178546Sjb			msgConfirm("Warning: This is smaller than the recommended size for a\n"
771178481Sjb				   "root partition.  For a variety of reasons, root\n"
772178481Sjb				   "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
773178481Sjb		    }
774178481Sjb		}
775178481Sjb		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
776178481Sjb					label_chunk_info[here].c,
777178481Sjb					size, part,
778178481Sjb					(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
779178481Sjb					flags);
780178481Sjb		if (!tmp) {
781178481Sjb		    msgConfirm("Unable to create the partition. Too big?");
782178481Sjb		    break;
783178481Sjb		}
784178481Sjb		if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) {
785178481Sjb		    msgConfirm("This region cannot be used for your root partition as it starts\n"
786178481Sjb			       "or extends past the 1024'th cylinder mark and is thus a\n"
787178481Sjb			       "poor location to boot from.  Please choose another\n"
788178481Sjb			       "location (or smaller size) for your root partition and try again!");
789178481Sjb		    Delete_Chunk(label_chunk_info[here].c->disk, tmp);
790178481Sjb		    break;
791178481Sjb		}
792178481Sjb		if (type != PART_SWAP) {
793178481Sjb		    /* This is needed to tell the newfs -u about the size */
794178481Sjb		    tmp->private_data = new_part(p->mountpoint, p->newfs, tmp->size);
795178481Sjb		    tmp->private_free = safe_free;
796178481Sjb		    safe_free(p);
797178481Sjb		} else {
798178481Sjb		    tmp->private_data = p;
799178481Sjb		}
800178481Sjb		tmp->private_free = safe_free;
801178481Sjb		variable_set2(DISK_LABELLED, "yes");
802178481Sjb		record_label_chunks(devs);
803178481Sjb	    }
804178481Sjb	    break;
805178481Sjb
806178481Sjb	case '\177':
807178481Sjb	case 'D':	/* delete */
808178481Sjb	    if (label_chunk_info[here].type == PART_SLICE) {
809178481Sjb		msg = MSG_NOT_APPLICABLE;
810178546Sjb		break;
811178481Sjb	    }
812178481Sjb	    else if (label_chunk_info[here].type == PART_FAT) {
813178481Sjb		msg = "Use the Disk Partition Editor to delete DOS partitions";
814178481Sjb		break;
815178481Sjb	    }
816178481Sjb	    Delete_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c);
817178481Sjb	    variable_set2(DISK_LABELLED, "yes");
818178481Sjb	    record_label_chunks(devs);
819178481Sjb	    break;
820178481Sjb
821178546Sjb	case 'M':	/* mount */
822178481Sjb	    switch(label_chunk_info[here].type) {
823178481Sjb	    case PART_SLICE:
824178481Sjb		msg = MSG_NOT_APPLICABLE;
825178481Sjb		break;
826178481Sjb
827178481Sjb	    case PART_SWAP:
828178481Sjb		msg = "You don't need to specify a mountpoint for a swap partition.";
829178481Sjb		break;
830178481Sjb
831178481Sjb	    case PART_FAT:
832178481Sjb	    case PART_FILESYSTEM:
833178481Sjb		oldp = label_chunk_info[here].c->private_data;
834178481Sjb		p = get_mountpoint(label_chunk_info[here].c);
835178481Sjb		if (p) {
836178481Sjb		    if (!oldp)
837178481Sjb		    	p->newfs = FALSE;
838178481Sjb		    if (label_chunk_info[here].type == PART_FAT
839178481Sjb			&& (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr")
840178481Sjb			    || !strcmp(p->mountpoint, "/var"))) {
841178481Sjb			msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
842178481Sjb			strcpy(p->mountpoint, "/bogus");
843178481Sjb		    }
844178481Sjb		}
845178481Sjb		variable_set2(DISK_LABELLED, "yes");
846178481Sjb		record_label_chunks(devs);
847178481Sjb		break;
848178481Sjb
849178481Sjb	    default:
850178546Sjb		msgFatal("Bogus partition under cursor???");
851178481Sjb		break;
852178481Sjb	    }
853178481Sjb	    break;
854178481Sjb
855178481Sjb	case 'N':	/* Set newfs options */
856178481Sjb	    if (label_chunk_info[here].c->private_data &&
857178481Sjb		((PartInfo *)label_chunk_info[here].c->private_data)->newfs)
858178481Sjb		getNewfsCmd(label_chunk_info[here].c->private_data);
859178481Sjb	    else
860178481Sjb		msg = MSG_NOT_APPLICABLE;
861178481Sjb	    break;
862178481Sjb
863178481Sjb	case 'T':	/* Toggle newfs state */
864178481Sjb	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
865178481Sjb		    PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
866178481Sjb		    label_chunk_info[here].c->private_data = new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
867178481Sjb		    safe_free(pi);
868178481Sjb		    label_chunk_info[here].c->private_free = safe_free;
869178481Sjb		    variable_set2(DISK_LABELLED, "yes");
870178481Sjb		}
871178481Sjb	    else
872178481Sjb		msg = MSG_NOT_APPLICABLE;
873178481Sjb	    break;
874178481Sjb
875178546Sjb	case 'U':
876178481Sjb	    clear();
877178481Sjb	    if (msgYesNo("Are you SURE you want to Undo everything?"))
878178481Sjb		break;
879178481Sjb	    variable_unset(DISK_PARTITIONED);
880178481Sjb	    for (i = 0; devs[i]; i++) {
881178481Sjb		extern void diskPartition(Device *dev, Disk *d);
882178481Sjb		Disk *d;
883178481Sjb
884178481Sjb		if (!devs[i]->enabled)
885178481Sjb		    continue;
886178481Sjb		else if ((d = Open_Disk(devs[i]->name)) != NULL) {
887178481Sjb		    Free_Disk(devs[i]->private);
888178481Sjb		    devs[i]->private = d;
889178481Sjb		    diskPartition(devs[i], d);
890178481Sjb		}
891178481Sjb	    }
892178481Sjb	    variable_unset(DISK_LABELLED);
893178481Sjb	    record_label_chunks(devs);
894178481Sjb	    break;
895178481Sjb
896178481Sjb	case 'W':
897178481Sjb	    if (!msgYesNo("Are you SURE that you wish to make and mount all filesystems\n"
898178481Sjb			  "at this time?  You also have the option of doing it later in\n"
899178481Sjb			  "one final 'commit' operation, and if you're at all unsure as\n"
900178481Sjb			  "to which option to chose, then PLEASE CHOSE NO!  This option\n"
901178481Sjb			  "is DANGEROUS if you're not EXACTLY sure what you are doing!")) {
902178481Sjb		variable_set2(DISK_LABELLED, "yes");
903178481Sjb		clear();
904178481Sjb		diskLabelCommit(NULL);
905178481Sjb	    }
906178481Sjb	    break;
907178481Sjb
908178481Sjb	case '|':
909178481Sjb	    if (!msgYesNo("Are you sure you want to go into Wizard mode?\n\n"
910178481Sjb			  "This is an entirely undocumented feature which you are not\n"
911178481Sjb			  "expected to understand!")) {
912178481Sjb		int i;
913178546Sjb		Device **devs;
914178481Sjb
915178481Sjb		dialog_clear();
916178481Sjb		end_dialog();
917178481Sjb		DialogActive = FALSE;
918178481Sjb		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
919178481Sjb		if (!devs) {
920178481Sjb		    dialog_clear();
921178481Sjb		    msgConfirm("Can't find any disk devices!");
922178481Sjb		    break;
923178481Sjb		}
924178481Sjb		for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
925178481Sjb		    if (devs[i]->enabled)
926178481Sjb		    	slice_wizard(((Disk *)devs[i]->private));
927178481Sjb		}
928178481Sjb		variable_set2(DISK_LABELLED, "yes");
929178481Sjb		DialogActive = TRUE;
930178481Sjb		dialog_clear();
931178481Sjb		record_label_chunks(devs);
932178481Sjb	    }
933178481Sjb	    else
934178481Sjb		msg = "A most prudent choice!";
935178481Sjb	    break;
936178481Sjb
937178481Sjb	case 'Q':
938178481Sjb	    labeling = FALSE;
939178481Sjb	    break;
940178481Sjb
941178481Sjb	default:
942178481Sjb	    beep();
943178481Sjb	    msg = "Type F1 or ? for help";
944178481Sjb	    break;
945178481Sjb	}
946178481Sjb    }
947178481Sjb    dialog_clear();
948178481Sjb    return RET_SUCCESS;
949178481Sjb}
950178481Sjb