view usr/src/cmd/lvm/metassist/common/volume_devconfig.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
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 2003 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"@(#)volume_devconfig.c	1.2	05/06/08 SMI"

#include "volume_devconfig.h"

#include <string.h>
#include <ctype.h>
#include <meta.h>
#include "volume_nvpair.h"
#include "volume_error.h"
#include "volume_output.h"
#include "volume_string.h"

/*
 * Methods which manipulate a devconfig_t struct
 */

/*
 * Constructor: Create a devconfig_t struct.  This devconfig_t must be
 * freed with free_devconfig().
 *
 * @param       devconfig
 *              RETURN: a new devconfig_t
 *
 * @param       type
 *              the type of devconfig_t to create
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
new_devconfig(
	devconfig_t **devconfig,
	component_type_t type)
{
	int error;

	*devconfig = (devconfig_t *)calloc(1, sizeof (devconfig_t));
	if (*devconfig == NULL) {
	    volume_set_error(gettext("new_devconfig() calloc() failed\n"));
	    return (-1);
	}

	/* Create attribute list */
	if ((error = nvlist_alloc(&((*devconfig)->attributes),
	    NV_UNIQUE_NAME_TYPE, 0)) != 0) {
	    volume_set_error(gettext("devconfig_t nvlist_alloc() failed\n"));
	    free_devconfig(*devconfig);
	    return (error);
	}

	if ((error = devconfig_set_type(*devconfig, type)) != 0) {
	    free_devconfig(*devconfig);
	    return (error);
	}

	return (0);
}

/*
 * Free memory (recursively) allocated to a devconfig_t struct
 *
 * @param       arg
 *              pointer to the devconfig_t to be freed
 */
void
free_devconfig(
	void *arg)
{
	devconfig_t *devconfig = (devconfig_t *)arg;

	if (devconfig == NULL) {
	    return;
	}

	/* Free the attributes nvlist */
	if (devconfig->attributes != NULL) {
	    nvlist_free(devconfig->attributes);
	}

	/* Free available devices */
	if (devconfig->available != NULL) {
	    free_string_array(devconfig->available);
	}

	/* Free unavailable devices */
	if (devconfig->unavailable != NULL) {
	    free_string_array(devconfig->unavailable);
	}

	/* Free the components */
	if (devconfig->components != NULL) {
	    dlist_free_items(devconfig->components, free_devconfig);
	}

	/* Free the devconfig itself */
	free(devconfig);
}

/*
 * Check the type of the given device.
 *
 * @param       device
 *              the device whose type to check
 *
 * @param       type
 *              the type of the device against which to compare
 *
 * @return      B_TRUE if the device is of the given type, B_FALSE
 *              otherwise
 */
boolean_t
devconfig_isA(
	devconfig_t *device,
	component_type_t type)
{
	component_type_t curtype;

	if (device == NULL) {
	    return (B_FALSE);
	}

	if (devconfig_get_type(device, &curtype) != 0) {
	    return (B_FALSE);
	}

	if (curtype != type) {
	    return (B_FALSE);
	}

	return (B_TRUE);
}

/*
 * Get the first component of the given type from the given
 * devconfig_t.  Create the component if create is B_TRUE.
 *
 * @return      ENOENT
 *              if the requested component does not exist and its
 *              creation was not requested
 *
 * @return      0
 *              if the requested component exists or was created
 *
 * @return      non-zero
 *              if the requested component did not exist and could not
 *              be created
 */
int
devconfig_get_component(
	devconfig_t *device,
	component_type_t type,
	devconfig_t **component,
	boolean_t create)
{
	dlist_t *list;
	int error = 0;
	char *typestr = devconfig_type_to_str(type);

	oprintf(OUTPUT_DEBUG, gettext("Searching for singleton %s\n"), typestr);

	/* For each component of this device... */
	for (list = devconfig_get_components(device);
	    list != NULL; list = list->next) {

	    *component = (devconfig_t *)list->obj;

	    /* Is this subcomponent an instance of the given type? */
	    if (*component != NULL && devconfig_isA(*component, type)) {
		oprintf(OUTPUT_DEBUG, gettext("Found %s\n"), typestr);
		return (0);
	    }
	}

	/* No component found */
	error = ENOENT;
	*component = NULL;

	oprintf(OUTPUT_DEBUG, gettext("%s not found\n"), typestr);

	if (create == B_TRUE) {
	    oprintf(OUTPUT_DEBUG, gettext("Creating %s\n"), typestr);

		/*
		 * An existing singleton component of the given type was
		 * not found under the given disk set.  So, create one.
		 */
	    if ((error = new_devconfig(component, type)) == 0) {
		/* Attach new component to given device */
		devconfig_set_components(
		    device, dlist_append(dlist_new_item(*component),
		    devconfig_get_components(device), AT_TAIL));
	    }
	}

	return (error);
}

