label.c revision 12661
11541Srgrimes/*
21541Srgrimes * The new sysinstall program.
31541Srgrimes *
41541Srgrimes * This is probably the last program in the `sysinstall' line - the next
51541Srgrimes * generation being essentially a complete rewrite.
61541Srgrimes *
71541Srgrimes * $Id: label.c,v 1.33 1995/09/18 16:52:28 peter Exp $
81541Srgrimes *
91541Srgrimes * Copyright (c) 1995
101541Srgrimes *	Jordan Hubbard.  All rights reserved.
111541Srgrimes *
121541Srgrimes * Redistribution and use in source and binary forms, with or without
131541Srgrimes * modification, are permitted provided that the following conditions
141541Srgrimes * are met:
151541Srgrimes * 1. Redistributions of source code must retain the above copyright
161541Srgrimes *    notice, this list of conditions and the following disclaimer,
171541Srgrimes *    verbatim and that no modifications are made prior to this
181541Srgrimes *    point in the file.
191541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
201541Srgrimes *    notice, this list of conditions and the following disclaimer in the
211541Srgrimes *    documentation and/or other materials provided with the distribution.
221541Srgrimes * 3. All advertising materials mentioning features or use of this software
231541Srgrimes *    must display the following acknowledgement:
241541Srgrimes *	This product includes software developed by Jordan Hubbard
251541Srgrimes *	for the FreeBSD Project.
261541Srgrimes * 4. The name of Jordan Hubbard or the FreeBSD project may not be used to
271541Srgrimes *    endorse or promote products derived from this software without specific
281541Srgrimes *    prior written permission.
291541Srgrimes *
301541Srgrimes * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
311541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
321541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
331541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
3412873Sbde * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
351541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
361541Srgrimes * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
372165Spaul * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
382811Sbde * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
392165Spaul * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
401541Srgrimes * SUCH DAMAGE.
411541Srgrimes *
421541Srgrimes */
431541Srgrimes
441541Srgrimes#include "sysinstall.h"
451541Srgrimes#include <ctype.h>
461541Srgrimes#include <sys/disklabel.h>
471541Srgrimes#include <sys/param.h>
481541Srgrimes#include <sys/sysctl.h>
491541Srgrimes
501541Srgrimes/*
511541Srgrimes * Everything to do with editing the contents of disk labels.
521541Srgrimes */
531541Srgrimes
541541Srgrimes/* A nice message we use a lot in the disklabel editor */
551541Srgrimes#define MSG_NOT_APPLICABLE	"That option is not applicable here"
561541Srgrimes
571541Srgrimes/* Where to start printing the freebsd slices */
581541Srgrimes#define CHUNK_SLICE_START_ROW		2
591541Srgrimes#define CHUNK_PART_START_ROW		11
601541Srgrimes
617945Sjulian/* The smallest filesystem we're willing to create */
621541Srgrimes#define FS_MIN_SIZE			ONE_MEG
631541Srgrimes
641541Srgrimes/* The smallest root filesystem we're willing to create */
651541Srgrimes#define ROOT_MIN_SIZE			20
661541Srgrimes
671541Srgrimes/* The smallest swap partition we want to create by default */
681541Srgrimes#define SWAP_MIN_SIZE			16
691541Srgrimes
7012158Sbde/* The smallest /usr partition we're willing to create by default */
7112158Sbde#define USR_MIN_SIZE			80
721541Srgrimes
731541Srgrimes/* The smallest /var partition we're willing to create by default */
741541Srgrimes#define VAR_MIN_SIZE			30
751541Srgrimes
761541Srgrimes/* All the chunks currently displayed on the screen */
771541Srgrimesstatic struct {
781541Srgrimes    struct chunk *c;
791541Srgrimes    PartType type;
8012158Sbde} label_chunk_info[MAX_CHUNKS + 1];
811541Srgrimesstatic int here;
821541Srgrimes
831541Srgrimesstatic int diskLabel(char *str);
841541Srgrimesstatic int scriptLabel(char *str);
851541Srgrimes
861541Srgrimesstatic int
871541SrgrimeslabelHook(char *str)
881541Srgrimes{
891541Srgrimes    Device **devs = NULL;
901541Srgrimes
911541Srgrimes    /* Clip garbage off the ends */
921541Srgrimes    string_prune(str);
931541Srgrimes    str = string_skipwhite(str);
941541Srgrimes    /* Try and open all the disks */
951541Srgrimes    while (str) {
961541Srgrimes	char *cp;
971541Srgrimes
981541Srgrimes	cp = index(str, '\n');
991541Srgrimes	if (cp)
1009356Sdg	   *cp++ = 0;
1011541Srgrimes	if (!*str) {
1021541Srgrimes	    beep();
1031541Srgrimes	    return 0;
1041541Srgrimes	}
1051541Srgrimes	devs = deviceFind(str, DEVICE_TYPE_DISK);
1061541Srgrimes	if (!devs) {
1071541Srgrimes	    dialog_clear();
1081541Srgrimes	    msgConfirm("Unable to find disk %s!", str);
1091541Srgrimes	    return 0;
1101541Srgrimes	}
1111541Srgrimes	else if (devs[1]) {
11212873Sbde	    dialog_clear();
11312873Sbde	    msgConfirm("Bizarre multiple match for %s!", str);
11412873Sbde	}
11512873Sbde	devs[0]->enabled = TRUE;
11612873Sbde	str = cp;
11712873Sbde    }
11812873Sbde    return devs ? 1 : 0;
11912873Sbde}
12012873Sbde
12112873Sbdeint
12212873SbdediskLabelEditor(char *str)
12312873Sbde{
12412873Sbde    Device **devs;
1251541Srgrimes    DMenu *menu;
1261541Srgrimes    int i, cnt;
1271541Srgrimes    char *cp;
1281541Srgrimes
1291541Srgrimes    cp = variable_get(VAR_DISK);
1301541Srgrimes    devs = deviceFind(cp, DEVICE_TYPE_DISK);
1311541Srgrimes    cnt = deviceCount(devs);
1321541Srgrimes    if (!cnt) {
1331541Srgrimes	dialog_clear();
1341541Srgrimes	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
1351541Srgrimes		   "properly probed at boot time.  See the Hardware Guide on the\n"
1361541Srgrimes		   "Documentation menu for clues on diagnosing this type of problem.");
1371541Srgrimes	return RET_FAIL;
1381541Srgrimes    }
1391541Srgrimes    else if (cnt == 1 || variable_get(DISK_SELECTED)) {
1401541Srgrimes	devs[0]->enabled = TRUE;
1411541Srgrimes	if (str && !strcmp(str, "script"))
1421541Srgrimes	    i = scriptLabel(str);
1431541Srgrimes	else
1441541Srgrimes	    i = diskLabel(str);
1451541Srgrimes    }
1461541Srgrimes    else {
1471541Srgrimes	menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, labelHook);
1481541Srgrimes	if (!menu) {
1491541Srgrimes	    dialog_clear();
1501541Srgrimes	    msgConfirm("No devices suitable for installation found!\n\n"
1511541Srgrimes		       "Please verify that your disk controller (and attached drives)\n"
1521541Srgrimes		       "were detected properly.  This can be done by pressing the\n"
1531541Srgrimes		       "[Scroll Lock] key and using the Arrow keys to move back to\n"
1541541Srgrimes		       "the boot messages.  Press [Scroll Lock] again to return.");
1551541Srgrimes	    i = RET_FAIL;
1561541Srgrimes	}
1571541Srgrimes	else {
1581541Srgrimes	    if (!dmenuOpenSimple(menu))
1591541Srgrimes		i = RET_FAIL;
1601541Srgrimes	    else
1611541Srgrimes		i = diskLabel(str);
1621541Srgrimes	    free(menu);
1631541Srgrimes	}
1641541Srgrimes    }
16512873Sbde    return i;
1661541Srgrimes}
1671541Srgrimes
1681541Srgrimesint
1691541SrgrimesdiskLabelCommit(char *str)
1701541Srgrimes{
1711541Srgrimes    char *cp;
1721541Srgrimes    int i;
1731541Srgrimes
1741541Srgrimes    /* Already done? */
1751541Srgrimes    if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes"))
1761541Srgrimes	i = RET_SUCCESS;
1771541Srgrimes    else if (!cp) {
1781541Srgrimes	dialog_clear();
1791541Srgrimes	msgConfirm("You must assign disk labels before this option can be used.");
1801541Srgrimes	i = RET_FAIL;
1811541Srgrimes    }
1821541Srgrimes    /* The routine will guard against redundant writes, just as this one does */
1831541Srgrimes    else if (diskPartitionWrite(str) != RET_SUCCESS)
1841541Srgrimes	i = RET_FAIL;
1851541Srgrimes    else if (installFilesystems(str) != RET_SUCCESS)
1861541Srgrimes	i = RET_FAIL;
1871541Srgrimes    else {
1881541Srgrimes	msgInfo("All filesystem information written successfully.");
1891541Srgrimes	variable_set2(DISK_LABELLED, "written");
1901541Srgrimes	i = RET_SUCCESS;
1911541Srgrimes    }
1921541Srgrimes    return i;
1931541Srgrimes}
1941541Srgrimes
1951541Srgrimes/* See if we're already using a desired partition name */
1961541Srgrimesstatic Boolean
1971541Srgrimescheck_conflict(char *name)
1981541Srgrimes{
1991541Srgrimes    int i;
2001541Srgrimes
2011541Srgrimes    for (i = 0; label_chunk_info[i].c; i++)
2021541Srgrimes	if (label_chunk_info[i].type == PART_FILESYSTEM && label_chunk_info[i].c->private
2031541Srgrimes	    && !strcmp(((PartInfo *)label_chunk_info[i].c->private)->mountpoint, name))
2041541Srgrimes	    return TRUE;
2051541Srgrimes    return FALSE;
2061541Srgrimes}
2071541Srgrimes
2081541Srgrimes/* How much space is in this FreeBSD slice? */
2091541Srgrimesstatic int
2101541Srgrimesspace_free(struct chunk *c)
2111541Srgrimes{
2121541Srgrimes    struct chunk *c1;
2131541Srgrimes    int sz = c->size;
2141541Srgrimes
2151541Srgrimes    for (c1 = c->part; c1; c1 = c1->next) {
2161541Srgrimes	if (c1->type != unused)
2171541Srgrimes	    sz -= c1->size;
2181541Srgrimes    }
2191541Srgrimes    if (sz < 0)
2202946Swollman	msgFatal("Partitions are larger than actual chunk??");
22112873Sbde    return sz;
2222946Swollman}
22312873Sbde
2242946Swollman/* Snapshot the current situation into the displayed chunks structure */
2252946Swollmanstatic void
2261541Srgrimesrecord_label_chunks(Device **devs)
2271541Srgrimes{
2281541Srgrimes    int i, j, p;
2291541Srgrimes    struct chunk *c1, *c2;
2301541Srgrimes    Disk *d;
2317090Sbde
2321541Srgrimes    j = p = 0;
2331541Srgrimes    /* First buzz through and pick up the FreeBSD slices */
2341541Srgrimes    for (i = 0; devs[i]; i++) {
2351541Srgrimes	if (!devs[i]->enabled)
2361541Srgrimes	    continue;
2371541Srgrimes	d = (Disk *)devs[i]->private;
2381541Srgrimes	if (!d->chunks)
2391541Srgrimes	    msgFatal("No chunk list found for %s!", d->name);
2402997Swollman
2412997Swollman	/* Put the slice entries first */
2422997Swollman	for (c1 = d->chunks->part; c1; c1 = c1->next) {
2432997Swollman	    if (c1->type == freebsd) {
2441541Srgrimes		label_chunk_info[j].type = PART_SLICE;
2457461Sdg		label_chunk_info[j].c = c1;
2461541Srgrimes		++j;
2471541Srgrimes	    }
2481541Srgrimes	}
24912873Sbde    }
25012873Sbde
2517461Sdg    /* Now run through again and get the FreeBSD partition entries */
2527461Sdg    for (i = 0; devs[i]; i++) {
25312873Sbde	if (!devs[i]->enabled)
2542997Swollman	    continue;
25512873Sbde	d = (Disk *)devs[i]->private;
2562997Swollman	/* Then buzz through and pick up the partitions */
2571541Srgrimes	for (c1 = d->chunks->part; c1; c1 = c1->next) {
2581541Srgrimes	    if (c1->type == freebsd) {
2591541Srgrimes		for (c2 = c1->part; c2; c2 = c2->next) {
2601541Srgrimes		    if (c2->type == part) {
2611541Srgrimes			if (c2->subtype == FS_SWAP)
2621541Srgrimes			    label_chunk_info[j].type = PART_SWAP;
2631541Srgrimes			else
2641541Srgrimes			    label_chunk_info[j].type = PART_FILESYSTEM;
2651541Srgrimes			label_chunk_info[j].c = c2;
2661541Srgrimes			++j;
2671541Srgrimes		    }
2681541Srgrimes		}
2691541Srgrimes	    }
2701541Srgrimes	    else if (c1->type == fat) {
2711541Srgrimes		label_chunk_info[j].type = PART_FAT;
2721541Srgrimes		label_chunk_info[j].c = c1;
2731541Srgrimes		++j;
2741541Srgrimes	    }
2751541Srgrimes	}
2761541Srgrimes    }
2771541Srgrimes    label_chunk_info[j].c = NULL;
2781541Srgrimes    if (here >= j)
2791541Srgrimes	here = j  ? j - 1 : 0;
2801541Srgrimes}
2811541Srgrimes
2821541Srgrimes/* A new partition entry */
2831541Srgrimesstatic PartInfo *
2841541Srgrimesnew_part(char *mpoint, Boolean newfs, u_long size)
2851541Srgrimes{
2861541Srgrimes    PartInfo *ret;
2871541Srgrimes    u_long target, divisor;
2881541Srgrimes
2891541Srgrimes    if (!mpoint)
2901541Srgrimes	mpoint = "/change_me";
2911541Srgrimes
2921541Srgrimes    ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
2931541Srgrimes    strncpy(ret->mountpoint, mpoint, FILENAME_MAX);
2941541Srgrimes    strcpy(ret->newfs_cmd, "newfs -b 8192 -f 2048");
2951541Srgrimes    ret->newfs = newfs;
2961541Srgrimes    if (!size)
2971541Srgrimes	    return ret;
2981541Srgrimes    for (target = size; target; target--) {
2991541Srgrimes	for (divisor = 4096 ; divisor > 1023; divisor--) {
3001541Srgrimes	    if (!(target % divisor)) {
3011541Srgrimes		sprintf(ret->newfs_cmd + strlen(ret->newfs_cmd), " -u %ld",divisor);
3021541Srgrimes		return ret;
3031541Srgrimes	    }
3041541Srgrimes	}
3051541Srgrimes    }
3061541Srgrimes    return ret;
3071541Srgrimes}
3081541Srgrimes
3091541Srgrimes/* Get the mountpoint for a partition and save it away */
3101541Srgrimesstatic PartInfo *
3111541Srgrimesget_mountpoint(struct chunk *old)
3121541Srgrimes{
3131541Srgrimes    char *val;
3141541Srgrimes    PartInfo *tmp;
3151541Srgrimes
3161541Srgrimes    if (old && old->private)
3171541Srgrimes	tmp = old->private;
3181541Srgrimes    else
3191541Srgrimes	tmp = NULL;
3201541Srgrimes    val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
3211541Srgrimes    if (!val || !*val) {
3221541Srgrimes	if (!old)
3231541Srgrimes	    return NULL;
3241541Srgrimes	else {
3251541Srgrimes	    free(old->private);
3261541Srgrimes	    old->private = NULL;
3271541Srgrimes	}
3281541Srgrimes	return NULL;
3291541Srgrimes    }
3301541Srgrimes
3311541Srgrimes    /* Is it just the same value? */
3321541Srgrimes    if (tmp && !strcmp(tmp->mountpoint, val))
3331541Srgrimes	return NULL;
3341541Srgrimes
3351541Srgrimes    /* Did we use it already? */
3361541Srgrimes    if (check_conflict(val)) {
33712158Sbde	msgConfirm("You already have a mount point for %s assigned!", val);
3381541Srgrimes	return NULL;
3391541Srgrimes    }
3401541Srgrimes
34112158Sbde    /* Is it bogus? */
3421541Srgrimes    if (*val != '/') {
3431541Srgrimes	msgConfirm("Mount point must start with a / character");
3441541Srgrimes	return NULL;
3451541Srgrimes    }
3461541Srgrimes
3471541Srgrimes    /* Is it going to be mounted on root? */
3481541Srgrimes    if (!strcmp(val, "/")) {
3491541Srgrimes	if (old)
3501541Srgrimes	    old->flags |= CHUNK_IS_ROOT;
3511541Srgrimes    }
3521541Srgrimes    else if (old)
3531541Srgrimes	old->flags &= ~CHUNK_IS_ROOT;
3541541Srgrimes
3551541Srgrimes    safe_free(tmp);
3561541Srgrimes    tmp = new_part(val, TRUE, 0);
3571541Srgrimes    if (old) {
3581541Srgrimes	old->private = tmp;
3591541Srgrimes	old->private_free = safe_free;
3601541Srgrimes    }
3611541Srgrimes    return tmp;
3621541Srgrimes}
3631541Srgrimes
3641541Srgrimes/* Get the type of the new partiton */
3651541Srgrimesstatic PartType
3661541Srgrimesget_partition_type(void)
3671541Srgrimes{
3681541Srgrimes    char selection[20];
3691541Srgrimes    int i;
3701541Srgrimes
3711541Srgrimes    static unsigned char *fs_types[] = {
3721541Srgrimes	"FS",
3731541Srgrimes	"A file system",
3741541Srgrimes	"Swap",
3751541Srgrimes	"A swap partition.",
3761541Srgrimes    };
3771541Srgrimes    i = dialog_menu("Please choose a partition type",
3781541Srgrimes		    "If you want to use this partition for swap space, select Swap.\n"
3791541Srgrimes		    "If you want to put a filesystem on it, choose FS.",
3801541Srgrimes		    -1, -1, 2, 2, fs_types, selection, NULL, NULL);
3812811Sbde    if (!i) {
3821541Srgrimes	if (!strcmp(selection, "FS"))
3831541Srgrimes	    return PART_FILESYSTEM;
3841541Srgrimes	else if (!strcmp(selection, "Swap"))
3851541Srgrimes	    return PART_SWAP;
3861541Srgrimes    }
3871541Srgrimes    return PART_NONE;
3881541Srgrimes}
3891541Srgrimes
3901541Srgrimes/* If the user wants a special newfs command for this, set it */
3911541Srgrimesstatic void
3921541SrgrimesgetNewfsCmd(PartInfo *p)
3931541Srgrimes{
3942811Sbde    char *val;
3952811Sbde
3962811Sbde    val = msgGetInput(p->newfs_cmd,
3972811Sbde		      "Please enter the newfs command and options you'd like to use in\n"
3982811Sbde		      "creating this file system.");
3992811Sbde    if (val)
4003098Sphk	strncpy(p->newfs_cmd, val, NEWFS_CMD_MAX);
40112873Sbde}
40212873Sbde
4031541Srgrimesstatic int
40412873SbdescriptLabel(char *str)
40512873Sbde{
4061541Srgrimes    char *cp;
4071541Srgrimes    PartType type;
4084465Sbde    PartInfo *p;
40912873Sbde    u_long flags = 0;
4101541Srgrimes    int i, status;
4111541Srgrimes    Device **devs;
4121541Srgrimes    Disk *d;
41312873Sbde
41412873Sbde    status = RET_SUCCESS;
4151541Srgrimes    cp = variable_get(VAR_DISK);
4161541Srgrimes    if (!cp) {
4171541Srgrimes	dialog_clear();
4181541Srgrimes	msgConfirm("scriptLabel:  No disk selected - can't label automatically.");
4191541Srgrimes	return RET_FAIL;
4201541Srgrimes    }
4211541Srgrimes
4221541Srgrimes    devs = deviceFind(cp, DEVICE_TYPE_DISK);
4231541Srgrimes    if (!devs) {
4241541Srgrimes	dialog_clear();
4251541Srgrimes	msgConfirm("scriptLabel: No disk device %s found!", cp);
4261541Srgrimes	return RET_FAIL;
42712873Sbde    }
42812873Sbde    d = devs[0]->private;
42912873Sbde
4301541Srgrimes    record_label_chunks(devs);
43112873Sbde    for (i = 0; label_chunk_info[i].c; i++) {
43212873Sbde	Chunk *c1 = label_chunk_info[i].c;
4331541Srgrimes
4341541Srgrimes	if (label_chunk_info[i].type == PART_SLICE) {
4351541Srgrimes	    if ((cp = variable_get(c1->name)) != NULL) {
4361541Srgrimes		int sz;
4372165Spaul		char typ[10], mpoint[50];
4382811Sbde
439		if (sscanf(cp, "%s %d %s", typ, &sz, mpoint) != 3) {
440		    dialog_clear();
441		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s",  c1->name, cp);
442		    status = RET_FAIL;
443		    continue;
444		}
445		else {
446		    Chunk *tmp;
447
448		    if (!strcmp(typ, "swap")) {
449			type = PART_SWAP;
450			strcpy(mpoint, "<swap>");
451		    }
452		    else {
453			type = PART_FILESYSTEM;
454			if (!strcmp(mpoint, "/"))
455			    flags |= CHUNK_IS_ROOT;
456		    }
457		    if (!sz)
458			sz = space_free(c1);
459		    if (sz > space_free(c1)) {
460			dialog_clear();
461			msgConfirm("Not enough free space to create partition: %s", mpoint);
462			status = RET_FAIL;
463			continue;
464		    }
465		    if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
466						  (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
467			dialog_clear();
468			msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
469			status = RET_FAIL;
470			break;
471		    }
472		    else {
473			tmp->private = new_part(mpoint, TRUE, sz);
474			tmp->private_free = safe_free;
475			status = RET_SUCCESS;
476		    }
477		}
478	    }
479	}
480	else {
481	    /* Must be something we can set a mountpoint */
482	    cp = variable_get(c1->name);
483	    if (cp) {
484		char mpoint[50], nwfs[8];
485		Boolean newfs = FALSE;
486
487		nwfs[0] = '\0';
488		if (sscanf(cp, "%s %s", mpoint, nwfs) != 2) {
489		    dialog_clear();
490		    msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
491		    status = RET_FAIL;
492		    continue;
493		}
494		newfs = toupper(nwfs[0]) == 'Y' ? TRUE : FALSE;
495		if (c1->private) {
496		    p = c1->private;
497		    p->newfs = newfs;
498		    strcpy(p->mountpoint, mpoint);
499		}
500		else {
501		    c1->private = new_part(mpoint, newfs, 0);
502		    c1->private_free = safe_free;
503		}
504		if (!strcmp(mpoint, "/"))
505		    c1->flags |= CHUNK_IS_ROOT;
506		else
507		    c1->flags &= ~CHUNK_IS_ROOT;
508	    }
509	}
510    }
511    if (status == RET_SUCCESS)
512	variable_set2(DISK_LABELLED, "yes");
513    return status;
514}
515
516#define MAX_MOUNT_NAME	12
517
518#define PART_PART_COL	0
519#define PART_MOUNT_COL	8
520#define PART_SIZE_COL	(PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
521#define PART_NEWFS_COL	(PART_SIZE_COL + 7)
522#define PART_OFF	38
523
524/* How many mounted partitions to display in column before going to next */
525#define CHUNK_COLUMN_MAX	5
526
527/* stick this all up on the screen */
528static void
529print_label_chunks(void)
530{
531    int i, j, srow, prow, pcol;
532    int sz;
533
534    attrset(A_REVERSE);
535    mvaddstr(0, 25, "FreeBSD Disklabel Editor");
536    clrtobot();
537    attrset(A_NORMAL);
538
539    for (i = 0; i < 2; i++) {
540	mvaddstr(CHUNK_PART_START_ROW - 2, PART_PART_COL + (i * PART_OFF), "Part");
541	mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF), "----");
542
543	mvaddstr(CHUNK_PART_START_ROW - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
544	mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
545
546	mvaddstr(CHUNK_PART_START_ROW - 2, PART_SIZE_COL + (i * PART_OFF) + 2, "Size");
547	mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF) + 2, "----");
548
549	mvaddstr(CHUNK_PART_START_ROW - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
550	mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
551    }
552    srow = CHUNK_SLICE_START_ROW;
553    prow = CHUNK_PART_START_ROW;
554    pcol = 0;
555
556    for (i = 0; label_chunk_info[i].c; i++) {
557	if (i == here)
558	    attrset(A_REVERSE);
559	/* Is it a slice entry displayed at the top? */
560	if (label_chunk_info[i].type == PART_SLICE) {
561	    sz = space_free(label_chunk_info[i].c);
562	    mvprintw(srow++, 0, "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
563		     label_chunk_info[i].c->disk->name, label_chunk_info[i].c->name, sz, (sz / ONE_MEG));
564	}
565	/* Otherwise it's a DOS, swap or filesystem entry, at the bottom */
566	else {
567	    char onestr[PART_OFF], num[10], *mountpoint, *newfs;
568
569	    /*
570	     * We copy this into a blank-padded string so that it looks like
571	     * a solid bar in reverse-video
572	     */
573	    memset(onestr, ' ', PART_OFF - 1);
574	    onestr[PART_OFF - 1] = '\0';
575	    /* Go for two columns */
576	    if (prow == (CHUNK_PART_START_ROW + CHUNK_COLUMN_MAX)) {
577		pcol = PART_OFF;
578		prow = CHUNK_PART_START_ROW;
579	    }
580	    memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
581	    /* If it's a filesystem, display the mountpoint */
582	    if (label_chunk_info[i].c->private
583		&& (label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT))
584	        mountpoint = ((PartInfo *)label_chunk_info[i].c->private)->mountpoint;
585	    else
586	        mountpoint = "<none>";
587
588	    /* Now display the newfs field */
589	    if (label_chunk_info[i].type == PART_FAT)
590	        newfs = "DOS";
591	    else if (label_chunk_info[i].c->private && label_chunk_info[i].type == PART_FILESYSTEM)
592		newfs = ((PartInfo *)label_chunk_info[i].c->private)->newfs ? "UFS Y" : "UFS N";
593	    else if (label_chunk_info[i].type == PART_SWAP)
594		newfs = "SWAP";
595	    else
596		newfs = "*";
597	    for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
598		onestr[PART_MOUNT_COL + j] = mountpoint[j];
599	    snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? label_chunk_info[i].c->size / ONE_MEG : 0);
600	    memcpy(onestr + PART_SIZE_COL, num, strlen(num));
601	    memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
602	    onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
603	    mvaddstr(prow, pcol, onestr);
604	    ++prow;
605	}
606	if (i == here)
607	    attrset(A_NORMAL);
608    }
609}
610
611static void
612print_command_summary()
613{
614    mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
615    mvprintw(18, 0, "C = Create      D = Delete         M = Mount   W = Write");
616    mvprintw(19, 0, "N = Newfs Opts  T = Newfs Toggle   U = Undo    Q = Finish");
617    mvprintw(20, 0, "A = Auto Defaults for all!");
618    mvprintw(22, 0, "The default target will be displayed in ");
619
620    attrset(A_REVERSE);
621    addstr("reverse");
622    attrset(A_NORMAL);
623    addstr(" video.");
624    mvprintw(23, 0, "Use F1 or ? to get more help, arrow keys to move.");
625    move(0, 0);
626}
627
628static int
629diskLabel(char *str)
630{
631    int sz, key = 0;
632    Boolean labeling;
633    char *msg = NULL;
634    PartInfo *p, *oldp;
635    PartType type;
636    Device **devs;
637
638    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
639    if (!devs) {
640	dialog_clear();
641	msgConfirm("No disks found!");
642	return RET_FAIL;
643    }
644
645    labeling = TRUE;
646    keypad(stdscr, TRUE);
647    record_label_chunks(devs);
648
649    dialog_clear(); clear();
650    while (labeling) {
651	clear();
652	print_label_chunks();
653	print_command_summary();
654	if (msg) {
655	    attrset(A_REVERSE); mvprintw(23, 0, msg); attrset(A_NORMAL);
656	    clrtoeol();
657	    beep();
658	    msg = NULL;
659	}
660	refresh();
661	key = toupper(getch());
662	switch (key) {
663	    int i, cnt;
664
665	case '\014':	/* ^L */
666	    continue;
667
668	case KEY_UP:
669	case '-':
670	    if (here != 0)
671		--here;
672	    else
673		while (label_chunk_info[here + 1].c)
674		    ++here;
675	    break;
676
677	case KEY_DOWN:
678	case '+':
679	case '\r':
680	case '\n':
681	    if (label_chunk_info[here + 1].c)
682		++here;
683	    else
684		here = 0;
685	    break;
686
687	case KEY_HOME:
688	    here = 0;
689	    break;
690
691	case KEY_END:
692	    while (label_chunk_info[here + 1].c)
693		++here;
694	    break;
695
696	case KEY_F(1):
697	case '?':
698	    systemDisplayHelp("partition");
699	    break;
700
701	case 'A':
702	    if (label_chunk_info[here].type != PART_SLICE) {
703		msg = "You can only do this in a master partition (see top of screen)";
704		break;
705	    }
706
707	    cnt = i = 0;
708	    while (label_chunk_info[i].c)
709		if (label_chunk_info[i++].type != PART_SLICE)
710		    cnt++;
711	    if (cnt == (CHUNK_COLUMN_MAX * 2) + 4) {
712		dialog_clear();
713		msgConfirm("Sorry, I can't fit any more partitions on the screen!  You can get around\n"
714			   "this limitation by partitioning your disks individually rather than all\n"
715			   "at once.  This will be fixed just as soon as we get a scrolling partition\n"
716			   "box written.  Sorry for the inconvenience!");
717		break;
718	    }
719
720	    sz = space_free(label_chunk_info[here].c);
721	    if (sz <= FS_MIN_SIZE) {
722		msg = "Not enough space to create an additional FreeBSD partition";
723		break;
724	    }
725	{
726	    struct chunk *tmp;
727	    int mib[2];
728	    int physmem;
729	    size_t size, swsize;
730	    char *cp;
731
732	    cp = variable_get(VAR_ROOT_SIZE);
733	    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
734				    label_chunk_info[here].c,
735				    (cp ? atoi(cp) : 32) * ONE_MEG, part, FS_BSDFFS,
736				    CHUNK_IS_ROOT);
737
738	    if (!tmp) {
739		dialog_clear();
740		msgConfirm("Unable to create the root partition. Too big?");
741		break;
742	    }
743	    tmp->private = new_part("/", TRUE, tmp->size);
744	    tmp->private_free = safe_free;
745	    record_label_chunks(devs);
746
747	    cp = variable_get(VAR_SWAP_SIZE);
748	    if (cp)
749		swsize = atoi(cp) * ONE_MEG;
750	    else {
751		mib[0] = CTL_HW;
752		mib[1] = HW_PHYSMEM;
753		size = sizeof physmem;
754		sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
755		swsize = 16 * ONE_MEG + (physmem * 2 / 512);
756	    }
757	    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
758				    label_chunk_info[here].c,
759				    swsize,
760				    part, FS_SWAP, 0);
761	    if (!tmp) {
762		dialog_clear();
763		msgConfirm("Unable to create the swap partition. Too big?");
764		break;
765	    }
766
767	    tmp->private = 0;
768	    tmp->private_free = safe_free;
769	    record_label_chunks(devs);
770
771	    cp = variable_get(VAR_VAR_SIZE);
772	    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
773				    label_chunk_info[here].c,
774				    (cp ? atoi(cp) : VAR_MIN_SIZE) * ONE_MEG, part, FS_BSDFFS, 0);
775	    if (!tmp) {
776		dialog_clear();
777		msgConfirm("Less than %dMB free for /var - you will need to\n"
778			   "partition your disk manually with a custom install!", (cp ? atoi(cp) : VAR_MIN_SIZE));
779		break;
780	    }
781	    tmp->private = new_part("/var", TRUE, tmp->size);
782	    tmp->private_free = safe_free;
783	    record_label_chunks(devs);
784
785	    cp = variable_get(VAR_USR_SIZE);
786	    if (cp)
787		sz = atoi(cp) * ONE_MEG;
788	    else
789		sz = space_free(label_chunk_info[here].c);
790	    if (!sz || sz < (USR_MIN_SIZE * ONE_MEG)) {
791		dialog_clear();
792		msgConfirm("Less than %dMB free for /usr - you will need to\n"
793			   "partition your disk manually with a custom install!", USR_MIN_SIZE);
794		break;
795	    }
796
797	    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
798				    label_chunk_info[here].c,
799				    sz, part, FS_BSDFFS, 0);
800	    if (!tmp) {
801		dialog_clear();
802		msgConfirm("Unable to create the /usr partition.  Not enough space?\n"
803			   "You will need to partition your disk manually with a custom install!");
804		break;
805	    }
806	    /* At this point, we're reasonably "labelled" */
807	    variable_set2(DISK_LABELLED, "yes");
808	    tmp->private = new_part("/usr", TRUE, tmp->size);
809	    tmp->private_free = safe_free;
810	    record_label_chunks(devs);
811	}
812	    break;
813
814	case 'C':
815	    if (label_chunk_info[here].type != PART_SLICE) {
816		msg = "You can only do this in a master partition (see top of screen)";
817		break;
818	    }
819	    else {
820		int i, cnt;
821
822		cnt = i = 0;
823		while (label_chunk_info[i].c)
824		    if (label_chunk_info[i++].type != PART_SLICE)
825			cnt++;
826		if (cnt == (CHUNK_COLUMN_MAX * 2)) {
827		    dialog_clear();
828		    msgConfirm("Sorry, I can't fit any more partitions on the screen!  You can get around\n"
829			       "this limitation by partitioning your disks individually rather than all\n"
830			       "at once.  This will be fixed just as soon as we get a scrolling partition\n"
831			       "box written.  Sorry for the inconvenience!");
832		    break;
833		}
834	    }
835	    sz = space_free(label_chunk_info[here].c);
836	    if (sz <= FS_MIN_SIZE) {
837		msg = "Not enough space to create an additional FreeBSD partition";
838		break;
839	    }
840	    {
841		char *val, *cp;
842		int size;
843		struct chunk *tmp;
844		char osize[80];
845		u_long flags = 0;
846
847		sprintf(osize, "%d", sz);
848		val = msgGetInput(osize, "Please specify the size for new FreeBSD partition in blocks, or\n"
849				  "append a trailing `M' for megabytes (e.g. 20M) or `C' for cylinders.\n\n"
850				  "Space free is %d blocks (%dMB)", sz, sz / ONE_MEG);
851		if (!val || (size = strtol(val, &cp, 0)) <= 0)
852		    break;
853
854		if (*cp) {
855		    if (toupper(*cp) == 'M')
856			size *= ONE_MEG;
857		    else if (toupper(*cp) == 'C')
858			size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
859		}
860		if (size <= FS_MIN_SIZE) {
861		    msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
862		    break;
863		}
864		type = get_partition_type();
865		if (type == PART_NONE)
866		    break;
867
868		if (type == PART_FILESYSTEM) {
869		    if ((p = get_mountpoint(NULL)) == NULL)
870			break;
871		    else if (!strcmp(p->mountpoint, "/"))
872			flags |= CHUNK_IS_ROOT;
873		    else
874			flags &= ~CHUNK_IS_ROOT;
875		} else
876		    p = NULL;
877
878		if ((flags & CHUNK_IS_ROOT)) {
879		    if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) {
880			msgConfirm("This region cannot be used for your root partition as the\n"
881				   "FreeBSD boot code cannot deal with a root partition created\n"
882				   "in that location.  Please choose another location or smaller\n"
883				   "size for your root partition and try again!");
884			break;
885		    }
886		    if (size < (ROOT_MIN_SIZE * ONE_MEG)) {
887			msgConfirm("Warning: This is smaller than the recommended size for a\n"
888				   "root partition.  For a variety of reasons, root\n"
889				   "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
890		    }
891		}
892		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
893					label_chunk_info[here].c,
894					size, part,
895					(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
896					flags);
897		if (!tmp) {
898		    msgConfirm("Unable to create the partition. Too big?");
899		    break;
900		}
901		if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) {
902		    msgConfirm("This region cannot be used for your root partition as it starts\n"
903			       "or extends past the 1024'th cylinder mark and is thus a\n"
904			       "poor location to boot from.  Please choose another\n"
905			       "location (or smaller size) for your root partition and try again!");
906		    Delete_Chunk(label_chunk_info[here].c->disk, tmp);
907		    break;
908		}
909		if (type != PART_SWAP) {
910		    /* This is needed to tell the newfs -u about the size */
911		    tmp->private = new_part(p->mountpoint, p->newfs, tmp->size);
912		    tmp->private_free = safe_free;
913		    safe_free(p);
914		} else {
915		    tmp->private = p;
916		}
917		tmp->private_free = safe_free;
918		variable_set2(DISK_LABELLED, "yes");
919		record_label_chunks(devs);
920	    }
921	    break;
922
923	case 'D':	/* delete */
924	    if (label_chunk_info[here].type == PART_SLICE) {
925		msg = MSG_NOT_APPLICABLE;
926		break;
927	    }
928	    else if (label_chunk_info[here].type == PART_FAT) {
929		msg = "Use the Disk Partition Editor to delete DOS partitions";
930		break;
931	    }
932	    Delete_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c);
933	    variable_set2(DISK_LABELLED, "yes");
934	    record_label_chunks(devs);
935	    break;
936
937	case 'M':	/* mount */
938	    switch(label_chunk_info[here].type) {
939	    case PART_SLICE:
940		msg = MSG_NOT_APPLICABLE;
941		break;
942
943	    case PART_SWAP:
944		msg = "You don't need to specify a mountpoint for a swap partition.";
945		break;
946
947	    case PART_FAT:
948	    case PART_FILESYSTEM:
949		oldp = label_chunk_info[here].c->private;
950		p = get_mountpoint(label_chunk_info[here].c);
951		if (p) {
952		    if (!oldp)
953		    	p->newfs = FALSE;
954		    if (label_chunk_info[here].type == PART_FAT
955			&& (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr")
956			    || !strcmp(p->mountpoint, "/var"))) {
957			msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
958			strcpy(p->mountpoint, "/bogus");
959		    }
960		}
961		variable_set2(DISK_LABELLED, "yes");
962		record_label_chunks(devs);
963		break;
964
965	    default:
966		msgFatal("Bogus partition under cursor???");
967		break;
968	    }
969	    break;
970
971	case 'N':	/* Set newfs options */
972	    if (label_chunk_info[here].c->private &&
973		((PartInfo *)label_chunk_info[here].c->private)->newfs)
974		getNewfsCmd(label_chunk_info[here].c->private);
975	    else
976		msg = MSG_NOT_APPLICABLE;
977	    break;
978
979	case 'T':	/* Toggle newfs state */
980	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
981		    PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private);
982		    label_chunk_info[here].c->private = new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
983		    safe_free(pi);
984		    label_chunk_info[here].c->private_free = safe_free;
985		    variable_set2(DISK_LABELLED, "yes");
986		}
987	    else
988		msg = MSG_NOT_APPLICABLE;
989	    break;
990
991	case 'U':
992	    clear();
993	    if (msgYesNo("Are you SURE you want to Undo everything?"))
994		break;
995	    variable_unset(DISK_PARTITIONED);
996	    for (i = 0; devs[i]; i++) {
997		extern void diskPartition(Device *dev, Disk *d);
998		Disk *d;
999
1000		if (!devs[i]->enabled)
1001		    continue;
1002		else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1003		    Free_Disk(devs[i]->private);
1004		    devs[i]->private = d;
1005		    diskPartition(devs[i], d);
1006		}
1007	    }
1008	    variable_unset(DISK_LABELLED);
1009	    record_label_chunks(devs);
1010	    break;
1011
1012	case 'W':
1013	    if (!msgYesNo("Are you SURE that you wish to make and mount all filesystems\n"
1014			  "at this time?  You also have the option of doing it later in\n"
1015			  "one final 'commit' operation, and if you're at all unsure as\n"
1016			  "to which option to chose, then PLEASE CHOSE NO!  This option\n"
1017			  "is DANGEROUS if you're not EXACTLY sure what you are doing!")) {
1018		variable_set2(DISK_LABELLED, "yes");
1019		clear();
1020		diskLabelCommit(NULL);
1021	    }
1022	    break;
1023
1024	case '|':
1025	    if (!msgYesNo("Are you sure you want to go into Wizard mode?\n\n"
1026			  "This is an entirely undocumented feature which you are not\n"
1027			  "expected to understand!")) {
1028		int i;
1029		Device **devs;
1030
1031		dialog_clear();
1032		end_dialog();
1033		DialogActive = FALSE;
1034		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1035		if (!devs) {
1036		    dialog_clear();
1037		    msgConfirm("Can't find any disk devices!");
1038		    break;
1039		}
1040		for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1041		    if (devs[i]->enabled)
1042		    	slice_wizard(((Disk *)devs[i]->private));
1043		}
1044		variable_set2(DISK_LABELLED, "yes");
1045		DialogActive = TRUE;
1046		dialog_clear();
1047		record_label_chunks(devs);
1048	    }
1049	    else
1050		msg = "A most prudent choice!";
1051	    break;
1052
1053	case 'Q':
1054	    labeling = FALSE;
1055	    break;
1056
1057	default:
1058	    beep();
1059	    msg = "Type F1 or ? for help";
1060	    break;
1061	}
1062    }
1063    dialog_clear();
1064    return RET_SUCCESS;
1065}
1066