view usr/src/cmd/lvm/metassist/layout/layout_device_util.c @ 13:f60a82e85167 default tip

Revert NEC's changes to fix krb5 build
author Andrew Stormont <andyjstormont@gmail.com>
date Fri, 02 Mar 2012 22:25:26 +0000
parents 1a15d5aaf794
children
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/vtoc.h>
#include <sys/dktp/fdisk.h>
#include <errno.h>
#include <meta.h>

#include <libdiskmgt.h>
#include "meta_repartition.h"

#define	_LAYOUT_DEVICE_UTIL_C

#include "volume_dlist.h"
#include "volume_error.h"
#include "volume_output.h"
#include "volume_nvpair.h"

#include "layout_device_cache.h"
#include "layout_device_util.h"
#include "layout_discovery.h"
#include "layout_dlist_util.h"
#include "layout_slice.h"

/*
 *	Macros to produce a quoted string containing the value of a
 *	preprocessor macro. For example, if SIZE is defined to be 256,
 *	VAL2STR(SIZE) is "256". This is used to construct format
 *	strings for scanf-family functions below.
 */
#define	QUOTE(x)	#x
#define	VAL2STR(x)	QUOTE(x)

/* private utilities for disks */
static int disk_get_uint64_attribute(
	dm_descriptor_t disk,
	char		*attr,
	uint64_t 	*val);

static int disk_get_boolean_attribute(
	dm_descriptor_t	disk,
	char		*attr,
	boolean_t	*bool);

static int disk_get_rpm(
	dm_descriptor_t disk,
	uint32_t 	*val);

static int disk_get_sync_speed(
	dm_descriptor_t disk,
	uint32_t 	*val);

static int disk_has_virtual_slices(
	dm_descriptor_t disk,
	boolean_t 	*bool);

static int disk_get_virtual_slices(
	dm_descriptor_t disk,
	dlist_t 	**list);

static int disk_get_reserved_indexes(
	dm_descriptor_t disk,
	uint16_t 	**array);

static int disk_get_associated_desc(
	dm_descriptor_t	disk,
	dm_desc_type_t	assoc_type,
	char		*assoc_type_str,
	dlist_t		**list);

/* utilities for slices */
static int slice_get_uint64_attribute(
	dm_descriptor_t slice,
	char		*attr,
	uint64_t 	*val);

static int slice_set_attribute(
	dm_descriptor_t slice,
	char		*attr,
	uint64_t 	val);

/*
 * Virtual slices are created to represent slices that will be
 * on the system after disks have been added to the destination
 * diskset.  For the purposes of layout, these slices must
 * look & function just as real slices that are currently on
 * the system.
 */
static dlist_t	*_virtual_slices = NULL;

/* temporary implementation */
static int virtual_repartition_drive(
	dm_descriptor_t disk,
	mdvtoc_t	*vtocp);

static int disk_add_virtual_slice(
	dm_descriptor_t disk,
	dm_descriptor_t slice);

static int virtual_slice_get_disk(
	dm_descriptor_t slice,
	dm_descriptor_t *diskp);

/*
 * attribute names for layout private information stored in
 * device nvpair attribute lists.
 */
static char *ATTR_RESERVED_INDEX = "vdu_reserved_index";
static char *ATTR_VIRTUAL_SLICES = "vdu_virtual_slices";
static char *ATTR_DISK_FOR_SLICE = "vdu_disk_for_slice";
static char *ATTR_DEV_CTD_NAME = "vdu_device_ctd_name";
static char *ATTR_HBA_N_DISKS = "vdu_hba_n_usable_disks";

/*
 * FUNCTION:	is_ctd_like_slice_name(char *name)
 * INPUT:	name	- a char *
 *
 * RETURNS:	boolean_t - B_TRUE - if name follows an alternate slice
 *				naming scheme similar to CTD
 *			    B_FALSE - otherwise
 *
 * PURPOSE:	Determines if the input name is of the form XXXsNNN
 *		(e.g., whizzy0s1)
 */
boolean_t
is_ctd_like_slice_name(
	char *name)
{
	uint_t		s = 0;
	uint_t		d = 0;
	int		l = 0;
	boolean_t	is = B_FALSE;

	/* The format strings below match and discard the non-numeric part. */
	if ((sscanf(name, "/dev/dsk/%*[^0-9/]%us%u%n", &d, &s, &l) == 2 ||
	    sscanf(name, "/dev/rdsk/%*[^0-9/]%us%u%n", &d, &s, &l) == 2 ||
	    sscanf(name, "%*[^0-9/]%us%u%n", &d, &s, &l) == 2) &&
		(l == strlen(name))) {
	    is = B_TRUE;
	}

	return (is);
}

/*
 * FUNCTION:	is_bsd_like_slice_name(char *name)
 * INPUT:	name	- a char *
 *
 * RETURNS:	boolean_t - B_TRUE - if name follows an alternate slice
 *				BSD-like naming scheme
 *			    B_FALSE - otherwise
 *
 * PURPOSE:	Determines if the input name is of the form XXXNNN[a-h]
 *			(e.g., whizzy0a)
 */
boolean_t
is_bsd_like_slice_name(
	char *name)
{
	uint_t		d = 0;
	int		l = 0;
	boolean_t	is = B_FALSE;

	/* The format strings below match and discard the non-numeric part. */
	if ((sscanf(name, "/dev/dsk/%*[^0-9/]%u%*[a-h]%n", &d, &l) == 1 ||
	    sscanf(name, "/dev/rdsk/%*[^0-9/]%u%*[a-h]%n", &d, &l) == 1 ||
	    sscanf(name, "%*[^0-9/]%u%*[a-h]%n", &d, &l) == 1) &&
		(l == strlen(name))) {
	    is = B_TRUE;
	}

	return (is);
}

/*
 * FUNCTION:	is_did_name(char *name)
 * INPUT:	name	- a char *
 *
 * RETURNS:	boolean_t - B_TRUE - if name is from the DID namespace
 *			    B_FALSE - otherwise
 *
 * PURPOSE:	Determines if the input name is from the DID namespace.
 */
boolean_t
is_did_name(
	char *name)
{
	return (is_did_slice_name(name) || is_did_disk_name(name));
}

/*
 * FUNCTION:	is_did_slice_name(char *name)
 * INPUT:	name	- a char *
 *
 * RETURNS:	boolean_t - B_TRUE - if name represents a slice from the DID
 *					namespace
 *			    B_FALSE - otherwise
 *
 * PURPOSE:	Determines if the input name is a slice from the DID namespace.
 */
boolean_t
is_did_slice_name(
	char *name)
{
	uint_t		d = 0, s = 0;
	int		l = 0;
	boolean_t	is = B_FALSE;

	if ((sscanf(name, "/dev/did/rdsk/d%us%u%n", &d, &s, &l) == 2 ||
		sscanf(name, "/dev/did/dsk/d%us%u%n", &d, &s, &l) == 2 ||
		sscanf(name, "d%us%u%n", &d, &s, &l) == 2) ||
		(l == strlen(name))) {
	    is = B_TRUE;
	}

	return (is);
}

/*
 * FUNCTION:	is_did_disk_name(char *name)
 * INPUT:	name	- a char *
 *
 * RETURNS:	boolean_t - B_TRUE - if name represents a disk from the DID
 *					namespace
 *			    B_FALSE - otherwise
 *
 * PURPOSE:	Determines if the input name is a disk from the DID namespace.
 */
boolean_t
is_did_disk_name(
	char *name)
{
	uint_t		d = 0;
	int		l = 0;
	boolean_t	is = B_FALSE;

	if ((sscanf(name, "/dev/did/rdsk/d%u%n", &d, &l) == 1 ||
		sscanf(name, "/dev/did/dsk/d%u%n", &d, &l) == 1 ||
		sscanf(name, "d%u%n", &d, &l) == 1) &&
		(l == strlen(name))) {
	    is = B_TRUE;
	}

	return (is);
}

/*
 * FUNCTION:	is_ctd_name(char *name)
 * INPUT:	name	- a char *
 *
 * RETURNS:	boolean_t - B_TRUE - if name is from the CTD namespace
 *			    B_FALSE - otherwise
 *
 * PURPOSE:	Determines if the input name is from the CTD namespace.
 *
 *		{/dev/dsk/, /dev/rdsk/}cXtXdXsX
 *		{/dev/dsk/, /dev/rdsk/}cXtXdX
 *		{/dev/dsk/, /dev/rdsk/}cXdXsX
 *		{/dev/dsk/, /dev/rdsk/}cXdX
 */
boolean_t
is_ctd_name(
	char *name)
{
	return (is_ctd_slice_name(name) || is_ctd_disk_name(name) ||
		is_ctd_target_name(name) || is_ctd_ctrl_name(name));
}

/*
 * FUNCTION:	is_ctd_slice_name(char *name)
 * INPUT:	name	- a char *
 *
 * RETURNS:	boolean_t - B_TRUE - if name represents a slice from the CTD
 *				namespace
 *			    B_FALSE - otherwise
 *
 * PURPOSE:	Determines if the input name is a slice name from the
 *		CTD namespace.
 *
 *		{/dev/dsk/, /dev/rdsk/}cXt<WWN>dXsX
 *		{/dev/dsk/, /dev/rdsk/}cXtXdXsX
 *		{/dev/dsk/, /dev/rdsk/}cXdXsX
 */
boolean_t
is_ctd_slice_name(
    char *name)
{
	uint_t		c = 0, t = 0, d = 0, s = 0;
	char		buf[MAXNAMELEN+1];
	int		l = 0;
	boolean_t	is = B_FALSE;

	if ((sscanf(name, "/dev/dsk/c%ut%ud%us%u%n", &c, &t, &d, &s, &l) == 4 ||
	    sscanf(name, "/dev/rdsk/c%ut%ud%us%u%n", &c, &t, &d, &s, &l) == 4 ||
	    sscanf(name, "c%ut%ud%us%u%n", &c, &t, &d, &s, &l) == 4 ||
	    sscanf(name, "/dev/dsk/c%ud%us%u%n", &c, &d, &s, &l) == 3 ||
	    sscanf(name, "/dev/rdsk/c%ud%us%u%n", &c, &d, &s, &l) == 3 ||
	    sscanf(name, "c%ud%us%u%n", &c, &d, &s, &l) == 3 ||
	    sscanf(name, "c%ud%us%u%n", &c, &d, &s, &l) == 2) &&
		(l == strlen(name))) {
	    is = B_TRUE;
	} else if (
	    (sscanf(name, "/dev/dsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n",
		&c, buf, &l) == 2 ||
	    sscanf(name, "/dev/rdsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n",
		&c, buf, &l) == 2 ||
	    sscanf(name, "c%ut%" VAL2STR(MAXNAMELEN) "s%n",
		&c, buf, &l) == 2) && (l == strlen(name))) {
	    char *dev_pos;

	    /* see if buf ends with "dXsX" */
	    if (((dev_pos = strrchr(buf, 'd')) != NULL) &&
		(sscanf(dev_pos, "d%us%u%n", &d, &s, &l) == 2) &&
		(l == strlen(dev_pos))) {

		char wwn[MAXNAMELEN+2];

		/* buf ends with "dXsX", truncate at the 'd' */
		*dev_pos = '\0';

		/* prepend "0X" to remainder and try to scan as a hex WWN */
		(void) snprintf(wwn, sizeof (wwn), "%s%s", "0X", buf);
		if ((sscanf(wwn, "%x%n", &t, &l) == 1) && (l == strlen(wwn))) {
		    is = B_TRUE;
		}
	    }
	}

	return (is);
}

/*
 * FUNCTION:	is_ctd_disk_name(char *name)
 * INPUT:	name	- a char *
 *
 * RETURNS:	boolean_t - B_TRUE - if name represents a disk from the CTD
 *				namespace
 *			    B_FALSE - otherwise
 *
 * PURPOSE:	Determines if the input name is a disk name from the
 *		CTD namespace.
 *
 *		{/dev/dsk/, /dev/rdsk/}cXt<WWN>dX
 *		{/dev/dsk/, /dev/rdsk/}cXtXdX
 *		{/dev/dsk/, /dev/rdsk/}cXdX
 */