/*
 * Set the available devices for use in creating this device
 *
 * @param       device
 *              a devconfig_t representing the device to modify
 *
 * @param       available
 *              A NULL-terminated array of device names
 */
void
devconfig_set_available(
	devconfig_t *device,
	char **available)
{
	device->available = available;
}

/*
 * Get the available devices for use in creating this device
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @return      available
 *              A NULL-terminated array of device names
 */
char **
devconfig_get_available(
	devconfig_t *device)
{
	return (device->available);
}

/*
 * Set the unavailable devices which may not be used in creating this
 * device
 *
 * @param       device
 *              a devconfig_t representing the device to modify
 *
 * @param       available
 *              A NULL-terminated array of device names
 */
void
devconfig_set_unavailable(
	devconfig_t *device,
	char **unavailable)
{
	device->unavailable = unavailable;
}

/*
 * Get the unavailable devices for use in creating this device
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @return      unavailable
 *              A NULL-terminated array of device names
 */
char **
devconfig_get_unavailable(
	devconfig_t *device)
{
	return (device->unavailable);
}

/*
 * Set the subcomponent devices of a given device
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       components
 *              A dlist_t containing devconfig_t devices
 */
void
devconfig_set_components(
	devconfig_t *device,
	dlist_t *components)
{
	device->components = components;
}

/*
 * Get the subcomponent devices of a given device
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @return      A dlist_t containing devconfig_t devices
 */
dlist_t *
devconfig_get_components(
	devconfig_t *device)
{
	return (device->components);
}

/*
 * Set the device name
 *
 * @param       device
 *              a devconfig_t representing the device to modify
 *
 * @param       name
 *              the value to set as the device name
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_name(
	devconfig_t *device,
	char *name)
{
	return (set_string(device->attributes, ATTR_NAME, name));
}

/*
 * Set the disk set name
 *
 * @param       diskset
 *              a devconfig_t representing the diskset to modify
 *
 * @param       name
 *              the value to set as the device name
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_diskset_name(
	devconfig_t *diskset,
	char *name)
{
	md_error_t error = mdnullerror;

	/* Verify syntax of disk set name */
	if (meta_set_checkname(name, &error)) {
	    volume_set_error(gettext("invalid disk set name: %s"), name);
	    return (-1);
	}

	return (devconfig_set_name(diskset, name));
}

/*
 * Set the device name
 *
 * @param       hsp
 *              a devconfig_t representing the hsp to modify
 *
 * @param       name
 *              the value to set as the device name
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_hsp_name(
	devconfig_t *hsp,
	char *name)
{
	/* Validate name */
	if (!is_hspname(name)) {
	    volume_set_error(gettext("invalid hot spare pool name: %s"), name);
	    return (-1);
	}

	return (devconfig_set_name(hsp, name));
}

/*
 * Set the device name
 *
 * @param       volume
 *              a devconfig_t representing the volume to modify
 *
 * @param       name
 *              the value to set as the device name
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_volume_name(
	devconfig_t *volume,
	char *name)
{
	/* Validate name */
	if (!is_metaname(name)) {
	    volume_set_error(gettext("invalid volume name: %s"), name);
	    return (-1);
	}

	return (devconfig_set_name(volume, name));
}

