label.c revision 14334
1/*
2 * The new sysinstall program.
3 *
4 * This is probably the last program in the `sysinstall' line - the next
5 * generation being essentially a complete rewrite.
6 *
7 * $Id: label.c,v 1.35 1995/12/11 16:32:33 jkh Exp $
8 *
9 * Copyright (c) 1995
10 *	Jordan Hubbard.  All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer,
17 *    verbatim and that no modifications are made prior to this
18 *    point in the file.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in the
21 *    documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 *    must display the following acknowledgement:
24 *	This product includes software developed by Jordan Hubbard
25 *	for the FreeBSD Project.
26 * 4. The name of Jordan Hubbard or the FreeBSD project may not be used to
27 *    endorse or promote products derived from this software without specific
28 *    prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
41 *
42 */
43
44#include "sysinstall.h"
45#include <ctype.h>
46#include <sys/disklabel.h>
47#include <sys/param.h>
48#include <sys/sysctl.h>
49
50/*
51 * Everything to do with editing the contents of disk labels.
52 */
53
54/* A nice message we use a lot in the disklabel editor */
55#define MSG_NOT_APPLICABLE	"That option is not applicable here"
56
57/* Where to start printing the freebsd slices */
58#define CHUNK_SLICE_START_ROW		2
59#define CHUNK_PART_START_ROW		11
60
61/* The smallest filesystem we're willing to create */
62#define FS_MIN_SIZE			ONE_MEG
63
64/* The smallest root filesystem we're willing to create */
65#define ROOT_MIN_SIZE			20
66
67/* The smallest swap partition we want to create by default */
68#define SWAP_MIN_SIZE			16
69
70/* The smallest /usr partition we're willing to create by default */
71#define USR_MIN_SIZE			80
72
73/* The smallest /var partition we're willing to create by default */
74#define VAR_MIN_SIZE			30
75
76/* All the chunks currently displayed on the screen */
77static struct {
78    struct chunk *c;
79    PartType type;
80} label_chunk_info[MAX_CHUNKS + 1];
81static int here;
82
83static int diskLabel(char *str);
84static int scriptLabel(char *str);
85
86static int
87labelHook(char *str)
88{
89    Device **devs = NULL;
90
91    /* Clip garbage off the ends */
92    string_prune(str);
93    str = string_skipwhite(str);
94    /* Try and open all the disks */
95    while (str) {
96	char *cp;
97
98	cp = index(str, '\n');
99	if (cp)
100	   *cp++ = 0;
101	if (!*str) {
102	    beep();
103	    return 0;
104	}
105	devs = deviceFind(str, DEVICE_TYPE_DISK);
106	if (!devs) {
107	    dialog_clear();
108	    msgConfirm("Unable to find disk %s!", str);
109	    return 0;
110	}
111	devs[0]->enabled = TRUE;
112	str = cp;
113    }
114    return devs ? 1 : 0;
115}
116
117int
118diskLabelEditor(char *str)
119{
120    Device **devs;
121    DMenu *menu;
122    int i, cnt;
123    char *cp;
124
125    cp = variable_get(VAR_DISK);
126    devs = deviceFind(cp, DEVICE_TYPE_DISK);
127    cnt = deviceCount(devs);
128    if (!cnt) {
129	dialog_clear();
130	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
131		   "properly probed at boot time.  See the Hardware Guide on the\n"
132		   "Documentation menu for clues on diagnosing this type of problem.");
133	return RET_FAIL;
134    }
135    else if (cnt == 1 || variable_get(DISK_SELECTED)) {
136	if (cnt == 1)
137	    devs[0]->enabled = TRUE;
138	if (str && !strcmp(str, "script"))
139	    i = scriptLabel(str);
140	else
141	    i = diskLabel(str);
142    }
143    else {
144	menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, labelHook);
145	if (!menu) {
146	    dialog_clear();
147	    msgConfirm("No devices suitable for installation found!\n\n"
148		       "Please verify that your disk controller (and attached drives)\n"
149		       "were detected properly.  This can be done by pressing the\n"
150		       "[Scroll Lock] key and using the Arrow keys to move back to\n"
151		       "the boot messages.  Press [Scroll Lock] again to return.");
152	    i = RET_FAIL;
153	}
154	else {
155	    if (!dmenuOpenSimple(menu))
156		i = RET_FAIL;
157	    else
158		i = diskLabel(str);
159	    free(menu);
160	}
161    }
162    return i;
163}
164
165int
166diskLabelCommit(char *str)
167{
168    char *cp;
169    int i;
170
171    /* Already done? */
172    if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes"))
173	i = RET_SUCCESS;
174    else if (!cp) {
175	dialog_clear();
176	msgConfirm("You must assign disk labels before this option can be used.");
177	i = RET_FAIL;
178    }
179    /* The routine will guard against redundant writes, just as this one does */
180    else if (diskPartitionWrite(str) != RET_SUCCESS)
181	i = RET_FAIL;
182    else if (installFilesystems(str) != RET_SUCCESS)
183	i = RET_FAIL;
184    else {
185	msgInfo("All filesystem information written successfully.");
186	variable_set2(DISK_LABELLED, "written");
187	i = RET_SUCCESS;
188    }
189    return i;
190}
191
192/* See if we're already using a desired partition name */
193static Boolean
194check_conflict(char *name)
195{
196    int i;
197
198    for (i = 0; label_chunk_info[i].c; i++)
199	if (label_chunk_info[i].type == PART_FILESYSTEM && label_chunk_info[i].c->private
200	    && !strcmp(((PartInfo *)label_chunk_info[i].c->private)->mountpoint, name))
201	    return TRUE;
202    return FALSE;
203}
204
205/* How much space is in this FreeBSD slice? */
206static int
207space_free(struct chunk *c)
208{
209    struct chunk *c1;
210    int sz = c->size;
211
212    for (c1 = c->part; c1; c1 = c1->next) {
213	if (c1->type != unused)
214	    sz -= c1->size;
215    }
216    if (sz < 0)
217	msgFatal("Partitions are larger than actual chunk??");
218    return sz;
219}
220
221/* Snapshot the current situation into the displayed chunks structure */
222static void
223record_label_chunks(Device **devs)
224{
225    int i, j, p;
226    struct chunk *c1, *c2;
227    Disk *d;
228
229    j = p = 0;
230    /* First buzz through and pick up the FreeBSD slices */
231    for (i = 0; devs[i]; i++) {
232	if (!devs[i]->enabled)
233	    continue;
234	d = (Disk *)devs[i]->private;
235	if (!d->chunks)
236	    msgFatal("No chunk list found for %s!", d->name);
237
238	/* Put the slice entries first */
239	for (c1 = d->chunks->part; c1; c1 = c1->next) {
240	    if (c1->type == freebsd) {
241		label_chunk_info[j].type = PART_SLICE;
242		label_chunk_info[j].c = c1;
243		++j;
244	    }
245	}
246    }
247
248    /* Now run through again and get the FreeBSD partition entries */
249    for (i = 0; devs[i]; i++) {
250	if (!devs[i]->enabled)
251	    continue;
252	d = (Disk *)devs[i]->private;
253	/* Then buzz through and pick up the partitions */
254	for (c1 = d->chunks->part; c1; c1 = c1->next) {
255	    if (c1->type == freebsd) {
256		for (c2 = c1->part; c2; c2 = c2->next) {
257		    if (c2->type == part) {
258			if (c2->subtype == FS_SWAP)
259			    label_chunk_info[j].type = PART_SWAP;
260			else
261			    label_chunk_info[j].type = PART_FILESYSTEM;
262			label_chunk_info[j].c = c2;
263			++j;
264		    }
265		}
266	    }
267	    else if (c1->type == fat) {
268		label_chunk_info[j].type = PART_FAT;
269		label_chunk_info[j].c = c1;
270		++j;
271	    }
272	}
273    }
274    label_chunk_info[j].c = NULL;
275    if (here >= j)
276	here = j  ? j - 1 : 0;
277}
278
279/* A new partition entry */
280static PartInfo *
281new_part(char *mpoint, Boolean newfs, u_long size)
282{
283    PartInfo *ret;
284    u_long target, divisor;
285
286    if (!mpoint)
287	mpoint = "/change_me";
288
289    ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
290    strncpy(ret->mountpoint, mpoint, FILENAME_MAX);
291    strcpy(ret->newfs_cmd, "newfs");
292    ret->newfs = newfs;
293    if (!size)
294	    return ret;
295    for (target = size; target; target--) {
296	for (divisor = 4096 ; divisor > 1023; divisor--) {
297	    if (!(target % divisor)) {
298		sprintf(ret->newfs_cmd + strlen(ret->newfs_cmd), " -u %ld",divisor);
299		return ret;
300	    }
301	}
302    }
303    return ret;
304}
305
306/* Get the mountpoint for a partition and save it away */
307static PartInfo *
308get_mountpoint(struct chunk *old)
309{
310    char *val;
311    PartInfo *tmp;
312
313    if (old && old->private)
314	tmp = old->private;
315    else
316	tmp = NULL;
317    val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
318    if (!val || !*val) {
319	if (!old)
320	    return NULL;
321	else {
322	    free(old->private);
323	    old->private = NULL;
324	}
325	return NULL;
326    }
327
328    /* Is it just the same value? */
329    if (tmp && !strcmp(tmp->mountpoint, val))
330	return NULL;
331
332    /* Did we use it already? */
333    if (check_conflict(val)) {
334	msgConfirm("You already have a mount point for %s assigned!", val);
335	return NULL;
336    }
337
338    /* Is it bogus? */
339    if (*val != '/') {
340	msgConfirm("Mount point must start with a / character");
341	return NULL;
342    }
343
344    /* Is it going to be mounted on root? */
345    if (!strcmp(val, "/")) {
346	if (old)
347	    old->flags |= CHUNK_IS_ROOT;
348    }
349    else if (old)
350	old->flags &= ~CHUNK_IS_ROOT;
351
352    safe_free(tmp);
353    tmp = new_part(val, TRUE, 0);
354    if (old) {
355	old->private = tmp;
356	old->private_free = safe_free;
357    }
358    return tmp;
359}
360
361/* Get the type of the new partiton */
362static PartType
363get_partition_type(void)
364{
365    char selection[20];
366    int i;
367
368    static unsigned char *fs_types[] = {
369	"FS",
370	"A file system",
371	"Swap",
372	"A swap partition.",
373    };
374    i = dialog_menu("Please choose a partition type",
375		    "If you want to use this partition for swap space, select Swap.\n"
376		    "If you want to put a filesystem on it, choose FS.",
377		    -1, -1, 2, 2, fs_types, selection, NULL, NULL);
378    if (!i) {
379	if (!strcmp(selection, "FS"))
380	    return PART_FILESYSTEM;
381	else if (!strcmp(selection, "Swap"))
382	    return PART_SWAP;
383    }
384    return PART_NONE;
385}
386
387/* If the user wants a special newfs command for this, set it */
388static void
389getNewfsCmd(PartInfo *p)
390{
391    char *val;
392
393    val = msgGetInput(p->newfs_cmd,
394		      "Please enter the newfs command and options you'd like to use in\n"
395		      "creating this file system.");
396    if (val)
397	strncpy(p->newfs_cmd, val, NEWFS_CMD_MAX);
398}
399
400static int
401scriptLabel(char *str)
402{
403    char *cp;
404    PartType type;
405    PartInfo *p;
406    u_long flags = 0;
407    int i, status;
408    Device **devs;
409    Disk *d;
410
411    status = RET_SUCCESS;
412    cp = variable_get(VAR_DISK);
413    if (!cp) {
414	dialog_clear();
415	msgConfirm("scriptLabel:  No disk selected - can't label automatically.");
416	return RET_FAIL;
417    }
418
419    devs = deviceFind(cp, DEVICE_TYPE_DISK);
420    if (!devs) {
421	dialog_clear();
422	msgConfirm("scriptLabel: No disk device %s found!", cp);
423	return RET_FAIL;
424    }
425    d = devs[0]->private;
426
427    record_label_chunks(devs);
428    for (i = 0; label_chunk_info[i].c; i++) {
429	Chunk *c1 = label_chunk_info[i].c;
430
431	if (label_chunk_info[i].type == PART_SLICE) {
432	    if ((cp = variable_get(c1->name)) != NULL) {
433		int sz;
434		char typ[10], mpoint[50];
435
436		if (sscanf(cp, "%s %d %s", typ, &sz, mpoint) != 3) {
437		    dialog_clear();
438		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s",  c1->name, cp);
439		    status = RET_FAIL;
440		    continue;
441		}
442		else {
443		    Chunk *tmp;
444
445		    if (!strcmp(typ, "swap")) {
446			type = PART_SWAP;
447			strcpy(mpoint, "<swap>");
448		    }
449		    else {
450			type = PART_FILESYSTEM;
451			if (!strcmp(mpoint, "/"))
452			    flags |= CHUNK_IS_ROOT;
453		    }
454		    if (!sz)
455			sz = space_free(c1);
456		    if (sz > space_free(c1)) {
457			dialog_clear();
458			msgConfirm("Not enough free space to create partition: %s", mpoint);
459			status = RET_FAIL;
460			continue;
461		    }
462		    if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
463						  (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
464			dialog_clear();
465			msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
466			status = RET_FAIL;
467			break;
468		    }
469		    else {
470			tmp->private = new_part(mpoint, TRUE, sz);
471			tmp->private_free = safe_free;
472			status = RET_SUCCESS;
473		    }
474		}
475	    }
476	}
477	else {
478	    /* Must be something we can set a mountpoint */
479	    cp = variable_get(c1->name);
480	    if (cp) {
481		char mpoint[50], nwfs[8];
482		Boolean newfs = FALSE;
483
484		nwfs[0] = '\0';
485		if (sscanf(cp, "%s %s", mpoint, nwfs) != 2) {
486		    dialog_clear();
487		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
488		    status = RET_FAIL;
489		    continue;
490		}
491		newfs = toupper(nwfs[0]) == 'Y' ? TRUE : FALSE;
492		if (c1->private) {
493		    p = c1->private;
494		    p->newfs = newfs;
495		    strcpy(p->mountpoint, mpoint);
496		}
497		else {
498		    c1->private = new_part(mpoint, newfs, 0);
499		    c1->private_free = safe_free;
500		}
501		if (!strcmp(mpoint, "/"))
502		    c1->flags |= CHUNK_IS_ROOT;
503		else
504		    c1->flags &= ~CHUNK_IS_ROOT;
505	    }
506	}
507    }
508    if (status == RET_SUCCESS)
509	variable_set2(DISK_LABELLED, "yes");
510    return status;
511}
512
513#define MAX_MOUNT_NAME	12
514
515#define PART_PART_COL	0
516#define PART_MOUNT_COL	8
517#define PART_SIZE_COL	(PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
518#define PART_NEWFS_COL	(PART_SIZE_COL + 7)
519#define PART_OFF	38
520
521/* How many mounted partitions to display in column before going to next */
522#define CHUNK_COLUMN_MAX	5
523
524/* stick this all up on the screen */
525static void
526print_label_chunks(void)
527{
528    int i, j, srow, prow, pcol;
529    int sz;
530
531    attrset(A_REVERSE);
532    mvaddstr(0, 25, "FreeBSD Disklabel Editor");
533    clrtobot();
534    attrset(A_NORMAL);
535
536    for (i = 0; i < 2; i++) {
537	mvaddstr(CHUNK_PART_START_ROW - 2, PART_PART_COL + (i * PART_OFF), "Part");
538	mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF), "----");
539
540	mvaddstr(CHUNK_PART_START_ROW - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
541	mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
542
543	mvaddstr(CHUNK_PART_START_ROW - 2, PART_SIZE_COL + (i * PART_OFF) + 2, "Size");
544	mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF) + 2, "----");
545
546	mvaddstr(CHUNK_PART_START_ROW - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
547	mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
548    }
549    srow = CHUNK_SLICE_START_ROW;
550    prow = CHUNK_PART_START_ROW;
551    pcol = 0;
552
553    for (i = 0; label_chunk_info[i].c; i++) {
554	if (i == here)
555	    attrset(A_REVERSE);
556	/* Is it a slice entry displayed at the top? */
557	if (label_chunk_info[i].type == PART_SLICE) {
558	    sz = space_free(label_chunk_info[i].c);
559	    mvprintw(srow++, 0, "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
560		     label_chunk_info[i].c->disk->name, label_chunk_info[i].c->name, sz, (sz / ONE_MEG));
561	}
562	/* Otherwise it's a DOS, swap or filesystem entry, at the bottom */
563	else {
564	    char onestr[PART_OFF], num[10], *mountpoint, *newfs;
565
566	    /*
567	     * We copy this into a blank-padded string so that it looks like
568	     * a solid bar in reverse-video
569	     */
570	    memset(onestr, ' ', PART_OFF - 1);
571	    onestr[PART_OFF - 1] = '\0';
572	    /* Go for two columns */
573	    if (prow == (CHUNK_PART_START_ROW + CHUNK_COLUMN_MAX)) {
574		pcol = PART_OFF;
575		prow = CHUNK_PART_START_ROW;
576	    }
577	    memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
578	    /* If it's a filesystem, display the mountpoint */
579	    if (label_chunk_info[i].c->private
580		&& (label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT))
581	        mountpoint = ((PartInfo *)label_chunk_info[i].c->private)->mountpoint;
582	    else
583	        mountpoint = "<none>";
584
585	    /* Now display the newfs field */
586	    if (label_chunk_info[i].type == PART_FAT)
587	        newfs = "DOS";
588	    else if (label_chunk_info[i].c->private && label_chunk_info[i].type == PART_FILESYSTEM)
589		newfs = ((PartInfo *)label_chunk_info[i].c->private)->newfs ? "UFS Y" : "UFS N";
590	    else if (label_chunk_info[i].type == PART_SWAP)
591		newfs = "SWAP";
592	    else
593		newfs = "*";
594	    for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
595		onestr[PART_MOUNT_COL + j] = mountpoint[j];
596	    snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? label_chunk_info[i].c->size / ONE_MEG : 0);
597	    memcpy(onestr + PART_SIZE_COL, num, strlen(num));
598	    memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
599	    onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
600	    mvaddstr(prow, pcol, onestr);
601	    ++prow;
602	}
603	if (i == here)
604	    attrset(A_NORMAL);
605    }
606}
607
608static void
609print_command_summary()
610{
611    mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
612    mvprintw(18, 0, "C = Create      D = Delete         M = Mount   W = Write");
613    mvprintw(19, 0, "N = Newfs Opts  T = Newfs Toggle   U = Undo    Q = Finish");
614    mvprintw(20, 0, "A = Auto Defaults for all!");
615    mvprintw(22, 0, "The default target will be displayed in ");
616
617    attrset(A_REVERSE);
618    addstr("reverse");
619    attrset(A_NORMAL);
620    addstr(" video.");
621    mvprintw(23, 0, "Use F1 or ? to get more help, arrow keys to move.");
622    move(0, 0);
623}
624
625static int
626diskLabel(char *str)
627{
628    int sz, key = 0;
629    Boolean labeling;
630    char *msg = NULL;
631    PartInfo *p, *oldp;
632    PartType type;
633    Device **devs;
634
635    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
636    if (!devs) {
637	dialog_clear();
638	msgConfirm("No disks found!");
639	return RET_FAIL;
640    }
641
642    labeling = TRUE;
643    keypad(stdscr, TRUE);
644    record_label_chunks(devs);
645
646    dialog_clear(); clear();
647    while (labeling) {
648	clear();
649	print_label_chunks();
650	print_command_summary();
651	if (msg) {
652	    attrset(A_REVERSE); mvprintw(23, 0, msg); attrset(A_NORMAL);
653	    clrtoeol();
654	    beep();
655	    msg = NULL;
656	}
657	refresh();
658	key = toupper(getch());
659	switch (key) {
660	    int i, cnt;
661
662	case '\014':	/* ^L */
663	    continue;
664
665	case KEY_UP:
666	case '-':
667	    if (here != 0)
668		--here;
669	    else
670		while (label_chunk_info[here + 1].c)
671		    ++here;
672	    break;
673
674	case KEY_DOWN:
675	case '+':
676	case '\r':
677	case '\n':
678	    if (label_chunk_info[here + 1].c)
679		++here;
680	    else
681		here = 0;
682	    break;
683
684	case KEY_HOME:
685	    here = 0;
686	    break;
687
688	case KEY_END:
689	    while (label_chunk_info[here + 1].c)
690		++here;
691	    break;
692
693	case KEY_F(1):
694	case '?':
695	    systemDisplayHelp("partition");
696	    break;
697
698	case 'A':
699	    if (label_chunk_info[here].type != PART_SLICE) {
700		msg = "You can only do this in a master partition (see top of screen)";
701		break;
702	    }
703
704	    cnt = i = 0;
705	    while (label_chunk_info[i].c)
706		if (label_chunk_info[i++].type != PART_SLICE)
707		    cnt++;
708	    if (cnt == (CHUNK_COLUMN_MAX * 2) + 4) {
709		dialog_clear();
710		msgConfirm("Sorry, I can't fit any more partitions on the screen!  You can get around\n"
711			   "this limitation by partitioning your disks individually rather than all\n"
712			   "at once.  This will be fixed just as soon as we get a scrolling partition\n"
713			   "box written.  Sorry for the inconvenience!");
714		break;
715	    }
716
717	    sz = space_free(label_chunk_info[here].c);
718	    if (sz <= FS_MIN_SIZE) {
719		msg = "Not enough space to create an additional FreeBSD partition";
720		break;
721	    }
722	{
723	    struct chunk *tmp;
724	    int mib[2];
725	    int physmem;
726	    size_t size, swsize;
727	    char *cp;
728
729	    cp = variable_get(VAR_ROOT_SIZE);
730	    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
731				    label_chunk_info[here].c,
732				    (cp ? atoi(cp) : 32) * ONE_MEG, part, FS_BSDFFS,
733				    CHUNK_IS_ROOT);
734
735	    if (!tmp) {
736		dialog_clear();
737		msgConfirm("Unable to create the root partition. Too big?");
738		break;
739	    }
740	    tmp->private = new_part("/", TRUE, tmp->size);
741	    tmp->private_free = safe_free;
742	    record_label_chunks(devs);
743
744	    cp = variable_get(VAR_SWAP_SIZE);
745	    if (cp)
746		swsize = atoi(cp) * ONE_MEG;
747	    else {
748		mib[0] = CTL_HW;
749		mib[1] = HW_PHYSMEM;
750		size = sizeof physmem;
751		sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
752		swsize = 16 * ONE_MEG + (physmem * 2 / 512);
753	    }
754	    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
755				    label_chunk_info[here].c,
756				    swsize,
757				    part, FS_SWAP, 0);
758	    if (!tmp) {
759		dialog_clear();
760		msgConfirm("Unable to create the swap partition. Too big?");
761		break;
762	    }
763
764	    tmp->private = 0;
765	    tmp->private_free = safe_free;
766	    record_label_chunks(devs);
767
768	    cp = variable_get(VAR_VAR_SIZE);
769	    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
770				    label_chunk_info[here].c,
771				    (cp ? atoi(cp) : VAR_MIN_SIZE) * ONE_MEG, part, FS_BSDFFS, 0);
772	    if (!tmp) {
773		dialog_clear();
774		msgConfirm("Less than %dMB free for /var - you will need to\n"
775			   "partition your disk manually with a custom install!", (cp ? atoi(cp) : VAR_MIN_SIZE));
776		break;
777	    }
778	    tmp->private = new_part("/var", TRUE, tmp->size);
779	    tmp->private_free = safe_free;
780	    record_label_chunks(devs);
781
782	    cp = variable_get(VAR_USR_SIZE);
783	    if (cp)
784		sz = atoi(cp) * ONE_MEG;
785	    else
786		sz = space_free(label_chunk_info[here].c);
787	    if (!sz || sz < (USR_MIN_SIZE * ONE_MEG)) {
788		dialog_clear();
789		msgConfirm("Less than %dMB free for /usr - you will need to\n"
790			   "partition your disk manually with a custom install!", USR_MIN_SIZE);
791		break;
792	    }
793
794	    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
795				    label_chunk_info[here].c,
796				    sz, part, FS_BSDFFS, 0);
797	    if (!tmp) {
798		dialog_clear();
799		msgConfirm("Unable to create the /usr partition.  Not enough space?\n"
800			   "You will need to partition your disk manually with a custom install!");
801		break;
802	    }
803	    /* At this point, we're reasonably "labelled" */
804	    variable_set2(DISK_LABELLED, "yes");
805	    tmp->private = new_part("/usr", TRUE, tmp->size);
806	    tmp->private_free = safe_free;
807	    record_label_chunks(devs);
808	}
809	    break;
810
811	case 'C':
812	    if (label_chunk_info[here].type != PART_SLICE) {
813		msg = "You can only do this in a master partition (see top of screen)";
814		break;
815	    }
816	    else {
817		int i, cnt;
818
819		cnt = i = 0;
820		while (label_chunk_info[i].c)
821		    if (label_chunk_info[i++].type != PART_SLICE)
822			cnt++;
823		if (cnt == (CHUNK_COLUMN_MAX * 2)) {
824		    dialog_clear();
825		    msgConfirm("Sorry, I can't fit any more partitions on the screen!  You can get around\n"
826			       "this limitation by partitioning your disks individually rather than all\n"
827			       "at once.  This will be fixed just as soon as we get a scrolling partition\n"
828			       "box written.  Sorry for the inconvenience!");
829		    break;
830		}
831	    }
832	    sz = space_free(label_chunk_info[here].c);
833	    if (sz <= FS_MIN_SIZE) {
834		msg = "Not enough space to create an additional FreeBSD partition";
835		break;
836	    }
837	    {
838		char *val, *cp;
839		int size;
840		struct chunk *tmp;
841		char osize[80];
842		u_long flags = 0;
843
844		sprintf(osize, "%d", sz);
845		val = msgGetInput(osize, "Please specify the size for new FreeBSD partition in blocks, or\n"
846				  "append a trailing `M' for megabytes (e.g. 20M) or `C' for cylinders.\n\n"
847				  "Space free is %d blocks (%dMB)", sz, sz / ONE_MEG);
848		if (!val || (size = strtol(val, &cp, 0)) <= 0)
849		    break;
850
851		if (*cp) {
852		    if (toupper(*cp) == 'M')
853			size *= ONE_MEG;
854		    else if (toupper(*cp) == 'C')
855			size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
856		}
857		if (size <= FS_MIN_SIZE) {
858		    msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
859		    break;
860		}
861		type = get_partition_type();
862		if (type == PART_NONE)
863		    break;
864
865		if (type == PART_FILESYSTEM) {
866		    if ((p = get_mountpoint(NULL)) == NULL)
867			break;
868		    else if (!strcmp(p->mountpoint, "/"))
869			flags |= CHUNK_IS_ROOT;
870		    else
871			flags &= ~CHUNK_IS_ROOT;
872		} else
873		    p = NULL;
874
875		if ((flags & CHUNK_IS_ROOT)) {
876		    if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) {
877			msgConfirm("This region cannot be used for your root partition as the\n"
878				   "FreeBSD boot code cannot deal with a root partition created\n"
879				   "in that location.  Please choose another location or smaller\n"
880				   "size for your root partition and try again!");
881			break;
882		    }
883		    if (size < (ROOT_MIN_SIZE * ONE_MEG)) {
884			msgConfirm("Warning: This is smaller than the recommended size for a\n"
885				   "root partition.  For a variety of reasons, root\n"
886				   "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
887		    }
888		}
889		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
890					label_chunk_info[here].c,
891					size, part,
892					(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
893					flags);
894		if (!tmp) {
895		    msgConfirm("Unable to create the partition. Too big?");
896		    break;
897		}
898		if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) {
899		    msgConfirm("This region cannot be used for your root partition as it starts\n"
900			       "or extends past the 1024'th cylinder mark and is thus a\n"
901			       "poor location to boot from.  Please choose another\n"
902			       "location (or smaller size) for your root partition and try again!");
903		    Delete_Chunk(label_chunk_info[here].c->disk, tmp);
904		    break;
905		}
906		if (type != PART_SWAP) {
907		    /* This is needed to tell the newfs -u about the size */
908		    tmp->private = new_part(p->mountpoint, p->newfs, tmp->size);
909		    tmp->private_free = safe_free;
910		    safe_free(p);
911		} else {
912		    tmp->private = p;
913		}
914		tmp->private_free = safe_free;
915		variable_set2(DISK_LABELLED, "yes");
916		record_label_chunks(devs);
917	    }
918	    break;
919
920	case 'D':	/* delete */
921	    if (label_chunk_info[here].type == PART_SLICE) {
922		msg = MSG_NOT_APPLICABLE;
923		break;
924	    }
925	    else if (label_chunk_info[here].type == PART_FAT) {
926		msg = "Use the Disk Partition Editor to delete DOS partitions";
927		break;
928	    }
929	    Delete_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c);
930	    variable_set2(DISK_LABELLED, "yes");
931	    record_label_chunks(devs);
932	    break;
933
934	case 'M':	/* mount */
935	    switch(label_chunk_info[here].type) {
936	    case PART_SLICE:
937		msg = MSG_NOT_APPLICABLE;
938		break;
939
940	    case PART_SWAP:
941		msg = "You don't need to specify a mountpoint for a swap partition.";
942		break;
943
944	    case PART_FAT:
945	    case PART_FILESYSTEM:
946		oldp = label_chunk_info[here].c->private;
947		p = get_mountpoint(label_chunk_info[here].c);
948		if (p) {
949		    if (!oldp)
950		    	p->newfs = FALSE;
951		    if (label_chunk_info[here].type == PART_FAT
952			&& (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr")
953			    || !strcmp(p->mountpoint, "/var"))) {
954			msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
955			strcpy(p->mountpoint, "/bogus");
956		    }
957		}
958		variable_set2(DISK_LABELLED, "yes");
959		record_label_chunks(devs);
960		break;
961
962	    default:
963		msgFatal("Bogus partition under cursor???");
964		break;
965	    }
966	    break;
967
968	case 'N':	/* Set newfs options */
969	    if (label_chunk_info[here].c->private &&
970		((PartInfo *)label_chunk_info[here].c->private)->newfs)
971		getNewfsCmd(label_chunk_info[here].c->private);
972	    else
973		msg = MSG_NOT_APPLICABLE;
974	    break;
975
976	case 'T':	/* Toggle newfs state */
977	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
978		    PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private);
979		    label_chunk_info[here].c->private = new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
980		    safe_free(pi);
981		    label_chunk_info[here].c->private_free = safe_free;
982		    variable_set2(DISK_LABELLED, "yes");
983		}
984	    else
985		msg = MSG_NOT_APPLICABLE;
986	    break;
987
988	case 'U':
989	    clear();
990	    if (msgYesNo("Are you SURE you want to Undo everything?"))
991		break;
992	    variable_unset(DISK_PARTITIONED);
993	    for (i = 0; devs[i]; i++) {
994		extern void diskPartition(Device *dev, Disk *d);
995		Disk *d;
996
997		if (!devs[i]->enabled)
998		    continue;
999		else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1000		    Free_Disk(devs[i]->private);
1001		    devs[i]->private = d;
1002		    diskPartition(devs[i], d);
1003		}
1004	    }
1005	    variable_unset(DISK_LABELLED);
1006	    record_label_chunks(devs);
1007	    break;
1008
1009	case 'W':
1010	    if (!msgYesNo("Are you SURE that you wish to make and mount all filesystems\n"
1011			  "at this time?  You also have the option of doing it later in\n"
1012			  "one final 'commit' operation, and if you're at all unsure as\n"
1013			  "to which option to chose, then PLEASE CHOSE NO!  This option\n"
1014			  "is DANGEROUS if you're not EXACTLY sure what you are doing!")) {
1015		variable_set2(DISK_LABELLED, "yes");
1016		clear();
1017		diskLabelCommit(NULL);
1018	    }
1019	    break;
1020
1021	case '|':
1022	    if (!msgYesNo("Are you sure you want to go into Wizard mode?\n\n"
1023			  "This is an entirely undocumented feature which you are not\n"
1024			  "expected to understand!")) {
1025		int i;
1026		Device **devs;
1027
1028		dialog_clear();
1029		end_dialog();
1030		DialogActive = FALSE;
1031		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1032		if (!devs) {
1033		    dialog_clear();
1034		    msgConfirm("Can't find any disk devices!");
1035		    break;
1036		}
1037		for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1038		    if (devs[i]->enabled)
1039		    	slice_wizard(((Disk *)devs[i]->private));
1040		}
1041		variable_set2(DISK_LABELLED, "yes");
1042		DialogActive = TRUE;
1043		dialog_clear();
1044		record_label_chunks(devs);
1045	    }
1046	    else
1047		msg = "A most prudent choice!";
1048	    break;
1049
1050	case 'Q':
1051	    labeling = FALSE;
1052	    break;
1053
1054	default:
1055	    beep();
1056	    msg = "Type F1 or ? for help";
1057	    break;
1058	}
1059    }
1060    dialog_clear();
1061    return RET_SUCCESS;
1062}
1063