boolean_t
is_ctd_disk_name(
    char *name)
{
	uint_t		c = 0, t = 0, d = 0;
	int		l = 0;
	char		buf[MAXNAMELEN+1];
	boolean_t	is = B_FALSE;

	if ((sscanf(name, "/dev/dsk/c%ut%ud%u%n", &c, &t, &d, &l) == 3 ||
	    sscanf(name, "/dev/rdsk/c%ut%ud%u%n", &c, &t, &d, &l) == 3 ||
	    sscanf(name, "c%ut%ud%u%n", &c, &t, &d, &l) == 3 ||
	    sscanf(name, "/dev/dsk/c%ud%u%n", &c, &d, &l) == 2 ||
	    sscanf(name, "/dev/rdsk/c%ud%n%n", &c, &d, &l) == 2 ||
	    sscanf(name, "c%ud%u%n", &c, &d, &l) == 2) &&
		(l == strlen(name))) {
	    is = B_TRUE;
	} else if ((sscanf(name, "/dev/dsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n",
	    &c, buf, &l) == 2 ||
	    sscanf(name, "/dev/rdsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n",
		&c, buf, &l) == 2 ||
	    sscanf(name, "c%ut%" VAL2STR(MAXNAMELEN) "s%n",
		&c, buf, &l) == 2) && (l == strlen(name))) {
	    char *dev_pos;

	    /* see if buf ends with "dX" */
	    if (((dev_pos = strrchr(buf, 'd')) != NULL) &&
		(sscanf(dev_pos, "d%u%n", &d, &l) == 1) &&
		(l == strlen(dev_pos))) {

		char wwn[MAXNAMELEN+2];

		/* buf ends with "dX", truncate at the 'd' */
		*dev_pos = '\0';

		/* prepend "0X" to remainder and try to scan as a hex WWN */
		(void) snprintf(wwn, sizeof (wwn), "%s%s", "0X", buf);
		if ((sscanf(wwn, "%x%n", &t, &l) == 1) && (l == strlen(wwn))) {
		    is = B_TRUE;
		}
	    }
	}

	return (is);
}

/*
 * FUNCTION:	is_ctd_disk_name(char *name)
 * INPUT:	name	- a char *
 *
 * RETURNS:	boolean_t - B_TRUE - if name represents a target from the CTD
 *				namespace
 *			    B_FALSE - otherwise
 *
 * PURPOSE:	Determines if the input name is a target name from the
 *		CTD namespace.
 *
 *		{/dev/dsk/, /dev/rdsk/}cXt<WWN>
 *		{/dev/dsk/, /dev/rdsk/}cXtX
 */
boolean_t
is_ctd_target_name(
    char *name)
{
	uint_t		c = 0, t = 0;
	int		l = 0;
	char		buf[MAXNAMELEN+1];
	boolean_t	is = B_FALSE;

	if ((sscanf(name, "/dev/dsk/c%ut%u%n", &c, &t, &l) == 2 ||
	    sscanf(name, "/dev/rdsk/c%ut%u%n", &c, &t, &l) == 2 ||
	    sscanf(name, "c%ut%u%n", &c, &t, &l) == 2) &&
		(l == strlen(name))) {
	    is = B_TRUE;
	} else if (
	    (sscanf(name, "/dev/dsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n",
		&c, buf, &l) == 2 ||
	    sscanf(name, "/dev/rdsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n",
		&c, buf, &l) == 2 ||
	    sscanf(name, "c%ut%" VAL2STR(MAXNAMELEN) "s%n",
		&c, &buf, &l) == 2) && (l == strlen(name))) {

	    char wwn[MAXNAMELEN+2];

	    /* prepend "0X" to buf and try to scan as a hex WWN */
	    (void) snprintf(wwn, sizeof (wwn), "%s%s", "0X", buf);
	    if ((sscanf(wwn, "%x%n", &t, &l) == 1) && (l == strlen(wwn))) {
		is = B_TRUE;
	    }
	}

	return (is);
}

/*
 * FUNCTION:	is_ctd_ctrl_name(char *name)
 * INPUT:	name	- a char *
 *
 * RETURNS:	boolean_t - B_TRUE - if name represents a controller/hba
 *				from the CTD namespace
 *			    B_FALSE - otherwise
 *
 * PURPOSE:	Determines if the input name is an HBA name from the
 *		CTD namespace.
 *
 *		{/dev/dsk/, /dev/rdsk/}cX
 */
boolean_t
is_ctd_ctrl_name(
	char	*name)
{
	uint_t		c = 0;
	int		l = 0;
	boolean_t	is = B_FALSE;

	if ((sscanf(name, "/dev/dsk/c%u%n", &c, &l) == 1 ||
	    sscanf(name, "/dev/rdsk/c%u%n", &c, &l) == 1 ||
	    sscanf(name, "c%u%n", &c, &l) == 1) &&
		(l == strlen(name))) {
	    is = B_TRUE;
	}

	return (is);
}

/*
 * FUNCTION:	set_display_name(dm_descriptor_t desc, char *name)
 *		get_display_name(dm_descriptor_t desc, char **name)
 *
 * INPUT:	desc	- a dm_descriptor_t handle for a device
 *		name    - a char * name
 *
 * OUTPUT:	**name	- a pointer to a char * to hold the display
 *			name associated with the input descriptor.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helpers to set/get the input descriptor's display name.
 *
 *		Only slices, disks and HBAs should have display names.
 *
 *		The attribute is only set in the cached copy of
 *		the device's nvpair attribute list.  This function
 *		does not affect the underlying physical device.
 *
 *		An entry is added in the name->descriptor cache
 *		so the descriptor can be found by name quickly.
 */
int
set_display_name(
	dm_descriptor_t	desc,
	char		*name)
{
	nvlist_t	*attrs	= NULL;
	int		error	= 0;

	((error = add_cached_descriptor(name, desc)) != 0) ||
	(error = get_cached_attributes(desc, &attrs)) ||
	(error = set_string(attrs, ATTR_DEV_CTD_NAME, name));

	return (error);
}

int
get_display_name(
	dm_descriptor_t	desc,
	char		**name)
{
	nvlist_t	*attrs	= NULL;
	int		error	= 0;

	((error = get_cached_attributes(desc, &attrs)) != 0) ||
	(error = get_string(attrs, ATTR_DEV_CTD_NAME, name));

	return (error);
}

/*
 * FUNCTION:	disk_get_slices(dm_descriptor_t disk, dlist_t **list)
 *
 * INPUT:	disk	- a dm_descriptor_t handle for a disk
 *
 * OUTPUT:	*list	- a pointer to list to hold the results.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Collect all of the known slices for the input disk.
 *
 *		These slices may be actual slices which currently exist
 *		on the disk, or virtual slices which will exist when the
 *		disk is added to the destination diskset.
 */
int
disk_get_slices(
	dm_descriptor_t disk,
	dlist_t		**list)
{
	dm_descriptor_t	*media = NULL;
	boolean_t	virtual = B_FALSE;
	int		i = 0;
	int		error = 0;

	*list = 0;

	if ((error = disk_has_virtual_slices(disk, &virtual)) != 0) {
	    return (error);
	}

	if (virtual == B_TRUE) {
	    error = disk_get_virtual_slices(disk, list);
	}

	/* add real slices from disk's media... */
	media = dm_get_associated_descriptors(disk, DM_MEDIA, &error);
	(void) add_descriptors_to_free(media);

	if (error == 0) {
	    /* if there's no media, this is a removeable drive */
	    if (media != NULL && *media != NULL) {

		/* examine media's slices... */
		dm_descriptor_t	*slices = NULL;
		slices = dm_get_associated_descriptors(*media,
			DM_SLICE, &error);
		(void) add_descriptors_to_free(slices);

		if (error != 0) {
		    print_get_assoc_desc_error(disk, gettext("slice"), error);
		} else {
		    for (i = 0; (slices[i] != NULL) && (error == 0); i++) {
			dlist_t *item =
			    dlist_new_item((void *)(uintptr_t)slices[i]);
			if (item == NULL) {
			    error = ENOMEM;
			} else {
			    *list = dlist_append(item, *list, AT_TAIL);
			}
		    }
		    free(slices);
		}
		free(media);
	    }
	} else {
	    print_get_assoc_desc_error(disk, gettext("media"), error);
	}

	return (error);
}

int
get_virtual_slices(
	dlist_t **list)
{
	*list = _virtual_slices;

	return (0);
}

/*
 * FUNCTION:	virtual_repartition_drive(dm_descriptor_t disk,
 *			mdvtoc_t *vtocp)
 *
 * INPUT:	disk	- the disk to be virtually repartitioned
 *
 * OUTPUT:	vtocp	- a poitner to a mdvtoc struct to hold the results
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helper which emulates the repartitioning that is done
 *		when a disk is added to a diskset.
 *
 *		Modified version of meta_partition_drive which uses info
 * 		from libdiskmgt to accomplish the repartitioning.
 *
 * 		This exists to allow the layout module to run with a
 *		simulated hardware environment.
 *
 *		XXX This method absolutely does not produce the exact
 *		same result as meta_repartition_drive: only information
 *		required by the layout code is returned.  Basically,
 *		a slice 7 (or 6 on EFI labelled disks) is created and
 *		sized, the remained of the available cylinders are put
 *		into slice 0.
 *
 *		XXX2 This method is required until there is resolution
 *		on whether metassist testing will be done using the
 *		hardware simulation mechanism libdiskmgt provides.
 *		Doing so will also require parts of libmeta to be
 *		simulated as well.  Some research has been done into
 *		building an alternate libmeta.so containing
 *		implementations of the functions used by metassist
 *		that are compatible with the simulated hardware.
 *		Actual work is currently on hold.
 */
static int
virtual_repartition_drive(
	dm_descriptor_t	disk,
	mdvtoc_t	*vtocp)
{
	uint_t		replicaslice = 7;
	unsigned long long cylsize;
	unsigned long long drvsize;
	uint_t		reservedcyl;
	ushort_t	resflag;
	unsigned long long ressize;
	diskaddr_t	replica_start;
	diskaddr_t	replica_size;
	diskaddr_t	data_start;
	diskaddr_t	data_size;

	boolean_t	efi = B_FALSE;
	uint64_t	ncyls = 0;
	uint64_t	nheads = 0;
	uint64_t	nsects = 0;
	int		error = 0;

	/*
	 * At this point, ressize is used as a minimum value.  Later it
	 * will be rounded up to a cylinder boundary.  ressize is in
	 * units of disk sectors.
	 */
	ressize = MD_DBSIZE + VTOC_SIZE;
	resflag = V_UNMNT;

	((error = disk_get_is_efi(disk, &efi)) != 0) ||
	(error = disk_get_ncylinders(disk, &ncyls)) ||
	(error = disk_get_nheads(disk, &nheads)) ||
	(error = disk_get_nsectors(disk, &nsects));
	if (error != 0) {
	    return (error);
	}

	if (efi) {
	    replicaslice = 6;
	}

	/*
	 * Both cylsize and drvsize are in units of disk sectors.
	 *
	 * The intended results are of type unsigned long long.  Since
	 * each operand of the first multiplication is of type
	 * unsigned int, we risk overflow by multiplying and then
	 * converting the result.  Therefore we explicitly cast (at
	 * least) one of the operands, forcing conversion BEFORE
	 * multiplication, and avoiding overflow.  The second
	 * assignment is OK, since one of the operands is already of
	 * the desired type.
	 */
	cylsize = ((unsigned long long)nheads) * nsects;
	drvsize = cylsize * ncyls;

	/*
	 * How many cylinders must we reserve for slice seven to
	 * ensure that it meets the previously calculated minimum
	 * size?
	 */
	reservedcyl = (ressize + cylsize - 1) / cylsize;

	/*
	 * It seems unlikely that someone would pass us too small a
	 * disk, but it's still worth checking for...
	 */
	if (reservedcyl >= ncyls) {
	    volume_set_error(
		    gettext("disk is too small to hold a metadb replica"));
	    return (-1);
	}

	replica_start = 0;
	replica_size = reservedcyl * cylsize;
	data_start = reservedcyl * cylsize;
	data_size = drvsize - (reservedcyl * cylsize);

	/*
	 * fill in the proposed VTOC information.
	 */

	/* We need at least replicaslice partitions in the proposed vtoc */
	vtocp->nparts = replicaslice + 1;
	vtocp->parts[MD_SLICE0].start = data_start;
	vtocp->parts[MD_SLICE0].size = data_size;
	vtocp->parts[MD_SLICE0].tag = V_USR;
	vtocp->parts[replicaslice].start = replica_start;
	vtocp->parts[replicaslice].size = replica_size;
	vtocp->parts[replicaslice].flag = resflag;
	vtocp->parts[replicaslice].tag = V_USR;

	return (0);
}