/*
 * Get the device name
 *
 * @param       volume
 *              a devconfig_t representing the volume to examine
 *
 * @param       name
 *              RETURN: the device name
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_name(
	devconfig_t *device,
	char **name)
{
	int error = get_string(device->attributes, ATTR_NAME, name);

	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	if (error == ENOENT) {
	    volume_set_error(gettext("device name not set"));
	    error = ERR_ATTR_UNSET;
	}

	return (error);
}

/*
 * Set the device type
 *
 * @param       device
 *              a devconfig_t representing the device to modify
 *
 * @param       type
 *              the value to set as the device type
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_type(
	devconfig_t *device,
	component_type_t type)
{
	return (set_uint16(device->attributes, ATTR_TYPE, (uint16_t)type));
}

/*
 * Get the device type
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       type
 *              RETURN: the device type
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_type(
	devconfig_t *device,
	component_type_t *type)
{
	uint16_t val;
	int error = get_uint16(device->attributes, ATTR_TYPE, &val);

	switch (error) {
	    /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	    case ENOENT:
		volume_set_error(gettext("device type not set"));
		error = ERR_ATTR_UNSET;
	    break;

	    /* Success */
	    case 0:
		*type = (component_type_t)val;
	}

	return (error);
}

/*
 * Set the device size (for volume, mirror, stripe, concat) in bytes
 *
 * Note that size in bytes in a 64-bit field cannot hold the size that
 * can be accessed in a 16 byte CDB.  Since CDBs operate on blocks,
 * the max capacity is 2^73 bytes with 512 byte blocks.
 *
 * @param       device
 *              a devconfig_t representing the device to modify
 *
 * @param       size_in_bytes
 *              the value to set as the device size in bytes
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_size(
	devconfig_t *device,
	uint64_t size_in_bytes)
{

	/* Validate against limits */
	/* LINTED -- MIN_SIZE may be 0 */
	if (size_in_bytes < MIN_SIZE) {
	    volume_set_error(gettext("size (in bytes) too small: %llu"),
		(unsigned long long)size_in_bytes);
	    return (-1);
	}

	return (set_uint64(device->attributes,
	    ATTR_SIZEINBYTES, size_in_bytes));
}

/*
 * Get the device size (for volume, mirror, stripe, concat) in bytes
 *
 * Note that size in bytes in a 64-bit field cannot hold the size that
 * can be accessed in a 16 byte CDB.  Since CDBs operate on blocks,
 * the max capacity is 2^73 bytes with 512 byte blocks.
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       size_in_bytes
 *              RETURN: the device size in bytes
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_size(
	devconfig_t *device,
	uint64_t *size_in_bytes)
{
	int error = get_uint64(
	    device->attributes, ATTR_SIZEINBYTES, size_in_bytes);

	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	if (error == ENOENT) {
	    volume_set_error(gettext("size (in bytes) not set"));
	    error = ERR_ATTR_UNSET;
	}

	return (error);
}

/*
 * Set the device size in blocks
 *
 * @param       device
 *              a devconfig_t representing the device to modify
 *
 * @param       type
 *              the value to set as the device size in blocks
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_size_in_blocks(
	devconfig_t *device,
	uint64_t size_in_blocks)
{
	/* Validate against limits */
	/* LINTED -- MIN_SIZE_IN_BLOCKS may be 0 */
	if (size_in_blocks < MIN_SIZE_IN_BLOCKS) {
	    volume_set_error(gettext("size (in blocks) too small: %llu"),
		(unsigned long long)size_in_blocks);
	    return (-1);
	}

	return (set_uint64(device->attributes,
	    ATTR_SIZEINBLOCKS, size_in_blocks));
}

