label.c revision 21971
18549Sjkh/*
28549Sjkh * The new sysinstall program.
38549Sjkh *
48549Sjkh * This is probably the last program in the `sysinstall' line - the next
58549Sjkh * generation being essentially a complete rewrite.
68549Sjkh *
721673Sjkh * $FreeBSD: head/usr.sbin/sysinstall/label.c 21971 1997-01-24 07:47:17Z jkh $
88549Sjkh *
98549Sjkh * Copyright (c) 1995
108549Sjkh *	Jordan Hubbard.  All rights reserved.
118549Sjkh *
128549Sjkh * Redistribution and use in source and binary forms, with or without
138549Sjkh * modification, are permitted provided that the following conditions
148549Sjkh * are met:
158549Sjkh * 1. Redistributions of source code must retain the above copyright
168881Srgrimes *    notice, this list of conditions and the following disclaimer,
178881Srgrimes *    verbatim and that no modifications are made prior to this
188549Sjkh *    point in the file.
198549Sjkh * 2. Redistributions in binary form must reproduce the above copyright
208549Sjkh *    notice, this list of conditions and the following disclaimer in the
218549Sjkh *    documentation and/or other materials provided with the distribution.
228549Sjkh *
238549Sjkh * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
248549Sjkh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
258549Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
268549Sjkh * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
278549Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
288549Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
298549Sjkh * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
308549Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
318549Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
328549Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
338549Sjkh * SUCH DAMAGE.
348549Sjkh *
358549Sjkh */
368549Sjkh
378549Sjkh#include "sysinstall.h"
388549Sjkh#include <ctype.h>
398549Sjkh#include <sys/disklabel.h>
4010882Speter#include <sys/param.h>
4110882Speter#include <sys/sysctl.h>
428549Sjkh
438549Sjkh/*
448549Sjkh * Everything to do with editing the contents of disk labels.
458549Sjkh */
468549Sjkh
478549Sjkh/* A nice message we use a lot in the disklabel editor */
488549Sjkh#define MSG_NOT_APPLICABLE	"That option is not applicable here"
498549Sjkh
508549Sjkh/* Where to start printing the freebsd slices */
518549Sjkh#define CHUNK_SLICE_START_ROW		2
528622Sjkh#define CHUNK_PART_START_ROW		11
538549Sjkh
548549Sjkh/* The smallest filesystem we're willing to create */
558702Sjkh#define FS_MIN_SIZE			ONE_MEG
568549Sjkh
578672Sjkh/* The smallest root filesystem we're willing to create */
5812661Speter#define ROOT_MIN_SIZE			20
598549Sjkh
6012661Speter/* The smallest swap partition we want to create by default */
6112661Speter#define SWAP_MIN_SIZE			16
6212661Speter
6312661Speter/* The smallest /usr partition we're willing to create by default */
6412661Speter#define USR_MIN_SIZE			80
6512661Speter
6612661Speter/* The smallest /var partition we're willing to create by default */
6712661Speter#define VAR_MIN_SIZE			30
6812661Speter
6915440Sjkh/* The bottom-most row we're allowed to scribble on */
7015440Sjkh#define CHUNK_ROW_MAX		16
7115440Sjkh
7215440Sjkh
738549Sjkh/* All the chunks currently displayed on the screen */
748549Sjkhstatic struct {
758549Sjkh    struct chunk *c;
768549Sjkh    PartType type;
778549Sjkh} label_chunk_info[MAX_CHUNKS + 1];
788549Sjkhstatic int here;
798549Sjkh
8015440Sjkhstatic int ChunkPartStartRow;
8115440Sjkhstatic WINDOW *ChunkWin;
8215440Sjkh
8312661Speterstatic int diskLabel(char *str);
8412661Speter
8512661Speterint
8615091SjkhdiskLabelEditor(dialogMenuItem *self)
8712661Speter{
8812661Speter    Device **devs;
8915242Sjkh    int i, cnt, enabled;
9015242Sjkh    char *cp;
9112661Speter
9212661Speter    cp = variable_get(VAR_DISK);
9312661Speter    devs = deviceFind(cp, DEVICE_TYPE_DISK);
9412661Speter    cnt = deviceCount(devs);
9512661Speter    if (!cnt) {
9612661Speter	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
9712661Speter		   "properly probed at boot time.  See the Hardware Guide on the\n"
9812661Speter		   "Documentation menu for clues on diagnosing this type of problem.");
9915242Sjkh	return DITEM_FAILURE;
10012661Speter    }
10115242Sjkh    for (i = 0, enabled = 0; i < cnt; i++) {
10215242Sjkh	if (devs[i]->enabled)
10315242Sjkh	    ++enabled;
10412661Speter    }
10515242Sjkh    if (!enabled) {
10615695Sjkh	msgConfirm("No disks have been selected.  Please visit the Partition\n"
10715695Sjkh		   "editor first to specify which disks you wish to operate on.");
10815695Sjkh	return DITEM_FAILURE;
10912661Speter    }
11015242Sjkh    i = diskLabel(devs[0]->name);
11118744Sjkh    if (DITEM_STATUS(i) != DITEM_FAILURE) {
11218744Sjkh	char *cp;
11318744Sjkh
11418744Sjkh	if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
11518744Sjkh	    variable_set2(DISK_LABELLED, "yes");
11618744Sjkh    }
11712661Speter    return i;
11812661Speter}
11912661Speter
12012661Speterint
12115091SjkhdiskLabelCommit(dialogMenuItem *self)
12212661Speter{
12312661Speter    char *cp;
12412661Speter    int i;
12512661Speter
12612661Speter    /* Already done? */
12717025Sjkh    if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes"))
12815242Sjkh	i = DITEM_SUCCESS;
12912661Speter    else if (!cp) {
13012661Speter	msgConfirm("You must assign disk labels before this option can be used.");
13115242Sjkh	i = DITEM_FAILURE;
13212661Speter    }
13312661Speter    /* The routine will guard against redundant writes, just as this one does */
13415419Sjkh    else if (DITEM_STATUS(diskPartitionWrite(self)) != DITEM_SUCCESS)
13515242Sjkh	i = DITEM_FAILURE;
13615419Sjkh    else if (DITEM_STATUS(installFilesystems(self)) != DITEM_SUCCESS)
13715242Sjkh	i = DITEM_FAILURE;
13812661Speter    else {
13912661Speter	msgInfo("All filesystem information written successfully.");
14012661Speter	variable_set2(DISK_LABELLED, "written");
14115242Sjkh	i = DITEM_SUCCESS;
14212661Speter    }
14312661Speter    return i;
14412661Speter}
14512661Speter
1468549Sjkh/* See if we're already using a desired partition name */
1478549Sjkhstatic Boolean
1488549Sjkhcheck_conflict(char *name)
1498549Sjkh{
1508549Sjkh    int i;
1518549Sjkh
1528751Sjkh    for (i = 0; label_chunk_info[i].c; i++)
15314793Sjoerg	if (label_chunk_info[i].type == PART_FILESYSTEM && label_chunk_info[i].c->private_data
15414793Sjoerg	    && !strcmp(((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint, name))
1558549Sjkh	    return TRUE;
1568549Sjkh    return FALSE;
1578549Sjkh}
1588549Sjkh
1598549Sjkh/* How much space is in this FreeBSD slice? */
1608549Sjkhstatic int
1618549Sjkhspace_free(struct chunk *c)
1628549Sjkh{
16312661Speter    struct chunk *c1;
1648549Sjkh    int sz = c->size;
1658549Sjkh
16612661Speter    for (c1 = c->part; c1; c1 = c1->next) {
1678549Sjkh	if (c1->type != unused)
1688549Sjkh	    sz -= c1->size;
1698549Sjkh    }
1708549Sjkh    if (sz < 0)
1718549Sjkh	msgFatal("Partitions are larger than actual chunk??");
1728549Sjkh    return sz;
1738549Sjkh}
1748549Sjkh
1758549Sjkh/* Snapshot the current situation into the displayed chunks structure */
1768549Sjkhstatic void
17712661Speterrecord_label_chunks(Device **devs)
1788549Sjkh{
1798549Sjkh    int i, j, p;
1808549Sjkh    struct chunk *c1, *c2;
1818556Sjkh    Disk *d;
1828549Sjkh
18315440Sjkh    ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3;
1848549Sjkh    j = p = 0;
1858556Sjkh    /* First buzz through and pick up the FreeBSD slices */
1868549Sjkh    for (i = 0; devs[i]; i++) {
1878556Sjkh	if (!devs[i]->enabled)
1888556Sjkh	    continue;
1898556Sjkh	d = (Disk *)devs[i]->private;
1908556Sjkh	if (!d->chunks)
1918556Sjkh	    msgFatal("No chunk list found for %s!", d->name);
1928549Sjkh
1938556Sjkh	/* Put the slice entries first */
1948556Sjkh	for (c1 = d->chunks->part; c1; c1 = c1->next) {
1958549Sjkh	    if (c1->type == freebsd) {
1968549Sjkh		label_chunk_info[j].type = PART_SLICE;
1978549Sjkh		label_chunk_info[j].c = c1;
1988549Sjkh		++j;
19915440Sjkh		++ChunkPartStartRow;
2008549Sjkh	    }
2018549Sjkh	}
2028549Sjkh    }
20312661Speter
2048556Sjkh    /* Now run through again and get the FreeBSD partition entries */
2058556Sjkh    for (i = 0; devs[i]; i++) {
2068556Sjkh	if (!devs[i]->enabled)
2078556Sjkh	    continue;
2088556Sjkh	d = (Disk *)devs[i]->private;
2098549Sjkh	/* Then buzz through and pick up the partitions */
2108556Sjkh	for (c1 = d->chunks->part; c1; c1 = c1->next) {
2118549Sjkh	    if (c1->type == freebsd) {
2128549Sjkh		for (c2 = c1->part; c2; c2 = c2->next) {
2138549Sjkh		    if (c2->type == part) {
2148549Sjkh			if (c2->subtype == FS_SWAP)
2158549Sjkh			    label_chunk_info[j].type = PART_SWAP;
2168549Sjkh			else
2178549Sjkh			    label_chunk_info[j].type = PART_FILESYSTEM;
2188549Sjkh			label_chunk_info[j].c = c2;
2198549Sjkh			++j;
2208549Sjkh		    }
2218549Sjkh		}
2228549Sjkh	    }
2238549Sjkh	    else if (c1->type == fat) {
2248549Sjkh		label_chunk_info[j].type = PART_FAT;
2258549Sjkh		label_chunk_info[j].c = c1;
2268702Sjkh		++j;
2278549Sjkh	    }
2288549Sjkh	}
2298549Sjkh    }
2308549Sjkh    label_chunk_info[j].c = NULL;
2318549Sjkh    if (here >= j)
2328549Sjkh	here = j  ? j - 1 : 0;
23315442Sjkh    if (ChunkWin) {
23415440Sjkh	wclear(ChunkWin);
23515442Sjkh	wrefresh(ChunkWin);
23615442Sjkh    }
23715440Sjkh    else
23815440Sjkh	ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
2398549Sjkh}
2408549Sjkh
2418549Sjkh/* A new partition entry */
2428549Sjkhstatic PartInfo *
2438665Sphknew_part(char *mpoint, Boolean newfs, u_long size)
2448549Sjkh{
2458549Sjkh    PartInfo *ret;
2468549Sjkh
2479202Srgrimes    if (!mpoint)
2489202Srgrimes	mpoint = "/change_me";
2499202Srgrimes
2508549Sjkh    ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
25120247Sjkh    sstrncpy(ret->mountpoint, mpoint, FILENAME_MAX);
25214335Sjkh    strcpy(ret->newfs_cmd, "newfs -b 8192 -f 1024");
2538666Sphk    ret->newfs = newfs;
2548669Sphk    if (!size)
2558669Sphk	    return ret;
2568549Sjkh    return ret;
2578549Sjkh}
2588549Sjkh
2598549Sjkh/* Get the mountpoint for a partition and save it away */
26012661Speterstatic PartInfo *
2618589Sjkhget_mountpoint(struct chunk *old)
2628549Sjkh{
2638549Sjkh    char *val;
2648549Sjkh    PartInfo *tmp;
2658549Sjkh
26614793Sjoerg    if (old && old->private_data)
26714793Sjoerg	tmp = old->private_data;
2688810Sjkh    else
2698810Sjkh	tmp = NULL;
27018619Sjkh    if (!old) {
27118621Sjkh	DialogX = 14;
27218621Sjkh	DialogY = 16;
27318619Sjkh    }
2748810Sjkh    val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
27518619Sjkh    DialogX = DialogY = 0;
2768764Sjkh    if (!val || !*val) {
2778751Sjkh	if (!old)
2788751Sjkh	    return NULL;
2798751Sjkh	else {
28014793Sjoerg	    free(old->private_data);
28114793Sjoerg	    old->private_data = NULL;
2828751Sjkh	}
2838669Sphk	return NULL;
2848751Sjkh    }
2858669Sphk
2868669Sphk    /* Is it just the same value? */
2878810Sjkh    if (tmp && !strcmp(tmp->mountpoint, val))
2888669Sphk	return NULL;
2898810Sjkh
2908810Sjkh    /* Did we use it already? */
2918669Sphk    if (check_conflict(val)) {
2928669Sphk	msgConfirm("You already have a mount point for %s assigned!", val);
2938669Sphk	return NULL;
2948549Sjkh    }
2958810Sjkh
2968810Sjkh    /* Is it bogus? */
2978669Sphk    if (*val != '/') {
2988669Sphk	msgConfirm("Mount point must start with a / character");
2998669Sphk	return NULL;
3008669Sphk    }
3018810Sjkh
3028810Sjkh    /* Is it going to be mounted on root? */
3038669Sphk    if (!strcmp(val, "/")) {
3048669Sphk	if (old)
3058669Sphk	    old->flags |= CHUNK_IS_ROOT;
3068810Sjkh    }
3078810Sjkh    else if (old)
3088669Sphk	old->flags &= ~CHUNK_IS_ROOT;
3098810Sjkh
3108810Sjkh    safe_free(tmp);
3118669Sphk    tmp = new_part(val, TRUE, 0);
3128669Sphk    if (old) {
31314793Sjoerg	old->private_data = tmp;
3148669Sphk	old->private_free = safe_free;
3158669Sphk    }
3168669Sphk    return tmp;
3178549Sjkh}
3188549Sjkh
3198549Sjkh/* Get the type of the new partiton */
3208549Sjkhstatic PartType
3218549Sjkhget_partition_type(void)
3228549Sjkh{
3238549Sjkh    char selection[20];
3248669Sphk    int i;
3258669Sphk
3268549Sjkh    static unsigned char *fs_types[] = {
3278549Sjkh	"FS",
3288549Sjkh	"A file system",
3298549Sjkh	"Swap",
3308549Sjkh	"A swap partition.",
3318549Sjkh    };
33218619Sjkh    DialogX = 7;
33318621Sjkh    DialogY = 8;
3348669Sphk    i = dialog_menu("Please choose a partition type",
33512661Speter		    "If you want to use this partition for swap space, select Swap.\n"
33612661Speter		    "If you want to put a filesystem on it, choose FS.",
33712661Speter		    -1, -1, 2, 2, fs_types, selection, NULL, NULL);
33818619Sjkh    DialogX = DialogY = 0;
3398669Sphk    if (!i) {
3408549Sjkh	if (!strcmp(selection, "FS"))
3418549Sjkh	    return PART_FILESYSTEM;
3428549Sjkh	else if (!strcmp(selection, "Swap"))
3438549Sjkh	    return PART_SWAP;
3448549Sjkh    }
3458549Sjkh    return PART_NONE;
3468549Sjkh}
3478549Sjkh
3488549Sjkh/* If the user wants a special newfs command for this, set it */
3498549Sjkhstatic void
3508549SjkhgetNewfsCmd(PartInfo *p)
3518549Sjkh{
3528549Sjkh    char *val;
3538549Sjkh
3548549Sjkh    val = msgGetInput(p->newfs_cmd,
35512661Speter		      "Please enter the newfs command and options you'd like to use in\n"
35612661Speter		      "creating this file system.");
3578549Sjkh    if (val)
35820247Sjkh	sstrncpy(p->newfs_cmd, val, NEWFS_CMD_MAX);
3598549Sjkh}
3608549Sjkh
3618549Sjkh#define MAX_MOUNT_NAME	12
3628549Sjkh
3638549Sjkh#define PART_PART_COL	0
3648549Sjkh#define PART_MOUNT_COL	8
3658549Sjkh#define PART_SIZE_COL	(PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
3668549Sjkh#define PART_NEWFS_COL	(PART_SIZE_COL + 7)
3678549Sjkh#define PART_OFF	38
3688549Sjkh
3698549Sjkh/* stick this all up on the screen */
3708549Sjkhstatic void
3718549Sjkhprint_label_chunks(void)
3728549Sjkh{
3738549Sjkh    int i, j, srow, prow, pcol;
3748549Sjkh    int sz;
3758549Sjkh
3768549Sjkh    attrset(A_REVERSE);
3778549Sjkh    mvaddstr(0, 25, "FreeBSD Disklabel Editor");
3788549Sjkh    attrset(A_NORMAL);
3798549Sjkh
3808549Sjkh    for (i = 0; i < 2; i++) {
38115440Sjkh	mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
38215440Sjkh	mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
3838549Sjkh
38415440Sjkh	mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
38515440Sjkh	mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
3868549Sjkh
38715440Sjkh	mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 2, "Size");
38815440Sjkh	mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 2, "----");
3898549Sjkh
39015440Sjkh	mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
39115440Sjkh	mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
3928549Sjkh    }
3938549Sjkh    srow = CHUNK_SLICE_START_ROW;
39415440Sjkh    prow = 0;
3958549Sjkh    pcol = 0;
3968549Sjkh
3978751Sjkh    for (i = 0; label_chunk_info[i].c; i++) {
3988549Sjkh	/* Is it a slice entry displayed at the top? */
3998549Sjkh	if (label_chunk_info[i].type == PART_SLICE) {
4008549Sjkh	    sz = space_free(label_chunk_info[i].c);
40115440Sjkh	    if (i == here)
40216208Sjkh		attrset(ATTR_SELECTED);
4038751Sjkh	    mvprintw(srow++, 0, "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
4048751Sjkh		     label_chunk_info[i].c->disk->name, label_chunk_info[i].c->name, sz, (sz / ONE_MEG));
40515440Sjkh	    attrset(A_NORMAL);
40615440Sjkh	    clrtoeol();
40715440Sjkh	    move(0, 0);
40815440Sjkh	    refresh();
4098549Sjkh	}
41015440Sjkh	/* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
4118549Sjkh	else {
4128549Sjkh	    char onestr[PART_OFF], num[10], *mountpoint, *newfs;
4138549Sjkh
4148549Sjkh	    /*
4158549Sjkh	     * We copy this into a blank-padded string so that it looks like
4168549Sjkh	     * a solid bar in reverse-video
4178549Sjkh	     */
4188549Sjkh	    memset(onestr, ' ', PART_OFF - 1);
4198549Sjkh	    onestr[PART_OFF - 1] = '\0';
42015440Sjkh	    /* Go for two columns if we've written one full columns worth */
42115440Sjkh	    if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) {
4228549Sjkh		pcol = PART_OFF;
42315440Sjkh		prow = 0;
4248549Sjkh	    }
4258705Sjkh	    memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
4268549Sjkh	    /* If it's a filesystem, display the mountpoint */
42714793Sjoerg	    if (label_chunk_info[i].c->private_data
42810882Speter		&& (label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT))
42914793Sjoerg	        mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
43010882Speter	    else
43110882Speter	        mountpoint = "<none>";
43210882Speter
43310882Speter	    /* Now display the newfs field */
43410882Speter	    if (label_chunk_info[i].type == PART_FAT)
43510882Speter	        newfs = "DOS";
43614793Sjoerg	    else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM)
43714793Sjoerg		newfs = ((PartInfo *)label_chunk_info[i].c->private_data)->newfs ? "UFS Y" : "UFS N";
43810882Speter	    else if (label_chunk_info[i].type == PART_SWAP)
43910882Speter		newfs = "SWAP";
44010882Speter	    else
4418549Sjkh		newfs = "*";
4428549Sjkh	    for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
4438549Sjkh		onestr[PART_MOUNT_COL + j] = mountpoint[j];
4448764Sjkh	    snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? label_chunk_info[i].c->size / ONE_MEG : 0);
4458549Sjkh	    memcpy(onestr + PART_SIZE_COL, num, strlen(num));
4468549Sjkh	    memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
4478549Sjkh	    onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
44815440Sjkh	    if (i == here)
44916208Sjkh		wattrset(ChunkWin, ATTR_SELECTED);
45015440Sjkh	    mvwaddstr(ChunkWin, prow, pcol, onestr);
45115440Sjkh	    wattrset(ChunkWin, A_NORMAL);
45215440Sjkh	    wrefresh(ChunkWin);
45315440Sjkh	    move(0, 0);
4548549Sjkh	    ++prow;
4558549Sjkh	}
4568549Sjkh    }
4578549Sjkh}
4588549Sjkh
4598549Sjkhstatic void
46018619Sjkhprint_command_summary(void)
4618549Sjkh{
4628820Sjkh    mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
46321971Sjkh    mvprintw(18, 0, "C = Create      D = Delete         M = Mount pt.");
46415440Sjkh    if (!RunningAsInit)
46515695Sjkh	mvprintw(18, 47, "W = Write");
46610882Speter    mvprintw(19, 0, "N = Newfs Opts  T = Newfs Toggle   U = Undo    Q = Finish");
46710882Speter    mvprintw(20, 0, "A = Auto Defaults for all!");
46815695Sjkh    mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
4698549Sjkh    move(0, 0);
4708549Sjkh}
4718549Sjkh
47218619Sjkhstatic void
47318619Sjkhclear_wins(void)
47418619Sjkh{
47518619Sjkh    clear();
47618619Sjkh    wclear(ChunkWin);
47718619Sjkh}
47818619Sjkh
47912661Speterstatic int
48012661SpeterdiskLabel(char *str)
4818549Sjkh{
48218619Sjkh    int sz, key = 0;
4838549Sjkh    Boolean labeling;
4848549Sjkh    char *msg = NULL;
4859202Srgrimes    PartInfo *p, *oldp;
4868549Sjkh    PartType type;
4878824Sjkh    Device **devs;
4888549Sjkh
48912661Speter    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
49012661Speter    if (!devs) {
49112661Speter	msgConfirm("No disks found!");
49215242Sjkh	return DITEM_FAILURE;
49312661Speter    }
49412661Speter
4958549Sjkh    labeling = TRUE;
4968549Sjkh    keypad(stdscr, TRUE);
49712661Speter    record_label_chunks(devs);
4988549Sjkh
49918619Sjkh    clear();
5008549Sjkh    while (labeling) {
50118744Sjkh	char *cp;
50218744Sjkh
5038549Sjkh	print_label_chunks();
50418619Sjkh	print_command_summary();
5058549Sjkh	if (msg) {
50615695Sjkh	    attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
50712661Speter	    clrtoeol();
5088549Sjkh	    beep();
5098549Sjkh	    msg = NULL;
5108549Sjkh	}
51115442Sjkh	else {
51215442Sjkh	    move(23, 0);
51315442Sjkh	    clrtoeol();
51415442Sjkh	}
51518744Sjkh
51618619Sjkh	refresh();
51717397Sjkh	key = getch();
51817397Sjkh	switch (toupper(key)) {
51915440Sjkh	    int i;
52017362Sjkh	    static char _msg[40];
5218549Sjkh
5228751Sjkh	case '\014':	/* ^L */
52318619Sjkh	    clear_wins();
52418619Sjkh	    break;
5258751Sjkh
52621698Sjkh	case '\020':	/* ^P */
5278549Sjkh	case KEY_UP:
5288549Sjkh	case '-':
5298549Sjkh	    if (here != 0)
5308549Sjkh		--here;
5318751Sjkh	    else
5328751Sjkh		while (label_chunk_info[here + 1].c)
5338751Sjkh		    ++here;
5348549Sjkh	    break;
5358549Sjkh
53621698Sjkh	case '\016':	/* ^N */
5378549Sjkh	case KEY_DOWN:
5388549Sjkh	case '+':
5398549Sjkh	case '\r':
5408549Sjkh	case '\n':
5418751Sjkh	    if (label_chunk_info[here + 1].c)
5428549Sjkh		++here;
5438751Sjkh	    else
5448751Sjkh		here = 0;
5458549Sjkh	    break;
5468549Sjkh
5478549Sjkh	case KEY_HOME:
5488549Sjkh	    here = 0;
5498549Sjkh	    break;
5508549Sjkh
5518549Sjkh	case KEY_END:
5528751Sjkh	    while (label_chunk_info[here + 1].c)
5538549Sjkh		++here;
5548549Sjkh	    break;
5558549Sjkh
5568549Sjkh	case KEY_F(1):
5578549Sjkh	case '?':
55812661Speter	    systemDisplayHelp("partition");
55918619Sjkh	    clear_wins();
5608549Sjkh	    break;
5618549Sjkh
56210882Speter	case 'A':
56310882Speter	    if (label_chunk_info[here].type != PART_SLICE) {
56415440Sjkh		msg = "You can only do this in a disk slice (at top of screen)";
56510882Speter		break;
56610882Speter	    }
56710882Speter	    sz = space_free(label_chunk_info[here].c);
56817362Sjkh	    if (sz <= FS_MIN_SIZE)
56915440Sjkh		msg = "Not enough free space to create a new partition in the slice";
57015440Sjkh	    else {
57115440Sjkh		struct chunk *tmp;
57215440Sjkh		int mib[2];
57315440Sjkh		int physmem;
57415440Sjkh		size_t size, swsize;
57515440Sjkh		char *cp;
57617362Sjkh		Chunk *rootdev, *swapdev, *usrdev, *vardev;
57712661Speter
57817368Sjkh		(void)checkLabels(FALSE, &rootdev, &swapdev, &usrdev, &vardev);
57917362Sjkh		if (!rootdev) {
58017362Sjkh		    cp = variable_get(VAR_ROOT_SIZE);
58117362Sjkh		    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
58217362Sjkh					    (cp ? atoi(cp) : 32) * ONE_MEG, part, FS_BSDFFS,  CHUNK_IS_ROOT);
58317362Sjkh		    if (!tmp) {
58417362Sjkh			msgConfirm("Unable to create the root partition. Too big?");
58518619Sjkh			clear_wins();
58617362Sjkh			break;
58717362Sjkh		    }
58817362Sjkh		    tmp->private_data = new_part("/", TRUE, tmp->size);
58917362Sjkh		    tmp->private_free = safe_free;
59017362Sjkh		    record_label_chunks(devs);
59115440Sjkh		}
59217362Sjkh
59317362Sjkh		if (!swapdev) {
59417362Sjkh		    cp = variable_get(VAR_SWAP_SIZE);
59517362Sjkh		    if (cp)
59617362Sjkh			swsize = atoi(cp) * ONE_MEG;
59717362Sjkh		    else {
59817362Sjkh			mib[0] = CTL_HW;
59917362Sjkh			mib[1] = HW_PHYSMEM;
60017362Sjkh			size = sizeof physmem;
60117362Sjkh			sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
60217362Sjkh			swsize = 16 * ONE_MEG + (physmem * 2 / 512);
60317362Sjkh		    }
60417362Sjkh		    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
60517362Sjkh					    swsize, part, FS_SWAP, 0);
60617362Sjkh		    if (!tmp) {
60717362Sjkh			msgConfirm("Unable to create the swap partition. Too big?");
60818619Sjkh			clear_wins();
60917362Sjkh			break;
61017362Sjkh		    }
61117362Sjkh		    tmp->private_data = 0;
61217362Sjkh		    tmp->private_free = safe_free;
61317362Sjkh		    record_label_chunks(devs);
61415440Sjkh		}
61517362Sjkh
61617362Sjkh		if (!vardev) {
61717362Sjkh		    cp = variable_get(VAR_VAR_SIZE);
61817362Sjkh		    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
61917362Sjkh					    (cp ? atoi(cp) : VAR_MIN_SIZE) * ONE_MEG, part, FS_BSDFFS, 0);
62017362Sjkh		    if (!tmp) {
62117362Sjkh			msgConfirm("Less than %dMB free for /var - you will need to\n"
62217362Sjkh				   "partition your disk manually with a custom install!",
62317362Sjkh				   (cp ? atoi(cp) : VAR_MIN_SIZE));
62418619Sjkh			clear_wins();
62517362Sjkh			break;
62617362Sjkh		    }
62717362Sjkh		    tmp->private_data = new_part("/var", TRUE, tmp->size);
62817362Sjkh		    tmp->private_free = safe_free;
62917362Sjkh		    record_label_chunks(devs);
63015440Sjkh		}
63112661Speter
63217362Sjkh		if (!usrdev) {
63317362Sjkh		    cp = variable_get(VAR_USR_SIZE);
63417362Sjkh		    if (cp)
63517362Sjkh			sz = atoi(cp) * ONE_MEG;
63617362Sjkh		    else
63717362Sjkh			sz = space_free(label_chunk_info[here].c);
63817362Sjkh		    if (!sz || sz < (USR_MIN_SIZE * ONE_MEG)) {
63917362Sjkh			msgConfirm("Less than %dMB free for /usr - you will need to\n"
64017362Sjkh				   "partition your disk manually with a custom install!", USR_MIN_SIZE);
64118619Sjkh			clear_wins();
64217362Sjkh			break;
64317362Sjkh		    }
64417362Sjkh
64517362Sjkh		    tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
64617362Sjkh					    label_chunk_info[here].c,
64717362Sjkh					    sz, part, FS_BSDFFS, 0);
64817362Sjkh		    if (!tmp) {
64917362Sjkh			msgConfirm("Unable to create the /usr partition.  Not enough space?\n"
65017362Sjkh				   "You will need to partition your disk manually with a custom install!");
65118619Sjkh			clear_wins();
65217362Sjkh			break;
65317362Sjkh		    }
65417362Sjkh		    tmp->private_data = new_part("/usr", TRUE, tmp->size);
65517362Sjkh		    tmp->private_free = safe_free;
65617362Sjkh		    record_label_chunks(devs);
65715440Sjkh		}
65815440Sjkh		/* At this point, we're reasonably "labelled" */
65918744Sjkh		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
66018744Sjkh		    variable_set2(DISK_LABELLED, "yes");
66110882Speter	    }
66215440Sjkh	    break;
66310882Speter
6648549Sjkh	case 'C':
6658549Sjkh	    if (label_chunk_info[here].type != PART_SLICE) {
6668549Sjkh		msg = "You can only do this in a master partition (see top of screen)";
6678549Sjkh		break;
6688549Sjkh	    }
6698549Sjkh	    sz = space_free(label_chunk_info[here].c);
6708669Sphk	    if (sz <= FS_MIN_SIZE) {
67112661Speter		msg = "Not enough space to create an additional FreeBSD partition";
6728669Sphk		break;
6738669Sphk	    }
67415440Sjkh	    else {
67518744Sjkh		char *val;
6768702Sjkh		int size;
6778702Sjkh		struct chunk *tmp;
6788820Sjkh		char osize[80];
6798702Sjkh		u_long flags = 0;
6808549Sjkh
6818820Sjkh		sprintf(osize, "%d", sz);
68218619Sjkh		DialogX = 3;
68318621Sjkh		DialogY = 2;
68418619Sjkh		val = msgGetInput(osize,
68518619Sjkh				  "Please specify the partition size in blocks or append a trailing M for\n"
68618619Sjkh				  "megabytes or C for cylinders.  %d blocks (%dMB) are free.",
68718619Sjkh				  sz, sz / ONE_MEG);
68818619Sjkh		DialogX = DialogY = 0;
68918619Sjkh		if (!val || (size = strtol(val, &cp, 0)) <= 0) {
69018619Sjkh		    clear_wins();
6918702Sjkh		    break;
69218619Sjkh		}
6938549Sjkh
6948751Sjkh		if (*cp) {
6958751Sjkh		    if (toupper(*cp) == 'M')
6968751Sjkh			size *= ONE_MEG;
6978751Sjkh		    else if (toupper(*cp) == 'C')
6988751Sjkh			size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
6998751Sjkh		}
7008820Sjkh		if (size <= FS_MIN_SIZE) {
7018820Sjkh		    msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
70218619Sjkh		    clear_wins();
7038820Sjkh		    break;
7048820Sjkh		}
7058702Sjkh		type = get_partition_type();
70618619Sjkh		if (type == PART_NONE) {
70718619Sjkh		    clear_wins();
70818619Sjkh		    beep();
7098669Sphk		    break;
71018619Sjkh		}
7118669Sphk
7128702Sjkh		if (type == PART_FILESYSTEM) {
71318619Sjkh		    if ((p = get_mountpoint(NULL)) == NULL) {
71418619Sjkh			clear_wins();
71518619Sjkh			beep();
7168702Sjkh			break;
71718619Sjkh		    }
7188702Sjkh		    else if (!strcmp(p->mountpoint, "/"))
7198702Sjkh			flags |= CHUNK_IS_ROOT;
7208702Sjkh		    else
7218702Sjkh			flags &= ~CHUNK_IS_ROOT;
72218619Sjkh		}
72318619Sjkh		else
7248702Sjkh		    p = NULL;
7258702Sjkh
7268702Sjkh		if ((flags & CHUNK_IS_ROOT)) {
7278702Sjkh		    if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) {
72812661Speter			msgConfirm("This region cannot be used for your root partition as the\n"
72912661Speter				   "FreeBSD boot code cannot deal with a root partition created\n"
73012661Speter				   "in that location.  Please choose another location or smaller\n"
73112661Speter				   "size for your root partition and try again!");
73218619Sjkh			clear_wins();
7338702Sjkh			break;
7348702Sjkh		    }
73512661Speter		    if (size < (ROOT_MIN_SIZE * ONE_MEG)) {
73612661Speter			msgConfirm("Warning: This is smaller than the recommended size for a\n"
73712661Speter				   "root partition.  For a variety of reasons, root\n"
73812661Speter				   "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
73912661Speter		    }
7408672Sjkh		}
7418751Sjkh		tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
7428702Sjkh					label_chunk_info[here].c,
7438702Sjkh					size, part,
7448702Sjkh					(type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
7458702Sjkh					flags);
7468702Sjkh		if (!tmp) {
7478702Sjkh		    msgConfirm("Unable to create the partition. Too big?");
74818619Sjkh		    clear_wins();
7498672Sjkh		    break;
7508672Sjkh		}
7518702Sjkh		if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) {
75212661Speter		    msgConfirm("This region cannot be used for your root partition as it starts\n"
75312661Speter			       "or extends past the 1024'th cylinder mark and is thus a\n"
75412661Speter			       "poor location to boot from.  Please choose another\n"
75512661Speter			       "location (or smaller size) for your root partition and try again!");
7568751Sjkh		    Delete_Chunk(label_chunk_info[here].c->disk, tmp);
75718619Sjkh		    clear_wins();
7588669Sphk		    break;
7598702Sjkh		}
7608702Sjkh		if (type != PART_SWAP) {
7618669Sphk		    /* This is needed to tell the newfs -u about the size */
76214793Sjoerg		    tmp->private_data = new_part(p->mountpoint, p->newfs, tmp->size);
7638669Sphk		    safe_free(p);
76415355Sjkh		}
76515355Sjkh		else
76614793Sjoerg		    tmp->private_data = p;
7678702Sjkh		tmp->private_free = safe_free;
76818744Sjkh		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
76918744Sjkh		    variable_set2(DISK_LABELLED, "yes");
77012661Speter		record_label_chunks(devs);
77118619Sjkh		clear_wins();
7728549Sjkh	    }
7738549Sjkh	    break;
7748549Sjkh
77517397Sjkh	case KEY_DC:
7768549Sjkh	case 'D':	/* delete */
7778549Sjkh	    if (label_chunk_info[here].type == PART_SLICE) {
7788549Sjkh		msg = MSG_NOT_APPLICABLE;
7798549Sjkh		break;
7808549Sjkh	    }
7818549Sjkh	    else if (label_chunk_info[here].type == PART_FAT) {
7828705Sjkh		msg = "Use the Disk Partition Editor to delete DOS partitions";
7838549Sjkh		break;
7848549Sjkh	    }
7858751Sjkh	    Delete_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c);
78618744Sjkh	    if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
78718744Sjkh		variable_set2(DISK_LABELLED, "yes");
78812661Speter	    record_label_chunks(devs);
7898549Sjkh	    break;
7908549Sjkh
7918549Sjkh	case 'M':	/* mount */
7928549Sjkh	    switch(label_chunk_info[here].type) {
7938549Sjkh	    case PART_SLICE:
7948549Sjkh		msg = MSG_NOT_APPLICABLE;
7958549Sjkh		break;
7968549Sjkh
7978549Sjkh	    case PART_SWAP:
7988549Sjkh		msg = "You don't need to specify a mountpoint for a swap partition.";
7998549Sjkh		break;
8008549Sjkh
8018556Sjkh	    case PART_FAT:
8028549Sjkh	    case PART_FILESYSTEM:
80314793Sjoerg		oldp = label_chunk_info[here].c->private_data;
8048589Sjkh		p = get_mountpoint(label_chunk_info[here].c);
8058549Sjkh		if (p) {
8069202Srgrimes		    if (!oldp)
8079202Srgrimes		    	p->newfs = FALSE;
8088722Sjkh		    if (label_chunk_info[here].type == PART_FAT
8098722Sjkh			&& (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr")
8108722Sjkh			    || !strcmp(p->mountpoint, "/var"))) {
8118722Sjkh			msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
8128722Sjkh			strcpy(p->mountpoint, "/bogus");
8138722Sjkh		    }
8148549Sjkh		}
81518744Sjkh		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
81618744Sjkh		    variable_set2(DISK_LABELLED, "yes");
81712661Speter		record_label_chunks(devs);
81818636Sjkh		clear_wins();
8198549Sjkh		break;
8208549Sjkh
8218549Sjkh	    default:
8228549Sjkh		msgFatal("Bogus partition under cursor???");
8238549Sjkh		break;
8248549Sjkh	    }
8258549Sjkh	    break;
8268549Sjkh
8278549Sjkh	case 'N':	/* Set newfs options */
82814793Sjoerg	    if (label_chunk_info[here].c->private_data &&
82914793Sjoerg		((PartInfo *)label_chunk_info[here].c->private_data)->newfs)
83014793Sjoerg		getNewfsCmd(label_chunk_info[here].c->private_data);
8318549Sjkh	    else
8328549Sjkh		msg = MSG_NOT_APPLICABLE;
83318636Sjkh	    clear_wins();
8348549Sjkh	    break;
8358549Sjkh
8368549Sjkh	case 'T':	/* Toggle newfs state */
8379202Srgrimes	    if (label_chunk_info[here].type == PART_FILESYSTEM) {
83814793Sjoerg		    PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
83915355Sjkh		    label_chunk_info[here].c->private_data =
84015355Sjkh			new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
8418669Sphk		    safe_free(pi);
8428820Sjkh		    label_chunk_info[here].c->private_free = safe_free;
84318744Sjkh		    if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
84418744Sjkh			variable_set2(DISK_LABELLED, "yes");
84518619Sjkh	    }
8468549Sjkh	    else
8478549Sjkh		msg = MSG_NOT_APPLICABLE;
8488549Sjkh	    break;
8498549Sjkh
8508820Sjkh	case 'U':
85112661Speter	    clear();
85218744Sjkh	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
85318744Sjkh		msgConfirm("You've already written out your changes -\n"
85418744Sjkh			   "it's too late to undo!");
85518744Sjkh	    }
85618744Sjkh	    else if (!msgYesNo("Are you SURE you want to Undo everything?")) {
85718744Sjkh		variable_unset(DISK_PARTITIONED);
85818744Sjkh		variable_unset(DISK_LABELLED);
85918744Sjkh		for (i = 0; devs[i]; i++) {
86018744Sjkh		    Disk *d;
86112661Speter
86218744Sjkh		    if (!devs[i]->enabled)
86318744Sjkh			continue;
86418744Sjkh		    else if ((d = Open_Disk(devs[i]->name)) != NULL) {
86518744Sjkh			Free_Disk(devs[i]->private);
86618744Sjkh			devs[i]->private = d;
86718744Sjkh			diskPartition(devs[i], d);
86818744Sjkh		    }
8698824Sjkh		}
87018744Sjkh		record_label_chunks(devs);
8718824Sjkh	    }
87218636Sjkh	    clear_wins();
8738824Sjkh	    break;
8748820Sjkh
8758549Sjkh	case 'W':
87618744Sjkh	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
87718744Sjkh		msgConfirm("You've already written out your changes - if you\n"
87818744Sjkh			   "wish to overwrite them, you'll have to start this\n"
87918744Sjkh			   "procedure again from the beginning.");
88018744Sjkh	    }
88118744Sjkh	    else if (!msgYesNo("WARNING:  This should only be used when modifying an EXISTING\n"
88218687Sjkh			  "installation.  If you are installing FreeBSD for the first time\n"
88318687Sjkh			  "then you should simply type Q when you're finished here and your\n"
88418687Sjkh			  "changes will be committed in one batch automatically at the end of\n"
88518687Sjkh			  "these questions.\n\n"
88618687Sjkh			  "Are you absolutely sure you want to do this now?")) {
88712661Speter		variable_set2(DISK_LABELLED, "yes");
88812661Speter		diskLabelCommit(NULL);
88912661Speter	    }
89018636Sjkh	    clear_wins();
89110882Speter	    break;
89210882Speter
89310882Speter	case '|':
89412661Speter	    if (!msgYesNo("Are you sure you want to go into Wizard mode?\n\n"
89512661Speter			  "This is an entirely undocumented feature which you are not\n"
89612661Speter			  "expected to understand!")) {
8978549Sjkh		int i;
8988549Sjkh		Device **devs;
8998549Sjkh
9008549Sjkh		dialog_clear();
9018549Sjkh		end_dialog();
9028549Sjkh		DialogActive = FALSE;
9038549Sjkh		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
9048549Sjkh		if (!devs) {
90512661Speter		    msgConfirm("Can't find any disk devices!");
9068549Sjkh		    break;
9078549Sjkh		}
9088668Sphk		for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
9098613Sjkh		    if (devs[i]->enabled)
9108613Sjkh		    	slice_wizard(((Disk *)devs[i]->private));
9118613Sjkh		}
91218744Sjkh		if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
91318744Sjkh		    variable_set2(DISK_LABELLED, "yes");
9148665Sphk		DialogActive = TRUE;
91512661Speter		record_label_chunks(devs);
91618636Sjkh		clear_wins();
9178549Sjkh	    }
9188549Sjkh	    else
9198549Sjkh		msg = "A most prudent choice!";
9208549Sjkh	    break;
9218549Sjkh
92221698Sjkh	case '\033':	/* ESC */
9239202Srgrimes	case 'Q':
9248549Sjkh	    labeling = FALSE;
9258549Sjkh	    break;
9268549Sjkh
9278549Sjkh	default:
9288549Sjkh	    beep();
92917362Sjkh	    sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
93017362Sjkh	    msg = _msg;
9318549Sjkh	    break;
9328549Sjkh	}
9338549Sjkh    }
93417362Sjkh    return DITEM_SUCCESS | DITEM_RESTORE;
9358549Sjkh}
936