/*
 * FUNCTION:	create_virtual_slices(dlist_t *disks)
 *
 * INPUT:	possibles - a list of dm_descriptor_t disk handles for
 *			disks known to be available for use by layout.
 *
 * SIDEEFFECT:	populates the private of virtual slices.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helper which creates virtual slices for each disk which
 *		could be added to a diskset if necessary...
 *
 *		Iterate the input list of available disks and see what the
 *		slicing would be if the disk were added to a diskset.
 *
 *		For the resulting slices, create virtual slice descriptors
 *		and attributes for these slices and add them to the list of
 *		available slices.
 */
int
create_virtual_slices(
	dlist_t 	*disks)
{
	int		error = 0;
	dlist_t		*iter;
	boolean_t	sim = B_FALSE;
	static char	*simfile = "METASSISTSIMFILE";

	sim = ((getenv(simfile) != NULL) && (strlen(getenv(simfile)) > 0));

	/* see what slices each of the disks will have when added to a set */
	for (iter = disks; error == 0 && iter != NULL; iter = iter->next) {

	    dm_descriptor_t 	disk = (uintptr_t)iter->obj;
	    dlist_t		*slices = NULL;
	    mdvtoc_t		vtoc;
	    char		*dname;
	    int			i = 0;

	    if ((error = get_display_name(disk, &dname)) != 0) {
		break;
	    }

	    if (sim != B_TRUE) {

		/* sim disabled: use meta_repartition_drive() */

		md_error_t	mderror = mdnullerror;
		int		opts = (MD_REPART_FORCE | MD_REPART_DONT_LABEL);
		mdsetname_t	*sp;
		mddrivename_t	*dnp;

		/* disk is in the local set */
		sp = metasetname(MD_LOCAL_NAME, &mderror);
		if (!mdisok(&mderror)) {
		    volume_set_error(mde_sperror(&mderror, NULL));
		    mdclrerror(&mderror);
		    error = -1;
		    break;
		}

		dnp = metadrivename(&sp, dname, &mderror);
		if (!mdisok(&mderror)) {
		    volume_set_error(mde_sperror(&mderror, NULL));
		    mdclrerror(&mderror);
		    error = -1;
		    break;
		}

		if (meta_repartition_drive(
		    sp, dnp, opts, &vtoc, &mderror) != 0) {
		    volume_set_error(
			    gettext("failed to repartition disk %s\n"),
			    dname);
		    error = -1;
		    break;
		}

	    } else {

		/* sim enabled: use faked repartition code */
		if (virtual_repartition_drive(disk, &vtoc) != 0) {
		    volume_set_error(
			    gettext("failed simulated repartition of %s\n"),
			    dname);
		    error = -1;
		    break;
		}
	    }

	    /* BEGIN CSTYLED */
	    /*
	     * get the existing slices on the disk, if the repartition
	     * was successful, these slices need to have their size, start
	     * blk and size in blks set to 0
	     */
	    /* END CSTYLED */
	    if ((error = disk_get_slices(disk, &slices)) == 0) {
		dlist_t *iter2 = slices;
		for (; iter2 != NULL; iter2 = iter2->next) {
		    dm_descriptor_t sp = (uintptr_t)iter2->obj;
		    ((error = slice_set_start_block(sp, 0)) != 0) ||
		    (error = slice_set_size_in_blocks(sp, 0)) ||
		    (error = slice_set_size(sp, 0));
		}
		dlist_free_items(slices, NULL);
	    }

	    /* scan VTOC, find slice with the free space */
	    for (i = 0; i < vtoc.nparts; i++) {

		if (vtoc.parts[i].tag == V_USR &&
			vtoc.parts[i].flag != V_UNMNT) {

		    /* non-replica slice with free space */
		    char buf[MAXPATHLEN];
		    (void) snprintf(buf, MAXPATHLEN-1, "%ss%d", dname, i);

		    if ((error = add_virtual_slice(buf,
			(uint32_t)i,
			(uint64_t)vtoc.parts[i].start,
			(uint64_t)vtoc.parts[i].size,
			disk)) != 0) {
			break;
		    }

		} else if (vtoc.parts[i].tag == V_RESERVED) {

		    /* skip EFI reserved slice */
		    continue;

		} else if (vtoc.parts[i].tag == V_USR &&
			vtoc.parts[i].flag == V_UNMNT) {

		    /* BEGIN CSTYLED */
		    /*
		     * Make the replica slice 0 sized -- this will
		     * force the disk to be repartitioned by
		     * metaset when it is added to the disk set.
		     *
		     * XXX this is a temporary workaround until
		     * 4712873 is integrated...
		     */
		    /* BEGIN CSTYLED */
		    char buf[MAXPATHLEN];
		    (void) snprintf(buf, MAXPATHLEN-1, "%ss%d", dname, i);
		    add_slice_to_remove(buf, i);

		    /* replica slice, stop here */
		    break;
		}
	    }
	}

	return (error);
}

/*
 * FUNCTION:	add_virtual_slice(char *name, uint32_t index,
 *			uint64_t startblk, uint64_t sizeblks,
 *			dm_descriptor_t disk)
 *
 * INPUT:	name	- the name of the new virtual slice
 *		index	- the VTOC index ...
 *		startblk - the start block ...
 *		sizeblks - the size in blocks ...
 *		disk	- the parent disk ...
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helper which adds the appropriate data structures to
 *		represent a new virtual slice.
 *
 *		allocates a new descriptor
 *		adds entries to name->desc and desc->name caches
 *		allocates an attribute nvpair list
 *		fills in the relevant attributes for the slice
 *		associates the slice with its parent disk
 *		adds an entry to the list of all virtual slices
 *		generates aliases if the associated disk has aliases.
 */
int
add_virtual_slice(
	char		*name,
	uint32_t	index,
	uint64_t	startblk,
	uint64_t	sizeblks,
	dm_descriptor_t	disk)
{
	dm_descriptor_t sp;
	nvlist_t	*attrs;
	char		*sname;
	dlist_t		*aliases = NULL;
	dlist_t		*item = NULL;
	int 		error = 0;

	if ((error = nvlist_alloc(&attrs, NV_UNIQUE_NAME, 0)) != 0) {
	    return (error);
	}

	/* create descriptor */
	((error = new_descriptor(&sp)) != 0) ||
	/* cache name for the descriptor */
	(error = add_cached_name(sp, name)) ||
	/* cache descriptor for the name */
	(error = add_cached_descriptor(name, sp)) ||

	/* fill in attributes */
	(error = set_string(attrs, ATTR_DEV_CTD_NAME, name)) ||
	(error = set_uint32(attrs, DM_INDEX, index)) ||
	(error = set_uint64(attrs, DM_START, startblk)) ||
	(error = set_uint64(attrs, DM_SIZE, sizeblks)) ||
	(error = set_uint64(attrs, ATTR_DISK_FOR_SLICE, (uint64_t)disk)) ||

	/* add attributes to the cache */
	(error = get_name(sp, &sname)) ||
	(error = add_cached_attributes(sname, attrs)) ||

	/* connect slice to disk */
	(error = disk_add_virtual_slice(disk, sp)) ||
	(error = get_display_name(disk, &name)) ||
	(error = get_aliases(disk, &aliases));

	if (error != 0) {
	    return (error);
	}

	/* generate slice's aliases if the disk has aliases */
	if (aliases != NULL) {
	    char buf[MAXNAMELEN];

	    for (; aliases != NULL; aliases = aliases->next) {
		(void) snprintf(buf, MAXNAMELEN-1, "%ss%d",
			(char *)aliases->obj, index);
		error = set_alias(sp, buf);
	    }
	    dlist_free_items(aliases, free);
	}

	if ((item = dlist_new_item((void *)(uintptr_t)sp)) == NULL) {
	    return (ENOMEM);
	}

	_virtual_slices = dlist_append(item, _virtual_slices, AT_HEAD);

	oprintf(OUTPUT_DEBUG,
		gettext("  created virtual slice %s start: %llu, size: %llu\n"),
		sname, startblk, sizeblks);

	return (error);
}

/*
 * FUNCTION:	release_virtual_slices()
 *
 * PURPOSE:	Helper which cleans up the module private list of virtual
 *		slices.
 *
 *		The descriptors for the virtual slices are cleaned up
 *		in device_cache_util.free_cached_descriptors
 */
void
release_virtual_slices()
{
	dlist_free_items(_virtual_slices, NULL);
	_virtual_slices = NULL;
}

/*
 * FUNCTION:	disk_add_virtual_slice(dm_descriptor_t disk,
 *			dm_descriptor_t slice)
 *
 * INPUT:	disk	- a dm_descriptor_t disk handle
 *		slice	- a dm_descriptor_t virtual slice handle
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise
 *
 * PURPOSE:	Helper which adds a virtual slice to the input disk's
 *		list of virtual slices.
 *
 *		The disk's virtual slice dm_descriptor_t handles are
 *		stored in the disk's nvpair attribute list.
 */
static int
disk_add_virtual_slice(
	dm_descriptor_t	disk,
	dm_descriptor_t slice)
{
	nvlist_t	*attrs = NULL;
	uint64_t	*old_slices = NULL;
	uint64_t	*new_slices = NULL;
	uint_t		nelem = 0;
	int		i = 0;
	int		error = 0;

	if ((error = get_cached_attributes(disk, &attrs)) != 0) {
	    return (error);
	}

	if ((error = get_uint64_array(
	    attrs, ATTR_VIRTUAL_SLICES, &old_slices, &nelem)) != 0) {
	    if (error != ENOENT) {
		return (error);
	    }
	    error = 0;
	}

	/* make a new array */
	new_slices = (uint64_t *)calloc(nelem + 1, sizeof (uint64_t));
	if (new_slices != NULL) {

	    for (i = 0; i < nelem; i++) {
		new_slices[i] = old_slices[i];
	    }
	    new_slices[i] = slice;

	    error = set_uint64_array(
		    attrs, ATTR_VIRTUAL_SLICES, new_slices, nelem);

	    free(new_slices);

	} else {
	    error = ENOMEM;
	}

	return (error);
}

/*
 * FUNCTION:	disk_has_virtual_slices(dm_descriptor_t disk, boolean_t *bool)
 *
 * INPUT:	disk	- a dm_descriptor_t disk handle
 *
 * OUTPUT:	bool	- B_TRUE - if the disk has virtual slices
 *			  B_FALSE - otherwise
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise
 *
 * PURPOSE:	Helper which determines if the input disk has virtual slices.
 *
 *		If a disk has virtual slices, their dm_descriptor_t handles
 *		will be stored in the disk's nvpair attribute list.
 */
static int
disk_has_virtual_slices(
	dm_descriptor_t	disk,
	boolean_t	*bool)
{
	nvlist_t	*attrs = NULL;
	uint64_t	*slices = NULL;
	uint_t		nelem = 0;
	int		error = 0;

	*bool = B_FALSE;

	if ((error = get_cached_attributes(disk, &attrs)) != 0) {
	    return (error);
	}

	if ((error = get_uint64_array(
	    attrs, ATTR_VIRTUAL_SLICES, &slices, &nelem)) != 0) {
	    if (error == ENOENT) {
		error = 0;
		nelem = 0;
	    } else {
		/* count actual number of elements */
		int i = 0;
		while (i < nelem) {
		    if (slices[i] != -1) {
			++i;
		    }
		}
		nelem = i;
	    }
	}

	*bool = (nelem != 0);

	return (error);
}

/*
 * FUNCTION:	disk_get_virtual_slices(dm_descriptor_t disk, boolean_t *bool)
 *
 * INPUT:	disk	- a dm_descriptor_t disk handle
 *
 * OUTPUT:	list	- a dlist_t list of dm_descriptor_t handles for the
 *				disk's virtual slices.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise
 *
 * PURPOSE:	Helper which retrieves a list of the input disk's virtual
 *		slices.
 *
 *		If a disk has virtual slices, their dm_descriptor_t handles
 *		will be stored in the disk's nvpair attribute list.
 */
static int
disk_get_virtual_slices(
	dm_descriptor_t	disk,
	dlist_t		**list)
{
	nvlist_t	*attrs = NULL;
	uint64_t	*slices = NULL;
	uint_t		nelem = 0;
	int		error = 0;
	int		i = 0;

	if ((error = get_cached_attributes(disk, &attrs)) != 0) {
	    return (error);
	}

	if ((error = get_uint64_array(
	    attrs, ATTR_VIRTUAL_SLICES, &slices, &nelem)) != 0) {
	    if (error != ENOENT) {
		return (error);
	    }

	    return (0);
	}

	for (i = 0; i < nelem && slices[i] != -1; i++) {
	    dlist_t *item = NULL;

	    if ((item = dlist_new_item((void*)(uintptr_t)slices[i])) == NULL) {
		error = ENOMEM;
		break;
	    }

	    *list = dlist_append(item, *list, AT_TAIL);
	}

	return (error);
}