/*
 * Get the device size in blocks
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       size_in_blocks
 *              RETURN: the device size in blocks
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_size_in_blocks(
	devconfig_t *device,
	uint64_t *size_in_blocks)
{
	int error = get_uint64(
	    device->attributes, ATTR_SIZEINBLOCKS, size_in_blocks);

	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	if (error == ENOENT) {
	    volume_set_error(gettext("size (in blocks) not set"));
	    error = ERR_ATTR_UNSET;
	}

	return (error);
}

/*
 * Set the the slice index
 *
 * @param       slice
 *              a devconfig_t representing the slice to modify
 *
 * @param       index
 *              the value to set as the the slice index
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_slice_index(
	devconfig_t *slice,
	uint16_t index)
{
	return (set_uint16(slice->attributes, ATTR_SLICE_INDEX, index));
}

/*
 * Get the slice index
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       index
 *              RETURN: the slice index
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_slice_index(
	devconfig_t *slice,
	uint16_t *index)
{
	int error = get_uint16(slice->attributes, ATTR_SLICE_INDEX, index);

	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	if (error == ENOENT) {
	    volume_set_error(gettext("slice index not set"));
	    error = ERR_ATTR_UNSET;
	}

	return (error);
}

/*
 * Set the the slice start block
 *
 * @param       slice
 *              a devconfig_t representing the slice to modify
 *
 * @param       start_block
 *              the value to set as the the slice start block
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_slice_start_block(
	devconfig_t *slice,
	uint64_t start_block)
{
	return (set_uint64(slice->attributes,
	    ATTR_SLICE_STARTSECTOR, start_block));
}

/*
 * Get the slice start block
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       start_block
 *              RETURN: the slice start block
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_slice_start_block(
	devconfig_t *slice,
	uint64_t *start_block)
{
	int error = get_uint64(
	    slice->attributes, ATTR_SLICE_STARTSECTOR, start_block);

	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	if (error == ENOENT) {
	    volume_set_error(gettext("slice start block not set"));
	    error = ERR_ATTR_UNSET;
	}

	return (error);
}

/*
 * Set the number of subcomponents in mirror
 *
 * @param       mirror
 *              a devconfig_t representing the mirror to modify
 *
 * @param       nsubs
 *              the value to set as the number of subcomponents in
 *              mirror
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_mirror_nsubs(
	devconfig_t *mirror,
	uint16_t nsubs)
{
	/* Validate against limits */
	if (nsubs < 1 || nsubs > NMIRROR) {
	    volume_set_error(
		gettext("number of submirrors (%d) out of valid range (%d-%d)"),
		nsubs, 1, NMIRROR);
	    return (-1);
	}

	return (set_uint16(mirror->attributes, ATTR_MIRROR_NSUBMIRRORS, nsubs));
}

/*
 * Get number of subcomponents in mirror
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       nsubs
 *              RETURN: number of subcomponents in mirror
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_mirror_nsubs(
	devconfig_t *mirror,
	uint16_t *nsubs)
{
	int error = get_uint16(
	    mirror->attributes, ATTR_MIRROR_NSUBMIRRORS, nsubs);

	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	if (error == ENOENT) {
	    volume_set_error(gettext("number or submirrors not set"));
	    error = ERR_ATTR_UNSET;
	}

	return (error);
}

/*
 * Set the read strategy for mirror
 *
 * @param       mirror
 *              a devconfig_t representing the mirror to modify
 *
 * @param       read
 *              the value to set as the read strategy for mirror
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_mirror_read(
	devconfig_t *mirror,
	mirror_read_strategy_t read)
{
	return (set_uint16(mirror->attributes,
	    ATTR_MIRROR_READ, (uint16_t)read));
}

/*
 * Get read strategy for mirror
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       read
 *              RETURN: read strategy for mirror
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_mirror_read(
	devconfig_t *mirror,
	mirror_read_strategy_t *read)
{
	uint16_t val;
	int error = get_uint16(mirror->attributes, ATTR_MIRROR_READ, &val);

	switch (error) {
	    /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	    case ENOENT:
		volume_set_error(gettext("mirror read strategy not set"));
		error = ERR_ATTR_UNSET;
	    break;

	    /* Success */
	    case 0:
		*read = (mirror_read_strategy_t)val;
	}

	return (error);
}

/*
 * Set the write strategy for mirror
 *
 * @param       mirror
 *              a devconfig_t representing the mirror to modify
 *
 * @param       write
 *              the value to set as the write strategy for mirror
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_mirror_write(
	devconfig_t *mirror,
	mirror_write_strategy_t write)
{
	return (set_uint16(mirror->attributes,
	    ATTR_MIRROR_WRITE, (uint16_t)write));
}

/*
 * Get write strategy for mirror
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       write
 *              RETURN: write strategy for mirror
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_mirror_write(
	devconfig_t *mirror,
	mirror_write_strategy_t *write)
{
	uint16_t val;
	int error = get_uint16(mirror->attributes, ATTR_MIRROR_WRITE, &val);

	switch (error) {
	    /* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	    case ENOENT:
		volume_set_error(gettext("mirror write strategy not set"));
		error = ERR_ATTR_UNSET;
	    break;

	    /* Success */
	    case 0:
		*write = (mirror_write_strategy_t)val;
	}

	return (error);
}

