summaryrefslogtreecommitdiff
path: root/usr/src/cmd/lvm/metassist
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/lvm/metassist
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/lvm/metassist')
-rw-r--r--usr/src/cmd/lvm/metassist/Makefile87
-rw-r--r--usr/src/cmd/lvm/metassist/Makefile.env30
-rw-r--r--usr/src/cmd/lvm/metassist/Makefile.targ156
-rw-r--r--usr/src/cmd/lvm/metassist/common/Makefile57
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_defaults.c1875
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_defaults.h853
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_devconfig.c1691
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_devconfig.h998
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_dlist.c512
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_dlist.h278
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_error.c97
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_error.h72
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_nvpair.c726
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_nvpair.h467
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_output.c177
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_output.h130
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_request.c178
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_request.h134
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_string.c462
-rw-r--r--usr/src/cmd/lvm/metassist/common/volume_string.h203
-rw-r--r--usr/src/cmd/lvm/metassist/controller/Makefile49
-rw-r--r--usr/src/cmd/lvm/metassist/controller/getopt_ext.c151
-rw-r--r--usr/src/cmd/lvm/metassist/controller/getopt_ext.h115
-rw-r--r--usr/src/cmd/lvm/metassist/controller/metassist.c1511
-rw-r--r--usr/src/cmd/lvm/metassist/controller/metassist.h79
-rw-r--r--usr/src/cmd/lvm/metassist/layout/Makefile64
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout.c1142
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout.h71
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_concat.c673
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_concat.h59
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_device_cache.c893
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_device_cache.h101
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_device_util.c3458
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_device_util.h140
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_discovery.c2454
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_discovery.h89
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_dlist_util.c454
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_dlist_util.h216
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_hsp.c963
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_hsp.h49
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_messages.c407
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_messages.h72
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_mirror.c2413
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_mirror.h53
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_request.c3418
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_request.h161
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_slice.c2335
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_slice.h135
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_stripe.c1034
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_stripe.h60
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_svm_util.c2087
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_svm_util.h87
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_validate.c1994
-rw-r--r--usr/src/cmd/lvm/metassist/layout/layout_validate.h54
-rw-r--r--usr/src/cmd/lvm/metassist/scripts/Makefile40
-rw-r--r--usr/src/cmd/lvm/metassist/scripts/errifoutput.sh48
-rw-r--r--usr/src/cmd/lvm/metassist/sysfiles/Makefile48
-rw-r--r--usr/src/cmd/lvm/metassist/sysfiles/metassist.xml37
-rw-r--r--usr/src/cmd/lvm/metassist/sysfiles/volume-command.xsl706
-rw-r--r--usr/src/cmd/lvm/metassist/sysfiles/volume-config.dtd163
-rw-r--r--usr/src/cmd/lvm/metassist/sysfiles/volume-defaults.dtd82
-rw-r--r--usr/src/cmd/lvm/metassist/sysfiles/volume-request.dtd388
-rw-r--r--usr/src/cmd/lvm/metassist/xml/Makefile48
-rw-r--r--usr/src/cmd/lvm/metassist/xml/xml_convert.c2311
-rw-r--r--usr/src/cmd/lvm/metassist/xml/xml_convert.h157
65 files changed, 40252 insertions, 0 deletions
diff --git a/usr/src/cmd/lvm/metassist/Makefile b/usr/src/cmd/lvm/metassist/Makefile
new file mode 100644
index 0000000000..418bdeb5b9
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/Makefile
@@ -0,0 +1,87 @@
+#
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+METASSIST_TOPLEVEL = .
+
+PROG = metassist
+
+# Subdirectories with message catalogs
+POSUBDIRS = \
+ common \
+ xml \
+ layout \
+ controller
+
+# For message catalog
+POFILES = $(POSUBDIRS:%=%/%p.po)
+
+# Subdirectories that must be made
+SUBDIRS = \
+ scripts \
+ sysfiles \
+ $(POSUBDIRS)
+
+SUBOBJS = \
+ common/volume_defaults.o \
+ common/volume_devconfig.o \
+ common/volume_dlist.o \
+ common/volume_error.o \
+ common/volume_nvpair.o \
+ common/volume_output.o \
+ common/volume_request.o \
+ common/volume_string.o \
+ xml/xml_convert.o \
+ layout/layout.o \
+ layout/layout_concat.o \
+ layout/layout_device_cache.o \
+ layout/layout_device_util.o \
+ layout/layout_discovery.o \
+ layout/layout_dlist_util.o \
+ layout/layout_hsp.o \
+ layout/layout_messages.o \
+ layout/layout_mirror.o \
+ layout/layout_request.o \
+ layout/layout_slice.o \
+ layout/layout_stripe.o \
+ layout/layout_svm_util.o \
+ layout/layout_validate.o \
+ controller/getopt_ext.o \
+ controller/metassist.o
+
+include $(METASSIST_TOPLEVEL)/../../Makefile.cmd
+include $(METASSIST_TOPLEVEL)/Makefile.env
+
+LDLIBS += -ldiskmgt -lmeta -lnvpair -lxml2 -lxslt -lm
+
+POFILE = metassistp.po
+
+include $(METASSIST_TOPLEVEL)/Makefile.targ
+
+# Build master .po file from subdirs' .po files
+$(POFILE): $(POSUBDIRS) .WAIT $(POFILES)
+ $(BUILDPO.pofiles)
diff --git a/usr/src/cmd/lvm/metassist/Makefile.env b/usr/src/cmd/lvm/metassist/Makefile.env
new file mode 100644
index 0000000000..598e2eba8b
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/Makefile.env
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+# Define DEBUG when running nightly DEBUG build
+$(NOT_RELEASE_BUILD) CPPFLAGS += -DDEBUG
diff --git a/usr/src/cmd/lvm/metassist/Makefile.targ b/usr/src/cmd/lvm/metassist/Makefile.targ
new file mode 100644
index 0000000000..41c91e587c
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/Makefile.targ
@@ -0,0 +1,156 @@
+#
+# 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 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+# Wrapper script that will fail if there is any output, for commands
+# that don't exit with a non-zero exit code when they find errors
+ERRIFOUTPUT_SCRIPT = $(METASSIST_TOPLEVEL)/scripts/errifoutput
+
+# Suffixes for files that flag a particular check
+SUFFIX_LINT = .lint
+SUFFIX_CSTYLE = .cstyle
+SUFFIX_HDRCHK = .hdrchk
+SUFFIX_KEYWORDS = .keywords
+
+# Get list of (non-deleted) SCCS files
+tmp1 :sh= echo SCCS/s.*
+tmp2 = ${tmp1:SCCS/s.%=%}
+SCCSFILES = ${tmp2:.del-%=}
+
+all := TARGET = all
+install := TARGET = install
+clean := TARGET = clean
+clobber := TARGET = clobber
+catalog := TARGET = catalog
+check := TARGET = check
+cstyle := TARGET = cstyle
+hdrchk := TARGET = hdrchk
+keywords := TARGET = keywords
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all: ${SUBDIRS} ${OBJS} .WAIT ${PROG}
+
+# Create the binary
+$(PROG): $(OBJS) $(SUBOBJS)
+ $(LINK.c) $(OBJS) $(SUBOBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+# Make objects in subdirectories
+$(SUBOBJS) $(POFILES):
+ @cd $(@D); ${MAKE} $(@F)
+
+ROOTXML= $(ROOTSHLIB)/xml
+
+# Rule/definitions for DTDs
+ROOTXMLDTD= $(ROOTXML)/dtd
+ROOTXMLDTDFILES= $(DTDFILES:%=$(ROOTXMLDTD)/%)
+$(ROOTXMLDTDFILES) := OWNER = root
+$(ROOTXMLDTDFILES) := GROUP = sys
+$(ROOTXMLDTDFILES) := FILEMODE = 0444
+
+$(ROOTXMLDTD)/%: %
+ $(INS.file)
+
+# Rule/definitions for XSL style sheets
+ROOTXMLSTYLE= $(ROOTXML)/style
+ROOTXMLSTYLEFILES= $(STYLEFILES:%=$(ROOTXMLSTYLE)/%)
+$(ROOTXMLSTYLEFILES) := OWNER = root
+$(ROOTXMLSTYLEFILES) := GROUP = sys
+$(ROOTXMLSTYLEFILES) := FILEMODE = 0444
+
+$(ROOTXMLSTYLE)/%: %
+ $(INS.file)
+
+# Rule/definitions for /etc/default
+ROOTETCDEFAULTFILES= $(DEFAULTFILES:%=$(ROOTETCDEFAULT)/%)
+$(ROOTETCDEFAULTFILES) := OWNER = root
+$(ROOTETCDEFAULTFILES) := GROUP = sys
+$(ROOTETCDEFAULTFILES) := FILEMODE = 0644
+
+$(ROOTETCDEFAULT)/%: %
+ $(INS.file)
+
+# Install recursively
+install: all .WAIT \
+ ${SUBDIRS} \
+ $(ROOTUSRSBINPROG) \
+ $(ROOTETCDEFAULTFILES) \
+ $(ROOTXMLDTDFILES) \
+ $(ROOTXMLSTYLEFILES)
+
+# Pattern-matching rule for lint
+%$(SUFFIX_LINT): %
+ ${LINT.c} -I. ${INCLUDES} -y -c $< && touch $@
+
+# Run lint on all source files
+lint: ${SUBDIRS} $(SRCS:%=%$(SUFFIX_LINT))
+
+# Pattern-matching rule for cstyle
+# cstyle doesn't exit with a non-zero exit code when it finds errors,
+# so run it through a wrapper script that fails if there is any output
+%$(SUFFIX_CSTYLE): %
+ $(ERRIFOUTPUT_SCRIPT) ${CSTYLE} -p -P $< && touch $@
+
+# Run cstyle on all source files and headers
+cstyle: $(ERRIFOUTPUT_SCRIPT) ${SUBDIRS} $(SRCS:%=%.cstyle) $(HDRS:%=%$(SUFFIX_CSTYLE))
+
+# Pattern-matching rule for hdrchk
+# hdrchk doesn't exit with a non-zero exit code when it finds errors,
+# so run it through a wrapper script that fails if there is any output
+%$(SUFFIX_HDRCHK): %
+ $(ERRIFOUTPUT_SCRIPT) ${HDRCHK} $< && touch $@
+
+# Run hdrchk on all headers
+hdrchk: $(ERRIFOUTPUT_SCRIPT) ${SUBDIRS} $(HDRS:%=%$(SUFFIX_HDRCHK))
+
+# Pattern-matching rule for keywords
+%$(SUFFIX_KEYWORDS): %
+ keywords $< && touch $@
+
+# Run keywords on all SCCS files
+keywords: ${SUBDIRS} ${SCCSFILES:%=%$(SUFFIX_KEYWORDS)}
+
+check: cstyle lint hdrchk keywords
+
+clobber: ${SUBDIRS}
+
+clean: ${SUBDIRS}
+ -${RM} *.o *.ln *.i *~ core a.out $(CLEANFILES) \
+ *$(SUFFIX_LINT) *$(SUFFIX_CSTYLE) *$(SUFFIX_HDRCHK) *$(SUFFIX_KEYWORDS)
+
+catalog: $(POFILE)
+
+${SUBDIRS}: FRC
+ @cd $@; pwd; ${MAKE} ${TARGET}
+
+FRC:
+
+# Included for message catalog handling
+include $(SRC)/Makefile.msg.targ
+include $(METASSIST_TOPLEVEL)/../../Makefile.targ
diff --git a/usr/src/cmd/lvm/metassist/common/Makefile b/usr/src/cmd/lvm/metassist/common/Makefile
new file mode 100644
index 0000000000..d3c5882528
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/Makefile
@@ -0,0 +1,57 @@
+#
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+METASSIST_TOPLEVEL = ..
+
+SRCS= \
+ volume_defaults.c \
+ volume_devconfig.c \
+ volume_dlist.c \
+ volume_error.c \
+ volume_nvpair.c \
+ volume_output.c \
+ volume_request.c \
+ volume_string.c
+
+OBJS = $(SRCS:%.c=%.o)
+HDRS = $(SRCS:%.c=%.h)
+MSGFILES = $(SRCS:%.c=%.i)
+
+include $(METASSIST_TOPLEVEL)/../../Makefile.cmd
+include $(METASSIST_TOPLEVEL)/Makefile.env
+
+INCLUDES += -I../../../../lib/lvm/libmeta/common/hdrs
+CFLAGS += $(INCLUDES)
+
+POFILE = commonp.po
+
+include $(METASSIST_TOPLEVEL)/Makefile.targ
+
+# Build .po file from message files
+$(POFILE): $(MSGFILES)
+ $(BUILDPO.msgfiles)
diff --git a/usr/src/cmd/lvm/metassist/common/volume_defaults.c b/usr/src/cmd/lvm/metassist/common/volume_defaults.c
new file mode 100644
index 0000000000..cb6909f77f
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_defaults.c
@@ -0,0 +1,1875 @@
+/*
+ * 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 "%Z%%M% %I% %E% SMI"
+
+#include <string.h>
+#include <libintl.h>
+#include "volume_defaults.h"
+#include "volume_error.h"
+
+/*
+ * Methods which manipulate a defaults_t struct
+ */
+
+static int defaults_get_singleton_component(
+ defaults_t *defaults, char *disksetname,
+ component_type_t type, devconfig_t **component, boolean_t create);
+
+/*
+ * Constructor: Create a defaults_t struct populated with default
+ * values. This defaults_t must be freed.
+ *
+ * @param defaults
+ * RETURN: a pointer to a new defaults_t
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+new_defaults(
+ defaults_t **defaults)
+{
+ devconfig_t *diskset;
+ int error = 0;
+
+ *defaults = (defaults_t *)calloc(1, sizeof (defaults_t));
+ if (*defaults == NULL) {
+ volume_set_error(gettext("new_defaults calloc() failed"));
+ return (-1);
+ }
+
+ /*
+ * Create initial "global" (disk set-independent) defaults, as
+ * a devconfig_t of type disk set with NULL name
+ */
+ if ((error = new_devconfig(&diskset, TYPE_DISKSET)) != 0) {
+ free_defaults(*defaults);
+ return (error);
+ }
+
+ /* Append global defaults disk set to disksets */
+ defaults_set_disksets(
+ *defaults, dlist_append(dlist_new_item(diskset),
+ defaults_get_disksets(*defaults), AT_TAIL));
+
+ /* Set defaults */
+ if ((error = defaults_set_mirror_nsubs(
+ *defaults, NULL, DEFAULT_MIRROR_NSUBS)) != 0 ||
+
+ (error = defaults_set_mirror_read(
+ *defaults, NULL, DEFAULT_MIRROR_READ)) != 0 ||
+
+ (error = defaults_set_mirror_write(
+ *defaults, NULL, DEFAULT_MIRROR_WRITE)) != 0 ||
+
+ (error = defaults_set_mirror_pass(
+ *defaults, NULL, DEFAULT_MIRROR_PASS)) != 0 ||
+
+ (error = defaults_set_mirror_usehsp(
+ *defaults, NULL, DEFAULT_MIRROR_USEHSP)) != 0 ||
+
+ (error = defaults_set_concat_usehsp(
+ *defaults, NULL, DEFAULT_CONCAT_USEHSP)) != 0 ||
+
+ (error = defaults_set_stripe_interlace(
+ *defaults, NULL, DEFAULT_STRIPE_INTERLACE)) != 0 ||
+
+ (error = defaults_set_stripe_mincomp(
+ *defaults, NULL, DEFAULT_STRIPE_MINCOMP)) != 0 ||
+
+ (error = defaults_set_stripe_maxcomp(
+ *defaults, NULL, DEFAULT_STRIPE_MAXCOMP)) != 0 ||
+
+ (error = defaults_set_stripe_usehsp(
+ *defaults, NULL, DEFAULT_STRIPE_USEHSP)) != 0 ||
+
+ (error = defaults_set_volume_redundancy_level(
+ *defaults, NULL, DEFAULT_VOLUME_REDUND_LEVEL)) != 0 ||
+
+ (error = defaults_set_volume_npaths(
+ *defaults, NULL, DEFAULT_VOLUME_NPATHS)) != 0 ||
+
+ (error = defaults_set_volume_usehsp(
+ *defaults, NULL, DEFAULT_VOLUME_USEHSP)) != 0) {
+
+ free_defaults(*defaults);
+ return (error);
+ }
+
+ return (0);
+}
+
+/*
+ * Free memory (recursively) allocated to a defaults_t struct
+ *
+ * @param arg
+ * pointer to the defaults_t struct to free
+ */
+void
+free_defaults(
+ void *arg)
+{
+ defaults_t *defaults = (defaults_t *)arg;
+
+ if (defaults == NULL) {
+ return;
+ }
+
+ /* Free the disksets */
+ if (defaults->disksets != NULL) {
+ dlist_free_items(defaults->disksets, free_devconfig);
+ }
+
+ /* Free the devconfig itself */
+ free(defaults);
+}
+
+/*
+ * Set list of diskset specific defaults
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param disksets
+ * a dlist_t representing the defaults for specific
+ * named disk sets
+ */
+void
+defaults_set_disksets(
+ defaults_t *defaults,
+ dlist_t *disksets)
+{
+ defaults->disksets = disksets;
+}
+
+/*
+ * Get list of diskset specific defaults
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @return a dlist_t representing the defaults for specific
+ * named disk sets
+ */
+dlist_t *
+defaults_get_disksets(
+ defaults_t *defaults)
+{
+ return (defaults->disksets);
+}
+
+/*
+ * Get a disk set with the given name from the given defaults_t
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param name
+ * the name of the disk set whose defaults to retrieve,
+ * or NULL to get the defaults for all disk sets
+ *
+ * @param diskset
+ * RETURN: defaults for the given named disk set, or
+ * defaults for all disk sets if name is NULL
+ *
+ * @return ENOENT
+ * if the named disk set does not exist
+ *
+ * @return 0
+ * if the named disk set exists
+ */
+int
+defaults_get_diskset_by_name(
+ defaults_t *defaults,
+ char *name,
+ devconfig_t **diskset)
+{
+ dlist_t *list;
+ *diskset = NULL;
+
+ /* Get list of disk sets */
+ list = defaults_get_disksets(defaults);
+ if (list != NULL) {
+
+ /* For each disk set-specific defaults... */
+ for (; list != NULL; list = list->next) {
+
+ char *dname = NULL;
+ devconfig_t *d = (devconfig_t *)list->obj;
+
+ /* Get the name if this disk set */
+ devconfig_get_name(d, &dname);
+
+ /* Do the names match? */
+ if (
+ /* Global defaults disk set */
+ (name == NULL && dname == NULL) ||
+
+ /* Named disk set */
+ (name != NULL && dname != NULL &&
+ strcmp(name, dname) == 0)) {
+
+ *diskset = d;
+ break;
+ }
+ }
+ }
+
+ /* Diskset doesn't exist */
+ if (*diskset == NULL) {
+ return (ENOENT);
+ }
+
+ return (0);
+}
+
+/*
+ * Get the first component of the given type from the given disk set.
+ * If not found, create the component if requested.
+ *
+ * @return ENOENT
+ * if the given disk set does not exist, or it exists,
+ * but the requested component does not exist under it
+ * and its creation was not requested
+ *
+ * @return 0
+ * if the requested component exists or was created
+ *
+ * @return non-zero
+ * if the requested component does not exist and could
+ * not be created
+ */
+static int
+defaults_get_singleton_component(
+ defaults_t *defaults,
+ char *disksetname,
+ component_type_t type,
+ devconfig_t **component,
+ boolean_t create)
+{
+ int error;
+ devconfig_t *diskset;
+
+ /* Get the disk set referred to */
+ if ((error = defaults_get_diskset_by_name(
+ defaults, disksetname, &diskset)) != 0) {
+
+ volume_set_error(
+ gettext("could not get defaults for disk set %s"),
+ disksetname == NULL ? gettext("<NULL>") : disksetname);
+
+ return (error);
+ }
+
+ /*
+ * Get the singleton component under this disk set, create if
+ * requested
+ */
+ return (devconfig_get_component(diskset, type, component, create));
+}
+
+/*
+ * Set name of the the default HSP to use
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param name
+ * the name of the default HSP to use
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_set_hsp_name(
+ defaults_t *defaults,
+ char *diskset,
+ char *name)
+{
+ devconfig_t *hsp = NULL;
+ int error = 0;
+
+ /* Get/create singleton HSP element for this disk set */
+ if ((error = defaults_get_singleton_component(
+ defaults, diskset, TYPE_HSP, &hsp, TRUE)) != 0) {
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Set the name attribute */
+ return (devconfig_set_hsp_name(hsp, name));
+}
+
+/*
+ * Get the name of the default HSP to use
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param name
+ * RETURN: the name of the default HSP to use
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_get_hsp_name(
+ defaults_t *defaults,
+ char *diskset,
+ char **name)
+{
+ char *disksets[2];
+ devconfig_t *hsp;
+ int error;
+ int i = 0;
+
+ /* Check both the given and global (NULL) disk sets for the value */
+ disksets[0] = diskset;
+ disksets[1] = NULL;
+ do {
+ /* Get/create singleton HSP element for this disk set */
+ error = defaults_get_singleton_component(
+ defaults, disksets[i], TYPE_HSP, &hsp, FALSE);
+
+ switch (error) {
+ /* HSP found for this disk set */
+ case 0:
+ /* Get the nsubs attribute */
+ if ((error = devconfig_get_name(hsp, name)) == 0) {
+ /* nsubs attribute found */
+ return (0);
+ }
+
+ /* FALLTHROUGH */
+
+ /* HSP not found for this disk set */
+ case ENOENT:
+ break;
+
+ /* Invalid disk set, or HSP couldn't be created */
+ default:
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Stop after the global (NULL) disk set has been searched */
+ } while (disksets[i++] != NULL);
+
+ return (ENOENT);
+}
+
+/*
+ * Set the default number of submirrors for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default number of submirrors
+ * for mirrored volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_set_mirror_nsubs(
+ defaults_t *defaults,
+ char *diskset,
+ uint16_t val)
+{
+ devconfig_t *mirror = NULL;
+ int error = 0;
+
+ /* Get/create singleton mirror element for this disk set */
+ if ((error = defaults_get_singleton_component(
+ defaults, diskset, TYPE_MIRROR, &mirror, TRUE)) != 0) {
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Set the nsubs attribute */
+ return (devconfig_set_mirror_nsubs(mirror, val));
+}
+
+/*
+ * Get the default number of submirrors for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default number of submirrors for mirrored
+ * volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_get_mirror_nsubs(
+ defaults_t *defaults,
+ char *diskset,
+ uint16_t *val)
+{
+ char *disksets[2];
+ devconfig_t *mirror;
+ int error;
+ int i = 0;
+
+ /* Check both the given and global (NULL) disk sets for the value */
+ disksets[0] = diskset;
+ disksets[1] = NULL;
+ do {
+ /* Get/create singleton mirror element for this disk set */
+ error = defaults_get_singleton_component(
+ defaults, disksets[i], TYPE_MIRROR, &mirror, FALSE);
+
+ switch (error) {
+ /* mirror found for this disk set */
+ case 0:
+ /* Get the nsubs attribute */
+ if ((error = devconfig_get_mirror_nsubs(
+ mirror, val)) == 0) {
+ /* nsubs attribute found */
+ return (0);
+ }
+
+ /* FALLTHROUGH */
+
+ /* mirror not found for this disk set */
+ case ENOENT:
+ break;
+
+ /* Invalid disk set, or mirror couldn't be created */
+ default:
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Stop after the global (NULL) disk set has been searched */
+ } while (disksets[i++] != NULL);
+
+ return (ENOENT);
+}
+
+/*
+ * Set the default read strategy for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default read strategy for
+ * mirrored volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_set_mirror_read(
+ defaults_t *defaults,
+ char *diskset,
+ mirror_read_strategy_t val)
+{
+ devconfig_t *mirror = NULL;
+ int error = 0;
+
+ /* Get/create singleton mirror element for this disk set */
+ if ((error = defaults_get_singleton_component(
+ defaults, diskset, TYPE_MIRROR, &mirror, TRUE)) != 0) {
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Set the read attribute */
+ return (devconfig_set_mirror_read(mirror, val));
+}
+
+/*
+ * Get the default read strategy for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default read strategy for mirrored volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_get_mirror_read(
+ defaults_t *defaults,
+ char *diskset,
+ mirror_read_strategy_t *val)
+{
+ char *disksets[2];
+ devconfig_t *mirror;
+ int error;
+ int i = 0;
+
+ /* Check both the given and global (NULL) disk sets for the value */
+ disksets[0] = diskset;
+ disksets[1] = NULL;
+ do {
+ /* Get/create singleton mirror element for this disk set */
+ error = defaults_get_singleton_component(
+ defaults, disksets[i], TYPE_MIRROR, &mirror, FALSE);
+
+ switch (error) {
+ /* mirror found for this disk set */
+ case 0:
+ /* Get the read attribute */
+ if ((error = devconfig_get_mirror_read(mirror, val)) == 0) {
+ /* read attribute found */
+ return (0);
+ }
+
+ /* FALLTHROUGH */
+
+ /* mirror not found for this disk set */
+ case ENOENT:
+ break;
+
+ /* Invalid disk set, or mirror couldn't be created */
+ default:
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Stop after the global (NULL) disk set has been searched */
+ } while (disksets[i++] != NULL);
+
+ return (ENOENT);
+}
+
+/*
+ * Set the default write strategy for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default write strategy for
+ * mirrored volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_set_mirror_write(
+ defaults_t *defaults,
+ char *diskset,
+ mirror_write_strategy_t val)
+{
+ devconfig_t *mirror = NULL;
+ int error = 0;
+
+ /* Get/create singleton mirror element for this disk set */
+ if ((error = defaults_get_singleton_component(
+ defaults, diskset, TYPE_MIRROR, &mirror, TRUE)) != 0) {
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Set the write attribute */
+ return (devconfig_set_mirror_write(mirror, val));
+}
+
+/*
+ * Get the default write strategy for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default write strategy for mirrored
+ * volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_get_mirror_write(
+ defaults_t *defaults,
+ char *diskset,
+ mirror_write_strategy_t *val)
+{
+ char *disksets[2];
+ devconfig_t *mirror;
+ int error;
+ int i = 0;
+
+ /* Check both the given and global (NULL) disk sets for the value */
+ disksets[0] = diskset;
+ disksets[1] = NULL;
+ do {
+ /* Get/create singleton mirror element for this disk set */
+ error = defaults_get_singleton_component(
+ defaults, disksets[i], TYPE_MIRROR, &mirror, FALSE);
+
+ switch (error) {
+ /* mirror found for this disk set */
+ case 0:
+ /* Get the write attribute */
+ if ((error = devconfig_get_mirror_write(
+ mirror, val)) == 0) {
+ /* write attribute found */
+ return (0);
+ }
+
+ /* FALLTHROUGH */
+
+ /* mirror not found for this disk set */
+ case ENOENT:
+ break;
+
+ /* Invalid disk set, or mirror couldn't be created */
+ default:
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Stop after the global (NULL) disk set has been searched */
+ } while (disksets[i++] != NULL);
+
+ return (ENOENT);
+}
+
+/*
+ * Set the default resync pass for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default resync pass for
+ * mirrored volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_set_mirror_pass(
+ defaults_t *defaults,
+ char *diskset,
+ uint16_t val)
+{
+ devconfig_t *mirror = NULL;
+ int error = 0;
+
+ /* Get/create singleton mirror element for this disk set */
+ if ((error = defaults_get_singleton_component(
+ defaults, diskset, TYPE_MIRROR, &mirror, TRUE)) != 0) {
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Set the pass attribute */
+ return (devconfig_set_mirror_pass(mirror, val));
+}
+
+/*
+ * Get the default resync pass for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default resync pass for mirrored volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_get_mirror_pass(
+ defaults_t *defaults,
+ char *diskset,
+ uint16_t *val)
+{
+ char *disksets[2];
+ devconfig_t *mirror;
+ int error;
+ int i = 0;
+
+ /* Check both the given and global (NULL) disk sets for the value */
+ disksets[0] = diskset;
+ disksets[1] = NULL;
+ do {
+ /* Get/create singleton mirror element for this disk set */
+ error = defaults_get_singleton_component(
+ defaults, disksets[i], TYPE_MIRROR, &mirror, FALSE);
+
+ switch (error) {
+ /* mirror found for this disk set */
+ case 0:
+ /* Get the pass attribute */
+ if ((error = devconfig_get_mirror_pass(mirror, val)) == 0) {
+ /* pass attribute found */
+ return (0);
+ }
+
+ /* FALLTHROUGH */
+
+ /* mirror not found for this disk set */
+ case ENOENT:
+ break;
+
+ /* Invalid disk set, or mirror couldn't be created */
+ default:
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Stop after the global (NULL) disk set has been searched */
+ } while (disksets[i++] != NULL);
+
+ return (ENOENT);
+}
+
+/*
+ * Set the default HSP creation flag for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default HSP creation flag for
+ * mirrored volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_set_mirror_usehsp(
+ defaults_t *defaults,
+ char *diskset,
+ boolean_t val)
+{
+ devconfig_t *mirror = NULL;
+ int error = 0;
+
+ /* Get/create singleton mirror element for this disk set */
+ if ((error = defaults_get_singleton_component(
+ defaults, diskset, TYPE_MIRROR, &mirror, TRUE)) != 0) {
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Set the usehsp attribute */
+ return (devconfig_set_volume_usehsp(mirror, val));
+}
+
+/*
+ * Get the default HSP creation flag for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default HSP creation flag for mirrored
+ * volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_get_mirror_usehsp(
+ defaults_t *defaults,
+ char *diskset,
+ boolean_t *val)
+{
+ char *disksets[2];
+ devconfig_t *mirror;
+ int error;
+ int i = 0;
+
+ /* Check both the given and global (NULL) disk sets for the value */
+ disksets[0] = diskset;
+ disksets[1] = NULL;
+ do {
+ /* Get/create singleton mirror element for this disk set */
+ error = defaults_get_singleton_component(
+ defaults, disksets[i], TYPE_MIRROR, &mirror, FALSE);
+
+ switch (error) {
+ /* mirror found for this disk set */
+ case 0:
+ /* Get the usehsp attribute */
+ if ((error = devconfig_get_volume_usehsp(
+ mirror, val)) == 0) {
+ /* usehsp attribute found */
+ return (0);
+ }
+
+ /* FALLTHROUGH */
+
+ /* mirror not found for this disk set */
+ case ENOENT:
+ break;
+
+ /* Invalid disk set, or mirror couldn't be created */
+ default:
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Stop after the global (NULL) disk set has been searched */
+ } while (disksets[i++] != NULL);
+
+ return (ENOENT);
+}
+
+/*
+ * Set the default HSP creation flag for concatenated volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default HSP creation flag for
+ * concatenated volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_set_concat_usehsp(
+ defaults_t *defaults,
+ char *diskset,
+ boolean_t val)
+{
+ devconfig_t *concat = NULL;
+ int error = 0;
+
+ /* Get/create singleton concat element for this disk set */
+ if ((error = defaults_get_singleton_component(
+ defaults, diskset, TYPE_CONCAT, &concat, TRUE)) != 0) {
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Set the usehsp attribute */
+ return (devconfig_set_volume_usehsp(concat, val));
+}
+
+/*
+ * Get the default HSP creation flag for concatenated volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default HSP creation flag for concatenated
+ * volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_get_concat_usehsp(
+ defaults_t *defaults,
+ char *diskset,
+ boolean_t *val)
+{
+ char *disksets[2];
+ devconfig_t *concat;
+ int error;
+ int i = 0;
+
+ /* Check both the given and global (NULL) disk sets for the value */
+ disksets[0] = diskset;
+ disksets[1] = NULL;
+ do {
+ /* Get/create singleton concat element for this disk set */
+ error = defaults_get_singleton_component(
+ defaults, disksets[i], TYPE_CONCAT, &concat, FALSE);
+
+ switch (error) {
+ /* concat found for this disk set */
+ case 0:
+ /* Get the usehsp attribute */
+ if ((error = devconfig_get_volume_usehsp(
+ concat, val)) == 0) {
+ /* usehsp attribute found */
+ return (0);
+ }
+
+ /* FALLTHROUGH */
+
+ /* concat not found for this disk set */
+ case ENOENT:
+ break;
+
+ /* Invalid disk set, or concat couldn't be created */
+ default:
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Stop after the global (NULL) disk set has been searched */
+ } while (disksets[i++] != NULL);
+
+ return (ENOENT);
+}
+
+/*
+ * Set the default minimum number of components for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default minimum number of
+ * components for striped volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_set_stripe_mincomp(
+ defaults_t *defaults,
+ char *diskset,
+ uint16_t val)
+{
+ devconfig_t *stripe = NULL;
+ int error = 0;
+
+ /* Get/create singleton stripe element for this disk set */
+ if ((error = defaults_get_singleton_component(
+ defaults, diskset, TYPE_STRIPE, &stripe, TRUE)) != 0) {
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Set the mincomp attribute */
+ return (devconfig_set_stripe_mincomp(stripe, val));
+}
+
+/*
+ * Get the default minimum number of components for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default minimum number of components for
+ * striped volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_get_stripe_mincomp(
+ defaults_t *defaults,
+ char *diskset,
+ uint16_t *val)
+{
+ char *disksets[2];
+ devconfig_t *stripe;
+ int error;
+ int i = 0;
+
+ /* Check both the given and global (NULL) disk sets for the value */
+ disksets[0] = diskset;
+ disksets[1] = NULL;
+ do {
+ /* Get/create singleton stripe element for this disk set */
+ error = defaults_get_singleton_component(
+ defaults, disksets[i], TYPE_STRIPE, &stripe, FALSE);
+
+ switch (error) {
+ /* stripe found for this disk set */
+ case 0:
+ /* Get the mincomp attribute */
+ if ((error = devconfig_get_stripe_mincomp(
+ stripe, val)) == 0) {
+ /* mincomp attribute found */
+ return (0);
+ }
+
+ /* FALLTHROUGH */
+
+ /* stripe not found for this disk set */
+ case ENOENT:
+ break;
+
+ /* Invalid disk set, or stripe couldn't be created */
+ default:
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Stop after the global (NULL) disk set has been searched */
+ } while (disksets[i++] != NULL);
+
+ return (ENOENT);
+}
+
+/*
+ * Set the default maximum number of components for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default maximum number of
+ * components for striped volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_set_stripe_maxcomp(
+ defaults_t *defaults,
+ char *diskset,
+ uint16_t val)
+{
+ devconfig_t *stripe = NULL;
+ int error = 0;
+
+ /* Get/create singleton stripe element for this disk set */
+ if ((error = defaults_get_singleton_component(
+ defaults, diskset, TYPE_STRIPE, &stripe, TRUE)) != 0) {
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Set the maxcomp attribute */
+ return (devconfig_set_stripe_maxcomp(stripe, val));
+}
+
+/*
+ * Get the default maximum number of components for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default maximum number of components for
+ * striped volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_get_stripe_maxcomp(
+ defaults_t *defaults,
+ char *diskset,
+ uint16_t *val)
+{
+ char *disksets[2];
+ devconfig_t *stripe;
+ int error;
+ int i = 0;
+
+ /* Check both the given and global (NULL) disk sets for the value */
+ disksets[0] = diskset;
+ disksets[1] = NULL;
+ do {
+ /* Get/create singleton stripe element for this disk set */
+ error = defaults_get_singleton_component(
+ defaults, disksets[i], TYPE_STRIPE, &stripe, FALSE);
+
+ switch (error) {
+ /* stripe found for this disk set */
+ case 0:
+ /* Get the maxcomp attribute */
+ if ((error = devconfig_get_stripe_maxcomp(
+ stripe, val)) == 0) {
+ /* maxcomp attribute found */
+ return (0);
+ }
+
+ /* FALLTHROUGH */
+
+ /* stripe not found for this disk set */
+ case ENOENT:
+ break;
+
+ /* Invalid disk set, or stripe couldn't be created */
+ default:
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Stop after the global (NULL) disk set has been searched */
+ } while (disksets[i++] != NULL);
+
+ return (ENOENT);
+}
+
+/*
+ * Set the default interlace for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default interlace for striped
+ * volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_set_stripe_interlace(
+ defaults_t *defaults,
+ char *diskset,
+ uint64_t val)
+{
+ devconfig_t *stripe = NULL;
+ int error = 0;
+
+ /* Get/create singleton stripe element for this disk set */
+ if ((error = defaults_get_singleton_component(
+ defaults, diskset, TYPE_STRIPE, &stripe, TRUE)) != 0) {
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Set the interlace attribute */
+ return (devconfig_set_stripe_interlace(stripe, val));
+}
+
+/*
+ * Get the default interlace for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default interlace for striped volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_get_stripe_interlace(
+ defaults_t *defaults,
+ char *diskset,
+ uint64_t *val)
+{
+ char *disksets[2];
+ devconfig_t *stripe;
+ int error;
+ int i = 0;
+
+ /* Check both the given and global (NULL) disk sets for the value */
+ disksets[0] = diskset;
+ disksets[1] = NULL;
+ do {
+ /* Get/create singleton stripe element for this disk set */
+ error = defaults_get_singleton_component(
+ defaults, disksets[i], TYPE_STRIPE, &stripe, FALSE);
+
+ switch (error) {
+ /* stripe found for this disk set */
+ case 0:
+ /* Get the interlace attribute */
+ if ((error = devconfig_get_stripe_interlace(
+ stripe, val)) == 0) {
+ /* interlace attribute found */
+ return (0);
+ }
+
+ /* FALLTHROUGH */
+
+ /* stripe not found for this disk set */
+ case ENOENT:
+ break;
+
+ /* Invalid disk set, or stripe couldn't be created */
+ default:
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Stop after the global (NULL) disk set has been searched */
+ } while (disksets[i++] != NULL);
+
+ return (ENOENT);
+}
+
+/*
+ * Set the default HSP creation flag for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default HSP creation flag for
+ * striped volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_set_stripe_usehsp(
+ defaults_t *defaults,
+ char *diskset,
+ boolean_t val)
+{
+ devconfig_t *stripe = NULL;
+ int error = 0;
+
+ /* Get/create singleton stripe element for this disk set */
+ if ((error = defaults_get_singleton_component(
+ defaults, diskset, TYPE_STRIPE, &stripe, TRUE)) != 0) {
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Set the usehsp attribute */
+ return (devconfig_set_volume_usehsp(stripe, val));
+}
+
+/*
+ * Get the default HSP creation flag for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default HSP creation flag for striped
+ * volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_get_stripe_usehsp(
+ defaults_t *defaults,
+ char *diskset,
+ boolean_t *val)
+{
+ char *disksets[2];
+ devconfig_t *stripe;
+ int error;
+ int i = 0;
+
+ /* Check both the given and global (NULL) disk sets for the value */
+ disksets[0] = diskset;
+ disksets[1] = NULL;
+ do {
+ /* Get/create singleton stripe element for this disk set */
+ error = defaults_get_singleton_component(
+ defaults, disksets[i], TYPE_STRIPE, &stripe, FALSE);
+
+ switch (error) {
+ /* stripe found for this disk set */
+ case 0:
+ /* Get the usehsp attribute */
+ if ((error = devconfig_get_volume_usehsp(
+ stripe, val)) == 0) {
+ /* usehsp attribute found */
+ return (0);
+ }
+
+ /* FALLTHROUGH */
+
+ /* stripe not found for this disk set */
+ case ENOENT:
+ break;
+
+ /* Invalid disk set, or stripe couldn't be created */
+ default:
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Stop after the global (NULL) disk set has been searched */
+ } while (disksets[i++] != NULL);
+
+ return (ENOENT);
+}
+
+/*
+ * Set the default redundancy level for generic volumes.
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * If 0, a stripe will be created by default. If > 0, a
+ * mirror with this number of submirrors will be created
+ * by default.
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_set_volume_redundancy_level(
+ defaults_t *defaults,
+ char *diskset,
+ uint16_t val)
+{
+ devconfig_t *volume = NULL;
+ int error = 0;
+
+ /* Get/create singleton volume element for this disk set */
+ if ((error = defaults_get_singleton_component(
+ defaults, diskset, TYPE_VOLUME, &volume, TRUE)) != 0) {
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Set the redundancy level */
+ return (devconfig_set_volume_redundancy_level(volume, val));
+}
+
+/*
+ * Get the default redundancy level for generic volumes.
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default redundancy level for generic
+ * volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_get_volume_redundancy_level(
+ defaults_t *defaults,
+ char *diskset,
+ uint16_t *val)
+{
+ char *disksets[2];
+ devconfig_t *volume;
+ int error;
+ int i = 0;
+
+ /* Check both the given and global (NULL) disk sets for the value */
+ disksets[0] = diskset;
+ disksets[1] = NULL;
+ do {
+ /* Get/create singleton volume element for this disk set */
+ error = defaults_get_singleton_component(
+ defaults, disksets[i], TYPE_VOLUME, &volume, FALSE);
+
+ switch (error) {
+ /* volume found for this disk set */
+ case 0:
+ /* Get the redundancy level */
+ if ((error = devconfig_get_volume_redundancy_level(
+ volume, val)) == 0) {
+ /* redundancy level found */
+ return (0);
+ }
+
+ /* FALLTHROUGH */
+
+ /* volume not found for this disk set */
+ case ENOENT:
+ break;
+
+ /* Invalid disk set, or volume couldn't be created */
+ default:
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Stop after the global (NULL) disk set has been searched */
+ } while (disksets[i++] != NULL);
+
+ return (ENOENT);
+}
+
+/*
+ * Set the default number of data paths for generic volume
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default number of data paths
+ * for generic volume
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_set_volume_npaths(
+ defaults_t *defaults,
+ char *diskset,
+ uint16_t val)
+{
+ devconfig_t *volume = NULL;
+ int error = 0;
+
+ /* Get/create singleton volume element for this disk set */
+ if ((error = defaults_get_singleton_component(
+ defaults, diskset, TYPE_VOLUME, &volume, TRUE)) != 0) {
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Set the npaths attribute */
+ return (devconfig_set_volume_npaths(volume, val));
+}
+
+/*
+ * Get the default number of data paths for generic volume
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default number of data paths for generic
+ * volume
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_get_volume_npaths(
+ defaults_t *defaults,
+ char *diskset,
+ uint16_t *val)
+{
+ char *disksets[2];
+ devconfig_t *volume;
+ int error;
+ int i = 0;
+
+ /* Check both the given and global (NULL) disk sets for the value */
+ disksets[0] = diskset;
+ disksets[1] = NULL;
+ do {
+ /* Get/create singleton volume element for this disk set */
+ error = defaults_get_singleton_component(
+ defaults, disksets[i], TYPE_VOLUME, &volume, FALSE);
+
+ switch (error) {
+ /* volume found for this disk set */
+ case 0:
+ /* Get the npaths attribute */
+ if ((error = devconfig_get_volume_npaths(
+ volume, val)) == 0) {
+ /* npaths attribute found */
+ return (0);
+ }
+
+ /* FALLTHROUGH */
+
+ /* volume not found for this disk set */
+ case ENOENT:
+ break;
+
+ /* Invalid disk set, or volume couldn't be created */
+ default:
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Stop after the global (NULL) disk set has been searched */
+ } while (disksets[i++] != NULL);
+
+ return (ENOENT);
+}
+
+/*
+ * Set the default HSP creation flag for generic volume
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default HSP creation flag for
+ * generic volume
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_set_volume_usehsp(
+ defaults_t *defaults,
+ char *diskset,
+ boolean_t val)
+{
+ devconfig_t *volume = NULL;
+ int error = 0;
+
+ /* Get/create singleton volume element for this disk set */
+ if ((error = defaults_get_singleton_component(
+ defaults, diskset, TYPE_VOLUME, &volume, TRUE)) != 0) {
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Set the usehsp attribute */
+ return (devconfig_set_volume_usehsp(volume, val));
+}
+
+/*
+ * Get the default HSP creation flag for generic volume
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default HSP creation flag for generic
+ * volume
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+defaults_get_volume_usehsp(
+ defaults_t *defaults,
+ char *diskset,
+ boolean_t *val)
+{
+ char *disksets[2];
+ devconfig_t *volume;
+ int error;
+ int i = 0;
+
+ /* Check both the given and global (NULL) disk sets for the value */
+ disksets[0] = diskset;
+ disksets[1] = NULL;
+ do {
+ /* Get/create singleton volume element for this disk set */
+ error = defaults_get_singleton_component(
+ defaults, disksets[i], TYPE_VOLUME, &volume, FALSE);
+
+ switch (error) {
+ /* volume found for this disk set */
+ case 0:
+ /* Get the usehsp attribute */
+ if ((error = devconfig_get_volume_usehsp(
+ volume, val)) == 0) {
+ /* usehsp attribute found */
+ return (0);
+ }
+
+ /* FALLTHROUGH */
+
+ /* volume not found for this disk set */
+ case ENOENT:
+ break;
+
+ /* Invalid disk set, or volume couldn't be created */
+ default:
+ /* volume_set_error already called */
+ return (error);
+ }
+
+ /* Stop after the global (NULL) disk set has been searched */
+ } while (disksets[i++] != NULL);
+
+ return (ENOENT);
+}
diff --git a/usr/src/cmd/lvm/metassist/common/volume_defaults.h b/usr/src/cmd/lvm/metassist/common/volume_defaults.h
new file mode 100644
index 0000000000..3d9cacacaa
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_defaults.h
@@ -0,0 +1,853 @@
+/*
+ * 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.
+ */
+
+#ifndef _VOLUME_DEFAULTS_H
+#define _VOLUME_DEFAULTS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "volume_devconfig.h"
+
+#define DEFAULT_MIRROR_NSUBS 2
+#define DEFAULT_MIRROR_READ MIRROR_READ_ROUNDROBIN
+#define DEFAULT_MIRROR_WRITE MIRROR_WRITE_PARALLEL
+#define DEFAULT_MIRROR_PASS 1
+#define DEFAULT_STRIPE_INTERLACE 1024 * 64
+#define DEFAULT_STRIPE_MINCOMP 3
+#define DEFAULT_STRIPE_MAXCOMP 10
+#define DEFAULT_VOLUME_REDUND_LEVEL 0
+#define DEFAULT_VOLUME_NPATHS 1
+
+/* For consistency, these should all have the same value */
+#define DEFAULT_MIRROR_USEHSP FALSE
+#define DEFAULT_CONCAT_USEHSP FALSE
+#define DEFAULT_STRIPE_USEHSP FALSE
+#define DEFAULT_VOLUME_USEHSP FALSE
+
+/*
+ * default_t - struct to hold layout defaults
+ */
+typedef struct defaults {
+ /*
+ * List of devconfig_t, each of which represents disk set-
+ * specific defaults. Each disk set has a name, except for
+ * the global set, whose name is NULL.
+ */
+ dlist_t *disksets;
+} defaults_t;
+
+/*
+ * Constructor: Create a defaults_t struct populated with default
+ * values. This defaults_t must be freed.
+ *
+ * @param defaults
+ * RETURN: a pointer to a new defaults_t
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int new_defaults(defaults_t **defaults);
+
+/*
+ * Free memory (recursively) allocated to a defaults_t struct
+ *
+ * @param arg
+ * pointer to the defaults_t struct to free
+ */
+extern void free_defaults(void *arg);
+
+/*
+ * Set list of diskset specific defaults
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param disksets
+ * a dlist_t representing the defaults for specific
+ * named disk sets
+ */
+extern void defaults_set_disksets(defaults_t *defaults, dlist_t *disksets);
+/*
+ * Get list of diskset specific defaults
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @return a dlist_t representing the defaults for specific
+ * named disk sets
+ */
+extern dlist_t *defaults_get_disksets(defaults_t *defaults);
+
+/*
+ * Get a disk set with the given name from the given defaults_t
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param name
+ * the name of the disk set whose defaults to retrieve,
+ * or NULL to get the defaults for all disk sets
+ *
+ * @param diskset
+ * RETURN: defaults for the given named disk set, or
+ * defaults for all disk sets if name is NULL
+ *
+ * @return ENOENT
+ * if the named disk set does not exist
+ *
+ * @return 0
+ * if the named disk set exists
+ */
+
+extern int defaults_get_diskset_by_name(
+ defaults_t *defaults, char *name, devconfig_t **diskset);
+
+/*
+ * Set name of the the default HSP to use
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param name
+ * the name of the default HSP to use
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_set_hsp_name(
+ defaults_t *defaults, char *diskset, char *name);
+/*
+ * Get the name of the default HSP to use
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param name
+ * RETURN: the name of the default HSP to use
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_get_hsp_name(
+ defaults_t *defaults, char *diskset, char **name);
+
+/*
+ * Set the default number of submirrors for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default number of submirrors
+ * for mirrored volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_set_mirror_nsubs(
+ defaults_t *defaults, char *diskset, uint16_t val);
+/*
+ * Get the default number of submirrors for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default number of submirrors for mirrored
+ * volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_get_mirror_nsubs(
+ defaults_t *defaults, char *diskset, uint16_t *val);
+
+/*
+ * Set the default read strategy for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default read strategy for
+ * mirrored volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_set_mirror_read(
+ defaults_t *defaults, char *diskset, mirror_read_strategy_t val);
+/*
+ * Get the default read strategy for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default read strategy for mirrored volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_get_mirror_read(
+ defaults_t *defaults, char *diskset, mirror_read_strategy_t *val);
+
+/*
+ * Set the default write strategy for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default write strategy for
+ * mirrored volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_set_mirror_write(
+ defaults_t *defaults, char *diskset, mirror_write_strategy_t val);
+/*
+ * Get the default write strategy for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default write strategy for mirrored
+ * volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_get_mirror_write(
+ defaults_t *defaults, char *diskset, mirror_write_strategy_t *val);
+
+/*
+ * Set the default resync pass for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default resync pass for
+ * mirrored volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_set_mirror_pass(
+ defaults_t *defaults, char *diskset, uint16_t val);
+/*
+ * Get the default resync pass for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default resync pass for mirrored volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_get_mirror_pass(
+ defaults_t *defaults, char *diskset, uint16_t *val);
+
+/*
+ * Set the default HSP creation flag for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default HSP creation flag for
+ * mirrored volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_set_mirror_usehsp(
+ defaults_t *defaults, char *diskset, boolean_t val);
+/*
+ * Get the default HSP creation flag for mirrored volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default HSP creation flag for mirrored
+ * volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_get_mirror_usehsp(
+ defaults_t *defaults, char *diskset, boolean_t *val);
+
+/*
+ * Set the default HSP creation flag for concatenated volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default HSP creation flag for
+ * concatenated volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_set_concat_usehsp(
+ defaults_t *defaults, char *diskset, boolean_t val);
+/*
+ * Get the default HSP creation flag for concatenated volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default HSP creation flag for concatenated
+ * volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_get_concat_usehsp(
+ defaults_t *defaults, char *diskset, boolean_t *val);
+
+/*
+ * Set the default minimum number of components for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default minimum number of
+ * components for striped volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_set_stripe_mincomp(
+ defaults_t *defaults, char *diskset, uint16_t val);
+/*
+ * Get the default minimum number of components for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default minimum number of components for
+ * striped volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_get_stripe_mincomp(
+ defaults_t *defaults, char *diskset, uint16_t *val);
+
+/*
+ * Set the default maximum number of components for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default maximum number of
+ * components for striped volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_set_stripe_maxcomp(
+ defaults_t *defaults, char *diskset, uint16_t val);
+/*
+ * Get the default maximum number of components for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default maximum number of components for
+ * striped volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_get_stripe_maxcomp(
+ defaults_t *defaults, char *diskset, uint16_t *val);
+
+/*
+ * Set the default interlace for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default interlace for striped
+ * volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_set_stripe_interlace(
+ defaults_t *defaults, char *diskset, uint64_t val);
+/*
+ * Get the default interlace for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default interlace for striped volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_get_stripe_interlace(
+ defaults_t *defaults, char *diskset, uint64_t *val);
+
+/*
+ * Set the default HSP creation flag for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default HSP creation flag for
+ * striped volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_set_stripe_usehsp(
+ defaults_t *defaults, char *diskset, boolean_t val);
+/*
+ * Get the default HSP creation flag for striped volumes
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default HSP creation flag for striped
+ * volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_get_stripe_usehsp(
+ defaults_t *defaults, char *diskset, boolean_t *val);
+
+/*
+ * Set the default redundancy level for generic volumes.
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * If 0, a stripe will be created by default. If > 0, a
+ * mirror with this number of submirrors will be created
+ * by default.
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_set_volume_redundancy_level(
+ defaults_t *defaults, char *diskset, uint16_t val);
+/*
+ * Get the default redundancy level for generic volumes.
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default redundancy level for generic
+ * volumes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_get_volume_redundancy_level(
+ defaults_t *defaults, char *diskset, uint16_t *val);
+
+/*
+ * Set the default number of data paths for generic volume
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default number of data paths
+ * for generic volume
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_set_volume_npaths(
+ defaults_t *defaults, char *diskset, uint16_t val);
+/*
+ * Get the default number of data paths for generic volume
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default number of data paths for generic
+ * volume
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_get_volume_npaths(
+ defaults_t *defaults, char *diskset, uint16_t *val);
+
+/*
+ * Set the default HSP creation flag for generic volume
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * the value to set as the default HSP creation flag for
+ * generic volume
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_set_volume_usehsp(
+ defaults_t *defaults, char *diskset, boolean_t val);
+/*
+ * Get the default HSP creation flag for generic volume
+ *
+ * @param defaults
+ * a defaults_t hierarchy representing default settings
+ * for all disk sets and specific disk sets
+ *
+ * @param diskset
+ * the name of the disk set to which to apply this
+ * default setting, or NULL to apply default
+ * setting to all disk sets
+ *
+ * @param val
+ * RETURN: the default HSP creation flag for generic
+ * volume
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int defaults_get_volume_usehsp(
+ defaults_t *defaults, char *diskset, boolean_t *val);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VOLUME_DEFAULTS_H */
diff --git a/usr/src/cmd/lvm/metassist/common/volume_devconfig.c b/usr/src/cmd/lvm/metassist/common/volume_devconfig.c
new file mode 100644
index 0000000000..1146b89466
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_devconfig.c
@@ -0,0 +1,1691 @@
+/*
+ * 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 "%Z%%M% %I% %E% 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 */
diff --git a/usr/src/cmd/lvm/metassist/common/volume_devconfig.h b/usr/src/cmd/lvm/metassist/common/volume_devconfig.h
new file mode 100644
index 0000000000..3898554591
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_devconfig.h
@@ -0,0 +1,998 @@
+/*
+ * 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.
+ */
+
+#ifndef _VOLUME_DEVCONFIG_H
+#define _VOLUME_DEVCONFIG_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libnvpair.h>
+#include "volume_dlist.h"
+#include <sys/lvm/md_mdiox.h>
+
+/*
+ * String constants for XML element/attribute names.
+ */
+#define ELEMENT_AVAILABLE "available"
+#define ELEMENT_COMMENT "comment"
+#define ELEMENT_CONCAT "concat"
+#define ELEMENT_DISK "disk"
+#define ELEMENT_DISKSET "diskset"
+#define ELEMENT_HSP "hsp"
+#define ELEMENT_L10N "localization"
+#define ELEMENT_MESSAGE "message"
+#define ELEMENT_MIRROR "mirror"
+#define ELEMENT_PARAM "param"
+#define ELEMENT_SLICE "slice"
+#define ELEMENT_STRIPE "stripe"
+#define ELEMENT_TEXT "text"
+#define ELEMENT_UNAVAILABLE "unavailable"
+#define ELEMENT_VARIABLE "variable"
+#define ELEMENT_VOLUME "volume"
+#define ELEMENT_VOLUMECONFIG "volume-config"
+#define ELEMENT_VOLUMEDEFAULTS "volume-defaults"
+#define ELEMENT_VOLUMEREQUEST "volume-request"
+
+#define ATTR_LANG "xml:lang"
+#define ATTR_MESSAGEID "msgid"
+#define ATTR_MIRROR_NSUBMIRRORS "nsubmirrors"
+#define ATTR_MIRROR_PASSNUM "passnum"
+#define ATTR_MIRROR_READ "read"
+#define ATTR_MIRROR_WRITE "write"
+#define ATTR_NAME "name"
+#define ATTR_SELECT "select"
+#define ATTR_SIZEINBLOCKS "sizeinblocks"
+#define ATTR_SIZEINBYTES "size"
+#define ATTR_SLICE_INDEX "index"
+#define ATTR_SLICE_STARTSECTOR "startsector"
+#define ATTR_STRIPE_INTERLACE "interlace"
+#define ATTR_STRIPE_MAXCOMP "maxcomp"
+#define ATTR_STRIPE_MINCOMP "mincomp"
+#define ATTR_TYPE "type"
+#define ATTR_VOLUME_CREATE "create"
+#define ATTR_VOLUME_DATAPATHS "datapaths"
+#define ATTR_VOLUME_FAULTRECOVERY "faultrecovery"
+#define ATTR_VOLUME_REDUNDANCY "redundancy"
+#define ATTR_VOLUME_USEHSP "usehsp"
+
+#define NAME_L10N_MESSAGE_FILE "msgfile"
+#define NAME_LANG "lang"
+
+/*
+ * Limits for attributes
+ */
+#define MIN_NSTRIPE_COMP 1
+#define MIN_SIZE 0
+#define MIN_SIZE_IN_BLOCKS 0
+#define MIN_NDATAPATHS 1
+#define MAX_NDATAPATHS 4
+
+/* Attribute requested but not set */
+#define ERR_ATTR_UNSET -10001
+
+/*
+ * Enumeration defining physical or logical device types
+ */
+typedef enum {
+ TYPE_UNKNOWN = 0,
+ TYPE_CONCAT = 1,
+ TYPE_CONTROLLER,
+ TYPE_DISKSET,
+ TYPE_DRIVE,
+ TYPE_EXTENT,
+ TYPE_HOST,
+ TYPE_HSP,
+ TYPE_MIRROR,
+ TYPE_RAID5,
+ TYPE_SLICE,
+ TYPE_SOFTPART,
+ TYPE_STRIPE,
+ TYPE_TRANS,
+ TYPE_VOLUME
+} component_type_t;
+
+/*
+ * enumerated constants for SVM Mirror read strategies
+ */
+typedef enum {
+ MIRROR_READ_ROUNDROBIN = 0,
+ MIRROR_READ_GEOMETRIC,
+ MIRROR_READ_FIRST
+} mirror_read_strategy_t;
+
+/*
+ * enumerated constants for SVM Mirror write strategies
+ */
+typedef enum {
+ MIRROR_WRITE_PARALLEL = 0,
+ MIRROR_WRITE_SERIAL
+} mirror_write_strategy_t;
+
+/*
+ * devconfig_t - struct to hold a device configuration hierarchy
+ */
+typedef struct devconfig {
+
+ /* Attributes of this device */
+ nvlist_t *attributes;
+
+ /*
+ * Available devices for use in construction of this device
+ * and its subcomponents
+ */
+ char **available;
+
+ /*
+ * Unavailable devices for use in construction of this device
+ * and its subcomponents
+ */
+ char **unavailable;
+
+ /*
+ * Subcomponents (devconfig_t) of this device
+ */
+ dlist_t *components;
+} devconfig_t;
+
+/*
+ * Function prototypes
+ */
+
+/*
+ * 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.
+ */
+extern int new_devconfig(devconfig_t **devconfig, component_type_t type);
+
+/*
+ * Free memory (recursively) allocated to a devconfig_t struct
+ *
+ * @param arg
+ * pointer to the devconfig_t to be freed
+ */
+extern void free_devconfig(void *arg);
+
+/*
+ * 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
+ */
+extern boolean_t devconfig_isA(devconfig_t *device, component_type_t type);
+
+/*
+ * 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
+ */
+extern int devconfig_get_component(devconfig_t *device,
+ component_type_t type, devconfig_t **component, boolean_t create);
+
+/*
+ * 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
+ */
+extern void devconfig_set_available(devconfig_t *device, char **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
+ */
+extern char ** devconfig_get_available(devconfig_t *device);
+
+/*
+ * 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
+ */
+extern void devconfig_set_unavailable(devconfig_t *device, char **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
+ */
+extern char ** devconfig_get_unavailable(devconfig_t *device);
+
+/*
+ * 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
+ */
+extern void devconfig_set_components(devconfig_t *device, dlist_t *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
+ */
+extern dlist_t *devconfig_get_components(devconfig_t *device);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_name(devconfig_t *device, char *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.
+ */
+extern int devconfig_set_diskset_name(devconfig_t *diskset, char *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.
+ */
+extern int devconfig_set_hsp_name(devconfig_t *hsp, char *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.
+ */
+extern int devconfig_set_volume_name(devconfig_t *volume, char *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.
+ */
+extern int devconfig_get_name(devconfig_t *device, char **name);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_type(devconfig_t *device, component_type_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.
+ */
+extern int devconfig_get_type(devconfig_t *device, component_type_t *type);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_size(devconfig_t *device, uint64_t 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.
+ */
+extern int devconfig_get_size(devconfig_t *device, uint64_t *size_in_bytes);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_size_in_blocks(
+ devconfig_t *device, uint64_t 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.
+ */
+extern int devconfig_get_size_in_blocks(
+ devconfig_t *device, uint64_t *size_in_blocks);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_slice_index(devconfig_t *slice, uint16_t 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.
+ */
+extern int devconfig_get_slice_index(devconfig_t *slice, uint16_t *index);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_slice_start_block(
+ devconfig_t *slice, uint64_t 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.
+ */
+extern int devconfig_get_slice_start_block(
+ devconfig_t *slice, uint64_t *start_block);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_mirror_nsubs(devconfig_t *mirror, uint16_t 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.
+ */
+extern int devconfig_get_mirror_nsubs(devconfig_t *mirror, uint16_t *nsubs);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_mirror_read(
+ devconfig_t *mirror, mirror_read_strategy_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.
+ */
+extern int devconfig_get_mirror_read(
+ devconfig_t *mirror, mirror_read_strategy_t *read);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_mirror_write(
+ devconfig_t *mirror, mirror_write_strategy_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.
+ */
+extern int devconfig_get_mirror_write(
+ devconfig_t *mirror, mirror_write_strategy_t *write);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_mirror_pass(devconfig_t *mirror, uint16_t 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.
+ */
+extern int devconfig_get_mirror_pass(devconfig_t *mirror, uint16_t *pass);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_stripe_mincomp(devconfig_t *stripe, uint16_t 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.
+ */
+extern int devconfig_get_stripe_mincomp(devconfig_t *stripe, uint16_t *mincomp);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_stripe_maxcomp(devconfig_t *stripe, uint16_t 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.
+ */
+extern int devconfig_get_stripe_maxcomp(devconfig_t *stripe, uint16_t *maxcomp);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_stripe_interlace(
+ devconfig_t *stripe, uint64_t 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.
+ */
+extern int devconfig_get_stripe_interlace(
+ devconfig_t *stripe, uint64_t *interlace);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_volume_redundancy_level(
+ devconfig_t *volume, uint16_t 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.
+ */
+extern int devconfig_get_volume_redundancy_level(
+ devconfig_t *volume, uint16_t *rlevel);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_volume_npaths(devconfig_t *volume, uint16_t 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.
+ */
+extern int devconfig_get_volume_npaths(devconfig_t *volume, uint16_t *npaths);
+
+/*
+ * 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.
+ */
+extern int devconfig_set_volume_usehsp(devconfig_t *volume, boolean_t 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.
+ */
+extern int devconfig_get_volume_usehsp(devconfig_t *volume, boolean_t *usehsp);
+
+/*
+ * Get the string representation of the volume's type
+ *
+ * @param type
+ * a valid component_type_t
+ *
+ * @return an internationalized string representing the given
+ * type
+ */
+extern char *devconfig_type_to_str(component_type_t type);
+
+/*
+ * 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
+ */
+extern char *devconfig_read_strategy_to_str(mirror_read_strategy_t read);
+
+/*
+ * 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
+ */
+extern char *devconfig_write_strategy_to_str(mirror_write_strategy_t write);
+
+#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
+ */
+extern void devconfig_dump(devconfig_t *device, char *prefix);
+#endif /* DEBUG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VOLUME_DEVCONFIG_H */
diff --git a/usr/src/cmd/lvm/metassist/common/volume_dlist.c b/usr/src/cmd/lvm/metassist/common/volume_dlist.c
new file mode 100644
index 0000000000..f836c2bc97
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_dlist.c
@@ -0,0 +1,512 @@
+/*
+ * 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 "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "volume_dlist.h"
+
+#define _volume_dlist_C
+
+/*
+ * public constant definitions
+ */
+const boolean_t ASCENDING = TRUE; /* list ordering */
+const boolean_t DESCENDING = FALSE;
+const boolean_t AT_TAIL = TRUE; /* list insertion location */
+const boolean_t AT_HEAD = FALSE;
+
+/*
+ * determine if the list contains an item
+ * that points at the object
+ */
+boolean_t
+dlist_contains(
+ dlist_t *list,
+ void *obj,
+ int (compare)(void *, void *))
+{
+ return (dlist_find(list, obj, compare) != NULL);
+}
+
+/*
+ * locate the item in the list that points at the object
+ */
+dlist_t *
+dlist_find(
+ dlist_t *list,
+ void *obj,
+ int (compare)(void *, void *))
+{
+ dlist_t *iter;
+
+ for (iter = list; iter != NULL; iter = iter->next) {
+ if ((compare)(obj, iter->obj) == 0) {
+ return (iter);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * insert item into list in the desired order (ascending or descending)
+ * using the comparison function provided.
+ *
+ * In the for loop, iter marks current position in the list
+ * and item is the item to be inserted.
+ *
+ * Iterate the list and find the correct place to insert temp.
+ *
+ * if (ascending && compare(item, iter) <= 0 ||
+ * (descending && compare(item, iter) >= 0)
+ * item goes before iter
+ * else
+ * item goes after iter
+ */
+dlist_t *
+dlist_insert_ordered(
+ dlist_t *item,
+ dlist_t *list,
+ boolean_t ascending,
+ int (compare)(void *, void *))
+{
+ dlist_t *head = NULL;
+ dlist_t *iter = NULL;
+ int result = 0;
+
+ if (list == NULL) {
+
+ head = item;
+
+ } else {
+
+ head = list;
+
+ for (iter = list; iter != NULL; iter = iter->next) {
+
+ result = (compare)(item->obj, iter->obj);
+
+ if ((ascending && (result <= 0)) ||
+ (!ascending && (result >= 0))) {
+
+ if (iter == head) {
+ head = item;
+ item->next = iter;
+ iter->prev = item;
+ } else {
+ item->prev = iter->prev;
+ item->prev->next = item;
+ iter->prev = item;
+ item->next = iter;
+ }
+ break;
+ }
+
+ if (iter->next == NULL) {
+ /* end of list, so item becomes the new end */
+ iter->next = item;
+ item->prev = iter;
+ break;
+ }
+ }
+ }
+
+ return (head);
+}
+
+/*
+ * Remove the first node pointing to same content as item from list,
+ * clear it's next and prev pointers, return new list head.
+ *
+ * The caller is responsible for freeing the removed item if it is no
+ * longer needed.
+ *
+ * The comparison function should be of the form:
+ *
+ * int compare(void *obj1, void* obj2);
+ *
+ * When called, obj1 will be the object passed into
+ * dlist_remove_equivalent_item and obj2 will be an object pointed to
+ * by an item in the list.
+ *
+ * The function should return 0 if the two objects are equivalent The
+ * function should return nonzero otherwise
+ *
+ * @param list
+ * the list containing the item to remove
+ *
+ * @param obj
+ * the object with which to compare each item
+ *
+ * @param compare
+ * the comparison function, passed obj and the obj member
+ * of each item, to return 0 if item should be removed
+ *
+ * @param removed
+ * RETURN: the removed item, or NULL if none was found
+ *
+ * @return the first element of the resulting list
+ */
+dlist_t *
+dlist_remove_equivalent_item(
+ dlist_t *list,
+ void *obj,
+ int (compare)(void *, void *),
+ dlist_t **removed)
+{
+ dlist_t *item;
+
+ *removed = NULL;
+
+ if (list == NULL) {
+ return (list);
+ }
+
+ item = dlist_find(list, obj, compare);
+ if (item == NULL) {
+ return (list);
+ }
+
+ *removed = item;
+
+ return (dlist_remove(item));
+}
+
+/*
+ * Remove an item from its list. Return the resulting list.
+ *
+ * @param item
+ * the item to remove, with prev and next pointers
+ * set to NULL
+ *
+ * @return the first element of the resulting list
+ */
+dlist_t *
+dlist_remove(
+ dlist_t *item)
+{
+ dlist_t *head = NULL;
+
+ if (item != NULL) {
+ if (item->next != NULL) {
+ item->next->prev = item->prev;
+ head = item->next;
+ }
+
+ if (item->prev != NULL) {
+ item->prev->next = item->next;
+ head = item->prev;
+ }
+
+ item->next = NULL;
+ item->prev = NULL;
+
+ /* Find head of list */
+ for (; head != NULL && head->prev != NULL; head = head->prev);
+ }
+
+ return (head);
+}
+
+/*
+ * append item to list, either beginning or end
+ */
+dlist_t *
+dlist_append(
+ dlist_t *item,
+ dlist_t *list,
+ boolean_t attail)
+{
+ dlist_t *head = list;
+
+ if (list == NULL) {
+
+ head = item;
+
+ } else if (item == NULL) {
+
+ head = list;
+
+ } else if (attail) {
+
+ dlist_t *iter;
+
+ /* append to end */
+ for (iter = head; iter->next != NULL; iter = iter->next);
+
+ iter->next = item;
+ item->prev = iter;
+
+ } else {
+ /* insert at begining */
+ item->next = head;
+ head->prev = item;
+ head = item;
+ }
+
+ return (head);
+}
+
+/*
+ * Create a dlist_t element for the given object and append to list.
+ *
+ * @param object
+ * the obj member of the dlist_t element to be created
+ *
+ * @param list
+ * the list to which to append the new dlist_t element
+ *
+ * @param attail
+ * whether to append at the beginning (AT_HEAD) or end
+ * (AT_TAIL) of the list
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOMEM
+ * if a dlist_t could not be allocated
+ */
+int
+dlist_append_object(
+ void *object,
+ dlist_t **list,
+ boolean_t attail)
+{
+ dlist_t *item = dlist_new_item(object);
+
+ if (item == NULL) {
+ return (ENOMEM);
+ }
+
+ *list = dlist_append(item, *list, attail);
+
+ return (0);
+}
+
+/*
+ * Appends list2 to the end of list1.
+ *
+ * Returns the resulting list.
+ */
+dlist_t *
+dlist_append_list(
+ dlist_t *list1,
+ dlist_t *list2)
+{
+ dlist_t *iter;
+
+ if (list1 == NULL) {
+ return (list2);
+ }
+
+ if (list2 != NULL) {
+ /* Find last element of list1 */
+ for (iter = list1; iter->next != NULL; iter = iter->next);
+
+ iter->next = list2;
+ list2->prev = iter;
+ }
+
+ return (list1);
+}
+
+/*
+ * compute number of items in list
+ */
+int
+dlist_length(
+ dlist_t *list)
+{
+ dlist_t *iter;
+ int length = 0;
+
+ for (iter = list; iter != NULL; iter = iter->next)
+ ++length;
+
+ return (length);
+}
+
+/*
+ * Allocate a new dlist_t structure and initialize the opaque object
+ * pointer the input object.
+ *
+ * @return A new dlist_t structure for the given object, or NULL
+ * if the memory could not be allocated.
+ */
+dlist_t *
+dlist_new_item(
+ void *obj)
+{
+ dlist_t *item = (dlist_t *)calloc(1, sizeof (dlist_t));
+
+ if (item != NULL) {
+ item->obj = obj;
+ }
+
+ return (item);
+}
+
+/*
+ * Traverse the list pointed to by head and free each
+ * list node. If freefunc is non-NULL, call freefunc
+ * for each node's object.
+ */
+void
+dlist_free_items(
+ dlist_t *head,
+ void (freefunc(void *)))
+{
+ while (head != NULL) {
+ dlist_t *item = head;
+ head = head->next;
+
+ if (freefunc != NULL) {
+ freefunc(item->obj);
+ }
+
+ free((void *) item);
+ }
+}
+
+/*
+ * Order the given list such that the number of similar elements
+ * adjacent to each other are minimized.
+ *
+ * The algorithm is:
+ *
+ * 1. Sort similar items into categories. Two elements are considered
+ * similar if the given compare function returns 0.
+ *
+ * 2. Create a new list by iterating through each category and
+ * selecting an element from the category with the most elements.
+ * Avoid choosing an element from the last category chosen.
+ *
+ * @param list
+ * the list to order
+ *
+ * @param compare
+ * the comparison function, passed the obj members
+ * of two items, to return 0 if the items can be
+ * considered similar
+ *
+ * @return the first element of the resulting list
+ */
+dlist_t *
+dlist_separate_similar_elements(
+ dlist_t *list,
+ int(compare)(void *, void *))
+{
+ dlist_t **categories = NULL;
+ dlist_t *item;
+ int ncategories = 0;
+ int max_elements;
+ int lastcat;
+
+ /*
+ * First, sort like items into categories, according to
+ * the passed-in compare function
+ */
+ for (item = list; item != NULL; ) {
+ dlist_t *removed;
+
+ /* Remove this item from the list */
+ list = dlist_remove(item);
+
+ /* Create new category */
+ categories = (dlist_t **)realloc(
+ categories, ++ncategories * sizeof (dlist_t *));
+ categories[ncategories - 1] = item;
+
+ /* Add like items to same category */
+ list = dlist_remove_equivalent_item(
+ list, item->obj, compare, &removed);
+ while (removed != NULL) {
+ /* Add removed item to category */
+ dlist_append(removed, item, AT_TAIL);
+ list = dlist_remove_equivalent_item(
+ list, item->obj, compare, &removed);
+ }
+
+ item = list;
+ }
+
+ /*
+ * Next, create a new list, minimizing the number of adjacent
+ * elements from the same category
+ */
+ list = NULL;
+ lastcat = -1;
+ do {
+ int i;
+ int curcat;
+
+ /*
+ * Find the category with the most elements, other than
+ * the last category chosen
+ */
+ max_elements = 0;
+ for (i = 0; i < ncategories; i++) {
+ int nelements;
+
+ if (i == lastcat) {
+ continue;
+ }
+
+ nelements = dlist_length(categories[i]);
+ if (nelements > max_elements) {
+ max_elements = nelements;
+ curcat = i;
+ }
+ }
+
+ /* If no elements were found, use the last category chosen */
+ if (max_elements == 0 && lastcat >= 0) {
+ max_elements = dlist_length(categories[lastcat]);
+ curcat = lastcat;
+ }
+
+ /* Was a category with elements found? */
+ if (max_elements != 0) {
+ /* Remove first element of chosen category */
+ item = categories[curcat];
+ categories[curcat] = dlist_remove(item);
+
+ /* Add removed element to resulting list */
+ list = dlist_append(item, list, AT_TAIL);
+
+ lastcat = curcat;
+ }
+ } while (max_elements != 0);
+
+ free(categories);
+
+ return (list);
+}
diff --git a/usr/src/cmd/lvm/metassist/common/volume_dlist.h b/usr/src/cmd/lvm/metassist/common/volume_dlist.h
new file mode 100644
index 0000000000..af0c5786cc
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_dlist.h
@@ -0,0 +1,278 @@
+/*
+ * 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.
+ */
+
+#ifndef _VOLUME_DLIST_H
+#define _VOLUME_DLIST_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/*
+ * Structure defining a doubly linked list of arbitrary objects
+ */
+typedef struct dlist {
+
+ struct dlist *next;
+ struct dlist *prev;
+ void *obj;
+
+} dlist_t;
+
+/*
+ * module globals
+ */
+extern const boolean_t ASCENDING;
+extern const boolean_t DESCENDING;
+extern const boolean_t AT_TAIL;
+extern const boolean_t AT_HEAD;
+
+/* from types.h */
+#ifndef TRUE
+#define TRUE B_TRUE
+#endif
+
+#ifndef FALSE
+#define FALSE B_FALSE
+#endif
+
+/*
+ * doubly linked list utility methods
+ */
+
+/*
+ * count the number of elements currently in the list
+ */
+extern int dlist_length(dlist_t *list);
+
+/*
+ * Traverse the list pointed to by head and free each
+ * list node. If freefunc is non-NULL, call freefunc
+ * for each node's object.
+ */
+extern void dlist_free_items(dlist_t *list, void (freefunc(void *)));
+
+/*
+ * append item to list. If atend is true, the item is
+ * added at the end of the list, otherwise it is added
+ * at the beginning.
+ *
+ * returns the possibly changed head of the list.
+ */
+extern dlist_t *dlist_append(
+ dlist_t *item,
+ dlist_t *list,
+ boolean_t atend);
+
+/*
+ * Create a dlist_t element for the given object and append to list.
+ *
+ * @param object
+ * the obj member of the dlist_t element to be created
+ *
+ * @param list
+ * the list to which to append the new dlist_t element
+ *
+ * @param attail
+ * whether to append at the beginning (AT_HEAD) or end
+ * (AT_TAIL) of the list
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOMEM
+ * if a dlist_t could not be allocated
+ */
+extern int dlist_append_object(
+ void *object,
+ dlist_t **list,
+ boolean_t attail);
+
+/*
+ * Appends list2 to the end of list1.
+ *
+ * Returns the resulting list.
+ */
+extern dlist_t *dlist_append_list(
+ dlist_t *list1,
+ dlist_t *list2);
+
+/*
+ * Remove the first node pointing to same content as item from list,
+ * clear it's next and prev pointers, return new list head.
+ *
+ * The caller is responsible for freeing the removed item if it is no
+ * longer needed.
+ *
+ * The comparison function should be of the form:
+ *
+ * int compare(void *obj1, void* obj2);
+ *
+ * When called, obj1 will be the object passed into
+ * dlist_remove_equivalent_item and obj2 will be an object pointed to by an
+ * item in the list.
+ *
+ * The function should return 0 if the two objects are equivalent The
+ * function should return nonzero otherwise
+ *
+ * @param list
+ * the list containing the item to remove
+ *
+ * @param obj
+ * the object with which to compare each item
+ *
+ * @param compare
+ * the comparison function, passed obj and the obj member
+ * of each item, to return 0 if item should be removed
+ *
+ * @param removed
+ * RETURN: the removed item, or NULL if none was found
+ *
+ * @return the first element of the resulting list
+ */
+extern dlist_t *dlist_remove_equivalent_item(
+ dlist_t *list,
+ void *obj,
+ int (compare)(void *obj1, void *obj2),
+ dlist_t **removed);
+
+/*
+ * Remove an item from its list. Return the resulting list.
+ *
+ * @param item
+ * the item to remove, with prev and next pointers
+ * set to NULL
+ *
+ * @return the first element of the resulting list
+ */
+dlist_t *
+dlist_remove(
+ dlist_t *item);
+
+/*
+ * allocates memory for a new list item. The list item will
+ * point at obj.
+ *
+ * returns the new list item.
+ */
+extern dlist_t *dlist_new_item(void *obj);
+
+/*
+ * inserts item in the correct position within the list based on
+ * the comparison function. if ascending is true, the list will
+ * be in ascending order, otherwise descending.
+ *
+ * the comparison function should be of the form:
+ *
+ * int compare(void *obj1, void *obj2);
+ *
+ * When called, obj1 will be the object pointed to by the item to
+ * be added to the list, obj2 will be an object pointed to by an
+ * item currently in the list.
+ *
+ * The function should return 0 if the two objects are equivalent
+ * The function should return <0 if obj1 comes before obj2
+ * The function should return >0 if obj1 comes after obj2
+ *
+ * dlist_insert_ordered returns the possibly changed head
+ * of the list.
+ */
+extern dlist_t *dlist_insert_ordered(
+ dlist_t *item,
+ dlist_t *list,
+ boolean_t ascending,
+ int (compare)(void *obj1, void *obj2));
+
+/*
+ * Locates the item in the list which contains object.
+ *
+ * the comparison function should be of the form:
+ *
+ * int compare(void *obj1, void *obj2);
+ *
+ * When called, obj1 will be the input object, obj2 will be
+ * an object pointed to by an item currently in the list.
+ *
+ * The function should return 0 if the two objects are equivalent
+ * The function should return non-zero otherwise
+ *
+ * dlist_find() returns the found item or NULL if one was not found.
+ */
+extern dlist_t *dlist_find(
+ dlist_t *list,
+ void *obj,
+ int (compare)(void *obj1, void *obj2));
+
+/*
+ * Determines if list has an item which contains object.
+ *
+ * the comparison function should be of the form:
+ *
+ * int compare(void *obj1, void *obj2);
+ *
+ * When called, obj1 will be the input object, obj2 will be
+ * an object pointed to by an item currently in the list.
+ *
+ * The function should return 0 if the two objects are equivalent
+ * The function should return non-zero otherwise
+ *
+ * dlist_contains() returns TRUE if the object is already
+ * in the list or FALSE otherwise.
+ */
+extern boolean_t dlist_contains(
+ dlist_t *list,
+ void *obj,
+ int (compare)(void *obj1, void *obj2));
+
+/*
+ * Order the given list such that the number of similar elements
+ * adjacent to each other are minimized. Two elements are considered
+ * similar if the given compare function returns 0.
+ *
+ * @param list
+ * the list to order
+ *
+ * @param compare
+ * the comparison function, passed the obj members
+ * of two items, to return 0 if the items can be
+ * considered similar
+ *
+ * @return the first element of the resulting list
+ */
+extern dlist_t *
+dlist_separate_similar_elements(
+ dlist_t *list,
+ int(*equals)(void *, void *));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VOLUME_DLIST_H */
diff --git a/usr/src/cmd/lvm/metassist/common/volume_error.c b/usr/src/cmd/lvm/metassist/common/volume_error.c
new file mode 100644
index 0000000000..67c2927ace
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_error.c
@@ -0,0 +1,97 @@
+/*
+ * 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 "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "volume_error.h"
+
+#define VOLUME_ERROR_BUFSIZE 1024
+static char volume_error[VOLUME_ERROR_BUFSIZE];
+
+/*
+ * Retrieve the error string for the given error code.
+ *
+ * @param error
+ * If error is less than zero, it is assumed to be a
+ * custom error code. If error is greater than zero, it
+ * is assumed to be an error defined in errno.h.
+ *
+ * @return the error string set by volume_set_error()
+ * if error < 0
+ *
+ * @return the error string returned by strerror()
+ * if error > 0
+ */
+char *
+get_error_string(
+ int error)
+{
+ if (error < 0) {
+ return (volume_error);
+ }
+
+ if (error > 0) {
+ return (strerror(error));
+ }
+
+ return (NULL);
+}
+
+/*
+ * Set the error string for the most recent error. This message can
+ * be retrieved with get_error_string(error), assuming error is less
+ * than zero.
+ *
+ * @param fmt
+ * printf format string
+ *
+ * @return the number of characters formatted
+ * if successful
+ *
+ * @return negative value
+ * if an error occurred
+ */
+/*PRINTFLIKE1*/
+int
+volume_set_error(
+ char *fmt,
+ ...)
+{
+ int ret = 0;
+
+ va_list ap;
+ va_start(ap, fmt);
+ ret = vsnprintf(volume_error, VOLUME_ERROR_BUFSIZE, fmt, ap);
+ va_end(ap);
+
+ return (ret);
+}
diff --git a/usr/src/cmd/lvm/metassist/common/volume_error.h b/usr/src/cmd/lvm/metassist/common/volume_error.h
new file mode 100644
index 0000000000..30fa08acc6
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_error.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef _VOLUME_ERROR_H
+#define _VOLUME_ERROR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Retrieve the error string for the given error code.
+ *
+ * @param error
+ * If error is less than zero, it is assumed to be a
+ * custom error code. If error is greater than zero, it
+ * is assumed to be an error defined in errno.h.
+ *
+ * @return the error string set by volume_set_error()
+ * if error < 0
+ *
+ * @return the error string returned by strerror()
+ * if error > 0
+ */
+extern char *get_error_string(int error);
+
+/*
+ * Set the error string for the most recent error. This message can
+ * be retrieved with get_error_string(error), assuming error is less
+ * than zero.
+ *
+ * @param fmt
+ * printf format string
+ *
+ * @return the number of characters formatted
+ * if successful
+ *
+ * @return negative value
+ * if an error occurred
+ */
+extern int volume_set_error(char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VOLUME_ERROR_H */
diff --git a/usr/src/cmd/lvm/metassist/common/volume_nvpair.c b/usr/src/cmd/lvm/metassist/common/volume_nvpair.c
new file mode 100644
index 0000000000..57592d8880
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_nvpair.c
@@ -0,0 +1,726 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libintl.h>
+#include <string.h>
+#include "volume_nvpair.h"
+#include "volume_error.h"
+
+/*
+ * ******************************************************************
+ *
+ * Function prototypes
+ *
+ * ******************************************************************
+ */
+
+static nvpair_t *nvlist_walk_nvpair(nvlist_t *nvl,
+ const char *name, data_type_t type, nvpair_t *nvp);
+
+/*
+ * ******************************************************************
+ *
+ * External functions
+ *
+ * ******************************************************************
+ */
+
+/*
+ * Get the named uint16 from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * RETURN: the value of the requested uint16
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ */
+int
+get_uint16(
+ nvlist_t *attrs,
+ char *which,
+ uint16_t *val)
+{
+ int error;
+ nvpair_t *match =
+ nvlist_walk_nvpair(attrs, which, DATA_TYPE_UINT16, NULL);
+
+ if (match == NULL) {
+ error = ENOENT;
+ } else {
+ error = nvpair_value_uint16(match, val);
+ }
+
+ return (error);
+}
+
+/*
+ * Set the named uint16 in the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value to set
+ *
+ * @return 0
+ * if successful
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ *
+ * @return ENOMEM
+ * if there is insufficient memory
+ */
+int
+set_uint16(
+ nvlist_t *attrs,
+ char *which,
+ uint16_t val)
+{
+ int error = 0;
+
+ if ((error = nvlist_add_uint16(attrs, which, val)) != 0) {
+ volume_set_error(
+ gettext("nvlist_add_int16(%s) failed: %d\n"), which, error);
+ }
+
+ return (error);
+}
+
+/*
+ * Get the named uint32 from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * RETURN: the value of the requested uint32
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ */
+int
+get_uint32(
+ nvlist_t *attrs,
+ char *which,
+ uint32_t *val)
+{
+ int error;
+ nvpair_t *match =
+ nvlist_walk_nvpair(attrs, which, DATA_TYPE_UINT32, NULL);
+
+ if (match == NULL) {
+ error = ENOENT;
+ } else {
+ error = nvpair_value_uint32(match, val);
+ }
+
+ return (error);
+}
+
+/*
+ * Set the named uint32 in the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value to set
+ *
+ * @return 0
+ * if successful
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ *
+ * @return ENOMEM
+ * if there is insufficient memory
+ */
+int
+set_uint32(
+ nvlist_t *attrs,
+ char *which,
+ uint32_t val)
+{
+ int error = 0;
+
+ if ((error = nvlist_add_uint32(attrs, which, val)) != 0) {
+ volume_set_error(
+ gettext("nvlist_add_int32(%s) failed: %d\n"), which, error);
+ }
+
+ return (error);
+}
+
+/*
+ * Get the named uint64 from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * RETURN: the value of the requested uint64
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ */
+int
+get_uint64(
+ nvlist_t *attrs,
+ char *which,
+ uint64_t *val)
+{
+ int error;
+ nvpair_t *match =
+ nvlist_walk_nvpair(attrs, which, DATA_TYPE_UINT64, NULL);
+
+ if (match == NULL) {
+ error = ENOENT;
+ } else {
+ error = nvpair_value_uint64(match, val);
+ }
+
+ return (error);
+}
+
+/*
+ * Set the named uint64 in the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value to set
+ *
+ * @return 0
+ * if successful
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ *
+ * @return ENOMEM
+ * if there is insufficient memory
+ */
+int
+set_uint64(
+ nvlist_t *attrs,
+ char *which,
+ uint64_t val)
+{
+ int error = 0;
+
+ if ((error = nvlist_add_uint64(attrs, which, val)) != 0) {
+ volume_set_error(
+ gettext("nvlist_add_int64(%s) failed: %d\n"), which, error);
+ }
+
+ return (error);
+}
+
+/*
+ * Set the named boolean in the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value to set
+ *
+ * @return 0
+ * if successful
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ *
+ * @return ENOMEM
+ * if there is insufficient memory
+ */
+int
+set_boolean(
+ nvlist_t *attrs,
+ char *which,
+ boolean_t val)
+{
+ /*
+ * Use set_uint16 to distinguish "attr = B_FALSE" from
+ * "attribute unset".
+ */
+ return (set_uint16(attrs, which, val == B_TRUE ? 1 : 0));
+}
+
+/*
+ * Get the named boolean from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param boolval
+ * RETURN: the value of the requested boolean
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ */
+int
+get_boolean(
+ nvlist_t *attrs,
+ char *which,
+ boolean_t *boolval)
+{
+ int error;
+ uint16_t val;
+
+ /*
+ * Use get_uint16 to distinguish "attr = B_FALSE" from
+ * "attribute unset".
+ */
+ if ((error = get_uint16(attrs, which, &val)) == 0) {
+ *boolval = (val ? B_TRUE : B_FALSE);
+ }
+
+ return (error);
+}
+
+/*
+ * Get the named string from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param str
+ * RETURN: the requested string
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ */
+int
+get_string(
+ nvlist_t *attrs,
+ char *which,
+ char **str)
+{
+ int error;
+ nvpair_t *match =
+ nvlist_walk_nvpair(attrs, which, DATA_TYPE_STRING, NULL);
+
+ if (match == NULL) {
+ error = ENOENT;
+ } else {
+ error = nvpair_value_string(match, str);
+ }
+
+ return (error);
+}
+
+/*
+ * Set the named string in the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value to set
+ *
+ * @return 0
+ * if successful
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ *
+ * @return ENOMEM
+ * if there is insufficient memory
+ */
+int
+set_string(
+ nvlist_t *attrs,
+ char *which,
+ char *val)
+{
+ int error = 0;
+
+ if ((error = nvlist_add_string(attrs, which, val)) != 0) {
+ volume_set_error(
+ gettext("nvlist_add_string(%s) failed: %d\n"), which, error);
+ }
+
+ return (error);
+}
+
+/*
+ * Get the named uint16 array from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * RETURN: the value of the requested uint16 array
+ *
+ * @param nelem
+ * RETURN: the number of elements in the array
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ */
+int
+get_uint16_array(
+ nvlist_t *attrs,
+ char *which,
+ uint16_t **val,
+ uint_t *nelem)
+{
+ int error;
+ nvpair_t *match =
+ nvlist_walk_nvpair(attrs, which, DATA_TYPE_UINT16_ARRAY, NULL);
+
+ if (match == NULL) {
+ error = ENOENT;
+ } else {
+ error = nvpair_value_uint16_array(match, val, nelem);
+ }
+
+ return (error);
+}
+
+/*
+ * Set the named uint16 array from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value of the requested uint16 array
+ *
+ * @param nelem
+ * the number of elements in the array
+ *
+ * @return 0
+ * if successful
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ *
+ * @return ENOMEM
+ * if there is insufficient memory
+ */
+int
+set_uint16_array(
+ nvlist_t *attrs,
+ char *which,
+ uint16_t *val,
+ uint_t nelem)
+{
+ int error = 0;
+
+ if ((error = nvlist_add_uint16_array(
+ attrs, which, val, nelem)) != 0) {
+ volume_set_error(
+ gettext("nvlist_add_uint16_array(%s) failed: %d.\n"),
+ which, error);
+ }
+
+ return (error);
+}
+
+/*
+ * Get the named uint64 array from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * RETURN: the value of the requested uint64 array
+ *
+ * @param nelem
+ * RETURN: the number of elements in the array
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ */
+int
+get_uint64_array(
+ nvlist_t *attrs,
+ char *which,
+ uint64_t **val,
+ uint_t *nelem)
+{
+ int error;
+ nvpair_t *match =
+ nvlist_walk_nvpair(attrs, which, DATA_TYPE_UINT64_ARRAY, NULL);
+
+ if (match == NULL) {
+ error = ENOENT;
+ } else {
+ error = nvpair_value_uint64_array(match, val, nelem);
+ }
+
+ return (error);
+}
+
+/*
+ * Set the named uint64 array from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value of the requested uint64 array
+ *
+ * @param nelem
+ * the number of elements in the array
+ *
+ * @return 0
+ * if successful
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ *
+ * @return ENOMEM
+ * if there is insufficient memory
+ */
+int
+set_uint64_array(
+ nvlist_t *attrs,
+ char *which,
+ uint64_t *val,
+ uint_t nelem)
+{
+ int error = 0;
+
+ if ((error = nvlist_add_uint64_array(
+ attrs, which, val, nelem)) != 0) {
+ volume_set_error(
+ gettext("nvlist_add_uint64_array(%s) failed: %d.\n"),
+ which, error);
+ }
+
+ return (error);
+}
+
+/*
+ * Get the named string array from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * RETURN: the value of the requested string array
+ *
+ * @param nelem
+ * RETURN: the number of elements in the array
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ */
+int
+get_string_array(
+ nvlist_t *attrs,
+ char *which,
+ char ***val,
+ uint_t *nelem)
+{
+ int error;
+ nvpair_t *match =
+ nvlist_walk_nvpair(attrs, which, DATA_TYPE_STRING_ARRAY, NULL);
+
+ if (match == NULL) {
+ error = ENOENT;
+ } else {
+ error = nvpair_value_string_array(match, val, nelem);
+ }
+
+ return (error);
+}
+
+/*
+ * Set the named string array from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value of the requested string array
+ *
+ * @param nelem
+ * the number of elements in the array
+ *
+ * @return 0
+ * if successful
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ *
+ * @return ENOMEM
+ * if there is insufficient memory
+ */
+int
+set_string_array(
+ nvlist_t *attrs,
+ char *which,
+ char **val,
+ uint_t nelem)
+{
+ int error = 0;
+
+ if ((error = nvlist_add_string_array(
+ attrs, which, val, nelem)) != 0) {
+ volume_set_error(
+ gettext("nvlist_add_string_array(%s) failed: %d.\n"),
+ which, error);
+ }
+
+ return (error);
+}
+
+/*
+ * ******************************************************************
+ *
+ * Static functions
+ *
+ * ******************************************************************
+ */
+
+/*
+ * Get a handle to the next nvpair with the specified name and data
+ * type in the list following the given nvpair.
+ *
+ * Some variation of this function will likely appear in the libnvpair
+ * library per 4981923.
+ *
+ * @param nvl
+ * the nvlist_t to search
+ *
+ * @param name
+ * the string key for the pair to find in the list, or
+ * NULL to match any name
+ *
+ * @param type
+ * the data type for the pair to find in the list, or
+ * DATA_TYPE_UNKNOWN to match any type
+ *
+ * @param nvp
+ * the pair to search from in the list, or NULL to search
+ * from the beginning of the list
+ *
+ * @return the next nvpair in the list matching the given
+ * criteria, or NULL if no matching nvpair is found
+ */
+static nvpair_t *
+nvlist_walk_nvpair(
+ nvlist_t *nvl,
+ const char *name,
+ data_type_t type,
+ nvpair_t *nvp)
+{
+ /* For each nvpair in the list following nvp... */
+ while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+
+ /* Does this pair's name match the given name? */
+ if ((name == NULL || strcmp(nvpair_name(nvp), name) == 0) &&
+
+ /* Does this pair's type match the given type? */
+ (type == DATA_TYPE_UNKNOWN || type == nvpair_type(nvp))) {
+ return (nvp);
+ }
+ }
+
+ return (NULL);
+}
diff --git a/usr/src/cmd/lvm/metassist/common/volume_nvpair.h b/usr/src/cmd/lvm/metassist/common/volume_nvpair.h
new file mode 100644
index 0000000000..de9aa134b0
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_nvpair.h
@@ -0,0 +1,467 @@
+/*
+ * 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.
+ */
+
+#ifndef _VOLUME_NVPAIR_H
+#define _VOLUME_NVPAIR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libnvpair.h>
+
+/*
+ * Get the named uint16 from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * RETURN: the value of the requested uint16
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ *
+ * @return ENOTSUP
+ * if an encode/decode method is not supported
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ */
+extern int get_uint16(nvlist_t *attrs, char *which, uint16_t *val);
+
+/*
+ * Set the named uint16 in the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value to set
+ *
+ * @return 0
+ * if successful
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ *
+ * @return ENOMEM
+ * if there is insufficient memory
+ */
+extern int set_uint16(nvlist_t *attrs, char *which, uint16_t val);
+
+/*
+ * Get the named uint32 from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * RETURN: the value of the requested uint32
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ *
+ * @return ENOTSUP
+ * if an encode/decode method is not supported
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ */
+extern int get_uint32(nvlist_t *attrs, char *which, uint32_t *val);
+
+/*
+ * Set the named uint32 in the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value to set
+ *
+ * @return 0
+ * if successful
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ *
+ * @return ENOMEM
+ * if there is insufficient memory
+ */
+extern int set_uint32(nvlist_t *attrs, char *which, uint32_t val);
+
+/*
+ * Get the named uint64 from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * RETURN: the value of the requested uint64
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ *
+ * @return ENOTSUP
+ * if an encode/decode method is not supported
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ */
+extern int get_uint64(nvlist_t *attrs, char *which, uint64_t *val);
+
+/*
+ * Set the named uint64 in the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value to set
+ *
+ * @return 0
+ * if successful
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ *
+ * @return ENOMEM
+ * if there is insufficient memory
+ */
+extern int set_uint64(nvlist_t *attrs, char *which, uint64_t val);
+
+/*
+ * Set the named boolean in the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value to set
+ *
+ * @return 0
+ * if successful
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ *
+ * @return ENOMEM
+ * if there is insufficient memory
+ */
+extern int set_boolean(nvlist_t *attrs, char *which, boolean_t val);
+
+/*
+ * Get the named boolean from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param boolval
+ * RETURN: the value of the requested boolean
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ *
+ * @return ENOTSUP
+ * if an encode/decode method is not supported
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ */
+extern int get_boolean(nvlist_t *attrs, char *which, boolean_t *boolval);
+
+/*
+ * Get the named string from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param str
+ * RETURN: the requested string
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ *
+ * @return ENOTSUP
+ * if an encode/decode method is not supported
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ */
+extern int get_string(nvlist_t *attrs, char *which, char **str);
+
+/*
+ * Set the named string in the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value to set
+ *
+ * @return 0
+ * if successful
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ *
+ * @return ENOMEM
+ * if there is insufficient memory
+ */
+extern int set_string(nvlist_t *attrs, char *which, char *val);
+
+/*
+ * Get the named uint16 array from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * RETURN: the value of the requested uint16 array
+ *
+ * @param nelem
+ * RETURN: the number of elements in the array
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ *
+ * @return ENOTSUP
+ * if an encode/decode method is not supported
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ */
+extern int get_uint16_array(
+ nvlist_t *attrs, char *which, uint16_t **val, uint_t *nelem);
+
+/*
+ * Set the named uint16 array from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value of the requested uint16 array
+ *
+ * @param nelem
+ * the number of elements in the array
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ *
+ * @return ENOTSUP
+ * if an encode/decode method is not supported
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ */
+extern int set_uint16_array(
+ nvlist_t *attrs, char *which, uint16_t *val, uint_t nelem);
+
+/*
+ * Get the named uint64 array from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * RETURN: the value of the requested uint64 array
+ *
+ * @param nelem
+ * RETURN: the number of elements in the array
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ *
+ * @return ENOTSUP
+ * if an encode/decode method is not supported
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ */
+extern int get_uint64_array(
+ nvlist_t *attrs, char *which, uint64_t **val, uint_t *nelem);
+
+/*
+ * Set the named uint64 array from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value of the requested uint64 array
+ *
+ * @param nelem
+ * the number of elements in the array
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ *
+ * @return ENOTSUP
+ * if an encode/decode method is not supported
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ */
+extern int set_uint64_array(
+ nvlist_t *attrs, char *which, uint64_t *val, uint_t nelem);
+
+/*
+ * Get the named string array from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * RETURN: the value of the requested string array
+ *
+ * @param nelem
+ * RETURN: the number of elements in the array
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ *
+ * @return ENOTSUP
+ * if an encode/decode method is not supported
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ */
+extern int get_string_array(
+ nvlist_t *attrs, char *which, char ***val, uint_t *nelem);
+
+/*
+ * Set the named string array from the given nvlist_t.
+ *
+ * @param attrs
+ * the nvlist_t to search
+ *
+ * @param which
+ * the string key for this element in the list
+ *
+ * @param val
+ * the value of the requested string array
+ *
+ * @param nelem
+ * the number of elements in the array
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOENT
+ * if no matching name-value pair is found
+ *
+ * @return ENOTSUP
+ * if an encode/decode method is not supported
+ *
+ * @return EINVAL
+ * if there is an invalid argument
+ */
+extern int set_string_array(
+ nvlist_t *attrs, char *which, char **val, uint_t nelem);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VOLUME_NVPAIR_H */
diff --git a/usr/src/cmd/lvm/metassist/common/volume_output.c b/usr/src/cmd/lvm/metassist/common/volume_output.c
new file mode 100644
index 0000000000..b6b177d0c5
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_output.c
@@ -0,0 +1,177 @@
+/*
+ * 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 "%Z%%M% %I% %E% SMI"
+
+#include <errno.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "volume_output.h"
+#include "volume_error.h"
+
+static int max_verbosity = OUTPUT_QUIET;
+static FILE *output = NULL;
+
+/*
+ * Set the maximum level of verbosity to be reported to the user.
+ * Strings sent to oprintf() with a higher verbosity level than this
+ * maximum level will not be reported to the user.
+ *
+ * @param verbosity
+ * One of the predefined constants:
+ * OUTPUT_QUIET
+ * OUTPUT_TERSE
+ * OUTPUT_VERBOSE
+ * OUTPUT_DEBUG
+ *
+ * @param stream
+ * The stream to print all qualifying output to.
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+int
+set_max_verbosity(
+ int verbosity,
+ FILE *stream)
+{
+ int error = 0;
+
+ switch (verbosity) {
+ case OUTPUT_QUIET:
+ case OUTPUT_TERSE:
+ case OUTPUT_VERBOSE:
+ case OUTPUT_DEBUG:
+ max_verbosity = verbosity;
+ output = stream;
+ break;
+
+ default:
+ volume_set_error(
+ gettext("%d: invalid verbosity level"), verbosity);
+ error = -1;
+ }
+
+ return (error);
+}
+
+/*
+ * Get the maximum level of verbosity to be reported to the user.
+ *
+ * @return OUTPUT_QUIET
+ *
+ * @return OUTPUT_TERSE
+ *
+ * @return OUTPUT_VERBOSE
+ *
+ * @return OUTPUT_DEBUG
+ */
+int
+get_max_verbosity()
+{
+ return (max_verbosity);
+}
+
+/*
+ * Prints the given formatted string arguments to a predefined stream,
+ * if the given verbosity is less than or equal to the set maximum
+ * verbosity.
+ *
+ * @param verbosity
+ * Same as for set_max_verbosity()
+ *
+ * @param fmt, ...
+ * printf-style arguments
+ *
+ * @return the number of characters output
+ * if successful
+ *
+ * @return negative value
+ * if unsuccessful
+ */
+int
+oprintf(
+ int verbosity,
+ char *fmt,
+ ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = oprintf_va(verbosity, fmt, ap);
+ va_end(ap);
+
+ return (ret);
+}
+
+/*
+ * Identical to oprintf but with a va_list instead of variable length
+ * argument list. This function is provided for external printf-style
+ * wrappers.
+ *
+ * @param verbosity
+ * Same as for set_max_verbosity()
+ *
+ * @param fmt
+ * printf format string
+ *
+ * @param ap
+ * a va_list containing remaining printf-style arguments
+ *
+ * @return the number of characters output
+ * if successful
+ *
+ * @return negative value
+ * if unsuccessful
+ */
+/*PRINTFLIKE2*/
+int
+oprintf_va(
+ int verbosity,
+ char *fmt,
+ va_list ap)
+{
+ int ret = 0;
+
+ /* Is this verbosity high enough to print? */
+ if (output != NULL && verbosity <= max_verbosity) {
+#ifdef DEBUG
+ if (getenv(METASSIST_DEBUG_ENV) != NULL) {
+ time_t now = time(NULL);
+ struct tm *time = localtime(&now);
+ fprintf(output, "%.2d:%.2d:%.2d: ",
+ time->tm_hour, time->tm_min, time->tm_sec);
+ }
+#endif
+ ret = vfprintf(output, fmt, ap);
+ }
+
+ return (ret);
+}
diff --git a/usr/src/cmd/lvm/metassist/common/volume_output.h b/usr/src/cmd/lvm/metassist/common/volume_output.h
new file mode 100644
index 0000000000..fbff0ed01c
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_output.h
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+#ifndef _VOLUME_OUTPUT_H
+#define _VOLUME_OUTPUT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#ifdef DEBUG
+/*
+ * The environment variable that must be set for metassist to
+ * enable debug output
+ */
+#define METASSIST_DEBUG_ENV "METASSIST_DEBUG"
+#endif
+
+/* Verbosity levels */
+#define OUTPUT_QUIET 0
+#define OUTPUT_TERSE 1
+#define OUTPUT_VERBOSE 2
+#define OUTPUT_DEBUG 3
+
+/*
+ * Set the maximum level of verbosity to be reported to the user.
+ * Strings sent to oprintf() with a higher verbosity level than this
+ * maximum level will not be reported to the user.
+ *
+ * @param verbosity
+ * One of the predefined constants:
+ * OUTPUT_QUIET
+ * OUTPUT_TERSE
+ * OUTPUT_VERBOSE
+ * OUTPUT_DEBUG
+ *
+ * @param stream
+ * The stream to print all qualifying output to.
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+extern int set_max_verbosity(int verbosity, FILE *stream);
+
+/*
+ * Get the maximum level of verbosity to be reported to the user.
+ *
+ * @return OUTPUT_QUIET
+ *
+ * @return OUTPUT_TERSE
+ *
+ * @return OUTPUT_VERBOSE
+ *
+ * @return OUTPUT_DEBUG
+ */
+extern int get_max_verbosity();
+
+/*
+ * Prints the given formatted string arguments to a predefined stream,
+ * if the given verbosity is less than or equal to the set maximum
+ * verbosity.
+ *
+ * @param verbosity
+ * Same as for set_max_verbosity()
+ *
+ * @param fmt, ...
+ * printf-style arguments
+ *
+ * @return the number of characters output
+ * if successful
+ *
+ * @return negative value
+ * if unsuccessful
+ */
+extern int oprintf(int verbosity, char *fmt, ...);
+
+/*
+ * Identical to oprintf but with a va_list instead of variable length
+ * argument list. This function is provided for external printf-style
+ * wrappers.
+ *
+ * @param verbosity
+ * Same as for set_max_verbosity()
+ *
+ * @param fmt
+ * printf format string
+ *
+ * @param ap
+ * a va_list containing remaining printf-style arguments
+ *
+ * @return the number of characters output
+ * if successful
+ *
+ * @return negative value
+ * if unsuccessful
+ */
+extern int oprintf_va(int verbosity, char *fmt, va_list ap);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VOLUME_OUTPUT_H */
diff --git a/usr/src/cmd/lvm/metassist/common/volume_request.c b/usr/src/cmd/lvm/metassist/common/volume_request.c
new file mode 100644
index 0000000000..665616089c
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_request.c
@@ -0,0 +1,178 @@
+/*
+ * 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 "%Z%%M% %I% %E% SMI"
+
+#include <libintl.h>
+#include "volume_request.h"
+#include "volume_error.h"
+
+/*
+ * Methods which manipulate a request_t struct
+ */
+
+/*
+ * Constructor: Create a request_t struct. This request_t must be
+ * freed.
+ *
+ * @param request
+ * RETURN: a pointer to a new request_t
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+new_request(
+ request_t **request)
+{
+ int error;
+ devconfig_t *diskset_req;
+ devconfig_t *diskset_config;
+
+ *request = (request_t *)calloc(1, sizeof (request_t));
+ if (*request == NULL) {
+ (void) volume_set_error(gettext("new_request calloc() failed\n"));
+ return (-1);
+ }
+
+ /* Create a new diskset_req */
+ if ((error = new_devconfig(&diskset_req, TYPE_DISKSET)) != 0) {
+ free_request(*request);
+ return (error);
+ }
+ request_set_diskset_req(*request, diskset_req);
+
+ /* Create a new diskset_config */
+ if ((error = new_devconfig(&diskset_config, TYPE_DISKSET)) != 0) {
+ free_request(*request);
+ return (error);
+ }
+ request_set_diskset_config(*request, diskset_config);
+
+ return (0);
+}
+
+/*
+ * Free memory (recursively) allocated to a request_t struct
+ *
+ * @param arg
+ * pointer to the request_t struct to free
+ */
+void
+free_request(
+ void *arg)
+{
+ request_t *request = (request_t *)arg;
+
+ if (request == NULL) {
+ return;
+ }
+
+ /* Free the diskset_req */
+ if (request->diskset_req != NULL) {
+ free_devconfig(request->diskset_req);
+ }
+
+ /* Free the diskset_config */
+ if (request->diskset_config != NULL) {
+ free_devconfig(request->diskset_config);
+ }
+
+ /* Free the devconfig itself */
+ free(request);
+}
+
+/*
+ * Set the disk set at the top of the request hierarchy
+ *
+ * @param request
+ * The request_t representing the request to modify
+ *
+ * @param diskset
+ * The devconfig_t representing the toplevel (disk set)
+ * device in the volume request hierarchy
+ */
+void
+request_set_diskset_req(
+ request_t *request,
+ devconfig_t *diskset)
+{
+ request->diskset_req = diskset;
+}
+
+/*
+ * Get the disk set at the top of the request hierarchy
+ *
+ * @param request
+ * The request_t representing the request to examine
+ *
+ * @return The devconfig_t representing the toplevel (disk set)
+ * device in the volume request hierarchy
+ */
+devconfig_t *
+request_get_diskset_req(
+ request_t *request)
+{
+ return (request->diskset_req);
+}
+
+/*
+ * Set/get the disk set at the top of the proposed volume hierarchy
+ *
+ * @param request
+ * The request_t representing the request to modify
+ *
+ * @param diskset
+ * The devconfig_t representing the toplevel (disk set)
+ * device in the proposed volume hierarchy
+ */
+void
+request_set_diskset_config(
+ request_t *request,
+ devconfig_t *diskset)
+{
+ request->diskset_config = diskset;
+}
+
+/*
+ * Get the disk set at the top of the request hierarchy
+ *
+ * @param request
+ * The request_t representing the request to examine
+ *
+ * @return The devconfig_t representing the toplevel (disk set)
+ * device in the proposed volume hierarchy
+ */
+devconfig_t *
+request_get_diskset_config(
+ request_t *request)
+{
+ return (request->diskset_config);
+}
diff --git a/usr/src/cmd/lvm/metassist/common/volume_request.h b/usr/src/cmd/lvm/metassist/common/volume_request.h
new file mode 100644
index 0000000000..5ba1d06d81
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_request.h
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+#ifndef _VOLUME_REQUEST_H
+#define _VOLUME_REQUEST_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "volume_devconfig.h"
+
+/*
+ * request_t - struct to hold a layout request
+ */
+typedef struct request {
+ /*
+ * The devconfig_t representing the disk set at the top of the
+ * request hierarchy. This hierarchy represents the requested
+ * volume configuration, as read from the volume-request.
+ */
+ devconfig_t *diskset_req;
+
+ /*
+ * The devconfig_t representing the disk set at the top of the
+ * resulting proposed volume hierarchy. This hierarchy
+ * represents the volume configuration proposed by the layout
+ * engine. This configuration will eventually be converted to
+ * a volume-spec.
+ */
+ devconfig_t *diskset_config;
+} request_t;
+
+/*
+ * Constructor: Create a request_t struct. This request_t must be
+ * freed.
+ *
+ * @param request
+ * RETURN: a pointer to a new request_t
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int new_request(request_t **request);
+
+/*
+ * Free memory (recursively) allocated to a request_t struct
+ *
+ * @param arg
+ * pointer to the request_t struct to free
+ */
+extern void free_request(void *arg);
+
+/*
+ * Set the disk set at the top of the request hierarchy
+ *
+ * @param request
+ * The request_t representing the request to modify
+ *
+ * @param diskset
+ * The devconfig_t representing the toplevel (disk set)
+ * device in the volume request hierarchy
+ */
+extern void request_set_diskset_req(request_t *request, devconfig_t *diskset);
+
+/*
+ * Get the disk set at the top of the request hierarchy
+ *
+ * @param request
+ * The request_t representing the request to examine
+ *
+ * @return The devconfig_t representing the toplevel (disk set)
+ * device in the volume request hierarchy
+ */
+extern devconfig_t *request_get_diskset_req(request_t *request);
+
+/*
+ * Set/get the disk set at the top of the proposed volume hierarchy
+ *
+ * @param request
+ * The request_t representing the request to modify
+ *
+ * @param diskset
+ * The devconfig_t representing the toplevel (disk set)
+ * device in the proposed volume hierarchy
+ */
+extern void request_set_diskset_config(
+ request_t *request, devconfig_t *diskset);
+
+/*
+ * Get the disk set at the top of the request hierarchy
+ *
+ * @param request
+ * The request_t representing the request to examine
+ *
+ * @return The devconfig_t representing the toplevel (disk set)
+ * device in the proposed volume hierarchy
+ */
+extern devconfig_t *request_get_diskset_config(request_t *request);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VOLUME_REQUEST_H */
diff --git a/usr/src/cmd/lvm/metassist/common/volume_string.c b/usr/src/cmd/lvm/metassist/common/volume_string.c
new file mode 100644
index 0000000000..0391fa7fc2
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_string.c
@@ -0,0 +1,462 @@
+/*
+ * 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 "volume_string.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <libintl.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "volume_error.h"
+#include "volume_output.h"
+
+/*
+ * ******************************************************************
+ *
+ * Function prototypes
+ *
+ * ******************************************************************
+ */
+
+static void *append_to_pointer_array(void **array, void *pointer);
+
+/*
+ * ******************************************************************
+ *
+ * Data
+ *
+ * ******************************************************************
+ */
+
+/* All-inclusive valid size units */
+units_t universal_units[] = {
+ {"BLOCKS", BYTES_PER_BLOCK},
+ {"KB", BYTES_PER_KILOBYTE},
+ {"MB", BYTES_PER_MEGABYTE},
+ {"GB", BYTES_PER_GIGABYTE},
+ {"TB", BYTES_PER_TERABYTE},
+ {NULL, 0}
+};
+
+/*
+ * ******************************************************************
+ *
+ * External functions
+ *
+ * ******************************************************************
+ */
+
+/*
+ * Concatenates a list of strings. The result must be free()d.
+ *
+ * @param numargs
+ * The number of strings to concatenate.
+ *
+ * @param ...
+ * The strings (type char *) to concatenate.
+ *
+ * @return the concatenated string
+ * if succesful
+ *
+ * @return NULL
+ * if memory could not be allocated
+ */
+char *
+stralloccat(
+ int numargs,
+ ...)
+{
+ va_list vl;
+ int i;
+ int len = 1;
+ char *cat;
+
+ /* Determine length of concatenated string */
+ va_start(vl, numargs);
+ for (i = 0; i < numargs; i++) {
+ char *str = va_arg(vl, char *);
+ if (str != NULL) {
+ len += strlen(str);
+ }
+ }
+ va_end(vl);
+
+ /* Allocate memory for concatenation plus a trailing NULL */
+ cat = (char *)calloc(1, len * sizeof (char));
+
+ if (cat == NULL) {
+ return (NULL);
+ }
+
+ /* Concatenate strings */
+ va_start(vl, numargs);
+ for (i = 0; i < numargs; i++) {
+ char *str = va_arg(vl, char *);
+ if (str != NULL) {
+ strcat(cat, str);
+ }
+ }
+ va_end(vl);
+
+ return (cat);
+}
+
+/*
+ * Convert the given string to a uint16_t, verifying that the value
+ * does not exceed the lower or upper bounds of a uint16_t.
+ *
+ * @param str
+ * the string to convert
+ *
+ * @param num
+ * the addr of the uint16_t
+ *
+ * @return 0
+ * if the given string was converted to a uint16_t
+ *
+ * @return -1
+ * if the string could could not be converted to a number
+ *
+ * @return -2
+ * if the converted number exceeds the lower or upper
+ * bounds of a uint16_t
+ */
+int
+str_to_uint16(
+ char *str,
+ uint16_t *num)
+{
+ long long lnum;
+ int error = 0;
+
+ /* Convert string to long long */
+ if (sscanf(str, "%lld", &lnum) != 1) {
+ error = -1;
+ } else {
+
+ /*
+ * Verify that the long long value does not exceed the
+ * lower or upper bounds of a uint16_t
+ */
+
+ /* Maximum value of uint16_t */
+ uint16_t max = (uint16_t)~0ULL;
+
+ if (lnum < 0 || lnum > max) {
+ error = -2;
+ } else {
+ *num = lnum;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Converts the given long long into a string. This string must be
+ * freed.
+ *
+ * @param num
+ * the long long to convert
+ *
+ * @param str
+ * the addr of the string
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOMEM
+ * if the physical limits of the system are exceeded by
+ * size bytes of memory which cannot be allocated
+ *
+ * @return EAGAIN
+ * if there is not enough memory available to allocate
+ * size bytes of memory
+ */
+int
+ll_to_str(
+ long long num,
+ char **str)
+{
+ int error = 0;
+
+ /* Allocate memory for the string */
+ if ((*str = calloc(1, LONG_LONG_STR_SIZE * sizeof (char))) == NULL) {
+ error = errno;
+ } else {
+ /* Convert the integer to a string */
+ snprintf(*str, LONG_LONG_STR_SIZE, "%lld", num);
+ }
+
+ return (error);
+}
+
+/*
+ * Convert a size specification to bytes.
+ *
+ * @param str
+ * a size specification strings of the form
+ * <value><units>, where valid <units> are specified by
+ * the units argument and <value> is the (floating-point)
+ * multiplier of the units
+ *
+ * @param bytes
+ * RETURN: the result of converting the given size string
+ * to bytes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+sizestr_to_bytes(
+ char *str,
+ uint64_t *bytes,
+ units_t units[])
+{
+ char *unit_str;
+ long double d;
+ int error = 0;
+ int i;
+
+ /* Convert <value> string to double */
+ if ((d = strtod(str, &unit_str)) == 0) {
+ volume_set_error(gettext("invalid size string: %s"), str);
+ error = -1;
+ } else {
+
+ /* Trim leading white space */
+ while (isspace(*unit_str) != 0) {
+ ++unit_str;
+ }
+
+ /* Convert to bytes based on <units> */
+ for (i = 0; units[i].unit_str != NULL; i++) {
+ if (strcasecmp(unit_str, units[i].unit_str) == 0) {
+ d *= units[i].bytes_per_unit;
+ break;
+ }
+ }
+
+ /* Was a valid unit string found? */
+ if (units[i].unit_str == NULL) {
+ volume_set_error(
+ gettext("missing or invalid units indicator in size: %s"),
+ str);
+ error = -1;
+ }
+ }
+
+ if (error) {
+ *bytes = 0;
+ } else {
+ *bytes = (uint64_t)d;
+ oprintf(OUTPUT_DEBUG,
+ gettext("converted \"%s\" to %llu bytes\n"), str, *bytes);
+ }
+
+ return (error);
+}
+
+/*
+ * Convert bytes to a size specification string.
+ *
+ * @param bytes
+ * the number of bytes
+ *
+ * @param str
+ * RETURN: a size specification strings of the form
+ * <value><units>, where valid <units> are specified by
+ * the units argument and <value> is the (floating-point)
+ * multiplier of the units. This string must be freed.
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+int
+bytes_to_sizestr(
+ uint64_t bytes,
+ char **str,
+ units_t units[],
+ boolean_t round)
+{
+ int i, len, error = 0;
+ double value;
+ const char *format;
+ units_t use_units = units[0];
+
+ /* Determine the units to use */
+ for (i = 0; units[i].unit_str != NULL; i++) {
+ if (bytes >= units[i].bytes_per_unit) {
+ use_units = units[i];
+ }
+ }
+
+ value = ((long double)bytes / use_units.bytes_per_unit);
+
+ /* Length of string plus trailing NULL */
+ len = LONG_LONG_STR_SIZE + strlen(use_units.unit_str) + 1;
+
+ if (round) {
+ value = floor(value + 0.5F);
+ format = "%.0f%s";
+ } else {
+ format = "%.2f%s";
+ }
+
+ /* Append units to string */
+ *str = calloc(1, len * sizeof (char));
+ if (*str == NULL) {
+ error = errno;
+ } else {
+ snprintf(*str, len, format, value, use_units.unit_str);
+ }
+
+ return (error);
+}
+
+/*
+ * Appends a copy of the given string to the given string array,
+ * ensuring that the last element in the array is NULL. This array
+ * must be freed via free_string_array.
+ *
+ * Note when an error occurs and NULL is returned, array is not freed.
+ * Subsequently callers should save a pointer to the original array
+ * until success is verified.
+ *
+ * @param array
+ * the array to append to, or NULL to create a new array
+ *
+ * @param str
+ * the string to copy and add to the array
+ *
+ * @return a pointer to the realloc'd (and possibly moved) array
+ * if succesful
+ *
+ * @return NULL
+ * if unsuccesful
+ */
+char **
+append_to_string_array(
+ char **array,
+ char *str)
+{
+ char *copy = strdup(str);
+
+ if (copy == NULL) {
+ return (NULL);
+ }
+
+ return ((char **)append_to_pointer_array((void **)array, copy));
+}
+
+/*
+ * Frees each element of the given string array, then frees the array
+ * itself.
+ *
+ * @param array
+ * a NULL-terminated string array
+ */
+void
+free_string_array(
+ char **array)
+{
+ int i;
+
+ /* Free each available element */
+ for (i = 0; array[i] != NULL; i++) {
+ free(array[i]);
+ }
+
+ /* Free the array itself */
+ free((void *)array);
+}
+
+/*
+ * ******************************************************************
+ *
+ * Static functions
+ *
+ * ******************************************************************
+ */
+
+/*
+ * Appends the given pointer to the given pointer array, ensuring that
+ * the last element in the array is NULL.
+ *
+ * Note when an error occurs and NULL is returned, array is not freed.
+ * Subsequently callers should save a pointer to the original array
+ * until success is verified.
+ *
+ * @param array
+ * the array to append to, or NULL to create a new array
+ *
+ * @param pointer
+ * the pointer to add to the array
+ *
+ * @return a pointer to the realloc'd (and possibly moved) array
+ * if succesful
+ *
+ * @return NULL
+ * if unsuccesful
+ */
+static void *
+append_to_pointer_array(
+ void **array,
+ void *pointer)
+{
+ void **newarray = NULL;
+ int i = 0;
+
+ if (array != NULL) {
+ /* Count the elements currently in the array */
+ for (i = 0; array[i] != NULL; ++i);
+ }
+
+ /* realloc, adding a slot for the new pointer */
+ newarray = (void **)realloc(array, (i + 2) * sizeof (*array));
+
+ if (newarray != NULL) {
+ /* Append pointer and terminal NULL */
+ newarray[i] = pointer;
+ newarray[i+1] = NULL;
+ }
+
+ return (newarray);
+}
diff --git a/usr/src/cmd/lvm/metassist/common/volume_string.h b/usr/src/cmd/lvm/metassist/common/volume_string.h
new file mode 100644
index 0000000000..a66b38b23c
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/common/volume_string.h
@@ -0,0 +1,203 @@
+/*
+ * 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.
+ */
+
+#ifndef _VOLUME_STRING_H
+#define _VOLUME_STRING_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The length of the string when the longest long long is converted to
+ * a string
+ */
+#define LONG_LONG_STR_SIZE 128
+
+#define BYTES_PER_BLOCK 512
+#define BYTES_PER_KILOBYTE 1024
+#define BYTES_PER_MEGABYTE 1024 * 1024
+#define BYTES_PER_GIGABYTE 1024 * 1024 * 1024
+#define BYTES_PER_TERABYTE (uint64_t)1024 * 1024 * 1024 * 1024
+
+/*
+ * Describes units when converting from bytes to string and back.
+ */
+typedef struct {
+ char *unit_str;
+ uint64_t bytes_per_unit;
+} units_t;
+
+/* All-inclusive valid size units */
+extern units_t universal_units[];
+
+/*
+ * Concatenates a list of strings. The result must be free()d.
+ *
+ * @param numargs
+ * The number of strings to concatenate.
+ *
+ * @param ...
+ * The strings (type char *) to concatenate.
+ *
+ * @return the concatenated string
+ * if succesful
+ *
+ * @return NULL
+ * if memory could not be allocated
+ */
+extern char *stralloccat(int numargs, ...);
+
+/*
+ * Convert the given string to a uint16_t, verifying that the value
+ * does not exceed the lower or upper bounds of a uint16_t.
+ *
+ * @param str
+ * the string to convert
+ *
+ * @param num
+ * the addr of the uint16_t
+ *
+ * @return 0
+ * if the given string was converted to a uint16_t
+ *
+ * @return -1
+ * if the string could could not be converted to a number
+ *
+ * @return -2
+ * if the converted number exceeds the lower or upper
+ * bounds of a uint16_t
+ */
+extern int str_to_uint16(char *str, uint16_t *num);
+
+/*
+ * Converts the given long long into a string. This string must be
+ * freed.
+ *
+ * @param num
+ * the long long to convert
+ *
+ * @param str
+ * the addr of the string
+ *
+ * @return 0
+ * if successful
+ *
+ * @return ENOMEM
+ * if the physical limits of the system are exceeded by
+ * size bytes of memory which cannot be allocated
+ *
+ * @return EAGAIN
+ * if there is not enough memory available to allocate
+ * size bytes of memory
+ */
+extern int ll_to_str(long long num, char **str);
+
+/*
+ * Convert a size specification to bytes.
+ *
+ * @param str
+ * a size specification strings of the form
+ * <value><units>, where valid <units> are specified by
+ * the units argument and <value> is the (floating-point)
+ * multiplier of the units
+ *
+ * @param bytes
+ * RETURN: the result of converting the given size string
+ * to bytes
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int sizestr_to_bytes(char *str, uint64_t *bytes, units_t units[]);
+
+/*
+ * Convert bytes to a size specification string.
+ *
+ * @param bytes
+ * the number of bytes
+ *
+ * @param str
+ * RETURN: a size specification strings of the form
+ * <value><units>, where valid <units> are specified by
+ * the units argument and <value> is the (floating-point)
+ * multiplier of the units. This string must be freed.
+ *
+ * @return 0
+ * if successful
+ *
+ * @return non-zero
+ * if an error occurred. Use get_error_string() to
+ * retrieve the associated error message.
+ */
+extern int bytes_to_sizestr(
+ uint64_t bytes, char **str, units_t units[], boolean_t round);
+
+/*
+ * Appends a copy of the given string to the given string array,
+ * ensuring that the last element in the array is NULL. This array
+ * must be freed via free_string_array.
+ *
+ * Note when an error occurs and NULL is returned, array is not freed.
+ * Subsequently callers should save a pointer to the original array
+ * until success is verified.
+ *
+ * @param array
+ * the array to append to, or NULL to create a new array
+ *
+ * @param str
+ * the string to copy and add to the array
+ *
+ * @return a pointer to the realloc'd (and possibly moved) array
+ * if succesful
+ *
+ * @return NULL
+ * if unsuccesful
+ */
+extern char ** append_to_string_array(char **array, char *str);
+
+/*
+ * Frees each element of the given string array, then frees the array
+ * itself.
+ *
+ * @param array
+ * a NULL-terminated string array
+ */
+extern void free_string_array(char **array);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VOLUME_STRING_H */
diff --git a/usr/src/cmd/lvm/metassist/controller/Makefile b/usr/src/cmd/lvm/metassist/controller/Makefile
new file mode 100644
index 0000000000..738a74e9fb
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/controller/Makefile
@@ -0,0 +1,49 @@
+#
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+METASSIST_TOPLEVEL = ..
+
+SRCS = getopt_ext.c metassist.c
+OBJS = $(SRCS:%.c=%.o)
+HDRS = $(SRCS:%.c=%.h)
+MSGFILES = $(SRCS:%.c=%.i)
+
+include $(METASSIST_TOPLEVEL)/../../Makefile.cmd
+include $(METASSIST_TOPLEVEL)/Makefile.env
+
+INCLUDES += -I.. -I../common -I../xml -I../layout \
+ -I/usr/include/libxml2
+CFLAGS += $(INCLUDES)
+
+POFILE = controllerp.po
+
+include $(METASSIST_TOPLEVEL)/Makefile.targ
+
+# Build .po file from message files
+$(POFILE): $(MSGFILES)
+ $(BUILDPO.msgfiles)
diff --git a/usr/src/cmd/lvm/metassist/controller/getopt_ext.c b/usr/src/cmd/lvm/metassist/controller/getopt_ext.c
new file mode 100644
index 0000000000..459bb9f8db
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/controller/getopt_ext.c
@@ -0,0 +1,151 @@
+/*
+ * 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 "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include "volume_error.h"
+#include "getopt_ext.h"
+
+/*
+ * Functions
+ */
+
+/*
+ * Identical to getopt(3), except that
+ *
+ * 1. If "-" is the first character of optstring, each non-option argv
+ * element is handled as if it were the argument of an option with
+ * character code GETOPT_NON_OPTION_ARG. The result is that
+ * GETOPT_DONE_PARSING will not be returned until the end of the
+ * argument list has been reached.
+ *
+ * This mirrors the functionality provided by GNU getopt.
+ *
+ * 2. GETOPT_ERR_INVALID_OPT or GETOPT_ERR_MISSING_ARG is returned
+ * instead of '?'. Subsequently "-?" can be used as a valid
+ * option.
+ *
+ * 3. GETOPT_DONE_PARSING, GETOPT_ERR_INVALID_ARG, or
+ * GETOPT_NON_OPTION_ARG is returned instead of -1.
+ *
+ * @param argc
+ * The number of arguments in the array
+ *
+ * @param argv
+ * The argument array
+ *
+ * @param optstring
+ * The option letters, with ':' following options with
+ * required arguments. See note about "-" as the first
+ * character.
+ *
+ * @return GETOPT_ERR_INVALID_OPT
+ * if the option is not found in optstring
+ *
+ * GETOPT_ERR_MISSING_ARG
+ * if the option requires an argument which is missing
+ *
+ * GETOPT_ERR_INVALID_ARG
+ * if "-" is not the first character in optstring and a
+ * non-option argument is encountered
+ *
+ * GETOPT_NON_OPTION_ARG
+ * if "-" is the first character in optstring and a
+ * non-option argument is encountered
+ *
+ * GETOPT_DONE_PARSING
+ * if the end of the argument list is reached
+ *
+ * <optopt>
+ * the option character itself, if none of the above
+ * scenarios applies.
+ */
+extern int
+getopt_ext(
+ int argc,
+ char * const argv[],
+ const char *optstring)
+{
+ int c;
+ int handle_non_options = (*optstring == '-');
+
+ /* Is "-" the first character of optstring? */
+ if (handle_non_options) {
+ /* getopt(3) doesn't understand "-" */
+ optstring++;
+ }
+
+ switch (c = getopt(argc, argv, optstring)) {
+
+ /*
+ * getopt(3) returns -1 when 1) it encounters a non-option
+ * argument or 2) reaches the end of the argument list.
+ * Distinguish from the two possibilities.
+ */
+ case -1:
+ if (optind < argc) {
+ optarg = argv[optind];
+
+ /* Non-option argument found */
+ if (handle_non_options) {
+ /* Non-option arguments are valid */
+ c = GETOPT_NON_OPTION_ARG;
+ optind++;
+ } else {
+ /* Non-option arguments are invalid */
+ c = GETOPT_ERR_INVALID_ARG;
+ }
+ } else {
+ /* End of the argument list reached */
+ c = GETOPT_DONE_PARSING;
+ }
+ break;
+
+ /*
+ * getopt(3) returns '?' when 1) the "-?" option is
+ * encountered, 2) an invalid option is given or 3) a
+ * valid option requiring an argument is found but no
+ * argument is specified. Distinguish from the three
+ * possibilities.
+ */
+ case '?':
+ /* Is this an error or was -? encountered? */
+ if (optopt != '?') {
+ if (strchr(optstring, optopt) == NULL) {
+ /* Invalid option */
+ c = GETOPT_ERR_INVALID_OPT;
+ optarg = argv[optind-1];
+ } else {
+ /* Valid option without required argument */
+ c = GETOPT_ERR_MISSING_ARG;
+ }
+ }
+ }
+
+ return (c);
+}
diff --git a/usr/src/cmd/lvm/metassist/controller/getopt_ext.h b/usr/src/cmd/lvm/metassist/controller/getopt_ext.h
new file mode 100644
index 0000000000..6b36477ce0
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/controller/getopt_ext.h
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+
+#ifndef _GETOPTEXT_H
+#define _GETOPTEXT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Returned chars for getopt_ext
+ */
+
+/* A non-option argument was found */
+#define GETOPT_NON_OPTION_ARG 1
+
+/* All arguments have been parsed */
+#define GETOPT_DONE_PARSING -1
+
+/* An invalid option was found */
+#define GETOPT_ERR_INVALID_OPT -2
+
+/* An invalid non-option argument was found */
+#define GETOPT_ERR_INVALID_ARG -3
+
+/* No argument for valid option expecting an argument */
+#define GETOPT_ERR_MISSING_ARG -4
+
+/*
+ * Function prototypes
+ */
+
+/*
+ * Identical to getopt(3), except that
+ *
+ * 1. If "-" is the first character of optstring, each non-option argv
+ * element is handled as if it were the argument of an option with
+ * character code GETOPT_NON_OPTION_ARG. The result is that
+ * GETOPT_DONE_PARSING will not be returned until the end of the
+ * argument list has been reached.
+ *
+ * This mirrors the functionality provided by GNU getopt.
+ *
+ * 2. GETOPT_ERR_INVALID_OPT or GETOPT_ERR_MISSING_ARG is returned
+ * instead of '?'. Subsequently "-?" can be used as a valid
+ * option.
+ *
+ * 3. GETOPT_DONE_PARSING, GETOPT_ERR_INVALID_ARG, or
+ * GETOPT_NON_OPTION_ARG is returned instead of -1.
+ *
+ * @param argc
+ * The number of arguments in the array
+ *
+ * @param argv
+ * The argument array
+ *
+ * @param optstring
+ * The option letters, with ':' following options with
+ * required arguments. See note about "-" as the first
+ * character.
+ *
+ * @return GETOPT_ERR_INVALID_OPT
+ * if the option is not found in optstring
+ *
+ * GETOPT_ERR_MISSING_ARG
+ * if the option requires an argument which is missing
+ *
+ * GETOPT_ERR_INVALID_ARG
+ * if "-" is not the first character in optstring and a
+ * non-option argument is encountered
+ *
+ * GETOPT_NON_OPTION_ARG
+ * if "-" is the first character in optstring and a
+ * non-option argument is encountered
+ *
+ * GETOPT_DONE_PARSING
+ * if the end of the argument list is reached
+ *
+ * <optopt>
+ * the option character itself, if none of the above
+ * scenarios applies.
+ */
+extern int getopt_ext(int argc, char * const argv[], const char *optstring);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPTEXT_H */
diff --git a/usr/src/cmd/lvm/metassist/controller/metassist.c b/usr/src/cmd/lvm/metassist/controller/metassist.c
new file mode 100644
index 0000000000..aeb9655937
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/controller/metassist.c
@@ -0,0 +1,1511 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Front end CLI to metassist. Parses command line, reads in data
+ * files, provides main() entry point into metassist. Here's the
+ * complete data validation stack for the project:
+ *
+ * 1. Controller validates command line syntax/order of arguments.
+ *
+ * 2. XML parser validates XML syntax, conformance with DTD
+ *
+ * 3. xml_convert validates proper conversion from string to
+ * size/integer/float/boolean/etc.
+ *
+ * 4. devconfig_t mutators validate limits/boundaries/min/max/names of
+ * data. References md_mdiox.h and possibly libmeta.
+ *
+ * 5. layout validates on remaining issues, including existence of
+ * given devices, feasibility of request, suitability of specified
+ * components, and subtle misuse of data structure (like both size
+ * and components specified).
+ */
+
+#include "metassist.h"
+
+#include <errno.h>
+#include <libintl.h>
+
+#include <math.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include "getopt_ext.h"
+#include "locale.h"
+#include "volume_error.h"
+#include "volume_output.h"
+#include "volume_request.h"
+#include "volume_defaults.h"
+#include "volume_string.h"
+#include "xml_convert.h"
+#include "layout.h"
+
+/*
+ * Function prototypes
+ */
+
+static void clean_up();
+static void interrupthandler(int x);
+static int copy_arg(char *option, char *value, char **saveto);
+static xmlDocPtr create_volume_request_XML();
+static int handle_common_opts(int c, boolean_t *handled);
+static int parse_create_opts(int argc, char *argv[]);
+static int parse_opts(int argc, char *argv[]);
+static int parse_tokenized_list(const char *string, dlist_t **list);
+static int parse_verbose_arg(char *arg, int *verbosity);
+static void print_help_create(FILE *stream);
+static void print_help_main(FILE *stream);
+static void print_manual_reference(FILE *stream);
+static void print_usage(FILE *stream);
+static void print_usage_create(FILE *stream);
+static void print_usage_main(FILE *stream);
+static int print_version(FILE *stream);
+static int get_doc_from_file(
+ char *file, char **valid_types, xmlDocPtr *doc, char **root);
+static int get_volume_request_or_config(xmlDocPtr *doc, char **root);
+static int handle_commands(char *commands);
+static int handle_config(devconfig_t *config);
+static int handle_request(request_t *request, defaults_t *defaults);
+static int write_temp_file(char *text, mode_t mode, char **file);
+
+/*
+ * Data
+ */
+
+/* Holds argv[0] */
+char *progname;
+
+/* The action to take */
+int action = ACTION_EXECUTE;
+
+/* Holds the name of the temporary command file */
+char *commandfile = NULL;
+
+/* The metassist subcommand */
+int subcmd = SUBCMD_NONE;
+
+/* The volume-request XML file to read */
+char *arg_inputfile = NULL;
+
+/* The size of the requested volume */
+char *arg_size = NULL;
+
+/* The disk set to use */
+char *arg_diskset = NULL;
+
+/* The volume name to use */
+char *arg_name = NULL;
+
+/* Redundancy level */
+char *arg_redundancy = NULL;
+
+/* Number of datapaths */
+char *arg_datapaths = NULL;
+
+/* Whether to implement fault recovery */
+boolean_t faultrecovery = B_FALSE;
+
+/* Whether to output the config file */
+boolean_t output_configfile = B_FALSE;
+
+/* Whether to output the command file instead of */
+boolean_t output_commandfile = B_FALSE;
+
+/* List of available devices */
+dlist_t *available = NULL;
+
+/* List of unavailable devices */
+dlist_t *unavailable = NULL;
+
+/*
+ * Functions
+ */
+
+/*
+ * Frees alloc'd memory, to be called prior to exiting.
+ */
+static void
+clean_up()
+{
+ /* Remove temporary command file */
+ if (commandfile != NULL) {
+ /* Ignore failure */
+ unlink(commandfile);
+ }
+
+ /* Free allocated argument strings */
+ if (commandfile != NULL) free(commandfile);
+ if (arg_diskset != NULL) free(arg_diskset);
+ if (arg_name != NULL) free(arg_name);
+ if (arg_inputfile != NULL) free(arg_inputfile);
+
+ /* Free available dlist and strings within */
+ dlist_free_items(available, free);
+
+ /* Free unavailable dlist and strings within */
+ dlist_free_items(unavailable, free);
+
+ /* Clean up XML data structures */
+ cleanup_xml();
+}
+
+/*
+ * Signal handler, called to exit gracefully
+ */
+static void
+interrupthandler(
+ int sig)
+{
+ char sigstr[SIG2STR_MAX];
+
+ if (sig2str(sig, sigstr) != 0) {
+ sigstr[0] = '\0';
+ }
+
+ fprintf(stderr,
+ gettext("Signal %d (%s) caught -- exiting...\n"), sig, sigstr);
+
+ /* Allow layout to cleanup on abnormal exit */
+ layout_clean_up();
+
+ clean_up();
+ exit(1);
+}
+
+/*
+ * Copies and saves the given argument, verifying that the argument
+ * has not already been saved.
+ *
+ * @param option
+ * The flag preceding or type of the argument. Used only
+ * in the error message when an option has already been
+ * saved to *saveto.
+ *
+ * @param value
+ * The argument to be copied.
+ *
+ * @param saveto
+ * Changed to point to the copied data. This must point
+ * to NULL data initially, or it will be assumed that
+ * this argument has already been set. This memory must
+ * be free()d by the caller.
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+copy_arg(
+ char *option,
+ char *value,
+ char **saveto)
+{
+ int error = 0;
+
+ /* Has this string already been set? */
+ if (*saveto != NULL) {
+ volume_set_error(
+ gettext("%s: option specified multiple times"), option);
+ error = -1;
+ } else
+
+ if ((*saveto = strdup(value)) == NULL) {
+ error = ENOMEM;
+ }
+
+ return (error);
+}
+
+/*
+ * Generates the XML volume request corresponding to the command-line
+ * parameters. No DTD node is included in this request.
+ *
+ * @return The XML request, or NULL if an error ocurred in
+ * generating the text. This memory must be freed with
+ * XMLFree().
+ */
+static xmlDocPtr
+create_volume_request_XML()
+{
+ xmlDocPtr doc;
+ xmlNodePtr request, volume;
+
+ /* Create the XML document */
+ doc = xmlNewDoc((xmlChar *)"1.0");
+
+ /* Create the root node */
+ request = xmlNewDocNode(
+ doc, NULL, (xmlChar *)ELEMENT_VOLUMEREQUEST, NULL);
+ xmlAddChild((xmlNodePtr) doc, (xmlNodePtr)request);
+
+ /* diskset element */
+ if (arg_diskset != NULL) {
+ xmlNodePtr node = xmlNewChild(
+ request, NULL, (xmlChar *)ELEMENT_DISKSET, NULL);
+ xmlSetProp(node,
+ (xmlChar *)ATTR_NAME, (xmlChar *)arg_diskset);
+ }
+
+ /* available elements */
+ if (available != NULL) {
+ dlist_t *item;
+ for (item = available; item != NULL; item = item->next) {
+ xmlNodePtr node = xmlNewChild(
+ request, NULL, (xmlChar *)ELEMENT_AVAILABLE, NULL);
+ xmlSetProp(node,
+ (xmlChar *)ATTR_NAME, (xmlChar *)item->obj);
+ }
+ }
+
+ /* unavailable elements */
+ if (unavailable != NULL) {
+ dlist_t *item;
+ for (item = unavailable; item != NULL; item = item->next) {
+ xmlNodePtr node = xmlNewChild(
+ request, NULL, (xmlChar *)ELEMENT_UNAVAILABLE, NULL);
+ xmlSetProp(node,
+ (xmlChar *)ATTR_NAME, (xmlChar *)item->obj);
+ }
+ }
+
+ /* volume element */
+ volume = xmlNewChild(request, NULL, (xmlChar *)ELEMENT_VOLUME, NULL);
+
+ /* Volume name - optional */
+ if (arg_name != NULL) {
+ xmlSetProp(volume,
+ (xmlChar *)ATTR_NAME, (xmlChar *)arg_name);
+ }
+
+ /* Volume size - required */
+ xmlSetProp(volume, (xmlChar *)ATTR_SIZEINBYTES, (xmlChar *)arg_size);
+
+ /* Volume redundancy - optional */
+ if (arg_redundancy != NULL) {
+ xmlSetProp(volume,
+ (xmlChar *)ATTR_VOLUME_REDUNDANCY, (xmlChar *)arg_redundancy);
+ }
+
+ /* Volume fault recovery - optional */
+ if (faultrecovery == B_TRUE) {
+ xmlSetProp(volume,
+ (xmlChar *)ATTR_VOLUME_FAULTRECOVERY, (xmlChar *)"TRUE");
+ }
+
+ /* Volume datapaths - optional */
+ if (arg_datapaths != NULL) {
+ xmlSetProp(volume,
+ (xmlChar *)ATTR_VOLUME_DATAPATHS, (xmlChar *)arg_datapaths);
+ }
+
+ if (get_max_verbosity() >= OUTPUT_DEBUG) {
+ xmlChar *text;
+ /* Get the text dump */
+ xmlDocDumpFormatMemory(doc, &text, NULL, 1);
+ oprintf(OUTPUT_DEBUG,
+ gettext("Generated volume-request:\n%s"), text);
+ xmlFree(text);
+ }
+
+ return (doc);
+}
+
+/*
+ * Checks the given flag for options common to all subcommands.
+ *
+ * @param c
+ * The option letter.
+ *
+ * @param handled
+ * RETURN: whether the given option flag was handled.
+ *
+ * @return Non-zero if an error occurred or the given option was
+ * invalid or incomplete, 0 otherwise.
+ */
+static int
+handle_common_opts(
+ int c,
+ boolean_t *handled)
+{
+ int error = 0;
+
+ /* Level of verbosity to report */
+ int verbosity;
+
+ *handled = B_TRUE;
+
+ switch (c) {
+ case COMMON_SHORTOPT_VERBOSITY:
+ if ((error = parse_verbose_arg(optarg, &verbosity)) == 0) {
+ set_max_verbosity(verbosity, stderr);
+ }
+ break;
+
+ case COMMON_SHORTOPT_VERSION:
+ if ((error = print_version(stdout)) == 0) {
+ clean_up();
+ exit(0);
+ }
+ break;
+
+ case GETOPT_ERR_MISSING_ARG:
+ volume_set_error(
+ gettext("option missing a required argument: -%c"), optopt);
+ error = -1;
+ break;
+
+ case GETOPT_ERR_INVALID_OPT:
+ volume_set_error(gettext("invalid option: -%c"), optopt);
+ error = -1;
+ break;
+
+ case GETOPT_ERR_INVALID_ARG:
+ volume_set_error(gettext("invalid argument: %s"), optarg);
+ error = -1;
+ break;
+
+ default:
+ *handled = B_FALSE;
+ }
+
+ return (error);
+}
+
+/*
+ * Parse the command line options for the create subcommand.
+ *
+ * @param argc
+ * The number of arguments in the array
+ *
+ * @param argv
+ * The argument array
+ */
+static int
+parse_create_opts(
+ int argc,
+ char *argv[])
+{
+ int c;
+ int error = 0;
+
+ /*
+ * Whether a volume request is specified on the command line
+ * (vs. a inputfile)
+ */
+ boolean_t request_on_command_line = B_FALSE;
+
+ /* Examine next arg */
+ while (!error && (c = getopt_ext(
+ argc, argv, CREATE_SHORTOPTS)) != GETOPT_DONE_PARSING) {
+
+ boolean_t handled;
+
+ /* Check for args common to all scopes */
+ error = handle_common_opts(c, &handled);
+ if (error == 0 && handled == B_FALSE) {
+
+ /* Check for args specific to this scope */
+ switch (c) {
+
+ /* Help */
+ case COMMON_SHORTOPT_HELP:
+ print_help_create(stdout);
+ clean_up();
+ exit(0);
+ break;
+
+ /* Config file */
+ case CREATE_SHORTOPT_CONFIGFILE:
+ action &= ~ACTION_EXECUTE;
+ action |= ACTION_OUTPUT_CONFIG;
+ break;
+
+ /* Command file */
+ case CREATE_SHORTOPT_COMMANDFILE:
+ action &= ~ACTION_EXECUTE;
+ action |= ACTION_OUTPUT_COMMANDS;
+ break;
+
+ /* Disk set */
+ case CREATE_SHORTOPT_DISKSET:
+ error = copy_arg(
+ argv[optind - 2], optarg, &arg_diskset);
+ request_on_command_line = B_TRUE;
+ break;
+
+ /* Name */
+ case CREATE_SHORTOPT_NAME:
+ error = copy_arg(
+ argv[optind - 2], optarg, &arg_name);
+ request_on_command_line = B_TRUE;
+ break;
+
+ /* Redundancy */
+ case CREATE_SHORTOPT_REDUNDANCY:
+ error = copy_arg(
+ argv[optind - 2], optarg, &arg_redundancy);
+ request_on_command_line = B_TRUE;
+ break;
+
+ /* Data paths */
+ case CREATE_SHORTOPT_DATAPATHS:
+ error = copy_arg(
+ argv[optind - 2], optarg, &arg_datapaths);
+ request_on_command_line = B_TRUE;
+ break;
+
+ /* Fault recovery */
+ case CREATE_SHORTOPT_FAULTRECOVERY:
+ faultrecovery = B_TRUE;
+ request_on_command_line = B_TRUE;
+ break;
+
+ /* Available devices */
+ case CREATE_SHORTOPT_AVAILABLE:
+ error = parse_tokenized_list(optarg, &available);
+ request_on_command_line = B_TRUE;
+ break;
+
+ /* Unavailable devices */
+ case CREATE_SHORTOPT_UNAVAILABLE:
+ error = parse_tokenized_list(optarg, &unavailable);
+ request_on_command_line = B_TRUE;
+ break;
+
+ /* Size */
+ case CREATE_SHORTOPT_SIZE:
+ request_on_command_line = B_TRUE;
+ error = copy_arg(
+ argv[optind - 1], optarg, &arg_size);
+ break;
+
+ /* Input file */
+ case CREATE_SHORTOPT_INPUTFILE:
+ error = copy_arg(gettext("request/configuration file"),
+ optarg, &arg_inputfile);
+ break;
+
+ default:
+ /* Shouldn't be here! */
+ volume_set_error(
+ gettext("unexpected option: %c (%d)"), c, c);
+ error = -1;
+ }
+ }
+ }
+
+ /*
+ * Now that the arguments have been parsed, verify that
+ * required options were specified.
+ */
+ if (!error) {
+ /* Third invocation method -- two required arguments */
+ if (request_on_command_line == B_TRUE) {
+ if (arg_inputfile != NULL) {
+ volume_set_error(
+ gettext("invalid option(s) specified with input file"));
+ error = -1;
+ } else
+
+ if (arg_size == NULL) {
+ volume_set_error(gettext("no size specified"));
+ error = -1;
+ } else
+
+ if (arg_diskset == NULL) {
+ volume_set_error(gettext("no disk set specified"));
+ error = -1;
+ }
+ } else
+
+ /* First or second invocation method -- one required argument */
+ if (arg_inputfile == NULL) {
+ volume_set_error(gettext("missing required arguments"));
+ error = -1;
+ }
+
+ /*
+ * The CREATE_SHORTOPT_CONFIGFILE and
+ * CREATE_SHORTOPT_COMMANDFILE arguments are mutually
+ * exclusive. Verify that these were not both specified.
+ */
+ if (!error &&
+ action & ACTION_OUTPUT_CONFIG &&
+ action & ACTION_OUTPUT_COMMANDS) {
+ volume_set_error(
+ gettext("-%c and -%c are mutually exclusive"),
+ CREATE_SHORTOPT_CONFIGFILE,
+ CREATE_SHORTOPT_COMMANDFILE);
+ error = -1;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Parse the main command line options.
+ *
+ * @param argc
+ * The number of arguments in the array
+ *
+ * @param argv
+ * The argument array
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+parse_opts(
+ int argc,
+ char *argv[])
+{
+ int c;
+ int error = 0;
+
+ /* Examine next arg */
+ while (!error && (c = getopt_ext(
+ argc, argv, MAIN_SHORTOPTS)) != GETOPT_DONE_PARSING) {
+
+ boolean_t handled;
+
+ /* Check for args common to all scopes */
+ error = handle_common_opts(c, &handled);
+
+ if (error == 0 && handled == B_FALSE) {
+
+ /* Check for args specific to this scope */
+ switch (c) {
+
+ /* Help */
+ case COMMON_SHORTOPT_HELP:
+ print_help_main(stdout);
+ clean_up();
+ exit(0);
+ break;
+
+ /* Non-option arg */
+ case GETOPT_NON_OPTION_ARG:
+
+ /* See if non-option arg is subcommand */
+ if (strcmp(optarg, MAIN_SUBCMD_CREATE) == 0) {
+ subcmd = SUBCMD_CREATE;
+ error = parse_create_opts(argc, argv);
+ } else {
+ /* Argument not recognized */
+ volume_set_error(
+ gettext("%s: invalid argument"), optarg);
+ error = -1;
+ }
+ break;
+
+ default:
+ /* Shouldn't be here! */
+ volume_set_error(
+ gettext("unexpected option: %c (%d)"), c, c);
+ error = -1;
+ }
+ } else
+
+ /*
+ * Check invalid arguments to see if they are valid
+ * options out of place.
+ *
+ * NOTE: IN THE FUTURE, A CODE BLOCK SIMILAR TO THIS
+ * ONE SHOULD BE ADDED FOR EACH NEW SUBCOMMAND.
+ */
+ if (c == GETOPT_ERR_INVALID_OPT &&
+ strchr(CREATE_SHORTOPTS, optopt) != NULL) {
+ /* Provide a more enlightening error message */
+ volume_set_error(
+ gettext("-%c specified before create subcommand"), optopt);
+ }
+ }
+
+ /* Parsing appears to be successful */
+ if (!error) {
+
+ /* Was a subcommand specified? */
+ if (subcmd == SUBCMD_NONE) {
+ volume_set_error(gettext("no subcommand specified"));
+ error = -1;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Convert a string containing a comma/space-separated list into a
+ * dlist.
+ *
+ * @param string
+ * a comma/space-separated list
+ *
+ * @param list
+ * An exisiting dlist to append to, or NULL to create a
+ * new list.
+ *
+ * @return The head node of the dlist_t, whether it was newly
+ * created or passed in. On memory allocation error,
+ * errno will be set and processing will stop.
+ */
+static int
+parse_tokenized_list(
+ const char *string,
+ dlist_t **list)
+{
+ char *stringdup;
+ char *device;
+ char *dup;
+ dlist_t *item;
+ int error = 0;
+
+ /* Don't let strtok alter original argument */
+ if ((stringdup = strdup(string)) == NULL) {
+ error = ENOMEM;
+ } else {
+
+ /* For each device in the string list... */
+ while ((device = strtok(stringdup, DEVICELISTDELIM)) != NULL) {
+
+ /* Duplicate the device string */
+ if ((dup = strdup(device)) == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ /* Create new dlist_t for this device */
+ if ((item = dlist_new_item((void *)dup)) == NULL) {
+ error = ENOMEM;
+ free(dup);
+ break;
+ }
+
+ /* Append item to list */
+ *list = dlist_append(item, *list, B_TRUE);
+
+ /* strtok needs NULL pointer on subsequent calls */
+ stringdup = NULL;
+ }
+
+ free(stringdup);
+ }
+
+ return (error);
+}
+
+/*
+ * Parses the given verbosity level argument string.
+ *
+ * @param arg
+ * A string representation of a verbosity level
+ *
+ * @param verbosity
+ * RETURN: the verbosity level
+ *
+ * @return 0 if the given verbosity level string cannot
+ * be interpreted, non-zero otherwise
+ */
+static int
+parse_verbose_arg(
+ char *arg,
+ int *verbosity)
+{
+ int level;
+
+ /* Scan for int */
+ if (sscanf(arg, "%d", &level) == 1) {
+
+ /* Argument was an integer */
+ switch (level) {
+ case OUTPUT_QUIET:
+ case OUTPUT_TERSE:
+ case OUTPUT_VERBOSE:
+#ifdef DEBUG
+ case OUTPUT_DEBUG:
+#endif
+
+ *verbosity = level;
+ return (0);
+ }
+ }
+
+ volume_set_error(gettext("%s: invalid verbosity level"), arg);
+ return (-1);
+}
+
+/*
+ * Print the help message for the command.
+ *
+ * @param stream
+ * stdout or stderr, as appropriate.
+ */
+static void
+print_help_create(
+ FILE *stream)
+{
+ print_usage_create(stream);
+
+ /* BEGIN CSTYLED */
+ fprintf(stream, gettext("\
+\n\
+Create Solaris Volume Manager volumes.\n\
+\n\
+-F <inputfile>\n\
+ Specify the volume request or volume configuration file to\n\
+ process.\n\
+\n\
+-s <set>\n\
+ Specify the disk set to use when creating volumes.\n\
+\n\
+-S <size>\n\
+ Specify the size of the volume to be created.\n\
+\n\
+-a <device1,device2,...>\n\
+ Explicitly specify the devices that can be used in the\n\
+ creation of this volume.\n\
+\n\
+-c Output the command script that would implement the specified or\n\
+ generated volume configuration.\n\
+\n\
+-d Output the volume configuration that satisfies the specified or\n\
+ generated volume request.\n\
+\n\
+-f Specify whether the volume should support automatic component\n\
+ replacement after a fault.\n\
+\n\
+-n <name>\n\
+ Specify the name of the new volume.\n\
+\n\
+-p <n>\n\
+ Specify the number of required paths to the storage volume.\n\
+\n\
+-r <n>\n\
+ Specify the redundancy level (0-4) of the data.\n\
+\n\
+-u <device1,device2,...>\n\
+ Explicitly specify devices to exclude in the creation of this\n\
+ volume.\n\
+\n\
+-v <value>\n\
+ Specify the level of verbosity.\n\
+\n\
+-V Display program version information.\n\
+\n\
+-? Display help information.\n"));
+
+ /* END CSTYLED */
+
+ print_manual_reference(stream);
+}
+
+/*
+ * Print the help message for the command.
+ *
+ * @param stream
+ * stdout or stderr, as appropriate.
+ */
+static void
+print_help_main(
+ FILE *stream)
+{
+ print_usage_main(stream);
+
+ /* BEGIN CSTYLED */
+ fprintf(stream, gettext("\
+\n\
+Provide assistance, through automation, with common Solaris Volume\n\
+Manager tasks.\n\
+\n\
+-V Display program version information.\n\
+\n\
+-? Display help information. This option can follow <subcommand>\n\
+ for subcommand-specific help.\n\
+\n\
+The accepted values for <subcommand> are:\n\
+\n\
+create Create Solaris Volume Manager volumes.\n"));
+ /* END CSTYLED */
+
+ print_manual_reference(stream);
+}
+
+/*
+ * Print the help postscript for the command.
+ *
+ * @param stream
+ * stdout or stderr, as appropriate.
+ */
+static void
+print_manual_reference(
+ FILE *stream)
+{
+ fprintf(stream, gettext("\nFor more information, see %s(1M).\n"),
+ progname);
+}
+
+/*
+ * Print the program usage to the given file stream.
+ *
+ * @param stream
+ * stdout or stderr, as appropriate.
+ */
+static void
+print_usage(
+ FILE *stream)
+{
+ switch (subcmd) {
+ case SUBCMD_CREATE:
+ print_usage_create(stream);
+ break;
+
+ case SUBCMD_NONE:
+ default:
+ print_usage_main(stream);
+ }
+}
+
+/*
+ * Print the program usage to the given file stream.
+ *
+ * @param stream
+ * stdout or stderr, as appropriate.
+ */
+static void
+print_usage_create(
+ FILE *stream)
+{
+ /* Create a blank the length of progname */
+ char *blank = strdup(progname);
+ memset(blank, ' ', strlen(blank) * sizeof (char));
+
+ /* BEGIN CSTYLED */
+ fprintf(stream, gettext("\
+Usage: %1$s create [-v <n>] [-c] -F <configfile>\n\
+ %1$s create [-v <n>] [-c|-d] -F <requestfile>\n\
+ %1$s create [-v <n>] [-c|-d]\n\
+ %2$s [-f] [-n <name>] [-p <datapaths>] [-r <redundancy>]\n\
+ %2$s [-a <available>[,<available>,...]]\n\
+ %2$s [-u <unavailable>[,<unavailable>,...]]\n\
+ %2$s -s <setname> -S <size>\n\
+ %1$s create -V\n\
+ %1$s create -?\n"), progname, blank);
+ /* END CSTYLED */
+
+ free(blank);
+}
+
+/*
+ * Print the program usage to the given file stream.
+ *
+ * @param stream
+ * stdout or stderr, as appropriate.
+ */
+static void
+print_usage_main(
+ FILE *stream)
+{
+ /* BEGIN CSTYLED */
+ fprintf(stream, gettext("\
+Usage: %1$s <subcommand> [-?] [options]\n\
+ %1$s -V\n\
+ %1$s -?\n"), progname);
+ /* END CSTYLED */
+}
+
+/*
+ * Print the program version to the given file stream.
+ *
+ * @param stream
+ * stdout or stderr, as appropriate.
+ */
+static int
+print_version(
+ FILE *stream)
+{
+ int error = 0;
+ struct utsname uname_info;
+
+ if (uname(&uname_info) < 0) {
+ error = -1;
+ volume_set_error(gettext("could not determine version"));
+ } else {
+ fprintf(stream, gettext("%s %s"), progname, uname_info.version);
+ }
+
+ fprintf(stream, "\n");
+
+ return (error);
+}
+
+/*
+ * Get an xmlDocPtr by parsing the given file.
+ *
+ * @param file
+ * The file to read
+ *
+ * @param valid_types
+ * An array of the allowable root elements. If the root
+ * element of the parsed XML file is not in this list, an
+ * error is returned.
+ *
+ * @param doc
+ * RETURN: the XML document
+ *
+ * @param root
+ * RETURN: the root element of the document
+ *
+ * @return 0 if the given XML file was successfully parsed,
+ * non-zero otherwise
+ */
+static int
+get_doc_from_file(
+ char *file,
+ char **valid_types,
+ xmlDocPtr *doc,
+ char **root)
+{
+ int error = 0;
+
+ *root = NULL;
+
+ /*
+ * Create XML doc by reading the specified file using the
+ * default SAX handler (which has been modified in init_xml())
+ */
+ *doc = xmlSAXParseFile((xmlSAXHandlerPtr)
+ &xmlDefaultSAXHandler, file, 0);
+
+ if (*doc != NULL) {
+ int i;
+ xmlNodePtr root_elem = xmlDocGetRootElement(*doc);
+
+ /* Is this a valid root element? */
+ for (i = 0; valid_types[i] != NULL; i++) {
+ if (xmlStrcmp(root_elem->name,
+ (const xmlChar *)valid_types[i]) == 0) {
+ *root = valid_types[i];
+ }
+ }
+
+ /* Was a valid root element found? */
+ if (*root == NULL) {
+ xmlFreeDoc(*doc);
+ }
+ }
+
+ /* Was a valid root element found? */
+ if (*root == NULL) {
+ volume_set_error(
+ gettext("%s: invalid or malformed XML file"), file);
+ error = -1;
+ }
+
+ return (error);
+}
+
+/*
+ * Creates a volume-request or volume-config XML document, based on the
+ * arguments passed into the command.
+ *
+ * @param doc
+ * RETURN: the XML document, or NULL if no valid document
+ * could be created.
+ *
+ * @param root
+ * RETURN: the root element of the document
+ *
+ * @return 0 if a volume-request or volume-config XML document
+ * could be read or created, non-zero otherwise
+ */
+static int
+get_volume_request_or_config(
+ xmlDocPtr *doc,
+ char **root)
+{
+ int error = 0;
+
+ if (arg_inputfile == NULL) {
+ /* Create a volume-request based on quality of service */
+ *doc = create_volume_request_XML();
+
+ if (*doc == NULL) {
+ volume_set_error(gettext("error creating volume request"));
+ error = -1;
+ *root = NULL;
+ } else {
+ *root = ELEMENT_VOLUMEREQUEST;
+ }
+ } else {
+ char *valid[] = {
+ ELEMENT_VOLUMEREQUEST,
+ ELEMENT_VOLUMECONFIG,
+ NULL
+ };
+
+ error = get_doc_from_file(arg_inputfile, valid, doc, root);
+ }
+
+ return (error);
+}
+
+/*
+ * Handle processing of the given meta* commands. Commands are
+ * written to a file, the file is optionally executed, and optionally
+ * deleted.
+ *
+ * @param commands
+ * The commands to write to the command script file.
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+handle_commands(
+ char *commands)
+{
+ int error = 0;
+
+ if (action & ACTION_OUTPUT_COMMANDS) {
+ printf("%s", commands);
+ }
+
+ if (action & ACTION_EXECUTE) {
+
+ /* Write a temporary file with 744 permissions */
+ if ((error = write_temp_file(commands,
+ S_IRWXU | S_IRGRP | S_IROTH, &commandfile)) == 0) {
+
+ char *command;
+
+ /* Create command line to execute */
+ if (get_max_verbosity() >= OUTPUT_VERBOSE) {
+ /* Verbose */
+ command = stralloccat(3,
+ commandfile, " ", COMMAND_VERBOSE_FLAG);
+ } else {
+ /* Terse */
+ command = strdup(commandfile);
+ }
+
+ if (command == NULL) {
+ volume_set_error(gettext("could not allocate memory"));
+ error = -1;
+ } else {
+
+ oprintf(OUTPUT_VERBOSE,
+ gettext("Executing command script: %s\n"), command);
+
+ /* Execute command */
+ switch (error = system(command)) {
+ /* system() failed */
+ case -1:
+ error = errno;
+ break;
+
+ /* Command succeded */
+ case 0:
+ break;
+
+ /* Command failed */
+ default:
+ volume_set_error(
+ /* CSTYLED */
+ gettext("execution of command script failed with status %d"),
+ WEXITSTATUS(error));
+ error = -1;
+ }
+ free(command);
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Handle processing of the given volume-config devconfig_t. The
+ * devconfig_t is first converted to XML. Then, depending
+ * on user input to the command, the XML is either written to a file
+ * or converted to a command script and passed on to
+ * handle_commands().
+ *
+ * @param config
+ * A devconfig_t representing a valid volume-config.
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+handle_config(
+ devconfig_t *config)
+{
+ int error;
+ xmlDocPtr doc;
+
+ /* Get the xml document for the config */
+ if ((error = config_to_xml(config, &doc)) == 0) {
+
+ /* Get the text dump */
+ xmlChar *text;
+ xmlDocDumpFormatMemory(doc, &text, NULL, 1);
+
+ /* Should we output the config file? */
+ if (action & ACTION_OUTPUT_CONFIG) {
+ printf("%s", text);
+ } else {
+ oprintf(OUTPUT_DEBUG,
+ gettext("Generated volume-config:\n%s"), text);
+ }
+
+ xmlFree(text);
+
+ /* Proceed to command generation? */
+ if (action & ACTION_OUTPUT_COMMANDS ||
+ action & ACTION_EXECUTE) {
+ char *commands;
+
+ /* Get command script from the file */
+ if ((error = xml_to_commands(doc, &commands)) == 0) {
+ if (commands == NULL) {
+ volume_set_error(
+ gettext("could not convert XML to commands"));
+ error = -1;
+ } else {
+ error = handle_commands(commands);
+ free(commands);
+ }
+ }
+ }
+
+ xmlFreeDoc(doc);
+ }
+
+ return (error);
+}
+
+/*
+ * Handle processing of the given volume-request request_t and
+ * volume-defaults defaults_t. A layout is generated from these
+ * structures and the resulting volume-config devconfig_t is passed on
+ * to handle_config().
+ *
+ * @param request
+ * A request_t representing a valid volume-request.
+ *
+ * @param defaults
+ * A defaults_t representing a valid volume-defaults.
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+handle_request(
+ request_t *request,
+ defaults_t *defaults)
+{
+ int error;
+
+ /* Get layout for given request and system defaults */
+ if ((error = get_layout(request, defaults)) == 0) {
+
+ /* Retrieve resulting volume config */
+ devconfig_t *config = request_get_diskset_config(request);
+
+ if (config != NULL) {
+ error = handle_config(config);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Write the given text to a temporary file with the given
+ * permissions. If the file already exists, return an error.
+ *
+ * @param text
+ * The text to write to the file.
+ *
+ * @param mode
+ * The permissions to give the file, passed to chmod(2).
+ *
+ * @param file
+ * RETURN: The name of the file written. Must be
+ * free()d.
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+write_temp_file(
+ char *text,
+ mode_t mode,
+ char **file)
+{
+ int error = 0;
+
+ /*
+ * Create temporary file name -- "XXXXXX" is replaced with
+ * unique char sequence by mkstemp()
+ */
+ *file = stralloccat(3, "/tmp/", progname, "XXXXXX");
+
+ if (*file == NULL) {
+ volume_set_error(gettext("out of memory"));
+ error = -1;
+ } else {
+ int fildes;
+ FILE *out = NULL;
+
+ /* Open temp file */
+ if ((fildes = mkstemp(*file)) != -1) {
+ out = fdopen(fildes, "w");
+ }
+
+ if (out == NULL) {
+ volume_set_error(gettext(
+ "could not open file for writing: %s"), *file);
+ error = -1;
+ } else {
+
+ fprintf(out, "%s", text);
+ fclose(out);
+
+ if (mode != 0) {
+ if (chmod(*file, mode)) {
+ volume_set_error(
+ gettext("could not change permissions of file: %s"),
+ *file);
+ error = -1;
+ }
+ }
+
+ /* Remove file on error */
+ if (error != 0) {
+ unlink(*file);
+ }
+ }
+
+ /* Free *file on error */
+ if (error != 0) {
+ free(*file);
+ *file = NULL;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Main entry to metassist. See the print_usage_* functions* for
+ * usage.
+ *
+ * @return 0 on successful exit, non-zero otherwise
+ */
+int
+main(
+ int argc,
+ char *argv[])
+{
+ int error = 0;
+ int printusage = 0;
+
+#ifdef DEBUG
+ time_t start = time(NULL);
+#endif
+
+ /*
+ * Get the locale set up before calling any other routines
+ * with messages to ouput. Just in case we're not in a build
+ * environment, make sure that TEXT_DOMAIN gets set to
+ * something.
+ */
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ /* Set program name, strip directory */
+ if ((progname = strrchr(argv[0], '/')) != NULL) {
+ progname++;
+ } else {
+ progname = argv[0];
+ }
+
+ /* Set up signal handlers to exit gracefully */
+ {
+ struct sigaction act;
+ act.sa_handler = interrupthandler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction(SIGHUP, &act, (struct sigaction *)0);
+ sigaction(SIGINT, &act, (struct sigaction *)0);
+ sigaction(SIGQUIT, &act, (struct sigaction *)0);
+ sigaction(SIGTERM, &act, (struct sigaction *)0);
+ }
+
+ /* Set default verbosity level */
+ set_max_verbosity(OUTPUT_TERSE, stderr);
+
+ /* Verify we're running as root */
+ if (geteuid() != 0) {
+ volume_set_error(gettext("must be run as root"));
+ error = -1;
+ } else {
+
+ /* Disable error messages from getopt */
+ opterr = 0;
+
+ /* Parse command-line options */
+ if ((error = parse_opts(argc, argv)) == 0) {
+ xmlDocPtr doc;
+ char *root;
+
+ /* Initialize XML defaults */
+ init_xml();
+
+ /* Read volume-request/config file */
+ if ((error = get_volume_request_or_config(&doc, &root)) == 0) {
+
+ /* Is this a volume-config? */
+ if (strcmp(root, ELEMENT_VOLUMECONFIG) == 0) {
+
+ /* Was the -d flag specified? */
+ if (action & ACTION_OUTPUT_CONFIG) {
+ /* -d cannot be used with -F <configfile> */
+ volume_set_error(gettext(
+ "-%c incompatible with -%c <configfile>"),
+ CREATE_SHORTOPT_CONFIGFILE,
+ CREATE_SHORTOPT_INPUTFILE);
+ error = -1;
+ printusage = 1;
+ } else {
+ devconfig_t *config;
+ if ((error = xml_to_config(doc, &config)) == 0) {
+ error = handle_config(config);
+ free_devconfig(config);
+ }
+ }
+ } else
+
+ /* Is this a volume-request? */
+ if (strcmp(root, ELEMENT_VOLUMEREQUEST) == 0) {
+ request_t *request;
+
+ if ((error = xml_to_request(doc, &request)) == 0) {
+
+ xmlDocPtr defaults_doc;
+ char *valid[] = {
+ ELEMENT_VOLUMEDEFAULTS,
+ NULL
+ };
+
+ /* Read defaults file */
+ if ((error = get_doc_from_file(VOLUME_DEFAULTS_LOC,
+ valid, &defaults_doc, &root)) == 0) {
+
+ defaults_t *defaults;
+
+ oprintf(OUTPUT_DEBUG,
+ gettext("Using defaults file: %s\n"),
+ VOLUME_DEFAULTS_LOC);
+
+ /* Parse defaults XML */
+ if ((error = xml_to_defaults(
+ defaults_doc, &defaults)) == 0) {
+ error = handle_request(request, defaults);
+ free_defaults(defaults);
+ }
+
+ xmlFreeDoc(defaults_doc);
+ }
+
+ free_request(request);
+ }
+ }
+
+ xmlFreeDoc(doc);
+ }
+ } else {
+ printusage = 1;
+ }
+ }
+
+ /* Handle any errors that were propogated */
+ if (error != 0) {
+ char *message = get_error_string(error);
+
+ if (message != NULL && strlen(message)) {
+ fprintf(stderr, "%s: %s\n", progname, message);
+
+ if (printusage) {
+ fprintf(stderr, "\n");
+ }
+ }
+
+ if (printusage) {
+ print_usage(stderr);
+ }
+ }
+
+#ifdef DEBUG
+ /* Print run report to stderr if METASSIST_DEBUG is set */
+ if (getenv(METASSIST_DEBUG_ENV) != NULL) {
+ time_t end = time(NULL);
+ struct tm *time;
+ int i;
+#define TIMEFMT "%8s: %.2d:%.2d:%.2d\n"
+
+ fprintf(stderr, " Command:");
+ for (i = 0; i < argc; i++) {
+ fprintf(stderr, " %s", argv[i]);
+ }
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, " Version: ");
+ print_version(stderr);
+
+ time = localtime(&start);
+ fprintf(stderr, TIMEFMT, "Start",
+ time->tm_hour, time->tm_min, time->tm_sec);
+
+ time = localtime(&end);
+ fprintf(stderr, TIMEFMT, "End",
+ time->tm_hour, time->tm_min, time->tm_sec);
+
+ end -= start;
+ time = gmtime(&end);
+ fprintf(stderr, TIMEFMT, "Duration",
+ time->tm_hour, time->tm_min, time->tm_sec);
+ }
+#endif
+
+ clean_up();
+
+ return (error != 0);
+}
diff --git a/usr/src/cmd/lvm/metassist/controller/metassist.h b/usr/src/cmd/lvm/metassist/controller/metassist.h
new file mode 100644
index 0000000000..d107c093bb
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/controller/metassist.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#ifndef _METASSIST_H
+#define _METASSIST_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Location of the volume-defaults.xml file */
+#define VOLUME_DEFAULTS_LOC "/etc/default/metassist.xml"
+
+/* Available/unavailable device list delimiters */
+#define DEVICELISTDELIM ", "
+
+/* Command-line arguments */
+#define COMMON_SHORTOPT_HELP '?'
+#define COMMON_SHORTOPT_VERBOSITY 'v'
+#define COMMON_SHORTOPT_VERSION 'V'
+#define CREATE_SHORTOPT_AVAILABLE 'a'
+#define CREATE_SHORTOPT_COMMANDFILE 'c'
+#define CREATE_SHORTOPT_DATAPATHS 'p'
+#define CREATE_SHORTOPT_DISKSET 's'
+#define CREATE_SHORTOPT_FAULTRECOVERY 'f'
+#define CREATE_SHORTOPT_INPUTFILE 'F'
+#define CREATE_SHORTOPT_NAME 'n'
+#define CREATE_SHORTOPT_REDUNDANCY 'r'
+#define CREATE_SHORTOPT_SIZE 'S'
+#define CREATE_SHORTOPT_CONFIGFILE 'd'
+#define CREATE_SHORTOPT_UNAVAILABLE 'u'
+#define CREATE_SHORTOPTS "a:cdfF:n:p:r:s:S:u:v:V?"
+#define MAIN_SHORTOPTS "-v:V?"
+#define MAIN_SUBCMD_CREATE "create"
+
+#define SUBCMD_NONE 0
+#define SUBCMD_CREATE 1
+
+/* Command action masks */
+#define ACTION_EXECUTE 1
+#define ACTION_OUTPUT_CONFIG 2
+#define ACTION_OUTPUT_COMMANDS 4
+
+/* Verbose flag sent to generated shell script */
+#define COMMAND_VERBOSE_FLAG "-v"
+
+/* The name used to invoke the command */
+extern char *progname;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _METASSIST_H */
diff --git a/usr/src/cmd/lvm/metassist/layout/Makefile b/usr/src/cmd/lvm/metassist/layout/Makefile
new file mode 100644
index 0000000000..6f02f010f8
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/Makefile
@@ -0,0 +1,64 @@
+#
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+METASSIST_TOPLEVEL = ..
+
+SRCS= \
+ layout.c \
+ layout_concat.c \
+ layout_device_cache.c \
+ layout_device_util.c \
+ layout_discovery.c \
+ layout_dlist_util.c \
+ layout_hsp.c \
+ layout_messages.c \
+ layout_mirror.c \
+ layout_request.c \
+ layout_slice.c \
+ layout_stripe.c \
+ layout_svm_util.c \
+ layout_validate.c
+
+OBJS = $(SRCS:%.c=%.o)
+HDRS = $(SRCS:%.c=%.h)
+MSGFILES = $(SRCS:%.c=%.i)
+
+include $(METASSIST_TOPLEVEL)/../../Makefile.cmd
+include $(METASSIST_TOPLEVEL)/Makefile.env
+
+INCLUDES += -I../common -I../controller -I../../../../lib/libdiskmgt/common \
+ -I../../../../lib/lvm/libmeta/common/hdrs
+CFLAGS += $(INCLUDES)
+
+POFILE = layoutp.po
+
+include $(METASSIST_TOPLEVEL)/Makefile.targ
+
+# Build .po file from message files
+$(POFILE): $(MSGFILES)
+ $(BUILDPO.msgfiles)
diff --git a/usr/src/cmd/lvm/metassist/layout/layout.c b/usr/src/cmd/lvm/metassist/layout/layout.c
new file mode 100644
index 0000000000..3a432dedad
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout.c
@@ -0,0 +1,1142 @@
+/*
+ * 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 "%Z%%M% %I% %E% SMI"
+
+#include <assert.h>
+#include <string.h>
+#include <libintl.h>
+
+#include "volume_error.h"
+#include "volume_defaults.h"
+#include "volume_dlist.h"
+#include "volume_output.h"
+#include "volume_request.h"
+
+#include "layout.h"
+#include "layout_request.h"
+
+#include "layout_concat.h"
+#include "layout_discovery.h"
+#include "layout_device_cache.h"
+#include "layout_device_util.h"
+#include "layout_dlist_util.h"
+#include "layout_hsp.h"
+#include "layout_mirror.h"
+#include "layout_slice.h"
+#include "layout_stripe.h"
+#include "layout_svm_util.h"
+#include "layout_validate.h"
+
+#define _LAYOUT_C
+
+static int layout_init(devconfig_t *request, defaults_t *defaults);
+static int layout_diskset(request_t *request, dlist_t *results);
+
+static int process_request(devconfig_t *request, dlist_t **results);
+static int process_qos_request(devconfig_t *request, dlist_t **results);
+static int process_hsp_request(devconfig_t *request, dlist_t **results);
+
+/*
+ * stuff for making/updating the HSP to service devices
+ * created by the toplevel request
+ */
+static devconfig_t *_hsp_request = NULL;
+static dlist_t *_hsp_devices = NULL;
+static void set_hsp_request(devconfig_t *request);
+static void unset_hsp_request();
+
+/*
+ * struct to track which disks have been explicitly modified
+ * during the layout process...
+ *
+ * disk is the dm_descriptor_t of the modified disk
+ * accessname is the name to access the disk thru
+ * slices is the list of modified slices on the disk
+ */
+typedef struct {
+ dm_descriptor_t disk;
+ char *accessname;
+ dlist_t *slices;
+} moddisk_t;
+
+/*
+ * modified_disks is a list of moddisk_t structs
+ * tracking disks have been modified during layout.
+ */
+static dlist_t *_modified_disks = NULL;
+
+static int collect_modified_disks(devconfig_t *request, dlist_t *results);
+static int add_modified_disks_to_diskset(
+ dlist_t *devices,
+ devconfig_t *diskset);
+static int release_modified_disks();
+static int get_removed_slices_for_disks(
+ dlist_t *mod_disks);
+static int get_modified_slices_for_disks(
+ dlist_t *moddisks);
+static int compare_disk_to_moddisk_disk(
+ void *disk,
+ void *moddisk);
+
+static int convert_device_names(devconfig_t *request, dlist_t *devs);
+
+/*
+ * FUNCTION: get_layout(devconfig_t *request, defaults_t *defaults)
+ *
+ * INPUT: request - a devconfig_t pointer to the toplevel request
+ * defaults - a results_t pointer to the defaults
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: Public entry point to layout module.
+ */
+int
+get_layout(
+ request_t *request,
+ defaults_t *defaults)
+{
+ devconfig_t *diskset_req = NULL;
+ dlist_t *iter = NULL;
+ dlist_t *results = NULL;
+ int error = 0;
+
+ if ((diskset_req = request_get_diskset_req(request)) != NULL) {
+
+ /* initialize using the the top-level disk set request... */
+ if ((error = layout_init(diskset_req, defaults)) != 0) {
+ return (error);
+ }
+
+ oprintf(OUTPUT_TERSE,
+ gettext("\nProcessing volume request...\n"));
+
+ iter = devconfig_get_components(diskset_req);
+ for (; (iter != NULL) && (error == 0); iter = iter->next) {
+
+ /* process each volume request, stop on any error */
+ devconfig_t *subreq = (devconfig_t *)iter->obj;
+ dlist_t *subres = NULL;
+
+ ((error = process_request(subreq, &subres)) != 0) ||
+ (error = collect_modified_disks(subreq, subres)) ||
+ (error = convert_device_names(subreq, subres));
+ if (error == 0) {
+ results = dlist_append(subres, results, AT_TAIL);
+ }
+ }
+
+ if (error == 0) {
+ /* process HSP request */
+ dlist_t *subres = NULL;
+ error = process_hsp_request(diskset_req, &subres);
+ if (error == 0) {
+ results = dlist_append(subres, results, AT_TAIL);
+ }
+ }
+
+ if (error == 0) {
+ oprintf(OUTPUT_TERSE,
+ gettext("\nAssembling volume specification...\n"));
+ /* determine required diskset modifications */
+ error = layout_diskset(request, results);
+ }
+
+ layout_clean_up();
+
+ if (error == 0) {
+ oprintf(OUTPUT_TERSE,
+ gettext("\nVolume request completed successfully.\n"));
+ }
+
+ } else {
+ volume_set_error(
+ gettext("Malformed request, missing top level "
+ "disk set request."));
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: layout_clean_up()
+ *
+ * PURPOSE: function which handles the details of cleaning up cached
+ * data and any other memory allocated during the layout
+ * process.
+ *
+ * release physical device data structs
+ * release SVM logical device data structs
+ * release validation data structs
+ * release modified device data structs
+ * release request processing data structs
+ *
+ * This function is also exported as part of the public
+ * interface to the layout module, clients of layout
+ * are required to call this function if get_layout()
+ * was called and was not allowed to return. For example,
+ * if SIGINT was received while a layout request was in
+ * process.
+ */
+void
+layout_clean_up()
+{
+ (void) release_request_caches();
+ (void) release_validation_caches();
+
+ (void) release_slices_to_remove();
+ (void) release_modified_slices();
+ (void) release_modified_disks();
+
+ (void) release_reserved_slices();
+ (void) release_used_slices();
+
+ (void) release_usable_devices();
+ (void) release_svm_names(get_request_diskset());
+ (void) release_known_devices();
+
+ (void) unset_hsp_request(NULL);
+ (void) unset_request_defaults(NULL);
+ (void) unset_request_diskset(NULL);
+ (void) unset_toplevel_request(NULL);
+}
+
+/*
+ * FUNCTION: layout_init(devconfig_t *diskset, defaults_t *defaults)
+ *
+ * INPUT: diskset - a devconfig_t pointer to the toplevel request
+ * defaults - a results_t pointer to the defaults
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: function which handles the details of initializing the layout
+ * module prior to processing a request.
+ *
+ * Determines the requested disk set and validates it.
+ *
+ * Scans the physical device configuration.
+ * Scans the SVM logical device configuration.
+ *
+ * Initializes layout private global data structures and does
+ * semantic validation of the request.
+ */
+static int
+layout_init(
+ devconfig_t *diskset,
+ defaults_t *defaults)
+{
+ dlist_t *iter = NULL;
+ int error = 0;
+ char *dsname = NULL;
+
+ ((error = validate_basic_svm_config()) != 0) ||
+
+ /* determine & validate requested disk set name */
+ (error = devconfig_get_name(diskset, &dsname)) ||
+ (error = set_request_diskset(dsname)) ||
+
+ /* discover known physical and logical devices */
+ (error = discover_known_devices()) ||
+ (error = scan_svm_names(dsname)) ||
+
+ /* validate and remember toplevel request */
+ (error = set_toplevel_request(diskset)) ||
+
+ /* validate and remember defaults for this request */
+ (error = set_request_defaults(defaults));
+
+ if (error != 0) {
+ return (error);
+ }
+
+ oprintf(OUTPUT_TERSE,
+ gettext("\nValidating volume request...\n"));
+
+ iter = devconfig_get_components(diskset);
+ for (; (iter != NULL) && (error == 0); iter = iter->next) {
+ devconfig_t *subreq = (devconfig_t *)iter->obj;
+ error = validate_request(subreq);
+ }
+
+ if (error == 0) {
+ error = discover_usable_devices(dsname);
+ }
+
+ if (error == 0) {
+ /* final validation on explicitly requested components */
+ error = validate_reserved_slices();
+ }
+
+ if (error == 0) {
+ /* final validation on request sizes vs. actual avail space */
+ error = validate_request_sizes(diskset);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: process_request(devconfig_t *req, dlist_t **results)
+ *
+ * INPUT: req - a devconfig_t pointer to the current request
+ * results - pointer to a list of resulting volumes
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: function which handles the details of an explicit
+ * volume request.
+ *
+ * Determines the requested volume type, whether the
+ * request contains specific subcomponents and dispatches
+ * to the appropriate layout function for that type.
+ *
+ * Resulting volumes are appended to the results list.
+ *
+ * Note that an HSP request is held until all the volumes
+ * in the request have been successfully composed. This
+ * ensures that HSP spare sizing can be appropriate to
+ * those volumes.
+ */
+static int
+process_request(
+ devconfig_t *req,
+ dlist_t **results)
+{
+ component_type_t type = TYPE_UNKNOWN;
+ uint64_t nbytes = 0; /* requested volume size */
+ dlist_t *comps = NULL;
+ int ncomps = 0;
+ int error = 0;
+
+ (void) devconfig_get_type(req, &type);
+ (void) devconfig_get_size(req, &nbytes);
+ comps = devconfig_get_components(req);
+
+ if (type == TYPE_HSP) {
+ /* HSP processing needs to happen after all other volumes. */
+ /* set the HSP request aside until all other requests have */
+ /* been completed successfully */
+ set_hsp_request(req);
+ return (0);
+ }
+
+ oprintf(OUTPUT_TERSE, "\n");
+ oprintf(OUTPUT_VERBOSE, "******************\n");
+
+ ncomps = dlist_length(comps);
+
+ if (type == TYPE_STRIPE) {
+ if (ncomps > 0) {
+ return (populate_explicit_stripe(req, results));
+ } else {
+ return (layout_stripe(req, nbytes, results));
+ }
+ }
+
+ if (type == TYPE_CONCAT) {
+ if (ncomps > 0) {
+ return (populate_explicit_concat(req, results));
+ } else {
+ return (layout_concat(req, nbytes, results));
+ }
+ }
+
+ if (type == TYPE_MIRROR) {
+ if (ncomps > 0) {
+ return (populate_explicit_mirror(req, results));
+ } else {
+ uint16_t nsubs = 0;
+ if ((error = get_mirror_nsubs(req, &nsubs)) != 0) {
+ return (error);
+ } else {
+ return (layout_mirror(req, nsubs, nbytes, results));
+ }
+ }
+ }
+
+ if (type == TYPE_VOLUME) {
+ error = process_qos_request(req, results);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: process_qos_request(devconfig_t *req, dlist_t **results)
+ *
+ * INPUT: req - a devconfig_t pointer to the current request
+ * results - pointer to a list of resulting volumes
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: function which handles the details of mapping an implicit
+ * volume request of QoS attributes into a volume type.
+ *
+ * Resulting volumes are appended to the results list.
+ */
+static int
+process_qos_request(
+ devconfig_t *req,
+ dlist_t **results)
+{
+ int error = 0;
+
+ uint64_t nbytes = 0;
+ uint16_t rlevel = 0;
+
+ /* get QoS attributes */
+ (void) devconfig_get_size(req, &nbytes);
+
+ if ((error = get_volume_redundancy_level(req, &rlevel)) != 0) {
+ if (error == ERR_ATTR_UNSET) {
+ error = 0;
+ rlevel = 0;
+ }
+ }
+
+ if (error == 0) {
+ if (rlevel == 0) {
+ error = layout_stripe(req, nbytes, results);
+ } else {
+ error = layout_mirror(req, rlevel, nbytes, results);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: layout_diskset(request_t *req, dlist_t **results)
+ *
+ * INPUT: req - a request_t pointer to the toplevel request
+ * results - pointer to the list of composed result volumes
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: function which handles the details of completing an layout
+ * request.
+ *
+ * Determines if the disk set specified in the request currently
+ * exists and sets it up for creation if it doesn't.
+ *
+ * Adds new disks required by the result volumes to the disk set.
+ *
+ * Attaches the result volumes to the disk set result.
+ *
+ * Convert slice and disk names to preferred names.
+ *
+ * Attaches the disk set result to the toplevel request.
+ */
+static int
+layout_diskset(
+ request_t *request,
+ dlist_t *results)
+{
+ int error = 0;
+ devconfig_t *diskset = NULL;
+ dlist_t *comps = NULL;
+
+ ((error = new_devconfig(&diskset, TYPE_DISKSET)) != 0) ||
+ (error = devconfig_set_name(diskset, get_request_diskset())) ||
+ (error = add_modified_disks_to_diskset(results, diskset));
+ if (error != 0) {
+ free_devconfig(diskset);
+ return (error);
+ }
+
+ /* add resulting volumes */
+ if (results != NULL) {
+ comps = devconfig_get_components(diskset);
+ comps = dlist_append(results, comps, AT_TAIL);
+ devconfig_set_components(diskset, comps);
+ }
+
+ request_set_diskset_config(request, diskset);
+
+ return (error);
+}
+
+/*
+ * FUNCTION: convert_device_names(devconfig_t request, dlist_t *devices)
+ *
+ * INPUT: request - a devconfig_t request pointer
+ * devices - a list of devconfig_t devices
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - on any error
+ *
+ * PURPOSE: Utility function to convert any slice or disk drive
+ * names in a result devconfig_t to the preferred name
+ * which should be used to access the device.
+ *
+ * This convert the temporary names used by layout to
+ * the proper DID or /dev/dsk alias.
+ */
+static int
+convert_device_names(
+ devconfig_t *request,
+ dlist_t *devices)
+{
+ int error = 0;
+ dlist_t *iter;
+
+ for (iter = devices;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ devconfig_t *dev = (devconfig_t *)iter->obj;
+ component_type_t type = TYPE_UNKNOWN;
+ dm_descriptor_t disk = (dm_descriptor_t)0;
+ char *devname = NULL;
+ char *diskname = NULL;
+ char *slicename = NULL;
+ uint16_t index;
+
+ if ((error = devconfig_get_type(dev, &type)) == 0) {
+ switch (type) {
+
+ case TYPE_MIRROR:
+ case TYPE_STRIPE:
+ case TYPE_CONCAT:
+ case TYPE_HSP:
+
+ error = convert_device_names(request,
+ devconfig_get_components(dev));
+
+ break;
+
+ case TYPE_SLICE:
+
+ ((error = devconfig_get_name(dev, &devname)) != 0) ||
+ (error = devconfig_get_slice_index(dev, &index)) ||
+ (error = get_disk_for_named_slice(devname, &disk)) ||
+ (error = get_device_access_name(request, disk,
+ &diskname)) ||
+ (error = make_slicename_for_diskname_and_index(
+ diskname, index, &slicename));
+
+ if ((error == 0) && (slicename != NULL)) {
+ error = devconfig_set_name(dev, slicename);
+ free(slicename);
+ }
+
+ break;
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: add_modified_disk(devconfig_t request, dm_descriptor_t disk);
+ *
+ * INPUT: request - a pointr to a devconfig_t request
+ * disk - dm_descriptor_t handle for a disk that has been modified
+ *
+ * SIDEEFFECTS: adds an entry to the _modified_disks list which tracks
+ * the disks that have been explicitly modified by
+ * the layout code.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Adds the input disk to the list of those that have been
+ * modified.
+ *
+ * Disks are modified during layout for two reasons:
+ *
+ * 1. any disk that is to be added to the disk set gets
+ * an explicitly updated label.
+ *
+ * 2. once a disk is in the disk set, existing slices
+ * may be resized or new slices can be added.
+ */
+int
+add_modified_disk(
+ devconfig_t *request,
+ dm_descriptor_t disk)
+{
+ dlist_t *iter = NULL;
+ moddisk_t *moddisk = NULL;
+ dlist_t *item = NULL;
+ int error = 0;
+
+ for (iter = _modified_disks; iter != NULL; iter = iter->next) {
+ moddisk = (moddisk_t *)iter->obj;
+ if (compare_descriptor_names(
+ (void *)moddisk->disk, (void *)disk) == 0) {
+ /* already in list */
+ return (0);
+ }
+ }
+
+ moddisk = (moddisk_t *)calloc(1, sizeof (moddisk_t));
+ if (moddisk == NULL) {
+ error = ENOMEM;
+ } else {
+ char *aname = NULL;
+ error = get_device_access_name(request, disk, &aname);
+ if (error == 0) {
+
+ /* add to list of modified disks */
+ moddisk->disk = disk;
+ moddisk->accessname = aname;
+ moddisk->slices = NULL;
+
+ if ((item = dlist_new_item((void *)moddisk)) == NULL) {
+ free(moddisk);
+ error = ENOMEM;
+ } else {
+ _modified_disks =
+ dlist_append(item, _modified_disks, AT_HEAD);
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: collect_modified_disks(devconfig_t *request, dlist_t* devs)
+ *
+ * INPUT: devs - pointer to a list of composed volumes
+ * OUTPUT: none -
+ * SIDEEFFECT: updates the module global list _modified_disks
+ *
+ * RETURNS: int - 0 - success
+ * !0 - failure
+ *
+ * PURPOSE: Helper to maintain the list of disks to be added to the
+ * disk set.
+ *
+ * Iterates the input list of devices and determines which
+ * disks they use. If a disk is not in the _modified_disks
+ * list, it is added.
+ */
+static int
+collect_modified_disks(
+ devconfig_t *request,
+ dlist_t *devs)
+{
+ int error = 0;
+
+ char *sname = NULL;
+ dm_descriptor_t disk = (dm_descriptor_t)0;
+
+ for (; (devs != NULL) && (error == 0); devs = devs->next) {
+
+ devconfig_t *dev = (devconfig_t *)devs->obj;
+ component_type_t type = TYPE_UNKNOWN;
+
+ if ((error = devconfig_get_type(dev, &type)) == 0) {
+
+ switch (type) {
+ case TYPE_MIRROR:
+ case TYPE_STRIPE:
+ case TYPE_CONCAT:
+ case TYPE_HSP:
+
+ error = collect_modified_disks(request,
+ devconfig_get_components(dev));
+ break;
+
+ case TYPE_SLICE:
+
+ ((error = devconfig_get_name(dev, &sname)) != 0) ||
+ (error = get_disk_for_named_slice(sname, &disk)) ||
+ (error = add_modified_disk(request, disk));
+
+ break;
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: add_modified_disks_to_diskset(dlist_t *devices,
+ * devconfig_t *diskset)
+ *
+ * INPUT: devices - pointer to a list of devices
+ *
+ * OUTPUT: diskset - pointer to a devconfig_t representing the disk set,
+ * updated to include modified disks and slices as
+ * components.
+ *
+ * RETURNS: int - 0 - success
+ * !0 - failure
+ *
+ * PURPOSE: Helper to add devconfig_t structs for disks and slices
+ * to the disk set.
+ *
+ * Updates the list of _modified_disks by examining the input
+ * list of composed devices.
+ *
+ * Iterates _modified_disks and creates a devconfig_t component
+ * for each disk in the list, the list of disks is then attached
+ * to the input disk set.
+ *
+ * Modified slices for disks in the disk set are added as well.
+ */
+static int
+add_modified_disks_to_diskset(
+ dlist_t *results,
+ devconfig_t *diskset)
+{
+ int error = 0;
+
+ dlist_t *iter;
+ dlist_t *list = NULL;
+ char *dsname = get_request_diskset();
+
+ /* add modified disks to disk set's component list */
+ list = devconfig_get_components(diskset);
+
+ oprintf(OUTPUT_TERSE,
+ gettext(" Collecting modified disks...\n"));
+
+ /* collect removed slices for modified disks */
+ error = get_removed_slices_for_disks(_modified_disks);
+
+ /* collect modified slices for modified disks */
+ error = get_modified_slices_for_disks(_modified_disks);
+
+ for (iter = _modified_disks;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ moddisk_t *moddisk = (moddisk_t *)iter->obj;
+ dm_descriptor_t disk = moddisk->disk;
+ devconfig_t *newdisk = NULL;
+ boolean_t in_set = B_FALSE;
+
+ oprintf(OUTPUT_VERBOSE, " %s\n", moddisk->accessname);
+
+ error = is_disk_in_diskset(disk, dsname, &in_set);
+ if ((error == 0) && (in_set != B_TRUE)) {
+ /* New disk, add it to the disk set */
+ ((error = new_devconfig(&newdisk, TYPE_DRIVE)) != 0) ||
+ (error = devconfig_set_name(newdisk, moddisk->accessname));
+ if (error == 0) {
+ dlist_t *item = dlist_new_item(newdisk);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ list = dlist_append(item, list, AT_TAIL);
+ oprintf(OUTPUT_DEBUG,
+ gettext(" must add %s to disk set \"%s\"\n"),
+ moddisk->accessname, dsname);
+ }
+ } else {
+ free_devconfig(newdisk);
+ }
+ }
+
+ if ((error == 0) && (moddisk->slices != NULL)) {
+ /* move moddisk's slice list to disk set comp list */
+ list = dlist_append(moddisk->slices, list, AT_TAIL);
+ moddisk->slices = NULL;
+ }
+ }
+
+ if (error == 0) {
+ devconfig_set_components(diskset, list);
+ } else {
+ dlist_free_items(list, NULL);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTIONS: void release_modified_disks()
+ *
+ * INPUT: none -
+ * OUTPUT: none -
+ *
+ * PURPOSE: cleanup the module global list of disks that need
+ * to be added to the disk set to satisfy the request.
+ */
+static int
+release_modified_disks()
+{
+ dlist_t *iter = _modified_disks;
+
+ for (; iter != NULL; iter = iter->next) {
+ moddisk_t *moddisk = (moddisk_t *)iter->obj;
+ if (moddisk->slices != NULL) {
+ dlist_free_items(moddisk->slices, free_devconfig);
+ moddisk->slices = NULL;
+ }
+ free(moddisk);
+ iter->obj = NULL;
+ }
+
+ dlist_free_items(_modified_disks, NULL);
+ _modified_disks = NULL;
+
+ return (0);
+}
+
+/*
+ * FUNCTION: get_removed_slices_for_disks(dlist_t *mod_disks)
+ *
+ * INPUT: mod_disks - a list of moddisk_t structs
+ *
+ * OUTPUT: mod_disks - the list of moddisk_t structs updated with
+ * the slices to be removed for each disk
+ *
+ * RETURNS: int - 0 - success
+ * !0 - failure
+ *
+ * PURPOSE: Helper to create a list of devconfig_t structs
+ * for slices on the input disks which need to be
+ * removed from the system.
+ *
+ * Iterates the list of slices to be removed and
+ * creates a devconfig_t component for each slice
+ * in the list that is on any of the input modified
+ * disks.
+ *
+ * Slice names are constructed using the modified disk's
+ * access name to ensure that the correct alias is
+ * used to get to the slice.
+ */
+static int
+get_removed_slices_for_disks(
+ dlist_t *mod_disks)
+{
+ int error = 0;
+ dlist_t *iter = NULL;
+
+ /* collect slices to be removed for the modified disks */
+ for (iter = get_slices_to_remove();
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ rmvdslice_t *rmvd = (rmvdslice_t *)iter->obj;
+ dm_descriptor_t disk = (dm_descriptor_t)0;
+ moddisk_t *moddisk = NULL;
+ char *sname = NULL;
+ devconfig_t *newslice = NULL;
+ dlist_t *item = NULL;
+
+ (void) get_disk_for_named_slice(rmvd->slice_name, &disk);
+
+ if ((item = dlist_find(mod_disks, (void *)disk,
+ compare_disk_to_moddisk_disk)) == NULL) {
+ /* slice on disk that we don't care about */
+ continue;
+ }
+
+ moddisk = (moddisk_t *)item->obj;
+
+ /* create output slice struct for the removed slice */
+ ((error = make_slicename_for_diskname_and_index(
+ moddisk->accessname, rmvd->slice_index, &sname)) != 0) ||
+ (error = new_devconfig(&newslice, TYPE_SLICE)) ||
+ (error = devconfig_set_name(newslice, sname)) ||
+ (error = devconfig_set_size_in_blocks(newslice, 0));
+
+ /* add to the moddisk's list of slices */
+ if (error == 0) {
+ if ((item = dlist_new_item(newslice)) == NULL) {
+ free_devconfig(newslice);
+ error = ENOMEM;
+ } else {
+ moddisk->slices =
+ dlist_append(item, moddisk->slices, AT_TAIL);
+ }
+ } else {
+ free_devconfig(newslice);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_modified_slices_for_disks(dlist_t *mod_disks)
+ *
+ * INPUT: mod_disks - a list of moddisk_t structs
+ *
+ * OUTPUT: mod_disks - the list of moddisk_t structs updated with
+ * the modified slices for each disk
+ *
+ * RETURNS: int - 0 - success
+ * !0 - failure
+ *
+ * PURPOSE: Helper to create a list of devconfig_t structs
+ * for slices on the input disks which have been
+ * modified for use by layout.
+ *
+ * Iterates the list of modified slices and creates a
+ * devconfig_t component for each slice in the list
+ * that is on any of the input modified disks.
+ *
+ * Slice names are constructed using the modified disk's
+ * access name to ensure that the correct alias is
+ * used to get to the slice.
+ */
+int
+get_modified_slices_for_disks(
+ dlist_t *mod_disks)
+{
+ int error = 0;
+ dlist_t *iter = NULL;
+
+ for (iter = get_modified_slices();
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ modslice_t *mods = (modslice_t *)iter->obj;
+ devconfig_t *slice = mods->slice_devcfg;
+ devconfig_t *newslice = NULL;
+ dm_descriptor_t disk;
+ moddisk_t *moddisk;
+ dlist_t *item;
+ char *sname = NULL;
+ uint64_t stblk = 0;
+ uint64_t nblks = 0;
+ uint16_t index;
+
+ /* only add modified slices that were sources */
+ if ((mods->times_modified == 0) ||
+ (mods->src_slice_desc != (dm_descriptor_t)0)) {
+ continue;
+ }
+
+ (void) devconfig_get_name(slice, &sname);
+ (void) get_disk_for_named_slice(sname, &disk);
+
+ if ((item = dlist_find(mod_disks, (void *)disk,
+ compare_disk_to_moddisk_disk)) == NULL) {
+ /* slice on disk that we don't care about */
+ continue;
+ }
+
+ moddisk = (moddisk_t *)item->obj;
+
+ /* create output slice struct for the modified slice */
+ ((error = devconfig_get_slice_start_block(slice,
+ &stblk)) != 0) ||
+ (error = devconfig_get_size_in_blocks(slice, &nblks)) ||
+ (error = devconfig_get_slice_index(slice, &index)) ||
+ (error = make_slicename_for_diskname_and_index(
+ moddisk->accessname, index, &sname)) ||
+ (error = new_devconfig(&newslice, TYPE_SLICE)) ||
+ (error = devconfig_set_name(newslice, sname)) ||
+ (error = devconfig_set_slice_start_block(newslice, stblk)) ||
+ (error = devconfig_set_size_in_blocks(newslice, nblks));
+
+ /* add to the moddisk's list of slices */
+ if (error == 0) {
+ if ((item = dlist_new_item(newslice)) == NULL) {
+ free_devconfig(newslice);
+ error = ENOMEM;
+ } else {
+ moddisk->slices =
+ dlist_append(item, moddisk->slices, AT_TAIL);
+ }
+ } else {
+ free_devconfig(newslice);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: compare_disk_to_moddisk_disk(void *disk, void *moddisk)
+ *
+ * INPUT: disk - opaque pointer to a dm_descriptor_t
+ * moddisk - opaque moddisk_t pointer
+ *
+ * RETURNS: int - 0 - if disk == moddisk->disk
+ * !0 - otherwise
+ *
+ * PURPOSE: dlist_t helper which compares the input disk dm_descriptor_t
+ * handle to the disk dm_descriptor_t handle in the input
+ * moddisk_t struct.
+ *
+ * Comparison is done via compare_descriptor_names.
+ */
+static int
+compare_disk_to_moddisk_disk(
+ void *disk,
+ void *moddisk)
+{
+ assert(disk != (dm_descriptor_t)0);
+ assert(moddisk != NULL);
+
+ return (compare_descriptor_names((void *)disk,
+ (void *)((moddisk_t *)moddisk)->disk));
+}
+
+/*
+ * FUNCTIONS: void set_hsp_request()
+ *
+ * INPUT: none -
+ * OUTPUT: none -
+ *
+ * PURPOSE: set the module global HSP request struct.
+ */
+static void
+set_hsp_request(
+ devconfig_t *req)
+{
+ _hsp_request = req;
+}
+
+/*
+ * FUNCTIONS: void unset_hsp_request()
+ *
+ * INPUT: none -
+ * OUTPUT: none -
+ *
+ * PURPOSE: unset the module global HSP request struct.
+ */
+static void
+unset_hsp_request()
+{
+ _hsp_request = NULL;
+}
+
+/*
+ * FUNCTION: process_hsp_request(devconfig_t *req, dlist_t **results)
+ * INPUT: req - pointer to the toplevel disk set devconfig_t request
+ * results - pointer to a list of composed results
+ *
+ * RETURNS: int - 0 - success
+ * !0 - failure
+ *
+ * PURPOSE: Helper which determines HSP processing for the
+ * composed volumes which need HSP spares.
+ */
+static int
+process_hsp_request(
+ devconfig_t *req,
+ dlist_t **results)
+{
+ int error = 0;
+
+ if (_hsp_request != NULL) {
+ oprintf(OUTPUT_TERSE,
+ gettext("\nProcessing HSP...\n"));
+ }
+
+ if (_hsp_devices == NULL) {
+ /* no devices -> no HSP */
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" No devices require hot spares...\n"));
+ } else {
+
+ oprintf(OUTPUT_TERSE, "\n");
+
+ ((error = layout_hsp(req, _hsp_request, _hsp_devices,
+ results)) != 0) ||
+ (error = collect_modified_disks(_hsp_request, *results)) ||
+ (error = convert_device_names(_hsp_request, *results));
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: add_to_hsp_list(dlist_t* list)
+ * INPUT: devs - pointer to a list of composed volumes
+ * OUTPUT: none -
+ * SIDEEFFECT: updates the module global list _hsp_devices
+ *
+ * RETURNS: int - 0 - success
+ * !0 - failure
+ *
+ * PURPOSE: Helper to update the list of devices which need HSP spares.
+ *
+ * Iterates the input list of devices and adds them them to the
+ * module provate list of devices needing spares.
+ */
+int
+add_to_hsp_list(
+ dlist_t *list)
+{
+ dlist_t *iter = NULL;
+ int error = 0;
+
+ for (iter = list; iter != NULL; iter = iter->next) {
+ dlist_t *item = NULL;
+
+ if ((item = dlist_new_item(iter->obj)) == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ _hsp_devices = dlist_append(item, _hsp_devices, AT_HEAD);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: string_case_compare(
+ * char *str1, char *str2)
+ *
+ * INPUT: str1 - char *
+ * str2 - char *
+ *
+ * RETURNS: int - <0 - if str1 < str2
+ * 0 - if str1 == str2
+ * >0 - if str1 > str2
+ *
+ * PURPOSE: More robust case independent string comparison function.
+ *
+ * Assumes str1 and str2 are both char *
+ *
+ * Compares the lengths of each and if equivalent compares
+ * the strings using strcasecmp.
+ */
+int
+string_case_compare(
+ char *str1,
+ char *str2)
+{
+ int result = 0;
+
+ assert(str1 != NULL);
+ assert(str2 != NULL);
+
+ if ((result = (strlen(str1) - strlen(str2))) == 0) {
+ result = strcasecmp(str1, str2);
+ }
+
+ return (result);
+}
diff --git a/usr/src/cmd/lvm/metassist/layout/layout.h b/usr/src/cmd/lvm/metassist/layout/layout.h
new file mode 100644
index 0000000000..3cb99f80ae
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef _VOLUME_LAYOUT_H
+#define _VOLUME_LAYOUT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "volume_request.h"
+#include "volume_defaults.h"
+
+/*
+ * FUNCTION: get_layout(devconfig_t *request, defaults_t *defaults)
+ *
+ * INPUT: request - a devconfig_t pointer to the toplevel request
+ * defaults - a results_t pointer to the defaults
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: Public entry point to layout module.
+ */
+extern int get_layout(request_t *request, defaults_t *defaults);
+
+/*
+ * FUNCTION: layout_clean_up()
+ * INPUT:
+ * OUTPUT:
+ * SIDEEFFECTS: releases all memory allocated during layout processing
+ *
+ * PURPOSE: function which handles the details of cleaning up memory
+ * allocated while processing a request.
+ *
+ * This function must be called explicitly if a call to
+ * get_layout() was terminated abnormally, for example,
+ * if the user terminates the calling process with a SIGINT.
+ */
+extern void layout_clean_up();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VOLUME_LAYOUT_H */
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_concat.c b/usr/src/cmd/lvm/metassist/layout/layout_concat.c
new file mode 100644
index 0000000000..3fe529763d
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_concat.c
@@ -0,0 +1,673 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <string.h>
+#include <libintl.h>
+
+#include "libdiskmgt.h"
+
+#include "volume_error.h"
+#include "volume_defaults.h"
+#include "volume_devconfig.h"
+#include "volume_dlist.h"
+#include "volume_output.h"
+#include "volume_request.h"
+
+#include "layout_concat.h"
+#include "layout_device_cache.h"
+#include "layout_device_util.h"
+#include "layout_discovery.h"
+#include "layout_dlist_util.h"
+#include "layout_messages.h"
+#include "layout_request.h"
+#include "layout_slice.h"
+#include "layout_svm_util.h"
+
+#define _LAYOUT_CONCAT_C
+
+static int
+compose_concat_within_hba(
+ devconfig_t *request,
+ dlist_t *hbas,
+ uint64_t nbytes,
+ devconfig_t **concat);
+
+static int
+assemble_concat(
+ devconfig_t *request,
+ dlist_t *comps,
+ devconfig_t **concat);
+
+/*
+ * FUNCTION: layout_concat(devconfig_t *request, uint64_t nbytes,
+ * dlist_t **results)
+ *
+ * INPUT: request - pointer to a devconfig_t of the current request
+ * nbytes - the desired capacity of the concat
+ *
+ * OUPUT: results - pointer to a list of composed volumes
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Main layout driver for composing concat volumes.
+ *
+ * Attempts to construct a concat of size nbytes.
+ *
+ * Several different layout strategies are tried in order
+ * of preference until one succeeds or there are none left.
+ *
+ * 1 - concat within an HBA
+ * . requires sufficient space available on the HBA
+ *
+ * 2 - concat across all available similar HBAs
+ *
+ * 3 - concat across all available HBAs
+ *
+ * get available HBAs
+ *
+ * group HBAs by characteristics
+ * for (each HBA grouping) and (concat not composed) {
+ * select next HBA group
+ * for (strategy[1,2]) and (concat not composed) {
+ * compose concat using HBAs in group
+ * }
+ * }
+ *
+ * if (concat not composed) {
+ * for (strategy[3]) and (concat not composed) {
+ * compose concat using all HBAs
+ * }
+ * }
+ *
+ * if (concat composed) {
+ * append composed concat to results
+ * }
+ */
+int
+layout_concat(
+ devconfig_t *request,
+ uint64_t nbytes,
+ dlist_t **results)
+{
+ /*
+ * these enums define the # of strategies and the preference order
+ * in which they are tried
+ */
+ typedef enum {
+ CONCAT_WITHIN_SIMILAR_HBA = 0,
+ CONCAT_ACROSS_SIMILAR_HBAS,
+ N_SIMILAR_HBA_STRATEGIES
+ } similar_hba_strategy_order_t;
+
+ typedef enum {
+ CONCAT_ACROSS_ANY_HBAS = 0,
+ N_ANY_HBA_STRATEGIES
+ } any_hba_strategy_order_t;
+
+ dlist_t *usable_hbas = NULL;
+ dlist_t *similar_hba_groups = NULL;
+ dlist_t *iter = NULL;
+ devconfig_t *concat = NULL;
+
+ int error = 0;
+
+ (error = get_usable_hbas(&usable_hbas));
+ if (error != 0) {
+ volume_set_error(gettext("There are no usable HBAs."));
+ return (error);
+ }
+
+ print_layout_volume_msg(devconfig_type_to_str(TYPE_CONCAT), nbytes);
+
+ if (dlist_length(usable_hbas) == 0) {
+ print_no_hbas_msg();
+ return (-1);
+ }
+
+ error = group_similar_hbas(usable_hbas, &similar_hba_groups);
+ if (error != 0) {
+ return (error);
+ }
+
+ for (iter = similar_hba_groups;
+ (error == 0) && (concat == NULL) && (iter != NULL);
+ iter = iter->next) {
+
+ dlist_t *hbas = (dlist_t *)iter->obj;
+
+ similar_hba_strategy_order_t order;
+
+ for (order = CONCAT_WITHIN_SIMILAR_HBA;
+ (order < N_SIMILAR_HBA_STRATEGIES) &&
+ (concat == NULL) && (error == 0);
+ order++) {
+
+ dlist_t *selhbas = NULL;
+ dlist_t *disks = NULL;
+
+ switch (order) {
+
+ case CONCAT_WITHIN_SIMILAR_HBA:
+
+ error = select_hbas_with_n_disks(
+ request, hbas, 1, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+ gettext(" -->Strategy 1: use disks from a single HBA - concat within HBA\n"));
+/* END CSTYLED */
+
+ error = compose_concat_within_hba(
+ request, selhbas, nbytes, &concat);
+ }
+
+ break;
+
+ case CONCAT_ACROSS_SIMILAR_HBAS:
+
+ error = select_hbas_with_n_disks(
+ request, hbas, 1, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+ gettext(" -->Strategy 2: use disks from all similar HBAs - concat across HBAs\n"));
+/* END CSTYLED */
+
+ error = populate_concat(
+ request, nbytes, disks,
+ NULL, &concat);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ dlist_free_items(disks, NULL);
+ dlist_free_items(selhbas, NULL);
+ }
+ }
+
+ for (iter = similar_hba_groups; iter != NULL; iter = iter->next) {
+ dlist_free_items((dlist_t *)iter->obj, NULL);
+ }
+ dlist_free_items(similar_hba_groups, NULL);
+
+ /* try all HBAs */
+ if (concat == NULL && error == 0) {
+
+ any_hba_strategy_order_t order;
+
+ for (order = CONCAT_ACROSS_ANY_HBAS;
+ (order < N_ANY_HBA_STRATEGIES) &&
+ (concat == NULL) && (error == 0);
+ order++) {
+
+ dlist_t *selhbas = NULL;
+ dlist_t *disks = NULL;
+
+ switch (order) {
+
+ case CONCAT_ACROSS_ANY_HBAS:
+
+ error = select_hbas_with_n_disks(
+ request, usable_hbas, 1, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_VERBOSE,
+ gettext(" -->Strategy 3: use disks from all available HBAs - concat across HBAs\n"));
+/* END CSTYLED */
+
+ error = populate_concat(
+ request, nbytes, disks,
+ NULL, &concat);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ dlist_free_items(disks, NULL);
+ dlist_free_items(selhbas, NULL);
+ }
+ }
+
+ if (concat != NULL) {
+
+ dlist_t *item = dlist_new_item(concat);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+
+ *results = dlist_append(item, *results, AT_TAIL);
+
+ print_layout_success_msg();
+ }
+
+ } else if (error != 0) {
+
+ print_debug_failure_msg(
+ devconfig_type_to_str(TYPE_CONCAT),
+ get_error_string(error));
+
+ } else {
+
+ print_insufficient_resources_msg(
+ devconfig_type_to_str(TYPE_CONCAT));
+ error = -1;
+ }
+
+ return (error);
+}
+
+static int
+compose_concat_within_hba(
+ devconfig_t *request,
+ dlist_t *hbas,
+ uint64_t nbytes,
+ devconfig_t **concat)
+{
+ int error = 0;
+
+ dlist_t *iter = NULL;
+
+ for (iter = hbas;
+ (iter != NULL) && (*concat == NULL) && (error == 0);
+ iter = iter->next) {
+
+ dm_descriptor_t hba = (uintptr_t)iter->obj;
+ dlist_t *disks = NULL;
+ uint64_t space = 0;
+ char *name;
+
+ /* check for sufficient space on the HBA */
+ ((error = get_display_name(hba, &name)) != 0) ||
+ (error = hba_get_avail_disks_and_space(request,
+ hba, &disks, &space));
+
+ if (error == 0) {
+ if (space >= nbytes) {
+ error = populate_concat(request, nbytes, disks,
+ NULL, concat);
+ } else {
+ print_hba_insufficient_space_msg(name, space);
+ }
+ }
+
+ dlist_free_items(disks, NULL);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: populate_concat(devconfig_t *request, uint64_t nbytes,
+ * dlist_t *disks, dlist_t *othervols,
+ * devconfig_t **concat)
+ *
+ * INPUT: request - pointer to a request devconfig_t
+ * nbytes - desired concat size
+ * disks - pointer to a list of availalb disks
+ * othervols - pointer to a list of other volumes whose
+ * composition may affect this concat
+ * (e.g., submirrors of the same mirror)
+ *
+ * OUTPUT: concat - pointer to a devconfig_t to hold resulting concat
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Helper to populate a concat with the specified aggregate
+ * capacity using slices on disks in the input list.
+ *
+ * If the othervols list is not empty, the slice components
+ * chosen for the concat must not on the same disks as any
+ * of the other volumes.
+ *
+ * If sufficient slice components can be found, the concat
+ * is assembled and returned.
+ */
+int
+populate_concat(
+ devconfig_t *request,
+ uint64_t nbytes,
+ dlist_t *disks,
+ dlist_t *othervols,
+ devconfig_t **concat)
+{
+ dlist_t *other_hbas = NULL;
+ dlist_t *other_disks = NULL;
+
+ dlist_t *slices = NULL;
+ dlist_t *comps = NULL;
+
+ uint16_t npaths = 0;
+ uint64_t capacity = 0;
+ int error = 0;
+
+ *concat = NULL;
+
+ ((error = disks_get_avail_slices(request, disks, &slices)) != 0) ||
+ (error = get_volume_npaths(request, &npaths));
+ if (error != 0) {
+ dlist_free_items(slices, NULL);
+ return (error);
+ }
+
+ print_populate_volume_msg(devconfig_type_to_str(TYPE_CONCAT), nbytes);
+
+ if (slices == NULL) {
+ print_populate_no_slices_msg();
+ return (0);
+ }
+
+ /* determine HBAs and disks used by othervols */
+ error = get_hbas_and_disks_used_by_volumes(othervols,
+ &other_hbas, &other_disks);
+ if (error != 0) {
+ dlist_free_items(other_hbas, NULL);
+ dlist_free_items(other_disks, NULL);
+ return (error);
+ }
+
+ print_populate_choose_slices_msg();
+
+ while (capacity < nbytes) {
+
+ devconfig_t *comp = NULL;
+ dlist_t *item = NULL;
+ dlist_t *rmvd = NULL;
+ char *cname = NULL;
+ uint64_t csize = 0;
+
+ /* BEGIN CSTYLED */
+ /*
+ * 1st B_TRUE: require a different disk than those used by
+ * comps and othervols
+ * 1st B_FALSE: slice with size less that requested is acceptable
+ * 2nd B_FALSE: do not add an extra cylinder when resizing slice,
+ * this is only necessary for Stripe components whose sizes
+ * get rounded down to an interlace multiple and then down
+ * to a cylinder boundary.
+ *
+ */
+ /* END CSTYLED */
+ error = choose_slice((nbytes-capacity), npaths, slices, comps,
+ other_hbas, other_disks, B_TRUE, B_FALSE, B_FALSE, &comp);
+
+ if ((error == 0) && (comp != NULL)) {
+
+ item = dlist_new_item(comp);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+
+ /* add selected component to comp list */
+ comps = dlist_append(item, comps, AT_HEAD);
+
+ /* remove it from the available list */
+ slices = dlist_remove_equivalent_item(slices, (void *) comp,
+ compare_devconfig_and_descriptor_names, &rmvd);
+
+ if (rmvd != NULL) {
+ free(rmvd);
+ }
+
+ /* add the component slice to the used list */
+ if ((error = devconfig_get_name(comp, &cname)) == 0) {
+ error = add_used_slice_by_name(cname);
+ }
+
+ /* increment concat's capacity */
+ if ((error == 0) &&
+ (error = devconfig_get_size(comp, &csize)) == 0) {
+ capacity += csize;
+ }
+ }
+
+ } else {
+ /* no possible slice */
+ break;
+ }
+ }
+
+ dlist_free_items(slices, NULL);
+ dlist_free_items(other_hbas, NULL);
+ dlist_free_items(other_disks, NULL);
+
+ if (capacity >= nbytes) {
+
+ error = assemble_concat(request, comps, concat);
+
+ if (error == 0) {
+ print_populate_success_msg();
+ } else {
+ /* undo any slicing done for the concat */
+ dlist_free_items(comps, free_devconfig_object);
+ }
+
+ } else if (error == 0) {
+
+ if (capacity > 0) {
+ dlist_free_items(comps, free_devconfig_object);
+ print_insufficient_capacity_msg(capacity);
+ } else {
+ print_populate_no_slices_msg();
+ }
+
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: populate_explicit_concat(devconfig_t *request,
+ * dlist_t **results)
+ *
+ * INPUT: request - pointer to a request devconfig_t
+ *
+ * OUTPUT: results - pointer to a list of volume devconfig_t results
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Processes the input concat request that specifies explicit
+ * slice components.
+ *
+ * The components have already been validated and reserved,
+ * all that is required is to create devconfig_t structs
+ * for each requested slice.
+ *
+ * The net size of the concat is determined by the slice
+ * components.
+ *
+ * The concat devconfig_t is assembled and appended to the
+ * results list.
+ *
+ * This function is also called from
+ * layout_mirror.populate_explicit_mirror()
+ */
+int
+populate_explicit_concat(
+ devconfig_t *request,
+ dlist_t **results)
+{
+ int error = 0;
+
+ dlist_t *comps = NULL;
+ dlist_t *iter = NULL;
+ dlist_t *item = NULL;
+
+ devconfig_t *concat = NULL;
+
+ print_layout_explicit_msg(devconfig_type_to_str(TYPE_CONCAT));
+
+ /* assemble components */
+ iter = devconfig_get_components(request);
+ for (; (iter != NULL) && (error == 0); iter = iter->next) {
+
+ devconfig_t *rqst = (devconfig_t *)iter->obj;
+ dm_descriptor_t rqst_slice = NULL;
+ char *rqst_name = NULL;
+ devconfig_t *comp = NULL;
+
+ /* slice components have been validated */
+ /* turn each into a devconfig_t */
+ ((error = devconfig_get_name(rqst, &rqst_name)) != 0) ||
+ (error = slice_get_by_name(rqst_name, &rqst_slice)) ||
+ (error = create_devconfig_for_slice(rqst_slice, &comp));
+
+ if (error == 0) {
+
+ print_layout_explicit_added_msg(rqst_name);
+
+ item = dlist_new_item((void *)comp);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ comps = dlist_append(item, comps, AT_TAIL);
+ }
+ }
+ }
+
+ if (error == 0) {
+ error = assemble_concat(request, comps, &concat);
+ }
+
+ if (error == 0) {
+ if ((item = dlist_new_item(concat)) == NULL) {
+ error = ENOMEM;
+ } else {
+ *results = dlist_append(item, *results, AT_TAIL);
+ print_populate_success_msg();
+ }
+ } else {
+ dlist_free_items(comps, free_devconfig);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: assemble_concat(devconfig_t *request, dlist_t *comps,
+ * devconfig_t **concat)
+ *
+ * INPUT: request - pointer to a devconfig_t of the current request
+ * comps - pointer to a list of slice components
+ *
+ * OUPUT: concat - pointer to a devconfig_t to hold final concat
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Helper which creates and populates a concat devconfig_t
+ * struct using information from the input request and the
+ * list of slice components.
+ *
+ * Determines the name of the concat either from the request
+ * or from the default naming scheme.
+ *
+ * Attaches the input list of components to the devconfig.
+ */
+static int
+assemble_concat(
+ devconfig_t *request,
+ dlist_t *comps,
+ devconfig_t **concat)
+{
+ char *name = NULL;
+ int error = 0;
+
+ if ((error = new_devconfig(concat, TYPE_CONCAT)) == 0) {
+ /* set concat name, use requested name if specified */
+ if ((error = devconfig_get_name(request, &name)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ volume_set_error(gettext("error getting requested name\n"));
+ } else {
+ error = 0;
+ }
+ }
+
+ if (error == 0) {
+ if (name == NULL) {
+ if ((error = get_next_volume_name(&name,
+ TYPE_CONCAT)) == 0) {
+ error = devconfig_set_name(*concat, name);
+ free(name);
+ }
+ } else {
+ error = devconfig_set_name(*concat, name);
+ }
+ }
+ }
+
+ if (error == 0) {
+
+ /* compute and save true size of concat */
+ if (error == 0) {
+ uint64_t nblks = 0;
+ dlist_t *iter;
+
+ for (iter = comps;
+ (error == 0) && (iter != NULL);
+ iter = iter->next) {
+
+ devconfig_t *comp = (devconfig_t *)iter->obj;
+ uint64_t comp_nblks = 0;
+
+ if ((error = devconfig_get_size_in_blocks(comp,
+ &comp_nblks)) == 0) {
+ nblks += comp_nblks;
+ }
+ }
+
+ if (error == 0) {
+ error = devconfig_set_size_in_blocks(*concat, nblks);
+ }
+ }
+ }
+
+ if (error == 0) {
+ devconfig_set_components(*concat, comps);
+ } else {
+ free_devconfig(*concat);
+ *concat = NULL;
+ }
+
+ return (error);
+}
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_concat.h b/usr/src/cmd/lvm/metassist/layout/layout_concat.h
new file mode 100644
index 0000000000..fb2d072d80
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_concat.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef _LAYOUT_CONCAT_H
+#define _LAYOUT_CONCAT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "volume_devconfig.h"
+#include "volume_dlist.h"
+
+extern int layout_concat(
+ devconfig_t *request,
+ uint64_t nbytes,
+ dlist_t **results);
+
+extern int populate_concat(
+ devconfig_t *request,
+ uint64_t nbytes,
+ dlist_t *disks,
+ dlist_t *othervols,
+ devconfig_t **concat);
+
+extern int populate_explicit_concat(
+ devconfig_t *request,
+ dlist_t **results);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LAYOUT_CONCAT_H */
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_device_cache.c b/usr/src/cmd/lvm/metassist/layout/layout_device_cache.c
new file mode 100644
index 0000000000..80a2865214
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_device_cache.c
@@ -0,0 +1,893 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <assert.h>
+#include <libintl.h>
+
+#include <string.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <search.h>
+
+#include "volume_dlist.h"
+#include "volume_error.h"
+#include "volume_output.h"
+
+#include "layout_device_cache.h"
+#include "layout_dlist_util.h"
+#include "layout_request.h"
+
+/*
+ * Implementation note:
+ * The current caches are implemented as linked lists of data
+ * structures described below. Cached object lookup uses hsearch()
+ * where possible to minimize the inefficiency of linear search.
+ */
+
+/*
+ * The name and attribute maps use hesarch() for faster lookup
+ */
+static const uint32_t MAX_CACHED_OBJECTS = 50000;
+
+/*
+ * The attribute cache is maintained as a list of these
+ * structs which map a device name to attributes. The
+ * device name is the unique device name returned from
+ * the device library, typically a devfs path. It should
+ * not be confused with the "display" name of the device
+ * which is typically a CTD or DID name.
+ */
+typedef struct {
+ char *name;
+ nvlist_t *attrs;
+} attr_cache_t;
+
+static dlist_t *_attr_cache = NULL;
+
+/*
+ * The name cache is maintained via a list of these structs
+ * which map a descriptor to its name.
+ * The descriptor is saved as a string for hsearch()
+ */
+typedef struct {
+ char *desc;
+ char *name;
+} name_cache_t;
+static dlist_t *_name_cache = NULL;
+
+/*
+ * The desc cache is maintained as a list of these
+ * structs which map a device display name (CTD or DID)
+ * or alias to a descriptor.
+ */
+typedef struct {
+ char *name;
+ dm_descriptor_t desc;
+} desc_cache_t;
+
+static dlist_t *_desc_cache = NULL;
+
+/*
+ * Since each of the lookup caches shares the same hsearch()
+ * hash table, the names used as lookup keys for the desc_cache_t
+ * and attr_cache_t may cause collisions.
+ *
+ * The desc_cache_t map alters the device name by prepending
+ * this string to avoid collisions.
+ */
+static const char *DESC_CACHE_KEY_PREFIX = "desc_cache";
+
+/*
+ * The set of descriptors to be returned to libdiskmgt is
+ * maintained via a list of dm_descriptor_t handles.
+ * descriptors are added by new_descriptor() and
+ * cache_descriptor_to_free().
+ */
+typedef struct {
+ dm_descriptor_t desc;
+ boolean_t virtual;
+} desc_free_t;
+static dlist_t *_desc_to_free = NULL;
+
+static char *find_cached_name(dm_descriptor_t desc);
+static nvlist_t *find_cached_attrs(char *name);
+
+static int add_descriptor_to_free(dm_descriptor_t desc);
+
+static void release_name_cache();
+static void release_desc_to_free_cache();
+static void release_attribute_cache();
+static void release_descriptor_cache();
+
+static uint32_t interal_name_count = 0;
+
+/*
+ * FUNCTION: create_device_caches()
+ *
+ * PURPOSE: Helper which initializes the module's private data
+ * structures.
+ */
+int
+create_device_caches()
+{
+ if (hcreate(MAX_CACHED_OBJECTS) == 0) {
+ return (ENOMEM);
+ }
+
+ return (0);
+}
+
+/*
+ * FUNCTION: release_device_caches()
+ *
+ * PURPOSE: Helper which cleans up memory allocated to the module's
+ * private data structures.
+ */
+int
+release_device_caches()
+{
+ release_name_cache();
+ release_desc_to_free_cache();
+ release_attribute_cache();
+ release_descriptor_cache();
+
+ return (0);
+}
+
+/*
+ * FUNCTION: free_desc_cache_object(void *obj)
+ *
+ * INPUT: obj - opaque pointer
+ *
+ * PURPOSE: Frees memory associated with an entry in the
+ * desc cache.
+ *
+ * Assumes that the input object is a pointer
+ * to a desc_cache_t struct.
+ */
+static void
+free_desc_cache_object(
+ void *obj)
+{
+ if (obj == NULL) {
+ return;
+ }
+
+ free(((desc_cache_t *)obj)->name);
+ free(obj);
+}
+/*
+ * FUNCTION: release_descriptor_cache()
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Frees all entries in the name cache.
+ */
+static void
+release_descriptor_cache()
+{
+ oprintf(OUTPUT_DEBUG,
+ gettext(" destroying descriptor cache (%d items)\n"),
+ dlist_length(_desc_cache));
+
+ dlist_free_items(_desc_cache, free_desc_cache_object);
+ _desc_cache = NULL;
+}
+
+/*
+ * FUNCTION: add_cached_descriptor(char *name, dm_descriptor_t desc)
+ *
+ * INPUT: name - a device name
+ * desc - a dm_descriptor_t handle
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Adds an entry to the descriptor cache using the input
+ * descriptor and name.
+ *
+ * Note that all of the lookup caches shares the same hsearch()
+ * hash table and that the names used as lookup keys for the
+ * desc_cache_t and attr_cache_t cause collisions.
+ *
+ * The desc_cache_t map alters the device name to avoid collisions.
+ */
+int
+add_cached_descriptor(
+ char *name,
+ dm_descriptor_t desc)
+{
+ desc_cache_t *dcp;
+ char buf[MAXNAMELEN+1];
+ dlist_t *item;
+ ENTRY entry;
+
+ if ((dcp = (desc_cache_t *)
+ calloc(1, sizeof (desc_cache_t))) == NULL) {
+ return (ENOMEM);
+ }
+
+ dcp->desc = desc;
+
+ (void) snprintf(buf, MAXNAMELEN, "%s-%s", DESC_CACHE_KEY_PREFIX, name);
+ dcp->name = strdup(buf);
+ if (dcp->name == NULL) {
+ free(dcp);
+ return (ENOMEM);
+ }
+
+ /*
+ * insert into the hashtable... ignore the return from hsearch(),
+ * there is no existing entry corresponding to desc since the
+ * map was already searched just before this function is called,
+ * see get_name() below
+ */
+ entry.key = dcp->name;
+ entry.data = (void *)dcp;
+ (void) hsearch(entry, ENTER);
+
+ /* insert into the list cache... */
+ if ((item = dlist_new_item((void *)dcp)) == NULL) {
+ free(dcp);
+ return (ENOMEM);
+ }
+
+ _desc_cache = dlist_append(item, _desc_cache, AT_HEAD);
+
+ return (0);
+}
+
+/*
+ * FUNCTION: dm_descriptor_t find_cached_descriptor(char *name)
+ *
+ * INPUT: char * - pointer to a name or alias.
+ *
+ * RETURNS: dm_descriptor_t - dm_descriptor_t handle cached under the
+ * input name if a match is found. A null descriptor
+ * is returned if no match is found.
+ *
+ * PURPOSE: Searches for the desc that has been cached for
+ * the input device name.
+ *
+ * Note that all of the lookup caches shares the same hsearch()
+ * hash table and that the names used as lookup keys for the
+ * desc_cache_t and attr_cache_t cause collisions.
+ *
+ * The desc_cache_t map alters the device name to avoid collisions.
+ */
+dm_descriptor_t
+find_cached_descriptor(
+ char *name)
+{
+ ENTRY item;
+ ENTRY *cached_item = NULL;
+ char buf[MAXNAMELEN+1];
+ dm_descriptor_t desc = (dm_descriptor_t)0;
+
+ (void) snprintf(buf, MAXNAMELEN, "%s-%s", DESC_CACHE_KEY_PREFIX, name);
+ item.key = buf;
+
+ /* get descriptor associated with this name */
+ if ((cached_item = hsearch(item, FIND)) != NULL) {
+ /* LINTED */
+ desc = ((desc_cache_t *)cached_item->data)->desc;
+ }
+
+ return (desc);
+}
+
+/*
+ * FUNCTION: free_name_cache_object(void *obj)
+ *
+ * INPUT: obj - opaque pointer
+ *
+ * PURPOSE: Frees memory associated with an entry in the
+ * name cache.
+ *
+ * Assumes that the input object is a pointer
+ * to a name_cache_t struct.
+ */
+static void
+free_name_cache_object(
+ void *obj)
+{
+ if (obj == NULL) {
+ return;
+ }
+
+ free(((name_cache_t *)obj)->desc);
+ free(((name_cache_t *)obj)->name);
+ free(obj);
+}
+
+/*
+ * FUNCTION: release_name_cache()
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Frees all entries in the name cache.
+ */
+static void
+release_name_cache()
+{
+ oprintf(OUTPUT_DEBUG,
+ gettext(" destroying name cache (%d items)\n"),
+ dlist_length(_name_cache));
+
+ dlist_free_items(_name_cache, free_name_cache_object);
+ _name_cache = NULL;
+}
+
+/*
+ * FUNCTION: add_cached_name(dm_descriptor_t desc, char *name)
+ *
+ * INPUT: desc - a dm_descriptor_t handle
+ * name - a device name
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Adds an entry to the name cache using the input
+ * descriptor and name.
+ */
+int
+add_cached_name(
+ dm_descriptor_t desc,
+ char *name)
+{
+ name_cache_t *ncp;
+ char buf[MAXNAMELEN+1];
+ dlist_t *item;
+ ENTRY entry;
+
+ if ((ncp = (name_cache_t *)
+ calloc(1, sizeof (name_cache_t))) == NULL) {
+ return (ENOMEM);
+ }
+
+ (void) snprintf(buf, MAXNAMELEN, "%llu", desc);
+ ncp->desc = strdup(buf);
+ if (ncp->desc == NULL) {
+ free(ncp);
+ return (ENOMEM);
+ }
+
+ ncp->name = strdup(name);
+ if (ncp->name == NULL) {
+ free(ncp->desc);
+ free(ncp);
+ return (ENOMEM);
+ }
+
+ /*
+ * insert into the hashtable... ignore the return from hsearch(),
+ * there is no existing entry corresponding to desc since the
+ * map was already searched just before this function is called,
+ * see get_name() below
+ */
+ entry.key = ncp->desc;
+ entry.data = (void *)ncp;
+ (void) hsearch(entry, ENTER);
+
+ /* insert into the list cache... */
+ if ((item = dlist_new_item((void *)ncp)) == NULL) {
+ free(ncp->desc);
+ free(ncp->name);
+ free(ncp);
+ return (ENOMEM);
+ }
+
+ _name_cache = dlist_append(item, _name_cache, AT_HEAD);
+
+ return (0);
+}
+
+/*
+ * FUNCTION: char *find_cached_name(dm_descriptor_t desc)
+ *
+ * INPUT: desc - a dm_descriptor_t handle
+ *
+ * RETURNS: char * - pointer to the name cached for the descriptor.
+ * Null otherwise.
+ *
+ * PURPOSE: Searches for the name that has been cached for
+ * the input dm_descriptor_t.
+ *
+ * Search linked list.
+ */
+static char *
+find_cached_name(
+ dm_descriptor_t desc)
+{
+ char buf[MAXNAMELEN+1];
+ ENTRY item;
+ ENTRY *cached_item = NULL;
+ char *name = NULL;
+
+ (void) snprintf(buf, MAXNAMELEN, "%llu", desc);
+ item.key = buf;
+
+ /* get name associated with this descriptor */
+ if ((cached_item = hsearch(item, FIND)) != NULL) {
+ /* LINTED */
+ name = ((name_cache_t *)cached_item->data)->name;
+ }
+
+ return (name);
+}
+
+/*
+ * FUNCTION: get_name(dm_descriptor_t desc,
+ * char_t **name)
+ *
+ * INPUT: desc - a dm_descriptor_t handle
+ *
+ * OUTPUT: name - pointer to char * to hold the name
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Searches for the name that has been cached for the
+ * input dm_descriptor_t.
+ *
+ * Names are cached using the dm_descriptor.
+ * If no name has yet been cached, it is retrieved from
+ * libdiskmgt and added to the cache.
+ *
+ * Names are cached so that all name strings obtained from
+ * libdiskmgt will get properly released when layout completes.
+ */
+int
+get_name(
+ dm_descriptor_t desc,
+ char **name)
+{
+
+ int dm_free = 1;
+ int error = 0;
+
+ if ((desc != (dm_descriptor_t)0) &&
+ (*name = find_cached_name(desc)) == NULL) {
+
+ /* not in descriptor->name cache/map, add it */
+
+ if (is_virtual_slice(desc) != B_TRUE) {
+
+ dm_desc_type_t type;
+
+ *name = dm_get_name(desc, &error);
+ if (error != 0) {
+ volume_set_error(
+ gettext("failed to get name for descriptor: %d\n"),
+ error);
+ return (-1);
+ }
+
+ /*
+ * some devices can be unnamed...
+ * assign a unique internal name if necessary
+ */
+ if (*name == NULL) {
+ char buf[MAXNAMELEN];
+
+ dm_free = 0;
+ (void) snprintf(buf, MAXNAMELEN-1, "temp-name-%lu",
+ interal_name_count++);
+ *name = strdup(buf);
+ if (*name == NULL) {
+ volume_set_error(
+ gettext("failed to get name for descriptor: %d\n"),
+ errno);
+ return (-1);
+ }
+ oprintf(OUTPUT_DEBUG,
+ gettext("unnamed descriptor %llu assigned %s\n"),
+ desc, *name);
+ }
+
+ /*
+ * media can have the same name as the associated drive
+ * which hoses the attribute caching scheme, so unique-ify
+ */
+ if ((type = dm_get_type(desc)) == DM_MEDIA) {
+ char buf[MAXNAMELEN];
+ (void) snprintf(buf, MAXNAMELEN-1, "%s-%d", *name, type);
+ error = add_cached_name(desc, buf);
+ } else {
+ error = add_cached_name(desc, *name);
+ }
+ if (dm_free)
+ dm_free_name(*name);
+ else
+ free(*name);
+
+ if (error == 0) {
+ /* return copied name */
+ *name = find_cached_name(desc);
+ } else {
+ *name = NULL;
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: free_attr_cache_object(void *obj)
+ *
+ * INPUT: obj - opaque pointer
+ *
+ * PURPOSE: Frees memory associated with an entry in the
+ * attribute cache.
+ *
+ * Assumes that the input object is a pointer
+ * to a attr_cache_t struct.
+ */
+static void
+free_attr_cache_object(
+ void *obj)
+{
+ if (obj == NULL) {
+ return;
+ }
+
+ nvlist_free(((attr_cache_t *)obj)->attrs);
+ free(obj);
+}
+
+/*
+ * FUNCTION: release_attribute_cache()
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Frees all entries in the attribute cache.
+ */
+void
+release_attribute_cache()
+{
+ oprintf(OUTPUT_DEBUG,
+ gettext(" destroying attribute cache (%d items)\n"),
+ dlist_length(_attr_cache));
+
+ dlist_free_items(_attr_cache, free_attr_cache_object);
+ _attr_cache = NULL;
+
+ /* cleanup attribute cache lookup hashtable */
+ hdestroy();
+}
+
+/*
+ * FUNCTION: add_cached_attributes(char *name, nvlist_t *attrs)
+ *
+ * INPUT: name - a device name
+ * attrs - pointer to an nvlist_t attribute structure
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Adds an entry to the attribute cache using the input
+ * name and attributes.
+ *
+ * Uses a linked list to cache attributes.
+ * Keeps a parallel hash table for faster lookup.
+ */
+int
+add_cached_attributes(
+ char *name,
+ nvlist_t *attrs)
+{
+ attr_cache_t *acp = NULL;
+ dlist_t *item = NULL;
+ ENTRY *exist = NULL;
+ ENTRY entry;
+
+ /* insert into the hashtable... */
+ entry.key = name;
+ entry.data = (void *)attrs;
+
+ if ((exist = hsearch(entry, ENTER)) != NULL) {
+ /* replace the existing attrs entry */
+ exist->data = (void *)attrs;
+ }
+
+ if ((acp = (attr_cache_t *)calloc(1, sizeof (attr_cache_t))) == NULL) {
+ return (ENOMEM);
+ }
+
+ acp->name = name;
+ acp->attrs = attrs;
+
+ /* and cache of attr structs to be freed */
+ if ((item = dlist_new_item((void *)acp)) == NULL) {
+ free(acp);
+ return (ENOMEM);
+ }
+
+ _attr_cache = dlist_append(item, _attr_cache, AT_HEAD);
+
+ return (0);
+}
+
+/*
+ * FUNCTION: nvlist_t *find_cached_attrs(char *name)
+ *
+ * INPUT: name - a device name
+ *
+ * RETURNS: nvlist_t * - pointer to an nvlist_t attribute structure
+ * cached under 'name'. Null otherwise.
+ *
+ * PURPOSE: Searches for the nvlist attributes that have been
+ * cached for the input name.
+ */
+static nvlist_t *
+find_cached_attrs(
+ char *name)
+{
+ ENTRY item;
+ ENTRY *cached_item = NULL;
+ nvlist_t *attrs = NULL;
+
+ item.key = name;
+
+ /* get attributes cached under this name */
+ if ((cached_item = hsearch(item, FIND)) != NULL) {
+ /* LINTED */
+ attrs = (nvlist_t *)cached_item->data;
+ }
+
+ return (attrs);
+}
+
+/*
+ * FUNCTION: get_cached_attributes(dm_descriptor_t desc,
+ * nvlist_t **attrs)
+ *
+ * INPUT: desc - a dm_descriptor_t handle
+ *
+ * OUTPUT: attrs - pointer to an nvlist_t attribute structure
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Searches for the nvlist attributes that have been
+ * cached for the input dm_descriptor_t.
+ *
+ * Attributes are cached using the name associated with
+ * the descriptor. If no attributes have yet been cached
+ * they are retrieved from libdiskmgt and added to the
+ * cache.
+ *
+ * Attributes are cached so that layout may store transient
+ * data relevant to the layout process.
+ */
+int
+get_cached_attributes(
+ dm_descriptor_t desc,
+ nvlist_t **attrs)
+{
+ int error = 0;
+ char *name = NULL;
+
+ if ((desc != (dm_descriptor_t)0) &&
+ (error = get_name(desc, &name)) == 0) {
+
+ if ((*attrs = find_cached_attrs(name)) == NULL) {
+ /* get attrs and cache them */
+ *attrs = dm_get_attributes(desc, &error);
+ if (error == 0) {
+ error = add_cached_attributes(name, *attrs);
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: new_descriptor(dm_descriptor_t *desc)
+ *
+ * INPUT: desc - a pointer to a dm_descriptor_t to hold
+ * the result.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Allocates a new dm_descriptor_t handle.
+ *
+ * This is necessary because the process may have to
+ * create "virtual" objects to represent devices that
+ * do not yet exist on the system and hence are unknown
+ * to libdiskmgt and diskmgtd.
+ *
+ * A unique handle is created for such objects and may
+ * be used by layout to access the virtual devices as
+ * if they were obtained from libdiskmgt.
+ */
+int
+new_descriptor(
+ dm_descriptor_t *desc)
+{
+ desc_free_t *dfp;
+ dlist_t *item;
+
+ *desc = NULL;
+
+ if ((dfp = (desc_free_t *)
+ calloc(1, sizeof (desc_free_t))) == NULL) {
+ return (ENOMEM);
+ }
+
+ dfp->desc = (uintptr_t)dfp;
+ dfp->virtual = B_TRUE;
+
+ if ((item = dlist_new_item((void *)dfp)) == NULL) {
+ free(dfp);
+ return (ENOMEM);
+ }
+
+ _desc_to_free = dlist_append(item, _desc_to_free, AT_HEAD);
+
+ *desc = (uintptr_t)dfp;
+
+ return (0);
+}
+
+/*
+ * FUNCTION: add_descriptors_to_free(dm_descriptor_t *desc)
+ *
+ * INPUT: desc - an array of dm_descriptor_t handles from
+ * libdiskmgt
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Function which accepts an array of dm_descriptor_t handles
+ * that need to be returned to libdiskmgt.
+ *
+ * The array is iterated and each handle is passed to
+ * add_descriptor_to_free.
+ */
+int
+add_descriptors_to_free(
+ dm_descriptor_t *desc_list)
+{
+ int i = 0;
+
+ if (desc_list != NULL) {
+ for (i = 0; desc_list[i] != NULL; i++) {
+ (void) add_descriptor_to_free(desc_list[i]);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * FUNCTION: add_descriptor_to_free(dm_descriptor_t desc)
+ *
+ * INPUT: desc - dm_descriptor_t handle from libdiskmgt
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Remembers a dm_descriptor_t handle which needs to be
+ * returned to libdiskmgt. These handles represent memory
+ * allocated by the the diskmgtd and must be returned in
+ * order for that memory to be released.
+ *
+ * The handles are cached for the duration of layout
+ * processing so that layout is guaranteed to have
+ * unique handles for all objects received from
+ * libdiskmgt.
+ *
+ * The caching is accomplished by adding the handle to
+ * a list of desc_free_t structs.
+ */
+static int
+add_descriptor_to_free(
+ dm_descriptor_t desc)
+{
+ desc_free_t *dfp = NULL;
+ dlist_t *item = NULL;
+
+ if (desc == (dm_descriptor_t)0) {
+ return (0);
+ }
+
+ if (is_virtual_slice(desc) == B_TRUE) {
+ /* don't return virtual slice descriptors to libdiskmgt */
+ return (0);
+ }
+
+ if ((dfp = calloc(1, sizeof (desc_free_t))) == NULL) {
+ return (ENOMEM);
+ }
+
+ dfp->desc = desc;
+ dfp->virtual = B_FALSE;
+
+ if ((item = dlist_new_item((void *)dfp)) == NULL) {
+ free(dfp);
+ return (ENOMEM);
+ }
+
+ _desc_to_free = dlist_append(item, _desc_to_free, AT_HEAD);
+
+ return (0);
+}
+
+/*
+ * FUNCTION: release_desc_to_free_cache()
+ *
+ * PURPOSE: Frees all entries in the desc_to_free cache.
+ *
+ * Iterates the _desc_to_free list and builds an
+ * array with all dm_descriptor_t handles that were
+ * obtained from libdiskmgt. Passing this array to
+ * dm_free_descriptors() is faster than calling
+ * dm_free_descriptor() to free individual handles.
+ */
+void
+release_desc_to_free_cache()
+{
+ dlist_t *iter;
+ dm_descriptor_t *array;
+ int i = 0;
+
+ oprintf(OUTPUT_DEBUG,
+ gettext(" destroying desc_to_free cache (%d items)\n"),
+ dlist_length(_desc_to_free));
+
+ array = (dm_descriptor_t *)calloc(
+ dlist_length(_desc_to_free) + 1, sizeof (dm_descriptor_t));
+
+ if (array != NULL) {
+ for (iter = _desc_to_free; iter != NULL; iter = iter->next) {
+ desc_free_t *dfp = (desc_free_t *)iter->obj;
+ if (dfp->virtual == B_FALSE) {
+ array[i++] = dfp->desc;
+ }
+ }
+ array[i] = (dm_descriptor_t)0;
+ dm_free_descriptors(array);
+ }
+
+ /*
+ * If the calloc failed, the descriptors aren't explicitly freed,
+ * but the libdiskmgt daemon will eventually reclaim them after
+ * a period of inactivity.
+ */
+ dlist_free_items(_desc_to_free, free);
+
+ _desc_to_free = NULL;
+}
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_device_cache.h b/usr/src/cmd/lvm/metassist/layout/layout_device_cache.h
new file mode 100644
index 0000000000..6328e6b7ec
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_device_cache.h
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+#ifndef _LAYOUT_DEVICE_CACHE_H
+#define _LAYOUT_DEVICE_CACHE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This module manages cached copies of a dm_descriptor_t's nvpair
+ * list of attributes and its device name. The caches are used to
+ * make sure that the memory allocated to these objects is correctly
+ * released after the layout process has finished. The cached attrs
+ * also allow the layout code to store and retrieve transient,
+ * layout-private data in the same data structure as the other
+ * relevant device information.
+ *
+ * There are two primary caches of information:
+ *
+ * descriptor->name - which maps a dm_descriptor_t handle to
+ * the associated device's name
+ *
+ * name->attributes - which maps a device name to an nvlist_t
+ * attribute collection.
+ *
+ * These two data structures thus allow the following lookup chain:
+ * descriptor->name->attributes.
+ *
+ * The attributes are accessed by device name because the it is the
+ * unique identifier for the device. The descriptor returned by
+ * libdiskmgt is just an arbitrary handle, multiple calls into the
+ * library may return different descriptors for the same device.
+ *
+ * Descriptors are also get re-cycled by the library which could
+ * result in the same descriptor being used to represent different
+ * devices (although not concurrently). To prevent such recycling
+ * all of the descriptors are held until the layout process has
+ * completed.
+ *
+ * Performance testing indicated that searching the lists of known
+ * devices by display (CTD or DID) name or alias was a significant
+ * bottleneck. A mapping from display name to descriptor was added
+ * to address this.
+ *
+ * The module should be initialized once by calling create_device_caches()
+ * prior to any call which accesses data maintained by the cache.
+ *
+ * The caches should be flushed after all accesses have completed by
+ * calling release_device_caches.
+ */
+
+#include "libdiskmgt.h"
+#include "layout_device_util.h"
+
+extern int create_device_caches();
+extern int release_device_caches();
+
+extern int add_cached_descriptor(char *name, dm_descriptor_t desc);
+extern dm_descriptor_t find_cached_descriptor(char *name);
+
+extern int add_cached_name(dm_descriptor_t desc, char *name);
+extern int get_name(dm_descriptor_t desc, char **name);
+
+extern int add_cached_attributes(char *name, nvlist_t *attrs);
+extern int get_cached_attributes(dm_descriptor_t desc, nvlist_t **list);
+
+extern int new_descriptor(dm_descriptor_t *desc);
+extern int add_descriptors_to_free(dm_descriptor_t *desc_list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LAYOUT_DEVICE_CACHE_H */
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_device_util.c b/usr/src/cmd/lvm/metassist/layout/layout_device_util.c
new file mode 100644
index 0000000000..1da277e4e3
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_device_util.c
@@ -0,0 +1,3458 @@
+/*
+ * 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 2004 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 *)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 *)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*)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*)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 *)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 *)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);
+}
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_device_util.h b/usr/src/cmd/lvm/metassist/layout/layout_device_util.h
new file mode 100644
index 0000000000..d8970ac5d5
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_device_util.h
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+#ifndef _LAYOUT_DEVICE_UTIL_H
+#define _LAYOUT_DEVICE_UTIL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libdiskmgt.h>
+
+extern boolean_t is_alt_slice_name(char *name);
+extern boolean_t is_did_name(char *name);
+extern boolean_t is_did_slice_name(char *name);
+extern boolean_t is_did_disk_name(char *name);
+extern boolean_t is_ctd_name(char *name);
+extern boolean_t is_ctd_slice_name(char *name);
+extern boolean_t is_ctd_disk_name(char *name);
+extern boolean_t is_ctd_target_name(char *name);
+extern boolean_t is_ctd_ctrl_name(char *name);
+
+extern int set_display_name(dm_descriptor_t desc, char *name);
+extern int get_display_name(dm_descriptor_t slice, char **name);
+
+extern int slice_get_by_name(char *name, dm_descriptor_t *slicep);
+extern int disk_get_by_name(char *name, dm_descriptor_t *diskp);
+extern int hba_get_by_name(char *name, dm_descriptor_t *hbap);
+
+extern int extract_diskname(char *slicename, char **diskname);
+extern int extract_hbaname(char *slicename, char **hbaname);
+
+extern int get_disk_for_named_slice(char *slicename,
+ dm_descriptor_t *diskp);
+
+/*
+ * functions to manipulate devices
+ */
+extern int group_similar_hbas(dlist_t *hbas, dlist_t **list);
+extern int hba_is_multiplex(dm_descriptor_t hba, boolean_t *bool);
+
+extern int hba_set_n_avail_disks(dm_descriptor_t hba, uint16_t val);
+extern int hba_get_n_avail_disks(dm_descriptor_t hba, uint16_t *val);
+
+extern int hba_get_type(dm_descriptor_t hba, char **type);
+extern int hba_is_fast(dm_descriptor_t hba, boolean_t *bool);
+extern int hba_is_fast_20(dm_descriptor_t hba, boolean_t *bool);
+extern int hba_is_fast_40(dm_descriptor_t hba, boolean_t *bool);
+extern int hba_is_fast_80(dm_descriptor_t hba, boolean_t *bool);
+extern int hba_supports_protocol(
+ dm_descriptor_t hba, char *attr, boolean_t *bool);
+extern int hba_supports_wide(dm_descriptor_t hba, boolean_t *bool);
+
+extern int disk_get_available_slice_index(
+ dm_descriptor_t diskp, uint32_t *index);
+
+extern int disk_get_hbas(dm_descriptor_t disk, dlist_t **list);
+extern int disk_get_paths(dm_descriptor_t disk, dlist_t **list);
+extern int disk_get_slices(dm_descriptor_t disk, dlist_t **list);
+extern int disk_get_aliases(dm_descriptor_t disk, dlist_t **list);
+extern int disk_get_blocksize(dm_descriptor_t disk, uint64_t *val);
+extern int disk_get_ncylinders(dm_descriptor_t disk, uint64_t *val);
+extern int disk_get_size_in_blocks(dm_descriptor_t disk, uint64_t *val);
+extern int disk_get_start_block(dm_descriptor_t disk, uint64_t *val);
+extern int disk_get_nheads(dm_descriptor_t disk, uint64_t *val);
+extern int disk_get_nsectors(dm_descriptor_t disk, uint64_t *val);
+extern int disk_get_is_efi(dm_descriptor_t disk, boolean_t *val);
+extern int disk_get_has_fdisk(dm_descriptor_t disk, boolean_t *val);
+extern int disk_get_has_solaris_partition(dm_descriptor_t disk,
+ boolean_t *val);
+extern int disk_get_is_online(dm_descriptor_t disk, boolean_t *val);
+extern int disk_get_drive_type(dm_descriptor_t disk, uint32_t *val);
+extern int disk_get_media_type(dm_descriptor_t disk, uint32_t *type);
+extern int disk_reserve_index(dm_descriptor_t disk, uint16_t index);
+extern int disk_release_index(dm_descriptor_t disk, uint16_t index);
+
+extern int slice_get_hbas(dm_descriptor_t slice, dlist_t **list);
+extern int slice_get_disk(dm_descriptor_t slice, dm_descriptor_t *diskp);
+extern int slice_get_size(dm_descriptor_t slice, uint64_t *val);
+extern int slice_get_index(dm_descriptor_t slice, uint32_t *val);
+extern int slice_get_size_in_blocks(dm_descriptor_t slice, uint64_t *val);
+extern int slice_get_start_block(dm_descriptor_t slice, uint64_t *val);
+extern int slice_get_start(dm_descriptor_t slice, uint64_t *val);
+
+extern int slice_set_size(dm_descriptor_t slice, uint64_t size);
+extern int slice_set_size_in_blocks(dm_descriptor_t slice, uint64_t size);
+extern int slice_set_start_block(dm_descriptor_t slice, uint64_t start);
+
+/*
+ * virtual slice utilities.
+ */
+extern int create_virtual_slices(dlist_t *unused);
+extern int add_virtual_slice(char *name, uint32_t index,
+ uint64_t startblk, uint64_t sizeblks, dm_descriptor_t disk);
+
+extern void release_virtual_slices();
+extern int get_virtual_slices(dlist_t **list);
+extern boolean_t is_virtual_slice(dm_descriptor_t slice);
+
+/*
+ * shared error output functions for dm_descriptor_t objects
+ */
+extern void print_get_assoc_desc_error(
+ dm_descriptor_t desc, char *which, int error);
+extern void print_get_desc_attr_error(
+ dm_descriptor_t desc, char *devtype, char *attr, int error);
+
+extern void print_set_desc_attr_error(
+ dm_descriptor_t desc, char *devtype, char *attr, int error);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LAYOUT_DEVICE_UTIL_H */
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_discovery.c b/usr/src/cmd/lvm/metassist/layout/layout_discovery.c
new file mode 100644
index 0000000000..28df972765
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_discovery.c
@@ -0,0 +1,2454 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <limits.h>
+#include <libdiskmgt.h>
+#include <libintl.h>
+
+#include <meta.h>
+
+#define _LAYOUT_DISCOVERY_C
+
+#include "volume_dlist.h"
+#include "volume_error.h"
+#include "volume_nvpair.h"
+#include "volume_output.h"
+
+#include "layout_device_cache.h"
+#include "layout_device_util.h"
+#include "layout_dlist_util.h"
+#include "layout_discovery.h"
+#include "layout_request.h"
+#include "layout_slice.h"
+#include "layout_svm_util.h"
+
+/*
+ * lists of device dm_descriptor_t handles discovered during
+ * the initial system probe. Lists are populated by
+ * discover_known_devices.
+ *
+ * "bad" slices are those that are known to libdiskmgt but
+ * cannot be accessed. An example would be a slice that has
+ * disappeared due to disk re-slicing: libdiskmgt may have a
+ * cached handle for it, but the slice no longer exists.
+ *
+ * "bad" disks are thoese that are known to libdiskmgt but
+ * cannot be accessed. An example would be a disk that has
+ * failed or has gone offline: libdiskmgt may have a cached
+ * handle for it, but the disk does not respond.
+ */
+static dlist_t *_bad_slices = NULL;
+static dlist_t *_bad_disks = NULL;
+
+static dlist_t *_known_slices = NULL;
+static dlist_t *_known_disks = NULL;
+static dlist_t *_known_hbas = NULL;
+
+/*
+ * helper functions for building known device lists, used by
+ * discover_known_devices.
+ */
+static int generate_known_slices(dlist_t *disks, dlist_t **known,
+ dlist_t **bad);
+static int generate_known_disks(dlist_t **known, dlist_t **bad);
+static int generate_known_hbas(dlist_t *disks, dlist_t **known);
+static int generate_known_hba_name(
+ dm_descriptor_t hba,
+ dm_descriptor_t alias,
+ dm_descriptor_t disk);
+
+static void print_known_devices();
+static void print_device_list(dlist_t *devices);
+
+/*
+ * lists of device dm_descriptor_t handles that are usable by layout.
+ * These devices must still pass the user specified available/unavailable
+ * filter before they're actually considered available.
+ *
+ * Lists are populated by discover_usable_devices.
+ */
+static dlist_t *_usable_slices = NULL;
+static dlist_t *_usable_disks = NULL;
+static dlist_t *_usable_hbas = NULL;
+
+/*
+ * private flag that remembers if any HBA is known to support MPXIO
+ */
+static boolean_t _mpxio_enabled = B_FALSE;
+
+/*
+ * The slice_class struct is used to group slices by usage class.
+ */
+typedef struct {
+ char *usage; /* usage description */
+ dlist_t *sliceinfo; /* list with info about each slice with usage */
+} slice_class_t;
+
+#define USE_DISKSET "diskset"
+
+static int check_slice_usage(
+ char *dsname,
+ dm_descriptor_t slice,
+ dm_descriptor_t disk,
+ boolean_t *avail,
+ dlist_t **bad,
+ dlist_t **classes);
+
+static int check_svm_slice_usage(
+ char *dsname,
+ dm_descriptor_t slice,
+ dm_descriptor_t disk,
+ boolean_t *avail,
+ dlist_t **classes);
+
+static int save_slice_classification(
+ char *dsname,
+ dm_descriptor_t slice,
+ dm_descriptor_t disk,
+ char *usage,
+ char *usage_detail,
+ dlist_t **classes);
+
+static int generate_usable_disks_and_slices_in_local_set(
+ dlist_t **classes,
+ dlist_t **bad_disks,
+ dlist_t **usable_disks,
+ dlist_t **usable_slices);
+
+static int generate_usable_disks_and_slices_in_named_set(
+ char *dsname,
+ dlist_t **classes,
+ dlist_t **bad_slices,
+ dlist_t **usable_disks,
+ dlist_t **usable_slices);
+
+static int create_usable_slices(
+ dm_descriptor_t disk,
+ dlist_t *used,
+ dlist_t *unused,
+ dlist_t **usable);
+
+static int add_new_usable(
+ dm_descriptor_t disk,
+ uint64_t stblk,
+ uint64_t nblks,
+ dlist_t **next_unused,
+ dlist_t **usable);
+
+static int update_slice_attributes(
+ dm_descriptor_t slice,
+ uint64_t stblk,
+ uint64_t nblks,
+ uint64_t nbytes);
+
+static int generate_usable_hbas(
+ dlist_t *disks,
+ dlist_t **usable);
+
+static void print_usable_devices();
+
+static void print_unusable_devices(
+ dlist_t *badslices,
+ dlist_t *baddisks,
+ dlist_t *usedslices);
+
+static char *get_slice_usage_msg(
+ char *usage);
+
+/*
+ * virtual slices...
+ */
+static int generate_virtual_slices(
+ dlist_t *avail_disks_local_set,
+ dlist_t **usable);
+
+/*
+ * multipathed disks have aliases, as do slices on those disks.
+ * these need to be tracked since the user may specify them.
+ * A multi-pathed disk is one connected to the system thru
+ * more than one physical HBA, each connection gets a distinct
+ * name in the device tree and they're all more or less equivalent.
+ * No indication as to how many possible physical connections a
+ * disk may have, so we pick an arbitrary number of aliases to
+ * support. There is nothing significant about this number,
+ * it just controls the number of alias slots that get allocated.
+ */
+#define MAX_ALIASES 8
+
+/*
+ * attribute name for layout private information stored in
+ * device nvpair attribute lists.
+ */
+static char *ATTR_DEVICE_ALIASES = "layout_device_aliases";
+
+static int compare_start_blocks(
+ void *desc1, void *desc2);
+
+static int compare_desc_display_names(
+ void *desc1, void *desc2);
+
+/*
+ * FUNCTION: is_mpxio_enabled()
+ *
+ * RETURNS: boolean_t - B_TRUE - if MPXIO appears enabled for the system
+ * B_FALSE - otherwise
+ *
+ * PURPOSE: returns the value of _mpxio_enabled which is set to B_TRUE
+ * during system configuration discovery if any of the knwon
+ * HBAs advertises itself as a "multiplex" controller.
+ */
+boolean_t
+is_mpxio_enabled()
+{
+ return (_mpxio_enabled);
+}
+
+/*
+ * FUNCTION: discover_known_devices()
+ *
+ * SIDEEFFECT: populates the module private lists of known devices
+ * (_known_slices, _known_disks, _known_hbas).
+ *
+ * All known devices will also have had their CTD
+ * short names inferred and stored.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Load physical devices discovered thru libdiskmgt.
+ */
+int
+discover_known_devices()
+{
+ int error = 0;
+
+ oprintf(OUTPUT_TERSE,
+ gettext("\nScanning system physical "
+ "device configuration...\n"));
+
+ /* initialize layout_device_cache */
+ ((error = create_device_caches()) != 0) ||
+
+ (error = generate_known_disks(&_known_disks, &_bad_disks)) ||
+ (error = generate_known_slices(_known_disks, &_known_slices,
+ &_bad_slices)) ||
+ (error = generate_known_hbas(_known_disks, &_known_hbas));
+
+ if (error == 0) {
+ print_known_devices();
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: release_known_devices()
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Unloads all state currently held for known
+ * physical devices.
+ */
+int
+release_known_devices(
+ char *diskset)
+{
+ /* these lists are module private */
+ dlist_free_items(_bad_slices, NULL);
+ dlist_free_items(_bad_disks, NULL);
+ dlist_free_items(_known_slices, NULL);
+ dlist_free_items(_known_disks, NULL);
+ dlist_free_items(_known_hbas, NULL);
+
+ _bad_slices = NULL;
+ _bad_disks = NULL;
+ _known_slices = NULL;
+ _known_disks = NULL;
+ _known_hbas = NULL;
+
+ /* clean up state kept in layout_device_cache */
+ release_device_caches();
+
+ return (0);
+}
+
+/*
+ * FUNCTION: discover_usable_devices(char *diskset)
+ *
+ * INPUT: diskset - a char * diskset name.
+ *
+ * SIDEEFFECT: Traverses the lists of known devices and populates the
+ * module private lists of usable devices (_usable_slices,
+ * _usable_disks, _usable_hbas), as well as the module
+ * private list of used slices.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Process the known devices and determine which of them are
+ * usable for generating volumes in the specified diskset.
+ *
+ * The specified diskset's name cannot be NULL or 0 length.
+ */
+int
+discover_usable_devices(
+ char *diskset)
+{
+ int error = 0;
+
+ dlist_t *used_classes = NULL;
+ dlist_t *iter = NULL;
+
+ if (diskset == NULL || diskset[0] == '\0') {
+ volume_set_error(
+ gettext("a diskset name must be specified in "
+ "the request\n"));
+ return (-1);
+ }
+
+ oprintf(OUTPUT_TERSE,
+ gettext("\nDetermining usable physical devices "
+ "for disk set \"%s\"...\n"),
+ diskset);
+
+ error = generate_usable_disks_and_slices_in_local_set(
+ &used_classes, &_bad_slices, &_usable_disks, &_usable_slices);
+ if (error == 0) {
+
+ error = generate_usable_disks_and_slices_in_named_set(
+ diskset, &used_classes, &_bad_slices, &_usable_disks,
+ &_usable_slices);
+ if (error == 0) {
+
+ error = generate_usable_hbas(_usable_disks, &_usable_hbas);
+ if (error == 0) {
+
+ print_usable_devices();
+ print_unusable_devices(
+ _bad_slices, _bad_disks, used_classes);
+ }
+ }
+ }
+
+ /*
+ * free slice classification usage and lists, items are char*
+ * the used_classes structure is only filled in if verbose
+ * output was requested.
+ */
+ for (iter = used_classes; iter != NULL; iter = iter->next) {
+ slice_class_t *class = (slice_class_t *)iter->obj;
+ free(class->usage);
+ dlist_free_items(class->sliceinfo, free);
+ }
+
+ dlist_free_items(used_classes, free);
+ return (error);
+}
+
+/*
+ * FUNCTION: release_usable_devices()
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Unloads all state currently held for usable
+ * physical devices.
+ */
+int
+release_usable_devices()
+{
+ /* list items are shared with _known_XXX lists */
+
+ dlist_free_items(_usable_slices, NULL);
+ dlist_free_items(_usable_disks, NULL);
+ dlist_free_items(_usable_hbas, NULL);
+
+ _usable_slices = NULL;
+ _usable_disks = NULL;
+ _usable_hbas = NULL;
+
+ /* clean up state kept in layout_device_util */
+ release_virtual_slices();
+
+ return (0);
+}
+
+/*
+ * FUNCTION: get_known_slices(dlist_t **list)
+ * get_known_disks(dlist_t **list)
+ * get_known_hbas(dlist_t **list)
+ *
+ * OUTPUT: list - a dlist_t pointer to hold the returned list of
+ * devices.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Public accessors for the module private lists of
+ * available devices.
+ */
+int
+get_known_slices(
+ dlist_t **list)
+{
+ *list = _known_slices;
+
+ return (0);
+}
+
+int
+get_known_disks(
+ dlist_t **list)
+{
+ *list = _known_disks;
+
+ return (0);
+}
+
+int
+get_known_hbas(
+ dlist_t **list)
+{
+ *list = _known_hbas;
+
+ return (0);
+}
+
+/* make fully qualified DID device name */
+static char *
+make_fully_qualified_did_device_name(
+ char *device)
+{
+ static char buf[MAXPATHLEN];
+
+ if (device != NULL && strrchr(device, '/') == NULL) {
+ (void) snprintf(buf, MAXPATHLEN-1, "%s/%s",
+ "/dev/did/dsk", device);
+ return (buf);
+ }
+
+ return (device);
+}
+
+/*
+ * FUNCTION: generate_known_disks(dlist_t **known,
+ * dlist_t **bad)
+ *
+ * INPUT: NONE
+ *
+ * OUTPUT: known - populated list of known disks
+ * bad - populated list of known bad disks
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Does the system configuration discovery to determine
+ * what disks are known to be attached to the system.
+ *
+ * Determines the CTD name for each disk and saves it.
+ */
+static int
+generate_known_disks(
+ dlist_t **known,
+ dlist_t **bad)
+{
+ int i;
+ int error = 0;
+ dm_descriptor_t *ddp;
+
+ ddp = dm_get_descriptors(DM_DRIVE, NULL, &error);
+ (void) add_descriptors_to_free(ddp);
+
+ *known = NULL;
+
+ if (error != 0) {
+ volume_set_error(
+ gettext("Error discovering system hardware configuration,\n"
+ "unable to communicate with libdiskmgt or diskmgtd.\n"));
+ return (-1);
+ }
+
+ if ((ddp == NULL) || (ddp[0] == NULL)) {
+ volume_set_error(gettext("there are no known disks\n"));
+ return (-1);
+ }
+
+ /* iterate all returned disks and add them to the known list */
+ for (i = 0; (ddp[i] != NULL) && (error == 0); i++) {
+ dm_descriptor_t disk = (dm_descriptor_t)ddp[i];
+ dlist_t *aliases = NULL;
+ uint32_t mtype = DM_MT_UNKNOWN;
+ uint32_t dtype = DM_DT_UNKNOWN;
+ boolean_t bad_disk = B_FALSE;
+ boolean_t online = B_TRUE;
+
+#if defined(i386)
+ /* on X86, disks must have a solaris FDISK partition */
+ boolean_t solpart = B_FALSE;
+#endif /* defined(i386) */
+
+ if (((error = disk_get_is_online(disk, &online)) == 0 &&
+ online == B_FALSE) || error == ENODEV) {
+ /* if the disk is offline, report it as bad */
+ bad_disk = B_TRUE;
+ error = 0;
+ } else
+
+ if (error == 0 &&
+ (((error = disk_get_media_type(disk, &mtype)) != 0) ||
+ ((error = disk_get_drive_type(disk, &dtype)) != 0)) &&
+ error == ENODEV) {
+ /*
+ * if any disk attribute access fails with ENODEV
+ * report it as bad
+ */
+ bad_disk = B_TRUE;
+ error = 0;
+ } else {
+
+ /*
+ * Determine whether disk is fixed by checking its
+ * drive type. If drive type is unknown, check media
+ * type.
+ */
+ int isfixed = (dtype == DM_DT_FIXED ||
+ (dtype == DM_DT_UNKNOWN && mtype == DM_MT_FIXED));
+
+ if (!isfixed) {
+ continue; /* ignore non-fixed disks */
+ }
+
+#if defined(i386)
+ if (((error = disk_get_has_solaris_partition(disk,
+ &solpart)) != 0) || (solpart != B_TRUE)) {
+
+ /* X86 drive has no solaris partition, report as bad */
+ oprintf(OUTPUT_DEBUG,
+ gettext("%s has no solaris FDISK partition.\n"));
+
+ bad_disk = B_TRUE;
+ }
+#endif /* defined(i386) */
+
+ }
+
+ if (bad_disk) {
+ /* remember bad disks and continue */
+ if (dlist_contains(*bad,
+ (void *)disk, compare_descriptor_names) != B_TRUE) {
+ dlist_t *item = dlist_new_item((void *)disk);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ *bad = dlist_append(item, *bad, AT_TAIL);
+ }
+ }
+ continue;
+ }
+
+ /* get disk name and multipath aliases */
+ if ((error = disk_get_aliases(disk, &aliases)) == 0) {
+ dlist_t *iter;
+ boolean_t disk_name_set = B_FALSE;
+
+ for (iter = aliases;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ dm_descriptor_t ap = (uintptr_t)iter->obj;
+ char *alias;
+
+ if ((error = get_name(ap, &alias)) == 0) {
+ /* save first alias as display name */
+ if (disk_name_set != B_TRUE) {
+ /* make sure DID disk alias is fully qualified */
+
+ if (is_did_disk_name(alias) == B_TRUE) {
+ char *qual_name =
+ make_fully_qualified_did_device_name(alias);
+
+ set_display_name(disk, qual_name);
+ oprintf(OUTPUT_DEBUG,
+ gettext("DID disk name: %s\n"),
+ qual_name);
+ } else {
+ set_display_name(disk, alias);
+ oprintf(OUTPUT_DEBUG,
+ gettext("disk name: %s\n"),
+ alias);
+ }
+ disk_name_set = B_TRUE;
+
+ } else {
+ /* save others as aliases */
+ set_alias(disk, alias);
+ oprintf(OUTPUT_DEBUG,
+ gettext(" alias: %s\n"),
+ alias);
+ }
+ }
+ }
+
+ dlist_free_items(aliases, NULL);
+ }
+
+ if (error == 0) {
+ dlist_t *item = dlist_new_item((void *)disk);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ *known =
+ dlist_insert_ordered(item, *known,
+ ASCENDING, compare_desc_display_names);
+ }
+ }
+ }
+
+ if (ddp != NULL) {
+ free(ddp);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: generate_known_slices(dlist_t *disks,
+ * dlist_t **known, dlist_t **bad)
+ *
+ * OUTPUT: disks - a pointer to a list of known disks
+ * known - a pointer to a dlist_t list to hold the known slices
+ * bad - a pointer to a dlist_t to hold the bad slices
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Examines input list of known disks and determines the slices
+ * attached to each.
+ *
+ * Some slices returned from libdiskmgt may not really exist,
+ * this is detected when trying to get more information about
+ * the slice -- ENODEV is returned. Any such slices will be
+ * added to the bad slice list.
+ */
+static int
+generate_known_slices(
+ dlist_t *disks,
+ dlist_t **known,
+ dlist_t **bad)
+{
+ dlist_t *iter;
+ int error = 0;
+
+ /* iterate list of disks and add their slices to the known list */
+ for (iter = disks; (iter != NULL) && (error == 0); iter = iter->next) {
+
+ dm_descriptor_t disk = (uintptr_t)iter->obj;
+ dlist_t *slices = NULL;
+ dlist_t *iter1;
+ char *dname = NULL;
+ boolean_t disk_ctd_alias_derived = B_FALSE;
+
+ if (((error = disk_get_slices(disk, &slices)) != 0) ||
+ ((error = get_display_name(disk, &dname)) != 0)) {
+ continue;
+ }
+
+ for (iter1 = slices;
+ (iter1 != NULL) && (error == 0);
+ iter1 = iter1->next) {
+
+ dm_descriptor_t slice = (uintptr_t)iter1->obj;
+ uint32_t index = 0;
+ nvlist_t *attrs = NULL;
+ char *sname = NULL;
+
+ if (((error = get_name(slice, &sname)) != 0) ||
+ ((error = slice_get_index(slice, &index)) != 0) ||
+ ((error = get_cached_attributes(slice, &attrs)) != 0)) {
+
+ if (error == ENODEV) {
+ /* bad slice, remember it and continue */
+ dlist_t *item = dlist_new_item((void *)slice);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ *bad = dlist_insert_ordered(
+ item, *bad,
+ ASCENDING, compare_descriptor_names);
+ error = 0;
+ }
+ }
+ continue;
+ }
+
+ if ((error == 0) && (is_did_slice_name(sname) == B_TRUE) &&
+ (disk_ctd_alias_derived == B_FALSE)) {
+ /* BEGIN CSTYLED */
+ /*
+ * If the slice name is a DID name, get the local CTD
+ * name for slice, extract the disk name and add it as
+ * an alias for the disk.
+ *
+ * This is the only way to derive the CTD alias for the
+ * disk when DID is enabled.
+ *
+ * The disk_ctd_alias_derived flag ensure the disk's
+ * CTD alias is only set once.
+ *
+ * The slice's CTD aliases are then derived from the
+ * disk's CTD alias in the normal, non-DID name processing
+ * which happens below.
+ */
+ /* END CSTYLED */
+ char *local = NULL;
+ if ((error = nvlist_lookup_string(attrs, DM_LOCALNAME,
+ &local)) != 0) {
+ if (error == ENOENT) {
+ /* no local name -> no DID */
+ error = 0;
+ }
+ } else {
+ char *localdisk = NULL;
+ char *diskonly = NULL;
+ if ((error = extract_diskname(local,
+ &localdisk)) == 0) {
+ if ((diskonly = strrchr(localdisk, '/')) != NULL) {
+ ++diskonly;
+ } else {
+ diskonly = localdisk;
+ }
+ oprintf(OUTPUT_DEBUG,
+ gettext(" set DID disk CTD alias: %s\n"),
+ diskonly);
+ error = set_alias(disk, diskonly);
+ free(localdisk);
+ disk_ctd_alias_derived = B_TRUE;
+ }
+ }
+ }
+
+ /* derive slice display name from disk's display name */
+ if (error == 0) {
+ if ((error = make_slicename_for_diskname_and_index(
+ dname, index, &sname)) == 0) {
+ error = set_display_name(slice, sname);
+ }
+ }
+
+ /* set slice aliases using disk aliases */
+ if (error == 0) {
+ dlist_t *aliases = NULL;
+ if ((error = get_aliases(disk, &aliases)) == 0) {
+
+ dlist_t *iter2 = aliases;
+ for (; (iter2 != NULL) && (error == 0);
+ iter2 = iter2->next) {
+
+ char *dalias = (char *)iter2->obj;
+ char *salias = NULL;
+
+ if ((error = make_slicename_for_diskname_and_index(
+ dalias, index, &salias)) == 0) {
+ error = set_alias(slice, salias);
+ free(salias);
+ }
+ }
+ dlist_free_items(aliases, free);
+ }
+ }
+
+ if (error == 0) {
+ dlist_t *item = dlist_new_item((void *)slice);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ *known =
+ dlist_insert_ordered(
+ item, *known,
+ ASCENDING, compare_desc_display_names);
+ }
+ }
+ }
+
+ dlist_free_items(slices, NULL);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: generate_known_hbas(dlist_t *disks, dlist_t **known)
+ *
+ * INPUT: diskset - a char * diskset name.
+ *
+ * OUTPUT: populates the list of known HBAs.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Examines known disk list and derives the list of known HBAs.
+ *
+ * Determines the CTD name for an HBA and saves it.
+ */
+static int
+generate_known_hbas(
+ dlist_t *disks,
+ dlist_t **known)
+{
+ dlist_t *iter;
+ int error = 0;
+
+ /*
+ * for each known disk follow its HBA connections and
+ * assemble the list of known HBAs.
+ */
+ for (iter = disks;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ dm_descriptor_t disk = (uintptr_t)iter->obj;
+ dlist_t *hbas = NULL;
+ dlist_t *iter2 = NULL;
+ dlist_t *iter3 = NULL;
+ dlist_t *aliases = NULL;
+ char *dname = NULL;
+
+ ((error = get_display_name(disk, &dname)) != 0) ||
+ (error = disk_get_aliases(disk, &aliases)) ||
+ (error = disk_get_hbas(disk, &hbas));
+
+ if (error == 0) {
+
+ if ((hbas == NULL) || (dlist_length(hbas) == 0)) {
+
+ oprintf(OUTPUT_DEBUG,
+ gettext("Disk %s has no HBA/Controller?!\n"),
+ dname);
+ error = -1;
+
+ dlist_free_items(hbas, NULL);
+ dlist_free_items(aliases, NULL);
+
+ continue;
+ }
+
+ for (iter2 = hbas, iter3 = aliases;
+ iter2 != NULL && iter3 != NULL;
+ iter2 = iter2->next, iter3 = iter3->next) {
+
+ dm_descriptor_t hba = (uintptr_t)iter2->obj;
+ dm_descriptor_t alias = (uintptr_t)iter3->obj;
+ dlist_t *item = NULL;
+
+ /* scan list of known HBAs and see if known */
+ if (dlist_contains(*known,
+ (void*)hba, compare_descriptor_names) == B_TRUE) {
+ /* known HBA */
+ continue;
+ }
+
+ /* see if HBA supports MPXIO */
+ if ((error == 0) && (_mpxio_enabled != B_TRUE)) {
+ hba_is_multiplex(hba, &_mpxio_enabled);
+ }
+
+ /* generate a CTD name for HBA */
+ error = generate_known_hba_name(hba, alias, disk);
+ if (error == 0) {
+ /* add to known HBA list */
+ if ((item = dlist_new_item((void *)hba)) == NULL) {
+ error = ENOMEM;
+ } else {
+ *known =
+ dlist_insert_ordered(item, *known,
+ ASCENDING, compare_desc_display_names);
+ }
+ }
+ }
+ }
+
+ dlist_free_items(aliases, NULL);
+ dlist_free_items(hbas, NULL);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: generate_known_hba_name(dm_descriptor_t hba,
+ * dm_descriptor_t alias, char *diskname)
+ *
+ * INPUT: hba - a dm_descriptor_t HBA handle.
+ * alias - a dm_descriptor_t disk alias handle.
+ * diskname - a char * disk name
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Sets the CTD name for the input HBA.
+ *
+ * The CTD name for the HBA is generated from the input
+ * disk alias (ex: cXdXtXsX) or from the disk name if
+ * the input alias is a DID name (ex: dX).
+ */
+static int
+generate_known_hba_name(
+ dm_descriptor_t hba,
+ dm_descriptor_t alias,
+ dm_descriptor_t disk)
+{
+ char *hbaname = NULL;
+ char *aliasname = NULL;
+ int error = 0;
+
+ ((error = get_name(alias, &aliasname)) != 0) ||
+ (error = extract_hbaname(aliasname, &hbaname));
+ if (error != 0) {
+ free(hbaname);
+ return (error);
+ }
+
+ /* see if the input alias is a DID name... */
+ if (is_did_disk_name(aliasname) == B_TRUE) {
+
+ /* look for a non-DID name in disk's aliases */
+ dlist_t *aliases = NULL;
+ error = get_aliases(disk, &aliases);
+
+ for (; (error == 0) && (aliases != NULL);
+ aliases = aliases->next) {
+
+ aliasname = (char *)aliases->obj;
+ if (is_did_disk_name(aliasname) != B_TRUE) {
+ /* this is the "local" CTD name generated by */
+ /* generate_known_disks() above */
+ error = extract_hbaname(aliasname, &hbaname);
+ if ((error == 0) && (hbaname != NULL)) {
+ set_display_name(hba, hbaname);
+ break;
+ }
+ }
+ }
+ dlist_free_items(aliases, free);
+
+ } else {
+ /* use whatever was derived from the alias name */
+ set_display_name(hba, hbaname);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: print_known_devices()
+ *
+ * PURPOSE: Print out the known devices.
+ *
+ * Iterates the lists of known slices, disks and HBAs
+ * and prints out their CTD and device names.
+ */
+static void
+print_known_devices(
+ char *diskset)
+{
+ int i = 0;
+ struct {
+ char *msg;
+ dlist_t *list;
+ } devs[3];
+
+ devs[0].msg = gettext("HBA/Controllers");
+ devs[0].list = _known_hbas;
+ devs[1].msg = gettext("disks");
+ devs[1].list = _known_disks;
+ devs[2].msg = gettext("slices");
+ devs[2].list = _known_slices;
+
+ for (i = 0; i < 3; i++) {
+
+ oprintf(OUTPUT_VERBOSE,
+ gettext("\n These %s are known:\n\n"),
+ devs[i].msg);
+
+ print_device_list(devs[i].list);
+ }
+}
+
+/*
+ * FUNCTION: get_usable_slices(dlist_t **list)
+ *
+ * OUTPUT: list - a dlist_t pointer to hold the returned list of
+ * devices.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Public accessors the the modules private lists of
+ * available devices.
+ *
+ * The functions are keyed by diskset name in the event
+ * objects in different disksets are loaded concurrently.
+ */
+int
+get_usable_slices(
+ dlist_t **list)
+{
+ *list = _usable_slices;
+
+ return (0);
+}
+
+int
+get_usable_disks(
+ dlist_t **list)
+{
+ *list = _usable_disks;
+
+ return (0);
+}
+
+int
+get_usable_hbas(
+ dlist_t **list)
+{
+ *list = _usable_hbas;
+
+ return (0);
+}
+
+/*
+ * FUNCTION: generate_usable_disks_and_slices_in_local_set(dlist_t **classes,
+ * dlist_t **bad_disks, dlist_t **usable_disks,
+ * dlist_t **usable_slices)
+ *
+ * OUTPUT: used_classes - a pointer to a list of slice_class_t structs
+ * updated with known slices that have detected uses
+ * added to the correct class'e list of slices.
+ * bad_disks - a pointer to a list of bad/unusable disks updated
+ * with any bad disks that were detected
+ * useable_disks - a pointer to a list of usable disks
+ * useable_slices - a pointer to a list of usable slices
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Scans the disks in the local set to determine which are
+ * usable during layout processing.
+ *
+ * Determines which are usable by layout using usages detected
+ * by libdiskmgt.
+ */
+static int
+generate_usable_disks_and_slices_in_local_set(
+ dlist_t **classes,
+ dlist_t **bad_slices,
+ dlist_t **usable_disks,
+ dlist_t **usable_slices)
+{
+ char *dsname = MD_LOCAL_NAME;
+ dlist_t *disks;
+ dlist_t *iter;
+ int error;
+
+ /* Get disks in local set */
+ error = get_disks_in_diskset(dsname, &disks);
+ if (error != 0) {
+ return (error);
+ }
+
+ /* For each disk in this set... */
+ for (iter = disks; iter != NULL && error == 0; iter = iter->next) {
+ dm_descriptor_t disk = (uintptr_t)iter->obj;
+ dlist_t *slices;
+
+ /* Get slices on this disk */
+ error = disk_get_slices(disk, &slices);
+ if (error == 0) {
+ dlist_t *iter2;
+
+ /*
+ * Assume disk is available until a bad or unavailable
+ * slice is found
+ */
+ boolean_t avail = B_TRUE;
+ boolean_t bad_disk = B_FALSE;
+
+ /* For each slice on this disk... */
+ for (iter2 = slices;
+ iter2 != NULL && error == 0 &&
+ avail == B_TRUE && bad_disk == B_FALSE;
+ iter2 = iter2->next) {
+
+ dm_descriptor_t slice = (uintptr_t)iter2->obj;
+ dlist_t *bad_slices_on_this_disk = NULL;
+
+ /* Is this slice available? */
+ error = check_slice_usage(dsname, slice,
+ disk, &avail, &bad_slices_on_this_disk, classes);
+
+ /* Is the slice bad (inaccessible)? */
+ if (error != 0 && bad_slices_on_this_disk != NULL) {
+ bad_disk = B_TRUE;
+ *bad_slices = dlist_append_list(
+ *bad_slices, bad_slices_on_this_disk);
+ }
+ }
+
+ /* Is the disk available? */
+ if (error == 0 && bad_disk == B_FALSE && avail == B_TRUE) {
+ error = dlist_append_object(
+ (void *)disk, usable_disks, AT_TAIL);
+ }
+
+ dlist_free_items(slices, NULL);
+ }
+ }
+
+ dlist_free_items(disks, NULL);
+
+ if (error == 0) {
+ /* BEGIN CSTYLED */
+ /*
+ * Now reslice usable disks in the local set to
+ * simulate the slices they'll have when they're added
+ * to the named disk set, and add these resulting
+ * virtual slices to the list of available slices.
+ */
+ /* END CSTYLED */
+ error = generate_virtual_slices(*usable_disks, usable_slices);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: generate_virtual_slices(dlist_t *unused, dlist_t **usable)
+ *
+ * INPUT: slice_classes - a list of unused slice dm_descriptor_t handles.
+ *
+ * OUTPUT: usable - pointer to the list of usable slices, updated
+ * with any created 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...
+ *
+ * Search the input list of slice classes for the entry
+ * containing slices known to be available for use by layout.
+ *
+ * Iterate the list of unused slices and determine the set
+ * of unique disks.
+ *
+ * For each unique disk, create virtual slice descriptors to
+ * represent those that will exist if/when the disk is added
+ * to the diskset.
+ *
+ * Add theese virtual slices to the list of usable slices.
+ */
+static int
+generate_virtual_slices(
+ dlist_t *avail_disks_local_set,
+ dlist_t **usable)
+{
+ dlist_t *iter = NULL;
+ int error = 0;
+
+ /* generate virtual slices */
+ error = create_virtual_slices(avail_disks_local_set);
+ if (error == 0) {
+
+ get_virtual_slices(&iter);
+ for (; (iter != NULL) && (error == 0); iter = iter->next) {
+
+ dlist_t *item = dlist_new_item((void *) iter->obj);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ *usable =
+ dlist_insert_ordered(item, *usable,
+ ASCENDING, compare_desc_display_names);
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: generate_usable_disks_and_slices_in_named_set(char *dsname,
+ * dlist_t **classes, dlist_t **bad_slices,
+ * dlist_t **usable_slices, dlist_t **usable_disks)
+ *
+ * INPUT: dsname - a char * diskset name.
+ *
+ * OUTPUT: classes - pointer to a list of slice_class_t structs,
+ * updated to include slices in the disk set with
+ * known uses.
+ * bad_slices - pointer to a list of bad/unusable slices,
+ * updated to include slices in the disk set that
+ * are inaccessible or no longer existent.
+ * usable_slices - pointer to a list of usable slices in the
+ * disk set.
+ * usable_disks - pointer to a list of usable disks in the
+ * disk set.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: 1. determine the disks in the named disk set
+ * 2. determine the used slices on the disks
+ * 3. determine the unused slices on the disks
+ * 4. look for unused space on the disks and collect it
+ * into an existing unused slice, or create a new
+ * virtual slice.
+ */
+static int
+generate_usable_disks_and_slices_in_named_set(
+ char *dsname,
+ dlist_t **classes,
+ dlist_t **bad_slices,
+ dlist_t **usable_disks,
+ dlist_t **usable_slices)
+{
+ dlist_t *disks = NULL;
+ dlist_t *iter = NULL;
+ int error = 0;
+
+ error = get_disks_in_diskset(dsname, &disks);
+ if (error != 0) {
+ return (error);
+ }
+
+ /* For each disk... */
+ for (iter = disks;
+ iter != NULL && error == 0;
+ iter = iter->next) {
+
+ dm_descriptor_t disk = (uintptr_t)iter->obj;
+ dlist_t *iter2;
+ dlist_t *slices = NULL;
+ dlist_t *bad_slices_on_this_disk = NULL;
+ dlist_t *used_slices_on_this_disk = NULL;
+ dlist_t *unused_slices_on_this_disk = NULL;
+ boolean_t bad_disk = B_FALSE;
+
+ error = disk_get_slices(disk, &slices);
+ if (error != 0) {
+ break;
+ }
+
+ /* Determine the used, unused, and bad slices on the disk */
+
+ /* For each slice... */
+ for (iter2 = slices;
+ iter2 != NULL && error == 0 && bad_disk == B_FALSE;
+ iter2 = iter2->next) {
+
+ dm_descriptor_t slice = (uintptr_t)iter2->obj;
+
+ boolean_t rsvd = B_FALSE;
+ boolean_t avail = B_FALSE;
+
+ /* Get slice usage */
+ if (((error = is_reserved_slice(slice, &rsvd)) == 0) &&
+ ((error = check_slice_usage(dsname, slice, disk, &avail,
+ &bad_slices_on_this_disk, classes)) == 0)) {
+
+ /* Is the slice bad (inaccessible)? */
+ if (bad_slices_on_this_disk != NULL) {
+ *bad_slices = dlist_append_list(
+ *bad_slices, bad_slices_on_this_disk);
+ /*
+ * Since one slice on this disk is bad, don't
+ * use any slices on this disk
+ */
+ bad_disk = B_TRUE;
+ } else {
+
+ dlist_t *item = dlist_new_item((void *)slice);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ /* Add slice to used/unused list as appropriate */
+ if (avail == B_TRUE && rsvd == B_FALSE) {
+ unused_slices_on_this_disk = dlist_append(
+ item, unused_slices_on_this_disk, AT_TAIL);
+ } else {
+ used_slices_on_this_disk =
+ dlist_insert_ordered(item,
+ used_slices_on_this_disk,
+ ASCENDING, compare_start_blocks);
+ }
+ }
+ }
+ }
+ }
+
+ /* Done iterating slices */
+
+ if (error == 0 && bad_disk == B_FALSE) {
+ /* For each unused slice... */
+ for (iter2 = unused_slices_on_this_disk;
+ iter2 != NULL && error == 0;
+ iter2 = iter2->next) {
+
+ dm_descriptor_t slice = (uintptr_t)iter2->obj;
+ error = update_slice_attributes(slice, 0, 0, 0);
+
+ /* Only do this once */
+ if (error == 0 && iter2 == unused_slices_on_this_disk) {
+ error = add_modified_disk(NULL, disk);
+ }
+ }
+
+ if (error == 0) {
+ /* Create usable slices from the used/unused slice lists */
+ error = create_usable_slices(disk, used_slices_on_this_disk,
+ unused_slices_on_this_disk, usable_slices);
+ if (error == 0) {
+ error = dlist_append_object(
+ (void *)disk, usable_disks, AT_TAIL);
+ }
+ }
+ }
+
+ dlist_free_items(slices, NULL);
+ dlist_free_items(used_slices_on_this_disk, NULL);
+ dlist_free_items(unused_slices_on_this_disk, NULL);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: create_usable_slices(dm_descriptor_t disk, dlist_t *used,
+ * dlist_t *unused, dlist_t **usable);
+ *
+ * INPUT: disk - a dm_descriptor_t disk handle
+ * used - pointer to a list of pvt_t structs
+ * representing existing used slices
+ * on the input disk.
+ * unused - pointer to a list of pvt_t structs
+ * representing existing unused slices
+ * on the input disk.
+ *
+ * OUTPUT: usable - pointer to a list of pvts representing slices
+ * which can be used for new volume layouts.
+ *
+ * Slices in this list have any available space on the
+ * disk collected into the fewest, lowest indexed slices
+ * possible.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: helper for generate_usable_slices_and_disks_in_diskset() which
+ * turns any detected free space on the input disk into one or
+ * more slices.
+ */
+static int
+create_usable_slices(
+ dm_descriptor_t disk,
+ dlist_t *used,
+ dlist_t *unused,
+ dlist_t **usable)
+{
+ dlist_t *iter;
+ int error = 0;
+ boolean_t first = B_TRUE;
+ dlist_t *next_unused = unused;
+
+ char *dname = NULL;
+ uint64_t disk_firstblk = 0;
+ uint64_t disk_nblks = 0;
+ uint64_t disk_endblk = 0;
+
+ oprintf(OUTPUT_DEBUG,
+ gettext("\n create_usable_slices for disk\n"));
+
+ /* get necessary info about disk: */
+ error = get_display_name(disk, &dname);
+ if (error != 0) {
+ return (error);
+ }
+
+ /* disk start block is first usable block */
+ error = disk_get_start_block(disk, &disk_firstblk);
+ if (error != 0) {
+ return (error);
+ }
+
+ /* disk size determines last usable disk block */
+ error = disk_get_size_in_blocks(disk, &disk_nblks);
+ if (error != 0) {
+ return (error);
+ }
+
+ disk_endblk = disk_firstblk + disk_nblks - 1;
+
+ /* search for gaps before, between and after used slices */
+ for (iter = used; iter != NULL && error == 0; iter = iter->next) {
+
+ dm_descriptor_t cur = (uintptr_t)iter->obj;
+
+ uint64_t cur_stblk = 0;
+ uint64_t cur_nblks = 0;
+ uint64_t cur_endblk = 0;
+ uint32_t cur_index = 0;
+
+ uint64_t new_stblk = 0;
+ uint64_t new_endblk = 0;
+
+ char *sname = NULL;
+ (void) get_display_name(cur, &sname);
+
+ if (((error = slice_get_index(cur, &cur_index)) != 0) ||
+ ((error = slice_get_start_block(cur, &cur_stblk)) != 0) ||
+ ((error = slice_get_size_in_blocks(cur, &cur_nblks)) != 0)) {
+ continue;
+ }
+
+ cur_endblk = cur_stblk + cur_nblks - 1;
+
+ oprintf(OUTPUT_DEBUG,
+ gettext(" used slice %d (%10llu to %10llu)\n"),
+ cur_index, cur_stblk, cur_endblk);
+
+ if (first == B_TRUE) {
+ /* first slice: make sure it starts at disk_firstblk */
+ first = B_FALSE;
+ if (cur_stblk != disk_firstblk) {
+ /* close gap at beginning of disk */
+ new_stblk = disk_firstblk;
+ new_endblk = cur_stblk - 1;
+
+ oprintf(OUTPUT_DEBUG,
+ gettext(" unused space before first "
+ "used slice\n"));
+ }
+ }
+
+ if (iter->next != NULL) {
+ /* check for gap between slices */
+ dm_descriptor_t next = (uintptr_t)iter->next->obj;
+ uint64_t next_stblk = 0;
+ uint32_t next_index = 0;
+
+ if (((error = slice_get_start_block(next, &next_stblk)) == 0) &&
+ ((error = slice_get_index(next, &next_index)) == 0)) {
+ if (cur_endblk != next_stblk - 1) {
+ /* close gap between slices */
+ new_stblk = cur_endblk + 1;
+ new_endblk = next_stblk - 1;
+
+ oprintf(OUTPUT_DEBUG,
+ gettext(" unused space between slices "
+ "%d and %d\n"), cur_index, next_index);
+ }
+ }
+
+ } else {
+ /* last slice: make sure it includes last block on disk */
+ if (cur_endblk != disk_endblk) {
+ /* close gap at end of disk */
+ new_stblk = cur_endblk + 1;
+ new_endblk = disk_endblk;
+
+ oprintf(OUTPUT_DEBUG,
+ gettext(" unused space after last slice "
+ "cur_endblk: %llu disk_endblk: %llu\n"),
+ cur_endblk, disk_endblk);
+ }
+ }
+
+ if ((error == 0) && (new_endblk != 0)) {
+ error = add_new_usable(disk, new_stblk,
+ new_endblk - new_stblk + 1, &next_unused, usable);
+ }
+ }
+
+ if (error != 0) {
+ dlist_free_items(*usable, free);
+ *usable = NULL;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: add_new_usable(dm_descriptor_t disk, uint64_t stblk,
+ * uint64_t nblks, dlist_t **next_unused,
+ * dlist_t **usable);
+ *
+ * INPUT: disk - a dm_descriptor_t disk handle
+ * stblk - start block of the usable space
+ * nblks - number of usable blocks
+ * next_unused - pointer to the next unused slice
+ *
+ * OUTPUT: next_unused - updated pointer to the next unused slice
+ * usable - possibly updated pointer to a list of slices on
+ * the disk with usable space
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: helper for create_usable_slices() which turns free space
+ * on the input disk into a usable slice.
+ *
+ * If possible an existing unused slice will be recycled
+ * into a usable slice. If there are none, a new virtual
+ * slice will be created.
+ */
+static int
+add_new_usable(
+ dm_descriptor_t disk,
+ uint64_t stblk,
+ uint64_t nblks,
+ dlist_t **next_unused,
+ dlist_t **usable)
+{
+ dm_descriptor_t new_usable = 0;
+ int error = 0;
+
+ /* try to use an existing unused slice for the usable slice */
+ if (*next_unused != NULL) {
+ new_usable = (uintptr_t)((*next_unused)->obj);
+ *next_unused = (*next_unused)->next;
+
+ oprintf(OUTPUT_DEBUG,
+ gettext("\trecyling used slice into usable slice "
+ "start: %llu, end: %llu\n"),
+ stblk, stblk + nblks + 1);
+ }
+
+ if (new_usable == NULL) {
+ /* no unused slices, try to make a new virtual slice */
+ uint32_t index = UINT32_MAX;
+ error = disk_get_available_slice_index(disk, &index);
+ if ((error == 0) && (index != UINT32_MAX)) {
+
+ char *dname = NULL;
+ error = get_display_name(disk, &dname);
+ if (error == 0) {
+
+ char buf[MAXNAMELEN];
+ (void) snprintf(buf, MAXNAMELEN-1, "%ss%d", dname, index);
+ error = add_virtual_slice(buf, index, 0, 0, disk);
+ if (error == 0) {
+ /* retrieve the virtual slice */
+ error = slice_get_by_name(buf, &new_usable);
+ }
+ }
+ }
+ }
+
+ if ((error == 0) && (new_usable != (dm_descriptor_t)0)) {
+ /* BEGIN CSTYLED */
+ /*
+ * have an unused slice, update its attributes to reflect
+ * the usable space it represents
+ */
+ /* END CSTYLED */
+ uint64_t disk_blksz = 0;
+ error = disk_get_blocksize(disk, &disk_blksz);
+ if (error == 0) {
+ error = update_slice_attributes(new_usable, stblk,
+ nblks, nblks * disk_blksz);
+ if (error == 0) {
+ error = dlist_append_object(
+ (void *)new_usable, usable, AT_TAIL);
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: update_slice_attributes(dm_descriptor_t slice, uint64_t stblk,
+ * uint64_t nblks, uint64_t nbytes)
+ *
+ * INPUT: slice - a dm_descriptor_t slice handle
+ * stblk - start block of the usable space
+ * nblks - size of slice in blocks
+ * nbytes - size of slice in bytes
+ *
+ * SIDEEFFECT: adds a modification record for the slice.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: utility which updates several slice attributes in one call.
+ */
+static int
+update_slice_attributes(
+ dm_descriptor_t slice,
+ uint64_t stblk,
+ uint64_t nblks,
+ uint64_t nbytes)
+{
+ char *sname = NULL;
+ uint32_t index = 0;
+ int error = 0;
+
+ if ((error = get_display_name(slice, &sname)) == 0) {
+ if ((error = slice_get_index(slice, &index)) == 0) {
+ if ((error = slice_set_start_block(slice, stblk)) == 0) {
+ if ((error = slice_set_size_in_blocks(slice, nblks)) == 0) {
+ if (nblks == 0) {
+ error = add_slice_to_remove(sname, index);
+ } else {
+ error = assemble_modified_slice((dm_descriptor_t)0,
+ sname, index, stblk, nblks, nbytes, NULL);
+ }
+ }
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: generate_usable_hbas(dlist_t *slices,
+ * dlist_t **usable)
+ *
+ * INPUT: disks - a list of usable disks.
+ *
+ * OUTPUT: usable - a populated list of usable HBAs.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Examines usable disk list and derives the list of usable HBAs.
+ *
+ */
+static int
+generate_usable_hbas(
+ dlist_t *disks,
+ dlist_t **usable)
+{
+ dlist_t *iter;
+ int error = 0;
+
+ /*
+ * for each usable disk, follow its HBA connections and
+ * add them to the list of usable HBAs.
+ */
+ for (iter = disks; (iter != NULL) && (error == 0); iter = iter->next) {
+
+ dm_descriptor_t dp = NULL;
+ dlist_t *hbas = NULL;
+ dlist_t *iter2 = NULL;
+
+ dp = (uintptr_t)iter->obj;
+
+ error = disk_get_hbas(dp, &hbas);
+ if (error == 0) {
+
+ for (iter2 = hbas;
+ (iter2 != NULL) && (error == 0);
+ iter2 = iter2->next) {
+
+ dm_descriptor_t hba = (uintptr_t)iter2->obj;
+ dlist_t *item = NULL;
+
+ /* scan list of usable HBAs and see if known */
+ if (dlist_contains(*usable,
+ (void*)hba, compare_descriptor_names) == B_TRUE) {
+ /* known HBA, continue to next HBA/alias */
+ continue;
+ }
+
+ /* add this HBA to the usable list */
+ if ((item = dlist_new_item((void *)hba)) == NULL) {
+ error = ENOMEM;
+ } else {
+ *usable =
+ dlist_insert_ordered(item, *usable,
+ ASCENDING, compare_desc_display_names);
+ }
+ }
+ }
+
+ dlist_free_items(hbas, NULL);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: check_slice_usage(char *dsname, dm_descriptor_t slice,
+ * dm_descriptor_t disk, boolean_t *avail,
+ * dlist_t **bad, dlist_t **classes)
+ *
+ * INPUT: dsname - a char * diskset name.
+ * slice - a dm_descriptor_t handle for a known slices.
+ * disk - a dm_descriptor_t handle the slice's disk.
+ *
+ * OUTPUT: avail - a boolean_t to hold the slice's availability.
+ * bad - pointer to a list of bad/unusable slices,
+ * possibly updated if the input slice
+ * was determined to be inaccessible.
+ * classes - pointer to a list of slice_class_t structs,
+ * possibly updated to include the input slice
+ * if it has a known use.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Handles the details of
+ * determining usage and/or availability of a single slice.
+ *
+ * Queries the device library for the input slice's detectable
+ * usage status.
+ *
+ * If the slice has a detected usage, its name is added to
+ * the appropriate slice_class_t list in the input list of
+ * slice classes, this is only done if verbose output was
+ * requested.
+ */
+static int
+check_slice_usage(
+ char *dsname,
+ dm_descriptor_t slice,
+ dm_descriptor_t disk,
+ boolean_t *avail,
+ dlist_t **bad,
+ dlist_t **classes)
+{
+ boolean_t online = B_FALSE;
+ boolean_t used = B_FALSE;
+ nvlist_t *stats = NULL;
+ char *name = NULL;
+ char *used_by = NULL;
+ char *use_detail = NULL;
+ int error = 0;
+
+ *avail = B_FALSE;
+
+ if (((error = get_display_name(slice, &name)) != 0) ||
+ (error = disk_get_is_online(disk, &online))) {
+ return (error);
+ }
+
+ /*
+ * if the disk is known to be offline, skip getting status
+ * for the slice since it will just fail and return ENODEV.
+ */
+ if (online != B_TRUE) {
+ error = ENODEV;
+ } else {
+ stats = dm_get_stats(slice, DM_SLICE_STAT_USE, &error);
+ }
+
+ if (error != 0) {
+ if (error == ENODEV) {
+ dlist_t *item = dlist_new_item((void *)slice);
+ oprintf(OUTPUT_TERSE,
+ gettext("Warning: unable to get slice information "
+ "for %s, it will not be used.\n"), name);
+
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ error = 0;
+ *bad = dlist_insert_ordered(item, *bad, ASCENDING,
+ compare_desc_display_names);
+ }
+ } else {
+ oprintf(OUTPUT_TERSE,
+ gettext("check_slice_usage: dm_get_stats for "
+ "%s failed %d\n"),
+ name, error);
+ }
+
+ return (error);
+ }
+
+ /*
+ * check if/how the slice is currently being used,
+ * device library provides this info in the nvpair_t list:
+ *
+ * stat_type is DM_SLICE_STAT_USE
+ * used_by: string (mount, svm, lu, vxvm, fs)
+ * used_name: string
+ *
+ */
+ if (stats != NULL) {
+ error = get_string(stats, DM_USED_BY, &used_by);
+ if (error != 0) {
+ if (error == ENOENT) {
+ used_by = NULL;
+ error = 0;
+ } else {
+ oprintf(OUTPUT_TERSE,
+ gettext("check_slice_usage: dm_get_stats.%s for "
+ "%s failed %d\n"),
+ DM_USED_BY, name, error);
+ }
+ }
+
+ if (error == 0) {
+ error = get_string(stats, DM_USED_NAME, &use_detail);
+ if (error != 0) {
+ if (error == ENOENT) {
+ use_detail = NULL;
+ error = 0;
+ } else {
+ oprintf(OUTPUT_TERSE,
+ gettext("check_slice_usage: "
+ "dm_get_stats.%s for "
+ "%s failed %d\n"),
+ DM_USED_NAME, name, error);
+ }
+ }
+ }
+ }
+
+ if ((error == 0) && (used_by != NULL) && (used_by[0] != '\0')) {
+
+ /* was detected usage SVM? */
+ if (string_case_compare(used_by, DM_USE_SVM) == 0) {
+
+ /* check use_detail, it is in the form diskset:name */
+ if (strncmp("diskset:", use_detail, 8) == 0) {
+
+ /* check disk set name */
+ char *str = strrchr(use_detail, ':');
+ if ((str != NULL) &&
+ (string_case_compare(str+1, dsname) == 0)) {
+
+ /* slice in the right diskset */
+ error = check_svm_slice_usage(
+ dsname, slice, disk, &used, classes);
+
+ } else {
+
+ /* slice in other diskset */
+ save_slice_classification(
+ dsname, slice, disk, used_by, use_detail,
+ classes);
+ used = B_TRUE;
+ }
+
+ } else {
+
+ /* slice is volume component */
+ save_slice_classification(
+ dsname, slice, disk, used_by, use_detail,
+ classes);
+ used = B_TRUE;
+ }
+
+ } else {
+
+ /* save usage */
+ save_slice_classification(
+ dsname, slice, disk, used_by, use_detail,
+ classes);
+ used = B_TRUE;
+ }
+ }
+
+ nvlist_free(stats);
+
+ if (error == 0) {
+ if (used == B_TRUE) {
+ *avail = B_FALSE;
+ } else {
+ *avail = B_TRUE;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: check_svm_slice_usage(char *dsname, dm_descriptor_t slice,
+ * dm_descriptor_t disk, boolean_t *used,
+ * dlist_t **classes)
+ *
+ * INPUT: dsname - a char * diskset name.
+ * slice - a dm_descriptor_t handle for a known slices.
+ * disk - a dm_descriptor_t handle the slice's disk.
+ *
+ * OUTPUT: used - a boolean_t to hold the slice usage status.
+ * classes - pointer to a list of slice_class_t possibly updated
+ * with the input slice's SVM specific usage
+ * classification.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Handles the finer details of
+ * a single slice is being used in the context of SVM.
+ *
+ * Currently, one thing is checked:
+ *
+ * 1. determine if the slice is reserved for metadb replicas.
+ * The convention for disks in disksets is that a single slice
+ * (index 6 or 7) is set aside for metadb replicas.
+ *
+ * If this condition does not hold, the slice is considered
+ * available for use by layout and 'used' is set to B_FALSE.
+ */
+static int
+check_svm_slice_usage(
+ char *dsname,
+ dm_descriptor_t slice,
+ dm_descriptor_t disk,
+ boolean_t *used,
+ dlist_t **classes)
+{
+ boolean_t is_replica = B_FALSE;
+ uint32_t index = 0;
+ char *diskname = NULL;
+ int error = 0;
+
+ ((error = slice_get_index(slice, &index)) != 0) ||
+ (error = get_display_name(disk, &diskname)) ||
+ (error = is_reserved_replica_slice_index(
+ dsname, diskname, index, &is_replica));
+
+ if (error == 0) {
+ if (is_replica == B_TRUE) {
+ /* is replica slice -> used */
+ save_slice_classification(dsname, slice, disk, DM_USE_SVM,
+ gettext("reserved for metadb replicas"), classes);
+ *used = B_TRUE;
+ } else {
+ *used = B_FALSE;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: save_slice_classification(char *dsname, dm_descriptor_t slice,
+ * dm_descriptor_t disk, char *used_by, char *usage_detail,
+ * dlist_t **classes)
+ *
+ * INPUT: dsname - a char * disk set name
+ * slice - a dm_descriptor_t slice handle.
+ * disk - a dm_descriptor_t handle for the slice's disk.
+ * used_by - a char * usage classification.
+ * usage_detail - a char * usage description for the slice.
+ *
+ * OUTPUT: classes - a list of slice_class_t updated to hold a usage
+ * entry for the input slicexs.
+ *
+ * SIDEEFFECT: adds the input slice to the list of known, used slices.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Adds an entry to the
+ * appropriate slice_class_t list of slices. If there is
+ * not an appropriate slice_class_t entry in the input list
+ * of classes, one is added.
+ *
+ * As a performance optimization the slice usage classification
+ * information is only saved if verbose output was requested by
+ * the user.
+ */
+static int
+save_slice_classification(
+ char *dsname,
+ dm_descriptor_t slice,
+ dm_descriptor_t disk,
+ char *usage,
+ char *usage_detail,
+ dlist_t **classes)
+{
+ int error = 0;
+
+ error = add_used_slice(slice);
+
+ if ((error == 0) && (get_max_verbosity() >= OUTPUT_VERBOSE)) {
+
+ dlist_t *iter;
+ dlist_t *item;
+ slice_class_t *class = NULL;
+
+ /* locate class struct matching 'usage' */
+ for (iter = *classes; iter != NULL; iter = iter->next) {
+ class = (slice_class_t *)iter->obj;
+ if (string_case_compare(usage, class->usage) == 0) {
+ break;
+ }
+ }
+
+ if (iter == NULL) {
+ /* add a new class to the list of classes */
+ class = (slice_class_t *)calloc(1, sizeof (slice_class_t));
+ if (class == NULL) {
+ error = ENOMEM;
+ } else {
+ class->usage = strdup(usage);
+ if (class->usage == NULL) {
+ free(class);
+ class = NULL;
+ error = ENOMEM;
+ } else {
+ item = dlist_new_item((void *)class);
+ if (item == NULL) {
+ free(class->usage);
+ free(class);
+ class = NULL;
+ error = ENOMEM;
+ } else {
+ *classes = dlist_append(item, *classes, AT_TAIL);
+ }
+ }
+ }
+ }
+
+ if ((error == 0) && (class != NULL)) {
+
+ char buf[BUFSIZ];
+ char *dup = NULL;
+ char *slicename = NULL;
+
+ (void) get_display_name(slice, &slicename);
+ (void) snprintf(buf, BUFSIZ-1, " %s: %s",
+ slicename, usage_detail);
+ if ((dup = strdup(buf)) == NULL) {
+ error = ENOMEM;
+ } else {
+ if ((item = dlist_new_item((void *)dup)) == NULL) {
+ free(dup);
+ error = ENOMEM;
+ } else {
+ class->sliceinfo =
+ dlist_insert_ordered(
+ item, class->sliceinfo,
+ ASCENDING, compare_strings);
+ }
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: print_usable_devices()
+ *
+ * PURPOSE: Print out the devices determined to be available for
+ * use by layout.
+ *
+ * Iterates the lists of usable slices, disks and HBAs
+ * and prints out their CTD and device names.
+ */
+static void
+print_usable_devices()
+{
+ int i = 0;
+
+ struct {
+ char *msg;
+ dlist_t *list;
+ } devs[3];
+
+ devs[0].msg = gettext("HBA/Controllers");
+ devs[0].list = _usable_hbas;
+ devs[1].msg = gettext("disks");
+ devs[1].list = _usable_disks;
+ devs[2].msg = gettext("slices");
+ devs[2].list = _usable_slices;
+
+ for (i = 0; i < 3; i++) {
+
+ oprintf(OUTPUT_VERBOSE,
+ gettext("\n These %s are usable:\n\n"),
+ devs[i].msg);
+
+ print_device_list(devs[i].list);
+ }
+}
+
+/*
+ * FUNCTION: print_device_list(dlist_t *devices)
+ *
+ * INPUT: devices - a list of device descriptor handles
+ *
+ * PURPOSE: A helper for the print_XXX_devices() routines which iterates
+ * the input list and prints out each device name, CTD name and
+ * alias(es).
+ */
+static void
+print_device_list(
+ dlist_t *devices)
+{
+ dlist_t *iter = NULL;
+
+ for (iter = devices; iter != NULL; iter = iter->next) {
+
+ dm_descriptor_t device = ((uintptr_t)iter->obj);
+ char *name = NULL;
+ char *ctd = NULL;
+ dlist_t *aliases = NULL;
+
+ (void) get_display_name(device, &ctd);
+ (void) get_name(device, &name);
+ oprintf(OUTPUT_VERBOSE,
+ " %-25s %s\n", (ctd != NULL ? ctd : ""), name);
+
+ (void) get_aliases(device, &aliases);
+ for (; aliases != NULL; aliases = aliases->next) {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" (alias: %s)\n"),
+ (char *)aliases->obj);
+ }
+
+ dlist_free_items(aliases, free);
+ }
+}
+
+/*
+ * FUNCTION: print_unusable_devices(
+ * dlist_t *bad_slices, dlist_t *bad_disks,
+ * dlist_t *used_classes)
+ *
+ * INPUT: used_classes - a list of slice_class_t structs
+ *
+ * PURPOSE: Print out the devices determined to be unavailable for
+ * use by layout.
+ *
+ * Iterates the input list of slice classifications and prints
+ * out a description of the class and the slices so classified.
+ *
+ * Also iterates the lists of bad slices and disks (those that
+ * libdiskmgt returned descriptors for but cannot be accessed)
+ * and notes them as unusable.
+ */
+static void
+print_unusable_devices(
+ dlist_t *bad_slices,
+ dlist_t *bad_disks,
+ dlist_t *used_classes)
+{
+ dlist_t *iter = NULL;
+ dlist_t *slices = NULL;
+ char *preamble;
+
+ struct {
+ char *msg;
+ dlist_t *list;
+ } devs[2];
+
+ /* report bad disks and slices */
+ devs[0].msg = gettext("disks");
+ devs[0].list = bad_disks;
+ devs[1].msg = gettext("slices");
+ devs[1].list = bad_slices;
+
+ if (bad_disks != NULL) {
+ oprintf(OUTPUT_VERBOSE,
+#if defined(sparc)
+ gettext("\n These disks are not usable, they may "
+ "may be offline or cannot be accessed:\n\n"));
+#elif defined(i386)
+ gettext("\n These disks are not usable, they may "
+ "may be offline,\n missing a Solaris FDISK "
+ "partition or cannot be accessed:\n\n"));
+#endif
+ print_device_list(bad_disks);
+ }
+
+ if (bad_slices != NULL) {
+ oprintf(OUTPUT_VERBOSE, gettext(
+ "\n These slices, and subsequently the disks on which they\n"
+ "reside, are not usable, they cannot be accessed:\n\n"));
+ print_device_list(bad_slices);
+ }
+
+ /* report used slices and usages */
+ preamble = gettext("\n These slices are not usable, %s:\n\n");
+ for (iter = used_classes; iter != NULL; iter = iter->next) {
+ slice_class_t *class = (slice_class_t *)iter->obj;
+
+ if (class->sliceinfo != NULL) {
+
+ oprintf(OUTPUT_VERBOSE, preamble,
+ get_slice_usage_msg(class->usage));
+
+ slices = class->sliceinfo;
+ for (; slices != NULL; slices = slices->next) {
+ oprintf(OUTPUT_VERBOSE, " %s\n", (char *)slices->obj);
+ }
+ }
+ }
+
+}
+
+/*
+ * FUNCTION: char * get_slice_usage_msg(char *usage)
+ *
+ * INPUT: usage - char * string representing a slice usage classification
+ *
+ * OUTPUT: char * "friendly" usage message
+ *
+ * PURPOSE: the input usage string comes from libdiskmgt and is very terse.
+ *
+ * Convert it into a friendlier usage description suitable for user
+ * consumption.
+ */
+static char *
+get_slice_usage_msg(
+ char *usage)
+{
+ char *str = NULL;
+
+ if (string_case_compare(usage, DM_USE_MOUNT) == 0) {
+ str = gettext("they have mounted filesystems");
+ } else if (string_case_compare(usage, DM_USE_FS) == 0) {
+ str = gettext("they appear to have unmounted filesystems");
+ } else if (string_case_compare(usage, DM_USE_SVM) == 0) {
+ str = gettext("they are utilized by SVM");
+ } else if (string_case_compare(usage, DM_USE_VXVM) == 0) {
+ str = gettext("they are utilized by VxVm");
+ } else if (string_case_compare(usage, DM_USE_LU) == 0) {
+ str = gettext("they are utilized by LiveUpgrade");
+ } else if (string_case_compare(usage, DM_USE_DUMP) == 0) {
+ str = gettext("they are reserved as dump devices");
+ } else if (string_case_compare(usage, USE_DISKSET) == 0) {
+ str = gettext("they have disk set issues");
+ } else {
+ /* libdiskmgt has detected a usage unknown to layout */
+ str = usage;
+ }
+
+ return (str);
+}
+
+/*
+ * FUNCTION: set_alias(dm_descriptor_t desc, char *alias)
+ *
+ * INPUT: desc - a dm_descriptor_t handle.
+ * alias - a char * alias for the device represented
+ * by the descriptor.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Adds the specified alias to the known aliases for the
+ * device associated with the input descriptor.
+ */
+int
+set_alias(
+ dm_descriptor_t desc,
+ char *alias)
+{
+ nvlist_t *attrs = NULL;
+ char **old_aliases = NULL;
+ char **new_aliases = NULL;
+ uint_t nelem = 0;
+ int error = 0;
+ int i = 0;
+
+ if ((error = get_cached_attributes(desc, &attrs)) != 0) {
+ return (error);
+ }
+
+ if ((error = get_string_array(
+ attrs, ATTR_DEVICE_ALIASES, &old_aliases, &nelem)) != 0) {
+ if (error != ENOENT) {
+ return (error);
+ }
+ /* no aliases yet */
+ error = 0;
+ }
+
+ /* add new alias */
+ new_aliases = (char **)calloc(MAX_ALIASES, sizeof (char *));
+ if (new_aliases != NULL) {
+
+ for (i = 0; i < nelem && i < MAX_ALIASES; i++) {
+ char *dup = strdup(old_aliases[i]);
+ if (dup != NULL) {
+ new_aliases[i] = dup;
+ } else {
+ error = ENOMEM;
+ }
+ }
+
+ if (error == 0) {
+ if (i == MAX_ALIASES) {
+ volume_set_error(
+ gettext("Maximum number of device aliases "
+ "(8) reached\n"),
+ MAX_ALIASES);
+ error = -1;
+
+ } else {
+ new_aliases[i] = alias;
+ error = set_string_array(attrs, ATTR_DEVICE_ALIASES,
+ new_aliases, i + 1);
+ }
+ }
+
+ free(new_aliases);
+ }
+
+ if (error == 0) {
+ /* cache descriptor under this alias */
+ error = add_cached_descriptor(alias, desc);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_aliases(dm_descriptor_t desc, dlist_t **list)
+ *
+ * INPUT: desc - a dm_descriptor_t handle.
+ *
+ * OUTPUT: list - a dlist_t list pointing to the list of
+ * aliases associated with the device
+ * represented by the descriptor.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Retrieves aliases for the input descriptor and
+ * appends them to the input list.
+ *
+ * The list of returned items must be freed by calling
+ * dlist_free_items(list, free)
+ */
+int
+get_aliases(
+ dm_descriptor_t desc,
+ dlist_t **list)
+{
+ nvlist_t *attrs = NULL;
+ char **aliases = NULL;
+ uint_t nelem = 0;
+ int error = 0;
+ int i;
+
+ if ((error = get_cached_attributes(desc, &attrs)) != 0) {
+ return (error);
+ }
+
+ if ((error = get_string_array(
+ attrs, ATTR_DEVICE_ALIASES, &aliases, &nelem)) != 0) {
+ if (error == ENOENT) {
+ /* no aliases */
+ return (0);
+ }
+ }
+
+ for (i = 0; i < nelem; i++) {
+ dlist_t *item;
+ char *dup;
+
+ if ((dup = strdup(aliases[i])) == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ if ((item = dlist_new_item(dup)) == NULL) {
+ free(dup);
+ error = ENOMEM;
+ break;
+ }
+
+ *list = dlist_append(item, *list, AT_TAIL);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: compare_start_blocks(
+ * void *obj1, void *obj2)
+ *
+ * INPUT: desc1 - opaque pointer to a dm_descriptor_t
+ * desc2 - opaque pointer to a dm_descriptor_t
+ *
+ * RETURNS: int - <0 - if desc1.stblk < desc2.stblk
+ * 0 - if desc1.stblk == desc2.stblk
+ * >0 - if desc1.stblk > desc.stblk
+ *
+ * PURPOSE: dlist_t helper which compares the start blocks of
+ * the two input dm_descriptor_t slice handles.
+ */
+static int
+compare_start_blocks(
+ void *desc1,
+ void *desc2)
+{
+ uint64_t stblk1 = 0;
+ uint64_t stblk2 = 0;
+
+ assert(desc1 != (dm_descriptor_t)0);
+ assert(desc2 != (dm_descriptor_t)0);
+
+ (void) slice_get_start_block((uintptr_t)desc1, &stblk1);
+ (void) slice_get_start_block((uintptr_t)desc2, &stblk2);
+
+ return (stblk1 - stblk2);
+}
+
+/*
+ * FUNCTION: compare_desc_display_names(
+ * void *desc1, void *desc2)
+ *
+ * INPUT: desc1 - opaque pointer to a dm_descriptor_t
+ * desc2 - opaque pointer to a dm_descriptor_t
+ *
+ * RETURNS: int - <0 - if desc1.name < desc2.name
+ * 0 - if desc1.name == desc2.name
+ * >0 - if desc1.name > desc.name
+ *
+ * PURPOSE: dlist_t helper which compares the CTD names of the
+ * two input dm_descriptor_t objects.
+ */
+static int
+compare_desc_display_names(
+ void *desc1,
+ void *desc2)
+{
+ char *name1 = NULL;
+ char *name2 = NULL;
+
+ assert(desc1 != (dm_descriptor_t)0);
+ assert(desc2 != (dm_descriptor_t)0);
+
+ (void) get_display_name((uintptr_t)desc1, &name1);
+ (void) get_display_name((uintptr_t)desc2, &name2);
+
+ return (string_case_compare(name1, name2));
+}
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_discovery.h b/usr/src/cmd/lvm/metassist/layout/layout_discovery.h
new file mode 100644
index 0000000000..edcc661f26
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_discovery.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#ifndef _LAYOUT_DISCOVERY_H
+#define _LAYOUT_DISCOVERY_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "libdiskmgt.h"
+
+/*
+ * scan physical devices and build lists of known devices.
+ */
+extern int discover_known_devices();
+
+/*
+ * release lists of known devices.
+ */
+extern int release_known_devices();
+
+/*
+ * scan known devices and build lists of usable devices.
+ */
+extern int discover_usable_devices(char *diskset);
+
+/*
+ * release lists of usable devices.
+ */
+extern int release_usable_devices();
+
+/*
+ * functions to access lists of known devices for the system,
+ * constructed by load_physical_devices
+ */
+extern int get_known_slices(dlist_t **list);
+extern int get_known_disks(dlist_t **list);
+extern int get_known_hbas(dlist_t **list);
+
+/*
+ * functions to access lists of devices for the named diskset
+ * constructed by load_physical_devices
+ */
+extern int get_usable_slices(dlist_t **list);
+extern int get_usable_disks(dlist_t **list);
+extern int get_usable_hbas(dlist_t **list);
+
+/*
+ * predicate indicating whether MPXIO appears enabled for the system
+ */
+extern boolean_t is_mpxio_enabled();
+
+/*
+ * functions that set/get a descriptor's multipath alias name(s).
+ */
+extern int get_aliases(dm_descriptor_t desc, dlist_t **aliases);
+extern int set_alias(dm_descriptor_t desc, char *alias);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LAYOUT_DISCOVERY_H */
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_dlist_util.c b/usr/src/cmd/lvm/metassist/layout/layout_dlist_util.c
new file mode 100644
index 0000000000..4ced855104
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_dlist_util.c
@@ -0,0 +1,454 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#define _LAYOUT_DLIST_UTIL_C
+
+#include <assert.h>
+#include <string.h>
+
+#include <libintl.h>
+#include <libdiskmgt.h>
+
+#include "volume_devconfig.h"
+#include "volume_dlist.h"
+#include "volume_output.h"
+
+#include "layout_device_cache.h"
+#include "layout_dlist_util.h"
+#include "layout_request.h"
+
+#include "layout_slice.h" /* destroy_new_slice */
+#include "layout_svm_util.h"
+
+/*
+ * FUNCTION: compare_strings(void *str1, void *str2)
+ *
+ * INPUT: str1 - opaque pointer to a char *
+ * str2 - opaque pointer to a char *
+ *
+ * RETURNS: int - <0 - if str1 < str2
+ * 0 - if str1 == str2
+ * >0 - if str1 > str2
+ *
+ * PURPOSE: dlist_t helper which compares the two input strings.
+ *
+ * Comparison is done with string_case_compare()
+ */
+int
+compare_strings(
+ void *str1,
+ void *str2)
+{
+ assert(str1 != NULL);
+ assert(str2 != NULL);
+
+ return (string_case_compare((char *)str1, (char *)str2));
+}
+
+/*
+ * FUNCTION: compare_devconfig_sizes(void *devconf1, void *devconf2)
+ *
+ * INPUT: devconf1 - opaque pointer
+ * devconf2 - opaque pointer
+ *
+ * RETURNS: int - <0 - if devconf1.size_in_blks < devconf2.size_in_blks
+ * 0 - if devconf1.size_in_blks == devconf2.size_in_blks
+ * >0 - if devconf1.size.in_blks > devconf2.size_in_blks
+ *
+ * PURPOSE: dlist_t helper which compares the sizes of two devconfig_t
+ * structs.
+ *
+ * Both input objects are assumed to be devconfig_t pointers.
+ */
+int
+compare_devconfig_sizes(
+ void *devconf1,
+ void *devconf2)
+{
+ uint64_t size1 = 0;
+ uint64_t size2 = 0;
+
+ assert(devconf1 != NULL);
+ assert(devconf2 != NULL);
+
+ (void) devconfig_get_size_in_blocks((devconfig_t *)devconf1, &size1);
+ (void) devconfig_get_size_in_blocks((devconfig_t *)devconf2, &size2);
+
+ return (size1 - size2);
+}
+
+/*
+ * FUNCTION: compare_slice_sizes(void *desc1, void *desc2)
+ *
+ * INPUT: desc1 - opaque pointer to a dm_descriptor_t slice handle
+ * desc2 - opaque pointer to a dm_descriptor_t slice handle
+ *
+ * RETURNS: int - <0 - if desc1.slicesize < desc2.slicesize
+ * 0 - if desc1.slicesize == desc2.slicesize
+ * >0 - if desc1.slicesize > desc2.slicesize
+ *
+ * PURPOSE: dlist_t helper which compares the sizes of two slices
+ * represented as dm_descriptor_t handles.
+ */
+int
+compare_slice_sizes(
+ void *desc1,
+ void *desc2)
+{
+ uint64_t size1 = 0;
+ uint64_t size2 = 0;
+
+ assert(desc1 != NULL);
+ assert(desc2 != NULL);
+
+ (void) slice_get_size((uintptr_t)desc1, &size1);
+ (void) slice_get_size((uintptr_t)desc2, &size2);
+
+ return (size1 - size2);
+}
+
+/*
+ * FUNCTION: compare_devconfig_and_descriptor_names(void *devconf,
+ * void *desc)
+ *
+ * INPUT: devconf - opaque pointer to a devconfig_t
+ * desc - opaque pointer to a dm_descriptor_t
+ *
+ * RETURNS: int - <0 - if devconf name is "less than" descr name
+ * 0 - if devconf name is "equal to" descr name
+ * >0 - if devconf name is "greater than" desc name
+ *
+ * PURPOSE: dlist_t helper which compares the name of a devconfig_t
+ * struct to the name for a dm_descriptor_t.
+ *
+ * Note that the order of the arguments is important.
+ * This function is intended to be passed into the various
+ * dlist_* functions which take a comparison function.
+ */
+int
+compare_devconfig_and_descriptor_names(
+ void *devconf,
+ void *desc)
+{
+ char *volname = NULL;
+ char *descname = NULL;
+
+ assert(devconf != NULL);
+ assert(desc != NULL);
+
+ (void) devconfig_get_name((devconfig_t *)devconf, &volname);
+ (void) get_display_name((uintptr_t)desc, &descname);
+
+ return (string_case_compare(volname, descname));
+}
+
+/*
+ * FUNCTION: compare_string_to_devconfig_name(void *str, void *devconf)
+ *
+ * INPUT: str - opaque pointer to a char *str
+ * devconf - opaque pointer to a devconfig_t
+ *
+ * RETURNS: int - <0 - if devconf name is "less than" str
+ * 0 - if devconf name is "equal to" str
+ * >0 - if devconf name is "greater than" str
+ *
+ * PURPOSE: dlist_t helper which compares a string to the name of
+ * a devconfig_t struct.
+ */
+int
+compare_string_to_devconfig_name(
+ void *str,
+ void *devconf)
+{
+ char *volname = NULL;
+
+ assert(str != NULL);
+ assert(devconf != NULL);
+
+ (void) devconfig_get_name((devconfig_t *)devconf, &volname);
+ if (volname == NULL) {
+ /* no memory for new string(s) */
+ return (-1);
+ }
+
+ return (string_case_compare(volname, (char *)str));
+}
+
+/*
+ * FUNCTION: free_devconfig_object(void *obj)
+ *
+ * INPUT: obj - an opaque pointer
+ *
+ * RETURNS: void
+ *
+ * PURPOSE: helper which decomposes a devconfig_t struct after a
+ * failed layout attempt.
+ *
+ * reclaims allocated space.
+ * releases reserved volume/HSP names
+ * undoes slicing
+ */
+void
+free_devconfig_object(
+ void *obj)
+{
+ devconfig_t *dev = NULL;
+ char *name = NULL;
+ dlist_t *iter = NULL;
+ component_type_t type = TYPE_UNKNOWN;
+
+ if (obj == NULL) {
+ return;
+ }
+
+ dev = (devconfig_t *)obj;
+
+ (void) devconfig_get_type(dev, &type);
+ (void) devconfig_get_name(dev, &name);
+
+ oprintf(OUTPUT_DEBUG,
+ gettext(" -->decomposing %s\n"), name);
+
+ switch (type) {
+ case TYPE_MIRROR:
+ case TYPE_CONCAT:
+ case TYPE_RAID5:
+ case TYPE_HSP:
+ case TYPE_STRIPE:
+
+ /* release name */
+ if (devconfig_isA(dev, TYPE_HSP)) {
+ release_hsp_name(name);
+ } else {
+ release_volume_name(name);
+ }
+
+ /* decompose volume's components */
+ iter = devconfig_get_components(dev);
+ dlist_free_items(iter, free_devconfig_object);
+
+ (void) devconfig_set_components(dev, NULL);
+
+ break;
+
+ case TYPE_SLICE:
+
+ (void) destroy_new_slice(dev);
+
+ break;
+
+ default:
+ break;
+
+ }
+
+ free_devconfig(dev);
+}
+
+/*
+ * FUNCTION: compare_device_names(
+ * void *str1, void *str2)
+ *
+ * INPUT: str1 - opaque pointer
+ * str2 - opaque pointer
+ *
+ * RETURNS: int - <0 - if str1 < str2
+ * 0 - if str1 == str2
+ * >0 - if str1 > str2
+ *
+ * PURPOSE: dlist_t helper which compares two device name strings.
+ *
+ * Both names are assumed to be in CTD form.
+ *
+ * Either name may be fully qualified by an absolute
+ * path. If only one name is fully qualified, the
+ * leading path with be stripped off prior to the
+ * comparison.
+ *
+ * Uses string_case_compare() to compare the names.
+ */
+int
+compare_device_names(
+ void *str1,
+ void *str2)
+{
+ char *name1 = (char *)str1;
+ char *name2 = (char *)str2;
+
+ int val = 0;
+
+ assert(str1 != NULL);
+ assert(str2 != NULL);
+
+ /* if one doesn't start with '/', just compare device names */
+ if (*name1 != '/' || *name2 != '/') {
+
+ char *short1 = strrchr(name1, '/');
+ char *short2 = strrchr(name2, '/');
+
+ if (short1 == NULL) {
+ short1 = name1;
+ } else {
+ ++short1;
+ }
+
+ if (short2 == NULL) {
+ short2 = name2;
+ } else {
+ ++short2;
+ }
+
+ val = string_case_compare(short2, short1);
+
+ } else {
+
+ /* if they both start with '/', assume they're full paths */
+ val = string_case_compare(name2, name1);
+ }
+
+ return (val);
+}
+
+/*
+ * FUNCTION: compare_descriptors(
+ * void *desc1, void *desc2)
+ *
+ * INPUT: desc1 - opaque pointer
+ * desc2 - opaque pointer
+ *
+ * RETURNS: int - <0 - if desc1 < desc2
+ * 0 - if desc1 == desc2
+ * >0 - if desc1 > desc2
+ *
+ * PURPOSE: dlist_t helper which compares two dm_descriptor_t handles.
+ */
+int
+compare_descriptors(
+ void *desc1,
+ void *desc2)
+{
+ assert(desc1 != NULL);
+ assert(desc2 != NULL);
+
+ return ((uintptr_t)desc1 - (uintptr_t)desc2);
+}
+
+/*
+ * FUNCTION: compare_descriptor_names(
+ * void *desc1, void *desc2)
+ *
+ * INPUT: desc1 - opaque pointer
+ * desc2 - opaque pointer
+ *
+ * RETURNS: int - <0 - if desc1.name < desc2.name
+ * 0 - if desc1.name == desc2.name
+ * >0 - if desc1.name > desc2.name
+ *
+ * PURPOSE: dlist_t helper which compares the names associated
+ * with the input dm_descriptor_t handles.
+ *
+ * Retrieves the names associated with both descriptors
+ * and compares them using string_case_compare.
+ */
+int
+compare_descriptor_names(
+ void *desc1,
+ void *desc2)
+{
+ char *name1 = NULL;
+ char *name2 = NULL;
+
+ assert(desc1 != NULL);
+ assert(desc2 != NULL);
+
+ (void) get_name((uintptr_t)desc1, &name1);
+ (void) get_name((uintptr_t)desc2, &name2);
+
+ return (string_case_compare(name1, name2));
+}
+
+/*
+ * FUNCTION: compare_slices_on_same_hba(
+ * void *slice1, void *slice2)
+ *
+ * INPUT: slice1 - opaque pointer
+ * slice2 - opaque pointer
+ *
+ * RETURNS: int - 0 - if slice1 is on the same hba as slice2
+ * !0 - otherwise
+ *
+ * PURPOSE: dlist_t helper which checks whether slice1 is on the
+ * same hba as slice2
+ */
+int
+compare_slices_on_same_hba(
+ void *slice1,
+ void *slice2)
+{
+ char *name1, *name2;
+
+ /* Retrieve the names of the slices */
+ if (devconfig_get_name((devconfig_t *)slice1, &name1) == 0 &&
+ devconfig_get_name((devconfig_t *)slice2, &name2) == 0) {
+
+ dm_descriptor_t desc1, desc2;
+
+ /* Retrieve the disk descriptors for the slices */
+ if (get_disk_for_named_slice(name1, &desc1) == 0 &&
+ get_disk_for_named_slice(name2, &desc2) == 0) {
+
+ dlist_t *hbas1 = NULL;
+ dlist_t *hbas2 = NULL;
+
+ assert(desc1 != (dm_descriptor_t)0);
+ assert(desc2 != (dm_descriptor_t)0);
+
+ /* Retrieve list of HBA descriptors for the slices */
+ if (disk_get_hbas(desc1, &hbas1) == 0 &&
+ disk_get_hbas(desc2, &hbas2) == 0) {
+
+ dlist_t *itr1;
+
+ for (itr1 = hbas1; itr1 != NULL; itr1 = itr1->next) {
+ dm_descriptor_t hba1 = (uintptr_t)itr1->obj;
+ dlist_t *itr2;
+
+ for (itr2 = hbas2; itr2 != NULL; itr2 = itr2->next) {
+ dm_descriptor_t hba2 = (uintptr_t)itr2->obj;
+
+ if (hba1 == hba2) {
+ return (0);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return (1);
+}
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_dlist_util.h b/usr/src/cmd/lvm/metassist/layout/layout_dlist_util.h
new file mode 100644
index 0000000000..ff65497c99
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_dlist_util.h
@@ -0,0 +1,216 @@
+/*
+ * 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.
+ */
+
+#ifndef _LAYOUT_DLIST_UTIL_H
+#define _LAYOUT_DLIST_UTIL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * A collection of utility functions for manipulating and traversing
+ * dlist_t linked lists.
+ */
+
+/*
+ * FUNCTION: compare_strings(void *str1, void *str2)
+ *
+ * INPUT: str1 - opaque pointer to a char *
+ * str2 - opaque pointer to a char *
+ *
+ * RETURNS: int - <0 - if str1 < str2
+ * 0 - if str1 == str2
+ * >0 - if str1 > str2
+ *
+ * PURPOSE: dlist_t helper which compares the two input strings.
+ *
+ * Comparison is done with string_compare()
+ */
+extern int compare_strings(void *str1, void *str2);
+
+/*
+ * FUNCTION: compare_device_names(
+ * void *str1, void *str2)
+ *
+ * INPUT: str1 - opaque pointer
+ * str2 - opaque pointer
+ *
+ * RETURNS: int - <0 - if str1 < str2
+ * 0 - if str1 == str2
+ * >0 - if str1 > str2
+ *
+ * PURPOSE: dlist_t helper which compares two device name strings.
+ *
+ * Both names are assumed to be in CTD form.
+ *
+ * Either name may be fully qualified by an absolute
+ * path. If only one name is fully qualified, the
+ * leading path with be stripped off prior to the
+ * comparison.
+ *
+ * Uses string_compare() to compare the names.
+ */
+extern int compare_device_names(void *str1, void *str2);
+
+/*
+ * FUNCTION: compare_devconfig_sizes(void *devconf1, void *devconf2)
+ *
+ * INPUT: devconf1 - opaque pointer
+ * devconf2 - opaque pointer
+ *
+ * RETURNS: int - <0 - if devconf1.size_in_blks < devconf2.size_in_blks
+ * 0 - if devconf1.size_in_blks == devconf2.size_in_blks
+ * >0 - if devconf1.size_in_blks > devconf2.size_in_blks
+ *
+ * PURPOSE: dlist_t helper which compares the sizes of two devconfig_t
+ * structs.
+ *
+ * Both input objects are assumed to be devconfig_t pointers.
+ */
+extern int compare_devconfig_sizes(void *devconf1, void *devconf2);
+
+/*
+ * FUNCTION: compare_slice_sizes(void *desc1, void *desc2)
+ *
+ * INPUT: desc1 - opaque pointer to a dm_descriptor_t slice handle
+ * desc2 - opaque pointer to a dm_descriptor_t slice handle
+ *
+ * RETURNS: int - <0 - if desc1.slicesize < desc2.slicesize
+ * 0 - if desc1.slicesize == desc2.slicesize
+ * >0 - if desc1.slicesize > desc2.slicesize
+ *
+ * PURPOSE: dlist_t helper which compares the sizes of two slices
+ * represented as dm_descriptor_t handles.
+ */
+extern int compare_slice_sizes(void *obj1, void *obj2);
+
+/*
+ * FUNCTION: compare_descriptors(
+ * void *desc1, void *desc2)
+ *
+ * INPUT: desc1 - opaque pointer
+ * desc2 - opaque pointer
+ *
+ * RETURNS: int - <0 - if desc1 < desc2
+ * 0 - if desc1 == desc2
+ * >0 - if desc1 > desc2
+ *
+ * PURPOSE: dlist_t helper which compares two dm_descriptor_t handles.
+ */
+extern int compare_descriptors(void *desc1, void *desc2);
+
+/*
+ * FUNCTION: compare_descriptor_names(
+ * void *desc1, void *desc2)
+ *
+ * INPUT: desc1 - opaque pointer
+ * desc2 - opaque pointer
+ *
+ * RETURNS: int - <0 - if desc1.name < desc2.name
+ * 0 - if desc1.name == desc2.name
+ * >0 - if desc1.name > desc2.name
+ *
+ * PURPOSE: dlist_t helper which compares the names associated
+ * with the input dm_descriptor_t handles.
+ *
+ * Retrieves the names associated with both descriptors
+ * and compares them using string_compare.
+ */
+extern int compare_descriptor_names(void *desc1, void *desc2);
+
+/*
+ * FUNCTION: compare_devconfig_and_descriptor_names(void *devconf,
+ * void *desc)
+ *
+ * INPUT: devconf - opaque pointer to a devconfig_t
+ * desc - opaque pointer to a dm_descriptor_t
+ *
+ * RETURNS: int - <0 - if devconf name is "less than" descr name
+ * 0 - if devconf name is "equal to" descr name
+ * >0 - if devconf name is "greater than" desc name
+ *
+ * PURPOSE: dlist_t helper which compares the name of a devconfig_t
+ * struct to the name for a dm_descriptor_t.
+ *
+ * Note that the order of the arguments is important.
+ * This function is intended to be passed into the various
+ * dlist_* functions which take a comparison function.
+ */
+extern int compare_devconfig_and_descriptor_names(void *devconf, void *desc);
+
+/*
+ * FUNCTION: compare_string_to_devconfig_name(void *str, void *devconf)
+ * INPUT: str - opaque pointer to a char *str
+ * devconf - opaque pointer to a devconfig_t
+ *
+ * RETURNS: int - <0 - if devconf name is "less than" str
+ * 0 - if devconf name is "equal to" str
+ * >0 - if devconf name is "greater than" str
+ *
+ * PURPOSE: dlist_t helper which compares a string to the name of
+ * a devconfig_t struct.
+ */
+extern int compare_string_to_devconfig_name(void *str, void *devconf);
+
+/*
+ * FUNCTION: compare_slices_on_same_hba(
+ * void *slice1, void *slice2)
+ *
+ * INPUT: slice1 - opaque pointer
+ * slice2 - opaque pointer
+ *
+ * RETURNS: int - 0 - if slice1 is on the same hba as slice2
+ * !0 - otherwise
+ *
+ * PURPOSE: dlist_t helper which checks whether slice1 is on the
+ * same hba as slice2
+ */
+extern int compare_slices_on_same_hba(void *slice1, void *slice2);
+
+/*
+ * FUNCTION: free_devconfig_object(void *obj)
+ *
+ * INPUT: obj - an opaque pointer
+ *
+ * RETURNS: void
+ *
+ * PURPOSE: helper which decomposes a devconfig_t struct after a
+ * failed layout attempt.
+ *
+ * reclaims allocated space.
+ * releases reserved volume/HSP names
+ * undoes slicing
+ */
+extern void free_devconfig_object(void *obj);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LAYOUT_DLIST_UTIL_H */
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_hsp.c b/usr/src/cmd/lvm/metassist/layout/layout_hsp.c
new file mode 100644
index 0000000000..289f87f1f7
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_hsp.c
@@ -0,0 +1,963 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <string.h>
+
+#include <libintl.h>
+
+#include "volume_error.h"
+#include "volume_dlist.h"
+#include "volume_output.h"
+
+#include "layout_device_cache.h"
+#include "layout_device_util.h"
+#include "layout_discovery.h"
+#include "layout_dlist_util.h"
+#include "layout_messages.h"
+#include "layout_request.h"
+#include "layout_slice.h"
+#include "layout_svm_util.h"
+
+#define _LAYOUT_HSP_C
+
+static int layout_explicit_hsp(
+ devconfig_t *hsprequest,
+ dlist_t *devices,
+ devconfig_t **hsp);
+
+static int layout_default_hsp(
+ devconfig_t *request,
+ dlist_t *devices,
+ devconfig_t **hsp);
+
+static int populate_hsp(
+ devconfig_t *request,
+ devconfig_t *hsp,
+ dlist_t *devices);
+
+static int assemble_hsp(
+ devconfig_t *hsp,
+ dlist_t *newspares,
+ dlist_t *devices);
+
+static int get_uniquely_sized_slices(
+ dlist_t *devices,
+ dlist_t **unique);
+
+static int remove_undersized_slices(
+ dlist_t *unique,
+ dlist_t **avail);
+
+static int find_spare_for_component(
+ devconfig_t *component,
+ dlist_t *all_spares,
+ dlist_t *hbas,
+ dlist_t *disks,
+ boolean_t *found);
+
+static int choose_spare_for_component(
+ devconfig_t *comp,
+ dlist_t **all_spares,
+ dlist_t **new_spares,
+ dlist_t **avail,
+ dlist_t *used_hbas,
+ dlist_t *used_disks,
+ uint16_t npaths);
+
+/*
+ * FUNCTION: layout_hsp(devconfig_t *request, devconfig_t hsprequest,
+ * dlist_t *devices, dlist_t **results)
+ *
+ * INPUT: request - pointer to the toplevel request devconfig_t
+ * hsp - pointer to the optional HSP request devconfig_t
+ * devices - pointer to a list of devices to be served by the HSP
+ *
+ * OUTPUT: results - pointer to a list result devconfig_t, if the HSP
+ * to service the input list of devices needs to be
+ * created or modified, it will be appended to the list.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Main layout driver for HSP, attempts to build/populate a
+ * single HSP to service the list of devices.
+ *
+ * If the input hsprequest is NULL, use the default HSP scheme:
+ * a. use the first HSP in the diskset
+ * b. create an HSP if the diskset has none
+ *
+ * If the hsprequest is not NULL:
+ * a. if the request names an HSP and it already exists, use it
+ * b. if the request names an HSP and it does not exist, create it
+ * c. if the request specifies components, use them
+ */
+int
+layout_hsp(
+ devconfig_t *request,
+ devconfig_t *hsprequest,
+ dlist_t *devices,
+ dlist_t **results)
+{
+ int error = 0;
+ devconfig_t *hsp = NULL;
+
+ oprintf(OUTPUT_TERSE,
+ gettext(" ->Layout a %s\n"),
+ devconfig_type_to_str(TYPE_HSP));
+
+ if (hsprequest == NULL) {
+ error = layout_default_hsp(request, devices, &hsp);
+ } else {
+ error = layout_explicit_hsp(hsprequest, devices, &hsp);
+ }
+
+ if (error != 0) {
+ print_debug_failure_msg(devconfig_type_to_str(TYPE_HSP),
+ get_error_string(error));
+ } else if (hsp != NULL) {
+
+ if (devconfig_get_components(hsp) == NULL) {
+ /* HSP is usable as it is */
+ free_devconfig(hsp);
+ hsp = NULL;
+ } else {
+ dlist_t *item = NULL;
+ if ((item = dlist_new_item(hsp)) == NULL) {
+ error = ENOMEM;
+ } else {
+ *results = dlist_append(item, *results, AT_TAIL);
+ print_layout_success_msg();
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: layout_default_hsp(devconfig_t *request,
+ * dlist_t *devices, devconfig_t **hsp)
+ *
+ * INPUT: request - pointer to the toplevel request devconfig_t
+ * devices - pointer to a list of devices to be served by the HSP
+ *
+ * OUTPUT: hsp - pointer to a devconfig_t to hold the resulting HSP
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Layout driver for default HSP construction.
+ *
+ * a. use the first HSP in the diskset
+ * b. create an HSP if the diskset has none
+ * c. add spares to the HSP to service the list of input devices.
+ */
+static int
+layout_default_hsp(
+ devconfig_t *request,
+ dlist_t *devices,
+ devconfig_t **hsp)
+{
+ char *dsname = get_request_diskset();
+ char *hspname = NULL;
+ boolean_t free_hspname = B_FALSE;
+ devconfig_t *default_hsp = NULL;
+ int error = 0;
+
+ oprintf(OUTPUT_TERSE,
+ gettext(" -->Using default HSP scheme...\n"));
+
+ if ((error = get_default_hsp_name(request, &hspname)) != 0) {
+ volume_set_error(
+ gettext("error getting HSP name from defaults\n"));
+ return (error);
+ }
+
+ if (hspname != NULL) {
+ if ((error = hsp_get_by_name(dsname, hspname, &default_hsp)) != 0) {
+ volume_set_error(
+ gettext("error getting default HSP by name\n"));
+ return (error);
+ }
+ } else {
+ /* no default HSP name, get diskset's default HSP */
+ if ((error = hsp_get_default_for_diskset(dsname,
+ &default_hsp)) != 0) {
+ volume_set_error(
+ gettext("error getting default HSP\n"));
+ return (error);
+ }
+
+ if (default_hsp == NULL) {
+ /* no default HSP name, no default HSP, make one */
+ if ((error = get_next_hsp_name(&hspname)) != 0) {
+ volume_set_error(
+ gettext("error making default HSP name\n"));
+ return (error);
+ }
+ free_hspname = B_TRUE;
+ }
+ }
+
+ if (default_hsp != NULL) {
+
+ /* Found existing default HSP, copy it */
+ dlist_t *spares = devconfig_get_components(default_hsp);
+
+ ((error = devconfig_get_name(default_hsp, &hspname)) != 0) ||
+ (error = new_devconfig(hsp, TYPE_HSP)) ||
+ (error = devconfig_set_name(*hsp, hspname));
+
+ if (error == 0) {
+ devconfig_set_components(*hsp, spares);
+ devconfig_set_components(default_hsp, NULL);
+
+ oprintf(OUTPUT_TERSE,
+ gettext(" --->Using %s from disk set %s...\n"),
+ hspname, dsname);
+ } else {
+ free_devconfig(*hsp);
+ *hsp = NULL;
+ }
+
+ } else {
+
+ /* no existing default HSP, make it */
+ ((error = new_devconfig(hsp, TYPE_HSP)) != 0) ||
+ (error = devconfig_set_name(*hsp, hspname));
+ if (error == 0) {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" --->Created %s for disk set %s...\n "),
+ hspname, dsname);
+ } else {
+ free_devconfig(*hsp);
+ *hsp = NULL;
+ }
+
+ if (free_hspname == B_TRUE) {
+ free(hspname);
+ }
+ }
+
+ if (error == 0) {
+ error = populate_hsp(request, *hsp, devices);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: layout_explicit_hsp(devconfig_t *hsprequest,
+ * dlist_t *devices, devconfig_t **hsp)
+ *
+ * INPUT: hsprequest - pointer to the explicit HSP request devconfig_t
+ * devices - pointer to a list of devices to be served by the HSP
+ *
+ * OUTPUT: hsp - pointer to a HSP devconfig_t to hold resulting HSP
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Layout driver for an explicit HSP request.
+ *
+ * a. if the request names an HSP and it already exists, use it
+ * b. if the request names an HSP and it does not exist, create it
+ * c. if the request specifies components, use them
+ * otherwise, add new spares to handle the input list
+ * of devices.
+ */
+static int
+layout_explicit_hsp(
+ devconfig_t *hsprequest,
+ dlist_t *devices,
+ devconfig_t **hsp)
+{
+ char *dsname = get_request_diskset();
+ char *hspname = NULL;
+ dlist_t *rspares = NULL;
+ int error = 0;
+
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" --->Explicit HSP request...\n"));
+
+ (void) devconfig_get_name(hsprequest, &hspname);
+ if (hspname != NULL) {
+
+ (void) hsp_get_by_name(dsname, hspname, hsp);
+ if (*hsp != NULL) {
+
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" --->Using %s...\n"),
+ hspname);
+ } else {
+
+ /* named HSP doesn't exist, create it */
+ ((error = new_devconfig(hsp, TYPE_HSP)) != 0) ||
+ (error = devconfig_set_name(*hsp, hspname));
+ if (error == 0) {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" --->%s does not exist, "
+ "created...\n"), hspname);
+ } else {
+ free_devconfig(*hsp);
+ *hsp = NULL;
+ }
+ free(hspname);
+ }
+ }
+
+ if (error == 0) {
+
+ /* does the hsprequest specify spares? */
+ rspares = devconfig_get_components(hsprequest);
+ if (rspares != NULL) {
+
+ /* put requested spares into HSP */
+ dlist_t *list = NULL;
+ dlist_t *iter = NULL;
+
+ for (iter = rspares;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ dlist_t *item = NULL;
+ if ((dlist_new_item(iter->obj)) == NULL) {
+ error = ENOMEM;
+ } else {
+ list = dlist_append(item, list, AT_TAIL);
+ }
+ }
+
+ if (error == 0) {
+ error = assemble_hsp(*hsp, rspares, devices);
+ }
+
+ } else {
+
+ /* select new spares */
+ error = populate_hsp(hsprequest, *hsp, devices);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: populate_hsp(devconfig_t *request, devconfig_t *hsp,
+ * dlist_t *devices)
+ *
+ * INPUT: request - pointer to a request devconfig_t
+ * hsp - pointer to a HSP devconfig_t
+ * devices - pointer to a list of devices to be served by the HSP
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Processes the input HSP request and add spares sufficient
+ * to service the input list of devices.
+ *
+ * Determine the available HBAs, disks, and slices.
+ * Sort thru the input list of devices and determine
+ * the unique component sizes which need to be spared.
+ * Filter the available slices and remove those that are
+ * too small to serve as spares.
+ *
+ * Iterate each device and its components and see if the
+ * HSP currently has a sufficient spare, if not, try
+ * to select one from the available slices.
+ *
+ * If a spare cannot be found for any device component,
+ * the HSP layout process stops.
+ *
+ * If spares are found for all device components, add
+ * any required new ones to the HSP.
+ */
+static int
+populate_hsp(
+ devconfig_t *request,
+ devconfig_t *hsp,
+ dlist_t *devices)
+{
+ int error = 0;
+ uint16_t npaths = 0;
+
+ dlist_t *usable_hbas = NULL;
+ dlist_t *sel_hbas = NULL;
+ dlist_t *disks = NULL;
+ dlist_t *iter = NULL;
+
+ dlist_t *avail = NULL; /* available slices */
+ dlist_t *slices = NULL; /* avail slices of sufficient size */
+ dlist_t *unique = NULL; /* volume slices that need spares */
+ dlist_t *curspares = NULL; /* current spares in the HSP */
+ dlist_t *newspares = NULL; /* slices to add to HSP */
+ dlist_t *allspares = NULL; /* current and new spares */
+
+ ((error = get_usable_hbas(&usable_hbas)) != 0) ||
+ (error = select_hbas_with_n_disks(request, usable_hbas, 1, &sel_hbas,
+ &disks)) ||
+ (error = disks_get_avail_slices(request, disks, &avail)) ||
+ (error = get_volume_npaths(request, &npaths));
+ if (error != 0) {
+ dlist_free_items(sel_hbas, NULL);
+ dlist_free_items(disks, NULL);
+ dlist_free_items(avail, NULL);
+ return (error);
+ }
+
+ if (disks == NULL || dlist_length(disks) == 0) {
+ /* all disks have been consumed by the devices */
+ volume_set_error(
+ gettext(" no available disks to populate HSP\n"));
+ dlist_free_items(sel_hbas, NULL);
+ dlist_free_items(avail, NULL);
+ return (-1);
+ }
+
+ if (avail == NULL || dlist_length(avail) == 0) {
+ /* all slices have been consumed by the devices */
+ volume_set_error(
+ gettext(" no available slices to populate HSP\n"));
+ dlist_free_items(sel_hbas, NULL);
+ dlist_free_items(disks, NULL);
+ return (-1);
+ }
+
+ dlist_free_items(sel_hbas, NULL);
+ dlist_free_items(disks, NULL);
+
+ /* build list of slices needing to be spared */
+ ((error = get_uniquely_sized_slices(devices, &unique)) != 0) ||
+
+ /* and list of slices of sufficient size to spare for them */
+ (error = remove_undersized_slices(unique, &avail));
+
+ if (error != 0) {
+ dlist_free_items(avail, NULL);
+ dlist_free_items(unique, NULL);
+ dlist_free_items(slices, NULL);
+ return (error);
+ }
+
+ /* get spares currently in the HSP */
+ curspares = devconfig_get_components(hsp);
+
+ /* clone current spares list */
+ for (iter = curspares;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+ dlist_t *item = dlist_new_item(iter->obj);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ allspares = dlist_append(item, allspares, AT_TAIL);
+ }
+ }
+
+ if (error != 0) {
+ dlist_free_items(avail, NULL);
+ dlist_free_items(unique, NULL);
+ dlist_free_items(slices, NULL);
+ dlist_free_items(allspares, NULL);
+ return (error);
+ }
+
+ /*
+ * examine device component slices and see if the HSP already
+ * has a suitable spare. If not, select the best available
+ * of the same (or larger) size
+ */
+ for (iter = devices;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ devconfig_t *device = (devconfig_t *)iter->obj;
+ dlist_t *components = devconfig_get_components(device);
+ dlist_t *hbas = NULL;
+ dlist_t *disks = NULL;
+ dlist_t *iter1;
+
+ error = get_hbas_and_disks_used_by_volume(device, &hbas, &disks);
+ for (iter1 = components; (iter1 != NULL) && (error == 0);
+ iter1 = iter1->next) {
+
+ devconfig_t *comp = (devconfig_t *)iter1->obj;
+ boolean_t found = B_FALSE;
+
+ if ((error = find_spare_for_component(
+ comp, allspares, hbas, disks, &found)) == 0) {
+ if (found != B_TRUE) {
+ error = choose_spare_for_component(
+ comp, &allspares, &newspares,
+ &avail, hbas, disks, npaths);
+ }
+ }
+ }
+ dlist_free_items(disks, NULL);
+ dlist_free_items(hbas, NULL);
+ }
+
+ if (error == 0) {
+ /* existing spares are no longer needed */
+ dlist_free_items(curspares, free_devconfig_object);
+ curspares = NULL;
+
+ error = assemble_hsp(hsp, newspares, devices);
+ } else {
+ dlist_free_items(newspares, free_devconfig_object);
+ newspares = NULL;
+ }
+
+ dlist_free_items(avail, NULL);
+ dlist_free_items(slices, NULL);
+ dlist_free_items(unique, NULL);
+ dlist_free_items(allspares, NULL);
+
+ return (error);
+}
+
+/*
+ * FUNCTION: assemble_hsp(devconfig_t *hsp, dlist_t *newspares,
+ * dlist_t *devices)
+ *
+ * INPUT: request - pointer to a HSP devconfig_t
+ * newspare - pointer to a list of new spares for the HSP
+ * devices - pointer to a list of devices to be served by the HSP
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Final assembly of an HSP. Attach new spare components
+ * and associate the HSP with each device in the input list.
+ */
+static int
+assemble_hsp(
+ devconfig_t *hsp,
+ dlist_t *newspares,
+ dlist_t *devices)
+{
+ dlist_t *iter;
+ char *hspname = NULL;
+ int error = 0;
+
+ /* add new spares to HSP */
+ (void) devconfig_set_components(hsp, newspares);
+ (void) devconfig_get_name(hsp, &hspname);
+
+ /* associate HSP with each of the devices */
+ for (iter = devices;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ devconfig_t *dev = iter->obj;
+ devconfig_t *hspcomp = NULL;
+ dlist_t *item = NULL;
+ char *devname = NULL;
+
+ ((error = devconfig_get_name(dev, &devname)) != 0) ||
+ (error = new_devconfig(&hspcomp, TYPE_HSP)) ||
+ (error = devconfig_set_name(hspcomp, hspname));
+
+ if (error != 0) {
+
+ free_devconfig(hspcomp);
+
+ } else if ((item = dlist_new_item(hspcomp)) == NULL) {
+
+ free_devconfig(hspcomp);
+ error = ENOMEM;
+
+ } else {
+
+ dlist_t *comps = devconfig_get_components(dev);
+ comps = dlist_append(comps, item, AT_TAIL);
+ (void) devconfig_set_components(dev, comps);
+
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" --->volume %s will use HSP %s\n"),
+ devname, hspname);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_uniquely_sized_slices(dlist_t *devices,
+ * dlist_t **unique)
+ *
+ * INPUT: devices - pointer to a list of devconfig_t devices
+ *
+ * OUTPUT: unique - pointer to a list of uniquely size slices
+ * from the input list of devices.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Examine each device's slice components and build a list
+ * of uniquely sized slices.
+ */
+static int
+get_uniquely_sized_slices(
+ dlist_t *devices,
+ dlist_t **unique)
+{
+ int error = 0;
+ dlist_t *iter = NULL;
+
+ for (iter = devices;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ dlist_t *iter1;
+ for (iter1 = devconfig_get_components((devconfig_t *)iter->obj);
+ (iter1 != NULL) && (error == 0);
+ iter1 = iter1->next) {
+
+ devconfig_t *comp = (devconfig_t *)iter1->obj;
+ if (dlist_contains(*unique, comp,
+ compare_devconfig_sizes) != B_TRUE) {
+
+ dlist_t *item = NULL;
+ if ((item = dlist_new_item(comp)) == NULL) {
+ error = ENOMEM;
+ } else {
+ *unique = dlist_insert_ordered(item, *unique,
+ ASCENDING, compare_devconfig_sizes);
+ }
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: remove_undersized_slices(dlist_t *unique,
+ * dlist_t **avail)
+ *
+ * INPUT: avail - pointer to a list of available slices
+ * unique - pointer to a list of uniquely size slices
+ *
+ * OUTPUT: avail - pointer to an updated list of available slices
+ * that are at least as large as slices in the
+ * unique list.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: filter available slices and remove those that aren't
+ * large enough for the device components which need spares.
+ *
+ * For each uniquely sized slice, find all available slices
+ * that are larger and add them to the filtered list.
+ */
+static int
+remove_undersized_slices(
+ dlist_t *unique,
+ dlist_t **avail)
+{
+ dlist_t *filtered = NULL;
+ dlist_t *iter = NULL;
+ int error = 0;
+
+ for (iter = unique;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ devconfig_t *uslice = (devconfig_t *)iter->obj;
+ uint64_t usize = 0;
+ dlist_t *iter2 = NULL;
+
+ error = devconfig_get_size(uslice, &usize);
+
+ for (iter2 = *avail;
+ (iter2 != NULL) && (error == 0);
+ iter2 = iter2->next) {
+
+ dm_descriptor_t aslice = (uintptr_t)iter2->obj;
+ uint64_t asize = 0;
+
+ error = slice_get_size(aslice, &asize);
+ if (asize >= usize) {
+
+ /* this slice is large enough */
+ dlist_t *item = NULL;
+ if ((item = dlist_new_item((void *)aslice)) == NULL) {
+ error = ENOMEM;
+ } else {
+ filtered = dlist_insert_ordered(item, filtered,
+ ASCENDING, compare_slice_sizes);
+ }
+
+ }
+ }
+ }
+
+ if (error == 0) {
+ dlist_free_items(*avail, NULL);
+ *avail = filtered;
+ } else {
+ dlist_free_items(filtered, NULL);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: find_spare_for_component(devconfig_t *component,
+ * dlist_t *all_spares, dlist_t *hbas, dlist_t *disks,
+ * boolean_t *found)
+ *
+ * INPUT: comp - pointer to a devconfig_t slice compenent that
+ * needs to be spared
+ * all_spares - pointer to a list of spares currently
+ * in the pool or that will be added
+ * hbas - pointer to a list of HBAs the component's
+ * parent device utilizes
+ * disks - pointer to a list of disks the component's
+ * parent device utilizes
+ *
+ * OUTPUT: found - pointer to a boolean_t to hold the result.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Find a spare for the input component.
+ *
+ * Searches the input list of spares to see if one is
+ * sufficient.
+ *
+ * A suffcient spare is one that is large enough to spare
+ * for the input component and not on the same disk as any
+ * of the components in the parent device.
+ *
+ * The optimal spare would be on a different controller/HBA
+ * as the component and any of the components in the parent
+ * device. We settle for sufficient.
+ */
+static int
+find_spare_for_component(
+ devconfig_t *component,
+ dlist_t *all_spares,
+ dlist_t *hbas,
+ dlist_t *disks,
+ boolean_t *found)
+{
+ dlist_t *iter = NULL;
+ uint64_t csize = 0;
+ int error = 0;
+
+ *found = B_FALSE;
+
+ (void) devconfig_get_size(component, &csize);
+
+ for (iter = all_spares;
+ (iter != NULL) && (*found == B_FALSE) && (error == 0);
+ iter = iter->next) {
+
+ devconfig_t *spare = (devconfig_t *)iter->obj;
+ char *spname = NULL;
+ uint64_t spsize = 0;
+
+ if (((error = devconfig_get_name(spare, &spname)) != 0) ||
+ ((error = devconfig_get_size(spare, &spsize)) != 0)) {
+ continue;
+ }
+
+ if (spsize >= csize) {
+
+ dm_descriptor_t disk = NULL;
+
+ /* see if spare's disk is independent of the volume */
+ error = get_disk_for_named_slice(spname, &disk);
+ if ((error == 0) && (dlist_contains(disks, (void *)disk,
+ compare_descriptor_names) == B_FALSE)) {
+ *found = B_TRUE;
+ }
+ }
+ }
+
+ if ((*found == B_TRUE) && (get_max_verbosity() >= OUTPUT_DEBUG)) {
+ char *cname = NULL;
+ (void) devconfig_get_name(component, &cname);
+ oprintf(OUTPUT_DEBUG,
+ gettext(" found existing spare for: %s (%llu)\n"),
+ cname, csize);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: choose_spare_for_component(devconfig_t *component,
+ * dlist_t *all_spares, dlist_t **new_spares,
+ * dlist_t avail, uint16_t npaths, dlist_t *used_hbas,
+ * dlist_t *used_disks)
+ *
+ * INPUT: comp - pointer to a devconfig_t slice compenent that
+ * needs to be spared
+ * all_spares - pointer to a list of spares currently
+ * in the pool and those to be added
+ * new_spares - pointer to a list of spares that need to
+ * be added to the pool
+ * avail - list of available slices
+ * npaths - required number of paths for the spare
+ * used_hbas - list of HBAs used by the component's parent
+ * used_disks - list of disks used by the component's parent
+ *
+ * OUTPUT: all_spares - the possibly updated list of all spares
+ * new_spares - the possibly updated list of spares which
+ * need to be added to the pool.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Find a new spare for the input component.
+ *
+ * Select a spare from the available slice list and add
+ * it to the new_spares list.
+ *
+ * The spare slice chosen should be on a unique HBA and
+ * disk relative to the input lists of used HBAs and disks
+ * and any spares in the pool.
+ */
+static int
+choose_spare_for_component(
+ devconfig_t *component,
+ dlist_t **all_spares,
+ dlist_t **new_spares,
+ dlist_t **avail,
+ dlist_t *used_hbas,
+ dlist_t *used_disks,
+ uint16_t npaths)
+{
+ devconfig_t *spare = NULL;
+ uint64_t csize = 0;
+ int error = 0;
+
+ (void) devconfig_get_size(component, &csize);
+
+ if (get_max_verbosity() >= OUTPUT_DEBUG) {
+ char *cname = NULL;
+ (void) devconfig_get_name(component, &cname);
+ oprintf(OUTPUT_DEBUG,
+ gettext(" select new spare for: %s (%llu)\n"),
+ cname, csize);
+ }
+
+ /*
+ * find a spare for the input component.
+ * select the best one from the available list that
+ * is on a unique disk.
+ */
+
+ /*
+ * 1st B_TRUE: require a different disk than those used by
+ * all spares and devices
+ * 2nd B_TRUE: requested size is the minimum acceptable
+ * 1st B_FALSE: do not add an extra cylinder when resizing slice,
+ * this is only necessary for Stripe components whose
+ * sizes get rounded down to an interlace multiple and
+ * then down to a cylinder boundary.
+ */
+ error = choose_slice(csize, npaths, *avail, *all_spares,
+ used_hbas, used_disks, B_TRUE, B_TRUE, B_FALSE, &spare);
+
+ if ((error == 0) && (spare == NULL)) {
+ /* can't find one on a unique disk, try again on any disk */
+
+ /* BEGIN CSTYLED */
+ /*
+ * 1st B_FALSE: don't require a different disk than those used
+ * by all spares and devices
+ * 2nd B_TRUE: requested size is still the minimum acceptable
+ * 2nd B_FALSE: do not add an extra cylinder when resizing slice
+ * this is only necessary for Stripe components whose
+ * sizes get rounded down to an interlace multiple and
+ * then down to a cylinder boundary.
+ */
+ /* END CSTYLED */
+ error = choose_slice(
+ csize, npaths, *avail, *all_spares, used_hbas,
+ used_disks, B_FALSE, B_TRUE, B_FALSE, &spare);
+ }
+
+ if ((error == 0) && (spare != NULL)) {
+
+ dlist_t *rmvd = NULL;
+ dlist_t *item = NULL;
+ char *spname = NULL;
+
+ if ((item = dlist_new_item(spare)) == NULL) {
+ error = ENOMEM;
+ } else {
+
+ /* add spare to the all spares list */
+ *all_spares = dlist_append(item, *all_spares, AT_HEAD);
+
+ if ((item = dlist_new_item(spare)) == NULL) {
+ error = ENOMEM;
+ } else {
+
+ /* add spare to the new spares list */
+ *new_spares = dlist_insert_ordered(
+ item, *new_spares, ASCENDING,
+ compare_devconfig_sizes);
+
+ /* remove it from the available list */
+ *avail = dlist_remove_equivalent_item(*avail, spare,
+ compare_devconfig_and_descriptor_names,
+ &rmvd);
+
+ if (rmvd != NULL) {
+ free(rmvd);
+ }
+
+ /* add the spare to the used slice list */
+ error = devconfig_get_name(spare, &spname);
+ if (error == 0) {
+ error = add_used_slice_by_name(spname);
+ }
+ }
+ }
+
+ } else {
+
+ /* no spare, give up on layout */
+ oprintf(OUTPUT_TERSE,
+ gettext(" <---Failed: insufficient suitable spares\n"));
+
+ volume_set_error(
+ gettext("failed to find sufficient spares for HSP\n"));
+
+ error = -1;
+ }
+
+ return (error);
+}
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_hsp.h b/usr/src/cmd/lvm/metassist/layout/layout_hsp.h
new file mode 100644
index 0000000000..7c50cc9213
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_hsp.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef _LAYOUT_HSP_H
+#define _LAYOUT_HSP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "volume_devconfig.h"
+#include "volume_dlist.h"
+
+extern int layout_hsp(
+ devconfig_t *request,
+ devconfig_t *hsprequest,
+ dlist_t *devices,
+ dlist_t **results);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LAYOUT_HSP_H */
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_messages.c b/usr/src/cmd/lvm/metassist/layout/layout_messages.c
new file mode 100644
index 0000000000..c652972b7b
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_messages.c
@@ -0,0 +1,407 @@
+/*
+ * 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 "%Z%%M% %I% %E% SMI"
+
+#include <libintl.h>
+#include <stdlib.h>
+
+#include "volume_error.h"
+#include "volume_output.h"
+#include "volume_string.h"
+
+#include "layout_messages.h"
+
+/*
+ * FUNCTION: print_layout_volume_msg(char *type, uint64_t nbytes)
+ *
+ * PURPOSE: Prints a generic message indicating the start of the
+ * layout process for a volume of the indicated type and
+ * capacity.
+ */
+void
+print_layout_volume_msg(
+ char *type,
+ uint64_t nbytes)
+{
+ char *spstr = NULL;
+
+ (void) bytes_to_sizestr(nbytes, &spstr, universal_units, B_FALSE);
+
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" ->Layout a %s with capacity %s\n"),
+ type, spstr);
+
+ free(spstr);
+}
+
+/*
+ * FUNCTION: print_layout_explicit_msg(char *type)
+ *
+ * PURPOSE: Prints a generic message indicating the start of the
+ * layout population process using explicit components
+ * for a volume of the indicated type.
+ */
+void
+print_layout_explicit_msg(
+ char *type)
+{
+ oprintf(OUTPUT_TERSE,
+ gettext(" ->Layout a %s with explicitly specified "
+ "components\n"),
+ type);
+}
+
+/*
+ * FUNCTION: print_layout_explicit_added_msg(char *comp)
+ *
+ * PURPOSE: Prints a generic message indicating the named component
+ * was added to a volume.
+ */
+void
+print_layout_explicit_added_msg(
+ char *comp)
+{
+ oprintf(OUTPUT_TERSE, gettext(" ---->added '%s'\n"), comp);
+}
+
+/*
+ * FUNCTION: print_success_msg()
+ *
+ * PURPOSE: Prints a generic layout success message.
+ */
+void
+print_layout_success_msg()
+{
+ oprintf(OUTPUT_TERSE, gettext(" <-Success!\n"));
+}
+
+/*
+ * FUNCTION: print_insufficient_resources_msg(char *type)
+ *
+ * PURPOSE: Prints a message indicating that there are insufficient
+ * resources.
+ *
+ * Also sets the metassist error string indicating why
+ * the metassist command failed. The volume type is included
+ * for context in this message.
+ */
+void
+print_insufficient_resources_msg(
+ char *type)
+{
+ oprintf(OUTPUT_TERSE,
+ gettext(" <-Failed: insufficient resources available\n"));
+
+ volume_set_error(
+ gettext("insufficient resources available to complete "
+ "requested %s\n"),
+ type);
+}
+
+/*
+ * FUNCTION: print_insufficient_hbas_msg(int n)
+ *
+ * PURPOSE: Prints a status message indicating that there are insufficient
+ * HBAs and that only 'n' are available.
+ *
+ * Used to indicate strategy selection during layouts.
+ */
+void
+print_insufficient_hbas_msg(
+ int n)
+{
+ if (n == 0) {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" <--Failed: no HBA has sufficient disks\n"));
+ } else if (n == 1) {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" <--Failed: only 1 HBA has sufficient disks\n"));
+ } else {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" <--Failed: only %d HBAs have sufficient disks\n"),
+ n);
+ }
+}
+
+/*
+ * FUNCTION: print_insufficient_disks_msg(int n)
+ *
+ * PURPOSE: Prints a status message indicating that there are insufficient
+ * disks and that only 'n' are available.
+ *
+ * Used to indicate strategy selection during layouts.
+ */
+void
+print_insufficient_disks_msg(
+ int n)
+{
+ if (n == 0) {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" <--Failed: no disks available\n"),
+ n);
+ } else if (n == 1) {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" <--Failed: only 1 disk available\n"),
+ n);
+ } else {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" <--Failed: only %d disks available\n"),
+ n);
+ }
+}
+
+/*
+ * FUNCTION: print_no_hbas_msg()
+ *
+ * PURPOSE: Prints a layout failure due to no usable HBAs message.
+ */
+void
+print_no_hbas_msg()
+{
+ oprintf(OUTPUT_TERSE,
+ gettext(" There are no usable HBAs.\n"));
+}
+
+/*
+ * FUNCTION: print_debug_failure_msg(char *type, char *err)
+ *
+ * PURPOSE: Prints a generic message for unexpected failures
+ * during layout.
+ */
+void
+print_debug_failure_msg(
+ char *type,
+ char *err)
+{
+ oprintf(OUTPUT_DEBUG,
+ gettext(" layout of %s failed: %s\n"),
+ type, err);
+}
+
+/*
+ * FUNCTION: print_insufficient_components_msg(int ncomp)
+ *
+ * INPUT: ncomp - number of available components
+ *
+ * PURPOSE: Helper to print out a message indicating that there
+ * are insufficient components for a volume, only ncomps
+ * are actually available.
+ */
+void
+print_insufficient_components_msg(
+ int ncomp)
+{
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" <---Failed: only found %d components\n"), ncomp);
+}
+
+/*
+ * FUNCTION: print_hba_insufficient_space_msg(char *name, uint64_t nbytes)
+ *
+ * INPUT: name - a char * HBA name
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Helper to print out a message indicating the the HBA has
+ * insufficient space for use by the mirror layout strategy.
+ */
+void
+print_hba_insufficient_space_msg(
+ char *name,
+ uint64_t nbytes)
+{
+ char *spstr = NULL;
+
+ (void) bytes_to_sizestr(nbytes, &spstr, universal_units, B_FALSE);
+
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" <--Failed: '%s' only has %s available\n"),
+ name, spstr);
+
+ free(spstr);
+}
+
+/*
+ * FUNCTION: print_insufficient_capacity_msg(uint64_t nbytes)
+ *
+ * INPUT: nbytes - available capacity in bytes
+ *
+ * PURPOSE: Helper to print out a message indicating that there
+ * is insufficient space for a volume, only nbytes are
+ * actually available.
+ */
+void
+print_insufficient_capacity_msg(
+ uint64_t nbytes)
+{
+ char *spstr = NULL;
+
+ (void) bytes_to_sizestr(nbytes, &spstr, universal_units, B_FALSE);
+
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" <---Failed: only found %s capacity\n"), spstr);
+
+ free(spstr);
+}
+
+/*
+ * FUNCTION: print_layout_submirrors_msg(char *type, uint64_t nbytes,
+ * int nsubs)
+ *
+ * PURPOSE: Prints a generic status message indicating that layout of
+ * nsub submirrors of the indicated type and size has begun.
+ */
+void
+print_layout_submirrors_msg(
+ char *type,
+ uint64_t nbytes,
+ int nsubs)
+{
+ char *spstr = NULL;
+
+ (void) bytes_to_sizestr(nbytes, &spstr, universal_units, B_FALSE);
+
+ oprintf(OUTPUT_TERSE,
+ gettext(" -->Layout %d %s submirrors with capacity %s\n"),
+ nsubs, type, spstr);
+
+ free(spstr);
+}
+
+/*
+ * FUNCTION: print_layout_submirrors_failed_msg(char *type, int count,
+ * int nsubs)
+ *
+ * PURPOSE: Prints a generic status message indicating that only count
+ * submirrors (out of nsubs) of the indicated type could be
+ * composed.
+ */
+void
+print_layout_submirrors_failed_msg(
+ char *type,
+ int count,
+ int nsubs)
+{
+ if (count == 0) {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" <---Failed, no %s submirrors could "
+ "be composed.\n"),
+ type);
+ } else {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" <---Failed, only %d of %d %s submirror(s) "
+ "could be composed.\n"),
+ count, nsubs, type);
+ }
+}
+
+/*
+ * FUNCTION: print_populate_volume_msg(char *type, uint64_t nbytes)
+ *
+ * PURPOSE: Prints a generic message indicating a population process
+ * for a volume of the indicated type and size is beginning.
+ */
+void
+print_populate_volume_msg(
+ char *type,
+ uint64_t nbytes)
+{
+ char *spstr = NULL;
+
+ (void) bytes_to_sizestr(nbytes, &spstr, universal_units, B_FALSE);
+
+ oprintf(OUTPUT_TERSE,
+ gettext(" --->Populate a %s of capacity %s\n"),
+ type, spstr);
+
+ free(spstr);
+}
+
+/*
+ * FUNCTION: print_populate_volume_ncomps_msg(char *type, uint64_t nbytes,
+ * int ncomps)
+ *
+ * PURPOSE: Prints a generic message indicating a population process
+ * for a volume of the indicated type, size and number of
+ * components is beginning.
+ */
+void
+print_populate_volume_ncomps_msg(
+ char *type,
+ uint64_t nbytes,
+ int ncomps)
+{
+ char *spstr = NULL;
+
+ (void) bytes_to_sizestr(nbytes, &spstr, universal_units, B_FALSE);
+
+ oprintf(OUTPUT_TERSE,
+ gettext(" --->Populate a %s of capacity %s (%d components)\n"),
+ type, spstr, ncomps);
+
+ free(spstr);
+}
+
+/*
+ * FUNCTION: print_populate_success_msg()
+ *
+ * PURPOSE: Prints a generic message indicating a population process
+ * completed successfully.
+ */
+void
+print_populate_success_msg()
+{
+ oprintf(OUTPUT_TERSE,
+ gettext(" <---Success!\n"));
+}
+
+/*
+ * FUNCTION: print_populate_choose_slices_msg()
+ *
+ * PURPOSE: Prints a generic message indicating a population process
+ * is beginning to choose slices.
+ */
+void
+print_populate_choose_slices_msg()
+{
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" choosing \"best\" slices from "
+ "those available...\n"));
+}
+
+/*
+ * FUNCTION: print_populate_no_slices_msg()
+ *
+ * PURPOSE: Prints a layout failure due to no available slices message.
+ */
+void
+print_populate_no_slices_msg()
+{
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" <---Failed: there are no slices available.\n"));
+}
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_messages.h b/usr/src/cmd/lvm/metassist/layout/layout_messages.h
new file mode 100644
index 0000000000..d9c7531b21
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_messages.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef _LAYOUT_MESSAGES_H
+#define _LAYOUT_MESSAGES_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/*
+ * Functions to print out progress, status and error messages that
+ * are shared by layout_concat.c, layout_hsp.c, layout_mirror.c,
+ * layout_stripe.c
+ */
+extern void print_layout_success_msg();
+extern void print_layout_volume_msg(char *type, uint64_t nbytes);
+extern void print_layout_explicit_msg(char *type);
+extern void print_layout_explicit_added_msg(char *comp);
+extern void print_layout_submirrors_msg(char *type, uint64_t nbytes, int nsubs);
+extern void print_layout_submirrors_failed_msg(char *type, int count,
+ int nsubs);
+
+extern void print_populate_volume_msg(char *type, uint64_t nbytes);
+extern void print_populate_volume_ncomps_msg(char *type, uint64_t nbytes,
+ int ncomps);
+extern void print_populate_success_msg();
+extern void print_populate_choose_slices_msg();
+extern void print_populate_no_slices_msg();
+
+extern void print_no_hbas_msg();
+extern void print_debug_failure_msg();
+
+extern void print_insufficient_resources_msg(char *type);
+extern void print_insufficient_hbas_msg(int n);
+extern void print_insufficient_disks_msg(int n);
+extern void print_hba_insufficient_space_msg(char *name, uint64_t nbytes);
+extern void print_insufficient_capacity_msg(uint64_t nbytes);
+extern void print_insufficient_components_msg(int ncomp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LAYOUT_MESSAGES_H */
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_mirror.c b/usr/src/cmd/lvm/metassist/layout/layout_mirror.c
new file mode 100644
index 0000000000..6daf1ab7d5
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_mirror.c
@@ -0,0 +1,2413 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libintl.h>
+
+#include "volume_error.h"
+#include "volume_dlist.h"
+#include "volume_output.h"
+
+#include "layout_concat.h"
+#include "layout_device_cache.h"
+#include "layout_device_util.h"
+#include "layout_discovery.h"
+#include "layout_dlist_util.h"
+#include "layout_messages.h"
+#include "layout_request.h"
+#include "layout_slice.h"
+#include "layout_stripe.h"
+#include "layout_svm_util.h"
+
+#define _LAYOUT_MIRROR_C
+
+static int layout_stripe_submirrors(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ dlist_t **results);
+
+static int layout_concat_submirrors(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ dlist_t **results);
+
+static int compose_stripe_per_hba(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ dlist_t *hbas,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ uint16_t ncomp,
+ uint16_t mincomp,
+ dlist_t **results);
+
+static int compose_stripes_across_hbas(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ dlist_t *hbas,
+ dlist_t *disks,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ uint16_t ncomp,
+ uint16_t mincomp,
+ dlist_t **results);
+
+static int compose_stripes_within_hba(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ dlist_t *hbas,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ uint16_t ncomp,
+ uint16_t mincomp,
+ dlist_t **results);
+
+static int compose_concat_per_hba(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ dlist_t *hbas,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ dlist_t **results);
+
+static int compose_concats_across_hbas(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ dlist_t *hbas,
+ dlist_t *disks,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ dlist_t **results);
+
+static int compose_concats_within_hba(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ dlist_t *hba,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ dlist_t **results);
+
+static int assemble_mirror(
+ devconfig_t *request,
+ dlist_t *subs,
+ devconfig_t **mirror);
+
+static int remove_used_disks(
+ dlist_t **disks,
+ devconfig_t *volume);
+
+static int volume_shares_disk(
+ dm_descriptor_t disk,
+ devconfig_t *volume,
+ boolean_t *bool);
+
+static int select_mpxio_hbas(
+ dlist_t *hbas,
+ dlist_t **mpxio_hbas);
+
+static int set_explicit_submirror_names(
+ dlist_t *reqs,
+ dlist_t *subs);
+
+static int set_explicit_submirror_name(
+ devconfig_t *req,
+ devconfig_t *sub);
+
+/*
+ * FUNCTION: layout_mirror(devconfig_t *request, nbytes, dlist_t **results)
+ *
+ * INPUT: request - pointer to a request devconfig_t
+ * nsubs - number of submirrors
+ * nbytes - desired mirror size
+ *
+ * OUTPUT: results - pointer to a list of volume devconfig_t results
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Main driver to handle a mirror request that does not specify
+ * subcomponents.
+ *
+ * Striped submirrors are tried first, then concats.
+ */
+int
+layout_mirror(
+ devconfig_t *request,
+ uint16_t nsubs,
+ uint64_t nbytes,
+ dlist_t **results)
+{
+ dlist_t *subs = NULL;
+ dlist_t *item = NULL;
+ boolean_t usehsp = B_FALSE;
+ int error = 0;
+
+ if ((error = get_volume_faultrecov(request, &usehsp)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ error = 0;
+ }
+
+ print_layout_volume_msg(devconfig_type_to_str(TYPE_MIRROR), nbytes);
+
+ /* prefer stripe submirrors */
+ if ((error = layout_stripe_submirrors(
+ request, NULL, nbytes, nsubs, &subs)) != 0) {
+ return (error);
+ }
+
+ if (subs == NULL) {
+ /* second chance: mirrored concats */
+ if ((error = layout_concat_submirrors(
+ request, NULL, nbytes, nsubs, &subs)) != 0) {
+ return (error);
+ }
+ }
+
+ if (subs != NULL) {
+
+ devconfig_t *mirror = NULL;
+ dlist_t *iter = NULL;
+
+ /* unset submirror names prior to final assembly */
+ for (iter = subs; iter != NULL; iter = iter->next) {
+ devconfig_t *sub = (devconfig_t *)iter->obj;
+ char *name = NULL;
+
+ (void) devconfig_get_name(sub, &name);
+ release_volume_name(name);
+ (void) devconfig_set_name(sub, "");
+ }
+
+ error = assemble_mirror(request, subs, &mirror);
+ if (error == 0) {
+
+ if ((item = dlist_new_item(mirror)) == NULL) {
+ error = ENOMEM;
+ } else {
+ *results = dlist_append(item, *results, AT_TAIL);
+
+ /* remember submirrors that need HSPs */
+ if (usehsp == B_TRUE) {
+ error = add_to_hsp_list(
+ devconfig_get_components(mirror));
+ }
+
+ print_layout_success_msg();
+ }
+ } else {
+ /* cleanup submirrors */
+ dlist_free_items(subs, free_devconfig_object);
+ subs = NULL;
+ }
+
+ } else if (error != 0) {
+
+ print_debug_failure_msg(devconfig_type_to_str(TYPE_MIRROR),
+ get_error_string(error));
+
+ } else {
+
+ print_insufficient_resources_msg(
+ devconfig_type_to_str(TYPE_MIRROR));
+ error = -1;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: populate_explicit_mirror(devconfig_t *request,
+ * dlist_t **results)
+ *
+ * INPUT: request - pointer to a request devconfig_t
+ *
+ * OUTPUT: results - pointer to a list of volume devconfig_t results
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Processes the input mirror request specifying explicit layout
+ * constraints on the submirrors.
+ *
+ * Primary submirror constraint is explicit type, either
+ * stripe or concat. Submirror types may be mixed.
+ *
+ * Submirror sizes or components may be specified explicitly.
+ *
+ * If the mirror does not specify a size, assume the first explicit
+ * submirror size is the desired size. If a submirror does not
+ * specify a size or components, use the mirror size.
+ *
+ * Scan the submirror requests: those with specific components
+ * get assembled as encountered. The remainder are grouped by
+ * type and handled by layout_stripe_submirrors() or
+ * layout_concat_submirrors().
+ *
+ * If all specified submirrors can be assembled, the final mirror
+ * is assembled and appended to the results list.
+ */
+int
+populate_explicit_mirror(
+ devconfig_t *request,
+ dlist_t **results)
+{
+ dlist_t *composed = NULL;
+ dlist_t *list = NULL;
+ dlist_t *iter = NULL;
+ dlist_t *concats_by_size = NULL;
+ dlist_t *stripes_by_size = NULL;
+ int nsubs = 0;
+ int error = 0;
+ uint64_t msize = 0;
+ boolean_t usehsp = B_FALSE;
+
+ list = devconfig_get_components(request);
+ nsubs = dlist_length(list);
+
+ if ((error = get_volume_faultrecov(request, &usehsp)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ error = 0;
+ }
+
+ if ((error = devconfig_get_size(request, &msize)) != 0) {
+ if (error == ERR_ATTR_UNSET) {
+ error = 0;
+ msize = 0;
+ } else {
+ return (error);
+ }
+ }
+
+ print_layout_explicit_msg(devconfig_type_to_str(TYPE_MIRROR));
+
+ /*
+ * Scan the list of specified submirrors, collect those that only
+ * specify size (or no size). Process those with explicit components
+ * immediately.
+ */
+ composed = NULL;
+ for (iter = list; (iter != NULL) && (error == 0); iter = iter->next) {
+
+ devconfig_t *comp = (devconfig_t *)iter->obj;
+ component_type_t ctype = TYPE_UNKNOWN;
+ dlist_t *clist = NULL;
+ uint64_t csize = 0;
+ dlist_t *item = NULL;
+
+ (void) devconfig_get_type(comp, &ctype);
+ (void) devconfig_get_size(comp, &csize);
+ clist = devconfig_get_components(comp);
+
+ if (clist != NULL) {
+
+ /* components specified */
+
+ if (ctype == TYPE_STRIPE) {
+ error = populate_explicit_stripe(comp, &item);
+ } else {
+ error = populate_explicit_concat(comp, &item);
+ }
+
+ if (error == 0) {
+ set_explicit_submirror_name(
+ comp, (devconfig_t *)item->obj);
+ composed = dlist_append(item, composed, AT_TAIL);
+ }
+
+ } else {
+
+ /* no components specified */
+
+ /* if no size is specified, it needs to be inferred */
+
+ if (msize == 0) {
+ /* mirror specified no size, first explicit submirror */
+ /* size is assumed to be the desired mirror size */
+ msize = csize;
+ }
+ if (csize == 0) {
+ /* this submirror specified no size, use mirror size */
+ devconfig_set_size(comp, msize);
+ }
+
+ if ((item = dlist_new_item(comp)) == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ if (ctype == TYPE_STRIPE) {
+ stripes_by_size = dlist_append(
+ item, stripes_by_size, AT_TAIL);
+ } else {
+ concats_by_size = dlist_append(
+ item, concats_by_size, AT_TAIL);
+ }
+
+ }
+ }
+
+ /* compose stripes specified by size */
+ if ((error == 0) && (stripes_by_size != NULL)) {
+ uint16_t n = dlist_length(stripes_by_size);
+ dlist_t *stripes = NULL;
+ if ((error = layout_stripe_submirrors(
+ request, composed, msize, n, &stripes)) == 0) {
+
+ /* adjust stripe names */
+ set_explicit_submirror_names(stripes_by_size, stripes);
+ composed = dlist_append(stripes, composed, AT_TAIL);
+
+ } else {
+ /* these stripes failed, skip concats_by_size */
+ dlist_free_items(stripes, free_devconfig_object);
+ dlist_free_items(concats_by_size, NULL);
+ concats_by_size = NULL;
+ }
+ dlist_free_items(stripes_by_size, NULL);
+ }
+
+ /* compose concats specified by size */
+ if ((error == 0) && (concats_by_size != NULL)) {
+ uint16_t n = dlist_length(concats_by_size);
+ dlist_t *concats = NULL;
+ if ((error = layout_concat_submirrors(
+ request, composed, msize, n, &concats)) == 0) {
+
+ /* adjust concat names */
+ set_explicit_submirror_names(concats_by_size, concats);
+ composed = dlist_append(concats, composed, AT_TAIL);
+
+ } else {
+
+ /* these concats failed */
+ dlist_free_items(concats, free_devconfig_object);
+ }
+
+ dlist_free_items(concats_by_size, NULL);
+ }
+
+ if ((composed != NULL) && ((dlist_length(composed) == nsubs))) {
+
+ /* assemble final mirror */
+
+ devconfig_t *mirror = NULL;
+ dlist_t *item = NULL;
+
+ if ((error = assemble_mirror(request, composed, &mirror)) == 0) {
+
+ if ((item = dlist_new_item(mirror)) == NULL) {
+ error = ENOMEM;
+ } else {
+ *results = dlist_append(item, *results, AT_TAIL);
+ if (usehsp == B_TRUE) {
+ error = add_to_hsp_list(
+ devconfig_get_components(mirror));
+ }
+ print_layout_success_msg();
+ }
+ }
+
+ } else if (error != 0) {
+
+ print_debug_failure_msg(
+ devconfig_type_to_str(TYPE_MIRROR),
+ get_error_string(error));
+
+ } else {
+
+ dlist_free_items(composed, free_devconfig_object);
+ print_insufficient_resources_msg(
+ devconfig_type_to_str(TYPE_MIRROR));
+ error = -1;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: assemble_mirror(devconfig_t *request, dlist_t *subs,
+ * devconfig_t **mirror)
+ *
+ * INPUT: request - pointer to a devconfig_t of the current request
+ * subs - pointer to a list of composed submirrors
+ *
+ * OUPUT: mirror - pointer to a devconfig_t to hold final mirror
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Helper which creates and populates a mirror devconfig_t
+ * struct using information from the input request and the
+ * list of submirror components.
+ *
+ * Determines the name of the mirror either from the request
+ * or from the default naming scheme and assigns names to
+ * unnamed submirrors according to the default naming scheme.
+ *
+ * Sets the read and write strategies, and the resync pass
+ * number for the mirror if values are specified in the request.
+ *
+ * Attaches the input list of submirrors to the devconfig.
+ */
+static int
+assemble_mirror(
+ devconfig_t *request,
+ dlist_t *subs,
+ devconfig_t **mirror)
+{
+ dlist_t *iter = NULL;
+ char *name = NULL;
+ int error = 0;
+
+ if ((error = new_devconfig(mirror, TYPE_MIRROR)) == 0) {
+ /* set stripe name, use requested name if specified */
+ if ((error = devconfig_get_name(request, &name)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ volume_set_error(gettext("error getting requested name\n"));
+ } else {
+ error = 0;
+ }
+ }
+
+ if (error == 0) {
+ if (name == NULL) {
+ if ((error = get_next_volume_name(&name,
+ TYPE_MIRROR)) == 0) {
+ error = devconfig_set_name(*mirror, name);
+ free(name);
+ /* get name for generating submirror names below */
+ error = devconfig_get_name(*mirror, &name);
+ }
+ } else {
+ error = devconfig_set_name(*mirror, name);
+ }
+ }
+ }
+
+ /* assign name to any unnamed submirror */
+ for (iter = subs;
+ (error == 0) && (iter != NULL);
+ iter = iter->next) {
+
+ devconfig_t *sub = (devconfig_t *)iter->obj;
+ char *subname = NULL;
+
+ error = devconfig_get_name(sub, &subname);
+ if ((error == ERR_ATTR_UNSET) || (subname == NULL) ||
+ (*subname == '\0')) {
+ ((error = get_next_submirror_name(name, &subname)) != 0) ||
+ (error = devconfig_set_name(sub, subname));
+ free(subname);
+ }
+ }
+
+ if (error == 0) {
+ mirror_read_strategy_t read = 0;
+ if ((error = get_mirror_read_strategy(request, &read)) == 0) {
+ error = devconfig_set_mirror_read(*mirror, read);
+ } else if (error == ERR_ATTR_UNSET) {
+ error = 0;
+ }
+ }
+
+ if (error == 0) {
+ mirror_write_strategy_t write = 0;
+ if ((error = get_mirror_write_strategy(request, &write)) == 0) {
+ error = devconfig_set_mirror_write(*mirror, write);
+ } else if (error == ERR_ATTR_UNSET) {
+ error = 0;
+ }
+ }
+
+ if (error == 0) {
+ uint16_t pass = 0;
+ if ((error = get_mirror_pass(request, &pass)) == 0) {
+ error = devconfig_set_mirror_pass(*mirror, pass);
+ } else if (error == ERR_ATTR_UNSET) {
+ error = 0;
+ }
+ }
+
+ /* arrange submirrors in ascending size order */
+ if (error == 0) {
+ dlist_t *sorted = NULL;
+ dlist_t *next = NULL;
+
+ iter = subs;
+ while (iter != NULL) {
+
+ next = iter->next;
+ iter->next = NULL;
+ iter->prev = NULL;
+
+ sorted = dlist_insert_ordered(iter,
+ sorted, ASCENDING, compare_devconfig_sizes);
+
+ iter = next;
+ }
+ subs = sorted;
+ }
+
+ if (error == 0) {
+ devconfig_set_components(*mirror, subs);
+ } else {
+ free_devconfig(*mirror);
+ *mirror = NULL;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: layout_stripe_submirrors(devconfig_t *request, dlist_t *cursubs,
+ * uint64_t nbytes, uint16_t nsubs, dlist_t **results)
+ *
+ * INPUT: request - pointer to a devconfig_t of the current request
+ * cursubs - pointer to a list of already composed submirrors
+ * these may affect disk and HBA choices for new
+ * submirrors being composed and are passed along
+ * into the component selection functions.
+ * nbytes - the desired capacity for the stripes
+ *
+ * OUPUT: results - pointer to a list of composed volumes
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Main layout driver for composing stripe submirrors.
+ *
+ * Attempts to construct nsub submirrors of size nbytes.
+ *
+ * Several different layout strategies are tried in order
+ * of preference until one succeeds or there are none left.
+ *
+ * 1 - mirror with all stripes on the MPXIO "controller"
+ * . requires MPXIO to be enabled
+ * . requires nsubs * mincomp available disks on the
+ * MPXIO HBA
+ *
+ * 2 - mirror with stripes within separate HBAs of same type
+ * . requires nsubs HBAs with mincomp disks
+ * . stripe width is driven by number of disks on HBA
+ *
+ * 3 - mirror with stripes across HBAs of same type
+ * . requires mincomp HBAs with nsubs disks
+ * (each stripe has a disk per HBA)
+ * . stripe width is driven by number of HBAs
+ *
+ * 4 - mirror with stripes within separate HBAs of mixed type
+ * . requires nsubs HBAs with mincomp disks
+ * . stripe width is driven by number of disks on HBA
+ *
+ * 5 - mirror with stripes across HBAs of mixed type
+ * . requires mincomp HBAs with nsubs disks
+ * (each stripe has a disk per HBA)
+ * . stripe width is driven by number of HBAs
+ *
+ * 6 - mirror with all stripes within the same HBA
+ * . requires an HBA with mincomp * nsubs disks
+ *
+ * get available HBAs
+ *
+ * group HBAs by characteristics
+ * for (each HBA grouping) and (nsub stripes not composed) {
+ * select next HBA group
+ * for (strategy[1,2,3]) and (nsub stripes not composed) {
+ * compose nsub stripes using HBAs in group
+ * }
+ * }
+ *
+ * if (nsub stripes not composed) {
+ * for (strategy[4,5,6]) and (nsub stripes not composed) {
+ * compose nsub stripes using all HBAs
+ * }
+ * }
+ *
+ * if (all stripes composed) {
+ * append composed stripes to results
+ * }
+ *
+ */
+static int
+layout_stripe_submirrors(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ dlist_t **results)
+{
+ /*
+ * these enums define the # of strategies and the preference order
+ * in which they are tried
+ */
+ typedef enum {
+ ALL_STRIPES_ON_MPXIO = 0,
+ STRIPE_PER_SIMILAR_HBA,
+ STRIPE_ACROSS_SIMILAR_HBAS,
+ N_SIMILAR_HBA_STRATEGIES
+ } similar_hba_strategy_order_t;
+
+ typedef enum {
+ STRIPE_PER_ANY_HBA = 0,
+ STRIPE_ACROSS_ANY_HBAS,
+ STRIPE_WITHIN_ANY_HBA,
+ N_ANY_HBA_STRATEGIES
+ } any_hba_strategy_order_t;
+
+ dlist_t *usable_hbas = NULL;
+ dlist_t *similar_hba_groups = NULL;
+ dlist_t *iter = NULL;
+ dlist_t *subs = NULL;
+
+ boolean_t usehsp = B_FALSE;
+ uint16_t mincomp = 0;
+ uint16_t maxcomp = 0;
+
+ int error = 0;
+
+ (error = get_usable_hbas(&usable_hbas));
+ if (error != 0) {
+ return (error);
+ }
+
+ print_layout_submirrors_msg(devconfig_type_to_str(TYPE_STRIPE),
+ nbytes, nsubs);
+
+ if (dlist_length(usable_hbas) == 0) {
+ print_no_hbas_msg();
+ volume_set_error(gettext("There are no usable HBAs."));
+ return (-1);
+ }
+
+ similar_hba_groups = NULL;
+ ((error = group_similar_hbas(usable_hbas, &similar_hba_groups)) != 0) ||
+
+ /*
+ * determine the min/max number of stripe components
+ * based on the request, the diskset defaults or the
+ * global defaults. These are absolute limits, the
+ * actual values are determined by the number of HBAs
+ * and/or disks available.
+ */
+ (error = get_stripe_min_comp(request, &mincomp)) ||
+ (error = get_stripe_max_comp(request, &maxcomp)) ||
+ (error = get_volume_faultrecov(request, &usehsp));
+ if (error != 0) {
+ return (error);
+ }
+
+ for (iter = similar_hba_groups;
+ (error == 0) && (subs == NULL) && (iter != NULL);
+ iter = iter->next) {
+
+ dlist_t *hbas = (dlist_t *)iter->obj;
+
+ similar_hba_strategy_order_t order;
+
+ for (order = ALL_STRIPES_ON_MPXIO;
+ (order < N_SIMILAR_HBA_STRATEGIES) &&
+ (subs == NULL) && (error == 0);
+ order++) {
+
+ dlist_t *selhbas = NULL;
+ dlist_t *disks = NULL;
+ int n = 0;
+
+ switch (order) {
+
+ case ALL_STRIPES_ON_MPXIO:
+
+ if (is_mpxio_enabled() == B_TRUE) {
+ dlist_t *mpxio_hbas = NULL;
+
+ /* see if any HBA supports MPXIO */
+ error = select_mpxio_hbas(hbas, &mpxio_hbas);
+ if ((error == 0) && (mpxio_hbas != NULL)) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+gettext(" -->Strategy 1: use %d-%d MPXIO disks\n"),
+ mincomp * nsubs, maxcomp * nsubs);
+/* END CSTYLED */
+
+ /* see if MPXIO HBA has enough disks */
+ error = select_hbas_with_n_disks(
+ request, mpxio_hbas, (mincomp * nsubs),
+ &selhbas, &disks);
+
+ if ((error == 0) && (dlist_length(selhbas) > 0)) {
+ error = compose_stripes_within_hba(
+ request, cursubs, mpxio_hbas, nbytes,
+ nsubs, maxcomp, mincomp, &subs);
+ } else {
+ print_insufficient_hbas_msg(n);
+ }
+ }
+
+ dlist_free_items(mpxio_hbas, NULL);
+ }
+
+ break;
+
+ case STRIPE_PER_SIMILAR_HBA:
+
+ error = select_hbas_with_n_disks(
+ request, hbas, mincomp, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+ gettext(" -->Strategy 2: use %d-%d disks from %d similar HBAs - stripe per HBA\n"),
+ mincomp, maxcomp, nsubs);
+/* END CSTYLED */
+
+ if ((n = dlist_length(selhbas)) >= nsubs) {
+ error = compose_stripe_per_hba(
+ request, cursubs, selhbas, nbytes,
+ nsubs, maxcomp, mincomp, &subs);
+ } else {
+ print_insufficient_hbas_msg(n);
+ }
+ }
+
+ break;
+
+ case STRIPE_ACROSS_SIMILAR_HBAS:
+
+ error = select_hbas_with_n_disks(
+ request, hbas, nsubs, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+gettext(" -->Strategy 3: use %d disks from %d-%d similar HBAs - stripe across HBAs \n"),
+ nsubs, mincomp, maxcomp);
+/* END CSTYLED */
+
+ if ((n = dlist_length(selhbas)) >= mincomp) {
+ error = compose_stripes_across_hbas(
+ request, cursubs, selhbas, disks,
+ nbytes, nsubs, maxcomp, mincomp, &subs);
+ } else {
+ print_insufficient_hbas_msg(n);
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ dlist_free_items(selhbas, NULL);
+ dlist_free_items(disks, NULL);
+ }
+ }
+
+ for (iter = similar_hba_groups; iter != NULL; iter = iter->next) {
+ dlist_free_items((dlist_t *)iter->obj, NULL);
+ }
+ dlist_free_items(similar_hba_groups, NULL);
+
+ /* retry using all available HBAs */
+ if (subs == NULL) {
+
+ any_hba_strategy_order_t order;
+
+ for (order = STRIPE_PER_ANY_HBA;
+ (order < N_ANY_HBA_STRATEGIES) &&
+ (subs == NULL) && (error == 0);
+ order++) {
+
+ dlist_t *selhbas = NULL;
+ dlist_t *disks = NULL;
+ int n = 0;
+
+ switch (order) {
+
+ case STRIPE_PER_ANY_HBA:
+
+ error = select_hbas_with_n_disks(
+ request, usable_hbas, nsubs, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+gettext(" -->Strategy 4: use %d-%d disks from any %d HBAs - stripe per HBA\n"),
+ mincomp, maxcomp, nsubs);
+/* END CSTYLED */
+
+ if ((n = dlist_length(selhbas)) >= nsubs) {
+ error = compose_stripe_per_hba(
+ request, cursubs, selhbas, nbytes,
+ nsubs, maxcomp, mincomp, &subs);
+ } else {
+ print_insufficient_hbas_msg(n);
+ }
+ }
+
+ break;
+
+ case STRIPE_ACROSS_ANY_HBAS:
+
+ error = select_hbas_with_n_disks(
+ request, usable_hbas, nsubs, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+gettext(" -->Strategy 5: use %d disks from %d-%d HBAs - stripe across HBAs \n"),
+ nsubs, mincomp, maxcomp);
+/* END CSTYLED */
+
+ if ((n = dlist_length(selhbas)) >= mincomp) {
+ error = compose_stripes_across_hbas(
+ request, cursubs, selhbas, disks,
+ nbytes, nsubs, maxcomp, mincomp, &subs);
+ } else {
+ print_insufficient_hbas_msg(n);
+ }
+ }
+
+ break;
+
+ case STRIPE_WITHIN_ANY_HBA:
+
+ error = select_hbas_with_n_disks(
+ request, usable_hbas, (mincomp * nsubs),
+ &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+gettext(" -->Strategy 6: use %d-%d disks from any single HBA - %d stripes within HBA\n"),
+ mincomp * nsubs, maxcomp * nsubs, nsubs);
+/* END CSTYLED */
+ if ((n = dlist_length(selhbas)) > 0) {
+ error = compose_stripes_within_hba(
+ request, cursubs, selhbas, nbytes,
+ nsubs, maxcomp, mincomp, &subs);
+ } else {
+ print_insufficient_hbas_msg(n);
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ dlist_free_items(selhbas, NULL);
+ dlist_free_items(disks, NULL);
+ }
+ }
+
+ if (error == 0) {
+ *results = dlist_append(subs, *results, AT_TAIL);
+ }
+ return (error);
+}
+
+/*
+ * FUNCTION: layout_concat_submirrors(devconfig_t *request, dlist_t *cursubs,
+ * uint64_t nbytes, uint16_t nsubs, dlist_t **results)
+ *
+ * INPUT: request - pointer to a devconfig_t of the current request
+ * cursubs - pointer to a list of already composed submirrors
+ * nbytes - the desired capacity for the concats
+ *
+ * OUPUT: results - pointer to a list of composed volumes
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Main layout driver for composing concat submirrors.
+ *
+ * Attempts to construct nsub submirrors of size nbytes.
+ *
+ * Several different layout strategies are tried in order
+ * of preference until one succeeds or there are none left.
+ *
+ * 1 - mirror with all concats on the MPXIO "controller"
+ * . requires MPXIO to be enabled
+ * . requires nsubs available disks on the MPXIO HBA
+ *
+ * 2 - mirror with concats on separate HBAs of same type
+ * . requires nsubs HBAs with available disks
+ *
+ * 3 - mirror with concats across HBAs of same type
+ * . requires an HBA with at least 1 available disk
+ *
+ * 4 - mirror with concats on separate HBAs of mixed type
+ * . requires nsubs HBAs with available disks
+ *
+ * 5 - mirror with concats across HBAs of mixed type
+ * . requires an HBA with at least 1 available disk
+ *
+ * 6 - mirror with all concats on the same HBA
+ * . requires an HBA with at least nsubs available disks
+ *
+ * get available HBAs
+ *
+ * group HBAs by characteristics
+ * for (each HBA grouping) and (nsub concats not composed) {
+ * select next HBA group
+ * for (strategy[1,2,3]) and (nsub concats not composed) {
+ * compose nsub concats, nbytes in size
+ * }
+ * }
+ *
+ * if (nsub concats not composed) {
+ * for (strategy[4,5,6]) and (nsub concats not composed) {
+ * compose nsub concats, nbytes in size
+ * }
+ * }
+ *
+ * if (all concats composed) {
+ * append composed concats to results
+ * }
+ *
+ */
+static int
+layout_concat_submirrors(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ dlist_t **results)
+{
+ /*
+ * these enums define the # of strategies and the preference order
+ * in which they are tried
+ */
+ typedef enum {
+ ALL_CONCATS_ON_MPXIO = 0,
+ CONCAT_PER_SIMILAR_HBA,
+ CONCAT_ACROSS_SIMILAR_HBAS,
+ N_SIMILAR_HBA_STRATEGIES
+ } similar_hba_strategy_order_t;
+
+ typedef enum {
+ CONCAT_PER_ANY_HBA = 0,
+ CONCAT_ACROSS_ANY_HBAS,
+ CONCAT_WITHIN_ANY_HBA,
+ N_ANY_HBA_STRATEGIES
+ } any_hba_strategy_order_t;
+
+ dlist_t *usable_hbas = NULL;
+ dlist_t *similar_hba_groups = NULL;
+ dlist_t *iter = NULL;
+ dlist_t *subs = NULL;
+
+ boolean_t usehsp = B_FALSE;
+
+ int error = 0;
+
+ (error = get_usable_hbas(&usable_hbas));
+ if (error != 0) {
+ return (error);
+ }
+
+ print_layout_submirrors_msg(devconfig_type_to_str(TYPE_CONCAT),
+ nbytes, nsubs);
+
+ if (dlist_length(usable_hbas) == 0) {
+ print_no_hbas_msg();
+ volume_set_error(gettext("There are no usable HBAs."));
+ return (-1);
+ }
+
+ similar_hba_groups = NULL;
+ ((error = group_similar_hbas(usable_hbas, &similar_hba_groups)) != 0) ||
+ (error = get_volume_faultrecov(request, &usehsp));
+ if (error != 0) {
+ return (error);
+ }
+
+ for (iter = similar_hba_groups;
+ (error == 0) && (subs == NULL) && (iter != NULL);
+ iter = iter->next) {
+
+ dlist_t *hbas = (dlist_t *)iter->obj;
+
+ similar_hba_strategy_order_t order;
+
+ for (order = ALL_CONCATS_ON_MPXIO;
+ (order < N_SIMILAR_HBA_STRATEGIES) &&
+ (subs == NULL) && (error == 0);
+ order++) {
+
+ dlist_t *selhbas = NULL;
+ dlist_t *disks = NULL;
+ int n = 0;
+
+ switch (order) {
+
+ case ALL_CONCATS_ON_MPXIO:
+
+ if (is_mpxio_enabled() == B_TRUE) {
+ dlist_t *mpxio_hbas = NULL;
+
+ /* see if any HBA supports MPXIO */
+ error = select_mpxio_hbas(hbas, &mpxio_hbas);
+ if ((error == 0) && (mpxio_hbas != NULL)) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+ gettext(" -->Strategy 1: use at least %d MPXIO disks\n"),
+ nsubs);
+/* END CSTYLED */
+
+ /* see if MPXIO HBA has enough disks */
+ error = select_hbas_with_n_disks(
+ request, hbas, nsubs, &selhbas, &disks);
+
+ if ((error == 0) &&
+ ((n = dlist_length(selhbas)) > 0)) {
+ error = compose_concats_within_hba(
+ request, cursubs, mpxio_hbas, nbytes,
+ nsubs, &subs);
+ } else {
+ print_insufficient_hbas_msg(n);
+ }
+ }
+
+ dlist_free_items(mpxio_hbas, NULL);
+ }
+
+ break;
+
+ case CONCAT_PER_SIMILAR_HBA:
+
+ error = select_hbas_with_n_disks(
+ request, hbas, 1, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+ gettext(" -->Strategy 2: use any disks from %d similar HBAs - concat per HBA\n"),
+ nsubs);
+/* END CSTYLED */
+
+ if ((n = dlist_length(selhbas)) >= nsubs) {
+ error = compose_concat_per_hba(
+ request, cursubs, selhbas,
+ nbytes, nsubs, &subs);
+ } else {
+ print_insufficient_hbas_msg(n);
+ }
+ }
+
+ break;
+
+ case CONCAT_ACROSS_SIMILAR_HBAS:
+
+ error = select_hbas_with_n_disks(
+ request, hbas, 1, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+ gettext(" -->Strategy 3: use any disks from any similar HBAs - "
+ "%d concats across HBAs\n"),
+ nsubs);
+/* END CSTYLED */
+ error = compose_concats_across_hbas(
+ request, cursubs, selhbas, disks,
+ nbytes, nsubs, &subs);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ dlist_free_items(selhbas, NULL);
+ dlist_free_items(disks, NULL);
+ }
+ }
+
+ for (iter = similar_hba_groups; iter != NULL; iter = iter->next) {
+ dlist_free_items((dlist_t *)iter->obj, NULL);
+ }
+ dlist_free_items(similar_hba_groups, NULL);
+
+ /* retry using all available HBAs */
+ if (subs == NULL) {
+
+ any_hba_strategy_order_t order;
+
+ for (order = CONCAT_PER_ANY_HBA;
+ (order < N_ANY_HBA_STRATEGIES) &&
+ (subs == NULL) && (error == 0);
+ order++) {
+
+ dlist_t *selhbas = NULL;
+ dlist_t *disks = NULL;
+ int n = 0;
+
+ switch (order) {
+
+ case CONCAT_PER_ANY_HBA:
+
+ error = select_hbas_with_n_disks(
+ request, usable_hbas, 1, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+ gettext(" -->Strategy 4: use any disks from %d HBAs - concat per HBA\n"),
+ nsubs);
+/* END CSTYLED */
+ if ((n = dlist_length(selhbas)) >= nsubs) {
+ error = compose_concat_per_hba(
+ request, cursubs, selhbas,
+ nbytes, nsubs, &subs);
+ } else {
+ print_insufficient_hbas_msg(n);
+ }
+ }
+ break;
+
+ case CONCAT_ACROSS_ANY_HBAS:
+
+ error = select_hbas_with_n_disks(
+ request, usable_hbas, 1, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+ gettext(" -->Strategy 5: use any disks from any HBA - %d concats across HBAs\n"),
+ nsubs);
+/* END CSTYLED */
+ error = compose_concats_across_hbas(
+ request, cursubs, selhbas, disks,
+ nbytes, nsubs, &subs);
+ }
+
+ break;
+
+ case CONCAT_WITHIN_ANY_HBA:
+
+ error = select_hbas_with_n_disks(
+ request, usable_hbas, 1, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+gettext(" -->Strategy 6: use any disks from any single HBA - %d concats within an HBA\n"),
+ nsubs);
+/* END CSTYLED */
+
+ if ((n = dlist_length(selhbas)) > 0) {
+ error = compose_concats_within_hba(
+ request, cursubs, selhbas,
+ nbytes, nsubs, &subs);
+ } else {
+ print_insufficient_hbas_msg(n);
+ }
+
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ dlist_free_items(selhbas, NULL);
+ dlist_free_items(disks, NULL);
+ }
+ }
+
+ if (error == 0) {
+ *results = dlist_append(subs, *results, AT_TAIL);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: compose_stripe_per_hba(devconfig_t *request,
+ * dlist_t *cursubs, dlist_t *hbas, uint64_t nbytes,
+ * uint16_t nsubs, int maxcomp, int mincomp,
+ * dlist_t **results)
+ *
+ * INPUT: request - pointer to a devconfig_t of the current request
+ * cursubs - pointer to a list of already composed submirrors
+ * hbas - pointer to a list of available HBAs
+ * nbytes - the desired capacity for the stripes
+ * nsubs - the desired number of stripes
+ * maxcomp - the maximum number of stripe components
+ * mincomp - the minimum number of stripe components
+ *
+ * OUPUT: results - pointer to a list of composed volumes
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Layout function which composes the requested number of stripes
+ * of the desired size using available disks on any of the HBAs
+ * from the input list.
+ *
+ * The number of components within the composed stripes will be
+ * in the range of mincomp to ncomp, preferring more components
+ * over fewer. All stripes composed by a single call to this
+ * function will have the same number of components.
+ *
+ * Each stripe will use disks from a single HBA.
+ *
+ * All input HBAs are expected to have at least mincomp available
+ * disks.
+ *
+ * If the stripes can be composed, they are appended to the list
+ * of result volumes.
+ *
+ * while (more HBAs and more stripes to compose) {
+ * select next HBA
+ * get available space for this HBA
+ * get available disks for this HBA
+ * if (not enough space or disks) {
+ * continue
+ * }
+ *
+ * use # disks as # of stripe components - limit to maxcomp
+ * for ((ncomps downto mincomp) && (more stripes to compose)) {
+ * while (more stripes to compose) {
+ * if a stripe can be composed using disks {
+ * save stripe
+ * increment stripe count
+ * }
+ * while (more HBAs and more stripes to compose) {
+ * select next HBA
+ * get available space for this HBA
+ * get available disks for this HBA
+ * if (not enough space or disks) {
+ * continue
+ * }
+ * if a stripe can be composed using disks {
+ * save stripe
+ * increment stripe count
+ * }
+ * }
+ *
+ * if (not all stripes composed) {
+ * delete any compose stripes
+ * }
+ * }
+ * }
+ *
+ * if (not all stripes composed) {
+ * delete any stripes composed
+ * }
+ * }
+ *
+ * if (not all stripes composed) {
+ * delete any stripes composed
+ * }
+ *
+ * append composed stripes to results
+ */
+static int
+compose_stripe_per_hba(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ dlist_t *hbas,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ uint16_t maxcomp,
+ uint16_t mincomp,
+ dlist_t **results)
+{
+ int error = 0;
+ dlist_t *list = NULL;
+ dlist_t *iter = NULL;
+
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" --->Trying to compose %d Stripes with "
+ "%d-%d components on separate HBAs.\n"),
+ nsubs, mincomp, maxcomp);
+
+ for (iter = hbas;
+ (list == NULL) && (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ dm_descriptor_t hba = (uintptr_t)iter->obj;
+ dlist_t *disks = NULL;
+ uint64_t space = 0;
+ int ncomp = 0;
+ char *name;
+
+ ((error = get_display_name(hba, &name)) != 0) ||
+ (error = hba_get_avail_disks_and_space(request,
+ hba, &disks, &space));
+ if (error != 0) {
+ continue;
+ }
+
+ /* check for sufficient space and minimum # of disks */
+ if (space < nbytes) {
+ (void) print_hba_insufficient_space_msg(name, space);
+ dlist_free_items(disks, NULL);
+ continue;
+ }
+
+ if ((ncomp = dlist_length(disks)) < mincomp) {
+ print_insufficient_disks_msg(ncomp);
+ dlist_free_items(disks, NULL);
+ continue;
+ }
+
+ /* make the stripe as wide as possible, up to maxcomp */
+ for (ncomp = ((ncomp > maxcomp) ? maxcomp : ncomp);
+ (list == NULL) && (ncomp >= mincomp) && (error == 0);
+ ncomp--) {
+
+ int count = 0;
+
+ /* try composing nsubs stripes with ncomp components */
+ while (count < nsubs) {
+
+ devconfig_t *stripe = NULL;
+ dlist_t *item = NULL;
+ dlist_t *iter1 = NULL;
+
+ /* build first stripe using disks on this HBA */
+ if (((error = populate_stripe(request, nbytes,
+ disks, ncomp, cursubs, &stripe)) != 0) ||
+ (stripe == NULL)) {
+ /* first stripe failed at the current width */
+ /* break while loop and try a different width */
+ break;
+ }
+
+ /* composed a stripe */
+ if ((item = dlist_new_item((void*)stripe)) == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ ++count;
+ list = dlist_append(item, list, AT_TAIL);
+
+ /* compose stripes on remaining HBAs */
+ for (iter1 = iter->next;
+ (count < nsubs) && (iter1 != NULL) && (error == 0);
+ iter1 = iter1->next) {
+
+ dm_descriptor_t hba1 = (uintptr_t)iter1->obj;
+ uint64_t space1 = 0;
+ dlist_t *disks1 = NULL;
+
+ error = hba_get_avail_disks_and_space(request,
+ hba1, &disks1, &space1);
+ if (error != 0) {
+ continue;
+ }
+
+ /* enough space/disks on this HBA? */
+ if ((dlist_length(disks1) < ncomp) ||
+ (space1 < nbytes)) {
+ dlist_free_items(disks1, NULL);
+ continue;
+ }
+
+ stripe = NULL;
+ error = populate_stripe(
+ request, nbytes, disks1,
+ ncomp, cursubs, &stripe);
+
+ if (stripe != NULL) {
+ /* prepare to compose another */
+ if ((item = dlist_new_item(
+ (void *)stripe)) == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ list = dlist_append(item, list, AT_TAIL);
+ ++count;
+ }
+
+ dlist_free_items(disks1, NULL);
+ disks1 = NULL;
+ }
+
+ if ((iter1 == NULL) && (count < nsubs)) {
+ /*
+ * no HBAs remain and haven't composed
+ * enough stripes at the current width.
+ * break while loop and try another width.
+ */
+ break;
+ }
+ }
+
+ if (count < nsubs) {
+ /*
+ * stripe composition at current width failed...
+ * prepare to try a narrower width.
+ * NB: narrower widths may work since some HBA(s)
+ * may have fewer available disks
+ */
+ print_layout_submirrors_failed_msg(
+ devconfig_type_to_str(TYPE_STRIPE),
+ count, nsubs);
+
+ dlist_free_items(list, free_devconfig_object);
+ list = NULL;
+ }
+ }
+
+ dlist_free_items(disks, NULL);
+ disks = NULL;
+ }
+
+ if (error == 0) {
+ *results = dlist_append(list, *results, AT_TAIL);
+ } else {
+ dlist_free_items(list, free_devconfig_object);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: compose_stripes_across_hbas(devconfig_t *request,
+ * dlist_t *cursubs, dlist_t *hbas, dlist_t *disks,
+ * uint64_t nbytes, uint16_t nsubs, int maxcomp,
+ * int mincomp, dlist_t **results)
+ *
+ * INPUT: request - pointer to a devconfig_t of the current request
+ * cursubs - pointer to a list of already composed submirrors
+ * hbas - pointer to a list of available HBAs
+ * disks - pointer to a list of available disks on the HBAs
+ * nbytes - the desired capacity for the stripes
+ * nsubs - the desired number of stripes
+ * ncomp - the maximum number of stripe components
+ * mincomp - the minimum number of stripe components
+ *
+ * OUPUT: results - pointer to a list of composed volumes
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Layout function which composes the requested number of stripes
+ * of the desired size using available disks on any of the HBAs
+ * from the input list.
+ *
+ * The number of components within the composed stripes will be
+ * in the range of mincomp to ncomp, preferring more components
+ * over fewer. All stripes composed by a single call to this
+ * function will have the same number of components.
+ *
+ * Each stripe will use a disk from several different HBAs.
+ *
+ * All input HBAs are expected to have at least nsubs available
+ * disks.
+ *
+ * If the stripes can be composed, they are appended to the list
+ * of result volumes.
+ *
+ * for (ncomps downto mincomp) {
+ *
+ * copy the input disk list
+ * while (more stripes to compose) {
+ * if a stripe can be composed using disks {
+ * save stripe
+ * remove used disks from disk list
+ * increment stripe count
+ * } else
+ * end while loop
+ * }
+ *
+ * free copied disk list
+ * if (not all stripes composed) {
+ * delete any stripes composed
+ * decrement ncomps
+ * }
+ * }
+ *
+ * if (not all stripes composed) {
+ * delete any stripes composed
+ * }
+ *
+ * append composed stripes to results
+ */
+static int
+compose_stripes_across_hbas(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ dlist_t *hbas,
+ dlist_t *disks,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ uint16_t ncomp,
+ uint16_t mincomp,
+ dlist_t **results)
+{
+ int error = 0;
+ int count = 0;
+
+ dlist_t *list = NULL;
+
+ while ((ncomp >= mincomp) && (count < nsubs) && (error == 0)) {
+
+ dlist_t *iter;
+ dlist_t *item;
+ dlist_t *disks_copy = NULL;
+
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" --->Trying to compose %d Stripes with "
+ "%d components across %d HBAs.\n"),
+ nsubs, ncomp, dlist_length(hbas));
+
+ /* copy disk list, it is modified by the while loop */
+ for (iter = disks; iter != NULL; iter = iter->next) {
+ if ((item = dlist_new_item(iter->obj)) == NULL) {
+ error = ENOMEM;
+ } else {
+ disks_copy = dlist_append(item, disks_copy, AT_HEAD);
+ }
+ }
+
+ /* compose nsubs stripe submirrors of ncomp components */
+ while ((count < nsubs) && (error == 0)) {
+
+ devconfig_t *stripe = NULL;
+ dlist_t *item = NULL;
+
+ error = populate_stripe(
+ request, nbytes, disks_copy, ncomp, cursubs, &stripe);
+
+ if ((error == 0) && (stripe != NULL)) {
+ if ((item = dlist_new_item((void *)stripe)) == NULL) {
+ error = ENOMEM;
+ } else {
+ ++count;
+ list = dlist_append(item, list, AT_TAIL);
+ error = remove_used_disks(&disks_copy, stripe);
+ }
+ } else if (stripe == NULL) {
+ break;
+ }
+ }
+
+ /* free copy of disk list */
+ dlist_free_items(disks_copy, NULL);
+ disks_copy = NULL;
+
+ if ((error == 0) && (count < nsubs)) {
+ /* failed to compose enough stripes at this width, */
+ /* prepare to try again with the next narrower width. */
+ print_layout_submirrors_failed_msg(
+ devconfig_type_to_str(TYPE_STRIPE),
+ count, nsubs);
+
+ dlist_free_items(list, free_devconfig_object);
+ list = NULL;
+ count = 0;
+ --ncomp;
+ }
+ }
+
+ if (count < nsubs) {
+ dlist_free_items(list, free_devconfig_object);
+ list = NULL;
+ } else {
+ *results = dlist_append(list, *results, AT_TAIL);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: compose_stripes_within_hba(devconfig_t *request,
+ * dlist_t *cursubs, dlist_t *hbas, uint64_t nbytes,
+ * uint16_t nsubs, int maxcomp, int mincomp,
+ * dlist_t **results)
+ *
+ * INPUT: request - pointer to a devconfig_t of the current request
+ * cursubs - pointer to a list of already composed submirrors
+ * hbas - pointer to a list of available HBAs
+ * nbytes - the desired capacity for the stripes
+ * nsubs - the desired number of stripes
+ * maxcomp - the maximum number of stripe components
+ * mincomp - the minimum number of stripe components
+ * nsubs - the number of stripes to be composed
+ *
+ * OUPUT: results - pointer to a list of composed volumes
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Layout function which composes the requested number of stripes
+ * of the desired size using available disks within any single
+ * HBA from the input list.
+ *
+ * The number of components within the composed stripes will be
+ * in the range of mincomp to maxcomp, preferring more components
+ * over fewer. All stripes composed by a single call to this
+ * function will have the same number of components.
+ *
+ * All stripes will use disks from a single HBA.
+ *
+ * All input HBAs are expected to have at least nsubs * mincomp
+ * available disks and total space sufficient for subs stripes.
+ *
+ * If the stripes can be composed, they are appended to the list
+ * of result volumes.
+ *
+ * while (more HBAs and more stripes need to be composed) {
+ * select next HBA
+ * if (not enough available space on this HBA) {
+ * continue;
+ * }
+ * get available disks for HBA
+ * use # disks as # of stripe components - limit to maxcomp
+ * for (ncomps downto mincomp) {
+ * if ((ncomps * nsubs) > ndisks) {
+ * continue;
+ * }
+ * while (more stripes need to be composed) {
+ * if a stripe can be composed using disks {
+ * save stripe
+ * remove used disks from disk list
+ * } else
+ * end while loop
+ * }
+ * if (not all stripes composed) {
+ * delete any stripes composed
+ * }
+ * }
+ * }
+ *
+ * if (not all stripes composed) {
+ * delete any stripes composed
+ * }
+ *
+ * append composed stripes to results
+ */
+static int
+compose_stripes_within_hba(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ dlist_t *hbas,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ uint16_t maxcomp,
+ uint16_t mincomp,
+ dlist_t **results)
+{
+ int error = 0;
+ int count = 0;
+
+ dlist_t *list = NULL;
+ dlist_t *iter = NULL;
+
+ for (iter = hbas;
+ (count < nsubs) && (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ dm_descriptor_t hba = (uintptr_t)iter->obj;
+ uint64_t space = 0;
+ dlist_t *disks = NULL;
+ int ndisks = 0;
+ int ncomp = 0;
+ char *name = NULL;
+
+ ((error = get_display_name(hba, &name)) != 0) ||
+ (error = hba_get_avail_disks_and_space(request,
+ hba, &disks, &space));
+ if (error != 0) {
+ dlist_free_items(disks, NULL);
+ continue;
+ }
+
+ if (space < (nsubs * nbytes)) {
+ (void) print_hba_insufficient_space_msg(name, space);
+ dlist_free_items(disks, NULL);
+ continue;
+ }
+
+ ndisks = dlist_length(disks);
+
+ /*
+ * try composing stripes from ncomp down to mincomp.
+ * stop when nsubs stripes have been composed, or when the
+ * minimum stripe width has been tried
+ */
+ for (ncomp = maxcomp;
+ (ncomp >= mincomp) && (count != nsubs) && (error == 0);
+ ncomp--) {
+
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" --->Trying to compose %d Stripes with "
+ "%d components on a single HBA.\n"),
+ nsubs, ncomp);
+
+ if (ndisks < (ncomp * nsubs)) {
+ print_insufficient_disks_msg(ndisks);
+ continue;
+ }
+
+ /* try composing nsubs stripes, each ncomp wide */
+ for (count = 0; (count < nsubs) && (error == 0); count++) {
+
+ devconfig_t *stripe = NULL;
+
+ error = populate_stripe(
+ request, nbytes, disks, ncomp, cursubs, &stripe);
+
+ if ((error == 0) && (stripe != NULL)) {
+
+ dlist_t *item = dlist_new_item((void *)stripe);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ list = dlist_append(item, list, AT_TAIL);
+ error = remove_used_disks(&disks, stripe);
+ }
+ } else if (stripe == NULL) {
+ break;
+ }
+ }
+
+ if (count < nsubs) {
+ /* failed to compose enough stripes at this width, */
+ /* prepare to try again with fewer components */
+ print_layout_submirrors_failed_msg(
+ devconfig_type_to_str(TYPE_STRIPE),
+ count, nsubs);
+
+ dlist_free_items(list, free_devconfig_object);
+ list = NULL;
+ }
+ }
+
+ dlist_free_items(disks, NULL);
+ }
+
+ if (count < nsubs) {
+ dlist_free_items(list, free_devconfig_object);
+ list = NULL;
+ }
+
+ *results = list;
+
+ return (error);
+}
+
+/*
+ * FUNCTION: compose_concats_per_hba(devconfig_t *request,
+ * dlist_t *cursubs, dlist_t *hbas, uint64_t nbytes,
+ * uint16_t nsubs, dlist_t **results)
+ *
+ * INPUT: request - pointer to a devconfig_t of the current request
+ * cursubs - pointer to a list of already composed submirrors
+ * hbas - pointer to a list of available HBAs
+ * nbytes - the desired capacity for the concats
+ * nsubs - the number of concats to be composed
+ *
+ * OUPUT: results - pointer to a list of composed volumes
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Layout function which composes the requested number of concats
+ * of the desired size using available disks within HBAs from the
+ * input list. Each concat will be composed using disks from a
+ * single HBA.
+ *
+ * If the concats can be composed, they are appended to the list
+ * of result volumes.
+ *
+ * while (more HBAs AND more concats need to be composed) {
+ * if (not enough available space on this HBA) {
+ * continue;
+ * }
+ *
+ * get available disks for HBA
+ * if (concat can be composed) {
+ * save concat
+ * increment count
+ * }
+ * }
+ *
+ * if (not all stripes composed) {
+ * delete any concats composed
+ * }
+ *
+ * append composed concats to results
+ */
+static int
+compose_concat_per_hba(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ dlist_t *hbas,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ dlist_t **results)
+{
+ int error = 0;
+ int count = 0;
+
+ dlist_t *list = NULL;
+ dlist_t *iter = NULL;
+
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" --->Trying to compose %d Concats on "
+ "separate HBAs.\n"), nsubs);
+
+ for (iter = hbas;
+ (iter != NULL) && (error == 0) && (count < nsubs);
+ iter = iter->next) {
+
+ dm_descriptor_t hba = (uintptr_t)iter->obj;
+ uint64_t space = 0;
+ devconfig_t *concat = NULL;
+ dlist_t *disks = NULL;
+
+ error = hba_get_avail_disks_and_space(request, hba, &disks, &space);
+ if ((error == 0) && (space >= nbytes)) {
+ error = populate_concat(
+ request, nbytes, disks, cursubs, &concat);
+ }
+
+ if ((error == 0) && (concat != NULL)) {
+ dlist_t *item = dlist_new_item((void *)concat);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ ++count;
+ list = dlist_append(item, list, AT_TAIL);
+ }
+ }
+
+ dlist_free_items(disks, NULL);
+ }
+
+ if (count != nsubs) {
+ print_layout_submirrors_failed_msg(
+ devconfig_type_to_str(TYPE_CONCAT),
+ count, nsubs);
+
+ dlist_free_items(list, free_devconfig_object);
+ list = NULL;
+ } else {
+ *results = dlist_append(list, *results, AT_TAIL);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: compose_concats_across_hbas(devconfig_t *request,
+ * dlist_t *cursubs, dlist_t *hbas, dlist_t *disks,
+ * uint64_t nbytes, uint16_t nsubs, dlist_t **results)
+ *
+ * INPUT: request - pointer to a devconfig_t of the current request
+ * cursubs - pointer to a list of already composed submirrors
+ * hbas - pointer to a list of available HBAs
+ * disks - pointer to a list of available disks on the HBAs
+ * nbytes - the desired capacity for the concats
+ * nsubs - the number of concats to be composed
+ *
+ * OUPUT: results - pointer to a list of composed volumes
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Layout function which composes the requested number of concats
+ * of the desired size using any available disks from the input
+ * list of available HBAs.
+ *
+ * If the concats can be composed, they are appended to the list
+ * of result volumes.
+ *
+ * copy the input disk list
+ * while (more concats need to be composed) {
+ * if (a concat can be composed using remaining disks) {
+ * save concat
+ * remove used disks from disk list
+ * increment count
+ * } else {
+ * end while loop
+ * }
+ * }
+ *
+ * if (not all concats composed) {
+ * delete any concats composed
+ * }
+ *
+ * append composed concats to results
+ */
+static int
+compose_concats_across_hbas(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ dlist_t *hbas,
+ dlist_t *disks,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ dlist_t **results)
+{
+ int error = 0;
+ int count = 0;
+
+ dlist_t *list = NULL;
+ dlist_t *item = NULL;
+ dlist_t *iter = NULL;
+ dlist_t *disks_copy = NULL;
+
+ /* copy disk list, it is modified by the while loop */
+ for (iter = disks; iter != NULL; iter = iter->next) {
+ if ((item = dlist_new_item(iter->obj)) == NULL) {
+ error = ENOMEM;
+ } else {
+ disks_copy = dlist_append(item, disks_copy, AT_HEAD);
+ }
+ }
+
+ while ((count < nsubs) && (error == 0)) {
+
+ devconfig_t *concat = NULL;
+
+ error = populate_concat(
+ request, nbytes, disks_copy, cursubs, &concat);
+
+ if ((error == 0) && (concat != NULL)) {
+
+ item = dlist_new_item((void *)concat);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ count++;
+ list = dlist_append(item, list, AT_TAIL);
+ error = remove_used_disks(&disks_copy, concat);
+ }
+ } else if (concat == NULL) {
+ break;
+ }
+ }
+
+ /* free copy of disk list */
+ dlist_free_items(disks_copy, NULL);
+ disks_copy = NULL;
+
+ if (count != nsubs) {
+ print_layout_submirrors_failed_msg(
+ devconfig_type_to_str(TYPE_CONCAT),
+ count, nsubs);
+
+ dlist_free_items(list, free_devconfig_object);
+ list = NULL;
+ } else {
+ *results = dlist_append(list, *results, AT_TAIL);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: compose_concats_within_hba(devconfig_t *request,
+ * dlist_t *cursubs, dlist_t *hbas, uint64_t nbytes,
+ * uint16_t nsubs, dlist_t **results)
+ *
+ * INPUT: request - pointer to a devconfig_t of the current request
+ * cursubs - pointer to a list of already composed submirrors
+ * hbas - pointer to a list of available HBAs
+ * nbytes - the desired capacity for the concats
+ * nsubs - the number of concats to be composed
+ *
+ * OUPUT: results - pointer to a list of composed volumes
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Layout function which composes the requested number of concats
+ * of the desired size using available disks within any single
+ * HBA from the input list.
+ *
+ *
+ * HBAs in the list are expected to have at least 2 available
+ * disks and total space sufficient for the submirrors.
+ *
+ * If the concats can be composed, they are appended to the list
+ * of result volumes.
+ *
+ * while (more HBAs) {
+ * if (not enough available space on this HBA) {
+ * continue;
+ * }
+ *
+ * get available disks for HBA
+ * while (more concats need to be composed) {
+ * if a concat can be composed using disks {
+ * save concat
+ * remove used disks from disk list
+ * increment count
+ * } else {
+ * delete any concats composed
+ * end while loop
+ * }
+ * }
+ * }
+ *
+ * if (not all concats composed) {
+ * delete any concats composed
+ * }
+ *
+ * append composed concats to results
+ */
+static int
+compose_concats_within_hba(
+ devconfig_t *request,
+ dlist_t *cursubs,
+ dlist_t *hbas,
+ uint64_t nbytes,
+ uint16_t nsubs,
+ dlist_t **results)
+{
+ int error = 0;
+
+ dlist_t *iter = NULL;
+ dlist_t *list = NULL;
+ int count = 0;
+
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" --->Trying to compose %d Concats within "
+ "a single HBA.\n"), nsubs);
+
+ for (iter = hbas;
+ (count < nsubs) && (error == 0) && (iter != NULL);
+ iter = iter->next) {
+
+ dm_descriptor_t hba = (uintptr_t)iter->obj;
+ dlist_t *disks = NULL;
+ uint64_t space = 0;
+
+ error = hba_get_avail_disks_and_space(request, hba, &disks, &space);
+ if ((error == 0) && (space >= (nsubs * nbytes))) {
+
+ /* try composing nsubs concats all on this HBA */
+ count = 0;
+ while ((count < nsubs) && (error == 0)) {
+ devconfig_t *concat = NULL;
+ dlist_t *item = NULL;
+
+ error = populate_concat(
+ request, nbytes, disks, cursubs, &concat);
+
+ if ((error == 0) && (concat != NULL)) {
+ item = dlist_new_item((void*)concat);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ count++;
+ list = dlist_append(item, list, AT_TAIL);
+ error = remove_used_disks(&disks, concat);
+ }
+ } else if (concat == NULL) {
+ dlist_free_items(list, free_devconfig_object);
+ list = NULL;
+ break;
+ }
+ }
+ }
+
+ dlist_free_items(disks, NULL);
+ }
+
+ if (count < nsubs) {
+ print_layout_submirrors_failed_msg(
+ devconfig_type_to_str(TYPE_CONCAT),
+ count, nsubs);
+
+ dlist_free_items(list, free_devconfig_object);
+ list = NULL;
+ } else {
+ *results = dlist_append(list, *results, AT_TAIL);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: remove_used_disks(dlist_t **disks, devconfig_t *volume)
+ *
+ * INPUT: disks - pointer to a list of disks
+ * volume - pointer to a devconfig_t volume
+ *
+ * OUPUT: disks - pointer to new list of disks
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Helper which updates the input list of disks by removing
+ * those which have slices used by the input volume.
+ *
+ * Constructs a new list containing only disks not used by
+ * the volume.
+ *
+ * The original list is freed.
+ */
+static int
+remove_used_disks(
+ dlist_t **disks,
+ devconfig_t *volume)
+{
+ dlist_t *list = NULL;
+ dlist_t *iter = NULL;
+ dlist_t *item = NULL;
+ int error = 0;
+
+ for (iter = *disks; (iter != NULL) && (error == 0); iter = iter->next) {
+
+ dm_descriptor_t diskp = (uintptr_t)iter->obj;
+ boolean_t shares = B_FALSE;
+
+ error = volume_shares_disk(diskp, volume, &shares);
+ if ((error == 0) && (shares != B_TRUE)) {
+ /* disk is unused */
+ if ((item = dlist_new_item((void*)diskp)) == NULL) {
+ error = ENOMEM;
+ } else {
+ list = dlist_append(item, list, AT_TAIL);
+ }
+ }
+ }
+
+ if (error != 0) {
+ dlist_free_items(list, NULL);
+ } else {
+
+ /* free original disk list, return new list */
+ dlist_free_items(*disks, NULL);
+
+ *disks = list;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: volume_shares_disk(dm_descriptor_t disk,
+ * devconfig_t *volume, boolean_t *shares)
+ *
+ * INPUT: disk - a dm_descriptor_t handle for the disk of interest
+ * volume - a devconfig_t pointer to a volume
+ * bool - a boolean_t pointer to hold the result
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Determines if the input disk has a slice that is used
+ * as a component by the input volume.
+ *
+ * If the disk contributes a slice component, bool is set
+ * to B_TRUE, B_FALSE otherwise.
+ */
+static int
+volume_shares_disk(
+ dm_descriptor_t disk,
+ devconfig_t *volume,
+ boolean_t *shares)
+{
+ dlist_t *iter = NULL;
+ int error = 0;
+
+ *shares = B_FALSE;
+
+ /* look at all slices in the volume */
+ for (iter = devconfig_get_components(volume);
+ (iter != NULL) && (*shares == B_FALSE) && (error == 0);
+ iter = iter->next) {
+
+ devconfig_t *dev = (devconfig_t *)iter->obj;
+
+ if (devconfig_isA(dev, TYPE_SLICE)) {
+
+ /* get disk for volume's slice */
+ dm_descriptor_t odisk = NULL;
+ char *oname = NULL;
+
+ ((error = devconfig_get_name(dev, &oname)) != 0) ||
+ (error = get_disk_for_named_slice(oname, &odisk));
+
+ if (error == 0) {
+ if (compare_descriptor_names(
+ (void*)disk, (void*)odisk) == 0) {
+ /* otherslice is on same disk, stop */
+ *shares = B_TRUE;
+ }
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: select_mpxio_hbas(dlist_t *hbas, dlist_t **mpxio_hbas)
+ *
+ * INPUT: hbas - pointer to a list of dm_descriptor_t HBA handles
+ *
+ * OUTPUT: mpxio_hbas - pointer to a new list of containing HBAs that
+ * are multiplex enabled.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Iterates the input list of HBAs and builds a new list
+ * containing those that are multiplex enabled.
+ *
+ * The output list should be passed to dlist_free_items()
+ * when no longer needed.
+ */
+static int
+select_mpxio_hbas(
+ dlist_t *hbas,
+ dlist_t **mpxio_hbas)
+{
+ dlist_t *iter;
+ int error = 0;
+
+ for (iter = hbas; (iter != NULL) && (error == 0); iter = iter->next) {
+ dm_descriptor_t hba = (uintptr_t)iter->obj;
+ boolean_t ismpxio = B_FALSE;
+ if ((error = hba_is_multiplex(hba, &ismpxio)) == 0) {
+ if (ismpxio == B_TRUE) {
+ dlist_t *item = dlist_new_item((void *)hba);
+ if (item != NULL) {
+ *mpxio_hbas =
+ dlist_append(item, *mpxio_hbas, AT_TAIL);
+ } else {
+ error = ENOMEM;
+ }
+ }
+ }
+ }
+
+ if (error != 0) {
+ dlist_free_items(*mpxio_hbas, NULL);
+ *mpxio_hbas = NULL;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: set_explicit_submirror_names(dlist_t *reqs, dlist_t *subs)
+ *
+ * INPUT: reqs - pointer to a list of request devconfig_ts
+ * subs - pointer to a list of volume devconfig_ts
+ *
+ * SIDEEFFECT: Modifies the volume names.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Iterates the lists of volumes and requests and calls
+ * set_explicit_mirror_name for each pair.
+ */
+static int
+set_explicit_submirror_names(
+ dlist_t *reqs,
+ dlist_t *subs)
+{
+ int error = 0;
+
+ while ((reqs != NULL) && (subs != NULL) && (error == 0)) {
+
+ error = set_explicit_submirror_name(
+ (devconfig_t *)reqs->obj,
+ (devconfig_t *)subs->obj);
+
+ reqs = reqs->next;
+ subs = subs->next;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: set_explicit_submirror_name(dlist_t *req, dlist_t *sub)
+ *
+ * INPUT: req - pointer to a request devconfig_t
+ * sub - pointer to a volume devconfig_t
+ *
+ * SIDEEFFECT: Modifies the volume name.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Clears the volume's current name and returns the name
+ * to the available pool.
+ *
+ * If a name is specified in the request, the name is used
+ * as the volume's name.
+ *
+ * (Unnamed submirrors will have default names assigned
+ * during final mirror assembly.)
+ */
+static int
+set_explicit_submirror_name(
+ devconfig_t *req,
+ devconfig_t *sub)
+{
+ char *name = NULL;
+ int error = 0;
+
+ /* unset current submirror name */
+ (void) devconfig_get_name(sub, &name);
+ release_volume_name(name);
+ (void) devconfig_set_name(sub, "");
+
+ if (devconfig_get_name(req, &name) != ERR_ATTR_UNSET) {
+ (void) devconfig_set_name(sub, name);
+ }
+
+ return (error);
+}
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_mirror.h b/usr/src/cmd/lvm/metassist/layout/layout_mirror.h
new file mode 100644
index 0000000000..36c5b5a8bf
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_mirror.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef _LAYOUT_MIRROR_H
+#define _LAYOUT_MIRROR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "volume_devconfig.h"
+#include "volume_dlist.h"
+
+extern int layout_mirror(
+ devconfig_t *request,
+ uint16_t nsubs,
+ uint64_t nbytes,
+ dlist_t **results);
+
+extern int populate_explicit_mirror(
+ devconfig_t *req,
+ dlist_t **results);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LAYOUT_MIRROR_H */
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_request.c b/usr/src/cmd/lvm/metassist/layout/layout_request.c
new file mode 100644
index 0000000000..bebd3c8be9
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_request.c
@@ -0,0 +1,3418 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <assert.h>
+#include <string.h>
+#include <libintl.h>
+
+#include "volume_error.h"
+#include "volume_defaults.h"
+#include "volume_dlist.h"
+#include "volume_output.h"
+#include "volume_request.h"
+
+#include "layout_device_cache.h"
+#include "layout_discovery.h"
+#include "layout_dlist_util.h"
+#include "layout_request.h"
+#include "layout_slice.h"
+#include "layout_validate.h"
+
+#define _LAYOUT_REQUEST_C
+
+static char *_request_diskset = NULL;
+static devconfig_t *_toplevel_request = NULL;
+static defaults_t *_defaults = NULL;
+
+/*
+ * This file contains code which handles various aspects of the
+ * request and defaults devconfig_t structs passed to the layout
+ * module.
+ *
+ * Functions are provided which determine what devices are available
+ * for use by the various volume layout mechanisms. These are based
+ * on the user specified available/unavailable devices included in
+ * a request or in the defaults associated with the destination diskset.
+ */
+
+/*
+ * A struct to hold device "specifications" extracted from a user
+ * specified device name. This struct is used to compare the user's
+ * available and unavailable device specifications against physical
+ * devices attached to the system.
+ *
+ * The spec struct holds one of two different specifications: if the
+ * user supplied device name is parsable as a CTD name, it is parsed
+ * into the component ids. Otherwise, it is stored as is.
+ *
+ * The CTD name space implies a device hierarchy and metassist
+ * supports an implied wildcarding scheme for the CTD name space.
+ * A CTD specification from the user is of the form cX, cXdX,
+ * cXdXsX, cXtX, cXtXdX, or cXtXdXsX, so it may or may nor
+ * correspond to an individual physical device depending on
+ * the context.
+ *
+ * For example, "c1" can mean the controller/HBA with the
+ * name "c1" or it can mean all devices attached to the
+ * controller named "c1".
+ *
+ * The ctd specs make matching physical devices against a
+ * user specification easier since the matching is based on
+ * the numeric values extracted from the cXtXdXsX string
+ * and not on the strings themselves. The strings are
+ * troublesome because of situations like "c1" being
+ * compared to "c11t1d0s0" and getting false matches.
+ *
+ * The ID_UNSPECIFIED value is used to flag components
+ * that were not in the CTD name:
+ *
+ * "c3" -> { ctrl=3, target=ID_UNSPECIFIED,
+ * lun=ID_UNSPECIFIED, slice=ID_UNSPECIFIED }
+ *
+ * "c3t2" -> { ctrl=3, target=2,
+ * lun=ID_UNSPECIFIED, slice=ID_UNSPECIFIED }
+ */
+
+#define ID_UNSPECIFIED -1
+typedef struct {
+ int ctrl;
+ int target;
+ int lun;
+ int slice;
+ boolean_t is_ide;
+} ctd_spec_t;
+
+typedef enum {
+ SPEC_TYPE_CTD = 0,
+ SPEC_TYPE_RAW,
+ SPEC_TYPE_OTHER
+} spec_type_t;
+
+typedef struct {
+ spec_type_t type;
+ union {
+ ctd_spec_t *ctd;
+ char *raw;
+ } data;
+} device_spec_t;
+
+static int get_spec_for_name(
+ char *name,
+ device_spec_t **id);
+
+static int create_device_spec(
+ char *name,
+ device_spec_t **spec);
+
+static int create_device_ctd_spec(
+ char *name,
+ device_spec_t **spec);
+
+static int create_device_raw_spec(
+ char *name,
+ device_spec_t **spec);
+
+static void destroy_device_spec(
+ device_spec_t *spec);
+
+static boolean_t ctd_spec_includes_device(
+ device_spec_t *spec,
+ device_spec_t *device);
+
+static boolean_t raw_spec_includes_device(
+ device_spec_t *spec,
+ device_spec_t *device);
+
+/*
+ * get_spec_for_name builds up a cached mapping of device
+ * names to the corresponding device_spec_t structs.
+ *
+ * This saves repeatedly converting the device names, which
+ * could get expensive since devices are checked against the
+ * user specified available/unavailable devices a lot.
+ *
+ * The cache is implemented as a list of these structs:
+ */
+typedef struct {
+
+ char *name;
+ device_spec_t *device_spec;
+
+} spec_cache_t;
+
+static dlist_t *_spec_cache = NULL;
+
+static int destroy_spec_cache();
+static int compare_name_to_spec_cache_name(
+ void *name, void *list_item);
+
+/*
+ * The user specified available/unavailable devices are
+ * accessed frequently during layout. To make this more
+ * efficient, the char *arrays of available/unavailable
+ * specifications for a request or defaults devconfig_t
+ * object are converted to device_spec_ts the first time
+ * they're accessed and then cached using this struct:
+ */
+typedef struct {
+
+ devconfig_t *request;
+
+ /*
+ * avail_specs_list is a list of device spec_t
+ * corresponding to available devices specified
+ * in the request object
+ */
+ dlist_t *avail_specs_list;
+
+ /*
+ * unavail_specs_list is a list of device spec_t
+ * corresponding to unavailable devices specified
+ * in the request object
+ */
+ dlist_t *unavail_specs_list;
+
+} request_spec_list_t;
+
+dlist_t *_request_spec_list_cache = NULL;
+
+static int destroy_request_spec_list_cache();
+static void destroy_request_spec_list_entry(void *obj);
+
+static int compare_request_to_request_spec_list_request(
+ void *object,
+ void *list_item);
+
+static int convert_usernames_to_specs(
+ char **specs,
+ dlist_t **list);
+
+/* other private functions */
+static int is_device_avail(
+ dm_descriptor_t desc,
+ devconfig_t *request,
+ boolean_t *avail);
+
+static int is_named_device_avail(
+ devconfig_t *request,
+ char *device_name,
+ boolean_t check_aliases,
+ boolean_t *avail);
+
+static int avail_list_includes_device_name(
+ dlist_t *list,
+ char *device_name,
+ boolean_t check_aliases,
+ boolean_t *includes);
+
+static int unavail_list_includes_device_name(
+ dlist_t *list,
+ char *device_name,
+ boolean_t check_aliases,
+ boolean_t *includes);
+
+static int spec_includes_device_name(
+ device_spec_t *spec,
+ char *device_name,
+ boolean_t check_aliases,
+ boolean_t *includes);
+
+static boolean_t spec_includes_device(
+ device_spec_t *spec,
+ device_spec_t *device);
+
+static int disk_get_avail_space(
+ devconfig_t *request,
+ dm_descriptor_t disk,
+ uint64_t *avail);
+
+static int compare_hba_n_avail_disks(
+ void *obj1,
+ void *obj2);
+
+/*
+ * FUNCTION: release_request_caches()
+ *
+ * RETURNS: 0
+ *
+ * PURPOSE: cleanup the module private caches.
+ */
+int
+release_request_caches()
+{
+ (void) destroy_request_spec_list_cache();
+ (void) destroy_spec_cache();
+
+ return (0);
+}
+/*
+ * FUNCTION: int set_request_diskset(char *)
+ *
+ * INPUT: char * - pointer to the diskset name
+ * OUTPUT: 0 - success
+ * !0 - validation failure
+ * RETURNS:
+ *
+ * PURPOSE: set the module global diskset name.
+ */
+int
+set_request_diskset(
+ char *dsname)
+{
+ _request_diskset = dsname;
+
+ if (dsname == NULL || dsname[0] == '\0') {
+ volume_set_error(
+ gettext("No disk set specified in request\n"));
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * FUNCTION: char *get_request_diskset()
+ *
+ * INPUT: none -
+ * OUTPUT: none -
+ * RETURNS: char * - pointer to the currently set diskset name
+ *
+ * PURPOSE: get the global name of the current diskset.
+ */
+char *
+get_request_diskset()
+{
+ return (_request_diskset);
+}
+
+/*
+ * FUNCTION: void unset_request_diskset()
+ *
+ * PURPOSE: unset the module global diskset name.
+ */
+void
+unset_request_diskset(
+ char *dsname)
+{
+ _request_diskset = NULL;
+}
+
+/*
+ * FUNCTION: int set_toplevel_request(devconfig_t *)
+ *
+ * INPUT: devconfig_t * - pointer to the diskset request
+ * OUTPUT: 0 - success
+ * !0 - validation failure
+ * RETURNS:
+ *
+ * PURPOSE: set the module global toplevel request struct.
+ * this will be set within the only public entry
+ * point to the module -- get_layout()
+ *
+ * SIDEEFFECT: The devconfig_t's list of available and unavailable
+ * devices will be validated.
+ */
+int
+set_toplevel_request(
+ devconfig_t *req)
+{
+ _toplevel_request = req;
+
+ return (validate_request_avail_unavail(req));
+}
+
+/*
+ * FUNCTION: void unset_toplevel_request()
+ *
+ * PURPOSE: unset the layout module global toplevel request struct.
+ *
+ */
+void
+unset_toplevel_request()
+{
+ _toplevel_request = NULL;
+}
+
+/*
+ * FUNCTION: int set_defaults(devconfig_t *)
+ *
+ * INPUT: devconfig_t * - pointer to the global defaults devconfig_t
+ * OUTPUT: 0 - success
+ * !0 - validation failure
+ * RETURNS:
+ *
+ * PURPOSE: set the module global defaults struct.
+ * this will be set within the only public entry
+ * point to the module -- get_layout()
+ *
+ * SIDEEFFECT: The devconfig_t's list of available and unavailable
+ * devices will be validated.
+ */
+int
+set_request_defaults(
+ defaults_t *defaults)
+{
+ int error = 0;
+ devconfig_t *diskset = NULL;
+
+ _defaults = defaults;
+
+ if ((error = defaults_get_diskset_by_name(
+ _defaults, get_request_diskset(), &diskset)) == 0) {
+
+ error = validate_request_avail_unavail(diskset);
+
+ } else if (error == ENOENT) {
+ /* no defaults to verify */
+ error = 0;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: void unset_request_defaults()
+ *
+ * PURPOSE: unset the layout module global defaults struct.
+ *
+ */
+void
+unset_request_defaults()
+{
+ _defaults = NULL;
+}
+
+/*
+ * FUNCTION: get_stripe_min_comp(devconfig_t *req, uint16_t *val)
+ * INPUT: req - a devconfig_t pointer to the current request
+ * val - pointer to a uint64_t to hold the result
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: helper which determines the minimum of components
+ * for striped volumes satisfying the input request.
+ *
+ * The value to use is taken from the input request, the
+ * toplevel diskset request, the diskset defaults or the
+ * global defaults.
+ */
+int
+get_stripe_min_comp(
+ devconfig_t *req,
+ uint16_t *val)
+{
+ int error = 0;
+
+ *val = 0;
+
+ if ((error = devconfig_get_stripe_mincomp(req, val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+
+ if (*val == 0) {
+ if ((error = defaults_get_stripe_mincomp(
+ _defaults, get_request_diskset(), val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_stripe_max_comp(devconfig_t *req, uint16_t *val)
+ * INPUT: req - a devconfig_t pointer to the current request
+ * val - pointer to a uint64_t to hold the result
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: helper which determines the maximum number of components
+ * for striped volumes satisfying the input request.
+ *
+ * The value to use is taken from the input request, the
+ * toplevel diskset request, the diskset defaults or the
+ * global defaults.
+ */
+int
+get_stripe_max_comp(
+ devconfig_t *req,
+ uint16_t *val)
+{
+ int error = 0;
+
+ *val = 0;
+
+ if ((error = devconfig_get_stripe_maxcomp(req, val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+
+ if (*val == 0) {
+ if ((error = defaults_get_stripe_maxcomp(
+ _defaults, get_request_diskset(), val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_stripe_interlace(devconfig_t *req, uint64_t *val)
+ * INPUT: req - a devconfig_t pointer to the current request
+ * val - pointer to a uint64_t to hold the result
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: helper which determines the interlace value for striped
+ * volumes satisfying the input request.
+ *
+ * The value to use is taken from the input request, the
+ * toplevel diskset request, the diskset defaults or the
+ * global defaults.
+ *
+ * If no value is explictly specified, ERR_ATTR_UNSET is
+ * returned.
+ */
+int
+get_stripe_interlace(
+ devconfig_t *req,
+ uint64_t *val)
+{
+ int error = 0;
+
+ *val = 0;
+
+ if ((error = devconfig_get_stripe_interlace(req, val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ error = 0;
+ }
+
+ if (*val == 0) {
+ if ((error = defaults_get_stripe_interlace(
+ _defaults, get_request_diskset(), val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_mirror_read_strategy(devconfig_t *req,
+ * mirror_read_strategy_t *val)
+ * INPUT: req - a devconfig_t pointer to the current request
+ * val - pointer to a mirror_read_strategy_t to hold the result
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: helper which determines the write strategy mirrored volumes
+ * should have for volumes satisfying the input request.
+ *
+ * The value to use is taken from the input request, the
+ * toplevel diskset request, the diskset defaults or the
+ * global defaults.
+ *
+ * If no value is explictly specified, ERR_ATTR_UNSET is
+ * returned.
+ */
+int
+get_mirror_read_strategy(
+ devconfig_t *req,
+ mirror_read_strategy_t *val)
+{
+ int error = 0;
+
+ *val = 0;
+
+ if ((error = devconfig_get_mirror_read(req, val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+
+ if (*val == 0) {
+ if ((error = defaults_get_mirror_read(
+ _defaults, get_request_diskset(), val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_mirror_write_strategy(devconfig_t *req,
+ * mirror_write_strategy_t *val)
+ * INPUT: req - a devconfig_t pointer to the current request
+ * val - pointer to a mirror_write_strategy_t to hold result
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: helper which determines the write strategy mirrored volumes
+ * should have for volumes satisfying the input request.
+ *
+ * The value to use is taken from the input request, the
+ * toplevel diskset request, the diskset defaults or the
+ * global defaults.
+ *
+ * If no value is explictly specified, ERR_ATTR_UNSET is
+ * returned.
+ */
+int
+get_mirror_write_strategy(
+ devconfig_t *req,
+ mirror_write_strategy_t *val)
+{
+ int error = 0;
+
+ *val = 0;
+
+ if ((error = devconfig_get_mirror_write(req, val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+
+ if (*val == 0) {
+ if ((error = defaults_get_mirror_write(
+ _defaults, get_request_diskset(), val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_mirror_pass(devconfig_t *req, uint16_t *val)
+ * INPUT: req - a devconfig_t pointer to the current request
+ * val - pointer to a uint16_t to hold the result
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: helper which determines the resync pass mirrored volumes
+ * should have for volumes satisfying the input request.
+ *
+ * The value to use is taken from the input request, the
+ * toplevel diskset request, the diskset defaults or the
+ * global defaults.
+ *
+ * If no value is explictly specified, ERR_ATTR_UNSET is
+ * returned.
+ */
+int
+get_mirror_pass(
+ devconfig_t *req,
+ uint16_t *val)
+{
+ int error = 0;
+
+ *val = 0;
+
+ if ((error = devconfig_get_mirror_pass(req, val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+
+ if (*val == 0) {
+ if ((error = defaults_get_mirror_pass(
+ _defaults, get_request_diskset(), val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_mirror_nsubs(devconfig_t *req, uint16_t *val)
+ * INPUT: req - a devconfig_t pointer to the current request
+ * val - pointer to a uint16_t to hold the result
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: helper which determines how many submirrors mirrored
+ * volumes should have for volumes satisfying the input
+ * request.
+ *
+ * The value to use is taken from the input request, the
+ * toplevel diskset request, the diskset defaults or the
+ * global defaults.
+ */
+int
+get_mirror_nsubs(
+ devconfig_t *req,
+ uint16_t *val)
+{
+ int error = 0;
+
+ *val = 0;
+
+ if ((error = devconfig_get_mirror_nsubs(req, val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+
+ if (*val == 0) {
+ if ((error = defaults_get_mirror_nsubs(
+ _defaults, get_request_diskset(), val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_volume_faultrecov(devconfig_t *req, boolean_t *val)
+ * INPUT: req - a devconfig_t pointer to the current request
+ * val - pointer to a boolean_t to hold the result
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: helper which determines whether data redundant volumes
+ * should also have fault recovery (e.g., HSPs) for volumes
+ * satisfying the input request.
+ *
+ * The value to use is taken from the input request, the
+ * toplevel diskset request, the diskset defaults or the
+ * global defaults.
+ */
+int
+get_volume_faultrecov(
+ devconfig_t *req,
+ boolean_t *val)
+{
+ int error = 0;
+
+ *val = B_FALSE;
+
+ if ((error = devconfig_get_volume_usehsp(req, val)) != 0) {
+ if (error == ERR_ATTR_UNSET) {
+ component_type_t type = TYPE_UNKNOWN;
+ (void) devconfig_get_type(req, &type);
+
+ switch (type) {
+ case TYPE_MIRROR:
+ error = defaults_get_mirror_usehsp(
+ _defaults, get_request_diskset(), val);
+ break;
+
+ case TYPE_STRIPE:
+ error = defaults_get_stripe_usehsp(
+ _defaults, get_request_diskset(), val);
+ break;
+
+ case TYPE_CONCAT:
+ error = defaults_get_concat_usehsp(
+ _defaults, get_request_diskset(), val);
+ break;
+
+ case TYPE_VOLUME:
+ error = defaults_get_volume_usehsp(
+ _defaults, get_request_diskset(), val);
+ break;
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_volume_redundancy_level(devconfig_t *req, uint16_t val)
+ * INPUT: req - a devconfig_t pointer to the current request
+ * val - pointer to a uint16-t to hold the result
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: helper which determines the appropriate level of data
+ * redundancy a volume should have for volumes satisfying
+ * the input request.
+ *
+ * The value to use is taken from the input request, the
+ * toplevel diskset request, the diskset defaults or the
+ * global defaults.
+ */
+int
+get_volume_redundancy_level(
+ devconfig_t *req,
+ uint16_t *val)
+{
+ int error = 0;
+
+ *val = 0;
+
+ if ((error = devconfig_get_volume_redundancy_level(req, val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+
+ if (*val == 0) {
+ if ((error = defaults_get_volume_redundancy_level(
+ _defaults, get_request_diskset(), val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_volume_npaths(devconfig_t *req, uint16_t val)
+ * INPUT: req - a devconfig_t pointer to the current request
+ * val - pointer to a uint16-t to hold the result
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: helper which determines the appropriate level of datapath
+ * redundancy a slice component should have for volumes
+ * satisfying the input request.
+ *
+ * The value to use is taken from the input request, the
+ * toplevel diskset request, the diskset defaults or the
+ * global defaults.
+ */
+int
+get_volume_npaths(
+ devconfig_t *req,
+ uint16_t *val)
+{
+ int error = 0;
+
+ *val = 0;
+
+ if ((error = devconfig_get_volume_npaths(req, val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+
+ if (*val == 0) {
+ if ((error = defaults_get_volume_npaths(
+ _defaults, get_request_diskset(), val)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ return (error);
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_default_hsp_name(devconfig_t *req, char **hspname)
+ * INPUT: req - a devconfig_t pointer to the current request
+ * hspname - pointer to a char * to hold the result, if any
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: helper which determines the default HSP name for the
+ * input request.
+ *
+ * The value to use is taken from the input request, the
+ * toplevel diskset request, the diskset defaults or the
+ * global defaults.
+ */
+int
+get_default_hsp_name(
+ devconfig_t *req,
+ char **name)
+{
+ int error = 0;
+
+ *name = NULL;
+
+ if ((error = defaults_get_hsp_name(_defaults,
+ get_request_diskset(), name)) != 0) {
+ if (error != ENOENT) {
+ return (error);
+ }
+ error = 0;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: slice_is_available(char *sname, devconfig_t *request,
+ * boolean_t bool)
+ * INPUT: sname - a slice name
+ * request - pointer to a devconfig_t struct representing
+ * the current layout request being processed
+ * bool - pointer to a boolean to hold the result
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: Validation helper which determines if the named slice can
+ * be used as a volume component when satisfying the input
+ * request.
+ *
+ * Check if the slice appears in the known slice list,
+ * then check the request's available and unavailable
+ * device specifications.
+ */
+int
+slice_is_available(
+ char *sname,
+ devconfig_t *request,
+ boolean_t *bool)
+{
+ dm_descriptor_t slice = (dm_descriptor_t)0;
+ int error = 0;
+
+ *bool = B_FALSE;
+
+ if ((error = slice_get_by_name(sname, &slice)) != 0) {
+ return (error);
+ }
+
+ if (slice == (dm_descriptor_t)0) {
+ /* no slice found */
+ return (ENODEV);
+ }
+
+ if (error == 0) {
+ error = is_named_device_avail(request, sname, B_TRUE, bool);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_disks_for_target(char *name, dlist_t **disks)
+ *
+ * INPUT: name - a char* device CTD name
+ *
+ * OUTPUT: disks - disks matching the input target name
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Validation helper function which finds all disks "on" the
+ * input target.
+ *
+ * The input name is assumed to be a target name, cXtX, and
+ * the list of known disks is searched to find any disk that
+ * looks to be "on" that target.
+ *
+ * "On" is determined by comparing a disk's name and
+ * aliases to the target to see if they match.
+ */
+int
+get_disks_for_target(
+ char *name,
+ dlist_t **disks)
+{
+ int error = 0;
+ device_spec_t *targetid = NULL;
+
+ error = get_spec_for_name(name, &targetid);
+ if (error == 0) {
+ dlist_t *known_disks = NULL;
+ dlist_t *iter = NULL;
+
+ get_known_disks(&known_disks);
+ for (iter = known_disks;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ dm_descriptor_t disk = (uintptr_t)iter->obj;
+ device_spec_t *diskid = NULL;
+ char *diskname = NULL;
+ dlist_t *diskaliases = NULL;
+ dlist_t *item;
+
+ ((error = get_display_name(disk, &diskname)) != 0) ||
+ (error = get_aliases(disk, &diskaliases)) ||
+ (error = get_spec_for_name(diskname, &diskid));
+
+ if (error == 0) {
+ if (spec_includes_device(targetid, diskid) == B_TRUE) {
+ /* add disk */
+ if ((item = dlist_new_item((void *)disk)) == NULL) {
+ error = ENOMEM;
+ } else {
+ *disks = dlist_append(item, *disks, AT_HEAD);
+ }
+ } else {
+ /* check disk's aliases */
+ dlist_t *iter2;
+ for (iter2 = diskaliases;
+ (iter2 != NULL) && (error == 0);
+ iter2 = iter2->next) {
+
+ char *aliasname = NULL;
+ device_spec_t *aliasid = NULL;
+ error = get_display_name(disk, &aliasname);
+ error = get_spec_for_name(aliasname, &aliasid);
+
+ if (spec_includes_device(
+ targetid, aliasid) == B_TRUE) {
+
+ /* alias matched, add disk */
+ item = dlist_new_item((void *)disk);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ *disks =
+ dlist_append(item, *disks, AT_HEAD);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: select_hbas_with_n_disks(devconfig_t *request,
+ * dlist_t *hbas, int mindisks, dlist_t **selhbas,
+ * dlist_t **seldisks)
+ *
+ * INPUT: request - pointer to a devconfig_t struct representing
+ * the current layout request being processed
+ * hbas - pointer to a list of HBAs
+ * mindisks - minimum number of disks required on the HBAs
+ *
+ * OUTPUT: selhbas - pointer to a list containing the HBAs with at
+ * least mindisks available disks.
+ * seldisks - pointer to a list containing the available disks
+ * for the HBAs in selhbas
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: helper which counts the number of available disks associated
+ * with each of the input HBAs and adds those that have at
+ * least mindisks to the output list.
+ *
+ * Only available disks that have available space are counted.
+ *
+ * Disks connected thru multiple HBAs are only counted for
+ * the first HBA they're accessed through.
+ *
+ * The list of HBAs returned will be in descending order,
+ * i.e., HBAs with more disks come before those with fewer.
+ *
+ * The returned lists of HBAs and disks must be passed to
+ * dlist_free_items() to recover the space allocated to hold
+ * each list item.
+ *
+ * for (each HBA) {
+ *
+ * select HBA
+ * get available disks on HBA
+ *
+ * for (each disk) {
+ * if (disk is not in selected disk list)
+ * add it to the list
+ * else
+ * count it as a distinct disk on this HBA
+ * }
+ *
+ * if (this HBA has >= mindisks distinct disks)
+ * add this HBA to the list of returned HBAs
+ *
+ * }
+ */
+int
+select_hbas_with_n_disks(
+ devconfig_t *request,
+ dlist_t *hbas,
+ int mindisks,
+ dlist_t **selhbas,
+ dlist_t **seldisks)
+{
+ dlist_t *iter = NULL;
+ int error = 0;
+
+ *selhbas = NULL;
+ *seldisks = NULL;
+
+ /* for each input HBA */
+ for (iter = hbas; (error == 0) && (iter != NULL); iter = iter->next) {
+
+ dm_descriptor_t hba = (uintptr_t)iter->obj;
+ dlist_t *iter2 = NULL;
+ dlist_t *disks = NULL;
+ uint64_t space = 0;
+ uint16_t ndistinct = 0;
+
+ error = hba_get_avail_disks_and_space(request, hba, &disks, &space);
+
+ /* for each of this HBA's disks */
+ for (iter2 = disks;
+ (iter2 != NULL) && (error == 0);
+ iter2 = iter2->next) {
+
+ dm_descriptor_t disk = (uintptr_t)iter2->obj;
+
+ /* unique disk? has it been seen thru some other HBA? */
+ if (dlist_contains(*seldisks, (void *)disk,
+ compare_descriptor_names) != B_TRUE) {
+
+ /* distinct, add to list of all_distinct */
+ dlist_t *item = dlist_new_item((void *)disk);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+
+ *seldisks =
+ dlist_append(item, *seldisks, AT_HEAD);
+
+ /* increment this HBA's distinct disk count */
+ ++ndistinct;
+ }
+ }
+ }
+
+ if (ndistinct >= mindisks) {
+
+ /* this HBA has minimum # of disks, add to output list */
+ dlist_t *item = dlist_new_item((void *)hba);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ *selhbas =
+ dlist_insert_ordered(
+ item, *selhbas, DESCENDING,
+ compare_hba_n_avail_disks);
+
+ /* save # of disks for ordering the list */
+ hba_set_n_avail_disks(hba, ndistinct);
+ }
+ }
+
+ dlist_free_items(disks, NULL);
+ }
+
+ if (error != 0) {
+ oprintf(OUTPUT_TERSE,
+ gettext("failed selecting HBAs with n disks: %d\n"),
+ error);
+
+ dlist_free_items(*selhbas, NULL);
+ *selhbas = NULL;
+ dlist_free_items(*seldisks, NULL);
+ *seldisks = NULL;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: hba_get_avail_disks_and_space(devconfig_t *request,
+ * dm_descriptor_t hba, dlist_t **disks, uint64_t *space)
+ *
+ * INPUT: request - pointer to a devconfig_t struct representing
+ * the current layout request being processed
+ * hba - dm_descriptor_t handle for an HBA
+ *
+ * OUTPUT: disks - pointer to a list to hold the computed available
+ * disks
+ * avail - pointer to a uint64_t to hold the aggregate
+ * available space on the available disks
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: helper which examines the disks associated with the
+ * input HBA and assembles a list of those that are available.
+ *
+ * Available is defined as being in the usable list, having
+ * unused space and not specifically excluded by the request's
+ * list of unavailable devices.
+ *
+ * The returned list must be passed to dlist_free_items()
+ * to recover the memory allocated to hold each list item.
+ */
+int
+hba_get_avail_disks_and_space(
+ devconfig_t *request,
+ dm_descriptor_t hba,
+ dlist_t **disks,
+ uint64_t *space)
+{
+ dlist_t *usable_disks = NULL;
+ dlist_t *iter = NULL;
+ int error = 0;
+
+ *disks = NULL;
+
+ /* for each usable disk */
+ error = get_usable_disks(&usable_disks);
+ for (iter = usable_disks;
+ (error == 0) && (iter != NULL);
+ iter = iter->next) {
+
+ dm_descriptor_t disk = (uintptr_t)iter->obj;
+ boolean_t avail = B_FALSE;
+ dlist_t *hbas = NULL;
+
+ /* is disk attached to HBA in question? */
+ error = disk_get_hbas(disk, &hbas);
+ if (error != 0) {
+ continue;
+ }
+
+ if (dlist_contains(hbas, (void *)hba,
+ compare_descriptor_names) == B_TRUE) {
+
+ /* is disk available? */
+ error = is_device_avail(disk, request, &avail);
+ if ((error == 0) && (avail == B_TRUE)) {
+ uint64_t disk_space = 0;
+
+ /* does disk have available space? */
+ error = disk_get_avail_space(request, disk, &disk_space);
+ if ((error == 0) && (disk_space > 0)) {
+
+ dlist_t *item = dlist_new_item((void *)disk);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ *disks = dlist_append(item, *disks, AT_HEAD);
+ }
+
+ *space += disk_space;
+ }
+ }
+ }
+
+ dlist_free_items(hbas, NULL);
+ }
+
+ if (error != 0) {
+ dlist_free_items(*disks, NULL);
+ *disks = NULL;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: disk_get_avail_space(devconfig_t *request,
+ * dlist_t *disks, uint64_t space)
+ *
+ * INPUT: request - pointer to a devconfig_t struct representing
+ * the current layout request being processed
+ * disks - pointer to a list of disks
+ * space - pointer to a uint64_t to hold the computed available
+ * space
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: helper which iterates the input list of disks and determines
+ * the aggregate amount of available space they represent.
+ *
+ * Only disk slices that are in the usable slice list and not
+ * specifically excluded by the request's list of unavailable
+ * devices will contribute to the aggregate space computation.
+ */
+static int
+disk_get_avail_space(
+ devconfig_t *request,
+ dm_descriptor_t disk,
+ uint64_t *space)
+{
+ dlist_t *usable_slices = NULL;
+ dlist_t *iter = NULL;
+ int error = 0;
+
+ *space = 0;
+
+ /* for each usable slice */
+ error = get_usable_slices(&usable_slices);
+ for (iter = usable_slices;
+ (error == 0) && (iter != NULL);
+ iter = iter->next) {
+
+ dm_descriptor_t slice = (uintptr_t)iter->obj;
+ dm_descriptor_t slice_disk;
+ boolean_t avail = B_FALSE;
+ boolean_t reserved = B_FALSE;
+ boolean_t used = B_FALSE;
+
+ /* is slice on disk in question? */
+ if (((error = slice_get_disk(slice, &slice_disk)) != 0) ||
+ (compare_descriptor_names((void *)slice_disk,
+ (void *)disk) != 0)) {
+ continue;
+ }
+
+ /* is slice reserved by an explicit layout request? */
+ if (((error = is_reserved_slice(slice, &reserved)) != 0) ||
+ (reserved == B_TRUE)) {
+ continue;
+ }
+
+ /* is slice used by a pending layout request? */
+ if (((error = is_used_slice(slice, &used)) != 0) ||
+ (used == B_TRUE)) {
+ continue;
+ }
+
+ /* is slice available? */
+ if (((error = is_device_avail(slice, request, &avail)) == 0) &&
+ (avail == B_TRUE)) {
+
+ /* does slice have usable space? */
+ uint64_t size = 0;
+ if ((error = slice_get_size(slice, &size)) == 0) {
+ *space += size;
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: disks_get_avail_slices(devconfig_t *request,
+ * dlist_t *disks, dlist_t **slices)
+ *
+ * INPUT: request - pointer to a devconfig_t struct representing
+ * the current layout request being processed
+ * disks - pointer to a list of disks
+ * slices - pointer to an output list of disks
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: helper which iterates the input list of disks and builds a
+ * new list which contains disks that are determined to be
+ * available for satisfying the input request.
+ *
+ * A disk must contain at least one slice in the available
+ * slice list as well as have available space in order
+ * to be available.
+ */
+int
+disks_get_avail_slices(
+ devconfig_t *request,
+ dlist_t *disks,
+ dlist_t **slices)
+{
+ dlist_t *usable_slices = NULL;
+ dlist_t *iter = NULL;
+ int error = 0;
+
+ *slices = NULL;
+
+ /* for each usable slice */
+ error = get_usable_slices(&usable_slices);
+ for (iter = usable_slices;
+ (error == 0) && (iter != NULL);
+ iter = iter->next) {
+
+ dm_descriptor_t slice = (uintptr_t)iter->obj;
+ dm_descriptor_t disk = (dm_descriptor_t)0;
+ boolean_t avail = B_FALSE;
+ boolean_t reserved = B_FALSE;
+ boolean_t used = B_FALSE;
+
+ /* is slice on a disk in the input list? */
+ if (((error = slice_get_disk(slice, &disk)) != 0) ||
+ (dlist_contains(disks, (void *)disk,
+ compare_descriptor_names) != B_TRUE)) {
+ continue;
+ }
+
+ /* is slice reserved by an explicit layout request? */
+ if (((error = is_reserved_slice(slice, &reserved)) != 0) ||
+ (reserved == B_TRUE)) {
+ continue;
+ }
+
+ /* is slice used by a pending layout request? */
+ if (((error = is_used_slice(slice, &used)) != 0) ||
+ (used == B_TRUE)) {
+ continue;
+ }
+
+ /* is slice available? */
+ if (((error = is_device_avail(slice, request, &avail)) == 0) &&
+ (avail == B_TRUE)) {
+
+ /* does slice have available space? */
+ uint64_t size = 0;
+ error = slice_get_size(slice, &size);
+ if ((error == 0) && (size > 0)) {
+ dlist_t *item = dlist_new_item((void *)slice);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ *slices = dlist_append(item, *slices, AT_TAIL);
+ }
+ }
+ }
+ }
+
+ if (error != 0) {
+ dlist_free_items(*slices, NULL);
+ *slices = NULL;
+ }
+
+ return (error);
+}
+
+
+/*
+ * FUNCTION: get_hbas_and_disks_used_by_volumes(dlist_t *volumes,
+ * dlist_t **hbas, dlist_t **disks)
+ *
+ * INPUT: volumes - pointer to a list of devconfig_t volumes
+ *
+ * OUTPUT: hbas - a list of HBAs utilized by the input volumes
+ * disks - a list of disks utilized by the input volumes
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: An aggregate list of HBAs and disks used by the input volumes
+ * is built up by iterating the list of volumes and calling
+ * get_hbas_disks_used_by_volume() to determine the HBAs and disk
+ * used by each volume.
+ *
+ * The returned lists of HBAs and disks may contain duplicates.
+ */
+int
+get_hbas_and_disks_used_by_volumes(
+ dlist_t *volumes,
+ dlist_t **hbas,
+ dlist_t **disks)
+{
+ dlist_t *iter = NULL;
+ int error = 0;
+
+ for (iter = volumes;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+ error = get_hbas_and_disks_used_by_volume(
+ (devconfig_t *)iter->obj, hbas, disks);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_hbas_and_disks_used_by_volume(devconfig_t *volume,
+ * dlist_t **hbas, dlist_t **disks)
+ *
+ * INPUT: volume - pointer to a devconfig_t volume
+ *
+ * OUTPUT: hbas - a list of HBAs updated to include those utilized
+ * by the input volume
+ * disks - a list of disks updated to inlclude those utilized
+ * by the input volume
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: The volume's components are iterated and the disks and HBAs
+ * for each component are determined and appended to the input
+ * lists of HBAs and disks.
+ *
+ * The returned lists of HBAs and disks may contain duplicates.
+ */
+int
+get_hbas_and_disks_used_by_volume(
+ devconfig_t *volume,
+ dlist_t **hbas,
+ dlist_t **disks)
+{
+ dlist_t *iter = NULL;
+ int error = 0;
+
+ for (iter = devconfig_get_components(volume);
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ devconfig_t *dev = (devconfig_t *)iter->obj;
+ if (devconfig_isA(dev, TYPE_SLICE)) {
+
+ dm_descriptor_t disk = NULL;
+ char *name = NULL;
+
+ /* get disk for component slice */
+ ((error = devconfig_get_name(dev, &name)) != 0) ||
+ (error = get_disk_for_named_slice(name, &disk));
+ if (error == 0) {
+ dlist_t *item = dlist_new_item((void *)disk);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ *disks = dlist_append(item, *disks, AT_HEAD);
+ }
+ }
+
+ /* get HBAs for disk */
+ if (error == 0) {
+ dlist_t *disk_hbas = NULL;
+ if ((error = disk_get_hbas(disk, &disk_hbas)) == 0) {
+ /* the hba list may contain dups, but that's ok */
+ *hbas = dlist_append(disk_hbas, *hbas, AT_HEAD);
+ }
+ }
+
+ } else if (devconfig_isA(dev, TYPE_MIRROR)) {
+
+ /* collect info for submirrors */
+ dlist_t *iter1;
+ for (iter1 = devconfig_get_components(dev);
+ (iter1 != NULL) && (error == 0);
+ iter1 = iter1->next) {
+ error = get_hbas_and_disks_used_by_volume(
+ (devconfig_t *)iter1->obj, hbas, disks);
+ }
+
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: compare_hba_n_avail_disks(void *obj1, void *obj2)
+ *
+ * INPUT: obj1 - opaque pointer
+ * obj2 - opaque pointer
+ *
+ * RETURNS: int - <0 - if obj1 has fewer available disks than obj2
+ * 0 - if obj1 has the same # of available disks as obj2
+ * >0 - if obj1 has more available disks than obj2
+ *
+ * PURPOSE: dlist_t helper which compares the number of available disks
+ * for two HBAs represented as dm_descriptor_t handles.
+ *
+ * Both input objects are assumed to be dm_descriptor_t handles.
+ *
+ * The number of available disks associated with the HBAs was
+ * computed and saved in select_hbas_with_n_disks(), this
+ * function just checks the saved values.
+ */
+static int
+compare_hba_n_avail_disks(
+ void *obj1,
+ void *obj2)
+{
+ uint16_t n1 = 0;
+ uint16_t n2 = 0;
+
+ assert(obj1 != NULL);
+ assert(obj2 != NULL);
+
+ (void) hba_get_n_avail_disks((uintptr_t)obj1, &n1);
+ (void) hba_get_n_avail_disks((uintptr_t)obj2, &n2);
+
+ return ((int)n1 - n2);
+}
+
+/*
+ * FUNCTION: is_device_avail(dm_descriptor_t desc,
+ * devconfig_t *request, boolean_t *avail)
+ *
+ * INPUT: desc - a dm_descriptor_t device handle
+ * request - pointer to a devconfig_t struct representing
+ * the current layout request being processed
+ * avail - pointer to a boolean to hold the result
+ *
+ * RETURNS: int - 0 - on success
+ * !0 - otherwise
+ *
+ * PURPOSE: Internal helper which determines if the input device can
+ * be used as a volume component when satisfying the input
+ * request.
+ *
+ * The device is assumed to be a known valid device.
+ *
+ * The function checks if the device passes the request's
+ * available and unavailable device specifications.
+ *
+ * The input device name may be either a DID name or a CTD
+ * name. All name comparisons are done using the CTD name.
+ */
+static int
+is_device_avail(
+ dm_descriptor_t desc,
+ devconfig_t *request,
+ boolean_t *avail)
+{
+ char *name = NULL;
+ int error = 0;
+
+ *avail = B_FALSE;
+
+ if ((error = get_display_name(desc, &name)) == 0) {
+ error = is_named_device_avail(request, name, B_TRUE, avail);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: compare_request_to_request_spec_list_request(
+ * void *request, void *list_item)
+ *
+ * INPUT: request - opaque pointer to a devconfig_t
+ * list_item - opaque pointer to a request_spec_list_t
+ *
+ * RETURNS: int - 0 - if request is the same as list_item->request
+ * !0 - otherwise
+ *
+ * PURPOSE: dlist_t helper which compares the input request pointer
+ * to the list_item's request pointer for equality.
+ *
+ * This function is the lookup mechanism for the lists of
+ * cached device_spec_ts representing available/unavailable
+ * devices for a given defaults_t request/defaults struct.
+ *
+ * The defaults_t struct pointer is the lookup key.
+ */
+static int
+compare_request_to_request_spec_list_request(
+ void *request,
+ void *list_item)
+{
+ request_spec_list_t *entry =
+ (request_spec_list_t *)list_item;
+
+ assert(request != NULL);
+ assert(entry != NULL);
+
+ /* compare two devconfig_t pointers, if identical, return 0 */
+ return ((devconfig_t *)request != entry->request);
+}
+
+/*
+ * FUNCTION: compare_device_spec_specificity(void *spec1, void *spec2)
+ *
+ * INPUT: spec1 - opaque pointer to a device_spec_t
+ * spec2 - opaque pointer to a device_spec_t
+ *
+ * RETURNS: int - <0 - if spec1 is less specific than spec2
+ * 0 - if spec1 is as specific than spec2
+ * >0 - if spec1 is more specific than spec2
+ *
+ * PURPOSE: dlist_t helper which compares the level of specificity
+ * in the two input device_spec_t structs. The one
+ * which specifies more "components" of a cXtXdXsX device
+ * name is considered more specific.
+ */
+static int
+compare_device_spec_specificity(
+ void *spec1,
+ void *spec2)
+{
+ if (spec1 == NULL || spec2 == NULL) {
+ return (-1);
+ }
+
+ if ((((device_spec_t *)spec1)->data.ctd->slice != ID_UNSPECIFIED) &&
+ (((device_spec_t *)spec2)->data.ctd->slice == ID_UNSPECIFIED)) {
+ /* spec1 has slice, spec2 does not, spec1 more specific */
+ return (1);
+ }
+
+ if ((((device_spec_t *)spec2)->data.ctd->slice != ID_UNSPECIFIED) &&
+ (((device_spec_t *)spec1)->data.ctd->slice == ID_UNSPECIFIED)) {
+ /* spec2 has slice, spec1 does not, spec2 more specific */
+ return (-1);
+ }
+
+ if ((((device_spec_t *)spec2)->data.ctd->slice != ID_UNSPECIFIED) &&
+ (((device_spec_t *)spec1)->data.ctd->slice != ID_UNSPECIFIED)) {
+ /* both spec1 and spec2 have slice */
+ return (0);
+ }
+
+ if ((((device_spec_t *)spec1)->data.ctd->lun != ID_UNSPECIFIED) &&
+ (((device_spec_t *)spec2)->data.ctd->lun == ID_UNSPECIFIED)) {
+ /* spec1 has lun, spec2 does not, spec1 more specific */
+ return (1);
+ }
+
+ if ((((device_spec_t *)spec2)->data.ctd->lun != ID_UNSPECIFIED) &&
+ (((device_spec_t *)spec1)->data.ctd->lun == ID_UNSPECIFIED)) {
+ /* spec2 has lun, spec1 does not, spec2 more specific */
+ return (-1);
+ }
+
+ if ((((device_spec_t *)spec2)->data.ctd->lun != ID_UNSPECIFIED) &&
+ (((device_spec_t *)spec1)->data.ctd->lun != ID_UNSPECIFIED)) {
+ /* both spec1 and spec2 have lun */
+ return (0);
+ }
+
+ if ((((device_spec_t *)spec1)->data.ctd->target != ID_UNSPECIFIED) &&
+ (((device_spec_t *)spec2)->data.ctd->target == ID_UNSPECIFIED)) {
+ /* spec1 has target, spec2 does not, spec1 more specific */
+ return (1);
+ }
+
+ if ((((device_spec_t *)spec2)->data.ctd->target != ID_UNSPECIFIED) &&
+ (((device_spec_t *)spec1)->data.ctd->target == ID_UNSPECIFIED)) {
+ /* spec2 has target, spec1 does not, spec2 more specific */
+ return (-1);
+ }
+
+ if ((((device_spec_t *)spec2)->data.ctd->target != ID_UNSPECIFIED) &&
+ (((device_spec_t *)spec1)->data.ctd->target != ID_UNSPECIFIED)) {
+ /* both spec1 and spec2 have target */
+ return (0);
+ }
+
+ /* both specify just ctrl */
+ return (0);
+}
+
+/*
+ * FUNCTION: find_request_spec_list_entry(devconfig_t *request)
+ *
+ * INPUT: request - pointer to a devconfig_t struct
+ *
+ * RETURNS: request_spec_list_entry - pointer to a
+ * request_spec_list_entry struct
+ *
+ * PURPOSE: Lookup function which encapsulates the details of locating
+ * the device_spec_list_t cache entry for the input request.
+ */
+static request_spec_list_t *
+find_request_spec_list_entry(
+ devconfig_t *request)
+{
+ dlist_t *list_item = NULL;
+ request_spec_list_t *entry = NULL;
+
+ list_item = dlist_find(
+ _request_spec_list_cache,
+ (void *)request,
+ compare_request_to_request_spec_list_request);
+
+ if (list_item != NULL) {
+ entry = (request_spec_list_t *)list_item->obj;
+ }
+
+ return (entry);
+}
+
+/*
+ * FUNCTION: add_request_spec_list_entry(devconfig_t *request,
+ * char **avail_device_specs, char **unavail_device_specs,
+ * request_spec_list_entry_t **entry)
+ *
+ * INPUT: entry - pointer to the request_spec_list_entry struct to be
+ * added to the cache.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Function which encapsulates the details of adding a
+ * device_spec_list_t cache entry.
+ */
+static int
+add_request_spec_list_entry(
+ request_spec_list_t *entry)
+{
+ dlist_t *list_item = dlist_new_item((void *)entry);
+
+ if (list_item == NULL) {
+ return (ENOMEM);
+ }
+
+ _request_spec_list_cache = dlist_append(list_item,
+ _request_spec_list_cache, AT_HEAD);
+
+ return (0);
+}
+
+/*
+ * FUNCTION: make_request_spec_list_entry(devconfig_t *request,
+ * char **avail_device_specs, char **unavail_device_specs,
+ * request_spec_list_entry_t **entry)
+ *
+ * INPUT: request - pointer to a devconfig_t struct
+ * avail_device_specs - char * array of user specified available
+ * devices associated with the input request
+ * unavail_device_specs - char * array of user specified
+ * unavailable devices associated with the input
+ * request
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Function which encapsulates the details of generating a new
+ * the device_spec_list_t cache entry for the input request
+ * and its lists of avail/unavail devices.
+ *
+ * Converts the input arrays of (un)available device names into
+ * equivalent lists of device_spec_t structs.
+ *
+ * Creates a new cache entry, populates it and adds it to the
+ * cache.
+ */
+static int
+make_request_spec_list_entry(
+ devconfig_t *request,
+ char **avail_device_specs,
+ char **unavail_device_specs,
+ request_spec_list_t **entry)
+{
+ int error = 0;
+ dlist_t *list = NULL;
+
+ *entry = calloc(1, sizeof (request_spec_list_t));
+ if (*entry == NULL) {
+ return (ENOMEM);
+ }
+
+ (*entry)->request = request;
+
+ /*
+ * map the avail_device_name array into a list of device_spec_t
+ * and save the list as the entry's available list
+ */
+ error = convert_usernames_to_specs(
+ avail_device_specs, &list);
+
+ if (error == 0) {
+ (*entry)->avail_specs_list = list;
+ }
+
+ /*
+ * map the unavail_device_name array into a list of device_spec_t
+ * and save the list as the entry's unavailable list
+ */
+ list = NULL;
+ error = convert_usernames_to_specs(
+ unavail_device_specs, &list);
+
+ if (error == 0) {
+ (*entry)->unavail_specs_list = list;
+ }
+
+ if (error != 0) {
+ /* delete the partial entry */
+ destroy_request_spec_list_entry((void *)*entry);
+ *entry = NULL;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: convert_usernames_to_specs(char **specs, dlist_t **list)
+ *
+ * INPUT: specs - char * array of device CTD names
+ *
+ * OUTPUT: list - pointer to a list of device_spec_t corresponding
+ * to each name in the input array
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Function which converts the input CTD device names to the
+ * equivalent device_spec_t structs.
+ *
+ * Iterates the input array and converts each CTD name to a
+ * device_spec_t using get_spec_for_name().
+ */
+static int
+convert_usernames_to_specs(
+ char **specs,
+ dlist_t **list)
+{
+ int i = 0;
+ int error = 0;
+
+ /*
+ * For each spec in the array, get the corresponding
+ * device_spec_t and add it to the list.
+ *
+ * Any spec in the array that looks to be a DID name
+ * is first converted to its equivalent CTD name.
+ */
+ for (i = 0;
+ (specs != NULL) && (specs[i] != NULL) && (error == 0);
+ i++) {
+
+ device_spec_t *spec = NULL;
+ char *userspec = specs[i];
+
+ error = get_spec_for_name(userspec, &spec);
+ if ((error == 0) && (spec != NULL)) {
+ dlist_t *list_item = dlist_new_item((void *)spec);
+ if (spec == NULL) {
+ error = ENOMEM;
+ } else {
+ *list = dlist_insert_ordered
+ (list_item, *list, DESCENDING,
+ compare_device_spec_specificity);
+ }
+ }
+ }
+
+ if (error != 0) {
+ /* the device_spec_t in the list items are maintained */
+ /* in a cache elsewhere, so don't free them here. */
+ dlist_free_items(*list, NULL);
+ *list = NULL;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: destroy_request_spec_list_entry(void *entry)
+ *
+ * INPUT: entry - opaque pointer to a request_spec_list_t
+ *
+ * RETURNS: nothing
+ *
+ * PURPOSE: Function which reclaims memory allocated to a
+ * request_spec_list_t.
+ *
+ * Frees memory allocated to the avail_spec_list and
+ * unavail_spec_list. Entries in the list are not freed,
+ * since they are owned by the device_spec cache.
+ */
+static void
+destroy_request_spec_list_entry(
+ void *obj)
+{
+ request_spec_list_t *entry = (request_spec_list_t *)obj;
+
+ if (entry != NULL) {
+ /* items in the list are in the spec_cache and will */
+ /* be cleaned up when it is destroyed. */
+ dlist_free_items(entry->avail_specs_list, NULL);
+ dlist_free_items(entry->unavail_specs_list, NULL);
+ free(entry);
+ }
+}
+
+/*
+ * FUNCTION: destroy_request_spec_list_cache()
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Function which destroys all entries in the request_spec_list
+ * cache.
+ */
+static int
+destroy_request_spec_list_cache()
+{
+ dlist_free_items(_request_spec_list_cache,
+ destroy_request_spec_list_entry);
+ _request_spec_list_cache = NULL;
+
+ return (0);
+}
+
+/*
+ * FUNCTION: get_request_avail_spec_list(devconfig_t *request,
+ * dlist_t **list)
+ *
+ * INPUT: request - a pointer to a devconfig_t
+ *
+ * OUTPUT: list - pointer to a list of device_spec_t corresponding
+ * to the devices specified as available by the
+ * input request.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Function which locates or builds the list of device_spec_t
+ * for the available devices specified in the input request.
+ *
+ * Looks up the input request in the request_spec_list cache.
+ * If there is currently no entry in the cache for the request,
+ * an entry is built and added.
+ *
+ * The entry's list of available device_spec_t is returned.
+ */
+static int
+get_request_avail_spec_list(
+ devconfig_t *request,
+ dlist_t **list)
+{
+ request_spec_list_t *entry = NULL;
+ int error = 0;
+
+ if ((entry = find_request_spec_list_entry(request)) == NULL) {
+
+ /* create cache entry for this request */
+ error = make_request_spec_list_entry(
+ request,
+ devconfig_get_available(request),
+ devconfig_get_unavailable(request),
+ &entry);
+
+ if ((error == 0) && (entry != NULL)) {
+ if ((error = add_request_spec_list_entry(entry)) != 0) {
+ destroy_request_spec_list_entry(entry);
+ entry = NULL;
+ }
+ }
+ }
+
+ if ((error == 0) && (entry != NULL)) {
+ *list = entry->avail_specs_list;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_request_unavail_spec_list(devconfig_t *request,
+ * dlist_t **list)
+ *
+ * INPUT: request - a pointer to a devconfig_t
+ *
+ * OUTPUT: list - pointer to a list of device_spec_t corresponding
+ * to the devices specified as unavailable by the
+ * input request.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Function which locates or builds the list of device_spec_t
+ * for the unavailable devices specified in the input request.
+ *
+ * Looks up the input request in the request_spec_list cache.
+ * If there is currently no entry in the cache for the request,
+ * an entry is built and added.
+ *
+ * The entry's list of unavailable device_spec_t is returned.
+ */
+static int
+get_request_unavail_spec_list(
+ devconfig_t *request,
+ dlist_t **list)
+{
+ request_spec_list_t *entry = NULL;
+ int error = 0;
+
+ if ((entry = find_request_spec_list_entry(request)) == NULL) {
+
+ /* create new entry for this request */
+ error = make_request_spec_list_entry(
+ request,
+ devconfig_get_available(request),
+ devconfig_get_unavailable(request),
+ &entry);
+
+ if ((error == 0) && (entry != NULL)) {
+ if ((error = add_request_spec_list_entry(entry)) != 0) {
+ destroy_request_spec_list_entry(entry);
+ entry = NULL;
+ }
+ }
+ }
+
+ if ((error == 0) && (entry != NULL)) {
+ *list = entry->unavail_specs_list;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_default_avail_spec_list(defaults_t *defaults,
+ * char *dsname, dlist_t **list)
+ *
+ * INPUT: defaults - a pointer to a defaults_t struct
+ * dsname - the name of the diskset whose defaults should be used
+ *
+ * OUTPUT: list - pointer to a list of device_spec_t corresponding
+ * to the devices specified as available by the
+ * defaults for the named diskset, or the global
+ * defaults for all disksets.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Function which locates or builds the list of device_spec_t
+ * for the available devices for the named diskset.
+ *
+ * Locates the defaults for the named diskset, if there are none,
+ * locates the global defaults for all disksets.
+ *
+ * The defaults devconfig_t struct is then used to look up the
+ * the corresponding entry in the request_spec_list cache.
+ *
+ * If there is currently no entry in the cache for the defaults,
+ * an entry is built and added.
+ *
+ * The entry's list of available device_spec_t is returned.
+ */
+static int
+get_default_avail_spec_list(
+ defaults_t *alldefaults,
+ char *dsname,
+ dlist_t **list)
+{
+ request_spec_list_t *entry = NULL;
+ devconfig_t *defaults = NULL;
+ int error = 0;
+
+ /* Get diskset defaults, or global if none for diskset */
+ error = defaults_get_diskset_by_name(
+ alldefaults, dsname, &defaults);
+
+ if (error != 0) {
+ if (error == ENOENT) {
+ /* to get global defaults, pass a NULL diskset name */
+ error = defaults_get_diskset_by_name(
+ alldefaults, NULL, &defaults);
+ }
+
+ if (error != 0) {
+ if (error != ENOENT) {
+ oprintf(OUTPUT_DEBUG,
+ gettext("get defaults for %s returned %d\n"),
+ dsname, error);
+ } else {
+ error = 0;
+ }
+ }
+ }
+
+ if ((entry = find_request_spec_list_entry(defaults)) == NULL) {
+
+ /* create new entry for these defaults */
+ error = make_request_spec_list_entry(
+ defaults,
+ devconfig_get_available(defaults),
+ devconfig_get_unavailable(defaults),
+ &entry);
+
+ if ((error == 0) && (entry != NULL)) {
+ if ((error = add_request_spec_list_entry(entry)) != 0) {
+ destroy_request_spec_list_entry(entry);
+ entry = NULL;
+ }
+ }
+ }
+
+ if ((error == 0) && (entry != NULL)) {
+ *list = entry->avail_specs_list;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_default_unavail_spec_list(defaults_t *defaults,
+ * char *dsname, dlist_t **list)
+ *
+ * INPUT: defaults - a pointer to a defaults_t struct
+ * dsname - the name of the diskset whose defaults should be used
+ *
+ * OUTPUT: list - pointer to a list of device_spec_t corresponding
+ * to the devices specified as unavailable by the
+ * defaults for the named diskset, or the global
+ * defaults for all disksets.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Function which locates or builds the list of device_spec_t
+ * for the unavailable devices for the named diskset.
+ *
+ * Locates the defaults for the named diskset, if there are none,
+ * locates the global defaults for all disksets.
+ *
+ * The defaults devconfig_t struct is then used to look up the
+ * the corresponding entry in the request_spec_list cache.
+ *
+ * If there is currently no entry in the cache for the defaults,
+ * an entry is built and added.
+ *
+ * The entry's list of unavailable device_spec_t is returned.
+ */
+static int
+get_default_unavail_spec_list(
+ defaults_t *alldefaults,
+ char *dsname,
+ dlist_t **list)
+{
+ request_spec_list_t *entry = NULL;
+ devconfig_t *defaults = NULL;
+ int error = 0;
+
+ /* Get diskset defaults, or global if none for diskset */
+ error = defaults_get_diskset_by_name(
+ alldefaults, dsname, &defaults);
+
+ if (error != 0) {
+
+ if (error == ENOENT) {
+ /* to get global defaults, pass a NULL diskset name */
+ error = defaults_get_diskset_by_name(
+ alldefaults, NULL, &defaults);
+ }
+
+ if (error != 0) {
+ if (error != ENOENT) {
+ oprintf(OUTPUT_DEBUG,
+ gettext("get defaults for %s returned %d\n"),
+ dsname, error);
+ } else {
+ error = 0;
+ }
+ }
+ }
+
+ if ((entry = find_request_spec_list_entry(defaults)) == NULL) {
+
+ /* create new entry for these defaults */
+ error = make_request_spec_list_entry(
+ defaults,
+ devconfig_get_available(defaults),
+ devconfig_get_unavailable(defaults),
+ &entry);
+
+ if ((error == 0) && (entry != NULL)) {
+ if ((error = add_request_spec_list_entry(entry)) != 0) {
+ destroy_request_spec_list_entry(entry);
+ entry = NULL;
+ }
+ }
+ }
+
+ if ((error == 0) && (entry != NULL)) {
+ *list = entry->unavail_specs_list;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: is_named_device_avail(devconfig_t *request, char *device_name,
+ * boolean_t check_aliases, boolean_t *avail)
+ *
+ * INPUT: request - the current request devconfig_t
+ * device_name - char * device name
+ * check_aliases - boolean_t which indicates whether the device's
+ * aliases should be considered by the availability checks.
+ *
+ * OUTPUT: avail - a boolean_t * to hold the result
+ *
+ * RETURNS: int - !0 on error
+ *
+ * avail is set to B_TRUE if the named device is available for
+ * the input request, B_FALSE otherwise.
+ *
+ * PURPOSE: Determine if the named device can be used to satisfy the
+ * input request.
+ *
+ * There are several levels at which device availabiity or
+ * unavailability may be specifed:
+ *
+ * 1. the volume subrequest,
+ * 2. the toplevel (diskset) request,
+ * 3. the diskset-specific defaults
+ * 4. the global defaults
+ *
+ * If the diskset-specific defaults exist, only they are checked.
+ *
+ * The precedence ordering that is enforced:
+ *
+ * 1. if request has an avail list, the name must be in it
+ * and not in the request's unavail list.
+ * 2. if request has an unavail list, the name must not be in it.
+ * 3. if toplevel request has an avail list, the name must be
+ * in it and not in the toplevel request's unavailable
+ * list.
+ * 4. if toplevel request has an unavail list, the name must
+ * not be in it.
+ * 5. if defaults have an avail list, the name must be in it
+ * and not in the defaults unavailable list.
+ * 6. if defaults have an unavail list, the name must not be
+ * in it.
+ */
+static int
+is_named_device_avail(
+ devconfig_t *request,
+ char *device_name,
+ boolean_t check_aliases,
+ boolean_t *avail)
+{
+ typedef enum check_types {
+ DEVICE_REQUEST = 0,
+ DISKSET_REQUEST,
+ DEFAULTS,
+ N_CHECKS
+ } check_type_t;
+
+ check_type_t check_type;
+
+ typedef enum list_types {
+ AVAIL = 0,
+ UNAVAIL,
+ N_LISTS
+ } list_type_t;
+
+ dlist_t *lists[N_CHECKS][N_LISTS];
+ boolean_t includes;
+ int error = 0;
+
+ memset(lists, 0, (N_CHECKS * N_LISTS) * sizeof (dlist_t *));
+
+ if (request != NULL) {
+ /* get avail/unavail specs for request */
+ ((error = get_request_avail_spec_list(
+ request, &lists[DEVICE_REQUEST][AVAIL])) != 0) ||
+ (error = get_request_unavail_spec_list(
+ request, &lists[DEVICE_REQUEST][UNAVAIL]));
+ }
+
+ if ((error == 0) && (_toplevel_request != NULL)) {
+ /* diskset request */
+ ((error = get_request_avail_spec_list(
+ _toplevel_request, &lists[DISKSET_REQUEST][AVAIL])) != 0) ||
+ (error = get_request_unavail_spec_list(
+ _toplevel_request, &lists[DISKSET_REQUEST][UNAVAIL]));
+ }
+
+ if ((error == 0) && (_defaults != NULL)) {
+ /* and diskset/global defaults */
+ ((error = get_default_avail_spec_list(_defaults,
+ get_request_diskset(), &lists[DEFAULTS][AVAIL])) != 0) ||
+ (error = get_default_unavail_spec_list(_defaults,
+ get_request_diskset(), &lists[DEFAULTS][UNAVAIL]));
+ }
+
+ if (error != 0) {
+ return (error);
+ }
+
+ *avail = B_TRUE;
+
+ for (check_type = DEVICE_REQUEST;
+ (check_type < N_CHECKS) && (error == 0);
+ check_type++) {
+
+ if (lists[check_type][AVAIL] != NULL) {
+
+ /* does avail spec list include named device? */
+ if ((error = avail_list_includes_device_name(
+ lists[check_type][AVAIL], device_name, check_aliases,
+ &includes)) == 0) {
+
+ if (includes != B_TRUE) {
+ *avail = B_FALSE;
+ }
+
+ if ((includes == B_TRUE) &&
+ (lists[check_type][UNAVAIL] != NULL)) {
+
+ /* device is available, is it in the unavail list? */
+ if ((error = unavail_list_includes_device_name(
+ lists[check_type][UNAVAIL], device_name,
+ check_aliases, &includes)) == 0) {
+
+ if (includes == B_TRUE) {
+ *avail = B_FALSE;
+ }
+ }
+ }
+ }
+
+ /* lists at this level checked, skip remainder */
+ break;
+
+ } else if (lists[check_type][UNAVAIL] != NULL) {
+
+ /* does unavail spec list include named device? */
+ if ((error = unavail_list_includes_device_name(
+ lists[check_type][UNAVAIL], device_name,
+ check_aliases, &includes)) == 0) {
+
+ if (includes == B_TRUE) {
+ *avail = B_FALSE;
+ }
+ }
+
+ /* list at this level checked, skip remainder */
+ break;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: avail_list_includes_device_name(dlist_t *list,
+ * char *device_name, boolean_t check_aliases,
+ * boolean_t *includes)
+ *
+ * INPUT: list - a dlist_t list of available device_spec_t
+ * device_name - a char * device CTD name
+ * check_aliases - boolean_t which indicates if the device's
+ * aliases should be considered in the availability
+ * checking.
+ *
+ * OUTPUT: includes - B_TRUE - if named device is "included" by any
+ * specification in the input list
+ * B_FALSE - otherwise
+ *
+ * RETURNS: int - 0 on success
+ * - !0 otherwise
+ *
+ * PURPOSE: Helper used by is_named_device_avail that determines
+ * if the input list of device specifications "includes"
+ * a specific device.
+ *
+ * Iterates the elements of the input array and searches
+ * for a match using spec_includes_device_name().
+ */
+static int
+avail_list_includes_device_name(
+ dlist_t *list,
+ char *device_name,
+ boolean_t check_aliases,
+ boolean_t *includes)
+{
+ dlist_t *iter = NULL;
+ int error = 0;
+
+ *includes = B_FALSE;
+
+ for (iter = list;
+ (*includes == B_FALSE) && (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ device_spec_t *spec = (device_spec_t *)iter->obj;
+ error = spec_includes_device_name(spec, device_name,
+ check_aliases, includes);
+ }
+
+ return (0);
+}
+
+/*
+ * FUNCTION: unavail_list_includes_device_name(dlist_t *list,
+ * char *device_name, boolean_t check_aliases,
+ * boolean_t *includes)
+ *
+ * INPUT: list - a dlist_t list of unavailable device_spec_t
+ * device_name - a char * device CTD name
+ * check_aliases - boolean_t which indicates if the device's
+ * aliases should be considered in the availability
+ * checking.
+ *
+ * OUTPUT: includes - B_TRUE - if named device is "included" by any
+ * specification in the input list
+ * B_FALSE - otherwise
+ *
+ * RETURNS: int - 0 on success
+ * - !0 otherwise
+ *
+ * PURPOSE: Helper used by is_named_device_avail that determines
+ * if the input list of device specifications "includes"
+ * a specific device.
+ *
+ * Iterates the elements of the input array and searches
+ * for a match using spec_includes_device_name_or_alias().
+ */
+static int
+unavail_list_includes_device_name(
+ dlist_t *list,
+ char *device_name,
+ boolean_t check_aliases,
+ boolean_t *includes)
+{
+ dlist_t *iter = NULL;
+ int error = 0;
+ device_spec_t *unavail_spec;
+ boolean_t check_for_alternate_hba = B_FALSE;
+
+ *includes = B_FALSE;
+
+ /*
+ * the specs in the list are in descending order of specificity.
+ * so a more exact spec will rule the device out before a less
+ * exact spec.
+ *
+ * Meaning: if the list has { "c3t0d0", ..., "c3", ... } and the
+ * input device name is "c3t0d0s0", it will match "c3t0d0"
+ * before "c3".
+ *
+ * This is important for the multi-path alias checking below.
+ * If the input device name is ruled out by a non-controller
+ * specification, it is really unavailable.
+ */
+ for (iter = list;
+ (*includes == B_FALSE) && (iter != NULL);
+ iter = iter->next) {
+
+ unavail_spec = (device_spec_t *)iter->obj;
+ error = spec_includes_device_name(
+ unavail_spec, device_name, check_aliases, includes);
+
+ }
+
+ if ((error == 0) && (*includes == B_TRUE)) {
+
+ /* matched an unavailable spec, was it a controller/HBA? */
+ oprintf(OUTPUT_DEBUG,
+ "device \"%s\" is unavailable, "
+ "it matched \"c(%d)t(%d)d(%d)s(%d)\"\n",
+ device_name,
+ unavail_spec->data.ctd->ctrl,
+ unavail_spec->data.ctd->target,
+ unavail_spec->data.ctd->lun,
+ unavail_spec->data.ctd->slice);
+
+ if ((unavail_spec->data.ctd->ctrl != ID_UNSPECIFIED) &&
+ (unavail_spec->data.ctd->target == ID_UNSPECIFIED) &&
+ (unavail_spec->data.ctd->lun == ID_UNSPECIFIED) &&
+ (unavail_spec->data.ctd->slice == ID_UNSPECIFIED)) {
+
+ /*
+ * Need to see if the named device is a disk or slice,
+ * and if so check to see if the it is multipathed
+ * and possibly accessible thru another controller/HBA.
+ */
+ check_for_alternate_hba = B_TRUE;
+ }
+ }
+
+ if ((error == 0) && (check_for_alternate_hba == B_TRUE)) {
+
+ dm_descriptor_t slice = (dm_descriptor_t)0;
+ dm_descriptor_t disk = (dm_descriptor_t)0;
+
+ ((error = slice_get_by_name(device_name, &slice)) != 0) ||
+ (error = disk_get_by_name(device_name, &disk));
+ if (error != 0) {
+ return (error);
+ }
+
+ /* if it is a slice, get its disk */
+ if ((error == 0) && (slice != (dm_descriptor_t)0)) {
+ error = slice_get_disk(slice, &disk);
+ }
+
+ if ((error == 0) && (disk != (dm_descriptor_t)0)) {
+
+ /* see if all the disk's HBAs are unavailable */
+ dlist_t *hbas = NULL;
+ dlist_t *iter = NULL;
+
+ error = disk_get_hbas(disk, &hbas);
+
+ if (hbas != NULL) {
+ oprintf(OUTPUT_DEBUG,
+ gettext(" checking alternate paths for %s\n"),
+ device_name);
+ } else {
+ oprintf(OUTPUT_DEBUG,
+ gettext(" no alternate paths for %s\n"),
+ device_name);
+ }
+
+ /* for each of the disk's HBAs */
+ for (iter = hbas;
+ (iter != NULL) && (*includes == B_TRUE) && (error == 0);
+ iter = iter->next) {
+
+ dm_descriptor_t hba = (uintptr_t)iter->obj;
+ device_spec_t *hbaspec;
+ char *hbaname = NULL;
+ dlist_t *iter2 = NULL;
+
+ *includes = B_FALSE;
+
+ ((error = get_display_name(hba, &hbaname)) != 0) ||
+ (error = get_spec_for_name(hbaname, &hbaspec));
+
+ /* is HBA unavailable? */
+ for (iter2 = list;
+ (iter2 != NULL) && (error == 0) &&
+ (*includes == B_FALSE);
+ iter2 = iter2->next) {
+
+ device_spec_t *spec =
+ (device_spec_t *)iter2->obj;
+
+ *includes = spec_includes_device(spec, hbaspec);
+ }
+ }
+ dlist_free_items(hbas, NULL);
+
+ /* if *includes==B_TRUE here, all HBAs are unavailable */
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: spec_includes_device_name(device_spec_t *spec,
+ * char *device_name, boolean_t check_aliases,
+ * boolean_t *includes)
+ *
+ * INPUT: spec - a device_spec_t CTD specification.
+ * device_name - a char * device CTD name
+ * check_aliases - boolean_t which indicates if the device's
+ * aliases should be considered in the checking.
+ *
+ * OUTPUT: includes - B_TRUE - if device is "included" by the input
+ * specification
+ * B_FALSE - otherwise
+ *
+ * RETURNS: int - 0 on success
+ * - !0 otherwise
+ *
+ * PURPOSE: Helper used by (un)avail_specs_includes_device_name() that
+ * determines if the input device specification "includes"
+ * the named device.
+ *
+ * If check_aliases is true and the named device is a slice or
+ * a disk drive, its multi-pathed aliases are also checked
+ * against the spec.
+ */
+static int
+spec_includes_device_name(
+ device_spec_t *spec,
+ char *device_name,
+ boolean_t check_aliases,
+ boolean_t *includes)
+{
+ device_spec_t *device_spec;
+ int error = 0;
+
+ error = get_spec_for_name(device_name, &device_spec);
+ if (error == 0) {
+
+ *includes = spec_includes_device(spec, device_spec);
+
+ if ((*includes == B_FALSE) && (check_aliases == B_TRUE)) {
+
+ /* spec doesn't include name, check aliases */
+
+ dm_descriptor_t device = (dm_descriptor_t)0;
+ dlist_t *aliases = NULL;
+
+ /* only slices and disks have aliases */
+ error = slice_get_by_name(device_name, &device);
+ if (device != (dm_descriptor_t)0) {
+ error = get_aliases(device, &aliases);
+ } else if (error == 0) {
+ error = disk_get_by_name(device_name, &device);
+ if (device != (dm_descriptor_t)0) {
+ error = get_aliases(device, &aliases);
+ }
+ }
+
+ if ((error == 0) && (aliases != NULL)) {
+
+ dlist_t *iter;
+ for (iter = aliases;
+ (iter != NULL) && (*includes == B_FALSE) &&
+ (error == 0);
+ iter = iter->next) {
+
+ char *alias = (char *)iter->obj;
+ device_spec_t *alias_spec;
+
+ error = get_spec_for_name(alias, &alias_spec);
+ if (error == 0) {
+ /* does spec include alias? */
+ *includes = spec_includes_device(spec, alias_spec);
+ }
+ }
+ }
+ dlist_free_items(aliases, free);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: destroy_device_spec(device_spec_t *spec)
+ *
+ * INPUT: spec - pointer to a device_spec_t
+ *
+ * RETURNS: nothing
+ *
+ * PURPOSE: Function which reclaims memory allocated to a device_spec_t.
+ *
+ * Frees memory allocated to hold the specific data in the spec.
+ */
+static void
+destroy_device_spec(
+ device_spec_t *spec)
+{
+ if (spec != NULL) {
+ if (spec->type == SPEC_TYPE_CTD) {
+ free(spec->data.ctd);
+ } else if (spec->type == SPEC_TYPE_RAW) {
+ free(spec->data.raw);
+ }
+ free(spec);
+ }
+}
+
+/*
+ * FUNCTION: create_device_spec(char *name, device_spec_t **spec);
+ *
+ * INPUT: name - pointer to a char* device name
+ *
+ * OUTPUT: spec - pointer to a device_spec_t to hold the result
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Function which creates a device_spec_t for the input
+ * device name.
+ *
+ */
+static int
+create_device_spec(
+ char *name,
+ device_spec_t **spec)
+{
+ int error = 0;
+
+ /* allocate the device spec and try various parsing schemes */
+ *spec = (device_spec_t *)calloc(1, sizeof (device_spec_t));
+ if (*spec == NULL) {
+ error = ENOMEM;
+ } else {
+ if (((error = create_device_ctd_spec(name, spec)) != 0) &&
+ (error != ENOMEM)) {
+ /* CTD failed, try other parsing schemes */
+ error = create_device_raw_spec(name, spec);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: create_device_ctd_spec(char *name, device_spec_t **spec);
+ *
+ * INPUT: name - pointer to a char* device name
+ *
+ * OUTPUT: spec - pointer to a device_spec_t updated with the parsed
+ * CTD spec, if successful
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Function which atttempts to parse the input device name into
+ * cXtXdXsX component ids. The ids are the integer values of each
+ * specified segment of the input name.
+ *
+ * If the name doesn't contain a segment, the id is set to
+ * ID_UNSPECIFIED.
+ *
+ * The input name must be well-formed.
+ *
+ * These are the acceptable forms:
+ *
+ * cXtXdXsX
+ * cXtXdX
+ * cXtX
+ * cXdXsX
+ * cXdX
+ * cX
+ */
+static int
+create_device_ctd_spec(
+ char *name,
+ device_spec_t **spec)
+{
+ uint_t ctrl;
+ uint_t target;
+ uint_t lun;
+ uint_t slice;
+
+ uint_t nscan;
+ uint_t nchars;
+
+ char *device_str;
+ char *target_str;
+ char *ctd_str;
+ char *t_ptr;
+ char *d_ptr;
+ char *s_ptr;
+
+ boolean_t is_ide = B_FALSE;
+ boolean_t got_slice = B_FALSE;
+ boolean_t got_lun = B_FALSE;
+ boolean_t got_target = B_FALSE;
+ boolean_t got_ctrl = B_FALSE;
+
+ int error = 0;
+
+ ctd_str = strdup(name);
+ if (ctd_str == NULL) {
+ return (ENOMEM);
+ }
+
+ /* trim any leading path (/dev/dsk/cXtXdXsX) */
+ if ((device_str = strrchr(ctd_str, '/')) != NULL) {
+ ++device_str;
+ } else {
+ device_str = ctd_str;
+ }
+
+ /* find each segment start position */
+ t_ptr = strrchr(device_str, 't');
+ d_ptr = strrchr(device_str, 'd');
+ s_ptr = strrchr(device_str, 's');
+
+ /*
+ * scan ids from each existing segment working backwards
+ * so as to leave the device_str in the correct state
+ * for the next expected segment
+ */
+ if (s_ptr != NULL) {
+
+ /* found 's', try to get slice */
+ nchars = strlen(s_ptr);
+ if ((sscanf(s_ptr, "s%u%n", &slice, &nscan) != 1) ||
+ (nscan != nchars)) {
+
+ error = -1;
+ oprintf(OUTPUT_DEBUG,
+ gettext("no slice component in device "
+ "name \"%s\".\n"),
+ name);
+
+ } else {
+ got_slice = B_TRUE;
+ *s_ptr = '\0';
+ }
+ }
+
+ if ((error == 0) && (d_ptr != NULL)) {
+
+ /* found 'd', try to get disk/lun */
+ nchars = strlen(d_ptr);
+ if ((sscanf(d_ptr, "d%u%n", &lun, &nscan) != 1) ||
+ (nscan != nchars)) {
+
+ error = -1;
+ oprintf(OUTPUT_DEBUG,
+ gettext("no disk/lun component "
+ "in device name \"%s\".\n"),
+ name);
+
+ } else {
+ got_lun = B_TRUE;
+ *d_ptr = '\0';
+ }
+ }
+
+ if ((error == 0) && (t_ptr != NULL)) {
+
+ /* found 't', try to get target, it may be a hex WWN id */
+
+ /* skip leading 't' and add two for the 'OX' */
+ nchars = strlen(t_ptr + 1) + 2;
+ if ((target_str = (char *)malloc(nchars+1)) == NULL) {
+
+ error = ENOMEM;
+
+ } else {
+
+ strcpy(target_str, "0X");
+ strcpy(target_str+2, t_ptr + 1);
+ target_str[nchars] = '\0';
+
+ if ((sscanf(target_str, "%x%n", &target, &nscan) != 1) ||
+ (nscan != nchars)) {
+
+ error = -1;
+ oprintf(OUTPUT_DEBUG,
+ gettext("no target/WWN component "
+ "in device name \"%s\".\n"),
+ name);
+
+ } else {
+ got_target = B_TRUE;
+ *t_ptr = '\0';
+ }
+
+ free(target_str);
+ }
+
+ } else {
+ is_ide = B_TRUE;
+ }
+
+ if ((error == 0) && (device_str != NULL)) {
+
+ /* get controller/hba/channel */
+ nchars = strlen(device_str);
+ if ((sscanf(device_str, "c%u%n", &ctrl, &nscan) != 1) ||
+ (nscan != nchars)) {
+
+ error = -1;
+ oprintf(OUTPUT_DEBUG,
+ gettext("no channel/HBA component "
+ "in device name \"%s\".\n"),
+ name);
+
+ } else {
+ got_ctrl = B_TRUE;
+ }
+ }
+
+ free(ctd_str);
+
+ if (error == 0) {
+
+ /* allocate the ctd_spec_t struct and store the ids */
+ (*spec)->type = SPEC_TYPE_CTD;
+ (*spec)->data.ctd = (ctd_spec_t *)calloc(1, sizeof (ctd_spec_t));
+
+ if ((*spec)->data.ctd == NULL) {
+ error = ENOMEM;
+ }
+
+ (*spec)->data.ctd->slice = ID_UNSPECIFIED;
+ (*spec)->data.ctd->lun = ID_UNSPECIFIED;
+ (*spec)->data.ctd->target = ID_UNSPECIFIED;
+ (*spec)->data.ctd->ctrl = ID_UNSPECIFIED;
+
+ if (got_slice == B_TRUE) {
+ (*spec)->data.ctd->slice = slice;
+ }
+
+ if (got_lun == B_TRUE) {
+ (*spec)->data.ctd->lun = lun;
+ }
+
+ if (got_target == B_TRUE) {
+ (*spec)->data.ctd->target = target;
+ }
+
+ if (got_ctrl == B_TRUE) {
+ (*spec)->data.ctd->ctrl = ctrl;
+ }
+
+ (*spec)->data.ctd->is_ide = is_ide;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: create_device_raw_spec(char *name, device_spec_t **spec);
+ *
+ * INPUT: name - pointer to a char* device name
+ *
+ * OUTPUT: spec - pointer to a device_spec_t updated with the raw spec
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Function which creates a "raw" spec for the input name.
+ *
+ * This is a last resort if all other spec parsing schemes failed,
+ * the "raw" spec is just the input device name.
+ */
+static int
+create_device_raw_spec(
+ char *name,
+ device_spec_t **spec)
+{
+ int error = 0;
+ char *ctd_str = strdup(name);
+
+ if (ctd_str == NULL) {
+ return (ENOMEM);
+ }
+
+ (*spec)->type = SPEC_TYPE_RAW;
+ (*spec)->data.raw = ctd_str;
+
+ oprintf(OUTPUT_DEBUG,
+ gettext("made raw device spec for \"%s\"\n"), ctd_str);
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_spec_for_name(char *name, device_spec_t **id);
+ *
+ * INPUT: name - pointer to a char* device name
+ *
+ * OUTPUT: id - pointer to a device_spec_t to hold the result
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Function which finds the device_spec_t that already
+ * exists for the input name or creates it.
+ *
+ * The returned struct should not be freed, it is maintained
+ * in a cache that will be purged when the layout process
+ * is complete.
+ */
+int
+get_spec_for_name(
+ char *name,
+ device_spec_t **id)
+{
+ dlist_t *item;
+ int error = 0;
+
+ item = dlist_find(_spec_cache, (void *)name,
+ compare_name_to_spec_cache_name);
+
+ if (item == NULL) {
+ if ((error = create_device_spec(name, id)) == 0) {
+
+ spec_cache_t *entry = (spec_cache_t *)
+ calloc(1, sizeof (spec_cache_t));
+
+ if (entry == NULL) {
+ destroy_device_spec(*id);
+ error = ENOMEM;
+ } else {
+ char *dup = strdup(name);
+ if (dup == NULL) {
+ free(entry);
+ destroy_device_spec(*id);
+ *id = NULL;
+ error = ENOMEM;
+ } else {
+ entry->name = dup;
+ entry->device_spec = *id;
+ }
+
+ if (error == 0) {
+ dlist_t *item = dlist_new_item((void *)entry);
+ if (item == NULL) {
+ free(entry);
+ destroy_device_spec(*id);
+ *id = NULL;
+ error = ENOMEM;
+ } else {
+ _spec_cache =
+ dlist_append(item, _spec_cache, AT_HEAD);
+ }
+ }
+ }
+ }
+ } else {
+ *id = ((spec_cache_t *)item->obj)->device_spec;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: spec_includes_device(device_spec_t *spec,
+ * device_spec_t *device)
+ *
+ * INPUT: spec - pointer to a device_spec struct
+ * device - pointer to a device_spec struct
+ *
+ * RETURNS: boolean_t - B_TRUE if the device is included in the spec
+ * B_FALSE otherwise
+ *
+ * PURPOSE: Function which determines if the input device matches the
+ * input spec.
+ *
+ * If both specs are of the same type, the appropriate
+ * comparison function is called.
+ *
+ * If the two specs are of different types, no comparison
+ * is done and B_FALSE is returned.
+ */
+boolean_t
+spec_includes_device(
+ device_spec_t *spec,
+ device_spec_t *device)
+{
+ if ((spec->type == SPEC_TYPE_CTD) && (device->type == SPEC_TYPE_CTD)) {
+ return (ctd_spec_includes_device(spec, device));
+ } else if ((spec->type == SPEC_TYPE_RAW) &&
+ (device->type == SPEC_TYPE_RAW)) {
+ return (raw_spec_includes_device(spec, device));
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * FUNCTION: ctd_spec_includes_device(device_spec_t *spec,
+ * device_spec_t *device)
+ *
+ * INPUT: spec - pointer to a device_spec struct
+ * device - pointer to a device_spec struct
+ *
+ * RETURNS: boolean_t - B_TRUE if the device is included in the spec
+ * B_FALSE otherwise
+ *
+ * PURPOSE: Function which determines if the input CTD device spec
+ * matches the input CTD spec.
+ *
+ * The device_spec_t structs contain component "ids" for
+ * both the specification and the device.
+ *
+ * The device must match each of the ids in the spec that
+ * are specified.
+ *
+ * spec devices matched
+ * --------------------------------------------------------
+ * cX cX, cXtX, cXtXdX, cXtXdXsX, cXdX, cXdXsX
+ * cXtX cXtX, cXtXdX, cXtXdXsX
+ * cXtXdX cXtXdX, cXtXdXsX
+ * cXtXdXsX cXtXdXsX
+ * cXdX cXdX, cXdXsX
+ * cXdXsX cXdXsX
+ */
+static boolean_t
+ctd_spec_includes_device(
+ device_spec_t *spec,
+ device_spec_t *device)
+{
+ boolean_t match = B_FALSE;
+
+ if (spec->data.ctd->is_ide) {
+
+ /* valid IDE names are cX, cXdX, cXdXsX, no target */
+
+ if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) &&
+ (spec->data.ctd->lun != ID_UNSPECIFIED) &&
+ (spec->data.ctd->slice != ID_UNSPECIFIED)) {
+
+ match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) &&
+ (spec->data.ctd->lun == device->data.ctd->lun) &&
+ (spec->data.ctd->slice == device->data.ctd->slice);
+
+ } else if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) &&
+ (spec->data.ctd->lun != ID_UNSPECIFIED)) {
+
+ match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) &&
+ (spec->data.ctd->lun == device->data.ctd->lun);
+
+ } else if (spec->data.ctd->ctrl != ID_UNSPECIFIED) {
+
+ match = (spec->data.ctd->ctrl == device->data.ctd->ctrl);
+
+ }
+
+ } else {
+
+ /* valid names are cX, cXtX, cXtXdX, cXtXdXsX */
+
+ if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) &&
+ (spec->data.ctd->target != ID_UNSPECIFIED) &&
+ (spec->data.ctd->lun != ID_UNSPECIFIED) &&
+ (spec->data.ctd->slice != ID_UNSPECIFIED)) {
+
+ match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) &&
+ (spec->data.ctd->target == device->data.ctd->target) &&
+ (spec->data.ctd->lun == device->data.ctd->lun) &&
+ (spec->data.ctd->slice == device->data.ctd->slice);
+
+ } else if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) &&
+ (spec->data.ctd->target != ID_UNSPECIFIED) &&
+ (spec->data.ctd->lun != ID_UNSPECIFIED)) {
+
+ match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) &&
+ (spec->data.ctd->target == device->data.ctd->target) &&
+ (spec->data.ctd->lun == device->data.ctd->lun);
+
+ } else if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) &&
+ (spec->data.ctd->target != ID_UNSPECIFIED)) {
+
+ match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) &&
+ (spec->data.ctd->target == device->data.ctd->target);
+
+ } else if (spec->data.ctd->ctrl != ID_UNSPECIFIED) {
+
+ match = (spec->data.ctd->ctrl == device->data.ctd->ctrl);
+
+ }
+ }
+
+ oprintf(OUTPUT_DEBUG,
+ gettext("spec: c(%d) t(%d) d(%d) s(%d) "
+ "%s: c(%d) t(%d) d(%d) s(%d)\n"),
+ spec->data.ctd->ctrl, spec->data.ctd->target,
+ spec->data.ctd->lun, spec->data.ctd->slice,
+ (match ? gettext("includes") : gettext("does not include")),
+ device->data.ctd->ctrl, device->data.ctd->target,
+ device->data.ctd->lun, device->data.ctd->slice);
+
+ return (match);
+}
+
+/*
+ * FUNCTION: raw_spec_includes_device(device_spec_t *spec,
+ * device_spec_t *device)
+ *
+ * INPUT: spec - pointer to a device_spec struct
+ * device - pointer to a device_spec struct
+ *
+ * RETURNS: boolean_t - B_TRUE if the device is included in the spec
+ * B_FALSE otherwise
+ *
+ * PURPOSE: Function which determines if the input raw device spec
+ * matches the input spec.
+ *
+ * The device_spec_t raw elements are checked.
+ *
+ * If the spec's raw device name is exactly contained at the
+ * beginning of the device spec's raw name, then the function
+ * evaluates to true.
+ */
+static boolean_t
+raw_spec_includes_device(
+ device_spec_t *spec,
+ device_spec_t *device)
+{
+ return (strncasecmp(spec->data.raw,
+ device->data.raw, strlen(spec->data.raw)) == 0);
+}
+
+/*
+ * FUNCTION: compare_name_to_spec_cache_name(void *name, void *list_item)
+ *
+ * INPUT: name - opaque pointer to a char * device name
+ * list_item - opaque pointer to a spec_cache_t entry
+ *
+ * RETURNS: int - 0 - if request is the same as list_item->request
+ * !0 - otherwise
+ *
+ * PURPOSE: dlist_t helper which compares the input device name
+ * to the list_item's device name for equality.
+ *
+ * This function is the lookup mechanism for the device_spec
+ * associated with the name.
+ */
+static int
+compare_name_to_spec_cache_name(
+ void *name,
+ void *list_item)
+{
+ spec_cache_t *entry = (spec_cache_t *)list_item;
+
+ assert(name != NULL);
+ assert(entry != NULL);
+
+ return (string_case_compare((char *)name, entry->name));
+}
+
+/*
+ * FUNCTION: destroy_spec_cache_entry(void *entry)
+ *
+ * INPUT: entry - opaque pointer to a spec_cache_t
+ *
+ * RETURNS: nothing
+ *
+ * PURPOSE: Function which reclaims memory allocated to a
+ * spec_cache_t entry.
+ *
+ * Frees memory allocated to hold the CTD name and the
+ * corresponding device_spec_t.
+ */
+static void
+destroy_spec_cache_entry(
+ void *obj)
+{
+ spec_cache_t *entry = (spec_cache_t *)obj;
+
+ if (entry != NULL) {
+ free(entry->name);
+ destroy_device_spec(entry->device_spec);
+ free(entry);
+ }
+}
+
+/*
+ * FUNCTION: destroy_spec_cache()
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Function which destroys all entries in the device_spec
+ * cache.
+ */
+static int
+destroy_spec_cache()
+{
+ dlist_free_items(_spec_cache, destroy_spec_cache_entry);
+ _spec_cache = NULL;
+
+ return (0);
+}
+
+/*
+ * FUNCTION: get_device_access_name(devconfig_t *request,
+ * dm_descriptor_t desc, char **name)
+ *
+ * INPUT: request - a devconfig_t request
+ * desc - a dm_descriptor_t device handle
+ *
+ * OUTPUT: name - a char * pointer to hold the preferred name
+ *
+ * RETURNS: int - 0 - if request is the same as list_item->request
+ * !0 - otherwise
+ *
+ * PURPOSE: Utility function to determine which of the possible device
+ * names should be used to access a known available device.
+ *
+ * Devices handled are slices and disks.
+ *
+ * If the input device is a multipathed disk or slice, it
+ * can have several possible names. Determine which of the
+ * names should be used based on the input request's available
+ * or unavailable device specifications.
+ *
+ */
+int
+get_device_access_name(
+ devconfig_t *request,
+ dm_descriptor_t desc,
+ char **name)
+{
+ int error = 0;
+ boolean_t avail = B_FALSE;
+ dlist_t *aliases = NULL;
+
+ assert(desc != (dm_descriptor_t)0);
+
+ *name = NULL;
+
+ if ((error = get_display_name(desc, name)) != 0) {
+ return (error);
+ }
+
+ if (is_did_name(*name) == B_TRUE) {
+ oprintf(OUTPUT_DEBUG,
+ gettext("device DID name %s is preferred\n"),
+ *name);
+ return (0);
+ }
+
+ error = is_named_device_avail(request, *name, B_FALSE, &avail);
+ if (error != 0) {
+ return (error);
+ }
+
+ if (avail == B_TRUE) {
+ oprintf(OUTPUT_DEBUG,
+ gettext("device name %s is accessible\n"),
+ *name);
+ return (0);
+ }
+
+ /* search aliases for an 'available' name, prefer DID names */
+ if ((error = get_aliases(desc, &aliases)) == 0) {
+
+ dlist_t *iter = aliases;
+ char *availname = NULL;
+ char *didname = NULL;
+
+ for (; (iter != NULL) && (error == 0); iter = iter->next) {
+
+ char *alias = (char *)iter->obj;
+ error = is_named_device_avail(request, alias, B_FALSE, &avail);
+
+ if ((error == 0) && (avail == B_TRUE)) {
+ oprintf(OUTPUT_DEBUG,
+ gettext("device alias %s is accessible for %s\n"),
+ alias, *name);
+
+ availname = alias;
+
+ if (is_did_name(availname) == B_TRUE) {
+ didname = alias;
+ break;
+ }
+ }
+ }
+
+ if (error == 0) {
+ if (didname != NULL) {
+ *name = didname;
+ } else if (availname != NULL) {
+ *name = availname;
+ }
+ }
+ }
+
+ return (error);
+}
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_request.h b/usr/src/cmd/lvm/metassist/layout/layout_request.h
new file mode 100644
index 0000000000..0388e59752
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_request.h
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#ifndef _LAYOUT_REQUEST_H
+#define _LAYOUT_REQUEST_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "libdiskmgt.h"
+
+#include "volume_dlist.h"
+#include "volume_defaults.h"
+#include "volume_devconfig.h"
+
+/* XXX these are really in layout.c */
+extern int string_case_compare(char *str1, char *str2);
+extern int add_modified_disk(devconfig_t *request, dm_descriptor_t diskx);
+extern int add_to_hsp_list(dlist_t *devices);
+
+extern int release_request_caches();
+
+extern int set_request_diskset(char *disksset);
+extern char *get_request_diskset();
+extern void unset_request_diskset();
+
+extern int set_toplevel_request(devconfig_t *request);
+extern void unset_toplevel_request();
+
+extern int set_request_defaults(defaults_t *defaults);
+extern void unset_request_defaults();
+
+extern int get_device_access_name(
+ devconfig_t *request,
+ dm_descriptor_t desc,
+ char **name);
+
+/*
+ * get list of HBAs, disks or slices that are available
+ * to satisfy the given request
+ */
+extern int slice_is_available(
+ char *name,
+ devconfig_t *request,
+ boolean_t *bool);
+
+extern int disks_get_avail_slices(
+ devconfig_t *request,
+ dlist_t *disks,
+ dlist_t **slices);
+
+extern int select_hbas_with_n_disks(
+ devconfig_t *request,
+ dlist_t *hbas,
+ int mindisks,
+ dlist_t **selhbas,
+ dlist_t **seldisks);
+
+extern int hba_get_avail_disks_and_space(
+ devconfig_t *request,
+ dm_descriptor_t hba,
+ dlist_t **list,
+ uint64_t *space);
+
+/*
+ * get lists of HBAs and disks that are used by volumes
+ */
+extern int get_hbas_and_disks_used_by_volumes(
+ dlist_t *volumes,
+ dlist_t **hbas,
+ dlist_t **disks);
+
+extern int get_hbas_and_disks_used_by_volume(
+ devconfig_t *volume,
+ dlist_t **hbas,
+ dlist_t **disks);
+
+/*
+ * accessors to get user-settable device parameters,
+ * values come from either the request or the diskset
+ * or global defaults
+ */
+extern int get_stripe_min_comp(
+ devconfig_t *request,
+ uint16_t *val);
+
+extern int get_stripe_max_comp(
+ devconfig_t *request,
+ uint16_t *val);
+
+extern int get_stripe_interlace(
+ devconfig_t *request,
+ uint64_t *val);
+
+extern int get_mirror_read_strategy(
+ devconfig_t *request,
+ mirror_read_strategy_t *val);
+
+extern int get_mirror_write_strategy(
+ devconfig_t *request,
+ mirror_write_strategy_t *val);
+
+extern int get_mirror_pass(
+ devconfig_t *request,
+ uint16_t *val);
+
+extern int get_mirror_nsubs(
+ devconfig_t *request,
+ uint16_t *val);
+
+extern int get_volume_faultrecov(
+ devconfig_t *request,
+ boolean_t *val);
+
+extern int get_volume_redundancy_level(
+ devconfig_t *request,
+ uint16_t *val);
+
+extern int get_volume_npaths(
+ devconfig_t *request,
+ uint16_t *val);
+
+extern int get_default_hsp_name(
+ devconfig_t *req,
+ char **name);
+
+extern int get_disks_for_target(
+ char *name,
+ dlist_t **disks);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LAYOUT_REQUEST_H */
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_slice.c b/usr/src/cmd/lvm/metassist/layout/layout_slice.c
new file mode 100644
index 0000000000..e1792b2b94
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_slice.c
@@ -0,0 +1,2335 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <meta.h>
+
+#include "volume_string.h"
+
+#include "volume_devconfig.h"
+#include "volume_error.h"
+#include "volume_dlist.h"
+#include "volume_output.h"
+
+#include "layout_device_cache.h"
+#include "layout_device_util.h"
+#include "layout_discovery.h"
+#include "layout_dlist_util.h"
+#include "layout_messages.h"
+#include "layout_request.h"
+#include "layout_slice.h"
+
+#define _LAYOUT_SLICE_C
+
+static int pick_from_best_hba_and_disk(
+ dlist_t *list,
+ dlist_t *used,
+ dm_descriptor_t *chosen);
+
+static int slice_has_same_disk_geom(
+ dm_descriptor_t slice,
+ dlist_t *used,
+ boolean_t *bool);
+
+static int slice_on_unique_disk(
+ dm_descriptor_t slice,
+ dlist_t *used,
+ dlist_t *othervols,
+ boolean_t *bool);
+
+static int slice_on_unique_hba(
+ dm_descriptor_t slice,
+ dlist_t *used,
+ dlist_t *othervols,
+ boolean_t *bool);
+
+static int slice_on_similar_bus(
+ dm_descriptor_t slice,
+ dlist_t *used,
+ boolean_t *bool);
+
+static int slice_has_n_paths(
+ dm_descriptor_t slice,
+ uint16_t npaths,
+ boolean_t *bool);
+
+static int compare_modslice_names(
+ void *obj1,
+ void *obj2);
+
+static int compare_string_to_modslice_name(
+ void *str,
+ void *modslice);
+
+static int create_new_slice(
+ dm_descriptor_t oslice,
+ uint64_t nbytes,
+ boolean_t add_extra_cyl,
+ devconfig_t **nslice);
+
+static int create_modified_slice(
+ dm_descriptor_t oslice,
+ char *oname,
+ uint32_t oindex,
+ uint64_t ostart,
+ uint64_t osize,
+ uint64_t bps,
+ char *nname,
+ uint32_t nindex,
+ uint64_t nsize,
+ devconfig_t **nslice);
+
+/*
+ * list to track resized slices
+ */
+static dlist_t *_modified_slices = NULL;
+
+/*
+ * struct to track used slices and their disks...
+ */
+typedef struct {
+ char *slicename;
+ dm_descriptor_t disk;
+} usedslice_t;
+
+/*
+ * list to of usedslice_t to track slices that have been
+ * used for any reason.
+ */
+static dlist_t *_used_slices = NULL;
+
+static int add_used_slice_list_entry(char *slicename, dm_descriptor_t disk);
+static int compare_usedslice_name_to_string(void *obj1, void *obj2);
+static void free_used_slice(void *obj);
+
+/*
+ * list of slices reserved to be used for explicit
+ * volume requests
+ */
+static dlist_t *_rsvd_slices = NULL;
+
+/*
+ * list of slices needing to be removed (zeroed out) prior to
+ * applying any metassist modifications to the system.
+ */
+static dlist_t *_rmvd_slices = NULL;
+
+/*
+ * FUNCTION: choose_slice(
+ * uint64_t nbytes,
+ * uint16_t npaths,
+ * dlist_t *slices,
+ * dlist_t *used,
+ * dlist_t *used_hbas,
+ * dlist_t *used_disks,
+ * boolean_t unused_disk,
+ * boolean_t nbytes_is_min,
+ * boolean_t add_extra_cyl,
+ * devconfig_t **chosen)
+ *
+ * INPUT: nbytes - required size
+ * npaths - minimum required data paths
+ * *slices - slices from which to choose
+ * *used - slices used by the volume under construction
+ * *used_hbas - hbas used by other volumes relevant to
+ * the volume under construction
+ * *used_disks - disks used by other volumes relevant to
+ * the volume under construction
+ * unused_disk - if true, the chosen slice must be from an
+ * unused disk
+ * nbytes_is_min - if true, the chosen slice may be larger than
+ * nbytes.
+ * add_extra_cyl - passed to create_new_slice, see comment there.
+ * **chosen - pointer to hold the chosen slice
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Choosen a slice from the list of those available.
+ *
+ * Of those available, choose in order of preference:
+ *
+ * - one on a unique HBA and disk that is of the exact size
+ * - one on a unique HBA and disk that is of sufficient size
+ * - one on unique HBA that is of the exact size
+ * - one on unique HBA that is of sufficient size
+ * - one on unique disk that is of the exact size
+ * - one on unique disk that is of sufficient size
+ * - one on any HBA that is of exact size
+ * - one on any HBA that is of sufficient size
+ * - one on a unique HBA that is the largest size
+ * - one on a unique disk that is the largest size
+ * - one on any HBA that is the largest size
+ *
+ * The function scans the available slices and builds lists of
+ * those meeting the criteria above. After the scan is complete,
+ * the lists are examined in order, the first non-empty list is
+ * chosen. If there are several possibilities in the chosen list,
+ * see if it is possible select the slice from the least used HBA
+ * and/or disk.
+ *
+ * If nbytes_is_min is true, the returned slice will be
+ * at least nbytes in capacity.
+ *
+ * If unused_disk is true, the returned slice will be from
+ * a disk with no other known uses.
+ */
+int
+choose_slice(
+ uint64_t nbytes,
+ uint16_t npaths,
+ dlist_t *slices,
+ dlist_t *used,
+ dlist_t *used_hbas,
+ dlist_t *used_disks,
+ boolean_t unused_disk,
+ boolean_t nbytes_is_min,
+ boolean_t add_extra_cyl,
+ devconfig_t **chosen)
+{
+ dlist_t *iter = NULL;
+
+ dm_descriptor_t slice = NULL;
+ boolean_t resize = B_FALSE;
+ boolean_t verbose = (get_max_verbosity() == OUTPUT_VERBOSE);
+
+ int error = 0;
+
+ /*
+ * indexes into the list array:
+ * i -> unique controller 0 = yes, 1 = no
+ * j -> same bus type 0 = yes, 1 = no
+ * k -> unique disk 0 = yes, 1 = no
+ * l -> same disk geom 0 = yes, 1 = no
+ * m -> size 0 == exact, 1 = larger, 2 = any
+ */
+ int i, j, k, l, m;
+ dlist_t *list[2][2][2][2][3];
+
+ /* output string arrays for each array dimension and index */
+ char *uniqhba[2];
+ char *samebus[2];
+ char *uniqdisk[2];
+ char *samegeom[2];
+ char *sizes[3];
+
+ /* other output strings */
+ char *look_msg = NULL;
+ char *npaths_msg = NULL;
+ char *samegeom_msg = NULL;
+ char *samebus_msg = NULL;
+ char *uniqhba_msg = NULL;
+ char *uniqdisk_msg = NULL;
+ char *exact_msg = NULL;
+ char *larger_msg = NULL;
+ char *smaller_msg = NULL;
+ char *insuff_paths = NULL;
+ char *too_small = NULL;
+ char *useddisk_msg = NULL;
+
+ if (verbose == B_TRUE) {
+ /* only initialize the output strings if needed */
+
+ /* BEGIN CSTYLED */
+ look_msg = gettext(
+ "\tlooking at slice: %s (%s)\n");
+ npaths_msg = gettext(
+ "\t has the requested number of data paths (%d)\n");
+ samegeom_msg = gettext(
+ "\t has the same disk geometry relative to used slices\n");
+ samebus_msg = gettext(
+ "\t on a similar I/O bus/HBA relative to used slices\n");
+ uniqhba_msg = gettext(
+ "\t on a unique HBA relative to used slices\n");
+ uniqdisk_msg = gettext(
+ "\t on a unique disk relative to used slices\n");
+ exact_msg = gettext(
+ "\t the exact size necessary\n");
+ larger_msg = gettext(
+ "\t larger than necessary\n");
+ smaller_msg = gettext(
+ "\t smaller than necessary\n");
+ insuff_paths = gettext(
+ "\t rejected: not enough paths (%d requested)\n");
+ too_small = gettext(
+ "\t rejected: too small\n");
+ useddisk_msg = gettext(
+ "\t rejected: on a disk with other volume component(s)\n");
+
+ uniqhba[0] = gettext("unique HBA");
+ uniqhba[1] = gettext("non unique HBA");
+ samebus[0] = gettext("same bus type");
+ samebus[1] = gettext("different bus type");
+ uniqdisk[0] = gettext("unique disk");
+ uniqdisk[1] = gettext("non unique disk");
+ samegeom[0] = gettext("same geometry");
+ samegeom[1] = gettext("different geometry");
+ sizes[0] = gettext("an exact size slice");
+ sizes[1] = gettext("a larger slice");
+ sizes[2] = gettext("a smaller slice");
+
+ /* END CSTYLED */
+ }
+
+ /* init list array pointers */
+ (void) memset(list, 0, 2*2*2*2*3 * sizeof (dlist_t *));
+
+ for (iter = slices;
+ (iter != NULL) && (error == 0); iter = iter->next) {
+
+ dm_descriptor_t slice = (uintptr_t)iter->obj;
+ uint64_t snbytes = 0;
+ boolean_t uniqdisk = B_FALSE;
+ boolean_t uniqhba = B_FALSE;
+ boolean_t samegeom = B_FALSE;
+ boolean_t samebus = B_FALSE;
+ boolean_t paths = B_FALSE;
+ dlist_t *item = NULL;
+
+ ((error = slice_get_size(slice, &snbytes)) != 0) ||
+ (error = slice_has_n_paths(slice, npaths, &paths)) ||
+ (error = slice_on_unique_hba(slice, used, used_hbas, &uniqhba)) ||
+ (error = slice_on_unique_disk(slice, used, used_disks,
+ &uniqdisk)) ||
+ (error = slice_on_similar_bus(slice, used, &samebus)) ||
+ (error = slice_has_same_disk_geom(slice, used, &samegeom));
+ if (error != 0) {
+ continue;
+ }
+
+ if (verbose == B_TRUE) {
+ char *sname = NULL;
+ char *sizestr = NULL;
+ (void) get_display_name(slice, &sname);
+ if (bytes_to_sizestr(snbytes, &sizestr,
+ universal_units, B_FALSE) == 0) {
+ oprintf(OUTPUT_VERBOSE, look_msg, sname, sizestr);
+ free(sizestr);
+ }
+ }
+
+ if (npaths > 1) {
+ if (paths && verbose) {
+ /* specifically asked for more paths, ... */
+ oprintf(OUTPUT_VERBOSE, npaths_msg);
+ }
+ } else if (npaths == 1) {
+ /* every disk has at least 1 path */
+ paths = B_TRUE;
+ }
+
+ if (verbose == B_TRUE) {
+ if (uniqhba) {
+ oprintf(OUTPUT_VERBOSE, uniqhba_msg);
+ }
+ if (uniqdisk) {
+ oprintf(OUTPUT_VERBOSE, uniqdisk_msg);
+ }
+
+ if (used != NULL) {
+ if (samebus) {
+ oprintf(OUTPUT_VERBOSE, samebus_msg);
+ }
+ if (samegeom) {
+ oprintf(OUTPUT_VERBOSE, samegeom_msg);
+ }
+ }
+
+ if (snbytes > nbytes) {
+ oprintf(OUTPUT_VERBOSE, larger_msg);
+ } else if (snbytes == nbytes) {
+ oprintf(OUTPUT_VERBOSE, exact_msg);
+ } else {
+ oprintf(OUTPUT_VERBOSE, smaller_msg);
+ }
+ }
+
+ /* filter slices not meeting minimum criteria */
+ if (nbytes_is_min && (snbytes < nbytes)) {
+ /* not large enough */
+ if (verbose == B_TRUE) {
+ oprintf(OUTPUT_VERBOSE, too_small);
+ }
+ continue;
+ }
+
+ if (paths == B_FALSE) {
+ /* not connected thru enough paths */
+ if (verbose == B_TRUE) {
+ oprintf(OUTPUT_VERBOSE, insuff_paths, npaths);
+ }
+ continue;
+ }
+
+ if (uniqdisk != B_TRUE && unused_disk == TRUE) {
+ /* not on a unique disk */
+ if (verbose == B_TRUE) {
+ oprintf(OUTPUT_VERBOSE, useddisk_msg);
+ }
+ continue;
+ }
+
+ /* map slice properties into array indices */
+ i = (uniqhba ? 0 : 1);
+ j = (samebus ? 0 : 1);
+ k = (uniqdisk ? 0 : 1);
+ l = (samegeom ? 0 : 1);
+ m = (snbytes == nbytes ? 0 : (snbytes > nbytes ? 1 : 2));
+
+ /*
+ * insert slice into the list array using derived indices.
+ * NB: lists of slices larger than necessary are kept in
+ * ascending order (results in best fit, not worst fit)
+ */
+ if ((item = dlist_new_item((void*)slice)) == NULL) {
+ error = ENOMEM;
+ } else {
+ list[i][j][k][l][m] =
+ dlist_insert_ordered(
+ item,
+ list[i][j][k][l][m],
+ (m == 1 ? ASCENDING : DESCENDING),
+ compare_slice_sizes);
+ }
+ }
+
+ /*
+ * Select a slice from one of the lists.
+ *
+ * The list with the combination of lowest indices
+ * is the most preferred list... in rough order:
+ *
+ * one on a unique HBA and disk that is of the exact size
+ * one on a unique HBA and disk that is of sufficient size (resize)
+ * one on unique HBA that is of the exact size
+ * one on unique HBA that is of sufficient size (resize)
+ * one on unique disk that is of the exact size
+ * one on unique disk that is of sufficient size (resize)
+ * one on any HBA that is of exact size
+ * one on any HBA that is of sufficient size (resize)
+ * one on a unique HBA that is the largest size
+ * one on a unique disk that is the largest size
+ * one on any HBA that is the largest size
+ */
+ slice = NULL;
+
+ for (i = 0; i < 2; i++) {
+ for (j = 0; j < 2; j++) {
+ for (k = 0; k < 2; k++) {
+ for (l = 0; l < 2; l++) {
+ for (m = 0; m < 3; m++) {
+ if (list[i][j][k][l][m] != NULL) {
+
+ /* pick least used slice from this list */
+ error = pick_from_best_hba_and_disk(
+ list[i][j][k][l][m],
+ used, &slice);
+
+ resize = (m == 1);
+
+ /* terminate all loops */
+ goto stop;
+ }
+ }
+ }
+ }
+ }
+ }
+stop:
+
+ /*
+ * Slice chosen, is a resize necessary?
+ */
+ if ((error == 0) && (slice != NULL)) {
+
+ if (error == 0) {
+ if (verbose == B_TRUE) {
+ uint64_t snbytes = 0;
+ char *sname = NULL;
+ char *sizestr = NULL;
+
+ (void) get_display_name(slice, &sname);
+ (void) slice_get_size(slice, &snbytes);
+
+ if (bytes_to_sizestr(snbytes, &sizestr,
+ universal_units, B_FALSE) == 0) {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" selected %s (%s)\n"
+ " it is %s on a\n"
+ " %s (%s) and a\n"
+ " %s (%s)\n"),
+ sname, sizestr,
+ sizes[m],
+ uniqhba[i], samebus[j],
+ uniqdisk[k], samegeom[l]);
+ free(sizestr);
+ }
+ }
+
+ if (resize) {
+ if (verbose == B_TRUE) {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" it has excess space, "
+ "resizing...\n"));
+ }
+
+ error = create_new_slice(slice, nbytes, add_extra_cyl,
+ chosen);
+ if ((error == 0) && (*chosen != NULL) && verbose) {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" exactly resized\n"));
+ }
+ }
+
+ if (error == 0) {
+ /* either no resize was necessary or the resize failed */
+ if (*chosen == NULL) {
+ /*
+ * use the original slice as it is.
+ * Make a devconfig_t for it.
+ */
+ error = create_devconfig_for_slice(slice, chosen);
+ }
+ }
+ }
+ } else if (slice == NULL) {
+ oprintf(OUTPUT_DEBUG,
+ gettext(" no possible slice\n"));
+ }
+
+ for (i = 0; i < 2; i++) {
+ for (j = 0; j < 2; j++) {
+ for (k = 0; k < 2; k++) {
+ for (l = 0; l < 2; l++) {
+ for (m = 0; m < 3; m++) {
+ if (list[i][j][k][l][m] != NULL) {
+ dlist_free_items(list[i][j][k][l][m], NULL);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: create_devconfig_for_slice(dm_descriptor_t slice,
+ * devconfig_t **nslice)
+ *
+ * INPUT: slice - dm_descriptor_t handle to an existing slice
+ * nslice - devconfig_t pointer to hold the new slice
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Creates a devconfig_t struct representation of the input
+ * slice dm_descriptor.
+ */
+int
+create_devconfig_for_slice(
+ dm_descriptor_t slice,
+ devconfig_t **nslice)
+{
+ uint64_t nbytes = 0;
+ uint64_t nblks = 0;
+ uint64_t stblk = 0;
+ uint32_t index = 0;
+ char *name = NULL;
+ int error = 0;
+
+ ((error = get_display_name(slice, &name)) != 0) ||
+ (error = slice_get_size(slice, &nbytes)) ||
+ (error = slice_get_size_in_blocks(slice, &nblks)) ||
+ (error = slice_get_start_block(slice, &stblk)) ||
+ (error = slice_get_index(slice, &index));
+ if (error != 0) {
+ return (error);
+ }
+
+ ((error = new_devconfig(nslice, TYPE_SLICE)) != 0) ||
+ (error = devconfig_set_name(*nslice, name)) ||
+ (error = devconfig_set_slice_index(*nslice, index)) ||
+ (error = devconfig_set_slice_start_block(*nslice, stblk)) ||
+ (error = devconfig_set_size_in_blocks(*nslice, nblks)) ||
+ (error = devconfig_set_size(*nslice, nbytes));
+ if (error != 0) {
+ free_devconfig(*nslice);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: make_slicename_for_disk_and_index(dm_descriptor_t disk,
+ * uint32_t index, char **slicename)
+ *
+ * INPUT: disk - a dm_descriptor_t disk handle
+ * index - a slice index
+ *
+ * OUTPUT slicename - a char * pointer to hold the resulting slicename
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Utility function to manufacture a new slice name given the
+ * "parent" disk and an available slice index.
+ *
+ * The caller should free the returned name when done with it.
+ */
+static int
+make_slicename_for_disk_and_index(
+ dm_descriptor_t disk,
+ uint16_t index,
+ char **slicename)
+{
+ char *dname;
+ int error = 0;
+
+ if ((error = get_display_name(disk, &dname)) == 0) {
+ error = make_slicename_for_diskname_and_index(dname,
+ index, slicename);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: make_slicename_for_diskname_and_index(char *diskname,
+ * uint32_t index, char **slicename)
+ *
+ * INPUT: diskname - a char * disk name
+ * index - a slice index
+ *
+ * OUTPUT slicename - a char * pointer to hold the resulting slicename
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Utility function to manufacture a new slice name given the
+ * name of a disk and an available slice index.
+ *
+ * The caller should free the returned name when done with it.
+ */
+int
+make_slicename_for_diskname_and_index(
+ char *diskname,
+ uint16_t index,
+ char **slicename)
+{
+ int error = 0;
+ char buf[MAXNAMELEN+1];
+
+ (void) snprintf(buf, sizeof (buf), "%ss%u", diskname, index);
+ if ((*slicename = strdup(buf)) == NULL) {
+ *slicename = NULL;
+ error = ENOMEM;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: create_new_slice(dm_descriptor_t oslice, uint64_t nbytes,
+ * boolean_t add_extra_cyl, devconfig_t **nslice)
+ *
+ * INPUT: oslice - dm_descriptor_t handle to an existing slice
+ * nbytes - desired minimum size of the new slice
+ * add_extra_cyl - boolean indicating whether the resized slice
+ * needs to be oversized by 1 cylinder to account for
+ * interlace rounding done for stripe components.
+ * nslice - devconfig_t pointer to hold the new slice
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Creates a new slice object using space from the input slice.
+ *
+ * If there is an open slice slot in the disk VTOC, it will be
+ * reserved for the new slice. Space for the new slice will be
+ * taken from the original slice.
+ *
+ * If there is no open slice slot, the original slice will be
+ * returned as the usable new slice.
+ *
+ * The new slice will be of at least 'nbytes' bytes and possibly
+ * larger due to sector and cylinder boundary alignment.
+ *
+ * For EFI labeled disks, nbytes is rounded up to the next block
+ * boundary.
+ *
+ * For VTOC labeled disks, nbytes is rounded up to the next
+ * cylinder boundary.
+ *
+ * Additionally, if add_extra_cyl is true, the new slice will be
+ * made 1 cylinder larger than necessary. This accounts for the
+ * interlace rounding done within libmeta when computing the
+ * usable size of stripe components on disks with VTOC labels.
+ * Rounding the size up to the next cylinder boundary is not
+ * sufficient because libmeta will round this size down to an
+ * integral multiple of the stripe interlace and then round that
+ * result down to a cylinder boundary. This makes the usable
+ * size of the slice one cylinder smaller and possibly less than
+ * nbytes. Adding an extra cylinder ensures the usable size is
+ * greater than nbytes despite the rounding.
+ *
+ * If the resize is successful a pointer to the devconfig_t
+ * representing the new slice will be returned in "newslice".
+ *
+ * If the resize cannot be done, the newslice pointer will
+ * be NULL.
+ */
+static int
+create_new_slice(
+ dm_descriptor_t oslice,
+ uint64_t nbytes,
+ boolean_t add_extra_cyl,
+ devconfig_t **nslice)
+{
+ dm_descriptor_t odisk = NULL;
+ boolean_t efi = B_FALSE;
+
+ char *oname = NULL;
+ uint64_t osize = 0; /* orig size (bytes) */
+ uint64_t ostart = 0; /* orig start (byte) */
+ uint64_t ostblk = 0; /* orig start (blk) */
+ uint64_t nsize = 0; /* new size (bytes) */
+ uint64_t bytes_per_sect = 0;
+
+ uint32_t oindex = 0;
+ uint32_t nindex = oindex;
+
+ int error = 0;
+
+ *nslice = NULL;
+
+ ((error = slice_get_disk(oslice, &odisk)) != 0) ||
+ (error = slice_get_index(oslice, &oindex));
+ if (error != 0) {
+ return (error);
+ }
+
+ /* find an unused slice number, default to oindex */
+ nindex = oindex;
+ if ((error = disk_get_available_slice_index(odisk, &nindex)) != 0) {
+ return (error);
+ }
+
+ ((error = get_display_name(oslice, &oname)) != 0) ||
+ (error = slice_get_size(oslice, &osize)) ||
+ (error = slice_get_start(oslice, &ostart)) ||
+ (error = slice_get_start_block(oslice, &ostblk)) ||
+ (error = disk_get_is_efi(odisk, &efi)) ||
+ (error = disk_get_blocksize(odisk, &bytes_per_sect));
+ if (error != 0) {
+ return (error);
+ }
+
+ if (efi) {
+
+ /* EFI: round size to an integral number of blocks (sectors) */
+ nsize = bytes_per_sect *
+ ((nbytes + (bytes_per_sect - 1)) / bytes_per_sect);
+
+ oprintf(OUTPUT_DEBUG,
+ gettext(" "
+ "rounded up to %10.2f blocks\n"),
+ (double)(nsize/bytes_per_sect));
+
+ } else {
+
+ /* VTOC: round size to an integral number of cylinders */
+ uint64_t nhead = 0;
+ uint64_t nsect = 0;
+ uint64_t ncyls = 0;
+
+ ((error = disk_get_ncylinders(odisk, &ncyls)) != 0) ||
+ (error = disk_get_nheads(odisk, &nhead)) ||
+ (error = disk_get_nsectors(odisk, &nsect));
+ if (error == 0) {
+ uint64_t bytes_per_cyl = nhead * nsect * bytes_per_sect;
+ nsize = bytes_per_cyl *
+ ((nbytes + (bytes_per_cyl - 1)) / bytes_per_cyl);
+
+ if (add_extra_cyl == TRUE) {
+ nsize += bytes_per_cyl;
+ }
+
+ oprintf(OUTPUT_DEBUG,
+ gettext(" "
+ "rounded VTOC slice to %10.2f cylinders "
+ "(out of %llu)\n"),
+ (double)(nsize/bytes_per_cyl), ncyls);
+ }
+ }
+
+ /* is sufficient space still available? */
+ if (error == 0) {
+ if (osize == nsize) {
+ /* use existing slice as is */
+ ((error = create_devconfig_for_slice(oslice, nslice)) != 0) ||
+ (error = disk_reserve_index(odisk, (uint16_t)nindex));
+ } else if (osize > nsize) {
+
+ if (nindex == oindex) {
+ /* no more slices, resize existing slice */
+ ((error = create_devconfig_for_slice(oslice,
+ nslice)) != 0) ||
+ (error = devconfig_set_size(*nslice, nsize)) ||
+ (error = devconfig_set_size_in_blocks(*nslice,
+ nsize/bytes_per_sect));
+ (error = disk_reserve_index(odisk, (uint16_t)nindex));
+
+ } else {
+ /* make a new slice */
+ char *nname = NULL;
+
+ ((error = make_slicename_for_disk_and_index(odisk,
+ nindex, &nname)) != 0) ||
+ (error = create_modified_slice(oslice, oname, oindex,
+ ostart, osize, bytes_per_sect, nname, nindex, nsize,
+ nslice)) ||
+ /* mark the new slice's index as used */
+ (error = disk_reserve_index(odisk, (uint16_t)nindex));
+
+ if ((error != 0) && (*nslice == NULL)) {
+ free(nname);
+ }
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: create_modified_slice(dm_descriptor_t oslice, char *oname,
+ * uint32_t oindex, uint64_t ostart, uint64_t osize,
+ * uint64_t bytes_per_sect, uint64_t nsize,
+ * char *nname, uint32_t nindex, devconfig_t **nslice)
+ *
+ * INPUT: oslice - dm_descriptor_t handle for the original slice
+ * oname - existing source slice name
+ * oindex - existing source slice VTOC index
+ * ostart - existing source slice start byte
+ * osize - existing source slice size in bytes
+ * bytes_per_sect - bytes per block (sector) for the disk
+ * nname - new slice name
+ * nindex - new slice VTOC index
+ * nsize - new slice size in bytes (cylinder and block aligned)
+ *
+ * SIDEEFFECTS: updates the module private list of modified slices
+ *
+ * OUTPUT: nslice - pointer to a devconfig_t to hold the new slice
+ *
+ * PURPOSE: create a new VTOC slice by taking space from an
+ * existing slice.
+ *
+ * The input size for the new slice is expected to be
+ * cylinder aligned.
+ */
+static int
+create_modified_slice(
+ dm_descriptor_t oslice,
+ char *oname,
+ uint32_t oindex,
+ uint64_t ostart,
+ uint64_t osize,
+ uint64_t bytes_per_sect,
+ char *nname,
+ uint32_t nindex,
+ uint64_t nsize,
+ devconfig_t **nslice)
+{
+ int error = 0;
+
+ /* compute start sector and size in sectors for the new slice */
+
+ /* subtract nsize from original slice to get starting byte */
+ uint64_t nstart = (ostart + osize) - nsize;
+
+ /* convert starting byte to a sector */
+ uint64_t nstblk = (uint64_t)(nstart / bytes_per_sect);
+
+ /* convert nsize to an integral number of blocks (sectors) */
+ uint64_t nblks = (uint64_t)(nsize / bytes_per_sect);
+
+ /* create a modified slice record for the new slice */
+ error = assemble_modified_slice(oslice, nname, nindex,
+ nstblk, nblks, nsize, nslice);
+ if (error != 0) {
+ free(nname);
+ return (error);
+ }
+
+ /* update the existing source slice's new size */
+ osize = osize - nsize;
+ (void) slice_set_size(oslice, osize);
+
+ /* update/create the modified slice record gfor the source slice */
+ error = assemble_modified_slice((dm_descriptor_t)0,
+ oname, oindex, (uint64_t)(ostart / bytes_per_sect),
+ (uint64_t)(osize / bytes_per_sect),
+ osize, NULL);
+
+ return (error);
+}
+
+/*
+ * FUNCTION: assemble_modified_slice(dm_descriptor_t src_slice,
+ * char *mod_name, uint32_t mod_index,
+ * uint64_t mod_stblk, uint64_t mod_nblks,
+ * uint64_t mod_size, devconfig_t **modslice)
+ *
+ * INPUT: src_slice - dm_descriptor_t handle of the slice space
+ * was taken from to create the modified slice
+ * mod_name - name of the modified slice
+ * mod_index - name of the modified slice
+ * mod_stblk - start block of the modified slice
+ * mod_nblks - size in blocks of the modified slice
+ * mod_size - size in bytes of the modified slice
+ *
+ * OUTPUT: mod_slice - if non-NULL, will be populated with a
+ * devconfig_t representing the modified slice.
+ *
+ * SIDEEFFECTS: adds or updates an entry in the modified slice list
+ * tracking the slices that have been explicitly modified
+ * by the layout code.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Utility function to which updates or creates a devconfig_t
+ * representing a slice that needs to be modified.
+ *
+ * If a modified slice record does not exist for the named
+ * slice, a new devconfig_t struct is allocated and added
+ * to the modified slice list.
+ *
+ * The existing or created devconfig_t struct is updated with
+ * the input values.
+ *
+ * The information about the slices in the modified slice list
+ * will eventually be handed to fmthard.
+ */
+int
+assemble_modified_slice(
+ dm_descriptor_t src_slice,
+ char *mod_name,
+ uint32_t mod_index,
+ uint64_t mod_stblk,
+ uint64_t mod_nblks,
+ uint64_t mod_size,
+ devconfig_t **mod_slice)
+{
+ devconfig_t *slice = NULL;
+ modslice_t *mstp = NULL;
+ dlist_t *item = NULL;
+ int error = 0;
+
+ /* see if the slice has been modified before */
+ if ((item = dlist_find(_modified_slices, mod_name,
+ compare_string_to_modslice_name)) != NULL) {
+
+ /* yes, update the resize count and attributes */
+ mstp = (modslice_t *)item->obj;
+ slice = mstp->slice_devcfg;
+
+ mstp->times_modified += 1;
+ mstp->src_slice_desc = src_slice;
+
+ ((error = devconfig_set_slice_start_block(slice,
+ mod_stblk)) != 0) ||
+ (error = devconfig_set_size(slice, mod_size)) ||
+ (error = devconfig_set_size_in_blocks(slice, mod_nblks));
+
+ } else {
+
+ /* no, first modification... */
+ /* create a devconfig_t representing the new slice */
+ ((error = new_devconfig(&slice, TYPE_SLICE)) != 0) ||
+ (error = devconfig_set_name(slice, mod_name)) ||
+ (error = devconfig_set_slice_index(slice, mod_index)) ||
+ (error = devconfig_set_slice_start_block(slice, mod_stblk)) ||
+ (error = devconfig_set_size_in_blocks(slice, mod_nblks)) ||
+ (error = devconfig_set_size(slice, mod_size));
+ if (error == 0) {
+ /* add to list of modified slices */
+ if ((mstp = (modslice_t *)
+ calloc(1, sizeof (modslice_t))) != NULL) {
+
+ /* count # of times source slice has been modified */
+ if (src_slice != (dm_descriptor_t)0) {
+ mstp->times_modified = 0;
+ } else {
+ mstp->times_modified = 1;
+ }
+ mstp->src_slice_desc = src_slice;
+ mstp->slice_devcfg = slice;
+
+ if ((item = dlist_new_item(mstp)) != NULL) {
+ _modified_slices =
+ dlist_insert_ordered(
+ item,
+ _modified_slices,
+ ASCENDING,
+ compare_modslice_names);
+ } else {
+ error = ENOMEM;
+ }
+ } else {
+ error = ENOMEM;
+ }
+ }
+
+ if (error != 0) {
+ free_devconfig(mstp);
+ free_devconfig(slice);
+ }
+ }
+
+ if (error == 0) {
+ oprintf(OUTPUT_DEBUG,
+ " "
+ "modified %s (start blk: %9llu, nblks: %9llu)\n",
+ mod_name, mod_stblk, mod_nblks);
+
+ /* return devconfig_t for modified slice */
+ if (mod_slice != NULL) {
+ *mod_slice = slice;
+ mstp->volume_component = B_TRUE;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: dlist_t *get_modified_slices()
+ *
+ * RETURNS: pointer to the list of modslice_t structs representing
+ * modified slices
+ *
+ * PURPOSE: public accessor to the list of slices modified while
+ * processing a request.
+ */
+dlist_t *
+get_modified_slices()
+{
+ return (_modified_slices);
+}
+
+/*
+ * FUNCTION: free_modslice_object(void *obj)
+ *
+ * INPUT: obj - opaque pointer
+ *
+ * PURPOSE: Frees memory associated with a modslice_t struct.
+ */
+static void
+free_modslice_object(
+ void *obj)
+{
+ assert(obj != (modslice_t *)NULL);
+
+ if (((modslice_t *)obj)->slice_devcfg != NULL) {
+ if (((modslice_t *)obj)->volume_component != B_TRUE) {
+ free_devconfig(((modslice_t *)obj)->slice_devcfg);
+ }
+ }
+
+ free(obj);
+}
+
+/*
+ * FUNCTION: void release_modified_slices()
+ *
+ * INPUT: none -
+ * OUTPUT: none -
+ *
+ * PURPOSE: cleanup the module global list of slices modified
+ * while processing a request.
+ */
+int
+release_modified_slices()
+{
+ dlist_free_items(_modified_slices, free_modslice_object);
+ _modified_slices = NULL;
+
+ return (0);
+}
+
+/*
+ * FUNCTION: destroy_new_slice(devconfig_t *dev)
+ *
+ * INPUT: dev - a devconfig_t pointer to a slice object
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Undoes slice creation done by create_new_slice():
+ *
+ * release index
+ * remove from used_slices
+ * remove from modified_slices
+ * return space to source slice
+ * free memory
+ */
+int
+destroy_new_slice(
+ devconfig_t *dev)
+{
+ dm_descriptor_t disk = NULL;
+ uint64_t size = 0;
+ uint16_t index = 0;
+ modslice_t *modified = NULL;
+ dlist_t *item = NULL;
+ char *name = NULL;
+ int error = 0;
+
+ ((error = devconfig_get_name(dev, &name)) != 0) ||
+ (error = devconfig_get_slice_index(dev, &index)) ||
+ (error = devconfig_get_size(dev, &size)) ||
+ (error = get_disk_for_named_slice(name, &disk)) ||
+ (error = disk_release_index(disk, index)) ||
+ (error = remove_used_slice_by_name(name));
+ if (error != 0) {
+ return (error);
+ }
+
+ /* remove from the modified_slices list */
+ _modified_slices =
+ dlist_remove_equivalent_item(
+ _modified_slices, name,
+ compare_string_to_modslice_name, &item);
+
+ if (item != NULL) {
+ modified = (modslice_t *)item->obj;
+ free((void*) item);
+ }
+
+ /* space from an existing slice? if so reclaim it. */
+ if (modified != NULL) {
+
+ dm_descriptor_t src = modified->src_slice_desc;
+ char *srcname = NULL;
+ dlist_t *srcitem = NULL;
+
+ if (src != (dm_descriptor_t)0) {
+ if ((error = get_display_name(src, &srcname)) == 0) {
+ srcitem =
+ dlist_find(
+ _modified_slices,
+ srcname,
+ compare_string_to_modslice_name);
+ }
+ }
+
+ if ((error == 0) && (srcitem != NULL)) {
+
+ modslice_t *source = (modslice_t *)srcitem->obj;
+ devconfig_t *srcdevcfg = NULL;
+ uint64_t srcsize = NULL;
+ uint64_t srcsizeblks = NULL;
+ uint64_t inblks = NULL;
+
+ srcdevcfg = source->slice_devcfg;
+ source->times_modified -= 1;
+
+ ((error = devconfig_get_size(srcdevcfg, &srcsize)) != 0) ||
+ (error = devconfig_set_size(srcdevcfg, srcsize + size)) ||
+ (error = slice_set_size(src, srcsize + size)) ||
+ (error = slice_get_size_in_blocks(src, &srcsizeblks)) ||
+ (error = devconfig_get_size_in_blocks(srcdevcfg, &inblks));
+ (error = devconfig_set_size_in_blocks(srcdevcfg, srcsizeblks));
+
+ if (error == 0) {
+
+ /* was only modification undone? */
+ if (source->times_modified == 0) {
+
+ _modified_slices =
+ dlist_remove_equivalent_item(
+ _modified_slices, srcname,
+ compare_string_to_modslice_name,
+ &srcitem);
+
+ free_modslice_object((modslice_t *)srcitem->obj);
+ free((void *)srcitem);
+ }
+ }
+ }
+
+ free_modslice_object(modified);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: pick_from_best_hba_and_disk(dlist_t *slices,
+ * dlist_t *used, dm_descriptor_t *chosen)
+ *
+ * INPUT: slices - a dlist_t poitner to a list of slices
+ * used - a dlist_t pointer to a list of used slices
+ * chosen - a dm_descriptor_t pointer to hold the result
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Examines the input list of slices and chooses the one
+ * that is on the least used HBA and disk.
+ *
+ * HBA and disk usage is determined by examining the input
+ * list of used slices and counting the number of slices
+ * each HBA and disk contributes.
+ *
+ * The HBA which contributes the fewest is selected, and
+ * then the disk on that HBA which contributes the fewest
+ * is selected.
+ *
+ * The largest slice from that disk is then returned.
+ */
+static int
+pick_from_best_hba_and_disk(
+ dlist_t *slices,
+ dlist_t *used,
+ dm_descriptor_t *chosen)
+{
+ dlist_t *iter = NULL;
+ dlist_t *iter1 = NULL;
+ dlist_t *iter2 = NULL;
+ dlist_t *item = NULL;
+
+ dlist_t *used_slice_hbas = NULL;
+
+ int maxuses = 128;
+ int maxslices = VTOC_SIZE; /* meta.h */
+
+ int i = 0;
+ int error = 0;
+
+ /*
+ * allocate an array to hold lists of slices grouped by
+ * HBA contribution... the list indexed by N is the list
+ * of slices that are on HBAs contributing N slices
+ */
+ dlist_t **prefhbas = (dlist_t **)calloc(maxuses, sizeof (dlist_t *));
+
+ /*
+ * allocate an array to hold lists of slices grouped by
+ * disk contribution... the list indexed by N is the list
+ * of slices that are on disks contributing N slices
+ */
+ dlist_t **prefdisks = (dlist_t **)calloc(maxslices, sizeof (dlist_t *));
+
+ *chosen = (dm_descriptor_t)0;
+
+ if (prefhbas == NULL || prefdisks == NULL) {
+ free(prefhbas);
+ free(prefdisks);
+ return (ENOMEM);
+ }
+
+ /*
+ * precompute the used slices' lists of HBAS: iterate the list
+ * of used slices and determine the HBA(s) each is connected thru.
+ * construct a list of lists containing the HBAs.
+ */
+ for (iter = used;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ devconfig_t *uslice = (devconfig_t *)iter->obj;
+ dm_descriptor_t udisk = NULL;
+ char *uname = NULL;
+ dlist_t *uhbas = NULL;
+
+ /* need to use disk to get to HBAs because */
+ /* the slice doesn't exist yet */
+ ((error = devconfig_get_name(uslice, &uname)) != 0) ||
+ (error = get_disk_for_named_slice(uname, &udisk)) ||
+ (error = disk_get_hbas(udisk, &uhbas));
+ if (error == 0) {
+ if ((item = dlist_new_item((void *)uhbas)) == NULL) {
+ error = ENOMEM;
+ } else {
+ used_slice_hbas = dlist_append(
+ item, used_slice_hbas, AT_HEAD);
+ }
+ }
+ }
+
+ /*
+ * iterate the list of chosen slices and for each,
+ * determine how many other slices from its HBA(s)
+ * are already being used...
+ *
+ * iter steps thru the list of slices
+ * iter1 steps thru each of the slice's HBAs
+ * iter2 steps thru the precomputed list of used slice's HBAs
+ * dlist_contains then searches each used slice's HBAs
+ * to see if it contains iter1's HBA
+ *
+ * If it does, increment the count for that HBA.
+ */
+ for (iter = slices;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ dm_descriptor_t slice = (uintptr_t)iter->obj;
+ dlist_t *hbas = NULL;
+ int n = 0; /* # slices each HBA contributes */
+
+ if ((error = slice_get_hbas(slice, &hbas)) != 0) {
+ continue;
+ }
+
+ for (iter1 = hbas; iter1 != NULL; iter1 = iter1->next) {
+ for (iter2 = used_slice_hbas; iter2 != NULL;
+ iter2 = iter2->next) {
+
+ dlist_t *uhbas = (dlist_t *)iter2->obj;
+ if (dlist_contains(uhbas, iter1->obj,
+ compare_descriptor_names) == B_TRUE) {
+ n++;
+ }
+ }
+ }
+
+ dlist_free_items(hbas, NULL);
+
+ /* group slices from HBAs contributing more than maxuses */
+ if (n >= maxuses) {
+ n = maxuses - 1;
+ }
+
+ /* add slice to list in descending size order */
+ if ((item = dlist_new_item((void*)slice)) == NULL) {
+ error = ENOMEM;
+ } else {
+ prefhbas[n] =
+ dlist_insert_ordered(
+ item,
+ prefhbas[n],
+ DESCENDING,
+ compare_slice_sizes);
+ }
+ }
+
+ /* free list of lists of used slices HBAs */
+ for (iter = used_slice_hbas; iter != NULL; iter = iter->next) {
+ dlist_free_items((dlist_t *)iter->obj, NULL);
+ }
+ dlist_free_items(used_slice_hbas, NULL);
+
+ /*
+ * Select the list of slices that are on the HBA(s) contributing
+ * the fewest slices... iterate these slices and for each, detemmine
+ * how many other slices from its disk are already being used...
+ */
+ for (i = 0; (i < maxuses) && (error == 0); i++) {
+
+ for (iter = (dlist_t *)prefhbas[i];
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ dm_descriptor_t slice = (uintptr_t)iter->obj;
+ dm_descriptor_t disk;
+ int n = 0;
+
+ (void) slice_get_disk(slice, &disk);
+
+ /*
+ * count how many slices this slice's disk is contributing
+ * by comparing it to the list of used slices
+ */
+ for (iter1 = _used_slices; iter1 != NULL; iter1 = iter1->next) {
+ usedslice_t *used = (usedslice_t *)iter1->obj;
+ if (compare_descriptors(
+ (void *)disk, (void *)used->disk) == 0) {
+ n++;
+ }
+ }
+
+ /* add slice to list in descending size order */
+ if ((item = dlist_new_item((void *)slice)) == NULL) {
+ error = ENOMEM;
+ } else {
+ prefdisks[n] =
+ dlist_insert_ordered(
+ item,
+ prefdisks[n],
+ DESCENDING,
+ compare_slice_sizes);
+ }
+ }
+ }
+
+ if (error == 0) {
+ /* select largest slice from least used disk */
+ for (i = 0; (i < maxslices) && (*chosen == NULL); i++) {
+ if (prefdisks[i] != NULL) {
+ *chosen = (uintptr_t)prefdisks[i]->obj;
+ }
+ }
+ }
+
+ for (i = 0; i < maxuses; i++) {
+ dlist_free_items(prefhbas[i], NULL);
+ }
+ for (i = 0; i < maxslices; i++) {
+ dlist_free_items(prefdisks[i], NULL);
+ }
+
+ free((void*)prefhbas);
+ free((void*)prefdisks);
+
+ return (error);
+}
+
+/*
+ * FUNCTION: slice_on_unique_hba(dm_descriptor_t slice,
+ * dlist_t *used, dlist_t *used_hbas,
+ * boolean_t *unique)
+ *
+ * INPUT: slice - a dm_descriptor_t handle for the slice of interest
+ * used - a dlist_t pointer to a list of used slices
+ * used_hbas - a dlist_t pointer to a list of used_hbas
+ * unique - a boolean_t pointer to hold the result
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Determines if the input slice is connected thru the same HBA
+ * as a slice in the used list.
+ *
+ * Also checks to see if the input slice is connected thru any
+ * HBA in the used_hbas list.
+ *
+ * If the slice is found to be on a unique HBA, bool is set
+ * to B_TRUE, B_FALSE otherwise.
+ */
+static int
+slice_on_unique_hba(
+ dm_descriptor_t slice,
+ dlist_t *used,
+ dlist_t *used_hbas,
+ boolean_t *unique)
+{
+ dlist_t *iter = NULL;
+ dlist_t *iter1 = NULL;
+
+ dlist_t *hbas = NULL;
+
+ int error = 0;
+
+ *unique = B_TRUE;
+
+ if ((error = slice_get_hbas(slice, &hbas)) != 0) {
+ return (error);
+ }
+
+ /*
+ * check to see if any of slice's HBAs is the same
+ * as the HBA for any of the used
+ */
+ for (iter = used;
+ (iter != NULL) && (*unique == B_TRUE) && (error == 0);
+ iter = iter->next) {
+
+ devconfig_t *dev = (devconfig_t *)iter->obj;
+ if (devconfig_isA(dev, TYPE_SLICE)) {
+
+ dm_descriptor_t odisk = NULL;
+ char *oname = NULL;
+ dlist_t *ohbas = NULL;
+
+ /* get HBAs for other slice using its disk */
+ /* because the slice doesn't exist yet. */
+ ((error = devconfig_get_name(dev, &oname)) != 0) ||
+ (error = get_disk_for_named_slice(oname, &odisk)) ||
+ (error = disk_get_hbas(odisk, &ohbas));
+
+ /* any HBA overlap? */
+ for (iter1 = hbas;
+ (iter1 != NULL) && (*unique == B_TRUE) && (error == 0);
+ iter1 = iter1->next) {
+
+ if (dlist_contains(ohbas, iter1->obj,
+ compare_descriptor_names) == B_TRUE) {
+ *unique = B_FALSE;
+ }
+ }
+ dlist_free_items(ohbas, NULL);
+ }
+ }
+
+ /*
+ * check to see if any of slice's HBAs is the contained
+ * in the list of used hbas
+ */
+ for (iter = hbas;
+ (iter != NULL) && (*unique == B_TRUE) && (error == 0);
+ iter = iter->next) {
+ if (dlist_contains(used_hbas,
+ iter->obj, compare_descriptor_names) == B_TRUE) {
+ *unique = B_FALSE;
+ }
+ }
+
+ dlist_free_items(hbas, NULL);
+
+ return (error);
+}
+
+/*
+ * FUNCTION: slice_on_unique_disk(dm_descriptor_t slice,
+ * dlist_t *used, dlist_t *used_disks,
+ * boolean_t *unique)
+ *
+ * INPUT: slice - a dm_descriptor_t handle for the slice of interest
+ * used - a dlist_t pointer to a list of used slices
+ * othervols - a dlist_t pointer to a list of other volumes
+ * bool - a boolean_t pointer to hold the result
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Determines if the input slice is on a drive that is not
+ * part of any volume in the othervols list, or on the same
+ * drive as any slice in the used list.
+ *
+ * If the slice is found to be on a unique disk, bool is set
+ * to B_TRUE, B_FALSE otherwise.
+ */
+static int
+slice_on_unique_disk(
+ dm_descriptor_t slice,
+ dlist_t *used,
+ dlist_t *used_disks,
+ boolean_t *unique)
+{
+ dm_descriptor_t disk = NULL;
+ dlist_t *iter = NULL;
+ int error = 0;
+
+ *unique = B_TRUE;
+
+ if ((error = slice_get_disk(slice, &disk)) != 0) {
+ return (error);
+ }
+
+ /*
+ * check to see if this disk is the same as the
+ * disk for any of the used
+ */
+ for (iter = used;
+ (iter != NULL) && (*unique == B_TRUE) && (error == 0);
+ iter = iter->next) {
+
+ devconfig_t *dev = (devconfig_t *)iter->obj;
+
+ if (devconfig_isA(dev, TYPE_SLICE)) {
+
+ /* get disk for otherslice */
+ dm_descriptor_t odisk = NULL;
+ char *oname = NULL;
+
+ ((error = devconfig_get_name(dev, &oname)) != 0) ||
+ (error = get_disk_for_named_slice(oname, &odisk));
+
+ if ((error == 0) &&
+ (compare_descriptor_names(
+ (void*)disk, (void*)odisk) == 0)) {
+ /* origslice is on same disk, stop */
+ *unique = B_FALSE;
+ }
+ }
+ }
+
+ /* check disk against the used disks */
+ if ((error == 0) && (*unique == B_TRUE) &&
+ dlist_contains(used_disks, (void *)disk,
+ compare_descriptor_names) == B_TRUE) {
+ *unique = B_FALSE;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: slice_has_same_disk_geom(dm_descriptor_t slice,
+ * dlist_t *used, boolean_t *has_same_geom)
+ *
+ * INPUT: slice - a dm_descriptor_t handle for the slice of interest
+ * used - a dlist_t pointer to a list of used slices
+ * bool - a boolean_t pointer to hold the result
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Determines if the input slice is on a drive with similar
+ * hardware geometry as the slices in the used list.
+ *
+ * If the slice is found to be on a disk with similar geometry,
+ * bool is set to B_TRUE, B_FALSE otherwise.
+ *
+ * The comparison is based on the available disk geometry
+ * information which may not be relevant or accurate for
+ * EFI labeled disks, so the disk drive type needs to be
+ * checked as well.
+ */
+static int
+slice_has_same_disk_geom(
+ dm_descriptor_t slice,
+ dlist_t *used,
+ boolean_t *has_same_geom)
+{
+ dm_descriptor_t disk = NULL;
+ boolean_t efi = B_FALSE;
+ uint64_t bsize = 0;
+ uint64_t ncyls = 0;
+ uint64_t nsects = 0;
+ uint64_t nheads = 0;
+ dlist_t *iter = NULL;
+ int error = 0;
+
+ *has_same_geom = B_TRUE;
+
+ ((error = slice_get_disk(slice, &disk)) != 0) ||
+ (error = disk_get_is_efi(disk, &efi)) ||
+ (error = disk_get_blocksize(disk, &bsize));
+
+ if ((error == 0) && (efi == B_FALSE)) {
+ ((error = disk_get_ncylinders(disk, &ncyls)) != 0) ||
+ (error = disk_get_nheads(disk, &nheads)) ||
+ (error = disk_get_nsectors(disk, &nsects));
+ }
+
+ if (error != 0) {
+ return (error);
+ }
+
+ /*
+ * check to see if slice's disk has the same geometry
+ * as the disks for the slices in the used list
+ */
+ for (iter = used;
+ (iter != NULL) && (*has_same_geom == B_TRUE) && (error = 0);
+ iter = iter->next) {
+
+ devconfig_t *dev = (devconfig_t *)iter->obj;
+
+ if (devconfig_isA(dev, TYPE_SLICE)) {
+
+ /* get disk info for otherslice */
+ dm_descriptor_t odisk = NULL;
+ char *oname = NULL;
+ boolean_t oefi = B_FALSE;
+ uint64_t obsize = 0;
+ uint64_t oncyls = 0;
+ uint64_t onsects = 0;
+ uint64_t onheads = 0;
+
+ ((error = devconfig_get_name(dev, &oname)) != 0) ||
+ (error = get_disk_for_named_slice(oname, &odisk)) ||
+ (error = disk_get_is_efi(odisk, &oefi)) ||
+ (error = disk_get_blocksize(odisk, &obsize));
+
+ if ((error == 0) && (oefi == B_FALSE)) {
+ ((error = disk_get_ncylinders(odisk, &oncyls)) != 0) ||
+ (error = disk_get_nheads(odisk, &onheads)) ||
+ (error = disk_get_nsectors(odisk, &onsects));
+ }
+
+ if (error == 0) {
+ if ((bsize != obsize) || (ncyls != oncyls) ||
+ (nsects != onsects) || (nheads != onheads)) {
+ /* this disk has a different geometry */
+ *has_same_geom = B_FALSE;
+ }
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: slice_on_similar_bus(dm_descriptor_t slice,
+ * dlist_t *used, boolean_t *on_smlr_bus)
+ *
+ * INPUT: slice - a dm_descriptor_t handle for the slice of interest
+ * used - a dlist_t pointer to a list of used slices
+ * bool - a boolean_t pointer to hold the result
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Determines if the input slice is connected thru a bus with
+ * characteristics similar to the slices in the used list.
+ *
+ * If the slice is found to be on a similar bus, bool is set
+ * to B_TRUE, B_FALSE otherwise.
+ *
+ * The comparison is actually between any of the HBA/controllers
+ * thru which the slices are connected to the system.
+ * If any are of similar type (e.g., fibre, SCSI) and
+ * protocol (SCSI-2, -3, fast/wide), then the slices are
+ * considered to be on similar busses.
+ */
+static int
+slice_on_similar_bus(
+ dm_descriptor_t slice,
+ dlist_t *used,
+ boolean_t *on_smlr_bus)
+{
+ dlist_t *iter = NULL;
+ dlist_t *iter1 = NULL;
+ dlist_t *hbas = NULL;
+ int error = 0;
+
+ /* if there are no used slices, then the bus is similar */
+ *on_smlr_bus = B_TRUE;
+ if (dlist_length(used) == 0) {
+ return (0);
+ }
+
+ (error = slice_get_hbas(slice, &hbas));
+ if (error != 0) {
+ return (error);
+ }
+
+ /* if there are used slices, then make sure the bus is similar */
+ *on_smlr_bus = B_FALSE;
+ for (iter = hbas;
+ (iter != NULL) && (*on_smlr_bus == B_FALSE) && (error == 0);
+ iter = iter->next) {
+
+ dm_descriptor_t hba = (uintptr_t)iter->obj;
+ char *type = NULL;
+ boolean_t fast80 = B_FALSE;
+ boolean_t fast40 = B_FALSE;
+ boolean_t fast20 = B_FALSE;
+ boolean_t wide = B_FALSE;
+
+ ((error = hba_get_type(hba, &type)) != 0) ||
+ (error = hba_is_fast_80(hba, &fast80)) ||
+ (error = hba_is_fast_40(hba, &fast40)) ||
+ (error = hba_is_fast_20(hba, &fast20)) ||
+ (error = hba_supports_wide(hba, &wide));
+ if (error != 0) {
+ continue;
+ }
+
+ /* check against the HBAs for the used slices */
+ for (iter1 = used;
+ (iter1 != NULL) && (*on_smlr_bus == B_FALSE) && (error == 0);
+ iter1 = iter1->next) {
+
+ devconfig_t *used = (devconfig_t *)iter1->obj;
+
+ /* get HBAs for otherslice */
+ dm_descriptor_t udisk = NULL;
+ char *uname = NULL;
+ dlist_t *uhbas = NULL;
+ dlist_t *iter2 = NULL;
+
+ ((error = devconfig_get_name(used, &uname)) != 0) ||
+ (error = get_disk_for_named_slice(uname, &udisk)) ||
+ (error = disk_get_hbas(udisk, &uhbas));
+
+ for (iter2 = uhbas;
+ (iter2 != NULL) && (*on_smlr_bus == B_FALSE) &&
+ (error == 0);
+ iter2 = iter2 ->next) {
+
+ dm_descriptor_t uhba = (uintptr_t)iter2->obj;
+ char *utype = NULL;
+ boolean_t ufast80 = B_FALSE;
+ boolean_t ufast40 = B_FALSE;
+ boolean_t ufast20 = B_FALSE;
+ boolean_t uwide = B_FALSE;
+
+ ((error = hba_get_type(uhba, &utype)) != 0) ||
+ (error = hba_is_fast_80(uhba, &ufast80)) ||
+ (error = hba_is_fast_40(uhba, &ufast40)) ||
+ (error = hba_is_fast_20(uhba, &ufast20)) ||
+ (error = hba_supports_wide(uhba, &uwide));
+
+ if (error == 0) {
+ /* check sync speed ? */
+ if ((fast80 == ufast80) && (fast40 == ufast40) &&
+ (fast20 == ufast20) && (wide == uwide) &&
+ (type == utype)) {
+ *on_smlr_bus = B_TRUE;
+ }
+ }
+ }
+ dlist_free_items(uhbas, NULL);
+ }
+ }
+
+ dlist_free_items(hbas, NULL);
+
+ return (error);
+}
+
+/*
+ * FUNCTION: slice_has_n_paths(dm_descriptor_t slice,
+ * uint16_t npaths, boolean_t *has_n_paths)
+ * INPUT: slice - a dm_descriptor_t handle for the slice of interest
+ * npaths - the number of paths desired
+ * has_n_paths - a boolean_t pointer to hold the result
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Determines if the input slice is connected via npaths.
+ * has_n_paths is set to B_TRUE if so, B_FALSE otherwise.
+ *
+ * In order for a disk to have multiple paths, MPXIO must
+ * be enabled and these conditions should hold:
+ *
+ * Slice will have one drive object.
+ * Drive will have one HBA (scsi_vhci)
+ * Drive will have one alias.
+ * Drive will have possibly > 1 paths.
+ *
+ * Getting the HBAs and aliases for the disk is relatively
+ * expensive, so they aren't checked. The actual number of
+ * paths is only checked if MPXIO is known to be enabled on
+ * the system and the input npaths is > 1.
+ */
+static int
+slice_has_n_paths(
+ dm_descriptor_t slice,
+ uint16_t npaths,
+ boolean_t *has_n_paths)
+{
+ int error = 0;
+
+ *has_n_paths = B_FALSE;
+
+ if ((npaths > 1) && (is_mpxio_enabled() == B_TRUE)) {
+
+ dm_descriptor_t disk = NULL;
+ dlist_t *paths = NULL;
+
+ ((error = slice_get_disk(slice, &disk)) != 0) ||
+ (error = disk_get_paths(disk, &paths));
+
+ if ((error == 0) && (dlist_length(paths) == npaths)) {
+ *has_n_paths = B_TRUE;
+ }
+ dlist_free_items(paths, NULL);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: compare_string_to_modslice_name(void *str, void *modslice)
+ *
+ * INPUT: str - opaque char * pointer
+ * modslice - opaque modslice_t pointer
+ *
+ * RETURNS: int - <0 - if str < modslice->slice_devcfg.name
+ * 0 - if str == modslice->slice_devcfg.name
+ * >0 - if str > modslice->slice_devcfg.name
+ *
+ * PURPOSE: dlist_t helper which compares the input string to
+ * the name of a slice represented as modslice_t struct.
+ *
+ * Comparison is done via string_case_compare.
+ */
+static int
+compare_string_to_modslice_name(
+ void *str,
+ void *modslice)
+{
+ char *name = NULL;
+
+ assert(str != NULL);
+ assert(modslice != NULL);
+
+ (void) devconfig_get_name(
+ ((modslice_t *)modslice)->slice_devcfg, &name);
+
+ return (string_case_compare((char *)str, name));
+}
+
+/*
+ * FUNCTION: compare_modslice_names(void *obj1, void *obj2)
+ *
+ * INPUT: obj1 - opaque pointer
+ * obj2 - opaque pointer
+ *
+ * RETURNS: int - <0 - if obj1 name < obj2 name
+ * 0 - if obj1 name == obj2 name
+ * >0 - if obj1 name > obj2 name
+ *
+ * PURPOSE: dlist_t helper which compares the names of two slices
+ * represented as modslice_t structs.
+ *
+ * Comparison is done by string_case_compare
+ */
+static int
+compare_modslice_names(
+ void *obj1,
+ void *obj2)
+{
+ char *name1 = NULL;
+ char *name2 = NULL;
+
+ assert(obj1 != NULL);
+ assert(obj2 != NULL);
+
+ (void) devconfig_get_name(
+ ((modslice_t *)obj1)->slice_devcfg, &name1);
+ (void) devconfig_get_name(
+ ((modslice_t *)obj2)->slice_devcfg, &name2);
+
+ return (string_case_compare(name1, name2));
+}
+
+/*
+ * FUNCTION: release_used_slices()
+ *
+ * PURPOSE: Helper which cleans up the module private list of used
+ * slices.
+ */
+void
+release_used_slices()
+{
+ dlist_free_items(_used_slices, free_used_slice);
+ _used_slices = NULL;
+}
+
+static void
+free_used_slice(
+ void *obj)
+{
+ if (obj != NULL) {
+ usedslice_t *used = (usedslice_t *)obj;
+ free(used->slicename);
+ free(used);
+ }
+}
+
+/*
+ * FUNCTION: is_used_slice(dm_descriptor_t slice, boolean_t *is_used)
+ *
+ * INPUT: slice - a dm_descriptor_t slice handle
+ *
+ * OUTPUT: is_reserved - pointer to a boolean_t to hold the
+ * return result.
+ *
+ * PURPOSE: Helper which checks to see if the input slice
+ * is in the used_slice list.
+ *
+ * Check the input name against any used slice name or alias.
+ * is_used is set to B_TRUE if the input slice is already used,
+ * B_FALSE otherwise.
+ */
+int
+is_used_slice(
+ dm_descriptor_t slice,
+ boolean_t *is_used)
+{
+ char *name;
+ int error = 0;
+
+ if ((error = get_display_name(slice, &name)) == 0) {
+ *is_used = dlist_contains(_used_slices, (void *)name,
+ compare_usedslice_name_to_string);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTIONS: add_used_slice(dm_descriptor_t slice)
+ * add_used_slice_by_name(char *slicename)
+ * add_used_slice_list_entry(char *slice)
+ * remove_used_slice_by_name(char *slicename)
+ *
+ * INPUT: diskset - a char * diskset name.
+ * slice - a dm_descriptor_t slice handle
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Access or maintain the list of used slices.
+ */
+int
+add_used_slice(
+ dm_descriptor_t slice)
+{
+ dm_descriptor_t disk;
+ char *name;
+ int error = 0;
+
+ assert(slice != (dm_descriptor_t)0);
+
+ ((error = get_display_name(slice, &name)) != 0) ||
+ (error = slice_get_disk(slice, &disk)) ||
+ (error = add_used_slice_list_entry(name, disk));
+
+ return (error);
+}
+
+int
+add_used_slice_by_name(
+ char *slicename)
+{
+ dm_descriptor_t disk = (dm_descriptor_t)0;
+ int error = 0;
+
+ assert(slicename != NULL);
+
+ /* find disk for slice */
+ error = get_disk_for_named_slice(slicename, &disk);
+ if (error == 0) {
+ error = add_used_slice_list_entry(slicename, disk);
+ }
+
+ return (error);
+}
+
+static int
+add_used_slice_list_entry(
+ char *slicename,
+ dm_descriptor_t disk)
+{
+ usedslice_t *used = NULL;
+ int error = 0;
+
+ assert(slicename != NULL);
+ assert(disk != (dm_descriptor_t)0);
+
+ used = (usedslice_t *)calloc(1, sizeof (usedslice_t));
+ if (used == NULL) {
+ error = ENOMEM;
+ } else {
+
+ used->disk = disk;
+ if ((used->slicename = strdup(slicename)) == NULL) {
+ free(used);
+ error = ENOMEM;
+ } else {
+ dlist_t *item = dlist_new_item((void *) used);
+ if (item == NULL) {
+ free(used->slicename);
+ free(used);
+ error = ENOMEM;
+ } else {
+ _used_slices =
+ dlist_append(item, _used_slices, AT_HEAD);
+ }
+ }
+ }
+ return (error);
+}
+
+int
+remove_used_slice_by_name(
+ char *slice)
+{
+ dlist_t *removed = NULL;
+
+ _used_slices =
+ dlist_remove_equivalent_item(_used_slices, (void *)slice,
+ compare_usedslice_name_to_string, &removed);
+
+ if (removed != NULL) {
+ free_used_slice(removed->obj);
+ removed->obj = NULL;
+ free(removed);
+ }
+
+ return (0);
+}
+
+/*
+ * FUNCTION: compare_usedslice_name_to_string(void *obj1, void *obj2)
+ * INPUT: obj1 - opaque pointer
+ * obj2 - opaque pointer
+ *
+ * RETURNS: int - <0 - if obj1 name < obj2 name
+ * 0 - if obj1 name == obj2 name
+ * >0 - if obj1 name > obj2 name
+ *
+ * PURPOSE: dlist_t helper which compares the names of a slice
+ * represented as modslice_t struct to a string.
+ *
+ * obj1 is assumed to be a char *
+ * obj2 is assumed to be a usedslice_t *
+ *
+ * Comparison is done via string_case_compare.
+ */
+static int
+compare_usedslice_name_to_string(
+ void *obj1,
+ void *obj2)
+{
+ assert(obj1 != NULL);
+ assert(obj2 != NULL);
+
+ return (string_case_compare((char *)obj1,
+ ((usedslice_t *)obj2)->slicename));
+}
+
+/*
+ * FUNCTION: disk_has_used_slice(dm_descriptor_t disk, boolean_t *hasused)
+ *
+ * INPUT: disk - a dm_descriptor_t disk handle.
+ * inuse - a boolean_t pointer to hold the result
+ *
+ * RETURNS: int - 0 on success
+ * !0 othersize.
+ *
+ * PURPOSE: Determines if any of the known used slices is on the
+ * input disk.
+ */
+int
+disk_has_used_slice(
+ dm_descriptor_t disk,
+ boolean_t *hasused)
+{
+ dlist_t *iter;
+ int error = 0;
+
+ *hasused = B_FALSE;
+ for (iter = _used_slices;
+ (iter != NULL) && (*hasused == B_FALSE);
+ iter = iter->next) {
+
+ usedslice_t *used = (usedslice_t *)iter->obj;
+
+ /* compare used slice's disk to disk */
+ if (compare_descriptors((void *)disk, (void *)used->disk) == 0) {
+ *hasused = B_TRUE;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: add_reserved_slice(dm_descriptor_t slice)
+ *
+ * INPUT: slice - a dm_descriptor_t slice handle
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Helper which remembers specfically requested slices
+ * in a private list to ensure that the same slice isn't
+ * requested more than once.
+ *
+ * Does not check to see if the slice already exists
+ * in the list of reserved slices. Assumes that the
+ * caller has checked using is_reserved_slice().
+ *
+ * The reserved slice list is used by several functions:
+ *
+ * 1. layout_validate.validate_slice_components() adds user
+ * requested slices to the list.
+ *
+ * 2. After all potentially usable slices have been scanned,
+ * layout_validate.validate_reserved_slices() checks the
+ * slices in the reserved and ensures that each slice is
+ * actually usable as a volume component.
+ *
+ * 3. layout.disk_get_avail_space(), layout.disk_get_avail_slices()
+ * exclude slices in the reserved list from being considered
+ * available for general layout use.
+ */
+int
+add_reserved_slice(
+ dm_descriptor_t slice)
+{
+ dlist_t *item = NULL;
+
+ if ((item = dlist_new_item((void *)slice)) == NULL) {
+ return (ENOMEM);
+ }
+
+ _rsvd_slices = dlist_append(item, _rsvd_slices, AT_HEAD);
+
+ return (0);
+}
+
+/*
+ * FUNCTION: is_reserved_slice(dm_descriptor_t slice,
+ * boolean_t *is_reserved)
+ *
+ * INPUT: slice - a dm_descriptor_t slice handle
+ *
+ * OUTPUT: is_reserved - pointer to a boolean_t to hold the
+ * return result.
+ *
+ * PURPOSE: Helper which checks to see if the input slice
+ * was previously reserved.
+ *
+ * Check the input name against any reserved slice
+ * name or alias. is_reserved is set to B_TRUE if the
+ * input slice is already reserved, B_FALSE otherwise.
+ */
+int
+is_reserved_slice(
+ dm_descriptor_t slice,
+ boolean_t *is_reserved)
+{
+ *is_reserved = dlist_contains(
+ _rsvd_slices, (void *)slice, compare_descriptor_names);
+
+ return (0);
+}
+
+/*
+ * FUNCTION: release_reserved_slice()
+ *
+ * PURPOSE: Helper which cleans up the module private list of reserved
+ * slices.
+ */
+void
+release_reserved_slices()
+{
+ dlist_free_items(_rsvd_slices, free);
+ _rsvd_slices = NULL;
+}
+
+/*
+ * FUNCTION: get_reserved_slices(dlist_t **list)
+ *
+ * OUTPUT: list - a dlist_t pointer to hold the returned list of
+ * reserverd slices.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Accessor to retrieve the current list of reserved slice
+ * dm_descriptor_t handles.
+ */
+int
+get_reserved_slices(
+ dlist_t **list)
+{
+ *list = _rsvd_slices;
+
+ return (0);
+}
+
+/*
+ * FUNCTION: add_slice_to_remove(char *name, uint32_t index)
+ *
+ * INPUT: name - name of a slice
+ * index - index for the slice
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Utility function to add the named slice to the list of
+ * those that need to be "removed" by having their sizes
+ * set to 0.
+ */
+int
+add_slice_to_remove(
+ char *name,
+ uint32_t index)
+{
+ rmvdslice_t *rmvd = NULL;
+ int error = 0;
+
+ assert(name != NULL);
+
+ rmvd = (rmvdslice_t *)calloc(1, sizeof (rmvdslice_t));
+ if (rmvd == NULL) {
+ error = ENOMEM;
+ } else {
+ rmvd->slice_index = index;
+ if ((rmvd->slice_name = strdup(name)) == NULL) {
+ free(rmvd);
+ error = ENOMEM;
+ } else {
+ dlist_t *item = dlist_new_item((void *) rmvd);
+ if (item == NULL) {
+ free(rmvd->slice_name);
+ free(rmvd);
+ error = ENOMEM;
+ } else {
+ _rmvd_slices =
+ dlist_append(item, _rmvd_slices, AT_HEAD);
+ }
+ }
+ }
+ return (error);
+}
+
+/*
+ * FUNCTION: get_removed_slices()
+ *
+ * RETURNS: dlist_t * - pointer to a list of rmvdslice_t structs
+ *
+ * PURPOSE: Accessor to retrieve the current list of names of slices
+ * to be removed.
+ */
+dlist_t *
+get_slices_to_remove(
+ dlist_t **list)
+{
+ return (_rmvd_slices);
+}
+
+static void
+free_rmvd_slice(
+ void *obj)
+{
+ if (obj != NULL) {
+ rmvdslice_t *rmvd = (rmvdslice_t *)obj;
+ free(rmvd->slice_name);
+ free(rmvd);
+ }
+}
+
+/*
+ * FUNCTION: release_removed_slices()
+ *
+ * PURPOSE: Helper which cleans up the module private list of removed
+ * slices.
+ */
+void
+release_slices_to_remove()
+{
+ dlist_free_items(_rmvd_slices, free_rmvd_slice);
+ _rmvd_slices = NULL;
+}
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_slice.h b/usr/src/cmd/lvm/metassist/layout/layout_slice.h
new file mode 100644
index 0000000000..5a3febdf65
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_slice.h
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+#ifndef _LAYOUT_SLICE_H
+#define _LAYOUT_SLICE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "libdiskmgt.h"
+#include "volume_devconfig.h"
+#include "volume_dlist.h"
+
+/*
+ * struct to track which slices need to be explicitly "removed" from
+ * the system before applying any metassist updates/changes.
+ */
+typedef struct {
+ char *slice_name;
+ uint32_t slice_index;
+} rmvdslice_t;
+
+extern void release_slices_to_remove();
+extern dlist_t *get_slices_to_remove();
+extern int add_slice_to_remove(char *name, uint32_t index);
+
+/*
+ * struct to track which slices have been explicitly modified
+ * during the layout process...
+ *
+ * src_slice_desc is the dm_descriptor_t of the slice which provided the
+ * space (this is only relevant to slices that have been created by
+ * taking space from some other "source" slice).
+ * slice_devconfig is the devconfig_t struct with the modified slice properties.
+ * times_modified is the number of times the slice has been modified
+ * (this is only relevant to slices that have been resized to
+ * provide space for new slices)
+ * volume_component is used to control when the slice_devcfg is freed.
+ * if volume_component is B_TRUE, the devconfig is returned as part
+ * of the result of layout and so cannot be freed by
+ * release_modified_slices.
+ */
+typedef struct {
+ dm_descriptor_t src_slice_desc;
+ devconfig_t *slice_devcfg;
+ int times_modified;
+ boolean_t volume_component;
+} modslice_t;
+
+extern dlist_t *get_modified_slices();
+extern int release_modified_slices();
+
+extern int make_slicename_for_diskname_and_index(
+ char *diskname,
+ uint16_t index,
+ char **slicename);
+
+extern int assemble_modified_slice(
+ dm_descriptor_t src_slice_desc,
+ char *mod_name,
+ uint32_t mod_index,
+ uint64_t mod_stblk,
+ uint64_t mod_nblks,
+ uint64_t mod_size,
+ devconfig_t **mod_slice);
+
+extern int choose_slice(
+ uint64_t nbytes,
+ uint16_t npaths,
+ dlist_t *slices,
+ dlist_t *used,
+ dlist_t *used_hbas,
+ dlist_t *used_disks,
+ boolean_t unused_disk,
+ boolean_t nbytes_is_min,
+ boolean_t add_extra_cyl,
+ devconfig_t **chosen);
+
+extern int create_devconfig_for_slice(
+ dm_descriptor_t slice,
+ devconfig_t **newslice);
+
+extern int destroy_new_slice(
+ devconfig_t *vol);
+
+/*
+ * accessors for the list of used slice names for named diskset.
+ */
+extern int is_used_slice(dm_descriptor_t slice, boolean_t *is_used);
+extern int add_used_slice_by_name(char *slicename);
+extern int remove_used_slice_by_name(char *slicename);
+extern int add_used_slice(dm_descriptor_t slice);
+extern void release_used_slices();
+extern int disk_has_used_slice(dm_descriptor_t disk, boolean_t *inuse);
+
+/*
+ * accessors to track slices reserved for use in explicit
+ * volume requests
+ */
+extern int add_reserved_slice(dm_descriptor_t slice);
+extern int is_reserved_slice(dm_descriptor_t slice, boolean_t *is_rsvd);
+extern int get_reserved_slices(dlist_t **list);
+extern void release_reserved_slices();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LAYOUT_SLICE_H */
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_stripe.c b/usr/src/cmd/lvm/metassist/layout/layout_stripe.c
new file mode 100644
index 0000000000..cfb4ed160e
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_stripe.c
@@ -0,0 +1,1034 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <string.h>
+
+#include <libintl.h>
+
+#include "volume_error.h"
+#include "volume_devconfig.h"
+#include "volume_dlist.h"
+#include "volume_output.h"
+
+#include "layout_device_cache.h"
+#include "layout_device_util.h"
+#include "layout_discovery.h"
+#include "layout_dlist_util.h"
+#include "layout_messages.h"
+#include "layout_request.h"
+#include "layout_slice.h"
+#include "layout_svm_util.h"
+
+#define _LAYOUT_STRIPE_C
+
+static int compose_stripe(
+ devconfig_t *request,
+ uint64_t nbytes,
+ dlist_t *disks,
+ int max,
+ int min,
+ dlist_t *othervols,
+ devconfig_t **stripe);
+
+static int compose_stripe_within_hba(
+ devconfig_t *request,
+ dlist_t *hbas,
+ uint64_t nbytes,
+ uint16_t min,
+ uint16_t max,
+ devconfig_t **stripe);
+
+static int assemble_stripe(
+ devconfig_t *request,
+ dlist_t *comps,
+ devconfig_t **stripe);
+
+static dlist_t *
+order_stripe_components_alternate_hbas(
+ dlist_t *comps);
+
+static int compute_usable_stripe_capacity(
+ dlist_t *comps,
+ uint64_t ilace,
+ uint64_t *nbytes);
+
+/*
+ * FUNCTION: layout_stripe(devconfig_t *request, uint64_t nbytes,
+ * dlist_t **results)
+ *
+ * INPUT: request - pointer to a devconfig_t of the current request
+ * nbytes - the desired capacity of the stripe
+ *
+ * OUPUT: results - pointer to a list of composed volumes
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Main layout driver for composing stripe volumes.
+ *
+ * Attempts to construct a stripe of size nbytes.
+ *
+ * Basic goal of all strategies is to build wide-thin stripes:
+ * build widest stripe possible across as many HBAs as possible.
+ *
+ * Several different layout strategies are tried in order
+ * of preference until one succeeds or there are none left.
+ *
+ * 1 - stripe across similar HBAs
+ * . number of components is driven by # of HBAs
+ * . requires mincomp available HBAs
+ *
+ * 2 - stripe within a single HBA
+ * . number of components is driven by # of disks
+ * . requires at least 1 HBA with mincomp disks
+ *
+ * 3 - stripe across all available disks on similar HBAs
+ * . number of components is driven by # of disk
+ * . requires at least mincomp disks
+ *
+ * 4 - stripe across all available HBAs
+ * . number of components is driven by # of HBAs
+ * . requires at least mincomp HBAs
+ *
+ * 5 - stripe across all available disks on all HBAs
+ * . number of components is driven by # of disks
+ * . requires at least mincomp disks
+ *
+ * Each strategy tries to compose a stripe with the
+ * maximum number of components first then reduces the
+ * number of components down to mincomp.
+ *
+ * get allowed minimum number of stripe components
+ * get allowed maximum number of stripe components
+ * get available HBAs
+ *
+ * group HBAs by characteristics
+ * for (each HBA grouping) and (stripe not composed) {
+ * select next HBA group
+ * for (strategy[1,2,3]) and (stripe not composed) {
+ * compose stripe using HBAs in group
+ * }
+ * }
+ *
+ * if (stripe not composed) {
+ * for (strategy[4,5]) and (stripe not composed) {
+ * compose stripe using all HBAs
+ * }
+ * }
+ *
+ * if (stripe composed) {
+ * append composed stripe to results
+ * }
+ *
+ */
+int
+layout_stripe(
+ devconfig_t *request,
+ uint64_t nbytes,
+ dlist_t **results)
+{
+ /*
+ * these enums define the # of strategies and the preference order
+ * in which they are tried
+ */
+ typedef enum {
+ STRIPE_ACROSS_SIMILAR_HBAS_DISK_PER = 0,
+ STRIPE_WITHIN_SIMILAR_HBA,
+ STRIPE_ACROSS_SIMILAR_HBAS,
+ N_SIMILAR_HBA_STRATEGIES
+ } similar_hba_strategy_order_t;
+
+ typedef enum {
+ STRIPE_ACROSS_ANY_HBAS_DISK_PER = 0,
+ STRIPE_ACROSS_ANY_HBAS,
+ N_ANY_HBA_STRATEGIES
+ } any_hba_strategy_order_t;
+
+
+ dlist_t *usable_hbas = NULL;
+ dlist_t *similar_hba_groups = NULL;
+ dlist_t *iter = NULL;
+ devconfig_t *stripe = NULL;
+
+ uint16_t mincomp = 0;
+ uint16_t maxcomp = 0;
+
+ int error = 0;
+
+ (error = get_usable_hbas(&usable_hbas));
+ if (error != 0) {
+ return (error);
+ }
+
+ print_layout_volume_msg(devconfig_type_to_str(TYPE_STRIPE), nbytes);
+
+ if (dlist_length(usable_hbas) == 0) {
+ print_no_hbas_msg();
+ volume_set_error(gettext("There are no usable HBAs."));
+ return (-1);
+ }
+
+ ((error = group_similar_hbas(usable_hbas, &similar_hba_groups)) != 0) ||
+
+ /*
+ * determine the min/max number of stripe components
+ * based on the request, the diskset defaults or the
+ * global defaults. These are absolute limits, the
+ * actual values are determined by the number of HBAs
+ * and/or disks available.
+ */
+ (error = get_stripe_min_comp(request, &mincomp)) ||
+ (error = get_stripe_max_comp(request, &maxcomp));
+ if (error != 0) {
+ return (error);
+ }
+
+ for (iter = similar_hba_groups;
+ (error == 0) && (stripe == NULL) && (iter != NULL);
+ iter = iter->next) {
+
+ dlist_t *hbas = (dlist_t *)iter->obj;
+
+ similar_hba_strategy_order_t order;
+
+ for (order = STRIPE_ACROSS_SIMILAR_HBAS_DISK_PER;
+ (order < N_SIMILAR_HBA_STRATEGIES) &&
+ (stripe == NULL) && (error == 0);
+ order++) {
+
+ dlist_t *selhbas = NULL;
+ dlist_t *disks = NULL;
+ int n = 0;
+
+ switch (order) {
+
+ case STRIPE_ACROSS_SIMILAR_HBAS_DISK_PER:
+
+ error = select_hbas_with_n_disks(
+ request, hbas, 1, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+gettext(" -->Strategy 1: use 1 disk from %d-%d similar HBAs - stripe across HBAs\n"),
+ mincomp, maxcomp);
+/* END CSTYLED */
+
+ if ((n = dlist_length(selhbas)) >= mincomp) {
+ n = ((n > maxcomp) ? maxcomp : n);
+ error = compose_stripe(
+ request, nbytes, disks, n,
+ mincomp, NULL, &stripe);
+ } else {
+ print_insufficient_hbas_msg(n);
+ }
+ }
+
+ break;
+
+ case STRIPE_WITHIN_SIMILAR_HBA:
+
+ error = select_hbas_with_n_disks(
+ request, hbas, mincomp, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+gettext(" -->Strategy 2: use %d-%d disks from any single HBA - stripe within HBA\n"),
+ mincomp, maxcomp);
+/* END CSTYLED */
+
+ if ((n = dlist_length(selhbas)) > 0) {
+ error = compose_stripe_within_hba(
+ request, selhbas, nbytes,
+ mincomp, maxcomp, &stripe);
+ } else {
+ print_insufficient_disks_msg(n);
+ }
+ }
+
+ break;
+
+ case STRIPE_ACROSS_SIMILAR_HBAS:
+
+ error = select_hbas_with_n_disks(
+ request, hbas, 1, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+gettext(" -->Strategy 3: use %d-%d disks from %d similar HBAs - stripe across HBAs\n"),
+ mincomp, maxcomp, dlist_length(hbas));
+/* END CSTYLED */
+
+ if ((n = dlist_length(selhbas)) > 0) {
+ if ((n = dlist_length(disks)) >= mincomp) {
+ n = ((n > maxcomp) ? maxcomp : n);
+ error = compose_stripe(
+ request, nbytes, disks, n,
+ mincomp, NULL, &stripe);
+ } else {
+ print_insufficient_disks_msg(n);
+ }
+ } else {
+ print_insufficient_hbas_msg(n);
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ dlist_free_items(disks, NULL);
+ dlist_free_items(selhbas, NULL);
+ }
+ }
+
+ for (iter = similar_hba_groups; iter != NULL; iter = iter->next) {
+ dlist_free_items((dlist_t *)iter->obj, NULL);
+ }
+ dlist_free_items(similar_hba_groups, NULL);
+
+ /*
+ * if striping within similar HBA groups failed,
+ * try across all available HBAs
+ */
+ if ((stripe == NULL) && (error == 0)) {
+
+ any_hba_strategy_order_t order;
+
+ for (order = STRIPE_ACROSS_ANY_HBAS_DISK_PER;
+ (order < N_ANY_HBA_STRATEGIES) &&
+ (stripe == NULL) && (error == 0);
+ order++) {
+
+ dlist_t *selhbas = NULL;
+ dlist_t *disks = NULL;
+ int n = 0;
+
+ switch (order) {
+
+ case STRIPE_ACROSS_ANY_HBAS_DISK_PER:
+
+ error = select_hbas_with_n_disks(
+ request, usable_hbas, 1, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+gettext(" -->Strategy 4: use 1 disk from %d-%d available HBAs - stripe across any HBAs\n"),
+ mincomp, maxcomp);
+/* END CSTYLED */
+
+ if ((n = dlist_length(selhbas)) >= mincomp) {
+
+ n = ((n > maxcomp) ? maxcomp : n);
+ error = compose_stripe(
+ request, nbytes, disks, n,
+ mincomp, NULL, &stripe);
+
+ } else {
+ print_insufficient_hbas_msg(n);
+ }
+ }
+
+ break;
+
+ case STRIPE_ACROSS_ANY_HBAS:
+
+ error = select_hbas_with_n_disks(
+ request, usable_hbas, 1, &selhbas, &disks);
+
+ if (error == 0) {
+
+/* BEGIN CSTYLED */
+oprintf(OUTPUT_TERSE,
+gettext(" -->Strategy 5: use %d-%d disks from %d available HBA - stripe across any HBAs\n"),
+ mincomp, maxcomp, dlist_length(selhbas));
+/* END CSTYLED */
+
+ if ((n = dlist_length(disks)) >= mincomp) {
+
+ n = ((n > maxcomp) ? maxcomp : n);
+ error = compose_stripe(
+ request, nbytes, disks, n,
+ mincomp, NULL, &stripe);
+
+ } else {
+ print_insufficient_disks_msg(n);
+ }
+ }
+
+ break;
+ }
+
+ dlist_free_items(disks, NULL);
+ dlist_free_items(selhbas, NULL);
+ }
+ }
+
+ if (stripe != NULL) {
+
+ dlist_t *item = NULL;
+ if ((item = dlist_new_item(stripe)) == NULL) {
+ error = ENOMEM;
+ } else {
+ *results = dlist_append(item, *results, AT_TAIL);
+ print_layout_success_msg();
+ }
+
+ } else if (error != 0) {
+
+ print_debug_failure_msg(
+ devconfig_type_to_str(TYPE_STRIPE),
+ get_error_string(error));
+
+ } else {
+
+ print_insufficient_resources_msg(
+ devconfig_type_to_str(TYPE_STRIPE));
+ error = -1;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: populate_stripe(devconfig_t *request, uint64_t nbytes,
+ * dlist_t *disks, uint16_t ncomp, dlist_t *othervols,
+ * devconfig_t **stripe)
+ *
+ * INPUT: request - pointer to a request devconfig_t
+ * nbytes - desired stripe size
+ * disks - pointer to a list of availalb disks
+ * ncomp - number of components desired
+ * othervols - pointer to a list of other volumes whose
+ * composition may affect this stripe
+ * (e.g., submirrors of the same mirror)
+ *
+ * OUTPUT: stripe - pointer to a devconfig_t to hold resulting stripe
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Helper to populate a stripe with the specified number of
+ * components and aggregate capacity using slices on disks
+ * in the input list.
+ *
+ * If the othervols list is not empty, the slice components
+ * chosen for the stripe must not on the same disks as any
+ * of the other volumes.
+ *
+ * If sufficient slice components can be found, the stripe
+ * is assembled and returned.
+ */
+int
+populate_stripe(
+ devconfig_t *request,
+ uint64_t nbytes,
+ dlist_t *disks,
+ uint16_t ncomp,
+ dlist_t *othervols,
+ devconfig_t **stripe)
+{
+ uint16_t npaths = 0;
+ uint16_t ncomps = 0; /* number of components found */
+ uint64_t rsize = 0; /* reqd component size */
+
+ dlist_t *other_hbas = NULL;
+ dlist_t *other_disks = NULL;
+
+ dlist_t *slices = NULL;
+ dlist_t *comps = NULL;
+
+ int error = 0;
+
+ *stripe = NULL;
+
+ ((error = disks_get_avail_slices(request, disks, &slices)) != 0) ||
+ (error = get_volume_npaths(request, &npaths));
+ if (error != 0) {
+ return (error);
+ }
+
+ print_populate_volume_ncomps_msg(
+ devconfig_type_to_str(TYPE_STRIPE), nbytes, ncomp);
+
+ if (slices == NULL) {
+ print_populate_no_slices_msg();
+ return (0);
+ }
+
+ /* determine HBAs and disks used by othervols */
+ error = get_hbas_and_disks_used_by_volumes(othervols,
+ &other_hbas, &other_disks);
+ if (error != 0) {
+ dlist_free_items(other_hbas, NULL);
+ dlist_free_items(other_disks, NULL);
+ return (error);
+ }
+
+ print_populate_choose_slices_msg();
+
+ /*
+ * each stripe component needs to be this size.
+ * Note that the stripe interlace doesn't need to be
+ * taken into account in this computation because any
+ * slice selected as a stripe component will be oversized
+ * to account for interlace and cylinder rounding done
+ * by libmeta.
+ */
+ rsize = nbytes / ncomp;
+
+ /*
+ * need to select 'ncomp' slices that are at least 'rsize'
+ * large in order to reach the desired capacity.
+ */
+ ncomps = 0;
+ while ((ncomps < ncomp) && (error == 0)) {
+
+ devconfig_t *comp = NULL;
+ dlist_t *item = NULL;
+ dlist_t *rmvd = NULL;
+ char *cname = NULL;
+
+ /* BEGIN CSTYLED */
+ /*
+ * 1st B_TRUE: require a different disk than those used by
+ * comps and othervols
+ * 2nd B_TRUE: requested size is minimum acceptable
+ * 3rd B_TRUE: add an extra cylinder to the resulting slice, this is
+ * necessary for Stripe components whose sizes get rounded
+ * down to an interlace multiple and then down to a cylinder
+ * boundary.
+ */
+ /* END CSTYLED */
+ error = choose_slice(rsize, npaths, slices, comps,
+ other_hbas, other_disks, B_TRUE, B_TRUE, B_TRUE, &comp);
+
+ if ((error == 0) && (comp != NULL)) {
+
+ ++ncomps;
+
+ item = dlist_new_item(comp);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+
+ /* add selected component to comp list */
+ comps = dlist_insert_ordered(
+ item,
+ comps,
+ ASCENDING,
+ compare_devconfig_sizes);
+
+ /* remove it from the available list */
+ slices = dlist_remove_equivalent_item(slices, (void *) comp,
+ compare_devconfig_and_descriptor_names, &rmvd);
+
+ if (rmvd != NULL) {
+ free(rmvd);
+ }
+
+ /* add the component slice to the used list */
+ if ((error = devconfig_get_name(comp, &cname)) == 0) {
+ error = add_used_slice_by_name(cname);
+ }
+ }
+ } else if (comp == NULL) {
+ /* no possible slice */
+ break;
+ }
+ }
+
+ dlist_free_items(slices, NULL);
+ dlist_free_items(other_hbas, NULL);
+ dlist_free_items(other_disks, NULL);
+
+ if (ncomps == ncomp) {
+
+ if ((error = assemble_stripe(request, comps, stripe)) == 0) {
+ print_populate_success_msg();
+ } else {
+ dlist_free_items(comps, free_devconfig_object);
+ }
+
+ } else if (error == 0) {
+
+ if (ncomps > 0) {
+ print_insufficient_components_msg(ncomps);
+ dlist_free_items(comps, free_devconfig_object);
+ } else {
+ print_populate_no_slices_msg();
+ }
+
+ }
+ return (error);
+}
+
+/*
+ * FUNCTION: populate_explicit_stripe(devconfig_t *request,
+ * dlist_t **results)
+ *
+ * INPUT: request - pointer to a request devconfig_t
+ *
+ * OUTPUT: results - pointer to a list of volume devconfig_t results
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Processes the input stripe request that specifies explicit
+ * slice components.
+ *
+ * The components have already been validated and reserved,
+ * all that is required is to create devconfig_t structs
+ * for each requested slice.
+ *
+ * The net size of the stripe is determined by the slice
+ * components.
+ *
+ * The stripe devconfig_t is assembled and appended to the
+ * results list.
+ *
+ * This function is also called from
+ * layout_mirror.populate_explicit_mirror()
+ */
+int
+populate_explicit_stripe(
+ devconfig_t *request,
+ dlist_t **results)
+{
+ devconfig_t *stripe = NULL;
+ int error = 0;
+
+ dlist_t *comps = NULL;
+ dlist_t *iter = NULL;
+ dlist_t *item = NULL;
+
+ print_layout_explicit_msg(devconfig_type_to_str(TYPE_STRIPE));
+
+ /* assemble components */
+ iter = devconfig_get_components(request);
+ for (; (iter != NULL) && (error == 0); iter = iter->next) {
+ devconfig_t *rqst = (devconfig_t *)iter->obj;
+ dm_descriptor_t rqst_slice = NULL;
+ char *rqst_name = NULL;
+ devconfig_t *comp = NULL;
+
+ /* slice components have been validated */
+ /* turn each into a devconfig_t */
+ ((error = devconfig_get_name(rqst, &rqst_name)) != 0) ||
+ (error = slice_get_by_name(rqst_name, &rqst_slice)) ||
+ (error = create_devconfig_for_slice(rqst_slice, &comp));
+
+ if (error == 0) {
+
+ print_layout_explicit_added_msg(rqst_name);
+
+ item = dlist_new_item((void *)comp);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ comps = dlist_append(item, comps, AT_TAIL);
+ }
+ }
+ }
+
+ if (error == 0) {
+ error = assemble_stripe(request, comps, &stripe);
+ }
+
+ if (error == 0) {
+ if ((item = dlist_new_item(stripe)) == NULL) {
+ error = ENOMEM;
+ } else {
+ *results = dlist_append(item, *results, AT_TAIL);
+ print_populate_success_msg();
+ }
+ } else {
+ dlist_free_items(comps, free_devconfig);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: compose_stripe(devconfig_t *request, uint64_t nbytes,
+ * dlist_t *disks, uint16_t max, uint16_t min,
+ * dlist_t *othervols, devconfig_t **stripe)
+ *
+ * INPUT: request - pointer to a request devconfig_t
+ * nbytes - desired stripe size
+ * disks - pointer to a list of availalb disks
+ * max - maximum number of components allowed
+ * min - minimum number of components allowed
+ * othervols - pointer to a list of other volumes whose
+ * composition may affect this stripe
+ * (e.g., submirrors of the same mirror)
+ *
+ * OUTPUT: stripe - pointer to a devconfig_t to hold resulting stripe
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Attempt to compose a stripe of capacity nbytes, with
+ * component slices chosen from the input list of disks.
+ * The number of components in the stripe should be in the
+ * range min <= N <= max, more components are preferred.
+ *
+ * If a stripe can be composed, a pointer to it will be
+ * returned in the stripe devconfig_t.
+ *
+ * This is a loop wrapped around populate_stripe which
+ * varies the number of components between 'max' and 'min'.
+ */
+static int
+compose_stripe(
+ devconfig_t *request,
+ uint64_t nbytes,
+ dlist_t *disks,
+ int max,
+ int min,
+ dlist_t *othervols,
+ devconfig_t **stripe)
+{
+ int error = 0;
+
+ *stripe = NULL;
+
+ for (; (error == 0) && (*stripe == NULL) && (max >= min); max--) {
+ error = populate_stripe(
+ request, nbytes, disks, max, othervols, stripe);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: compose_stripe_within_hba(devconfig_t *request,
+ * dlist_t *hbas, uint64_t nbytes,
+ * int maxcomp, int mincomp, dlist_t **stripe)
+ *
+ * INPUT: request - pointer to a devconfig_t of the current request
+ * hbas - pointer to a list of available HBAs
+ * nbytes - the desired capacity for the stripe
+ * maxcomp - the maximum number of stripe components
+ * mincomp - the minimum number of stripe components
+ *
+ * OUTPUT: stripe - pointer to a stripe devconfig_t result
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Layout function which compose a stripe of the desired size
+ * using available disks within any single HBA from the input list.
+ *
+ * The number of components within the composed stripe will be
+ * in the range of min to max, preferring more components
+ * over fewer.
+ *
+ * All input HBAs are expected to have at least mincomp
+ * available disks and total space sufficient for the stripe.
+ *
+ * If the stripe can be composed, a pointer to it is returned in
+ * the stripe devconfig_t *.
+ *
+ *
+ * while (more hbas and stripe not composed) {
+ * select HBA
+ * if (not enough available space on this HBA) {
+ * continue;
+ * }
+ * get available disks for HBA
+ * use # disks as max # of stripe components
+ * try to compose stripe
+ * }
+ *
+ */
+static int
+compose_stripe_within_hba(
+ devconfig_t *request,
+ dlist_t *hbas,
+ uint64_t nbytes,
+ uint16_t min,
+ uint16_t max,
+ devconfig_t **stripe)
+{
+ int error = 0;
+
+ dlist_t *iter = NULL;
+
+ *stripe = NULL;
+
+ for (iter = hbas;
+ (iter != NULL) && (error == 0) && (*stripe == NULL);
+ iter = iter->next) {
+
+ dm_descriptor_t hba = (uintptr_t)iter->obj;
+ dlist_t *disks = NULL;
+ uint64_t space = 0;
+ uint16_t ncomp = 0;
+ char *name;
+
+ ((error = get_display_name(hba, &name)) != 0) ||
+ (error = hba_get_avail_disks_and_space(request,
+ hba, &disks, &space));
+
+ if (error == 0) {
+ if (space >= nbytes) {
+ ncomp = dlist_length(disks);
+ ncomp = ((ncomp > max) ? max : ncomp);
+ error = compose_stripe(
+ request, nbytes, disks, ncomp,
+ min, NULL, stripe);
+ } else {
+ print_hba_insufficient_space_msg(name, space);
+ }
+ }
+
+ dlist_free_items(disks, NULL);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: assemble_stripe(devconfig_t *request, dlist_t *comps,
+ * devconfig_t **stripe)
+ *
+ * INPUT: request - pointer to a devconfig_t of the current request
+ * comps - pointer to a list of slice components
+ *
+ * OUPUT: stripe - pointer to a devconfig_t to hold final stripe
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Helper which creates and populates a stripe devconfig_t
+ * struct using information from the input request and the
+ * list of slice components.
+ *
+ * Determines the name of the stripe either from the request
+ * or from the default naming scheme.
+ *
+ * Sets the interlace for the stripe if a value is specified
+ * in the request.
+ *
+ * Attaches the input list of components to the devconfig.
+ */
+static int
+assemble_stripe(
+ devconfig_t *request,
+ dlist_t *comps,
+ devconfig_t **stripe)
+{
+ uint64_t ilace = 0;
+ char *name = NULL;
+ int error = 0;
+
+ if ((error = new_devconfig(stripe, TYPE_STRIPE)) == 0) {
+ /* set stripe name, use requested name if specified */
+ if ((error = devconfig_get_name(request, &name)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ volume_set_error(gettext("error getting requested name\n"));
+ } else {
+ error = 0;
+ }
+ }
+
+ if (error == 0) {
+ if (name == NULL) {
+ if ((error = get_next_volume_name(&name,
+ TYPE_STRIPE)) == 0) {
+ error = devconfig_set_name(*stripe, name);
+ free(name);
+ }
+ } else {
+ error = devconfig_set_name(*stripe, name);
+ }
+ }
+ }
+
+ if (error == 0) {
+ if ((error = get_stripe_interlace(request, &ilace)) == 0) {
+ error = devconfig_set_stripe_interlace(*stripe, ilace);
+ } else if (error == ENOENT) {
+ ilace = get_default_stripe_interlace();
+ error = 0;
+ }
+ }
+
+ if (error == 0) {
+ uint64_t nbytes = 0;
+ if ((error = compute_usable_stripe_capacity(comps,
+ ilace, &nbytes)) == 0) {
+ error = devconfig_set_size_in_blocks(*stripe, nbytes/DEV_BSIZE);
+ }
+ }
+
+ if (error == 0) {
+ comps = order_stripe_components_alternate_hbas(comps);
+ devconfig_set_components(*stripe, comps);
+ } else {
+ free_devconfig(*stripe);
+ *stripe = NULL;
+ }
+
+ return (error);
+}
+
+/*
+ * Order the given stripe component list such that the number of
+ * slices on the same hba adjacent to each other in the list are
+ * minimized.
+ *
+ * @param comps
+ * the slice component list to order
+ *
+ * @return the first element of the resulting list
+ */
+static dlist_t *
+order_stripe_components_alternate_hbas(
+ dlist_t *comps)
+{
+ dlist_t *iter;
+
+ oprintf(OUTPUT_DEBUG,
+ gettext("Stripe components before ordering to alternate HBAs:\n"));
+
+ for (iter = comps; iter != NULL; iter = iter->next) {
+ devconfig_t *slice = (devconfig_t *)(iter->obj);
+ char *name;
+ devconfig_get_name(slice, &name);
+ oprintf(OUTPUT_DEBUG, " %s\n", name);
+ }
+
+ return (dlist_separate_similar_elements(
+ comps, compare_slices_on_same_hba));
+}
+
+/*
+ * FUNCTION: compute_usable_stripe_capacity(dlist_t *comps, uint64_t ilace,
+ * uint64_t *nbytes)
+ *
+ * INPUT: comps - pointer to a list of stripe components
+ * ilace - the expected stripe interlace in bytes
+ *
+ * OUPUT: nbytes - pointer to hold the computed capacity
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Helper which computes the usable size of a stripe taking
+ * into account the interlace and cylinder rounding that
+ * libmeta uses: a stripe component's size is rounded down to
+ * an integral multiple of the interlace and then rounded down
+ * to a cylinder boundary on VTOC labeled disks.
+ *
+ * (These libmeta computations are in the meta_stripe_attach()
+ * function of .../lib/lvm/libmeta/common/meta_stripe.c and
+ * meta_adjust_geom() in .../lib/lvm/libmeta/common/meta_init.c)
+ *
+ * This function's implementation iterates the input list of
+ * stripe component slices and determines the smallest usable
+ * component capacity.
+ *
+ * The usable stripe capacity is then that component capacity
+ * times the number of components.
+ */
+static int
+compute_usable_stripe_capacity(
+ dlist_t *comps,
+ uint64_t ilace,
+ uint64_t *nbytes)
+{
+ uint64_t bytes_per_component = 0;
+ dlist_t *iter;
+ int ncomps = 0;
+ int error = 0;
+
+ for (iter = comps; (iter != NULL) && (error == 0); iter = iter->next) {
+
+ devconfig_t *comp = (devconfig_t *)iter->obj;
+ char *comp_name = NULL;
+ uint64_t comp_nbytes = 0;
+ dm_descriptor_t comp_disk;
+ boolean_t comp_disk_efi = B_FALSE;
+ uint64_t comp_disk_bps = 0; /* disk bytes per sector */
+
+ ((error = devconfig_get_size(comp, &comp_nbytes)) != 0) ||
+ (error = devconfig_get_name(comp, &comp_name)) ||
+ (error = get_disk_for_named_slice(comp_name, &comp_disk)) ||
+ (error = disk_get_blocksize(comp_disk, &comp_disk_bps)) ||
+ (error = disk_get_is_efi(comp_disk, &comp_disk_efi));
+ if (error == 0) {
+
+ if (comp_disk_efi == B_FALSE) {
+ uint64_t nhead = 0;
+ uint64_t nsect = 0;
+ uint64_t ncyls = 0;
+
+ /* do cylinder and interlace rounding for non-EFI disks */
+ ((error = disk_get_ncylinders(comp_disk, &ncyls)) != 0) ||
+ (error = disk_get_nheads(comp_disk, &nhead)) ||
+ (error = disk_get_nsectors(comp_disk, &nsect));
+ if (error == 0) {
+ /* compute bytes per cyl */
+ uint64_t bpc = nhead * nsect * comp_disk_bps;
+
+ /* round nbytes down to a multiple of interlace */
+ comp_nbytes = (comp_nbytes / ilace) * ilace;
+
+ /* round nbytes down to a cylinder boundary */
+ comp_nbytes = (comp_nbytes / bpc) * bpc;
+ }
+ }
+
+ /* save smallest component size */
+ if ((bytes_per_component == 0) ||
+ (comp_nbytes < bytes_per_component)) {
+ bytes_per_component = comp_nbytes;
+ }
+
+ ++ncomps;
+ }
+ }
+
+ if (error == 0) {
+ /* size of stripe = smallest component size * n components */
+ *nbytes = (bytes_per_component * ncomps);
+ }
+
+ return (error);
+}
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_stripe.h b/usr/src/cmd/lvm/metassist/layout/layout_stripe.h
new file mode 100644
index 0000000000..b41c9c3ecf
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_stripe.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef _LAYOUT_STRIPE_H
+#define _LAYOUT_STRIPE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "volume_devconfig.h"
+#include "volume_dlist.h"
+
+extern int layout_stripe(
+ devconfig_t *request,
+ uint64_t nbytes,
+ dlist_t **results);
+
+extern int populate_stripe(
+ devconfig_t *request,
+ uint64_t nbytes,
+ dlist_t *disks,
+ uint16_t ncomps,
+ dlist_t *othervols,
+ devconfig_t **stripe);
+
+extern int populate_explicit_stripe(
+ devconfig_t *request,
+ dlist_t **results);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LAYOUT_STRIPE_H */
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_svm_util.c b/usr/src/cmd/lvm/metassist/layout/layout_svm_util.c
new file mode 100644
index 0000000000..add5b9fdfa
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_svm_util.c
@@ -0,0 +1,2087 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <meta.h>
+#include <sdssc.h>
+#include <mdiox.h>
+#include <meta_repartition.h>
+
+#include "volume_dlist.h"
+#include "volume_error.h"
+#include "volume_output.h"
+
+#include "layout_device_util.h"
+#include "layout_discovery.h"
+#include "layout_dlist_util.h"
+#include "layout_request.h"
+#include "layout_svm_util.h"
+
+static int _max_hsps = 1000; /* # of HSPs (arbitrary limit) */
+static int _max_devs = 8192; /* # of SVM volumes allowed */
+static int _max_devs_cfg = 128; /* # of SVM volumes configured */
+static int _max_sets = 4; /* # of SVM disk sets */
+
+/* volume name prefixes for generating new names */
+static const char *_hsp_prefix = "hsp";
+static const char *_dev_prefix = "d";
+
+/*
+ * dynamically allocated arrays to track used HSP (hspXXX) and volume
+ * names (dXXX) by number
+ */
+static boolean_t *hsps_by_number = NULL;
+static boolean_t *devs_by_number = NULL;
+
+/*
+ * This struct remembers a diskset and the names of
+ * the disks in the set
+ */
+typedef struct {
+ char *name;
+ dlist_t *disknames;
+ dlist_t *hsps;
+} diskset_t;
+
+/*
+ * list of diskset_t for known disksets
+ */
+static dlist_t *_disksets = NULL;
+
+static int add_diskset(
+ char *diskset);
+
+static int add_diskset_diskname(
+ char *diskset,
+ char *diskname);
+
+static int add_diskset_hsp(
+ char *diskset,
+ char *hspname);
+
+static int add_diskset_hsp_spare(
+ char *diskset,
+ char *hspname,
+ char *spare);
+
+static int is_disk_in_local_diskset(
+ dm_descriptor_t disk,
+ boolean_t *bool);
+
+static int is_disk_in_named_diskset(
+ dm_descriptor_t disk,
+ char *dsname,
+ boolean_t *bool);
+
+/* SVM snapshot stuff */
+typedef enum {
+ SVM_DISKSET = 0,
+ SVM_MDB,
+ SVM_STRIPE,
+ SVM_MIRROR,
+ SVM_RAID,
+ SVM_TRANS,
+ SVM_SP,
+ SVM_HSP,
+ SVM_HS,
+ SVM_DRIVE
+} svm_type_t;
+
+typedef struct svm_snap_entry {
+ struct svm_snap_entry *next;
+ char *diskset;
+ svm_type_t type;
+ char *name;
+ char *slice;
+} svm_snap_t;
+
+static svm_snap_t *svm_snapshot(int *errp);
+static void free_svm_snapshot(svm_snap_t *listp);
+
+static char *type_name(svm_type_t type);
+static int add_record(
+ svm_snap_t **listp,
+ char *setname,
+ svm_type_t type,
+ char *mname,
+ char *slice_name);
+static int diskset_info(svm_snap_t **listp, mdsetname_t *sp);
+static void free_names(mdnamelist_t *nlp);
+static int load_svm(svm_snap_t **listp);
+static int new_entry(
+ svm_snap_t **listp,
+ char *sname,
+ svm_type_t type,
+ char *mname,
+ mdsetname_t *sp);
+
+/*
+ * FUNCTION: scan_svm_names(char *diskset)
+ *
+ * INPUT: diskset - a char * disk set name
+ *
+ * PURPOSE: Take a snapshot of the current SVM config.
+ *
+ * Scan it and remember:
+ * 1. all known disk sets
+ * s. the disks in the named disk set
+ * 3. the used device and HSP names in the named disk set
+ * 4. the HSPs in the disk set
+ * 5. the spares in the HSPs
+ */
+int
+scan_svm_names(
+ char *diskset)
+{
+ int ndisks = 0;
+ int nhsps = 0;
+ int ndevices = 0;
+ int nsets = 0;
+
+ int number = 0;
+ int error = 0;
+ svm_snap_t *headp = NULL;
+ svm_snap_t *listp = NULL;
+ char *tablefmt = " %-20s %-10s %-20s %-10s\n";
+
+ oprintf(OUTPUT_TERSE,
+ gettext("\nScanning system SVM configuration...\n"));
+
+ headp = svm_snapshot(&error);
+ if (error != 0) {
+ oprintf(OUTPUT_TERSE,
+ gettext("failed to scan SVM devices\n"));
+ return (error);
+ }
+
+ if (error == 0) {
+ if ((error = get_max_number_of_devices(&_max_devs_cfg)) == 0) {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" configured maximum number of "
+ "volumes: %d\n"),
+ _max_devs_cfg);
+ }
+ }
+
+ if (error == 0) {
+ if ((error = get_max_number_of_disksets(&_max_sets)) == 0) {
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" configured maximum number of "
+ "disk sets: %d\n"),
+ _max_sets);
+ }
+ }
+
+ if (error == 0) {
+ /* array is realloc'ed as necessary */
+ if ((hsps_by_number =
+ (boolean_t *)calloc(_max_hsps, sizeof (boolean_t))) == NULL) {
+ oprintf(OUTPUT_TERSE,
+ gettext("failed to allocate HSP name array\n"));
+ error = ENOMEM;
+ }
+ }
+
+ if (error == 0) {
+ /* array is realloc'ed as necessary */
+ if ((devs_by_number =
+ (boolean_t *)calloc(_max_devs, sizeof (boolean_t))) == NULL) {
+ oprintf(OUTPUT_TERSE,
+ gettext("failed to allocate volume name array\n"));
+ error = ENOMEM;
+ }
+ }
+
+ if ((error == 0) && (get_max_verbosity() >= OUTPUT_DEBUG)) {
+ (void) oprintf(OUTPUT_DEBUG, "\n");
+ (void) oprintf(OUTPUT_DEBUG,
+ tablefmt,
+ gettext("disk set"),
+ gettext("dev type"),
+ gettext("name"),
+ gettext("slice"));
+ (void) oprintf(OUTPUT_DEBUG,
+ " -----------------------------------"
+ "-----------------------------------\n");
+ }
+
+ for (listp = headp; listp != NULL && error == 0; listp = listp->next) {
+
+ oprintf(OUTPUT_DEBUG,
+ tablefmt,
+ listp->diskset,
+ type_name(listp->type),
+ listp->name,
+ listp->slice);
+
+ switch (listp->type) {
+ case SVM_DISKSET:
+
+ error = add_diskset(listp->name);
+ ++nsets;
+ break;
+
+ case SVM_DRIVE:
+
+ error = add_diskset_diskname(listp->diskset, listp->name);
+
+ /* is this drive in the requested diskset? */
+ if (string_case_compare(diskset, listp->diskset) == 0) {
+ ++ndisks;
+ }
+ break;
+
+ case SVM_MIRROR:
+ case SVM_RAID:
+ case SVM_TRANS:
+ case SVM_SP:
+ case SVM_STRIPE:
+
+ /* is this SVM volume in the requested diskset? */
+ if (string_case_compare(diskset, listp->diskset) == 0) {
+
+ /* isolate device name from "poolname/dXXXX" */
+ char *cp = strrchr(listp->name, '/');
+ if (cp != NULL) {
+ ++cp;
+ } else {
+ cp = listp->name;
+ }
+
+ /* BEGIN CSTYLED */
+ /*
+ * names for requested devices and HSPs are remembered
+ * so that the default name generation scheme knows
+ * which names are already being used
+ */
+ /* END CSTYLED */
+ /* extract device number from name "dXXXX" */
+ if (sscanf(cp, "d%d", &number) != EOF) {
+ oprintf(OUTPUT_DEBUG,
+ gettext(" device: %6s number: %3d\n"),
+ cp, number);
+
+ if (number > _max_devs) {
+ /* hit current limit, expand it */
+ boolean_t *tmp =
+ (boolean_t *)realloc((void *)_max_devs,
+ (number * sizeof (boolean_t)));
+
+ if (tmp == NULL) {
+ error = ENOMEM;
+ } else {
+ _max_devs = number;
+ devs_by_number = tmp;
+ }
+ }
+
+ if ((error == 0) &&
+ (devs_by_number[number] == B_FALSE)) {
+ devs_by_number[number] = B_TRUE;
+ ++ndevices;
+ }
+ }
+ }
+ break;
+
+ case SVM_HSP:
+
+ /* is this HSP in the requested diskset? */
+ if (string_case_compare(diskset, listp->diskset) == 0) {
+
+ /* isolate HSP name from "poolname/hspXXX" */
+ char *cp = strrchr(listp->name, '/');
+ if (cp != NULL) {
+ ++cp;
+ } else {
+ cp = listp->name;
+ }
+
+ /* extract pool number from name "hspXXX" */
+ if (sscanf(cp, "hsp%03d", &number) != EOF) {
+ oprintf(OUTPUT_DEBUG,
+ gettext(" HSP: %6s number: %3d\n"),
+ cp, number);
+
+ if (number > _max_hsps) {
+ /* hit our arbitrary limit, double it */
+ boolean_t *tmp =
+ (boolean_t *)realloc((void *)hsps_by_number,
+ 2 * _max_hsps * sizeof (boolean_t));
+
+ if (tmp != NULL) {
+ _max_hsps *= 2;
+ hsps_by_number = tmp;
+ } else {
+ error = ENOMEM;
+ }
+ }
+
+ if ((error == 0) &&
+ (hsps_by_number[number] == B_FALSE)) {
+ hsps_by_number[number] = B_TRUE;
+ error = add_diskset_hsp(diskset, cp);
+ ++nhsps;
+ }
+ }
+ }
+
+ break;
+
+ case SVM_HS:
+
+ /* is this hot spare in the requested disk set? */
+ if (string_case_compare(diskset, listp->diskset) == 0) {
+
+ /* isolate HSP name from "poolname/hspXXXX" */
+ char *cp = strrchr(listp->name, '/');
+ if (cp != NULL) {
+ ++cp;
+ } else {
+ cp = listp->name;
+ }
+
+ error = add_diskset_hsp_spare(diskset, cp, listp->slice);
+ }
+ break;
+
+ case SVM_MDB:
+ default:
+ break;
+ }
+ }
+
+ free_svm_snapshot(headp);
+
+ if (error == 0) {
+ /* available diskset? subtract 1 for the local set */
+ if ((diskset_exists(diskset) != B_TRUE) &&
+ (nsets >= _max_sets)) {
+ volume_set_error(
+ gettext("Disk set \"%s\" cannot be created, the "
+ "maximum number of disk sets (%d) already "
+ "exists.\n"),
+ diskset, _max_sets);
+ error = -1;
+ }
+ }
+
+ if (error == 0) {
+ oprintf(OUTPUT_VERBOSE,
+ gettext("\n Disk set \"%s\" has:\n\n"), diskset);
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" %d drives\n"), ndisks);
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" %d volumes\n"), ndevices);
+ oprintf(OUTPUT_VERBOSE,
+ gettext(" %d HSPs\n"), nhsps);
+ } else {
+ free(hsps_by_number);
+ free(devs_by_number);
+ hsps_by_number = (boolean_t *)NULL;
+ devs_by_number = (boolean_t *)NULL;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: release_svm_names()
+ *
+ * PURPOSE: Release snapshot of the current SVM config.
+ *
+ * Free memory allocated by scan_svm_names()
+ */
+void
+release_svm_names()
+{
+ dlist_t *iter;
+
+ for (iter = _disksets; iter != NULL; iter = iter->next) {
+ diskset_t *diskset = (diskset_t *)iter->obj;
+ dlist_free_items(diskset->disknames, free);
+ dlist_free_items(diskset->hsps, free_devconfig);
+ free(diskset->name);
+ }
+ dlist_free_items(_disksets, free);
+ _disksets = NULL;
+
+ if (hsps_by_number != NULL)
+ free(hsps_by_number);
+ if (devs_by_number != NULL)
+ free(devs_by_number);
+
+ hsps_by_number = (boolean_t *)NULL;
+ devs_by_number = (boolean_t *)NULL;
+}
+
+/*
+ * FUNCTION: diskset_exists(char *diskset)
+ *
+ * INPUT: dsname - a char * diskset name
+ *
+ * RETURNS: boolean_t - B_TRUE if the named diskset exists
+ * B_FALSE otherwise
+ *
+ * PURPOSE: Checks the list of known disk sets and determines
+ * if the input name is in that list.
+ */
+boolean_t
+diskset_exists(
+ char *dsname)
+{
+ dlist_t *iter;
+
+ for (iter = _disksets; iter != NULL; iter = iter->next) {
+ diskset_t *diskset = (diskset_t *)iter->obj;
+ if (string_case_compare(dsname, diskset->name) == 0) {
+ return (B_TRUE);
+ }
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * FUNCTION: add_diskset(char *dsname)
+ *
+ * INPUT: dsname - a char * disk set name
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Add the named disk set to the list of known disk sets.
+ */
+static int
+add_diskset(
+ char *dsname)
+{
+ dlist_t *iter;
+ int error = 0;
+
+ for (iter = _disksets; iter != NULL; iter = iter->next) {
+ diskset_t *diskset = (diskset_t *)iter->obj;
+ if (string_case_compare(diskset->name, dsname) == 0) {
+ break;
+ }
+ }
+
+ if (iter == NULL) {
+
+ dlist_t *item = NULL;
+ diskset_t *diskset = (diskset_t *)calloc(1, sizeof (diskset_t));
+
+ if (diskset == NULL) {
+ error = ENOMEM;
+ } else {
+ diskset->hsps = NULL;
+ diskset->name = strdup(dsname);
+ if (diskset->name == NULL) {
+ free(diskset);
+ error = ENOMEM;
+ } else {
+ if ((item = dlist_new_item(diskset)) == NULL) {
+ free(diskset->name);
+ free(diskset);
+ error = ENOMEM;
+ } else {
+ _disksets = dlist_append(item, _disksets, AT_HEAD);
+ oprintf(OUTPUT_DEBUG,
+ gettext(" added disk set %s \n"), dsname);
+ }
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: add_diskset_diskname(char *diskset, char *diskname)
+ *
+ * INPUT: dsname - a char * disk set name
+ * diskname - a char * disk name
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Add the disk name to the named disk set's list of disks.
+ *
+ * The input diskname is fully qualified with the path
+ * to the raw disk device (/dev/rdsk/cXtXdXsX) which is
+ * not relevant, so it is removed.
+ */
+static int
+add_diskset_diskname(
+ char *dsname,
+ char *diskname)
+{
+ dlist_t *iter;
+ int error = 0;
+
+ for (iter = _disksets; iter != NULL; iter = iter->next) {
+
+ diskset_t *diskset = (diskset_t *)iter->obj;
+ if (string_case_compare(diskset->name, dsname) == 0) {
+
+ dlist_t *item = NULL;
+ char *name = NULL;
+ char *cp = NULL;
+
+ /* trim leading path */
+ if ((cp = strrchr(diskname, '/')) != 0) {
+ if ((name = strdup(cp+1)) == NULL) {
+ error = ENOMEM;
+ }
+ } else if ((name = strdup(diskname)) == NULL) {
+ error = ENOMEM;
+ }
+
+ if ((item = dlist_new_item(name)) == NULL) {
+ free(name);
+ error = ENOMEM;
+ } else {
+ diskset->disknames =
+ dlist_append(item, diskset->disknames, AT_HEAD);
+ }
+
+ break;
+ }
+ }
+
+ if ((error == 0) && (iter == NULL)) {
+ /* new disk set */
+ if ((error = add_diskset(dsname)) == 0) {
+ return (add_diskset_diskname(dsname, diskname));
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: add_diskset_hsp(char *dsname, char *hspname)
+ *
+ * INPUT: dsname - a char * disk set name
+ * hspname - a char * HSP name
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Model a new HSP for the named disk set.
+ *
+ * Metassist can use existing HSPs to service new volumes.
+ *
+ * It is necessary to have a model of what HSPs currently
+ * exist for each disk set.
+ *
+ * This function takes information found during discovery
+ * and turns it into a form usable by the HSP layout code.
+ */
+static int
+add_diskset_hsp(
+ char *dsname,
+ char *hspname)
+{
+ dlist_t *iter;
+ int error = 0;
+
+ for (iter = _disksets; iter != NULL; iter = iter->next) {
+
+ diskset_t *diskset = (diskset_t *)iter->obj;
+
+ if (string_case_compare(diskset->name, dsname) == 0) {
+
+ dlist_t *item = NULL;
+ devconfig_t *hsp = NULL;
+
+ if (((error = new_devconfig(&hsp, TYPE_HSP)) != 0) ||
+ (error = devconfig_set_name(hsp, hspname))) {
+ free_devconfig(hsp);
+ } else {
+ if ((item = dlist_new_item(hsp)) == NULL) {
+ free_devconfig(hsp);
+ error = ENOMEM;
+ } else {
+ diskset->hsps =
+ dlist_append(item, diskset->hsps, AT_TAIL);
+
+ oprintf(OUTPUT_DEBUG,
+ gettext(" added %s to disk set %s\n"),
+ hspname, dsname);
+ }
+ }
+ break;
+ }
+ }
+
+ if ((error == 0) && (iter == NULL)) {
+ if ((error = add_diskset(dsname)) == 0) {
+ return (add_diskset_hsp(dsname, hspname));
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: add_diskset_hsp_spare(char *dsname, char *hspname,
+ * char *sparename)
+ *
+ * INPUT: dsname - a char * diskset name
+ * hspname - a char * HSP name
+ * sparename - a char * hot spare (slice) name
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Locate the named hot spare pool in the named disk set and
+ * add the named spare slice to its list of spares.
+ *
+ * Metassist can use existing HSPs to service new volumes.
+ *
+ * It is necessary to have a model of what HSPs currently
+ * exist for each disk set.
+ *
+ * This function takes information found during discovery
+ * and turns it into a form usable by the HSP layout code.
+ */
+static int
+add_diskset_hsp_spare(
+ char *dsname,
+ char *hspname,
+ char *sparename)
+{
+ dlist_t *iter;
+ int error = 0;
+
+ for (iter = _disksets; iter != NULL; iter = iter->next) {
+
+ diskset_t *diskset = (diskset_t *)iter->obj;
+
+ if (string_case_compare(diskset->name, dsname) == 0) {
+
+ dlist_t *item =
+ dlist_find(
+ diskset->hsps, hspname,
+ compare_string_to_devconfig_name);
+
+ if (item != NULL) {
+
+ /* add spare to HSP */
+ devconfig_t *hsp = (devconfig_t *)item->obj;
+ dm_descriptor_t slice = (dm_descriptor_t)0;
+
+ (void) slice_get_by_name(sparename, &slice);
+ if (slice == (dm_descriptor_t)0) {
+ oprintf(OUTPUT_TERSE,
+ gettext("warning: ignoring nonexistent "
+ "slice %s defined in %s\n"),
+ sparename, hspname);
+ } else {
+
+ uint64_t nbytes = 0;
+ uint32_t index = 0;
+ devconfig_t *spare = NULL;
+
+ /* build a devconfig_t model of the slice */
+ if (((error = slice_get_size(slice, &nbytes)) != 0) ||
+ (error = slice_get_index(slice, &index)) ||
+ (error = new_devconfig(&spare, TYPE_SLICE)) ||
+ (error = devconfig_set_name(spare, sparename)) ||
+ (error = devconfig_set_size(spare, nbytes)) ||
+ (error = devconfig_set_slice_index(spare, index))) {
+ free_devconfig(spare);
+ } else {
+
+ if ((item = dlist_new_item(spare)) == NULL) {
+ error = ENOMEM;
+ free_devconfig(spare);
+ } else {
+ dlist_t *spares;
+ spares = devconfig_get_components(hsp);
+ spares = dlist_append(item, spares, AT_TAIL);
+ devconfig_set_components(hsp, spares);
+
+ oprintf(OUTPUT_DEBUG,
+ gettext(" added %s to %s in "
+ "disk set %s\n"),
+ sparename, hspname, dsname);
+ }
+ }
+ }
+
+ break;
+
+ } else {
+ if ((error = add_diskset_hsp(dsname, hspname)) == 0) {
+ return (add_diskset_hsp_spare(
+ dsname, hspname, sparename));
+ }
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Return a list of disks in the given diskset.
+ *
+ * @param dsname
+ * The name of the named disk set, or "" for the local
+ * set.
+ *
+ * @param disks
+ * RETURN: pointer to the list of disks in the given disk
+ * set
+ *
+ * @return 0 if succesful, non-zero otherwise
+ */
+int
+get_disks_in_diskset(
+ char *dsname,
+ dlist_t **disks)
+{
+ dlist_t *known_disks;
+ int error = 0;
+
+ *disks = NULL;
+
+ if ((error = get_known_disks(&known_disks)) == 0) {
+ dlist_t *iter;
+
+ /* For each known disk... */
+ for (iter = known_disks;
+ iter != NULL && error == 0;
+ iter = iter->next) {
+ dm_descriptor_t disk = (uintptr_t)iter->obj;
+ boolean_t in_diskset = B_FALSE;
+
+ /* If this disk is in the given set... */
+ error = is_disk_in_diskset(disk, dsname, &in_diskset);
+ if (error == 0 && in_diskset == B_TRUE) {
+ dlist_t *item = dlist_new_item((void *)disk);
+ *disks = dlist_append(item, *disks, AT_TAIL);
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: is_disk_in_diskset(dm_descriptor_t disk, char *dsname,
+ * boolean_t *bool)
+ *
+ * INPUT: disk - dm_descriptor_t disk handle
+ * dsname - char * diskset name, or MD_LOCAL_NAME for
+ * the local set.
+ *
+ * OUTPUT: bool - pointer to a boolean_t to hold the result
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Determine if the input disk is known to be in the
+ * given diskset.
+ */
+int
+is_disk_in_diskset(
+ dm_descriptor_t disk,
+ char *dsname,
+ boolean_t *bool)
+{
+ if (string_case_compare(dsname, MD_LOCAL_NAME) == 0) {
+ return (is_disk_in_local_diskset(disk, bool));
+ }
+
+ return (is_disk_in_named_diskset(disk, dsname, bool));
+}
+
+static int
+is_disk_in_local_diskset(
+ dm_descriptor_t disk,
+ boolean_t *bool)
+{
+ dlist_t *iter;
+ dlist_t *aliases = NULL;
+ boolean_t in_named_diskset = B_FALSE;
+ char *name = NULL;
+ int error = 0;
+
+ *bool = B_FALSE;
+
+ error = get_display_name(disk, &name);
+ if (error == 0) {
+
+ error = get_aliases(disk, &aliases);
+ if (error == 0) {
+
+ /* For each known disk set... */
+ for (iter = _disksets;
+ iter != NULL && in_named_diskset == B_FALSE;
+ iter = iter->next) {
+
+ diskset_t *diskset = (diskset_t *)iter->obj;
+ dlist_t *names = diskset->disknames;
+
+ /* Check disk name */
+ in_named_diskset = dlist_contains(
+ names, name, compare_device_names);
+
+ /* Check disk aliases */
+ if (in_named_diskset == B_FALSE) {
+ dlist_t *iter2;
+ for (iter2 = aliases;
+ iter2 != NULL && in_named_diskset == B_FALSE;
+ iter2 = iter2->next) {
+ in_named_diskset = dlist_contains(names,
+ (char *)iter2->obj, compare_device_names);
+ }
+ }
+ }
+ }
+ }
+
+ if (error == 0) {
+ *bool = (in_named_diskset == B_TRUE ? B_FALSE : B_TRUE);
+ }
+
+ return (error);
+}
+
+static int
+is_disk_in_named_diskset(
+ dm_descriptor_t disk,
+ char *dsname,
+ boolean_t *bool)
+{
+ dlist_t *iter;
+ int error = 0;
+ boolean_t in_diskset = B_FALSE;
+
+ *bool = B_FALSE;
+
+ for (iter = _disksets;
+ (iter != NULL) && (in_diskset == B_FALSE);
+ iter = iter->next) {
+
+ diskset_t *diskset = (diskset_t *)iter->obj;
+
+ if (string_case_compare(diskset->name, dsname) == 0) {
+
+ dlist_t *names = diskset->disknames;
+ dlist_t *aliases = NULL;
+ char *name = NULL;
+
+ ((error = get_display_name(disk, &name)) != 0) ||
+ (error = get_aliases(disk, &aliases));
+ if (error != 0) {
+ break;
+ }
+
+ /* check disk name */
+ in_diskset = dlist_contains(names, name, compare_device_names);
+
+ /* check disk aliases */
+ if (in_diskset == B_FALSE) {
+ dlist_t *iter2;
+ for (iter2 = aliases;
+ (iter2 != NULL) && (in_diskset == B_FALSE);
+ iter2 = iter2->next) {
+ in_diskset = dlist_contains(names,
+ (char *)iter2->obj, compare_device_names);
+ }
+ }
+ }
+ }
+
+ *bool = in_diskset;
+
+ return (error);
+}
+
+/*
+ * FUNCTION: is_disk_in_other_diskset(dm_descriptor_t disk, char *dsname,
+ * boolean_t *bool)
+ *
+ * INPUT: disk - dm_descriptor_t disk handle
+ * dsname - char * disk set name
+ *
+ * OUTPUT: bool - pointer to a boolean_t to hold the result.
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Determine if the named disk is known to be in a disk set
+ * other than the named disk set.
+ */
+int
+is_disk_in_other_diskset(
+ dm_descriptor_t disk,
+ char *dsname,
+ boolean_t *bool)
+{
+ boolean_t in_other = B_FALSE;
+ dlist_t *iter;
+ dlist_t *aliases = NULL;
+ char *name = NULL;
+ char *cp = NULL;
+ int error = 0;
+
+ ((error = get_display_name(disk, &name)) != 0) ||
+ (error = get_aliases(disk, &aliases));
+ if (error != 0) {
+ return (error);
+ }
+
+ /*
+ * discard the leading path, it is probably /dev/dsk
+ * and the disk set disk names are all /dev/rdsk/...
+ *
+ * aliases do not have leading paths
+ */
+ cp = strrchr(name, '/');
+ if (cp != NULL) {
+ ++cp;
+ } else {
+ cp = name;
+ }
+ name = cp;
+
+ for (iter = _disksets;
+ (iter != NULL) && (in_other == B_FALSE);
+ iter = iter->next) {
+
+ diskset_t *diskset = (diskset_t *)iter->obj;
+ dlist_t *names = diskset->disknames;
+
+ if (string_case_compare(diskset->name, dsname) == 0) {
+ /* skip named disk set */
+ continue;
+ }
+
+ /* see if disk's name is in disk set's name list */
+ in_other = dlist_contains(names, name, compare_device_names);
+
+ /* see if any of the disk's aliases is in name list */
+ if (in_other == B_FALSE) {
+ dlist_t *iter2;
+ for (iter2 = aliases;
+ (iter2 != NULL) && (in_other == B_FALSE);
+ iter2 = iter2->next) {
+
+ in_other = dlist_contains(names,
+ (char *)iter2->obj, compare_device_names);
+ }
+ }
+ }
+
+ *bool = in_other;
+
+ return (error);
+}
+
+/*
+ * FUNCTION: hsp_get_default_for_diskset(char *diskset,
+ * devconfig_t **hsp)
+ *
+ * INPUT: diskset - char * disk set name
+ *
+ * RETURNS: devconfig_t * - pointer to the first HSP in the disk set
+ * NULL if none found
+ *
+ * PURPOSE: Locate the first HSP in the named disk set.
+ */
+int
+hsp_get_default_for_diskset(
+ char *diskset,
+ devconfig_t **hsp)
+{
+ dlist_t *iter = _disksets;
+
+ *hsp = NULL;
+
+ for (; (iter != NULL) && (*hsp == NULL); iter = iter->next) {
+ diskset_t *set = (diskset_t *)iter->obj;
+ if (string_case_compare(set->name, diskset) == 0) {
+ dlist_t *item = set->hsps;
+ if (item != NULL) {
+ *hsp = item->obj;
+ }
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * FUNCTION: get_n_metadb_replicas(int *nreplicas)
+ *
+ * OUTPUT: nreplicas - pointer to int to hold the result
+ *
+ * RETURNS: int - 0 on success
+ * !0 on failure
+ *
+ * PURPOSE: Check the number of replicas configured for the local set.
+ */
+int
+get_n_metadb_replicas(
+ int *nreplicas)
+{
+ mdsetname_t *sp;
+ md_replicalist_t *rlp = NULL;
+ md_error_t mderror = mdnullerror;
+ int error = 0;
+
+ *nreplicas = 0;
+
+ sp = metasetname(MD_LOCAL_NAME, &mderror);
+ if (!mdisok(&mderror)) {
+ volume_set_error(mde_sperror(&mderror, NULL));
+ mdclrerror(&mderror);
+ error = -1;
+ } else {
+ *nreplicas = metareplicalist(sp, MD_BASICNAME_OK, &rlp, &mderror);
+ if (!mdisok(&mderror)) {
+ volume_set_error(mde_sperror(&mderror, NULL));
+ mdclrerror(&mderror);
+ error = -1;
+ } else if (rlp != NULL) {
+ metafreereplicalist(rlp);
+ rlp = NULL;
+ }
+
+ if (*nreplicas < 0) {
+ *nreplicas = 0;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: hsp_get_by_name(char *diskset, char *name,
+ * devconfig_t **hsp)
+ *
+ * INPUT: diskset - char * disk set name
+ * name - char * HSP name
+ *
+ * OUTPUT: hsp - a devconfig_t * - pointer to hold
+ * the named HSP if none found
+ *
+ * PURPOSE: Locate the named HSP in the named disk set.
+ */
+int
+hsp_get_by_name(
+ char *diskset,
+ char *name,
+ devconfig_t **hsp)
+{
+ dlist_t *iter = _disksets;
+
+ *hsp = NULL;
+
+ for (; (iter != NULL) && (*hsp == NULL); iter = iter->next) {
+ diskset_t *set = (diskset_t *)iter->obj;
+ if (string_case_compare(set->name, diskset) == 0) {
+ dlist_t *item = dlist_find(
+ set->hsps, name, compare_string_to_devconfig_name);
+ if (item != NULL) {
+ *hsp = item->obj;
+ }
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * FUNCTION: is_volume_name_valid(char *name)
+ *
+ * OUTPUT: name - pointer to a char * volume name
+ *
+ * RETURNS: boolean_t - B_TRUE if the input name is valid
+ * B_FALSE otherwise
+ *
+ * PURPOSE: Wrapper around libmeta volume name validation method.
+ */
+boolean_t
+is_volume_name_valid(
+ char *name)
+{
+ return (is_metaname(name));
+}
+
+/*
+ * FUNCTION: is_hsp_name_valid(char *name)
+ *
+ * INPUT: name - char * HSP name
+ *
+ * RETURNS: boolean_t - B_TRUE if the input name is valid
+ * B_FALSE otherwise
+ *
+ * PURPOSE: Wrapper around libmeta HSP name validation method.
+ */
+boolean_t
+is_hsp_name_valid(
+ char *name)
+{
+ return (is_hspname(name));
+}
+
+/*
+ * FUNCTION: extract_index(char *name, char *prefix, char *num_fmt,
+ * int *index)
+ *
+ * INPUT: name - const char * volume name
+ * prefix - const char * fixed part of format string
+ * num_fmt - const char * format of number to extract (e.g. %d)
+ *
+ * OUTPUT: index - pointer to int to hold numeric part of name
+ *
+ * RETURNS: boolean_t - B_TRUE if the input name is parsed correctly
+ * B_FALSE otherwise
+ *
+ * PURPOSE: Extract the numeric portion of a device name for use
+ * by higher-level functions.
+ */
+static boolean_t
+extract_index(
+ const char *name,
+ const char *prefix,
+ const char *num_fmt,
+ int *index)
+{
+ char buf[MAXNAMELEN];
+ const char *cp;
+ const char *fmt = buf;
+
+ if ((cp = strrchr(name, '/')) != NULL) {
+ ++cp;
+ } else {
+ cp = name;
+ }
+
+ (void) snprintf(buf, sizeof (buf), "%s%s", prefix, num_fmt);
+ if (sscanf(cp, fmt, index) == 1)
+ return (B_TRUE);
+ else
+ return (B_FALSE);
+}
+
+/*
+ * FUNCTION: is_volume_name_in_range(char *name)
+ *
+ * INPUT: name - char * volume name
+ *
+ * RETURNS: boolean_t - B_TRUE if the input name is in the allowed
+ * range of names
+ * B_FALSE otherwise
+ *
+ * PURPOSE: Determine if the input volume name is within the allowed
+ * range of device names (0 <= n < max # of devices configured).
+ */
+boolean_t
+is_volume_name_in_range(
+ char *name)
+{
+ int index = -1;
+
+ if (extract_index(name, _dev_prefix, "%d", &index)) {
+ if (index >= 0 && index < _max_devs_cfg) {
+ return (B_TRUE);
+ }
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * FUNCTION: reserve_volume_name(char *name)
+ *
+ * INPUT: name - a char * volume name
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Mark a volume name/number as used.
+ *
+ * Assumes that the input name has been validated.
+ *
+ * if the name is not currently available, return -1
+ */
+int
+reserve_volume_name(
+ char *name)
+{
+ int index = -1;
+
+ if (extract_index(name, _dev_prefix, "%d", &index)) {
+ if (devs_by_number[index] != B_TRUE) {
+ devs_by_number[index] = B_TRUE;
+ return (0);
+ }
+ }
+
+ return (-1);
+}
+
+/*
+ * FUNCTION: reserve_hsp_name(char *name)
+ *
+ * INPUT: name - a char * hsp name
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Mark a HSP name/number as used.
+ *
+ * Assumes that the input name has been validated.
+ *
+ * if the name is not currently available, return -1
+ */
+int
+reserve_hsp_name(
+ char *name)
+{
+ int index = -1;
+
+ if (extract_index(name, _hsp_prefix, "%03d", &index)) {
+ if (hsps_by_number[index] != B_TRUE) {
+ hsps_by_number[index] = B_TRUE;
+ return (0);
+ }
+ }
+
+ return (-1);
+}
+
+/*
+ * FUNCTION: release_volume_name(char *name)
+ *
+ * INPUT: name - a char * volume name
+ *
+ * PURPOSE: release the input volume name.
+ *
+ * Extract volume number from the input name
+ * and use it to index into the array of used
+ * volume numbers. Make that volume number
+ * available for use again.
+ */
+void
+release_volume_name(
+ char *name)
+{
+ int index = -1;
+
+ if (name != NULL && extract_index(name, _dev_prefix, "%d", &index)) {
+ oprintf(OUTPUT_DEBUG,
+ gettext("released volume name %s%d\n"),
+ _dev_prefix, index);
+ devs_by_number[index] = B_FALSE;
+ }
+}
+
+/*
+ * FUNCTION: release_hsp_name(char *name)
+ *
+ * INPUT: name - a char * HSP name
+ *
+ * PURPOSE: release the input HSP name.
+ *
+ * Extract volume number from the input name
+ * and use it to index into the array of used
+ * hsp numbers. Make that hsp number available
+ * for use again.
+ */
+void
+release_hsp_name(
+ char *name)
+{
+ int index = -1;
+
+ if (name != NULL && extract_index(name, _hsp_prefix, "%d", &index)) {
+ oprintf(OUTPUT_DEBUG,
+ gettext("released hsp name %s%d\n"),
+ _hsp_prefix, index);
+ hsps_by_number[index] = B_FALSE;
+ }
+}
+
+/*
+ * FUNCTION: get_next_volume_name(char **name)
+ *
+ * OUTPUT: name - pointer to a char * to hold volume name
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: generate a new volume name using the standard device
+ * name prefix and the lowest available device number.
+ *
+ * if type == MIRROR, determine the next available mirror
+ * name according to the convention that a mirror name is
+ * a multiple of 10.
+ *
+ * If such a name is unavailable, use the next available name.
+ */
+int
+get_next_volume_name(
+ char **name,
+ component_type_t type)
+{
+ int next = 0;
+
+ for (next = 0; next < _max_devs_cfg; ++next) {
+ if ((type == TYPE_MIRROR && ((next % 10) != 0)) ||
+ (type != TYPE_MIRROR && ((next % 10) == 0))) {
+ /* use/save multiples of 10 for mirrors */
+ continue;
+ }
+ if (devs_by_number[next] != B_TRUE) {
+ break;
+ }
+ }
+
+ if ((next == _max_devs_cfg) && (type == TYPE_MIRROR)) {
+ /* try next sequentially available name */
+ for (next = 0; next < _max_devs_cfg; ++next) {
+ if (devs_by_number[next] != B_TRUE) {
+ break;
+ }
+ }
+ }
+
+ if (next == _max_devs_cfg) {
+ volume_set_error(
+ gettext("ran out of logical volume names.\n"));
+ return (-1);
+ }
+
+ *name = (char *)calloc(MAXNAMELEN, sizeof (char));
+ if (*name == NULL) {
+ return (ENOMEM);
+ }
+
+ (void) snprintf(*name, MAXNAMELEN-1, "%s%d", _dev_prefix, next);
+
+ devs_by_number[next] = B_TRUE;
+ return (0);
+}
+
+/*
+ * FUNCTION: get_next_submirror_name(char *mname, char **subname)
+ *
+ * INPUT: mname - pointer to a char * mirror name
+ * OUTPUT: subname - pointer to a char * to hold submirror name
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Determine the next available submirror name according
+ * to the convention that each submirror name is a sequential
+ * increment of its mirror's name.
+ *
+ * If such a name is unavailable, return the next sequentially
+ * available volume name.
+ */
+int
+get_next_submirror_name(
+ char *mname,
+ char **subname)
+{
+ char buf[MAXNAMELEN];
+ int error = 0;
+ int next = 0;
+ int i = 0;
+
+ *subname = NULL;
+
+ /* try next sequential name: mirror + 1... */
+ if (extract_index(mname, _dev_prefix, "%d", &next)) {
+ for (i = next + 1; i < _max_devs_cfg; i++) {
+ if ((i % 10) == 0) {
+ /* save for mirrors */
+ continue;
+ }
+ if (devs_by_number[i] == B_FALSE) {
+ (void) snprintf(buf, MAXNAMELEN-1, "%s%d", _dev_prefix, i);
+ if ((*subname = strdup(buf)) != NULL) {
+ devs_by_number[i] = B_TRUE;
+ } else {
+ error = ENOMEM;
+ }
+ break;
+ }
+ }
+ }
+
+ if ((error == 0) && (*subname == NULL)) {
+ /* name adhering to convention isn't available, */
+ /* use next sequentially available name */
+ error = get_next_volume_name(subname, TYPE_STRIPE);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: get_next_hsp_name(char **name)
+ *
+ * OUTPUT: name - pointer to a char * to hold name
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Helper which generates a new hotsparepool name
+ * using the standard name prefix and the lowest
+ * available hsp number.
+ */
+int
+get_next_hsp_name(
+ char **name)
+{
+ int next = 0;
+
+ for (next = 0; next < _max_hsps; ++next) {
+ if (hsps_by_number[next] != B_TRUE) {
+ break;
+ }
+ }
+
+ if (next == _max_hsps) {
+ volume_set_error(gettext("ran out of HSP names"));
+ return (-1);
+ }
+
+ *name = (char *)calloc(MAXNAMELEN, sizeof (char));
+ if (*name == NULL) {
+ oprintf(OUTPUT_TERSE,
+ gettext("failed to allocate volume name string, "
+ "out of memory"));
+ return (ENOMEM);
+ }
+
+ (void) snprintf(*name, MAXNAMELEN-1, "%s%03d", _hsp_prefix, next);
+
+ hsps_by_number[next] = B_TRUE;
+
+ return (0);
+}
+
+static char *
+type_name(
+ svm_type_t type)
+{
+ switch (type) {
+ case SVM_DISKSET: return (gettext("disk set"));
+ case SVM_MDB: return (gettext("metadb"));
+ case SVM_STRIPE: return (gettext("stripe"));
+ case SVM_MIRROR: return (gettext("mirror"));
+ case SVM_RAID: return (gettext("raid"));
+ case SVM_TRANS: return (gettext("trans"));
+ case SVM_SP: return (gettext("soft partition"));
+ case SVM_HSP: return (gettext("hot spare pool"));
+ case SVM_HS: return (gettext("hot spare"));
+ case SVM_DRIVE: return (gettext("drive"));
+ default: return (gettext("unknown"));
+ }
+}
+
+static svm_snap_t *
+svm_snapshot(int *errp)
+{
+ svm_snap_t *svm_listp = NULL;
+
+ *errp = 0;
+
+ /* initialize the cluster library entry points */
+ if (sdssc_bind_library() == SDSSC_ERROR) {
+
+ volume_set_error(gettext("sdssc_bin_library() failed\n"));
+ *errp = -1;
+
+ } else {
+
+ /* load the SVM cache */
+ *errp = load_svm(&svm_listp);
+
+ if (*errp != 0) {
+ free_svm_snapshot(svm_listp);
+ svm_listp = NULL;
+ }
+
+ }
+
+ return (svm_listp);
+}
+
+static void
+free_svm_snapshot(svm_snap_t *listp) {
+
+ svm_snap_t *nextp;
+
+ while (listp != NULL) {
+ nextp = listp->next;
+ free((void *)listp->diskset);
+ free((void *)listp->name);
+ free((void *)listp->slice);
+ free((void *)listp);
+ listp = nextp;
+ }
+}
+
+static int
+add_record(
+ svm_snap_t **listp,
+ char *setname,
+ svm_type_t type,
+ char *mname,
+ char *slice_name)
+{
+ svm_snap_t *sp;
+
+ sp = (svm_snap_t *)malloc(sizeof (svm_snap_t));
+ if (sp == NULL) {
+ return (ENOMEM);
+ }
+
+ if ((sp->diskset = strdup(setname)) == NULL) {
+ free(sp);
+ return (ENOMEM);
+ }
+
+ if ((sp->name = strdup(mname)) == NULL) {
+ free(sp->diskset);
+ free(sp);
+ return (ENOMEM);
+ }
+
+ sp->type = type;
+
+ if ((sp->slice = strdup(slice_name)) == NULL) {
+ free(sp->diskset);
+ free(sp->name);
+ free(sp);
+ return (ENOMEM);
+ }
+
+ sp->next = *listp;
+ *listp = sp;
+
+ return (0);
+}
+
+static int
+diskset_info(
+ svm_snap_t **listp,
+ mdsetname_t *sp)
+{
+ md_error_t error = mdnullerror;
+ md_replicalist_t *replica_list = NULL;
+ md_replicalist_t *mdbp;
+ mdnamelist_t *nlp;
+ mdnamelist_t *trans_list = NULL;
+ mdnamelist_t *mirror_list = NULL;
+ mdnamelist_t *raid_list = NULL;
+ mdnamelist_t *stripe_list = NULL;
+ mdnamelist_t *sp_list = NULL;
+ mdhspnamelist_t *hsp_list = NULL;
+
+ if (metareplicalist(sp, MD_BASICNAME_OK, &replica_list, &error) < 0) {
+ /* there are no metadb's; that is ok, no need to check the rest */
+ mdclrerror(&error);
+ return (0);
+ }
+ mdclrerror(&error);
+
+ for (mdbp = replica_list; mdbp != NULL; mdbp = mdbp->rl_next) {
+ char size[MAXPATHLEN];
+
+ (void) snprintf(size, sizeof (size), "%d",
+ (int)mdbp->rl_repp->r_nblk);
+
+ if (new_entry(listp, mdbp->rl_repp->r_namep->cname, SVM_MDB, size,
+ sp)) {
+ metafreereplicalist(replica_list);
+ return (ENOMEM);
+ }
+ }
+ metafreereplicalist(replica_list);
+
+ if (meta_get_trans_names(sp, &trans_list, 0, &error) >= 0) {
+ for (nlp = trans_list; nlp != NULL; nlp = nlp->next) {
+ if (new_entry(listp, nlp->namep->cname, SVM_TRANS,
+ nlp->namep->cname, sp)) {
+ free_names(trans_list);
+ return (ENOMEM);
+ }
+ }
+
+ free_names(trans_list);
+ }
+ mdclrerror(&error);
+
+ if (meta_get_mirror_names(sp, &mirror_list, 0, &error) >= 0) {
+ for (nlp = mirror_list; nlp != NULL; nlp = nlp->next) {
+ if (add_record(listp, sp->setname, SVM_MIRROR,
+ nlp->namep->cname, "")) {
+ free_names(mirror_list);
+ return (ENOMEM);
+ }
+ }
+
+ free_names(mirror_list);
+ }
+ mdclrerror(&error);
+
+ if (meta_get_raid_names(sp, &raid_list, 0, &error) >= 0) {
+ for (nlp = raid_list; nlp != NULL; nlp = nlp->next) {
+ mdname_t *mdn;
+ md_raid_t *raid;
+
+ mdn = metaname(&sp, nlp->namep->cname, &error);
+ mdclrerror(&error);
+ if (mdn == NULL) {
+ continue;
+ }
+
+ raid = meta_get_raid(sp, mdn, &error);
+ mdclrerror(&error);
+
+ if (raid != NULL) {
+ int i;
+
+ for (i = 0; i < raid->cols.cols_len; i++) {
+ if (new_entry(listp,
+ raid->cols.cols_val[i].colnamep->cname, SVM_RAID,
+ nlp->namep->cname, sp)) {
+ free_names(raid_list);
+ return (ENOMEM);
+ }
+ }
+ }
+ }
+
+ free_names(raid_list);
+ }
+ mdclrerror(&error);
+
+ if (meta_get_stripe_names(sp, &stripe_list, 0, &error) >= 0) {
+ for (nlp = stripe_list; nlp != NULL; nlp = nlp->next) {
+ mdname_t *mdn;
+ md_stripe_t *stripe;
+
+ mdn = metaname(&sp, nlp->namep->cname, &error);
+ mdclrerror(&error);
+ if (mdn == NULL) {
+ continue;
+ }
+
+ stripe = meta_get_stripe(sp, mdn, &error);
+ mdclrerror(&error);
+
+ if (stripe != NULL) {
+ int i;
+
+ for (i = 0; i < stripe->rows.rows_len; i++) {
+ md_row_t *rowp;
+ int j;
+
+ rowp = &stripe->rows.rows_val[i];
+
+ for (j = 0; j < rowp->comps.comps_len; j++) {
+ md_comp_t *component;
+
+ component = &rowp->comps.comps_val[j];
+ if (new_entry(listp, component->compnamep->cname,
+ SVM_STRIPE, nlp->namep->cname, sp)) {
+ free_names(stripe_list);
+ return (ENOMEM);
+ }
+ }
+ }
+ }
+ }
+
+ free_names(stripe_list);
+ }
+ mdclrerror(&error);
+
+ if (meta_get_sp_names(sp, &sp_list, 0, &error) >= 0) {
+ for (nlp = sp_list; nlp != NULL; nlp = nlp->next) {
+ mdname_t *mdn;
+ md_sp_t *soft_part;
+
+ mdn = metaname(&sp, nlp->namep->cname, &error);
+ mdclrerror(&error);
+ if (mdn == NULL) {
+ continue;
+ }
+
+ soft_part = meta_get_sp(sp, mdn, &error);
+ mdclrerror(&error);
+
+ if (soft_part != NULL) {
+ if (new_entry(listp, soft_part->compnamep->cname, SVM_SP,
+ nlp->namep->cname, sp)) {
+ free_names(sp_list);
+ return (ENOMEM);
+ }
+ }
+ }
+
+ free_names(sp_list);
+ }
+ mdclrerror(&error);
+
+ if (meta_get_hsp_names(sp, &hsp_list, 0, &error) >= 0) {
+ mdhspnamelist_t *nlp;
+
+ for (nlp = hsp_list; nlp != NULL; nlp = nlp->next) {
+ md_hsp_t *hsp;
+
+ hsp = meta_get_hsp(sp, nlp->hspnamep, &error);
+ mdclrerror(&error);
+ if (hsp != NULL) {
+ int i;
+
+ for (i = 0; i < hsp->hotspares.hotspares_len; i++) {
+ md_hs_t *hs;
+
+ hs = &hsp->hotspares.hotspares_val[i];
+
+ if (add_record(listp, sp->setname, SVM_HS,
+ nlp->hspnamep->hspname, hs->hsnamep->bname)) {
+ metafreehspnamelist(hsp_list);
+ return (ENOMEM);
+ }
+ }
+ }
+
+ if (add_record(listp, sp->setname, SVM_HSP,
+ nlp->hspnamep->hspname, "")) {
+ metafreehspnamelist(hsp_list);
+ return (ENOMEM);
+ }
+ }
+
+ metafreehspnamelist(hsp_list);
+ }
+
+ mdclrerror(&error);
+
+ return (0);
+}
+
+static void
+free_names(
+ mdnamelist_t *nlp)
+{
+ mdnamelist_t *p;
+
+ for (p = nlp; p != NULL; p = p->next) {
+ meta_invalidate_name(p->namep);
+ }
+ metafreenamelist(nlp);
+}
+
+/*
+ * Create a list of SVM devices
+ */
+static int
+load_svm(
+ svm_snap_t **listp)
+{
+ int max_sets;
+ md_error_t error = mdnullerror;
+ int i;
+
+ if ((max_sets = get_max_sets(&error)) == 0) {
+ return (0);
+ }
+
+ if (!mdisok(&error)) {
+ volume_set_error(
+ gettext("failed to get maximum number of disk sets.\n"));
+ mdclrerror(&error);
+ return (-1);
+ }
+
+ /* for each possible set number, see if we really have a disk set */
+ for (i = 0; i < max_sets; i++) {
+ mdsetname_t *sp;
+
+ if ((sp = metasetnosetname(i, &error)) == NULL) {
+ if (!mdisok(&error) && error.info.errclass == MDEC_RPC) {
+ /* rpc error - no metasets */
+ break;
+ }
+
+ mdclrerror(&error);
+ continue;
+ }
+
+ mdclrerror(&error);
+
+ if (add_record(listp, sp->setname, SVM_DISKSET, sp->setname, "")) {
+ metaflushsetname(sp);
+ return (ENOMEM);
+ }
+
+ /* check for drives in disk sets */
+ if (sp->setno != 0) {
+ md_drive_desc *dd;
+
+ dd = metaget_drivedesc(sp, MD_BASICNAME_OK | PRINT_FAST,
+ &error);
+ mdclrerror(&error);
+ for (; dd != NULL; dd = dd->dd_next) {
+ if (add_record(listp, sp->setname, SVM_DRIVE,
+ dd->dd_dnp->rname, "")) {
+ metaflushsetname(sp);
+ return (ENOMEM);
+ }
+ }
+ }
+
+ if (diskset_info(listp, sp)) {
+ metaflushsetname(sp);
+ return (ENOMEM);
+ }
+
+ metaflushsetname(sp);
+ }
+
+ mdclrerror(&error);
+
+ return (0);
+}
+
+/* determine if 'sp' is built on a slice */
+static int
+new_entry(
+ svm_snap_t **listp,
+ char *slice_name,
+ svm_type_t type,
+ char *mname,
+ mdsetname_t *sp)
+{
+ mdname_t *mdn;
+ md_error_t error = mdnullerror;
+
+ mdn = metaname(&sp, slice_name, &error);
+ if (!mdisok(&error)) {
+ mdn = NULL;
+ }
+ mdclrerror(&error);
+
+ if (mdn != NULL && (
+ mdn->drivenamep->type == MDT_ACCES ||
+ mdn->drivenamep->type == MDT_COMP ||
+ mdn->drivenamep->type == MDT_FAST_COMP)) {
+
+ return (add_record(listp, sp->setname, type, mname, mdn->bname));
+ } else {
+ return (add_record(listp, sp->setname, type, mname, ""));
+ }
+}
+
+/*
+ * FUNCTION: get_default_stripe_interlace()
+ *
+ * RETURNS: uint64_t - default stripe interlace value
+ *
+ * PURPOSE: Helper which retrieves the default stripe interlace
+ * from libmeta.
+ */
+uint64_t
+get_default_stripe_interlace()
+{
+ /* convert back to bytes */
+ return ((uint64_t)meta_default_stripe_interlace() * DEV_BSIZE);
+}
+
+/*
+ * FUNCTION: get_max_number_of_devices(int *max)
+ *
+ * OUTPUT: max - pointer to int to hold the configured maximum number
+ * of SVM devices
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Helper which determines the maximum number of allowed
+ * SVM devices configured for the system.
+ *
+ * Wrapper around libmeta function meta_get_max_nunits().
+ */
+int
+get_max_number_of_devices(
+ int *max)
+{
+ md_error_t mderror = mdnullerror;
+
+ *max = meta_get_nunits(&mderror);
+ if (!mdisok(&mderror)) {
+ volume_set_error(mde_sperror(&mderror, NULL));
+ mdclrerror(&mderror);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * FUNCTION: get_max_number_of_disksets(int *max)
+ *
+ * OUTPUT: max - pointer to in to hold the configured maximum number
+ * of disk sets
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise
+ *
+ * PURPOSE: Helper which determines the maximum number of allowed
+ * disk sets which has been configured for the system.
+ *
+ * Wrapper around libmeta function get_max_sets().
+ */
+int
+get_max_number_of_disksets(
+ int *max)
+{
+ md_error_t mderror = mdnullerror;
+
+ *max = get_max_sets(&mderror);
+ if (!mdisok(&mderror)) {
+ volume_set_error(mde_sperror(&mderror, NULL));
+ mdclrerror(&mderror);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * FUNCTION: is_reserved_replica_slice_index(char *diskset, char *dname,
+ * uint32_t index, boolean_t *bool)
+ *
+ * INPUT: diskset - char * disk set name
+ * dname - char * disk name
+ * index - integer index of interest
+ *
+ * OUTPUT: bool - pointer to a boolean_t to hold the result
+ *
+ * RETURNS: int - 0 - success
+ * !0 - failure
+ *
+ * PURPOSE: Helper which determines if the input slice index on
+ * the named disk in the named disk set is the replica
+ * slice that is reserved on disks in disk sets.
+ *
+ * The named disk is assumed to be in the named disk set.
+ *
+ * Determines if metassist is being run in a simulated
+ * hardware enironment, if not the libmeta function to
+ * determine the replica slice index is called.
+ *
+ * If simulation is active, then a local implementation
+ * is used to determine the replica slice index.
+ */
+int
+is_reserved_replica_slice_index(
+ char *diskset,
+ char *dname,
+ uint32_t index,
+ boolean_t *bool)
+{
+ int error = 0;
+ boolean_t sim = B_FALSE;
+ static char *simfile = "METASSISTSIMFILE";
+
+ sim = ((getenv(simfile) != NULL) && (strlen(getenv(simfile)) > 0));
+
+ if (sim != B_TRUE) {
+
+ /* sim disabled: use meta_replicaslice() */
+
+ md_error_t mderror = mdnullerror;
+ mdsetname_t *sp;
+ mddrivename_t *dnp;
+ uint_t replicaslice;
+
+ /* slice assumed to be on disk in the named disk set */
+ sp = metasetname(diskset, &mderror);
+ if (!mdisok(&mderror)) {
+ volume_set_error(mde_sperror(&mderror, NULL));
+ mdclrerror(&mderror);
+ return (-1);
+ }
+
+ dnp = metadrivename(&sp, dname, &mderror);
+ if (!mdisok(&mderror)) {
+ volume_set_error(mde_sperror(&mderror, NULL));
+ mdclrerror(&mderror);
+ return (-1);
+ }
+
+ if (meta_replicaslice(dnp, &replicaslice, &mderror) != 0) {
+ volume_set_error(mde_sperror(&mderror, NULL));
+ mdclrerror(&mderror);
+ return (-1);
+ }
+
+ *bool = (replicaslice == (uint_t)index);
+
+ } else {
+
+ dm_descriptor_t disk;
+ boolean_t efi = B_FALSE;
+
+ /* sim enabled: use same logic as meta_replicaslice() */
+ ((error = disk_get_by_name(dname, &disk)) != 0) ||
+ (error = disk_get_is_efi(disk, &efi));
+ if (error == 0) {
+
+ if (efi == B_FALSE) {
+ *bool = (index == MD_SLICE7);
+ } else {
+ *bool = (index == MD_SLICE6);
+ }
+ }
+ }
+
+ return (error);
+}
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_svm_util.h b/usr/src/cmd/lvm/metassist/layout/layout_svm_util.h
new file mode 100644
index 0000000000..d979144146
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_svm_util.h
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+#ifndef _VOLUME_SVM_UTIL_H
+#define _VOLUME_SVM_UTIL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "volume_devconfig.h"
+
+/*
+ * scan existing SVM config for the named diskset
+ * and build lists of device, HSP and diskset names.
+ */
+extern int scan_svm_names(char *diskset);
+extern void release_svm_names();
+
+extern int hsp_get_default_for_diskset(char *diskset,
+ devconfig_t **hsp);
+extern int hsp_get_by_name(char *diskset, char *hspname,
+ devconfig_t **hsp);
+
+extern int get_next_volume_name(char **name,
+ component_type_t type);
+extern int get_next_hsp_name(char **name);
+extern int get_next_submirror_name(char *mname, char **subname);
+
+extern int reserve_volume_name(char *name);
+extern int reserve_hsp_name(char *name);
+
+extern void release_volume_name(char *name);
+extern void release_hsp_name(char *name);
+
+extern boolean_t is_volume_name_valid(char *name);
+extern boolean_t is_hsp_name_valid(char *name);
+
+extern boolean_t is_volume_name_in_range(char *name);
+
+extern int get_disks_in_diskset(char *dsname, dlist_t **disks);
+
+extern int is_disk_in_diskset(
+ dm_descriptor_t disk, char *diskset, boolean_t *bool);
+extern int is_disk_in_other_diskset(
+ dm_descriptor_t disk, char *diskset, boolean_t *bool);
+
+extern boolean_t diskset_exists(char *name);
+extern uint64_t get_default_stripe_interlace();
+
+extern int get_n_metadb_replicas(int *nreplicas);
+extern int get_max_number_of_devices(int *max);
+extern int get_max_number_of_disksets(int *max);
+
+extern int is_reserved_replica_slice_index(
+ char *diskset, char *dname, uint32_t index, boolean_t *bool);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VOLUME_SVM_UTIL_H */
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_validate.c b/usr/src/cmd/lvm/metassist/layout/layout_validate.c
new file mode 100644
index 0000000000..2e65b5cf71
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_validate.c
@@ -0,0 +1,1994 @@
+/*
+ * 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 <libintl.h>
+
+#include "metassist.h"
+#include "volume_dlist.h"
+#include "volume_error.h"
+#include "volume_string.h"
+#include "volume_output.h"
+
+#define _LAYOUT_VALIDATE_C
+
+#include "layout_discovery.h"
+#include "layout_dlist_util.h"
+#include "layout_device_cache.h"
+#include "layout_device_util.h"
+#include "layout_request.h"
+#include "layout_slice.h"
+#include "layout_svm_util.h"
+#include "layout_validate.h"
+
+/*
+ * This module contains the majority of the validation code which
+ * layout applies to input requests. The assumption/agreement with
+ * the controller implementation is that requests passed into layout
+ * have undergone syntactic validation and that layout is responsible
+ * for semantic validation.
+ *
+ * The semantic validation that is handled:
+ *
+ * 1. For a toplevel diskset request, validate:
+ *
+ * - the number of disksets is not exceeded
+ * - the number of devices is not exceeded
+ *
+ * (These items are not directly validated within this module,
+ * but it is useful to document that they are handled somewhere).
+ *
+ * 2. For any devconfig_t representing a volume request, verify that:
+ *
+ * - all HSP names are semantically valid. The name should conform
+ * to the HSP naming convention: hspXXX.
+ *
+ * - all concat, stripe, mirror, and volume names refer to
+ * unused, semantically valid metadevice names. Examples of
+ * bad data:
+ *
+ * - a valid volume name that is already in use (d0, d10)
+ *
+ * - a valid volume name that is used two or more times to
+ * refer to new elements in the request.
+ *
+ * - a valid volume name that is out of range (d99877,
+ * d44356) or exceeds the maximum number of possible
+ * volumes given the current SVM configuration.
+ *
+ * - all available and unavailable device specifications refer
+ * to existing controllers, disks, or slices on the system.
+ * Examples of bad data:
+ *
+ * - a valid but non-existent controller (c23, c2)
+ * - a valid but non-existent disk (c0t0d8, c1t0d0)
+ * - a valid slice on a non-existent disk or controller
+ * (c0t0d8s7, c1t0d05)
+ * - a valid slice on an existing disk (c0t0d0s12,
+ * c0t0d0s9)
+ *
+ * - any typed volume request that explicitly specifies components
+ * requires additional validation to detect syntactically valid
+ * expressions that are semantically ambiguous:
+ *
+ * a concat request that:
+ * - specifies size and components is invalid
+ *
+ * a stripe request that:
+ * - specifies size and components is invalid
+ * - specifies mincomp and components but not enough
+ * components is invalid
+ * - specifies maxcomp and components but too many
+ * components is invalid
+ *
+ * a HSP request that:
+ * - specifies components that are not appropriate for
+ * the volumes the HSP serves is invalid (?)
+ *
+ * a stripe, concat or HSP request that:
+ * - specifies a component that was used in a prior
+ * request is invalid
+ * - specifies a component that does not exist in the
+ * diskset is invalid (e.g., c0t0d0s0, but c0t0d0 is
+ * not yet in the diskset)
+ *
+ * a mirror request that:
+ * - specifies nsubs and components but not enough
+ * components is invalid
+ * - specifies components and the components specify
+ * different sizes results in a WARNING since the total
+ * usable capacity of the mirror is determined by the
+ * smallest of its submirrors.
+ * - specifies components and the components specify
+ * components results in a WARNING since the submirrors
+ * may end up with different sizes
+ */
+static int validate_request_name(
+ devconfig_t *req,
+ component_type_t type);
+
+static int validate_request_size(
+ devconfig_t *req,
+ component_type_t type);
+
+static int validate_minimum_size(
+ uint64_t nbytes);
+
+static uint64_t apply_layout_overhead_factor(
+ uint64_t req_size);
+
+static int get_space_available_for_request(
+ devconfig_t *request,
+ dlist_t *usable_slices,
+ uint64_t *avail_space);
+
+static int do_available_space_check(
+ uint64_t req_size,
+ uint64_t raw_avail_space,
+ devconfig_t *request,
+ dlist_t *usable_slices);
+
+static int validate_request_redundancy_level(
+ devconfig_t *req);
+
+static int validate_request_npaths(
+ devconfig_t *req);
+
+static int validate_request_submirrors(
+ devconfig_t *req);
+
+static int validate_submirror_types(
+ dlist_t *submirrors);
+
+static int validate_submirror_number(
+ devconfig_t *req,
+ dlist_t *submirrors);
+
+static int validate_submirror_sizes(
+ devconfig_t *req,
+ dlist_t *submirrors);
+
+static int validate_submirror_size_and_components(
+ devconfig_t *submir,
+ uint64_t mirror_size,
+ uint64_t *assumed_size,
+ dlist_t **submirs_with_size,
+ dlist_t **submirs_with_comps,
+ dlist_t **submirs_no_size_or_comps);
+
+static int validate_slice_components(
+ devconfig_t *req,
+ component_type_t type);
+
+static char *get_device_aliases_string(
+ dm_descriptor_t desc);
+
+static int validate_device_array(
+ char **array,
+ char *which,
+ dlist_t **list);
+
+static int add_reserved_name(char *name);
+static boolean_t is_rsvd_name(char *name);
+static dlist_t *_rsvd_names = NULL;
+
+/*
+ * FUNCTION: release_validatation_caches()
+ *
+ * RETURNS: int - 0
+ *
+ * PURPOSE: Cleanup function.
+ *
+ * Purges list of reserved volume names. Should be called
+ * after all layout requests have been processed.
+ */
+int
+release_validation_caches()
+{
+ dlist_free_items(_rsvd_names, NULL);
+ _rsvd_names = NULL;
+
+ return (0);
+}
+
+/*
+ * FUNCTION: validate_basic_svm_config()
+ *
+ * RETURNS: int - 0 on success
+ * !0 on failure
+ *
+ * PURPOSE: Check to see if the local set metadb replicas have been created.
+ *
+ * Makes sure at least 1 metadb replica exists for the local set.
+ */
+int
+validate_basic_svm_config()
+{
+ int error = 0;
+ int nreplicas = 0;
+
+ if ((error = get_n_metadb_replicas(&nreplicas)) == 0) {
+ if (nreplicas == 0) {
+ volume_set_error(
+ gettext("Failed: State database replicas must "
+ "exist before using %s.\n"
+ "See metadb(1M) and %s(1M)."),
+ progname, progname);
+ error = -1;
+ } else {
+ oprintf(OUTPUT_DEBUG,
+ gettext("%d metadb replicas found.\n"),
+ nreplicas);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: validate_request_sizes(devconfig_t *req)
+ *
+ * INPUT: req: a devconfig_t pointer to the toplevel request
+ *
+ * RETURNS: int - 0 on success
+ * !0 on failure
+ *
+ * PURPOSE: Check to see if the any of the individual volume request
+ * sizes exceeds the raw available space on the system or
+ * the space available to that specific request.
+ *
+ * Check to see if the total space for all requests exceeds
+ * the raw available space.
+ *
+ * If any check fails, stop checking, emit an error and
+ * return -1.
+ *
+ * Note: this function must be called after the slice
+ * usages have been determined and the list of usable
+ * slices has been generated.
+ */
+int
+validate_request_sizes(
+ devconfig_t *request)
+{
+ int error = 0;
+ dlist_t *usable_slices;
+ dlist_t *iter;
+ char bad_rqst_info[BUFSIZ];
+ uint64_t bad_rqst_space = 0;
+ uint64_t total_rqst_space = 0;
+ uint64_t raw_space = 0;
+
+ (void) get_usable_slices(&usable_slices);
+
+ /*
+ * calculate raw available space: space on slices that are
+ * "available" based on the diskset defaults or global defaults
+ */
+ if ((error = get_space_available_for_request(request,
+ usable_slices, &raw_space)) != 0) {
+ return (error);
+ }
+
+ if (raw_space == 0) {
+ volume_set_error(
+ gettext("Failed: there is no available space.\n"));
+ return (-1);
+ }
+
+ /* deduct sizes of reserved components */
+ (void) get_reserved_slices(&iter);
+ for (; (iter != NULL) && (raw_space != 0) && (error == 0);
+ iter = iter->next) {
+ dm_descriptor_t slice = (uintptr_t)iter->obj;
+ uint64_t nbytes;
+ if ((error = slice_get_size(slice, &nbytes)) == 0) {
+ if (raw_space >= nbytes) {
+ raw_space -= nbytes;
+ } else {
+ raw_space = 0;
+ }
+ }
+ }
+
+ /*
+ * check each volume request's size against raw_space,
+ * if that looks ok, do a closer check with the request's
+ * available devices
+ */
+ iter = devconfig_get_components(request);
+ for (; (iter != NULL) && (error == 0); iter = iter->next) {
+
+ devconfig_t *req = (devconfig_t *)iter->obj;
+ component_type_t type = TYPE_UNKNOWN;
+ char *typestr = NULL;
+ uint64_t nbytes = 0;
+
+ (void) devconfig_get_type(req, &type);
+ if (type == TYPE_HSP) {
+ continue;
+ }
+
+ typestr = devconfig_type_to_str(type);
+
+ if ((error = devconfig_get_size(req, &nbytes)) == 0) {
+
+ /* check specified size */
+
+ if (type == TYPE_CONCAT || type == TYPE_STRIPE) {
+ if ((error = do_available_space_check(
+ apply_layout_overhead_factor(nbytes),
+ raw_space, req, usable_slices)) == 0) {
+ total_rqst_space += nbytes;
+ } else if (error == ENOSPC || error == E2BIG) {
+ (void) snprintf(bad_rqst_info, BUFSIZ-1,
+ "%s", typestr);
+ bad_rqst_space = nbytes;
+ }
+ } else if (type == TYPE_MIRROR) {
+ uint16_t nsubs = 0;
+ if ((error = get_mirror_nsubs(req, &nsubs)) == 0) {
+ if ((error = do_available_space_check(
+ apply_layout_overhead_factor(nbytes * nsubs),
+ raw_space, req, usable_slices)) == 0) {
+ total_rqst_space += (nsubs * nbytes);
+ } else {
+ (void) snprintf(bad_rqst_info, BUFSIZ-1,
+ gettext("%s with %d submirrors"),
+ typestr, nsubs);
+ bad_rqst_space = nbytes;
+ }
+ }
+ }
+
+ } else if ((error == ERR_ATTR_UNSET) && (type == TYPE_MIRROR)) {
+
+ /* mirror specified no size: find submirror that does */
+
+ dlist_t *subs = devconfig_get_components(req);
+
+ error = 0;
+ if (subs != NULL) {
+ dlist_t *iter2;
+ int nsubs = dlist_length(subs);
+ for (iter2 = subs;
+ (iter2 != NULL) && (error == 0);
+ iter2 = iter2->next) {
+ devconfig_t *sub = (devconfig_t *)iter2->obj;
+ if ((error = devconfig_get_size(sub, &nbytes)) == 0) {
+ if ((error = do_available_space_check(
+ apply_layout_overhead_factor(nbytes * nsubs),
+ raw_space, req, usable_slices)) == 0) {
+ total_rqst_space += (nbytes * nsubs);
+ } else {
+ (void) snprintf(bad_rqst_info, BUFSIZ-1,
+ gettext("%s with %d submirrors"),
+ typestr, nsubs);
+ bad_rqst_space = nbytes;
+ }
+ break;
+ } else if (error == ERR_ATTR_UNSET) {
+ error = 0;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * do_available_space_check may return ENOSPC or E2BIG
+ */
+ if (error == ENOSPC) {
+ char *sizestr = NULL;
+ (void) bytes_to_sizestr(bad_rqst_space,
+ &sizestr, universal_units, B_FALSE);
+
+ volume_set_error(
+ gettext("Failed: the request for a %s %s "
+ "exceeds the available space.\n"),
+ sizestr, bad_rqst_info);
+
+ free(sizestr);
+ error = -1;
+
+ } else if (error == E2BIG) {
+ char *sizestr = NULL;
+ (void) bytes_to_sizestr(bad_rqst_space,
+ &sizestr, universal_units, B_FALSE);
+
+ volume_set_error(
+ gettext("Failed: the request for a %s %s "
+ "exceeds the usable space on the device(s) "
+ "specified as available.\n"),
+ sizestr, bad_rqst_info);
+
+ free(sizestr);
+ error = -1;
+
+ } else if (apply_layout_overhead_factor(total_rqst_space) > raw_space) {
+ char *sizestr = NULL;
+ (void) bytes_to_sizestr(
+ total_rqst_space, &sizestr, universal_units, B_FALSE);
+
+ volume_set_error(
+ gettext("Failed: the total space requested for the "
+ "volumes (about %s) exceeds the available "
+ "space.\n"),
+ sizestr);
+
+ free(sizestr);
+ error = -1;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: apply_layout_overhead_factor(uint64_t req_size)
+ *
+ * INPUT: req_size: a requested volume size
+ *
+ * RETURNS: the requested volume size with an overhead factor applied
+ *
+ * PURPOSE: The input size size is inflated by a "fudge" factor
+ * to account for some of the expected overhead required for
+ * volumes such as block and cylinder boundary alignment.
+ */
+static uint64_t
+apply_layout_overhead_factor(
+ uint64_t req_size)
+{
+ double overhead = 1.15;
+ double d_size = req_size;
+ uint64_t result = (uint64_t)(d_size * overhead);
+
+ return (result);
+}
+
+/*
+ * FUNCTION: get_space_available_for_request(devconfig_t *request,
+ * dlist_t *usable_slices, uint64_t *avail_space)
+ *
+ * INPUT: request: a devconfig_t volume request
+ * usable_slices: a list of usable slice dm_descriptor_t handles
+ *
+ * OUTPUT: avail_space: the total space on slices in the usable_slice
+ * list that is available for use by the input
+ * request.
+ *
+ * RETURNS: int - 0 on success
+ * !0 on failure
+ *
+ * PURPOSE: Iterate the input list of usable slices, determine which are
+ * available to the input request and accumulate the total space
+ * they represent.
+ *
+ * The slices in the usable_slice list are those with no apparent
+ * usage detected. The slice_is_available() check determines
+ * whether the slice passes the available/unavailable device
+ * specification associated with the input request.
+ */
+static int
+get_space_available_for_request(
+ devconfig_t *request,
+ dlist_t *usable_slices,
+ uint64_t *avail_space)
+{
+ dlist_t *iter;
+ int error = 0;
+
+ *avail_space = 0;
+
+ for (iter = usable_slices;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+ dm_descriptor_t slice = (uintptr_t)iter->obj;
+ char *sname;
+ uint64_t nbytes;
+ boolean_t avail = B_FALSE;
+ if ((error = get_display_name(slice, &sname)) == 0) {
+ if ((error = slice_is_available(sname, request, &avail)) == 0) {
+ if (avail == B_TRUE) {
+ if ((error = slice_get_size(slice, &nbytes)) == 0) {
+ *avail_space += nbytes;
+ }
+ }
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: do_available_space_check(uint64_t req_size,
+ * uint64_t raw_avail_space, devconfig_t *request,
+ * dlist_t *usable_slices)
+ *
+ * INPUT: req_size: the requested size of a volume
+ * raw_avail_space:the total available space for all volumes
+ * request: a devconfig_t volume request
+ * usable_slices: a list of usable slice dm_descriptor_t handles
+ *
+ * RETURNS: int - ENOSPC if the requested size exceeds the raw
+ * available space.
+ *
+ * E2BIG if the requested size exceeds the space
+ * available specifically to the input request,
+ * taking into account its available and
+ * unavailable device specifications.
+ *
+ * 0 otherwise
+ *
+ * PURPOSE: Check the input request size against different forms of
+ * available space.
+ *
+ * If the requested size is less than the raw_avail_space, do the
+ * more expensive check against the space specifically available
+ * to the input request.
+ */
+static int
+do_available_space_check(
+ uint64_t req_size,
+ uint64_t raw_avail_space,
+ devconfig_t *request,
+ dlist_t *usable_slices)
+{
+ int error = 0;
+
+ if (req_size > raw_avail_space) {
+ error = ENOSPC;
+ } else {
+ uint64_t avail_space = 0;
+ if ((error = get_space_available_for_request(request,
+ usable_slices, &avail_space)) == 0) {
+ if (req_size > avail_space) {
+ error = E2BIG;
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: validate_request(devconfig_t *req)
+ *
+ * INPUT: req - a devconfig_t representing a volume layout request.
+ *
+ * RETURNS: int - 0 if the request passes validation
+ * !0 otherwise.
+ *
+ * PURPOSE: Main entry point into the layout request semantic
+ * validatation.
+ *
+ * Determines the type of volume requested and invokes the
+ * appropriate validation functions.
+ */
+int
+validate_request(
+ devconfig_t *req)
+{
+ int error = 0;
+ component_type_t type = TYPE_UNKNOWN;
+
+ ((error = validate_request_avail_unavail(req)) != 0) ||
+ (error = devconfig_get_type(req, &type));
+ if (error != 0) {
+ return (error);
+ }
+
+ if (type == TYPE_MIRROR) {
+
+ ((error = validate_request_name(req, type)) != 0) ||
+ (error = validate_request_size(req, type)) ||
+ (error = validate_request_submirrors(req));
+
+ } else if (type == TYPE_CONCAT || type == TYPE_STRIPE) {
+
+ ((error = validate_request_name(req, type)) != 0) ||
+ (error = validate_request_size(req, type)) ||
+ (error = validate_slice_components(req, type));
+
+ } else if (type == TYPE_HSP) {
+
+ ((error = validate_request_name(req, type)) != 0) ||
+ (error = validate_slice_components(req, type));
+
+ } else if (type == TYPE_VOLUME) {
+
+ ((error = validate_request_name(req, type)) != 0) ||
+ (error = validate_request_redundancy_level(req)) ||
+ (error = validate_request_npaths(req));
+
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: validate_reserved_slices()
+ *
+ * RETURNS: int - 0 if all reserved slices are usable in
+ * new devices.
+ * !0 otherwise.
+ *
+ * PURPOSE: Ensures that each reserved slice is actually usable
+ * as a volume component.
+ *
+ * Retrieves list of reserved slices and list of usable
+ * slices. Ensures that each reserved slice is in the
+ * usable list, generates an error if it is not.
+ *
+ * This is broken out as a separate function because
+ * initial validation is using the lists of all known
+ * devices. Device "usability" is only determined after
+ * the initial validation has completed successfully.
+ */
+int
+validate_reserved_slices()
+{
+ dlist_t *reserved_slices;
+ dlist_t *usable_slices;
+ int error = 0;
+
+ ((error = get_reserved_slices(&reserved_slices)) != 0) ||
+ (error = get_usable_slices(&usable_slices));
+ if (error == 0) {
+
+ dlist_t *iter;
+ for (iter = reserved_slices;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ if (dlist_contains(usable_slices, iter->obj,
+ compare_descriptor_names) != B_TRUE) {
+
+ dm_descriptor_t slice = (uintptr_t)iter->obj;
+ char *name = NULL;
+
+ error = get_display_name(slice, &name);
+ if (error == 0) {
+ char *aliases = get_device_aliases_string(slice);
+ if (aliases[0] != NULL) {
+ volume_set_error(
+ gettext("A requested volume component "
+ "is currently in use: \"%s\" "
+ "(aliases: %s).\n"),
+ name, aliases);
+ } else {
+ volume_set_error(
+ gettext("A requested volume component "
+ "is currently in use: \"%s\"\n"),
+ name);
+ }
+ error = -1;
+ }
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: validate_request_avail_unavail(devconfig_t *req)
+ *
+ * INPUT: req - a devconfig_t representing a volume layout request.
+ *
+ * RETURNS: int - 0 if the request passes validation
+ * !0 otherwise.
+ *
+ * PURPOSE: validation function for a request's lists of available
+ * and unavailable devices.
+ *
+ * validates that both lists contain names of known devices.
+ *
+ * validates that the same name does not appear in both lists.
+ */
+int
+validate_request_avail_unavail(
+ devconfig_t *req)
+{
+ dlist_t *avail = NULL;
+ dlist_t *unavail = NULL;
+ int error = 0;
+
+ /* check that each array contains valid devices */
+ ((error = validate_device_array(devconfig_get_available(req),
+ gettext("available"), &avail)) != 0) ||
+ (error = validate_device_array(devconfig_get_unavailable(req),
+ gettext("unavailable"), &unavail));
+
+ /* check that the arrays don't both contain the same device(s) */
+ if (error == 0) {
+ dlist_t *iter;
+ for (iter = avail; iter != NULL; iter = iter->next) {
+ if (dlist_contains(unavail, iter->obj,
+ compare_descriptor_names) == B_TRUE) {
+ char *name;
+ char *aliases =
+ get_device_aliases_string((uintptr_t)iter->obj);
+
+ (void) get_display_name((uintptr_t)iter->obj, &name);
+ if (aliases[0] != NULL) {
+ volume_set_error(
+ gettext("\"%s\" specified as both available "
+ "and unavailable.\n"
+ "It has these aliases: %s\n"),
+ name, aliases);
+ } else {
+ volume_set_error(
+ gettext("\"%s\" specified as both available "
+ "and unavailable.\n"),
+ name);
+ }
+ error = -1;
+ break;
+ }
+ }
+ }
+
+ dlist_free_items(avail, NULL);
+ dlist_free_items(unavail, NULL);
+
+ return (error);
+}
+
+/*
+ * FUNCTION: validate_device_array(char **array, char *which, dlist_t **list)
+ *
+ * INPUT: array - an array of char * device names
+ * which - either "available" or "unavailable"
+ * indicating the array name to use in
+ * error strings.
+ * OUTPUT: list - a list of device descriptors corresponding the each
+ * of the input names.
+ *
+ * RETURNS: int - 0 if the array passes validation
+ * !0 otherwise.
+ *
+ * PURPOSE: validation function for a request's list of available
+ * or unavailable devices.
+ *
+ * DID names are converted to CTD names.
+ *
+ * The CTD name must be of an available slice, disk or
+ * HBA, or a known used slice, disk or HBA that was
+ * discovered when the system's devices were probed.
+ *
+ * Any other name is assumed to refer to a device not
+ * attached to the system and results in a validation
+ * failure.
+ *
+ * Descriptors for validated devices are added to the input
+ * list.
+ */
+int
+validate_device_array(
+ char **array,
+ char *which,
+ dlist_t **list)
+{
+ int error = 0;
+ int i = 0;
+
+ if (array == NULL || *array == NULL) {
+ return (0);
+ }
+
+ for (i = 0; (array[i] != NULL) && (error == 0); i++) {
+
+ dm_descriptor_t slice = (dm_descriptor_t)0;
+ dm_descriptor_t disk = (dm_descriptor_t)0;
+ dm_descriptor_t hba = (dm_descriptor_t)0;
+ char *name = array[i];
+
+ /* name must correspond to a known HBA, disk, or slice */
+ if ((error = hba_get_by_name(name, &hba)) == 0) {
+ if (hba == (dm_descriptor_t)0) {
+ if ((error = disk_get_by_name(name, &disk)) == 0) {
+ if (disk == (dm_descriptor_t)0) {
+ error = slice_get_by_name(name, &slice);
+ }
+ }
+ }
+ }
+
+ if (error != 0) {
+ break;
+ }
+
+ /* 0 sized slices cannot be used as-is, pretend non-existant */
+ if (slice != (dm_descriptor_t)0) {
+ uint64_t size = 0;
+ if ((error = slice_get_size(slice, &size)) == 0) {
+ if (size == 0) {
+ slice = (dm_descriptor_t)0;
+ }
+ }
+ }
+
+ oprintf(OUTPUT_DEBUG,
+ gettext(" validate %s (%s): s=%llu, d=%llu, c=%llu\n"),
+ which, array[i], slice, disk, hba);
+
+ if ((error == 0) && ((slice != 0) || (disk != 0) || (hba != 0))) {
+
+ /* name represents an individual "device", add it to the list */
+ dm_descriptor_t desc = (dm_descriptor_t)0;
+ dlist_t *item;
+
+ if (slice != 0) {
+ desc = slice;
+ } else if (disk != 0) {
+ desc = disk;
+ } else if (hba != 0) {
+ desc = hba;
+ }
+
+ if ((item = dlist_new_item((void *)desc)) == NULL) {
+ error = ENOMEM;
+ } else {
+ *list = dlist_append(item, *list, AT_HEAD);
+ }
+
+ } else if (is_ctd_target_name(name) == B_TRUE) {
+
+ /* expand target to all of its disks */
+ dlist_t *disks = NULL;
+ if ((error = get_disks_for_target(name, &disks)) == 0) {
+ if ((disks == NULL) || (dlist_length(disks) == 0)) {
+ volume_set_error(
+ gettext("nonexistent device specified "
+ "as %s: \"%s\"."),
+ which, array[i]);
+ error = -1;
+ } else {
+ dlist_t *iter;
+ for (iter = disks;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ dlist_t *item;
+ if ((item = dlist_new_item(iter->obj)) == NULL) {
+ error = ENOMEM;
+ } else {
+ *list = dlist_append(item, *list, AT_HEAD);
+ }
+ }
+ }
+ }
+
+ } else {
+
+ /* not a slice, disk, target or ctrl */
+ volume_set_error(
+ gettext("nonexistent device specified "
+ "as %s: \"%s\"."),
+ which, array[i]);
+ error = -1;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: validate_request_name(devconfig_t *req, component_type_t type)
+ *
+ * INPUT: req - a devconfig_t volume request
+ * type - the volume type being requested
+ *
+ * SIDEEFFECT: if the request specifies a name and the name is valid and
+ * not currently in use an attempt is made to reserve it.
+ * if the name has already been reserved by a prior volume
+ * request, validation fails.
+ *
+ * RETURNS: int - 0 if the requested name passes validation
+ * (or there is no name request)
+ * !0 otherwise.
+ *
+ * PURPOSE: Validation function for a request's volume name.
+ *
+ * a HSP name must be valid and reservable.
+ *
+ * a volume name must be valid and reservable.
+ */
+static int
+validate_request_name(
+ devconfig_t *req,
+ component_type_t type)
+{
+ char *name = NULL;
+ char *typestr = devconfig_type_to_str(type);
+ int error = 0;
+
+ if ((error = devconfig_get_name(req, &name)) != 0) {
+ if (error != ERR_ATTR_UNSET) {
+ volume_set_error(
+ gettext("error getting requested name.\n"));
+ return (error);
+ }
+ /* no name specified */
+ return (0);
+ }
+
+ if (type == TYPE_HSP) {
+ if (is_hsp_name_valid(name) == 0) {
+ volume_set_error(
+ gettext("requested %s name \"%s\" is not valid.\n"),
+ typestr, name);
+ error = -1;
+ } else if (reserve_hsp_name(name) != 0) {
+ if (is_rsvd_name(name) == B_TRUE) {
+ volume_set_error(
+ gettext("requested %s name \"%s\" used "
+ "previously in this request.\n"),
+ typestr, name);
+ } else {
+ volume_set_error(
+ gettext("requested %s name \"%s\" is not "
+ "available.\n"),
+ typestr, name);
+ }
+ error = -1;
+ } else {
+ error = add_reserved_name(name);
+ }
+ } else {
+ if (is_volume_name_valid(name) == 0) {
+ volume_set_error(
+ gettext("requested %s name \"%s\" is not valid.\n"),
+ typestr, name);
+ error = -1;
+ } else if (is_volume_name_in_range(name) != B_TRUE) {
+ int max = 0;
+ (void) get_max_number_of_devices(&max);
+ volume_set_error(
+ gettext("requested %s name \"%s\" is not legal.\n"
+ "Use a name less than d%d.\n"),
+ typestr, name, max);
+ error = -1;
+ } else if (reserve_volume_name(name) != 0) {
+ if (is_rsvd_name(name) == B_TRUE) {
+ volume_set_error(
+ gettext("requested %s name \"%s\" used "
+ "previously in this request.\n"),
+ typestr, name);
+ } else {
+ volume_set_error(
+ gettext("requested %s name \"%s\" is not "
+ "available, a volume with that name "
+ "already exists.\n"),
+ typestr, name);
+ }
+ error = -1;
+ } else {
+ error = add_reserved_name(name);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: add_reserved_name(char *name)
+ *
+ * INPUT: name - a char * volume name
+ *
+ * RETURNS: int - 0 on success
+ * !0 otherwise.
+ *
+ * PURPOSE: Helper which remembers specfically requested names
+ * in a private list to ensure that the same name isn't
+ * requested more than once.
+ */
+static int
+add_reserved_name(
+ char *name)
+{
+ dlist_t *item = NULL;
+
+ if ((item = dlist_new_item(name)) == NULL) {
+ return (ENOMEM);
+ }
+
+ _rsvd_names = dlist_append(item, _rsvd_names, AT_TAIL);
+
+ return (0);
+}
+
+/*
+ * FUNCTION: is_rsvd_name(char *name)
+ *
+ * INPUT: name - a char * volume name
+ *
+ * RETURNS: boolean_t - B_TRUE if the requested name is currently
+ * reserved, B_FALSE otherwise.
+ *
+ * PURPOSE: Helper which checks to see if the input volume
+ * name was previously reserved.
+ */
+static boolean_t
+is_rsvd_name(
+ char *name)
+{
+ dlist_t *iter = NULL;
+
+ for (iter = _rsvd_names; iter != NULL; iter = iter->next) {
+ if ((string_case_compare(name, (char *)iter->obj)) == 0) {
+ return (B_TRUE);
+ }
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * FUNCTION: validate_request_size(devconfig_t *req, component_type_t type)
+ *
+ * INPUT: req - a devconfig_t volume request
+ * type - the volume type being requested
+ *
+ * RETURNS: int - 0 if the requested size passes validation
+ * (or there is no size request)
+ * !0 otherwise.
+ *
+ * PURPOSE: Validation function for a request's volume size.
+ *
+ * a HSP request can have no size.
+ *
+ * a concat, stripe or mirror request may have a size.
+ * if size is specified, the request cannot also specify
+ * components. Conversely, if the request does not specify
+ * a size, it must specify components.
+ */
+static int
+validate_request_size(
+ devconfig_t *req,
+ component_type_t type)
+{
+ uint64_t nbytes = 0;
+ int error = 0;
+
+ if (type == TYPE_HSP) {
+ return (0);
+ }
+
+ if ((error = devconfig_get_size(req, &nbytes)) != 0) {
+ if (error == ERR_ATTR_UNSET) {
+ /* nbytes not specified, request must have subcomponents */
+ dlist_t *list = devconfig_get_components(req);
+ if (list != NULL && dlist_length(list) > 0) {
+ error = 0;
+ } else {
+ volume_set_error(
+ gettext("%s request specifies no size or "
+ "subcomponents.\n"),
+ devconfig_type_to_str(type));
+ error = -1;
+ }
+ }
+ return (error);
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: validate_minimum_size(uint64_t nbytes)
+ *
+ * INPUT: nbytes - requested volume size in bytes
+ *
+ * RETURNS: int - 0 if the requested size passes validation
+ * (or there is no size request)
+ * !0 otherwise.
+ *
+ * PURPOSE: Validation function for a request's volume size.
+ *
+ * an error is issued if the requested size <= 512K.
+ */
+static int
+validate_minimum_size(
+ uint64_t nbytes)
+{
+ static uint64_t min = (512 * 1024) - 1;
+ int error = 0;
+
+ if (nbytes <= min) {
+ char *sizestr = NULL;
+ char *minstr = NULL;
+
+ (void) bytes_to_sizestr(
+ nbytes, &sizestr, universal_units, B_FALSE);
+ (void) bytes_to_sizestr(
+ min, &minstr, universal_units, B_FALSE);
+
+ volume_set_error(
+ gettext("requested volume size (%s) must be "
+ "greater than %s.\n"),
+ sizestr, minstr);
+
+ free(sizestr);
+ free(minstr);
+
+ error = -1;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: validate_request_redundancy_level(devconfig_t *req)
+ *
+ * INPUT: req - a devconfig_t volume request
+ *
+ * RETURNS: int - 0 if the requested redundancy level
+ * passes validation (or none was requested)
+ * !0 otherwise.
+ *
+ * PURPOSE: Validation function for a redundant volume request's
+ * redundancy level.
+ *
+ * If the request specifies redundancy, the value must be
+ * between 1 and 4.
+ */
+static int
+validate_request_redundancy_level(
+ devconfig_t *req)
+{
+ uint16_t rlevel = 0;
+ int error = 0;
+
+ if ((error = devconfig_get_volume_redundancy_level(
+ req, &rlevel)) != 0) {
+ if (error == ERR_ATTR_UNSET) {
+ error = 0;
+ }
+ return (error);
+ }
+
+ if (rlevel > 4) {
+ volume_set_error(gettext(
+ "requested redundancy level must be between 0 and 4.\n"));
+ error = -1;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: validate_request_npaths(devconfig_t *req)
+ *
+ * INPUT: req - a devconfig_t volume request
+ *
+ * RETURNS: int - 0 if the requested # of redundant data paths
+ * passes validation (or none was requested)
+ * !0 otherwise.
+ *
+ * PURPOSE: Validation function for a volume request's number of
+ * redundant data paths. This value controls the number
+ * of independent data paths slices components selected
+ * for the volume should have.
+ *
+ * If the request specifies npaths, the value must be
+ * between 1 and 4 (4 is an arbitrary upper limit, there
+ * is no known physical limit).
+ */
+static int
+validate_request_npaths(
+ devconfig_t *req)
+{
+ uint16_t npaths = 0;
+ uint16_t minpaths = 1;
+ uint16_t maxpaths = 4;
+
+ int error = 0;
+
+ if ((error = devconfig_get_volume_npaths(req, &npaths)) != 0) {
+ if (error == ERR_ATTR_UNSET) {
+ error = 0;
+ }
+ return (error);
+ }
+
+ if (npaths < minpaths || npaths > maxpaths) {
+ volume_set_error(
+ gettext("requested number of redundant paths must be "
+ "between %d and %d.\n"), minpaths, maxpaths);
+ error = -1;
+ }
+
+
+ if ((npaths > 1) && (is_mpxio_enabled() != B_TRUE)) {
+ volume_set_error(
+ gettext("requested number of redundant paths (%d) cannot "
+ "be provided, MPXIO is not enabled on this "
+ "system."),
+ npaths);
+ error = -1;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: validate_request_submirrors(devconfig_t *req)
+ *
+ * INPUT: req - a devconfig_t volume request
+ *
+ * RETURNS: int - 0 if the requested mirror's submirrors
+ * pass validation
+ * !0 otherwise.
+ *
+ * PURPOSE: Validation function for a mirror volume request's
+ * explicitly specified submirror components.
+ *
+ * Items to check:
+ * a. submirror types
+ * b. submirror number
+ * c. submirror sizes
+ */
+static int
+validate_request_submirrors(
+ devconfig_t *req)
+{
+ dlist_t *submirrors = NULL;
+ int error = 0;
+
+ submirrors = devconfig_get_components(req);
+
+ ((error = validate_submirror_types(submirrors)) != 0) ||
+ (error = validate_submirror_number(req, submirrors)) ||
+ (error = validate_submirror_sizes(req, submirrors));
+
+ return (error);
+}
+
+/*
+ * FUNCTION: validate_submirror_types(dlist_t *subs)
+ *
+ * INPUT: subs - a list of submirror requests
+ *
+ * RETURNS: int - 0 if the requested submirrors
+ * pass validation
+ * !0 otherwise.
+ *
+ * PURPOSE: Validation function for a mirror volume request's
+ * explicitly specified submirror components.
+ *
+ * Checks that each requested submirror request
+ * is for a concat or stripe.
+ */
+static int
+validate_submirror_types(
+ dlist_t *submirrors)
+{
+ dlist_t *iter;
+ int error = 0;
+
+ /* specified submirrors must be stripes or concats */
+ for (iter = submirrors;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ devconfig_t *submir = (devconfig_t *)iter->obj;
+ component_type_t submirtype = TYPE_UNKNOWN;
+
+ if ((error = devconfig_get_type(submir, &submirtype)) != 0) {
+ volume_set_error(
+ gettext("failed to get requested component type.\n"));
+ break;
+ }
+
+ if (submirtype != TYPE_CONCAT && submirtype != TYPE_STRIPE) {
+ volume_set_error(
+ gettext("requested submirror type \"%s\" "
+ "is not valid.\n"),
+ devconfig_type_to_str(submirtype));
+ error = -1;
+ break;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: validate_submirror_number(devconfig_t *req, dlist_t *subs)
+ *
+ * INPUT: req - the mirror request
+ * subs - the list of requested submirrors
+ *
+ * RETURNS: int - 0 if the requested submirrors
+ * pass validation
+ * !0 otherwise.
+ *
+ * PURPOSE: Validation function for a mirror volume request's
+ * explicitly specified submirror components.
+ *
+ * Checks that the number of submirror components
+ * that have been specified matches the number of
+ * submirrors specified.
+ */
+static int
+validate_submirror_number(
+ devconfig_t *req,
+ dlist_t *submirrors)
+{
+ uint16_t nsubs = 0;
+ int error = 0;
+
+ if ((error = devconfig_get_mirror_nsubs(req, &nsubs)) != 0) {
+ if (error == ERR_ATTR_UNSET) {
+ /* not specified */
+ error = 0;
+ }
+ } else if ((submirrors != NULL) &&
+ (dlist_length(submirrors) != nsubs)) {
+ volume_set_error(
+ gettext("the requested number of submirrors (%d) differs "
+ "from the number of specified submirrors (%d).\n"),
+ nsubs, dlist_length(submirrors));
+ error = -1;
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: validate_submirror_sizes(devconfig_t *req,
+ * dlist_t *submirrors)
+ *
+ * INPUT: req - the mirror request
+ * submirrors - the list of requested submirrors
+ *
+ * RETURNS: int - 0 if the requested submirrors
+ * pass validation
+ * !0 otherwise.
+ *
+ * PURPOSE: Validation function for a mirror volume request's
+ * explicitly specified size. Assumes that the mirror's size
+ * has been validated by validate_request_size().
+ *
+ * Compares explicitly requested mirror size against specified
+ * component sizes and checks:
+ *
+ * - any submirror request that specifies both size and
+ * components is invalid
+ * - any submirror request specifying a size different
+ * than that explictly requested for the mirror is
+ * invalid
+ * - a submirror request specifying a size < 512K is invalid.
+ *
+ * Other validation/warnings:
+ *
+ * - submirrors that specify components may end up with
+ * usable capacity that differs from what was specified
+ * for the mirror.
+ *
+ * - submirrors which specify neither size nor components are
+ * assumed to be the size requested for the mirror. If the
+ * mirror size is not specified, the first explicit size for
+ * a submirror is assumed as the size for the mirror.
+ */
+static int
+validate_submirror_sizes(
+ devconfig_t *req,
+ dlist_t *submirrors)
+{
+ dlist_t *submirs_with_size = NULL;
+ dlist_t *submirs_with_comps = NULL;
+ dlist_t *submirs_with_nothing = NULL;
+
+ dlist_t *iter = NULL;
+ uint64_t mirror_size = 0;
+ uint64_t assumed_size = 0;
+ int error = 0;
+
+ if (submirrors == NULL || dlist_length(submirrors) == 0) {
+ return (0);
+ }
+
+ if ((error = devconfig_get_size(req, &mirror_size)) != 0) {
+ if (error == ERR_ATTR_UNSET) {
+ error = 0;
+ } else {
+ return (error);
+ }
+ }
+
+ /*
+ * check size and component for each submirror,
+ * collect those that specify size, components or neither
+ * into separate lists.
+ */
+ for (iter = submirrors;
+ (iter != NULL) && (error == 0);
+ iter = iter->next) {
+
+ devconfig_t *submir = (devconfig_t *)iter->obj;
+
+ error = validate_submirror_size_and_components(submir,
+ mirror_size, &assumed_size, &submirs_with_size,
+ &submirs_with_comps, &submirs_with_nothing);
+
+ }
+
+ if (error == 0) {
+
+ int n_size = dlist_length(submirs_with_size);
+ int n_comp = dlist_length(submirs_with_comps);
+ int n_none = dlist_length(submirs_with_nothing);
+
+ if ((n_size != 0) && (n_comp != 0)) {
+ /* some submirrors specified size, some components */
+ oprintf(OUTPUT_TERSE,
+ gettext(" *** warning: %d submirrors are specified "
+ "by size, %d specified by components.\n"
+ " The resulting mirror capacity will be "
+ "that of the smallest submirror.\n"),
+ n_size, n_comp);
+ }
+
+ if (n_none != 0) {
+ if (assumed_size != 0) {
+ /* some submirrors specified neither size or components */
+ char *sizestr = NULL;
+
+ (void) bytes_to_sizestr(
+ assumed_size, &sizestr, universal_units, B_FALSE);
+
+ oprintf(OUTPUT_TERSE,
+ gettext(" *** warning: %d submirrors specified "
+ "neither size or components,\n"
+ " the assumed size is %s.\n"),
+ n_none, sizestr);
+
+ free(sizestr);
+
+ } else if (mirror_size == 0) {
+ volume_set_error(
+ gettext("no size specified for requested "
+ "mirror and no sizes/components "
+ "specified for its submirrors."));
+
+ error = -1;
+ }
+ }
+
+ dlist_free_items(submirs_with_size, NULL);
+ dlist_free_items(submirs_with_comps, NULL);
+ dlist_free_items(submirs_with_nothing, NULL);
+
+ }
+
+ return (error);
+}
+
+/*
+ * FUNCTION: validate_submirror_size_and_components(
+ * devconfig_t *submir,
+ * uint64_t mirror_size,
+ * uint64_t *assumed_size,
+ * dlist_t **submirs_with_size,
+ * dlist_t **submirs_with_comps,
+ * dlist_t **submirs_no_size_or_comps)
+ *
+ * INPUT: submir - a specific submirror request
+ * mirror_size, - the size specified for the mirror
+ *
+ * OUTPUT: assumed_size - the assumed size of the mirror,
+ * if none specified.
+ * submirs_with_size - pointer to a list of submirror
+ * requests that specify a size
+ * submirs_with_comps - pointer to a list of submirror
+ * requests that specify components
+ * submirs_no_size_or_comps - pointer to a list of
+ * submirror requests that specify neither
+ * a size or components
+ *
+ * RETURNS: int - 0 if the requested submirrors
+ * pass validation
+ * !0 otherwise.
+ *
+ * PURPOSE: Validation function which checks a specific submirror
+ * request's size and components against the parent mirror's
+ * size.
+ *
+ * - any submirror request that specifies both size and
+ * components is invalid
+ * - any submirror request specifying a size different
+ * than that explictly requested for the mirror is
+ * invalid
+ * - a submirror request specifying a size < 512K is invalid.
+ * - any components specified for a submirror are validated.
+ *
+ * If the submirror passes the validation checks, it is added
+ * to the appropriate output list.
+ *
+ * If the input mirror_size is 0 and the submirror specifies
+ * a valid size, the submirror size is returned as the
+ * assumed_size for the mirror.
+ */
+static int
+validate_submirror_size_and_components(
+ devconfig_t *submir,
+ uint64_t mirror_size,
+ uint64_t *assumed_size,
+ dlist_t **submirs_with_size,
+ dlist_t **submirs_with_comps,
+ dlist_t **submirs_no_size_or_comps)
+{
+ uint64_t submir_size = 0;
+ component_type_t submir_type = TYPE_UNKNOWN;
+ char *submir_typestr = NULL;
+ dlist_t *submir_comps = NULL;
+ dlist_t *item = NULL;
+ int n_submir_comps = 0;
+ int error = 0;
+
+ submir_comps = devconfig_get_components(submir);
+ if (submir_comps != NULL) {
+ n_submir_comps = dlist_length(submir_comps);
+ }
+
+ if ((error = devconfig_get_size(submir, &submir_size)) != 0) {
+ if (error == ERR_ATTR_UNSET) {
+ /* submirror size not specified */
+ error = 0;
+ submir_size = 0;
+ }
+ }
+
+ if (error != 0) {
+ return (error);
+ }
+
+ /* submirror type previously validated */
+ (void) devconfig_get_type(submir, &submir_type);
+ submir_typestr = devconfig_type_to_str(submir_type);
+
+ if (submir_size == 0) {
+
+ /* submirror has no size, components? */
+ if (n_submir_comps > 0) {
+
+ /* validate components */
+ error = validate_slice_components(submir, submir_type);
+
+ item = dlist_new_item((void *)submir);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ *submirs_with_comps =
+ dlist_append(item, *submirs_with_comps, AT_TAIL);
+ }
+
+ } else {
+
+ /* no size or components */
+ item = dlist_new_item((void *)submir);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ *submirs_no_size_or_comps =
+ dlist_append(item, *submirs_no_size_or_comps, AT_TAIL);
+ }
+
+ }
+
+ } else {
+
+ /* submirror has size, check it */
+ if (error == 0) {
+ error = validate_minimum_size(submir_size);
+ }
+
+ /* check size against mirror's size */
+ if ((error == 0) && (submir_size != mirror_size)) {
+
+ if (mirror_size != 0) {
+
+ /* sizes differ */
+ char *sizestr = NULL;
+ char *mstr = NULL;
+
+ (void) bytes_to_sizestr(
+ submir_size, &sizestr, universal_units, B_FALSE);
+ (void) bytes_to_sizestr(
+ mirror_size, &mstr, universal_units, B_FALSE);
+
+ volume_set_error(
+ gettext("the requested submirror size (%s) "
+ "differs from the requested mirror "
+ "size (%s).\n"),
+ sizestr, mstr);
+
+ error = -1;
+
+ free(sizestr);
+ free(mstr);
+
+ } else if (*assumed_size == 0) {
+
+ /* first size assumed as mirror size */
+ char *sizestr = NULL;
+
+ (void) bytes_to_sizestr(
+ submir_size, &sizestr, universal_units, B_FALSE);
+
+ oprintf(OUTPUT_TERSE,
+ gettext(" *** warning, using first "
+ "explicit submirror size (%s)\n"
+ " as the mirror size\n"),
+ sizestr);
+
+ *assumed_size = submir_size;
+
+ free(sizestr);
+
+ } else if (submir_size != *assumed_size) {
+
+ /* submirror sizes differ */
+ char *sizestr1 = NULL;
+ char *sizestr2 = NULL;
+
+ (void) bytes_to_sizestr(
+ submir_size, &sizestr1, universal_units, B_FALSE);
+ (void) bytes_to_sizestr(
+ *assumed_size, &sizestr2, universal_units, B_FALSE);
+
+ volume_set_error(
+ gettext("submirror specifies different "
+ "size (%s) than a previous "
+ "submirror (%s)\n"),
+ sizestr1, sizestr2);
+
+ free(sizestr1);
+ free(sizestr2);
+
+ error = -1;
+ }
+ }
+
+ if ((error == 0) && (n_submir_comps > 0)) {
+
+ /* size and subcomponents specified */
+ char *sizestr = NULL;
+
+ (void) bytes_to_sizestr(
+ submir_size, &sizestr, universal_units, B_FALSE);
+
+ volume_set_error(
+ gettext("%s submirror specifies both an "
+ "explicit size (%s) and components.\n"),
+ submir_typestr, sizestr);
+
+ free(sizestr);
+ error = -1;
+
+ }
+
+ if (error == 0) {
+ item = dlist_new_item((void *)submir);
+ if (item == NULL) {
+ error = ENOMEM;
+ } else {
+ *submirs_with_size =
+ dlist_append(item, *submirs_with_size, AT_TAIL);
+ }
+ }
+ }
+
+ return (error);
+}
+
+
+/*
+ * FUNCTION: validate_slice_components(devconfig_t *req,
+ * component_type_t type)
+ *
+ * INPUT: req - the request
+ * type - the type of volume being requested
+ *
+ * SIDEEFFECT: if the slice component is otherwise valid, an attempt is made
+ * to reserve it.
+ *
+ * RETURNS: int - 0 if the request passes slice component validation
+ * !0 otherwise.
+ *
+ * PURPOSE: Validation function for a concat, stripe or HSP request's
+ * explicitly specified slice components.
+ *
+ * Is the component slice a known device
+ * Is the component slice available
+ * Is the component slice already reserved
+ *
+ * If the request is for a stripe or concat and the
+ * request specifies an explicit size, it cannot also
+ * specify component slices. This is a validation failure.
+ *
+ * If the request is for a stripe, the number of specified
+ * slice components must agree with any expilcit specification
+ * of the minimum or maximum number of components the stripe
+ * should have.
+ */
+static int
+validate_slice_components(
+ devconfig_t *req,
+ component_type_t type)
+{
+ dlist_t *list = NULL;
+ dlist_t *iter = NULL;
+ int error = 0;
+ int ncomp = 0;
+
+ char *dsname = get_request_diskset();
+ char *voltype = devconfig_type_to_str(type);
+
+ list = devconfig_get_components(req);
+
+ for (iter = list; (iter != NULL) && (error == 0); iter = iter->next) {
+
+ devconfig_t *comp = (devconfig_t *)iter->obj;
+ component_type_t ctype = TYPE_UNKNOWN;
+ char *cname = NULL;
+ dm_descriptor_t slice = (dm_descriptor_t)0;
+
+ if ((error = devconfig_get_type(comp, &ctype)) != 0) {
+ volume_set_error(
+ gettext("error getting requested component type."),
+ voltype);
+
+ continue;
+ }
+
+ if ((error = devconfig_get_name(comp, &cname)) != 0) {
+ volume_set_error(
+ gettext("error getting requested component name."));
+
+ continue;
+ }
+
+ if (cname == NULL || cname[0] == '\0') {
+ volume_set_error(
+ gettext("%s requested component has no name."),
+ voltype);
+
+ error = -1;
+ continue;
+ }
+
+ if (ctype == TYPE_SLICE) {
+
+ boolean_t in_set = B_FALSE;
+ boolean_t is_avail = B_FALSE;
+ boolean_t is_rsvd = B_FALSE;
+ dm_descriptor_t disk = (dm_descriptor_t)0;
+
+ /* is the slice known and explicitly available? */
+ if ((error = slice_is_available(cname, req,
+ &is_avail)) != 0) {
+
+ if (error == ENODEV) {
+ volume_set_error(
+ gettext("%s requested component does not "
+ "exist: \"%s\"."),
+ voltype, cname);
+ error = -1;
+ }
+ continue;
+ }
+
+ if (is_avail != B_TRUE) {
+ volume_set_error(
+ gettext("%s requested component is "
+ "unavailable: \"%s\"."),
+ voltype, cname);
+
+ error = -1;
+ continue;
+ }
+
+ /* get slice and its disk */
+ ((error = slice_get_by_name(cname, &slice)) != 0) ||
+ (error = slice_get_disk(slice, &disk)) ||
+ (error = is_reserved_slice(slice, &is_rsvd)) ||
+ (error = is_disk_in_diskset(disk, dsname, &in_set));
+ if (error != 0) {
+ continue;
+ }
+
+ /* is disk in the set? */
+ if (in_set != B_TRUE) {
+ volume_set_error(
+ gettext("%s specifies a component not in "
+ "disk set \"%s\": \"%s\"."),
+ voltype, dsname, cname);
+
+ error = -1;
+ continue;
+ }
+
+ /* was slice specified in some other request? */
+ if (is_rsvd == B_TRUE) {
+ /* include aliases in the error */
+ char *aliases =
+ get_device_aliases_string((dm_descriptor_t)slice);
+
+ if (aliases[0] != NULL) {
+ volume_set_error(
+ gettext("%s specifies a previously used "
+ "component: \"%s\" "
+ "(aliases: %s).\n"),
+ voltype, cname, aliases);
+ } else {
+ volume_set_error(
+ gettext("%s specifies a previously used "
+ "component: \"%s\"\n"),
+ voltype, cname);
+ }
+
+ error = -1;
+ continue;
+ }
+
+ /* component is ok, reserve it */
+ error = add_reserved_slice(slice);
+
+ /*
+ * the reserved slice component still needs to be
+ * checked against slices in use by SVM, but that
+ * information isn't available yet: the usable
+ * slice derivation happens after validation.
+ *
+ * validate_reserved_slices() can be used to check
+ * them once the usable slices are determined.
+ */
+
+ } else {
+ volume_set_error(
+ gettext("%s requested component has illegal type."),
+ voltype);
+
+ error = -1;
+ continue;
+ }
+ }
+
+ if (error != 0) {
+ return (error);
+ }
+
+ ncomp = dlist_length(list);
+ if ((ncomp > 0) && (type == TYPE_CONCAT || type == TYPE_STRIPE)) {
+ /* explicit size requested for the stripe/concat? */
+ uint64_t size = 0;
+ if ((error = devconfig_get_size(req, &size)) != 0) {
+ if (error == ERR_ATTR_UNSET) {
+ error = 0;
+ }
+ } else {
+ /* size and components both specified */
+ char *sizestr = NULL;
+
+ (void) bytes_to_sizestr(
+ size, &sizestr, universal_units, B_FALSE);
+
+ volume_set_error(
+ gettext("%s specifies both an explicit size (%s) "
+ "and components."),
+ voltype, sizestr);
+
+ free(sizestr);
+ error = -1;
+ }
+ }
+
+ if (error != 0) {
+ return (error);
+ }
+
+ if ((ncomp > 0) && (type == TYPE_STRIPE)) {
+ /* does # of components agree with min & max comps? */
+ uint16_t min = 0;
+ uint16_t max = 0;
+ if ((error = devconfig_get_stripe_mincomp(req, &min)) != 0) {
+ if (error == ERR_ATTR_UNSET) {
+ /* min comp not requested */
+ error = 0;
+ } else {
+ /* error getting requested mincomp */
+ return (error);
+ }
+
+ } else if (ncomp < min) {
+ /* specified comps < requested mincomp */
+ volume_set_error(
+ gettext("%s specifies fewer components (%d) than the "
+ "minimum number requested (%d).\n"),
+ voltype, ncomp, min);
+
+ error = -1;
+ return (error);
+ }
+
+ if ((error = devconfig_get_stripe_maxcomp(req, &max)) != 0) {
+ if (error == ERR_ATTR_UNSET) {
+ /* max comp not requested */
+ error = 0;
+ } else {
+ /* error getting request maxcomp */
+ return (error);
+ }
+ } else if (ncomp > max) {
+ /* specified comps > requested maxcomp */
+ volume_set_error(
+ gettext("%s specifies more components (%d) than the "
+ "maximum number requested (%d).\n"),
+ voltype, ncomp, max);
+ error = -1;
+ return (error);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Generate a list of known aliases for the input descriptor.
+ *
+ * The returned string buffer is in the form: "alias1", "alias2"...
+ */
+static char *
+get_device_aliases_string(
+ dm_descriptor_t desc)
+{
+ static char buf[BUFSIZ];
+ dlist_t *aliases = NULL;
+ dlist_t *iter = NULL;
+
+ buf[0] = '\0';
+ (void) get_aliases(desc, &aliases);
+ for (iter = aliases; iter != NULL; iter = iter->next) {
+ if (*buf == '\0') {
+ (void) snprintf(buf, BUFSIZ-1, "\"%s\"", (char *)iter->obj);
+ } else {
+ char tmp[BUFSIZ];
+ (void) strcpy(buf, tmp);
+ (void) snprintf(buf, BUFSIZ-1, "%s, \"%s\"",
+ tmp, (char *)iter->obj);
+ }
+ }
+ dlist_free_items(aliases, free);
+
+ return (buf);
+}
diff --git a/usr/src/cmd/lvm/metassist/layout/layout_validate.h b/usr/src/cmd/lvm/metassist/layout/layout_validate.h
new file mode 100644
index 0000000000..f4af5e7839
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/layout/layout_validate.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef _LAYOUT_VALIDATE_H
+#define _LAYOUT_VALIDATE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "volume_devconfig.h"
+
+extern int validate_request(devconfig_t *req);
+
+extern int validate_request_sizes(devconfig_t *req);
+
+extern int validate_request_avail_unavail(devconfig_t *req);
+
+extern int validate_reserved_slices();
+
+extern int release_validation_caches();
+
+extern int validate_basic_svm_config();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LAYOUT_VALIDATE_H */
diff --git a/usr/src/cmd/lvm/metassist/scripts/Makefile b/usr/src/cmd/lvm/metassist/scripts/Makefile
new file mode 100644
index 0000000000..92eff55f20
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/scripts/Makefile
@@ -0,0 +1,40 @@
+#
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+METASSIST_TOPLEVEL = ..
+
+include $(METASSIST_TOPLEVEL)/../../Makefile.cmd
+include $(METASSIST_TOPLEVEL)/Makefile.env
+
+SHFILES = errifoutput
+
+CLEANFILES += $(SHFILES)
+
+all: $(SHFILES)
+
+include $(METASSIST_TOPLEVEL)/Makefile.targ
diff --git a/usr/src/cmd/lvm/metassist/scripts/errifoutput.sh b/usr/src/cmd/lvm/metassist/scripts/errifoutput.sh
new file mode 100644
index 0000000000..cab32d4ab3
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/scripts/errifoutput.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Runs the command passed as arguments, echoes the output to stderr.
+#
+# Exits with 0 (success) if the command exits with 0 and has no
+# output.
+#
+# Exits with 1 (failure) if the command exits with 0 and has output.
+#
+# Exits with the exit code of the command if it exits with a non-zero
+# exit code.
+
+output=`"$@" 2>&1`
+result=$?
+
+if [ -n "$output" ]
+then
+ echo "$output" >&2
+ test $result = 0 && result=1
+fi
+
+exit $result
diff --git a/usr/src/cmd/lvm/metassist/sysfiles/Makefile b/usr/src/cmd/lvm/metassist/sysfiles/Makefile
new file mode 100644
index 0000000000..13a60dcf9c
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/sysfiles/Makefile
@@ -0,0 +1,48 @@
+#
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+METASSIST_TOPLEVEL = ..
+
+include $(METASSIST_TOPLEVEL)/../../Makefile.cmd
+include $(METASSIST_TOPLEVEL)/Makefile.env
+
+# Files to be copied to /etc/defaults
+DEFAULTFILES = \
+ metassist.xml
+
+# Files to be copied to /usr/share/lib/xml/dtd
+DTDFILES = \
+ volume-config.dtd \
+ volume-defaults.dtd \
+ volume-request.dtd
+
+# Files to be copied to /usr/share/lib/xml/style
+STYLEFILES = \
+ volume-command.xsl
+
+include $(METASSIST_TOPLEVEL)/Makefile.targ
diff --git a/usr/src/cmd/lvm/metassist/sysfiles/metassist.xml b/usr/src/cmd/lvm/metassist/sysfiles/metassist.xml
new file mode 100644
index 0000000000..3fae198174
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/sysfiles/metassist.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8" ?>
+
+<!--
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * 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
+ *
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Specifies the default settings for volume requests used by
+ * metassist(1M).
+ *
+ * See volume-request(4) for a detailed description of the syntax.
+ -->
+<!DOCTYPE volume-defaults SYSTEM "/usr/share/lib/xml/dtd/volume-defaults.dtd">
+
+<volume-defaults>
+</volume-defaults>
diff --git a/usr/src/cmd/lvm/metassist/sysfiles/volume-command.xsl b/usr/src/cmd/lvm/metassist/sysfiles/volume-command.xsl
new file mode 100644
index 0000000000..1d669ab420
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/sysfiles/volume-command.xsl
@@ -0,0 +1,706 @@
+<?xml version="1.0" encoding="utf-8" ?>
+
+<!--
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * 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
+ *
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Used by metassist(1M) to create a Bourne shell script from an XML
+ * file conforming to the volume-config DTD.
+ *
+ * See volume-config(4) for a detailed description of the volume-
+ * config syntax.
+ -->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:output method="text"/>
+
+ <!-- Set a default lang -->
+ <xsl:param name="lang">en</xsl:param>
+
+ <!-- The file containing localized <message> elements -->
+ <!-- Currently set to local doc until an i18n scheme is established -->
+ <xsl:variable name="msgfile" select="document('')"/>
+ <xsl:variable name="langprefix" select="substring-before($lang,'-')"/>
+
+ <!-- Root template -->
+ <xsl:template match="/">
+
+ <!-- Use Bourne shell -->
+ <xsl:text>#!/bin/sh
+
+#
+# </xsl:text>
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Environment</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:text>
+#
+
+# </xsl:text>
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Amend PATH</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:text>
+PATH="/usr/sbin:/usr/bin:$PATH"
+export PATH
+
+# </xsl:text>
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Disk set name</xsl:with-param>
+ </xsl:call-template>
+
+ <!-- Set disk set -->
+ <xsl:text>&#x0a;</xsl:text>
+ <xsl:text>diskset='</xsl:text>
+ <xsl:value-of select="//diskset/@name" />
+
+ <!-- &#x0a; is a newline entity -->
+ <xsl:text>'&#x0a;</xsl:text>
+
+ <xsl:text>
+#
+# </xsl:text>
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Functions</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:text>
+#
+
+# </xsl:text>
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Echo (verbose) and exec given command, exit on error</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:text>
+execho () {
+ test -n "$verbose" &amp;&amp; echo "$@"
+ "$@" || exit
+}
+
+# </xsl:text>
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Get full /dev/rdsk path of given slice</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:text>
+fullpath () {
+ case "$1" in
+ /dev/dsk/*|/dev/did/dsk/*) echo "$1" | sed 's/dsk/rdsk/' ;;
+ /*) echo "$1" ;;
+ *) echo /dev/rdsk/"$1" ;;
+ esac
+}
+
+# </xsl:text>
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Run fmthard, ignore partboot error, error if output</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:text>
+fmthard_special () {
+ ignore='Error writing partboot'
+ out=`fmthard "$@" 2&gt;&amp;1`
+ result=$?
+ echo "$out" |
+ case "$out" in
+ *"$ignore"*) grep -v "$ignore"; return 0 ;;
+ '') return "$result" ;;
+ *) cat; return 1 ;;
+ esac &gt;&amp;2
+}
+
+#
+# </xsl:text>
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Main</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:text>
+#
+
+# </xsl:text>
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Verify root</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:text>
+if [ "`id | sed 's/^[^(]*(\([^)]*\).*/\1/'`" != root ]
+then
+ echo "</xsl:text>
+
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">This script must be run as root.</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:text>" >&amp;2
+ exit 1;
+fi
+
+# </xsl:text>
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Check for verbose option</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:text>
+case "$1" in
+ -v) verbose=1 ;;
+ *) verbose= ;;
+esac
+ </xsl:text>
+
+ <!-- Create disk set -->
+ <xsl:apply-templates select="//diskset" mode="create"/>
+
+ <!-- Format unused slices -->
+ <xsl:apply-templates select="//slice[@sizeinblocks = 0]" mode="create"/>
+
+ <!-- Add disks to set -->
+ <xsl:apply-templates select="//disk" mode="add"/>
+
+ <!-- Format used slices -->
+ <xsl:apply-templates select="//slice[@sizeinblocks != 0]" mode="create"/>
+
+ <!-- Create HSPs -->
+ <xsl:apply-templates select="//hsp" mode="create"/>
+
+ <!-- Create stripes -->
+ <xsl:apply-templates select="//stripe" mode="create"/>
+
+ <!-- Create concats -->
+ <xsl:apply-templates select="//concat" mode="create"/>
+
+ <!-- Create mirrors -->
+ <xsl:apply-templates select="//mirror" mode="create"/>
+
+ </xsl:template>
+
+ <!-- "Create disk set" template -->
+ <xsl:template match="diskset" mode="create">
+ <xsl:text>&#x0a;# </xsl:text>
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Does the disk set exist?</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:text>
+if metaset -s "$diskset" >/dev/null 2>&amp;1
+then
+ # </xsl:text>
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Take control of disk set</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:text>
+ execho metaset -s "$diskset" -t
+else
+ # </xsl:text>
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Create the disk set</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:text>
+ autotakeargs=
+ /usr/sbin/clinfo || autotakeargs='-A enable'
+ execho metaset -s "$diskset" $autotakeargs -a -h `uname -n | cut -f1 -d.`
+fi
+ </xsl:text>
+ </xsl:template>
+
+ <!-- "Add disk" template -->
+ <xsl:template match="disk" mode="add">
+ <!-- Add comment -->
+ <xsl:if test="position() = 1">
+ <xsl:text>&#x0a;# </xsl:text>
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Add disks to set</xsl:with-param>
+ </xsl:call-template>
+ <xsl:text>&#x0a;</xsl:text>
+ </xsl:if>
+
+ <!-- Output command -->
+ <xsl:call-template name="parameterize">
+ <xsl:with-param name="string">execho metaset -s "$diskset" -a {1}</xsl:with-param>
+ <xsl:with-param name="1" select="@name"/>
+ </xsl:call-template>
+
+ <xsl:text>&#x0a;</xsl:text>
+ </xsl:template>
+
+ <!-- "Create slice" template -->
+ <xsl:template match="slice" mode="create">
+
+ <!-- Add comment -->
+ <xsl:if test="position() = 1">
+ <xsl:text>&#x0a;# </xsl:text>
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Format slices</xsl:with-param>
+ </xsl:call-template>
+ <xsl:text>&#x0a;</xsl:text>
+ </xsl:if>
+
+ <!-- Does this slice have a start sector and size? -->
+ <xsl:if test="(@startsector and @sizeinblocks) or (@sizeinblocks = 0)">
+
+ <!-- Output command -->
+ <xsl:call-template name="parameterize">
+ <xsl:with-param name="string">execho fmthard_special -d {1}:{5}:0:{2}:{3} `fullpath {4}`</xsl:with-param>
+ <xsl:with-param name="1">
+ <xsl:call-template name="getslice">
+ <xsl:with-param name="device" select="@name" />
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="2">
+ <xsl:choose>
+ <!-- When zeroing out a slice, use 0 for start sector -->
+ <xsl:when test="@sizeinblocks = 0">0</xsl:when>
+ <!-- Otherwise, use the start sector supplied -->
+ <xsl:otherwise>
+ <xsl:value-of select="@startsector" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ <xsl:with-param name="3" select="@sizeinblocks" />
+ <xsl:with-param name="4" select="@name" />
+ <xsl:with-param name="5">
+ <xsl:choose>
+ <!-- When zeroing out a slice, use 0 (V_UNASSIGNED) slice tag -->
+ <xsl:when test="@sizeinblocks = 0">0</xsl:when>
+ <!-- Otherwise, use 4 (V_USR) -->
+ <xsl:otherwise>4</xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:text>&#x0a;</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <!-- Template for a "create volume" comment -->
+ <xsl:template name="createdevcomment">
+ <!-- Indent parameter -->
+ <xsl:param name = "indent" />
+
+ <xsl:text>&#x0a;</xsl:text>
+ <xsl:value-of select="$indent" />
+ <xsl:text># </xsl:text>
+
+ <!-- Add comment -->
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Create {1} {2}</xsl:with-param>
+ <xsl:with-param name="1" select="name()"/>
+ <xsl:with-param name="2" select="@name"/>
+ </xsl:call-template>
+
+ <xsl:text>&#x0a;</xsl:text>
+ </xsl:template>
+
+ <!-- "Create hsp" template -->
+ <xsl:template match="hsp" mode="create">
+
+ <!-- Does this HSP contain slice elements? -->
+ <xsl:if test="slice">
+
+ <xsl:text>&#x0a;# </xsl:text>
+
+ <!-- Add comment -->
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Does {1} exist?</xsl:with-param>
+ <xsl:with-param name="1" select="@name"/>
+ </xsl:call-template>
+
+ <xsl:text>&#x0a;</xsl:text>
+
+ <!-- Output command to test for existence of HSP -->
+ <xsl:call-template name="parameterize">
+ <xsl:with-param name="string">metahs -s "$diskset" -i {1} >/dev/null 2>&amp;1 || {</xsl:with-param>
+ <xsl:with-param name="1" select="@name"/>
+ </xsl:call-template>
+
+ <!-- Add comment -->
+ <xsl:call-template name="createdevcomment">
+ <xsl:with-param name = "indent" xml:space="preserve"> </xsl:with-param>
+ </xsl:call-template>
+
+ <!-- Output command to create HSP -->
+ <xsl:call-template name="parameterize">
+ <xsl:with-param name="string"> execho metainit -s "$diskset" {1}</xsl:with-param>
+ <xsl:with-param name="1" select="@name"/>
+ </xsl:call-template>
+
+ <xsl:text>&#x0a;}&#x0a;&#x0a;# </xsl:text>
+
+ <!-- Add comment -->
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Add slices to {1}</xsl:with-param>
+ <xsl:with-param name="1" select="@name"/>
+ </xsl:call-template>
+
+ <xsl:text>&#x0a;</xsl:text>
+
+ <xsl:for-each select="slice">
+
+ <!-- Output command -->
+ <xsl:call-template name="parameterize">
+ <xsl:with-param name="string">execho metahs -s "$diskset" -a {1} {2}</xsl:with-param>
+ <xsl:with-param name="1" select="../@name"/>
+ <xsl:with-param name="2" select="@name"/>
+ </xsl:call-template>
+
+ <xsl:text>&#x0a;</xsl:text>
+
+ </xsl:for-each>
+ </xsl:if>
+
+ </xsl:template>
+
+ <!-- "Create stripe/concat" template -->
+ <xsl:template match="stripe|concat" mode="create">
+
+ <!-- Does this stripe/concat contain slice elements? -->
+ <xsl:if test="slice">
+
+ <!-- Add comment -->
+ <xsl:call-template name="createdevcomment"/>
+
+ <!-- Output command -->
+ <xsl:text>execho metainit -s "$diskset" </xsl:text>
+ <xsl:value-of select="@name" />
+
+ <xsl:choose>
+ <!-- Stripe-specific parameters -->
+ <xsl:when test="name() = 'stripe'">
+ <xsl:text> 1 </xsl:text>
+ <xsl:value-of select="count(slice)" />
+
+ <xsl:for-each select="slice">
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="@name" />
+ </xsl:for-each>
+
+ <!-- Does this stripe contain an interlace attribute? -->
+ <xsl:if test="@interlace">
+
+ <!-- Write interlace with unit string -->
+ <xsl:variable name="interlace"
+ select="substring-before(@interlace, 'KB')"/>
+ <xsl:choose>
+ <xsl:when test="$interlace != ''">
+ <xsl:value-of select="concat(' -i ', $interlace, 'k')" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="interlace"
+ select="substring-before(@interlace, 'MB')"/>
+ <xsl:choose>
+ <xsl:when test="$interlace != ''">
+ <xsl:value-of select="concat(' -i ', $interlace, 'm')" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="interlace"
+ select="substring-before(@interlace, 'BLOCKS')"/>
+ <xsl:if test="$interlace != ''">
+ <xsl:value-of select="concat(' -i ', $interlace, 'b')" />
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:when>
+
+ <!-- Concat-specific parameters -->
+ <xsl:otherwise>
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="count(slice)" />
+
+ <xsl:for-each select="slice">
+ <xsl:text> 1 </xsl:text>
+ <xsl:value-of select="@name" />
+ </xsl:for-each>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <xsl:text>&#x0a;</xsl:text>
+ </xsl:if>
+
+ <!-- Does this stripe/concat contain hsp elements? -->
+ <xsl:if test="hsp">
+
+ <xsl:text>&#x0a;# </xsl:text>
+
+ <!-- Add comment -->
+ <xsl:call-template name="gettext">
+ <xsl:with-param name="msgid">Associate {1} {2} with hot spare pool {3}</xsl:with-param>
+ <xsl:with-param name="1" select="name()"/>
+ <xsl:with-param name="2" select="@name"/>
+ <xsl:with-param name="3" select="hsp/@name"/>
+ </xsl:call-template>
+
+ <xsl:text>&#x0a;</xsl:text>
+
+ <!-- Output command -->
+ <xsl:call-template name="parameterize">
+ <xsl:with-param name="string">execho metaparam -s "$diskset" -h {1} {2}</xsl:with-param>
+ <xsl:with-param name="1" select="hsp/@name"/>
+ <xsl:with-param name="2" select="@name"/>
+ </xsl:call-template>
+
+ <xsl:text>&#x0a;</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <!-- "Create mirror" template -->
+ <xsl:template match="mirror" mode="create">
+ <!-- Add comment -->
+ <xsl:call-template name="createdevcomment"/>
+
+ <!-- Attach submirrors -->
+ <xsl:for-each select="stripe|concat">
+ <xsl:choose >
+ <xsl:when test="position() = 1">
+
+ <!-- Output create command -->
+ <xsl:call-template name="parameterize">
+ <xsl:with-param name="string">execho metainit -s "$diskset" {1} -m {2}</xsl:with-param>
+ <xsl:with-param name="1" select="../@name"/>
+ <xsl:with-param name="2" select="@name"/>
+ </xsl:call-template>
+
+ <!-- Read option -->
+ <xsl:choose >
+ <!-- Geometric -->
+ <xsl:when test="../@read = 'GEOMETRIC'">
+ <xsl:text> -g</xsl:text>
+ </xsl:when>
+
+ <!-- First -->
+ <xsl:when test="../@read = 'FIRST'">
+ <xsl:text> -r</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+
+ <!-- Write option - serial -->
+ <xsl:if test="../@write = 'SERIAL'">
+ <xsl:text> -S</xsl:text>
+ </xsl:if>
+
+ <!-- Pass number -->
+ <xsl:if test="../@passnum">
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="../@passnum" />
+ </xsl:if>
+
+ <xsl:text>&#x0a;</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <!-- Output attach command -->
+ <xsl:call-template name="parameterize">
+ <xsl:with-param name="string">execho metattach -s "$diskset" {1} {2}</xsl:with-param>
+ <xsl:with-param name="1" select="../@name"/>
+ <xsl:with-param name="2" select="@name"/>
+ </xsl:call-template>
+
+ <xsl:text>&#x0a;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+ </xsl:template>
+
+ <!-- Get the slice index from a device string -->
+ <xsl:template name="getslice">
+ <xsl:param name="device" />
+
+ <xsl:choose>
+
+ <!-- Does $device contain 's'? -->
+ <xsl:when test="contains($device, 's')">
+
+ <!-- Recurse with remaining text -->
+ <xsl:call-template name="getslice">
+ <xsl:with-param name="device" select="substring-after($device, 's')" />
+ </xsl:call-template>
+ </xsl:when>
+
+ <!-- No match -->
+ <xsl:otherwise>
+ <xsl:value-of select="$device" />
+ </xsl:otherwise>
+
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- Generic (global) search and replace template -->
+ <xsl:template name="searchreplace">
+ <xsl:param name="text" />
+ <xsl:param name="search" />
+ <xsl:param name="replace" />
+
+ <xsl:choose>
+
+ <!-- Does $text contain $search? -->
+ <xsl:when test="contains($text, $search)">
+
+ <!-- Print text before match -->
+ <xsl:value-of select="substring-before($text, $search)" />
+
+ <!-- Print replaced text -->
+ <xsl:value-of select="$replace" />
+
+ <!-- Recurse with remaining text -->
+ <xsl:call-template name="searchreplace">
+ <xsl:with-param name="text" select="substring-after($text, $search)" />
+ <xsl:with-param name="search" select="$search" />
+ <xsl:with-param name="replace" select="$replace" />
+ </xsl:call-template>
+ </xsl:when>
+
+ <!-- No match -->
+ <xsl:otherwise>
+ <xsl:value-of select="$text" />
+ </xsl:otherwise>
+
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ * Given a message ID (msgid), find a localized message string
+ * stored in $msgfile as a xsl:variable. Return the first
+ * occurance of:
+ *
+ * 1. The message localized for the language code and country
+ * code, ie. "en-us"
+ *
+ * 2. The message localized for a sublanguage
+ *
+ * 3. The message localized for the language code only, ie. "en"
+ *
+ * 4. The message localized for the language code, with any
+ * country code, ie. "en-gb"
+ *
+ * 5. $msgid
+ *
+ * Parameters:
+ *
+ * msgid: The message identification key
+ *
+ * 1, 2, 3, 4, 5: Parameters to replace "{1}", "{2}", "{3}",
+ * "{4}", "{5}" respectively, in the retrieved message
+ -->
+ <xsl:template name="gettext">
+ <xsl:param name="msgid"/>
+ <xsl:param name="1"/>
+ <xsl:param name="2"/>
+ <xsl:param name="3"/>
+ <xsl:param name="4"/>
+ <xsl:param name="5"/>
+ <xsl:variable name="messages" select="$msgfile//message[@msgid=$msgid]"/>
+
+ <xsl:call-template name="parameterize">
+ <xsl:with-param name="1" select="$1"/>
+ <xsl:with-param name="2" select="$2"/>
+ <xsl:with-param name="3" select="$3"/>
+ <xsl:with-param name="4" select="$4"/>
+ <xsl:with-param name="5" select="$5"/>
+ <xsl:with-param name="string">
+
+ <xsl:choose>
+
+ <!-- Exact match for $lang -->
+ <xsl:when test="$messages[@xml:lang=$lang]">
+ <xsl:value-of select="$messages[@xml:lang=$lang][1]"/>
+ </xsl:when>
+
+ <!-- Sublanguage of $lang -->
+ <xsl:when test="$messages[lang($lang)]">
+ <xsl:value-of name="message" select="$messages[lang($lang)][1]"/>
+ </xsl:when>
+
+ <!-- Exact match for $langprefix -->
+ <xsl:when test="$messages[@xml:lang=$langprefix]">
+ <xsl:value-of select="$messages[@xml:lang=$langprefix][1]"/>
+ </xsl:when>
+
+ <!-- Sublanguage of $langprefix -->
+ <xsl:when test="$messages[lang($langprefix)]">
+ <xsl:value-of select="$messages[lang($langprefix)][1]"/>
+ </xsl:when>
+
+ <!-- No match found, return msgid -->
+ <xsl:otherwise>
+ <xsl:value-of select="$msgid"/>
+ </xsl:otherwise>
+
+ </xsl:choose>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!-- Parameterize up to 5 parameters -->
+ <xsl:template name="parameterize">
+ <xsl:param name="string"/>
+ <xsl:param name="1"/>
+ <xsl:param name="2"/>
+ <xsl:param name="3"/>
+ <xsl:param name="4"/>
+ <xsl:param name="5"/>
+
+ <xsl:call-template name="searchreplace">
+ <xsl:with-param name="text">
+ <xsl:call-template name="searchreplace">
+ <xsl:with-param name="text">
+ <xsl:call-template name="searchreplace">
+ <xsl:with-param name="text">
+ <xsl:call-template name="searchreplace">
+ <xsl:with-param name="text">
+ <xsl:call-template name="searchreplace">
+ <xsl:with-param name="text" select="$string"/>
+ <xsl:with-param name="search">{1}</xsl:with-param>
+ <xsl:with-param name="replace" select="$1"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="search">{2}</xsl:with-param>
+ <xsl:with-param name="replace" select="$2"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="search">{3}</xsl:with-param>
+ <xsl:with-param name="replace" select="$3"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="search">{4}</xsl:with-param>
+ <xsl:with-param name="replace" select="$4"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="search">{5}</xsl:with-param>
+ <xsl:with-param name="replace" select="$5"/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!-- Localized message strings used throughout -->
+ <xsl:template name="localization">
+ <message xml:lang="de" msgid="Sample message">Beispielanzeige</message>
+ </xsl:template>
+
+</xsl:stylesheet>
diff --git a/usr/src/cmd/lvm/metassist/sysfiles/volume-config.dtd b/usr/src/cmd/lvm/metassist/sysfiles/volume-config.dtd
new file mode 100644
index 0000000000..979e6e0e59
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/sysfiles/volume-config.dtd
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="utf-8" ?>
+
+<!--
+ 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.
+ *
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Describes a storage configuration used by metassist(1M).
+ *
+ * See volume-config(4) for a detailed description of the syntax.
+ -->
+<!ELEMENT volume-config (diskset,disk*,slice*,hsp*,(concat|stripe)*,mirror*)>
+
+<!-- The existing disk set to use -->
+<!ELEMENT diskset EMPTY>
+<!ATTLIST diskset name CDATA #REQUIRED>
+
+<!--
+ *****************************************************************
+ *
+ * Disk definition
+ *
+ *****************************************************************
+ -->
+
+<!-- Describes a disk that should be added to the disk set -->
+<!ELEMENT disk EMPTY>
+
+<!-- The full name of the disk -->
+<!ATTLIST disk name CDATA #REQUIRED>
+
+<!--
+ *****************************************************************
+ *
+ * Slice/soft partition definitions
+ *
+ *****************************************************************
+ -->
+
+<!--
+ * Describes a newly-defined or existing slice.
+ *
+ * If existing, only the name is required.
+ *
+ * If newly-defined, the start sector and size are also required.
+ -->
+<!ELEMENT slice EMPTY>
+
+<!-- The full name of the slice -->
+<!ATTLIST slice name CDATA #REQUIRED>
+
+<!-- The starting sector for this slice -->
+<!ATTLIST slice startsector CDATA #IMPLIED>
+
+<!--
+ * The size of this slice in blocks or sectors.
+ -->
+<!ATTLIST slice sizeinblocks CDATA #IMPLIED>
+
+
+<!--
+ *****************************************************************
+ *
+ * HSP definition
+ *
+ *****************************************************************
+ -->
+
+<!--
+ * Describes a new or existing HSP
+ *
+ * The slices defined within will be added to this HSP.
+ -->
+<!ELEMENT hsp (slice*)>
+<!ATTLIST hsp name CDATA #REQUIRED>
+
+
+<!--
+ *****************************************************************
+ *
+ * Concat definition
+ *
+ *****************************************************************
+ -->
+
+<!--
+ * Describes a new or existing concat
+ *
+ * If existing, only the name is required.
+ *
+ * If new, the underlying slices and soft partitions are also
+ * required. The HSP definition is optional.
+ -->
+<!ELEMENT concat (slice*,hsp?)>
+<!ATTLIST concat name CDATA #REQUIRED>
+
+<!--
+ *****************************************************************
+ *
+ * Stripe definition
+ *
+ *****************************************************************
+ -->
+
+<!--
+ * Describes a new or existing stripe
+ *
+ * If existing, only the name is required.
+ *
+ * If new, the underlying slices and soft partitions are also
+ * required. The interlace and HSP definitions are optional.
+ -->
+<!ELEMENT stripe (slice*,hsp?)>
+<!ATTLIST stripe name CDATA #REQUIRED>
+
+<!--
+ * The interlace of this stripe, in the format <value><units>,
+ * where <units> is "BLOCKS", "KB", or "MB", and <value> is the
+ * multiplier of the units.
+ -->
+<!ATTLIST stripe interlace CDATA #IMPLIED>
+
+
+<!--
+ *****************************************************************
+ *
+ * Mirror definition
+ *
+ *****************************************************************
+ -->
+
+<!--
+ * Describes a new mirror
+ *
+ * The name of the mirror, and the underlying submirrors are
+ * required. The remaining attributes are optional.
+ -->
+<!ELEMENT mirror ((concat|stripe)+)>
+<!ATTLIST mirror name CDATA #REQUIRED>
+<!ATTLIST mirror read (ROUNDROBIN|GEOMETRIC|FIRST) #IMPLIED>
+<!ATTLIST mirror write (PARALLEL|SERIAL) #IMPLIED>
+<!ATTLIST mirror passnum CDATA #IMPLIED>
diff --git a/usr/src/cmd/lvm/metassist/sysfiles/volume-defaults.dtd b/usr/src/cmd/lvm/metassist/sysfiles/volume-defaults.dtd
new file mode 100644
index 0000000000..42e023e116
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/sysfiles/volume-defaults.dtd
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8" ?>
+
+<!--
+ 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.
+ *
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Describes the system- and diskset-wide defaults for new volume
+ * requests used by metassist(1M). Both global and diskset-specific
+ * defaults can be specified.
+ *
+ * See volume-request(4) for a detailed description of the syntax.
+ -->
+<!ELEMENT volume-defaults ((available|unavailable)*,(hsp|concat|stripe|mirror|volume)*,diskset*)>
+
+<!--
+ *
+ * Specify global defaults
+ *
+ -->
+
+<!-- Specify device to use instead of defaults -->
+<!ELEMENT available EMPTY>
+<!ATTLIST available name CDATA #REQUIRED>
+
+<!-- Specify device not to use -->
+<!ELEMENT unavailable EMPTY>
+<!ATTLIST unavailable name CDATA #REQUIRED>
+
+<!ELEMENT hsp EMPTY>
+<!ATTLIST hsp name CDATA #IMPLIED>
+
+<!ELEMENT concat EMPTY>
+<!ATTLIST concat usehsp (TRUE|FALSE) #IMPLIED>
+
+<!ELEMENT stripe EMPTY>
+<!ATTLIST stripe interlace CDATA #IMPLIED>
+<!ATTLIST stripe maxcomp CDATA #IMPLIED>
+<!ATTLIST stripe mincomp CDATA #IMPLIED>
+<!ATTLIST stripe usehsp (TRUE|FALSE) #IMPLIED>
+
+<!ELEMENT mirror EMPTY>
+<!ATTLIST mirror nsubmirrors CDATA #IMPLIED>
+<!ATTLIST mirror read (ROUNDROBIN|GEOMETRIC|FIRST) #IMPLIED>
+<!ATTLIST mirror write (PARALLEL|SERIAL) #IMPLIED>
+<!ATTLIST mirror passnum CDATA #IMPLIED>
+<!ATTLIST mirror usehsp (TRUE|FALSE) #IMPLIED>
+
+<!ELEMENT volume EMPTY>
+<!ATTLIST volume redundancy CDATA #IMPLIED>
+<!ATTLIST volume datapaths CDATA #IMPLIED>
+<!ATTLIST volume faultrecovery (TRUE|FALSE) #IMPLIED>
+
+<!--
+ *
+ * Specify per-disk set defaults
+ *
+ -->
+
+<!ELEMENT diskset ((available|unavailable)*,hsp?,(concat|stripe|mirror|volume)*)>
+<!ATTLIST diskset name CDATA #REQUIRED>
diff --git a/usr/src/cmd/lvm/metassist/sysfiles/volume-request.dtd b/usr/src/cmd/lvm/metassist/sysfiles/volume-request.dtd
new file mode 100644
index 0000000000..f4b3f9e68f
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/sysfiles/volume-request.dtd
@@ -0,0 +1,388 @@
+<?xml version="1.0" encoding="utf-8" ?>
+
+<!--
+ 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.
+ *
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Describes the request for a new volume configuration used by
+ * metassist(1M).
+ *
+ * See volume-request(4) for a detailed description of the syntax.
+ -->
+<!ELEMENT volume-request (diskset,(available|unavailable)*,hsp?,(concat|stripe|mirror|volume)*)>
+
+<!--
+ *****************************************************************
+ *
+ * Disk set definition
+ *
+ *****************************************************************
+ -->
+
+<!--
+ * The new or existing disk set to use. If the disk set does not
+ * exist, it will be created.
+ -->
+<!ELEMENT diskset EMPTY>
+<!ATTLIST diskset name CDATA #REQUIRED>
+
+
+<!--
+ *****************************************************************
+ *
+ * Available devices definition
+ *
+ *****************************************************************
+ -->
+
+<!--
+ * Specify a controller, target, disk, or slice that may be used in
+ * the construction of new SVM configuration.
+ *
+ * Disks specified herein should be:
+ *
+ * 1. part of the disk set named above
+ *
+ * or
+ *
+ * 2. unused and not belong to any disk set
+ *
+ * If a controller is specified, all slices on all disks on the
+ * controller which match the above criteria are considered
+ * available.
+ *
+ * If a target is specified, all slices on all disks on the target
+ * which match the above criteria are considered available.
+ *
+ * If no available devices are specified, all disks on the system
+ * which match the above criteria are considered available.
+ -->
+<!ELEMENT available EMPTY>
+<!ATTLIST available name CDATA #REQUIRED>
+
+<!--
+ * Specify a controller, target, disk, or slice to exclude from the
+ * available pool.
+ -->
+<!ELEMENT unavailable EMPTY>
+<!ATTLIST unavailable name CDATA #REQUIRED>
+
+
+<!--
+ *****************************************************************
+ *
+ * Hot spare pool definition
+ *
+ *****************************************************************
+ -->
+
+<!--
+ * Specify a new or existing hot spare pool for submirrors defined
+ * in this request. If a HSP is required by this request, and no
+ * HSP is specified here, the first existing HSP in the above disk
+ * set will be used instead. If no HSPs exist, a new HSP will be
+ * created.
+ *
+ * Optionally specify disks that can be used/excluded if new hot
+ * spares must be created.
+ -->
+<!ELEMENT hsp ((available|unavailable)*)>
+
+<!--
+ * The name to assign to this hot spare pool. If no name is
+ * specified, a name will be chosen automatically. No guarantees
+ * are made about the algorithm used, except that the resulting name
+ * will be one that is not currently in use.
+ -->
+<!ATTLIST hsp name CDATA #IMPLIED>
+
+<!--
+ *****************************************************************
+ *
+ * Slice definition
+ *
+ *****************************************************************
+ -->
+
+<!ELEMENT slice EMPTY>
+<!ATTLIST slice name CDATA #REQUIRED>
+
+
+<!--
+ *****************************************************************
+ *
+ * Stripe definition
+ *
+ *****************************************************************
+ -->
+
+<!--
+ * the following line should work, but fails xmllint for some reason
+ *
+ * <!ELEMENT stripe (slice*|(available|unavailable)*)>
+ -->
+
+<!--
+ * Create a new top-level stripe either by specifying the underlying
+ * components (slices and soft partitions) or by specifying the
+ * required size and optional available/unavailable resources.
+ -->
+<!ELEMENT stripe ((available|unavailable)*,slice*)>
+
+<!--
+ * The volume name to assign to this stripe. If no name is
+ * specified, a name will be chosen automatically. No guarantees
+ * are made about the algorithm used, except that the resulting name
+ * will be one that is not currently in use.
+ -->
+<!ATTLIST stripe name CDATA #IMPLIED>
+
+<!--
+ * The size of this stripe, in the format <value><units>, where
+ * <units> is "KB", "MB", "GB", or "TB", and <value> is the
+ * multiplier of the units.
+ *
+ * This attribute is ignored if the underlying components are
+ * explicitly specified.
+ -->
+<!ATTLIST stripe size CDATA #IMPLIED>
+
+<!--
+ * Specify the minimum and maximum number of components (slices and
+ * soft partitions) to use when constructing this stripe.
+ *
+ * The default value for mincomp is 4.
+ *
+ * The default value for maxcomp is 10.
+ *
+ * These attributes should not be specified if the underlying
+ * components are explicitly specified.
+ -->
+<!ATTLIST stripe mincomp CDATA #IMPLIED>
+<!ATTLIST stripe maxcomp CDATA #IMPLIED>
+
+<!--
+ * The interlace of this stripe, in the format <value><units>, where
+ * <units> is "BLOCKS", "KB", or "MB", and <value> is the multiplier
+ * of the units.
+ *
+ * The default value is 64KB.
+ -->
+<!ATTLIST stripe interlace CDATA #IMPLIED>
+
+<!--
+ * Boolean (TRUE or FALSE) indicating whether to use a HSP.
+ *
+ * This is only relevant if this stripe is a submirror, i.e. it is
+ * defined within a <mirror> element. If not, this attribute is
+ * ignored.
+ *
+ * The default value is FALSE.
+ -->
+<!ATTLIST stripe usehsp (TRUE|FALSE) #IMPLIED>
+
+
+<!--
+ *****************************************************************
+ *
+ * Concat definition
+ *
+ *****************************************************************
+ -->
+
+<!--
+ * the following line should work, but fails xmllint for some reason
+ *
+ * <!ELEMENT concat (slice+|(available|unavailable)*)>
+ -->
+
+<!--
+ * Create a new concat either by specifying the underlying slices
+ * and soft partitions or by specifying the required size and
+ * optional available/unavailable resources.
+ -->
+<!ELEMENT concat ((available|unavailable)*,slice*)>
+
+<!--
+ * The volume name to assign to this concat. If no name is
+ * specified, a name will be chosen automatically. No guarantees
+ * are made about the algorithm used, except that the resulting name
+ * will be one that is not currently in use.
+ -->
+<!ATTLIST concat name CDATA #IMPLIED>
+
+<!--
+ * The size of this concat, in the format <value><units>, where
+ * <units> is "KB", "MB", "GB", or "TB", and <value> is the
+ * multiplier of the units.
+ *
+ * This attribute is ignored if the underlying slices/soft
+ * partitions are explicitly specified.
+ -->
+<!ATTLIST concat size CDATA #IMPLIED>
+
+<!--
+ * Boolean (TRUE or FALSE) indicating whether to use a HSP.
+ *
+ * This is only relevant if this concat is a submirror, i.e. it is
+ * defined within a <mirror> element. If not, this attribute is
+ * ignored.
+ *
+ * The default value is FALSE.
+ -->
+<!ATTLIST concat usehsp (TRUE|FALSE) #IMPLIED>
+
+
+<!--
+ *****************************************************************
+ *
+ * Mirror definition
+ *
+ *****************************************************************
+ -->
+
+<!--
+ * Create a new mirror either by specifying the underlying
+ * submirrors or by specifying the required size and optional
+ * available/unavailable resources.
+ -->
+<!ELEMENT mirror ((available|unavailable)*,(stripe|concat)*)>
+
+<!--
+ * The volume name to assign to this mirror. If no name is
+ * specified, a name will be chosen automatically. No guarantees
+ * are made about the algorithm used, except that the resulting name
+ * will be one that is not currently in use.
+ -->
+<!ATTLIST mirror name CDATA #IMPLIED>
+
+<!--
+ * The number of submirrors under this mirror. If this number
+ * exceeds the number of explicitly specified submirrors, the
+ * remaining submirrors will be created from the available
+ * resources.
+ *
+ * The default value is 2.
+ -->
+<!ATTLIST mirror nsubmirrors CDATA #IMPLIED>
+
+<!--
+ * The size of this mirror, in the format <value><units>, where
+ * <units> is "KB", "MB", "GB", or "TB", and <value> is the
+ * multiplier of the units.
+ *
+ * This attribute is required if no submirrors are defined.
+ -->
+<!ATTLIST mirror size CDATA #IMPLIED>
+
+<!--
+ * Specify values for common mirror options.
+ *
+ * The default values are described in the metainint(1M) man page.
+ -->
+<!ATTLIST mirror read (ROUNDROBIN|GEOMETRIC|FIRST) #IMPLIED>
+<!ATTLIST mirror write (PARALLEL|SERIAL) #IMPLIED>
+<!ATTLIST mirror passnum CDATA #IMPLIED>
+
+<!--
+ * Boolean (TRUE or FALSE) indicating whether to use a HSP for each
+ * submirror. This can be overridden using the submirror's usehsp
+ * attribute.
+ *
+ * The default value is FALSE.
+ -->
+<!ATTLIST mirror usehsp (TRUE|FALSE) #IMPLIED>
+
+
+
+<!--
+ *****************************************************************
+ *
+ * QoS volume definition
+ *
+ *****************************************************************
+ -->
+
+<!--
+ * Create a volume defined by Quality of Service attributes. The
+ * default is to create a non-redundant volume. In any case, high
+ * performance is attempted.
+ *
+ * Translates to a stripe (non-redundant) or mirror of stripes
+ * (redundant) for SVM.
+ -->
+<!ELEMENT volume ((available|unavailable)*)>
+
+<!--
+ * The name to assign to this volume. If no name is specified, a
+ * name will be chosen automatically. No guarantees are made about
+ * the algorithm used, except that the resulting name will be one
+ * that is not currently in use.
+ -->
+<!ATTLIST volume name CDATA #IMPLIED>
+
+<!--
+ * The size of this mirror, in the format <value><units>, where
+ * <units> is "KB", "MB", "GB", or "TB", and <value> is the
+ * multiplier of the units.
+ *
+ * This attribute is required if no submirrors are defined.
+ -->
+<!ATTLIST volume size CDATA #REQUIRED>
+
+<!--
+ * Specify the redundancy level (0-4) of the data.
+ *
+ * If redundancy is 0, a stripe will be created.
+ *
+ * If redundancy is > 0, a mirror with this number of submirrors
+ * will be created. In this case, the volume can suffer a disk
+ * failure on <n-1> copies without data loss. With the use of HSPs
+ * (see the "faultrecovery" attribute), a volume can suffer a disk
+ * failure on <n+hsps-1> volumes without data loss, assuming non-
+ * concurrent failures.
+ *
+ * The default value is 0.
+ -->
+<!ATTLIST volume redundancy CDATA #IMPLIED>
+
+<!--
+ * Boolean (TRUE or FALSE) indicating whether to include fault
+ * recovery.
+ *
+ * If this attribute is TRUE, a mirror will be created and its
+ * submirror(s) will be associated with a HSP.
+ *
+ * The default value is FALSE.
+ -->
+<!ATTLIST volume faultrecovery (TRUE|FALSE) #IMPLIED>
+
+<!--
+ * Number of data paths through which to ensure the volume is
+ * reachable.
+ *
+ * The default value is 1.
+ -->
+<!ATTLIST volume datapaths CDATA #IMPLIED>
diff --git a/usr/src/cmd/lvm/metassist/xml/Makefile b/usr/src/cmd/lvm/metassist/xml/Makefile
new file mode 100644
index 0000000000..5290dfaaed
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/xml/Makefile
@@ -0,0 +1,48 @@
+#
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+METASSIST_TOPLEVEL = ..
+
+SRCS = xml_convert.c
+OBJS = $(SRCS:%.c=%.o)
+HDRS = $(SRCS:%.c=%.h)
+MSGFILES = $(SRCS:%.c=%.i)
+
+include $(METASSIST_TOPLEVEL)/../../Makefile.cmd
+include $(METASSIST_TOPLEVEL)/Makefile.env
+
+INCLUDES += -I /usr/include/libxml2 -I../common
+CFLAGS += $(INCLUDES)
+
+POFILE = xmlp.po
+
+include $(METASSIST_TOPLEVEL)/Makefile.targ
+
+# Build .po file from message files
+$(POFILE): $(MSGFILES)
+ $(BUILDPO.msgfiles)
diff --git a/usr/src/cmd/lvm/metassist/xml/xml_convert.c b/usr/src/cmd/lvm/metassist/xml/xml_convert.c
new file mode 100644
index 0000000000..fb2ef57e60
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/xml/xml_convert.c
@@ -0,0 +1,2311 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "xml_convert.h"
+
+#include <errno.h>
+#include <string.h>
+#include <libintl.h>
+#include <libxslt/xslt.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/transform.h>
+#include <libxslt/xsltutils.h>
+#include <locale.h>
+#include <unistd.h>
+#include "volume_error.h"
+#include "volume_output.h"
+#include "volume_string.h"
+
+/*
+ * IDs for localized messages in the generated command script
+ */
+
+#define CMD_MSG_ENVIRONMENT "Environment"
+#define CMD_MSG_AMEND_PATH "Amend PATH"
+#define CMD_MSG_DISK_SET_NAME "Disk set name"
+#define CMD_MSG_FUNCTIONS "Functions"
+/* CSTYLED */
+#define CMD_MSG_ECHO_AND_EXEC "Echo (verbose) and exec given command, exit on error"
+#define CMD_MSG_GET_FULL_PATH "Get full /dev/rdsk path of given slice"
+/* CSTYLED */
+#define CMD_MSG_FMTHARD_SPECIAL "Run fmthard, ignore partboot error, error if output"
+#define CMD_MSG_MAIN "Main"
+#define CMD_MSG_VERIFY_ROOT "Verify root"
+#define CMD_MSG_RUN_AS_ROOT "This script must be run as root."
+#define CMD_MSG_CHECK_FOR_VERBOSE "Check for verbose option"
+#define CMD_MSG_DOES_DISK_SET_EXIST "Does the disk set exist?"
+#define CMD_MSG_TAKE_DISK_SET "Take control of disk set"
+#define CMD_MSG_CREATE_THE_DISK_SET "Create the disk set"
+#define CMD_MSG_ADD_DISKS_TO_SET "Add disks to set"
+#define CMD_MSG_FORMAT_SLICES "Format slices"
+#define CMD_MSG_CREATE "Create {1} {2}"
+#define CMD_MSG_DOES_EXIST "Does {1} exist?"
+#define CMD_MSG_ADD_SLICES_TO "Add slices to {1}"
+/* CSTYLED */
+#define CMD_MSG_ASSOCIATE_WITH_HSP "Associate {1} {2} with hot spare pool {3}"
+
+/*
+ * ******************************************************************
+ *
+ * Data types
+ *
+ * ******************************************************************
+ */
+
+/*
+ * Encapsulates the parsing of an XML attribute
+ */
+typedef struct {
+
+ /* The name of the attribute */
+ char *name;
+
+ /*
+ * A function to validate and set the XML attribute value in
+ * the given devconfig_t structure.
+ *
+ * @param name
+ * the name of the XML attribute
+ *
+ * @param value
+ * the value of the XML attribute
+ *
+ * @return 0 if the given value was valid and set
+ * successfully, non-zero otherwise.
+ */
+ int (*validate_set)(devconfig_t *device, char *name, char *value);
+
+ /*
+ * A function to get the XML attribute value in the given
+ * devconfig_t structure.
+ *
+ * @param name
+ * the name of the XML attribute
+ *
+ * @param value
+ * the value of the XML attribute
+ *
+ * @return 0 if the given value was retrieved
+ * successfully, non-zero otherwise.
+ */
+ int (*get_as_string)(devconfig_t *device, char *name, char **value);
+} attr_t;
+
+/*
+ * Encapsulates the parsing of an XML element
+ */
+typedef struct {
+ /* The name of the element */
+ char *name;
+
+ /* The type of element to set in the devconfig_t */
+ component_type_t type;
+
+ /*
+ * When converting from XML to a devconfig_t hierarchy,
+ * indicates whether to create a new devconfig_t structure in
+ * the hierarchy when this XML element is encountered.
+ */
+ boolean_t is_hierarchical;
+
+ /*
+ * If is_hierarchical is B_TRUE, whether to use an existing
+ * devconfig_t structure of this type when this element is
+ * encountered
+ */
+ boolean_t singleton;
+
+ /* The valid XML attributes for this element */
+ attr_t *attributes;
+} element_t;
+
+typedef struct {
+ char *msgid;
+ char *message;
+} l10nmessage_t;
+
+/*
+ * ******************************************************************
+ *
+ * Function prototypes
+ *
+ * ******************************************************************
+ */
+
+static int validate_doc(xmlDocPtr doc, const char *name, const char *systemID);
+static int devconfig_to_xml(
+ xmlNodePtr parent, element_t elements[], devconfig_t *device);
+static int xml_to_devconfig(
+ xmlNodePtr cur, element_t elements[], devconfig_t *device);
+static int compare_is_a_diskset(void *obj1, void *obj2);
+static xmlNodePtr xml_find_node(
+ xmlNodePtr node, xmlChar *element, xmlChar *name);
+static xmlDocPtr create_localized_message_doc();
+static int create_localized_message_file(char **tmpfile);
+static int strtobool(char *str, boolean_t *value);
+static int ofprintf_terse(void *unused, char *fmt, ...);
+static int ofprintf_verbose(void *unused, char *fmt, ...);
+
+static int validate_set_size(
+ devconfig_t *volume, char *attr, char *value);
+static int validate_set_size_in_blocks(
+ devconfig_t *slice, char *attr, char *value);
+static int validate_set_diskset_name(
+ devconfig_t *diskset, char *attr, char *name);
+static int validate_add_available_name(
+ devconfig_t *device, char *attr, char *name);
+static int validate_add_unavailable_name(
+ devconfig_t *device, char *attr, char *name);
+static int validate_set_hsp_name(
+ devconfig_t *hsp, char *attr, char *name);
+static int validate_set_disk_name(
+ devconfig_t *disk, char *attr, char *name);
+static int validate_set_slice_name(
+ devconfig_t *slice, char *attr, char *name);
+static int validate_set_slice_start_block(
+ devconfig_t *slice, char *attr, char *value);
+static int validate_set_volume_name(
+ devconfig_t *volume, char *attr, char *name);
+static int validate_set_stripe_interlace(
+ devconfig_t *stripe, char *attr, char *value);
+static int validate_set_stripe_mincomp(
+ devconfig_t *stripe, char *attr, char *value);
+static int validate_set_stripe_maxcomp(
+ devconfig_t *stripe, char *attr, char *value);
+static int validate_set_volume_usehsp(
+ devconfig_t *volume, char *attr, char *value);
+static int validate_set_mirror_nsubmirrors(
+ devconfig_t *mirror, char *attr, char *value);
+static int validate_set_mirror_read(
+ devconfig_t *mirror, char *attr, char *value);
+static int validate_set_mirror_write(
+ devconfig_t *mirror, char *attr, char *value);
+static int validate_set_mirror_passnum(
+ devconfig_t *mirror, char *attr, char *value);
+static int validate_set_volume_redundancy(
+ devconfig_t *volume, char *attr, char *value);
+static int validate_set_volume_datapaths(
+ devconfig_t *volume, char *attr, char *value);
+
+static int get_as_string_name(
+ devconfig_t *device, char *attr, char **value);
+static int get_as_string_mirror_passnum(
+ devconfig_t *mirror, char *attr, char **value);
+static int get_as_string_mirror_read(
+ devconfig_t *mirror, char *attr, char **value);
+static int get_as_string_mirror_write(
+ devconfig_t *mirror, char *attr, char **value);
+static int get_as_string_size_in_blocks(
+ devconfig_t *device, char *attr, char **value);
+static int get_as_string_slice_start_block(
+ devconfig_t *slice, char *attr, char **value);
+static int get_as_string_stripe_interlace(
+ devconfig_t *stripe, char *attr, char **value);
+
+/*
+ * ******************************************************************
+ *
+ * Data
+ *
+ * ******************************************************************
+ */
+
+/* Valid units for the size attribute */
+units_t size_units[] = {
+ {UNIT_KILOBYTES, BYTES_PER_KILOBYTE},
+ {UNIT_MEGABYTES, BYTES_PER_MEGABYTE},
+ {UNIT_GIGABYTES, BYTES_PER_GIGABYTE},
+ {UNIT_TERABYTES, BYTES_PER_TERABYTE},
+ {NULL, 0}
+};
+
+/* Valid units for the interlace attribute */
+units_t interlace_units[] = {
+ {UNIT_BLOCKS, BYTES_PER_BLOCK},
+ {UNIT_KILOBYTES, BYTES_PER_KILOBYTE},
+ {UNIT_MEGABYTES, BYTES_PER_MEGABYTE},
+ {NULL, 0}
+};
+
+/* <diskset> attributes */
+static attr_t diskset_attrs[] = {
+ { ATTR_NAME, validate_set_diskset_name, get_as_string_name },
+ { NULL, NULL, NULL }
+};
+
+/* <available> attributes */
+static attr_t available_attrs[] = {
+ { ATTR_NAME, validate_add_available_name, NULL },
+ { NULL, NULL, NULL }
+};
+
+/* <unavailable> attributes */
+static attr_t unavailable_attrs[] = {
+ { ATTR_NAME, validate_add_unavailable_name, NULL },
+ { NULL, NULL, NULL }
+};
+
+/* <hsp> attributes */
+static attr_t hsp_attrs[] = {
+ { ATTR_NAME, validate_set_hsp_name, get_as_string_name },
+ { NULL, NULL, NULL }
+};
+
+/* <disk> attributes */
+static attr_t disk_attrs[] = {
+ { ATTR_NAME, validate_set_disk_name, get_as_string_name },
+ { NULL, NULL, NULL }
+};
+
+/* <slice> attributes */
+static attr_t slice_attrs[] = {
+ { ATTR_NAME, validate_set_slice_name, get_as_string_name },
+ { ATTR_SIZEINBLOCKS, validate_set_size_in_blocks,
+ get_as_string_size_in_blocks },
+ { ATTR_SLICE_STARTSECTOR, validate_set_slice_start_block,
+ get_as_string_slice_start_block },
+ { NULL, NULL, NULL }
+};
+
+/* <stripe> attributes */
+static attr_t stripe_attrs[] = {
+ { ATTR_NAME, validate_set_volume_name, get_as_string_name },
+ { ATTR_SIZEINBYTES, validate_set_size, NULL },
+ { ATTR_STRIPE_MINCOMP, validate_set_stripe_mincomp, NULL },
+ { ATTR_STRIPE_MAXCOMP, validate_set_stripe_maxcomp, NULL },
+ { ATTR_STRIPE_INTERLACE, validate_set_stripe_interlace,
+ get_as_string_stripe_interlace },
+ { ATTR_VOLUME_USEHSP, validate_set_volume_usehsp, NULL },
+ { NULL, NULL, NULL }
+};
+
+/* <concat> attributes */
+static attr_t concat_attrs[] = {
+ { ATTR_NAME, validate_set_volume_name, get_as_string_name },
+ { ATTR_SIZEINBYTES, validate_set_size, NULL },
+ { ATTR_VOLUME_USEHSP, validate_set_volume_usehsp, NULL },
+ { NULL, NULL, NULL }
+};
+
+/* <mirror> attributes */
+static attr_t mirror_attrs[] = {
+ { ATTR_NAME, validate_set_volume_name, get_as_string_name },
+ { ATTR_MIRROR_NSUBMIRRORS, validate_set_mirror_nsubmirrors, NULL },
+ { ATTR_SIZEINBYTES, validate_set_size, NULL },
+ { ATTR_MIRROR_READ, validate_set_mirror_read,
+ get_as_string_mirror_read },
+ { ATTR_MIRROR_WRITE, validate_set_mirror_write,
+ get_as_string_mirror_write },
+ { ATTR_MIRROR_PASSNUM, validate_set_mirror_passnum,
+ get_as_string_mirror_passnum },
+ { ATTR_VOLUME_USEHSP, validate_set_volume_usehsp, NULL },
+ { NULL, NULL, NULL }
+};
+
+/* <volume> attributes */
+static attr_t volume_attrs[] = {
+ { ATTR_NAME, validate_set_volume_name, get_as_string_name },
+ { ATTR_SIZEINBYTES, validate_set_size, NULL },
+ { ATTR_VOLUME_REDUNDANCY, validate_set_volume_redundancy, NULL },
+ { ATTR_VOLUME_FAULTRECOVERY, validate_set_volume_usehsp, NULL },
+ { ATTR_VOLUME_DATAPATHS, validate_set_volume_datapaths, NULL },
+ { NULL, NULL, NULL }
+};
+
+/* volume-request elements */
+static element_t request_elements[] = {
+ { ELEMENT_DISKSET, TYPE_DISKSET, B_FALSE, B_FALSE, diskset_attrs },
+ { ELEMENT_AVAILABLE, TYPE_UNKNOWN, B_FALSE, B_FALSE, available_attrs },
+ { ELEMENT_UNAVAILABLE, TYPE_UNKNOWN, B_FALSE, B_FALSE,
+ unavailable_attrs },
+ { ELEMENT_HSP, TYPE_HSP, B_TRUE, B_FALSE, hsp_attrs },
+ { ELEMENT_SLICE, TYPE_SLICE, B_TRUE, B_FALSE, slice_attrs },
+ { ELEMENT_STRIPE, TYPE_STRIPE, B_TRUE, B_FALSE, stripe_attrs },
+ { ELEMENT_CONCAT, TYPE_CONCAT, B_TRUE, B_FALSE, concat_attrs },
+ { ELEMENT_MIRROR, TYPE_MIRROR, B_TRUE, B_FALSE, mirror_attrs },
+ { ELEMENT_VOLUME, TYPE_VOLUME, B_TRUE, B_FALSE, volume_attrs },
+ { NULL, NULL, B_FALSE, B_FALSE, NULL }
+};
+
+/* volume-defaults elements */
+static element_t default_elements[] = {
+ { ELEMENT_DISKSET, TYPE_DISKSET, B_TRUE, B_FALSE, diskset_attrs },
+ { ELEMENT_AVAILABLE, TYPE_UNKNOWN, B_FALSE, B_TRUE, available_attrs },
+ { ELEMENT_UNAVAILABLE, TYPE_UNKNOWN, B_FALSE, B_TRUE,
+ unavailable_attrs },
+ { ELEMENT_HSP, TYPE_HSP, B_TRUE, B_TRUE, hsp_attrs },
+ { ELEMENT_SLICE, TYPE_SLICE, B_TRUE, B_TRUE, slice_attrs },
+ { ELEMENT_STRIPE, TYPE_STRIPE, B_TRUE, B_TRUE, stripe_attrs },
+ { ELEMENT_CONCAT, TYPE_CONCAT, B_TRUE, B_TRUE, concat_attrs },
+ { ELEMENT_MIRROR, TYPE_MIRROR, B_TRUE, B_TRUE, mirror_attrs },
+ { ELEMENT_VOLUME, TYPE_VOLUME, B_TRUE, B_TRUE, volume_attrs },
+ { NULL, NULL, B_FALSE, B_FALSE, NULL }
+};
+
+/* volume-config elements */
+static element_t config_elements[] = {
+ { ELEMENT_DISKSET, TYPE_DISKSET, B_FALSE, B_FALSE, diskset_attrs },
+ { ELEMENT_DISK, TYPE_DRIVE, B_TRUE, B_FALSE, disk_attrs },
+ { ELEMENT_SLICE, TYPE_SLICE, B_TRUE, B_FALSE, slice_attrs },
+ { ELEMENT_HSP, TYPE_HSP, B_TRUE, B_FALSE, hsp_attrs },
+ { ELEMENT_STRIPE, TYPE_STRIPE, B_TRUE, B_FALSE, stripe_attrs },
+ { ELEMENT_CONCAT, TYPE_CONCAT, B_TRUE, B_FALSE, concat_attrs },
+ { ELEMENT_MIRROR, TYPE_MIRROR, B_TRUE, B_FALSE, mirror_attrs },
+ { NULL, NULL, B_FALSE, B_FALSE, NULL }
+};
+
+/*
+ * ******************************************************************
+ *
+ * External functions
+ *
+ * ******************************************************************
+ */
+
+/*
+ * Initialize the XML parser, setting defaults across all XML
+ * routines.
+ */
+void
+init_xml()
+{
+ /* COMPAT: Do not generate nodes for formatting spaces */
+ LIBXML_TEST_VERSION
+ xmlKeepBlanksDefault(0);
+
+ /* Turn on line numbers for debugging */
+ xmlLineNumbersDefault(1);
+
+ /* Substitute entities as files are parsed */
+ xmlSubstituteEntitiesDefault(1);
+
+ /* Don't load external entity subsets */
+ xmlLoadExtDtdDefaultValue = 0;
+
+ /* Don't validate against DTD by default */
+ xmlDoValidityCheckingDefaultValue = 0;
+
+ /* Set up output handlers for XML parsing */
+ xmlDefaultSAXHandler.warning = (warningSAXFunc)ofprintf_verbose;
+ xmlDefaultSAXHandler.error = (errorSAXFunc)ofprintf_terse;
+ xmlDefaultSAXHandler.fatalError = (fatalErrorSAXFunc)ofprintf_terse;
+}
+
+/*
+ * Clean up any remaining structures before exiting.
+ */
+void
+cleanup_xml()
+{
+ xsltCleanupGlobals();
+ xmlCleanupParser();
+}
+
+/*
+ * Converts a volume-request XML document into a request_t.
+ *
+ * @param doc
+ * an existing volume-request XML document
+ *
+ * @param request
+ * RETURN: a new request_t which must be freed via
+ * free_request
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+int
+xml_to_request(
+ xmlDocPtr doc,
+ request_t **request)
+{
+ int error = 0;
+
+ *request = NULL;
+
+ /* Validate doc against known DTD */
+ if ((error = validate_doc(
+ doc, ELEMENT_VOLUMEREQUEST, VOLUME_REQUEST_DTD_LOC)) == 0) {
+
+ /* Create a request */
+ if ((error = new_request(request)) == 0) {
+
+ /* Convert the XML doc into a request_t */
+ error = xml_to_devconfig(xmlDocGetRootElement(doc),
+ request_elements, request_get_diskset_req(*request));
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Converts a volume-defaults XML document into a defaults_t.
+ *
+ * @param doc
+ * an existing volume-defaults XML document
+ *
+ * @param defaults
+ * RETURN: a new defaults_t which must be freed via
+ * free_defaults
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+int
+xml_to_defaults(
+ xmlDocPtr doc,
+ defaults_t **defaults)
+{
+ int error = 0;
+
+ *defaults = NULL;
+
+ /* Validate doc against known DTD */
+ if ((error = validate_doc(doc, ELEMENT_VOLUMEDEFAULTS,
+ VOLUME_DEFAULTS_DTD_LOC)) == 0) {
+
+ /* Create request defaults */
+ if ((error = new_defaults(defaults)) == 0) {
+
+ devconfig_t *global;
+
+ /* Get defaults for all disk sets */
+ if ((error = defaults_get_diskset_by_name(
+ *defaults, NULL, &global)) == 0) {
+
+ /* Populate the global devconfig_t from the XML doc */
+ if ((error = xml_to_devconfig(xmlDocGetRootElement(doc),
+ default_elements, global)) == 0) {
+
+ /* Get the components of the global devconfig_t */
+ dlist_t *list = devconfig_get_components(global);
+
+ /*
+ * Move all named disk set settings out from
+ * under global settings
+ */
+ /* CONSTANTCONDITION */
+ while (1) {
+ dlist_t *removed = NULL;
+ devconfig_t *component;
+
+ /* Remove named disk set from under global */
+ list = dlist_remove_equivalent_item(
+ list, NULL, compare_is_a_diskset, &removed);
+
+ if (removed == NULL) {
+ /* No named disk set found */
+ break;
+ }
+
+ component = removed->obj;
+
+ /* Append named disk set to disk set list */
+ defaults_set_disksets(*defaults,
+ dlist_append(dlist_new_item(component),
+ defaults_get_disksets(*defaults), AT_TAIL));
+ }
+ }
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Converts a volume-config XML document into a devconfig_t.
+ *
+ * @param doc
+ * an existing volume-config XML document
+ *
+ * @param config
+ * RETURN: a new devconfig_t which must be freed via
+ * free_devconfig
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+int
+xml_to_config(
+ xmlDocPtr doc,
+ devconfig_t **config)
+{
+ int error = 0;
+
+ *config = NULL;
+
+ /* Validate doc against known DTD */
+ if ((error = validate_doc(
+ doc, ELEMENT_VOLUMECONFIG, VOLUME_CONFIG_DTD_LOC)) == 0) {
+
+ /* Create a devconfig_t */
+ if ((error = new_devconfig(config, TYPE_DISKSET)) == 0) {
+
+ /* Populate the devconfig_t from the XML doc */
+ error = xml_to_devconfig(
+ xmlDocGetRootElement(doc), config_elements, *config);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Converts a devconfig_t into a volume-config XML document.
+ *
+ * @param config
+ * an existing devconfig_t representing a volume
+ * configuration.
+ *
+ * @param doc
+ * RETURN: a new volume-config XML document which must be
+ * freed via xmlFreeDoc
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+int
+config_to_xml(
+ devconfig_t *config,
+ xmlDocPtr *doc)
+{
+ xmlNodePtr root;
+ int error = 0;
+
+ /* Create the XML document */
+ *doc = xmlNewDoc((xmlChar *)"1.0");
+
+ /* Create the root node */
+ root = xmlNewDocNode(
+ *doc, NULL, (xmlChar *)ELEMENT_VOLUMECONFIG, NULL);
+ xmlAddChild((xmlNodePtr)*doc, (xmlNodePtr)root);
+
+ /* Create sub-nodes from the config devconfig_t */
+ if ((error = devconfig_to_xml(root, config_elements, config)) == 0) {
+
+ /* Add DTD node and validate */
+ error = validate_doc(
+ *doc, ELEMENT_VOLUMECONFIG, VOLUME_CONFIG_DTD_LOC);
+ }
+
+ if (error) {
+ xmlFreeDoc(*doc);
+ }
+
+ return (error);
+}
+
+/*
+ * Converts a volume-config XML document into a Bourne shell script.
+ *
+ * @param doc
+ * an existing volume-config XML document
+ *
+ * @param commands
+ * RETURN: a new char* which must be freed
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+int
+xml_to_commands(
+ xmlDocPtr doc,
+ char **commands)
+{
+ char *tmpfile = NULL;
+ int error = 0;
+ xsltStylesheetPtr style = NULL;
+
+ /* Read in XSL stylesheet as a normal XML document */
+ xmlDocPtr xsl_doc = xmlSAXParseFile((xmlSAXHandlerPtr)
+ &xmlDefaultSAXHandler, VOLUME_COMMAND_XSL_LOC, 0);
+
+ if (xsl_doc != NULL && xsl_doc->xmlChildrenNode != NULL) {
+
+ /*
+ * Find the "msgfile" variable node. This is where
+ * we'll set the location of the file we'll create
+ * containing the localized messages.
+ */
+ xmlNodePtr msgfile_node = xml_find_node(
+ xmlDocGetRootElement(xsl_doc), (xmlChar *)ELEMENT_VARIABLE,
+ (xmlChar *)NAME_L10N_MESSAGE_FILE);
+
+ /*
+ * Find the "lang" node. This is where we'll set the
+ * current locale.
+ */
+ xmlNodePtr lang_node = xml_find_node(xmlDocGetRootElement(xsl_doc),
+ (xmlChar *)ELEMENT_PARAM, (xmlChar *)NAME_LANG);
+
+ /*
+ * Ignore if the nodes are not found -- the script
+ * will default to the C locale.
+ */
+ if (msgfile_node != NULL && lang_node != NULL) {
+ /* Get/set current locale in the "lang" node */
+ char *locale = setlocale(LC_MESSAGES, NULL);
+ xmlNodeSetContent(lang_node, (xmlChar *)locale);
+
+ /* Write localized messages to a temporary file */
+ if ((error = create_localized_message_file(&tmpfile)) == 0) {
+
+ char *newsel;
+
+ /* Clear current value of select attribute, if any */
+ xmlChar *cursel = xmlGetProp(
+ msgfile_node, (xmlChar *)ATTR_SELECT);
+ if (cursel != NULL) {
+ xmlFree(cursel);
+ }
+
+ /*
+ * The select attribute calls the XSLT function
+ * document() to load an external XML file
+ */
+ newsel = stralloccat(3, "document('", tmpfile, "')");
+
+ if (newsel == NULL) {
+ volume_set_error(gettext("out of memory"));
+ error = -1;
+ } else {
+
+ /* Set the new value of the select attribute */
+ xmlSetProp(msgfile_node,
+ (xmlChar *)ATTR_SELECT, (xmlChar *)newsel);
+
+ free(newsel);
+ }
+ }
+ }
+
+ if (error == 0) {
+ style = xsltParseStylesheetDoc(xsl_doc);
+ }
+ }
+
+ if (style == NULL) {
+ volume_set_error(
+ gettext("could not load stylesheet from %s"),
+ VOLUME_COMMAND_XSL_LOC);
+ error = -1;
+ } else {
+
+ xmlDocPtr result = xsltApplyStylesheet(style, doc, NULL);
+
+ if (result == NULL) {
+ volume_set_error(
+ gettext("could not apply stylesheet to volume-config"));
+ error = -1;
+ } else {
+ int length;
+
+ if (xsltSaveResultToString((xmlChar **)commands,
+ &length, result, style) == -1) {
+ error = ENOMEM;
+ }
+ }
+
+ xsltFreeStylesheet(style);
+ }
+
+ if (tmpfile != NULL) {
+ /* Ignore failure */
+ unlink(tmpfile);
+
+ free(tmpfile);
+ }
+
+ return (error);
+}
+
+/*
+ * ******************************************************************
+ *
+ * Static functions
+ *
+ * ******************************************************************
+ */
+
+/*
+ * Sets the external DTD node in the given XML document and then
+ * validates it.
+ *
+ * @param doc
+ * an existing XML document
+ *
+ * @param name
+ * the expected root element name of the XML document
+ *
+ * @param systemID
+ * the location of the DTD
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_doc(
+ xmlDocPtr doc,
+ const char *name,
+ const char *systemID)
+{
+ xmlValidCtxt context;
+ xmlDtdPtr dtd;
+
+ if (doc == NULL) {
+ volume_set_error(gettext("NULL %s document"), name);
+ return (-1);
+ }
+
+ /*
+ * Assume that we can't trust any DTD but our own.
+ */
+
+ /* Was a DTD (external or internal) included in the document? */
+ if ((dtd = xmlGetIntSubset(doc)) != NULL) {
+ /* Remove the DTD node */
+ oprintf(OUTPUT_DEBUG, gettext("Removing DTD from %s\n"), name);
+ xmlUnlinkNode((xmlNodePtr)dtd);
+ xmlFreeDtd(dtd);
+ }
+
+ /* Create the (external) DTD node */
+ oprintf(OUTPUT_DEBUG,
+ gettext("Creating new external DTD for %s\n"), name);
+ dtd = xmlCreateIntSubset(
+ doc, (xmlChar *)name, NULL, (xmlChar *)systemID);
+ if (dtd == NULL) {
+ volume_set_error(
+ gettext("could not create DTD node from %s"), systemID);
+ return (-1);
+ }
+
+ /* Validate against DTD */
+ oprintf(OUTPUT_DEBUG, gettext("Validating %s against DTD\n"), name);
+ context.userData = NULL;
+ context.error = (xmlValidityErrorFunc)ofprintf_terse;
+ context.warning = (xmlValidityWarningFunc)ofprintf_terse;
+ if (!xmlValidateDocument(&context, doc)) {
+ volume_set_error(gettext("invalid %s"), name);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Converts a devconfig_t into an XML node subject to the rules in
+ * the given element_t array.
+ *
+ * @param parent
+ * the XML node to which to add new XML nodes resulting
+ * from conversion of the given devconfig_t
+ *
+ * @param elements
+ * the element_ts that describe the structure of the XML
+ * document and govern the conversion of the given
+ * devconfig_t
+ *
+ * @param device
+ * the devconfig_t to convert
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+devconfig_to_xml(
+ xmlNodePtr parent,
+ element_t elements[],
+ devconfig_t *device)
+{
+ int i;
+ int error = 0;
+ xmlNodePtr node = NULL;
+
+ /* Get device type */
+ component_type_t type;
+ if ((error = devconfig_get_type(device, &type)) != 0) {
+ return (error);
+ }
+
+ /* Search for this element definition */
+ for (i = 0; elements[i].name != NULL; i++) {
+ element_t *element = &(elements[i]);
+
+ if (element->type == type) {
+ int j;
+ char **array;
+ dlist_t *components;
+
+ oprintf(OUTPUT_DEBUG, gettext("Element: %s\n"),
+ devconfig_type_to_str(type));
+
+ /* Create the XML node */
+ node = xmlNewChild(
+ parent, NULL, (xmlChar *)element->name, NULL);
+
+ /* For each attribute defined for this element... */
+ for (j = 0; element->attributes[j].name != NULL; j++) {
+ attr_t *attribute = &(element->attributes[j]);
+ char *value;
+
+ /* Is there a valid accessor for this attribute? */
+ if (attribute->get_as_string != NULL) {
+
+ /* Get the attribute value from the device */
+ switch (error = attribute->get_as_string(
+ device, attribute->name, &value)) {
+
+ /* Attribute is set in this device */
+ case 0:
+ oprintf(OUTPUT_DEBUG, " %s: %s\n",
+ attribute->name, value);
+
+ /* Set the value in the XML node */
+ xmlSetProp(node, (uchar_t *)attribute->name,
+ (uchar_t *)value);
+ free(value);
+
+ /* FALLTHROUGH */
+
+ /* Attribute is not set in this device */
+ case ERR_ATTR_UNSET:
+
+ error = 0;
+ break;
+
+ /* Error */
+ default:
+ return (error);
+ }
+ }
+ }
+
+ /* Is this node hierarchical? */
+ if (element->is_hierarchical == B_FALSE) {
+ node = parent;
+ }
+
+ /* Create <available> nodes */
+ array = devconfig_get_available(device);
+ if (array != NULL) {
+ for (j = 0; array[j] != NULL; j++) {
+ xmlNodePtr child = xmlNewChild(
+ node, NULL, (xmlChar *)ELEMENT_AVAILABLE, NULL);
+ xmlSetProp(child,
+ (xmlChar *)ATTR_NAME, (xmlChar *)array[j]);
+ }
+ }
+
+ /* Create <unavailable> nodes */
+ array = devconfig_get_unavailable(device);
+ if (array != NULL) {
+ for (j = 0; array[j] != NULL; j++) {
+ xmlNodePtr child = xmlNewChild(
+ node, NULL, (xmlChar *)ELEMENT_UNAVAILABLE, NULL);
+ xmlSetProp(child,
+ (xmlChar *)ATTR_NAME, (xmlChar *)array[j]);
+ }
+ }
+
+ /*
+ * Recursively convert subcomponents of this device to
+ * XML, taking care to encode them in the order
+ * specified in the element_t list (which should
+ * mirror what's expected by the DTD).
+ */
+
+ /* For each element type... */
+ for (j = 0; elements[j].name != NULL; j++) {
+
+ /* For each component of this device... */
+ for (components = devconfig_get_components(device);
+ components != NULL && error == 0;
+ components = components->next) {
+
+ devconfig_t *component = (devconfig_t *)components->obj;
+ component_type_t t;
+
+ /* Are the types the same? */
+ if ((error = devconfig_get_type(component, &t)) != 0) {
+ return (error);
+ } else {
+ if (elements[j].type == t) {
+ /* Encode child */
+ error = devconfig_to_xml(
+ node, elements, component);
+ }
+ }
+ }
+ }
+
+ /* Element found */
+ break;
+ }
+ }
+
+ /* Was this device successfully converted? */
+ if (node == NULL) {
+ volume_set_error(
+ gettext("can't convert device of type \"%s\" to XML element"),
+ devconfig_type_to_str(type));
+ error = -1;
+ }
+
+ return (error);
+}
+
+/*
+ * Converts an XML node into a devconfig_t subject to the rules in
+ * the given element_t array.
+ *
+ * @param cure
+ * the existing XML node to convert
+ *
+ * @param elements
+ * the element_ts that describe the structure of the XML
+ * document and govern the conversion of the given XML
+ * node
+ *
+ * @param device
+ * the devconfig_t node to which to add new devconfig_ts
+ * resulting from conversion of the given XML node
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+xml_to_devconfig(
+ xmlNodePtr cur,
+ element_t elements[],
+ devconfig_t *device)
+{
+ int error = 0;
+
+ /* For each child node... */
+ for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
+ int i;
+ boolean_t parsed_elem = B_FALSE;
+
+ /* Search for this element definition */
+ for (i = 0; elements[i].name != NULL; i++) {
+ element_t *element = &(elements[i]);
+
+ if (xmlStrcmp(cur->name, (xmlChar *)element->name) == 0) {
+ int j;
+ devconfig_t *component = NULL;
+
+ /* Flag that this element has been parsed */
+ parsed_elem = B_TRUE;
+
+ oprintf(OUTPUT_DEBUG, gettext("line %d: Element <%s>\n"),
+ XML_GET_LINE(cur), cur->name);
+
+ /* Should a new device be created for this element? */
+ if (element->is_hierarchical == B_TRUE) {
+
+ /* Should we use an existing device of this type? */
+ if (element->singleton) {
+ devconfig_get_component(
+ device, element->type, &component, B_FALSE);
+ }
+
+ if (component == NULL) {
+ oprintf(OUTPUT_DEBUG,
+ gettext("Creating new device\n"));
+
+ /* Create device of this type */
+ if ((error = new_devconfig(
+ &component, element->type)) != 0) {
+ return (error);
+ }
+
+ /* Add component to the toplevel device */
+ devconfig_set_components(
+ device, dlist_append(dlist_new_item(component),
+ devconfig_get_components(device), AT_TAIL));
+ }
+ } else {
+ component = device;
+ }
+
+ /* For each attribute defined for this element... */
+ for (j = 0; element->attributes[j].name != NULL; j++) {
+ attr_t *attribute = &(element->attributes[j]);
+
+ /* Get the value of this attribute */
+ char *value = (char *)
+ xmlGetProp(cur, (xmlChar *)attribute->name);
+
+ /* Was this attribute specified? */
+ if (value != NULL) {
+ oprintf(OUTPUT_DEBUG,
+ gettext("line %d:\tAttribute %s=%s\n"),
+ XML_GET_LINE(cur), attribute->name, value);
+
+ /* Set this value in the device */
+ if ((error = attribute->validate_set(
+ component, attribute->name, value)) != 0) {
+ return (error);
+ }
+ }
+ }
+
+ /* Get recursive sub-elements */
+ if ((error = xml_to_devconfig(
+ cur, elements, component)) != 0) {
+ return (error);
+ }
+
+ /* Element found */
+ break;
+ }
+ }
+
+
+ /* Make sure all non-text/comment elements were parsed */
+ if (parsed_elem == B_FALSE &&
+ xmlStrcmp(cur->name, (xmlChar *)ELEMENT_TEXT) != 0 &&
+ xmlStrcmp(cur->name, (xmlChar *)ELEMENT_COMMENT) != 0) {
+
+ oprintf(OUTPUT_DEBUG, gettext("Element <%s> NOT PARSED!!!\n"),
+ cur->name);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Returns 0 if obj2 (devconfig_t *) is a disk set, 1 otherwise.
+ */
+static int
+compare_is_a_diskset(
+ void *obj1,
+ void *obj2)
+{
+ return (devconfig_isA(
+ (devconfig_t *)obj2, TYPE_DISKSET) == B_TRUE ? 0 : 1);
+}
+
+/*
+ * Recursively searches the given xmlNodePtr for an element of the
+ * specified type and name.
+ *
+ * @param node
+ * the root node to search
+ *
+ * @param element
+ * the name of the element type
+ *
+ * @param name
+ * the value of the name attribute
+ *
+ * @return a valid xmlNodePtr if an element of the specified
+ * type and name was found, NULL otherwise.
+ */
+static xmlNodePtr
+xml_find_node(
+ xmlNodePtr node,
+ xmlChar *element,
+ xmlChar *name)
+{
+ xmlNodePtr child;
+
+ /* Is the element the right type? */
+ if (xmlStrcmp(element, node->name) == 0 &&
+
+ /* Does this element's name attribute match? */
+ xmlStrcmp(name, xmlGetProp(node, (xmlChar *)ATTR_NAME)) == 0) {
+
+ return (node);
+ }
+
+ /* Check child nodes */
+ for (child = node->xmlChildrenNode; child != NULL;
+ child = child->next) {
+ xmlNodePtr found = xml_find_node(child, element, name);
+
+ if (found != NULL) {
+ return (found);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * Creates an XML document containing all of the localized message
+ * strings for the generated command script.
+ *
+ * @return a xmlDocPtr which must be freed via xmlFreeDoc
+ */
+static xmlDocPtr
+create_localized_message_doc()
+{
+ int i;
+ char *locale;
+ xmlDocPtr doc;
+ xmlNodePtr root;
+ l10nmessage_t _cmd_messages[21];
+
+ /* Create the XML document */
+ doc = xmlNewDoc((xmlChar *)"1.0");
+
+ /* Create the root node */
+ root = xmlNewDocNode(
+ doc, NULL, (xmlChar *)ELEMENT_L10N, NULL);
+ xmlAddChild((xmlNodePtr) doc, (xmlNodePtr)root);
+
+ _cmd_messages[0].msgid = CMD_MSG_ENVIRONMENT;
+ _cmd_messages[0].message = gettext(CMD_MSG_ENVIRONMENT);
+ _cmd_messages[1].msgid = CMD_MSG_AMEND_PATH;
+ _cmd_messages[1].message = gettext(CMD_MSG_AMEND_PATH);
+ _cmd_messages[2].msgid = CMD_MSG_DISK_SET_NAME;
+ _cmd_messages[2].message = gettext(CMD_MSG_DISK_SET_NAME);
+ _cmd_messages[3].msgid = CMD_MSG_FUNCTIONS;
+ _cmd_messages[3].message = gettext(CMD_MSG_FUNCTIONS);
+ _cmd_messages[4].msgid = CMD_MSG_ECHO_AND_EXEC;
+ _cmd_messages[4].message = gettext(CMD_MSG_ECHO_AND_EXEC);
+ _cmd_messages[5].msgid = CMD_MSG_FMTHARD_SPECIAL;
+ _cmd_messages[5].message = gettext(CMD_MSG_FMTHARD_SPECIAL);
+ _cmd_messages[6].msgid = CMD_MSG_GET_FULL_PATH;
+ _cmd_messages[6].message = gettext(CMD_MSG_GET_FULL_PATH);
+ _cmd_messages[7].msgid = CMD_MSG_MAIN;
+ _cmd_messages[7].message = gettext(CMD_MSG_MAIN);
+ _cmd_messages[8].msgid = CMD_MSG_VERIFY_ROOT;
+ _cmd_messages[8].message = gettext(CMD_MSG_VERIFY_ROOT);
+ _cmd_messages[9].msgid = CMD_MSG_RUN_AS_ROOT;
+ _cmd_messages[9].message = gettext(CMD_MSG_RUN_AS_ROOT);
+ _cmd_messages[10].msgid = CMD_MSG_CHECK_FOR_VERBOSE;
+ _cmd_messages[10].message = gettext(CMD_MSG_CHECK_FOR_VERBOSE);
+ _cmd_messages[11].msgid = (CMD_MSG_DOES_DISK_SET_EXIST);
+ _cmd_messages[11].message = gettext(CMD_MSG_DOES_DISK_SET_EXIST);
+ _cmd_messages[12].msgid = (CMD_MSG_TAKE_DISK_SET);
+ _cmd_messages[12].message = gettext(CMD_MSG_TAKE_DISK_SET);
+ _cmd_messages[13].msgid = (CMD_MSG_CREATE_THE_DISK_SET);
+ _cmd_messages[13].message = gettext(CMD_MSG_CREATE_THE_DISK_SET);
+ _cmd_messages[14].msgid = (CMD_MSG_ADD_DISKS_TO_SET);
+ _cmd_messages[14].message = gettext(CMD_MSG_ADD_DISKS_TO_SET);
+ _cmd_messages[15].msgid = (CMD_MSG_FORMAT_SLICES);
+ _cmd_messages[15].message = gettext(CMD_MSG_FORMAT_SLICES);
+ _cmd_messages[16].msgid = (CMD_MSG_CREATE);
+ _cmd_messages[16].message = gettext(CMD_MSG_CREATE);
+ _cmd_messages[17].msgid = (CMD_MSG_DOES_EXIST);
+ _cmd_messages[17].message = gettext(CMD_MSG_DOES_EXIST);
+ _cmd_messages[18].msgid = (CMD_MSG_ADD_SLICES_TO);
+ _cmd_messages[18].message = gettext(CMD_MSG_ADD_SLICES_TO);
+ _cmd_messages[19].msgid = (CMD_MSG_ASSOCIATE_WITH_HSP);
+ _cmd_messages[19].message = gettext(CMD_MSG_ASSOCIATE_WITH_HSP);
+ _cmd_messages[20].msgid = NULL;
+
+ /* Get/set current locale in the "lang" node */
+ locale = setlocale(LC_MESSAGES, NULL);
+
+ /* Add localized <message> elements to stylesheet */
+ for (i = 0; _cmd_messages[i].msgid != NULL; i++) {
+ xmlNsPtr ns = xmlNewNs(NULL, NULL, NULL);
+
+ xmlNodePtr node = xmlNewTextChild(
+ root, ns, (xmlChar *)ELEMENT_MESSAGE,
+ (xmlChar *)_cmd_messages[i].message);
+ /* Lang attribute */
+ xmlSetProp(node,
+ (xmlChar *)ATTR_LANG, (xmlChar *)locale);
+
+ /* Message ID attribute */
+ xmlSetProp(node, (xmlChar *)ATTR_MESSAGEID,
+ (xmlChar *)_cmd_messages[i].msgid);
+ }
+
+ if (get_max_verbosity() >= OUTPUT_DEBUG) {
+ xmlChar *text;
+ /* Get the text dump */
+ xmlDocDumpFormatMemory(doc, &text, NULL, 1);
+ oprintf(OUTPUT_DEBUG,
+ gettext("Generated message file:\n%s"), text);
+ xmlFree(text);
+ }
+
+ return (doc);
+}
+
+/*
+ * Creates a temporary XML file containing all of the localized
+ * message strings for the generated command script.
+ *
+ * @param tmpfile
+ * RETURN: the name of the temporary XML file
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+create_localized_message_file(
+ char **tmpfile)
+{
+ int error = 0;
+
+ /*
+ * Create temporary file name -- "XXXXXX" is replaced with
+ * unique char sequence by mkstemp()
+ */
+ *tmpfile = stralloccat(3, "/tmp/", ELEMENT_L10N, "XXXXXX");
+
+ if (*tmpfile == NULL) {
+ volume_set_error(gettext("out of memory"));
+ error = -1;
+ } else {
+ int fildes;
+ FILE *msgfile = NULL;
+
+ /* Open temp file */
+ if ((fildes = mkstemp(*tmpfile)) != -1) {
+ msgfile = fdopen(fildes, "w");
+ }
+
+ if (msgfile == NULL) {
+ volume_set_error(gettext(
+ "could not open file for writing: %s"), *tmpfile);
+ error = -1;
+ } else {
+
+ xmlChar *text;
+ xmlDocPtr message_doc = create_localized_message_doc();
+ xmlDocDumpFormatMemory(message_doc, &text, NULL, 1);
+
+ if (fprintf(msgfile, "%s", text) < 0) {
+ volume_set_error(gettext(
+ "could not create localized message file: %s"),
+ *tmpfile);
+ error = -1;
+ }
+
+ xmlFree(text);
+ xmlFreeDoc(message_doc);
+ }
+
+ fclose(msgfile);
+ }
+
+ return (error);
+}
+
+/*
+ * Converts the given string into a boolean. The string must be
+ * either VALID_ATTR_TRUE or VALID_ATTR_FALSE.
+ *
+ * @param str
+ * the string to convert
+ *
+ * @param bool
+ * the addr of the boolean_t
+ *
+ * @return 0 if the given string could be converted to a boolean
+ * non-zero otherwise.
+ */
+static int
+strtobool(
+ char *str,
+ boolean_t *value)
+{
+ int error = 0;
+
+ if (strcmp(str, VALID_ATTR_TRUE) == 0) {
+ *value = B_TRUE;
+ } else
+
+ if (strcmp(str, VALID_ATTR_FALSE) == 0) {
+ *value = B_FALSE;
+ } else
+
+ error = -1;
+
+ return (error);
+}
+
+/*
+ * Wrapper for oprintf with a OUTPUT_TERSE level of verbosity.
+ * Provides an fprintf-like syntax to enable use as substitute output
+ * handler for man of the XML commands.
+ *
+ * @param unused
+ * unused, in favor of the FILE* passed to
+ * set_max_verbosity().
+ *
+ * @param fmt
+ * a printf-style format string
+ *
+ * @return the number of characters output
+ */
+static int
+ofprintf_terse(
+ void *unused,
+ char *fmt,
+ ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = oprintf_va(OUTPUT_TERSE, fmt, ap);
+ va_end(ap);
+
+ return (ret);
+}
+
+/*
+ * Wrapper for oprintf with a OUTPUT_VERBOSE level of verbosity.
+ * Provides an fprintf-like syntax to enable use as substitute output
+ * handler for man of the XML commands.
+ *
+ * @param unused
+ * unused, in favor of the FILE* passed to
+ * set_max_verbosity().
+ *
+ * @param fmt
+ * a printf-style format string
+ *
+ * @return the number of characters output
+ */
+static int
+ofprintf_verbose(
+ void *unused,
+ char *fmt,
+ ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = oprintf_va(OUTPUT_VERBOSE, fmt, ap);
+ va_end(ap);
+
+ return (ret);
+}
+
+/*
+ * ******************************************************************
+ *
+ * XML attribute validators/mutators
+ *
+ * These functions convert the given XML attribute string to the
+ * appropriate data type, and then pass it on to the appropriate
+ * devconfig_t mutator. A non-zero status is returned if the given
+ * string could not be converted or was invalid.
+ *
+ * ******************************************************************
+ */
+
+/*
+ * Validate and set the size attribute in the given volume
+ * devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the size
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_size(
+ devconfig_t *volume,
+ char *attr,
+ char *value)
+{
+ int error;
+ uint64_t size = 0;
+
+ /* Convert size string to bytes */
+ if ((error = sizestr_to_bytes(value, &size, size_units)) != 0) {
+ return (error);
+ }
+
+ /* Set size in volume */
+ return (devconfig_set_size(volume, size));
+}
+
+/*
+ * Validate and set the size_in_blocks attribute in the given slice
+ * devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the size_in_blocks
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_size_in_blocks(
+ devconfig_t *slice,
+ char *attr,
+ char *value)
+{
+ long long size;
+
+ /* Convert string to long long */
+ if (sscanf(value, "%lld", &size) != 1) {
+ volume_set_error(gettext("%s: invalid size in blocks"), value);
+ return (-1);
+ }
+
+ /* Set the number of submirrors in the slice */
+ return (devconfig_set_size_in_blocks(slice, (uint64_t)size));
+}
+
+/*
+ * Validate and set the name attribute in the given diskset
+ * devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the name
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param name
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_diskset_name(
+ devconfig_t *diskset,
+ char *attr,
+ char *name)
+{
+ return (devconfig_set_diskset_name(diskset, name));
+}
+
+/*
+ * Validate and add the given name to the list of available devices in
+ * the given volume devconfig_t.
+ *
+ * @param device
+ * the devconfig_t whose available device list to modify
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param name
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_add_available_name(
+ devconfig_t *device,
+ char *attr,
+ char *name)
+{
+ char **available;
+
+ /* Get available devices for this device */
+ available = devconfig_get_available(device);
+
+ /* Try to add name to array via realloc */
+ if ((available = append_to_string_array(available, name)) == NULL) {
+ return (ENOMEM);
+ }
+
+ /* Set available devices in the device */
+ devconfig_set_available(device, available);
+
+ return (0);
+}
+
+/*
+ * Validate and add the given name to the list of unavailable devices
+ * in the given volume devconfig_t.
+ *
+ * @param device
+ * the devconfig_t whose unavailable device list to modify
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param name
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_add_unavailable_name(
+ devconfig_t *device,
+ char *attr,
+ char *name)
+{
+ char **unavailable;
+
+ /* Get unavailable devices for this device */
+ unavailable = devconfig_get_unavailable(device);
+
+ /* Try to add name to array via realloc */
+ if ((unavailable = append_to_string_array(unavailable, name)) == NULL) {
+ return (ENOMEM);
+ }
+
+ /* Set unavailable devices in the device */
+ devconfig_set_unavailable(device, unavailable);
+
+ return (0);
+}
+
+/*
+ * Validate and set the name attribute in the given hsp devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the name
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param name
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_hsp_name(
+ devconfig_t *hsp,
+ char *attr,
+ char *name)
+{
+ return (devconfig_set_hsp_name(hsp, name));
+}
+
+/*
+ * Validate and set the name attribute in the given disk devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the name
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param name
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_disk_name(
+ devconfig_t *disk,
+ char *attr,
+ char *name)
+{
+ return (devconfig_set_name(disk, name));
+}
+
+/*
+ * Validate and set the name attribute in the given slice devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the name
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param name
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_slice_name(
+ devconfig_t *slice,
+ char *attr,
+ char *name)
+{
+ return (devconfig_set_name(slice, name));
+}
+
+/*
+ * Validate and set the start_block attribute in the given slice
+ * devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the start_block
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_slice_start_block(
+ devconfig_t *slice,
+ char *attr,
+ char *value)
+{
+ long long startsector;
+
+ /* Convert string to long long */
+ if (sscanf(value, "%lld", &startsector) != 1) {
+ volume_set_error(gettext("%s: invalid start sector"), value);
+ return (-1);
+ }
+
+ /* Set the number of submirrors in the slice */
+ return (devconfig_set_slice_start_block(slice, (uint64_t)startsector));
+}
+
+/*
+ * Validate and set the name attribute in the given volume
+ * devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the name
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param name
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_volume_name(
+ devconfig_t *volume,
+ char *attr,
+ char *name)
+{
+ return (devconfig_set_volume_name(volume, name));
+}
+
+/*
+ * Validate and set the interlace attribute in the given stripe
+ * devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the interlace
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_stripe_interlace(
+ devconfig_t *stripe,
+ char *attr,
+ char *value)
+{
+ int error;
+ uint64_t interlace = 0;
+
+ /* Convert interlace string to bytes */
+ if ((error = sizestr_to_bytes(
+ value, &interlace, interlace_units)) != 0) {
+ return (error);
+ }
+
+ /* Set interlace in stripe */
+ return (devconfig_set_stripe_interlace(stripe, interlace));
+}
+
+/*
+ * Validate and set the mincomp attribute in the given stripe
+ * devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the mincomp
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_stripe_mincomp(
+ devconfig_t *stripe,
+ char *attr,
+ char *value)
+{
+ uint16_t mincomp;
+
+ /* Convert string to a uint16_t */
+ if (str_to_uint16(value, &mincomp) != 0) {
+ volume_set_error(
+ gettext("invalid minimum stripe components (%s): %s"),
+ attr, value);
+ return (-1);
+ }
+
+ /* Set in stripe */
+ return (devconfig_set_stripe_mincomp(stripe, mincomp));
+}
+
+/*
+ * Validate and set the maxcomp attribute in the given stripe
+ * devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the maxcomp
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_stripe_maxcomp(
+ devconfig_t *stripe,
+ char *attr,
+ char *value)
+{
+ uint16_t maxcomp;
+
+ /* Convert string to a uint16_t */
+ if (str_to_uint16(value, &maxcomp) != 0) {
+ volume_set_error(
+ gettext("invalid maximum stripe components (%s): %s"),
+ attr, value);
+ return (-1);
+ }
+
+ /* Set in stripe */
+ return (devconfig_set_stripe_maxcomp(stripe, maxcomp));
+}
+
+/*
+ * Validate and set the usehsp attribute in the given volume
+ * devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the usehsp
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_volume_usehsp(
+ devconfig_t *volume,
+ char *attr,
+ char *value)
+{
+ boolean_t usehsp;
+
+ /* Get boolean value */
+ if (strtobool(value, &usehsp) != 0) {
+ volume_set_error(
+ gettext("%s: invalid boolean value for \"%s\" attribute"),
+ value, attr);
+ return (-1);
+ }
+
+ /* Set in volume */
+ return (devconfig_set_volume_usehsp(volume, usehsp));
+}
+
+/*
+ * Validate and set the nsubmirrors attribute in the given mirror
+ * devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the nsubmirrors
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_mirror_nsubmirrors(
+ devconfig_t *mirror,
+ char *attr,
+ char *value)
+{
+ uint16_t nsubmirrors;
+
+ /* Convert string to a uint16_t */
+ if (str_to_uint16(value, &nsubmirrors) != 0) {
+ volume_set_error(
+ gettext("invalid number of submirrors (%s): %s"),
+ attr, value);
+ return (-1);
+ }
+
+ /* Set in stripe */
+ return (devconfig_set_mirror_nsubs(mirror, nsubmirrors));
+}
+
+/*
+ * Validate and set the read attribute in the given mirror
+ * devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the read
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_mirror_read(
+ devconfig_t *mirror,
+ char *attr,
+ char *value)
+{
+ mirror_read_strategy_t strategy;
+
+ if (strcmp(value, VALID_MIRROR_READ_ROUNDROBIN) == 0) {
+ strategy = MIRROR_READ_ROUNDROBIN;
+ } else
+
+ if (strcmp(value, VALID_MIRROR_READ_GEOMETRIC) == 0) {
+ strategy = MIRROR_READ_GEOMETRIC;
+ } else
+
+ if (strcmp(value, VALID_MIRROR_READ_FIRST) == 0) {
+ strategy = MIRROR_READ_FIRST;
+ } else
+
+ {
+ volume_set_error(gettext("%s: invalid mirror read value"), value);
+ return (-1);
+ }
+
+ return (devconfig_set_mirror_read(mirror, strategy));
+}
+
+/*
+ * Validate and set the write attribute in the given mirror
+ * devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the write
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_mirror_write(
+ devconfig_t *mirror,
+ char *attr,
+ char *value)
+{
+ mirror_write_strategy_t strategy;
+
+ if (strcmp(value, VALID_MIRROR_WRITE_PARALLEL) == 0) {
+ strategy = MIRROR_WRITE_PARALLEL;
+ } else
+
+ if (strcmp(value, VALID_MIRROR_WRITE_SERIAL) == 0) {
+ strategy = MIRROR_WRITE_SERIAL;
+ } else
+
+ {
+ volume_set_error(gettext("%s: invalid mirror write value"), value);
+ return (-1);
+ }
+
+ return (devconfig_set_mirror_write(mirror, strategy));
+}
+
+/*
+ * Validate and set the passnum attribute in the given mirror
+ * devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the passnum
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_mirror_passnum(
+ devconfig_t *mirror,
+ char *attr,
+ char *value)
+{
+ uint16_t passnum;
+
+ /* Convert string to a uint16_t */
+ if (str_to_uint16(value, &passnum) != 0) {
+ volume_set_error(
+ gettext("invalid mirror pass number (%s): %s"),
+ attr, value);
+ return (-1);
+ }
+
+ /* Set in stripe */
+ return (devconfig_set_mirror_pass(mirror, passnum));
+}
+
+/*
+ * Validate and set the redundancy attribute in the given volume
+ * devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the redundancy
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_volume_redundancy(
+ devconfig_t *volume,
+ char *attr,
+ char *value)
+{
+ uint16_t redundancy;
+
+ /* Convert string to a uint16_t */
+ if (str_to_uint16(value, &redundancy) != 0) {
+ volume_set_error(
+ gettext("invalid redundancy level (%s): %s"),
+ attr, value);
+ return (-1);
+ }
+
+ /* Set in stripe */
+ return (devconfig_set_volume_redundancy_level(volume, redundancy));
+}
+
+/*
+ * Validate and set the datapaths attribute in the given volume
+ * devconfig_t.
+ *
+ * @param volume
+ * the devconfig_t in which to set the datapaths
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+validate_set_volume_datapaths(
+ devconfig_t *volume,
+ char *attr,
+ char *value)
+{
+ uint16_t redundancy;
+
+ /* Convert string to a uint16_t */
+ if (str_to_uint16(value, &redundancy) != 0) {
+ volume_set_error(
+ gettext("invalid number of data paths (%s): %s"),
+ attr, value);
+ return (-1);
+ }
+
+ /* Set in stripe */
+ return (devconfig_set_volume_npaths(volume, redundancy));
+}
+
+/*
+ * ******************************************************************
+ *
+ * XML attribute accessors/converters
+ *
+ * These functions get a value from the appropriate devconfig_t
+ * accessor, and then convert it to a string.
+ *
+ * ******************************************************************
+ */
+
+/*
+ * Get, as a string, the value of the name attribute of the given
+ * devconfig_t. This data must be freed.
+ *
+ * @param device
+ * the devconfig_t from which to retrieve the name
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * RETURN: the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+get_as_string_name(
+ devconfig_t *device,
+ char *attr,
+ char **value)
+{
+ int error;
+ char *name;
+
+ /* Get name */
+ if ((error = devconfig_get_name(device, &name)) == 0) {
+ if ((*value = strdup(name)) == NULL) {
+ error = ENOMEM;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Get, as a string, the value of the passnum attribute of the given
+ * mirror devconfig_t. This data must be freed.
+ *
+ * @param device
+ * the devconfig_t from which to retrieve the passnum
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * RETURN: the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+get_as_string_mirror_passnum(
+ devconfig_t *mirror,
+ char *attr,
+ char **value)
+{
+ int error;
+ uint16_t passnum;
+
+ /* Get mirror pass number */
+ if ((error = devconfig_get_mirror_pass(mirror, &passnum)) == 0) {
+ error = ll_to_str(passnum, value);
+ }
+
+ return (error);
+}
+
+/*
+ * Get, as a string, the value of the read attribute of the given
+ * mirror devconfig_t. This data must be freed.
+ *
+ * @param device
+ * the devconfig_t from which to retrieve the read
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * RETURN: the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+get_as_string_mirror_read(
+ devconfig_t *mirror,
+ char *attr,
+ char **value)
+{
+ int error;
+ mirror_read_strategy_t read;
+
+ /* Get mirror read strategy */
+ if ((error = devconfig_get_mirror_read(mirror, &read)) == 0) {
+ if ((*value = strdup(
+ devconfig_read_strategy_to_str(read))) == NULL) {
+ error = ENOMEM;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Get, as a string, the value of the write attribute of the given
+ * mirror devconfig_t. This data must be freed.
+ *
+ * @param device
+ * the devconfig_t from which to retrieve the write
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * RETURN: the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+get_as_string_mirror_write(
+ devconfig_t *mirror,
+ char *attr,
+ char **value)
+{
+ int error;
+ mirror_write_strategy_t write;
+
+ /* Get mirror write strategy */
+ if ((error = devconfig_get_mirror_write(mirror, &write)) == 0) {
+ if ((*value = strdup(
+ devconfig_write_strategy_to_str(write))) == NULL) {
+ error = ENOMEM;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Get, as a string, the value of the in_blocks attribute of the given
+ * device devconfig_t. This data must be freed.
+ *
+ * @param device
+ * the devconfig_t from which to retrieve the in_blocks
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * RETURN: the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+get_as_string_size_in_blocks(
+ devconfig_t *device,
+ char *attr,
+ char **value)
+{
+ int error;
+ uint64_t size;
+
+ /* Get size in blocks */
+ if ((error = devconfig_get_size_in_blocks(device, &size)) == 0) {
+ error = ll_to_str(size, value);
+ }
+
+ return (error);
+}
+
+/*
+ * Get, as a string, the value of the start_block attribute of the
+ * given slice devconfig_t. This data must be freed.
+ *
+ * @param device
+ * the devconfig_t from which to retrieve the start_block
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * RETURN: the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+get_as_string_slice_start_block(
+ devconfig_t *slice,
+ char *attr,
+ char **value)
+{
+ int error;
+ uint64_t start;
+
+ /* Get slice start block */
+ if ((error = devconfig_get_slice_start_block(slice, &start)) == 0) {
+ error = ll_to_str(start, value);
+ }
+
+ return (error);
+}
+
+/*
+ * Get, as a string, the value of the interlace attribute of the given
+ * stripe devconfig_t. This data must be freed.
+ *
+ * @param device
+ * the devconfig_t from which to retrieve the interlace
+ *
+ * @param attr
+ * the name of the XML attribute
+ *
+ * @param value
+ * RETURN: the value of the XML attribute
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+static int
+get_as_string_stripe_interlace(
+ devconfig_t *stripe,
+ char *attr,
+ char **value)
+{
+ int error;
+ uint64_t interlace;
+
+ /* Get interlace */
+ if ((error = devconfig_get_stripe_interlace(
+ stripe, &interlace)) == 0) {
+ error = bytes_to_sizestr(interlace, value, interlace_units, B_TRUE);
+ }
+
+ return (error);
+}
diff --git a/usr/src/cmd/lvm/metassist/xml/xml_convert.h b/usr/src/cmd/lvm/metassist/xml/xml_convert.h
new file mode 100644
index 0000000000..a6017111bc
--- /dev/null
+++ b/usr/src/cmd/lvm/metassist/xml/xml_convert.h
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ */
+
+#ifndef _XML_CONVERT_H
+#define _XML_CONVERT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libxml/parser.h>
+#include "volume_request.h"
+#include "volume_defaults.h"
+
+/* The location of the volume-request.dtd */
+#define VOLUME_REQUEST_DTD_LOC "/usr/share/lib/xml/dtd/volume-request.dtd"
+
+/* The location of the volume-request-defaults.dtd */
+#define VOLUME_DEFAULTS_DTD_LOC "/usr/share/lib/xml/dtd/volume-defaults.dtd"
+
+/* The location of the volume-config.dtd */
+#define VOLUME_CONFIG_DTD_LOC "/usr/share/lib/xml/dtd/volume-config.dtd"
+
+/* Location of the volume-command.xsl file */
+#define VOLUME_COMMAND_XSL_LOC "/usr/share/lib/xml/style/volume-command.xsl"
+
+/*
+ * Valid values for attributes
+ */
+#define VALID_ATTR_TRUE "TRUE"
+#define VALID_ATTR_FALSE "FALSE"
+#define VALID_MIRROR_READ_GEOMETRIC "GEOMETRIC"
+#define VALID_MIRROR_READ_FIRST "FIRST"
+#define VALID_MIRROR_READ_ROUNDROBIN "ROUNDROBIN"
+#define VALID_MIRROR_WRITE_SERIAL "SERIAL"
+#define VALID_MIRROR_WRITE_PARALLEL "PARALLEL"
+
+/*
+ * Standard units
+ */
+#define UNIT_BLOCKS "BLOCKS"
+#define UNIT_KILOBYTES "KB"
+#define UNIT_MEGABYTES "MB"
+#define UNIT_GIGABYTES "GB"
+#define UNIT_TERABYTES "TB"
+
+/*
+ * Initialize the XML parser, setting defaults across all XML
+ * routines.
+ */
+extern void init_xml();
+
+/*
+ * Clean up any remaining structures before exiting.
+ */
+extern void cleanup_xml();
+
+/*
+ * Converts a volume-request XML document into a request_t.
+ *
+ * @param doc
+ * an existing volume-request XML document
+ *
+ * @param request
+ * RETURN: a new request_t which must be freed via
+ * free_request
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+extern int xml_to_request(xmlDocPtr doc, request_t **request);
+
+/*
+ * Converts a volume-defaults XML document into a defaults_t.
+ *
+ * @param doc
+ * an existing volume-defaults XML document
+ *
+ * @param defaults
+ * RETURN: a new defaults_t which must be freed via
+ * free_defaults
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+extern int xml_to_defaults(xmlDocPtr doc, defaults_t **defaults);
+
+/*
+ * Converts a volume-config XML document into a devconfig_t.
+ *
+ * @param doc
+ * an existing volume-config XML document
+ *
+ * @param config
+ * RETURN: a new devconfig_t which must be freed via
+ * free_devconfig
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+extern int xml_to_config(xmlDocPtr doc, devconfig_t **config);
+
+/*
+ * Converts a devconfig_t into a volume-config XML document.
+ *
+ * @param config
+ * an existing devconfig_t representing a volume
+ * configuration.
+ *
+ * @param doc
+ * RETURN: a new volume-config XML document which must be
+ * freed via xmlFreeDoc
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+extern int config_to_xml(devconfig_t *config, xmlDocPtr *doc);
+
+/*
+ * Converts a volume-config XML document into a Bourne shell script.
+ *
+ * @param doc
+ * an existing volume-config XML document
+ *
+ * @param commands
+ * RETURN: a new char* which must be freed
+ *
+ * @return 0 on success, non-zero otherwise.
+ */
+extern int xml_to_commands(xmlDocPtr doc, char **commands);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _XML_CONVERT_H */