/*
 * FUNCTION:	is_virtual_slice(dm_descriptor_t desc)
 *
 * INPUT:	desc	- a dm_descriptor_t handle
 *
 * RETURNS:	boolean_t - B_TRUE if the input descriptor is for
 *				a virtual slice.
 * 			B_FALSE otherwise
 *
 * PURPOSE:	Helper which determines whether the input descriptor
 *		corresponds to a virtual slice.
 *
 *		All virtual slices are stored in a module private list.
 *		This list is iterated to see if it contains the input
 *		descriptor.
 */
boolean_t
is_virtual_slice(
	dm_descriptor_t desc)
{
        return (dlist_contains(_virtual_slices,
			(void*)(uintptr_t)desc, compare_descriptors));
}

/*
 * FUNCTION:	disk_get_available_slice_index(dm_descriptor_t disk,
 *			uint32_t *newindex)
 *
 * INPUT:	disk	- a dm_descriptor_t handle for a disk
 *
 * OUTPUT:	*newindex - a pointer to a uint32_t to hold the available
 *			index.  If no index is available, the value pointed
 *			to is not modified.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	examine the input disk's list of slices and find an unused
 *		slice index.  The replica slice (index 7 or 6) is always
 *		off-limits -- it shows up as in use.  Slice 0 should only
 *		be used as a last resort.
 *
 *		If an available index is found, it is stored into newindex.
 *		Otherwise, newindex is unchanged.  This allows the caller to
 *		pass in an index and check if it has been modified on return.
 *
 *		V_NUMPAR is used as the number of available slices,
 *		SPARC systems have V_NUMPAR == 8, X86 have V_NUMPAR == 16.
 *
 *		EFI disks have only 7.
 */
int
disk_get_available_slice_index(
	dm_descriptor_t	disk,
	uint32_t	*newindex)
{
	dlist_t		*iter	= NULL;
	dlist_t		*slices = NULL;
	uint32_t	index	= 0;
	uint16_t	*reserved = NULL;
	boolean_t 	*used 	= NULL;
	boolean_t 	is_efi	= B_FALSE;
	int		error	= 0;
	int		i	= 0;
	int		nslices = V_NUMPAR;

	if (((error = disk_get_slices(disk, &slices)) != 0) ||
	    (error = disk_get_is_efi(disk, &is_efi)) != 0) {
	    return (error);
	}

	if (is_efi == B_TRUE) {
	    /* limit possible indexes to 7 for EFI */
	    nslices = 7;
	}

	used = (boolean_t *)calloc(nslices, sizeof (boolean_t));
	if (used == NULL) {
	    oprintf(OUTPUT_DEBUG,
		    gettext("failed allocating slice index array\n"),
		    NULL);
	    return (ENOMEM);
	}

	/* eliminate indexes that are reserved */
	if ((error = disk_get_reserved_indexes(disk, &reserved)) != 0) {
	    return (error);
	}

	if (reserved != NULL) {
	    for (i = 0; i < nslices; i++) {
		if (reserved[i] == 1) {
		    used[i] = B_TRUE;
		}
	    }
	}

	/* eliminate slices that are in use (have a size > 0) */
	/* 0 sized slices unused slices */
	for (iter = slices; iter != NULL; iter = iter->next) {
	    dm_descriptor_t sp = (uintptr_t)iter->obj;
	    uint64_t	size = 0;

	    ((error = slice_get_index(sp, &index)) != 0) ||
	    (error = slice_get_size_in_blocks(sp, &size));
	    if (error != 0) {
		return (error);
	    }

	    if (size > 0) {
		used[(int)index] = B_TRUE;
	    }
	}
	dlist_free_items(slices, NULL);

	for (i = 0; i < nslices; i++) {

	    /* skip the index passed in */
	    if (i == *newindex) {
		continue;
	    }

	    if (used[i] != B_TRUE) {
		index = i;
		break;
	    }
	}

	if (i != nslices) {
	    /* return unused slice index */
	    *newindex = index;
	}

	free((void *)used);

	return (0);
}

/*
 * FUNCTION:	disk_get_media_type(dm_descriptor_t slice, uint32_t *type)
 *
 * INPUT:	slice	- a dm_descriptor_t handle for a disk
 *
 * OUTPUT:	*type	- a pointer to a uint32_t to hold the
 *			current type value for the media on which
 *			the input slice resides.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Retrieves the media type for the disk.
 *
 *		Get the media associate with the input disk descriptor
 *		and determine its type.
 */
int
disk_get_media_type(
	dm_descriptor_t	disk,
	uint32_t	*type)
{
	int		error = 0;
	dm_descriptor_t	*mdp = NULL;

	mdp = dm_get_associated_descriptors(disk, DM_MEDIA, &error);
	(void) add_descriptors_to_free(mdp);

	if (error != 0) {
	    print_get_assoc_desc_error(disk, gettext("media"), error);
	} else {
	    /* disk should have exactly 1 media */
	    if ((mdp != NULL) && (*mdp != NULL)) {
		nvlist_t *attrs = dm_get_attributes(*mdp, &error);
		if ((error == 0) && (attrs != NULL)) {
		    error = get_uint32(attrs, DM_MTYPE, type);
		}

		nvlist_free(attrs);
	    }
	    /* no media: removeable drive */
	}

	if (mdp != NULL) {
	    free(mdp);
	}

	return (error);
}

/*
 * FUNCTION:	disk_get_rpm(dm_descriptor_t disk, uint32_t *val)
 *		disk_get_sync_speed(dm_descriptor_t disk, uint32_t *val)
 *		disk_get_size_in_blocks(dm_descriptor_t disk, uint64_t *val)
 *		disk_get_blocksize(dm_descriptor_t disk, uint64_t *val)
 *		disk_get_ncylinders(dm_descriptor_t disk, uint64_t *val)
 *		disk_get_nheads(dm_descriptor_t disk, uint64_t *val)
 *		disk_get_nsectors(dm_descriptor_t disk, uint64_t *val)
 *		disk_get_is_efi(dm_descriptor_t disk, boolean_t *val)
 *		disk_get_is_online(dm_descriptor_t disk, boolean_t *val)
 *		disk_get_media_type(dm_descriptor_t disk, uint32_t *type)
 *		disk_get_has_fdisk(dm_descriptor_t disk, boolean_t *val)
 *		disk_get_start_block(dm_descriptor_t disk, uint64_t *val)
 *
 * INPUT:	disk	- a dm_descriptor_t handle for a disk
 *
 * OUTPUT:	*bool	- a pointer to a variable of the appropriate
 *			type to hold the current value for the attribute
 *			of interest.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Wrappers around disk_get_XXX_attribute that know
 *	        which attribute needs to be retrieved and also handle
 *		any necesasry type or units conversions.
 */
static int
disk_get_rpm(
	dm_descriptor_t	disk,
	uint32_t	*val)
{
	uint64_t	val64 = 0;
	int		error = 0;

	if ((error = disk_get_uint64_attribute(
	    disk, DM_RPM, &val64)) != 0) {
	    return (error);
	}

	*val = (uint32_t)val64;

	return (error);
}

int
disk_get_drive_type(
	dm_descriptor_t	disk,
	uint32_t	*val)
{
	uint64_t	val64 = 0;
	int		error = 0;

	if ((error = disk_get_uint64_attribute(
	    disk, DM_DRVTYPE, &val64)) != 0) {
	    return (error);
	}

	*val = (uint32_t)val64;

	return (error);
}

static int
disk_get_sync_speed(
	dm_descriptor_t	disk,
	uint32_t	*val)
{
	uint64_t	val64 = 0;
	int		error = 0;

	if ((error = disk_get_uint64_attribute(
	    disk, DM_SYNC_SPEED, &val64)) != 0) {
	    return (error);
	}

	*val = (uint32_t)val64;

	return (error);
}

/* returns number of usable blocks */
int
disk_get_size_in_blocks(
	dm_descriptor_t	disk,
	uint64_t	*val)
{
	return (disk_get_uint64_attribute(disk, DM_NACCESSIBLE, val));
}

/* returns first usable block on disk */
int
disk_get_start_block(
	dm_descriptor_t	disk,
	uint64_t	*val)
{
	return (disk_get_uint64_attribute(disk, DM_START, val));
}

int
disk_get_blocksize(
	dm_descriptor_t	disk,
	uint64_t	*val)
{
	return (disk_get_uint64_attribute(disk, DM_BLOCKSIZE, val));
}

int
disk_get_ncylinders(
	dm_descriptor_t	disk,
	uint64_t	*val)
{
	return (disk_get_uint64_attribute(disk, DM_NCYLINDERS, val));
}

int
disk_get_nheads(
	dm_descriptor_t	disk,
	uint64_t	*val)
{
	return (disk_get_uint64_attribute(disk, DM_NHEADS, val));
}

int
disk_get_nsectors(
	dm_descriptor_t	disk,
	uint64_t	*val)
{
	return (disk_get_uint64_attribute(disk, DM_NSECTORS, val));
}

/*
 * FUNCTION:	disk_get_is_online(dm_descriptor_t disk, boolean_t *val)
 *
 * INPUT:	disk	- a dm_descriptor_t handle for a disk
 *
 * OUTPUT:	*bool	- a pointer to a boolean_t to hold the result.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Determine if the input disk is "online".
 *
 *		Check the status bit of the drive, if it is 1 the drive
 *		is online, if it is 0 the drive is offline.
 */
int
disk_get_is_online(
	dm_descriptor_t	disk,
	boolean_t	*val)
{
	uint64_t	status = 0;
	int		error = 0;

	*val = B_FALSE;

	error = disk_get_uint64_attribute(disk, DM_STATUS, &status);
	if (error == 0) {
	    *val = (status == 1) ? B_TRUE : B_FALSE;
	}

	return (error);
}

/*
 * FUNCTION:	disk_get_is_efi(dm_descriptor_t disk, boolean_t *bool)
 *
 * INPUT:	disk	- a dm_descriptor_t handle for a disk
 *
 * OUTPUT:	*bool	- a pointer to a boolean_t to hold the result.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Determine if the input disk is labeled with an EFI label.
 *
 *		The label type is actually a property of the media
 *		associated with the disk, so retrieve the media and
 *		check if it is EFI labeled.
 */
int
disk_get_is_efi(
	dm_descriptor_t	disk,
	boolean_t	*bool)
{
	return (disk_get_boolean_attribute(disk, DM_EFI, bool));
}

/*
 * FUNCTION:	disk_get_has_fdisk(dm_descriptor_t disk, boolean_t *bool)
 *
 * INPUT:	disk	- a dm_descriptor_t handle for a disk
 *
 * OUTPUT:	*bool	- a pointer to a boolean_t to hold the result.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Determine if the input disk has an FDISK partition.
 */
int
disk_get_has_fdisk(
	dm_descriptor_t	disk,
	boolean_t	*bool)
{
	return (disk_get_boolean_attribute(disk, DM_FDISK, bool));
}

/*
 * FUNCTION:	disk_get_has_solaris_partition(dm_descriptor_t disk, boolean_t *bool)
 *
 * INPUT:	disk	- a dm_descriptor_t handle for a disk
 *
 * OUTPUT:	*bool	- a pointer to a boolean_t to hold the result.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Determine if the input disk has a Solaris FDISK partition.
 */
int
disk_get_has_solaris_partition(
	dm_descriptor_t	disk,
	boolean_t	*bool)
{
	boolean_t	has_fdisk = B_FALSE;
	int		error = 0;

	if ((error = disk_get_has_fdisk(disk, &has_fdisk)) != 0) {
	    return (error);
	}

	*bool = B_FALSE;

	if (has_fdisk == B_TRUE) {
	    /* get disk's media */
	    dm_descriptor_t *media;
	    media = dm_get_associated_descriptors(disk, DM_MEDIA, &error);
	    (void) add_descriptors_to_free(media);
	    if (error != 0) {
		print_get_assoc_desc_error(disk, gettext("media"), error);
	    } else if ((media != NULL) && (*media != NULL)) {
		/* get media's partitions */
		dm_descriptor_t *parts;
		parts = dm_get_associated_descriptors(
			media[0], DM_PARTITION, &error);
		(void) add_descriptors_to_free(parts);
		if (error != 0) {
		    print_get_assoc_desc_error(media[0],
			    gettext("partitions"), error);
		} else {
		    /* search partitions for one with type Solaris */
		    int i = 0;
		    for (; (parts != NULL) && (parts[i] != NULL) &&
			(error == 0) && (*bool == B_FALSE); i++) {
			nvlist_t *attrs = dm_get_attributes(parts[i], &error);
			uint32_t ptype = 0;
			if ((error == 0) && (attrs != NULL)) {
			    error = get_uint32(attrs, DM_PTYPE, &ptype);
			    if ((error == 0) &&
			        (ptype == SUNIXOS || ptype == SUNIXOS2)) {
				    *bool = B_TRUE;
			    }
			}
			nvlist_free(attrs);
		    }
		}
		   
		free(parts);
		free(media);
	    }

	    /* if there was no media, it was a removeable drive */
	}

	return (error);
}