/*
 * Set the resync pass for mirror
 *
 * @param       mirror
 *              a devconfig_t representing the mirror to modify
 *
 * @param       pass
 *              the value to set as the resync pass for mirror
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_mirror_pass(
	devconfig_t *mirror,
	uint16_t pass)
{
	/* Validate against max value */
	if (pass > MD_PASS_MAX) {
	    volume_set_error(
		gettext("mirror pass number (%d) out of valid range (0-%d)"),
		pass, MD_PASS_MAX);
	    return (-1);
	}

	return (set_uint16(mirror->attributes, ATTR_MIRROR_PASSNUM, pass));
}

/*
 * Get resync pass for mirror
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       pass
 *              RETURN: resync pass for mirror
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_mirror_pass(
	devconfig_t *mirror,
	uint16_t *pass)
{
	int error = get_uint16(mirror->attributes, ATTR_MIRROR_PASSNUM, pass);

	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	if (error == ENOENT) {
	    volume_set_error(gettext("mirror pass number not set"));
	    error = ERR_ATTR_UNSET;
	}

	return (error);
}

/*
 * Set the minimum number of components in stripe
 *
 * @param       stripe
 *              a devconfig_t representing the stripe to modify
 *
 * @param       mincomp
 *              the value to set as the minimum number of components
 *              in stripe
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_stripe_mincomp(
	devconfig_t *stripe,
	uint16_t mincomp)
{
	/* Validate against minimum value */
	if (mincomp < MIN_NSTRIPE_COMP) {
	    volume_set_error(gettext(
		"minimum stripe components (%d) below minimum allowable (%d)"),
		mincomp, MIN_NSTRIPE_COMP);
	    return (-1);
	}

	return (set_uint16(stripe->attributes, ATTR_STRIPE_MINCOMP, mincomp));
}

/*
 * Get minimum number of components in stripe
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       mincomp
 *              RETURN: minimum number of components in stripe
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_stripe_mincomp(
	devconfig_t *stripe,
	uint16_t *mincomp)
{
	int error = get_uint16(
	    stripe->attributes, ATTR_STRIPE_MINCOMP, mincomp);

	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	if (error == ENOENT) {
	    volume_set_error(
		gettext("minimum number of stripe components not set"));
	    error = ERR_ATTR_UNSET;
	}

	return (error);
}

/*
 * Set the maximum number of components in stripe
 *
 * @param       stripe
 *              a devconfig_t representing the stripe to modify
 *
 * @param       maxcomp
 *              the value to set as the maximum number of components
 *              in stripe
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_stripe_maxcomp(
	devconfig_t *stripe,
	uint16_t maxcomp)
{
	/* Validate against minimum value */
	if (maxcomp < MIN_NSTRIPE_COMP) {
	    volume_set_error(gettext(
		"maximum stripe components (%d) below minimum allowable (%d)"),
		maxcomp, MIN_NSTRIPE_COMP);
	    return (-1);
	}

	return (set_uint16(stripe->attributes, ATTR_STRIPE_MAXCOMP, maxcomp));
}

/*
 * Get maximum number of components in stripe
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       maxcomp
 *              RETURN: maximum number of components in stripe
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_stripe_maxcomp(
	devconfig_t *stripe,
	uint16_t *maxcomp)
{
	int error = get_uint16(
	    stripe->attributes, ATTR_STRIPE_MAXCOMP, maxcomp);

	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	if (error == ENOENT) {
	    volume_set_error(
		gettext("maximum number of stripe components not set"));
	    error = ERR_ATTR_UNSET;
	}

	return (error);
}

/*
 * Set the stripe interlace
 *
 * @param       stripe
 *              a devconfig_t representing the stripe to modify
 *
 * @param       interlace
 *              the value to set as the stripe interlace
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_stripe_interlace(
	devconfig_t *stripe,
	uint64_t interlace)
{
	if (interlace < MININTERLACE || interlace > MAXINTERLACE) {
	    char *intstr = NULL;
	    char *minstr = NULL;
	    char *maxstr = NULL;

	    /* Get string representations of interlaces */
	    bytes_to_sizestr(interlace, &intstr, universal_units, B_FALSE);
	    bytes_to_sizestr(MININTERLACE, &minstr, universal_units, B_FALSE);
	    bytes_to_sizestr(MAXINTERLACE, &maxstr, universal_units, B_FALSE);

	    volume_set_error(
		gettext("interlace (%s) out of valid range (%s - %s)"),
		intstr, minstr, maxstr);

	    free(intstr);
	    free(minstr);
	    free(maxstr);

	    return (-1);
	}

	return (set_uint64(stripe->attributes,
	    ATTR_STRIPE_INTERLACE, interlace));
}

/*
 * Get stripe interlace
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       interlace
 *              RETURN: stripe interlace
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_stripe_interlace(
	devconfig_t *stripe,
	uint64_t *interlace)
{
	int error = get_uint64(
	    stripe->attributes, ATTR_STRIPE_INTERLACE, interlace);

	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	if (error == ENOENT) {
	    volume_set_error(gettext("stripe interlace not set"));
	    error = ERR_ATTR_UNSET;
	}

	return (error);
}

/*
 * Set the redundancy level for a volume.
 *
 * @param       volume
 *              a devconfig_t representing the volume to modify
 *
 * @param       rlevel
 *              If 0, a stripe will be created.  If > 0, a mirror with
 *              this number of submirrors will be created.
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_volume_redundancy_level(
	devconfig_t *volume,
	uint16_t rlevel)
{
	/* Validate against limits */
	if (rlevel > NMIRROR) {
	    volume_set_error(gettext(
		"volume redundancy level (%d) out of valid range (%d-%d)"),
		rlevel, 0, NMIRROR);
	    return (-1);
	}

	return (set_uint16(volume->attributes, ATTR_VOLUME_REDUNDANCY, rlevel));
}

/*
 * Get the redundancy level for a volume.
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       rlevel
 *              RETURN: the redundancy level for a volume
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_volume_redundancy_level(
	devconfig_t *volume,
	uint16_t *rlevel)
{
	int error = get_uint16(
	    volume->attributes, ATTR_VOLUME_REDUNDANCY, rlevel);

	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	if (error == ENOENT) {
	    volume_set_error(gettext("volume redundancy level not set"));
	    error = ERR_ATTR_UNSET;
	}

	return (error);
}

/*
 * Set the number of paths in volume
 *
 * @param       volume
 *              a devconfig_t representing the volume to modify
 *
 * @param       npaths
 *              the value to set as the number of paths in volume
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_volume_npaths(
	devconfig_t *volume,
	uint16_t npaths)
{
	/* Validate against limits */
	if (npaths < MIN_NDATAPATHS || npaths > MAX_NDATAPATHS) {
	    volume_set_error(
		gettext("number of data paths (%d) out of valid range (%d-%d)"),
		npaths, MIN_NDATAPATHS, MAX_NDATAPATHS);
	    return (-1);
	}

	return (set_uint16(volume->attributes, ATTR_VOLUME_DATAPATHS, npaths));
}

/*
 * Get number of paths in volume
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       npaths
 *              RETURN: number of paths in volume
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_volume_npaths(
	devconfig_t *volume,
	uint16_t *npaths)
{
	int error = get_uint16(
	    volume->attributes, ATTR_VOLUME_DATAPATHS, npaths);

	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	if (error == ENOENT) {
	    volume_set_error(gettext("number of data paths not set"));
	    error = ERR_ATTR_UNSET;
	}

	return (error);
}

/*
 * Set the HSP creation option (for volume, stripe, concat, mirror)
 *
 * @param       volume
 *              a devconfig_t representing the volume to modify
 *
 * @param       usehsp
 *              the value to set as the HSP creation option
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_set_volume_usehsp(
	devconfig_t *volume,
	boolean_t usehsp)
{
	return (set_boolean(volume->attributes, ATTR_VOLUME_USEHSP, usehsp));
}

/*
 * Get HSP creation option (for volume, stripe, concat, mirror)
 *
 * @param       device
 *              a devconfig_t representing the device to examine
 *
 * @param       usehsp
 *              RETURN: HSP creation option (for volume, stripe,
 *              concat, mirror)
 *
 * @return      0
 *              if successful
 *
 * @return      non-zero
 *              if an error occurred.  Use get_error_string() to
 *              retrieve the associated error message.
 */