static int
disk_get_boolean_attribute(
	dm_descriptor_t	disk,
	char		*attr,
	boolean_t	*bool)
{
	nvlist_t	*attrs	= NULL;
	int		error	= 0;

	*bool = B_FALSE;

	if ((strcmp(attr, DM_EFI) == 0) ||
	    (strcmp(attr, DM_FDISK) == 0)) {

	    /*
	     * these attributes are actually on the media,
	     * not the disk... so get the media descriptor
	     * for this disk
	     */
	    dm_descriptor_t *media;

	    media = dm_get_associated_descriptors(disk, DM_MEDIA, &error);
	    (void) add_descriptors_to_free(media);

	    if (error != 0) {
		print_get_assoc_desc_error(disk, gettext("media"), error);
	    } else if ((media != NULL) && (*media != NULL)) {
		/* if there's no media, it is a removeable drive */
		error = get_cached_attributes(media[0], &attrs);
	    }
	    free(media);

	} else {
	    error = get_cached_attributes(disk, &attrs);
	    if (error != 0) {
		print_get_desc_attr_error(disk, gettext("drive"), attr, error);
	    }
	}

	if (error != 0) {
	    return (error);
	}

	if (nvlist_lookup_boolean(attrs, attr) == 0) {
	    *bool = B_TRUE;
	}

	return (error);
}

static int
disk_get_uint64_attribute(
	dm_descriptor_t	disk,
	char		*attr,
	uint64_t	*val)
{
	nvlist_t	*attrs	= NULL;
	uint32_t	ui32	= 0;
	int		error	= 0;

	/*
	 * these attributes are actually on the media,
	 * not the disk... so get the media descriptor
	 * for this disk
	 */
	if ((strcmp(attr, DM_SIZE) == 0) ||
	    (strcmp(attr, DM_START) == 0) ||
	    (strcmp(attr, DM_NACCESSIBLE) == 0) ||
	    (strcmp(attr, DM_BLOCKSIZE) == 0) ||
	    (strcmp(attr, DM_NCYLINDERS) == 0) ||
	    (strcmp(attr, DM_NHEADS) == 0) ||
	    (strcmp(attr, DM_NSECTORS) == 0)) {

	    dm_descriptor_t *media;

	    media = dm_get_associated_descriptors(disk, DM_MEDIA, &error);
	    (void) add_descriptors_to_free(media);

	    if (error != 0) {
		print_get_assoc_desc_error(disk, gettext("media"), error);
	    } else if ((media == NULL) || (*media == NULL)) {
		print_get_assoc_desc_error(disk, gettext("media"), error);
		error = -1;
	    } else {
		error = get_cached_attributes(media[0], &attrs);
		free(media);
	    }

	} else {
	    error = get_cached_attributes(disk, &attrs);
	    if (error != 0) {
		print_get_desc_attr_error(disk, gettext("drive"), attr, error);
	    }
	}

	if (error != 0) {
	    return (error);
	}

	if (strcmp(attr, DM_SIZE) == 0 ||
	    strcmp(attr, DM_NACCESSIBLE) == 0 ||
	    strcmp(attr, DM_START) == 0) {
	    error = get_uint64(attrs, attr, val);
	} else if (strcmp(attr, DM_BLOCKSIZE) == 0 ||
	    strcmp(attr, DM_NCYLINDERS) == 0 ||
	    strcmp(attr, DM_NHEADS) == 0 ||
	    strcmp(attr, DM_NSECTORS) == 0 ||
	    strcmp(attr, DM_RPM) == 0 ||
	    strcmp(attr, DM_DRVTYPE) == 0 ||
	    strcmp(attr, DM_SYNC_SPEED) == 0 ||
	    strcmp(attr, DM_STATUS) == 0) {
	    error = get_uint32(attrs, attr, &ui32);
	    *val = (uint64_t)ui32;
	}

	return (error);
}

/*
 * FUNCTION:	group_similar_hbas(dlist_t *hbas, dlist_t **list)
 *
 * INPUT:	hbas	- a list of HBA dm_descriptor_t handles.
 *
 * OUTPUT:	**list	- a pointer to a list to hold the lists of HBAs
 *			grouped by characteristics.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Examine the input HBAs and collate them into separate
 *		lists, grouped by their type and the protocols they
 *		support.
 *
 *		The returned list of list is arranged in decreasing order
 *		of preference, "better" HBAs come first.
 *
 *		find all MPXIO controllers
 *		find all similar FC HBAs
 *		find all similar SCSI HBAs
 *		    fast{wide}80
 *		    fast{wide}40
 *		    fast{wide}20
 *		    clock         uint32  ??
 *		find all similar ATA/IDE HBAs
 *		find all similar USB HBAs
 */
int
group_similar_hbas(
	dlist_t	*hbas,
	dlist_t **list)
{
	/* preference order of HBAs */
	enum {
		HBA_FIBRE_MPXIO = 0,
		HBA_SCSI_MPXIO,
		HBA_FIBRE,
		HBA_SCSI_FW80,
		HBA_SCSI_FW40,
		HBA_SCSI_FW20,
		HBA_SCSI_F80,
		HBA_SCSI_F40,
		HBA_SCSI_F20,
		HBA_SCSI,
		HBA_ATA,
		HBA_USB,
		HBA_LAST
	};

	dlist_t		*groups	= NULL;
	dlist_t		*iter = NULL;
	dlist_t		*item = NULL;
	dlist_t		*lists[HBA_LAST];

	int		error = 0;
	int		i = 0;

	(void) memset(lists, '\0', HBA_LAST * sizeof (dlist_t *));

	for (iter = hbas;
	    (iter != NULL) && (error == 0);
	    iter = iter->next) {

	    dm_descriptor_t hba = (uintptr_t)iter->obj;
	    char	*type = NULL;

	    /* if item doesn't go into a list it must be freed */
	    if ((item = dlist_new_item((void *)(uintptr_t)hba)) == NULL) {
		error = ENOMEM;
		continue;
	    }

	    if ((error = hba_get_type(hba, &type)) != 0) {
		free(item);
		continue;
	    }

	    if (strcmp(type, DM_CTYPE_FIBRE) == 0) {

		boolean_t	ismpxio = B_FALSE;

		if ((error = hba_is_multiplex(hba, &ismpxio)) == 0) {
		    if (ismpxio) {
			lists[HBA_FIBRE_MPXIO] =
			    dlist_append(item,
				    lists[HBA_FIBRE_MPXIO], AT_TAIL);
		    } else {
			lists[HBA_FIBRE] =
			    dlist_append(item,
				    lists[HBA_FIBRE], AT_TAIL);
		    }
		} else {
		    free(item);
		}

	    } else if (strcmp(type, DM_CTYPE_SCSI) == 0) {

		/* determine subtype */
		boolean_t	iswide = B_FALSE;
		boolean_t	ismpxio = B_FALSE;
		boolean_t	is80 = B_FALSE;
		boolean_t	is40 = B_FALSE;
		boolean_t	is20 = B_FALSE;

		((error = hba_supports_wide(hba, &iswide)) != 0) ||
		(error = hba_is_multiplex(hba, &ismpxio)) ||
		(error = hba_is_fast_80(hba, &is80)) ||
		(error = hba_is_fast_40(hba, &is40)) ||
		(error = hba_is_fast_20(hba, &is20));

		if (error == 0) {

		    if (ismpxio) {

			lists[HBA_SCSI_MPXIO] =
			    dlist_append(item,
				    lists[HBA_SCSI_MPXIO], AT_TAIL);

		    } else if (is80) {

			if (iswide) {
			    lists[HBA_SCSI_FW80] =
				dlist_append(item,
					lists[HBA_SCSI_FW80], AT_TAIL);
			} else {
			    lists[HBA_SCSI_F80] =
				dlist_append(item,
					lists[HBA_SCSI_F80], AT_TAIL);
			}

		    } else if (is40) {

			if (iswide) {
			    lists[HBA_SCSI_FW40] =
				dlist_append(item,
					lists[HBA_SCSI_FW40], AT_TAIL);
			} else {
			    lists[HBA_SCSI_F40] =
				dlist_append(item,
					lists[HBA_SCSI_F40], AT_TAIL);
			}

		    } else if (is20) {

			if (iswide) {
			    lists[HBA_SCSI_FW20] =
				dlist_append(item,
					lists[HBA_SCSI_FW20], AT_TAIL);
			} else {
			    lists[HBA_SCSI_F20] =
				dlist_append(item,
					lists[HBA_SCSI_F20], AT_TAIL);
			}

		    } else {
			lists[HBA_SCSI] =
			    dlist_append(item, lists[HBA_SCSI], AT_TAIL);
		    }

		} else {
		    free(item);
		}

	    } else if (strcmp(type, DM_CTYPE_ATA) == 0) {
		lists[HBA_ATA] =
		    dlist_append(item, lists[HBA_ATA], AT_TAIL);
	    } else if (strcmp(type, DM_CTYPE_USB) == 0) {
		lists[HBA_USB] =
		    dlist_append(item, lists[HBA_USB], AT_TAIL);
	    } else if (strcmp(type, DM_CTYPE_UNKNOWN) == 0) {
		oprintf(OUTPUT_DEBUG,
			gettext("found an HBA with unknown type\n"));
		free(item);
	    }
	}

	if (error == 0) {
	    /* collect individual lists into a list of lists */
	    for (i = 0; (i < HBA_LAST) && (error == 0); i++) {
		if (lists[i] != NULL) {
		    if ((item = dlist_new_item(lists[i])) == NULL) {
			error = ENOMEM;
		    } else {
			groups = dlist_append(item, groups, AT_TAIL);
		    }
		}
	    }
	}

	if (error != 0) {
	    for (i = 0; i < HBA_LAST; i++) {
		dlist_free_items(lists[i], NULL);
		lists[i] = NULL;
	    }

	    if (groups != NULL) {
		dlist_free_items(groups, NULL);
	    }
	}

	*list = groups;

	return (error);
}

/*
 * FUNCTION:	hba_group_usable_disks(dm_descriptor_t hba, dlist_t **list)
 *
 * INPUT:	hba	- a dm_descriptor_t handle for a slice
 *
 * OUTPUT:	**list	- a pointer to a list to hold the lists of disks
 *			grouped by characteristics.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Examine the disks assocated with the HBA and collates them
 *		into separate lists, grouped by similar characteristics.
 *
 *		get disks on HBA
 *		check disks against _usable_disks list
 *		group disks by similarities:
 *			sync-speed    uint32
 *			wide          boolean
 *			rpm           uint32
 *
 *		XXX this function is currently unused.  At some point,
 *		it may be useful to group disks by performance
 *		characteristics and use "better" disks before others.
 */
int
hba_group_usable_disks(
	dm_descriptor_t	hba,
	dlist_t		**list)
{
	dm_descriptor_t *disk = NULL;
	char 		*name = NULL;
	int		i = 0;
	int		error = 0;

	disk = dm_get_associated_descriptors(hba, DM_DRIVE, &error);
	(void) add_descriptors_to_free(disk);

	if (error != 0) {
	    print_get_assoc_desc_error(hba, gettext("drive"), error);
	    return (error);
	} else if ((disk == NULL) || (*disk == NULL)) {
	    print_get_assoc_desc_error(hba, gettext("drive"), error);
	    error = -1;
	}

	for (i = 0; (disk[i] != NULL) && (error == 0); i++) {

	    uint32_t dtype = DM_DT_UNKNOWN;
	    dlist_t *usable = NULL;

	    /* ignore non fixed media drives */
	    if (((error = disk_get_drive_type(disk[i], &dtype)) != 0) ||
		(dtype != DM_DT_FIXED)) {
		continue;
	    }

	    if (dlist_contains(usable, &disk[i],
		compare_descriptor_names) == B_TRUE) {

		uint64_t bsize	= 0;
		uint64_t ncyls	= 0;
		uint64_t nsects	= 0;
		uint64_t nheads	= 0;
		uint32_t rpm	= 0;
		uint32_t sync	= 0;

		name = NULL;
		((error = get_display_name(disk[i], &name)) != 0) ||
		(error = disk_get_blocksize(disk[i], &bsize)) ||
		(error = disk_get_nheads(disk[i], &nheads)) ||
		(error = disk_get_nsectors(disk[i], &nsects)) ||
		(error = disk_get_ncylinders(disk[i], &ncyls)) ||
		(error = disk_get_rpm(disk[i], &rpm)) ||
		(error = disk_get_sync_speed(disk[i], &sync));
		if (error != 0) {
		    continue;
		}

		oprintf(OUTPUT_VERBOSE,
			gettext("found an available disk: %s\n\t"
			"sync_speed = %u, rpm = %u, "
			"nsect = %llu, blksiz = %llu\n"),
			name, sync, rpm, nsects, bsize);

		/* add to the appropriate list */
	    }
	}

	if (disk != NULL) {
	    free(disk);
	}

	return (error);
}