int
devconfig_get_volume_usehsp(
	devconfig_t *volume,
	boolean_t *usehsp)
{
	int error = get_boolean(
	    volume->attributes, ATTR_VOLUME_USEHSP, usehsp);

	/* Convert ENOENT to ERR_ATTR_UNSET for a custom error message */
	if (error == ENOENT) {
	    volume_set_error(gettext("volume usehsp not set"));
	    error = ERR_ATTR_UNSET;
	}

	return (error);
}

/*
 * Get the string representation of the volume's type
 *
 * @param       type
 *              a valid component_type_t
 *
 * @return      an internationalized string representing the given
 *              type
 */
char *
devconfig_type_to_str(
	component_type_t type)
{
	char *str;

	switch (type) {
	    case TYPE_CONCAT:	    str = gettext("Concat");	    break;
	    case TYPE_CONTROLLER:   str = gettext("Controller");    break;
	    case TYPE_DISKSET:	    str = gettext("Diskset");	    break;
	    case TYPE_DRIVE:	    str = gettext("Disk");	    break;
	    case TYPE_EXTENT:	    str = gettext("Extent");	    break;
	    case TYPE_HOST:	    str = gettext("Host");	    break;
	    case TYPE_HSP:	    str = gettext("Hot Spare Pool"); break;
	    case TYPE_MIRROR:	    str = gettext("Mirror");	    break;
	    case TYPE_RAID5:	    str = gettext("Raid5");	    break;
	    case TYPE_SLICE:	    str = gettext("Slice");	    break;
	    case TYPE_SOFTPART:	    str = gettext("Soft Partition"); break;
	    case TYPE_STRIPE:	    str = gettext("Stripe");	    break;
	    case TYPE_TRANS:	    str = gettext("Trans");	    break;
	    case TYPE_VOLUME:	    str = gettext("Volume");	    break;
	    default:
	    case TYPE_UNKNOWN:	    str = gettext("Unknown");	    break;
	}

	return (str);
}

/*
 * Get the string representation of the mirror's read strategy
 *
 * @param       read
 *              a valid mirror_read_strategy_t
 *
 * @return      an internationalized string representing the given
 *              read strategy
 */
char *
devconfig_read_strategy_to_str(
	mirror_read_strategy_t read)
{
	char *str;

	switch (read) {
	    case MIRROR_READ_ROUNDROBIN: str = gettext("ROUNDROBIN");	break;
	    case MIRROR_READ_GEOMETRIC:	 str = gettext("GEOMETRIC");	break;
	    case MIRROR_READ_FIRST:	 str = gettext("FIRST");	break;
	    default:			 str = "";
	}

	return (str);
}

/*
 * Get the string representation of the mirror's write strategy
 *
 * @param       write
 *              a valid mirror_write_strategy_t
 *
 * @return      an internationalized string representing the given
 *              write strategy
 */
char *
devconfig_write_strategy_to_str(
	mirror_write_strategy_t write)
{
	char *str;

	switch (write) {
	    case MIRROR_WRITE_PARALLEL:	str = gettext("PARALLEL");	break;
	    case MIRROR_WRITE_SERIAL:	str = gettext("SERIAL");	break;
	    default:			str = "";
	}

	return (str);
}

#ifdef DEBUG
/*
 * Dump the contents of a devconfig_t struct to stdout.
 *
 * @param       device
 *              the devconfig_t to examine
 *
 * @param       prefix
 *              a prefix string to print before each line
 */