/*
 * FUNCTION:	hba_get_n_avail_disks(dm_descriptor_t hba, uint16_t *val)
 *		hba_set_n_avail_disks(dm_descriptor_t hba, uint16_t val)
 *
 * INPUT:	hba	- a dm_descriptor_t handle for a slice
 *
 * OUTPUT:	*val	- a pointer to a uint16_t to hold the current number
 *				of available disks for the input HBA.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 */
int
hba_set_n_avail_disks(
	dm_descriptor_t	hba,
	uint16_t	val)
{
	nvlist_t	*attrs;
	int		error = 0;

	((error = get_cached_attributes(hba, &attrs)) != 0) ||
	(error = set_uint16(attrs, ATTR_HBA_N_DISKS, val));

	return (error);
}

int
hba_get_n_avail_disks(
	dm_descriptor_t	hba,
	uint16_t	*val)
{
	nvlist_t	*attrs;
	int		error = 0;

	*val = 0;

	((error = get_cached_attributes(hba, &attrs)) != 0) ||
	(error = get_uint16(attrs, ATTR_HBA_N_DISKS, val));

	return (error);
}

/*
 * FUNCTION:	hba_get_type(dm_descriptor_t hba, char **type)
 *
 * INPUT:	hba	- a dm_descriptor_t handle for a HBA
 *
 * OUTPUT:	**type	- a char * to hold the current type value for
 *			the HBA.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Retrieves the type attribute for the HBA.
 */
int
hba_get_type(
	dm_descriptor_t	hba,
	char		**type)
{
	nvlist_t	*attrs;
	int		error = 0;

	*type = NULL;

	((error = get_cached_attributes(hba, &attrs)) != 0) ||
	(error = get_string(attrs, DM_CTYPE, type));

	return (error);
}

/*
 * FUNCTION:	hba_is_fast(dm_descriptor_t hba, boolean_t *bool)
 *		hba_is_fast20(dm_descriptor_t hba, boolean_t *bool)
 *		hba_is_fast40(dm_descriptor_t hba, boolean_t *bool)
 *		hba_is_fast80(dm_descriptor_t hba, boolean_t *bool)
 *		hba_is_multiplex(dm_descriptor_t hba, boolean_t *bool)
 *		hba_is_wide(dm_descriptor_t hba, boolean_t *bool)
 *
 * INPUT:	hba	- a dm_descriptor_t handle for a HBA
 *
 * OUTPUT:	*bool	- a pointer to a boolean_t to hold the
 *			boolean value of the predicate.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Wrappers around hba_supports_protocol which determines
 *		if the input HBA supports the protocol of interest.
 */
int
hba_is_fast(
	dm_descriptor_t	hba,
	boolean_t	*bool)
{
	return (hba_supports_protocol(hba, DM_FAST, bool));
}

int
hba_is_fast_20(
	dm_descriptor_t	hba,
	boolean_t	*bool)
{
	return (hba_supports_protocol(hba, DM_FAST20, bool));
}

int
hba_is_fast_40(
	dm_descriptor_t	hba,
	boolean_t	*bool)
{
	return (hba_supports_protocol(hba, DM_FAST40, bool));
}

int
hba_is_fast_80(
	dm_descriptor_t	hba,
	boolean_t	*bool)
{
	return (hba_supports_protocol(hba, DM_FAST80, bool));
}

int
hba_is_multiplex(
	dm_descriptor_t	hba,
	boolean_t	*bool)
{
	return (hba_supports_protocol(hba, DM_MULTIPLEX, bool));
}

int
hba_supports_wide(
	dm_descriptor_t	hba,
	boolean_t	*bool)
{
	nvlist_t	*attrs	= NULL;
	int		error	= 0;

	*bool = B_FALSE;

	if ((error = get_cached_attributes(hba, &attrs)) != 0) {
	    return (error);
	}

	*bool = (0 == nvlist_lookup_boolean(attrs, DM_WIDE));

	return (error);
}

/*
 * FUNCTION:	hba_supports_protocol(dm_descriptor_t hba, char *attr,
 *			boolean_t *bool)
 *
 * INPUT:	hba	- a dm_descriptor_t handle for a HBA
 *		attr	- a protocol "name"
 *
 * OUTPUT:	*bool	- a pointer to a boolean_t to hold the
 *			boolean value of the predicate.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Checks the HBAs attributes to see if it is known to
 *		support the protocol of interest.
 *
 *		If the protocol is supported, it will have an entry
 *		in the nvpair attribute list that can be retrieved.
 *
 *		If the entry cannot be retrieved, the protocol is not
 *		supported.
 */
int
hba_supports_protocol(
	dm_descriptor_t	hba,
	char		*attr,
	boolean_t	*bool)
{
	nvlist_t	*attrs	= NULL;
	int		error	= 0;

	*bool = B_FALSE;

	if ((error = get_cached_attributes(hba, &attrs)) != 0) {
	    return (error);
	}

	*bool = (0 == nvlist_lookup_boolean(attrs, attr));

	return (error);
}

/*
 * FUNCTION:	slice_set_size(dm_descriptor_t slice, uint64_t size)
 *
 * INPUT:	slice	- a dm_descriptor_t handle for a slice
 *
 * OUTPUT:	size	- a uint64_t value representing the size of the
 *			slice.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Wrapper around slice_set_uint64_attribute which converts
 *		the input size in bytes to blocks prior to storing it.
 *
 *		This function is used when an existing slice gets resized
 *		to provide space for a new slice. It is necessary to update
 *		the slice's size so that it is accurate.
 */
int
slice_set_size(
	dm_descriptor_t	slice,
	uint64_t	size)
{
	dm_descriptor_t	disk	= NULL;
	uint64_t	blksize	= 0;
	int		error	= 0;

	((error = slice_get_disk(slice, &disk)) != 0) ||
	(error = disk_get_blocksize(disk, &blksize)) ||
	(error = slice_set_size_in_blocks(slice, (uint64_t)(size / blksize)));

	return (error);
}

/*
 * FUNCTION:	slice_set_size_in_blocks(dm_descriptor_t slice, uint64_t size)
 *
 * INPUT:	slice	- a dm_descriptor_t handle for a slice
 *
 * OUTPUT:	size	- a uint64_t value representing the size of the
 *			slice.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Wrapper around slice_set_uint64_attribute to set the slice
 *		size.
 *
 *		This function is used when an existing slice gets resized
 *		to provide space for a new slice. It is necessary to update
 *		the slice's size so that it is accurate.
 */
int
slice_set_size_in_blocks(
	dm_descriptor_t	slice,
	uint64_t	size)
{
	return (slice_set_attribute(slice, DM_SIZE, size));
}

/*
 * FUNCTION:	slice_set_start_block(dm_descriptor_t slice, uint64_t start)
 *
 * INPUT:	slice	- a dm_descriptor_t handle for a slice
 *
 * OUTPUT:	size	- a uint64_t value representing the start block of the
 *			slice.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Wrapper around slice_set_attribute.
 *
 *		This function is used when an existing slice gets adjusted
 *		due to being resized or combined with another slice.
 */
int
slice_set_start_block(
	dm_descriptor_t	slice,
	uint64_t	start)
{
	return (slice_set_attribute(slice, DM_START, start));
}

/*
 * FUNCTION:	slice_get_start_block(dm_descriptor_t slice, uint64_t *val)
 *		slice_get_size_in_blocks(dm_descriptor_t slice, uint64_t *val)
 *		slice_get_start(dm_descriptor_t slice, uint64_t *val)
 *		slice_get_size(dm_descriptor_t slice, uint64_t *val)
 *		slice_get_index(dm_descriptor_t slice, uint64_t *val)
 *
 * INPUT:	slice	- a dm_descriptor_t handle for a slice
 *
 * OUTPUT:	*val	- a pointer to a uint64_t to hold the
 *			current value of the desired attribute.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Wrappers around slice_get_uint64_attribute which retrieve
 *		specific attribute values.
 */
int
slice_get_start_block(
	dm_descriptor_t	slice,
	uint64_t	*val)
{
	return (slice_get_uint64_attribute(slice, DM_START, val));
}

int
slice_get_size_in_blocks(
	dm_descriptor_t	slice,
	uint64_t	*val)
{
	return (slice_get_uint64_attribute(slice, DM_SIZE, val));
}

int
slice_get_start(
	dm_descriptor_t	slice,
	uint64_t	*val)
{
	dm_descriptor_t	disk	= NULL;
	uint64_t	blksize	= 0;
	uint64_t	nblks	= 0;
	int		error	= 0;

	((error = slice_get_disk(slice, &disk)) != 0) ||
	(error = disk_get_blocksize(disk, &blksize)) ||
	(error = slice_get_start_block(slice, &nblks));

	if (error == 0) {
	    *val = (blksize * nblks);
	}

	return (error);
}

int
slice_get_size(
	dm_descriptor_t	slice,
	uint64_t	*val)
{
	dm_descriptor_t	disk	= NULL;
	uint64_t	blksize	= 0;
	uint64_t	nblks	= 0;
	int		error	= 0;

	*val = 0;

	((error = slice_get_disk(slice, &disk)) != 0) ||
	(error = slice_get_size_in_blocks(slice, &nblks)) ||
	(error = disk_get_blocksize(disk, &blksize));

	if (error == 0) {
	    *val = (blksize * nblks);
	}

	return (error);
}

int
slice_get_index(
	dm_descriptor_t	slice,
	uint32_t	*val)
{
	uint64_t	index = 0;
	int		error = 0;

	if ((error = slice_get_uint64_attribute(
	    slice, DM_INDEX, &index)) != 0) {
	    return (error);
	}

	*val = (uint32_t)index;

	return (0);
}

/*
 * FUNCTION:	slice_set_uint64_attribute(dm_descriptor_t slice,
 *			char *attr, uint64_t val)
 * 		slice_get_uint64_attribute(dm_descriptor_t slice,
 *			char *attr, uint64_t *val)
 *
 * INPUT:	slice	- a dm_descriptor_t handle for a slice
 *		attr    - a char * attribute name
 *		val	- auint64_t value
 *
 * OUTPUT:	*val	- a pointer to a uint64_t to hold the
 *			current value of the named attribute.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helpers to set/get the value for a slice's attribute.
 *
 *		Consolidate the details of getting/setting slice
 *		attributes.  Some attributes are actually stored as
 *		uint32_t or uint16_t values, these functions mask
 *		the type conversions.
 */
static int
slice_get_uint64_attribute(
	dm_descriptor_t	slice,
	char		*attr,
	uint64_t	*val)
{
	nvlist_t	*attrs	= NULL;
	uint32_t	ui32	= 0;
	int		error	= 0;

	if ((error = get_cached_attributes(slice, &attrs)) != 0) {
	    return (error);
	}

	if (strcmp(attr, DM_INDEX) == 0) {
	    error = get_uint32(attrs, attr, &ui32);
	    *val = (uint64_t)ui32;
	} else if (strcmp(attr, DM_START) == 0) {
	    error = get_uint64(attrs, attr, val);
	} else if (strcmp(attr, DM_SIZE) == 0) {
	    error = get_uint64(attrs, attr, val);
	} else if (strcmp(attr, ATTR_DISK_FOR_SLICE) == 0) {
	    error = get_uint64(attrs, attr, val);
	}

	if (error != 0) {
	    print_get_desc_attr_error(slice, "slice", attr, error);
	}

	return (error);
}

/*
 * Set a slice attribute.  The attribute is only set in the cached
 * copy of the slice's nvpair attribute list.  This function does
 * NOT affect the underlying physical device.
 */
static int
slice_set_attribute(
	dm_descriptor_t	slice,
	char		*attr,
	uint64_t	val)
{
	nvlist_t	*attrs = NULL;
	int		error = 0;

	if ((error = get_cached_attributes(slice, &attrs)) != 0) {
	    return (error);
	}

	if (strcmp(attr, DM_INDEX) == 0) {
	    error = set_uint32(attrs, attr, (uint32_t)val);
	} else if (strcmp(attr, DM_START) == 0) {
	    error = set_uint64(attrs, attr, val);
	} else if (strcmp(attr, DM_SIZE) == 0) {
	    error = set_uint64(attrs, attr, val);
	} else if (strcmp(attr, ATTR_DISK_FOR_SLICE) == 0) {
	    error = set_uint64(attrs, attr, val);
	}

	if (error != 0) {
	    print_set_desc_attr_error(slice, "slice", attr, error);
	}

	return (error);
}

/*
 * FUNCTION:	virtual_slice_get_disk(dm_descriptor_t slice,
 *			dm_descriptor_t *diskp)
 *
 * INPUT:	slice	- a dm_descriptor_t virtual slice handle
 *		diskp	- pointer to a dm_descriptor_t disk handle
 *				to return the slice's disk
 *
 * OUTPUT:	the disk associated with the virtual slice.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise
 *
 * PURPOSE:	Helper which determines the disk that the input virtual
 *		slice "belongs" to.
 *
 *		The virtual slice's disk is stored in the slice's nvpair
 *		attribute list when the slice gets created.
 */
static int
virtual_slice_get_disk(
	dm_descriptor_t	slice,
	dm_descriptor_t	*diskp)
{
	uint64_t disk = 0;
	int	error = 0;

	if ((error = slice_get_uint64_attribute(
	    slice, ATTR_DISK_FOR_SLICE, &disk)) != 0) {
	    return (error);
	}

	*diskp = (dm_descriptor_t)disk;

	if (disk == 0) {
	    print_get_desc_attr_error(slice, "virtual slice", "disk", error);
	    return (-1);
	}

	return (0);
}

/*
 * FUNCTION:	slice_get_disk(dm_descriptor_t disk, dm_descriptor_t *diskp)
 *
 * INPUT:	slice	- a dm_descriptor_t handle for a slice
 *
 * OUTPUT:	diskp	- a pointer to a dm_descriptor_t to hold the
 *			disk associated with the input slice
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helper which retrieves the disk for a slice device.
 *
 *		A slice is actually connected to its disk thru an intermediate
 *		device known as the "media". The media concept exists to
 *		model drives with removeable disk media. For the purposes
 *		of layout, such devices aren't relevant and the intermediate
 *		media can mostly be ignored.
 */
int
slice_get_disk(
	dm_descriptor_t	slice,
	dm_descriptor_t *diskp)
{
	dm_descriptor_t	*media = NULL;

	int	i = 0;
	int	error = 0;

	*diskp = 0;

	if (is_virtual_slice(slice)) {
	    return (virtual_slice_get_disk(slice, diskp));
	}

	media = dm_get_associated_descriptors(slice, DM_MEDIA, &error);
	(void) add_descriptors_to_free(media);

	if (error != 0) {
	    print_get_assoc_desc_error(slice, gettext("media"), error);
	} else if ((media == NULL) || (*media == NULL)) {
	    print_get_assoc_desc_error(slice, gettext("media"), error);
	    error = -1;
	}

	if (error != 0) {
	    return (error);
	}

	/* slice should have exactly 1 media */
	for (i = 0; (media[i] != NULL) && (*diskp == NULL); i++) {
	    /* get disk from media */
	    dm_descriptor_t *disks = NULL;
	    disks = dm_get_associated_descriptors(media[i], DM_DRIVE, &error);
	    (void) add_descriptors_to_free(disks);

	    if ((error == 0) && (disks != NULL) && (disks[0] != NULL)) {
		*diskp = disks[0];
	    }
	    free(disks);
	}

	if (media != NULL) {
	    free(media);
	}

	if (*diskp == 0) {
	    print_get_desc_attr_error(slice,
		    gettext("slice"), gettext("disk"), ENODEV);
	    error = -1;
	}

	return (error);
}

/*
 * FUNCTION:	slice_get_hbas(dm_descriptor_t slice, dlist_t **list)
 *
 * INPUT:	slice	- a dm_descriptor_t handle for a slice
 *
 * OUTPUT:	list	- a pointer to a dlist_t list to hold the
 *			HBAs associated with the input slice
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helper which retrieves the known HBAs for a slice device.
 *
 */
int
slice_get_hbas(
	dm_descriptor_t	slice,
	dlist_t		**list)
{
	dm_descriptor_t	disk	= NULL;
	int		error	= 0;

	*list = NULL;

	((error = slice_get_disk(slice, &disk)) != 0) ||
	(error = disk_get_hbas(disk, list));

	if (*list == NULL) {
	    print_get_desc_attr_error(slice, "slice", "HBA", ENODEV);
	    error = -1;
	}

	return (error);
}

/*
 * FUNCTION:	disk_get_associated_desc(dm_descriptor_t disk,
 *			dm_desc_type_t assoc_type, char *assoc_type_str,
 *			dlist_t **list)
 *
 * INPUT:	disk	- a dm_descriptor_t handle for a disk
 *		assoc_type - the type of associated object to get
 *		assoc_type_str - a char * string for the associated type
 *
 * OUTPUT:	list	- a pointer to a dlist_t list to hold the
 *			objects associated with the input disk
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helper which retrieves the associated objects of the
 *		requested type for a disk device.
 */
static int
disk_get_associated_desc(
	dm_descriptor_t	disk,
	dm_desc_type_t 	assoc_type,
	char		*assoc_type_str,
	dlist_t		**list)
{
	int	i = 0;
	int	error = 0;

	dm_descriptor_t	*assoc =
	    dm_get_associated_descriptors(disk, assoc_type, &error);

	(void) add_descriptors_to_free(assoc);

	if (error == 0) {
	    for (i = 0;
		(assoc != NULL) && (assoc[i] != NULL) && (error == 0);
		i++) {
		dlist_t *item = dlist_new_item((void *)(uintptr_t)assoc[i]);
		if (item == NULL) {
		    error = ENOMEM;
		} else {
		    *list = dlist_append(item, *list, AT_TAIL);
		}
	    }
	} else {
	    print_get_assoc_desc_error(disk, assoc_type_str, error);
	}

	if (assoc != NULL) {
	    free(assoc);
	}

	if (error != 0) {
	    dlist_free_items(*list, NULL);
	    *list = NULL;
	}

	return (error);
}

/*
 * FUNCTION:	disk_get_hbas(dm_descriptor_t disk, dlist_t **list)
 *
 * INPUT:	disk	- a dm_descriptor_t handle for a disk
 *
 * OUTPUT:	list	- a pointer to a dlist_t list to hold the
 *			HBAs associated with the input disk
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helper which retrieves the known HBAs for a disk device.
 *
 */
int
disk_get_hbas(
	dm_descriptor_t	disk,
	dlist_t		**list)
{
	return (disk_get_associated_desc(disk, DM_CONTROLLER,
			gettext("controller"), list));
}

/*
 * FUNCTION:	disk_get_paths(dm_descriptor_t disk, dlist_t **list)
 *
 * INPUT:	disk	- a dm_descriptor_t handle for a disk
 *
 * OUTPUT:	list	- a pointer to a dlist_t list to hold the
 *			paths associated with the input disk
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helper which retrieves the known paths for a disk device.
 *
 *		Paths are managed by the MPXIO driver, they represent hardware
 *		paths to the disk drive managed by the MPXIO and not visible
 *		externally, unlike aliases which are.
 */
int
disk_get_paths(
	dm_descriptor_t	disk,
	dlist_t		**list)
{
	return (disk_get_associated_desc(disk, DM_PATH,
			gettext("path"), list));
}

/*
 * FUNCTION:	disk_get_aliases(dm_descriptor_t disk, dlist_t **list)
 *
 * INPUT:	disk	- a dm_descriptor_t handle for a disk
 *
 * OUTPUT:	list	- a pointer to a dlist_t list to hold the
 *			alias descriptors associated with the input disk
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helper which retrieves the known aliases for a disk device.
 *
 *		Aliases are the different CTD names for the disk drive when
 *		MPXIO is not enabled for multipathed drives.
 */
int
disk_get_aliases(
	dm_descriptor_t	disk,
	dlist_t		**list)
{
	return (disk_get_associated_desc(disk, DM_ALIAS,
			gettext("alias"), list));
}

/*
 * FUNCTION:	compare_string_to_desc_name_or_alias(
 *			void *str, void *desc)
 *
 * INPUT:	str	- opaque pointer
 * 		descr	- opaque pointer
 *
 * RETURNS:	int	- <0 - if str < desc.name
 *			   0 - if str == desc.name
 *			  >0 - if str > desc.name
 *
 * PURPOSE:	dlist_t helper which compares a string to the name
 *		and aliases associated with the input dm_descriptor_t
 *		handle.
 *
 *		Comparison is done via compare_device_names.
 */
static int
compare_string_to_desc_name_or_alias(
	void	*str,
	void	*desc)
{
	char	*dname = NULL;
	int	result = -1;

	assert(str != (char *)NULL);
	assert(desc != (dm_descriptor_t)0);

	(void) get_display_name((uintptr_t)desc, &dname);

	/* try name first, then aliases */
	if ((result = compare_device_names(str, dname)) != 0) {
	    dlist_t *aliases = NULL;

	    (void) get_aliases((uintptr_t)desc, &aliases);
	    if ((aliases != NULL) && (dlist_contains(aliases,
			str, compare_device_names) == B_TRUE)) {
		result = 0;
	    }
	    dlist_free_items(aliases, free);
	}

	return (result);
}

/*
 * FUNCTION:	hba_get_by_name(char *name, dm_descriptor_t *hba)
 *
 * INPUT:	name	- a char * disk name
 *
 * OUTPUT:	hba	- a pointer to a dm_descriptor_t to hold the
 *			HBA corresponding to the input name, if found
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise
 *
 * PURPOSE:	Helper which iterates the known HBAs, searching for
 *		the one matching name.
 *
 *		If no HBA matches the name, 0 is returned and the
 *		value of 'hba' will be (dm_descriptor_t)0;
 */
int
hba_get_by_name(
	char		*name,
	dm_descriptor_t *hba)
{
	int		error = 0;
	dlist_t		*list = NULL;
	dlist_t		*item = NULL;

	*hba = (dm_descriptor_t)0;

	if (name == NULL) {
	    return (0);
	}

	if ((error = get_known_hbas(&list)) != 0) {
	    return (error);
	}

	if ((item = dlist_find(list, name,
	    compare_string_to_desc_name_or_alias)) != NULL) {
	    *hba = (uintptr_t)item->obj;
	}

	return (error);
}

/*
 * FUNCTION:	disk_get_by_name(char *name, dm_descriptor_t *disk)
 *
 * INPUT:	name	- a char * disk name
 *
 * OUTPUT:	disk	- a pointer to a dm_descriptor_t to hold the
 *			disk corresponding to the input name, if found
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helper which retrieves a dm_descriptor_t disk handle
 *		by name.
 *
 *		If no disk is found for the input name, variations of
 *		the name are tried.
 *
 *		If the input name is unqualified, an appropriate leading
 *		path is prepended.
 *
 *		If the input name is qualified, the leading path is
 *		removed.
 *
 *		If no disk is found for the variations, 0 is returned
 *		and the	value of 'disk' will be (dm_descriptor_t)0;
 */
int
disk_get_by_name(
	char		*name,
	dm_descriptor_t *disk)
{
	assert(name != (char *)NULL);

	*disk = find_cached_descriptor(name);
	if (*disk == (dm_descriptor_t)0) {
	    if (name[0] == '/') {
		/* fully qualified, try unqualified */
		char *cp = strrchr(name, '/');
		if (cp != NULL) {
		    *disk = find_cached_descriptor(cp + 1);
		}
	    } else {
		/* unqualified, try fully qualified */
		char buf[MAXNAMELEN+1];
		if (is_ctd_disk_name(name)) {
		    (void) snprintf(buf, MAXNAMELEN, "/dev/dsk/%s", name);
		} else if (is_did_disk_name(name)) {
		    (void) snprintf(buf, MAXNAMELEN, "/dev/did/dsk/%s", name);
		}
		*disk = find_cached_descriptor(buf);
	    }
	}

	/*
	 * since the descriptor cache includes HBAs, disks and slices,
	 * what gets returned may not be a disk... make sure it is
	 */
	if (*disk != (dm_descriptor_t)0) {
	    if (dm_get_type(*disk) != DM_DRIVE) {
		*disk = (dm_descriptor_t)0;
	    }
	}

	return (0);
}