void
devconfig_dump(
	devconfig_t *device,
	char *prefix)
{
	dlist_t *comps = NULL;
	char **array = NULL;
	char *str = NULL;
	int i = 0;

	component_type_t type = TYPE_UNKNOWN;
	boolean_t bool = B_FALSE;
	uint16_t val16 = 0;
	uint64_t val64 = 0;
	mirror_read_strategy_t read;
	mirror_write_strategy_t write;

	if (device == NULL) {
	    return;
	}

	/* Type */
	if (devconfig_get_type(device, &type) == 0) {
	    printf("%s%s\n", prefix, devconfig_type_to_str(type));
	}

	/* Name */
	if (devconfig_get_name(device, &str) == 0) {
	    printf("%s  name: %s\n", prefix, str);
	}

	/* Size in bytes */
	if (devconfig_get_size(device, &val64) == 0) {
	    printf("%s  size in bytes: %llu\n", prefix, val64);
	}

	/* Size in blocks */
	if (devconfig_get_size_in_blocks(device, &val64) == 0) {
	    printf("%s  size in blocks: %llu\n", prefix, val64);
	}

	/* Use HSP */
	if (devconfig_get_volume_usehsp(device, &bool) == 0) {
	    printf("%s  usehsp: %s\n", prefix, bool? "TRUE" : "FALSE");
	}

	switch (type) {
	    case TYPE_VOLUME:
		/* Volume rlevel */
		if (devconfig_get_volume_redundancy_level(
		    device, &val16) == 0) {
		    printf("%s  volume redundancy level: %d\n", prefix, val16);
		}

		/* Volume npaths */
		if (devconfig_get_volume_npaths(device, &val16) == 0) {
		    printf("%s  volume npaths: %d\n", prefix, val16);
		}
	    break;

	    case TYPE_MIRROR:

		/* Mirror nsubs */
		if (devconfig_get_mirror_nsubs(device, &val16) == 0) {
		    printf("%s  mirror nsubs: %d\n", prefix, val16);
		}

		/* Mirror read */
		if (devconfig_get_mirror_read(device, &read) == 0) {
		    printf("%s  mirror read: %s\n", prefix,
			devconfig_read_strategy_to_str(read));
		}

		/* Mirror write */
		if (devconfig_get_mirror_write(device, &write) == 0) {
		    printf("%s  mirror write: %s\n", prefix,
			devconfig_write_strategy_to_str(write));
		}

		/* Mirror pass */
		if (devconfig_get_mirror_pass(device, &val16) == 0) {
		    printf("%s  mirror pass: %d\n", prefix, val16);
		}
	    break;

	    case TYPE_STRIPE:
		/* Stripe mincomp */
		if (devconfig_get_stripe_mincomp(device, &val16) == 0) {
		    printf("%s  stripe mincomp: %d\n", prefix, val16);
		}

		/* Stripe maxcomp */
		if (devconfig_get_stripe_maxcomp(device, &val16) == 0) {
		    printf("%s  stripe maxcomp: %d\n", prefix, val16);
		}

		/* Stripe interlace */
		if (devconfig_get_stripe_interlace(device, &val64) == 0) {
		    printf("%s  stripe interlace: %lld\n", prefix, val64);
		}
	    break;

	    case TYPE_SLICE:
		/* Slice index */
		if (devconfig_get_slice_index(device, &val16) == 0) {
		    printf("%s  slice index: %d\n", prefix, val16);
		}

		/* Slice start block */
		if (devconfig_get_slice_start_block(device, &val64) == 0) {
		    printf("%s  slice start block: %llu\n", prefix, val64);
		}
	    break;
	}

	array = devconfig_get_available(device);
	if (array != NULL) {
	    printf("%s  available:\n", prefix);
	    for (i = 0; array[i] != NULL; i++) {
		printf("%s    %s\n", prefix, array[i]);
	    }
	}

	array = devconfig_get_unavailable(device);
	if (array != NULL) {
	    printf("%s  unavailable:\n", prefix);
	    for (i = 0; array[i] != NULL; i++) {
		printf("%s    %s\n", prefix, array[i]);
	    }
	}

	printf("\n");

	comps = devconfig_get_components(device);
	if (comps != NULL) {
	    char buf[128];
	    snprintf(buf, 128, "%s%s", prefix, "    ");
	    for (; comps != NULL; comps = comps->next) {
		devconfig_dump((devconfig_t *)comps->obj, buf);
	    }
	}
}
#endif /* DEBUG */