/*
 * FUNCTION:	slice_get_by_name(char *name, dm_descriptor_t *slice)
 *
 * INPUT:	name	- a char * slice name
 *
 * OUTPUT:	slice	- a pointer to a dm_descriptor_t to hold the
 *			slice corresponding to the input name, if found.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helper which iterates the known slices, searching for
 *		the one matching name.
 *
 *		If no slice is found for the input name, variations of
 *		the name are tried.
 *
 *		If the input name is unqualified, an appropriate leading
 *		path is prepended.
 *
 *		If the input name is qualified, the leading path is
 *		removed.
 *
 *		If no slice matches the variations, 0 is returned and the
 *		value of 'slice' will be (dm_descriptor_t)0;
 */
int
slice_get_by_name(
	char		*name,
	dm_descriptor_t *slice)
{
	assert(name != (char *)NULL);

	*slice = find_cached_descriptor(name);
	if (*slice == (dm_descriptor_t)0) {
	    if (name[0] == '/') {
		/* fully qualified, try unqualified */
		char *cp = strrchr(name, '/');
		if (cp != NULL) {
		    *slice = find_cached_descriptor(cp + 1);
		}
	    } else {
		/* unqualified, try fully qualified */
		char buf[MAXNAMELEN+1];
		if (is_ctd_slice_name(name) || is_ctd_like_slice_name(name) ||
			is_bsd_like_slice_name(name)) {
		    (void) snprintf(buf, MAXNAMELEN, "/dev/dsk/%s", name);
		} else if (is_did_slice_name(name)) {
		    (void) snprintf(buf, MAXNAMELEN, "/dev/did/dsk/%s", name);
		}
		*slice = find_cached_descriptor(buf);
	    }
	}

	/*
	 * since the descriptor cache includes HBAs, disks and slices,
	 * what gets returned may not be a slice... make sure it is
	 */
	if (*slice != (dm_descriptor_t)0) {
	    if (dm_get_type(*slice) != DM_SLICE &&
		is_virtual_slice(*slice) != B_TRUE) {
		*slice = (dm_descriptor_t)0;
	    }
	}

	return (0);
}

/*
 * FUNCTION:	extract_hbaname(char *name, char **hbaname)
 *
 * INPUT:	slicename - a char * device name
 *
 * OUTPUT:	hbaname - a pointer to a char * to hold the hbaname derived
 *			from the input name.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helper which extracts the HBA name from the input name.
 *
 *		If the input name is in ctd form, extracts just the cX part,
 *		by truncating everything following the last 't'.
 *
 *		Of course on X86, with IDE drives, there is no 't' in the
 *		ctd name, so start by truncating everything following 'd'
 *		and then look for 't'.
 *
 * 		The returned string must be passed to free().
 */
int
extract_hbaname(
	char	*name,
	char	**hbaname)
{
	char	*cp;

	if (is_ctd_name(name)) {
	    if ((*hbaname = strdup(name)) == NULL) {
		return (ENOMEM);
	    }
	    if ((cp = strrchr(*hbaname, 'd')) != NULL) {
		*cp = '\0';
	    }
	    if ((cp = strrchr(*hbaname, 't')) != NULL) {
		*cp = '\0';
	    }
	}

	return (0);
}

/*
 * FUNCTION:	extract_diskname(char *slicename, char **diskname)
 *
 * INPUT:	slicename - a char * slice name
 *
 * OUTPUT:	diskname - a pointer to a char * to hold the diskname derived
 *			from the input slicename.
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helper which extracts the disk's name from a slice name.
 *
 *		Checks to see if the input slicename is in ctd or did form,
 *		and if so, truncates everything following the last 's'.
 *
 *		If the input slicename is BSD-like, truncate the last
 *		character (a-h).
 *
 * 		The returned string must be passed to free().
 */
int
extract_diskname(
	char	*slicename,
	char	**diskname)
{
	char	*cp;

	if (is_ctd_slice_name(slicename) || is_did_slice_name(slicename) ||
	    is_ctd_like_slice_name(slicename)) {

	    if ((*diskname = strdup(slicename)) == NULL) {
		return (ENOMEM);
	    }
	    if ((cp = strrchr(*diskname, 's')) != NULL) {
		*cp = '\0';
	    }

	} else if (is_bsd_like_slice_name(slicename)) {

	    if ((*diskname = strdup(slicename)) == NULL) {
		return (ENOMEM);
	    }
	    (*diskname)[strlen((*diskname)-1)] = '\0';

	}

	return (0);
}

/*
 * FUNCTION:	get_disk_for_named_slice(char *slicename,
 *			dm_descriptor_t disk)
 *
 * INPUT:	slicename - a char * slice name
 *
 * OUTPUT:	disk	- a pointer to a dm_descriptor_t to hold the
 *			disk corresponding to the input name, if found
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise.
 *
 * PURPOSE:	Helper which locates the disk dm_descriptor_t handle for
 *		the input slice name.
 *
 *		If no disk matches the name, 0 is returned and the
 *		value of 'disk' will be (dm_descriptor_t)0;
 */
int
get_disk_for_named_slice(
	char		*slicename,
	dm_descriptor_t *disk)
{
	dm_descriptor_t slice = (dm_descriptor_t)0;
	int		error = 0;

	assert(slicename != NULL);

	/* find disk for slice */
	if ((error = slice_get_by_name(slicename, &slice)) == 0) {

	    if (slice != (dm_descriptor_t)0) {
		error = slice_get_disk(slice, disk);
	    } else {
		/* named slice was created by layout: */
		/* need to find disk by name */
		char *dname;

		error = extract_diskname(slicename, &dname);
		if (error == 0) {
		    error = disk_get_by_name(dname, disk);
		}
		free(dname);
	    }
	}

	assert(*disk != (dm_descriptor_t)0);

	return (error);
}

/*
 * FUNCTION:	disk_get_reserved_indexes(dm_descriptor_t disk,
 *			uint16_t **array)
 *
 * INPUT:	disk	- a dm_descriptor_t disk handle
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise
 *
 * PURPOSE:	Retrieves the input disk's list of reserved slice indices.
 *
 *		The list of reserved indices is stored as an array in
 *		the disk's nvpair attribute list.
 */
static int
disk_get_reserved_indexes(
	dm_descriptor_t	disk,
	uint16_t	**array)
{
	nvlist_t	*attrs = NULL;
	uint_t		nelem = 0;
	int		error = 0;

	if ((error = get_cached_attributes(disk, &attrs)) != 0) {
	    return (error);
	}

	if ((error = get_uint16_array(
	    attrs, ATTR_RESERVED_INDEX, array, &nelem)) != 0) {
	    if (error == ENOENT) {
		/* no reserved indices yet */
		error = 0;
	    }
	}

	return (error);
}

/*
 * FUNCTION:	disk_reserve_index(dm_descriptor_t disk, uint16_t index)
 *
 * INPUT:	disk	- a disk dm_descirptor_t handle
 *		undex	- a VTOC slice index
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise
 *
 * PURPOSE:	Reserves the input VTOC slice index for the input disk.
 *
 *		The list of reserved indices is stored as an array in
 *		the disk's nvpair attribute list.
 */
int
disk_reserve_index(
	dm_descriptor_t	disk,
	uint16_t	index)
{
	nvlist_t	*attrs = NULL;
	uint16_t	*oldindexes = NULL;
	uint16_t	*newindexes = NULL;
	uint_t		nelem = 0;
	int		error = 0;
	int		i = 0;

	if ((error = get_cached_attributes(disk, &attrs)) != 0) {
	    return (error);
	}

	if ((error = get_uint16_array(
	    attrs, ATTR_RESERVED_INDEX, &oldindexes, &nelem)) != 0) {
	    if (error != ENOENT) {
		return (error);
	    }
	    /* no reserved indices yet */
	    error = 0;
	}

	/* add new index */
	newindexes = (uint16_t *)calloc(VTOC_SIZE, sizeof (uint16_t));
	if (newindexes != NULL) {
	    for (i = 0; i < nelem; i++) {
		newindexes[i] = oldindexes[i];
	    }
	    newindexes[(int)index] = 1;

	    error = set_uint16_array(attrs, ATTR_RESERVED_INDEX,
		    newindexes, VTOC_SIZE);

	    free(newindexes);
	} else {
	    error = ENOMEM;
	}
	return (error);
}

/*
 * FUNCTION:	disk_release_index(dm_descriptor_t disk, uint16_t index)
 *
 * INPUT:	disk	- a disk dm_descirptor_t handle
 *		undex	- a VTOC slice index
 *
 * RETURNS:	int	- 0 on success
 *			 !0 otherwise
 *
 * PURPOSE:	Releases the input VTOC slice index for the input disk.
 *		The index was previously reserved by disk_reserve_index()
 */
int
disk_release_index(
	dm_descriptor_t	disk,
	uint16_t	index)
{
	nvlist_t	*attrs = NULL;
	uint16_t	*oldindexes = NULL;
	uint16_t	*newindexes = NULL;
	uint_t		nelem = 0;
	int		error = 0;
	int		i = 0;

	if ((error = get_cached_attributes(disk, &attrs)) != 0) {
	    return (error);
	}

	if ((error = get_uint16_array(
	    attrs, ATTR_RESERVED_INDEX, &oldindexes, &nelem)) != 0) {
	    if (error != ENOENT) {
		return (error);
	    }
	    error = 0;
	}

	newindexes = (uint16_t *)calloc(VTOC_SIZE, sizeof (uint16_t));
	if (newindexes != NULL) {
	    for (i = 0; i < nelem; i++) {
		newindexes[i] = oldindexes[i];
	    }

	    /* release index */
	    newindexes[(int)index] = 0;

	    error = set_uint16_array(attrs, ATTR_RESERVED_INDEX,
		    newindexes, VTOC_SIZE);

	    free(newindexes);
	} else {
	    error = ENOMEM;
	}

	return (error);
}

/*
 * FUNCTION:	print_get_assoc_desc_error(dm_descriptor_t desc, char *which,
 *			int error)
 *
 * INPUT:	desc	- a dm_descriptor_t handle
 *		which	- a char * indicating which association
 *		error	- an integer error value
 *
 * PURPOSE:	Utility function to print an error message for a failed
 *		call to dm_get_associated_descriptors().
 *
 *		Extracts the device's CTD name and formats an error message.
 */
void
print_get_assoc_desc_error(
	dm_descriptor_t desc,
	char		*which,
	int		error)
{
	char *name = "";

	(void) get_display_name(desc, &name);
	oprintf(OUTPUT_TERSE,
		gettext("dm_get_associated_descriptors(%s) for "
			"'%s' failed: %d\n"),
		which, name, error);

	volume_set_error(
		gettext("Unexpected error getting associated "
			"descriptors for '%s'"),
			name);
}

/*
 * FUNCTION:	print_get_desc_attr_error(dm_descriptor_t desc,
 *			char *devtype, char *attr, int error)
 *
 * INPUT:	desc	- a dm_descriptor_t handle
 *		devtype	- a char * device type that's being accessed
 *		attr	- a char * attribute name
 *		error	- an integer error value
 *
 * PURPOSE:	Shared utility function to print an error message for a failed
 *		call to retrieve an attribute for a descriptor.
 *
 *		Extracts the device's CTD name and formats an error message.
 */
void
print_get_desc_attr_error(
	dm_descriptor_t desc,
	char		*devtype,
	char		*attr,
	int		error)
{
	char *name = "";

	(void) get_display_name(desc, &name);
	oprintf(OUTPUT_TERSE,
		gettext("'%s' get attribute (%s.%s) error: %d\n"),
		name, devtype, attr, error);

	volume_set_error(
		gettext("Unexpected error getting attribute '%s.%s' for '%s'"),
			devtype, attr, name);
}

/*
 * FUNCTION:	print_set_desc_attr_error(dm_descriptor_t desc,
 *			char *devtype, char *attr, int error)
 *
 * INPUT:	desc	- a dm_descriptor_t handle
 *		devtype	- a char * device type that's being accessed
 *		attr	- a char * attribute name
 *		error	- an integer error value
 *
 * PURPOSE:	Shared utility function to print an error message for a failed
 *		call to set an attribute for a descriptor.
 *
 *		Extracts the device's CTD name and formats an error message.
 */
void
print_set_desc_attr_error(
	dm_descriptor_t desc,
	char		*devtype,
	char		*attr,
	int		error)
{
	char *name = "";

	(void) get_display_name(desc, &name);
	oprintf(OUTPUT_TERSE,
		gettext("'%s' set attribute (%s.%s) error: %d\n"),
		name, devtype, attr, error);

	volume_set_error(
		gettext("Unexpected error setting attribute '%s.%s' for '%s'"),
			devtype, attr, name);
}